codeBOKER commited on
Commit
aa966e0
·
1 Parent(s): bc2df26

feat: welcome returning drivers discovered from WhatsApp groups

Browse files

- Detect unregistered drivers (from group trips) who later DM the bot
- Send personalized welcome with their name and explanation of FALSA benefits
- Upgrade user_mode from new_user to driver automatically
- Improve driver phone lookup to handle /-separated numbers

app/database/supabase.py CHANGED
@@ -631,6 +631,18 @@ class SupabaseRepository:
631
  .maybe_single()
632
  .execute()
633
  )
 
 
 
 
 
 
 
 
 
 
 
 
634
  return _response_data(response)
635
 
636
  async def create_driver(self, *, customer_id: str) -> dict[str, Any]:
 
631
  .maybe_single()
632
  .execute()
633
  )
634
+ result = _response_data(response)
635
+ if result:
636
+ return result
637
+
638
+ # Check if any stored customer has a /-separated phone_number containing our phone
639
+ response = await (
640
+ self.client.table("drivers")
641
+ .select("*, customers!inner(*)")
642
+ .like("customers.phone_number", f"%{phone}%")
643
+ .maybe_single()
644
+ .execute()
645
+ )
646
  return _response_data(response)
647
 
648
  async def create_driver(self, *, customer_id: str) -> dict[str, Any]:
app/services/conversation_service.py CHANGED
@@ -75,6 +75,16 @@ class ConversationService:
75
  )
76
 
77
  user_mode = _resolve_user_mode(customer)
 
 
 
 
 
 
 
 
 
 
78
  registry = self._tool_registry(
79
  customer,
80
  remoteJid=inbound.remoteJid,
@@ -185,11 +195,35 @@ class ConversationService:
185
  )
186
  return reply
187
 
188
- reply = await self.ai.generate_reply(
189
- messages=self._ai_messages(context, user_mode=user_mode),
190
- tools=get_tool_schemas(user_mode),
191
- registry=registry,
192
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
  if not reply:
195
  return reply
@@ -203,6 +237,12 @@ class ConversationService:
203
  metadata={"provider_flow": "groq_primary_hf_fallback", "user_mode": user_mode},
204
  )
205
 
 
 
 
 
 
 
206
  return reply
207
 
208
  async def _store_and_send_assistant_reply(
 
75
  )
76
 
77
  user_mode = _resolve_user_mode(customer)
78
+
79
+ is_returning_driver = False
80
+ if user_mode == "new_user" and customer.get("phone_number"):
81
+ existing_driver = await self.repository.get_driver_by_phone_number(
82
+ customer["phone_number"],
83
+ )
84
+ if existing_driver:
85
+ is_returning_driver = True
86
+ user_mode = "driver"
87
+
88
  registry = self._tool_registry(
89
  customer,
90
  remoteJid=inbound.remoteJid,
 
195
  )
196
  return reply
197
 
198
+ if is_returning_driver:
199
+ driver_name = customer.get("name") or ""
200
+ system_note = (
201
+ f"SYSTEM: This is the first message from driver \"{driver_name}\". "
202
+ "They were previously tracked from WhatsApp group trip posts. "
203
+ "Welcome them warmly by name. Tell them we have been following their trips "
204
+ "in the groups and we are impressed. Explain that we have registered them in "
205
+ "FALSA so they can now send trips directly here instead of posting in groups. "
206
+ "Show them how: just send the trip details (route, date, time) in chat. "
207
+ "Explain the benefits: passengers find their trips via AI search, they get "
208
+ "notified immediately when a passenger selects their trip, and registered "
209
+ "drivers get priority visibility in search results. Tell them we will no "
210
+ "longer add their trips from groups — they are in full control now. "
211
+ "Keep it warm, personal, and exciting. Use emojis. Write in Arabic. "
212
+ "8-10 lines max. Do NOT call any tools."
213
+ )
214
+ messages = self._ai_messages(context, user_mode=user_mode)
215
+ messages.append({"role": "system", "content": system_note})
216
+ reply = await self.ai.generate_reply(
217
+ messages=messages,
218
+ tools=get_tool_schemas(user_mode),
219
+ registry=registry,
220
+ )
221
+ else:
222
+ reply = await self.ai.generate_reply(
223
+ messages=self._ai_messages(context, user_mode=user_mode),
224
+ tools=get_tool_schemas(user_mode),
225
+ registry=registry,
226
+ )
227
 
228
  if not reply:
229
  return reply
 
237
  metadata={"provider_flow": "groq_primary_hf_fallback", "user_mode": user_mode},
238
  )
239
 
240
+ if is_returning_driver:
241
+ await self.repository.update_customer_user_mode(
242
+ customer_id=str(customer["id"]),
243
+ user_mode="driver",
244
+ )
245
+
246
  return reply
247
 
248
  async def _store_and_send_assistant_reply(
tests/test_conversation_service.py CHANGED
@@ -120,6 +120,74 @@ async def test_conversation_uses_passenger_tools_when_user_mode_is_passenger(set
120
  assert "travel booking assistant" in ai.calls[0]["messages"][0]["content"]
121
 
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  @pytest.mark.asyncio
124
  async def test_conversation_uses_driver_tools_when_user_mode_is_driver(settings):
125
  repository = FakeRepository()
 
120
  assert "travel booking assistant" in ai.calls[0]["messages"][0]["content"]
121
 
122
 
123
+ @pytest.mark.asyncio
124
+ async def test_returning_driver_gets_welcome_and_upgraded_to_driver(settings):
125
+ """An unregistered driver saved from group trips who later DMs the bot
126
+ should get a personalized welcome system note, use driver tools, and
127
+ have their user_mode upgraded to 'driver'."""
128
+ repository = FakeRepository()
129
+
130
+ # Step 1: Simulate group-trip extraction creating an unregistered driver
131
+ customer = await repository.upsert_customer(
132
+ remote_jid=None,
133
+ name="فهد",
134
+ phone_number="967712345678",
135
+ registered=False,
136
+ )
137
+ await repository.create_driver(customer_id=customer["id"])
138
+
139
+ assert customer["user_mode"] is None
140
+ assert customer["remoteJid"] is None
141
+
142
+ # Step 2: Same driver sends a DM
143
+ ai = FakeAI(reply="مرحباً فهد! نتابع رحلاتك...")
144
+ whatsapp = FakeWhatsApp()
145
+ service = ConversationService(
146
+ repository=repository,
147
+ embeddings=FakeEmbeddings(),
148
+ whatsapp=whatsapp,
149
+ ai=ai,
150
+ settings=settings,
151
+ )
152
+
153
+ reply = await service.handle_inbound_message(
154
+ WhatsAppInboundMessage(
155
+ message_id="wamid.returning",
156
+ remoteJid="967712345678",
157
+ text="مرحبا، أبي أضيف رحلة",
158
+ phone_number="967712345678",
159
+ profile_name="فهد",
160
+ )
161
+ )
162
+
163
+ assert reply == "مرحباً فهد! نتابع رحلاتك..."
164
+
165
+ # Should use driver tools (not new_user tools)
166
+ tool_names = {tool["function"]["name"] for tool in ai.calls[0]["tools"]}
167
+ assert tool_names == {
168
+ "about_falsa",
169
+ "check_driver_info",
170
+ "check_driver_trips",
171
+ "add_driver_car",
172
+ "add_trip_by_driver",
173
+ "initiate_trip_action",
174
+ "update_trip_field",
175
+ "switch_to_passenger",
176
+ }
177
+
178
+ # System note should be appended with returning-driver context
179
+ ai_messages = ai.calls[0]["messages"]
180
+ system_contents = [m["content"] for m in ai_messages if m["role"] == "system"]
181
+ assert any("فهد" in msg for msg in system_contents)
182
+ assert any("group" in msg.lower() or "مجموعات" in msg for msg in system_contents)
183
+
184
+ # user_mode should be upgraded to 'driver'
185
+ assert customer["user_mode"] == "driver"
186
+
187
+ # WhatsApp message was sent
188
+ assert len(whatsapp.sent) == 1
189
+
190
+
191
  @pytest.mark.asyncio
192
  async def test_conversation_uses_driver_tools_when_user_mode_is_driver(settings):
193
  repository = FakeRepository()