
    qiD                         d Z ddlZddlZddlZddlmZ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mZmZmZmZmZ  G d d      Zy)	zWhatsApp API Client.

This module provides a client for communicating with the WhatsApp Cloud API,
handling webhook requests, managing WebRTC connections, and processing
WhatsApp call events.
    N)	AwaitableCallableDictListOptional)logger)	IceServerSmallWebRTCConnection)WhatsAppApiWhatsAppConnectCallWhatsAppConnectCallValueWhatsAppTerminateCallWhatsAppTerminateCallValueWhatsAppWebhookRequestc                   P   e Zd ZdZ	 	 ddededej                  deee	      dee   ddfd	Z
ddeee	      fd
Zddee   fdZdefdZdefdZddZdeeef   dedefdZdedefdZ	 	 	 d dedeeeged   f      dee   dee   def
dZdedefdZdedefdZdedefdZy)!WhatsAppClienta  WhatsApp Cloud API client for handling calls and webhook requests.

    This client manages WhatsApp call connections using WebRTC, processes webhook
    events from WhatsApp, and maintains ongoing call state. It supports both
    incoming call handling and call termination through the WhatsApp Cloud API.

    Attributes:
        _whatsapp_api: WhatsApp API instance for making API calls
        _ongoing_calls_map: Dictionary mapping call IDs to WebRTC connections
        _ice_servers: List of ICE servers for WebRTC connections
    Nwhatsapp_tokenphone_number_idsessionice_serverswhatsapp_secretreturnc                 ~    t        |||      | _        || _        i | _        |t	        d      g| _        y|| _        y)a  Initialize the WhatsApp client.

        Args:
            whatsapp_token: WhatsApp API access token
            phone_number_id: WhatsApp phone number ID for the business account
            session: aiohttp session for making HTTP requests
            ice_servers: List of ICE servers for WebRTC connections. If None,
                        defaults to Google's public STUN server
            whatsapp_secret: WhatsApp APP secret for validating that the webhook request came from WhatsApp.
        )r   r   r   Nzstun:stun.l.google.com:19302)urls)r   _whatsapp_api_whatsapp_secret_ongoing_calls_mapr	   _ice_servers)selfr   r   r   r   r   s         T/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/transports/whatsapp/client.py__init__zWhatsAppClient.__init__.   sL    $ ))?T[
 !0DF !*0N!O PD +D    c                     || _         y)z;Update the list of ICE servers used for WebRTC connections.N)r   )r   r   s     r    update_ice_serversz!WhatsAppClient.update_ice_serversL   s
    'r"   c                     || _         y)zZUpdate the WhatsApp APP secret for validating that the webhook request came from WhatsApp.N)r   )r   r   s     r    update_whatsapp_secretz%WhatsAppClient.update_whatsapp_secretP   s
     /r"   c                 :    | j                   j                  |       y)z%Update the WhatsApp API access token.N)r   update_whatsapp_token)r   r   s     r    r(   z$WhatsAppClient.update_whatsapp_tokenT   s    00@r"   c                 :    | j                   j                  |       y)z7Update the WhatsApp phone number ID for authentication.N)r   update_whatsapp_phone_number_id)r   r   s     r    r*   z.WhatsAppClient.update_whatsapp_phone_number_idX   s    ::?Kr"   c                   K   t        j                  d       | j                  st        j                  d       yt        j                  dt        | j                         d       g }| j                  j	                         D ]r  \  }}t        j                  d|        | j
                  r*|j                  | j
                  j                  |             |j                  |j                                t t        j                  |ddi d{    | j                  j                          t        j                  d	       y7 4w)
a,  Terminate all ongoing WhatsApp calls.

        This method will:
        1. Send termination requests to WhatsApp API for each ongoing call
        2. Disconnect all WebRTC connections
        3. Clear the ongoing calls map

        All terminations are executed concurrently for efficiency.
        z)Will terminate all ongoing WhatsApp callszNo ongoing calls to terminateNzTerminating z ongoing callszTerminating call return_exceptionsTz!All calls terminated successfully)r   debugr   lenitemsr   appendterminate_call_to_whatsapp
disconnectasynciogatherclear)r   termination_taskscall_idpipecat_connections       r    terminate_all_callsz"WhatsAppClient.terminate_all_calls\   s     	@A&&LL89|C(?(?$@#APQ +/+B+B+H+H+J 	F'G'LL,WI67!!!((););)V)VW^)_`$$%7%B%B%DE	F nn/H4HHH 	%%'89	 	Is   DEE5Eparamsexpected_verification_tokenc                    K   |j                  d      }|j                  d      }|j                  d      }|r|r|st        d      |dk7  rt        d| d      ||k7  rt        d      t        |      S w)	a  Handle a verify webhook request from WhatsApp.

        Args:
            params: Dictionary containing webhook parameters from query string
            expected_verification_token: The expected verification token to validate against

        Returns:
            int: The challenge value if verification succeeds

        Raises:
            ValueError: If verification fails due to missing parameters or invalid token
        zhub.modezhub.challengezhub.verify_tokenz0Missing required webhook verification parameters	subscribez-Invalid hub mode: expected 'subscribe', got ''z#Webhook verification token mismatch)get
ValueErrorint)r   r:   r;   mode	challengeverify_tokens         r    handle_verify_webhook_requestz,WhatsAppClient.handle_verify_webhook_request   s      zz*%JJ/	zz"459LOPP;LTFRSTUU66BCC9~s   A4A6raw_bodysha256_signaturec                 T  K   t        j                  | j                  j                  d      |t        j
                        j                         }|st        d      |j                  d      d   }t        j                  ||      st        d      t        j                  d       yw)	z6Common handler for both /start and /connect endpoints.zutf-8)keymsg	digestmodz"Missing X-Hub-Signature-256 headerzsha256=zInvalid webhook signaturezWebhook signature verified!N)hmacnewr   encodehashlibsha256	hexdigest	Exceptionsplitcompare_digestr   r-   )r   rF   rG   expected_signaturereceived_signatures        r    "_validate_whatsapp_webhook_requestz1WhatsAppClient._validate_whatsapp_webhook_request   s      "XX%%,,W5nn
 )+	 	  @AA-33I>rB ""#57IJ78824s   B&B(requestconnection_callbackc           
      `  K   	 | j                   r| j                  ||       d{    |j                  D ]Y  }|j                  D ]F  }t	        |j
                  t              r|j
                  j                  D ]  }|j                  dk(  st        j                  d|j                          	 | j                  |       d{   }|r5|r3	  ||       d{    t        j                  d|j                             y t	        |j
                  t              s|j
                  j                  D ]S  }|j                  d	k(  st        j                  d
|j                          	 | j!                  |       d{   c c c S  I \ d}t        j"                  | d|        t%        |      7 7 7 # t        $ r/}	t        j                  d|j                   d|	        Y d}	~	d}	~	ww xY w# t        $ r+}
t        j                  d|j                   d|
         d}
~
ww xY w7 # t        $ r+}t        j                  d|j                   d|         d}~ww xY w# t        $ r6}t        j                  d|        t        j                  d|         d}~ww xY ww)a  Handle a webhook request from WhatsApp.

        This method processes incoming webhook requests and handles both
        connect and terminate events. For connect events, it establishes
        a WebRTC connection and optionally invokes a callback with the
        new connection.

        Args:
            request: The webhook request from WhatsApp containing call events
            connection_callback: Optional callback function to invoke when a new
                               WebRTC connection is established. The callback
                               receives the SmallWebRTCConnection instance.
            raw_body: Optional bytes containing the raw request body.
            sha256_signature: Optional X-Hub-Signature-256 header value from the request.

        Returns:
            bool: True if the webhook request was handled successfully, False otherwise

        Raises:
            ValueError: If the webhook request contains no supported events
            Exception: If connection establishment or API calls fail
        Nconnectz"Processing connect event for call z3Connection callback executed successfully for call z$Connection callback failed for call : T(Failed to handle connect event for call 	terminatez$Processing terminate event for call z*Failed to handle terminate event for call z+No supported event found in webhook requestz"Error processing webhook request: zWebhook request details: )r   rX   entrychanges
isinstancevaluer   callseventr   r-   id_handle_connect_eventrS   errorr   _handle_terminate_eventwarningr@   )r   rY   rZ   rF   rG   r`   changecall
connectioncallback_errorconnect_errorterminate_error	error_msges                 r    handle_webhook_requestz%WhatsAppClient.handle_webhook_request   s    :6	$$==hHXYYY  )*#mm (*F!&,,0HI$*LL$6$6 *D#zzY6 &/QRVRYRYQZ-[ \!*7;7Q7QRV7W1WJ (;z).2Ej2Q,Q,Q,2LL2efjfmfmen0o-. ,0'*6 $FLL2LM$*LL$6$6 	*D#zz[8 &/STXT[T[S\-] ^!*151M1Md1S+S$S		*?(*)*X FINNi[7)45Y''_ Z 2X
 -R 09 ).,2LL2VW[W^W^V__abpaq0r-. -.). (1 !*$*LL*RSWSZSZR[[]^k]l(m%& %*	!* ,T'0 !*$*LL*TUYU\U\T]]_`o_p(q%& %*	!*  	LL=aSABLL4WI>?	s   J.!I, F:A&I, "I, 0G<F=
G<GF?
&G I, J.I, !(I, 
"I, -H5H3
H5I, J./I, =G<?G	G9	
%G4	/G<4G9	9G<<	H0	&H+	+H0	0I, 3H55	I)	>&I$	$I)	)I, ,	J+51J&&J++J.sdpc                     |j                         }g }|D ]6  }|j                  d      r|j                  d      s&|j                  |       8 dj                  |      dz   S )a>  Filter SDP to be compatible with WhatsApp requirements.

        WhatsApp only supports SHA-256 fingerprints, so this method removes
        other fingerprint types from the SDP.

        Args:
            sdp: The original SDP string

        Returns:
            Filtered SDP string compatible with WhatsApp
        za=fingerprint:za=fingerprint:sha-256z
)
splitlines
startswithr0   join)r   rt   linesfilteredlines        r    _filter_sdp_for_whatsappz'WhatsAppClient._filter_sdp_for_whatsapp  sb       	"D/0I`9aOOD!	" {{8$v--r"   rl   c                 J   K   t        j                  dj                   dj                          d}	 t	         j
                        }|j                  j                  j                  j                  j                         d{    |j                         j                  d      } j                  |      }t        j                  dj                          	  j                  j                  j                  d|j                         d{   }|j                  dd	      s3t        j                  d
j                   d|        t!        d|       t        j                  dj                          	  j                  j                  j                  d|j                         d{   }|j                  dd	      s3t        j                  dj                   d|        t!        d|       t        j                  dj                          | j"                  j                  <   |j%                  d      dt        f fd       }t        j                  dj                          |S 7 7 o# t         $ r8}t        j                  dj                   d|        t!        d|       d}~ww xY w7 # t         $ r8}t        j                  dj                   d|        t!        d|       d}~ww xY w# t         $ r}|rV	 |j'                          d{  7   n;# t         $ r/}t        j                  dj                   d|        Y d}~nd}~ww xY wt        j                  dj                   d|         d}~ww xY ww)a  Handle a CONNECT event by establishing WebRTC connection and accepting the call.

        This method:
        1. Creates a new WebRTC connection using configured ICE servers
        2. Initializes the connection with the provided SDP
        3. Generates an SDP answer and filters it for WhatsApp compatibility
        4. Pre-accepts the call with WhatsApp API
        5. Accepts the call with WhatsApp API
        6. Stores the connection for later management

        Args:
            call: WhatsApp connect call event

        Returns:
            The established SmallWebRTCConnection instance

        Raises:
            Exception: If pre-accept or accept API calls fail
        zIncoming call from , call_id: N)rt   typert   zSDP answer generated for call 
pre_acceptsuccessFzFailed to pre-accept call r]   zFailed to pre-accept call: zPre-accept successful for call z$Pre-accept API call failed for call acceptzFailed to accept call zFailed to accept call: zAccept successful for call z Accept API call failed for call closedwebrtc_connectionc                    K   t        j                  d| j                   dj                          j                  j                  j                  d        y w)NzPeer connection closed: z
 for call )r   r-   pc_idrf   r   pop)r   rl   r   s    r    handle_disconnectedzAWhatsAppClient._handle_connect_event.<locals>.handle_disconnectedZ  sO     ./@/F/F.GzRVRYRYQZ[ ''++DGGT:s   AAz4WebRTC connection established successfully for call z&Failed to cleanup connection for call r^   )r   r-   from_rf   r
   r   
initializer   rt   sdp_type
get_answerr?   r|   r   answer_call_to_whatsapprh   rS   r   event_handlerr2   )	r   rl   r8   
sdp_answerpre_accept_resprr   accept_respr   cleanup_errors	   ``       r    rg   z$WhatsAppClient._handle_connect_event  sK    ( 	*4::,k$''KL!?	!6t7H7H!I$//DLL4D4D4<<K`K`/aaa+668<<UCJ66zBJLL9$''CDC(,(:(:(R(RGG\:tzz) # '**9e<LL#=dggYbHY!Z[#&A/AR$STT>twwiHI?$($6$6$N$NGGXz4::%  #y%8LL#9$''"[M!RS#&=k]$KLL:477)DE 0BD##DGG,  --h7;=R ; 8; LLOPTPWPWyYZ%%_ b#  CCDGG9BqcRS"=aS ABBC  ??y1#NO"9! =>>?&  	!,77999  LL@	M?[ 
 LLCDGG9BqcRS	s   2N#AL 
JAL "5J	 JA+J	 5K 9K:A+K %AL N#L J	 		K
3KK

L K 	L3LLL 	N N L:3L64L:9N:	M2%M-(N-M22)NN  N#c                   K   t        j                  d|j                   d|j                          t        j                  d|j                          |j
                  r#t        j                  d|j
                   d       	 |j                  | j                  v r| j                  |j                     }t        j                  d|j                          	 |j                          d{    t        j                  d|j                          | j                  j                  |j                  d       yt        j                  d|j                   d       y7 q# t        $ r/}t        j                  d	|j                   d
|        Y d}~d}~ww xY w# t        $ r/}t        j                  d|j                   d
|        Y d}~yd}~ww xY ww)a  Handle a TERMINATE event by cleaning up resources and logging call completion.

        This method:
        1. Logs call termination details including duration if available
        2. Disconnects the associated WebRTC connection
        3. Removes the call from the ongoing calls map

        Args:
            call: WhatsApp terminate call event

        Returns:
            bool: True if the call was terminated successfully, False otherwise
        zCall terminated from r~   zCall status: zCall duration: z secondsz)Disconnecting WebRTC connection for call Nz5WebRTC connection disconnected successfully for call z0Failed to disconnect WebRTC connection for call r]   zCall z not found in ongoing calls mapTz(Error handling terminate event for call F)r   r-   r   rf   statusdurationr   r2   rS   rh   r   rj   )r   rl   r8   disconnect_errorrr   s        r    ri   z&WhatsAppClient._handle_terminate_eventr  s     	,TZZLDGG9MN}T[[M23==LL?4==/BC	ww$111%)%<%<TWW%E"H	RS,77999LL#XY]Y`Y`Xa!bc ''++DGGT:  twwi/NOP :  LLJ477)SUVfUgh   	LLCDGG9BqcRS	sy   BGAF E +E,&E &F 8G9#F GE 	F(%FF FF 	G#%GGGG)NN)N)r   N)NNN) __name__
__module____qualname____doc__straiohttpClientSessionr   r   r	   r!   r$   r&   r(   r*   r9   r   rA   rE   bytesrX   r   r   r
   r   boolrs   r|   r   rg   r   ri    r"   r    r   r   !   s   
" 26)-,, , &&	,
 d9o., "#, 
,<(htI.G (0hsm 0AC ALs L!:F38nCF	<5 5Z] 5. ]a$(*.S'S &h0E/F	RV/W&XYS 5/	S
 #3-S 
Sj.C .C .(V0C VH] Vp)2G )D )r"   r   )r   r3   rP   rM   typingr   r   r   r   r   r   logurur   )pipecat.transports.smallwebrtc.connectionr	   r
   pipecat.transports.whatsapp.apir   r   r   r   r   r   r   r   r"   r    <module>r      s:       < <   V z zr"   