Spaces:
Runtime error
Runtime error
Upload app (1).py
Browse files- app (1).py +923 -0
app (1).py
ADDED
@@ -0,0 +1,923 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import re
|
3 |
+
import json
|
4 |
+
import time
|
5 |
+
from datetime import datetime
|
6 |
+
|
7 |
+
# Simple language detection function instead of using transformers
|
8 |
+
def simple_detect_language(text):
|
9 |
+
# Check for Arabic characters
|
10 |
+
arabic_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]+')
|
11 |
+
if arabic_pattern.search(text):
|
12 |
+
return "ar"
|
13 |
+
return "en"
|
14 |
+
|
15 |
+
# Import customer service enhancements
|
16 |
+
try:
|
17 |
+
from customer_service_enhancements import (
|
18 |
+
ENHANCED_CUSTOMER_SERVICE_PHRASES_AR,
|
19 |
+
ENHANCED_CUSTOMER_SERVICE_PHRASES_EN,
|
20 |
+
BANKING_FAQS_AR,
|
21 |
+
BANKING_FAQS_EN,
|
22 |
+
BANKING_GLOSSARY_AR,
|
23 |
+
BANKING_GLOSSARY_EN,
|
24 |
+
get_enhanced_response,
|
25 |
+
handle_banking_faq,
|
26 |
+
offer_satisfaction_survey,
|
27 |
+
get_banking_term_definition
|
28 |
+
)
|
29 |
+
CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE = True
|
30 |
+
except ImportError:
|
31 |
+
CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE = False
|
32 |
+
# Fallback customer service phrases
|
33 |
+
ENHANCED_CUSTOMER_SERVICE_PHRASES_AR = {
|
34 |
+
"greeting": [
|
35 |
+
"مرحبًا بك في بنك أم درمان الوطني! كيف يمكنني مساعدتك اليوم؟",
|
36 |
+
"أهلاً بك في خدمة العملاء الافتراضية لبنك أم درمان الوطني. كيف يمكنني خدمتك؟"
|
37 |
+
],
|
38 |
+
"thanks": [
|
39 |
+
"شكرًا لتواصلك مع بنك أم درمان الوطني!",
|
40 |
+
"نشكرك على ثقتك في خدماتنا المصرفية."
|
41 |
+
],
|
42 |
+
"follow_up": [
|
43 |
+
"هل هناك خدمة مصرفية أخرى يمكنني مساعدتك بها اليوم؟",
|
44 |
+
"هل لديك أي استفسارات أخرى حول منتجاتنا أو خدماتنا المصرفية؟"
|
45 |
+
]
|
46 |
+
}
|
47 |
+
ENHANCED_CUSTOMER_SERVICE_PHRASES_EN = {
|
48 |
+
"greeting": [
|
49 |
+
"Welcome to Omdurman National Bank! How may I assist you today?",
|
50 |
+
"Hello and welcome to ONB virtual customer service. How can I help you?"
|
51 |
+
],
|
52 |
+
"thanks": [
|
53 |
+
"Thank you for contacting Omdurman National Bank!",
|
54 |
+
"We appreciate your trust in our banking services."
|
55 |
+
],
|
56 |
+
"follow_up": [
|
57 |
+
"Is there any other banking service I can assist you with today?",
|
58 |
+
"Do you have any other questions about our products or banking services?"
|
59 |
+
]
|
60 |
+
}
|
61 |
+
|
62 |
+
# Omdurman National Bank-specific guidelines in Arabic
|
63 |
+
ONB_GUIDELINES_AR = {
|
64 |
+
"balance": "يمكنك التحقق من رصيدك عبر الإنترنت أو عبر تطبيق الهاتف الخاص ببنك أم درمان الوطني. <a href='#' onclick='window.open(\"https://onb.sd/balance\", \"_blank\")'>افحص رصيدك الآن</a>",
|
65 |
+
"lost_card": "في حالة فقدان البطاقة، اتصل بالرقم <a href='tel:249123456789'>249-123-456-789</a> فورًا أو <a href='#' onclick='window.open(\"https://onb.sd/block-card\", \"_blank\")'>أوقف البطاقة عبر الإنترنت</a>.",
|
66 |
+
"loan": "شروط القرض تشمل الحد الأدنى للدخل (5000 جنيه سوداني) وتاريخ ائتماني جيد. <a href='#' onclick='window.open(\"https://onb.sd/loans\", \"_blank\")'>تقدم بطلب قرض الآن</a>",
|
67 |
+
"transfer": "لتحويل الأموال، استخدم <a href='#' onclick='window.open(\"https://onb.sd/mobile-app\", \"_blank\")'>تطبيق الهاتف</a> أو <a href='#' onclick='window.open(\"https://onb.sd/online-banking\", \"_blank\")'>الخدمة المصرفية عبر الإنترنت</a>.",
|
68 |
+
"new_account": "لفتح حساب جديد، قم بزيارة أقرب فرع مع جواز سفرك أو هويتك الوطنية. <a href='#' onclick='window.open(\"https://onb.sd/new-account\", \"_blank\")'>احجز موعدًا الآن</a>",
|
69 |
+
"interest_rates": "أسعار الفائدة على الودائع تتراوح بين 5% إلى 10% سنويًا. <a href='#' onclick='window.open(\"https://onb.sd/rates\", \"_blank\")'>اطلع على جميع الأسعار</a>",
|
70 |
+
"branches": "فروعنا موجودة في أم درمان، الخرطوم، وبورتسودان. <a href='#' onclick='window.open(\"https://onb.sd/branches\", \"_blank\")'>اعثر على أقرب فرع</a>",
|
71 |
+
"working_hours": "ساعات العمل من 8 صباحًا إلى 3 مساءً من الأحد إلى الخميس. <a href='#' onclick='window.open(\"https://onb.sd/hours\", \"_blank\")'>تحقق من ساعات العمل الخاصة</a>",
|
72 |
+
"contact": "الاتصال بنا على الرقم <a href='tel:249123456789'>249-123-456-789</a> أو عبر البريد الإلكتروني <a href='mailto:[email protected]'>[email protected]</a>. <a href='#' onclick='window.open(\"https://onb.sd/contact\", \"_blank\")'>نموذج الاتصال</a>"
|
73 |
+
}
|
74 |
+
|
75 |
+
# Omdurman National Bank-specific guidelines in English
|
76 |
+
ONB_GUIDELINES_EN = {
|
77 |
+
"balance": "You can check your balance online or via the ONB mobile app. <a href='#' onclick='window.open(\"https://onb.sd/balance\", \"_blank\")'>Check your balance now</a>",
|
78 |
+
"lost_card": "In case of a lost card, call <a href='tel:249123456789'>249-123-456-789</a> immediately or <a href='#' onclick='window.open(\"https://onb.sd/block-card\", \"_blank\")'>block your card online</a>.",
|
79 |
+
"loan": "Loan requirements include minimum income (5000 SDG) and good credit history. <a href='#' onclick='window.open(\"https://onb.sd/loans\", \"_blank\")'>Apply for a loan now</a>",
|
80 |
+
"transfer": "To transfer funds, use the <a href='#' onclick='window.open(\"https://onb.sd/mobile-app\", \"_blank\")'>mobile app</a> or <a href='#' onclick='window.open(\"https://onb.sd/online-banking\", \"_blank\")'>online banking service</a>.",
|
81 |
+
"new_account": "To open a new account, visit your nearest branch with your passport or national ID. <a href='#' onclick='window.open(\"https://onb.sd/new-account\", \"_blank\")'>Book an appointment now</a>",
|
82 |
+
"interest_rates": "Interest rates on deposits range from 5% to 10% annually. <a href='#' onclick='window.open(\"https://onb.sd/rates\", \"_blank\")'>View all rates</a>",
|
83 |
+
"branches": "Our branches are located in Omdurman, Khartoum, and Port Sudan. <a href='#' onclick='window.open(\"https://onb.sd/branches\", \"_blank\")'>Find your nearest branch</a>",
|
84 |
+
"working_hours": "Working hours are from 8 AM to 3 PM, Sunday to Thursday. <a href='#' onclick='window.open(\"https://onb.sd/hours\", \"_blank\")'>Check special hours</a>",
|
85 |
+
"contact": "Contact us at <a href='tel:249123456789'>249-123-456-789</a> or via email at <a href='mailto:[email protected]'>[email protected]</a>. <a href='#' onclick='window.open(\"https://onb.sd/contact\", \"_blank\")'>Contact form</a>"
|
86 |
+
}
|
87 |
+
|
88 |
+
# Quick action buttons in Arabic
|
89 |
+
QUICK_ACTIONS_AR = [
|
90 |
+
{"text": "تحقق من الرصيد", "intent": "balance"},
|
91 |
+
{"text": "الإبلاغ عن بطاقة مفقودة", "intent": "lost_card"},
|
92 |
+
{"text": "معلومات القرض", "intent": "loan"},
|
93 |
+
{"text": "تحويل الأموال", "intent": "transfer"},
|
94 |
+
{"text": "فتح حساب جديد", "intent": "new_account"},
|
95 |
+
{"text": "أسعار الفائدة", "intent": "interest_rates"},
|
96 |
+
{"text": "مواقع الفروع", "intent": "branches"},
|
97 |
+
{"text": "ساعات العمل", "intent": "working_hours"},
|
98 |
+
{"text": "اتصل بنا", "intent": "contact"}
|
99 |
+
]
|
100 |
+
|
101 |
+
# Quick action buttons in English
|
102 |
+
QUICK_ACTIONS_EN = [
|
103 |
+
{"text": "Check Balance", "intent": "balance"},
|
104 |
+
{"text": "Report Lost Card", "intent": "lost_card"},
|
105 |
+
{"text": "Loan Information", "intent": "loan"},
|
106 |
+
{"text": "Transfer Funds", "intent": "transfer"},
|
107 |
+
{"text": "Open New Account", "intent": "new_account"},
|
108 |
+
{"text": "Interest Rates", "intent": "interest_rates"},
|
109 |
+
{"text": "Branch Locations", "intent": "branches"},
|
110 |
+
{"text": "Working Hours", "intent": "working_hours"},
|
111 |
+
{"text": "Contact Us", "intent": "contact"}
|
112 |
+
]
|
113 |
+
|
114 |
+
# Menu options in both languages
|
115 |
+
MENU_AR = """
|
116 |
+
قائمة الخدمات المصرفية:
|
117 |
+
1. رصيد - استعلام عن رصيد حسابك
|
118 |
+
2. بطاقة - الإبلاغ عن بطاقة مفقودة
|
119 |
+
3. قرض - معلومات عن القروض
|
120 |
+
4. تحويل - تحويل الأموال
|
121 |
+
5. حساب - فتح حساب جديد
|
122 |
+
6. فائدة - أسعار الفائدة
|
123 |
+
7. فرع - مواقع الفروع
|
124 |
+
8. ساعات - ساعات العمل
|
125 |
+
9. اتصال - معلومات الاتصال
|
126 |
+
"""
|
127 |
+
|
128 |
+
MENU_EN = """
|
129 |
+
Banking Services Menu:
|
130 |
+
1. balance - Check your account balance
|
131 |
+
2. card - Report a lost card
|
132 |
+
3. loan - Information about loans
|
133 |
+
4. transfer - Transfer funds
|
134 |
+
5. account - Open a new account
|
135 |
+
6. interest - Interest rates
|
136 |
+
7. branch - Branch locations
|
137 |
+
8. hours - Working hours
|
138 |
+
9. contact - Contact information
|
139 |
+
"""
|
140 |
+
|
141 |
+
# Map intents to keywords (enhanced)
|
142 |
+
INTENT_KEYWORDS = {
|
143 |
+
"balance": ["balance", "check balance", "account balance", "how much", "رصيد", "حساب", "كم المبلغ", "1"],
|
144 |
+
"lost_card": ["lost", "card", "stolen", "missing", "فقدت", "بطاقة", "مسروقة", "ضائعة", "2"],
|
145 |
+
"loan": ["loan", "borrow", "borrowing", "credit", "قرض", "استدانة", "إئتمان", "3"],
|
146 |
+
"transfer": ["transfer", "send money", "payment", "تحويل", "ارسال", "دفع", "4"],
|
147 |
+
"new_account": ["account", "open", "create", "new", "حساب", "فتح", "جديد", "إنشاء", "5"],
|
148 |
+
"interest_rates": ["interest", "rate", "rates", "return", "فائدة", "نسبة", "عائد", "6"],
|
149 |
+
"branches": ["branch", "location", "where", "office", "فرع", "موقع", "أين", "مكتب", "7"],
|
150 |
+
"working_hours": ["hours", "time", "open", "close", "ساعات", "وقت", "مفتوح", "مغلق", "8"],
|
151 |
+
"contact": ["contact", "phone", "email", "call", "اتصال", "هاتف", "بريد", "اتصل", "9"]
|
152 |
+
}
|
153 |
+
|
154 |
+
# Function to get a random phrase from the customer service phrases
|
155 |
+
def get_random_phrase(category, language):
|
156 |
+
import random
|
157 |
+
if language == "ar":
|
158 |
+
return random.choice(ENHANCED_CUSTOMER_SERVICE_PHRASES_AR[category])
|
159 |
+
else:
|
160 |
+
return random.choice(ENHANCED_CUSTOMER_SERVICE_PHRASES_EN[category])
|
161 |
+
|
162 |
+
def classify_intent(message: str):
|
163 |
+
# Check for menu request
|
164 |
+
menu_keywords = ["menu", "options", "help", "قائمة", "خيارات", "مساعدة"]
|
165 |
+
message_lower = message.lower()
|
166 |
+
|
167 |
+
for keyword in menu_keywords:
|
168 |
+
if keyword in message_lower:
|
169 |
+
return "menu"
|
170 |
+
|
171 |
+
# Use keyword matching for intent classification
|
172 |
+
for intent_key, keywords in INTENT_KEYWORDS.items():
|
173 |
+
for keyword in keywords:
|
174 |
+
if keyword.lower() in message_lower:
|
175 |
+
return intent_key
|
176 |
+
|
177 |
+
return "unknown"
|
178 |
+
|
179 |
+
# Function to log customer interactions
|
180 |
+
def log_interaction(user_message, bot_response, intent, language):
|
181 |
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
182 |
+
log_entry = {
|
183 |
+
"timestamp": timestamp,
|
184 |
+
"user_message": user_message,
|
185 |
+
"bot_response": bot_response,
|
186 |
+
"intent": intent,
|
187 |
+
"language": language
|
188 |
+
}
|
189 |
+
|
190 |
+
try:
|
191 |
+
with open("interaction_logs.jsonl", "a") as f:
|
192 |
+
f.write(json.dumps(log_entry) + "\n")
|
193 |
+
except Exception as e:
|
194 |
+
print(f"Error logging interaction: {e}")
|
195 |
+
|
196 |
+
def respond(message: str):
|
197 |
+
if not message.strip():
|
198 |
+
return {
|
199 |
+
"ar": "الرجاء كتابة سؤالك.",
|
200 |
+
"en": "Please type your question."
|
201 |
+
}
|
202 |
+
|
203 |
+
# Detect language using simple function
|
204 |
+
language = simple_detect_language(message)
|
205 |
+
|
206 |
+
# Classify the user's intent using keyword matching
|
207 |
+
intent = classify_intent(message)
|
208 |
+
|
209 |
+
# Prepare responses in both languages
|
210 |
+
responses = {
|
211 |
+
"ar": "",
|
212 |
+
"en": ""
|
213 |
+
}
|
214 |
+
|
215 |
+
# Special handling for menu request
|
216 |
+
if intent == "menu":
|
217 |
+
responses["ar"] = MENU_AR
|
218 |
+
responses["en"] = MENU_EN
|
219 |
+
log_interaction(message, responses[language], "menu", language)
|
220 |
+
return responses
|
221 |
+
|
222 |
+
# Check if it's a banking FAQ
|
223 |
+
if CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE:
|
224 |
+
faq_answer = handle_banking_faq(message, language)
|
225 |
+
if faq_answer:
|
226 |
+
# Add a greeting phrase at the beginning
|
227 |
+
greeting_ar = get_random_phrase("greeting", "ar")
|
228 |
+
greeting_en = get_random_phrase("greeting", "en")
|
229 |
+
|
230 |
+
# Add a follow-up phrase at the end
|
231 |
+
follow_up_ar = get_random_phrase("follow_up", "ar")
|
232 |
+
follow_up_en = get_random_phrase("follow_up", "en")
|
233 |
+
|
234 |
+
# Combine all parts
|
235 |
+
responses["ar"] = f"{greeting_ar}<br><br>{faq_answer}<br><br>{follow_up_ar}"
|
236 |
+
responses["en"] = f"{greeting_en}<br><br>{faq_answer}<br><br>{follow_up_en}"
|
237 |
+
|
238 |
+
log_interaction(message, responses[language], "faq", language)
|
239 |
+
return responses
|
240 |
+
|
241 |
+
# Check if it's a banking term definition request
|
242 |
+
if CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE:
|
243 |
+
term_definition = get_banking_term_definition(message, language)
|
244 |
+
if term_definition:
|
245 |
+
# Add a greeting phrase at the beginning
|
246 |
+
greeting_ar = get_random_phrase("greeting", "ar")
|
247 |
+
greeting_en = get_random_phrase("greeting", "en")
|
248 |
+
|
249 |
+
# Add a follow-up phrase at the end
|
250 |
+
follow_up_ar = get_random_phrase("follow_up", "ar")
|
251 |
+
follow_up_en = get_random_phrase("follow_up", "en")
|
252 |
+
|
253 |
+
# Combine all parts
|
254 |
+
responses["ar"] = f"{greeting_ar}<br><br>{term_definition}<br><br>{follow_up_ar}"
|
255 |
+
responses["en"] = f"{greeting_en}<br><br>{term_definition}<br><br>{follow_up_en}"
|
256 |
+
|
257 |
+
log_interaction(message, responses[language], "term", language)
|
258 |
+
return responses
|
259 |
+
|
260 |
+
# If intent is recognized, return the corresponding response
|
261 |
+
if intent != "unknown":
|
262 |
+
if CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE:
|
263 |
+
# Use enhanced response if available
|
264 |
+
responses["ar"] = get_enhanced_response(intent, "ar")
|
265 |
+
responses["en"] = get_enhanced_response(intent, "en")
|
266 |
+
else:
|
267 |
+
# Add a greeting phrase at the beginning
|
268 |
+
greeting_ar = get_random_phrase("greeting", "ar")
|
269 |
+
greeting_en = get_random_phrase("greeting", "en")
|
270 |
+
|
271 |
+
# Add the main response
|
272 |
+
main_response_ar = ONB_GUIDELINES_AR.get(intent, "عذرًا، لم يتم التعرف على الخيار المحدد.")
|
273 |
+
main_response_en = ONB_GUIDELINES_EN.get(intent, "Sorry, the selected option was not recognized.")
|
274 |
+
|
275 |
+
# Add a follow-up phrase at the end
|
276 |
+
follow_up_ar = get_random_phrase("follow_up", "ar")
|
277 |
+
follow_up_en = get_random_phrase("follow_up", "en")
|
278 |
+
|
279 |
+
# Combine all parts
|
280 |
+
responses["ar"] = f"{greeting_ar}<br><br>{main_response_ar}<br><br>{follow_up_ar}"
|
281 |
+
responses["en"] = f"{greeting_en}<br><br>{main_response_en}<br><br>{follow_up_en}"
|
282 |
+
else:
|
283 |
+
# Default response if no intent is matched - show menu
|
284 |
+
responses["ar"] = "عذرًا، لم أفهم سؤالك. إليك قائمة بالخدمات المتاحة:" + MENU_AR
|
285 |
+
responses["en"] = "Sorry, I didn't understand your question. Here's a menu of available services:" + MENU_EN
|
286 |
+
|
287 |
+
# Log the interaction
|
288 |
+
log_interaction(message, responses[language], intent, language)
|
289 |
+
|
290 |
+
return responses
|
291 |
+
|
292 |
+
# Custom CSS for better UI
|
293 |
+
custom_css = """
|
294 |
+
.gradio-container {
|
295 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
296 |
+
}
|
297 |
+
|
298 |
+
.chat-message {
|
299 |
+
padding: 1rem;
|
300 |
+
border-radius: 10px;
|
301 |
+
margin-bottom: 1rem;
|
302 |
+
max-width: 80%;
|
303 |
+
}
|
304 |
+
|
305 |
+
.user-message {
|
306 |
+
background-color: #e6f7ff;
|
307 |
+
margin-left: auto;
|
308 |
+
text-align: right;
|
309 |
+
}
|
310 |
+
|
311 |
+
.bot-message {
|
312 |
+
background-color: #f0f0f0;
|
313 |
+
margin-right: auto;
|
314 |
+
text-align: left;
|
315 |
+
}
|
316 |
+
|
317 |
+
.bot-message-ar {
|
318 |
+
background-color: #f0f0f0;
|
319 |
+
margin-left: auto;
|
320 |
+
text-align: right;
|
321 |
+
}
|
322 |
+
|
323 |
+
.header-section {
|
324 |
+
background-color: #1a5276;
|
325 |
+
color: white;
|
326 |
+
padding: 1rem;
|
327 |
+
border-radius: 10px;
|
328 |
+
margin-bottom: 1rem;
|
329 |
+
text-align: center;
|
330 |
+
}
|
331 |
+
|
332 |
+
.footer-section {
|
333 |
+
font-size: 0.8rem;
|
334 |
+
text-align: center;
|
335 |
+
margin-top: 2rem;
|
336 |
+
color: #666;
|
337 |
+
}
|
338 |
+
|
339 |
+
.lang-selector {
|
340 |
+
text-align: right;
|
341 |
+
margin-bottom: 1rem;
|
342 |
+
}
|
343 |
+
|
344 |
+
.menu-button {
|
345 |
+
margin-top: 0.5rem;
|
346 |
+
}
|
347 |
+
|
348 |
+
.quick-actions {
|
349 |
+
display: flex;
|
350 |
+
flex-wrap: wrap;
|
351 |
+
gap: 0.5rem;
|
352 |
+
margin: 1rem 0;
|
353 |
+
}
|
354 |
+
|
355 |
+
.quick-action-button {
|
356 |
+
background-color: #1a5276;
|
357 |
+
color: white;
|
358 |
+
border: none;
|
359 |
+
border-radius: 20px;
|
360 |
+
padding: 0.5rem 1rem;
|
361 |
+
cursor: pointer;
|
362 |
+
font-size: 0.9rem;
|
363 |
+
transition: background-color 0.3s;
|
364 |
+
}
|
365 |
+
|
366 |
+
.quick-action-button:hover {
|
367 |
+
background-color: #2980b9;
|
368 |
+
}
|
369 |
+
|
370 |
+
.chat-container {
|
371 |
+
border: 1px solid #ddd;
|
372 |
+
border-radius: 10px;
|
373 |
+
padding: 1rem;
|
374 |
+
background-color: #f9f9f9;
|
375 |
+
}
|
376 |
+
|
377 |
+
.typing-indicator {
|
378 |
+
display: inline-block;
|
379 |
+
width: 50px;
|
380 |
+
text-align: left;
|
381 |
+
}
|
382 |
+
|
383 |
+
.typing-indicator span {
|
384 |
+
display: inline-block;
|
385 |
+
width: 8px;
|
386 |
+
height: 8px;
|
387 |
+
background-color: #1a5276;
|
388 |
+
border-radius: 50%;
|
389 |
+
margin-right: 5px;
|
390 |
+
animation: typing 1s infinite;
|
391 |
+
}
|
392 |
+
|
393 |
+
.typing-indicator span:nth-child(2) {
|
394 |
+
animation-delay: 0.2s;
|
395 |
+
}
|
396 |
+
|
397 |
+
.typing-indicator span:nth-child(3) {
|
398 |
+
animation-delay: 0.4s;
|
399 |
+
}
|
400 |
+
|
401 |
+
@keyframes typing {
|
402 |
+
0%, 100% {
|
403 |
+
transform: translateY(0);
|
404 |
+
}
|
405 |
+
50% {
|
406 |
+
transform: translateY(-5px);
|
407 |
+
}
|
408 |
+
}
|
409 |
+
|
410 |
+
.live-agent-button {
|
411 |
+
background-color: #27ae60;
|
412 |
+
color: white;
|
413 |
+
border: none;
|
414 |
+
border-radius: 5px;
|
415 |
+
padding: 0.5rem 1rem;
|
416 |
+
cursor: pointer;
|
417 |
+
font-size: 0.9rem;
|
418 |
+
margin-top: 1rem;
|
419 |
+
transition: background-color 0.3s;
|
420 |
+
}
|
421 |
+
|
422 |
+
.live-agent-button:hover {
|
423 |
+
background-color: #2ecc71;
|
424 |
+
}
|
425 |
+
|
426 |
+
/* Add custom styling for links */
|
427 |
+
a {
|
428 |
+
color: #2980b9;
|
429 |
+
text-decoration: none;
|
430 |
+
font-weight: bold;
|
431 |
+
}
|
432 |
+
|
433 |
+
a:hover {
|
434 |
+
text-decoration: underline;
|
435 |
+
}
|
436 |
+
|
437 |
+
/* Add styling for action buttons */
|
438 |
+
.action-button {
|
439 |
+
display: inline-block;
|
440 |
+
background-color: #3498db;
|
441 |
+
color: white;
|
442 |
+
padding: 0.5rem 1rem;
|
443 |
+
border-radius: 5px;
|
444 |
+
margin: 0.5rem 0;
|
445 |
+
text-decoration: none;
|
446 |
+
}
|
447 |
+
|
448 |
+
.action-button:hover {
|
449 |
+
background-color: #2980b9;
|
450 |
+
text-decoration: none;
|
451 |
+
}
|
452 |
+
|
453 |
+
/* Styling for satisfaction survey */
|
454 |
+
.satisfaction-survey {
|
455 |
+
background-color: #f8f9fa;
|
456 |
+
border: 1px solid #ddd;
|
457 |
+
border-radius: 10px;
|
458 |
+
padding: 1rem;
|
459 |
+
margin-top: 1rem;
|
460 |
+
}
|
461 |
+
|
462 |
+
.survey-question {
|
463 |
+
margin-bottom: 1rem;
|
464 |
+
}
|
465 |
+
|
466 |
+
.rating-options {
|
467 |
+
display: flex;
|
468 |
+
justify-content: space-between;
|
469 |
+
margin-bottom: 0.5rem;
|
470 |
+
}
|
471 |
+
|
472 |
+
.survey-submit {
|
473 |
+
background-color: #1a5276;
|
474 |
+
color: white;
|
475 |
+
border: none;
|
476 |
+
border-radius: 5px;
|
477 |
+
padding: 0.5rem 1rem;
|
478 |
+
cursor: pointer;
|
479 |
+
font-size: 0.9rem;
|
480 |
+
margin-top: 1rem;
|
481 |
+
}
|
482 |
+
|
483 |
+
.survey-submit:hover {
|
484 |
+
background-color: #2980b9;
|
485 |
+
}
|
486 |
+
"""
|
487 |
+
|
488 |
+
# Custom JavaScript for enhanced functionality
|
489 |
+
custom_js = """
|
490 |
+
function simulateTyping(message, elementId, delay = 30) {
|
491 |
+
const element = document.getElementById(elementId);
|
492 |
+
if (!element) return;
|
493 |
+
|
494 |
+
element.innerHTML = "";
|
495 |
+
let i = 0;
|
496 |
+
|
497 |
+
function type() {
|
498 |
+
if (i < message.length) {
|
499 |
+
element.innerHTML += message.charAt(i);
|
500 |
+
i++;
|
501 |
+
setTimeout(type, delay);
|
502 |
+
}
|
503 |
+
}
|
504 |
+
|
505 |
+
type();
|
506 |
+
}
|
507 |
+
|
508 |
+
// Function to show typing indicator
|
509 |
+
function showTypingIndicator() {
|
510 |
+
const chatbox = document.getElementById('chatbox');
|
511 |
+
if (!chatbox) return;
|
512 |
+
|
513 |
+
const typingIndicator = document.createElement('div');
|
514 |
+
typingIndicator.className = 'typing-indicator';
|
515 |
+
typingIndicator.id = 'typing-indicator';
|
516 |
+
typingIndicator.innerHTML = '<span></span><span></span><span></span>';
|
517 |
+
|
518 |
+
chatbox.appendChild(typingIndicator);
|
519 |
+
chatbox.scrollTop = chatbox.scrollHeight;
|
520 |
+
}
|
521 |
+
|
522 |
+
// Function to hide typing indicator
|
523 |
+
function hideTypingIndicator() {
|
524 |
+
const typingIndicator = document.getElementById('typing-indicator');
|
525 |
+
if (typingIndicator) {
|
526 |
+
typingIndicator.remove();
|
527 |
+
}
|
528 |
+
}
|
529 |
+
|
530 |
+
// Function to connect with a live agent
|
531 |
+
function connectLiveAgent() {
|
532 |
+
alert('Connecting to a live customer service agent. Please wait a moment...');
|
533 |
+
// In a real implementation, this would initiate a connection to a live agent system
|
534 |
+
}
|
535 |
+
|
536 |
+
// Function to show satisfaction survey
|
537 |
+
function showSatisfactionSurvey(surveyHtml) {
|
538 |
+
const chatbox = document.getElementById('chatbox');
|
539 |
+
if (!chatbox) return;
|
540 |
+
|
541 |
+
const surveyDiv = document.createElement('div');
|
542 |
+
surveyDiv.innerHTML = surveyHtml;
|
543 |
+
|
544 |
+
chatbox.appendChild(surveyDiv);
|
545 |
+
chatbox.scrollTop = chatbox.scrollHeight;
|
546 |
+
}
|
547 |
+
|
548 |
+
// Function to submit survey
|
549 |
+
function submitSurvey() {
|
550 |
+
const form = document.getElementById('satisfaction-form');
|
551 |
+
if (!form) return;
|
552 |
+
|
553 |
+
// In a real implementation, this would send the survey data to a server
|
554 |
+
alert('Thank you for your feedback!');
|
555 |
+
form.style.display = 'none';
|
556 |
+
}
|
557 |
+
"""
|
558 |
+
|
559 |
+
# Chat interface with enhanced UI
|
560 |
+
with gr.Blocks(css=custom_css, js=custom_js) as demo:
|
561 |
+
# Store conversation history
|
562 |
+
state = gr.State(value=[])
|
563 |
+
# Store selected language
|
564 |
+
selected_lang = gr.State(value="ar")
|
565 |
+
# Store user name for personalization
|
566 |
+
user_name = gr.State(value=None)
|
567 |
+
|
568 |
+
with gr.Row(elem_classes="header-section"):
|
569 |
+
with gr.Column():
|
570 |
+
gr.Markdown("# Omdurman National Bank | بنك أم درمان الوطني")
|
571 |
+
gr.Markdown("### Virtual Banking Assistant | المساعد المصرفي الافتراضي")
|
572 |
+
|
573 |
+
with gr.Row():
|
574 |
+
with gr.Column(elem_classes="lang-selector"):
|
575 |
+
language_btn = gr.Radio(
|
576 |
+
["العربية", "English"],
|
577 |
+
value="العربية",
|
578 |
+
label="Language | اللغة"
|
579 |
+
)
|
580 |
+
|
581 |
+
with gr.Row(elem_classes="chat-container"):
|
582 |
+
chat_box = gr.HTML(elem_id="chatbox", value="<div style='height: 400px; overflow-y: auto;'></div>")
|
583 |
+
|
584 |
+
# Quick action buttons (will be populated based on language)
|
585 |
+
with gr.Row(elem_classes="quick-actions", visible=True) as quick_actions_container:
|
586 |
+
quick_action_buttons = []
|
587 |
+
for i in range(9): # Create 9 buttons (one for each intent)
|
588 |
+
button = gr.Button("", visible=False, elem_classes="quick-action-button")
|
589 |
+
quick_action_buttons.append(button)
|
590 |
+
|
591 |
+
with gr.Row():
|
592 |
+
with gr.Column(scale=8):
|
593 |
+
text_input = gr.Textbox(
|
594 |
+
placeholder="Type your question here | اكتب سؤالك هنا",
|
595 |
+
label="",
|
596 |
+
elem_id="chat-input"
|
597 |
+
)
|
598 |
+
with gr.Column(scale=1):
|
599 |
+
submit_btn = gr.Button("Send | إرسال", variant="primary")
|
600 |
+
|
601 |
+
with gr.Row():
|
602 |
+
with gr.Column(scale=1):
|
603 |
+
menu_btn = gr.Button("Show Menu | إظهار القائمة", elem_classes="menu-button")
|
604 |
+
with gr.Column(scale=1):
|
605 |
+
live_agent_btn = gr.Button("Connect to Live Agent | الاتصال بوكيل حي", elem_classes="live-agent-button")
|
606 |
+
with gr.Column(scale=1):
|
607 |
+
survey_btn = gr.Button("Feedback | تقييم الخدمة", elem_classes="menu-button")
|
608 |
+
|
609 |
+
with gr.Row(elem_classes="footer-section"):
|
610 |
+
gr.Markdown("© 2025 Omdurman National Bank. All Rights Reserved. | جميع الحقوق محفوظة لبنك أم درمان الوطني ٢٠٢٥ ©")
|
611 |
+
|
612 |
+
# Update language state and quick action buttons when language is changed
|
613 |
+
def update_language_and_buttons(lang):
|
614 |
+
language_code = "ar" if lang == "العربية" else "en"
|
615 |
+
|
616 |
+
# Get the appropriate quick actions based on language
|
617 |
+
quick_actions = QUICK_ACTIONS_AR if language_code == "ar" else QUICK_ACTIONS_EN
|
618 |
+
|
619 |
+
# Update button visibility and text
|
620 |
+
button_updates = []
|
621 |
+
for i, button in enumerate(quick_action_buttons):
|
622 |
+
if i < len(quick_actions):
|
623 |
+
button_updates.append(gr.Button.update(visible=True, value=quick_actions[i]["text"]))
|
624 |
+
else:
|
625 |
+
button_updates.append(gr.Button.update(visible=False))
|
626 |
+
|
627 |
+
return [language_code] + button_updates
|
628 |
+
|
629 |
+
# Connect language button to update function
|
630 |
+
outputs = [selected_lang] + quick_action_buttons
|
631 |
+
language_btn.change(
|
632 |
+
fn=update_language_and_buttons,
|
633 |
+
inputs=language_btn,
|
634 |
+
outputs=outputs
|
635 |
+
)
|
636 |
+
|
637 |
+
# Function to add message to chat
|
638 |
+
def add_message_to_chat(message, is_user, lang):
|
639 |
+
# Create a JavaScript function to add the message to the chat
|
640 |
+
alignment = "right" if (is_user or (not is_user and lang == "ar")) else "left"
|
641 |
+
background = "#e6f7ff" if is_user else "#f0f0f0"
|
642 |
+
|
643 |
+
js_code = f"""
|
644 |
+
(function() {{
|
645 |
+
const chatbox = document.getElementById('chatbox').querySelector('div');
|
646 |
+
const messageDiv = document.createElement('div');
|
647 |
+
messageDiv.style.padding = '1rem';
|
648 |
+
messageDiv.style.borderRadius = '10px';
|
649 |
+
messageDiv.style.marginBottom = '1rem';
|
650 |
+
messageDiv.style.maxWidth = '80%';
|
651 |
+
messageDiv.style.backgroundColor = '{background}';
|
652 |
+
messageDiv.style.marginLeft = alignment === "right" ? "auto" : "0";
|
653 |
+
messageDiv.style.marginRight = alignment === "left" ? "auto" : "0";
|
654 |
+
messageDiv.style.textAlign = alignment;
|
655 |
+
messageDiv.innerHTML = `{message}`;
|
656 |
+
chatbox.appendChild(messageDiv);
|
657 |
+
chatbox.scrollTop = chatbox.scrollHeight;
|
658 |
+
}})();
|
659 |
+
"""
|
660 |
+
|
661 |
+
return js_code
|
662 |
+
|
663 |
+
# Handle message submission with typing effect
|
664 |
+
def on_submit(message, chat_history, lang, name):
|
665 |
+
if not message.strip():
|
666 |
+
return "", chat_history, "", name
|
667 |
+
|
668 |
+
# Check if this is a name introduction
|
669 |
+
name_patterns = [
|
670 |
+
r"my name is (\w+)",
|
671 |
+
r"i am (\w+)",
|
672 |
+
r"i'm (\w+)",
|
673 |
+
r"اسمي (\w+)",
|
674 |
+
r"أنا (\w+)"
|
675 |
+
]
|
676 |
+
|
677 |
+
for pattern in name_patterns:
|
678 |
+
match = re.search(pattern, message.lower())
|
679 |
+
if match:
|
680 |
+
name = match.group(1)
|
681 |
+
break
|
682 |
+
|
683 |
+
# Add user message to chat
|
684 |
+
user_js = add_message_to_chat(message, True, lang)
|
685 |
+
|
686 |
+
# Show typing indicator
|
687 |
+
typing_js = "showTypingIndicator();"
|
688 |
+
|
689 |
+
# Get response
|
690 |
+
responses = respond(message)
|
691 |
+
|
692 |
+
# Select response based on language
|
693 |
+
response = responses[lang]
|
694 |
+
|
695 |
+
# Personalize response if name is available
|
696 |
+
if name and CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE:
|
697 |
+
if lang == "ar":
|
698 |
+
response = response.replace("مرحبًا", f"مرحبًا {name}")
|
699 |
+
else:
|
700 |
+
response = response.replace("Welcome", f"Welcome {name}")
|
701 |
+
response = response.replace("Hello", f"Hello {name}")
|
702 |
+
|
703 |
+
# Hide typing indicator and add bot response
|
704 |
+
bot_js = f"""
|
705 |
+
setTimeout(function() {{
|
706 |
+
hideTypingIndicator();
|
707 |
+
{add_message_to_chat(response, False, lang)}
|
708 |
+
}}, 1000);
|
709 |
+
"""
|
710 |
+
|
711 |
+
# Combine all JavaScript
|
712 |
+
combined_js = user_js + typing_js + bot_js
|
713 |
+
|
714 |
+
return "", chat_history, combined_js, name
|
715 |
+
|
716 |
+
# Handle menu button click
|
717 |
+
def show_menu(chat_history, lang):
|
718 |
+
menu_responses = {
|
719 |
+
"ar": MENU_AR,
|
720 |
+
"en": MENU_EN
|
721 |
+
}
|
722 |
+
|
723 |
+
# Get menu text
|
724 |
+
menu_text = menu_responses[lang]
|
725 |
+
|
726 |
+
# Add system message showing the menu
|
727 |
+
js_code = add_message_to_chat(menu_text.replace("\n", "<br>"), False, lang)
|
728 |
+
|
729 |
+
return chat_history, js_code
|
730 |
+
|
731 |
+
# Handle quick action button clicks
|
732 |
+
def handle_quick_action(button_index, chat_history, lang, name):
|
733 |
+
# Get the appropriate quick actions based on language
|
734 |
+
quick_actions = QUICK_ACTIONS_AR if lang == "ar" else QUICK_ACTIONS_EN
|
735 |
+
|
736 |
+
if button_index < len(quick_actions):
|
737 |
+
# Get the intent for this button
|
738 |
+
intent = quick_actions[button_index]["intent"]
|
739 |
+
|
740 |
+
# Get the response for this intent
|
741 |
+
if CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE:
|
742 |
+
# Use enhanced response if available
|
743 |
+
response_ar = get_enhanced_response(intent, "ar", name)
|
744 |
+
response_en = get_enhanced_response(intent, "en", name)
|
745 |
+
else:
|
746 |
+
# Add a greeting phrase at the beginning
|
747 |
+
greeting_ar = get_random_phrase("greeting", "ar")
|
748 |
+
greeting_en = get_random_phrase("greeting", "en")
|
749 |
+
|
750 |
+
# Add the main response
|
751 |
+
main_response_ar = ONB_GUIDELINES_AR.get(intent, "")
|
752 |
+
main_response_en = ONB_GUIDELINES_EN.get(intent, "")
|
753 |
+
|
754 |
+
# Add a follow-up phrase at the end
|
755 |
+
follow_up_ar = get_random_phrase("follow_up", "ar")
|
756 |
+
follow_up_en = get_random_phrase("follow_up", "en")
|
757 |
+
|
758 |
+
# Combine all parts
|
759 |
+
response_ar = f"{greeting_ar}<br><br>{main_response_ar}<br><br>{follow_up_ar}"
|
760 |
+
response_en = f"{greeting_en}<br><br>{main_response_en}<br><br>{follow_up_en}"
|
761 |
+
|
762 |
+
responses = {
|
763 |
+
"ar": response_ar,
|
764 |
+
"en": response_en
|
765 |
+
}
|
766 |
+
|
767 |
+
# Select response based on language
|
768 |
+
response = responses[lang]
|
769 |
+
|
770 |
+
# Personalize response if name is available
|
771 |
+
if name:
|
772 |
+
if lang == "ar":
|
773 |
+
response = response.replace("مرحبًا", f"مرحبًا {name}")
|
774 |
+
else:
|
775 |
+
response = response.replace("Welcome", f"Welcome {name}")
|
776 |
+
response = response.replace("Hello", f"Hello {name}")
|
777 |
+
|
778 |
+
# Add button text as user message
|
779 |
+
button_text = quick_actions[button_index]["text"]
|
780 |
+
user_js = add_message_to_chat(button_text, True, lang)
|
781 |
+
|
782 |
+
# Show typing indicator
|
783 |
+
typing_js = "showTypingIndicator();"
|
784 |
+
|
785 |
+
# Hide typing indicator and add bot response
|
786 |
+
bot_js = f"""
|
787 |
+
setTimeout(function() {{
|
788 |
+
hideTypingIndicator();
|
789 |
+
{add_message_to_chat(response, False, lang)}
|
790 |
+
}}, 1000);
|
791 |
+
"""
|
792 |
+
|
793 |
+
# Combine all JavaScript
|
794 |
+
combined_js = user_js + typing_js + bot_js
|
795 |
+
|
796 |
+
# Log the interaction
|
797 |
+
log_interaction(button_text, response, intent, lang)
|
798 |
+
|
799 |
+
return chat_history, combined_js
|
800 |
+
|
801 |
+
return chat_history, ""
|
802 |
+
|
803 |
+
# Handle live agent button click
|
804 |
+
def connect_to_live_agent():
|
805 |
+
return "connectLiveAgent();"
|
806 |
+
|
807 |
+
# Handle satisfaction survey button click
|
808 |
+
def show_satisfaction_survey(lang):
|
809 |
+
if CUSTOMER_SERVICE_ENHANCEMENTS_AVAILABLE:
|
810 |
+
survey_html = offer_satisfaction_survey(lang)
|
811 |
+
return f"showSatisfactionSurvey(`{survey_html}`);"
|
812 |
+
else:
|
813 |
+
# Simple survey HTML if enhancements not available
|
814 |
+
title = "استطلاع رضا العملاء" if lang == "ar" else "Customer Satisfaction Survey"
|
815 |
+
intro = "نقدر ملاحظاتك!" if lang == "ar" else "We value your feedback!"
|
816 |
+
submit = "إرسال" if lang == "ar" else "Submit"
|
817 |
+
|
818 |
+
survey_html = f"""
|
819 |
+
<div class="satisfaction-survey" dir="{('rtl' if lang == 'ar' else 'ltr')}">
|
820 |
+
<h3>{title}</h3>
|
821 |
+
<p>{intro}</p>
|
822 |
+
<button onclick="submitSurvey()" class="survey-submit">{submit}</button>
|
823 |
+
</div>
|
824 |
+
"""
|
825 |
+
|
826 |
+
return f"showSatisfactionSurvey(`{survey_html}`);"
|
827 |
+
|
828 |
+
# Link inputs and button to response function
|
829 |
+
submit_btn.click(
|
830 |
+
fn=on_submit,
|
831 |
+
inputs=[text_input, state, selected_lang, user_name],
|
832 |
+
outputs=[text_input, state, chat_box, user_name]
|
833 |
+
)
|
834 |
+
|
835 |
+
# Link menu button to show menu function
|
836 |
+
menu_btn.click(
|
837 |
+
fn=show_menu,
|
838 |
+
inputs=[state, selected_lang],
|
839 |
+
outputs=[state, chat_box]
|
840 |
+
)
|
841 |
+
|
842 |
+
# Link live agent button to connect function
|
843 |
+
live_agent_btn.click(
|
844 |
+
fn=connect_to_live_agent,
|
845 |
+
inputs=[],
|
846 |
+
outputs=[chat_box]
|
847 |
+
)
|
848 |
+
|
849 |
+
# Link survey button to show survey function
|
850 |
+
survey_btn.click(
|
851 |
+
fn=show_satisfaction_survey,
|
852 |
+
inputs=[selected_lang],
|
853 |
+
outputs=[chat_box]
|
854 |
+
)
|
855 |
+
|
856 |
+
# Link quick action buttons to handler function
|
857 |
+
for i, button in enumerate(quick_action_buttons):
|
858 |
+
button.click(
|
859 |
+
fn=lambda idx=i, s=state, l=selected_lang, n=user_name: handle_quick_action(idx, s, l, n),
|
860 |
+
inputs=[state, selected_lang, user_name],
|
861 |
+
outputs=[state, chat_box]
|
862 |
+
)
|
863 |
+
|
864 |
+
# Also trigger on Enter key
|
865 |
+
text_input.submit(
|
866 |
+
fn=on_submit,
|
867 |
+
inputs=[text_input, state, selected_lang, user_name],
|
868 |
+
outputs=[text_input, state, chat_box, user_name]
|
869 |
+
)
|
870 |
+
|
871 |
+
# Initialize the chat with a welcome message
|
872 |
+
def init_chat(lang):
|
873 |
+
# Get welcome message based on language
|
874 |
+
welcome_ar = """
|
875 |
+
<div style='text-align: center; margin-bottom: 20px;'>
|
876 |
+
<img src='https://via.placeholder.com/150?text=ONB+Logo' alt='ONB Logo' style='max-width: 150px;'>
|
877 |
+
<h3>مرحبًا بك في المساعد المصرفي الافتراضي لبنك أم درمان الوطني!</h3>
|
878 |
+
<p>يمكنك طرح أي سؤال حول خدماتنا المصرفية أو استخدام أزرار الإجراءات السريعة أدناه.</p>
|
879 |
+
</div>
|
880 |
+
"""
|
881 |
+
|
882 |
+
welcome_en = """
|
883 |
+
<div style='text-align: center; margin-bottom: 20px;'>
|
884 |
+
<img src='https://via.placeholder.com/150?text=ONB+Logo' alt='ONB Logo' style='max-width: 150px;'>
|
885 |
+
<h3>Welcome to Omdurman National Bank Virtual Banking Assistant!</h3>
|
886 |
+
<p>You can ask any question about our banking services or use the quick action buttons below.</p>
|
887 |
+
</div>
|
888 |
+
"""
|
889 |
+
|
890 |
+
welcome_message = welcome_ar if lang == "ar" else welcome_en
|
891 |
+
|
892 |
+
# Add welcome message to chat
|
893 |
+
js_code = f"""
|
894 |
+
(function() {{
|
895 |
+
const chatbox = document.getElementById('chatbox').querySelector('div');
|
896 |
+
chatbox.innerHTML = `{welcome_message}`;
|
897 |
+
}})();
|
898 |
+
"""
|
899 |
+
|
900 |
+
# Update quick action buttons
|
901 |
+
quick_actions = QUICK_ACTIONS_AR if lang == "ar" else QUICK_ACTIONS_EN
|
902 |
+
button_updates = []
|
903 |
+
for i, button in enumerate(quick_action_buttons):
|
904 |
+
if i < len(quick_actions):
|
905 |
+
button_updates.append(gr.Button.update(visible=True, value=quick_actions[i]["text"]))
|
906 |
+
else:
|
907 |
+
button_updates.append(gr.Button.update(visible=False))
|
908 |
+
|
909 |
+
return [js_code] + button_updates
|
910 |
+
|
911 |
+
# Initialize the chat when the app starts
|
912 |
+
demo.load(
|
913 |
+
fn=lambda: init_chat("ar"),
|
914 |
+
inputs=[],
|
915 |
+
outputs=[chat_box] + quick_action_buttons
|
916 |
+
)
|
917 |
+
|
918 |
+
if __name__ == "__main__":
|
919 |
+
demo.launch(
|
920 |
+
server_name="0.0.0.0",
|
921 |
+
server_port=7860,
|
922 |
+
share=True # Enable public link
|
923 |
+
)
|