Spaces:
Running
Running
Restore to commit 11a233f + add text message saving for ZGR group with detailed HELP_INSTRUCTIONS
Browse files
app.py
CHANGED
|
@@ -282,6 +282,44 @@ def _save_product_to_main_dataset(image_url, image_data_b64, description, price,
|
|
| 282 |
return None
|
| 283 |
|
| 284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
def _ocr_extract_text(image_bytes):
|
| 286 |
"""Use HF Inference API with Vietnamese OCR model (Vintern-1B) to extract text from image."""
|
| 287 |
try:
|
|
@@ -405,7 +443,7 @@ def _log(event, sender_id, chat_id, text, sender_name="", chat_type=""):
|
|
| 405 |
|
| 406 |
_log("startup", "system", "SYSTEM", "Proxy space initialized. PROXY_NAME=" + PROXY_NAME)
|
| 407 |
|
| 408 |
-
def _save_to_main_dataset(image_url, image_data_b64, description, price, category, sender_id, sender_name, product_name="", chat_id=""):
|
| 409 |
_log("main_dataset_save_start", sender_id, chat_id, "product_name=" + str(product_name) + " price=" + str(price))
|
| 410 |
if not HF_TOKEN or not MAIN_DATASET_ID:
|
| 411 |
_log("main_dataset_skip", sender_id, chat_id, "HF_TOKEN or MAIN_DATASET_ID missing")
|
|
@@ -416,8 +454,12 @@ def _save_to_main_dataset(image_url, image_data_b64, description, price, categor
|
|
| 416 |
file_ts = time.strftime("%Y%m%d_%H%M%S")
|
| 417 |
rec_ts = time.strftime("%Y-%m-%d %H:%M:%S")
|
| 418 |
safe_sender = _safe_name(sender_id) or "unknown"
|
| 419 |
-
|
| 420 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
img_bytes = None
|
| 422 |
if image_data_b64:
|
| 423 |
try:
|
|
@@ -444,7 +486,10 @@ def _save_to_main_dataset(image_url, image_data_b64, description, price, categor
|
|
| 444 |
_log("image_upload_fail", sender_id, chat_id, str(e))
|
| 445 |
finally:
|
| 446 |
pathlib.Path(tmp_path).unlink(missing_ok=True)
|
| 447 |
-
|
|
|
|
|
|
|
|
|
|
| 448 |
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp:
|
| 449 |
json.dump(record, tmp, indent=2, ensure_ascii=False)
|
| 450 |
tmp_path = tmp.name
|
|
@@ -582,6 +627,16 @@ async def webhooks(request: Request):
|
|
| 582 |
reply = "GOT IT! Product saved!"
|
| 583 |
else:
|
| 584 |
reply = "GOT IT! Saved to main dataset!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
else:
|
| 586 |
reply = "Hi! Send image + product info to save."
|
| 587 |
try:
|
|
@@ -779,10 +834,19 @@ def get_proxy_spaces():
|
|
| 779 |
|
| 780 |
|
| 781 |
HELP_INSTRUCTIONS = (
|
| 782 |
-
"
|
| 783 |
-
"1
|
| 784 |
-
"
|
| 785 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 786 |
)
|
| 787 |
|
| 788 |
|
|
@@ -913,8 +977,20 @@ async def handle_webhook(request: Request):
|
|
| 913 |
"text": "Saved! " + str(product_name)[:50],
|
| 914 |
"time": time.strftime("%Y-%m-%d %H:%M:%S"),
|
| 915 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 916 |
|
| 917 |
-
reply = "
|
| 918 |
asyncio.create_task(asyncio.to_thread(zapi.send_message, cid, reply))
|
| 919 |
|
| 920 |
elif event == "message.image.received" and chat_id:
|
|
|
|
| 282 |
return None
|
| 283 |
|
| 284 |
|
| 285 |
+
def _save_text_message_to_dataset(text, description, price, category, sender_id, sender_name, chat_id):
|
| 286 |
+
"""Save a text-only message to the main Zalo products dataset."""
|
| 287 |
+
try:
|
| 288 |
+
from huggingface_hub import HfApi
|
| 289 |
+
api = HfApi()
|
| 290 |
+
filename_ts = time.strftime("%Y%m%d_%H%M%S")
|
| 291 |
+
safe_name = re.sub(r'[^a-zA-Z0-9_\-]', '_', str(sender_name)[:30]) if sender_name else ""
|
| 292 |
+
file_name = f"{filename_ts}_{safe_name}_text.json"
|
| 293 |
+
upload_path = "data/" + file_name
|
| 294 |
+
record = {
|
| 295 |
+
"text": text,
|
| 296 |
+
"description": description[:500] if description else text[:500],
|
| 297 |
+
"price": price or "",
|
| 298 |
+
"category": category or "",
|
| 299 |
+
"sender_id": str(sender_id),
|
| 300 |
+
"sender_name": str(sender_name),
|
| 301 |
+
"chat_id": str(chat_id),
|
| 302 |
+
"image": "",
|
| 303 |
+
"product_name": "",
|
| 304 |
+
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
|
| 305 |
+
"message_type": "text",
|
| 306 |
+
}
|
| 307 |
+
json_content = json.dumps(record, ensure_ascii=False, indent=2)
|
| 308 |
+
api.upload_file(
|
| 309 |
+
path=upload_path,
|
| 310 |
+
path_in_repo=upload_path,
|
| 311 |
+
repo_id=MAIN_DATASET_ID,
|
| 312 |
+
repo_type="dataset",
|
| 313 |
+
token=HF_TOKEN,
|
| 314 |
+
commit_message=f"Add text message from {sender_name}",
|
| 315 |
+
)
|
| 316 |
+
logger.info("Text message saved to dataset: %s", file_name)
|
| 317 |
+
return upload_path
|
| 318 |
+
except Exception as e:
|
| 319 |
+
logger.error("Failed to save text to dataset: %s", e)
|
| 320 |
+
return None
|
| 321 |
+
|
| 322 |
+
|
| 323 |
def _ocr_extract_text(image_bytes):
|
| 324 |
"""Use HF Inference API with Vietnamese OCR model (Vintern-1B) to extract text from image."""
|
| 325 |
try:
|
|
|
|
| 443 |
|
| 444 |
_log("startup", "system", "SYSTEM", "Proxy space initialized. PROXY_NAME=" + PROXY_NAME)
|
| 445 |
|
| 446 |
+
def _save_to_main_dataset(image_url, image_data_b64, description, price, category, sender_id, sender_name, product_name="", chat_id="", text_message=None):
|
| 447 |
_log("main_dataset_save_start", sender_id, chat_id, "product_name=" + str(product_name) + " price=" + str(price))
|
| 448 |
if not HF_TOKEN or not MAIN_DATASET_ID:
|
| 449 |
_log("main_dataset_skip", sender_id, chat_id, "HF_TOKEN or MAIN_DATASET_ID missing")
|
|
|
|
| 454 |
file_ts = time.strftime("%Y%m%d_%H%M%S")
|
| 455 |
rec_ts = time.strftime("%Y-%m-%d %H:%M:%S")
|
| 456 |
safe_sender = _safe_name(sender_id) or "unknown"
|
| 457 |
+
if text_message:
|
| 458 |
+
img_filename = ""
|
| 459 |
+
meta_filename = "data/" + file_ts + "_" + safe_sender + "_text.json"
|
| 460 |
+
else:
|
| 461 |
+
img_filename = "images/" + file_ts + "_" + safe_sender + ".jpg"
|
| 462 |
+
meta_filename = "data/" + file_ts + "_" + safe_sender + ".json"
|
| 463 |
img_bytes = None
|
| 464 |
if image_data_b64:
|
| 465 |
try:
|
|
|
|
| 486 |
_log("image_upload_fail", sender_id, chat_id, str(e))
|
| 487 |
finally:
|
| 488 |
pathlib.Path(tmp_path).unlink(missing_ok=True)
|
| 489 |
+
if text_message:
|
| 490 |
+
record = {"image": "", "product_name": str(product_name)[:200] if product_name else "", "category": str(category) if category else "", "description": str(description)[:500] if description else str(text_message)[:500], "price": str(price) if price else "", "sender_id": str(sender_id), "sender_name": str(sender_name), "chat_id": str(chat_id), "is_zgr_group": _is_zgr_sender(sender_id), "timestamp": rec_ts, "text": str(text_message)[:1000], "message_type": "text"}
|
| 491 |
+
else:
|
| 492 |
+
record = {"image": uploaded_img, "product_name": str(product_name)[:200] if product_name else "", "category": str(category) if category else "", "description": str(description)[:500] if description else "", "price": str(price) if price else "", "sender_id": str(sender_id), "sender_name": str(sender_name), "chat_id": str(chat_id), "is_zgr_group": _is_zgr_sender(sender_id), "timestamp": rec_ts}
|
| 493 |
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp:
|
| 494 |
json.dump(record, tmp, indent=2, ensure_ascii=False)
|
| 495 |
tmp_path = tmp.name
|
|
|
|
| 627 |
reply = "GOT IT! Product saved!"
|
| 628 |
else:
|
| 629 |
reply = "GOT IT! Saved to main dataset!"
|
| 630 |
+
elif is_zgr and text:
|
| 631 |
+
# Save all text messages from ZGR group to main dataset
|
| 632 |
+
_save_to_main_dataset(
|
| 633 |
+
image_url=image_url, image_data_b64=image_data_b64,
|
| 634 |
+
description=text[:200], price=price, category=category,
|
| 635 |
+
sender_id=sender_id, sender_name=sender_name,
|
| 636 |
+
product_name=product_name, chat_id=chat_id,
|
| 637 |
+
text_message=text,
|
| 638 |
+
)
|
| 639 |
+
reply = "👋 Xin chào " + str(sender_name) + " (Zalo ID: " + str(sender_id) + ")!\n\n" + HELP_INSTRUCTIONS
|
| 640 |
else:
|
| 641 |
reply = "Hi! Send image + product info to save."
|
| 642 |
try:
|
|
|
|
| 834 |
|
| 835 |
|
| 836 |
HELP_INSTRUCTIONS = (
|
| 837 |
+
"🎓 HƯỚNG DẪN CẤU HÌNH ZALO BOT CHI TIẾT\n\n"
|
| 838 |
+
"1️⃣ Cách đặt tên Zalobot (QUAN TRỌNG):\n"
|
| 839 |
+
"• Tên bot không được chứa 'Zalo' hoặc 'bot'\n"
|
| 840 |
+
"• Ví dụ đúng: Shop, ChămSóc, HỗTrợ247, CSKH-TựĐộng ✅\n"
|
| 841 |
+
"• Ví dụ sai: Zalo Support, ShopBot, ZaloBot ❌\n\n"
|
| 842 |
+
"2️⃣ Cách lấy HTTP API:\n"
|
| 843 |
+
"• Truy cập https://zalo.me/s/botcreator\n"
|
| 844 |
+
"• Chọn bot → Cài đặt → API/HTTP API\n"
|
| 845 |
+
"• Copy Bot token: 4179413508988279245:XXXXXXXXXXXXXXXXXXXXXX\n\n"
|
| 846 |
+
"3️⃣ Cách dùng:\n"
|
| 847 |
+
"• Gửi HTTP API: <bot_token> để tạo proxy tự động\n"
|
| 848 |
+
"• Gửi ảnh + mô tả sản phẩm để lưu vào dataset\n"
|
| 849 |
+
"• Mọi tin nhắn trong nhóm sẽ được lưu tự động"
|
| 850 |
)
|
| 851 |
|
| 852 |
|
|
|
|
| 977 |
"text": "Saved! " + str(product_name)[:50],
|
| 978 |
"time": time.strftime("%Y-%m-%d %H:%M:%S"),
|
| 979 |
})
|
| 980 |
+
elif _is_zgr_sender_local(sender_id) and text:
|
| 981 |
+
# Save all text messages from ZGR group to dataset
|
| 982 |
+
_save_text_message_to_dataset(
|
| 983 |
+
text=text, description=text[:200], price=price, category=category,
|
| 984 |
+
sender_id=sender_id, sender_name=sender_name, chat_id=chat_id,
|
| 985 |
+
)
|
| 986 |
+
BOT_STATE["logs"].append({
|
| 987 |
+
"event": "text_saved", "sender_id": sender_id, "chat_id": chat_id,
|
| 988 |
+
"sender_name": sender_name, "chat_type": chat_type,
|
| 989 |
+
"text": "Text saved: " + str(text[:100]),
|
| 990 |
+
"time": time.strftime("%Y-%m-%d %H:%M:%S"),
|
| 991 |
+
})
|
| 992 |
|
| 993 |
+
reply = "👋 Xin chào " + str(sender_name) + " (Zalo ID: " + str(sender_id) + ")!\n\n" + HELP_INSTRUCTIONS
|
| 994 |
asyncio.create_task(asyncio.to_thread(zapi.send_message, cid, reply))
|
| 995 |
|
| 996 |
elif event == "message.image.received" and chat_id:
|