VietCat commited on
Commit
625fd6c
·
1 Parent(s): c3190aa

init project

Browse files
.env.template ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # .env.template
2
+ FB_VERIFY_TOKEN=
3
+ SUPABASE_KEY=
4
+ VECTOR_API_KEY=
.gitignore CHANGED
@@ -191,4 +191,5 @@ cython_debug/
191
  # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
192
  # refer to https://docs.cursor.com/context/ignore-files
193
  .cursorignore
194
- .cursorindexingignore
 
 
191
  # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
192
  # refer to https://docs.cursor.com/context/ignore-files
193
  .cursorignore
194
+ .cursorindexingignore.env
195
+ .DS_Store
Dockerfile ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+ WORKDIR /app
3
+ COPY . .
4
+ RUN pip install --no-cache-dir -r requirements.txt
5
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
app/config.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ # Tải biến môi trường từ file `.env`
5
+ load_dotenv()
6
+
7
+ # === Facebook Webhook ===
8
+ # Token xác thực khi Facebook gửi GET webhook
9
+ FB_VERIFY_TOKEN = os.getenv("FB_VERIFY_TOKEN")
10
+
11
+ # Địa chỉ gửi tin nhắn Facebook Graph API
12
+ FB_GRAPH_API = "https://graph.facebook.com/v22.0"
13
+
14
+ # === Google Sheets ===
15
+ # ID bảng tính và tên sheet
16
+ GOOGLE_SHEET_ID = "1H7BxVgXy5GgzRVxg34TMWN6xgqI4gA8zwzIAIZp2NdI"
17
+ GOOGLE_SHEET_NAME = "chat"
18
+
19
+ # === Supabase ===
20
+ # Địa chỉ Supabase và key truy cập
21
+ SUPABASE_URL = "https://hekathwdexaukowdjtxj.supabase.co" # Thay bằng domain thật
22
+ SUPABASE_KEY = os.getenv("SUPABASE_KEY")
23
+
24
+ # === Vector Search (pgvector + Supabase function) ===
25
+ VECTOR_SEARCH_URL = "https://hekathwdexaukowdjtxj.supabase.co/rest/v1/rpc/match_documents"
26
+ VECTOR_API_KEY = os.getenv("VECTOR_API_KEY")
app/handlers/parser.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.utils import log_task
2
+
3
+ @log_task("Phân tích tin nhắn")
4
+ def parse_message(message: dict) -> dict:
5
+ """
6
+ Args:
7
+ message: JSON từ Messenger
8
+ Returns:
9
+ dict chứa sender, page_id, text, attachments, command, content, vehicle
10
+ """
11
+ t = message.get("message", {}).get("text", "")
12
+ attachments = message.get("message", {}).get("attachments", [])
13
+ sid = message.get("sender", {}).get("id")
14
+ pid = message.get("recipient", {}).get("id")
15
+
16
+ res = {
17
+ "text": t,
18
+ "attachments": attachments,
19
+ "recipient_id": sid,
20
+ "page_id": pid,
21
+ "command": "",
22
+ "password": "",
23
+ "content": "",
24
+ "vehicle": detect_vehicle(t.lower())
25
+ }
26
+
27
+ if t.startswith("\\"):
28
+ parts = t[1:].split("\\")
29
+ res.update({
30
+ "command": parts[0].strip(),
31
+ "password": parts[1].strip() if len(parts) > 1 else "",
32
+ "content": "\\".join(parts[2:]).strip() if len(parts) > 2 else ""
33
+ })
34
+
35
+ return res
36
+
37
+ def detect_vehicle(text: str) -> str:
38
+ """
39
+ Args:
40
+ text: nội dung tin nhắn
41
+ Returns:
42
+ một trong các phương tiện, hoặc "" nếu không tìm thấy
43
+ """
44
+ keywords = ["mô tô","xe máy điện","xe máy","ô tô","xe đạp","đi bộ"]
45
+ for k in keywords:
46
+ if k in text:
47
+ return k
48
+ return ""
app/handlers/webhook.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request
2
+ from app.config import FB_VERIFY_TOKEN
3
+ from app.handlers.parser import parse_message
4
+ from app.services.supabase import get_page_token
5
+ from app.services.responder import send_fallback_message
6
+
7
+ router = APIRouter()
8
+
9
+ @router.get("/facebook-webhook")
10
+ def verify_token(request: Request):
11
+ q = dict(request.query_params)
12
+ if q.get("hub.mode") == "subscribe" and q.get("hub.verify_token") == FB_VERIFY_TOKEN:
13
+ return q.get("hub.challenge")
14
+ return {"error": "Invalid token"}
15
+
16
+ @router.post("/facebook-webhook")
17
+ async def handle_message(request: Request):
18
+ body = await request.json()
19
+ try:
20
+ ev = body["entry"][0]["messaging"][0]
21
+ if ev.get("message", {}).get("is_echo"):
22
+ return {"status": "echo ignored"}
23
+
24
+ parsed = parse_message(ev)
25
+ parsed["token"] = get_page_token(parsed["page_id"])
26
+
27
+ if not parsed.get("vehicle"):
28
+ send_fallback_message(parsed["page_id"], parsed["recipient_id"], parsed["token"])
29
+ return {"status": "fallback sent"}
30
+
31
+ return {"status": "ok"}
32
+ except Exception as e:
33
+ return {"error": str(e)}
app/main.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from app.handlers.webhook import router
3
+
4
+ app = FastAPI()
5
+ app.include_router(router)
app/services/responder.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from app.utils import log_task
3
+ from app.config import FB_GRAPH_API
4
+
5
+ @log_task("Gửi fallback message")
6
+ def send_fallback_message(page_id: str, recipient_id: str, token: str):
7
+ url = f"{FB_GRAPH_API}/{page_id}/messages"
8
+ data = {
9
+ "recipient": {"id": recipient_id},
10
+ "messaging_type": "RESPONSE",
11
+ "message": {"text": "Tôi chưa rõ phương tiện bạn đang sử dụng. Bạn có thể cung cấp thêm không?"}
12
+ }
13
+ params = {"access_token": token}
14
+ r = requests.post(url, json=data, params=params)
15
+ r.raise_for_status()
app/services/supabase.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.utils import log_task
2
+ from app.config import SUPABASE_URL, SUPABASE_KEY
3
+
4
+ @log_task("Lấy token page từ Supabase")
5
+ def get_page_token(page_id: str) -> str:
6
+ """
7
+ Giả lập hoặc thực gọi Supabase để lấy token
8
+ """
9
+ dummy = {"123": "fake_token"}
10
+ return dummy.get(page_id, "unknown_token")
app/utils.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import logging
3
+ from functools import wraps
4
+
5
+ logging.basicConfig(level=logging.INFO)
6
+
7
+ def log_task(name):
8
+ """
9
+ Decorator log thông tin task: bắt đầu, kết thúc, lỗi, thời gian.
10
+ """
11
+ def decorator(func):
12
+ @wraps(func)
13
+ def wrapper(*args, **kwargs):
14
+ logging.info(f"🔵 Bắt đầu: {name}")
15
+ start = time.time()
16
+ try:
17
+ result = func(*args, **kwargs)
18
+ logging.info(f"🟢 Kết thúc: {name} trong {time.time() - start:.2f}s")
19
+ return result
20
+ except Exception as e:
21
+ logging.error(f"🔴 Lỗi ở {name}: {e}", exc_info=True)
22
+ return {"error": str(e)}
23
+ return wrapper
24
+ return decorator
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ requests
4
+ python-dotenv