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)