Mr-Help commited on
Commit
48aaf31
·
verified ·
1 Parent(s): 9217f8f

Update main.py

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