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:
|
try:
|
||||||
user_id = msg.from_user.id
|
user_id = msg.from_user.id
|
||||||
sd_workflow_logger.info(f"🚀 [SD Workflow Start] User: {user_id}")
|
sd_workflow_logger.info(f"🚀 [SD Workflow Start] User: {user_id}")
|
||||||
if states.get_state(user_id) != "SD_MODE":
|
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")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Если только вложение без текста — сохраняем и ждем текст
|
msg_text = _extract_text_from_content(msg.content)
|
||||||
if not msg_text.strip() and inline_attachments:
|
is_attachment = hasattr(msg.type, "name") and msg.type.name == "ATTACHMENT"
|
||||||
session["files_queue"].extend(inline_attachments)
|
inline_attachments = _extract_attachments_from_content(msg.content, is_attachment)
|
||||||
sd_workflow_logger.info(f"📎 [SD] Saved {len(inline_attachments)} attachment(s), waiting for text")
|
|
||||||
await msg.answer(SD_TEXT_REQUIRED, parse_mode="html")
|
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
|
return
|
||||||
|
|
||||||
# Если пришел текст (с вложениями или без)
|
if user_id not in sd_sessions:
|
||||||
if inline_attachments:
|
sd_sessions[user_id] = {"step": "need_text", "files_queue": [], "post_create_queue": []}
|
||||||
session["files_queue"].extend(inline_attachments)
|
session = sd_sessions[user_id]
|
||||||
sd_workflow_logger.info(f"📎 [SD] Added {len(inline_attachments)} inline attachment(s) to queue")
|
|
||||||
|
|
||||||
# Настраиваем параметры сессии для ожидания
|
# --- ШАГ 1: ОЖИДАНИЕ ТЕКСТА ---
|
||||||
session["step"] = "waiting_for_attachments"
|
if session["step"] == "need_text":
|
||||||
session["msg_text"] = msg_text
|
if not msg_text.strip() and not inline_attachments:
|
||||||
session["msg"] = msg
|
await msg.answer(SD_UNKNOWN_CMD_TEXT, parse_mode="html")
|
||||||
session["user_id"] = user_id
|
return
|
||||||
session["login"] = login
|
|
||||||
|
|
||||||
# Сразу выдаем ОДНО сообщение пользователю, чтобы он видел реакцию бота
|
# Если только вложение без текста — сохраняем и ждем текст
|
||||||
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():
|
if inline_attachments:
|
||||||
session["timer_task"].cancel()
|
session["files_queue"].extend(inline_attachments)
|
||||||
session["timer_task"] = asyncio.create_task(_wait_for_attachments_and_create(user_id, session))
|
sd_workflow_logger.info(f"📎 [SD] Added {len(inline_attachments)} inline attachment(s) to queue")
|
||||||
sd_workflow_logger.info(f"⏳ [SD] Timer started: waiting {ATTACHMENT_WAIT_TIMEOUT}s for potentially more attachments...")
|
|
||||||
return
|
|
||||||
|
|
||||||
# --- ШАГ 2: РЕЖИМ ОЖИДАНИЯ ДОП. ВЛОЖЕНИЙ (Сброс таймаута) ---
|
# Настраиваем параметры сессии для ожидания
|
||||||
if session["step"] == "waiting_for_attachments":
|
session["step"] = "waiting_for_attachments"
|
||||||
if inline_attachments:
|
session["msg_text"] = msg_text
|
||||||
session["files_queue"].extend(inline_attachments)
|
session["msg"] = msg
|
||||||
sd_workflow_logger.info(f"📎 [SD] Received {len(inline_attachments)} more files. Total queue: {len(session['files_queue'])}")
|
session["user_id"] = user_id
|
||||||
if msg_text.strip():
|
session["login"] = login
|
||||||
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():
|
await msg.answer(SD_CREATING, parse_mode="html")
|
||||||
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: ЗАЯВКА СОЗДАЕТСЯ (Перехват файлов из "черной дыры") ---
|
# Запускаем фоновый таймер с механизмом сброса (Debounce)
|
||||||
if session["step"] == "creating_ticket":
|
if "timer_task" in session and not session["timer_task"].done():
|
||||||
if inline_attachments:
|
session["timer_task"].cancel()
|
||||||
if "post_create_queue" not in session:
|
session["timer_task"] = asyncio.create_task(_wait_for_attachments_and_create(user_id, session))
|
||||||
session["post_create_queue"] = []
|
sd_workflow_logger.info(f"⏳ [SD] Timer started: waiting {ATTACHMENT_WAIT_TIMEOUT}s for potentially more attachments...")
|
||||||
session["post_create_queue"].extend(inline_attachments)
|
return
|
||||||
sd_workflow_logger.info(f"📥 [SD Hole-Fix] Captured {len(inline_attachments)} file(s) DURING ticket creation API call.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# --- ШАГ 4: ПРИЕМ ДОП. ФАЙЛОВ К УЖЕ СОЗДАННОЙ ЗАЯВКЕ ---
|
# --- ШАГ 2: РЕЖИМ ОЖИДАНИЯ ДОП. ВЛОЖЕНИЙ (Сброс таймаута) ---
|
||||||
if session["step"] == "ticket_created":
|
if session["step"] == "waiting_for_attachments":
|
||||||
if inline_attachments:
|
if inline_attachments:
|
||||||
ticket_id = session["ticket_id"]
|
session["files_queue"].extend(inline_attachments)
|
||||||
for att in inline_attachments:
|
sd_workflow_logger.info(f"📎 [SD] Received {len(inline_attachments)} more files. Total queue: {len(session['files_queue'])}")
|
||||||
await msg.answer(sd_uploading_file(ticket_id), parse_mode="html")
|
if msg_text.strip():
|
||||||
ok = await process_and_upload_file(att['file_id'], att['file_name'], ticket_id)
|
session["msg_text"] = session["msg_text"] + " " + msg_text.strip()
|
||||||
sd_workflow_logger.info(f"📎 [Late File Upload] Name: {att['file_name']} -> Success: {ok}")
|
sd_workflow_logger.info(f"📝 [SD] Additional text appended")
|
||||||
if ok:
|
|
||||||
log_menu_stats(user_id, "Service Desk", f"Добавление файла к заявке #{ticket_id}")
|
# Перезапускаем таймер (пользователь активен, сдвигаем окно создания вперед)
|
||||||
await msg.answer(sd_file_attached(), parse_mode="html")
|
if "timer_task" in session and not session["timer_task"].done():
|
||||||
else:
|
session["timer_task"].cancel()
|
||||||
await msg.answer(system_error_text(EMOJI_DIGITS), parse_mode="html")
|
session["timer_task"] = asyncio.create_task(_wait_for_attachments_and_create(user_id, session))
|
||||||
elif msg_text.strip():
|
sd_workflow_logger.info(f"🔄 [SD] Timer reset due to user activity. Waiting another {ATTACHMENT_WAIT_TIMEOUT}s")
|
||||||
await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html")
|
return
|
||||||
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