From ca2270a36b6715334106c3c28aaf603f6f9a6ee8 Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Aug 2024 14:22:49 +0530 Subject: [PATCH] - Fix conversation lookups in twilio - Modify Conversation model to include twilio_call_sid in indexes - Update TwilioVoice handler to not create a conversation entry for missed calls - Simplify logic for determining if call will be rejected - rename TwilioVoice.from_data -> TwilioVoice.from_webhook_data --- bots/admin.py | 1 + ...bots_conver_bot_int_73ac7b_idx_and_more.py | 21 ++++++++++++++++ bots/models.py | 17 +++++++++++-- daras_ai_v2/twilio_bot.py | 25 +++++++++++++------ routers/twilio_api.py | 4 +-- 5 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 bots/migrations/0081_remove_conversation_bots_conver_bot_int_73ac7b_idx_and_more.py diff --git a/bots/admin.py b/bots/admin.py index 0b8b28d10..3a4ae8e20 100644 --- a/bots/admin.py +++ b/bots/admin.py @@ -77,6 +77,7 @@ "twilio_username", "twilio_password", "twilio_use_missed_call", + "twilio_fresh_conversation_per_call", "twilio_initial_text", "twilio_initial_audio_url", "twilio_waiting_text", diff --git a/bots/migrations/0081_remove_conversation_bots_conver_bot_int_73ac7b_idx_and_more.py b/bots/migrations/0081_remove_conversation_bots_conver_bot_int_73ac7b_idx_and_more.py new file mode 100644 index 000000000..3e98ac4f8 --- /dev/null +++ b/bots/migrations/0081_remove_conversation_bots_conver_bot_int_73ac7b_idx_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2024-08-28 06:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bots', '0080_botintegration_twilio_fresh_conversation_per_call_and_more'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='conversation', + name='bots_conver_bot_int_73ac7b_idx', + ), + migrations.AddIndex( + model_name='conversation', + index=models.Index(fields=['bot_integration', 'twilio_phone_number', 'twilio_call_sid'], name='bots_conver_bot_int_477755_idx'), + ), + ] diff --git a/bots/models.py b/bots/models.py index 6ea64e773..2566e976f 100644 --- a/bots/models.py +++ b/bots/models.py @@ -1113,7 +1113,9 @@ class Meta: "slack_channel_is_personal", ], ), - models.Index(fields=["bot_integration", "twilio_phone_number"]), + models.Index( + fields=["bot_integration", "twilio_phone_number", "twilio_call_sid"] + ), models.Index(fields=["-created_at", "bot_integration"]), ] @@ -1123,7 +1125,18 @@ def __str__(self): def get_display_name(self): return ( (self.wa_phone_number and self.wa_phone_number.as_international) - or (self.twilio_phone_number and self.twilio_phone_number.as_international) + or " | ".join( + filter( + None, + [ + ( + self.twilio_phone_number + and self.twilio_phone_number.as_international + ), + self.twilio_call_sid, + ], + ) + ) or self.ig_username or self.fb_page_name or " in #".join( diff --git a/daras_ai_v2/twilio_bot.py b/daras_ai_v2/twilio_bot.py index 4529952ec..cd397ca06 100644 --- a/daras_ai_v2/twilio_bot.py +++ b/daras_ai_v2/twilio_bot.py @@ -24,7 +24,9 @@ def __init__(self, data: dict): twilio_account_sid=account_sid, twilio_phone_number=data["To"][0] ) self.convo = Conversation.objects.get_or_create( - bot_integration=bi, twilio_phone_number=data["From"][0] + bot_integration=bi, + twilio_phone_number=data["From"][0], + twilio_call_sid="", )[0] self.bot_id = bi.twilio_phone_number.as_e164 @@ -86,7 +88,7 @@ class TwilioVoice(BotInterface): platform = Platform.TWILIO @classmethod - def from_data(cls, data: dict): + def from_webhook_data(cls, data: dict): ## data samples: # {'AccountSid': ['XXXX'], 'ApiVersion': ['2010-04-01'], 'CallSid': ['XXXX'], 'CallStatus': ['ringing'], 'CallToken': ['XXXX'], 'Called': ['XXXX'], 'CalledCity': ['XXXX'], 'CalledCountry': ['XXXX'], 'CalledState': ['XXXX'], 'CalledZip': ['XXXX'], 'Caller': ['XXXX'], 'CallerCity': ['XXXX'], 'CallerCountry': ['XXXX'], 'CallerState': ['XXXX'], 'CallerZip': ['XXXX'], 'Direction': ['inbound'], 'From': ['XXXX'], 'FromCity': ['XXXX'], 'FromCountry': ['XXXX'], 'FromState': ['XXXX'], 'FromZip': ['XXXX'], 'StirVerstat': ['XXXX'], 'To': ['XXXX'], 'ToCity': ['XXXX'], 'ToCountry': ['XXXX'], 'ToState': ['XXXX'], 'ToZip': ['XXXX']} # {'AccountSid': ['XXXX'], 'ApiVersion': ['2010-04-01'], 'CallSid': ['XXXX'], 'CallStatus': ['in-progress'], 'Called': ['XXXX'], 'CalledCity': ['XXXX'], 'CalledCountry': ['XXXX'], 'CalledState': ['XXXX'], 'CalledZip': ['XXXX'], 'Caller': ['XXXX'], 'CallerCity': ['XXXX'], 'CallerCountry': ['XXXX'], 'CallerState': ['XXXX'], 'CallerZip': ['XXXX'], 'Confidence': ['0.9128386'], 'Direction': ['inbound'], 'From': ['XXXX'], 'FromCity': ['XXXX'], 'FromCountry': ['XXXX'], 'FromState': ['XXXX'], 'FromZip': ['XXXX'], 'Language': ['en-US'], 'SpeechResult': ['Hello.'], 'To': ['XXXX'], 'ToCity': ['XXXX'], 'ToCountry': ['XXXX'], 'ToState': ['XXXX'], 'ToZip': ['XXXX']} @@ -96,25 +98,29 @@ def from_data(cls, data: dict): if account_sid == settings.TWILIO_ACCOUNT_SID: account_sid = "" call_sid = data["CallSid"][0] - caller_number = data["Caller"][0] user_number, bot_number = data["From"][0], data["To"][0] try: # cases where user is calling the bot bi = BotIntegration.objects.get( twilio_account_sid=account_sid, twilio_phone_number=bot_number ) + will_be_missed = bi.twilio_use_missed_call except BotIntegration.DoesNotExist: # cases where bot is calling the user user_number, bot_number = bot_number, user_number bi = BotIntegration.objects.get( twilio_account_sid=account_sid, twilio_phone_number=bot_number ) + will_be_missed = False - will_be_missed = caller_number == user_number and bi.twilio_use_missed_call if will_be_missed: - # for call_sids that we will reject and re-call, the convo is not used, so we don't want to create a new one - convo = None - if bi.twilio_fresh_conversation_per_call and not will_be_missed: + # for calls that we will reject and callback, the convo is not used so we don't want to create one + convo = Conversation( + bot_integration=bi, + twilio_phone_number=user_number, + twilio_call_sid=call_sid, + ) + elif bi.twilio_fresh_conversation_per_call: convo = Conversation.objects.get_or_create( bot_integration=bi, twilio_phone_number=user_number, @@ -122,8 +128,11 @@ def from_data(cls, data: dict): )[0] else: convo = Conversation.objects.get_or_create( - bot_integration=bi, twilio_phone_number=user_number + bot_integration=bi, + twilio_phone_number=user_number, + twilio_call_sid="", )[0] + return cls( convo, text=data.get("SpeechResult", [None])[0], diff --git a/routers/twilio_api.py b/routers/twilio_api.py index 15e50d4a9..7893c15d1 100644 --- a/routers/twilio_api.py +++ b/routers/twilio_api.py @@ -70,7 +70,7 @@ def twilio_voice_call( """Handle incoming Twilio voice call.""" try: - bot = TwilioVoice.from_data(data) + bot = TwilioVoice.from_webhook_data(data) except BotIntegration.DoesNotExist as e: logger.debug(f"could not find bot integration for {data=} {e=}") resp = VoiceResponse() @@ -199,7 +199,7 @@ def twilio_voice_call_asked( ): """After the initial call, the user has asked a question via Twilio/Gooey ASR. Handle their question.""" - bot = TwilioVoice.from_data(data) + bot = TwilioVoice.from_webhook_data(data) resolve_twilio_tts_voice(bot) background_tasks.add_task(msg_handler, bot)