fix: wrap entire SD handler in try-except to prevent crashes when SD is unreachable
This commit is contained in:
+103
-96
@@ -287,113 +287,120 @@ async def sd_module_handler(msg: Message):
|
||||
try:
|
||||
user_id = msg.from_user.id
|
||||
sd_workflow_logger.info(f"🚀 [SD Workflow Start] User: {user_id}")
|
||||
if states.get_state(user_id) != "SD_MODE":
|
||||
return
|
||||
|
||||
msg_text = _extract_text_from_content(msg.content)
|
||||
is_attachment = hasattr(msg.type, "name") and msg.type.name == "ATTACHMENT"
|
||||
inline_attachments = _extract_attachments_from_content(msg.content, is_attachment)
|
||||
|
||||
sd_workflow_logger.info(f"🚀 [SD Debug] msg.type={msg.type}, content_type={type(msg.content).__name__}, msg_text='{msg_text}', is_attachment={is_attachment}, inline_attachments={len(inline_attachments)}")
|
||||
|
||||
msg.handled = True
|
||||
login = user_id.split("@")[0] if "@" in user_id else user_id
|
||||
cmd = msg_text.lower()
|
||||
|
||||
# 🔄 НОРМАЛИЗАЦИЯ КНОПОК ВК-ЭМОДЗИ
|
||||
for raw_num, emoji_num in EMOJI_DIGITS.items():
|
||||
if cmd == emoji_num:
|
||||
cmd = raw_num
|
||||
break
|
||||
|
||||
# --- ВЫХОД (Кнопки 0 и 9 возвращают в общее меню) ---
|
||||
if cmd in ["0", "9", "/start", "меню"]:
|
||||
log_menu_stats(user_id, "Service Desk", "Выход в главное меню")
|
||||
states.clear_state(user_id)
|
||||
sd_sessions.pop(user_id, None)
|
||||
await msg.answer(MENU_TEXT, parse_mode="html")
|
||||
return
|
||||
|
||||
if user_id not in sd_sessions:
|
||||
sd_sessions[user_id] = {"step": "need_text", "files_queue": [], "post_create_queue": []}
|
||||
session = sd_sessions[user_id]
|
||||
|
||||
# --- ШАГ 1: ОЖИДАНИЕ ТЕКСТА ---
|
||||
if session["step"] == "need_text":
|
||||
if not msg_text.strip() and not inline_attachments:
|
||||
await msg.answer(SD_UNKNOWN_CMD_TEXT, parse_mode="html")
|
||||
if states.get_state(user_id) != "SD_MODE":
|
||||
return
|
||||
|
||||
# Если только вложение без текста — сохраняем и ждем текст
|
||||
if not msg_text.strip() and inline_attachments:
|
||||
session["files_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📎 [SD] Saved {len(inline_attachments)} attachment(s), waiting for text")
|
||||
await msg.answer(SD_TEXT_REQUIRED, parse_mode="html")
|
||||
msg_text = _extract_text_from_content(msg.content)
|
||||
is_attachment = hasattr(msg.type, "name") and msg.type.name == "ATTACHMENT"
|
||||
inline_attachments = _extract_attachments_from_content(msg.content, is_attachment)
|
||||
|
||||
sd_workflow_logger.info(f"🚀 [SD Debug] msg.type={msg.type}, content_type={type(msg.content).__name__}, msg_text='{msg_text}', is_attachment={is_attachment}, inline_attachments={len(inline_attachments)}")
|
||||
|
||||
msg.handled = True
|
||||
login = user_id.split("@")[0] if "@" in user_id else user_id
|
||||
cmd = msg_text.lower()
|
||||
|
||||
# 🔄 НОРМАЛИЗАЦИЯ КНОПОК ВК-ЭМОДЗИ
|
||||
for raw_num, emoji_num in EMOJI_DIGITS.items():
|
||||
if cmd == emoji_num:
|
||||
cmd = raw_num
|
||||
break
|
||||
|
||||
# --- ВЫХОД (Кнопки 0 и 9 возвращают в общее меню) ---
|
||||
if cmd in ["0", "9", "/start", "меню"]:
|
||||
log_menu_stats(user_id, "Service Desk", "Выход в главное меню")
|
||||
states.clear_state(user_id)
|
||||
sd_sessions.pop(user_id, None)
|
||||
await msg.answer(MENU_TEXT, parse_mode="html")
|
||||
return
|
||||
|
||||
# Если пришел текст (с вложениями или без)
|
||||
if inline_attachments:
|
||||
session["files_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📎 [SD] Added {len(inline_attachments)} inline attachment(s) to queue")
|
||||
if user_id not in sd_sessions:
|
||||
sd_sessions[user_id] = {"step": "need_text", "files_queue": [], "post_create_queue": []}
|
||||
session = sd_sessions[user_id]
|
||||
|
||||
# Настраиваем параметры сессии для ожидания
|
||||
session["step"] = "waiting_for_attachments"
|
||||
session["msg_text"] = msg_text
|
||||
session["msg"] = msg
|
||||
session["user_id"] = user_id
|
||||
session["login"] = login
|
||||
# --- ШАГ 1: ОЖИДАНИЕ ТЕКСТА ---
|
||||
if session["step"] == "need_text":
|
||||
if not msg_text.strip() and not inline_attachments:
|
||||
await msg.answer(SD_UNKNOWN_CMD_TEXT, parse_mode="html")
|
||||
return
|
||||
|
||||
# Сразу выдаем ОДНО сообщение пользователю, чтобы он видел реакцию бота
|
||||
await msg.answer(SD_CREATING, parse_mode="html")
|
||||
# Если только вложение без текста — сохраняем и ждем текст
|
||||
if not msg_text.strip() and inline_attachments:
|
||||
session["files_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📎 [SD] Saved {len(inline_attachments)} attachment(s), waiting for text")
|
||||
await msg.answer(SD_TEXT_REQUIRED, parse_mode="html")
|
||||
return
|
||||
|
||||
# Запускаем фоновый таймер с механизмом сброса (Debounce)
|
||||
if "timer_task" in session and not session["timer_task"].done():
|
||||
session["timer_task"].cancel()
|
||||
session["timer_task"] = asyncio.create_task(_wait_for_attachments_and_create(user_id, session))
|
||||
sd_workflow_logger.info(f"⏳ [SD] Timer started: waiting {ATTACHMENT_WAIT_TIMEOUT}s for potentially more attachments...")
|
||||
return
|
||||
# Если пришел текст (с вложениями или без)
|
||||
if inline_attachments:
|
||||
session["files_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📎 [SD] Added {len(inline_attachments)} inline attachment(s) to queue")
|
||||
|
||||
# --- ШАГ 2: РЕЖИМ ОЖИДАНИЯ ДОП. ВЛОЖЕНИЙ (Сброс таймаута) ---
|
||||
if session["step"] == "waiting_for_attachments":
|
||||
if inline_attachments:
|
||||
session["files_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📎 [SD] Received {len(inline_attachments)} more files. Total queue: {len(session['files_queue'])}")
|
||||
if msg_text.strip():
|
||||
session["msg_text"] = session["msg_text"] + " " + msg_text.strip()
|
||||
sd_workflow_logger.info(f"📝 [SD] Additional text appended")
|
||||
# Настраиваем параметры сессии для ожидания
|
||||
session["step"] = "waiting_for_attachments"
|
||||
session["msg_text"] = msg_text
|
||||
session["msg"] = msg
|
||||
session["user_id"] = user_id
|
||||
session["login"] = login
|
||||
|
||||
# Перезапускаем таймер (пользователь активен, сдвигаем окно создания вперед)
|
||||
if "timer_task" in session and not session["timer_task"].done():
|
||||
session["timer_task"].cancel()
|
||||
session["timer_task"] = asyncio.create_task(_wait_for_attachments_and_create(user_id, session))
|
||||
sd_workflow_logger.info(f"🔄 [SD] Timer reset due to user activity. Waiting another {ATTACHMENT_WAIT_TIMEOUT}s")
|
||||
return
|
||||
# Сразу выдаем ОДНО сообщение пользователю, чтобы он видел реакцию бота
|
||||
await msg.answer(SD_CREATING, parse_mode="html")
|
||||
|
||||
# --- ШАГ 3: ЗАЯВКА СОЗДАЕТСЯ (Перехват файлов из "черной дыры") ---
|
||||
if session["step"] == "creating_ticket":
|
||||
if inline_attachments:
|
||||
if "post_create_queue" not in session:
|
||||
session["post_create_queue"] = []
|
||||
session["post_create_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📥 [SD Hole-Fix] Captured {len(inline_attachments)} file(s) DURING ticket creation API call.")
|
||||
return
|
||||
# Запускаем фоновый таймер с механизмом сброса (Debounce)
|
||||
if "timer_task" in session and not session["timer_task"].done():
|
||||
session["timer_task"].cancel()
|
||||
session["timer_task"] = asyncio.create_task(_wait_for_attachments_and_create(user_id, session))
|
||||
sd_workflow_logger.info(f"⏳ [SD] Timer started: waiting {ATTACHMENT_WAIT_TIMEOUT}s for potentially more attachments...")
|
||||
return
|
||||
|
||||
# --- ШАГ 4: ПРИЕМ ДОП. ФАЙЛОВ К УЖЕ СОЗДАННОЙ ЗАЯВКЕ ---
|
||||
if session["step"] == "ticket_created":
|
||||
if inline_attachments:
|
||||
ticket_id = session["ticket_id"]
|
||||
for att in inline_attachments:
|
||||
await msg.answer(sd_uploading_file(ticket_id), parse_mode="html")
|
||||
ok = await process_and_upload_file(att['file_id'], att['file_name'], ticket_id)
|
||||
sd_workflow_logger.info(f"📎 [Late File Upload] Name: {att['file_name']} -> Success: {ok}")
|
||||
if ok:
|
||||
log_menu_stats(user_id, "Service Desk", f"Добавление файла к заявке #{ticket_id}")
|
||||
await msg.answer(sd_file_attached(), parse_mode="html")
|
||||
else:
|
||||
await msg.answer(system_error_text(EMOJI_DIGITS), parse_mode="html")
|
||||
elif msg_text.strip():
|
||||
await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html")
|
||||
return
|
||||
# --- ШАГ 2: РЕЖИМ ОЖИДАНИЯ ДОП. ВЛОЖЕНИЙ (Сброс таймаута) ---
|
||||
if session["step"] == "waiting_for_attachments":
|
||||
if inline_attachments:
|
||||
session["files_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📎 [SD] Received {len(inline_attachments)} more files. Total queue: {len(session['files_queue'])}")
|
||||
if msg_text.strip():
|
||||
session["msg_text"] = session["msg_text"] + " " + msg_text.strip()
|
||||
sd_workflow_logger.info(f"📝 [SD] Additional text appended")
|
||||
|
||||
# Перезапускаем таймер (пользователь активен, сдвигаем окно создания вперед)
|
||||
if "timer_task" in session and not session["timer_task"].done():
|
||||
session["timer_task"].cancel()
|
||||
session["timer_task"] = asyncio.create_task(_wait_for_attachments_and_create(user_id, session))
|
||||
sd_workflow_logger.info(f"🔄 [SD] Timer reset due to user activity. Waiting another {ATTACHMENT_WAIT_TIMEOUT}s")
|
||||
return
|
||||
|
||||
# --- ШАГ 3: ЗАЯВКА СОЗДАЕТСЯ (Перехват файлов из "черной дыры") ---
|
||||
if session["step"] == "creating_ticket":
|
||||
if inline_attachments:
|
||||
if "post_create_queue" not in session:
|
||||
session["post_create_queue"] = []
|
||||
session["post_create_queue"].extend(inline_attachments)
|
||||
sd_workflow_logger.info(f"📥 [SD Hole-Fix] Captured {len(inline_attachments)} file(s) DURING ticket creation API call.")
|
||||
return
|
||||
|
||||
# --- ШАГ 4: ПРИЕМ ДОП. ФАЙЛОВ К УЖЕ СОЗДАННОЙ ЗАЯВКЕ ---
|
||||
if session["step"] == "ticket_created":
|
||||
if inline_attachments:
|
||||
ticket_id = session["ticket_id"]
|
||||
for att in inline_attachments:
|
||||
await msg.answer(sd_uploading_file(ticket_id), parse_mode="html")
|
||||
ok = await process_and_upload_file(att['file_id'], att['file_name'], ticket_id)
|
||||
sd_workflow_logger.info(f"📎 [Late File Upload] Name: {att['file_name']} -> Success: {ok}")
|
||||
if ok:
|
||||
log_menu_stats(user_id, "Service Desk", f"Добавление файла к заявке #{ticket_id}")
|
||||
await msg.answer(sd_file_attached(), parse_mode="html")
|
||||
else:
|
||||
await msg.answer(system_error_text(EMOJI_DIGITS), parse_mode="html")
|
||||
elif msg_text.strip():
|
||||
await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html")
|
||||
return
|
||||
except Exception as e:
|
||||
logger.exception(f"❌ [SD Fatal Error] user={getattr(msg.from_user, 'id', 'unknown')}: {e}")
|
||||
sd_workflow_logger.error(f"❌ [SD Fatal] {e}")
|
||||
try:
|
||||
await msg.answer(system_error_text(EMOJI_DIGITS), parse_mode="html")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user