VietCat commited on
Commit
298cf0a
·
1 Parent(s): 24a6546

refactor message flow on purposes

Browse files
Files changed (2) hide show
  1. app/facebook.py +56 -16
  2. app/message_processor.py +54 -57
app/facebook.py CHANGED
@@ -63,27 +63,67 @@ class FacebookClient:
63
 
64
  return hmac.compare_digest(signature[7:], expected)
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  @timing_decorator_async
67
- async def send_message(self, page_access_token: Optional[str] = None, recipient_id: Optional[str] = None, message: str = "") -> Dict[str, Any]:
68
  page_access_token = page_access_token or self.page_token
69
  recipient_id = recipient_id or self.sender_id
70
  if not page_access_token or not recipient_id:
71
  raise ValueError("FacebookClient: page_access_token and recipient_id must not be None when sending a message.")
72
- # Replace all occurrences of '**' with '*' before sending
73
- response_to_send = message.replace('**', '*') if isinstance(message, str) else message
74
- logger.info(f"Đang gửi tin nhắn đến Facebook Messenger....\n\t{response_to_send}")
75
- url = f"https://graph.facebook.com/v18.0/me/messages?access_token={page_access_token}"
76
- payload = {
77
- "recipient": {"id": recipient_id},
78
- "message": {"text": response_to_send} # Sử dụng response_to_send đã được xử lý
79
- }
80
- try:
81
- response = await self._client.post(url, json=payload)
82
- response.raise_for_status()
83
- return response.json()
84
- except httpx.HTTPError as e:
85
- logger.error(f"Error sending message to Facebook: {e}")
86
- raise HTTPException(status_code=500, detail="Failed to send message to Facebook")
 
 
 
 
 
 
87
 
88
  @timing_decorator_sync
89
  def parse_message(self, body: Dict[str, Any]) -> Optional[Dict[str, Any]]:
 
63
 
64
  return hmac.compare_digest(signature[7:], expected)
65
 
66
+ def format_message(self, text: str) -> str:
67
+ # 1. Thay bullet markdown bằng ký hiệu khác
68
+ text = text.replace('\n* ', '\n- ')
69
+ text = text.replace('\n * ', '\n + ')
70
+ text = text.replace('\n* ', '\n- ')
71
+ text = text.replace('\n * ', '\n + ')
72
+ # 2. Chuyển **text** hoặc __text__ thành *text*
73
+ import re
74
+ text = re.sub(r'\*\*([^\*]+)\*\*', r'*\1*', text)
75
+ text = re.sub(r'__([^_]+)__', r'*\1*', text)
76
+ # 3. Loại bỏ các tiêu đề markdown kiểu #, ##, ###, ...
77
+ text = re.sub(r'^#+\s+', '', text, flags=re.MULTILINE)
78
+ # 4. Rút gọn nhiều dòng trống liên tiếp thành 1 dòng trống
79
+ text = re.sub(r'\n{3,}', '\n\n', text)
80
+ # 5. Loại bỏ các markdown không hỗ trợ khác nếu cần
81
+ return text
82
+
83
+ def split_message(self, text: str, max_length: int = 2000) -> list:
84
+ """
85
+ Chia message thành các đoạn <= max_length ký tự, ưu tiên chia theo dòng.
86
+ """
87
+ lines = text.split('\n')
88
+ messages = []
89
+ current = ""
90
+ for line in lines:
91
+ # +1 cho ký tự xuống dòng
92
+ if len(current) + len(line) + 1 > max_length:
93
+ messages.append(current.rstrip())
94
+ current = ""
95
+ current += (line + '\n')
96
+ if current.strip():
97
+ messages.append(current.rstrip())
98
+ return messages
99
+
100
  @timing_decorator_async
101
+ async def send_message(self, page_access_token: Optional[str] = None, recipient_id: Optional[str] = None, message: str = "") -> dict:
102
  page_access_token = page_access_token or self.page_token
103
  recipient_id = recipient_id or self.sender_id
104
  if not page_access_token or not recipient_id:
105
  raise ValueError("FacebookClient: page_access_token and recipient_id must not be None when sending a message.")
106
+ # Format message
107
+ response_to_send = self.format_message(message.replace('**', '*')) if isinstance(message, str) else message
108
+ # Chia nhỏ nếu quá dài
109
+ messages = self.split_message(response_to_send)
110
+ results = []
111
+ for msg in messages:
112
+ if len(msg) > 2000:
113
+ msg = msg[:2000] # fallback cắt cứng
114
+ url = f"https://graph.facebook.com/v18.0/me/messages?access_token={page_access_token}"
115
+ payload = {
116
+ "recipient": {"id": recipient_id},
117
+ "message": {"text": msg}
118
+ }
119
+ try:
120
+ response = await self._client.post(url, json=payload)
121
+ response.raise_for_status()
122
+ results.append(response.json())
123
+ except httpx.HTTPError as e:
124
+ logger.error(f"Error sending message to Facebook: {e}")
125
+ raise HTTPException(status_code=500, detail="Failed to send message to Facebook")
126
+ return results[0] if results else {}
127
 
128
  @timing_decorator_sync
129
  def parse_message(self, body: Dict[str, Any]) -> Optional[Dict[str, Any]]:
app/message_processor.py CHANGED
@@ -196,52 +196,15 @@ class MessageProcessor:
196
  response = None
197
  if not command:
198
  if muc_dich_to_use == "hỏi về mức phạt":
199
- # Giữ nguyên luồng cho hỏi về mức phạt
200
- vehicle = conv.get('originalvehicle', '')
201
- action = conv.get('originalaction', '')
202
- keywords = [kw.strip() for kw in vehicle.split(',') if kw.strip()]
203
- if keywords:
204
- if action:
205
- logger.info(f"[DEBUG] tạo embedding: {action}")
206
- embedding = await self.channel.embedder.create_embedding(action)
207
- logger.info(f"[DEBUG] embedding: {embedding[:5]} ... (total {len(embedding)})")
208
- matches = self.channel.supabase.match_documents(
209
- embedding,
210
- vehicle_keywords=keywords,
211
- user_question=action
212
- )
213
- logger.info(f"[DEBUG] matches: {matches}")
214
- if matches:
215
- response = await self.format_search_results(message_text, matches, page_token, sender_id)
216
- else:
217
- response = "Xin lỗi, tôi không tìm thấy thông tin phù hợp."
218
- else:
219
- logger.info(f"[DEBUG] Không có hành vi vi phạm: {message_text}")
220
- response = "Xin lỗi, tôi không tìm thấy thông tin về hành vi vi phạm trong câu hỏi của bạn."
221
- conv['isdone'] = True
222
- else:
223
- response = "Vui lòng cho biết loại phương tiện bạn cần tìm (xe máy, ô tô...)"
224
- conv['isdone'] = False
225
  elif muc_dich_to_use == "hỏi về quy tắc giao thông":
226
- # Dùng LLM để trả lời câu hỏi về quy tắc giao thông
227
- answer = await self.channel.llm.generate_text(message_text)
228
- response = answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng trả lời về quy tắc giao thông sẽ sớm có mặt."
229
- conv['isdone'] = True
230
  elif muc_dich_to_use == "hỏi về báo hiệu đường bộ":
231
- # Dùng LLM để trả lời câu hỏi về báo hiệu đường bộ
232
- answer = await self.channel.llm.generate_text(message_text)
233
- response = answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng trả lời về báo hiệu đường bộ sẽ sớm có mặt."
234
- conv['isdone'] = True
235
  elif muc_dich_to_use == "hỏi về quy trình xử lý vi phạm giao thông":
236
- # Dùng LLM để trả lời câu hỏi về quy trình xử lý vi phạm giao thông
237
- answer = await self.channel.llm.generate_text(message_text)
238
- response = answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng trả lời về quy trình xử lý vi phạm giao thông sẽ sớm có mặt."
239
- conv['isdone'] = True
240
  else:
241
- # Dùng LLM cho các mục đích khác
242
- answer = await self.channel.llm.generate_text(message_text)
243
- response = answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng này sẽ sớm có mặt."
244
- conv['isdone'] = True
245
  else:
246
  # Có command
247
  if command == "xong":
@@ -256,11 +219,9 @@ class MessageProcessor:
256
  conv['isdone'] = False
257
 
258
  # 6. Gửi response và cập nhật final state
259
- # Replace all occurrences of '**' with '*' before sending
260
- response_to_send = format_for_facebook(response.replace('**', '*')) if isinstance(response, str) else response
261
- await self.facebook.send_message(message=response_to_send)
262
  if hasattr(self.channel, 'sheets'):
263
- await loop.run_in_executor(None, lambda: self.channel.sheets.log_conversation(**conv))
264
  return
265
 
266
  def flatten_timestamp(self, ts):
@@ -454,14 +415,50 @@ class MessageProcessor:
454
  logger.info(f"[MOCK] Creating Facebook post for sender_id={sender_id} with history={history}")
455
  return "https://facebook.com/mock_post_url"
456
 
457
- def format_for_facebook(text: str) -> str:
458
- # 1. Thay bullet markdown bằng ký hiệu khác
459
- text = text.replace('\n* ', '\n- ')
460
- text = text.replace('\n * ', '\n + ')
461
- text = text.replace('\n* ', '\n- ')
462
- text = text.replace('\n * ', '\n + ')
463
- # 2. Chuyển **text** hoặc __text__ thành *text*
464
- text = re.sub(r'\*\*([^\*]+)\*\*', r'*\1*', text)
465
- text = re.sub(r'__([^_]+)__', r'*\1*', text)
466
- # 3. Loại bỏ các markdown không hỗ trợ khác nếu cần
467
- return text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  response = None
197
  if not command:
198
  if muc_dich_to_use == "hỏi về mức phạt":
199
+ response = await self.handle_muc_phat(conv, message_text, page_token, sender_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  elif muc_dich_to_use == "hỏi về quy tắc giao thông":
201
+ response = await self.handle_quy_tac(conv, message_text)
 
 
 
202
  elif muc_dich_to_use == "hỏi về báo hiệu đường bộ":
203
+ response = await self.handle_bao_hieu(conv, message_text)
 
 
 
204
  elif muc_dich_to_use == "hỏi về quy trình xử lý vi phạm giao thông":
205
+ response = await self.handle_quy_trinh(conv, message_text)
 
 
 
206
  else:
207
+ response = await self.handle_khac(conv, message_text)
 
 
 
208
  else:
209
  # Có command
210
  if command == "xong":
 
219
  conv['isdone'] = False
220
 
221
  # 6. Gửi response và cập nhật final state
222
+ await self.facebook.send_message(message=response)
 
 
223
  if hasattr(self.channel, 'sheets'):
224
+ await loop.run_in_executor(None, lambda: sheets_client.log_conversation(**conv))
225
  return
226
 
227
  def flatten_timestamp(self, ts):
 
415
  logger.info(f"[MOCK] Creating Facebook post for sender_id={sender_id} with history={history}")
416
  return "https://facebook.com/mock_post_url"
417
 
418
+ async def handle_muc_phat(self, conv, message_text, page_token, sender_id):
419
+ vehicle = conv.get('originalvehicle', '')
420
+ action = conv.get('originalaction', '')
421
+ keywords = [kw.strip() for kw in vehicle.split(',') if kw.strip()]
422
+ if keywords:
423
+ if action:
424
+ logger.info(f"[DEBUG] tạo embedding: {action}")
425
+ embedding = await self.channel.embedder.create_embedding(action)
426
+ logger.info(f"[DEBUG] embedding: {embedding[:5]} ... (total {len(embedding)})")
427
+ matches = self.channel.supabase.match_documents(
428
+ embedding,
429
+ vehicle_keywords=keywords,
430
+ user_question=action
431
+ )
432
+ logger.info(f"[DEBUG] matches: {matches}")
433
+ if matches:
434
+ response = await self.format_search_results(message_text, matches, page_token, sender_id)
435
+ else:
436
+ response = "Xin lỗi, tôi không tìm thấy thông tin phù hợp."
437
+ else:
438
+ logger.info(f"[DEBUG] Không có hành vi vi phạm: {message_text}")
439
+ response = "Xin lỗi, tôi không tìm thấy thông tin về hành vi vi phạm trong câu hỏi của bạn."
440
+ conv['isdone'] = True
441
+ else:
442
+ response = "Vui lòng cho biết loại phương tiện bạn cần tìm (xe máy, ô tô...)"
443
+ conv['isdone'] = False
444
+ return response
445
+
446
+ async def handle_quy_tac(self, conv, message_text):
447
+ answer = await self.channel.llm.generate_text(message_text)
448
+ conv['isdone'] = True
449
+ return answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng trả lời về quy tắc giao thông sẽ sớm có mặt."
450
+
451
+ async def handle_bao_hieu(self, conv, message_text):
452
+ answer = await self.channel.llm.generate_text(message_text)
453
+ conv['isdone'] = True
454
+ return answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng trả lời về báo hiệu đường bộ sẽ sớm có mặt."
455
+
456
+ async def handle_quy_trinh(self, conv, message_text):
457
+ answer = await self.channel.llm.generate_text(message_text)
458
+ conv['isdone'] = True
459
+ return answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng trả lời về quy trình xử lý vi phạm giao thông sẽ sớm có mặt."
460
+
461
+ async def handle_khac(self, conv, message_text):
462
+ answer = await self.channel.llm.generate_text(message_text)
463
+ conv['isdone'] = True
464
+ return answer.strip() if answer and answer.strip() else "[Đang phát triển] Tính năng này sẽ sớm có mặt."