NimrodDev commited on
Commit
13c99d8
·
1 Parent(s): 3b47ed4
Files changed (1) hide show
  1. app.py +192 -123
app.py CHANGED
@@ -1,120 +1,170 @@
1
  #!/usr/bin/env python3
2
  """
3
  LD Events + Lamaki Designs Intelligent Secretary Bot
4
- Dynamic, low-latency, dictionary + rule + entity-based.
5
- Covers LD Events and Lamaki Designs services dynamically.
6
  """
7
 
8
  from __future__ import annotations
9
  import os
10
  import re
11
- from typing import Dict, List
12
  from flask import Flask, request, jsonify
13
 
14
  # ---------- CONFIG ----------
15
  VERIFY_TOKEN = os.getenv("WEBHOOK_VERIFY", "ldlamaki2025")
16
 
17
- # ---------- NO DB ----------
18
  def save_msg(user: str, text: str, role: str = "assistant"):
19
- return # no-op, kept for future logging
20
 
21
  # ==========================================================
22
  # NORMALIZATION
23
  # ==========================================================
24
- _rx_clean = re.compile(r"[^a-z0-9]+")
 
25
  def normalize(text: str) -> str:
26
  return _rx_clean.sub(" ", text.lower()).strip()
27
 
28
  # ==========================================================
29
- # LD EVENTS ITEMS
30
- # ==========================================================
31
- LD_ITEMS = {
32
- "line array": {"price": 4000, "unit": "per day"},
33
- "moving head": {"price": 3000, "unit": "per day"},
34
- "parcan light": {"price": 1500, "unit": "per day"},
35
- "led screen panel": {"price": 2200, "unit": "per panel"},
36
- "double 18 sub": {"price": 4000, "unit": "per day"},
37
- "single 18 sub": {"price": 3000, "unit": "per day"},
38
- "monitor speaker": {"price": 2000, "unit": "per day"},
39
- "cordless mic": {"price": 2000, "unit": "per day"},
40
- "a frame tent": {"price": 30000, "capacity": 100, "unit": "per day"},
41
- "dome tent": {"price": 45000, "capacity": 200, "unit": "per day"},
42
- "clear span tent": {"price": 80000, "capacity": 500, "unit": "per day"},
43
- "stage 8x4": {"price": 15000, "unit": "per day"},
44
- "stage 12x6": {"price": 25000, "unit": "per day"},
45
- "stage 16x8": {"price": 40000, "unit": "per day"},
46
- "dj console": {"price": 25000, "unit": "6 hrs"},
47
- "mc service": {"price": 15000, "unit": "per event"},
48
- "live band": {"price": 55000, "unit": "2 sets"},
49
- "photography 8h": {"price": 25000, "unit": "8 hours"},
50
- "videography 4k": {"price": 45000, "unit": "4 hours"},
51
- "drone 2h": {"price": 10000, "unit": "2 hours"},
52
- }
53
 
54
- LD_PACKAGES = {
55
- "bronze": ["4 speakers", "mixer", "amplifier", "cordless mic", "basic lights", "A-frame tent", "8x4 stage"],
56
- "silver": ["line arrays", "subs", "mixer", "monitors", "LED wash", "dome tent", "12x6 stage", "DJ", "MC"],
57
- "gold": ["full line-array rig", "double subs", "LED wall", "monitors", "DJ", "MC", "photo", "video"],
58
- "platinum": ["full concert rig", "live streaming", "drone", "large clear-span tent"]
59
- }
60
 
61
- LD_COMBOS = {
62
- "4 tops 2 subs combo": "4 line arrays + 2 double-18 subs + mixer + monitors + mics: KES 22k/day",
63
- "6 tops 4 subs combo": "6 line arrays + 4 double-18 subs + amp rack + monitors + mics: KES 36k/day",
64
- "wedding combo": "Line arrays + sub + monitors + mics + DJ console: KES 28k/day",
65
- "corporate combo": "Line arrays + sub + monitors + mics + projector: KES 18k/day"
66
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- LD_PHONE = "+254 113 710584"
69
- LAMAKI_PHONE = "+254 757 299 299"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  # ==========================================================
72
- # LAMAKI DESIGNS SERVICES
73
  # ==========================================================
74
- LAMAKI_SERVICES_LIST = [
75
- "New Construction (residential, commercial, institutional)",
76
- "Renovation & Refurbishment (kitchen, bathroom, interiors, roofing)",
77
- "House lighting design & installation",
78
- "Electrical wiring & fixtures",
79
- "Gypsum and fabrication works",
80
- "Architectural Designs & 3D Visualisation",
81
- "Project Management (timelines, quality control, subcontractors)",
82
- "Interior Design & Decoration",
83
- "Custom Furniture & Fabrication"
84
- ]
85
 
86
  # ==========================================================
87
- # HELPER LISTS
88
  # ==========================================================
89
  GREETINGS = ("hello", "hi", "hey", "greetings", "morning", "evening")
90
- GOODBYES = ("bye", "goodbye", "good night", "see you")
91
- LD_KEYWORDS = ("ld", "event", "wedding", "concert", "party", "dj", "stage", "tent", "light", "speaker", "sub")
92
- LAMAKI_KEYWORDS = ("lamaki", "construction", "build", "house", "renovation", "interior", "architect", "kitchen", "bathroom")
93
  SERVICE_ASK = ("services", "what do you offer", "offer", "service", "do you have")
 
 
 
 
 
 
 
 
94
 
95
  # ==========================================================
96
- # REPLY ENGINE
97
  # ==========================================================
98
- def find_tent_by_capacity(text: str) -> str:
99
- """
100
- Detects tent request with capacity number and returns suitable tent.
101
- """
102
- # extract number
103
- match = re.search(r"(\d+)", text)
104
- if match:
105
- guests = int(match.group(1))
106
- suitable = []
107
- for tent, info in LD_ITEMS.items():
108
- if "capacity" in info and info["capacity"] >= guests:
109
- suitable.append((tent, info))
110
- if suitable:
111
- tent_name, info = min(suitable, key=lambda x: x[1]["capacity"])
112
- return f"{tent_name.title()} for {guests} guests: KES {info['price']} {info['unit']}"
113
- else:
114
- return "Sorry, we don't have a tent large enough for that number of guests."
115
- return None
116
 
117
- def secretary_reply(text: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  t = normalize(text)
119
 
120
  # --- Greetings ---
@@ -125,49 +175,68 @@ def secretary_reply(text: str) -> str:
125
  if any(b in t for b in GOODBYES):
126
  return "Thank you for contacting us! Have a wonderful day."
127
 
128
- # --- Service inquiry ---
129
  if any(q in t for q in SERVICE_ASK):
130
- if any(w in t for w in LAMAKI_KEYWORDS):
131
- return "We offer:\n• " + "\n• ".join(LAMAKI_SERVICES_LIST) + f"\n\nPhone: {LAMAKI_PHONE}"
132
- if any(w in t for w in LD_KEYWORDS):
133
- return "We offer:\n• Event Planning & Concept Design\n• Décor & Styling\n• Sound, Stage & Lighting Setup\n• DJ, MC & Entertainment\n• LED screens, live-streaming, photography, videography\n• On-day event management\n\nPhone: {LD_PHONE}"
134
-
135
- # --- Tent capacity detection ---
136
- tent_reply = find_tent_by_capacity(text)
137
- if tent_reply:
138
- return tent_reply
139
-
140
- # --- LD items dynamic detection ---
141
- for item in LD_ITEMS:
142
- if item in t:
143
- info = LD_ITEMS[item]
144
- price_str = f"KES {info['price']} {info['unit']}"
145
- if "capacity" in info:
146
- return f"{item.title()} ({info['capacity']} guests): {price_str}"
147
- return f"{item.title()}: {price_str}"
148
-
149
- # --- LD packages ---
150
- for pkg in LD_PACKAGES:
151
- if pkg in t:
152
- items = ", ".join(LD_PACKAGES[pkg])
153
- return f"{pkg.title()} Package includes: {items}"
154
-
155
- # --- LD combos ---
156
- for combo in LD_COMBOS:
157
- if combo in t:
158
- return LD_COMBOS[combo]
159
-
160
- # --- Payment intent ---
161
- if any(w in t for w in ("pay", "mpesa", "deposit", "book", "lock")):
162
- if any(w in t for w in LAMAKI_KEYWORDS):
163
- return f"Pay via MPESA {LAMAKI_PHONE} (Lamaki Designs). Send code to confirm."
164
- return f"Pay via MPESA {LAMAKI_PHONE} (LD Events). Send code to confirm."
165
-
166
- # --- Fallback ---
167
- if any(w in t for w in LAMAKI_KEYWORDS):
168
- return "We offer:\n• " + "\n• ".join(LAMAKI_SERVICES_LIST) + f"\n\nPhone: {LAMAKI_PHONE}"
169
- if any(w in t for w in LD_KEYWORDS):
170
- return "We offer:\n• Event Planning & Concept Design\n• Décor & Styling\n• Sound, Stage & Lighting Setup\n• DJ, MC & Entertainment\n• LED screens, live-streaming, photography, videography\n• On-day event management\n\nPhone: {LD_PHONE}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
  return "How may I help you today? (Type LD for events or LAMAKI for construction)"
173
 
@@ -188,7 +257,7 @@ def whatsapp():
188
  return jsonify(reply="Kindly send a text message.")
189
 
190
  save_msg(user, msg, "user")
191
- ans = secretary_reply(msg)
192
  save_msg(user, ans, "assistant")
193
 
194
  return jsonify(reply=ans)
 
1
  #!/usr/bin/env python3
2
  """
3
  LD Events + Lamaki Designs Intelligent Secretary Bot
4
+ Ultra-fast, dictionary + rule-based, zero DB, zero latency.
5
+ Includes dynamic event-based package suggestions and guest-aware tent selection.
6
  """
7
 
8
  from __future__ import annotations
9
  import os
10
  import re
11
+ from typing import Dict
12
  from flask import Flask, request, jsonify
13
 
14
  # ---------- CONFIG ----------
15
  VERIFY_TOKEN = os.getenv("WEBHOOK_VERIFY", "ldlamaki2025")
16
 
17
+ # ---------- NO SUPABASE ----------
18
  def save_msg(user: str, text: str, role: str = "assistant"):
19
+ return # no-op
20
 
21
  # ==========================================================
22
  # NORMALIZATION
23
  # ==========================================================
24
+ _rx_clean = re.compile(r"[^a-z0-9+]+")
25
+
26
  def normalize(text: str) -> str:
27
  return _rx_clean.sub(" ", text.lower()).strip()
28
 
29
  # ==========================================================
30
+ # MAIN REPLY DICT
31
+ # ==========================================================
32
+ REPLY: Dict[str, str] = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ def add(k: str, v: str):
35
+ REPLY[normalize(k)] = v
 
 
 
 
36
 
37
+ # ==========================================================
38
+ # LD EVENTS ITEMS + PACKAGES
39
+ # ==========================================================
40
+ # Audio
41
+ add("line array", "Line-array-top: KES 4 k / day per unit. Includes Silver Package option: line arrays, subs, mixer, monitors, LED wash, tent, stage, DJ, MC.")
42
+ add("double 18 sub", "Double-18 sub: KES 4 k / day. Included in Silver Package with line arrays + monitors + mixer.")
43
+ add("single 18 sub", "Single-18 sub: KES 3 k / day.")
44
+ add("monitor speaker", "Monitor speaker: KES 2 k / day.")
45
+ add("cordless mic", "Cordless mic: KES 2 k / day.")
46
+
47
+ # Lights
48
+ add("moving head", "Moving-head-light: KES 3 k / day per unit. Included in Silver Package and Wedding combo.")
49
+ add("parcan light", "Parcan-light: KES 1.5 k / day per unit. Included in Bronze Package.")
50
+
51
+ # Visuals
52
+ add("led screen panel", "LED-screen-panel: KES 2.2 k / panel. Included in Gold Package with LED wall, line arrays, DJ, MC, photo, video.")
53
+
54
+ # Tents
55
+ add("a frame tent", "A-frame tent: KES 30 k / day (100 guests).")
56
+ add("dome tent", "Dome tent: KES 45 k / day (200 guests).")
57
+ add("clear span tent", "Clear-span tent: KES 80 k / day (500 guests).")
58
+
59
+ # Stage
60
+ add("stage 8x4", "Stage 8x4: KES 15 k / day.")
61
+ add("stage 12x6", "Stage 12x6: KES 25 k / day.")
62
+ add("stage 16x8", "Stage 16x8: KES 40 k / day.")
63
+
64
+ # Entertainment
65
+ add("dj console", "DJ console: KES 25 k / 6 hrs.")
66
+ add("mc service", "MC service: KES 15 k / event.")
67
+ add("live band", "Live band: KES 55 k / 2 sets.")
68
+
69
+ # Media
70
+ add("photography 8h", "Photography (8 hours): KES 25 k.")
71
+ add("videography 4k", "Videography 4K: KES 45 k.")
72
+ add("drone 2h", "Drone coverage (2 hours): KES 10 k.")
73
+
74
+ # Packages
75
+ add("bronze package", "LD Bronze Package (KES 150–200k): 4 speakers, mixer, amp, cordless mic, basic lights, A-frame tent, 8x4 stage.")
76
+ add("silver package", "LD Silver Package (KES 350–450k): Line arrays, subs, mixer, monitors, LED wash, dome tent, 12x6 stage, DJ, MC.")
77
+ add("gold package", "LD Gold Package (KES 650–850k): Full line-array rig, double subs, LED wall, monitors, DJ, MC, photo, video.")
78
+ add("platinum package", "LD Platinum Package (KES 1.2–1.8M): Full concert rig + live streaming + drone + large clear-span tent.")
79
+
80
+ # Combos
81
+ add("4 tops 2 subs combo", "4 line arrays + 2 double-18 subs + mixer + monitors + mics: KES 22 k/day.")
82
+ add("6 tops 4 subs combo", "6 line arrays + 4 double-18 subs + amp rack + monitors + mics: KES 36 k/day.")
83
+ add("wedding combo", "Wedding combo: KES 28 k/day (line arrays + sub + monitors + mics + DJ console).")
84
+ add("corporate combo", "Corporate combo: KES 18 k/day (line arrays + sub + monitors + mics + projector).")
85
+
86
+ # Billing
87
+ add("ld+pay+today", "Pay via MPESA 0757-299-299 (LD Events). Send code for instant confirmation.")
88
+ add("ld+deposit", "50% deposit to MPESA 0757-299-299. Balance on delivery.")
89
+ add("ld+paid", "✅ Deposit received – gear locked!")
90
+ add("ld+receipt", "Forward MPESA message for instant confirmation.")
91
 
92
+ # ==========================================================
93
+ # LAMAKI DESIGNS KNOWLEDGE BASE
94
+ # ==========================================================
95
+ LAMAKI_SERVICES = (
96
+ "We offer:\n"
97
+ "• New Construction (residential, commercial, institutional)\n"
98
+ "• Renovation & Refurbishment (kitchen, bathroom, interiors, roofing)\n"
99
+ "• House lighting design & installation\n"
100
+ "• Electrical wiring & fixtures\n"
101
+ "• Gypsum and fabrication works\n"
102
+ "• Architectural Designs & 3D Visualisation\n"
103
+ "• Project Management (timelines, quality control, subcontractors)\n"
104
+ "• Interior Design & Decoration\n"
105
+ "• Custom Furniture & Fabrication\n\n"
106
+ "Free site visit + proposal in 24 hours. Phone: +254 757 299 299."
107
+ )
108
+
109
+ LD_EVENTS_SERVICES = (
110
+ "We offer:\n"
111
+ "• Event Planning & Concept Design\n"
112
+ "• Décor & Styling\n"
113
+ "• Sound, Stage & Lighting Setup\n"
114
+ "• DJ, MC & Entertainment\n"
115
+ "• LED screens, live-streaming, photography, videography\n"
116
+ "• On-day event management\n\n"
117
+ "Phone: +254 113 710584."
118
+ )
119
 
120
  # ==========================================================
121
+ # EVENT TYPES & SUGGESTIONS
122
  # ==========================================================
123
+ EVENT_TYPES = {
124
+ "wedding": ["wedding combo", "silver package", "gold package"],
125
+ "graduation": ["silver package", "gold package"],
126
+ "church": ["bronze package", "silver package"],
127
+ "seminar": ["bronze package", "silver package", "corporate combo"],
128
+ "corporate": ["corporate combo", "silver package", "gold package"]
129
+ }
 
 
 
 
130
 
131
  # ==========================================================
132
+ # HELPER WORD LISTS
133
  # ==========================================================
134
  GREETINGS = ("hello", "hi", "hey", "greetings", "morning", "evening")
135
+ GOODBYES = ("bye", "goodbye", "good night", "have a nice day", "see you", "talk later")
 
 
136
  SERVICE_ASK = ("services", "what do you offer", "offer", "service", "do you have")
137
+ GEAR_WORDS = (
138
+ "sound", "light", "tent", "stage", "speaker", "sub", "array", "dj",
139
+ "mic", "wedding", "concert", "event", "ld"
140
+ )
141
+ LAMAKI_WORDS = (
142
+ "lamaki", "construction", "build", "house", "renovation", "remodel",
143
+ "kitchen", "bathroom", "architect", "interior"
144
+ )
145
 
146
  # ==========================================================
147
+ # CONFIRMATION REPLY FOR CLIENT SELECTION
148
  # ==========================================================
149
+ CONFIRMATION_REPLY = "Okay, great! Expect a call from us to discuss further."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ # Auto-add variations for all REPLY keys to trigger confirmation
152
+ for key in list(REPLY.keys()):
153
+ add(f"{key} selected", CONFIRMATION_REPLY)
154
+ add(f"i want {key}", CONFIRMATION_REPLY)
155
+ add(f"choose {key}", CONFIRMATION_REPLY)
156
+ add(f"{key} please", CONFIRMATION_REPLY)
157
+ add(f"{key} package", CONFIRMATION_REPLY)
158
+
159
+ # ==========================================================
160
+ # LAST ITEM TRACKER
161
+ # ==========================================================
162
+ LAST_ITEM: Dict[str, str] = {} # track last item per user for price queries
163
+
164
+ # ==========================================================
165
+ # REPLY ENGINE
166
+ # ==========================================================
167
+ def secretary_reply(text: str, user: str = "unknown") -> str:
168
  t = normalize(text)
169
 
170
  # --- Greetings ---
 
175
  if any(b in t for b in GOODBYES):
176
  return "Thank you for contacting us! Have a wonderful day."
177
 
178
+ # --- Lamaki service questions ---
179
  if any(q in t for q in SERVICE_ASK):
180
+ if any(w in t for w in LAMAKI_WORDS):
181
+ return LAMAKI_SERVICES
182
+ if "ld" in t or "event" in t:
183
+ return LD_EVENTS_SERVICES
184
+
185
+ # --- EVENT-BASED PACKAGE SUGGESTION ---
186
+ for event, packages in EVENT_TYPES.items():
187
+ if event in t:
188
+ suggestions = "\n".join([REPLY.get(p, p) for p in packages])
189
+ return f"For a {event}, we usually recommend these options:\n{suggestions}\nWhich one would you like?"
190
+
191
+ # --- Tent with guest number detection ---
192
+ match = re.search(r"tent.*?(\d{1,5})\s*people", t)
193
+ if match:
194
+ guests = int(match.group(1))
195
+ if guests <= 100:
196
+ LAST_ITEM[user] = "a frame tent"
197
+ return REPLY["a frame tent"]
198
+ elif guests <= 200:
199
+ LAST_ITEM[user] = "dome tent"
200
+ return REPLY["dome tent"]
201
+ elif guests <= 500:
202
+ LAST_ITEM[user] = "clear span tent"
203
+ return REPLY["clear span tent"]
204
+ else:
205
+ return "We can provide a custom tent for more than 500 guests. Please contact us."
206
+
207
+ # --- Generic tent request ---
208
+ if "tent" in t:
209
+ return (
210
+ "We have the following tents available:\n"
211
+ f" {REPLY['a frame tent']}\n"
212
+ f"• {REPLY['dome tent']}\n"
213
+ f" {REPLY['clear span tent']}\n"
214
+ "\nPlease tell us the number of guests to suggest the best option."
215
+ )
216
+
217
+ # --- PRICE / COST queries ---
218
+ if any(word in t for word in ("price", "cost", "how much")):
219
+ last = LAST_ITEM.get(user)
220
+ if last and last in REPLY:
221
+ return REPLY[last]
222
+ else:
223
+ return "Please specify the item or package you want the price for."
224
+
225
+ # --- Check all REPLY keys for a match ---
226
+ for k, v in REPLY.items():
227
+ if k in t:
228
+ LAST_ITEM[user] = k
229
+ return v
230
+
231
+ # --- Lamaki context ---
232
+ if any(w in t for w in LAMAKI_WORDS):
233
+ return LAMAKI_SERVICES
234
+
235
+ # --- LD Events context fallback ---
236
+ if any(w in t for w in GEAR_WORDS):
237
+ # Return all available LD items with prices
238
+ items_list = "\n".join([f"• {v}" for k, v in REPLY.items() if k in normalize(" ".join(GEAR_WORDS))])
239
+ return f"{LD_EVENTS_SERVICES}\n\nAvailable items:\n{items_list}"
240
 
241
  return "How may I help you today? (Type LD for events or LAMAKI for construction)"
242
 
 
257
  return jsonify(reply="Kindly send a text message.")
258
 
259
  save_msg(user, msg, "user")
260
+ ans = secretary_reply(msg, user)
261
  save_msg(user, ans, "assistant")
262
 
263
  return jsonify(reply=ans)