File size: 14,436 Bytes
cd9bca9 81a7334 cd9bca9 81a7334 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
"""
LINE Bot 服務 - 整合業務查詢功能
處理來自 LINE 官方帳號的用戶訊息
"""
import logging
from typing import Dict, Any, Optional
from backend.services.business_query_service import BusinessQueryService
from backend.services.database_service import DatabaseService
logger = logging.getLogger(__name__)
class LineBotService:
"""LINE Bot 服務類"""
def __init__(self):
self.business_query_service = BusinessQueryService()
self.db_service = DatabaseService()
def handle_text_message(self, user_id: str, message_text: str, display_name: str = None) -> Dict[str, Any]:
"""
處理 LINE 用戶的文字訊息
Args:
user_id: LINE 用戶ID
message_text: 用戶發送的訊息內容
display_name: 用戶顯示名稱(可選)
Returns:
包含回應訊息和相關資料的字典
"""
try:
# 記錄用戶訊息
logger.info(f"收到來自用戶 {user_id} 的訊息: {message_text}")
# 🔧 後門測試:直接與 DeepSeek V3 聊天
if message_text.startswith('##test##'):
return self._handle_deepseek_test(user_id, message_text[8:].strip())
# 檢查是否為特殊指令
if message_text.lower() in ['help', '幫助', '說明', '指令']:
return self._handle_help_command(user_id)
elif message_text.lower() in ['menu', '選單', '功能']:
return self._handle_menu_command(user_id)
# 確保用戶資料存在
self._ensure_user_profile(user_id, display_name)
# 處理業務查詢
query_result = self.business_query_service.process_user_query(message_text, user_id)
# 準備回應
response = {
"type": "text",
"text": query_result["response_message"],
"user_id": user_id,
"success": query_result["success"],
"intent": query_result["intent"],
"confidence": query_result["confidence"]
}
# 如果查詢成功且有資料,可以添加快速回覆按鈕
if query_result["success"] and query_result["data"]:
response["quick_reply"] = self._generate_quick_reply_buttons(query_result["intent"])
return response
except Exception as e:
logger.error(f"處理 LINE 訊息時發生錯誤: {str(e)}")
return {
"type": "text",
"text": "抱歉,系統暫時無法處理您的請求,請稍後再試。",
"user_id": user_id,
"success": False,
"error": str(e)
}
def _handle_help_command(self, user_id: str) -> Dict[str, Any]:
"""處理幫助指令"""
help_message = self.business_query_service.get_help_message()
return {
"type": "text",
"text": help_message,
"user_id": user_id,
"success": True,
"intent": "help"
}
def _handle_menu_command(self, user_id: str) -> Dict[str, Any]:
"""處理選單指令"""
menu_message = """
🏪 智能查詢系統主選單
請選擇您需要的功能:
1️⃣ 商品查詢
輸入:查詢商品 [商品名稱]
2️⃣ 庫存查詢
輸入:庫存查詢 [商品名稱]
3️⃣ 訂單查詢
輸入:我的訂單
4️⃣ 低庫存警告
輸入:低庫存商品
5️⃣ 業務統計
輸入:業務摘要
💡 您也可以直接用自然語言提問!
例如:「iPhone 還有多少庫存?」
輸入「幫助」查看詳細說明
"""
return {
"type": "text",
"text": menu_message.strip(),
"user_id": user_id,
"success": True,
"intent": "menu",
"quick_reply": {
"items": [
{"type": "action", "action": {"type": "message", "label": "商品查詢", "text": "查詢商品"}},
{"type": "action", "action": {"type": "message", "label": "庫存查詢", "text": "庫存查詢"}},
{"type": "action", "action": {"type": "message", "label": "我的訂單", "text": "我的訂單"}},
{"type": "action", "action": {"type": "message", "label": "低庫存", "text": "低庫存商品"}},
{"type": "action", "action": {"type": "message", "label": "業務統計", "text": "業務摘要"}}
]
}
}
def _ensure_user_profile(self, user_id: str, display_name: str = None):
"""確保用戶資料存在於資料庫中"""
try:
# 檢查用戶是否已存在
existing_user = self.db_service.get_user_profile(user_id)
if not existing_user:
# 建立新用戶資料
user_data = {
"user_id": user_id,
"display_name": display_name or f"用戶_{user_id[:8]}",
"created_at": "now()"
}
success = self.db_service.create_user_profile(user_data)
if success:
logger.info(f"已建立新用戶資料: {user_id}")
else:
logger.warning(f"建立用戶資料失敗: {user_id}")
except Exception as e:
logger.error(f"處理用戶資料時發生錯誤: {str(e)}")
def _generate_quick_reply_buttons(self, intent: str) -> Dict[str, Any]:
"""根據查詢意圖生成快速回覆按鈕"""
if intent == "product_search":
return {
"items": [
{"type": "action", "action": {"type": "message", "label": "查看庫存", "text": "庫存查詢"}},
{"type": "action", "action": {"type": "message", "label": "相關商品", "text": "查詢商品"}},
{"type": "action", "action": {"type": "message", "label": "主選單", "text": "選單"}}
]
}
elif intent == "inventory_check":
return {
"items": [
{"type": "action", "action": {"type": "message", "label": "低庫存警告", "text": "低庫存商品"}},
{"type": "action", "action": {"type": "message", "label": "商品詳情", "text": "查詢商品"}},
{"type": "action", "action": {"type": "message", "label": "主選單", "text": "選單"}}
]
}
elif intent == "order_search":
return {
"items": [
{"type": "action", "action": {"type": "message", "label": "業務統計", "text": "業務摘要"}},
{"type": "action", "action": {"type": "message", "label": "主選單", "text": "選單"}}
]
}
else:
return {
"items": [
{"type": "action", "action": {"type": "message", "label": "商品查詢", "text": "查詢商品"}},
{"type": "action", "action": {"type": "message", "label": "庫存查詢", "text": "庫存查詢"}},
{"type": "action", "action": {"type": "message", "label": "主選單", "text": "選單"}}
]
}
def handle_postback_action(self, user_id: str, postback_data: str) -> Dict[str, Any]:
"""處理 Postback 動作(按鈕點擊等)"""
try:
# 解析 postback 資料
if postback_data.startswith("query_"):
query_type = postback_data.replace("query_", "")
if query_type == "products":
return self.handle_text_message(user_id, "查詢商品")
elif query_type == "inventory":
return self.handle_text_message(user_id, "庫存查詢")
elif query_type == "orders":
return self.handle_text_message(user_id, "我的訂單")
elif query_type == "summary":
return self.handle_text_message(user_id, "業務摘要")
# 預設回應
return self._handle_menu_command(user_id)
except Exception as e:
logger.error(f"處理 Postback 動作時發生錯誤: {str(e)}")
return {
"type": "text",
"text": "抱歉,無法處理此操作。",
"user_id": user_id,
"success": False,
"error": str(e)
}
def get_user_activity_summary(self, user_id: str, days: int = 7) -> Dict[str, Any]:
"""取得用戶活動摘要"""
try:
# 這裡可以實作用戶活動統計
# 例如:查詢次數、常用功能等
summary = {
"user_id": user_id,
"period_days": days,
"total_queries": 0, # 從資料庫查詢
"most_used_features": [], # 最常使用的功能
"last_activity": None # 最後活動時間
}
return {
"success": True,
"data": summary
}
except Exception as e:
logger.error(f"取得用戶活動摘要時發生錯誤: {str(e)}")
return {
"success": False,
"error": str(e)
}
def _handle_deepseek_test(self, user_id: str, test_message: str) -> Dict[str, Any]:
"""
🔧 後門測試:直接與 DeepSeek V3 聊天
使用方式:在 LINE 中發送 "##test##你好"
"""
try:
import asyncio
import httpx
from backend.config import settings
if not test_message:
return {
"type": "text",
"text": "🔧 DeepSeek V3 測試模式\n\n使用方式:##test##[您的訊息]\n\n範例:##test##你好,請介紹一下自己",
"user_id": user_id,
"success": True,
"intent": "deepseek_test"
}
# 同步調用異步函數的方法
def sync_call_deepseek():
return asyncio.run(self._call_deepseek_api(test_message))
try:
response_text = sync_call_deepseek()
return {
"type": "text",
"text": f"🤖 DeepSeek V3 回應:\n\n{response_text}",
"user_id": user_id,
"success": True,
"intent": "deepseek_test"
}
except Exception as api_error:
return {
"type": "text",
"text": f"❌ DeepSeek V3 API 錯誤:\n{str(api_error)}\n\n請檢查:\n1. API Key 設定\n2. 網路連線\n3. OpenRouter 服務狀態",
"user_id": user_id,
"success": False,
"intent": "deepseek_test",
"error": str(api_error)
}
except Exception as e:
logger.error(f"DeepSeek 測試錯誤: {str(e)}")
return {
"type": "text",
"text": f"❌ 測試功能錯誤:{str(e)}",
"user_id": user_id,
"success": False,
"intent": "deepseek_test",
"error": str(e)
}
async def _call_deepseek_api(self, message: str) -> str:
"""
直接調用 DeepSeek V3 API
"""
from backend.config import settings
api_key = settings.OPENROUTER_API_KEY
model = settings.OPENROUTER_MODEL
if not api_key:
return "❌ OPENROUTER_API_KEY 未設定"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"HTTP-Referer": "https://huggingface.co/spaces",
"X-Title": "LINE Bot DeepSeek Test"
}
try:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"https://openrouter.ai/api/v1/chat/completions",
headers=headers,
json={
"model": model,
"messages": [
{
"role": "system",
"content": "你是 DeepSeek V3,一個強大的中文 AI 助手。請用繁體中文回應,並在回應開頭說明你是 DeepSeek V3。"
},
{
"role": "user",
"content": message
}
],
"temperature": 0.7,
"max_tokens": 500
}
)
if response.status_code == 200:
result = response.json()
content = result["choices"][0]["message"]["content"]
return content
else:
return f"❌ API 錯誤 {response.status_code}: {response.text}"
except httpx.TimeoutException:
return "❌ API 請求超時,請稍後再試"
except Exception as e:
return f"❌ 網路錯誤: {str(e)}" |