
    `gj:                       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 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#jH                  jK                  d e& ee'      jQ                         jR                  d                ddl*m+Z+m,Z, ddl-m.Z. ddl/m0Z0 ddl1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8 ddl9m:Z: de&d
efdZ;ded
e&fdZ<d
efd Z= G d! d"e.e2      Z>h d#Z?h d$Z@h d%ZAd&e&d'ed(ed
e&fd)ZBddddd*d+ZCd3d,ZDd-eEd.eEd
eEdz  fd/ZFd
efd0ZGd1 ZHd3d2ZIy)4aG  
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)windows_detach_popen_kwargs)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                  j                         j	                         D ]  }	 |j                  t        |              |r|S 	 	 t        j                  dd	d
|  gddd      }t        j                  d|j                        D ]+  }|j                  t        |j                  d                   - 	 |S # t        $ r Y w xY w# t        $ r Y w xY w# t        $ r Y |S w xY w)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        U/root/.hermes/venv/lib/python3.12/site-packages/plugins/platforms/whatsapp/adapter.py_listener_pids_on_portr(   ,   s)    DUd4&M>:dA
 MM'')446 	DCI&	
 K 
8y/0dA
 \6==9 	)AKKAGGAJ(	) K!      KsI   AC1 C"*C1 2A-D  "	C.+C1 -C..C1 1	C=<C= 	DDc                 ^   	 t         rddlm} t        j                  g dddd |             }|j
                  j                         D ]j  }|j                         }t        |      dk\  s"|d   dk(  s+|d	   }|j                  d
|        sE	 t        j                  dd|d   dgdd |              l yt        |       D ]'  }	 t        j                  |t        j                         ) y# t        j                  $ r Y w xY w# t         t"        t$        f$ r Y Zw xY w# t&        $ r Y y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_processrF   R   s'   !H  ^^0#$02F
 002 !

u:?uQx;'>!&qJ!**Qtf:6!&NN!+VU1Xt D/3Q.@.B!" .d3 GGC0  *99 ! ! +OWE  sl   A(D  +D  4D  %C-4D  7D  $D*D  -D D  DD  DD  DD   	D,+D,rE   session_pathc                     ddl m}  ||       sy|ddl m}  ||       |k(  S ddl m}  ||       }|syd|v xr 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_existsFget_process_start_time)_read_process_cmdlinenode)gateway.statusrJ   rL   rM   str)rE   rG   expected_startrJ   rL   rM   cmdlines          r'   _bridge_pid_is_oursrS   x   sZ     +s!9%c*n<< 5#C(GgAC$5$@A    c                    | dz  }|j                         syd}d}	 |j                         j                  d      }t        |d   j	                               }t        |      dkD  r/|d   j	                         rt        |d   j	                               }t        || |      r<	 t        j                  |t        j                         t         j#                  d|       n$ddlm}  ||      rt         j-                  d|       	 |j                          y# t        t        t        t        f$ r$ 	 |j                          Y y# t        $ r Y Y yw xY ww xY w# t$        t&        t        f$ r Y hw xY w# t        $ r Y y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 pidfilerI   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_textr7   r   r   r8   r   rA   	TypeError
IndexErrorunlinkrS   r;   r<   r=   r>   loggerinfor?   r@   rO   rJ   warning)rG   pid_filerE   recorded_startlinesrJ   s         r'   _kill_stale_bridge_by_pidfilerc      s]    l*H??
CN ""$**40%(.."#u:>eAhnn. q!12N 3n=	GGC(KKLcR 	/sNN&'*
+ J7 	OO 	  		 #OW= 		  sN   A8D ":E E- E-D??	EEEEE*)E*-	E98E9c                     	 ddl m}  ||      }|t        |      ndj                  ||      }| dz  j	                  |       y# t
        $ r Y y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   rK   Nz{}
{}rV   )rO   rL   rP   format
write_textrA   )rG   rE   rL   startr   s        r'   _write_bridge_pidfilerh      sS    9&s+ =s3xhooc5.I		$006 s   AA 	AAFforcerj   c                :   t         rddt        | j                        dg}|r|j                  d       	 t	        j
                  |ddd      }|j                  d	k7  rH|j                  xs |j                  xs d
j                         }t        |xs d| j                         yd	dl}	 |j                  | j                        }|j!                  d      }|r)|D ]  }	 |j                           |j                          y|D ]  }	 |j                           |j                          y# t        $ r' |r| j                          Y y| j                          Y yw xY w# |j"                  $ r Y w xY w# |j"                  $ r Y w xY w# |j"                  $ r Y yw xY w)zITerminate the bridge process using process-tree semantics where possible.r1   r2   z/Tr4   T
   r   Nr    ztaskkill failed for PID )	recursive)r5   rP   rE   r   r   r   r   r<   	terminate
returncodestderrr   r   rA   psutilProcesschildrenNoSuchProcess)	procrj   cmdr$   detailsrr   parentrt   childs	            r'   _terminate_bridge_processr{      s   63txx=$7JJt	^^#	F !}};;BBDG'J'?z%JKK)??T?2! JJL
 KKM! OO%
 ; ! 			   	( ++  ++   sw   D+ *4F E/F F E3F +EEEE0-F /E00F 3FF F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                 8   	 t        |       j                         }ddlm}m}m}m}  |        |        |        |       fD ]-  }	 |j                  t        |      j                               r y/ y# t        t        f$ r Y yw xY w# t        t        f$ r Y Ww xY w)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   resolverA   r   gateway.platforms.baser   r   r   r   is_relative_to)r   resolvedr   r   r   r   roots          r'   _is_allowed_bridge_pathr     s    9$$&  	 	 
	&&tDz'9'9';< =
 1 Z  , $ 		s#   A2 (B2BBBBpathc                     ddl }	 |j                  | j                               j                         dd S # t        $ r Y y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   rm   )hashlibsha256
read_bytes	hexdigestrA   )r   r   s     r'   _file_content_hashr   A  sD     ~~doo/0::<SbAA s   /6 	AAc                      t        d      } | sy	 t        j                  | dgddd      }|j                  dk(  S # t        $ r Y yw xY w)z}
    Check if WhatsApp dependencies are available.
    
    WhatsApp requires a Node.js bridge for most implementations.
    rN   Fz	--versionTr   r   r   )r	   r   r   rp   rB   )_noder$   s     r'   check_whatsapp_requirementsr   Q  s]     !(E	K 	
   A%% 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                  j                  dd      | _        |j                  j                  dt        | j
                  dz              | _        t        |j                  j                  dt        dd	                  | _        |j                  j                  d
      | _        t        |j                  j                  d      xs t%        j&                  dd            j)                         j+                         | _        | j/                  |j                  j                  d      xs |j                  j                  d            | _        t        |j                  j                  d      xs t%        j&                  dd            j)                         j+                         | _        | j/                  |j                  j                  d      xs |j                  j                  d            | _        | j7                         | _        t;        j<                         | _        d | _         d | _!        d | _"        d | _#        d| _$        | jK                  dd      | _&        | jK                  dd      | _'        i | _(        i | _)        y )Nr   )resolve_whatsapp_bridge_dirbridge_port  bridge_scriptz	bridge.jsrG   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_portrP   _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__  sb   !2!23..6U2M2OO/;?!'!1!1-!F-3\\-=-=((;67.
 $((8(879KL)
 $ -3LL,<,<^,Lfll..{;iryyI]_h?ijpprxxz226<<3C3CL3Q3rU[UaUaUeUefqUrs !1!1.!A!rRYYOfhqErsyy{  B  B  D!%!8!89I9IJ\9]  :Dagamamaqaq  sC  bD  "E!%!?!?!A-4]]_"+/26@D %* *.)A)A&*
& 04/G/G,d0
, ?A"BD&rT   keydefaultr   c                 >   ddl }t        | j                  dd      r%| j                  j                  j	                  |      nd}|t        |      S 	 t        |      }|j                  |      r|dk  rt        |      S |S # t        t        f$ r t        |      cY S w xY w)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   floatrZ   r   isfinite)r   r   r   r   valueparseds         r'   r   z#WhatsAppAdapter._coerce_float_extra  s     	.5dkk7D.Q!!%%c*W[=>!	"5\F }}V$
>!	 :& 	">!	"s   A? ?BBF)is_reconnectr   c                v  K   t               s5t        j                  d| j                         | j	                  ddd       yt        | j                        }|j                         s:t        j                  d| j                  |       | j	                  dd| d	d       y| j                  d
z  }|j                         s6t        j                  d| j                  |       | j	                  ddd       yt        j                  d| j                  |       d}	 | j                  dt        | j                        d      syd}	 |j                  }|dz  }|dz  dz  }t        |      }	d}
|dz  j                         r/	 |j                         j!                         |	k(  xr t#        |	      }
|
st'        d| j                   d       t)        d      xs d}	 t+        dd      }t-        j.                  |ddgt        |      dd|t1                     }|j2                  dk7  rVt'        d| j                   d |j4                          	 | j6                  s#|r| j9                          | j;                          yyt'        d| j                   d!       |	r	 |j=                  |	       | j                  j?                  dd#       ddl }	 |jC                         4 d{   }|jE                  d$| jF                   d%|jI                  d&'      (      4 d{   }|jJ                  d)k(  rW|jM                          d{   }|jE                  d*d+      }|d,k(  r|jE                  d-d.      }t        |      }|r|r||k(  rt'        d| j                   d/| d0       | jO                          d| _(        |jC                         | _)        tU        jV                  | jY                               | _-        	 ddd      d{    ddd      d{    | j6                  s#|r| j9                          | j;                          yyt'        d| j                   d1|xs d2 d3| d4       nt'        d| j                   d5| d4       ddd      d{    ddd      d{    t]        | j                         t_        | jF                         tU        j`                  d6       d{    tc        jd                  d7d8      }| j                  j                  d9z  | _3        ti        | jf                  d:d;<      }|| _5        t1               }| jl                  | jl                  |d=<   dd>l7m8}m9}m:} t         |             |d?<   t         |             |d@<   t         |             |dA<   t-        jv                  t)        dB      xs dBt        |      dCt        | jF                        dDt        | j                        dE|gf|||dFty               | _(        t{        | j                  | jP                  j|                         ddl }d}i }t        dG      D ]  }tU        j`                  d6       d{    | jP                  j                         t'        d| j                   dH| jP                  j2                   d0       t'        d| j                   dI| jf                          | j;                           | j6                  s#|r| j9                          | j;                          yy	 |jC                         4 d{   }|jE                  d$| jF                   d%|jI                  d&'      (      4 d{   }|jJ                  d)k(  rjd}|jM                          d{   }|jE                  d*      d,k(  r<t'        d| j                   dJ       	 ddd      d{    ddd      d{     n#ddd      d{    ddd      d{     |st'        d| j                   dK       t'        d| j                   dI| jf                          | j;                          	 | j6                  s#|r| j9                          | j;                          yy|jE                  d*      d,k7  r#t'        d| j                   dL       t        dG      D ]  }tU        j`                  d6       d{    | jP                  j                         t'        d| j                   dM       t'        d| j                   dI| jf                          | j;                           | j6                  s#|r| j9                          | j;                          yy	 |jC                         4 d{   }|jE                  d$| jF                   d%|jI                  d&'      (      4 d{   }|jJ                  d)k(  rh|jM                          d{   }|jE                  d*      d,k(  r<t'        d| j                   dJ       	 ddd      d{    ddd      d{     nzddd      d{    ddd      d{     t'        d| j                   dN       t'        d| j                   dO| jf                          t'        d| j                   dP       |jC                         | _)        tU        jV                  | jY                               | _-        | jO                          t'        d| j                   dQ| jF                          	 | j6                  s#|r| j9                          | j;                          yy# t        $ r,}t        j                  d| j                  |       Y d}~
Td}~ww xY w# t$        $ r d}
Y 
w xY w# t$        $ r Y 	w xY w# t        $ rT}t'        d| j                   d"|        Y d}~| j6                  s#|r| j9                          | j;                          yyd}~ww xY w7 	G7 	7 7 /7 "7 # 1 d{  7  sw Y   xY w7 # 1 d{  7  sw Y   xY w# t        $ r Y w xY w7 7 7 H7 7 7 7 7 # 1 d{  7  sw Y   xY w7 # 1 d{  7  sw Y   xY w# t        $ r Y {w xY w7 7 87 7 7 7 7 # 1 d{  7  sw Y   xY w7 # 1 d{  7  sw Y   xY w# t        $ r Y Tw xY w# t        $ r\}t        j                  dR| j                  |dS       Y d}~| j6                  s#|r| j9                          | j;                          yyd}~ww xY w# | j6                  s#|r| j9                          | j;                          w w xY w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). Pair from the dashboard or run `hermes whatsapp`; remove WHATSAPP_ENABLED from your .env to disable.whatsapp_not_paireduU   WhatsApp enabled but not paired — pair from the dashboard or run `hermes whatsapp`.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
scriptHashrm   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_DIRrN   z--portz	--sessionz--mode)r   rq   r      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)Br   r]   r_   name_set_fatal_errorr   r   rX   r   r^   _acquire_platform_lockrP   rB   ry   r   rY   r   boolrA   printr	   r   r   r   r   rp   rq   _running_release_platform_lock_close_bridge_logrf   mkdiraiohttpClientSessionr   r   ClientTimeoutr   json_mark_connectedr   r   r   create_task_poll_messagesr   rc   rF   sleepr;   r   r   openr   r   r   r   r   r   Popenr   rh   rE   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  s     +,NNOQUQZQZ[!!'[ " 
 4../!!#NN=tyy+V!!)4[MC " 
  '',6
  "NN> 		:	 !!%g " 
 -tyy+F 	`../A3tGYGYCZ\no MY	)
 %++J"^3I#n47IIJ*95IK^+335(#-#7#7#9#?#?#AY#N"cTXYbTcK $))$PQR 06?%! +22PRU*V'%/^^!9j9
O'+! 313&N &00A5$)),B>CXCXBYZ[$h == //1&&( !g Adii[(@AB !&11)< $$TD$A #"002  }  }g&{{+D,=,=+>gF ' 5 5A 5 >  +   } } ;;#-)-#4D,0HHXy,IM,; 04xxb/I,>{,K	#/I,R[B[$)Adii[8YZgYhhi*j$k$($8$8$:;?D$89@9N9N9PD$66=6I6I$J]J]J_6`DO+/3} } }  }F == //1&&( !Q !&&'		{ 300<0M/NgV_U``m%o!"
 !&$))4^_l^mmz&{ |?} } }  }J *$*<*<=t001--"""
 IIo{CM#1188<GD !1!13IM"/D /0J!!-6:6H6H
23
 
 47|~3FJ/036~7G3HJ/069,.6IJ23#-#3#3(0:F$c$"3"34T%7%7!8m$ %$$ ./$D  "$"4"4d6J6J6N6NO
 JD 9 mmA&&&'',,.:Adii[(I$J^J^JiJiIjjklmAdii[d6F6F5GHI**, F == //1&&( !E&446 
* 
*'#*;;/0A0A/B'J$+$9$9$9$B $/ $ 	* 	* "#{{c1-1
-1YY['8#'88H#5#D$)Adii[8Z*[$\$)	* 	*
* 
* 
*	* 	*
* 
*, $))$MNO$))M$2B2B1CDE&&(^ == //1&&( !W xx![0$))$[\]$Ry [G!--***++002>$)),STU$))M$:J:J9KLM..0$F == //1&&( !E!#*#8#8#: 	. 	.g'.{{"3D4E4E3Fg N(/(=(=A(=(F (3 ( . . "&#';;##515+<D'+xx'9['H(-$))<^._(`(-. .	. 	. 	.. .	. 	.[. Adii[(NOPAdii[(89I9I8JKLAdii[(XYZ ")!6!6!8D &11$2E2E2GHDO  "Adii[ 9$:K:K9LMN == //1&&( !s  	`NNPRVR[R[]^__	`   ("'K(4  ' ! !  !Adii[(J1#NO V == //1&&( ![! }}
 $5} }} } } } }  }  }  }B   #h '
*	* (9	*
*	* 	* 	* 	*
* 
* 
* 
* !  +	..
 ,=.	.. . . .	. 	. 	. 	. % ! !&  	LL:DIIqSWLX== //1&&( !		 == //1&&( !s6  Dv9&m'  v9m' 9t >.n ,*t A/o 0v97o n1 $!t q" p!q" 6qp$q$p3<p'=B3p30q;p*<q q" p-q" 0v9 A p3 qp0qq" q	q"  At "q2#Ft &q5'Bt +0v9r90q81r946r#*q;
+r#.%r
q>
2r
r#rr#r9!r"r9&t (r#3r4r#8r9r r9At 0v9At !s	"A-t 0v9 tst6s7s
s7#s5s
62s(s73s4s78tstt 
s7ss7t%s4&t*Ct 70v9'	n0!nv9nv9n.*t -n..t 1	n>:o =n>>o 	p
p%t )0v9pt !q" $q'p3*q-q" 0q3q	9p<:q	q	q" qqqq" "	q/+t .q//t 5t 8r9;r#>r
r#r9r#
rrrr# r9#r6	)r,*r6	1r99	st st ts7ss7ts7s1$s'%s1,s74t7t
	=t >t
	t	tt tt 	v&#u=	v 0v9=vv 1v66v9c                     | j                   r#	 | j                   j                          d| _         yy# t        $ r Y w xY w)z)Close the bridge log file handle if open.N)r   closerB   )r   s    r'   r  z!WhatsAppAdapter._close_bridge_log  sE    ##))+ #'D   s   1 	==c                   K   | j                   y| j                   j                         }|yt        | dd      r&|dv r"t        j	                  d| j
                  |       yd| d}| j                  s]t        j                  d| j
                  |       | j                  d	|d
       | j                          | j                          d{    | j                  xs |S 7 w)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   rp   messages      r'   _check_managed_bridge_exitz*WhatsAppAdapter._check_managed_bridge_exit  s     '))..0
 4)51jL6PKK?		
 FzlRTU##LLDIIw7!!":Gt!T""$**,,,''272 -s   CC!
CC!c                   K   d| _         | j                  ri	 	 t        | j                  d       t        j                  d       d{    | j                  j                         	 t        | j                  d       nt        d| j                   d       	 | j                  d	z  j                  d
       | j                   rI| j                   j#                         s/| j                   j%                          	 | j                    d{    d| _        | j(                  r8| j(                  j*                  s"| j(                  j-                          d{    d| _        | j/                          | j1                          d| _        | j3                          t        d| j                   d       y# t        t        f$ r | j                  j                          Y w xY w7 # t        t        f$ r | j                  j                          Y w xY w# t        $ r&}t        d| j                   d|        Y d}~d}~ww xY w# t        $ r Y w xY w7 M# t        j&                  t        f$ r Y gw xY w7 %w)z=Stop the WhatsApp bridge and clean up any orphaned processes.TFri   r   Nr   z] Error stopping bridge: z.] Disconnecting (external bridge left running)rV   )
missing_okz] Disconnected)r   r   r{   r?   r@   ro   r   r  r  r<   rB   r  r  r   r\   rA   r   donecancelCancelledErrorr   closedr9  r  _mark_disconnectedr  )r   r   s     r'   
disconnectzWhatsAppAdapter.disconnect  s%    
 #C5-d.B.B%P mmA&&&'',,.641$2F2FdS Adii[ NOP	,.66$6G
 ??4??#7#7#9OO""$oo%%  d&8&8&?&?$$**,,,!##%!# $))N+,O +O< 5((2245& /@ 4,,1134 C$))$=aSABBC  		 &**I6  -s   I;F0 H G H 'G# >I;I 8A I;9I I	I AI;I8AI;0)GH GH #)HH HH 	IH=7I;=II;	II;II;I I51I;4I55I;chat_idcontentreply_tometadatac           	        K   | j                   r| j                  st        dd      S | j                          d{   }|rt        d|      S |r|j	                         st        dd      S t        |      }	 ddl}| j                  |      }| j                  || j                               }g }	d}
t        |      D ]&  \  }}||d}|r
|dk(  r||d	<   | j                  j                  d
| j                   d||j                  d            4 d{   }|j                  dk(  rF|j                          d{   }|j!                  d      }
|
rS|	j#                  t%        |
             n8|j'                          d{   }t        d|      cddd      d{    c S ddd      d{    t)        |      dkD  s
t+        j,                  d       d{    ) t        d|
t/        |	dd       d|	i      S 7 7 7 7 7 m7 \# 1 d{  7  sw Y   lxY w7 F# t0        $ r }t        dt%        |            cY d}~S d}~ww xY w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   rP   r   r8   r   r  tuplerB   )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.sendA  sW     }}D$6$6e?CC ;;==e;??gmmodt<<!'*,	; ++G4I**9d6P6P6RSF*,"O'/ -
U%$+ q *2GI&--22'(9(9':%@ #111; 3  F F {{c)%)YY[0*.((;*?*,33C4HI&*iik 1)%uEF F F F F v;?!--,,,5-8 *)./?/D)E+-=>	 ] >8F  1
 !2F F F F F  -  	;e3q6::	;s   9IH=I:BH) HH) #H=H
>AHHHH)  H!H) 'I(H) 3H4H) H)  H'!#H) IH) 
HHH) H) H$	HH$	 H) )	I2IIIII)finalizerT  ro  c                  K   | j                   r| j                  st        dd      S | j                          d{   }|rt        d|      S 	 ddl}| j                  j                  d| j                   dt        |      ||d|j                  d	
            4 d{   }|j                  dk(  rt        d|      cddd      d{    S |j                          d{   }t        d|      cddd      d{    S 7 7 i7 ?7 (7 # 1 d{  7  sw Y   yxY w# t        $ r }	t        dt        |	            cY d}	~	S d}	~	ww xY w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   rB   rP   )
r   rK  rT  rL  ro  rf  r  r*  r  r   s
             r'   edit_messagezWhatsAppAdapter.edit_message  sY     }}D$6$6e?CC ;;==e;??	;))..#D$5$5#6e<-g6!+&
  --B-7 /  B B ;;#%%dzJB B B #'))+-E%e5AB B B >
B B .B B B B  	;e3q6::	;s   9ED	EAD( !D"D( %DD( DD( ED&D'D7D( DD( ED( D( DD( D%DD%!D( $E%D( (	E1EEEEE	file_path
media_typecaption	file_namec                   K   | j                   r| j                  st        dd      S | j                          d{   }|rt        d|      S 	 ddl}t
        j                  j                  |      st        dd|       S t        |      ||d}|r||d<   |r||d	<   | j                  j                  d
| j                   d||j                  d            4 d{   }	|	j                  dk(  rF|	j                          d{   }
t        d|
j                  d      |
      cddd      d{    S |	j                          d{   }t        d|      cddd      d{    S 7 57 7 m7 B7 +7 # 1 d{  7  sw Y   yxY w# t         $ r }t        dt#        |            cY d}~S d}~ww xY w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   rX   r   rd  r   r  r   r  r   r   rB   rP   )r   rK  rr  rs  rt  ru  rf  r  rm  r*  r+  r  r   s                r'   _send_media_to_bridgez%WhatsAppAdapter._send_media_to_bridge  s     }}D$6$6e?CC ;;==e;??!	;77>>),!%9I)7UVV *'2%''G
 %,	"&/
#))..#D$5$5#6kB--C-8 /  B B ;;#%!%,D% $#'88K#8%)B B B #'))+-E%e5AB B B' >&B -B .B B B B   	;e3q6::	;s   9F>E0F>2F F>AF  E3!F $#E=E5 E=(F 4E75F 9F>:E=E9E=F *E;+F /F>3F 5E=7F 9E=;F =FFFF F>F 	F;F60F;1F>6F;;F>r   selectable_countquestionoptionsr  c                "  K   | j                   r| j                  st        dd      S | j                          d{   }|rt        d|      S 	 ddl}t        |      |t        |xs g       |d}| j                  j                  d| j                   d||j                  d	
            4 d{   }|j                  dk(  rF|j                          d{   }	t        d|	j                  d      |	      cddd      d{    S |j                          d{   }
t        d|
      cddd      d{    S 7 7 7 m7 B7 +7 # 1 d{  7  sw Y   yxY w# t        $ r }t        dt        |            cY d}~S d}~ww xY w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   rB   rP   )r   rK  r  r  r  rf  r  rm  r*  r+  r  r   s               r'   	send_pollzWhatsAppAdapter.send_poll  s     }}D$6$6e?CC ;;==e;??	; *'2$2.#3	'G ))..#D$5$5#6jA--B-7 /  > > ;;#%!%,D% $#'88K#8%)> > > #iik)!%u=> > > >> -> *> > > >  	;e3q6::	;s   9FEFA!E# 1E2E# 5#EE E9E# EE# 
FEE
E/E# ;E<E#  FE# EE# 
EE# E EE E# F E# #	F,FFFFFchoices
clarify_idsession_keyc                   K   |xs g D cg c]5  }t        |      j                         st        |      j                         7 }}dt        |      cxk  rdk  rtn nq| j                  |t        |xs d      j                         |d       d{   }	|	j                  r|	S t
        j                  d| j                  |	j                         t        
| )  ||||||       d{   S c c}w 7 _7 w)	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.
        r      rm   r   r  NzB[%s] Native WhatsApp clarify poll failed; falling back to text: %s)rK  r  r  r  r  rN  )rP   r   r8   r  rR  r]   r_   r  r  r   send_clarify)r   rK  r  r  r  r  rN  choiceclean_choicesr$   r   s             r'   r  zWhatsAppAdapter.send_clarify  s     " =DMrbcRXkN_N_NaV**,bbM"(b(>>HN#))+!"	 *  F ~~NNT		
 W)!# * 
 
 	
 c
s5   	C6C-C-AC6C2AC6(C4)
C64C6)r  addressrM  rN  latitude	longituder  r  c                F  K   | j                   r| j                  st        dd      S | j                          d{   }|rt        d|      S 	 ddl}	t        |      t        |      t        |      d}
|r||
d<   |r||
d<   | j                  j                  d	| j                   d
|
|	j                  d            4 d{   }|j                  dk(  rF|j                          d{   }t        d|j                  d      |      cddd      d{    S |j                          d{   }t        d|      cddd      d{    S 7 7 7 m7 B7 +7 # 1 d{  7  sw Y   yxY w# t        $ r }t        dt        |            cY d}~S d}~ww xY w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   rB   rP   )r   rK  r  r  r  r  rM  rN  rf  r  rm  r*  r+  r  r   s                  r'   send_locationzWhatsAppAdapter.send_location.  s     }}D$6$6e?CC ;;==e;??	; *'2!(O"9-'G
 "&%,	"))..#D$5$5#6nE--B-7 /  > > ;;#%!%,D% $#'88K#8%)> > > #iik)!%u=> > > >> -> *> > > >  	;e3q6::	;s   9F!EF!A3E5 EE5 #E *E+ E E5 EE5 F!E 0E1E E5 EE5 F!E5 E E5 E E5  E2&E)'E2.E5 1F!2E5 5	F>FFF!FF!	image_urlc                    K   	 t        |       d{   }| j                  ||d|       d{   S 7  7 # t        $ r t        |   |||||       d{  7  cY S w xY w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~  rB   r   
send_image)r   rK  r  rt  rM  rN  
local_pathr   s          r'   r  zWhatsAppAdapter.send_image\  sm     	]3I>>J33GZRYZZZ ?Z 	]+GYS[\\\\	]sI   A#8 48 68 A#8 8 A AA A#A  A#
image_pathc                 F   K   | j                  ||d|       d{   S 7 w)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_fileq  %      //WgVVVV   !!
video_pathc                 F   K   | j                  ||d|       d{   S 7 w)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  r  
audio_pathc                 F   K   | j                  ||d|       d{   S 7 w)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  r  c           	         K   | j                  ||d||xs t        j                  j                  |             d{   S 7 w)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  sE      //Y
G4)))4
 
 	
 
s   :AAAc                   K   | j                   r| j                  sy| j                          d{   ry	 ddl}| j                  j	                  d| j
                   ddt        |      i|j                  d            4 d{    	 ddd      d{    y7 l7 7 	# 1 d{  7  sw Y   yxY w# t        $ r Y yw xY ww)	z!Send typing indicator via bridge.Nr   r   z/typingrV  r   r   rZ  )	r  r   rB  r  rd  r   r   r  rB   )r   rK  rN  r  s       r'   send_typingzWhatsAppAdapter.send_typing  s     }}D$6$600222	
 ))..#D$5$5#6g> 89--A-6 /   
    3      		s   -CBCAB7 BB7 	B"B7 B B7 CB7  B7 "B4(B+)B40B7 3C4B7 7	C CCCc                   K   | j                   r| j                  sdddS | j                          d{   r|ddS 	 ddl}| j                  j	                  d| j
                   dt        |       |j                  d	      
      4 d{   }|j                  dk(  rb|j                          d{   }|j	                  d|      |j	                  d      rdnd|j	                  dg       dcddd      d{    S ddd      d{    |ddS 7 7 7 g7  7 # 1 d{  7  sw Y   !xY w# t        $ r!}t        j                  d||       Y d}~Id}~ww xY ww)z&Get information about a WhatsApp chat.Unknowndm)r  typeNr   r   z/chat/rl   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  rB   r]   debug)r   rK  r  r*  r+  r   s         r'   get_chat_infozWhatsAppAdapter.get_chat_info  sb    }}D$6$6%t4400222#T22	T))--#D$5$5#6f_W=U<VW--B-7 .  
 
 ;;#%!%,D $ 9+/88I+>D(,(D
 
 
 
 
  ..) 3

 -
 
 
 
 
  	TLLFQRSS	Ts   1E#D	E#AD6 DD6 #D!3D4<D!0D6 <D=D6 E#D6 DD6 E#D6 D!D6 D6 !D3'D*(D3/D6 6	E ?EE#E  E#c                 ^  K   ddl }| j                  rV| j                  sy| j                          d{   }|rt	        d| j
                   d|        y	 | j                  j                  d| j                   d|j                  d      	      4 d{   }|j                  d
k(  r|j                          d{   }|D ]f  }| j                  |       d{   }|s|j                  t        j                  k(  r| j                  |       N| j!                  |       d{    h ddd      d{    t#        j(                  d       d{    | j                  rUyy7 77 7 7 7 L7 <# 1 d{  7  sw Y   LxY w# t"        j$                  $ r Y yt&        $ r~}| j                          d{  7  }|r t	        d| j
                   d|        Y d}~yt	        d| j
                   d|        t#        j(                  d       d{  7   Y d}~d}~ww xY w7 ԭw)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  rB   r  )r   r  rf  r*  messagesmsg_dataeventr   s           r'   r  zWhatsAppAdapter._poll_messages  s    mm%% $ ? ? AAK$))B{m45'--11'(9(9':)D#111; 2  E E {{c))-#4(0 EH*.*C*CH*M$ME$#(#5#59I9I#I$($<$<U$C*.*=*=e*D$D$DEE E. --"""? mm B
E
 $5$M
 %EE E E E ))  '$($C$C$EEEAdii[;-89$))N1#67mmA&&&' #s   2H-E+"H-?F E.F #E8>E0?E8E2E8$AE8'E4(E8.F 9E6:F >H-H+H-)H-.F 0E82E84E86F 8F
>F?F
F H( H-"H(*H#=G >"H# H-%3H#HH#H-#H((H-ip  r  c                     ddl m}  ||j                  | j                  j                  j                  dd      | j                  j                  j                  dd      |j                  j                        S )z-Session-scoped key for text message batching.r   )build_session_keygroup_sessions_per_userTthread_sessions_per_userF)r  r  profile)gateway.sessionr  sourcer   r   r   r  )r   r  r  s      r'   _text_batch_keyzWhatsAppAdapter._text_batch_key  s^    5 LL$(KK$5$5$9$9:SUY$Z%)[[%6%6%:%:;UW\%]LL((	
 	
rT   c                    | j                  |      }| j                  j                  |      }t        |j                  xs d      }|||_        || j                  |<   n|j                  r8|j                  r|j                   d|j                   n|j                  |_        ||_        |j                  rJ|j                  j                  |j                         |j                  j                  |j                         | j                  j                  |      }|r |j                         s|j                          t        j                  | j                  |            | j                  |<   y)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.
        rm   NrW   )r  r   r   r8   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  s'    ""5)--11#6

(b)	$-E!.3D&&s+zzDLMM8==/EJJ< @W\WaWa'0H$##**5+;+;<$$++E,=,=>3377<
joo/.5.A.A""3'/
&&s+rT   c                   K   t        j                         }	 | j                  j                  |      }|rt	        |dd      nd}|| j
                  k\  r| j                  }n| j                  }t        j                  |       d{    | j                  j                  |d      }|s<	 | j                  j                  |      |u r| j                  j                  |d       yy| j                  |       d{    | j                  j                  |      |u r| j                  j                  |d       yy7 7 A# | j                  j                  |      |u r| j                  j                  |d       w w xY w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   sY    ++-	>0044S9GAHww(91=aH4000<<66--&&&..223=E --11#6,F..223= G %%e,,,--11#6,F..223= G ' ---11#6,F..223= GsM   E:A,D; D7"D; (;E:#D; 7D98D; <;E:7D; 9D; ;<E77E:r+  c                 F  K   	 | j                  |      syt        j                  }t        |j	                  dd      xs d      }|dv rt        j
                  }n|dk(  rt        j                  }nu|j	                  d      rdd|v rt        j                  }nOd|v rt        j                  }n:d	|v rt        j                  }n%d
|v rt        j                  }nt        j                  }|j	                  dd      }|rdnd}| j                  |j	                  dd      |j	                  d      ||j	                  d      |j	                  d            }|j	                  dg       }g }g }	|D ]S  }
t        |j	                  d      xs d      j                         }|t        j                  k(  rk|
j                  d      rZ	 t        |
d       d{   }|j!                  |       |	j!                  |xs d       t#        d| j$                   d| d       |t        j                  k(  rt(        j*                  j-                  |
      rot/        |
      rE|j!                  |
       |	j!                  |xs d       t#        d| j$                   d|
 d       0t#        d| j$                   d |
 d       O|t        j                  t        j                  hv r|
j                  d      rp	 t1        |
d!       d{   }|j!                  |       |	j!                  |xs |t        j                  k(  rd"nd#       t#        d| j$                   d$| d       |t        j                  t        j                  hv rt(        j*                  j-                  |
      rt/        |
      rZ|j!                  |
       |	j!                  |xs |t        j                  k(  rd"nd#       t#        d| j$                   d&|
 d       t#        d| j$                   d'|
 d       |t        j                  k(  rt(        j*                  j-                  |
      rt/        |
      r~|j!                  |
       t3        |
      j4                  j7                         }|xs t9        j                  |d(      }|	j!                  |       t#        d| j$                   d)|
 d       rt#        d| j$                   d*|
 d       |t        j                  k(  rt(        j*                  j-                  |
      rot/        |
      rE|j!                  |
       |	j!                  |xs d+       t#        d| j$                   d,|
 d       t#        d| j$                   d-|
 d       2|j!                  |
       |	j!                  d.       V |j	                  d/d      }|j	                  d      r| j;                  ||      }t        |j	                  d0      xs d      j                         }|xs d}d}d}d}|j	                  d1      rS|j	                  d2      }|t        |      }| j=                  |j	                  d3            }|r|}| j?                  |      }d4}|t        j                  k(  r|r|D ]  }t3        |      j4                  j7                         }|d5v s,	 t3        |      jA                         jB                  }||kD  r%t#        d| j$                   d6| d7| d8| d9	d       zt3        |      jE                  d:d;<      }t3        |      j$                  }|}d=|v r%|jG                  d=d>      }tI        |      d?k\  r|d>   }d@| dA| }|r| dB| }n|}t#        d| j$                   dC| d       	 i } t        |j	                  dE      xs d      j                         }!|j	                  dF      }"|!r|!| dG<   tK        |"tL              r|"r|"| dH<   |j	                  dI      r$d| dJ<   |j                  tN              s
tN         | }tQ        |||||j	                  dK      ||	| ||||L      S 7 # t&        $ rN}t#        d| j$                   d| d       |j!                  |
       |	j!                  |xs d       Y d}~Zd}~ww xY w7 # t&        $ rc}t#        d| j$                   d%| d       |j!                  |
       |	j!                  |xs |t        j                  k(  rd"nd#       Y d}~d}~ww xY w# t&        $ r(}t#        d| j$                   dD| d       Y d}~d}~ww xY w# t&        $ r%}t#        d| j$                   dM|        Y d}~yd}~ww xY ww)NzKBuild a MessageEvent from bridge message data, downloading images to cache.Nry  rm   >   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  rP   r   LOCATIONSTICKERPHOTOVIDEOVOICEAUDIODOCUMENTbuild_sourcer   
startswithr   r   r  r  rB   r;   r   isabsr   r   r   suffixr   r   _clean_bot_mention_text_normalize_whatsapp_id_message_is_reply_to_botstatst_sizerY   r7   r8   
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_namerC   	injectionrN  native_typenative_metadatas#                                      r'   r  z$WhatsAppAdapter._build_message_event3  s	    }	//5 #''HTXXk26<"=J::&//y(&..*%j(*00H
**00Hj(*00H
**00H*33H xx	51H#+I &&2.((:.#,((<0 ' F xxR0HKK 92!$((6"2"8b9??A{000S^^D[5\H,@&,Q&Q#**;7#**;+F,G$)),A+OW[\
 !2!22rww}}S7I.s3#**3/#**;+F,G$)),I#OW[\$)),\]`\abjno+"3"3[5F5F!GGCNN[rLs|,@&,Q&Q#**;7#**;+z(VaVgVgJg;my{$)),A+OW[\
 +"3"3[5F5F!GGBGGMMZ]L^.s3#**3/#**;+z(VaVgVgJg;my{$)),I#OW[\$)),\]`\abjno!5!55"''--:L.s3#**3/"3i..446*k.F.J.J3Pj.k#**40$)),LSERZ^_$)),_`c_demqr!2!22rww}}S7I.s3#**3/#**;+E+F$)),I#OW[\$)),\]`\abjno&&s+&&y1s92| 88FB'Dxx	"33D$? dhh|4:;AACK'/4M"&!%&+#xx*+#xx(9:+*-l*;'%)%@%@J]A^%_"%);&*.*G*G*M'$.!;///K + bHx.//557C  F  Fb(,X(;(;(=(E(EI(+@@ %$))4RS[R\\^_h^iir  tI  sJ  JK  'L  TX  !Y (&*8n&>&>Xa&>&bG$(N$7$7E+0L"e|(-C(;#&u:?388L*6|nD	(RI#*3D'?'0!Adii[0Nxj"Yaef+b2 (*Hdhh|4:;AACK"hh'78O3>/0/40_.=*+ xx$26./':;124&9D% 88K0&'!$7+#5(? A 'R % H$)),EaSIQUV#**3/#**;+F,GGH 'R % |$)),EaSIQUV#**3/#**;+z(VaVgVgJg;my{{|b  ) b!Adii[0PQRPS"T\`aabN  	Adii[ 8<=	s   f!e0 f!F&e0 =a0a-Aa0Ce0 +c;c
<AcM>e0 Ad<$e0 %Bd<2B:e0 ,f!-a00	c9Ac<e0 ce0 
c	d9Ad4.e0 4d99e0 <	e-e("e0 (e--e0 0	f9ff!ff!r   N)NNN)NNN)+__name__
__module____qualname____doc__r   splits_long_messagesr}   r   rP   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   h  s   8 3E~ 3Ejs U u & 5: S)T S)d S)j'3(3- 3@1-n #'-1C;C; C; 3-	C;
 4S>*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
 3-	W 
	W "&"&	W	W 	W #		W
 3-	W 
	W "&"&	W	W 	W #		W
 3-	W 
	W "&#'"&

 
 #	

 C=
 3-
 

  ,/3 /4S> /4##N 
\ 
c 

 
$ 
8>3 >4 >&tCH~ (<BX rT   r   >   .gif.png.jpeg.webpr  >   .3gp.avi.mkv.mov.mp4.webm>   .m4a.mp3.wav.flac.opusr  rr  is_voiceforce_documentc                     |ryt         j                  j                  |       d   j                         }|s|t        v ry|t
        v ry|t        v ryy)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  rF  rG  r  s       r'   _bridge_media_typerM  	  sR     
''

9
%a
(
.
.
0C3.(
n
nrT   )	thread_idmedia_filesrG  rt  c          	      (  K   t        | di       xs i }	 ddl}	 |j                  dd      }	t	        |      }
|xs g }|xs d}|rt        |      d	k(  r|nd}d}|j                         4 d{   }|j                         r|s|j                  d
|	 d|
|d|j                  d            4 d{   }|j                  dk7  rN|j                          d{   }dd|j                   d| icddd      d{    cddd      d{    S |j                          d{   }|j                  d      }ddd      d{    |D ]  \  }}t        j                  j                  |      s|r{	 |j                  d
|	 d|
|d|j                  d            4 d{   }|j                  dk(  r'|j                          d{   j                  d      }ddd      d{    dd| ic cddd      d{    S t'        |||      }|
||d}|dk(  r"t        j                  j)                  |      |d<   |r||d<   |j                  d
|	 d||j                  d            4 d{   }|j                  dk7  rP|j                          d{   }dd|j                   d| icddd      d{    c cddd      d{    S |j                          d{   }|j                  d      xs |}ddd      d{     ddd      d{    dd|
|dS # t        $ r ddicY S w xY w7 7 7 t7 S7 E7 /7 # 1 d{  7  sw Y   "xY w7 7 7 # 1 d{  7  sw Y   xY w# t         $ r t"        j%                  d       Y w xY w7 7 @7 7 7 7 7 # 1 d{  7  sw Y   xY w7 # 1 d{  7  sw Y   xY w# t         $ r}dd | icY d}~S d}~ww xY ww)!a  Out-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.

    When ``caption`` is provided (single-file ``MEDIA:<path> caption`` send),
    the text rides on the media bubble's native caption via the bridge
    ``/send-media`` ``caption`` field instead of being posted as a separate
    ``/send`` message beforehand.
    r   r   Nr  z/aiohttp not installed. Run: pip install aiohttpr   r   rm   r   zhttp://localhost:rX  rU  rY  r   rZ  r   zWhatsApp bridge error (z): r[  z7WhatsApp caption-fallback send failed for missing mediazWhatsApp media file not found: rw  r  rz  rt  r{  r|  zWhatsApp media error (Twhatsapp)rR  platformrK  rT  zWhatsApp send failed: )r   r  ImportErrorr   r   r8   r  r   rd  r  r   r   r  r;   r   rX   rB   r]   r_   rM  r  )pconfigrK  rA  rN  rO  rG  rt  r   r  r   normalized_chat_idmediar   media_captionrj  r)  r*  r  r+  
media_pathrF  rs  rm  r   s                           r'   _standalone_sendrY    s    * GWb)/RELJ7iit4,W5!r}" %,E
ad((* 8	O 8	Og zz|M"<<'}E:$64H#111; (  	< 	< {{c)%)YY[0 '+B4;;-sSWRX)YZ	< 	< 	<	8	O 8	O 8	O "&,D&*hh{&;O	< 	< ). %O$
Hww~~j1
 %	f'.||"3K= F0B}%](/(=(=B(=(G (4 ( [ [ "&#';;##5=AYY[7H6M6Mk6ZO[ [ $'Fzl%STTI8	O 8	O 8	OJ 0
HnU
0 *!++
 +*,''*:*::*FGJ' )6GI&"<<'}K@ #111< (  	O 	O {{c)%)YY[0 '+A$++cRVQW)XY	O 	O 	O_8	O 8	O 8	On "&,D&*hh{&;&NO	O 	O 	O9%O'8	O 8	Ov "))	
 	
K  LJKKL8	O	<  1	<	8	O -	< 	< 	< 	<.[ 8I[ [ [ [  ) f"NN+defE8	O^	O  1	O_8	On -	O 	O 	O 	O_8	O 8	O 8	O 8	O@  71!5667s  PL5 AO5 -M.O5 1AO 3M4O 7#MMM2O >M?O O5 MO5 PM(M)M>O 	M
.O 9/N(M3
)N,#M<M6
M<#N.M9/N3O ;O5 N7O5 PA-O :N:
;O >#O!N=
"O9O O 
O O5 OO5 PO1O
2OO OO O5 )O*
O5 4P5MPMPO5 O MO O5 MO M0	#M&$M0	+O 3N6M<9N<NNN
NN40O 3N44O 7O5 :O =O O O5 OO OOOO O5  O2&O)'O2.O5 5	P>P
PP
PPc                     ddl m} m} ddlm}m}m}m}m}  |d        |d        |d        | d      }|r$|j                         dv r |d	        |d
d      sy |dd      r |dd        |d       n |dd        |d       y |d      }|r! |d|j                  dd              |d        |d      }	|	r |d|	j                                yy)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?Trf  zWhatsApp enabledfalsezWhatsApp left disabledz@Allowed user IDs (comma-separated, leave empty for no allowlist)WHATSAPP_ALLOWED_USERS rm   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_  r`  ra  r   r  r   )
r[  r\  r]  r^  r_  r`  ra  r  allowed_usershome_channels
             r'   interactive_setupro    s     @  LMST/0HHNN$(<<./4e<'.)62())73+,JM /1F1FsB1OP56PQL.0B0B0DE rT   yaml_cfgwhatsapp_cfgc                    ddl }d|v rBt        j                  d      s-t        |d         j	                         t        j
                  d<   d|v r:t        j                  d      s%|j                  |d         t        j
                  d<   |j                  d      }|Yt        j                  d      sDt        |t              rd	j                  d
 |D              }t        |      t        j
                  d<   d|v rBt        j                  d      s-t        |d         j	                         t        j
                  d<   |j                  d      }|Yt        j                  d      sDt        |t              rd	j                  d |D              }t        |      t        j
                  d<   d|v rBt        j                  d      s-t        |d         j	                         t        j
                  d<   |j                  d      }|Yt        j                  d      sDt        |t              rd	j                  d |D              }t        |      t        j
                  d<   y)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   2   K   | ]  }t        |        y wr0  rP   .0vs     r'   	<genexpr>z%_apply_yaml_config.<locals>.<genexpr>       /a3q6/   r   r   r   rh  c              3   2   K   | ]  }t        |        y wr0  r{  r|  s     r'   r  z%_apply_yaml_config.<locals>.<genexpr>  s     -Q#a&-r  r   r   r   WHATSAPP_GROUP_ALLOWED_USERSc              3   2   K   | ]  }t        |        y wr0  r{  r|  s     r'   r  z%_apply_yaml_config.<locals>.<genexpr>  r  r  )r  r;   r   rP   r   environdumpsr   r  r  join)rp  rq  _jsonfrcafgafs         r'   _apply_yaml_configr    s    L(;U1V14\BS5T1U1[1[1]

-.\)"))<W2X27++lK]>^2_

./


0
1C
ryy)GHc4 ((/3//C58X

12l"2995I+J+.|K/H+I+O+O+Q

'(			,	'B	~bii(@Ab$-"--B/22w

+,%bii8O.P.1,~2N.O.U.U.W

*+


-
.C
ryy)GHc4 ((/3//C58X

12rT   c                     t        | di       xs i }| t        | dd      r|ryddlm} |j                  d      xs dj	                         j                         }|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   rc  rm   >   rd  re  rf  )r   hermes_cli.gatewaygatewayr[  r   r   )r   r   gateway_modvals       r'   _is_connectedr    sh     FGR(.BEgfi?E  -$$%78>B
E
E
G
M
M
OC&&&rT   c                     t        |       S )zFFactory wrapper that constructs WhatsAppAdapter from a PlatformConfig.)r   )r   s    r'   _build_adapterr    s    6""rT   c                 x    | j                  ddt        t        t        dgdt        t
        dddt        dd	d
       y)u:   Plugin entry point — called by the Hermes plugin system.rQ  rb  rc  uF   WhatsApp requires a Node.js bridge — see the WhatsApp messaging docsrh  WHATSAPP_ALLOW_ALL_USERSrj  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  ro  r  rY  )ctxs    r'   registerr    sH    &,"()]"/204-!!  rT   r/  )Jr4  r   loggingr;   rR  r    r=   r   systemr5   pathlibr   typingr   r   r   r6   r   hermes_constantsr	   r
   r   	getLoggerr1  r]   r  r   r  r(   rF   r
  rS   rc   rh   r{   sysr   insertrP   __file__r   r   gateway.configr|   r}   r   r~   gateway.whatsapp_identityr   r   r   r   r   r   r   r   r   utilsr   r   r   r   r   rK  rL  rJ  rM  rY  ro  r  r  r  r  r   rT   r'   <module>r     s  "   	  	  hoo9,  & & E  
		8	$ ' # # #L#S #T #LBS B B B>. . .b 3 4   6; ,d ,t ,\  3tH~--/77:; < 3 C 5   & & &RT c  T .J+-@ Jx$ <BC#  t PS 2 d7N*FZ T dTk D'T '0#
rT   