Mr-Help commited on
Commit
aaa3615
·
verified ·
1 Parent(s): 5686492

Update engine/flow_router.py

Browse files
Files changed (1) hide show
  1. engine/flow_router.py +144 -346
engine/flow_router.py CHANGED
@@ -30,244 +30,61 @@ from knowledge.replies import (
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
-
93
- def is_support_request(text: str) -> bool:
94
- t = normalize_text(text)
95
- return contains_any(t, [
96
- "استفسار", "سؤال", "عندي سؤال", "مشكلة", "مش فاهم",
97
- "عايز اسأل", "عايزة اسأل", "محتاج مساعدة", "محتاجه مساعدة",
98
- "support", "خدمة العملاء"
99
- ])
100
-
101
-
102
- def is_next_level_booking(text: str) -> bool:
103
- t = normalize_text(text)
104
- return contains_any(t, [
105
- "حجز", "احجز", "المستوى التالي", "مستوى تالي",
106
- "next level", "احجز المستوى", "حجز مستوى"
107
- ])
108
-
109
-
110
- def detect_level(text: str):
111
- t = normalize_text(text)
112
-
113
- if contains_any(t, ["1a", "a1", "a1.1", "1 a"]):
114
- return "1A"
115
-
116
- if contains_any(t, ["2a", "a2", "a1.2", "2 a"]):
117
- return "2A"
118
-
119
- if contains_any(t, ["1b", "b1", "b1.1", "1 b"]):
120
- return "1B"
121
-
122
- if contains_any(t, ["1c", "2b", "b2", "1c2/b", "1 c", "2 b"]):
123
- return "1C2/B"
124
-
125
- return None
126
-
127
-
128
- def detect_payment_method(text: str):
129
- t = normalize_text(text)
130
-
131
- if contains_any(t, ["فرع", "فروع", "كاش", "cash"]):
132
- return "branch_or_cash"
133
-
134
- if contains_any(t, ["تحويل", "بنكي", "bank", "transfer"]):
135
- return "bank_transfer"
136
-
137
- if contains_any(t, ["فودافون", "vodafone", "vodafone cash"]):
138
- return "vodafone_cash"
139
-
140
- if contains_any(t, ["فيزا", "visa", "ماستر", "master", "credit card", "card"]):
141
- return "card"
142
-
143
- if contains_any(t, ["تقسيط", "value", "فاليو"]):
144
- return "installments"
145
-
146
- return None
147
-
148
-
149
- def is_complaint(text: str) -> bool:
150
- t = normalize_text(text)
151
- return contains_any(t, [
152
- "شكوى", "اشتكي", "اشتك", "مشكلة كبيرة", "complaint"
153
- ])
154
-
155
-
156
- def wants_direct_support(text: str) -> bool:
157
- t = normalize_text(text)
158
- return contains_any(t, [
159
- "تواصل", "اكلم", "عايز حد يكلمني", "عايزة حد يكلمني",
160
- "عايز اكلم خدمة العملاء", "عايزة اكلم خدمة العملاء"
161
- ])
162
-
163
-
164
- def wants_start(text: str) -> bool:
165
- t = normalize_text(text)
166
- return contains_any(t, [
167
- "ابدأ", "ابدا", "مساعدة", "مساعده", "start", "menu", "القائمة"
168
- ])
169
-
170
-
171
- def wants_restart(text: str) -> bool:
172
- t = normalize_text(text)
173
- return contains_any(t, [
174
- "من جديد", "ابدأ من جديد", "restart", "مينيو", "القائمة", "ابدأ"
175
- ])
176
-
177
-
178
- def wants_new_topic(text: str) -> bool:
179
- t = normalize_text(text)
180
- return contains_any(t, [
181
- "عايز اسال عن حاجة تانية",
182
- "عايزة اسال عن حاجة تانية",
183
- "استفسار جديد",
184
- "موضوع تاني",
185
- "حاجة تانية"
186
- ])
187
-
188
-
189
- def wants_courses_info(text: str) -> bool:
190
- t = normalize_text(text)
191
- return contains_any(t, [
192
- "كورسات",
193
- "الكورسات",
194
- "ايه الكورسات",
195
- "ما هي الكورسات",
196
- "الأنواع",
197
- "ال��نواع",
198
- "عايز اعرف الكورسات",
199
- "عايزة اعرف الكورسات",
200
- "ايه الكورسات المتاحة",
201
- "الكورسات المتاحة"
202
- ])
203
-
204
-
205
- def asks_about_prior_study_case(text: str) -> bool:
206
- t = normalize_text(text)
207
- return contains_any(t, [
208
- "لو كنت درست",
209
- "لو كنت دارس",
210
- "لو درست قبل كده",
211
- "طب لو درست",
212
- "ولو درست",
213
- "اذا كنت درست",
214
- "إذا كنت درست",
215
- "اختبار تحديد مستوى",
216
- "تحديد مستوى"
217
- ])
218
-
219
-
220
- def asks_about_beginner_case(text: str) -> bool:
221
- t = normalize_text(text)
222
- return contains_any(t, [
223
- "لو مكنتش درست",
224
- "لو ما درستش",
225
- "لو مدرستش",
226
- "لو لسه جديد",
227
- "لو مبتدئ",
228
- "لو بادئ",
229
- "لو اول مرة",
230
- "لو أول مرة"
231
- ])
232
-
233
-
234
  def route_message(state: str, text: str, session: dict):
235
- t = normalize_text(text)
236
  flow_data = session.get("flow_data", {}) or {}
 
 
 
 
 
237
 
238
  # =========================
239
- # Global interrupts / rerouting
240
  # =========================
241
- if wants_restart(t):
242
- return ask_new_or_current_reply({})
 
 
 
 
 
243
 
244
- if is_complaint(t):
245
- return create_complaint_flow_result(flow_data)
246
 
247
- if wants_direct_support(t):
248
- return create_support_handoff_result(
249
- flow_data=flow_data,
250
- summary=f"طلب تواصل مباشر أثناء المحادثة: {text}"
251
- )
252
 
253
- if wants_new_topic(t):
254
- return {
255
- "next_state": WAITING_MAIN_MENU,
256
- "flow_data": {},
257
- "reply": "تمام، نبدأ من جديد. إنت طالب جديد ولا طالب حالي؟"
258
- }
259
 
260
- if is_children(t) and state not in [WAITING_AUDIENCE, WAITING_PRIOR_STUDY]:
261
- new_flow = dict(flow_data)
262
- new_flow["customer_type"] = "new"
263
- new_flow["audience"] = "children"
264
- return ask_prior_study_reply(new_flow)
265
 
266
- if is_adults(t) and state not in [WAITING_AUDIENCE, WAITING_PRIOR_STUDY]:
267
- new_flow = dict(flow_data)
268
- new_flow["customer_type"] = "new"
269
- new_flow["audience"] = "adults"
270
- return ask_prior_study_reply(new_flow)
 
 
 
 
271
 
272
  # =========================
273
  # START
@@ -279,32 +96,18 @@ def route_message(state: str, text: str, session: dict):
279
  # WAITING_MAIN_MENU
280
  # =========================
281
  if state == WAITING_MAIN_MENU:
282
- if wants_start(t):
283
- return ask_new_or_current_reply(flow_data)
284
-
285
- if is_new_student(t):
286
- flow_data["customer_type"] = "new"
287
- return ask_audience_reply(flow_data)
288
-
289
- if is_current_student(t):
290
- flow_data["customer_type"] = "current"
291
- return current_student_menu_reply(flow_data)
292
-
293
- if wants_courses_info(t):
294
- return ask_new_or_current_reply(flow_data)
295
-
296
  return ask_new_or_current_reply(flow_data)
297
 
298
  # =========================
299
  # WAITING_USER_TYPE
300
  # =========================
301
  if state == WAITING_USER_TYPE:
302
- if is_new_student(t):
303
- flow_data["customer_type"] = "new"
304
  return ask_audience_reply(flow_data)
305
 
306
- if is_current_student(t):
307
- flow_data["customer_type"] = "current"
308
  return current_student_menu_reply(flow_data)
309
 
310
  return {
@@ -317,12 +120,12 @@ def route_message(state: str, text: str, session: dict):
317
  # WAITING_AUDIENCE
318
  # =========================
319
  if state == WAITING_AUDIENCE:
320
- if is_adults(t):
321
- flow_data["audience"] = "adults"
322
  return ask_prior_study_reply(flow_data)
323
 
324
- if is_children(t):
325
- flow_data["audience"] = "children"
326
  return ask_prior_study_reply(flow_data)
327
 
328
  return {
@@ -335,12 +138,12 @@ def route_message(state: str, text: str, session: dict):
335
  # WAITING_PRIOR_STUDY
336
  # =========================
337
  if state == WAITING_PRIOR_STUDY:
338
- if is_no(t):
339
- flow_data["prior_study"] = False
340
  return beginner_schedule_reply(flow_data)
341
 
342
- if is_yes(t):
343
- flow_data["prior_study"] = True
344
  return placement_test_reply(flow_data)
345
 
346
  return {
@@ -353,57 +156,55 @@ def route_message(state: str, text: str, session: dict):
353
  # WAITING_BEGINNER_SCHEDULE_CHOICE
354
  # =========================
355
  if state == WAITING_BEGINNER_SCHEDULE_CHOICE:
356
- if asks_about_prior_study_case(t):
357
  new_flow = dict(flow_data)
358
- new_flow["prior_study"] = True
359
  return placement_test_reply(new_flow)
360
 
361
- if asks_about_beginner_case(t):
362
  new_flow = dict(flow_data)
363
- new_flow["prior_study"] = False
364
  return beginner_schedule_reply(new_flow)
365
 
366
- if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
367
- return pdf_102_reply(flow_data)
368
-
369
- if contains_any(t, ["عايز احجز", "عايزة احجز", "احجز", "حجز", "اشترك", "اشتراك"]):
370
- return pdf_102_reply(flow_data)
371
-
372
- if contains_any(t, ["محتاج مساعدة", "محتاجه مساعدة", "استفسار", "سؤال"]):
373
  return create_support_handoff_result(
374
  flow_data=flow_data,
375
  summary="استفسار عن مواعيد كورسات المبتدئين"
376
  )
377
 
378
- return {
379
- "next_state": WAITING_BEGINNER_SCHEDULE_CHOICE,
380
- "flow_data": flow_data,
381
- "reply": "بعد ما تراجع المواعيد، ابعتلي تم أو قولّي تحب نكمّل الحجز."
382
- }
 
 
 
 
383
 
384
  # =========================
385
  # WAITING_PDF_102_CONFIRMATION
386
  # =========================
387
  if state == WAITING_PDF_102_CONFIRMATION:
388
- if asks_about_prior_study_case(t):
389
  new_flow = dict(flow_data)
390
- new_flow["prior_study"] = True
391
  return placement_test_reply(new_flow)
392
 
393
- if asks_about_beginner_case(t):
394
  new_flow = dict(flow_data)
395
- new_flow["prior_study"] = False
396
  return beginner_schedule_reply(new_flow)
397
 
398
- if contains_any(t, ["تم", "خلصت", "قريت", "اطلعت", "جاهز", "جاهزة"]):
399
- return payment_methods_reply(flow_data)
400
-
401
- if contains_any(t, ["محتاج مساعدة", "محتاجه مساعدة", "استفسار", "سؤال"]):
402
  return create_support_handoff_result(
403
  flow_data=flow_data,
404
  summary="استفسار بعد إرسال ملف 102"
405
  )
406
 
 
 
 
407
  return {
408
  "next_state": WAITING_PDF_102_CONFIRMATION,
409
  "flow_data": flow_data,
@@ -414,26 +215,26 @@ def route_message(state: str, text: str, session: dict):
414
  # WAITING_PLACEMENT_TEST_CONFIRMATION
415
  # =========================
416
  if state == WAITING_PLACEMENT_TEST_CONFIRMATION:
417
- if asks_about_beginner_case(t):
418
  new_flow = dict(flow_data)
419
- new_flow["prior_study"] = False
420
  return beginner_schedule_reply(new_flow)
421
 
422
- if asks_about_prior_study_case(t):
423
  new_flow = dict(flow_data)
424
- new_flow["prior_study"] = True
425
  return placement_test_reply(new_flow)
426
 
427
- if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
428
- return create_sales_handoff_result(
429
  flow_data=flow_data,
430
- summary="عميل اختار أو مهتم باختبار تحديد مستوى"
431
  )
432
 
433
- if contains_any(t, ["تفاصيل", "اعرف اكتر", "محتاج مساعدة", "مساعدة", "استفسار"]):
434
- return create_support_handoff_result(
435
  flow_data=flow_data,
436
- summary="استفسار عن اختبار تحديد المستوى"
437
  )
438
 
439
  return {
@@ -446,10 +247,10 @@ def route_message(state: str, text: str, session: dict):
446
  # WAITING_CURRENT_STUDENT_ACTION
447
  # =========================
448
  if state == WAITING_CURRENT_STUDENT_ACTION:
449
- if is_support_request(t):
450
  return support_question_reply(flow_data)
451
 
452
- if is_next_level_booking(t):
453
  return ask_level_selection_reply(flow_data)
454
 
455
  return {
@@ -462,24 +263,28 @@ def route_message(state: str, text: str, session: dict):
462
  # WAITING_SUPPORT_QUESTION
463
  # =========================
464
  if state == WAITING_SUPPORT_QUESTION:
465
- flow_data["support_question"] = text
 
 
 
 
 
466
 
467
- return create_support_handoff_result(
468
- flow_data=flow_data,
469
- summary=f"استفسار طالب حالي: {text}"
470
- )
 
471
 
472
  # =========================
473
  # WAITING_LEVEL_SELECTION
474
  # =========================
475
  if state == WAITING_LEVEL_SELECTION:
476
- level = detect_level(t)
477
-
478
- if level:
479
- flow_data["selected_level"] = level
480
  return payment_methods_reply(flow_data)
481
 
482
- if contains_any(t, ["مش عارف", "مش متأكد", "مش متاكدة", "محتاج مساعدة", "استفسار"]):
483
  return create_support_handoff_result(
484
  flow_data=flow_data,
485
  summary="طالب حالي محتاج مساعدة في اختيار المستوى التالي"
@@ -495,11 +300,10 @@ def route_message(state: str, text: str, session: dict):
495
  # WAITING_PAYMENT_METHOD
496
  # =========================
497
  if state == WAITING_PAYMENT_METHOD:
498
- payment_method = detect_payment_method(t)
499
-
500
- if payment_method:
501
- flow_data["payment_method"] = payment_method
502
 
 
503
  payment_summaries = {
504
  "branch_or_cash": "عميل جاهز للحجز واختار الدفع كاش أو في الفرع",
505
  "bank_transfer": "عميل جاهز للحجز واختار تحويل بنكي",
@@ -513,7 +317,7 @@ def route_message(state: str, text: str, session: dict):
513
  summary=payment_summaries[payment_method]
514
  )
515
 
516
- if contains_any(t, ["استفسار", "سؤال", "محتاج مساعدة", "محتاجه مساعدة"]):
517
  return create_support_handoff_result(
518
  flow_data=flow_data,
519
  summary="استفسار عن طرق الدفع أو إتمام الحجز"
@@ -529,7 +333,7 @@ def route_message(state: str, text: str, session: dict):
529
  # WAITING_COMPLAINT_FORM
530
  # =========================
531
  if state == WAITING_COMPLAINT_FORM:
532
- if contains_any(t, ["تم", "خلصت", "سجلت", "قدمت", "بعت"]):
533
  return {
534
  "next_state": WAITING_COMPLAINT_FORM,
535
  "flow_data": flow_data,
@@ -546,57 +350,51 @@ def route_message(state: str, text: str, session: dict):
546
  # HANDOFF_DONE
547
  # =========================
548
  if state == HANDOFF_DONE:
549
- if contains_any(t, ["شكرا", "متشكر", "تسلم", "ميرسي"]):
550
  return {
551
  "next_state": HANDOFF_DONE,
552
  "flow_data": flow_data,
553
  "reply": "العفو، تحت أمرك في أي وقت."
554
  }
555
 
556
- if wants_restart(t) or wants_new_topic(t):
557
- return {
558
- "next_state": WAITING_MAIN_MENU,
559
- "flow_data": {},
560
- "reply": "تمام، نبدأ من جديد. إنت طالب جديد ولا طالب حالي؟"
561
- }
562
-
563
- if is_children(t):
564
- new_flow = {}
565
- new_flow["customer_type"] = "new"
566
- new_flow["audience"] = "children"
567
- return ask_prior_study_reply(new_flow)
568
-
569
- if is_adults(t):
570
- new_flow = {}
571
- new_flow["customer_type"] = "new"
572
- new_flow["audience"] = "adults"
573
- return ask_prior_study_reply(new_flow)
574
-
575
- if is_new_student(t):
576
- new_flow = {}
577
- new_flow["customer_type"] = "new"
578
- return ask_audience_reply(new_flow)
579
-
580
- if is_current_student(t):
581
- new_flow = {}
582
- new_flow["customer_type"] = "current"
583
- return current_student_menu_reply(new_flow)
584
-
585
- if wants_courses_info(t):
586
- return {
587
- "next_state": WAITING_USER_TYPE,
588
- "flow_data": {},
589
- "reply": "تمام، أقدر أساعدك في كورسات الطلبة الجدد أو الحاليين. إنت طالب جديد ولا طالب حالي؟"
590
- }
591
-
592
- if is_support_request(t):
593
- return create_support_handoff_result(
594
- flow_data=flow_data,
595
- summary=f"استفسار إضافي بعد handoff: {text}"
596
- )
597
-
598
- if is_complaint(t):
599
- return create_complaint_flow_result(flow_data)
600
 
601
  return {
602
  "next_state": HANDOFF_DONE,
 
30
  unknown_reply,
31
  )
32
 
33
+ from knowledge.message_understanding import classify_message
34
  from handoff.sales import create_sales_handoff_result
35
  from handoff.support import create_support_handoff_result
36
  from handoff.complaints import create_complaint_flow_result
37
 
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  def route_message(state: str, text: str, session: dict):
 
40
  flow_data = session.get("flow_data", {}) or {}
41
+ parsed = classify_message(state, text, flow_data)
42
+
43
+ kind = parsed["kind"]
44
+ value = parsed["value"]
45
+ entities = parsed.get("entities", {}) or {}
46
 
47
  # =========================
48
+ # Global topic switches
49
  # =========================
50
+ if kind == "topic_switch":
51
+ if value in ["restart", "new_topic", "courses_info"]:
52
+ return {
53
+ "next_state": WAITING_MAIN_MENU,
54
+ "flow_data": {},
55
+ "reply": "تمام، نبدأ من جديد. إنت طالب جديد ولا طالب حالي؟"
56
+ }
57
 
58
+ if value == "complaint":
59
+ return create_complaint_flow_result(flow_data)
60
 
61
+ if value == "direct_support":
62
+ return create_support_handoff_result(
63
+ flow_data=flow_data,
64
+ summary=f"طلب تواصل مباشر أثناء المحادثة: {text}"
65
+ )
66
 
67
+ if value == "children_courses":
68
+ new_flow = dict(flow_data)
69
+ new_flow["customer_type"] = "new"
70
+ new_flow["audience"] = "children"
71
+ return ask_prior_study_reply(new_flow)
 
72
 
73
+ if value == "adults_courses":
74
+ new_flow = dict(flow_data)
75
+ new_flow["customer_type"] = "new"
76
+ new_flow["audience"] = "adults"
77
+ return ask_prior_study_reply(new_flow)
78
 
79
+ if value == "new_student":
80
+ new_flow = dict(flow_data)
81
+ new_flow["customer_type"] = "new"
82
+ return ask_audience_reply(new_flow)
83
+
84
+ if value == "current_student":
85
+ new_flow = dict(flow_data)
86
+ new_flow["customer_type"] = "current"
87
+ return current_student_menu_reply(new_flow)
88
 
89
  # =========================
90
  # START
 
96
  # WAITING_MAIN_MENU
97
  # =========================
98
  if state == WAITING_MAIN_MENU:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  return ask_new_or_current_reply(flow_data)
100
 
101
  # =========================
102
  # WAITING_USER_TYPE
103
  # =========================
104
  if state == WAITING_USER_TYPE:
105
+ if kind == "direct_answer" and value == "new_student":
106
+ flow_data.update(entities)
107
  return ask_audience_reply(flow_data)
108
 
109
+ if kind == "direct_answer" and value == "current_student":
110
+ flow_data.update(entities)
111
  return current_student_menu_reply(flow_data)
112
 
113
  return {
 
120
  # WAITING_AUDIENCE
121
  # =========================
122
  if state == WAITING_AUDIENCE:
123
+ if kind == "direct_answer" and value == "adults":
124
+ flow_data.update(entities)
125
  return ask_prior_study_reply(flow_data)
126
 
127
+ if kind == "direct_answer" and value == "children":
128
+ flow_data.update(entities)
129
  return ask_prior_study_reply(flow_data)
130
 
131
  return {
 
138
  # WAITING_PRIOR_STUDY
139
  # =========================
140
  if state == WAITING_PRIOR_STUDY:
141
+ if kind == "direct_answer" and value == "prior_study_no":
142
+ flow_data.update(entities)
143
  return beginner_schedule_reply(flow_data)
144
 
145
+ if kind == "direct_answer" and value == "prior_study_yes":
146
+ flow_data.update(entities)
147
  return placement_test_reply(flow_data)
148
 
149
  return {
 
156
  # WAITING_BEGINNER_SCHEDULE_CHOICE
157
  # =========================
158
  if state == WAITING_BEGINNER_SCHEDULE_CHOICE:
159
+ if kind == "state_switch" and value == "switch_to_prior_study_true":
160
  new_flow = dict(flow_data)
161
+ new_flow.update(entities)
162
  return placement_test_reply(new_flow)
163
 
164
+ if kind == "state_switch" and value == "switch_to_prior_study_false":
165
  new_flow = dict(flow_data)
166
+ new_flow.update(entities)
167
  return beginner_schedule_reply(new_flow)
168
 
169
+ if kind == "state_switch" and value == "support_needed":
 
 
 
 
 
 
170
  return create_support_handoff_result(
171
  flow_data=flow_data,
172
  summary="استفسار عن مواعيد كورسات المبتدئين"
173
  )
174
 
175
+ if kind == "direct_answer" and value in ["confirm_schedule_reviewed", "proceed_booking"]:
176
+ return pdf_102_reply(flow_data)
177
+
178
+ if kind == "unclear":
179
+ return {
180
+ "next_state": WAITING_BEGINNER_SCHEDULE_CHOICE,
181
+ "flow_data": flow_data,
182
+ "reply": "تقصد تكمل الحجز، ولا بتسأل عن حالة لو كنت درست ألماني قبل كده؟"
183
+ }
184
 
185
  # =========================
186
  # WAITING_PDF_102_CONFIRMATION
187
  # =========================
188
  if state == WAITING_PDF_102_CONFIRMATION:
189
+ if kind == "state_switch" and value == "switch_to_prior_study_true":
190
  new_flow = dict(flow_data)
191
+ new_flow.update(entities)
192
  return placement_test_reply(new_flow)
193
 
194
+ if kind == "state_switch" and value == "switch_to_prior_study_false":
195
  new_flow = dict(flow_data)
196
+ new_flow.update(entities)
197
  return beginner_schedule_reply(new_flow)
198
 
199
+ if kind == "state_switch" and value == "support_needed":
 
 
 
200
  return create_support_handoff_result(
201
  flow_data=flow_data,
202
  summary="استفسار بعد إرسال ملف 102"
203
  )
204
 
205
+ if kind == "direct_answer" and value == "confirm_pdf_reviewed":
206
+ return payment_methods_reply(flow_data)
207
+
208
  return {
209
  "next_state": WAITING_PDF_102_CONFIRMATION,
210
  "flow_data": flow_data,
 
215
  # WAITING_PLACEMENT_TEST_CONFIRMATION
216
  # =========================
217
  if state == WAITING_PLACEMENT_TEST_CONFIRMATION:
218
+ if kind == "state_switch" and value == "switch_to_prior_study_false":
219
  new_flow = dict(flow_data)
220
+ new_flow.update(entities)
221
  return beginner_schedule_reply(new_flow)
222
 
223
+ if kind == "state_switch" and value == "switch_to_prior_study_true":
224
  new_flow = dict(flow_data)
225
+ new_flow.update(entities)
226
  return placement_test_reply(new_flow)
227
 
228
+ if kind == "state_switch" and value == "support_needed":
229
+ return create_support_handoff_result(
230
  flow_data=flow_data,
231
+ summary="استفسار عن اختبار تحديد المستوى"
232
  )
233
 
234
+ if kind == "direct_answer" and value == "confirm_placement_test_reviewed":
235
+ return create_sales_handoff_result(
236
  flow_data=flow_data,
237
+ summary="عميل اختار أو مهتم باختبار تحديد مستوى"
238
  )
239
 
240
  return {
 
247
  # WAITING_CURRENT_STUDENT_ACTION
248
  # =========================
249
  if state == WAITING_CURRENT_STUDENT_ACTION:
250
+ if kind == "direct_answer" and value == "current_student_support":
251
  return support_question_reply(flow_data)
252
 
253
+ if kind == "direct_answer" and value == "current_student_next_level":
254
  return ask_level_selection_reply(flow_data)
255
 
256
  return {
 
263
  # WAITING_SUPPORT_QUESTION
264
  # =========================
265
  if state == WAITING_SUPPORT_QUESTION:
266
+ if kind == "direct_answer" and value == "support_question_text":
267
+ flow_data.update(entities)
268
+ return create_support_handoff_result(
269
+ flow_data=flow_data,
270
+ summary=f"استفسار طالب حالي: {text}"
271
+ )
272
 
273
+ return {
274
+ "next_state": WAITING_SUPPORT_QUESTION,
275
+ "flow_data": flow_data,
276
+ "reply": "اكتبلي استفسارك بالتفصيل، وأنا هرفعه لفريق خدمة العملاء."
277
+ }
278
 
279
  # =========================
280
  # WAITING_LEVEL_SELECTION
281
  # =========================
282
  if state == WAITING_LEVEL_SELECTION:
283
+ if kind == "direct_answer" and value == "level_selected":
284
+ flow_data.update(entities)
 
 
285
  return payment_methods_reply(flow_data)
286
 
287
+ if kind == "state_switch" and value == "support_needed":
288
  return create_support_handoff_result(
289
  flow_data=flow_data,
290
  summary="طالب حالي محتاج مساعدة في اختيار المستوى التالي"
 
300
  # WAITING_PAYMENT_METHOD
301
  # =========================
302
  if state == WAITING_PAYMENT_METHOD:
303
+ if kind == "direct_answer" and value == "payment_method_selected":
304
+ flow_data.update(entities)
 
 
305
 
306
+ payment_method = flow_data.get("payment_method")
307
  payment_summaries = {
308
  "branch_or_cash": "عميل جاهز للحجز واختار الدفع كاش أو في الفرع",
309
  "bank_transfer": "عميل جاهز للحجز واختار تحويل بنكي",
 
317
  summary=payment_summaries[payment_method]
318
  )
319
 
320
+ if kind == "state_switch" and value == "support_needed":
321
  return create_support_handoff_result(
322
  flow_data=flow_data,
323
  summary="استفسار عن طرق الدفع أو إتمام الحجز"
 
333
  # WAITING_COMPLAINT_FORM
334
  # =========================
335
  if state == WAITING_COMPLAINT_FORM:
336
+ if kind == "direct_answer" and value == "complaint_form_submitted":
337
  return {
338
  "next_state": WAITING_COMPLAINT_FORM,
339
  "flow_data": flow_data,
 
350
  # HANDOFF_DONE
351
  # =========================
352
  if state == HANDOFF_DONE:
353
+ if kind == "direct_answer" and value == "thanks":
354
  return {
355
  "next_state": HANDOFF_DONE,
356
  "flow_data": flow_data,
357
  "reply": "العفو، تحت أمرك في أي وقت."
358
  }
359
 
360
+ if kind == "topic_switch":
361
+ if value in ["restart", "new_topic", "courses_info"]:
362
+ return {
363
+ "next_state": WAITING_MAIN_MENU,
364
+ "flow_data": {},
365
+ "reply": "تمام، نبدأ من جديد. إنت طالب جديد ولا طالب حالي؟"
366
+ }
367
+
368
+ if value == "children_courses":
369
+ new_flow = {}
370
+ new_flow["customer_type"] = "new"
371
+ new_flow["audience"] = "children"
372
+ return ask_prior_study_reply(new_flow)
373
+
374
+ if value == "adults_courses":
375
+ new_flow = {}
376
+ new_flow["customer_type"] = "new"
377
+ new_flow["audience"] = "adults"
378
+ return ask_prior_study_reply(new_flow)
379
+
380
+ if value == "new_student":
381
+ new_flow = {}
382
+ new_flow["customer_type"] = "new"
383
+ return ask_audience_reply(new_flow)
384
+
385
+ if value == "current_student":
386
+ new_flow = {}
387
+ new_flow["customer_type"] = "current"
388
+ return current_student_menu_reply(new_flow)
389
+
390
+ if value == "direct_support":
391
+ return create_support_handoff_result(
392
+ flow_data=flow_data,
393
+ summary=f"استفسار إضافي بعد handoff: {text}"
394
+ )
395
+
396
+ if value == "complaint":
397
+ return create_complaint_flow_result(flow_data)
 
 
 
 
 
 
398
 
399
  return {
400
  "next_state": HANDOFF_DONE,