fix: add try/except guards to all unprotected Telegram handler functions
All checks were successful
CD Pipeline / deploy (push) Successful in 1m29s

- Replace 2 silent `except Exception: pass` with logger.warning in handle_callback
- Wrap _handle_await_callback, _handle_main_menu_callback with top-level try/except (query.answer on error)
- Wrap _handle_complex_ai_response, _handle_simple_ai_response, _enhanced_keyword_matching, _process_await_input with top-level try/except (update.message.reply_text on error)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ogt
2026-04-27 19:47:49 +08:00
parent b4d208d34a
commit b3a7909b2b

View File

@@ -1412,134 +1412,155 @@ class TrendTelegramBot:
async def _handle_complex_ai_response(self, update: Update, ai_result: dict):
"""Handle complex AI responses"""
response_text = ai_result.get('response_text', '正在處理您的請求...')
try:
response_text = ai_result.get('response_text', '正在處理您的請求...')
await update.message.reply_text(
f"⏳ 處理中...\n\n{response_text}",
parse_mode='Markdown'
)
await update.message.reply_text(
f"⏳ 處理中...\n\n{response_text}",
parse_mode='Markdown'
)
query_type = ai_result.get('query_type', 'general query')
suggestions = self._get_query_suggestions(query_type)
query_type = ai_result.get('query_type', 'general query')
suggestions = self._get_query_suggestions(query_type)
await update.message.reply_text(
"根據您的需求,建議使用以下功能:\n\n" +
"\n".join([f"· {s}" for s in suggestions]) +
"\n\n或透過選單直接操作:",
reply_markup=self._get_main_menu_keyboard()
)
await update.message.reply_text(
"根據您的需求,建議使用以下功能:\n\n" +
"\n".join([f"· {s}" for s in suggestions]) +
"\n\n或透過選單直接操作:",
reply_markup=self._get_main_menu_keyboard()
)
except Exception as e:
logger.error(f"[_handle_complex_ai_response] 處理失敗: {e}", exc_info=True)
try:
await update.message.reply_text("❌ 處理時發生錯誤,請稍後再試或使用 /help")
except Exception:
pass
async def _handle_simple_ai_response(self, update: Update, ai_result: dict):
"""Handle simple AI responses"""
response_text = ai_result.get('response_text', '請問有什麼我可以幫您的嗎?')
suggestions = ai_result.get('suggestions', [])
show_menu = ai_result.get('show_menu', False)
if suggestions and not show_menu:
# Show suggestions as buttons
keyboard = []
for i, suggestion in enumerate(suggestions):
if i % 2 == 0:
keyboard.append([])
keyboard[-1].append({
'text': suggestion,
'callback_data': f'momo:cmd:suggestion:{suggestion.lower().replace(" ", "_")}'
})
try:
response_text = ai_result.get('response_text', '請問有什麼我可以幫您的嗎?')
suggestions = ai_result.get('suggestions', [])
show_menu = ai_result.get('show_menu', False)
# Add main menu button
keyboard.append([{'text': '主選單', 'callback_data': 'momo:menu:main'}])
await update.message.reply_text(
response_text,
reply_markup=InlineKeyboardMarkup(keyboard)
)
else:
# Show with main menu
await update.message.reply_text(
response_text,
reply_markup=self._get_main_menu_keyboard()
)
if suggestions and not show_menu:
# Show suggestions as buttons
keyboard = []
for i, suggestion in enumerate(suggestions):
if i % 2 == 0:
keyboard.append([])
keyboard[-1].append({
'text': suggestion,
'callback_data': f'momo:cmd:suggestion:{suggestion.lower().replace(" ", "_")}'
})
# Add main menu button
keyboard.append([{'text': '主選單', 'callback_data': 'momo:menu:main'}])
await update.message.reply_text(
response_text,
reply_markup=InlineKeyboardMarkup(keyboard)
)
else:
# Show with main menu
await update.message.reply_text(
response_text,
reply_markup=self._get_main_menu_keyboard()
)
except Exception as e:
logger.error(f"[_handle_simple_ai_response] 處理失敗: {e}", exc_info=True)
try:
await update.message.reply_text("❌ 處理時發生錯誤,請稍後再試或使用 /help")
except Exception:
pass
async def _enhanced_keyword_matching(self, update: Update, context, text: str):
"""Enhanced keyword matching as fallback with Traditional Chinese responses"""
import re
from datetime import datetime, timedelta
# Check for date range queries
date_pattern = r'(\d{4}[./-]\d{2}[./-]\d{2})\s*[-~]\s*(\d{4}[./-]\d{2}[./-]\d{2})'
date_match = re.search(date_pattern, text)
# Check for brand queries (Traditional Chinese and English)
brands_mapping = {
'neutrogena': 'Neutrogena',
'aveeno': 'Aveeno',
'nivea': 'Nivea',
'loreal': 'Loreal',
'shiseido': 'Shiseido',
'sk-ii': 'SK-II',
'kiehls': 'Kiehls',
'clinique': 'Clinique',
'dior': 'Dior',
'chanel': 'Chanel'
}
found_brands = []
text_lower = text.lower()
for brand_key, brand_name in brands_mapping.items():
if brand_key in text_lower:
found_brands.append(brand_name)
# Enhanced pattern matching with Traditional Chinese responses
if date_match and any(word in text.lower() for word in ['momo', 'limited', 'flash', 'sale']):
start_date = date_match.group(1).replace('/', '-').replace('.', '-')
end_date = date_match.group(2).replace('/', '-').replace('.', '-')
brand_text = f"品牌:{', '.join(found_brands)}" if found_brands else "全部品牌"
try:
import re
from datetime import datetime, timedelta
await update.message.reply_text(
f"⏳ 正在查詢 {start_date}{end_date} 的促銷業績...\n\n"
f"{brand_text}\n\n"
f"請使用下方選單查看詳細分析:",
reply_markup=self._get_main_menu_keyboard()
)
# Check for date range queries
date_pattern = r'(\d{4}[./-]\d{2}[./-]\d{2})\s*[-~]\s*(\d{4}[./-]\d{2}[./-]\d{2})'
date_match = re.search(date_pattern, text)
elif found_brands and any(word in text.lower() for word in ['momo', 'product', 'brand']):
brand_list = ', '.join(found_brands)
await update.message.reply_text(
f"🔍 正在搜尋 {brand_list} 商品...\n\n"
f"請使用下方選單查看詳細品牌分析:",
reply_markup=self._get_main_menu_keyboard()
)
# Check for brand queries (Traditional Chinese and English)
brands_mapping = {
'neutrogena': 'Neutrogena',
'aveeno': 'Aveeno',
'nivea': 'Nivea',
'loreal': 'Loreal',
'shiseido': 'Shiseido',
'sk-ii': 'SK-II',
'kiehls': 'Kiehls',
'clinique': 'Clinique',
'dior': 'Dior',
'chanel': 'Chanel'
}
elif any(word in text for word in ['trend', 'popular']):
await update.message.reply_text(
"請選擇功能:",
reply_markup=self._get_main_menu_keyboard()
)
found_brands = []
text_lower = text.lower()
for brand_key, brand_name in brands_mapping.items():
if brand_key in text_lower:
found_brands.append(brand_name)
elif any(word in text for word in ['search', 'query']):
context.user_data['waiting_for'] = 'search_query'
await update.message.reply_text(
"🔍 請輸入搜尋關鍵字:",
reply_markup=InlineKeyboardMarkup([[
InlineKeyboardButton("❌ 取消", callback_data="menu:main")
]])
)
# Enhanced pattern matching with Traditional Chinese responses
if date_match and any(word in text.lower() for word in ['momo', 'limited', 'flash', 'sale']):
start_date = date_match.group(1).replace('/', '-').replace('.', '-')
end_date = date_match.group(2).replace('/', '-').replace('.', '-')
elif any(word in text for word in ['copy', 'generate']):
context.user_data['waiting_for'] = 'copy_product'
await update.message.reply_text(
"✍️ 請輸入商品名稱:",
reply_markup=InlineKeyboardMarkup([[
InlineKeyboardButton("❌ 取消", callback_data="menu:main")
]])
)
brand_text = f"品牌:{', '.join(found_brands)}" if found_brands else "全部品牌"
else:
await update.message.reply_text(
"🤔 收到您的訊息!請選擇下方功能,或輸入 /help 查看指令說明:",
reply_markup=self._get_main_menu_keyboard()
)
await update.message.reply_text(
f"⏳ 正在查詢 {start_date}{end_date} 的促銷業績...\n\n"
f"{brand_text}\n\n"
f"請使用下方選單查看詳細分析:",
reply_markup=self._get_main_menu_keyboard()
)
elif found_brands and any(word in text.lower() for word in ['momo', 'product', 'brand']):
brand_list = ', '.join(found_brands)
await update.message.reply_text(
f"🔍 正在搜尋 {brand_list} 商品...\n\n"
f"請使用下方選單查看詳細品牌分析:",
reply_markup=self._get_main_menu_keyboard()
)
elif any(word in text for word in ['trend', 'popular']):
await update.message.reply_text(
"請選擇功能:",
reply_markup=self._get_main_menu_keyboard()
)
elif any(word in text for word in ['search', 'query']):
context.user_data['waiting_for'] = 'search_query'
await update.message.reply_text(
"🔍 請輸入搜尋關鍵字:",
reply_markup=InlineKeyboardMarkup([[
InlineKeyboardButton("❌ 取消", callback_data="menu:main")
]])
)
elif any(word in text for word in ['copy', 'generate']):
context.user_data['waiting_for'] = 'copy_product'
await update.message.reply_text(
"✍️ 請輸入商品名稱:",
reply_markup=InlineKeyboardMarkup([[
InlineKeyboardButton("❌ 取消", callback_data="menu:main")
]])
)
else:
await update.message.reply_text(
"🤔 收到您的訊息!請選擇下方功能,或輸入 /help 查看指令說明:",
reply_markup=self._get_main_menu_keyboard()
)
except Exception as e:
logger.error(f"[_enhanced_keyword_matching] 處理失敗: {e}", exc_info=True)
try:
await update.message.reply_text("❌ 處理時發生錯誤,請稍後再試或使用 /help")
except Exception:
pass
def _get_query_suggestions(self, query_type: str) -> list:
"""Get suggestions based on query type (Traditional Chinese)"""
@@ -1585,65 +1606,72 @@ class TrendTelegramBot:
async def _process_await_input(self, update, context, await_type: str, text: str):
"""處理所有 await: 狀態的用戶輸入,路由到對應 cmd"""
import re
chat_id = update.effective_chat.id
try:
import re
chat_id = update.effective_chat.id
date_cmd_map = {
'date_range_sales': ('sales', text.strip()),
'date_top': ('top', text.strip()),
'date_competitor': ('ppt', 'competitor ' + text.strip()),
'date_ppt_daily': ('ppt', 'daily ' + text.strip()),
'date_ppt_monthly': ('ppt', 'monthly ' + text.strip()),
'date_ppt_vendor': ('ppt', 'vendor ' + text.strip()),
'date_trend_month': ('trend', text.strip()),
'date_trend_year': ('trend', text.strip()),
'date_trend_quarter':('trend', text.strip()),
'promo_range': ('promo', text.strip()),
'search_compare': ('competitor', text.strip()),
}
goal_types = {
'goal_daily': 'daily',
'goal_monthly': 'monthly',
'goal_quarterly': 'quarterly',
'goal_half': 'half',
'goal_yearly': 'yearly',
}
date_cmd_map = {
'date_range_sales': ('sales', text.strip()),
'date_top': ('top', text.strip()),
'date_competitor': ('ppt', 'competitor ' + text.strip()),
'date_ppt_daily': ('ppt', 'daily ' + text.strip()),
'date_ppt_monthly': ('ppt', 'monthly ' + text.strip()),
'date_ppt_vendor': ('ppt', 'vendor ' + text.strip()),
'date_trend_month': ('trend', text.strip()),
'date_trend_year': ('trend', text.strip()),
'date_trend_quarter':('trend', text.strip()),
'promo_range': ('promo', text.strip()),
'search_compare': ('competitor', text.strip()),
}
goal_types = {
'goal_daily': 'daily',
'goal_monthly': 'monthly',
'goal_quarterly': 'quarterly',
'goal_half': 'half',
'goal_yearly': 'yearly',
}
if await_type in date_cmd_map:
cmd, arg = date_cmd_map[await_type]
await update.message.reply_text(f"⏳ 正在處理 `{text.strip()}`...", parse_mode='Markdown')
import threading as _t
_t.Thread(
target=self._forward_cmd_to_openclaw,
args=(cmd, arg, chat_id),
daemon=True,
).start()
elif await_type in goal_types:
period = goal_types[await_type]
amount_str = re.sub(r'[^\d]', '', text)
if not amount_str:
if await_type in date_cmd_map:
cmd, arg = date_cmd_map[await_type]
await update.message.reply_text(f"⏳ 正在處理 `{text.strip()}`...", parse_mode='Markdown')
import threading as _t
_t.Thread(
target=self._forward_cmd_to_openclaw,
args=(cmd, arg, chat_id),
daemon=True,
).start()
elif await_type in goal_types:
period = goal_types[await_type]
amount_str = re.sub(r'[^\d]', '', text)
if not amount_str:
await update.message.reply_text(
"⚠️ 格式錯誤請輸入純數字例如500000",
reply_markup=InlineKeyboardMarkup([[
InlineKeyboardButton("🔙 返回目標管理", callback_data="menu:goals")
]])
)
return
await update.message.reply_text(
"⚠️ 格式錯誤請輸入純數字例如500000",
reply_markup=InlineKeyboardMarkup([[
InlineKeyboardButton("🔙 返回目標管理", callback_data="menu:goals")
]])
f"⏳ 正在設定{period}目標 {int(amount_str):,} 元...",
parse_mode='Markdown'
)
return
await update.message.reply_text(
f"⏳ 正在設定{period}目標 {int(amount_str):,} 元...",
parse_mode='Markdown'
)
import threading as _t
_t.Thread(
target=self._forward_cmd_to_openclaw,
args=('goal', f'set:{period}:{amount_str}', chat_id),
daemon=True,
).start()
else:
await update.message.reply_text(
"⚠️ 無法識別輸入類型,請重新選擇功能",
reply_markup=self._get_main_menu_keyboard()
)
import threading as _t
_t.Thread(
target=self._forward_cmd_to_openclaw,
args=('goal', f'set:{period}:{amount_str}', chat_id),
daemon=True,
).start()
else:
await update.message.reply_text(
"⚠️ 無法識別輸入類型,請重新選擇功能",
reply_markup=self._get_main_menu_keyboard()
)
except Exception as e:
logger.error(f"[_process_await_input] 處理失敗: {e}", exc_info=True)
try:
await update.message.reply_text("❌ 處理時發生錯誤,請稍後再試或使用 /help")
except Exception:
pass
async def _process_search(self, update: Update, query: str):
"""處理搜尋請求"""