Mr-Help commited on
Commit
703d236
·
verified ·
1 Parent(s): 19997a5

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +274 -6
main.py CHANGED
@@ -1,15 +1,283 @@
1
- from fastapi import FastAPI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  app = FastAPI()
4
 
5
- @app.get("/")
6
- async def root():
7
- return {"version": "ONLY_TEST_2026_03_16_A"}
 
 
 
 
 
8
 
9
  @app.get("/health")
10
  async def health():
11
- return {"ok": True, "version": "ONLY_TEST_2026_03_16_A"}
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  @app.get("/receive-test")
14
  async def receive_test():
15
- return {"ok": True, "message": "receive endpoint reachable", "version": "ONLY_TEST_2026_03_16_A"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from fastapi import FastAPI, Request
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.responses import JSONResponse
5
+
6
+ from greenapi import (
7
+ extract_text_message,
8
+ get_device_key_by_instance_id,
9
+ send_text_message,
10
+ send_url_button,
11
+ )
12
+
13
+ from engine.conversation_engine import process_message
14
+ from messages import log_message
15
+
16
+ from handoff.sales import create_sales_handoff
17
+ from handoff.support import create_support_handoff
18
 
19
  app = FastAPI()
20
 
21
+ app.add_middleware(
22
+ CORSMiddleware,
23
+ allow_origins=["*"],
24
+ allow_credentials=False,
25
+ allow_methods=["POST", "OPTIONS"],
26
+ allow_headers=["*"],
27
+ )
28
+
29
 
30
  @app.get("/health")
31
  async def health():
32
+ print(">>> /health called")
33
+ return {"ok": True, "service": "up"}
34
+
35
+
36
+ @app.get("/")
37
+ async def root():
38
+ return {
39
+ "ok": True,
40
+ "service": "ADK Bot API",
41
+ "message": "Server is running"
42
+ }
43
+
44
 
45
  @app.get("/receive-test")
46
  async def receive_test():
47
+ print(">>> /receive-test called")
48
+ return {"ok": True, "message": "receive endpoint reachable"}
49
+
50
+
51
+ @app.post("/receive")
52
+ async def webhook_receiver(req: Request):
53
+ print("========== /receive HIT ==========")
54
+
55
+ try:
56
+ raw_body = await req.body()
57
+ print(">>> RAW BODY BYTES:")
58
+ print(raw_body)
59
+ except Exception as e:
60
+ print(">>> FAILED TO READ RAW BODY:", str(e))
61
+ raw_body = b""
62
+
63
+ try:
64
+ headers = dict(req.headers)
65
+ print(">>> HEADERS:")
66
+ print(json.dumps(headers, ensure_ascii=False, indent=2))
67
+ except Exception as e:
68
+ print(">>> FAILED TO READ HEADERS:", str(e))
69
+
70
+ body = None
71
+
72
+ try:
73
+ if raw_body:
74
+ body = json.loads(raw_body.decode("utf-8"))
75
+ else:
76
+ body = {}
77
+ except Exception as e:
78
+ print(">>> FAILED TO PARSE JSON:", str(e))
79
+ return JSONResponse(
80
+ status_code=200,
81
+ content={
82
+ "ok": True,
83
+ "debug": "request reached /receive but body is not valid JSON"
84
+ }
85
+ )
86
+
87
+ print("===== RECEIVED JSON =====")
88
+ print(json.dumps(body, ensure_ascii=False, indent=2))
89
+ print("=========================")
90
+
91
+ # مؤقتًا نطبع typeWebhook حتى لو مختلفة
92
+ webhook_type = body.get("typeWebhook")
93
+ print(">>> typeWebhook:", webhook_type)
94
+
95
+ if webhook_type != "incomingMessageReceived":
96
+ print(">>> ignored non-incoming webhook")
97
+ return {
98
+ "ok": True,
99
+ "ignored": True,
100
+ "typeWebhook": webhook_type
101
+ }
102
+
103
+ instance_data = body.get("instanceData") or {}
104
+ sender_data = body.get("senderData") or {}
105
+ msg_data = body.get("messageData") or {}
106
+
107
+ # مبدئيًا هنطبع كل الاحتمالات
108
+ print(">>> instanceData:", instance_data)
109
+ print(">>> senderData:", sender_data)
110
+ print(">>> messageData:", msg_data)
111
+
112
+ # مهم: بعض payloads فيها idInstance وبعضها wid فقط
113
+ instance_id = str(
114
+ instance_data.get("idInstance")
115
+ or instance_data.get("wid")
116
+ or ""
117
+ )
118
+
119
+ chat_id = sender_data.get("chatId")
120
+ text_message = extract_text_message(msg_data)
121
+
122
+ print(">>> resolved instance_id:", instance_id)
123
+ print(">>> resolved chat_id:", chat_id)
124
+ print(">>> resolved text_message:", text_message)
125
+
126
+ if not instance_id or not chat_id or not text_message:
127
+ print(">>> skipped because missing required fields")
128
+ return {
129
+ "ok": True,
130
+ "skipped": True,
131
+ "resolved_instance_id": instance_id,
132
+ "resolved_chat_id": chat_id,
133
+ "resolved_text_message": text_message,
134
+ }
135
+
136
+ device_key = get_device_key_by_instance_id(instance_id)
137
+ print(">>> resolved device_key:", device_key)
138
+
139
+ if not device_key:
140
+ print(">>> Unknown instance_id:", instance_id)
141
+ return {
142
+ "ok": True,
143
+ "skipped": True,
144
+ "reason": "unknown_instance_id",
145
+ "instance_id": instance_id
146
+ }
147
+
148
+ customer_phone = chat_id.replace("@c.us", "")
149
+ bot_number = device_key
150
+
151
+ try:
152
+ log_message(customer_phone, bot_number, "in", text_message)
153
+ except Exception as e:
154
+ print(">>> failed to log incoming message:", str(e))
155
+
156
+ try:
157
+ result = process_message(
158
+ bot_number=bot_number,
159
+ customer_phone=customer_phone,
160
+ text=text_message
161
+ )
162
+ print(">>> engine result:")
163
+ print(json.dumps(result, ensure_ascii=False, indent=2))
164
+ except Exception as e:
165
+ print(">>> engine error:", str(e))
166
+
167
+ fallback_reply = "تمام، حصلت مشكلة بسيطة. ممكن تعيد إرسال رسالتك مرة تانية؟"
168
+ try:
169
+ status, txt = send_text_message(device_key, chat_id, fallback_reply)
170
+ print(">>> fallback send status:", status)
171
+ print(">>> fallback send body:", txt)
172
+ log_message(customer_phone, bot_number, "out", fallback_reply)
173
+ except Exception as send_err:
174
+ print(">>> failed to send fallback:", str(send_err))
175
+
176
+ return JSONResponse(
177
+ status_code=200,
178
+ content={"ok": True, "error": str(e), "fallback_sent": True}
179
+ )
180
+
181
+ reply = (result.get("reply") or "").strip()
182
+ action = result.get("action")
183
+
184
+ if action and action.get("type") == "url_button":
185
+ try:
186
+ status, txt = send_url_button(
187
+ device_key=device_key,
188
+ chat_id=chat_id,
189
+ header=action.get("header", "مرحباً بك"),
190
+ body=action.get("body", reply),
191
+ footer=action.get("footer", ""),
192
+ url=action.get("url"),
193
+ button_text=action.get("button_text", "فتح الرابط")
194
+ )
195
+ print(">>> sent url button status:", status)
196
+ print(">>> sent url button body:", txt)
197
+
198
+ try:
199
+ log_message(customer_phone, bot_number, "out", action.get("body", reply))
200
+ except Exception as e:
201
+ print(">>> failed to log outgoing url button body:", str(e))
202
+
203
+ except Exception as e:
204
+ print(">>> failed to send url button:", str(e))
205
+ fallback_reply = action.get("body", reply) or "تمام، افتح الرابط من فضلك."
206
+ try:
207
+ status, txt = send_text_message(device_key, chat_id, fallback_reply)
208
+ print(">>> url fallback send status:", status)
209
+ print(">>> url fallback send body:", txt)
210
+ log_message(customer_phone, bot_number, "out", fallback_reply)
211
+ except Exception as send_err:
212
+ print(">>> failed to send url fallback:", str(send_err))
213
+
214
+ else:
215
+ try:
216
+ status, txt = send_text_message(device_key, chat_id, reply)
217
+ print(">>> sent text status:", status)
218
+ print(">>> sent text body:", txt)
219
+
220
+ try:
221
+ log_message(customer_phone, bot_number, "out", reply)
222
+ except Exception as e:
223
+ print(">>> failed to log outgoing text:", str(e))
224
+
225
+ except Exception as e:
226
+ print(">>> failed to send text:", str(e))
227
+
228
+ if action and action.get("type") == "handoff":
229
+ department = action.get("department")
230
+ summary = action.get("summary", "")
231
+ metadata = result.get("flow_data", {})
232
+
233
+ try:
234
+ if department == "sales":
235
+ create_sales_handoff(
236
+ customer_phone=customer_phone,
237
+ bot_number=bot_number,
238
+ summary=summary,
239
+ metadata=metadata
240
+ )
241
+ elif department == "support":
242
+ create_support_handoff(
243
+ customer_phone=customer_phone,
244
+ bot_number=bot_number,
245
+ summary=summary,
246
+ metadata=metadata
247
+ )
248
+ print(">>> handoff created:", department)
249
+ except Exception as e:
250
+ print(">>> failed to create handoff:", str(e))
251
+
252
+ return {
253
+ "ok": True,
254
+ "device_key": device_key,
255
+ "customer_phone": customer_phone,
256
+ "state": result.get("next_state"),
257
+ "has_action": bool(action),
258
+ }
259
+
260
+
261
+ @app.post("/complaints/callback")
262
+ async def complaints_callback(req: Request):
263
+ print("========== /complaints/callback HIT ==========")
264
+
265
+ try:
266
+ payload = await req.json()
267
+ except Exception:
268
+ return JSONResponse(status_code=400, content={"ok": False, "error": "invalid json"})
269
+
270
+ print("===== COMPLAINT CALLBACK =====")
271
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
272
+ print("==============================")
273
+
274
+ uid = payload.get("uid", "")
275
+ complaint_number = payload.get("complaint_number", "")
276
+ status = payload.get("status", "")
277
+
278
+ return {
279
+ "ok": True,
280
+ "uid": uid,
281
+ "complaint_number": complaint_number,
282
+ "status": status
283
+ }