ariansyahdedy commited on
Commit
db6bff5
·
1 Parent(s): 9569b7d

Add reply function

Browse files
Files changed (2) hide show
  1. app/main.py +105 -5
  2. app/services/message.py +47 -0
app/main.py CHANGED
@@ -1,15 +1,115 @@
1
- from fastapi import FastAPI, Request
2
  from fastapi.responses import JSONResponse
3
  from fastapi.responses import Response
4
  from fastapi.exceptions import HTTPException
 
 
 
 
 
5
 
 
 
 
 
 
 
 
6
  app = FastAPI()
7
 
8
- # Webhook to handle incoming messages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  @app.post("/webhook")
10
- async def whatsapp_webhook(request: Request):
11
- data = await request.json()
12
- print("Webhook received:", data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
 
15
  @app.get("/webhook")
 
1
+ from fastapi import FastAPI, Request, status
2
  from fastapi.responses import JSONResponse
3
  from fastapi.responses import Response
4
  from fastapi.exceptions import HTTPException
5
+ from typing import Dict
6
+ from app.services.message import generate_reply, send_reply, process_message_with_retry
7
+ import logging
8
+ from datetime import datetime
9
+ import time
10
 
11
+ # Configure logging
12
+ logging.basicConfig(
13
+ level=logging.INFO,
14
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
15
+ )
16
+ logger = logging.getLogger(__name__)
17
+ # Initialize FastAPI app
18
  app = FastAPI()
19
 
20
+ # In-memory cache with timestamp cleanup
21
+ class MessageCache:
22
+ def __init__(self, max_age_hours: int = 24):
23
+ self.messages: Dict[str, float] = {}
24
+ self.max_age_seconds = max_age_hours * 3600
25
+
26
+ def add(self, message_id: str) -> None:
27
+ self.cleanup()
28
+ self.messages[message_id] = time.time()
29
+
30
+ def exists(self, message_id: str) -> bool:
31
+ self.cleanup()
32
+ return message_id in self.messages
33
+
34
+ def cleanup(self) -> None:
35
+ current_time = time.time()
36
+ self.messages = {
37
+ msg_id: timestamp
38
+ for msg_id, timestamp in self.messages.items()
39
+ if current_time - timestamp < self.max_age_seconds
40
+ }
41
+
42
+ message_cache = MessageCache()
43
+
44
  @app.post("/webhook")
45
+ async def webhook(request: Request):
46
+ request_id = f"req_{int(time.time()*1000)}"
47
+ logger.info(f"Processing webhook request {request_id}")
48
+ # Parse incoming request
49
+ payload = await request.json()
50
+
51
+ processed_count = 0
52
+ error_count = 0
53
+ results = []
54
+
55
+ changes = payload.get("entry", [])[0].get("changes", [])
56
+
57
+ for change in changes:
58
+ messages = change.get("value", {}).get("messages", [])
59
+
60
+ for message in messages:
61
+ message_id = message.get("id")
62
+ timestamp = message.get("timestamp")
63
+ content = message.get("text", {}).get("body")
64
+ sender_id = message.get("from")
65
+
66
+ # Check for duplicate message ID
67
+ # Check for duplicate message
68
+ if message_cache.exists(message_id):
69
+ logger.info(f"Duplicate message detected: {message_id}")
70
+ continue
71
+
72
+ try:
73
+ # Process message with retry logic
74
+ result = await process_message_with_retry(
75
+ sender_id,
76
+ content,
77
+ timestamp
78
+ )
79
+ message_cache.add(message_id)
80
+ processed_count += 1
81
+ results.append(result)
82
+
83
+ except Exception as e:
84
+ error_count += 1
85
+ logger.error(
86
+ f"Failed to process message {message_id}: {str(e)}",
87
+ exc_info=True
88
+ )
89
+ results.append({
90
+ "status": "error",
91
+ "message_id": message_id,
92
+ "error": str(e)
93
+ })
94
+
95
+ # Return detailed response
96
+ response_data = {
97
+ "request_id": request_id,
98
+ "processed": processed_count,
99
+ "errors": error_count,
100
+ "results": results
101
+ }
102
+
103
+ logger.info(
104
+ f"Webhook processing completed - "
105
+ f"Processed: {processed_count}, Errors: {error_count}"
106
+ )
107
+
108
+ return JSONResponse(
109
+ content=response_data,
110
+ status_code=status.HTTP_200_OK
111
+ )
112
+
113
 
114
 
115
  @app.get("/webhook")
app/services/message.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ from fastapi.exceptions import HTTPException
4
+ from dotenv import load_dotenv
5
+ import datetime
6
+ load_dotenv()
7
+
8
+ # Define the WhatsApp API URL
9
+ WHATSAPP_API_URL = os.environ.get("WHATSAPP_API_URL")
10
+
11
+ # Access the secret token
12
+ ACCESS_TOKEN = os.environ.get("ACCESS_TOKEN")
13
+
14
+
15
+ # Helper function to send a reply
16
+ def send_reply(to: str, body: str):
17
+ headers = {
18
+ "Authorization": f"Bearer {ACCESS_TOKEN}",
19
+ "Content-Type": "application/json"
20
+ }
21
+ data = {
22
+ "messaging_product": "whatsapp",
23
+ "to": to,
24
+ "type": "text",
25
+ "text": {
26
+ "body": body
27
+ }
28
+ }
29
+ print(f"ACCESS_TOKEN: {ACCESS_TOKEN}")
30
+ response = requests.post(WHATSAPP_API_URL, json=data, headers=headers)
31
+ if response.status_code != 200:
32
+ raise HTTPException(status_code=response.status_code, detail=response.json())
33
+ return response.json()
34
+
35
+ async def generate_reply(sender: str, content: str, timestamp: str) -> str:
36
+ # Example customization logic
37
+ received_time = datetime.fromtimestamp(int(timestamp))
38
+ if "hello" in content.lower():
39
+ return f"Hi {sender}, how can I assist you today?"
40
+ elif "test" in content.lower():
41
+ return f"Hi {sender}, this is a reply to your test message."
42
+ elif received_time.hour < 12:
43
+ return f"Good morning, {sender}! How can I help you?"
44
+ else:
45
+ return f"Hello {sender}, I hope you're having a great day!"
46
+
47
+