diff --git a/services/telegram_bot_service.py b/services/telegram_bot_service.py index 2125637..28f0e17 100644 --- a/services/telegram_bot_service.py +++ b/services/telegram_bot_service.py @@ -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): """處理搜尋請求"""