File size: 11,359 Bytes
fefd7d4
41f2352
fefd7d4
a42f9f6
ff24891
fefd7d4
ff24891
41f2352
 
 
 
 
 
 
 
 
f1b8b54
e2defd2
41f2352
f1b8b54
 
 
41f2352
 
 
 
 
 
806a675
41f2352
 
 
 
 
dfd20db
21725a9
6b6dd1e
ff24891
 
 
 
 
 
fa969c2
 
41f2352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f1b8b54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41f2352
 
 
 
 
 
 
 
 
 
 
f1b8b54
41f2352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2a7019
 
f703380
 
 
 
b2a7019
 
 
 
 
 
f703380
b2a7019
 
 
f703380
b2a7019
 
 
f703380
 
 
 
 
 
 
 
 
 
 
b2a7019
 
 
f703380
 
b2a7019
f703380
b2a7019
 
 
 
 
 
 
 
 
1a36746
 
 
 
 
41f2352
 
 
 
 
1a36746
41f2352
 
 
1a36746
41f2352
 
 
 
 
 
 
 
 
 
b2a7019
 
 
41f2352
 
 
b2a7019
41f2352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
055d179
41f2352
 
 
 
 
 
 
 
 
f1b8b54
 
41f2352
 
 
 
055d179
41f2352
 
 
f1b8b54
 
 
 
 
 
 
 
41f2352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1a36746
ff24891
 
fefd7d4
 
 
 
41f2352
fa969c2
437c637
 
 
 
fefd7d4
 
 
 
 
 
 
 
 
 
 
41f2352
fefd7d4
 
 
 
 
 
 
fa969c2
b6f476e
fefd7d4
 
ff24891
fefd7d4
 
 
 
 
 
 
 
b2a7019
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
import json
import re
import requests
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

# =========================
# Config
# =========================

GREENAPI_INSTANCE_ID = "7105210836"
GREENAPI_TOKEN = "805b69f6c85d4e6caea0edaba692b889abecc9e6bb8b457e8f"

GREENAPI_BASE = f"https://7105.api.greenapi.com/waInstance{GREENAPI_INSTANCE_ID}"

# Interactive buttons endpoint
GREENAPI_SEND_URL_BUTTON_URL = f"{GREENAPI_BASE}/sendInteractiveButtons/{GREENAPI_TOKEN}"

# Interactive REPLY buttons endpoint
GREENAPI_SEND_REPLY_BUTTONS_URL = f"{GREENAPI_BASE}/sendInteractiveButtonsReply/{GREENAPI_TOKEN}"

# Plain text endpoint (لو عندك endpoint مختلف في GreenAPI غيّره هنا)
GREENAPI_SEND_TEXT_URL = (
    f"https://7105.api.greenapi.com/"
    f"waInstance{GREENAPI_INSTANCE_ID}/sendMessage/{GREENAPI_TOKEN}"
)

COMPLAINT_FORM_URL = "https://mrhelp92.github.io/Complaint-form/"  # <-- حط لينك فورم الشكوى الحقيقي
AI_BOT_URL = "https://mr-help-adkbase.hf.space/processtext"  # <-- endpoint بتاع البوت

TIMEOUT_SEC = 60

# (لو عايز تفضل GAS زي ما هو موجود)
GAS_WEBAPP_URL = "https://script.google.com/macros/s/AKfycbxFLlPzSvXM6xpVXR3-BjoK1lkNjYb8o1pctXI4Q2NZvv6wqKnIaSQA6hhL5lP_TQbTgQ/exec"

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=False,
    allow_methods=["POST", "OPTIONS"],
    allow_headers=["*"],
)

# =========================
# Intent detection (بسيط)
# =========================

GREET_WORDS = [
    "اهلا", "أهلا", "مرحبا", "هاي", "hello", "hi",
    "السلام عليكم", "سلام عليكم", "مساء الخير", "صباح الخير",
    "مساء الفل", "صباح الفل"
]

COMPLAINT_WORDS = [
    "شكوى", "شكاوي", "اشتكي", "مشكلة", "مشكلتي", "تذمر", "زعلان",
    "سيء", "خدمة سيئة", "عايز أشتكي", "عايز اشتكي", "complaint"
]

def detect_intent_simple(text: str) -> str:
    t = (text or "").strip().lower()

    if any(w in t for w in [x.lower() for x in GREET_WORDS]):
        return "GREETING"

    if any(w in t for w in [x.lower() for x in COMPLAINT_WORDS]):
        return "COMPLAINT"

    return "OTHER"

# =========================
# GreenAPI send helpers
# =========================

def send_url_button(chat_id: str, header: str, body: str, footer: str, url: str, button_text: str = "تسجيل الشكوى"):
    payload = {
        "chatId": chat_id,
        "body": body,
        "header": header,
        "footer": footer,
        "buttons": [
            {
                "type": "url",
                "buttonId": "1",
                "buttonText": button_text,
                "url": url
            }
        ]
    }
    resp = requests.post(GREENAPI_SEND_URL_BUTTON_URL, json=payload, timeout=TIMEOUT_SEC)
    return resp.status_code, (resp.text or "")[:1500]


def send_reply_buttons(chat_id: str, header: str, body: str, footer: str, buttons: list):
    """
    buttons example:
      [{"buttonId":"1","buttonText":"..."}, ...]
    """
    payload = {
        "chatId": chat_id,
        "body": body,
        "header": header,
        "footer": footer,
        "buttons": buttons
    }
    resp = requests.post(GREENAPI_SEND_REPLY_BUTTONS_URL, json=payload, timeout=TIMEOUT_SEC)
    return resp.status_code, (resp.text or "")[:1500]

def send_text_message(chat_id: str, message: str):
    payload = {
        "chatId": chat_id,
        "message": message
    }
    headers = {
        "Content-Type": "application/json"
    }

    resp = requests.post(
        GREENAPI_SEND_TEXT_URL,
        json=payload,
        headers=headers,
        timeout=TIMEOUT_SEC
    )

    return resp.status_code, (resp.text or "")[:1500]

# =========================
# AI bot call
# =========================

def call_ai_bot(message: str):
    payload = {"message": message}
    resp = requests.post(AI_BOT_URL, json=payload, timeout=TIMEOUT_SEC)
    resp.raise_for_status()
    try:
        return resp.json()
    except Exception:
        return {"ok": False, "raw": resp.text}

# =========================
# Webhook receiver
# =========================

def extract_text_message(msg_data: dict) -> str:
    """
    يرجّع نص المستخدم سواء كان:
    - textMessage
    - extendedTextMessage
    - interactiveButtonsResponse (ضغط زرار)
    """
    if not isinstance(msg_data, dict):
        return ""

    t = (msg_data.get("typeMessage") or "").strip()

    # 1) رسالة نصية عادية
    if t == "textMessage":
        return ((msg_data.get("textMessageData") or {}).get("textMessage") or "").strip()

    # 2) رسالة نصية ممتدة (شائع)
    if t == "extendedTextMessage":
        return ((msg_data.get("extendedTextMessageData") or {}).get("text") or "").strip()

    # 3) ضغط زرار (Interactive Buttons Response)
    if t == "interactiveButtonsResponse":
        r = msg_data.get("interactiveButtonsResponse") or {}
        # أهم حاجة: selectedDisplayText (نص الزرار)
        txt = (r.get("selectedDisplayText") or "").strip()
        if txt:
            return txt
        # fallback: selectedId
        return (r.get("selectedId") or "").strip()

    # fallback عام
    for path in [
        ("textMessageData", "textMessage"),
        ("extendedTextMessageData", "text"),
        ("interactiveButtonsResponse", "selectedDisplayText"),
        ("interactiveButtonsResponse", "selectedId"),
        ("text",),
        ("message",),
    ]:
        cur = msg_data
        for k in path:
            cur = cur.get(k) if isinstance(cur, dict) else None
        if isinstance(cur, str) and cur.strip():
            return cur.strip()

    return ""

@app.post("/recieve")
async def webhook_receiver(req: Request):
    try:
        body = await req.json()
    except Exception:
        raw = await req.body()
        print("===== RECEIVED RAW =====")
        print(raw)
        print("========================")
        return {"ok": True}

    print("===== RECEIVED JSON =====")
    print(json.dumps(body, ensure_ascii=False, indent=2))
    print("=========================")

    # 1) فلتر نوع الويبهوك
    if body.get("typeWebhook") != "incomingMessageReceived":
        return {"ok": True, "ignored": True}

    # 2) استخرج wid + chatId + الرسالة
    instance_wid = (body.get("instanceData") or {}).get("wid")  # ده wid بتاع الـ instance
    sender_data = body.get("senderData") or {}
    chat_id = sender_data.get("chatId")  # ده اللي لازم تبعت له الرد
    msg_data = body.get("messageData") or {}

    type_message = (msg_data.get("typeMessage") or "")
    text_message = extract_text_message(msg_data)


    print(">>> instance_wid:", instance_wid)
    print(">>> chat_id:", chat_id)
    print(">>> typeMessage:", type_message)
    print(">>> text_message:", text_message)

    # لو مفيش chat_id أو مفيش رسالة نصية، نوقف
    if not chat_id or not text_message:
        return {"ok": True, "skipped": True}

    # 3) حدّد intent
    intent = detect_intent_simple(text_message)
    print(">>> intent:", intent)

    # 4) ردود حسب intent
    if intent == "GREETING":
        header = "أهلاً بيك في ÄDK 👋"
        body_txt = (
            "منورنا! 😊\n"
            "أنا مساعد ÄDK، تحب اساعدك ازاي؟"
        )
        footer = "ÄDK - German Courses"

        buttons = [
            {"buttonId": "1", "buttonText": "أنواع الكورسات"},
            {"buttonId": "2", "buttonText": "تحديد مستوى"},
            {"buttonId": "3", "buttonText": "تواصل معنا"},
        ]

        status, txt = send_reply_buttons(chat_id, header, body_txt, footer, buttons)

        print(">>> sent GREETING buttons:", status, txt)
        return {"ok": True, "intent": intent}

    if intent == "COMPLAINT":
        header = "احنا هنا عشان نساعدك 🤝"
        body_txt = "يسعدنا تواصلك. سجل الشكوى من الرابط ده، وفريقنا هيتواصل معاك في أقرب وقت."
        footer = "شكراً لاختيار ÄDK"

        status, txt = send_url_button(
            chat_id=chat_id,
            header=header,
            body=body_txt,
            footer=footer,
            url=COMPLAINT_FORM_URL,
            button_text="تسجيل الشكوى"
        )

        print(">>> sent COMPLAINT url button:", status, txt)
        return {"ok": True, "intent": intent}

    # 5) غير كده → نبعته للـ AI bot
    try:
        bot_res = call_ai_bot(text_message)
        # نتوقع {ok, intent, reply}
        reply = ""
        if isinstance(bot_res, dict):
            reply = (bot_res.get("reply") or "").strip()
        if not reply:
            reply = "تمام — وصلت رسالتك. ممكن توضّحلي قصدك أكتر؟"

        status, txt = send_text_message(chat_id, reply)
        print(">>> sent AI reply:", status, txt)

        return {"ok": True, "intent": "AI", "bot_intent": (bot_res.get("intent") if isinstance(bot_res, dict) else None)}

    except Exception as e:
        print("❌ AI bot failed:", str(e))
        # fallback لطيف
        status, txt = send_text_message(chat_id, "تمام — حصلت مشكلة بسيطة. ممكن تعيد إرسال رسالتك تاني؟")
        print(">>> sent fallback:", status, txt)
        return JSONResponse(status_code=200, content={"ok": True, "intent": "AI_FAILED", "error": str(e)})

# =========================
# Complaints proxy (زي ما هو عندك)
# =========================

@app.post("/complaints")
async def complaints_proxy(req: Request):
    try:
        payload = await req.json()
    except Exception as e:
        print("❌ Failed to parse incoming JSON:", str(e))
        return JSONResponse(status_code=400, content={"ok": False, "error": "invalid json body"})

    print("========== INCOMING PAYLOAD ==========")
    print(json.dumps(payload, ensure_ascii=False, indent=2))
    print("======================================")

    try:
        gas_resp = requests.post(
            GAS_WEBAPP_URL,
            json=payload,
            headers={"Content-Type": "application/json"},
            timeout=60
        )
    except Exception as e:
        print("❌ ERROR calling GAS:", repr(e))
        return JSONResponse(
            status_code=502,
            content={"ok": False, "error": "failed to reach GAS", "details": str(e)}
        )

    print("========== GAS STATUS ==========")
    print(gas_resp.status_code)
    print("========== GAS RAW TEXT (first 1500 chars) ==========")
    print((gas_resp.text or "")[:1500])
    print("=====================================================")

    try:
        gas_json = gas_resp.json()
        return JSONResponse(status_code=200, content=gas_json)
    except Exception:
        return JSONResponse(
            status_code=200,
            content={
                "ok": False,
                "error": "GAS returned non-JSON",
                "gas_status": gas_resp.status_code,
                "raw": (gas_resp.text or "")[:1500]
            }
        )