
    [h}W                        d dl m Z mZmZ d dlmZ d dlmZmZmZ d dl	m
Z
 d dlmZ d dlZd dlZd dlmZ d dlmZ d d	lmZmZ d d
lmZ d dlmZmZ d dlmZ d dlmZ d dlm Z  d dl!m"Z" d dl#m$Z$ d dl%m&Z&m'Z'm(Z(m)Z) d dl*Z*d dl+m,Z, d dlmZmZ d dl-m.Z. d dl+m,Z,m/Z/  e       Z0de1fdZ2e0jg                  d      defd       Z4e0jg                  d       ee      fdedefd       Z5e0jg                  d      defd       Z6e0jg                  d        ee      fdedefd!       Z7e0jg                  d"      defd#       Z8e0jg                  d$       ee      fdedefd%       Z9d& Z:e0jg                  d'       ee      fdedefd(       Z;e0jg                  d)      defd*       Z< G d+ d,e
      Z=e0jg                  d-      de=fd.       Z>e0jg                  d/       ee      fdedefd0       Z?y)1    )datetime	timedeltatimezone)Optional)	APIRouterDependsRequest)	BaseModel)SessionN)HTTPBasicAuth)UltravoxService)
CallSourceTwilioTerminationReason)get_db)get_twilio_credentialsget_twillio_client)get_current_time_in_timezone)SettingsRepo)get_directory)get_caller_name_by_phone_number)get_app_url)SettingsAdvisorCallsLogTransferResult)VoiceResponse)r   r   )Response)r   Dialcall_sidc                 N  K   	 t        j                  d      }t        j                  d      }d| d|  d}t                ddd}t        j                  ||t        ||      d	d
i      }|j                         }d|dS # t        $ r}dt        |      dcY d }~S d }~ww xY ww)NTWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKENz+https://api.twilio.com/2010-04-01/Accounts/z/Calls/z/Recordings.jsonz)/advisor/api/v1/twilio/recording-callbackzin-progress completed absent)RecordingStatusCallbackRecordingStatusCallbackEventContent-Typez!application/x-www-form-urlencoded)dataauthheaderssuccess)status	recordingerrorr*   message)	osgetenvr   requestspostr   json	Exceptionstr)r   account_sid
auth_tokenurlpayloadresponser&   es           M/var/www/html/DP/alpha_backend/app/advisor_service/routers/ultravox_router.pystart_recordingr=      s     6ii 45YY23
;K=PXzYij*5-8a'b,J

 =={J7#%HI	
 }}#$77 6!c!f556s5   B%A<B  B%	B"
BB"B%B""B%z/twilio/recording-callbackrequestc                    K   | j                          d {   }t        dt        |             t        dd      S 7 &w)Nrecording_callbackzRecording callback received
text/plaincontent
media_type)formprintdictr   )r>   	form_datas     r<   r@   r@   3   s6     lln$I	
Y09lSS %s   ?='?z/twilio/incoming-webhookdbc                   K   	 | j                          d {   }t        |      }|j                  d      }|j                  d      }t        d|       t	        |       d {   }t        d|       d}d}dt        |      z   }	|j                  t              j                  t        j                  dk(        j                         }
t        |
j                        }t        |      }t        |      }|j                         }|d	k(  rd
}	dddddddgddddddddddddddgt!                ddddi}dddddddgd ddddddd!dd"ddddd#ddddddgt!                d$dddi}|d%d&ig}|s|j#                  |      nd  |	d'd(d)iid*d+d,i i|d-}d }t%        j&                         4 d {   }|j)                  ||d.|d/0       d {   }|j+                          |j-                         }t/        t1        j2                  t4        j6                        |j                  d      |j                  d      |j                  d      |j                  d1      d2t8        j:                  d3|j                  d4      d56
      }|j=                  |       |j?                          |jA                  |       t        d7|       d d d       d {    |j                  d4      }|stC        d8      tE               }|jG                  t!                d9:      }|jI                  |t!                d;d<       tK        t        |      d=>      S 7 :7 7 7 7 # 1 d {  7  sw Y   xY w# tL        $ r]}t        d?|        d@d l'}|jQ                          tE               }|jS                  dA       tK        t        |      dB>      cY d }~S d }~ww xY ww)CNCallSidCallertwilio_datadirectory_responsez)p44BQKIv.S22V6VmcBwGwvkvcHANXTnqXixTpStn6z!https://api.ultravox.ai/api/callsu_	  
You are Maya, a helpful, friendly receptionist from Surf City Nissan. Your role is to assist callers with service updates, advisor transfers, and scheduling inquiries.

## IDENTITY
- You are Maya from Surf City Nissan dealership.
- You speak with a pleasant and professional tone, and care deeply about helping the caller.
- Greet every caller by saying:
  "Hi. This is Maya from Surf City Nissan. How may I help you?"
- If the caller asks "Is this Surf City Nissan?" or says a variation like "Serf City Nissan", confidently respond:
  "Yes, you have reached Surf City Nissan! How can I assist you today?"

## GENERAL BEHAVIOR
- Always respond in the **same language** as the caller.
- Do not whisper; maintain a confident, professional tone.
- Stay in character at all times — gently guide the conversation back if it goes off-topic.

## TASKS

1. Always ask for the caller's **name** politely and professionally.

2. If the caller wants to speak with an advisor:
   - First, ask for the name of the caller.
   - Use `ADVISOR_LIST` to check if the requested advisor exists:
     - If not listed: say, "I'm sorry, we don't have anyone by that name."
     - If unavailable: say, "That advisor is currently unavailable."
   - If valid and available: call the `transferCall` function with required parameters.

3. If the user wants to schedule a **visit**, **meeting**, or **test drive**, transfer the call to BDC.
   - Say: "I will transfer you to the scheduling department to assist you further."

4. If the caller wants to **record a message for an advisor**, check the current office hours:
   - Only allow message recording **after office hours**.
   - If it's after hours: ask for the advisor's name and call the `recordMessage` tool.
   - If it's **not** after hours: respond, "The message recording option is only available after office hours have ended."

5. When the caller naturally wraps up (e.g., says "thank you", "bye"):
   - Use the `hangUp` tool to end the call.

6. Do **not** ask the caller to leave a message unless it's after hours and they requested it.

## SPEECH STYLE GUIDELINES

- Do not list service items using numbers (e.g., "first", "second"); explain them in natural, conversational language.
- When giving an address, say the numbers **individually** for a natural sound (e.g., "1 7 3 3 1" instead of "seventeen thousand three hundred thirty-one").

## ADVISOR_LIST:
TIMEZONE_OFFSETFa  
- Bot will say:  
  "Our office hours have ended today and no staff member is available to take your call."
- Bot cannot tranfer to any advisors.
- Bot will check if the user wants to leave a message for an advisor:
  - If yes, ask for the advisor's name.
  - Then say something like:  
    "Sure, please tell me your name and the message you'd like me deliver."
  - Wait and Listen for the user's response, which should include both their name and the message.
  - Once both are received, call the `recordMessage` tool with:
    - advisor_name: [name of advisor provided by the user]
    - customer_name: [customer's name from the same response]
    - message: [message content from the same response]

- Bot should not directly ask to end the call but when the bot feels that the call naturally wraps up and the user says end-call greetings (e.g., "thank you", "goodbye"), use the `hangUp` tool to end the call.
temporaryTooltransferCallz=Transfers call to the advisor. Use this to transfer the call.callIdPARAMETER_LOCATION_BODYKNOWN_PARAM_CALL_ID)namelocation
knownValueadvisorNamezThe advisors namestring)descriptiontypeT)rU   rV   schemarequiredadvisorPhoneNumberzThe advisors phone numberz$/advisor/api/v1/twilio/transfer-callPOST)baseUrlPattern
httpMethod)modelToolNamerZ   automaticParametersdynamicParametershttprecordMessagez<Records message for advisor. Use this to record the message.advisor_namecustomer_namez1The customer's name who is recording the message.r.   z'/advisor/api/v1/ultravox/record-messagetoolNamehangUp
elevenLabsvoiceIdg6xIsTj2HwM6VR4iXFCwg333333?FIRST_SPEAKER_AGENTtwilio)systemPromptexternalVoicetemperaturefirstSpeakermediumselectedToolszapplication/json)r%   z	X-API-Key)r3   r(   Fromuser-hangupNeutraljoinUrl	completed)
call_date_timecall_idtwilio_call_idcaller_numbercaller_name
end_reasonsourcesentiment_scorerecording_urlcall_statusultravox_responsez!Ultravox did not return a joinUrlz%/advisor/api/v1/twilio/call-connected)actionz$/advisor/api/v1/twilio/stream-events)r8   status_callbackstatus_callback_methodtext/xmlrB   zError: r   z/Sorry, there was an error connecting your call.zapplication/xml)*rE   rG   getrF   r   r5   queryr   filterrU   firstintvaluer   r   is_company_onliner   appendhttpxAsyncClientr2   raise_for_statusr3   r   r   nowr   utcr   ULTRAVOXaddcommitrefresh
ValueErrorr   connectstreamr   r4   	traceback	print_excsay)r>   rI   rH   rM   r}   customer_numberrN   ULTRAVOX_API_KEYULTRAVOX_API_URLSYSTEM_PROMPTtimezone_settingtimezone_offsetcurrent_timesettings_repor   transfer_call_toolrecord_message_tooltoolsULTRAVOX_CALL_CONFIGultravox_callclientr   call_logjoin_urlr:   r   r;   r   error_responses                                r<   incoming_callr   9   s    
oS!,,.(	9o$3%//(3m[)#0#44"$67F>.\ 

].` 88H-44MM..005 	.4453OD$R();;=%M" !/^ !)$=&;( !.$=+>$,# %) !5$=+F$,# %)&* *57[&\"(="$
N !0] !)$=&;* !/$=+>$,# %) !0$=+^$,# %) !*$=+>$,# %)'&> *57^&_"(Q,.
b ($

 2C() & 0

 -b" 
 $$&&&&,kk ));JZ[ '2 ' !
 ..0-224M'||HLL9%))(3*y9)ooh7)--f5(!** )+//	:'H FF8IIKJJx %}51 '&4 !$$Y/@AA ?""!m_$IJ # 
 	*}o-QR#) 	 	

 H*EEM ) 5r '! '&&&R  Ssm&LMN 3@QRRSs   OM4 MAM4 #M$EM4 6M7M4 :MMC1MM4 MA=M4 OM4 M4 M4 MM4 M1%M(&M1-M4 4	O=AOOOOOz/twilio/call-connectedc                 (  K   | j                          d {   }|j                  d      }t        d|       t        dt        |             |rt	        |       d {   }t        d|       t               }t        t        |      d      S 7 w7 2w)NrK   call_connected_sidcall_connected_formRecording response:r   rB   )rE   r   rF   rG   r=   r   r   r5   )r>   rE   r   recording_responser:   s        r<   call_connectedr   /  s|     Dxx	"H	
)	
d,#28#<<#%78HCMjAA  
 =s"   BBABB1BBz/ultravox/record-messagec                   K   | j                          d {   }|j                  d      }|j                  d      }|j                  d      }|j                  d      }t        d|       t        d|       t        d|       t        d|       t        |      }|j	                  ||||       t        dd	      S 7 w)
Nrg   r.   rR   rh   r|   )rg   r.   rh   r|   zRecord message receivedrA   rB   )r3   r   rF   r   create_offline_messager   )r>   rI   r&   rg   r.   r|   rh   ultravox_services           r<   record_messager   <  s     D88N+Lhhy!Ghhx GHH_-M	/=)	.,'	)W	)W&r*++4;:G4; , =
 5,OO#  s   B>B<B&B>z/twilio/stream-eventsc                 *  K   | j                          d {   }|j                  d      }|j                  d      }t        d|       t        dt        |             |dk(  rt	        |       d {   }t        d|       t        dd	      S 7 x7 w)
NrK   StreamEventstream_events_sidstream_events_formzstream-startedr   zStream events receivedrA   rB   )rE   r   rF   rG   r=   r   )r>   rE   r   stream_eventr   s        r<   stream_eventsr   Q  s     Dxx	"H88M*L	
x(	
T
+%%#28#<<#%78
 4NN   =s"   BBAB1B2BBz/twilio/status-callbackc           
        K   | j                          d {   }t        |      }|j                  d      }|j                  d      }|j                  d      }|j                  d      }|j                  d      }t        d|       t        d|       t        d|j                  d             t        d	|j                  d             |j	                  t
              j                  t
        j                  |k(        j                         }	|	rk||	_	        |t        j                  k(  r0d
|	_        t        t        |j                  d                  |	_        |j!                          |j#                  |	       |j	                  t$              j                  t$        j                  |k(        j                         }
|
r:|t        j                  k(  |
_        |j!                          |j#                  |
       n|j	                  t(              j                  t(        j*                  |k(        j                         }|r|j,                  nd }t/        |      }t%        ||t        j                  k(  |||t1        j2                  t4        j6                              }|j9                  |       |j!                          |j#                  |       t;        dd      S 7 ­w)N
CallStatusParentCallSidCalledrv   rK   r   z&status_callback_data.get("CallStatus")z)status_callback_data.get("ParentCallSid")z#status_callback_data.get("CallSid")rw   CallDurationseconds)r}   r*   transferred_tor   rh   call_datetimeStatus callback receivedrA   rB   )rE   rG   r   rF   r   r   r   r}   r   r   r   	COMPLETEDr   r   r   call_durationr   r   r   r*   r   phone_numberidr   r   r   r   r   r   r   )r>   rI   rH   status_callback_datar   parent_call_sidadvisor_numberr   r   	call_dataexisting_transfer_resultadvisor
advisor_idrh   transfer_results                  r<   r   r   a  sD    lln$I	?&**<8K*..?O)--h7N*..v6O#''	2H	
12	
2K@	
57K7O7OP_7`a	
/1E1I1I)1TU"))(*A*AX*MNTTVI +	1;;;#0I &/<P<T<TUc<d8e&fI#
		


9!xx7>>~?\?\`o?opvvx*59P9Z9Z*Z '
		


+,((7#**7+?+?>+QRXXZ#*WZZ
7H(*"9"C"CC%+'",,x||4
 	
		


?#6<PP[ %s   KKKKc                    	 t        d| |       |j                  t              j                  t        j                  | k(        j                         }|st        d      |j                  }t               }t               }t               }|j                  |dt                dd       |j                  |       |j                  |      j                  t!        |            }||_        d|_        |j'                          |j)                  |       d	d
ddS # t*        $ r }	t        d|	       ddt!        |	      dd }	~	ww xY w)Ntransfer_active_callz!Call not found or invalid CallSidz$initiated ringing answered completedz&/advisor/api/v1/twilio/status-callbackr_   )status_callback_eventr   r   )twiml	initiatedr)   zCall transfer initiatednono)r*   r.   call_detailszError transferring call:r,   zFailed to transfer call)r*   r.   r,   )rF   r   r   r   r|   r   r   r}   r   r   r   numberr   r   callsupdater5   r   transfer_statusr   r   r4   )
ultravox_call_iddestination_numberrI   r   r}   r   r:   dialupdated_callr;   s
             r<   r   r     s7   %
$&68JKHHX&--h.>.>BR.RSYY[	@AA"11#% ?v&-S*5-8^'_/5 	 	
 	 ||N3::X:O#5	 $/	!
		


90"
 	
  
(!,0V
 	

s   DD 	E"D==Ez/twilio/transfer-callc                 ,  K   t        d       t        d| j                          d{          | j                          d{   }|j                  d      }|j                  d      }t        d|       t        |||       t	        dd	      S 7 h7 Nw)
-
    Handle incoming calls from Ultravox
    z$Received transfer call from Ultravoxr>   NrR   r^   zdestination numberzTransfer call receivedrA   rB   )rF   r3   r   r   r   )r>   rI   r&   r   r   s        r<   transfer_callr     s      

01	)7<<>)*Dxx)"67	
 23)+=rB4NN *s"   %BB
BBABBz/ultravox/webhookc                    K   t        d       t        d| j                          d{          t        dd      S 7 w)r   zReceived webhook from Ultravoxr>   NzWebhook receivedrA   rB   )rF   r3   r   )r>   s    r<   ultravox_webhookr     s7      

*+	)7<<>)*.<HH *s   %?=
?c                   n    e Zd ZU dZee   ed<   dZee   ed<   dZee   ed<    e	        dZ
ee   ed<   y)SwitchPlatformRequestNmodetwilioNumbertrunkIdz'/advisor/api/v1/twilio/incoming-webhookwebhook_url)__name__
__module____qualname__r   r   r5   __annotations__r   r   r   r        r<   r   r     sD    D(3-"&L(3-&!GXc]!$/M?2Y!ZK#Zr   r   z/ultravox/update-modec                   K   | j                   }| j                  }| j                  }| j                  }t	               }|j
                  j                  |      d   }t        d|j                         |j                  }d}|dk(  rt               \  }	}
d|	 d}n	|dk(  r|n|}|j                  |      j                  |d	t                d
d	      }|dk(  s|dk(  rC|j                  j                  j                  |      j!                  |      j#                          n?|j                  j                  j                  |      j                   j%                  |       t        d|       t        d|        t        d|        dd| dS w)N)r   r   znumber.__dict__:z"https://api.retellai.com/api/twimlstudioz(https://webhooks.twilio.com/v1/Accounts/z)/Flows/FW9aa04a6627116ee4f892854d8e0d8b7awebhookr_   z+/advisor/api/v1/twilio/call-status-callback)	voice_urlvoice_methodr   r   )phone_number_sidr   zTwilio number updated to use: r)   r-   )r   r   r   r   r   incoming_phone_numberslistrF   __dict__sidr   r   r   trunkingv1trunksphone_numbersdeletecreate)r>   r   twilio_numberr   r   r   r   
number_sidsip_twiml_urlr6   r7   selected_urlr   s                r<   switch_plateformr    s    <<D((MooG%%K!F**	=	)!-F 

foo.J8Mx"8":ZA+Nwx&*i&7{]00<CC&=/)TU%	 D L yDH,!!'*88DKKM!!'*88??Q[?\	.,'	*4&
12	*<.
9:3D6: s   FFz/twilio/call-status-callbackc                 t  K   	 | j                          d{   }t        |      }t        d|       |j                  d      }|j                  d      }|j                  d      }|j	                  t
              j                  t
        j                  |k(        j                         }|rD||_	        |rt        t        |            |_        |j                          |j                  |       t        dd	      S 7 # t         $ r.}t        d
t#        |              t        dd	      cY d}~S d}~ww xY ww)z2
    Handle call status callbacks from Twilio
    NzCall Status Callback:rK   r   r   r   r   rA   rB   zError processing call status: z Error processing status callback)rE   rG   rF   r   r   r   r   r}   r   r   r   r   r   r   r   r   r4   r5   )	r>   rI   rH   status_datar   r   r   r   r;   s	            r<   r   r     s    
]!,,.(	9o%{3??9-!ool3#788H%,,X-D-D-PQWWY#.H )23};M)N&IIKJJx  :|TT# )&  ].s1vh78 B|\\]sE   D8C> C<C#C> ;D8<C> >	D5#D0*D5+D80D55D8)@r   r   r   typingr   fastapir   r   r	   pydanticr
   sqlalchemy.ormr   r1   r/   requests.authr   -app.advisor_service.services.ultravox_servicer   app.common.constantsr   r   app.common.databaser   app.common.twillio_helperr   r   app.common.utilsr   app.repositories.settings_repor   !app.retell_service.routers.retellr   /app.retell_service.services.caller_info_servicer   app.utils.helpers.commonr   app.common.modelsr   r   r   r   r   twilio.twiml.voice_responser   fastapi.responsesr   r   routerr5   r=   r2   r@   r   r   r   r   r   r   r   r   r   r  r   r   r   r<   <module>r"     sc   3 3  0 0  "  	 ' I D & P 9 7 ; [ 0 I I  5 & & ;	6C 60 )*Tg T +T
 '( 6?sSsS	sS )sSj %&
B' 
B '
B '(9@ P' Pw P )P( $%O O &O &':A&/ .Q7 .Q .Q (.Q`&
R $% &/OOO &O"  !II "I[I [ $%'$9 ' &'R +,6=fo ]w ]G ] -]r   