
    Jj3                       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mZmZ  ej        e          ZdZded	efd
Zded	dfdZdeded	efdZded	dfdZde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, ddl-m.Z. ddl/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6 ddl7m8Z8 de$d	efdZ9ded	e$fdZ:d	efdZ; G d  d!e,e0          Z<h d"Z=h d#Z>h d$Z?d%e$d&ed'ed	e$fd(Z@dddd)d*ZAd2d+ZBd,eCd-eCd	eCdz  fd.ZDd	efd/ZEd0 ZFd2d1ZGdS )3aG  
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)find_node_executableget_hermes_dirwith_hermes_node_pathz[owner reply] portreturnc                 :   g }	 t          j        ddd|  dgddd          }|j                                                                        D ]5}	 |                    t          |                     &# t          $ r Y 2w xY w|r|S n# t          $ r Y nw xY w	 t          j        dd	d
|  gddd          }t          j
        d|j                  D ]7}|                    t          |                    d                               8n# t          $ r Y nw xY w|S )u  PIDs of processes *listening* on ``port`` (POSIX) — never clients.

    This must match only LISTEN sockets. A bare ``lsof -i :PORT`` (or
    ``fuser PORT/tcp``) also returns *clients* whose connection merely involves
    that port number — e.g. a browser with a tab open on a local dev server
    sharing the port. SIGTERMing those closed the user's browser at irregular
    intervals. Restricting to LISTEN state frees the port for a new bridge
    without ever touching an unrelated client.
    lsofz-tiztcp:z-sTCP:LISTENT   capture_outputtexttimeoutssz-ltnHpz	sport = :z	pid=(\d+)   )
subprocessrunstdoutstrip
splitlinesappendint
ValueErrorFileNotFoundErrorrefinditergroup)r   pidsresultlinems        H/home/rurouni/.hermes/hermes-agent/plugins/platforms/whatsapp/adapter.py_listener_pids_on_portr'   +   s}    DUM4MM>:dA
 
 
 M''))4466 	 	DCII&&&&    	K	   8///0dA
 
 
 \6=99 	) 	)AKKAGGAJJ((((	)   KsI   AB	 "A43B	 4
B>B	  BB	 	
BBA0D 
DDc                    	 t           rddlm} t          j        g dddd |                      }|j                                        D ]}|                                }t          |          dk    rm|d         dk    ra|d	         }|	                    d
|            rA	 t          j        dd|d         dgdd |                       # t          j
        $ r Y w xY wdS t          |           D ]?}	 t          j        |t          j                   ## t           t"          t$          f$ r Y <w xY wdS # t&          $ r Y dS w xY w)zDKill any process *listening* on the given TCP port (a stale bridge).r   )windows_hide_flags)netstatz-anoz-pTCPTr   )r   r   r   creationflags   	LISTENINGr   :taskkill/PID   /F)r   r   r,   N)_IS_WINDOWShermes_cli._subprocess_compatr)   r   r   r   r   splitlenendswithSubprocessErrorr'   oskillsignalSIGTERMProcessLookupErrorPermissionErrorOSError	Exception)r   r)   r#   r$   parts
local_addrpids          r&   _kill_port_processrE   Q   s   ! 	HHHHHH  ^000#$0022  F
 0022 ! !

u::??uQx;'>'>!&qJ!**:t::66 !!&N!+VU1Xt D/3Q.@.@.B.B    
  *9 ! ! ! D!! !" .d33  GC0000*OWE   D 
    s`   BD5 !*CD5 CD5 CD5 $D5 6DD5 D0-D5 /D00D5 5
EErD   session_pathc                     ddl m}  ||           sdS |ddl m}  ||           |k    S ddl m}  ||           }|sdS d|v ot	          |          |v S )u  True only if ``pid`` is alive AND still our node bridge for this session.

    The PID is read from a file written by a previous run.  Once that process
    exits and is reaped the kernel can recycle the number onto an unrelated
    process — observed in the wild landing on a desktop browser's main process,
    which a bare-liveness ``os.kill`` then SIGTERMed, closing the whole browser
    at irregular intervals (every time the flapping bridge restarted).

    Identity is confirmed two ways: the kernel start time captured when we wrote
    the pidfile (definitive), and — for legacy pidfiles with no baseline — the
    command line, which must contain ``node`` and this session's unique path.
    A recycled PID (different start time / different cmdline) is never ours.
    r   _pid_existsFNget_process_start_time)_read_process_cmdlinenode)gateway.statusrI   rK   rL   str)rD   rF   expected_startrI   rK   rL   cmdlines          r&   _bridge_pid_is_oursrR   w   s     +*****;s u!999999%%c**n<< 544444##C((G ugAC$5$5$@A    c                    | dz  }|                                 sdS d}d}	 |                                                    d          }t          |d                                                   }t          |          dk    rA|d                                         r't          |d                                                   }nJ# t          t          t          t          f$ r* 	 |
                                 n# t          $ r Y nw xY wY dS w xY wt          || |          rY	 t          j        |t          j                   t                               d|           nI# t$          t&          t          f$ r Y n0w xY wddlm}  ||          rt                               d|           	 |
                                 dS # t          $ r Y dS w xY w)	u  Kill a bridge process recorded in a PID file from a previous run.

    The bridge writes ``bridge.pid`` into the session directory when it
    starts.  If the gateway crashed without a clean shutdown the old bridge
    process becomes orphaned — this helper finds and kills it.

    Critically, the recorded PID is re-validated against the live process
    (:func:`_bridge_pid_is_ours`) before any signal, so a recycled PID that now
    names an unrelated process (e.g. the user's browser) is never killed.
    
bridge.pidN
r   r   z2[whatsapp] Killed stale bridge PID %d from pidfilerH   z[whatsapp] Not killing pidfile PID %d: it is no longer the bridge (recycled onto an unrelated process); skipping to avoid killing a stranger.)exists	read_textr6   r   r   r7   r   r@   	TypeError
IndexErrorunlinkrR   r:   r;   r<   r=   loggerinfor>   r?   rN   rI   warning)rF   pid_filerD   recorded_startlinesrI   s         r&   _kill_stale_bridge_by_pidfilerb      s    l*H?? 
CN ""$$**400%(..""##u::>>eAhnn..> q!1!122NJ7   	OO 	 	 	D	 3n== 	GC(((KKLcRRRR"OW= 	 	 	D	 	/.....;s 	NN&'*  
   sZ   B"C D"C76D7
DDDD
D :E E54E5%F; ;
G	G	c                     	 ddl m}  ||          }|t          |          nd                    ||          }| dz                      |           dS # t
          $ r Y dS w xY w)a'  Write the bridge PID (and its kernel start time) for later cleanup.

    The start time on line 2 lets a future run prove the PID still names this
    exact process before signalling it, so a recycled PID can never be killed
    as a "stale bridge". Older single-line files remain readable.
    r   rJ   Nz{}
{}rU   )rN   rK   rO   format
write_textr@   )rF   rD   rK   startr   s        r&   _write_bridge_pidfilerg      s    999999&&s++ =s3xxxhooc5.I.I		$0066666   s   AA 
A"!A"Fforceri   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}	 |                    | j                  }|                    d          }|r@|D ]'}	 |                                 # |j        $ r Y $w xY w|                                 dS |D ]'}	 |                                 # |j        $ r Y $w xY w|                                 dS # |j        $ r Y dS w xY w)zITerminate the bridge process using process-tree semantics where possible.r0   r1   z/Tr3   T
   r   Nr    ztaskkill failed for PID )	recursive)r4   rO   rD   r   r   r   r   r;   	terminate
returncodestderrr   r   r@   psutilProcesschildrenNoSuchProcess)	procri   cmdr#   detailsrq   parentrs   childs	            r&   _terminate_bridge_processrz      s    63tx==$7 	JJt	^#	  FF ! 	 	 	 !		   FF	 !!};;BBDDG'J%J%J%JKKKMMM))??T?22 	!  JJLLLL+   DKKMMMMM!  OO%%%%+   D   sq   A 5B
	B
6F D&%F &
D30F 2D33F F E&%F &
E30F 2E33F 
FFr-   )PlatformPlatformConfig)WhatsAppBehaviorMixin)to_whatsapp_jid)BasePlatformAdapterMessageEventMessageType
SendResultSUPPORTED_DOCUMENT_TYPEScache_image_from_urlcache_audio_from_url)env_inturlc                    	 t          |                                           }n# t          t          f$ r Y dS w xY wddlm}m}m}m}  |             |             |             |            fD ]Q}	 |	                    t          |                                                    r dS ;# t          t          f$ r Y Nw xY wdS )u~  Return True only when an absolute path from the bridge resolves inside a
    known Hermes media cache directory.

    The Baileys bridge is a local subprocess that downloads inbound media and
    hands back absolute file paths. A compromised or buggy bridge could hand
    back an arbitrary path (e.g. ``/etc/passwd``) which would otherwise be
    attached verbatim and sent to the model. Resolve the path (following any
    symlinks) and require it to live under one of the real cache roots — this
    covers both the canonical ``cache/<kind>`` layout and the legacy
    ``<kind>_cache`` layout that ``get_hermes_dir`` may return.
    Fr   )get_audio_cache_dirget_document_cache_dirget_image_cache_dirget_video_cache_dirT)
r   resolver@   r   gateway.platforms.baser   r   r   r   is_relative_to)r   resolvedr   r   r   r   roots          r&   _is_allowed_bridge_pathr     s)   99$$&&Z    uu            	  	 
 
	&&tDzz'9'9';';<< tt$ 	 	 	H	5s   !$ 9914B))B=<B=pathc                     ddl }	 |                    |                                                                           dd         S # t          $ r Y dS w xY w)a  Return the first 16 hex chars of the SHA-256 of *path*'s contents.

    Used for the bridge staleness handshake: bridge.js reports its own
    source hash in ``/health`` (``scriptHash``), and the adapter compares
    it against the hash of bridge.js currently on disk.  A mismatch means
    a long-lived bridge process is serving code from before an update.
    Returns ``""`` when the file can't be read.
    r   N   rl   )hashlibsha256
read_bytes	hexdigestr@   )r   r   s     r&   _file_content_hashr   @  se     NNN~~doo//00::<<SbSAA   rrs   A A 
AAc                      t          d          } | sdS 	 t          j        | 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.
    rM   Fz	--versionTr   r   r   )r   r   r   ro   rA   )_noder#   s     r&   check_whatsapp_requirementsr   P  s|     !((E u	K 	
 
 
  A%%   uus   $: 
AAc                       e Zd ZdZdZdZdef fdZdede	de	fd	Z
d
ddedefdZdBdZdee         fdZdBdZ	 	 dCd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	 	 dCdedededee         dee         defdZdd ded!ed"ee         d#edef
d$Z	 dDded!ed%ee         d&ed'edeeeef                  def fd(Zddddd)ded*e	d+e	d,ee         d-ee         dee         deeeef                  defd.Z	 	 	 dEded/edee         dee         deeeef                  def fd0Z	 	 dCded1edee         dee         def
d2Z	 	 dCded3edee         dee         def
d4Z	 	 dCded5edee         dee         def
d6Z	 	 	 dEdededee         dee         dee         defd7Z dDdeddfd8Z!dedeeef         fd9Z"dBd:Z#d;Z$d<e%defd=Z&d<e%ddfd>Z'deddfd?Z(d@eeef         dee%         fdAZ) xZ*S )F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" | "pairing" — how DMs are handled (default: "pairing")
    - allow_from: List of sender IDs allowed in DMs (when dm_policy="allowlist")
    - group_policy: "open" | "allowlist" | "disabled" | "pairing" — which groups are processed (default: "pairing")
    - group_allow_from: List of group JIDs allowed (when group_policy="allowlist")

    Behavior (gating, mention parsing, markdown conversion, chunking) is
    provided by ``WhatsAppBehaviorMixin`` so the Cloud API adapter can
    share it. Only transport-specific code lives here.
    NTconfigc           	         t                                          |t          j                   t          j        ddlm}  |            t          _        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                    | _        |                                 | _        t;          j                    | _        d | _         d | _!        d | _"        d | _#        d| _$        | %                    dd          | _&        | %                    dd          | _'        i | _(        i | _)        d S )Nr   )resolve_whatsapp_bridge_dirbridge_port  bridge_scriptz	bridge.jsrF   zplatforms/whatsapp/sessionzwhatsapp/sessionreply_prefix	dm_policyWHATSAPP_DM_POLICYpairing
allow_from	allowFromgroup_policyWHATSAPP_GROUP_POLICYgroup_allow_fromgroupAllowFromFtext_batch_delay_secondsg      @text_batch_split_delay_secondsg      $@)*super__init__r{   WHATSAPPr   _DEFAULT_BRIDGE_DIR!gateway.platforms.whatsapp_commonr   _bridge_processextraget_bridge_portrO   _bridge_scriptr   r	   _session_path_reply_prefixr:   getenvr   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_coerce_float_extra_text_batch_delay_seconds_text_batch_split_delay_seconds_pending_text_batches_pending_text_batch_tasks)selfr   r   	__class__s      r&   r   zWhatsAppAdapter.__init__  s   !2333.6UUUUUU2M2M2O2OO/;?!'!1!1-!F!F-3\-=-=(;677.
 .
 $((8(879KLL)
 )
 $ $ -3L,<,<^,L,Lfl..{;;iryI]_h?i?ijjpprrxxzz226<3C3CL3Q3Q3rU[UaUeUefqUrUrss !1!1.!A!A!rRYOfhqErErssyy{{  B  B  D  D!%!8!89I9IJ\9]9]  :Dagamaqaq  sC  bD  bD  "E  "E!%!?!?!A!A-4]__"+/26@D %* *.)A)A&*
 *
& 04/G/G,d0
 0
, ?A"BD&&&rS   keydefaultr   c                 `   ddl }t          | j        dd          r| j        j                            |          nd}|t          |          S 	 t          |          }n&# t          t          f$ r t          |          cY S w xY w|                    |          r|dk     rt          |          S |S )zRead a float from ``config.extra``, guarding against bad/non-finite values.

        The result is fed directly to ``asyncio.sleep()``, so NaN/Inf and
        unparseable values fall back to ``default``.
        r   Nr   )	mathgetattrr   r   r   floatrY   r   isfinite)r   r   r   r   valueparseds         r&   r   z#WhatsAppAdapter._coerce_float_extra  s     	.5dk7D.Q.Q[!%%c***W[=>>!	"5\\FF:& 	" 	" 	">>!!!	"}}V$$ 	"

>>!s   A  B BF)is_reconnectr   c                |  K   t                      s:t                              d| j                   |                     ddd           dS t          | j                  }|                                s?t                              d| j        |           |                     dd| d	d           dS | j        d
z  }|                                s;t                              d| j        |           |                     ddd           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  }|dz  dz  }t          |          }	d}
|dz                                  rM	 |                                                                |	k    ot#          |	          }
n# t$          $ r d}
Y nw xY w|
swt'          d| j         d           t)          d          pd}	 t+          dd          }t-          j        |ddgt          |          dd|t1                                }|j        dk    rUt'          d| j         d |j                    	 | j        s,|r|                                  |                                  dS dS t'          d| j         d!           |	r'	 |                    |	           n# t$          $ r Y nw xY w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    rt|&                                 d{V }|"                    d*d+          }|d,k    r"|"                    d-d.          }t          |          }|r|r||k    rt'          d| j         d/| d0           | '                                 d| _(        |!                                | _)        tU          j+        | ,                                          | _-        	 ddd          d{V  ddd          d{V  | j        s,|r|                                  |                                  dS dS t'          d| j         d1|pd2 d3| d4           nt'          d| j         d5| d4           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t]          | j                   t_          | j#                   tU          j0        d6           d{V  tc          j2        d7d8          }| j        j        d9z  | _3        ti          | j3        d:d;<          }|| _5        t1                      }| j6        
| j6        |d=<   dd>l7m8}m9}m:} t           |                      |d?<   t           |                      |d@<   t           |                      |dA<   t-          j;        t)          dB          pdBt          |          dCt          | j#                  dDt          | j                  dE|g||d|F          | _(        ty          | j        | j(        j=                   ddl }d}i }t}          dG          D ]}tU          j0        d6           d{V  | j(        ?                                t'          d| j         dH| j(        j         d0           t'          d| j         dI| j3                    |                                   | 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         dJ           	 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         dK           t'          d| j         dI| j3                    |                                  	 | j        s,|r|                                  |                                  dS dS |"                    d*          d,k    rYt'          d| j         dL           t}          dG          D ]}tU          j0        d6           d{V  | j(        ?                                t'          d| j         dM           t'          d| j         dI| j3                    |                                   | 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         dJ           	 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         dN           t'          d| j         dO| j3                    t'          d| j         dP           |!                                | _)        tU          j+        | ,                                          | _-        | '                                 t'          d| j         dQ| j#                    	 | j        s,|r|                                  |                                  dS dS # t          $ ra}t          @                    dR| j        |dS           Y d}~| j        s,|r|                                  |                                  dS dS d}~ww xY w# | j        s+|r|                                  |                                  w w xY w)Tz
        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.whatsapp_node_missinguI   Node.js is not installed — install Node.js and re-run `hermes gateway`.F	retryablez [%s] Bridge script not found: %swhatsapp_bridge_missingz"WhatsApp bridge script missing at .z
creds.jsonz[%s] WhatsApp is enabled but not paired (no creds.json at %s). Run `hermes whatsapp` to pair, or remove WHATSAPP_ENABLED from your .env to disable.whatsapp_not_paireduB   WhatsApp enabled but not paired — run `hermes whatsapp` to pair.z[%s] Bridge found at %szwhatsapp-sessionzWhatsApp sessionTz3[%s] Could not acquire session lock (non-fatal): %sNzpackage.jsonnode_modulesz.hermes-pkg-hash[z,] Installing WhatsApp bridge dependencies...npmWHATSAPP_NPM_INSTALL_TIMEOUTi,  installz--silent)cwdr   r   r   envr   z] npm install failed: z] Dependencies installedz"] Failed to install dependencies: )parentsexist_okhttp://127.0.0.1:z/health   totalr      statusunknown	connected
scriptHashrl   z!] Using existing bridge (status: )z#] Running bridge is stale (running=unversionedz, disk=z), restartingz*] Bridge found but not connected (status: r   WHATSAPP_MODEz	self-chatz
bridge.logautf-8)encodingWHATSAPP_REPLY_PREFIX)r   r   r   HERMES_IMAGE_CACHE_DIRHERMES_AUDIO_CACHE_DIRHERMES_DOCUMENT_CACHE_DIRrM   z--portz	--sessionz--mode)r   rp   start_new_sessionr      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)Ar   r\   r^   name_set_fatal_errorr   r   rW   r   r]   _acquire_platform_lockrO   rA   rx   r   rX   r   boolr@   printr   r   r   r   r
   ro   rp   _running_release_platform_lock_close_bridge_logre   mkdiraiohttpClientSessionr   r   ClientTimeoutr   json_mark_connectedr   r   r   create_task_poll_messagesr   rb   rE   sleepr:   r   r   openr   r   r   r   r   r   Popenrg   rD   rangepollerror)r   r   bridge_path
creds_pathlock_acquirede
bridge_dir	_pkg_json
_dep_stamp	_pkg_hash_deps_fresh_npm_binnpm_install_timeoutinstall_resultr  sessionrespdatabridge_statusrunning_hash	disk_hashwhatsapp_modebridge_log_fh
bridge_env_get_audio_dir_get_doc_dir_get_img_dir
http_readyattempts                                r&   connectzWhatsAppAdapter.connect  s1      +,, 	NNOQUQZ[[[!!'[ "   
 54.//!!## 	NN=ty+VVV!!)C[CCC "   
 5 ',6
  "" 	NN( 	:	   !!%T "   
 5-ty+FFF 	`../A3tGYCZCZ\noo u MM 	` 	` 	`NNPRVR[]^________	`Y	)
 %+J"^3I#n47IIJ*955IK^+3355 ((#-#7#7#9#9#?#?#A#AY#N"cTXYbTcTcKK ( ( ("'KKK( !Q$)QQQRRR 066?%! +22PRU*V*V'%/^!9j9
OO'+! 3133& & &N &0A55Z$)ZZ>CXZZ[[[$h = )  2//111&&((((() )g AdiAAABBB  !!&11)<<<<& ! ! ! D!  ! ! !NdiNN1NNOOO 555V = )  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,;; 04xxb/I/I,>{,K,K	#/ !0I !0,R[B[B[$)*jdi*j*jZg*j*j*j$k$k$k$($8$8$:$:$:;?D$89@9N9N9P9PD$66=6I$J]J]J_J_6`6`DO+/3} } } } } } } } } } } } }  }  }  }  }  }  }  }  }  }  }  }F = )  2//111&&((((() )Q !&%o	 %o %o0<0M%o %oV_%o %o %o!" !" !" !"
 !&&{$)&{&{_l&{&{&{ | | |?} } } } } } } } } } } } } } } } } } } } } } } } } } } }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }  }B     *$*<===t0111-"""""""""
 Io{CCM#18<GD !13IIIM"/D /00J!-6:6H
23
         
 47||~~3F3FJ/036~~7G7G3H3HJ/069,,..6I6IJ23#-#3(00:F$$c$"344T%7!8!8m %$"&$ $ $D  "$"4d6J6NOOO
 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&&(((()sH  /)E E 
F'!FF<t 9H t Ht H/t A0L4 2L4 L# "L4 #
L0-L4 /L00L4 3t 4
N>Nt N#t :W ;WC.V#>WW <V#W#
V-	-W0V-	1W4W 
WW WW t 
W%"t $W%%H.t 	e%#;eAd/9e
e%t e/
d99e<d9=e e%
e	e%e	 e%#t %
e3/t 2e33At 9Ct 2p;o9Ao o91pt o9
o  o9#o $o9'p9
p	pp	p
t 
pt pC	t 
v##u?v ?vv 4v;c                 |    | 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   closerA   )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   z-[%s] Bridge exited during shutdown (code %d).z2WhatsApp bridge process exited unexpectedly (code z).z[%s] %swhatsapp_bridge_exitedTr   )r   r  r   r\   r]   r  has_fatal_errorr  r  r  _notify_fatal_errorfatal_error_message)r   ro   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2rS   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        d	z                      d
           n# t          $ r Y nw xY w| 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.TFrh   r   Nr   z] Error stopping bridge: z.] Disconnecting (external bridge left running)rU   )
missing_okz] Disconnected)r   r   rz   r>   r?   rn   r   r  r  r;   rA   r  r  r   r[   r@   r   donecancelCancelledErrorr   closedr9  r  _mark_disconnectedr  )r   r   s     r&   
disconnectzWhatsAppAdapter.disconnect  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	,.66$6GGGG 	 	 	D	 ? 	4?#7#7#9#9 	O""$$$o%%%%%%%%*I6     	-d&8&? 	-$**,,,,,,,,,!##%%%!!!#   +$)+++,,,,,sz   * C *AC A6C B% $C %*CC CC 
D C??D D? ?
EE	F F0/F0chat_idcontentreply_tometadatac           	        K   | j         r| j        st          dd          S |                                  d{V }|rt          d|          S |r|                                st          dd          S t          |          }	 ddl}|                     |          }|                     || 	                                          }g }	d}
t          |          D ]H\  }}||d}|r|dk    r||d	<   | j                            d
| j         d||                    d                    4 d{V 	 }|j        dk    rT|                                 d{V }|                    d          }
|
r"|	                    t%          |
                     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  Jt          d|
t/          |	dd                   d|	i          S # t0          $ 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rR  
message_idr   chatIdrA  replyTor   /send   r   r  r   r   	messageIdr   g333333?message_ids)rR  rT  continuation_message_idsraw_response)r  r   r   rB  r   r~   r  format_messagetruncate_message_outgoing_chunk_limit	enumeratepostr   r  r   r  r   r   rO   r   r7   r   r  tuplerA   )r   rK  rL  rM  rN  bridge_exitr  	formattedchunkssent_message_idslast_message_ididx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**9d6P6P6R6RSSF*,"O'// - -
U%$+ +  2q *2GI&-22@(9@@@ #111;; 3   F F F F F F F F {c))%)YY[[000000*.((;*?*?* J,33C4H4HIII&*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;;??!-,,,,,,,,,*)./?/D)E)E+-=>	     	; 	; 	;e3q66:::::::::	;sK   B,I 3B
G$=I I $
G.	.I 1G.	2AI 
J I;5J ;J )finalizerT  ro  c                  K   | j         r| j        st          dd          S |                                  d{V }|rt          d|          S 	 ddl}| j                            d| j         dt          |          ||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.FrP  rQ  Nr   r   z/edit)rV  r[  rA  r  r   rZ  r   TrS  )r  r   r   rB  r  rd  r   r~   r  r   r   rA   rO   )
r   rK  rT  rL  ro  rf  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<<<-g66!+& 
  --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D% *D?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 t          |          ||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.FrP  rQ  Nr   zFile not found: rV  filePath	mediaTypert  fileNamer   /send-mediax   r   rZ  r   Tr[  rR  rT  r_  )r  r   r   rB  r  r:   r   rW   r~   rd  r   r  r   r  r   r   rA   rO   )r   rK  rr  rs  rt  ru  rf  r  rm  r*  r+  r  r   s                r&   _send_media_to_bridgez%WhatsAppAdapter._send_media_to_bridge  sF      } 	DD$6 	De?CCCC ;;======== 	@e;????!	;NNN7>>),, W!%7U)7U7UVVVV *'22%'' '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
F3F *F0F 
FF FF 
G G=GGr   selectable_countquestionoptionsr  c                4  K   | j         r| j        st          dd          S |                                  d{V }|rt          d|          S 	 ddl}t          |          |t          |pg           |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)a   Send a native WhatsApp poll via the Baileys bridge.

        This is a low-level transport primitive only. Gateway approval UX must
        remain gateway-owned and add text fallback plus explicit confirmation
        semantics before approval prompts are ever mapped onto polls.
        FrP  rQ  Nr   )rV  r  r  selectableCountr   z
/send-pollrY  r   rZ  r   Tr[  r}  )r  r   r   rB  r  r~   listrd  r   r  r   r  r   r   rA   rO   )r   rK  r  r  r  rf  r  rm  r*  r+  r  r   s               r&   	send_pollzWhatsAppAdapter.send_poll  s      } 	DD$6 	De?CCCC ;;======== 	@e;????	;NNN *'22$2..#3	' 'G )..AD$5AAA--B-77 /   > > > > > > > > ;#%%!%,,,,,,D% $#'88K#8#8%)  > > > > > > > > > > > > > > #iikk))))))!%u===> > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  	; 	; 	;e3q66:::::::::	;sP   A'E% 8A
EE% *E?E% 
EE% E E% %
F/FFFchoices
clarify_idsession_keyc                   K   d |pg D             }dt          |          cxk    rdk    rrn no|                     |t          |pd                                          |d           d{V }|j        r|S t
                              d| j        |j                   t                      
                    ||||||	           d{V S )
a  Render multiple-choice clarify as a native WhatsApp poll.

        The gateway registers the pending clarify before calling this method.
        When Baileys later emits a poll_update with the selected option as
        message text, the normal clarify text-intercept resolves the pending
        question and the blocked agent continues. Open-ended clarifies use the
        text fallback so the user's next typed message is captured.
        c                     g | ]D}t          |                                          #t          |                                          ES  )rO   r   ).0choices     r&   
<listcomp>z0WhatsAppAdapter.send_clarify.<locals>.<listcomp>  sB    bbbcRXkkN_N_NaNabV**,,bbbrS   r      rl   r   r  NzB[%s] Native WhatsApp clarify poll failed; falling back to text: %s)rK  r  r  r  r  rN  )r7   r  rO   r   rR  r\   r^   r  r  r   send_clarify)
r   rK  r  r  r  r  rN  clean_choicesr#   r   s
            r&   r  zWhatsAppAdapter.send_clarify  s,     " cbGMrbbbM""((((b(((((>>HN##))++!"	 *        F ~ NNT	  
 WW))!# * 
 
 
 
 
 
 
 
 	
rS   )r  addressrM  rN  latitude	longituder  r  c                d  K   | j         r| j        st          dd          S |                                  d{V }|rt          d|          S 	 ddl}	t          |          t          |          t          |          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)z;Send a native WhatsApp location pin via the Baileys bridge.FrP  rQ  Nr   )rV  r  r  r  r  r   z/send-locationrY  r   rZ  r   Tr[  r}  )r  r   r   rB  r  r~   r   rd  r   r  r   r  r   r   rA   rO   )r   rK  r  r  r  r  rM  rN  rf  r  rm  r*  r+  r  r   s                  r&   send_locationzWhatsAppAdapter.send_location-  s      } 	DD$6 	De?CCCC ;;======== 	@e;????	;NNN *'22!(OO"9--' 'G
  '"& -%,	")..ED$5EEE--B-77 /   > > > > > > > > ;#%%!%,,,,,,D% $#'88K#8#8%)  > > > > > > > > > > > > > > #iikk))))))!%u===> > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  	; 	; 	;e3q66:::::::::	;sP   A?E= A
E*E= -*E*E= *
E44E= 7E48E= =
F/F*$F/*F/	image_urlc                    K   	 t          |           d{V }|                     ||d|           d{V S # t          $ r. t                                          |||||           d{V cY S w xY w)u^  Download image URL to cache, send natively via bridge.

        ``metadata`` is accepted to honor the base-class contract — the
        batch sender ``send_multiple_images`` passes it through to every
        send path. The bridge media call doesn't use it, matching the
        sibling overrides (send_video / send_voice / send_document).
        Nimage)r   r~  rA   r   
send_image)r   rK  r  rt  rM  rN  
local_pathr   s          r&   r  zWhatsAppAdapter.send_image[  s      	]3I>>>>>>>>J33GZRYZZZZZZZZZ 	] 	] 	]++GYS[\\\\\\\\\\\	]s   28 5A0/A0
image_pathc                 B   K   |                      ||d|           d{V S )z,Send a local image file natively via bridge.r  Nr~  )r   rK  r  rt  rM  kwargss         r&   send_image_filezWhatsAppAdapter.send_image_filep  4       //WgVVVVVVVVVrS   
video_pathc                 B   K   |                      ||d|           d{V S )u>   Send a video natively via bridge — plays inline in WhatsApp.videoNr  )r   rK  r  rt  rM  r  s         r&   
send_videozWhatsAppAdapter.send_video{  r  rS   
audio_pathc                 B   K   |                      ||d|           d{V S )z:Send an audio file as a WhatsApp voice message via bridge.audioNr  )r   rK  r  rt  rM  r  s         r&   
send_voicezWhatsAppAdapter.send_voice  r  rS   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   rK  rr  rt  ru  rM  r  s          r&   send_documentzWhatsAppAdapter.send_document  s_       //Y
G4)))44
 
 
 
 
 
 
 
 	
rS   c                 x  K   | j         r| j        sdS |                                  d{V rdS 	 ddl}| j                            d| j         ddt          |          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/typingrV  r   r   rZ  )	r  r   rB  r  rd  r   r~   r  rA   )r   rK  rN  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>>> 8 89--A-66 /          
                                	 	 	DD	s6   AB+ BB+ 
B""B+ %B"&B+ +
B98B9c                   K   | j         r| j        sdddS |                                  d{V r|ddS 	 ddl}| j                            d| j         dt          |           |                    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  typeNr   r   z/chat/rk   r   r   r   r  isGroupr!   participants)r  r  r  z+Could not get WhatsApp chat info for %s: %s)r  r   rB  r  r   r   r~   r  r   r  rA   r\   debug)r   rK  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)--WD$5WW_W=U=UWW--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3D+ D+ 
D##D+ &D#'D+ +
E5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    r|
                                 d{V }|D ]e}|                     |           d{V }|rF|j        t          j        k    r|                     |           J|                     |           d{V  fd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	/messagesrY  r   r   r   z] Poll error: r   r   )r  r  r   rB  r  r  r   r   r  r   r  _build_message_eventmessage_typer   TEXT_enqueue_text_eventhandle_messager   rG  rA   r  )r   r  rf  r*  messagesmsg_dataeventr   s           r&   r  zWhatsAppAdapter._poll_messages  s     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   E E E E E E E E {c)))-#4#4#4#4#4#4(0 E EH*.*C*CH*M*M$M$M$M$M$M$ME$ E#(#59I#I#I$($<$<U$C$C$C$C*.*=*=e*D*D$D$D$D$D$D$D$DE E E E E E E E E E E E E E E E E E E E E E E E E E E )    ' ' '$($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&&&&&&&&&&&&&&' -"""""""""? m 	# 	# 	# 	# 	#sI   ?E BD1E 1
D;;E >D;?E G	G6G4GGip  r  c                     ddl m}  ||j        | j        j                            dd          | j        j                            dd                    S )z-Session-scoped key for text message batching.r   )build_session_keygroup_sessions_per_userTthread_sessions_per_userF)r  r  )gateway.sessionr  sourcer   r   r   )r   r  r  s      r&   _text_batch_keyzWhatsAppAdapter._text_batch_key  sg    555555  L$(K$5$9$9:SUY$Z$Z%)[%6%:%:;UW\%]%]
 
 
 	
rS   c                    |                      |          }| j                            |          }t          |j        pd          }|||_        || j        |<   nw|j        r$|j        r|j         d|j         n|j        |_        ||_        |j        r>|j                            |j                   |j                            |j                   | j	                            |          }|r(|
                                s|                                 t          j        |                     |                    | j	        |<   dS )zBuffer a text event and reset the flush timer.

        When WhatsApp delivers rapid-fire messages (e.g. forwarded
        batches), this concatenates them and waits for a short quiet
        period before dispatching the combined message.
        rl   NrV   )r  r   r   r7   r   _last_chunk_len
media_urlsextendmedia_typesr   rE  rF  r   r  _flush_text_batch)r   r  r   existing	chunk_len
prior_tasks         r&   r  z#WhatsAppAdapter._enqueue_text_event  sL    ""5))-11#66
(b))	$-E!.3D&s++z bDLM a8= @ @EJ @ @ @W\Wa'0H$ ?#**5+;<<<$++E,=>>>377<<
 	 joo// 	 .5.A""3''/
 /
&s+++rS   c                   K   t          j                    }	 | j                            |          }|rt	          |dd          nd}|| j        k    r| j        }n| j        }t          j        |           d{V  | j        	                    |d          }|s<	 | j
                            |          |u r| j
        	                    |d           dS dS |                     |           d{V  | j
                            |          |u r| j
        	                    |d           dS dS # | j
                            |          |u r| j
        	                    |d           w w xY w)z4Wait for quiet period then dispatch aggregated text.r  r   N)r   current_taskr   r   r   _SPLIT_THRESHOLDr   r   r  popr   r  )r   r   r  pendinglast_lendelayr  s          r&   r  z!WhatsAppAdapter._flush_text_batch  s     +--	>044S99GAHOww(91===aH4000<6-&&&&&&&&&.223==E  -11#66,FF.223===== GF %%e,,,,,,,,,-11#66,FF.223===== GFt-11#66,FF.223==== Gs   B D) D) ):E#r+  c                 z  K   	 |                      |          sdS t          j        }t          |                    dd          pd          }|dv rt          j        }nx|dk    rt          j        }ne|                    d          rPd|v rt          j        }n?d|v rt          j        }n.d	|v rt          j	        }nd
|v rt          j
        }nt          j        }|                    dd          }|rdnd}|                     |                    dd          |                    d          ||                    d          |                    d                    }|                    dg           }g }g }	|D ]M}
t          |                    d          pd                                          }|t          j        k    r|
                    d          r	 t          |
d           d{V }|                    |           |	                    |pd           t#          d| j         d| d           # t&          $ rS}t#          d| j         d| d           |                    |
           |	                    |pd           Y d}~d}~ww xY w|t          j        k    rt(          j                            |
          rwt/          |
          rJ|                    |
           |	                    |pd           t#          d| j         d|
 d           t#          d| j         d |
 d           |t          j	        t          j
        hv r|
                    d          r	 t1          |
d!           d{V }|                    |           |	                    |p|t          j	        k    rd"nd#           t#          d| j         d$| d           h# t&          $ re}t#          d| j         d%| d           |                    |
           |	                    |p|t          j	        k    rd"nd#           Y d}~d}~ww xY w|t          j	        t          j
        hv rt(          j                            |
          rt/          |
          r\|                    |
           |	                    |p|t          j	        k    rd"nd#           t#          d| j         d&|
 d           ~t#          d| j         d'|
 d           |t          j        k    rt(          j                            |
          rt/          |
          r|                    |
           t3          |
          j                                        }|pt9          j        |d(          }|	                    |           t#          d| j         d)|
 d           _t#          d| j         d*|
 d           }|t          j        k    rt(          j                            |
          rwt/          |
          rJ|                    |
           |	                    |pd+           t#          d| j         d,|
 d           t#          d| j         d-|
 d           #|                    |
           |	                    d.           O|                    d/d          }|                    d          r|                     ||          }t          |                    d0          pd                                          }|pd}d}d}d}|                    d1          rg|                    d2          }|t          |          }|                     |                    d3                    }|r|}|                     |          }d4}|t          j        k    r[|rX|D ]T}t3          |          j                                        }|d5v r&	 t3          |                                           j!        }||k    r$t#          d| j         d6| d7| d8| d9	d           t3          |          "                    d:d;<          }t3          |          j        }|}d=|v r1|#                    d=d>          }tI          |          d?k    r|d>         }d@| dA| }|r| dB| }n|}t#          d| j         dC| d            # t&          $ r'}t#          d| j         dD| d           Y d}~Ld}~ww xY wVi } t          |                    dE          pd                                          }!|                    dF          }"|!r|!| dG<   tK          |"tL                    r|"r|"| dH<   |                    dI          r*d| dJ<   |                    tN                    stN           | }tQ          |||||                    dK          ||	| ||||L          S # t&          $ r%}t#          d| j         dM|            Y d}~dS d}~ww xY w)NzKBuild a MessageEvent from bridge message data, downloading images to cache.Nry  rl   >   locationlive_locationstickerhasMediar  r  pttr  r  Fr!   r  rV  chatNamesenderId
senderName)rK  	chat_name	chat_typeuser_id	user_name	mediaUrlsmime)zhttp://zhttps://.jpg)extz
image/jpegr   z] Cached user image: T)flushz] Failed to cache image: z] Using bridge-cached image: z0] Rejected bridge image path outside cache dir: .oggz	audio/oggz
audio/mpegz] Cached user audio: z] Failed to cache audio: z] Using bridge-cached audio: z0] Rejected bridge audio path outside cache dir: zapplication/octet-streamz ] Using bridge-cached document: z3] Rejected bridge document path outside cache dir: z	video/mp4z] Using bridge-cached video: z0] Rejected bridge video path outside cache dir: r   body
quotedTexthasQuotedMessagequotedMessageIdquotedParticipanti  >   .js.md.py.ts.css.csv.log.txt.xml.yml.html.json.yamlz] Skipping text injection for z (z	 bytes > r   r   replace)r   errors_r   r-   z[Content of z]:
z

z] Injected text content from: z ] Failed to read document text: 
nativeTypenativeMetadatawhatsapp_native_typewhatsapp_native	fromOwnerwhatsapp_from_ownerr[  )r   r  r  raw_messagerT  r  r  rN  reply_to_message_idreply_to_textreply_to_author_idreply_to_is_own_messagez] Error building event: ))_should_process_messager   r  rO   r   LOCATIONSTICKERPHOTOVIDEOVOICEAUDIODOCUMENTbuild_sourcer   
startswithr   r   r  r  rA   r:   r   isabsr   r   r   suffixr   r   _clean_bot_mention_text_normalize_whatsapp_id_message_is_reply_to_botstatst_sizerX   r6   r7   
isinstancedict_OWNER_REPLY_PREFIXr   )#r   r+  msg_typers  is_groupr  r  raw_urlscached_urlsr  r   bridge_mimecached_pathr   r  r  r  quoted_textr	  r  r
  r  raw_reply_idquoted_participantMAX_TEXT_INJECT_BYTESdoc_path	file_sizerL  fnamedisplay_namerB   	injectionrN  native_typenative_metadatas#                                      r&   r  z$WhatsAppAdapter._build_message_event1  s     }	//55 t #'HTXXk266<"==J:::&/y((&.*%% 
4j((*0HH
***0HHj((*0HH
***0HH*3H xx	511H#+5I &&2..((:..#,,((<00 '  F xxR00HKK 92 92!$((6"2"2"8b99??AA{000S^^D[5\5\0H,@&,Q,Q,Q&Q&Q&Q&Q&Q&Q#**;777#**;+F,GGGO$)OO+OOW[\\\\\$ H H HI$)IIaIIQUVVVV#**3///#**;+F,GGGGGGGGH !222rw}}S7I7I2.s33 p#**3///#**;+F,GGGO$)OO#OOW[\\\\\b$)bb]`bbjnooooo+"3[5F!GGGCNN[rLsLsG|,@&,Q,Q,Q&Q&Q&Q&Q&Q&Q#**;777#**;+z(VaVgJgJg;;my{{{O$)OO+OOW[\\\\\$ | | |I$)IIaIIQUVVVV#**3///#**;+z(VaVgJgJg;;my{{{{{{{{| +"3[5F!GGGBGMMZ]L^L^G.s33 p#**3///#**;+z(VaVgJgJg;;my{{{O$)OO#OOW[\\\\\b$)bb]`bbjnooooo!555"'--:L:L5.s33 s#**3///"3ii.4466*k.F.J3Pj.k.k#**4000R$)RRSRRZ^_____e$)ee`ceemqrrrrr!222rw}}S7I7I2.s33 p#**3///#**;+E+FFFO$)OO#OOW[\\\\\b$)bb]`bbjnooooo&&s+++&&y1111
 88FB''Dxx	"" @33D$?? dhh|44:;;AACCK'/4M"&!%&+#xx*++ N#xx(9::+*-l*;*;'%)%@%@J]A^A^%_%_"% <);&*.*G*G*M*M'$.!;///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&>&>Xa&>&b&bG$(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. (*Hdhh|44:;;AACCK"hh'788O ?3>/0/400 >_ >.=*+ xx$$ :26./':;; :19499D% 88K00&'!$7+#5(?     	 	 	<di<<<<===44444	s   h F7h AH32h 3
J=AJh JCh )A1Oh 
Q&AQ h QN;h Ac8h Bc86h 8
d)d$h $d))C!h 
h:h55h:r   N)NNN)NNN)+__name__
__module____qualname____doc__r   splits_long_messagesr|   r   rO   r   r   r
  r7  r  r   rB  rJ  r   r   r   rn  rq  r~  r  r   r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  __classcell__)r   s   @r&   r   r   g  s        8 3E~ 3E 3E 3E 3E 3E 3Ejs U u    & 5: S) S) S)T S)d S) S) S) S)j' ' ' '3(3- 3 3 3 3@1- 1- 1- 1-n #'-1C; C;C; C; 3-	C;
 4S>*C; 
C; C; C; C;V ; ; ;; ; 	; ; 
; ; ; ;L "&#'/; /;/; /; 	/;
 #/; C=/; 
/; /; /; /;n !"+; +; +;+; +; c	+; +; 
+; +; +; +;h .2'
 '
'
 '
 $	'

 '
 '
 4S>*'
 
'
 '
 '
 '
 '
 '
^ #!%"&-1,; ,; ,;,; ,; 	,; sm,; #,; 3-,; 4S>*,; 
,; ,; ,; ,;d "&"&-1] ]] ] #	]
 3-] 4S>*] 
] ] ] ] ] ]2 "&"&	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## ## ## ##N 
\ 
c 
 
 
 

 
$ 
 
 
 
8>3 >4 > > > >&tCH~ (<BX        rS   r   >   .gif.png.jpeg.webpr  >   .3gp.avi.mkv.mov.mp4.webm>   .m4a.mp3.wav.flac.opusr  rr  is_voiceforce_documentc                     |rdS t           j                            |           d                                         }|s	|t          v rdS |t
          v rdS |t          v rdS dS )ay  Map a local media file to the bridge /send-media ``mediaType``.

    Returns one of ``image`` | ``video`` | ``audio`` | ``document`` so the
    Baileys bridge renders the right native WhatsApp message kind. Voice notes
    and audio files route to ``audio``; ``force_document`` (the [[as_document]]
    directive) forces every file to ``document`` regardless of extension.
    r  r   r  r  r  )r:   r   splitextr   _WA_AUDIO_EXTS_WA_IMAGE_EXTS_WA_VIDEO_EXTS)rr  rH  rI  r  s       r&   _bridge_media_typerO    ss      z
'

9
%
%a
(
.
.
0
0C 3.((w
nw
nw:rS   )	thread_idmedia_filesrI  c          	      L  K   t          | di           pi }	 ddl}n# t          $ r ddicY S w xY w	 |                    dd          }t	          |          }	|pg }
|pd}d}|                                4 d{V }|                                r|                    d	| d
|	|d|                    d                    4 d{V 	 }|j	        dk    rM|
                                 d{V }dd|j	         d| icddd          d{V  cddd          d{V  S |                                 d{V }|                    d          }ddd          d{V  n# 1 d{V swxY w Y   |
D ]e\  }}t          j                            |          sdd| ic cddd          d{V  S t          |||          }|	||d}|dk    r"t          j                            |          |d<   |                    d	| d||                    d                    4 d{V 	 }|j	        dk    rO|
                                 d{V }dd|j	         d| icddd          d{V  c cddd          d{V  S |                                 d{V }|                    d          p|}ddd          d{V  n# 1 d{V swxY w Y   g	 ddd          d{V  n# 1 d{V swxY w Y   dd|	|dS # t"          $ r}dd| icY d}~S d}~ww xY w)zOut-of-process WhatsApp delivery via the local bridge HTTP API.

    Implements the standalone_sender_fn contract so deliver=whatsapp cron jobs
    succeed when cron runs separately from the gateway. Replaces the legacy
    _send_whatsapp helper.
    r   r   Nr  z/aiohttp not installed. Run: pip install aiohttpr   r   rl   zhttp://localhost:rX  rU  rY  r   rZ  r   zWhatsApp bridge error (z): r[  zWhatsApp media file not found: rw  r  rz  r{  r|  zWhatsApp media error (Twhatsapp)rR  platformrK  rT  zWhatsApp send failed: )r   r  ImportErrorr   r~   r  r   rd  r  r   r   r  r:   r   rW   rO  r  rA   )pconfigrK  rA  rP  rQ  rI  r   r  r   normalized_chat_idmediar   rj  r)  r*  r  r+  
media_pathrH  rs  rm  r   s                         r&   _standalone_sendrZ    s,      GWb))/REL L L LJKKKKL57iit44,W55!r}"((** &	O &	O &	O &	O &	O &	O &	Ogzz|| 
<"<<::::$64HH#111;; (   	< 	< 	< 	< 	< 	< 	< 	< {c))%)YY[[000000 ')Y4;)Y)YSW)Y)YZ	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	<&	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O "&,,,,,,D&*hh{&;&;O	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< ). O O$
Hw~~j11 U#%Sz%S%STTT)&	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O* 0
HnUU
0 *!++ +
 ++*,'*:*::*F*FGJ'"<<@@@@ #111<< (   	O 	O 	O 	O 	O 	O 	O 	O {c))%)YY[[000000 ')X$+)X)XRV)X)XY	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O;&	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	OJ "&,,,,,,D&*hh{&;&;&NO	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	OO%&	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	O &	OR "))	
 
 	
  7 7 75!5566666667s    --A	L :AK0	4E#=K0L "/E#K0#
E-	-K00E-	12K0#L 6A5K0,4K
 K04L 1K
8K0

KK0KK0L 0
K::L =K:>	L 
L#LL#L#c                  0   ddl m} m} ddlm}m}m}m}m}  |d            |d            |d            | d          }|r/|	                                dv r |d	            |d
d          sdS  |dd          r |dd            |d           n |dd            |d           dS  |d          }|r+ |d|
                    dd                      |d            |d          }	|	r  |d|	                                           dS dS )zGuide the user through WhatsApp setup.

    Replaces the central _setup_whatsapp in hermes_cli/gateway.py and the
    static _PLATFORMS["whatsapp"] dict. CLI helpers are lazy-imported so the
    plugin's module-load surface stays minimal.
    r   )get_env_valuesave_env_value)promptprompt_yes_noprint_header
print_infoprint_successWhatsAppz;WhatsApp uses a local Node.js bridge (WhatsApp Web client).zBStart the bridge separately; the gateway connects to it over HTTP.WHATSAPP_ENABLED>   1yestruezWhatsApp: already enabledzReconfigure WhatsApp?FNzEnable WhatsApp?Trg  zWhatsApp enabledfalsezWhatsApp left disabledz@Allowed user IDs (comma-separated, leave empty for no allowlist)WHATSAPP_ALLOWED_USERS rl   zWhatsApp allowlist configuredz4Home chat ID for cron delivery (leave empty to skip)WHATSAPP_HOME_CHANNEL)hermes_cli.configr\  r]  hermes_cli.cli_outputr^  r_  r`  ra  rb  r   r  r   )
r\  r]  r^  r_  r`  ra  rb  r  allowed_usershome_channels
             r&   interactive_setuprp  g  s    @???????              LJLMMMJSTTT}/00H HNN$$(<<<
.///}4e<< 	F}'.. )6222()))))7333
+,,,FJ M  7/1F1FsB1O1OPPP56666PQQL F.0B0B0D0DEEEEEF FrS   yaml_cfgwhatsapp_cfgc                 8   ddl }d|v rHt          j        d          s4t          |d                                                   t          j        d<   d|v r<t          j        d          s(|                    |d                   t          j        d<   |                    d          }|dt          j        d          sPt          |t                    rd	
                    d
 |D                       }t          |          t          j        d<   d|v rHt          j        d          s4t          |d                                                   t          j        d<   |                    d          }|dt          j        d          sPt          |t                    rd	
                    d |D                       }t          |          t          j        d<   d|v rHt          j        d          s4t          |d                                                   t          j        d<   |                    d          }|dt          j        d          sPt          |t                    rd	
                    d |D                       }t          |          t          j        d<   dS )u/  Translate config.yaml whatsapp: keys into WHATSAPP_* env vars.

    Implements the apply_yaml_config_fn contract (#24849). Mirrors the legacy
    whatsapp_cfg block from gateway/config.py::load_gateway_config(). Env vars
    take precedence over YAML. Returns None — everything flows through env.
    r   Nrequire_mentionWHATSAPP_REQUIRE_MENTIONmention_patternsWHATSAPP_MENTION_PATTERNSfree_response_chatsWHATSAPP_FREE_RESPONSE_CHATS,c              3   4   K   | ]}t          |          V  d S r2  rO   r  vs     r&   	<genexpr>z%_apply_yaml_config.<locals>.<genexpr>  (      //a3q66//////rS   r   r   r   ri  c              3   4   K   | ]}t          |          V  d S r2  r|  r}  s     r&   r  z%_apply_yaml_config.<locals>.<genexpr>  s(      --Q#a&&------rS   r   r   r   WHATSAPP_GROUP_ALLOWED_USERSc              3   4   K   | ]}t          |          V  d S r2  r|  r}  s     r&   r  z%_apply_yaml_config.<locals>.<genexpr>  r  rS   )r  r:   r   rO   r   environdumpsr   r  r  join)rq  rr  _jsonfrcafgafs         r&   _apply_yaml_configr    sY    L((;U1V1V(14\BS5T1U1U1[1[1]1]
-.\))")<W2X2X)27++lK]>^2_2_
./


0
1
1C
ry)GHHc4   	0((//3/////C58XX
12l""295I+J+J"+.|K/H+I+I+O+O+Q+Q
'(			,	'	'B	~bi(@AA~b$ 	.--"-----B/22ww
+,%%bi8O.P.P%.1,~2N.O.O.U.U.W.W
*+


-
.
.C
ry)GHHc4   	0((//3/////C58XX
124rS   c                     t          | di           pi }| t          | dd          r|rdS ddlm} |                    d          pd                                                                }|d	v S )
uV  WhatsApp is considered connected when the user has explicitly enabled it
    via ``WHATSAPP_ENABLED`` (or the YAML-bridged equivalent on the config).

    Auth itself is handled by the external Node.js bridge — we can't verify the
    bridge token here — so the opt-in flag is the connection signal. The legacy
    built-in path keyed off ``WHATSAPP_ENABLED`` in both the connected-platforms
    check and the setup-status display; returning an unconditional True here
    would make WhatsApp always show as "configured" in ``hermes setup`` even
    when the user never enabled it. #41112.
    r   NenabledFTr   rd  rl   >   re  rf  rg  )r   hermes_cli.gatewaygatewayr\  r   r   )r   r   gateway_modvals       r&   _is_connectedr    s     FGR((.BEgfi??E t -,,,,,$$%788>B
E
E
G
G
M
M
O
OC&&&rS   c                      t          |           S )zFFactory wrapper that constructs WhatsAppAdapter from a PlatformConfig.)r   )r   s    r&   _build_adapterr    s    6"""rS   c                     |                      ddt          t          t          dgdt          t
          dddt          dd	d
           dS )u:   Plugin entry point — called by the Hermes plugin system.rS  rc  rd  uF   WhatsApp requires a Node.js bridge — see the WhatsApp messaging docsri  WHATSAPP_ALLOW_ALL_USERSrk  i   u   💬T)r  labeladapter_factorycheck_fnis_connectedrequired_envinstall_hintsetup_fnapply_yaml_config_fnallowed_users_envallow_all_envcron_deliver_env_varstandalone_sender_fnmax_message_lengthemojiallow_update_commandN)register_platformr  r   r  rp  r  rZ  )ctxs    r&   registerr    s\    &,"()]"/204-!!      rS   r1  )Hr6  r   loggingr:   rT  r   r<   r   systemr4   pathlibr   typingr   r   r   hermes_constantsr   r	   r
   	getLoggerr3  r\   r  r   r  r'   rE   r
  rR   rb   rg   rz   sysr   insertrO   __file__r   r   gateway.configr{   r|   r   r}   gateway.whatsapp_identityr~   r   r   r   r   r   r   r   r   utilsr   r   r   r   r   rM  rN  rL  rO  rZ  rp  r  r  r  r  r  r  rS   r&   <module>r     sn   "   				  				     ho9,       & & & & & & & & & &          
	8	$	$ ' # # # # # #L#S #T # # # #LBS B B B B B B>. . . . . .b 3 4      6; , , ,d ,t , , , ,\ 


 33ttH~~--//7:;; < < < 3 3 3 3 3 3 3 3 C C C C C C 5 5 5 5 5 5                       & & & & & &RT c     T    .I I I I I+-@ I I Iv$ <;;BBBCCC#  t PS    2 I7 I7 I7 I7 I7X*F *F *F *FZ T dTk    D'T ' ' ' '0# # #
     rS   