Mr-Help commited on
Commit
92087d5
·
verified ·
1 Parent(s): e995f68

Update engine/flow_router.py

Browse files
Files changed (1) hide show
  1. engine/flow_router.py +488 -434
engine/flow_router.py CHANGED
@@ -1,435 +1,489 @@
1
- from engine.states import (
2
- START,
3
- WAITING_MAIN_MENU,
4
- WAITING_USER_TYPE,
5
- WAITING_AUDIENCE,
6
- WAITING_PRIOR_STUDY,
7
- WAITING_BEGINNER_SCHEDULE_CHOICE,
8
- WAITING_PLACEMENT_TEST_CONFIRMATION,
9
- WAITING_CURRENT_STUDENT_ACTION,
10
- WAITING_SUPPORT_QUESTION,
11
- WAITING_LEVEL_SELECTION,
12
- WAITING_PAYMENT_METHOD,
13
- WAITING_COMPLAINT_FORM,
14
- HANDOFF_DONE,
15
- )
16
-
17
- from knowledge.replies import (
18
- greeting_main_menu_reply,
19
- ask_new_or_current_reply,
20
- ask_audience_reply,
21
- ask_prior_study_reply,
22
- beginner_schedule_reply,
23
- placement_test_reply,
24
- current_student_menu_reply,
25
- support_question_reply,
26
- ask_level_selection_reply,
27
- payment_methods_reply,
28
- unknown_reply,
29
- )
30
-
31
- from handoff.sales import create_sales_handoff_result
32
- from handoff.support import create_support_handoff_result
33
- from handoff.complaints import create_complaint_flow_result
34
-
35
-
36
- def normalize_text(text: str) -> str:
37
- return (text or "").strip().lower()
38
-
39
-
40
- def contains_any(text: str, keywords: list) -> bool:
41
- return any(k in text for k in keywords)
42
-
43
-
44
- def is_yes(text: str) -> bool:
45
- t = normalize_text(text)
46
- return t in [
47
- "نعم", "اه", "أه", "ايوه", "أيوه", "yes", "y",
48
- "درست", "اه درست", "أيوه درست"
49
- ]
50
-
51
-
52
- def is_no(text: str) -> bool:
53
- t = normalize_text(text)
54
- return t in [
55
- "لا", "لأ", "لاا", "no", "n",
56
- "مدرستش", "ما درستش", "لا مدرستش"
57
- ]
58
-
59
-
60
- def is_new_student(text: str) -> bool:
61
- t = normalize_text(text)
62
- return contains_any(t, [
63
- "طالب جديد", "جديد", "عميل جديد", "اول مرة", "أول مرة",
64
- "لسه جديد", "مشترك جديد"
65
- ])
66
-
67
-
68
- def is_current_student(text: str) -> bool:
69
- t = normalize_text(text)
70
- return contains_any(t, [
71
- "طالب حالي", "حالي", "عميل حالي", "مشترك", "مشترك حالي",
72
- "أنا طالب", "انا طالب عندكم", "انا مشترك"
73
- ])
74
-
75
-
76
- def is_adults(text: str) -> bool:
77
- t = normalize_text(text)
78
- return contains_any(t, [
79
- "كبار", "adult", "adults", "الكبار"
80
- ])
81
-
82
-
83
- def is_children(text: str) -> bool:
84
- t = normalize_text(text)
85
- return contains_any(t, [
86
- "اطفال", "أطفال", "طفل", "children", "kids"
87
- ])
88
-
89
-
90
- def is_support_request(text: str) -> bool:
91
- t = normalize_text(text)
92
- return contains_any(t, [
93
- "استفسار", "سؤال", "عندي سؤال", "مشكلة", "مش فاهم",
94
- "عايز اسأل", "عايزة اسأل", "محتاج مساعدة", "محتاجه مساعدة",
95
- "support", "خدمة العملاء"
96
- ])
97
-
98
-
99
- def is_next_level_booking(text: str) -> bool:
100
- t = normalize_text(text)
101
- return contains_any(t, [
102
- "حجز", "احجز", "المستوى التالي", "مستوى تالي",
103
- "next level", "احجز المستوى", "حجز مستوى"
104
- ])
105
-
106
-
107
- def detect_level(text: str):
108
- t = normalize_text(text)
109
-
110
- if contains_any(t, ["1a", "a1", "a1.1", "1 a"]):
111
- return "1A"
112
-
113
- if contains_any(t, ["2a", "a2", "a1.2", "2 a"]):
114
- return "2A"
115
-
116
- if contains_any(t, ["1b", "b1", "b1.1", "1 b"]):
117
- return "1B"
118
-
119
- if contains_any(t, ["1c", "2b", "b2", "1c2/b", "1 c", "2 b"]):
120
- return "1C2/B"
121
-
122
- return None
123
-
124
-
125
- def detect_payment_method(text: str):
126
- t = normalize_text(text)
127
-
128
- if contains_any(t, ["فرع", "فروع", "كاش", "cash"]):
129
- return "branch_or_cash"
130
-
131
- if contains_any(t, ["تحويل", "بنكي", "bank", "transfer"]):
132
- return "bank_transfer"
133
-
134
- if contains_any(t, ["فودافون", "vodafone", "vodafone cash"]):
135
- return "vodafone_cash"
136
-
137
- if contains_any(t, ["فيزا", "visa", "ماستر", "master", "credit card", "card"]):
138
- return "card"
139
-
140
- if contains_any(t, ["تقسيط", "value", "فاليو"]):
141
- return "installments"
142
-
143
- return None
144
-
145
-
146
- def is_complaint(text: str) -> bool:
147
- t = normalize_text(text)
148
- return contains_any(t, [
149
- "شكوى", "اشتكي", "اشتك", "مشكلة كبيرة", "complaint"
150
- ])
151
-
152
-
153
- def wants_direct_support(text: str) -> bool:
154
- t = normalize_text(text)
155
- return contains_any(t, [
156
- "تواصل", "اكلم", "عايز حد يكلمني", "عايزة حد يكلمني",
157
- "عايز اكلم خدمة العملاء", "عايزة اكلم خدمة العملاء"
158
- ])
159
-
160
-
161
- def wants_start(text: str) -> bool:
162
- t = normalize_text(text)
163
- return contains_any(t, [
164
- "ابدأ", "ابدا", "مساعدة", "مساعده", "start", "menu", "القائمة"
165
- ])
166
-
167
-
168
- def route_message(state: str, text: str, session: dict):
169
- t = normalize_text(text)
170
- flow_data = session.get("flow_data", {}) or {}
171
-
172
- # =========================
173
- # START
174
- # =========================
175
- if state == START:
176
- return greeting_main_menu_reply(flow_data)
177
-
178
- # =========================
179
- # WAITING_MAIN_MENU
180
- # =========================
181
- if state == WAITING_MAIN_MENU:
182
- if is_complaint(t):
183
- return create_complaint_flow_result(flow_data)
184
-
185
- if wants_direct_support(t):
186
- return create_support_handoff_result(
187
- flow_data=flow_data,
188
- summary="طلب تواصل مباشر مع خدمة العملاء"
189
- )
190
-
191
- if wants_start(t):
192
- return ask_new_or_current_reply(flow_data)
193
-
194
- # لو العميل دخل مباشرة بنية واضحة
195
- if is_new_student(t):
196
- flow_data["customer_type"] = "new"
197
- return ask_audience_reply(flow_data)
198
-
199
- if is_current_student(t):
200
- flow_data["customer_type"] = "current"
201
- return current_student_menu_reply(flow_data)
202
-
203
- return ask_new_or_current_reply(flow_data)
204
-
205
- # =========================
206
- # WAITING_USER_TYPE
207
- # =========================
208
- if state == WAITING_USER_TYPE:
209
- if is_new_student(t):
210
- flow_data["customer_type"] = "new"
211
- return ask_audience_reply(flow_data)
212
-
213
- if is_current_student(t):
214
- flow_data["customer_type"] = "current"
215
- return current_student_menu_reply(flow_data)
216
-
217
- if is_complaint(t):
218
- return create_complaint_flow_result(flow_data)
219
-
220
- return {
221
- "next_state": WAITING_USER_TYPE,
222
- "flow_data": flow_data,
223
- "reply": "تمام، محتاج أعرف الأول: إنت طالب جديد ولا طالب حالي؟"
224
- }
225
-
226
- # =========================
227
- # WAITING_AUDIENCE
228
- # =========================
229
- if state == WAITING_AUDIENCE:
230
- if is_adults(t):
231
- flow_data["audience"] = "adults"
232
- return ask_prior_study_reply(flow_data)
233
-
234
- if is_children(t):
235
- flow_data["audience"] = "children"
236
- return ask_prior_study_reply(flow_data)
237
-
238
- return {
239
- "next_state": WAITING_AUDIENCE,
240
- "flow_data": flow_data,
241
- "reply": "تمام، الكورس للكبار ولا للأطفال؟"
242
- }
243
-
244
- # =========================
245
- # WAITING_PRIOR_STUDY
246
- # =========================
247
- if state == WAITING_PRIOR_STUDY:
248
- if is_no(t):
249
- flow_data["prior_study"] = False
250
- return beginner_schedule_reply(flow_data)
251
-
252
- if is_yes(t):
253
- flow_data["prior_study"] = True
254
- return placement_test_reply(flow_data)
255
-
256
- return {
257
- "next_state": WAITING_PRIOR_STUDY,
258
- "flow_data": flow_data,
259
- "reply": "هل درست اللغة الألمانية قبل كده؟ رد بنعم أو لا."
260
- }
261
-
262
- # =========================
263
- # WAITING_BEGINNER_SCHEDULE_CHOICE
264
- # =========================
265
- if state == WAITING_BEGINNER_SCHEDULE_CHOICE:
266
- if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
267
- return payment_methods_reply(flow_data)
268
-
269
- if contains_any(t, ["عايز احجز", "عايزة احجز", "احجز", "حجز", "اشترك", "اشتراك"]):
270
- return payment_methods_reply(flow_data)
271
-
272
- if contains_any(t, ["محتاج مساعدة", "محتاجه مساعدة", "استفسار", "سؤال"]):
273
- return create_support_handoff_result(
274
- flow_data=flow_data,
275
- summary="استفسار عن مواعيد كورسات المبتدئين"
276
- )
277
-
278
- return {
279
- "next_state": WAITING_BEGINNER_SCHEDULE_CHOICE,
280
- "flow_data": flow_data,
281
- "reply": "بعد ما تختار المعاد المناسب، ابعتلي تم أو قولّي تحب نكمّل الحجز."
282
- }
283
-
284
- # =========================
285
- # WAITING_PLACEMENT_TEST_CONFIRMATION
286
- # =========================
287
- if state == WAITING_PLACEMENT_TEST_CONFIRMATION:
288
- if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
289
- return create_sales_handoff_result(
290
- flow_data=flow_data,
291
- summary="عميل اختار أو مهتم باختبار تحديد مستوى"
292
- )
293
-
294
- if contains_any(t, ["تفاصيل", "اعرف اكتر", "محتاج مساعدة", "مساعدة", "استفسار"]):
295
- return create_support_handoff_result(
296
- flow_data=flow_data,
297
- summary="استفسار عن اختبار تحديد المستوى"
298
- )
299
-
300
- return {
301
- "next_state": WAITING_PLACEMENT_TEST_CONFIRMATION,
302
- "flow_data": flow_data,
303
- "reply": "بعد ما تراجع مواعيد اختبار تحديد المستوى، ابعتلي تم أو قولّي لو محتاج مساعدة."
304
- }
305
-
306
- # =========================
307
- # WAITING_CURRENT_STUDENT_ACTION
308
- # =========================
309
- if state == WAITING_CURRENT_STUDENT_ACTION:
310
- if is_support_request(t):
311
- return support_question_reply(flow_data)
312
-
313
- if is_next_level_booking(t):
314
- return ask_level_selection_reply(flow_data)
315
-
316
- if is_complaint(t):
317
- return create_complaint_flow_result(flow_data)
318
-
319
- return {
320
- "next_state": WAITING_CURRENT_STUDENT_ACTION,
321
- "flow_data": flow_data,
322
- "reply": "تحب استفسار بخصوص الكورس ولا حجز المستوى التالي؟"
323
- }
324
-
325
- # =========================
326
- # WAITING_SUPPORT_QUESTION
327
- # =========================
328
- if state == WAITING_SUPPORT_QUESTION:
329
- flow_data["support_question"] = text
330
-
331
- return create_support_handoff_result(
332
- flow_data=flow_data,
333
- summary=f"استفسار طالب حالي: {text}"
334
- )
335
-
336
- # =========================
337
- # WAITING_LEVEL_SELECTION
338
- # =========================
339
- if state == WAITING_LEVEL_SELECTION:
340
- level = detect_level(t)
341
-
342
- if level:
343
- flow_data["selected_level"] = level
344
- return payment_methods_reply(flow_data)
345
-
346
- if contains_any(t, ["مش عارف", "مش متأكد", "مش متاكدة", "محتاج مساعدة", "استفسار"]):
347
- return create_support_handoff_result(
348
- flow_data=flow_data,
349
- summary="طالب حالي محتاج مساعدة في اختيار المستوى التالي"
350
- )
351
-
352
- return {
353
- "next_state": WAITING_LEVEL_SELECTION,
354
- "flow_data": flow_data,
355
- "reply": "قولّي المستوى اللي تحب تحجزه: 1A أو 2A أو 1B أو 1C2/B."
356
- }
357
-
358
- # =========================
359
- # WAITING_PAYMENT_METHOD
360
- # =========================
361
- if state == WAITING_PAYMENT_METHOD:
362
- payment_method = detect_payment_method(t)
363
-
364
- if payment_method:
365
- flow_data["payment_method"] = payment_method
366
-
367
- payment_summaries = {
368
- "branch_or_cash": "عميل جاهز للحجز واختار الدفع كاش أو في الفرع",
369
- "bank_transfer": "عميل جاهز للحجز واختار تحويل بنكي",
370
- "vodafone_cash": "عميل جاهز للحجز واختار Vodafone Cash",
371
- "card": "عميل جاهز للحجز واختار الدفع بالبطاقة",
372
- "installments": "عميل مهتم بالتقسيط",
373
- }
374
-
375
- return create_sales_handoff_result(
376
- flow_data=flow_data,
377
- summary=payment_summaries[payment_method]
378
- )
379
-
380
- if contains_any(t, ["استفسار", "سؤال", "محتاج مساعدة", "محتاجه مساعدة"]):
381
- return create_support_handoff_result(
382
- flow_data=flow_data,
383
- summary="استفسار عن طرق الدفع أو إتمام الحجز"
384
- )
385
-
386
- return {
387
- "next_state": WAITING_PAYMENT_METHOD,
388
- "flow_data": flow_data,
389
- "reply": "اختار طريقة الدفع المناسبة: الفرع / تحويل بنكي / Vodafone Cash / Visa / تقسيط Value."
390
- }
391
-
392
- # =========================
393
- # WAITING_COMPLAINT_FORM
394
- # =========================
395
- if state == WAITING_COMPLAINT_FORM:
396
- if contains_any(t, ["تم", "خلصت", "سجلت", "قدمت", "بعت"]):
397
- return {
398
- "next_state": WAITING_COMPLAINT_FORM,
399
- "flow_data": flow_data,
400
- "reply": "تمام، بعد مراجعة الشكوى هيتم التواصل معاك. ولو حابب تكتب أي تفاصيل إضافية ابعتها هنا."
401
- }
402
-
403
- return {
404
- "next_state": WAITING_COMPLAINT_FORM,
405
- "flow_data": flow_data,
406
- "reply": "سجّل الشكوى من الفورم أولًا، ولو احتجت مساعدة ابعتلي."
407
- }
408
-
409
- # =========================
410
- # HANDOFF_DONE
411
- # =========================
412
- if state == HANDOFF_DONE:
413
- if is_complaint(t):
414
- return create_complaint_flow_result(flow_data)
415
-
416
- if is_support_request(t):
417
- return create_support_handoff_result(
418
- flow_data=flow_data,
419
- summary=f"استفسار إضافي بعد handoff: {text}"
420
- )
421
-
422
- if contains_any(t, ["شكرا", "متشكر", "تسلم", "ميرسي"]):
423
- return {
424
- "next_state": HANDOFF_DONE,
425
- "flow_data": flow_data,
426
- "reply": "العفو، تحت أمرك في أي وقت."
427
- }
428
-
429
- return {
430
- "next_state": HANDOFF_DONE,
431
- "flow_data": flow_data,
432
- "reply": "تم تسجيل طلبك بالفعل، وفريقنا هيتواصل معاك قريب. لو عندك استفسار إضافي ابعته هنا."
433
- }
434
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  return unknown_reply(flow_data)
 
1
+ from engine.states import (
2
+ START,
3
+ WAITING_MAIN_MENU,
4
+ WAITING_USER_TYPE,
5
+ WAITING_AUDIENCE,
6
+ WAITING_PRIOR_STUDY,
7
+ WAITING_BEGINNER_SCHEDULE_CHOICE,
8
+ WAITING_PDF_102_CONFIRMATION,
9
+ WAITING_PLACEMENT_TEST_CONFIRMATION,
10
+ WAITING_CURRENT_STUDENT_ACTION,
11
+ WAITING_SUPPORT_QUESTION,
12
+ WAITING_LEVEL_SELECTION,
13
+ WAITING_PAYMENT_METHOD,
14
+ WAITING_COMPLAINT_FORM,
15
+ HANDOFF_DONE,
16
+ )
17
+
18
+ from knowledge.replies import (
19
+ greeting_main_menu_reply,
20
+ ask_new_or_current_reply,
21
+ ask_audience_reply,
22
+ ask_prior_study_reply,
23
+ beginner_schedule_reply,
24
+ pdf_102_reply,
25
+ placement_test_reply,
26
+ current_student_menu_reply,
27
+ support_question_reply,
28
+ ask_level_selection_reply,
29
+ payment_methods_reply,
30
+ unknown_reply,
31
+ )
32
+
33
+ from handoff.sales import create_sales_handoff_result
34
+ from handoff.support import create_support_handoff_result
35
+ from handoff.complaints import create_complaint_flow_result
36
+
37
+
38
+ def normalize_text(text: str) -> str:
39
+ return (text or "").strip().lower()
40
+
41
+
42
+ def contains_any(text: str, keywords: list) -> bool:
43
+ return any(k in text for k in keywords)
44
+
45
+
46
+ def is_yes(text: str) -> bool:
47
+ t = normalize_text(text)
48
+ return t in [
49
+ "نعم", "اه", "أه", "ايوه", "أيوه", "yes", "y",
50
+ "درست", "اه درست", "أيوه درست"
51
+ ]
52
+
53
+
54
+ def is_no(text: str) -> bool:
55
+ t = normalize_text(text)
56
+ return t in [
57
+ "لا", "لأ", "لاا", "no", "n",
58
+ "مدرستش", "ما درستش", "لا مدرستش"
59
+ ]
60
+
61
+
62
+ def is_new_student(text: str) -> bool:
63
+ t = normalize_text(text)
64
+ return contains_any(t, [
65
+ "طالب جديد", "جديد", "عميل جديد", "اول مرة", "أول مرة",
66
+ "لسه جديد", "مشترك جديد"
67
+ ])
68
+
69
+
70
+ def is_current_student(text: str) -> bool:
71
+ t = normalize_text(text)
72
+ return contains_any(t, [
73
+ "طالب حالي", "حالي", "عميل حالي", "مشترك", "مشترك حالي",
74
+ "أنا طالب", "انا طالب عندكم", "انا مشترك"
75
+ ])
76
+
77
+
78
+ def is_adults(text: str) -> bool:
79
+ t = normalize_text(text)
80
+ return contains_any(t, [
81
+ "كبار", "adult", "adults", "الكبار", "كورسات الكبار"
82
+ ])
83
+
84
+
85
+ def is_children(text: str) -> bool:
86
+ t = normalize_text(text)
87
+ return contains_any(t, [
88
+ "اطفال", "أطفال", "طفل", "children", "kids", "كورسات الأطفال", "كورسات الاطفال"
89
+ ])
90
+
91
+
92
+ def is_support_request(text: str) -> bool:
93
+ t = normalize_text(text)
94
+ return contains_any(t, [
95
+ "استفسار", "سؤال", "عندي سؤال", "مشكلة", "مش فاهم",
96
+ "عايز اسأل", "عايزة اسأل", "محتاج مساعدة", "محتاجه مساعدة",
97
+ "support", "خدمة العملاء"
98
+ ])
99
+
100
+
101
+ def is_next_level_booking(text: str) -> bool:
102
+ t = normalize_text(text)
103
+ return contains_any(t, [
104
+ "حجز", "احجز", "المستوى التالي", "مستوى تالي",
105
+ "next level", "احجز المستوى", "حجز مستوى"
106
+ ])
107
+
108
+
109
+ def detect_level(text: str):
110
+ t = normalize_text(text)
111
+
112
+ if contains_any(t, ["1a", "a1", "a1.1", "1 a"]):
113
+ return "1A"
114
+
115
+ if contains_any(t, ["2a", "a2", "a1.2", "2 a"]):
116
+ return "2A"
117
+
118
+ if contains_any(t, ["1b", "b1", "b1.1", "1 b"]):
119
+ return "1B"
120
+
121
+ if contains_any(t, ["1c", "2b", "b2", "1c2/b", "1 c", "2 b"]):
122
+ return "1C2/B"
123
+
124
+ return None
125
+
126
+
127
+ def detect_payment_method(text: str):
128
+ t = normalize_text(text)
129
+
130
+ if contains_any(t, ["فرع", "فروع", "كاش", "cash"]):
131
+ return "branch_or_cash"
132
+
133
+ if contains_any(t, ["تحويل", "بنكي", "bank", "transfer"]):
134
+ return "bank_transfer"
135
+
136
+ if contains_any(t, ["فودافون", "vodafone", "vodafone cash"]):
137
+ return "vodafone_cash"
138
+
139
+ if contains_any(t, ["فيزا", "visa", "ماستر", "master", "credit card", "card"]):
140
+ return "card"
141
+
142
+ if contains_any(t, ["تقسيط", "value", "فاليو"]):
143
+ return "installments"
144
+
145
+ return None
146
+
147
+
148
+ def is_complaint(text: str) -> bool:
149
+ t = normalize_text(text)
150
+ return contains_any(t, [
151
+ "شكوى", "اشتكي", "اشتك", "مشكلة كبيرة", "complaint"
152
+ ])
153
+
154
+
155
+ def wants_direct_support(text: str) -> bool:
156
+ t = normalize_text(text)
157
+ return contains_any(t, [
158
+ "تواصل", "اكلم", "عايز حد يكلمني", "عايزة حد يكلمني",
159
+ "عايز اكلم خدمة العملاء", "عايزة اكلم خدمة العملاء"
160
+ ])
161
+
162
+
163
+ def wants_start(text: str) -> bool:
164
+ t = normalize_text(text)
165
+ return contains_any(t, [
166
+ "ابدأ", "ابدا", "مساعدة", "مساعده", "start", "menu", "القائمة"
167
+ ])
168
+
169
+
170
+ def wants_restart(text: str) -> bool:
171
+ t = normalize_text(text)
172
+ return contains_any(t, [
173
+ "من جديد", "ابدأ من جديد", "restart", "مينيو", "القائمة", "ابدأ"
174
+ ])
175
+
176
+
177
+ def wants_new_topic(text: str) -> bool:
178
+ t = normalize_text(text)
179
+ return contains_any(t, [
180
+ "عايز اسال عن حاجة تانية",
181
+ "عايزة اسال عن حاجة تانية",
182
+ "استفسار جديد",
183
+ "موضوع تاني",
184
+ "حاجة تانية"
185
+ ])
186
+
187
+
188
+ def route_message(state: str, text: str, session: dict):
189
+ t = normalize_text(text)
190
+ flow_data = session.get("flow_data", {}) or {}
191
+
192
+ # =========================
193
+ # Global interrupts / rerouting
194
+ # =========================
195
+ if wants_restart(t):
196
+ return ask_new_or_current_reply({})
197
+
198
+ if is_complaint(t):
199
+ return create_complaint_flow_result(flow_data)
200
+
201
+ if wants_direct_support(t):
202
+ return create_support_handoff_result(
203
+ flow_data=flow_data,
204
+ summary=f"طلب تواصل مباشر أثناء المحادثة: {text}"
205
+ )
206
+
207
+ if wants_new_topic(t):
208
+ return {
209
+ "next_state": WAITING_MAIN_MENU,
210
+ "flow_data": {},
211
+ "reply": "تمام، نبدأ من جديد. إنت طالب جديد ولا طالب حالي؟"
212
+ }
213
+
214
+ if is_children(t) and state not in [WAITING_AUDIENCE, WAITING_PRIOR_STUDY]:
215
+ new_flow = dict(flow_data)
216
+ new_flow["customer_type"] = "new"
217
+ new_flow["audience"] = "children"
218
+ return ask_prior_study_reply(new_flow)
219
+
220
+ if is_adults(t) and state not in [WAITING_AUDIENCE, WAITING_PRIOR_STUDY]:
221
+ new_flow = dict(flow_data)
222
+ new_flow["customer_type"] = "new"
223
+ new_flow["audience"] = "adults"
224
+ return ask_prior_study_reply(new_flow)
225
+
226
+ # =========================
227
+ # START
228
+ # =========================
229
+ if state == START:
230
+ return greeting_main_menu_reply(flow_data)
231
+
232
+ # =========================
233
+ # WAITING_MAIN_MENU
234
+ # =========================
235
+ if state == WAITING_MAIN_MENU:
236
+ if wants_start(t):
237
+ return ask_new_or_current_reply(flow_data)
238
+
239
+ if is_new_student(t):
240
+ flow_data["customer_type"] = "new"
241
+ return ask_audience_reply(flow_data)
242
+
243
+ if is_current_student(t):
244
+ flow_data["customer_type"] = "current"
245
+ return current_student_menu_reply(flow_data)
246
+
247
+ return ask_new_or_current_reply(flow_data)
248
+
249
+ # =========================
250
+ # WAITING_USER_TYPE
251
+ # =========================
252
+ if state == WAITING_USER_TYPE:
253
+ if is_new_student(t):
254
+ flow_data["customer_type"] = "new"
255
+ return ask_audience_reply(flow_data)
256
+
257
+ if is_current_student(t):
258
+ flow_data["customer_type"] = "current"
259
+ return current_student_menu_reply(flow_data)
260
+
261
+ return {
262
+ "next_state": WAITING_USER_TYPE,
263
+ "flow_data": flow_data,
264
+ "reply": "تمام، محتاج أعرف الأول: إنت طالب جديد ولا طالب حالي؟"
265
+ }
266
+
267
+ # =========================
268
+ # WAITING_AUDIENCE
269
+ # =========================
270
+ if state == WAITING_AUDIENCE:
271
+ if is_adults(t):
272
+ flow_data["audience"] = "adults"
273
+ return ask_prior_study_reply(flow_data)
274
+
275
+ if is_children(t):
276
+ flow_data["audience"] = "children"
277
+ return ask_prior_study_reply(flow_data)
278
+
279
+ return {
280
+ "next_state": WAITING_AUDIENCE,
281
+ "flow_data": flow_data,
282
+ "reply": "تمام، الكورس للكبار ولا للأطفال؟"
283
+ }
284
+
285
+ # =========================
286
+ # WAITING_PRIOR_STUDY
287
+ # =========================
288
+ if state == WAITING_PRIOR_STUDY:
289
+ if is_no(t):
290
+ flow_data["prior_study"] = False
291
+ return beginner_schedule_reply(flow_data)
292
+
293
+ if is_yes(t):
294
+ flow_data["prior_study"] = True
295
+ return placement_test_reply(flow_data)
296
+
297
+ return {
298
+ "next_state": WAITING_PRIOR_STUDY,
299
+ "flow_data": flow_data,
300
+ "reply": "هل درست اللغة الألمانية قبل كده؟ رد بنعم أو لا."
301
+ }
302
+
303
+ # =========================
304
+ # WAITING_BEGINNER_SCHEDULE_CHOICE
305
+ # =========================
306
+ if state == WAITING_BEGINNER_SCHEDULE_CHOICE:
307
+ if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
308
+ return pdf_102_reply(flow_data)
309
+
310
+ if contains_any(t, ["عايز احجز", "عايزة احجز", "احجز", "حجز", "اشترك", "اشتراك"]):
311
+ return pdf_102_reply(flow_data)
312
+
313
+ if contains_any(t, ["محتاج مساعدة", "محتاجه مساعدة", "استفسار", "سؤال"]):
314
+ return create_support_handoff_result(
315
+ flow_data=flow_data,
316
+ summary="استفسار عن مواعيد كورسات المبتدئين"
317
+ )
318
+
319
+ return {
320
+ "next_state": WAITING_BEGINNER_SCHEDULE_CHOICE,
321
+ "flow_data": flow_data,
322
+ "reply": "بعد ما تراجع المواعيد، ابعتلي تم أو قولّي تحب نكمّل الحجز."
323
+ }
324
+
325
+ # =========================
326
+ # WAITING_PDF_102_CONFIRMATION
327
+ # =========================
328
+ if state == WAITING_PDF_102_CONFIRMATION:
329
+ if contains_any(t, ["تم", "خلصت", "قريت", "اطلعت", "جاهز", "جاهزة"]):
330
+ return payment_methods_reply(flow_data)
331
+
332
+ if contains_any(t, ["محتاج مساعدة", "محتاجه مساعدة", "استفسار", "سؤال"]):
333
+ return create_support_handoff_result(
334
+ flow_data=flow_data,
335
+ summary="استفسار بعد إرسال ملف 102"
336
+ )
337
+
338
+ return {
339
+ "next_state": WAITING_PDF_102_CONFIRMATION,
340
+ "flow_data": flow_data,
341
+ "reply": "بعد ما تطلع على الملف، ابعتلي تم عشان أكمل معاك إجراءات الدفع والحجز."
342
+ }
343
+
344
+ # =========================
345
+ # WAITING_PLACEMENT_TEST_CONFIRMATION
346
+ # =========================
347
+ if state == WAITING_PLACEMENT_TEST_CONFIRMATION:
348
+ if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
349
+ return create_sales_handoff_result(
350
+ flow_data=flow_data,
351
+ summary="عميل اختار أو مهتم باختبار تحديد مستوى"
352
+ )
353
+
354
+ if contains_any(t, ["تفاصيل", "اعرف اكتر", "محتاج مساعدة", "مساعدة", "استفسار"]):
355
+ return create_support_handoff_result(
356
+ flow_data=flow_data,
357
+ summary="استفسار عن اختبار تحديد المستوى"
358
+ )
359
+
360
+ return {
361
+ "next_state": WAITING_PLACEMENT_TEST_CONFIRMATION,
362
+ "flow_data": flow_data,
363
+ "reply": "بعد ما تراجع مواعيد اختبار تحديد المستوى، ابعتلي تم أو قولّي لو محتاج مساعدة."
364
+ }
365
+
366
+ # =========================
367
+ # WAITING_CURRENT_STUDENT_ACTION
368
+ # =========================
369
+ if state == WAITING_CURRENT_STUDENT_ACTION:
370
+ if is_support_request(t):
371
+ return support_question_reply(flow_data)
372
+
373
+ if is_next_level_booking(t):
374
+ return ask_level_selection_reply(flow_data)
375
+
376
+ return {
377
+ "next_state": WAITING_CURRENT_STUDENT_ACTION,
378
+ "flow_data": flow_data,
379
+ "reply": "تحب استفسار بخصوص الكورس ولا حجز المستوى التالي؟"
380
+ }
381
+
382
+ # =========================
383
+ # WAITING_SUPPORT_QUESTION
384
+ # =========================
385
+ if state == WAITING_SUPPORT_QUESTION:
386
+ flow_data["support_question"] = text
387
+
388
+ return create_support_handoff_result(
389
+ flow_data=flow_data,
390
+ summary=f"استفسار طالب حالي: {text}"
391
+ )
392
+
393
+ # =========================
394
+ # WAITING_LEVEL_SELECTION
395
+ # =========================
396
+ if state == WAITING_LEVEL_SELECTION:
397
+ level = detect_level(t)
398
+
399
+ if level:
400
+ flow_data["selected_level"] = level
401
+ return payment_methods_reply(flow_data)
402
+
403
+ if contains_any(t, ["مش عارف", "مش متأكد", "مش متاكدة", "محتاج مساعدة", "استفسار"]):
404
+ return create_support_handoff_result(
405
+ flow_data=flow_data,
406
+ summary="طالب حالي محتاج مساعدة في اختيار المستوى التالي"
407
+ )
408
+
409
+ return {
410
+ "next_state": WAITING_LEVEL_SELECTION,
411
+ "flow_data": flow_data,
412
+ "reply": "قولّي المستوى اللي تحب تحجزه: 1A أو 2A أو 1B أو 1C2/B."
413
+ }
414
+
415
+ # =========================
416
+ # WAITING_PAYMENT_METHOD
417
+ # =========================
418
+ if state == WAITING_PAYMENT_METHOD:
419
+ payment_method = detect_payment_method(t)
420
+
421
+ if payment_method:
422
+ flow_data["payment_method"] = payment_method
423
+
424
+ payment_summaries = {
425
+ "branch_or_cash": "عميل جاهز للحجز واختار الدفع كاش أو في الفرع",
426
+ "bank_transfer": "عميل جاهز للحجز واختار تحويل بنكي",
427
+ "vodafone_cash": "عميل جاهز للحجز واختار Vodafone Cash",
428
+ "card": "عميل جاهز للحجز واختار الدفع بالبطاقة",
429
+ "installments": "عميل مهتم بالتقسيط",
430
+ }
431
+
432
+ return create_sales_handoff_result(
433
+ flow_data=flow_data,
434
+ summary=payment_summaries[payment_method]
435
+ )
436
+
437
+ if contains_any(t, ["استفسار", "سؤال", "محتاج مساعدة", "محتاجه مساعدة"]):
438
+ return create_support_handoff_result(
439
+ flow_data=flow_data,
440
+ summary="استفسار عن طرق الدفع أو إتمام الحجز"
441
+ )
442
+
443
+ return {
444
+ "next_state": WAITING_PAYMENT_METHOD,
445
+ "flow_data": flow_data,
446
+ "reply": "اختار طريقة الدفع المناسبة: الفرع / تحويل بنكي / Vodafone Cash / Visa / تقسيط Value."
447
+ }
448
+
449
+ # =========================
450
+ # WAITING_COMPLAINT_FORM
451
+ # =========================
452
+ if state == WAITING_COMPLAINT_FORM:
453
+ if contains_any(t, ["تم", "خلصت", "سجلت", "قدمت", "بعت"]):
454
+ return {
455
+ "next_state": WAITING_COMPLAINT_FORM,
456
+ "flow_data": flow_data,
457
+ "reply": "تمام، بعد مراجعة الشكوى هيتم التواصل معاك. ولو حابب تكتب أي تفاصيل إضافية ابعتها هنا."
458
+ }
459
+
460
+ return {
461
+ "next_state": WAITING_COMPLAINT_FORM,
462
+ "flow_data": flow_data,
463
+ "reply": "سجّل الشكوى من الفورم أولًا، ولو احتجت مساعدة ابعتلي."
464
+ }
465
+
466
+ # =========================
467
+ # HANDOFF_DONE
468
+ # =========================
469
+ if state == HANDOFF_DONE:
470
+ if contains_any(t, ["شكرا", "متشكر", "تسلم", "ميرسي"]):
471
+ return {
472
+ "next_state": HANDOFF_DONE,
473
+ "flow_data": flow_data,
474
+ "reply": "العفو، تحت أمرك في أي وقت."
475
+ }
476
+
477
+ if is_support_request(t):
478
+ return create_support_handoff_result(
479
+ flow_data=flow_data,
480
+ summary=f"استفسار إضافي بعد handoff: {text}"
481
+ )
482
+
483
+ return {
484
+ "next_state": HANDOFF_DONE,
485
+ "flow_data": flow_data,
486
+ "reply": "تم تسجيل طلبك بالفعل، وفريقنا هيتواصل معاك قريب. لو عندك استفسار إضافي ابعته هنا."
487
+ }
488
+
489
  return unknown_reply(flow_data)