File size: 15,377 Bytes
02fbdc7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
from engine.states import (
    START,
    WAITING_MAIN_MENU,
    WAITING_USER_TYPE,
    WAITING_AUDIENCE,
    WAITING_PRIOR_STUDY,
    WAITING_BEGINNER_SCHEDULE_CHOICE,
    WAITING_PLACEMENT_TEST_CONFIRMATION,
    WAITING_CURRENT_STUDENT_ACTION,
    WAITING_SUPPORT_QUESTION,
    WAITING_LEVEL_SELECTION,
    WAITING_PAYMENT_METHOD,
    WAITING_COMPLAINT_FORM,
    HANDOFF_DONE,
)

from knowledge.replies import (
    greeting_main_menu_reply,
    ask_new_or_current_reply,
    ask_audience_reply,
    ask_prior_study_reply,
    beginner_schedule_reply,
    placement_test_reply,
    current_student_menu_reply,
    support_question_reply,
    ask_level_selection_reply,
    payment_methods_reply,
    unknown_reply,
)

from handoff.sales import create_sales_handoff_result
from handoff.support import create_support_handoff_result
from handoff.complaints import create_complaint_flow_result


def normalize_text(text: str) -> str:
    return (text or "").strip().lower()


def contains_any(text: str, keywords: list) -> bool:
    return any(k in text for k in keywords)


def is_yes(text: str) -> bool:
    t = normalize_text(text)
    return t in [
        "نعم", "اه", "أه", "ايوه", "أيوه", "yes", "y",
        "درست", "اه درست", "أيوه درست"
    ]


def is_no(text: str) -> bool:
    t = normalize_text(text)
    return t in [
        "لا", "لأ", "لاا", "no", "n",
        "مدرستش", "ما درستش", "لا مدرستش"
    ]


def is_new_student(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "طالب جديد", "جديد", "عميل جديد", "اول مرة", "أول مرة",
        "لسه جديد", "مشترك جديد"
    ])


def is_current_student(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "طالب حالي", "حالي", "عميل حالي", "مشترك", "مشترك حالي",
        "أنا طالب", "انا طالب عندكم", "انا مشترك"
    ])


def is_adults(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "كبار", "adult", "adults", "الكبار"
    ])


def is_children(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "اطفال", "أطفال", "طفل", "children", "kids"
    ])


def is_support_request(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "استفسار", "سؤال", "عندي سؤال", "مشكلة", "مش فاهم",
        "عايز اسأل", "عايزة اسأل", "محتاج مساعدة", "محتاجه مساعدة",
        "support", "خدمة العملاء"
    ])


def is_next_level_booking(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "حجز", "احجز", "المستوى التالي", "مستوى تالي",
        "next level", "احجز المستوى", "حجز مستوى"
    ])


def detect_level(text: str):
    t = normalize_text(text)

    if contains_any(t, ["1a", "a1", "a1.1", "1 a"]):
        return "1A"

    if contains_any(t, ["2a", "a2", "a1.2", "2 a"]):
        return "2A"

    if contains_any(t, ["1b", "b1", "b1.1", "1 b"]):
        return "1B"

    if contains_any(t, ["1c", "2b", "b2", "1c2/b", "1 c", "2 b"]):
        return "1C2/B"

    return None


def detect_payment_method(text: str):
    t = normalize_text(text)

    if contains_any(t, ["فرع", "فروع", "كاش", "cash"]):
        return "branch_or_cash"

    if contains_any(t, ["تحويل", "بنكي", "bank", "transfer"]):
        return "bank_transfer"

    if contains_any(t, ["فودافون", "vodafone", "vodafone cash"]):
        return "vodafone_cash"

    if contains_any(t, ["فيزا", "visa", "ماستر", "master", "credit card", "card"]):
        return "card"

    if contains_any(t, ["تقسيط", "value", "فاليو"]):
        return "installments"

    return None


def is_complaint(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "شكوى", "اشتكي", "اشتك", "مشكلة كبيرة", "complaint"
    ])


def wants_direct_support(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "تواصل", "اكلم", "عايز حد يكلمني", "عايزة حد يكلمني",
        "عايز اكلم خدمة العملاء", "عايزة اكلم خدمة العملاء"
    ])


def wants_start(text: str) -> bool:
    t = normalize_text(text)
    return contains_any(t, [
        "ابدأ", "ابدا", "مساعدة", "مساعده", "start", "menu", "القائمة"
    ])


def route_message(state: str, text: str, session: dict):
    t = normalize_text(text)
    flow_data = session.get("flow_data", {}) or {}

    # =========================
    # START
    # =========================
    if state == START:
        return greeting_main_menu_reply(flow_data)

    # =========================
    # WAITING_MAIN_MENU
    # =========================
    if state == WAITING_MAIN_MENU:
        if is_complaint(t):
            return create_complaint_flow_result(flow_data)

        if wants_direct_support(t):
            return create_support_handoff_result(
                flow_data=flow_data,
                summary="طلب تواصل مباشر مع خدمة العملاء"
            )

        if wants_start(t):
            return ask_new_or_current_reply(flow_data)

        # لو العميل دخل مباشرة بنية واضحة
        if is_new_student(t):
            flow_data["customer_type"] = "new"
            return ask_audience_reply(flow_data)

        if is_current_student(t):
            flow_data["customer_type"] = "current"
            return current_student_menu_reply(flow_data)

        return ask_new_or_current_reply(flow_data)

    # =========================
    # WAITING_USER_TYPE
    # =========================
    if state == WAITING_USER_TYPE:
        if is_new_student(t):
            flow_data["customer_type"] = "new"
            return ask_audience_reply(flow_data)

        if is_current_student(t):
            flow_data["customer_type"] = "current"
            return current_student_menu_reply(flow_data)

        if is_complaint(t):
            return create_complaint_flow_result(flow_data)

        return {
            "next_state": WAITING_USER_TYPE,
            "flow_data": flow_data,
            "reply": "تمام، محتاج أعرف الأول: إنت طالب جديد ولا طالب حالي؟"
        }

    # =========================
    # WAITING_AUDIENCE
    # =========================
    if state == WAITING_AUDIENCE:
        if is_adults(t):
            flow_data["audience"] = "adults"
            return ask_prior_study_reply(flow_data)

        if is_children(t):
            flow_data["audience"] = "children"
            return ask_prior_study_reply(flow_data)

        return {
            "next_state": WAITING_AUDIENCE,
            "flow_data": flow_data,
            "reply": "تمام، الكورس للكبار ولا للأطفال؟"
        }

    # =========================
    # WAITING_PRIOR_STUDY
    # =========================
    if state == WAITING_PRIOR_STUDY:
        if is_no(t):
            flow_data["prior_study"] = False
            return beginner_schedule_reply(flow_data)

        if is_yes(t):
            flow_data["prior_study"] = True
            return placement_test_reply(flow_data)

        return {
            "next_state": WAITING_PRIOR_STUDY,
            "flow_data": flow_data,
            "reply": "هل درست اللغة الألمانية قبل كده؟ رد بنعم أو لا."
        }

    # =========================
    # WAITING_BEGINNER_SCHEDULE_CHOICE
    # =========================
    if state == WAITING_BEGINNER_SCHEDULE_CHOICE:
        if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
            return payment_methods_reply(flow_data)

        if contains_any(t, ["عايز احجز", "عايزة احجز", "احجز", "حجز", "اشترك", "اشتراك"]):
            return payment_methods_reply(flow_data)

        if contains_any(t, ["محتاج مساعدة", "محتاجه مساعدة", "استفسار", "سؤال"]):
            return create_support_handoff_result(
                flow_data=flow_data,
                summary="استفسار عن مواعيد كورسات المبتدئين"
            )

        return {
            "next_state": WAITING_BEGINNER_SCHEDULE_CHOICE,
            "flow_data": flow_data,
            "reply": "بعد ما تختار المعاد المناسب، ابعتلي تم أو قولّي تحب نكمّل الحجز."
        }

    # =========================
    # WAITING_PLACEMENT_TEST_CONFIRMATION
    # =========================
    if state == WAITING_PLACEMENT_TEST_CONFIRMATION:
        if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
            return create_sales_handoff_result(
                flow_data=flow_data,
                summary="عميل اختار أو مهتم باختبار تحديد مستوى"
            )

        if contains_any(t, ["تفاصيل", "اعرف اكتر", "محتاج مساعدة", "مساعدة", "استفسار"]):
            return create_support_handoff_result(
                flow_data=flow_data,
                summary="استفسار عن اختبار تحديد المستوى"
            )

        return {
            "next_state": WAITING_PLACEMENT_TEST_CONFIRMATION,
            "flow_data": flow_data,
            "reply": "بعد ما تراجع مواعيد اختبار تحديد المستوى، ابعتلي تم أو قولّي لو محتاج مساعدة."
        }

    # =========================
    # WAITING_CURRENT_STUDENT_ACTION
    # =========================
    if state == WAITING_CURRENT_STUDENT_ACTION:
        if is_support_request(t):
            return support_question_reply(flow_data)

        if is_next_level_booking(t):
            return ask_level_selection_reply(flow_data)

        if is_complaint(t):
            return create_complaint_flow_result(flow_data)

        return {
            "next_state": WAITING_CURRENT_STUDENT_ACTION,
            "flow_data": flow_data,
            "reply": "تحب استفسار بخصوص الكورس ولا حجز المستوى التالي؟"
        }

    # =========================
    # WAITING_SUPPORT_QUESTION
    # =========================
    if state == WAITING_SUPPORT_QUESTION:
        flow_data["support_question"] = text

        return create_support_handoff_result(
            flow_data=flow_data,
            summary=f"استفسار طالب حالي: {text}"
        )

    # =========================
    # WAITING_LEVEL_SELECTION
    # =========================
    if state == WAITING_LEVEL_SELECTION:
        level = detect_level(t)

        if level:
            flow_data["selected_level"] = level
            return payment_methods_reply(flow_data)

        if contains_any(t, ["مش عارف", "مش متأكد", "مش متاكدة", "محتاج مساعدة", "استفسار"]):
            return create_support_handoff_result(
                flow_data=flow_data,
                summary="طالب حالي محتاج مساعدة في اختيار المستوى التالي"
            )

        return {
            "next_state": WAITING_LEVEL_SELECTION,
            "flow_data": flow_data,
            "reply": "قولّي المستوى اللي تحب تحجزه: 1A أو 2A أو 1B أو 1C2/B."
        }

    # =========================
    # WAITING_PAYMENT_METHOD
    # =========================
    if state == WAITING_PAYMENT_METHOD:
        payment_method = detect_payment_method(t)

        if payment_method:
            flow_data["payment_method"] = payment_method

            payment_summaries = {
                "branch_or_cash": "عميل جاهز للحجز واختار الدفع كاش أو في الفرع",
                "bank_transfer": "عميل جاهز للحجز واختار تحويل بنكي",
                "vodafone_cash": "عميل جاهز للحجز واختار Vodafone Cash",
                "card": "عميل جاهز للحجز واختار الدفع بالبطاقة",
                "installments": "عميل مهتم بالتقسيط",
            }

            return create_sales_handoff_result(
                flow_data=flow_data,
                summary=payment_summaries[payment_method]
            )

        if contains_any(t, ["استفسار", "سؤال", "محتاج مساعدة", "محتاجه مساعدة"]):
            return create_support_handoff_result(
                flow_data=flow_data,
                summary="استفسار عن طرق الدفع أو إتمام الحجز"
            )

        return {
            "next_state": WAITING_PAYMENT_METHOD,
            "flow_data": flow_data,
            "reply": "اختار طريقة الدفع المناسبة: الفرع / تحويل بنكي / Vodafone Cash / Visa / تقسيط Value."
        }

    # =========================
    # WAITING_COMPLAINT_FORM
    # =========================
    if state == WAITING_COMPLAINT_FORM:
        if contains_any(t, ["تم", "خلصت", "سجلت", "قدمت", "بعت"]):
            return {
                "next_state": WAITING_COMPLAINT_FORM,
                "flow_data": flow_data,
                "reply": "تمام، بعد مراجعة الشكوى هيتم التواصل معاك. ولو حابب تكتب أي تفاصيل إضافية ابعتها هنا."
            }

        return {
            "next_state": WAITING_COMPLAINT_FORM,
            "flow_data": flow_data,
            "reply": "سجّل الشكوى من الفورم أولًا، ولو احتجت مساعدة ابعتلي."
        }

    # =========================
    # HANDOFF_DONE
    # =========================
    if state == HANDOFF_DONE:
        if is_complaint(t):
            return create_complaint_flow_result(flow_data)

        if is_support_request(t):
            return create_support_handoff_result(
                flow_data=flow_data,
                summary=f"استفسار إضافي بعد handoff: {text}"
            )

        if contains_any(t, ["شكرا", "متشكر", "تسلم", "ميرسي"]):
            return {
                "next_state": HANDOFF_DONE,
                "flow_data": flow_data,
                "reply": "العفو، تحت أمرك في أي وقت."
            }

        return {
            "next_state": HANDOFF_DONE,
            "flow_data": flow_data,
            "reply": "تم تسجيل طلبك بالفعل، وفريقنا هيتواصل معاك قريب. لو عندك استفسار إضافي ابعته هنا."
        }

    return unknown_reply(flow_data)