
    i_                        d Z ddlZddlZddlZddlZddlZddlZddlZ ej                    dk    Z	ddl
mZ ddlmZmZmZ ddlmZ  ej        e          Zdeddfd	Zd
ddeddfdZddlZej                            d e ee                                          j        d                              ddl m!Z!m"Z" ddl#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* defdZ+ G d de$          Z,dS )aG  
WhatsApp platform adapter.

WhatsApp integration is more complex than Telegram/Discord because:
- No official bot API for personal accounts
- Business API requires Meta Business verification
- Most solutions use web-based automation

This adapter supports multiple backends:
1. WhatsApp Business API (requires Meta verification)
2. whatsapp-web.js (via Node.js subprocess) - for personal accounts
3. Baileys (via Node.js subprocess) - alternative for personal accounts

For simplicity, we'll implement a generic interface that can work
with different backends via a bridge pattern.
    NWindows)Path)DictOptionalAny)get_hermes_dirportreturnc                 J   	 t           rt          j        g dddd          }|j                                        D ]}|                                }t          |          dk    rd|d         dk    rX|d         }|                    d|            r8	 t          j        d	d
|d         dgdd           x# t          j        $ r Y w xY wdS t          j        d|  dgdd          }|j	        dk    rt          j        dd|  dgdd           dS dS # t          $ r Y dS w xY w)z1Kill any process listening on the given TCP port.)netstatz-anoz-pTCPT   capture_outputtexttimeout   	LISTENING   :taskkill/PID   /F)r   r   fuserz/tcpr   z-kN)_IS_WINDOWS
subprocessrunstdout
splitlinessplitlenendswithSubprocessError
returncode	Exception)r	   resultlineparts
local_addrs        ?/home/ubuntu/.hermes/hermes-agent/gateway/platforms/whatsapp.py_kill_port_processr,   #   s    	^000#$  F 0022 ! !

u::??uQx;'>'>!&qJ!**:t::66 !!&N!+VU1Xt D/3Q      *9 ! ! ! D!! !  ^T---(#Q  F  A%%dtMMM2#'      &%
    s=   BD !B43D 4CD CD AD 
D"!D"Fforcer.   c                .   t           rddt          | j                  dg}|r|                    d           	 t	          j        |ddd          }n<# t          $ r/ |r|                                  n|                                  Y dS w xY w|j	        d	k    r;|j
        p|j        pd
                                }t          |p	d| j                   dS d	dl}|s|j        n|j        }t#          j        t#          j        | j                  |           dS )zITerminate the bridge process using process-tree semantics where possible.r   r   z/Tr   T
   r   Nr    ztaskkill failed for PID )r   strpidappendr   r   FileNotFoundErrorkill	terminater%   stderrr   stripOSErrorsignalSIGTERMSIGKILLoskillpggetpgid)procr.   cmdr'   detailsr;   sigs          r+   _terminate_bridge_processrE   F   s?    63tx==$7 	JJt	^#	  FF ! 	 	 	 !		   FF	 !!};;BBDDG'J%J%J%JKKKMMM %
9&..6>CIbj""C(((((s   A 5B
	B
   )PlatformPlatformConfig)BasePlatformAdapterMessageEventMessageType
SendResultSUPPORTED_DOCUMENT_TYPEScache_image_from_urlcache_audio_from_urlc                  p    	 t          j        ddgddd          } | j        dk    S # t          $ r Y dS w xY w)z}
    Check if WhatsApp dependencies are available.
    
    WhatsApp requires a Node.js bridge for most implementations.
    nodez	--versionTr   r   r   F)r   r   r%   r&   )r'   s    r+   check_whatsapp_requirementsrR   s   s`    	[!	
 
 
  A%%   uus   $' 
55c                       e Zd ZdZdZ ee                                          j        d         dz  dz  Z	de
f fdZdefd	Zdee         fd
Zedee         fd            ZdedefdZdedefdZd Zedee         defd            Zdeeef         dee         fdZdeeef         defdZdeeef         defdZdeeef         defdZdedeeef         defdZdeeef         defdZdefdZ d<dZ!dee         fdZ"d<dZ#d edefd!Z$	 	 d=ded ed"ee         d#eeeef                  de%f
d$Z&d%d&ded'ed ed(ede%f
d)Z'	 	 d=ded*ed+ed,ee         d-ee         de%fd.Z(	 	 d=ded/ed,ee         d"ee         de%f
 fd0Z)	 	 d=ded1ed,ee         d"ee         de%f
d2Z*	 	 d=ded3ed,ee         d"ee         de%f
d4Z+	 	 d=ded5ed,ee         d"ee         de%f
d6Z,	 	 	 d>ded*ed,ee         d-ee         d"ee         de%fd7Z-d?deddfd8Z.dedeeef         fd9Z/d<d:Z0deeef         dee1         fd;Z2 xZ3S )@WhatsAppAdapteru  
    WhatsApp adapter.
    
    This implementation uses a simple HTTP bridge pattern where:
    1. A Node.js process runs the WhatsApp Web client
    2. Messages are forwarded via HTTP/IPC to this Python adapter
    3. Responses are sent back through the bridge
    
    The actual Node.js bridge implementation can vary:
    - whatsapp-web.js based
    - Baileys based
    - Business API based
    
    Configuration:
    - bridge_script: Path to the Node.js bridge script
    - bridge_port: Port for HTTP communication (default: 3000)
    - session_path: Path to store WhatsApp session data
    - dm_policy: "open" | "allowlist" | "disabled" — how DMs are handled (default: "open")
    - allow_from: List of sender IDs allowed in DMs (when dm_policy="allowlist")
    - group_policy: "open" | "allowlist" | "disabled" — which groups are processed (default: "open")
    - group_allow_from: List of group JIDs allowed (when group_policy="allowlist")
    i   rF   scriptszwhatsapp-bridgeconfigc           	      8   t                                          |t          j                   d | _        |j                            dd          | _        |j                            dt          | j	        dz                      | _
        t          |j                            dt          dd                              | _        |j                            d          | _        t          |j                            d	          pt          j        d
d                                                                                    | _        |                     |j                            d          p|j                            d                    | _        t          |j                            d          pt          j        dd                                                                                    | _        |                     |j                            d          p|j                            d                    | _        |                                 | _        t5          j                    | _        d | _        d | _        d | _        d | _         d| _!        d S )Nbridge_porti  bridge_scriptz	bridge.jssession_pathzplatforms/whatsapp/sessionzwhatsapp/sessionreply_prefix	dm_policyWHATSAPP_DM_POLICYopen
allow_from	allowFromgroup_policyWHATSAPP_GROUP_POLICYgroup_allow_fromgroupAllowFromF)"super__init__rG   WHATSAPP_bridge_processextraget_bridge_portr2   _DEFAULT_BRIDGE_DIR_bridge_scriptr   r   _session_path_reply_prefixr>   getenvr9   lower
_dm_policy_coerce_allow_list_allow_from_group_policy_group_allow_from_compile_mention_patterns_mention_patternsasyncioQueue_message_queue_bridge_log_fh_bridge_log
_poll_task_http_session_shutting_down)selfrV   	__class__s     r+   rf   zWhatsAppAdapter.__init__   sD   !2333;?!'!1!1-!F!F-3\-=-=(;677.
 .
 $((8(879KLL)
 )
 $ $ -3L,<,<^,L,Lfl..{;;fryI]_e?f?fggmmoouuww226<3C3CL3Q3Q3rU[UaUeUefqUrUrss !1!1.!A!A!oRYOfhnEoEoppvvxx~~  A  A!%!8!89I9IJ\9]9]  :Dagamaqaq  sC  bD  bD  "E  "E!%!?!?!A!A-4]__"+/26@D %*    r
   c                 
   | j         j                            d          }|:t          |t                    r|                                dv S t          |          S t          j        dd                                          dv S )Nrequire_mention)true1yesonWHATSAPP_REQUIRE_MENTIONfalse)	rV   ri   rj   
isinstancer2   rq   boolr>   rp   )r   
configureds     r+   _whatsapp_require_mentionz)WhatsAppAdapter._whatsapp_require_mention   s    [&**+<==
!*c** H!''))-GGG
###y3W==CCEEIcccr   c                    | j         j                            d          }|t          j        dd          }t          |t                    rd |D             S d t          |                              d          D             S )Nfree_response_chatsWHATSAPP_FREE_RESPONSE_CHATSr1   c                     h | ]D}t          |                                          #t          |                                          ES  r2   r9   .0parts     r+   	<setcomp>z@WhatsAppAdapter._whatsapp_free_response_chats.<locals>.<setcomp>   =    KKK$T9J9JKCIIOO%%KKKr   c                 ^    h | ]*}|                                 |                                 +S r   r9   r   s     r+   r   z@WhatsAppAdapter._whatsapp_free_response_chats.<locals>.<setcomp>   -    MMM

M

MMMr   ,)	rV   ri   rj   r>   rp   r   listr2   r!   )r   raws     r+   _whatsapp_free_response_chatsz-WhatsAppAdapter._whatsapp_free_response_chats   s    k##$9::;):B??Cc4   	LKK#KKKKMMS)<)<MMMMr   c                     | t                      S t          | t                    rd | D             S d t          |                               d          D             S )z;Parse allow_from / group_allow_from from config or env var.Nc                     h | ]D}t          |                                          #t          |                                          ES r   r   r   s     r+   r   z5WhatsAppAdapter._coerce_allow_list.<locals>.<setcomp>   r   r   c                 ^    h | ]*}|                                 |                                 +S r   r   r   s     r+   r   z5WhatsAppAdapter._coerce_allow_list.<locals>.<setcomp>   r   r   r   )setr   r   r2   r!   )r   s    r+   rs   z"WhatsAppAdapter._coerce_allow_list   s_     ;55Lc4   	LKK#KKKKMMS)<)<MMMMr   	sender_idc                 H    | j         dk    rdS | j         dk    r	|| j        v S dS )z=Check whether a DM from the given sender should be processed.disabledF	allowlistT)rr   rt   )r   r   s     r+   _is_dm_allowedzWhatsAppAdapter._is_dm_allowed   s4    ?j((5?k)) 000tr   chat_idc                 H    | j         dk    rdS | j         dk    r	|| j        v S dS )z/Check whether a group chat should be processed.r   Fr   T)ru   rv   )r   r   s     r+   _is_group_allowedz!WhatsAppAdapter._is_group_allowed   s6    ++5,,d444tr   c                    | j         j                            d          }|t          j        dd                                          }|re	 t          j        |          }nO# t          $ rB d |	                                D             }|sd |
                    d          D             }Y nw xY w|g S t          |t                    r|g}t          |t                    s5t                              d| j        t#          |          j                   g S g }|D ]}t          |t                    r|                                s,	 |                    t)          j        |t(          j                             `# t(          j        $ r,}t                              d| j        ||           Y d }~d }~ww xY w|r.t                              d	| j        t3          |                     |S )
Nmention_patternsWHATSAPP_MENTION_PATTERNSr1   c                 ^    g | ]*}|                                 |                                 +S r   r   r   s     r+   
<listcomp>z=WhatsAppAdapter._compile_mention_patterns.<locals>.<listcomp>   s-    ZZZTZZ\\Z

ZZZr   c                 ^    g | ]*}|                                 |                                 +S r   r   r   s     r+   r   z=WhatsAppAdapter._compile_mention_patterns.<locals>.<listcomp>   s-    #\#\#\Ttzz||#\DJJLL#\#\#\r   r   z?[%s] whatsapp mention_patterns must be a list or string; got %sz,[%s] Invalid WhatsApp mention pattern %r: %sz*[%s] Loaded %d WhatsApp mention pattern(s))rV   ri   rj   r>   rp   r9   jsonloadsr&   r    r!   r   r2   r   loggerwarningnametype__name__r4   recompile
IGNORECASEerrorinfor"   )r   patternsr   compiledpatternexcs         r+   rw   z)WhatsAppAdapter._compile_mention_patterns   s   ;$(();<<)7<<BBDDC ]]#z#HH  ] ] ]ZZ9I9IZZZH# ]#\#\SYYs^^#\#\#\] Ih$$ 	" zH(D)) 	NN\^b^gimnviwiw  jA  B  B  BI 	h 	hGgs++ 7==?? h
7BM B BCCCC8 h h hMtyZacfggggggggh 	`KKDdiQTU]Q^Q^___s+   A! !A	B-,B-2E::F5	"F00F5valuec                     | sdS t          |                                           }d|v rd|v r|                    ddd          }|S )Nr1   r   @r   )r2   r9   replace)r   
normalizeds     r+   _normalize_whatsapp_idz&WhatsAppAdapter._normalize_whatsapp_id  sX     	2ZZ%%''
*
!2!2#++Ca88Jr   datac                     t                      }|                    d          pg D ].}|                     |          }|r|                    |           /|S )NbotIds)r   rj   r   add)r   r   bot_ids	candidater   s        r+   _bot_ids_from_messagez%WhatsAppAdapter._bot_ids_from_message  s^    %%(++1r 	( 	(I44Y??J (J'''r   c                     |                      |                    d                    }|sdS ||                     |          v S )NquotedParticipantF)r   rj   r   )r   r   quoted_participants      r+   _message_is_reply_to_botz(WhatsAppAdapter._message_is_reply_to_bot  sI    !88BU9V9VWW! 	5!T%?%?%E%EEEr   c                                           |          }|sdS  fd|                    d          pg D             }||z  rdS t          |                    d          pd          }|                                }|D ]@}|                    dd          d	                                         }|rd| |v s||v r dS AdS )
NFc                 B    h | ]}                     |          xS r   )r   )r   r   nidr   s     r+   r   z8WhatsAppAdapter._message_mentions_bot.<locals>.<setcomp>)  sB     
 
 
229===

 
 
r   mentionedIdsTbodyr1   r   r   r   )r   rj   r2   rq   r!   )	r   r   r   mentioned_idsr   
lower_bodybot_idbare_idr   s	   `       @r+   _message_mentions_botz%WhatsAppAdapter._message_mentions_bot%  s   ,,T22 	5
 
 
 
 
"hh~66<"
 
 

 7" 	4488F##)r**ZZ\\
 	 	Fll3**1-3355G MMMZ777j;P;Pttur   c                     | j         sdS t          |                    d          pd          t          fd| j         D                       S )NFr   r1   c              3   B   K   | ]}|                               V  d S N)search)r   r   r   s     r+   	<genexpr>zDWhatsAppAdapter._message_matches_mention_patterns.<locals>.<genexpr>=  s/      NNG7>>$''NNNNNNr   )rx   r2   rj   any)r   r   r   s     @r+   !_message_matches_mention_patternsz1WhatsAppAdapter._message_matches_mention_patterns9  sW    % 	5488F##)r**NNNNt7MNNNNNNr   r   c                    |s|S |                      |          }|}|D ]L}|                    dd          d         }|r,t          j        dt          j        |           dd|          }M|                                p|S )Nr   r   r   z\b[,:\-]*\s*r1   )r   r!   r   subescaper9   )r   r   r   r   cleanedr   r   s          r+   _clean_bot_mention_textz'WhatsAppAdapter._clean_bot_mention_text?  s     	K,,T22 	U 	UFll3**1-G U&!Fbi&8&8!F!F!FGTT}}&$&r   c                    |                     dd          }|r<t          |                     d          pd          }|                     |          sdS nRt          |                     d          p|                     d          pd          }|                     |          sdS dS t          |                     d          pd          }||                                 v rdS |                                 sdS t          |                     d          pd                                          }|                    d	          rdS |                     |          rdS | 	                    |          rdS | 
                    |          S )
NisGroupFchatIdr1   senderIdfromTr   /)rj   r2   r   r   r   r   r9   
startswithr   r   r   )r   r   is_groupr   r   r   s         r+   _should_process_messagez'WhatsAppAdapter._should_process_messageJ  s   88Iu-- 		$((8,,233G))'22 u DHHZ00JDHHV4D4DJKKI&&y11 u4dhhx((.B//d88::::4--// 	4488F##)r**0022??3 	4((.. 	4%%d++ 	455d;;;r   c           
      D  K   t                      s"t                              d| j                   dS t	          | j                  }|                                s#t                              d| j        |           dS t                              d| j        |           d}	 |                     dt          | j
                  d          sdS d}n8# t          $ r+}t                              d| j        |           Y d	}~nd	}~ww xY w	 |j        }|d
z                                  st          d| j         d           	 t          j        g dt          |          ddd          }|j        dk    rUt          d| j         d|j                    	 | j        s,|r|                                  |                                  dS dS t          d| j         d           ne# t          $ rX}t          d| j         d|            Y d	}~| j        s,|r|                                  |                                  dS dS d	}~ww xY w| j
                            dd           dd	l}	 |                                4 d	{V }|                    d| j         d|                    d                    4 d	{V 	 }|j        dk    r#|                                 d	{V }	|	                    dd          }
|
dk    rt          d| j         d|
 d           |                                  d	| _        |                                | _        tA          j!        | "                                          | _#        	 d	d	d	          d	{V  d	d	d	          d	{V  | j        s,|r|                                  |                                  dS dS t          d| j         d |
 d!           d	d	d	          d	{V  n# 1 d	{V swxY w Y   d	d	d	          d	{V  n# 1 d	{V swxY w Y   n# t          $ r Y nw xY wtI          | j                   tA          j%        d"           d	{V  tM          j'        d#d$          }| j
        j        d%z  | _(        tS          | j(        d&          }|| _*        tL          j+        ,                                }| j-        
| j-        |d'<   t          j.        d(t          |          d)t          | j                  d*t          | j
                  d+|g||t^          rd	ntL          j0        |,          | _        dd	l}d}i }	tc          d-          D ]}tA          j%        d"           d	{V  | j        2                                t          d| j         d.| j        j         d           t          d| j         d/| j(                    |                                   | j        s,|r|                                  |                                  dS dS 	 |                                4 d	{V }|                    d| j         d|                    d                    4 d	{V 	 }|j        dk    rrd}|                                 d	{V }	|	                    d          dk    r=t          d| j         d0           	 d	d	d	          d	{V  d	d	d	          d	{V   nYd	d	d	          d	{V  n# 1 d	{V swxY w Y   d	d	d	          d	{V  n# 1 d	{V swxY w Y   # t          $ r Y w xY w|st          d| j         d1           t          d| j         d/| j(                    |                                  	 | j        s,|r|                                  |                                  dS dS |	                    d          dk    rYt          d| j         d2           tc          d-          D ]}tA          j%        d"           d	{V  | j        2                                t          d| j         d3           t          d| j         d/| j(                    |                                   | j        s,|r|                                  |                                  dS dS 	 |                                4 d	{V }|                    d| j         d|                    d                    4 d	{V 	 }|j        dk    rp|                                 d	{V }	|	                    d          dk    r=t          d| j         d0           	 d	d	d	          d	{V  d	d	d	          d	{V   nd	d	d	          d	{V  n# 1 d	{V swxY w Y   d	d	d	          d	{V  n# 1 d	{V swxY w Y   # t          $ r Y w xY wt          d| j         d4           t          d| j         d5| j(                    t          d| j         d6           |                                | _        tA          j!        | "                                          | _#        |                                  t          d| j         d7| j                    	 | j        s,|r|                                  |                                  dS dS # t          $ ra}t          3                    d8| j        |d9           Y d	}~| j        s,|r|                                  |                                  dS dS d	}~ww xY w# | j        s+|r|                                  |                                  w w xY w):z
        Start the WhatsApp bridge.
        
        This launches the Node.js bridge process and waits for it to be ready.
        z2[%s] Node.js not found. WhatsApp requires Node.js.Fz [%s] Bridge script not found: %sz[%s] Bridge found at %szwhatsapp-sessionzWhatsApp sessionTz3[%s] Could not acquire session lock (non-fatal): %sNnode_modules[z,] Installing WhatsApp bridge dependencies...)npminstallz--silent<   )cwdr   r   r   r   z] npm install failed: z] Dependencies installedz"] Failed to install dependencies: )parentsexist_okhttp://127.0.0.1:z/healthrF   totalr      statusunknown	connectedz!] Using existing bridge (status: )z*] Bridge found but not connected (status: z), restartingr   WHATSAPP_MODEz	self-chatz
bridge.logaWHATSAPP_REPLY_PREFIXrQ   z--portz	--sessionz--mode)r   r8   
preexec_fnenv   z!] Bridge process died (exit code z] Check log: z"] Bridge ready (status: connected)z)] Bridge HTTP server did not start in 15sz7] Bridge HTTP ready, waiting for WhatsApp connection...z'] Bridge process died during connectionu&   ] ⚠ WhatsApp not connected after 30sz]   Bridge log: z0]   If session expired, re-pair: hermes whatsappz] Bridge started on port z[%s] Failed to start bridge: %s)exc_info)4rR   r   r   r   r   rm   existsr   _acquire_platform_lockr2   rn   r&   parentprintr   r   r%   r8   _running_release_platform_lock_close_bridge_logmkdiraiohttpClientSessionrj   rk   ClientTimeoutr  r   _mark_connectedrh   r   ry   create_task_poll_messagesr~   r,   sleepr>   rp   r}   r^   r|   environcopyro   Popenr   setsidrangepollr   )r   bridge_pathlock_acquirede
bridge_dirinstall_resultr  sessionrespr   bridge_statuswhatsapp_modebridge_log_fh
bridge_env
http_readyattempts                   r+   connectzWhatsAppAdapter.connecte  s      +,, 	NNOQUQZ[[[54.//!!## 	NN=ty+VVV5-ty+FFF 	`../A3tGYCZCZ\noo u MM 	` 	` 	`NNPRVR[]^________	`b	)$+J/7799 !Q$)QQQRRR!%/^666
OO'+! "& & &N &0A55Z$)ZZ>CXZZ[[[$` = )  2//111&&((((() )_ AdiAAABBBB  ! ! !NdiNN1NNOOO 555X = )  2//111&&((((() )]!
 $$TD$AAA NNN"0022 } } } } } } }g&{{FD,=FFF ' 5 5A 5 > >  +     } } } } } } } } ;#--)-#4#4#4#4#4#4D,0HHXy,I,IM,;; %&f$)&f&fVc&f&f&f g g g $ 4 4 6 6 67; 45<5J5J5L5L 2292EdFYFYF[F[2\2\'+} } } } } } } } } } } }} } } } } } } } } } } }H = )  2//111&&((((() )i !&&{$)&{&{_l&{&{&{ | | |} } } } } } } } } } } } } } } } } } } } } } } } } } }} } } } } } } } } } } } } } } } } } } } } } } } } } }"     t0111-"""""""""
 Io{CCM#18<GD !1377M"/D
 **J!-6:6H
23#-#3$$c$"344T%7!8!8m %$#.=44BI$ $ $D " NNNJD 99  mA&&&&&&&&&',,..:ldill$J^JilllmmmHdiHHd6FHHIII**,,, F = )  2//111&&((((() )E&4466 
* 
* 
* 
* 
* 
* 
*'#*;;J0AJJJ$+$9$9$9$B$B $/ $ $ 	* 	* 	* 	* 	* 	* 	* 	* "#{c11-1
-1YY[['8'8'8'8'8'8#'88H#5#5#D#D$)*[di*[*[*[$\$\$\$)	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	*
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
*	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	*
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* 
* !   H  N$)NNNOOOD$)DD$2BDDEEE&&(((^ = )  2//111&&((((() )W xx!![00\$)\\\]]]$Ryy [ [G!-*********+0022>T$)TTTUUUL$)LL$:JLLMMM..000$F = )  2//111&&((((() )E!#*#8#8#:#: 	. 	. 	. 	. 	. 	. 	.g'.{{ ND4E N N N(/(=(=A(=(F(F (3 ( ( . . . . . . . . "&#';##5#515+<+<+<+<+<+<D'+xx'9'9['H'H(-._$)._._._(`(`(`(-. . . . . . . . . . . .	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	.. . . . . . . . . . . . . . . . . . . . . . . . . . .	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. % ! ! ! !
 OdiOOOPPPKdiKK9IKKLLLYdiYYYZZZ ")!6!6!8!8D &1$2E2E2G2GHHDO  """MdiMM$:KMMNNN = )  2//111&&((((() )	  	 	 	LL:DIqSWLXXX555= )  2//111&&((((() )		 = )  2//111&&(((()s   ")C C 
D!D  D	7k= AG" 	G" !k= "
I,H?k= ?I#k= (P5 ;P#>B>P <P#P5 P .P# 
P
	
P#P
	P#P5 #
P--P5 0P-1P5 4k= 5
Q?k= QF5k= -]	;\6A\\6.]	?k= \6
\\6 \!\6$]	6
] 	 ]	] 	]	k= 	
]k= ]Ak= Ck= g00;g,Af:gg0&k= (g:
ggggg0
g'	'g0*g'	+g0.k= 0
g>:k= =g>>C	k= =
m(#m#*m+ #m((m+ +4nNc                 |    | j         r4	 | j                                          n# t          $ r Y nw xY wd| _         dS dS )z)Close the bridge log file handle if open.N)r|   closer&   )r   s    r+   r  z!WhatsAppAdapter._close_bridge_log#  sb     	'#))++++   "&D	' 	's   # 
00c                   K   | j         dS | j                                         }|dS t          | dd          r'|dv r#t                              d| j        |           dS d| d}| j        sgt                              d| j        |           |                     d	|d
           | 	                                 | 
                                 d{V  | j        p|S )z@Return a fatal error message if the managed bridge child exited.Nr   F)r   iz-[%s] Bridge exited during shutdown (code %d).z2WhatsApp bridge process exited unexpectedly (code z).z[%s] %swhatsapp_bridge_exitedT)	retryable)rh   r!  getattrr   r   r   has_fatal_errorr   _set_fatal_errorr  _notify_fatal_errorfatal_error_message)r   r%   messages      r+   _check_managed_bridge_exitz*WhatsAppAdapter._check_managed_bridge_exit,  s     '4)..00
4 4)511 	jL6P6PKK?	  
 4UzUUU# 	-LLDIw777!!":Gt!TTT""$$$**,,,,,,,,,'272r   c                 @  K   d| _         | j        r	 	 t          | j        d           n0# t          t          f$ r | j                                         Y nw xY wt          j        d           d{V  | j                                        H	 t          | j        d           n0# t          t          f$ r | j        	                                 Y nw xY wnI# t          $ r$}t          d| j         d|            Y d}~n d}~ww xY wt          d| j         d           | j        r]| j                                        sD| j                                         	 | j         d{V  n# t          j        t          f$ r Y nw xY wd| _        | j        r+| j        j        s| j                                         d{V  d| _        |                                  |                                  d| _        |                                  t          d| j         d	           dS )
z=Stop the WhatsApp bridge and clean up any orphaned processes.TFr-   r   Nr   z] Error stopping bridge: z.] Disconnecting (external bridge left running)z] Disconnected)r   rh   rE   ProcessLookupErrorPermissionErrorr7   ry   r  r!  r6   r&   r  r   r~   donecancelCancelledErrorr   closedr1  r  _mark_disconnectedr  )r   r$  s     r+   
disconnectzWhatsAppAdapter.disconnectL  s     
 # 	QC5-d.B%PPPPP*O< 5 5 5(22444445mA&&&&&&&&&',,..641$2FdSSSSS.@ 4 4 4,11333334 C C CA$)AAaAABBBBBBBBC OdiOOOPPP ? 	4?#7#7#9#9 	O""$$$o%%%%%%%%*I6     	-d&8&? 	-$**,,,,,,,,,!##%%%!!!#   +$)+++,,,,,sh   * C *AC A6C B% $C %*CC CC 
D C??DE' 'F ?F contentc                   	
 |s|S d	g dt           j        dt          f	fd}t          j        d||          }dg 
dt           j        dt          f
fd}t          j        d||          }t          j        d	d
|          }t          j        dd
|          }t          j        dd|          }t          j        dd
|t           j                  }t          j        dd|          }t                    D ] \  }}|                    	 | d|          }!t          
          D ] \  }}|                     | d|          }!|S )a  Convert standard markdown to WhatsApp-compatible formatting.

        WhatsApp supports: *bold*, _italic_, ~strikethrough~, ```code```,
        and monospaced `inline`. Standard markdown uses different syntax
        for bold/italic/strikethrough, so we convert here.

        Code blocks (``` fenced) and inline code (`) are protected from
        conversion via placeholder substitution.
        z FENCEmr
   c                                          |                     d                      t                    dz
   dS Nr   r    r4   groupr"   )rH  	_FENCE_PHfencess    r+   _save_fencez3WhatsAppAdapter.format_message.<locals>._save_fence  s=    MM!''!**%%%6Vq6666r   z```[\s\S]*?```z CODEc                                          |                     d                      t                    dz
   dS rJ  rL  )rH  _CODE_PHcodess    r+   
_save_codez2WhatsAppAdapter.format_message.<locals>._save_code  s=    LL$$$4E

Q4444r   z	`[^`\n]+`z\*\*(.+?)\*\*z*\1*z	__(.+?)__z	~~(.+?)~~z~\1~z^#{1,6}\s+(.+)$)flagsz\[([^\]]+)\]\(([^)]+)\)z\1 (\2)rK  )r   Matchr2   r   	MULTILINE	enumerater   )r   rF  rP  r'   rT  ifencecoderR  rN  rS  rO  s           @@@@r+   format_messagezWhatsAppAdapter.format_messagey  s     	N  		728 	7 	7 	7 	7 	7 	7 	7 	7 );@@ 	5"( 	5s 	5 	5 	5 	5 	5 	5 	5 j&99 ('6::gv66gv66 *GV2<PPP 2JGG "&)) 	B 	BHAu^^y$9!$9$9$95AAFF '' 	@ 	@GAt^^x$8$8$8$8$??FFr   reply_tometadatac                 *  K   | j         r| j        st          dd          S |                                  d{V }|rt          d|          S |r|                                st          dd          S 	 ddl}|                     |          }|                     || j                  }d}	|D ]}
||
d}|r|	||d	<   | j        	                    d
| j
         d||                    d                    4 d{V 	 }|j        dk    r0|                                 d{V }|                    d          }	n?|                                 d{V }t          d|          cddd          d{V  c S 	 ddd          d{V  n# 1 d{V swxY w Y   t!          |          dk    rt#          j        d           d{V   t          d|	          S # t&          $ r(}t          dt)          |                    cY d}~S d}~ww xY w)zSend a message via the WhatsApp bridge.

        Formats markdown for WhatsApp, splits long messages into chunks
        that preserve code block boundaries, and sends each chunk sequentially.
        FNot connectedsuccessr   NTrb  
message_idr   )r   r;  replyTor   z/send   r   r   r   r  	messageIdr   g333333?)r  r   rL   r<  r9   r  r\  truncate_messageMAX_MESSAGE_LENGTHpostrk   r  r  r   rj   r   r"   ry   r  r&   r2   )r   r   rF  r]  r^  bridge_exitr  	formattedchunkslast_message_idchunkpayloadr(  r   r   r$  s                   r+   sendzWhatsAppAdapter.send  s      } 	DD$6 	De?CCCC ;;======== 	@e;???? 	=gmmoo 	=dt<<<<&	;NNN ++G44I**9d6MNNF"O - -%$+ +  2 7)1GI&-22@(9@@@ #111;; 3   
F 
F 
F 
F 
F 
F 
F 
F {c))%)YY[[000000*.((;*?*?&*iikk 1 1 1 1 1 1)%uEEE
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F (
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F v;;??!-,,,,,,,,,*     	; 	; 	;e3q66:::::::::	;sK   7B	G  A&F'G  =G  
F	G  F	AG   
H*HHHF)finalizerd  rs  c                  K   | j         r| j        st          dd          S |                                  d{V }|rt          d|          S 	 ddl}| j                            d| j         d|||d|                    d	
                    4 d{V 	 }|j        dk    r#t          d|          cddd          d{V  S |	                                 d{V }t          d|          cddd          d{V  S # 1 d{V swxY w Y   dS # t          $ r(}	t          dt          |	                    cY d}	~	S d}	~	ww xY w)z7Edit a previously sent message via the WhatsApp bridge.Fr`  ra  Nr   r   z/edit)r   rh  r;  r  r   rg  r  Trc  )r  r   rL   r<  r  rk  rk   r  r  r   r&   r2   )
r   r   rd  rF  rs  rl  r  r(  r   r$  s
             r+   edit_messagezWhatsAppAdapter.edit_message  s      } 	DD$6 	De?CCCC ;;======== 	@e;????	;NNN)..<D$5<<<%!+& 
  --B-77 /   B B B B B B B B ;#%%%dzJJJB B B B B B B B B B B B B B #'))++------E%e5AAAB B B B B B B B B B B B B B B B B B B B B B B B B B B B B B  	; 	; 	;e3q66:::::::::	;sO   AD D5D *D2D 
DD DD 
E
"E?E
E
	file_path
media_typecaption	file_namec                 |  K   | j         r| j        st          dd          S |                                  d{V }|rt          d|          S 	 ddl}t
          j                            |          st          dd|           S |||d}|r||d<   |r||d	<   | j                            d
| j	         d||
                    d                    4 d{V 	 }	|	j        dk    rQ|	                                 d{V }
t          d|
                    d          |
          cddd          d{V  S |	                                 d{V }t          d|          cddd          d{V  S # 1 d{V swxY w Y   dS # t          $ r(}t          dt!          |                    cY d}~S d}~ww xY w)z4Send any media file via bridge /send-media endpoint.Fr`  ra  Nr   zFile not found: )r   filePath	mediaTyperx  fileNamer   z/send-mediax   r   rg  r  Trh  )rb  rd  raw_response)r  r   rL   r<  r  r>   pathr  rk  rk   r  r  r   rj   r   r&   r2   )r   r   rv  rw  rx  ry  rl  r  rq  r(  r   r   r$  s                r+   _send_media_to_bridgez%WhatsAppAdapter._send_media_to_bridge  s>      } 	DD$6 	De?CCCC ;;======== 	@e;????!	;NNN7>>),, W!%7U)7U7UVVVV "%'' 'G
  -%,	" 0&/
#)..BD$5BBB--C-88 /   B B B B B B B B ;#%%!%,,,,,,D% $#'88K#8#8%)  B B B B B B B B B B B B B B #'))++------E%e5AAAB B B B B B B B B B B B B B B B B B B B B B B B B B B B B B   	; 	; 	;e3q66:::::::::	;sV   6F	 AF	 A
E6&F	 9*E6#F	 6
F  F	 F F	 	
F;F60F;6F;	image_urlc                    K   	 t          |           d{V }|                     ||d|           d{V S # t          $ r- t                                          ||||           d{V cY S w xY w)z6Download image URL to cache, send natively via bridge.Nimage)rN   r  r&   re   
send_image)r   r   r  rx  r]  
local_pathr   s         r+   r  zWhatsAppAdapter.send_imageA  s      	S3I>>>>>>>>J33GZRYZZZZZZZZZ 	S 	S 	S++GYRRRRRRRRRRR	Ss   28 4A/.A/
image_pathc                 B   K   |                      ||d|           d{V S )z,Send a local image file natively via bridge.r  Nr  )r   r   r  rx  r]  kwargss         r+   send_image_filezWhatsAppAdapter.send_image_fileO  4       //WgVVVVVVVVVr   
video_pathc                 B   K   |                      ||d|           d{V S )u>   Send a video natively via bridge — plays inline in WhatsApp.videoNr  )r   r   r  rx  r]  r  s         r+   
send_videozWhatsAppAdapter.send_videoZ  r  r   
audio_pathc                 B   K   |                      ||d|           d{V S )z:Send an audio file as a WhatsApp voice message via bridge.audioNr  )r   r   r  rx  r]  r  s         r+   
send_voicezWhatsAppAdapter.send_voicee  r  r   c           	         K   |                      ||d||pt          j                            |                     d{V S )z=Send a document/file as a downloadable attachment via bridge.documentN)r  r>   r  basename)r   r   rv  rx  ry  r]  r  s          r+   send_documentzWhatsAppAdapter.send_documentp  s_       //Y
G4)))44
 
 
 
 
 
 
 
 	
r   c                 ^  K   | j         r| j        sdS |                                  d{V rdS 	 ddl}| j                            d| j         dd|i|                    d                    4 d{V  	 ddd          d{V  dS # 1 d{V swxY w Y   dS # t          $ r Y dS w xY w)	z!Send typing indicator via bridge.Nr   r   z/typingr   r   r   rg  )r  r   r<  r  rk  rk   r  r&   )r   r   r^  r  s       r+   send_typingzWhatsAppAdapter.send_typing  s     } 	D$6 	F0022222222 	F	NNN
 )..>D$5>>>(--A-66 /          
                                	 	 	DD	s6   AB 6B8B 
BB BB 
B,+B,c                   K   | j         r| j        sdddS |                                  d{V r|ddS 	 ddl}| j                            d| j         d| |                    d	          
          4 d{V 	 }|j        dk    rq|                                 d{V }|                    d|          |                    d          rdnd|                    dg           dcddd          d{V  S 	 ddd          d{V  n# 1 d{V swxY w Y   n3# t          $ r&}t                              d||           Y d}~nd}~ww xY w|ddS )z&Get information about a WhatsApp chat.Unknowndm)r   r   Nr   r   z/chat/r0   r   r   r  r   r   rM  participants)r   r   r  z+Could not get WhatsApp chat info for %s: %s)r  r   r<  r  rj   rk   r  r  r   r&   r   debug)r   r   r  r(  r   r$  s         r+   get_chat_infozWhatsAppAdapter.get_chat_info  s     } 	5D$6 	5%t4440022222222 	3#T222	TNNN)--FD$5FFWFF--B-77 .   
 
 
 
 
 
 
 
 ;#%%!%,,,,,,D $ 9 9+/88I+>+> HD(,(D(D 
 
 
 
 
 
 
 
 
 
 
 
 
 
 &	
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  	T 	T 	TLLFQRSSSSSSSS	T  ...sC   AD <A*D&D :D 
DD DD 
E(E		Ec                   K   ddl }| j        r| j        sdS |                                  d{V }|rt	          d| j         d|            dS 	 | j                            d| j         d|                    d          	          4 d{V 	 }|j	        d
k    rW|
                                 d{V }|D ]:}|                     |           d{V }|r|                     |           d{V  ;ddd          d{V  n# 1 d{V swxY w Y   n# t          j        $ r Y dS t          $ rz}|                                  d{V }|r t	          d| j         d|            Y d}~dS t	          d| j         d|            t          j        d           d{V  Y d}~nd}~ww xY wt          j        d           d{V  | j        dS dS )z&Poll the bridge for incoming messages.r   Nr   z] r   z	/messagesrf  r   r   r  z] Poll error: r   r   )r  r  r   r<  r  r   rj   rk   r  r  r   _build_message_eventhandle_messagery   rB  r&   r  )r   r  rl  r(  messagesmsg_dataeventr$  s           r+   r  zWhatsAppAdapter._poll_messages  sX     m 	#%  $ ? ? A AAAAAAAK 4$)44{44555'-11D(9DDD#111;; 2   	A 	A 	A 	A 	A 	A 	A 	A {c)))-#4#4#4#4#4#4(0 A AH*.*C*CH*M*M$M$M$M$M$M$ME$ A&*&9&9%&@&@ @ @ @ @ @ @ @	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A )    ' ' '$($C$C$E$EEEEEEE 8di88;88999EEEEE6$)66166777mA&&&&&&&&&&&&&&' -"""""""""9 m 	# 	# 	# 	# 	#sI   ?D A#D4D 
DD DD F-*	F-36F(/4F((F-c                   K   	 |                      |          sdS t          j        }|                    d          rY|                    dd          }d|v rt          j        }n2d|v rt          j        }n!d|v sd|v rt          j        }nt          j        }|                    d	d
          }|rdnd}|                     |                    dd          |                    d          ||                    d          |                    d                    }|                    dg           }g }g }	|D ]}
|t          j        k    r|
	                    d          r	 t          |
d           d{V }|                    |           |	                    d           t          d| j         d| d           # t          $ rP}t          d| j         d| d           |                    |
           |	                    d           Y d}~d}~ww xY w|t          j        k    rgt          j                            |
          rH|                    |
           |	                    d           t          d| j         d|
 d           [|t          j        k    r|
	                    d          r	 t%          |
d           d{V }|                    |           |	                    d           t          d| j         d| d           # t          $ rQ}t          d| j         d | d           |                    |
           |	                    d           Y d}~6d}~ww xY w|t          j        k    rgt          j                            |
          rH|                    |
           |	                    d           t          d| j         d!|
 d           |t          j        k    rt          j                            |
          r|                    |
           t'          |
          j                                        }t-          j        |d"          }|	                    |           t          d| j         d#|
 d           g|t          j        k    rgt          j                            |
          rH|                    |
           |	                    d$           t          d| j         d%|
 d           |                    |
           |	                    d&           
|                    d'd          }|                    d	          r|                     ||          }d(}|t          j        k    rZ|rW|D ]S}t'          |          j                                        }|d)v r%	 t'          |                                          j        }||k    r$t          d| j         d*| d+| d,| d-	d           t'          |                              d./          }t'          |          j        }|}d0|v r1|                    d0d1          }t9          |          d2k    r|d1         }d3| d4| }|r| d5| }n|}t          d| j         d6| d           # t          $ r'}t          d| j         d7| d           Y d}~Kd}~ww xY wUt;          |||||                    d8          ||	9          S # t          $ r%}t          d| j         d:|            Y d}~dS d}~ww xY w);zKBuild a MessageEvent from bridge message data, downloading images to cache.NhasMediar|  r1   r  r  r  pttr   FrM  r  r   chatNamer   
senderName)r   	chat_name	chat_typeuser_id	user_name	mediaUrls)zhttp://zhttps://z.jpg)extz
image/jpegr   z] Cached user image: T)flushz] Failed to cache image: z] Using bridge-cached image: z.oggz	audio/oggz] Cached user voice: z] Failed to cache voice: z] Using bridge-cached audio: zapplication/octet-streamz ] Using bridge-cached document: z	video/mp4z] Using bridge-cached video: r  r   i  )z.txtz.mdz.csvz.jsonz.xmlz.yamlz.ymlz.logz.pyz.jsz.tsz.htmlz.cssz] Skipping text injection for z (z	 bytes > r  r   )errors_rF   r   z[Content of z]:
z

z] Injected text content from: z ] Failed to read document text: rh  )r   message_typesourceraw_messagerd  
media_urlsmedia_typesz] Error building event: )r   rK   TEXTrj   PHOTOVIDEOVOICEDOCUMENTbuild_sourcer   rN   r4   r  r   r&   r>   r  isabsrO   r   suffixrq   rM   r   statst_size	read_textr!   r"   rJ   )r   r   msg_typerw  r   r  r  raw_urlscached_urlsr  urlcached_pathr$  r  mimer   MAX_TEXT_INJECT_BYTESdoc_path	file_sizerF  fnamedisplay_namer)   	injections                           r+   r  z$WhatsAppAdapter._build_message_event  s     }	//55 t #'Hxx
## 	4!XXk266
j((*0HH
***0HH
**ez.A.A*0HH*3H xx	511H#+5I &&2..((:..#,,((<00 '  F xxR00HKK ,2 ,2{000S^^D[5\5\09,@&,Q,Q,Q&Q&Q&Q&Q&Q&Q#**;777#**<888O$)OO+OOW[\\\\\$ 9 9 9I$)IIaIIQUVVVV#**3///#**<888888889 !222rw}}S7I7I2&&s+++&&|444KdiKKcKKSWXXXXX!222s~~F]7^7^28,@&,Q,Q,Q&Q&Q&Q&Q&Q&Q#**;777#**;777O$)OO+OOW[\\\\\$ 8 8 8I$)IIaIIQUVVVV#**3///#**;777777778 !222rw}}S7I7I2&&s+++&&{333KdiKKcKKSWXXXXX!555"'--:L:L5&&s+++s))*0022C37=WXXD&&t,,,NdiNNNNVZ[[[[[!222rw}}S7I7I2&&s+++&&{333KdiKKcKKSWXXXXX&&s+++&&y1111
 88FB''Dxx	"" @33D$??$.!;///K/ + b bHx../5577C  F  F  Fb(,X(;(;(=(=(EI(+@@@ %  'L$)  'L  'LS[  'L  'L_h  'L  'L  tI  'L  'L  'L  TX  !Y  !Y  !Y  !Y (&*8nn&>&>i&>&P&PG$(NN$7E+0L"e||(-C(;(;#&u::??388L(R|(R(R(R(RI# 1*3'?'?'?'?'0!"Ydi"Y"Yx"Y"Yaefffff( b b b!"Tdi"T"TQR"T"T\`aaaaaaaaab) F.  % 88K00&'     	 	 	<di<<<<===44444	s   \ E\ AF;:\ ;
HAH\ HB\ 5AL\ 
M/AM*$\ *M//I\ AZ6\ BZ64\ 6
[' ["\ "[''-\ 
]\??])r
   N)NN)NNNr   )4r   
__module____qualname____doc__rj  r   __file__resolver   rl   rH   rf   r   r   r   r2   r   staticmethodrs   r   r   rw   r   r   r   r   r   r   r   r   r   r   r/  r  r<  rE  r\  rL   rr  ru  r  r  r  r  r  r  r  r  r  rJ   r  __classcell__)r   s   @r+   rT   rT      s        2  $x..0022:1=	IL]]*~ * * * * * *>d4 d d d dNs3x N N N N N3s8 N N N \N            > hsm     \$sCx. SX    FT#s(^ F F F F F$sCx. T    (Od38n O O O O O	'C 	'tCH~ 	'# 	' 	' 	' 	'<DcN <t < < < <6|)t |) |) |) |)|' ' ' '3(3- 3 3 3 3@+- +- +- +-Z7c 7c 7 7 7 7z #'-1;; ;;;; ;; 3-	;;
 4S>*;; 
;; ;; ;; ;;F ; ; ;; ; 	; ; 
; ; ; ;L "&#'/; /;/; /; 	/;
 #/; C=/; 
/; /; /; /;j "&"&S SS S #	S
 3-S 
S S S S S S$ "&"&	W 	W	W 	W #		W
 3-	W 
	W 	W 	W 	W "&"&	W 	W	W 	W #		W
 3-	W 
	W 	W 	W 	W "&"&	W 	W	W 	W #		W
 3-	W 
	W 	W 	W 	W "&#'"&
 

 
 #	

 C=
 3-
 

 
 
 
      ,/3 /4S> / / / /4 #  #  #  #DtCH~ (<BX        r   rT   )-r  ry   r   loggingr>   platformr   r   systemr   pathlibr   typingr   r   r   hermes_constantsr   	getLoggerr   r   intr,   r   rE   sysr  insertr2   r  r  r   gateway.configrG   rH   gateway.platforms.baserI   rJ   rK   rL   rM   rN   rO   rR   rT   r   r   r+   <module>r     s"   "    				  				    ho9,       & & & & & & & & & & + + + + + +		8	$	$ S  T        F 6; ) ) )d )t ) ) ) )< 


 33ttH~~--//7:;; < < < 3 3 3 3 3 3 3 3                 T    &J J J J J) J J J J Jr   