devZenaight commited on
Commit
4eaa4e3
·
verified ·
1 Parent(s): c703916

Upload 5 files

Browse files
Files changed (3) hide show
  1. Dockerfile +8 -21
  2. main.py +57 -131
  3. requirements.txt +3 -4
Dockerfile CHANGED
@@ -1,25 +1,12 @@
1
- FROM python:3.11-slim
2
 
3
- # Set working directory
4
- WORKDIR /app
5
-
6
- # Copy requirements first for better caching
7
- COPY requirements.txt .
8
-
9
- # Install dependencies
10
- RUN pip install --no-cache-dir -r requirements.txt
11
 
12
- # Copy application code
13
- COPY main.py .
14
-
15
- # Create a test image file (optional - for development)
16
- RUN echo "This is a test image file" > test.jpg
17
-
18
- # Expose port 8080 for Hugging Face Spaces
19
- EXPOSE 8080
20
 
21
- # Set environment variables for better logging
22
- ENV PYTHONUNBUFFERED=1
23
 
24
- # Run the application with Uvicorn
25
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
 
1
+ FROM python:3.9
2
 
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
 
 
 
 
 
6
 
7
+ WORKDIR /app
8
+ COPY --chown=user . .
 
 
 
 
 
 
9
 
10
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
 
11
 
12
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
main.py CHANGED
@@ -5,171 +5,97 @@ import os
5
  import httpx
6
  import base64
7
 
8
- app = FastAPI(title="WhatsApp ChatBot", version="1.0.0")
 
 
 
9
 
10
- # Load environment variables
11
- VERIFY_TOKEN = os.getenv("VERIFY_TOKEN", "")
12
- WHATSAPP_API_TOKEN = os.getenv("WHATSAPP_API_TOKEN", "")
13
- PHONE_NUMBER_ID = os.getenv("PHONE_NUMBER_ID", "")
14
 
15
  @app.get("/")
16
  def root():
17
- return {"message": "WhatsApp ChatBot API", "status": "running"}
18
 
19
- @app.get("/health")
20
- def health_check():
21
- return {"status": "healthy", "verify_token_loaded": bool(VERIFY_TOKEN), "api_token_loaded": bool(WHATSAPP_API_TOKEN)}
 
 
 
 
 
 
 
 
22
 
23
  @app.get("/webhook")
24
- async def verify_webhook(
25
- hub_mode: str = Query(..., alias="hub.mode"),
26
- hub_token: str = Query(..., alias="hub.verify_token"),
27
- hub_challenge: str = Query(..., alias="hub.challenge")
28
  ):
29
- """
30
- Verify webhook with Meta's WhatsApp Business API
31
- """
32
- if hub_mode == "subscribe" and hub_token == VERIFY_TOKEN:
33
- return PlainTextResponse(hub_challenge)
34
- raise HTTPException(status_code=403, detail="Verification failed")
 
 
 
 
 
35
 
36
  @app.post("/webhook")
37
  async def receive_webhook(request: Request):
38
- """
39
- Receive incoming WhatsApp messages
40
- """
41
  try:
42
- data = await request.json()
43
-
44
- # Extract message from webhook payload
45
- entry = data.get("entry", [])
46
- if not entry:
47
- return {"status": "no_entry"}
48
-
49
- changes = entry[0].get("changes", [])
50
- if not changes:
51
- return {"status": "no_changes"}
52
-
53
- value = changes[0].get("value", {})
54
- messages = value.get("messages", [])
55
-
56
- if not messages:
57
- return {"status": "no_messages"}
58
-
59
- msg = messages[0]
60
- wa_id = msg.get("from")
61
- msg_type = msg.get("type")
62
-
63
- if not wa_id:
64
- return {"status": "no_sender_id"}
65
-
66
  if msg_type == "image":
67
- # Handle image message
68
  media_id = msg["image"]["id"]
69
  image_bytes = await download_whatsapp_media(media_id)
70
-
71
- if image_bytes:
72
- # Convert to base64
73
- b64_str = base64.b64encode(image_bytes).decode('utf-8')
74
- reply = f"Image received! Base64 (first 50 chars): {b64_str[:50]}..."
75
-
76
- # Send reply
77
- await send_whatsapp_message(wa_id, reply)
78
-
79
- return {
80
- "status": "success",
81
- "message_type": "image",
82
- "base64_length": len(b64_str),
83
- "base64_preview": b64_str[:50]
84
- }
85
- else:
86
- await send_whatsapp_message(wa_id, "Sorry, I couldn't process your image.")
87
- return {"status": "error", "message": "Failed to download image"}
88
  else:
89
- # Handle non-image messages
90
- await send_whatsapp_message(wa_id, "Please send an image and I'll process it for you!")
91
- return {"status": "replied", "message": "Requested image"}
92
-
93
  except Exception as e:
94
- print(f"Webhook error: {e}")
95
- return JSONResponse(
96
- content={"error": str(e), "status": "error"},
97
- status_code=400
98
- )
99
 
100
- async def download_whatsapp_media(media_id: str) -> Optional[bytes]:
101
- """
102
- Download media from WhatsApp Cloud API
103
- Currently stubbed - replace with actual implementation
104
- """
105
  try:
106
- # Stub implementation - replace with actual WhatsApp media download
107
- # For now, return a test image or None
108
  with open("test.jpg", "rb") as f:
109
  return f.read()
110
  except FileNotFoundError:
111
- print(f"Media file not found for ID: {media_id}")
112
- return None
113
- except Exception as e:
114
- print(f"Error downloading media {media_id}: {e}")
115
  return None
116
 
117
- async def send_whatsapp_message(to: str, text: str) -> bool:
118
- """
119
- Send WhatsApp message via Cloud API
120
- """
121
- if not WHATSAPP_API_TOKEN or not PHONE_NUMBER_ID:
122
- print("Missing WhatsApp API token or phone number ID")
123
- return False
124
-
125
  url = f"https://graph.facebook.com/v19.0/{PHONE_NUMBER_ID}/messages"
126
-
127
  headers = {
128
  "Authorization": f"Bearer {WHATSAPP_API_TOKEN}",
129
  "Content-Type": "application/json"
130
  }
131
-
132
  payload = {
133
  "messaging_product": "whatsapp",
134
- "to": to,
135
  "type": "text",
136
  "text": {"body": text}
137
  }
138
-
139
- try:
140
- async with httpx.AsyncClient() as client:
141
- response = await client.post(url, headers=headers, json=payload)
142
- response.raise_for_status()
143
- print(f"Message sent successfully to {to}")
144
- return True
145
- except Exception as e:
146
- print(f"Error sending message to {to}: {e}")
147
- return False
148
-
149
- @app.get("/test")
150
- async def test_endpoint():
151
- """
152
- Test endpoint to verify API is working
153
- """
154
- return {
155
- "message": "API is working!",
156
- "verify_token_configured": bool(VERIFY_TOKEN),
157
- "api_token_configured": bool(WHATSAPP_API_TOKEN),
158
- "phone_number_configured": bool(PHONE_NUMBER_ID)
159
- }
160
 
161
  @app.post("/send")
162
  async def check_token():
163
- """
164
- Health check endpoint to verify WhatsApp API token configuration
165
- """
166
- if WHATSAPP_API_TOKEN:
167
- return {"token_loaded": True, "message": "WhatsApp API token is configured"}
168
- return JSONResponse(
169
- content={"error": "Token not found in environment", "token_loaded": False},
170
- status_code=400
171
- )
172
-
173
- if __name__ == "__main__":
174
- import uvicorn
175
- uvicorn.run(app, host="0.0.0.0", port=8080)
 
5
  import httpx
6
  import base64
7
 
8
+ app = FastAPI()
9
+ VERIFY_TOKEN = os.getenv('VERIFY_TOKEN')
10
+ PHONE_NUMBER_ID = os.getenv('PHONE_NUMBER_ID')
11
+ WHATSAPP_API_TOKEN = os.getenv('WHATSAPP_API_TOKEN')
12
 
13
+ if not all([VERIFY_TOKEN, WHATSAPP_API_TOKEN, PHONE_NUMBER_ID]):
14
+ raise ValueError("Missing required environment variables")
 
 
15
 
16
  @app.get("/")
17
  def root():
18
+ return {"message": "Hello from WhatsApp ChatBot"}
19
 
20
+ @app.get("/ping")
21
+ def ping():
22
+ return {"pong": True}
23
+
24
+ @app.get("/test")
25
+ async def test_get():
26
+ return {"detail": "GET method working"}
27
+
28
+ @app.post("/test")
29
+ async def test_post():
30
+ return {"detail": "POST method working"}
31
 
32
  @app.get("/webhook")
33
+ def verify(
34
+ hub_mode: Optional[str] = Query(None, alias="hub.mode"),
35
+ hub_challenge: Optional[str] = Query(None, alias="hub.challenge"),
36
+ hub_verify_token: Optional[str] = Query(None, alias="hub.verify_token"),
37
  ):
38
+ # 1) Initial probe
39
+ if hub_mode is None and hub_challenge is None and hub_verify_token is None:
40
+ return PlainTextResponse(status_code=200, content="")
41
+
42
+ # 2) Real handshake: echo back challenge (temporarily ignore token)
43
+ if hub_mode == "subscribe":
44
+ return PlainTextResponse(content=hub_challenge or "", media_type="text/plain")
45
+
46
+ # 3) Everything else → forbidden
47
+ if hub_verify_token != VERIFY_TOKEN:
48
+ raise HTTPException(status_code=403, detail="Invalid verify token")
49
 
50
  @app.post("/webhook")
51
  async def receive_webhook(request: Request):
52
+ data = await request.json()
 
 
53
  try:
54
+ msg = data["entry"][0]["changes"][0]["value"]["messages"][0]
55
+ wa_id = msg["from"]
56
+ msg_type = msg["type"]
57
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  if msg_type == "image":
 
59
  media_id = msg["image"]["id"]
60
  image_bytes = await download_whatsapp_media(media_id)
61
+ if not image_bytes:
62
+ return {"status": "error", "message": "Failed to process image"}
63
+ b64_str = base64.b64encode(image_bytes).decode('utf-8')
64
+ reply = f"Base64: {b64_str[:10]}"
65
+ send_whatsapp_message(wa_id, reply)
66
+ return {"status": "success", "base64_first_10": b64_str[:10]}
 
 
 
 
 
 
 
 
 
 
 
 
67
  else:
68
+ send_whatsapp_message(wa_id, "Please send an image.")
69
+ return {"status": "replied", "message": "Please send an image"}
 
 
70
  except Exception as e:
71
+ print("Webhook error:", e)
72
+ return JSONResponse(content={"error": str(e)}, status_code=400)
 
 
 
73
 
74
+ async def download_whatsapp_media(media_id):
 
 
 
 
75
  try:
 
 
76
  with open("test.jpg", "rb") as f:
77
  return f.read()
78
  except FileNotFoundError:
79
+ print("test.jpg not found")
 
 
 
80
  return None
81
 
82
+ def send_whatsapp_message(wa_id, text):
 
 
 
 
 
 
 
83
  url = f"https://graph.facebook.com/v19.0/{PHONE_NUMBER_ID}/messages"
 
84
  headers = {
85
  "Authorization": f"Bearer {WHATSAPP_API_TOKEN}",
86
  "Content-Type": "application/json"
87
  }
 
88
  payload = {
89
  "messaging_product": "whatsapp",
90
+ "to": wa_id,
91
  "type": "text",
92
  "text": {"body": text}
93
  }
94
+ httpx.post(url, headers=headers, json=payload)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  @app.post("/send")
97
  async def check_token():
98
+ token = WHATSAPP_API_TOKEN
99
+ if token:
100
+ return {"token_loaded": True}
101
+ return JSONResponse(content={"error": "Token not found in environment"}, status_code=400)
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,4 +1,3 @@
1
- fastapi==0.104.1
2
- uvicorn[standard]==0.24.0
3
- httpx==0.25.2
4
- python-multipart==0.0.6
 
1
+ fastapi
2
+ uvicorn
3
+ httpx