U
    iyZ                    @   s   d Z ddlZddlZddlZddlmZ ddlmZmZ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 ddlmZ G dd dZdS )z
FIXED Call Session Management for Concurrent Calls
Each call gets completely isolated services and state with proper database handling
    N)datetime)OptionalDictAnyList)	WebSocket)WebSocketDisconnect)Config)Log)WebSocketConnectionManager)AudioService)BotConfigurationServicec                   @   sh  e Zd ZdZeedddZedddZe	dd	d
dZ
ddddZe	dd	ddZeddddZddddZddddZddddZeddddZe	ddddZeddd d!Zedd"d#d$Zeeedd%d&d'Zedd(d)d*Zd<eedd,d-d.Zeedd/d0d1Zedd2d3d4Zeee	dd5d6d7Zddd8d9Zeeef dd:d;Z dS )=CallSessionz
    Completely isolated session for each concurrent call.
    Each call gets its own instances of all services with proper database isolation.
    )
session_id	websocketc                 C   s8  || _ || _t | _t|| _t | _d| _d | _	d | _
d| _d| _d| _tjdd| _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| _-d| _.d| _/g | _0g | _1d| _2t34d|  d S )N   r      d   )maxsizeFg333333?g      ??333333?Tu$   🆕 Created isolated call session: )5r   r   r   bot_configuration_servicer   connection_managerr   audio_serviceZservice_typeelevenlabs_websocket_serviceelevenlabs_receiver_taskZelevenlabs_last_reconnect_timeelevenlabs_reconnect_cooldownelevenlabs_reconnect_attemptsasyncioQueueelevenlabs_audio_queueelevenlabs_audio_queue_task elevenlabs_connection_retry_taskelevenlabs_conversation_idelevenlabs_initializingelevenlabs_audio_playinglast_interrupt_timeinterrupt_debounce_secondsvad_score_historyvad_history_durationmin_sustained_scoremin_sustained_durationbusiness_idcall_id	stream_idagent_idphone_numberdidcustomer_name
bot_configbot_namebot_idcompany_namecall_start_time	is_active
call_endedpending_end_call_tool_idpending_transfer_tool_idpending_transfer_target_last_connection_warning_time_last_call_ended_log_timeconversation_countuser_messagesbot_messagesmax_messagesr
   info)selfr   r    rE   :/var/www/html/live_calls/homebook/services/call_session.py__init__   s^    
zCallSession.__init__)returnc              
      s`   zt d| j d W dS  tk
rZ } z"t d| j d|  W Y dS d}~X Y nX dS )z:Initialize the call session with all required connections.u   ✅ Session z4 initialized (service_type 4 - ElevenLabs WebSocket)Tu!   ❌ Failed to initialize session : FN)r
   rC   r   	Exceptionerror)rD   erE   rE   rF   
initializeK   s    zCallSession.initializeN)datarH   c              
      s   z| di }| d| _| d| _| di }| dd| j | _| dd| _| d	d| _| d
d| _| dd| _| 	 I dH  t
d| j  |  I dH  t | _W n: tk
r } zt
d| j d|  W 5 d}~X Y nX dS )zOHandle call start event with business context - OPTIMIZED for concurrent calls.startstreamIdcallIdextra_headerszX-BIDZBID_z
X-Agent-idzNot providedzX-CustNumberz
X-CustNamezValued CustomerzX-DIDNu>   🎙️ Initializing ElevenLabs WebSocket service for session z&Error handling call start for session rI   )getr.   r-   r   r,   r/   r0   r2   r1   _load_bot_configurationr
   rC    _initialize_elevenlabs_websocketr   nowr7   rJ   rK   )rD   rN   
start_inforR   rL   rE   rE   rF   handle_call_startm   s     zCallSession.handle_call_startc              
      s  z| j sJtd| j d d| _d| _d| _ttdr>tj	nd| _
W dS | j| j I dH }|d| _|dd	| _|d
| _|dttdrtj	nd| _
W nd tk
r } zDtd| j d|  d| _d| _d| _ttdrtj	nd| _
W 5 d}~X Y nX dS )zcLoad bot configuration for this call (only agent_id, company_name, bot_name needed for ElevenLabs).u#   ⚠️ No DID provided for session z, using default configurationNzDefault AssistantCOMPANY_NAMEZMCuber3   r4   	Assistantr5   r6   u0   ❌ Error loading bot configuration for session rI   )r1   r
   warningr   r3   r4   r5   hasattrr	   rY   r6   r   Zget_bot_configuration_by_didrS   rJ   rK   )rD   Zconfig_resultrL   rE   rE   rF   rT      s&    "z#CallSession._load_bot_configurationc              
      s  zZ j slz0 j|  jr( j r8t   _W n* tjk
rd   t	
d j d Y nX W dS  jrxW dS ddl}| }d} j r j jrzFt j jdrt j jjdr j jjjdkrd	} j jsd	 j _W n tk
r   Y nX |s j  }|s jsd
} j jrjt j jdrdt j jjdrT j jjj}nt j jj}nd}nd}|dkr| }| j dkrt	
d j d|  | _|sH jr| }| j dkrt	d j  | _W dS d} jrzt jdr^t jjdr( jjjn
t jj}|dkr~d	}t	d| d j d n t jds~t	d j  W n4 tk
r } zt	d|  W 5 d}~X Y nX |rd	 _W dS  j rN j jrNz<t j jdrt j jjdr j jjj}	nt j jj}	|	dkrt	d|	 d j d tdI dH   jrrt	d j  W W dS  jrzlt jdrt jjdr jjjn
t jj}|dkrd	 _t	d| d j d W W W dS W n tk
r   Y nX t	d|	 d  j  W n4 tk
rL } zt	d!|  W 5 d}~X Y nX  jsnt	d" j  W dS | j j }
t j d#t j!d#  d$}|
|k rz j| W n, tjk
r   t	
d j d Y nX W dS  j!d$krt	"d% j! d& j d' W dS t	
d( j d) j!d*  d  j#rb j# j _$t	d+ j#dd,  d- n0 j j$r j j$ _#t	d. j#dd,  d-  j!dkrĈ j#st	
d/ j!d*  d0 W dS zD j j%dd1I dH  t	d2 j    j!d*7  _!| j _ j&r j& r(t '  _& fd3d4}t|   j(r j( s j()  z j(I dH  W n tj*k
r   Y nX  j+ j _, j- j _. j/ j _0 j1 j _2 j3 j _4 j5 j _6 j7 j _8 j9 j _: j; j _<t=d5 fd6d7}t j >| _(W n< tk
rF } zt	"d8|  W Y W dS d}~X Y nX |?d9i ?d:d;}|rXzddl@}ddlA}|B|} j r j  sW W dS |C|d#}tD|dkrz j E|I dH  W nD tk
r } z$d<tF|jGkrt	
d=|  W 5 d}~X Y nX W nD tk
rV } z$d<tF|jGkrFt	"d>|  W 5 d}~X Y nX W dS  tk
r } zt	"d? j d@|  W 5 d}~X Y nX dS )Az)Handle incoming media data for this call.u$   ⚠️ Audio queue full for session z, dropping chunkNr   FstatenameOPENTunknownzno state attributezno websocket)r_   
CONNECTINGg      @u1   ⚠️ Connection state check failed for session z - WebSocket state: uT   🛑 Call already ended (end_call or voicemail) - skipping reconnection for session client_state	CONNECTEDra   #   🛑 MCube WebSocket disconnected (1) - user hung up, marking call as ended (session )uJ   ⚠️ MCube WebSocket state unavailable - assuming connected for session z=Could not check MCube WebSocket state in handle_media_event: )ZCLOSINGZCLOSEDu   ⏳ WebSocket is z9 - waiting for close reason before reconnection (session g       @uZ   🛑 Call ended legitimately (close reason processed) - skipping reconnection for session z.) after wait - marking call as ended (session u   ⚠️ WebSocket is zD but call_ended is False - may have missed close reason for session z!Could not check WebSocket state: uB   🛑 Session no longer active - skipping reconnection for session r   
   u   ❌ Max reconnection attempts (z) reached for session z!. Stopping reconnection attempts.u6   ⚠️ ElevenLabs WebSocket not connected for session z&, attempting to reconnect... (attempt    u;   🔄 Restored conversation_id to service before reconnect:    ...u9   🔄 Synced conversation_id from service to CallSession: uG   ⚠️ Reconnect blocked: conversation_id not yet established (attempt z) - waiting for metadataZuse_signed_urlu4   ✅ Reconnected to ElevenLabs WebSocket for session c                      s*   t dI d H   jr& j r&d _d S )N   r   )r   sleepr   is_connectedr   rE   rD   rE   rF   $reset_reconnect_attempts_after_delays  s    zLCallSession.handle_media_event.<locals>.reset_reconnect_attempts_after_delaymessagec              
      sf   z j | I dH  W nJ tk
r` } z,td j d|  ddl}|  W 5 d}~X Y nX dS z+Wrapper to handle messages from ElevenLabs.Nu)   ❌ Error in message handler for session rI   r   r   Zhandle_messagerJ   r
   rK   r   	traceback	print_excrs   rL   rv   rp   rE   rF   message_handler_wrapper  s    z?CallSession.handle_media_event.<locals>.message_handler_wrapperu1   ❌ Failed to reconnect to ElevenLabs WebSocket: mediapayload ZConnectionClosedu#   ⚠️ Failed to send audio chunk: u(   ❌ Failed to send audio to ElevenLabs: z!Error handling media for session rI   )Hr   r    
put_nowaitr"   doner   create_task_retry_elevenlabs_connection	QueueFullr
   r[   r   r$   timer   r\   r]   r^   	connectedrJ   ro   strr=   r9   r>   rC   rb   debugrn   r8   Zlast_reconnect_timeminr   r   rK   r#   conversation_idconnectr!   _process_audio_queuer   cancelCancelledError_handle_elevenlabs_audioon_audio_received_handle_elevenlabs_interruptionon_interruption_received_handle_elevenlabs_responseon_response_received_handle_elevenlabs_tool_requeston_tool_request_received_handle_elevenlabs_transcripton_transcript_received_handle_elevenlabs_vad_scoreon_vad_score_received$_handle_elevenlabs_connection_closedon_connection_closed#_handle_automated_greeting_detectedon_automated_greeting_detected_handle_tool_abandonedon_tool_abandoneddictreceive_messagesrS   base64audioop	b64decodeulaw2linlensend_audio_chunktype__name__)rD   rN   r   current_timero   Zwebsocket_statemcube_disconnectedmcube_staterL   Zws_stateZtime_since_last_reconnectZcooldownrq   ry   Zreconnect_erroraudio_payloadr   r   mulaw_bytes	pcm_bytesZaudio_send_error
send_errorrE   rp   rF   handle_media_event   sN   



&
"
&
"
 












	


&"zCallSession.handle_media_event)r^   rH   c              
      s   zn| j | | jr"d|kr"d| _| jrlt| jdrl| jjjdkrld| jjj	|d}| j
t|I dH  W nN tk
r } z0ddl}td	|  td
|   W 5 d}~X Y nX dS )z:Handle playedStream event from MCube for audio completion.Zelevenlabs_audioFrb   rd   playedStream)eventrP   r^   Nr   u'   ❌ Error handling playedStream event: Full traceback: )r   handle_played_stream_eventr%   r   r\   rb   r^   r   r]   r.   	send_textjsondumpsrJ   rv   r
   rK   
format_exc)rD   r^   Zplayed_stream_messagerL   rv   rE   rE   rF   r     s    z&CallSession.handle_played_stream_eventc              
      s  zpd _ ddlm} ddlm} |j jddI dH sjtd j d	|	  d
|j
 d d _ W dS z jr~ jdnd}|sttdrtjnd}|std||d _ jrވ j j_td jdd  d  jjddI dH  W n8 tk
r, } z| jI dH   W 5 d}~X Y nX  j j_ j j_ j j_ j j_ j  j_! j" j_# j$ j_% j& j_' j( j_) j* j_+ jrʈ jjsʈ j j_td jdd  d  jdkr jj,d j-pdt. ddddI dH  ntd jdd  d t/d fdd}t01 j2| _3d _  j4r` j45 rpt01 6  _4W nH tk
r } z(t7d j d |  d _d _ W 5 d}~X Y nX dS )!z;Initialize ElevenLabs WebSocket service for service_type 4.Tr   )ElevenLabsWebSocketServiceelevenlabs_limiterg      $@timeoutNu7   ⚠️ ElevenLabs connection limit reached for session z
. Active: /z=. Audio chunks will be queued and connection will be retried.Fr/   ELEVENLABS_AGENT_IDz@ELEVENLABS_AGENT_ID is required. Set it in bot config or Config.)r/   u<   🔄 Restored conversation_id to service on initialization: rj   rk   rl   u*   🔄 Restored conversation_id to service: rZ   r0   ZUnknown)r4   r0   )Z	text_onlyZdynamic_variablesuH   🔄 Skipping conversation_initiation - resuming existing conversation: rr   c              
      sf   z j | I dH  W nJ tk
r` } z,td j d|  ddl}|  W 5 d}~X Y nX dS rt   ru   rx   rp   rE   rF   ry     s    zMCallSession._initialize_elevenlabs_websocket.<locals>.message_handler_wrapperu8   ❌ Error initializing ElevenLabs WebSocket for session rI   )8r$   Z%services.elevenlabs_websocket_servicer   &services.elevenlabs_connection_limiterr   acquirer   r
   r[   Zget_active_countZmax_connectionsr3   rS   r\   r	   r   
ValueErrorr   r#   r   rC   r   rJ   releaser   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    _handle_conversation_id_receivedZon_conversation_id_receivedZsend_conversation_initiationr6   getattrr   r   r   r   r   r!   r~   r   rK   )rD   r   r   r/   rL   ry   rE   rp   rF   rU     sp    














	
z,CallSession._initialize_elevenlabs_websocketc                    s  d}d}t |D ]}z| jr.| j r.W  dS ddlm} |j| jddI dH rz8|  I dH  | jrp| j	 rt
|  | _W W  dS  tk
r } z&|| jI dH  td|  W 5 d}~X Y nX W n tk
r   Y nX t
|I dH  qtd	| j d
| d dS )zYRetry ElevenLabs connection initialization periodically when connection limit is reached.rm   rh   Nr   r   g      ?r   u>   ⚠️ Connection initialization failed after acquiring slot: u%   ❌ Failed to connect ElevenLabs for z after z retries)ranger   ro   r   r   r   r   rU   r!   r~   r   r   r   rJ   r   r
   r[   rn   rK   )rD   Zmax_retriesZretry_intervalZattemptr   Z
init_errorrE   rE   rF   r   ,  s,    
&z(CallSession._retry_elevenlabs_connectionc           	   
      sf  z"d}| j  s"| jr$| j s:td| j  q"ztj| j 	 ddI dH }|	di 	dd}|rddl
}ddl}||}||d	}t|dkr| j|I dH  |d
7 }| j   W q tjk
r   Y q"Y q tk
r } ztd|  W Y q"W 5 d}~X Y qX qW n< tk
r` } ztd| j d|  W 5 d}~X Y nX dS )z/Process queued audio chunks after reconnection.r   u@   ⚠️ Connection lost while processing audio queue for session g?r   Nrz   r{   r|   r   ri   u,   ⚠️ Error processing queued audio chunk: u0   ❌ Error in audio queue processing for session rI   )r    emptyr   ro   r
   r[   r   r   wait_forrS   r   r   r   r   r   r   	task_doneTimeoutErrorrJ   rK   )	rD   Zprocessed_countZqueued_datar   r   r   r   r   rL   rE   rE   rF   r   R  s2    

"z CallSession._process_audio_queue)audio_bytesrH   c                    s8  z|rt |dkrW dS | jjjs*W dS | jjjdkr>W dS ddl}ddl}t	| j
dd}tj}d}dt|ks~dt|kr|}nt|tod| kpd	| k}d
}t|trd|ksd| krd
}qd|ksd| krd}q|rdnd
}nd
}|r@|}||krr||d}	||	dd||d\}
}||
d}n2||krf||dd||d\}}|}||d}d}t || d | }d| _tdt ||D ]}| jjjdkrd| _ W dS ||||  }||d}dtjtj|dtt   d||  dd}z| j|I dH  W nh ttfk
r } zDdt|ksndt|ksndt|krd| _W Y  W dS  W 5 d}~X Y nX qzJdtt   d|d  }| j | jjj|}| j|I dH  W n t!k
r   Y nX W n4 t!k
r2 } zt"#d|  W 5 d}~X Y nX dS )z8Handle audio received from ElevenLabs and send to Mcube.r   Nrd   Zagent_audio_formatZ	pcm_16000Z	ulaw_8000Z
mulaw_8000ZmulawZulawi>  Z16000Z16kZ8000Z8ki@  r   ri   i  TFzutf-8	playAudioZelevenlabs_audio__)contentType
sampleRater{   r^   )r   rz   zwebsocket.sendzwebsocket.closezalready completedu%   ❌ Error handling ElevenLabs audio: )$r   r   r]   r.   r   rb   r^   r   r   r   r   r	   MCUBE_SAMPLE_RATEr   
isinstancelowerr   Zratecvlin2ulawr%   r   	b64encodedecodeMCUBE_AUDIO_FORMATintr   rV   	timestampsend_to_mcuber   RuntimeErrorr   create_checkpoint_messagerJ   r
   rK   )rD   r   r   r   Zelevenlabs_formattarget_sample_rater   Zis_mulawZsource_sample_rater   Zresampled_pcmr   Zresampled_bytes
chunk_sizeZtotal_chunksichunkaudio_base64audio_messager   Zlast_chunk_nameZcheckpoint_messagerL   rE   rE   rF   r   x  s    
"

	
	* 
z$CallSession._handle_elevenlabs_audio)
transcriptrH   c              
      s  zt |tr|ddnt|}td|  |r| js|  dddddd	d
dddg
}t fdd|D rtd| j	 d d| _W dS | j
|t  dd t| j
| jkr| j
| j d | _
W n4 tk
r } ztd|  W 5 d}~X Y nX dS )z+Handle transcript received from ElevenLabs.Zuser_transcriptr|   u#   🎤 [ElevenLabs User Transcript]: 	voicemailleave a messagerecord your messagenot availableat the tonezplease recordzafter the tonezfinished recordingzperson you are trying to reachzplease record your messagec                 3   s   | ]}| kV  qd S NrE   ).0patternZtranscript_lowerrE   rF   	<genexpr>  s     z<CallSession._handle_elevenlabs_transcript.<locals>.<genexpr>uI   🛑 Voicemail detected from transcript - marking call as ended (session rg   TNusercontentr   r   u*   ❌ Error handling ElevenLabs transcript: )r   r   rS   r   r
   rC   r9   r   anyr   r@   appendr   rV   	isoformatr   rB   rJ   rK   )rD   r   Ztranscript_textZvoicemail_patternsrL   rE   r   rF   r     s:    

z)CallSession._handle_elevenlabs_transcript)responserH   c              
      s   zTt d|  | j|t  dd t| j| jkrR| j| j d | _W n2 t	k
r } zt 
d|  W 5 d}~X Y nX dS )z.Handle text response received from ElevenLabs.u"   🤖 [ElevenLabs Agent Response]: Zbotr   Nu(   ❌ Error handling ElevenLabs response: )r
   rC   rA   r   r   rV   r   r   rB   rJ   rK   )rD   r   rL   rE   rE   rF   r     s    
z'CallSession._handle_elevenlabs_response)scorerH   c              
      s  zddl }|  }js W dS j||f |j   fddjD _tjdk rdW dS dd jD }t|t| }t|}d}fdd|D }t|d	 }	||kr||kr|	jkr|j	 }
|
j
k rW dS |_	td
|dd|dd|	dd d_j  jrFj rFj I dH  jjjrz&djjjd}j|I dH  W n4 tk
r } ztd|  W 5 d}~X Y nX W n4 tk
r } ztd|  W 5 d}~X Y nX dS )zZHandle VAD score received from ElevenLabs for interruption detection with noise filtering.r   Nc                    s    g | ]\}}| kr||fqS rE   rE   )r   ts)cutoff_timerE   rF   
<listcomp>0  s      z<CallSession._handle_elevenlabs_vad_score.<locals>.<listcomp>r   c                 S   s   g | ]\}}|qS rE   rE   )r   r   r   rE   rE   rF   r   5  s     r   c                    s   g | ]}| j kr|qS rE   )r*   )r   r   rp   rE   rF   r   ;  s     
 g?u>   🎙️ ElevenLabs VAD detected sustained user speech (score: z.2fz, avg: z, sustained: zs) - triggering interruptionF
clearAudior   rP   u   ❌ Error sending clearAudio: u)   ❌ Error handling ElevenLabs VAD score: )r   r%   r(   r   r)   r   summaxr+   r&   r'   r
   rC   clearr   ro   Zsend_interruptionr   r]   r.   r   rJ   rK   )rD   r   r   r   Zrecent_scoresZ	avg_scoreZ	max_scoreZVAD_THRESHOLDZhigh_scoresZsustained_durationZtime_since_last_interruptclear_audio_messagerL   rE   )r   rD   rF   r   %  sH    
 

$
&z(CallSession._handle_elevenlabs_vad_score)
close_codeclose_reasonis_terminal_reasonrH   c              
      s  zV|r*d| _ td| d| j  n,td| d| d d}| jr0zt| jdrt| jjd	rt| jjjn
t| jj}|d
krd}td| d| j d nPz"t| jdst	d| j  W n, t
k
r   d}td| j d Y nX W n4 t
k
r. } zt	d|  W 5 d}~X Y nX |rVd| _ td| d| j d W n4 t
k
r } ztd|  W 5 d}~X Y nX dS )a*  
        Handle ElevenLabs WebSocket connection closed event.
        
        Args:
            close_code: WebSocket close code
            close_reason: Close reason string from ElevenLabs
            is_terminal_reason: True if this is a terminal call end reason (should not reconnect)
        Tu'   🛑 Call marked as ended (ElevenLabs: ') - reconnections disabled for session u+   ⚠️ ElevenLabs connection closed (Code: z
, Reason: z) - may allow reconnectionFrb   r^   rc   re   rf   rg   uR   ⚠️ MCube WebSocket state unavailable in connection_closed handler for session uU   🛑 MCube WebSocket appears closed (access failed) - marking call as ended (session zDCould not check MCube WebSocket state in connection_closed handler: Nu<   🛑 Call marked as ended (MCube disconnected + ElevenLabs: z$) - reconnections disabled (session u,   ❌ Error handling connection closed event: )r9   r
   rC   r   r   r\   rb   r^   r   r   rJ   rK   )rD   r   r   r   r   r   rL   rE   rE   rF   r   \  s4    	$"z0CallSession._handle_elevenlabs_connection_closed)r   rH   c              
      sj   z2| j s0|| _ td|dd  d| j d W n2 tk
rd } ztd|  W 5 d}~X Y nX dS )z
        Handle conversation_id received from ElevenLabs.
        Store it in CallSession for safety (prevents loss if service is recreated).
        
        Args:
            conversation_id: The conversation ID from ElevenLabs
        u,   💾 Stored conversation_id in CallSession: Nrj   z... (session rg   u#   ❌ Error storing conversation_id: )r#   r
   rC   r   rJ   rK   )rD   r   rL   rE   rE   rF   r     s    &z,CallSession._handle_conversation_id_receivedF)greeting_textis_voicemailrH   c              
      sv   z>|r"t d| j d d| _nt d|dd  d W n2 tk
rp } zt d|  W 5 d}~X Y nX dS )	z
        Handle automated greeting detection from ElevenLabs (voicemail detection).
        
        Args:
            greeting_text: The detected greeting text
            is_voicemail: True if this is a voicemail greeting
        uP   🛑 Voicemail detected via automated greeting - marking call as ended (session rg   Tu2   📞 Automated greeting detected (not voicemail): Nr   rk   u1   ❌ Error handling automated greeting detection: )r
   rC   r   r9   rJ   rK   )rD   r  r  rL   rE   rE   rF   r     s    z/CallSession._handle_automated_greeting_detected)tool_call_idresultrH   c              
      s  z| j |krTd| _d| _ td| d| j d td|rH|dd nd  n| j|kr| jpfd	}td
| d| d| j d td|r|dd nd  d| _d| _n&td| d|r|dd nd  W n4 tk
r } zt	d|  W 5 d}~X Y nX dS )a  
        Handle tool execution abandonment (e.g., end_call or transfer_to_number tool abandoned due to user input).
        
        Args:
            tool_call_id: The tool call ID that was abandoned
            result: The abandonment message from ElevenLabs
        FNu8   ⚠️ end_call tool execution abandoned (tool_call_id: z=) - call is still active, resetting call_ended flag (session rg   z   Abandonment reason:    zNo reason providedzunknown targetuB   ⚠️ transfer_to_number tool execution abandoned (tool_call_id: z
, target: z') - user interrupted transfer (session u-   🔧 Tool execution abandoned (tool_call_id: z): z	No resultu+   ❌ Error handling tool abandonment event: )
r:   r9   r
   r[   r   r;   r<   rC   rJ   rK   )rD   r  r  transfer_targetrL   rE   rE   rF   r     s    
"

  *z"CallSession._handle_tool_abandoned)event_idrH   c              
      s   zt d| d d| _| j  | jjjrz&d| jjjd}| j|I dH  W q t	k
r } zt 
d|  W 5 d}~X Y qX n
t d W n2 t	k
r } zt 
d	|  W 5 d}~X Y nX dS )
z3Handle interruption event received from ElevenLabs.u3   ⚠️ ElevenLabs interruption detected (event_id: z) - sending clearAudio to McubeFr   r   Nu.   ❌ Error sending clearAudio on interruption: u5   ⚠️ No stream_id available, cannot send clearAudiou,   ❌ Error handling ElevenLabs interruption: )r
   rC   r%   r(   r   r   r]   r.   r   rJ   rK   r[   )rD   r  r   rL   rE   rE   rF   r     s    

$z+CallSession._handle_elevenlabs_interruption)	tool_namer  tool_requestrH   c              i      s  zvt d| d| d |dkrh|| _d| _t d| d| j  | jjjrz2dd	lm	} |
| jjj}| j|I d
H  W q tk
r } z0dd
l}t d|  t d|   W 5 d
}~X Y qX n
t d | jrv| j rvz| jj|dddI d
H  W nP tk
rb } z0dd
l}t d|  t d|   W 5 d
}~X Y nX n|dkr$d}d}	|di }
|
rddddg}t|
 tfdd|D rd}d}	|sJ| jrJ| jdd
 }dddddg}|D ]V}t|dd  t fdd|D rd}d |ddd
d!  }	 qJq|rhd| _t d"| j  | jr| j rzF||r|	nd#d$}| jj|t|ddI d
H  t d%|  W nP tk
r } z0dd
l}t d&|  t d|   W 5 d
}~X Y nX n
t d' 
nR|d(ks8|d)krd*}d+}|dpNi }
|
d,p|
d-p|
d.p|
d/p|
d0}|d
k	rt| rt| }| }d1d1d1d1d2d2d3d3d3d3d4d4d5d5d5d6d6d7d7d8d8d8d8d9d9d:d:d;d;d<d<d=d=d>d>d?d?d?d@d@dAdAdBdBdCdCdDdDdEdEdFdFdGdGdHdHdIdIdJdJdKdKdLdLdLdLdMdMdNdNdOdOdOdOdPdPdQdQdQdRdRdRdSdSdSdTdTdTdUdUdUdVdVdVdWdWdWdXdXdXdXdYdYdYdZh}||}|d
krt|d[kr|n
|d
d[ }t|
d\pd]}|d^}|d*kr| js|rtd_I d
H  | jr| jd`d
 }|D ]}t|dd  t fdaddbD r\dc\}} qt fddddeD rdf\}} qt fdgddhD rdi\}} qt fdjddkD rdl\}} qt fdmddnD rdo\}} qt fdpddqD rdr\}} qq||ds}| jr| j rz2| jj|t|ddI d
H  t dt|  W n4 tk
r } zt du|  W 5 d
}~X Y nX n
t dv n|dwkrb| jrT| j rTz.| jj|dxddI d
H  t dy| j  W nP tk
rP } z0dd
l}t dz|  t d|   W 5 d
}~X Y nX n
t d{ n|d|kr|di }
|
r|
d}nd
}|st t!d~rt!j"nd
}| jjjr|rzzdd	lm	} |j#| jjj|dd}| j|I d
H  t d|  | jr2| j r2| jj|d| ddI d
H  W n tk
r } zbdd
l}t d|  t d|   | jr| j r| jj|d| ddI d
H  W 5 d
}~X Y nX n@d}t d|  | jrv| j rv| jj||ddI d
H  nx|dkr|| _$|di }
|
	r*|
dnd
}|
	r>|
dnd
}|
	rR|
dnd
}|	pd|	pd|}|	szd}t d |	r|%d	r|&dd &dd }t d|  |}|	r| dk
r.d| d}t d| d| d d
| _'| j
r"| j 
r"| jj||ddI d
H  d
| _$W d
S || _'t d|  t d| jjj  | jjjr|rzdd	lm	} |j#| jjj|dd}| j|I d
H  t d|  | j
r| j 
r| jj|d| ddI d
H  d
| _$d
| _'W n tk
r } zndd
l}t d|  t d|   | jrd| j rd| jj|d| ddI d
H  d
| _$d
| _'W 5 d
}~X Y nX nld| jjj d| d| d| d	}t d|  | jr| j r| jj||ddI d
H  d
| _$d
| _'n|dkrd|di }
|
r|
dnd
}| jjjr"|r"zld| jjj|d}| j|I d
H  t d|  | jr| j r| jj|d| dddI d
H  W n tk
r } zbdd
l}t d|  t d|   | jr| j r| jj|d| ddI d
H  W 5 d
}~X Y nX n@d}t d|  | jrv| j rv| jj||ddI d
H  nt d| d W n4 tk
r } zt d|  W 5 d
}~X Y nX d
S )z5Handle tool request from ElevenLabs (e.g., end_call).u   🔧 Received tool request: z (call_id: rg   Zend_callTuB   🛑 Call marked as ended (end_call tool triggered, tool_call_id: r  r   )McubeServiceNu%   ❌ Error sending terminate message: r   u<   ⚠️ No stream_id available, cannot send terminate messagezCall terminated successfullyF)r  r  Zis_erroru   ❌ Error sending tool result: Zvoicemail_detectionr|   
parametersr   r   r   r   c                 3   s   | ]}| kV  qd S r   rE   r   keyword)
params_strrE   rF   r     s     z>CallSession._handle_elevenlabs_tool_request.<locals>.<genexpr>z'Voicemail detected from tool parametersr   r   c                 3   s   | ]}| kV  qd S r   rE   r  msg_textrE   rF   r   %  s     z"Voicemail detected in transcript: r   uT   🛑 Call marked as ended (voicemail detected) - reconnections disabled for session zNo voicemail detected)voicemail_detectedrs   u%   ✅ Voicemail detection result sent: u.   ❌ Error sending voicemail detection result: uQ   ⚠️ ElevenLabs WebSocket not connected, cannot send voicemail detection resultZdetect_languageZlanguage_detectionr`   g        Zlanguage_idlanguageZlanguage_codedetected_languagelangenjazhdehifrkoptitesruidnltrfilplsvbgroarcselfihrmsskdataukvinohuteknmlmrbngupaoras)hr  zen-inzen-usenglishr  japaneser  chinesezzh-cnzzh-twr  germanr  zhi-inZhindir  frenchr  koreanr  
portuguesezpt-brzpt-ptr  italianr   spanishr!  russianr"  Z
indonesianr#  dutchr$  turkishr%  ZfilipinoZtagalogr&  polishr'  swedishr(  	bulgarianr)  romanianr*  arabicr+  czechr,  greekr-  finnishr.  croatianr/  Zmalayr0  slovakr1  danishr2  zta-inZtamilzin flag tamilr3  Z	ukrainianr4  Z
vietnameser5  	norwegiannbnnr6  	hungarianr7  zte-inZtelugur8  zkn-inZkannadar9  zml-inZ	malayalamr:  zmr-inZmarathir;  zbn-inZbengalir<  zgu-inZgujaratir=  zpa-inZpunjabir>  zor-inZodiaZoryar?  zas-inZassameserh   
confidenceg?r  r   c                 3   s   | ]}| kV  qd S r   rE   r   wr  rE   rF   r     s     )	Zhelloyesr5  ZthankZpleaseokZokayz
it's cleanzcome on)r  ffffff?c                 3   s   | ]}| kV  qd S r   rE   r]  r  rE   rF   r     s     )ZnamasteZhaanZnahiZkaiseZkya)r  ra  c                 3   s   | ]}| kV  qd S r   rE   r]  r  rE   rF   r     s     )u	   ஆமாu	   சரிu	   உம்u   வணக்கம்u   எனக்குu   புரியுதுu   கண்டிப்பாu!   சொல்லுங்கள்u'   பேசுகிறீர்களாZammasariZ	puriyuthuZ	solungala)r2  ra  c                 3   s   | ]}| kV  qd S r   rE   r]  r  rE   rF   r     s     )u   అవునుu	   సరేu	   ఎలాZemuZsareZela)r7  ra  c                 3   s   | ]}| kV  qd S r   rE   r]  r  rE   rF   r     s     )u   ಹೌದುu	   ಸರಿZhowdurb  )r8  ra  c                 3   s   | ]}| kV  qd S r   rE   r]  r  rE   rF   r     s     )u	   അതെu	   ശരിZathaerb  )r9  ra  )r  r[  u$   ✅ Language detection result sent: u-   ❌ Error sending language detection result: uP   ⚠️ ElevenLabs WebSocket not connected, cannot send language detection resultZ	skip_turnzTurn skipped successfullyu#   ✅ Skip turn executed for session u$   ❌ Error sending skip turn result: uG   ⚠️ ElevenLabs WebSocket not connected, cannot send skip turn resultZtransfer_to_agentagent_numberAGENT_PHONE_NUMBER)show_original_caller_idu!   ✅ Transfer to agent initiated: zCall transferred to agent: u!   ❌ Error transferring to agent: zError transferring to agent: z3No stream_id or agent_number available for transferu   ⚠️ Ztransfer_to_numberr0   sip_uritransfer_numberzsip:9873216540@1.6.192.216uP      ⚠️ ElevenLabs sent empty parameters, using configured SIP URI as fallbackzsip::ri   @u-      🔄 Extracted phone number from SIP URI: )r`   zunknown numbernoner|   zInvalid transfer target: 'z%' - cannot transfer to unknown numberz (tool_call_id: u(      📞 Transfer target (E.164 format): u      🌐 Stream ID: u   ✅ Transfer initiated to: zCall transferred to: u"   ❌ Error transferring to number: zError transferring to number: zCNo stream_id or transfer target available for transfer (stream_id: z, phone_number: z, sip_uri: z, transfer_number: Zplay_keypad_touch_tonetoneZdtmf)r   rP   rk  u   ✅ Keypad touch tone played: zKeypad tone z played successfullyu   ❌ Error playing keypad tone: zError playing keypad tone: z.No stream_id or tone available for keypad toneu   ⚠️ Unknown tool request: z - ignoringu!   ❌ Error handling tool request: )(r
   rC   r:   r9   r   r   r]   r.   Zservices.mcube_servicer  create_terminate_messager   rJ   rv   rK   r   r[   r   ro   Zsend_client_tool_resultrS   r   r   r   r@   r   r   stripr   floatr   rn   r\   r	   rd  create_transfer_messager;   
startswithsplitr<   )rD   r	  r  r
  r  Zterminate_messagerL   rv   r  Zvoicemail_messageZtool_paramsZvoicemail_keywordsZrecent_messagesmsgr  r  r[  Zraw_langZ_normZ_NAME_TO_CODEZtool_event_idrc  Ztransfer_messageZ	error_msgr0   rf  rg  r  Zphone_number_onlyrk  Zdtmf_messagerE   )r  r  rF   r     s   

(
*

(                                                               -





$
(
 


$


 z+CallSession._handle_elevenlabs_tool_requestc              
      s  zt d| j  ddlm} || jI dH  | jrv| j sv| j  z| jI dH  W n t	j
k
rt   Y nX | jr| j s| j  z| jI dH  W n t	j
k
r   Y nX | j sz| j  | j  W q t	jk
r   Y qY qX q| jrJz| j I dH  W n4 tk
rH } zt d|  W 5 d}~X Y nX | jr| j  z| jI dH  W n t	j
k
r   Y nX d| _W n4 tk
r } zt d|  W 5 d}~X Y nX dS )z+Cleanup call session and release resources.u"   🧹 Starting cleanup for session r   r   Nz$Error closing ElevenLabs WebSocket: Fu   ❌ Error cleaning up: )r
   rC   r   r   r   r   r!   r~   r   r   r   r"   r    r   
get_nowaitr   
QueueEmptyr   closerJ   rK   r   r8   )rD   r   Zel_errorrL   rE   rE   rF   cleanup  sH    



"

zCallSession.cleanupc                 C   s   | j | j| j| j| jdS )z(Get current status of this call session.r   r,   r-   r8   r?   rw  rp   rE   rE   rF   
get_status  s    zCallSession.get_status)F)!r   
__module____qualname____doc__r   r   rG   boolrM   r   rX   rT   r   r   rU   r   r   bytesr   r   r   rn  r   r   r   r   r   r   r   r   rv  r   rx  rE   rE   rE   rF   r      s6   2"  b&&o,73    =3r   )r{  r   r   r   r   typingr   r   r   r   fastapir   fastapi.websocketsr   configr	   services.log_utilsr
   Zservices.connection_managerr   services.audio_servicer   Z"services.bot_configuration_servicer   r   rE   rE   rE   rF   <module>   s   