VietCat commited on
Commit
3d65afc
·
1 Parent(s): 9dcf8cb

add history

Browse files
Files changed (2) hide show
  1. app/message_processor.py +98 -62
  2. app/sheets.py +79 -164
app/message_processor.py CHANGED
@@ -44,15 +44,21 @@ class MessageProcessor:
44
  logger.info(f"[DEBUG] Không có message_text và attachments, không xử lý...")
45
  return
46
 
47
- # Get conversation history (run in thread pool)
48
  loop = asyncio.get_event_loop()
49
  sheets_client = self.channel.get_sheets_client()
50
- history = []
51
  history = await loop.run_in_executor(
52
  None, lambda: sheets_client.get_conversation_history(sender_id, page_id)
53
  )
54
- logger.info(f"[DEBUG] history: {history}")#
55
 
 
 
 
 
 
 
 
56
  log_kwargs = {
57
  'conversation_id': None,
58
  'recipient_id': sender_id,
@@ -65,52 +71,16 @@ class MessageProcessor:
65
  'originalaction': '',
66
  'originalpurpose': '',
67
  'originalquestion': '',
68
- 'timestamp': [timestamp],
 
69
  'isdone': False
70
  }
71
-
72
- logger.info(f"[DEBUG] Message cơ bản: {log_kwargs}")
73
- conv = None
74
-
75
- if history:
76
- # 1. Chặn duplicate message (trùng sender_id, page_id, timestamp)
77
- for row in history:
78
- row_timestamps = self.flatten_timestamp(row.get('timestamp', []))
79
- if isinstance(row_timestamps, list) and len(row_timestamps) == 1 and isinstance(row_timestamps[0], list):
80
- row_timestamps = row_timestamps[0]
81
- if (
82
- str(timestamp) in [str(ts) for ts in row_timestamps]
83
- and str(row.get('recipient_id')) == str(sender_id)
84
- and str(row.get('page_id')) == str(page_id)
85
- ):
86
- logger.info("[DUPLICATE] Message duplicate, skipping log.")
87
- return
88
- conv = {
89
- 'conversation_id': row.get('conversation_id'),
90
- 'recipient_id': row.get('recipient_id'),
91
- 'page_id': row.get('page_id'),
92
- 'originaltext': row.get('originaltext'),
93
- 'originalcommand': row.get('originalcommand'),
94
- 'originalcontent': row.get('originalcontent'),
95
- 'originalattachments': row.get('originalattachments'),
96
- 'originalvehicle': row.get('originalvehicle'),
97
- 'originalaction': row.get('originalaction'),
98
- 'originalpurpose': row.get('originalpurpose'),
99
- 'originalquestion': row.get('originalquestion'),
100
- 'timestamp': row_timestamps,
101
- 'isdone': row.get('isdone')
102
- }
103
- else:
104
- # 2. Ghi conversation mới NGAY LẬP TỨC với thông tin cơ bản
105
- conv = await loop.run_in_executor(None, lambda: sheets_client.log_conversation(**log_kwargs))
106
  if not conv:
107
  logger.error("Không thể tạo conversation mới!")
108
  return
109
- else:
110
- logger.info(f"[DEBUG] Message history: {conv}")
111
- for key, value in log_kwargs.items():
112
- if value not in (None, "", []) and conv.get(key) in (None, "", []):
113
- conv[key] = value
114
  # Thêm timestamp mới nếu chưa có
115
  conv['timestamp'] = self.flatten_timestamp(conv['timestamp'])
116
  if timestamp not in conv['timestamp']:
@@ -182,6 +152,7 @@ class MessageProcessor:
182
  'originalaction': hanh_vi,
183
  'originalpurpose': muc_dich,
184
  'originalquestion': cau_hoi or "",
 
185
  'timestamp': self.flatten_timestamp(conv['timestamp']),
186
  'isdone': False
187
  }
@@ -199,20 +170,47 @@ class MessageProcessor:
199
  muc_dich_to_use = muc_dich
200
  logger.info(f"[DEBUG] Định hướng mục đích xử lý: {muc_dich_to_use}")
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  response = None
203
  if not command:
204
  if muc_dich_to_use == "hỏi về mức phạt":
205
- response = await self.handle_muc_phat(conv, page_token, sender_id)
206
  elif muc_dich_to_use == "hỏi về quy tắc giao thông":
207
- response = await self.handle_quy_tac(conv, message_text)
208
  elif muc_dich_to_use == "hỏi về báo hiệu đường bộ":
209
- response = await self.handle_bao_hieu(conv, message_text)
210
  elif muc_dich_to_use == "hỏi về quy trình xử lý vi phạm giao thông":
211
- response = await self.handle_quy_trinh(conv, message_text)
212
  elif muc_dich_to_use == "thông tin cá nhân của AI":
213
- response = await self.handle_ca_nhan(conv, message_text)
214
  else:
215
- response = await self.handle_khac(conv, message_text)
216
  else:
217
  # Có command
218
  if command == "xong":
@@ -226,10 +224,10 @@ class MessageProcessor:
226
  response = "Vui lòng cung cấp thêm thông tin và gõ lệnh \\xong khi hoàn tất."
227
  conv['isdone'] = False
228
 
229
- # 6. Gửi response và cập nhật final state
230
  await self.facebook.send_message(message=response)
231
- if hasattr(self.channel, 'sheets'):
232
- await loop.run_in_executor(None, lambda: sheets_client.log_conversation(**conv))
 
233
  return
234
 
235
  def flatten_timestamp(self, ts):
@@ -253,7 +251,7 @@ class MessageProcessor:
253
  return k
254
  return keyword
255
 
256
- async def format_search_results(self, question: str, matches: List[Dict[str, Any]], page_token: str, sender_id: str) -> str:
257
  if not matches:
258
  return "Không tìm thấy kết quả phù hợp."
259
  await self.facebook.send_message(message=get_random_message(FOUND_REGULATIONS_MESSAGES))
@@ -335,7 +333,10 @@ class MessageProcessor:
335
  else:
336
  result_text = "Không có kết quả phù hợp!"
337
  prompt = (
338
- "Bạn một trợ lý AI kiến thức pháp luật, hãy trả lời câu hỏi dựa trên các đoạn luật sau. "
 
 
 
339
  "Chỉ sử dụng thông tin có trong các đoạn, không tự đoán.\n"
340
  f"\nCác đoạn luật liên quan:\n{full_result_text}"
341
  "\n\nHãy trả lời ngắn gọn, dễ hiểu, trích dẫn rõ ràng thông tin từ các đoạn luật nếu cần."
@@ -384,7 +385,7 @@ class MessageProcessor:
384
  logger.info(f"[MOCK] Creating Facebook post for sender_id={sender_id} with history={history}")
385
  return "https://facebook.com/mock_post_url"
386
 
387
- async def handle_muc_phat(self, conv, page_token, sender_id):
388
  vehicle = conv.get('originalvehicle', '')
389
  action = conv.get('originalaction', '')
390
  question = conv.get('originalquestion', '')
@@ -406,7 +407,7 @@ class MessageProcessor:
406
  )
407
  logger.info(f"[DEBUG] matches: {matches}")
408
  if matches:
409
- response = await self.format_search_results(question, matches, page_token, sender_id)
410
  else:
411
  response = "Xin lỗi, tôi không tìm thấy thông tin phù hợp."
412
  else:
@@ -418,24 +419,51 @@ class MessageProcessor:
418
  # conv['isdone'] = False
419
  return response
420
 
421
- async def handle_quy_tac(self, conv, message_text):
 
 
 
 
 
 
 
 
422
  answer = await self.channel.llm.generate_text(message_text)
423
  conv['isdone'] = True
424
  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."
425
 
426
- async def handle_bao_hieu(self, conv, message_text):
 
 
 
 
 
 
 
 
427
  answer = await self.channel.llm.generate_text(message_text)
428
  conv['isdone'] = True
429
  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."
430
 
431
- async def handle_quy_trinh(self, conv, message_text):
 
 
 
 
 
 
 
 
432
  answer = await self.channel.llm.generate_text(message_text)
433
  conv['isdone'] = True
434
  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."
435
 
436
- async def handle_ca_nhan(self, conv, message_text):
437
  # Nếu câu hỏi là về thông tin cá nhân của bot, hướng dẫn LLM trả lời đúng
438
  prompt = (
 
 
 
439
  'Với các thông tin sau: "Bạn có tên là WeThoong AI, là trợ lý giao thông thông minh. Bạn được anh Viet Cat tạo ra và facebook cá nhân của anh ý là https://facebook.com/vietcat". '
440
  'Không được trả lời bạn là AI của Google, OpenAI, hay bất kỳ hãng nào khác. '
441
  'Hãy trả lời thông minh, hài hước, ngắn gọn cho câu hỏi sau:\n'
@@ -445,7 +473,15 @@ class MessageProcessor:
445
  conv['isdone'] = True
446
  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."
447
 
448
- async def handle_khac(self, conv, message_text):
 
 
 
 
 
 
 
 
449
  answer = await self.channel.llm.generate_text(message_text)
450
  conv['isdone'] = True
451
  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."
 
44
  logger.info(f"[DEBUG] Không có message_text và attachments, không xử lý...")
45
  return
46
 
47
+ # Lấy toàn bộ history (không lọc isdone)
48
  loop = asyncio.get_event_loop()
49
  sheets_client = self.channel.get_sheets_client()
 
50
  history = await loop.run_in_executor(
51
  None, lambda: sheets_client.get_conversation_history(sender_id, page_id)
52
  )
53
+ logger.info(f"[DEBUG] history: {history}")
54
 
55
+ # Chống trùng: nếu đã có bản ghi với sender_id, page_id, timestamp thì bỏ qua
56
+ for row in history:
57
+ if str(row.get('timestamp')) == str(timestamp) and str(row.get('recipient_id')) == str(sender_id) and str(row.get('page_id')) == str(page_id):
58
+ logger.info("[DUPLICATE] Message duplicate, skipping log.")
59
+ return
60
+
61
+ # Luôn lưu mỗi message thành 1 bản ghi mới
62
  log_kwargs = {
63
  'conversation_id': None,
64
  'recipient_id': sender_id,
 
71
  'originalaction': '',
72
  'originalpurpose': '',
73
  'originalquestion': '',
74
+ 'systemresponse': '',
75
+ 'timestamp': timestamp,
76
  'isdone': False
77
  }
78
+ conv = await loop.run_in_executor(None, lambda: sheets_client.log_conversation(**log_kwargs))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  if not conv:
80
  logger.error("Không thể tạo conversation mới!")
81
  return
82
+ logger.info(f"[DEBUG] Message history: {conv}")
83
+
 
 
 
84
  # Thêm timestamp mới nếu chưa có
85
  conv['timestamp'] = self.flatten_timestamp(conv['timestamp'])
86
  if timestamp not in conv['timestamp']:
 
152
  'originalaction': hanh_vi,
153
  'originalpurpose': muc_dich,
154
  'originalquestion': cau_hoi or "",
155
+ 'systemresponse': conv.get('systemresponse', ''),
156
  'timestamp': self.flatten_timestamp(conv['timestamp']),
157
  'isdone': False
158
  }
 
170
  muc_dich_to_use = muc_dich
171
  logger.info(f"[DEBUG] Định hướng mục đích xử lý: {muc_dich_to_use}")
172
 
173
+ # Tin nhắn không có command: lấy toàn bộ history để truyền vào LLM
174
+ # Chuẩn bị context hội thoại cho LLM
175
+ MAX_CONTEXT_CHARS = 20_000
176
+ conversation_context = []
177
+ total_chars = 0
178
+
179
+ # Bước 1: Sắp xếp history theo timestamp tăng dần (cũ -> mới)
180
+ sorted_history = sorted(history, key=lambda x: x.get('timestamp', 0))
181
+
182
+ # Bước 2: Duyệt từ mới -> cũ để loại bỏ message cũ nếu cần
183
+ for row in reversed(sorted_history):
184
+ temp_blocks = []
185
+ if row.get('systemresponse'):
186
+ temp_blocks.append({"role": "assistant", "content": row['systemresponse']})
187
+ if row.get('originaltext'):
188
+ temp_blocks.append({"role": "user", "content": row['originaltext']})
189
+
190
+ temp_total = sum(len(block['content']) for block in temp_blocks)
191
+
192
+ if total_chars + temp_total > MAX_CONTEXT_CHARS:
193
+ continue # bỏ qua những block quá cũ
194
+
195
+ # prepend để đảm bảo thứ tự cuối cùng là từ cũ đến mới
196
+ conversation_context = temp_blocks + conversation_context
197
+ total_chars += temp_total
198
+
199
+
200
  response = None
201
  if not command:
202
  if muc_dich_to_use == "hỏi về mức phạt":
203
+ response = await self.handle_muc_phat(conv, conversation_context, page_token, sender_id)
204
  elif muc_dich_to_use == "hỏi về quy tắc giao thông":
205
+ response = await self.handle_quy_tac(conv, conversation_context, message_text)
206
  elif muc_dich_to_use == "hỏi về báo hiệu đường bộ":
207
+ response = await self.handle_bao_hieu(conv, conversation_context, message_text)
208
  elif muc_dich_to_use == "hỏi về quy trình xử lý vi phạm giao thông":
209
+ response = await self.handle_quy_trinh(conv, conversation_context, message_text)
210
  elif muc_dich_to_use == "thông tin cá nhân của AI":
211
+ response = await self.handle_ca_nhan(conv, conversation_context, message_text)
212
  else:
213
+ response = await self.handle_khac(conv, conversation_context, message_text)
214
  else:
215
  # Có command
216
  if command == "xong":
 
224
  response = "Vui lòng cung cấp thêm thông tin và gõ lệnh \\xong khi hoàn tất."
225
  conv['isdone'] = False
226
 
 
227
  await self.facebook.send_message(message=response)
228
+ # Lưu lại systemresponse cho bản ghi vừa tạo
229
+ conv['systemresponse'] = response
230
+ await loop.run_in_executor(None, lambda: sheets_client.log_conversation(**conv))
231
  return
232
 
233
  def flatten_timestamp(self, ts):
 
251
  return k
252
  return keyword
253
 
254
+ async def format_search_results(self, conversation_context: str, question: str, matches: List[Dict[str, Any]], page_token: str, sender_id: str) -> str:
255
  if not matches:
256
  return "Không tìm thấy kết quả phù hợp."
257
  await self.facebook.send_message(message=get_random_message(FOUND_REGULATIONS_MESSAGES))
 
333
  else:
334
  result_text = "Không có kết quả phù hợp!"
335
  prompt = (
336
+ "Biết rằng bạn đãlịch sử trao đổi như sau:"
337
+ f"Lịch sử:\n{conversation_context}"
338
+
339
+ "Bạn là một trợ lý AI có kiến thức pháp luật, hãy trả lời câu hỏi dựa trên lịch sử trao đổi và các đoạn luật sau. "
340
  "Chỉ sử dụng thông tin có trong các đoạn, không tự đoán.\n"
341
  f"\nCác đoạn luật liên quan:\n{full_result_text}"
342
  "\n\nHãy trả lời ngắn gọn, dễ hiểu, trích dẫn rõ ràng thông tin từ các đoạn luật nếu cần."
 
385
  logger.info(f"[MOCK] Creating Facebook post for sender_id={sender_id} with history={history}")
386
  return "https://facebook.com/mock_post_url"
387
 
388
+ async def handle_muc_phat(self, conversation_context, conv, page_token, sender_id):
389
  vehicle = conv.get('originalvehicle', '')
390
  action = conv.get('originalaction', '')
391
  question = conv.get('originalquestion', '')
 
407
  )
408
  logger.info(f"[DEBUG] matches: {matches}")
409
  if matches:
410
+ response = await self.format_search_results(conversation_context, question, matches, page_token, sender_id)
411
  else:
412
  response = "Xin lỗi, tôi không tìm thấy thông tin phù hợp."
413
  else:
 
419
  # conv['isdone'] = False
420
  return response
421
 
422
+ async def handle_quy_tac(self, conversation_context, conv, message_text):
423
+ prompt = (
424
+ "Biết rằng bạn đã có lịch sử trao đổi như sau:"
425
+ f"Lịch sử:\n{conversation_context}"
426
+
427
+ "Bạn là một trợ lý AI có kiến thức pháp luật, hãy trả lời câu hỏi dựa trên lịch sử trao đổi"
428
+ "\n\nHãy trả lời ngắn gọn, dễ hiểu, trích dẫn rõ ràng thông tin từ các đoạn luật nếu cần."
429
+ f"\n\nCâu hỏi của người dùng: {message_text}\n"
430
+ )
431
  answer = await self.channel.llm.generate_text(message_text)
432
  conv['isdone'] = True
433
  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."
434
 
435
+ async def handle_bao_hieu(self, conversation_context, conv, message_text):
436
+ prompt = (
437
+ "Biết rằng bạn đã có lịch sử trao đổi như sau:"
438
+ f"Lịch sử:\n{conversation_context}"
439
+
440
+ "Bạn là một trợ lý AI có kiến thức pháp luật, hãy trả lời câu hỏi dựa trên lịch sử trao đổi"
441
+ "\n\nHãy trả lời ngắn gọn, dễ hiểu, trích dẫn rõ ràng thông tin từ các đoạn luật nếu cần."
442
+ f"\n\nCâu hỏi của người dùng: {message_text}\n"
443
+ )
444
  answer = await self.channel.llm.generate_text(message_text)
445
  conv['isdone'] = True
446
  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."
447
 
448
+ async def handle_quy_trinh(self, conversation_context, conv, message_text):
449
+ prompt = (
450
+ "Biết rằng bạn đã có lịch sử trao đổi như sau:"
451
+ f"Lịch sử:\n{conversation_context}"
452
+
453
+ "Bạn là một trợ lý AI có kiến thức pháp luật, hãy trả lời câu hỏi dựa trên lịch sử trao đổi"
454
+ "\n\nHãy trả lời ngắn gọn, dễ hiểu, trích dẫn rõ ràng thông tin từ các đoạn luật nếu cần."
455
+ f"\n\nCâu hỏi của người dùng: {message_text}\n"
456
+ )
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_ca_nhan(self, conversation_context, conv, message_text):
462
  # Nếu câu hỏi là về thông tin cá nhân của bot, hướng dẫn LLM trả lời đúng
463
  prompt = (
464
+ "Biết rằng bạn đã có lịch sử trao đổi như sau:"
465
+ f"Lịch sử:\n{conversation_context}"
466
+
467
  'Với các thông tin sau: "Bạn có tên là WeThoong AI, là trợ lý giao thông thông minh. Bạn được anh Viet Cat tạo ra và facebook cá nhân của anh ý là https://facebook.com/vietcat". '
468
  'Không được trả lời bạn là AI của Google, OpenAI, hay bất kỳ hãng nào khác. '
469
  'Hãy trả lời thông minh, hài hước, ngắn gọn cho câu hỏi sau:\n'
 
473
  conv['isdone'] = True
474
  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."
475
 
476
+ async def handle_khac(self, conversation_context, conv, message_text):
477
+ prompt = (
478
+ "Biết rằng bạn đã có lịch sử trao đổi như sau:"
479
+ f"Lịch sử:\n{conversation_context}"
480
+
481
+ "Bạn là một trợ lý AI có kiến thức pháp luật, hãy trả lời câu hỏi dựa trên lịch sử trao đổi"
482
+ "\n\nHãy trả lời ngắn gọn, dễ hiểu, trích dẫn rõ ràng thông tin từ các đoạn luật nếu cần."
483
+ f"\n\nCâu hỏi của người dùng: {message_text}\n"
484
+ )
485
  answer = await self.channel.llm.generate_text(message_text)
486
  conv['isdone'] = True
487
  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."
app/sheets.py CHANGED
@@ -87,14 +87,14 @@ class SheetsClient:
87
  values = result.get('values', [])
88
  history = []
89
  for row in values:
90
- row = row + [""] * (13 - len(row))
91
  try:
92
- timestamps = json.loads(row[11]) if row[11] else []
93
  except Exception:
94
  timestamps = []
95
  if not isinstance(timestamps, list):
96
  timestamps = [timestamps]
97
- if row[4] == user_id and row[5] == page_id and row[12].lower() == 'false':
98
  history.append({
99
  'conversation_id': row[0],
100
  'originalcommand': row[1],
@@ -107,8 +107,9 @@ class SheetsClient:
107
  'originalaction': row[8],
108
  'originalpurpose': row[9],
109
  'originalquestion': row[10],
110
- 'timestamp': timestamps,
111
- 'isdone': row[12].lower() == 'true'
 
112
  })
113
  return history
114
  except Exception as e:
@@ -128,8 +129,9 @@ class SheetsClient:
128
  originalvehicle: str = "",
129
  originalaction: str = "",
130
  originalpurpose: str = "",
131
- originalquestion: str = "", # <-- thêm dòng này
132
- timestamp: Any = None,
 
133
  isdone: bool = False
134
  ) -> Optional[Dict[str, Any]]:
135
  """
@@ -149,164 +151,77 @@ class SheetsClient:
149
  ).execute()
150
  values = result.get('values', [])
151
  # logger.info(f"[DEBUG] Gsheet values {values}")
152
- ts = datetime.now().isoformat()
153
- # Đảm bảo timestamp luôn là list
154
  if timestamp is None:
155
- timestamp = []
156
- elif not isinstance(timestamp, list):
157
- timestamp = [timestamp]
158
- if not conversation_id:
159
- # Check for duplicates before creating new conversation
160
- for row in values:
161
- if len(row) >= 11:
162
- try:
163
- row_timestamps = json.loads(row[10]) if row[10] else []
164
- except Exception:
165
- row_timestamps = []
166
- if not isinstance(row_timestamps, list):
167
- row_timestamps = [row_timestamps]
168
- row_recipient_id = row[4]
169
- row_page_id = row[5]
170
- if (str(timestamp) in [str(ts) for ts in row_timestamps] and str(row_recipient_id) == str(recipient_id) and str(row_page_id) == str(page_id)):
171
- # Found duplicate, return existing conversation
172
- logger.info(f"Found duplicate conversation for user {recipient_id}, page {page_id}, timestamp {timestamp}")
173
- return {
174
- 'conversation_id': row[0],
175
- 'originalcommand': row[1],
176
- 'originalcontent': row[2],
177
- 'originalattachments': json.loads(row[3]) if row[3] else [],
178
- 'recipient_id': row[4],
179
- 'page_id': row[5],
180
- 'originaltext': row[6],
181
- 'originalvehicle': row[7],
182
- 'originalaction': row[8],
183
- 'originalpurpose': row[9],
184
- 'originalquestion': row[10],
185
- 'timestamp': row_timestamps,
186
- 'isdone': row[11].lower() == 'true' if len(row) > 11 else False
187
- }
188
-
189
- # No duplicate found, create new conversation
190
- conversation_id = generate_conversation_id(recipient_id, page_id, ts)
191
- new_row = [
192
- conversation_id,
193
- originalcommand,
194
- originalcontent,
195
- json.dumps(originalattachments or []),
196
- recipient_id,
197
- page_id,
198
- originaltext,
199
- originalvehicle,
200
- originalaction,
201
- originalpurpose,
202
- originalquestion, # <-- thêm dòng này
203
- json.dumps(timestamp),
204
- str(isdone).lower()
205
- ]
206
- body = {
207
- 'values': [new_row]
208
- }
209
- range_name = SHEET_RANGE
210
- self.service.spreadsheets().values().append(
211
- spreadsheetId=self.sheet_id,
212
- range=range_name,
213
- valueInputOption='RAW',
214
- body=body
215
- ).execute()
216
- logger.info(f"Thêm mới conversation: {conversation_id} | Giá trị: {dict(zip(['conversation_id','originalcommand','originalcontent','originalattachments','recipient_id','page_id','originaltext','originalvehicle','originalaction','originalpurpose','originalquestion','timestamp','isdone'], new_row))}")
217
-
218
- # Return the conversation data directly
219
- return {
220
- 'conversation_id': conversation_id,
221
- 'originalcommand': originalcommand,
222
- 'originalcontent': originalcontent,
223
- 'originalattachments': originalattachments or [],
224
- 'recipient_id': recipient_id,
225
- 'page_id': page_id,
226
- 'originaltext': originaltext,
227
- 'originalvehicle': originalvehicle,
228
- 'originalaction': originalaction,
229
- 'originalpurpose': originalpurpose,
230
- 'originalquestion': originalquestion,
231
- 'timestamp': timestamp,
232
- 'isdone': isdone
233
- }
234
- else:
235
- # Update existing conversation
236
- if not values:
237
- logger.error("No data in sheet, cannot update conversation.")
238
- return None
239
- row_index = None
240
- for i, row in enumerate(values):
241
- if row[0] == conversation_id:
242
- row_index = i
243
- break
244
- logger.info(f"[DEBUG] Gsheet row index {row_index}")
245
- if row_index is not None:
246
- sheet_row_number = row_index + 2 # +2 vì values[0] là dòng 2 trên sheet
247
- current_row = values[row_index]
248
- logger.info(f"[DEBUG] Gsheet current row {current_row}")
249
- while len(current_row) < 13:
250
- current_row.append("")
251
- try:
252
- current_timestamps = json.loads(current_row[10]) if current_row[10] else []
253
- except Exception:
254
- current_timestamps = []
255
- if not isinstance(current_timestamps, list):
256
- current_timestamps = [current_timestamps]
257
- # Chỉ append nếu chưa có
258
- for ts in timestamp:
259
- if ts not in current_timestamps:
260
- current_timestamps.append(ts)
261
- new_row = [
262
- conversation_id,
263
- originalcommand if originalcommand else current_row[1],
264
- originalcontent if originalcontent else current_row[2],
265
- json.dumps(originalattachments) if originalattachments is not None else current_row[3],
266
- recipient_id if recipient_id else current_row[4],
267
- page_id if page_id else current_row[5],
268
- originaltext if originaltext else current_row[6],
269
- originalvehicle if originalvehicle else current_row[7],
270
- originalaction if originalaction else current_row[8],
271
- originalpurpose if originalpurpose else current_row[9],
272
- originalquestion if originalquestion else current_row[10], # <-- thêm dòng này
273
- json.dumps(current_timestamps),
274
- str(isdone).lower() if isdone is not None else current_row[12]
275
- ]
276
- update_range = f"{SHEET_RANGE.split('!')[0]}!A{sheet_row_number}"
277
- logger.info(f"[DEBUG] Gsheet update range {update_range}")
278
- body = {
279
- 'values': [new_row]
280
- }
281
- self.service.spreadsheets().values().update(
282
- spreadsheetId=self.sheet_id,
283
- range=update_range,
284
- valueInputOption='RAW',
285
- body=body
286
- ).execute()
287
- changed_cols = ['conversation_id','originalcommand','originalcontent','originalattachments','recipient_id','page_id','originaltext','originalvehicle','originalaction','originalpurpose','originalquestion','timestamp','isdone']
288
- for idx, (old, new) in enumerate(zip(current_row, new_row)):
289
- if old != new:
290
- changed_cols.append(changed_cols[idx])
291
- logger.info(f"Cập nhật conversation: {conversation_id} tại dòng {sheet_row_number} | Cột cập nhật: {changed_cols} | Giá trị mới: {dict(zip(changed_cols, new_row))}")
292
-
293
- # Return the updated conversation data
294
- return {
295
- 'conversation_id': conversation_id,
296
- 'originalcommand': new_row[1],
297
- 'originalcontent': new_row[2],
298
- 'originalattachments': json.loads(new_row[3]) if new_row[3] else [],
299
- 'recipient_id': new_row[4],
300
- 'page_id': new_row[5],
301
- 'originaltext': new_row[6],
302
- 'originalvehicle': new_row[7],
303
- 'originalaction': new_row[8],
304
- 'originalpurpose': new_row[9],
305
- 'originalquestion': new_row[10], # <-- thêm dòng này
306
- 'timestamp': current_timestamps,
307
- 'isdone': new_row[12].lower() == 'true'
308
- }
309
- return None
310
  except Exception as e:
311
  logger.error(f"Error logging conversation: {e}")
312
  return None
 
87
  values = result.get('values', [])
88
  history = []
89
  for row in values:
90
+ row = row + [""] * (14 - len(row))
91
  try:
92
+ timestamps = json.loads(row[12]) if row[12] else []
93
  except Exception:
94
  timestamps = []
95
  if not isinstance(timestamps, list):
96
  timestamps = [timestamps]
97
+ if row[4] == user_id and row[5] == page_id:
98
  history.append({
99
  'conversation_id': row[0],
100
  'originalcommand': row[1],
 
107
  'originalaction': row[8],
108
  'originalpurpose': row[9],
109
  'originalquestion': row[10],
110
+ 'systemresponse': row[11],
111
+ 'timestamp': row[12],
112
+ 'isdone': row[13].lower() == 'true'
113
  })
114
  return history
115
  except Exception as e:
 
129
  originalvehicle: str = "",
130
  originalaction: str = "",
131
  originalpurpose: str = "",
132
+ originalquestion: str = "",
133
+ systemresponse: str = "",
134
+ timestamp: str = None,
135
  isdone: bool = False
136
  ) -> Optional[Dict[str, Any]]:
137
  """
 
151
  ).execute()
152
  values = result.get('values', [])
153
  # logger.info(f"[DEBUG] Gsheet values {values}")
154
+ # Đảm bảo timestamp là str
 
155
  if timestamp is None:
156
+ from datetime import datetime
157
+ timestamp = datetime.now().isoformat()
158
+ # Chống trùng bản ghi dựa trên sender_id, page_id, timestamp
159
+ for row in values:
160
+ if len(row) >= 12:
161
+ row_timestamp = row[12] if len(row) > 12 else ""
162
+ if str(row_timestamp) == str(timestamp) and str(row[4]) == str(recipient_id) and str(row[5]) == str(page_id):
163
+ logger.info(f"Found duplicate conversation for user {recipient_id}, page {page_id}, timestamp {timestamp}")
164
+ return {
165
+ 'conversation_id': row[0],
166
+ 'originalcommand': row[1],
167
+ 'originalcontent': row[2],
168
+ 'originalattachments': json.loads(row[3]) if row[3] else [],
169
+ 'recipient_id': row[4],
170
+ 'page_id': row[5],
171
+ 'originaltext': row[6],
172
+ 'originalvehicle': row[7],
173
+ 'originalaction': row[8],
174
+ 'originalpurpose': row[9],
175
+ 'originalquestion': row[10],
176
+ 'systemresponse': row[11],
177
+ 'timestamp': row[12],
178
+ 'isdone': row[13].lower() == 'true' if len(row) > 13 else False
179
+ }
180
+ # Nếu không trùng thì thêm mới bản ghi
181
+ conversation_id = generate_conversation_id(recipient_id, page_id, timestamp)
182
+ new_row = [
183
+ conversation_id,
184
+ originalcommand,
185
+ originalcontent,
186
+ json.dumps(originalattachments or []),
187
+ recipient_id,
188
+ page_id,
189
+ originaltext,
190
+ originalvehicle,
191
+ originalaction,
192
+ originalpurpose,
193
+ originalquestion,
194
+ systemresponse,
195
+ timestamp,
196
+ str(isdone).lower()
197
+ ]
198
+ body = {
199
+ 'values': [new_row]
200
+ }
201
+ range_name = SHEET_RANGE
202
+ self.service.spreadsheets().values().append(
203
+ spreadsheetId=self.sheet_id,
204
+ range=range_name,
205
+ valueInputOption='RAW',
206
+ body=body
207
+ ).execute()
208
+ logger.info(f"Thêm mới conversation: {conversation_id} | Giá trị: {{dict(zip(['conversation_id','originalcommand','originalcontent','originalattachments','recipient_id','page_id','originaltext','originalvehicle','originalaction','originalpurpose','originalquestion','systemresponse','timestamp','isdone'], new_row))}}")
209
+ return {
210
+ 'conversation_id': conversation_id,
211
+ 'originalcommand': originalcommand,
212
+ 'originalcontent': originalcontent,
213
+ 'originalattachments': originalattachments or [],
214
+ 'recipient_id': recipient_id,
215
+ 'page_id': page_id,
216
+ 'originaltext': originaltext,
217
+ 'originalvehicle': originalvehicle,
218
+ 'originalaction': originalaction,
219
+ 'originalpurpose': originalpurpose,
220
+ 'originalquestion': originalquestion,
221
+ 'systemresponse': systemresponse,
222
+ 'timestamp': timestamp,
223
+ 'isdone': isdone
224
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  except Exception as e:
226
  logger.error(f"Error logging conversation: {e}")
227
  return None