
    PL
jF                        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mZm	Z	m
Z
mZ  ej        e          ZddlmZmZmZmZ ddlmZ ddlmZmZ dedefd	Zd
edefdZ G d de          ZdefdZdefdZ d dZ!defdZ"dedz  fdZ#dedefdZ$dedefdZ%dddddededee         dee
e                  dede	eef         fdZ&d Z'dS )!u  
IRC Platform Adapter for Hermes Agent.

A plugin-based gateway adapter that connects to an IRC server and relays
messages to/from the Hermes agent.  Zero external dependencies — uses
Python's stdlib asyncio for the IRC protocol.

Configuration in config.yaml::

    gateway:
      platforms:
        irc:
          enabled: true
          extra:
            server: irc.libera.chat
            port: 6697
            nickname: hermes-bot
            channel: "#hermes"
            use_tls: true
            server_password: ""       # optional server password
            nickserv_password: ""     # optional NickServ identification
            allowed_users: []         # empty = allow all, or list of nicks
            max_message_length: 450   # IRC line limit (safe default)

Or via environment variables (overrides config.yaml):
    IRC_SERVER, IRC_PORT, IRC_NICKNAME, IRC_CHANNEL, IRC_USE_TLS,
    IRC_SERVER_PASSWORD, IRC_NICKSERV_PASSWORD
    N)AnyDictListOptional)BasePlatformAdapter
SendResultMessageEventMessageType)SessionSource)PlatformConfigPlatformrawreturnc                    d}d}|                      d          r?	 | dd                             dd          \  }} n# t          $ r | dd         }d} Y nw xY wd| v r|                     dd          \  } }|                                 }|r|d         nd}t          |          dk    r
|dd         ng }|r|                    |           |||dS )	ziParse a raw IRC protocol line into components.

    Returns dict with keys: prefix, command, params.
     :   N  :r   )prefixcommandparams)
startswithsplit
ValueErrorlenappend)r   r   trailingpartsr   r   s         A/home/kuhnn/.hermes/hermes-agent/plugins/platforms/irc/adapter.py_parse_irc_messager!   <   s   
 FH
~~c 	abb'--Q//KFCC 	 	 	WFCCC	 s{{		$**XIIKKE'eAhhRGe**q..U122YYbF  hFCCCs   != AAr   c                 D    d| v r|                      d          d         n| S )z2Extract nickname from IRC prefix (nick!user@host).!r   )r   )r   s    r    _extract_nickr$   W   s&    #&&==6<<Qf<    c                   R    e Zd ZdZ fdZedefd            ZdefdZ	ddZ
	 	 dded	ed
ee         deeeef                  fdZddeddfdZdedeeef         fdZd	ededee         fdZededefd            ZdeddfdZddZdeddfdZdedededededdfdZ xZS )
IRCAdapterzAsync IRC adapter implementing the BasePlatformAdapter interface.

    This class is instantiated by the adapter_factory passed to
    register_platform().
    c                 F   t          d          }t                                          ||           t          |di           pi }t	          j        d          p|                    dd          | _        t          t	          j        d          p|                    dd	                    | _	        t	          j        d
          p|                    dd          | _
        t	          j        d          p|                    dd          | _        t	          j        d          r)t	          j        dd                                          dv n|                    dd          | _        t	          j        d          p|                    dd          | _        t	          j        d          p|                    dd          | _        |                    dg           | _        d | j        D             | _        |                    d          }|=	 ddlm} |                    d          }|r|j        r|j        }n# t*          $ r Y nw xY wt          |pd          | _        d | _        d | _        d | _        | j
        | _        d| _        t7          j                    | _        d S )Nirc)configplatformextra
IRC_SERVERserverr   IRC_PORTport)  IRC_NICKNAMEnickname
hermes-botIRC_CHANNELchannelIRC_USE_TLS>   1yestrueuse_tlsTIRC_SERVER_PASSWORDserver_passwordIRC_NICKSERV_PASSWORDnickserv_passwordallowed_usersc                 `    h | ]+}t          |t                    |                                ,S  )
isinstancestrlower).0us     r    	<setcomp>z&IRCAdapter.__init__.<locals>.<setcomp>}   s2    )f)f)fS]^_adSeSe)f!'')))f)f)fr%   max_message_lengthr   )platform_registry  F)r   super__init__getattrosgetenvgetr.   intr0   r3   r6   rE   r;   r=   r?   r@   _allowed_users_lowergateway.platform_registryrJ   rI   	Exception_reader_writer
_recv_task_current_nick_registeredasyncioEvent_registration_event)	selfr*   kwargsr+   r,   max_msgrJ   entry	__class__s	           r    rM   zIRCAdapter.__init__g   s|   E??:::,,2 i--H8R1H1H	*--H641H1HII		.11XUYYz<5X5Xy//K599Y3K3K y'',BImR((..004HHH9d++ 	
  "y)>??c599M^`bCcCc!#+B!C!C!iuyyQdfhGiGi $)99_b#A#A)f)fT=O)f)f)f! ))011?GGGGGG)--e44 7U5 7#6G   "%gn"5"5 8<7;26!] #*=??   s   +H; ;
IIr   c                     dS )NIRCrB   )r^   s    r    namezIRCAdapter.name   s    ur%   c                   K   | j         r| j        s4t                              d           |                     ddd           dS 	 ddlm}m} | j          d| j         } |d	|          s@t                              d
| j        | j                    |                     ddd           dS || _	        n# t          $ r
 d| _	        Y nw xY w	 d}| j        rt          j                    }t          j        t          j        | j         | j        |          d           d{V \  | _        | _        nd# t(          $ rW}t                              d| j         | j        |           |                     dt+          |          d           Y d}~dS d}~ww xY w| j        r#|                     d| j                    d{V  |                     d| j                    d{V  |                     d| j         d           d{V  t          j        |                                           | _        	 t          j        | j                                        d           d{V  nb# t          j        $ rP t                              d           |                                  d{V  |                     ddd           Y dS w xY w| j        r=|                     d| j                    d{V  t          j         d           d{V  |                     d| j                    d{V  | !                                 t          "                    d| j         | j        | j#        | j                   dS )z:Connect to the IRC server, register, and join the channel.z*IRC: server and channel must be configuredconfig_missingz&IRC_SERVER and IRC_CHANNEL must be setF	retryabler   )acquire_scoped_lockrelease_scoped_lockr   r)   z,IRC: %s@%s already in use by another profilelock_conflictz&IRC identity in use by another profileNsslg      >@timeoutu&   IRC: failed to connect to %s:%s — %sconnect_failedTPASS NICK USER z 0 * :Hermes AgentzIRC: registration timed outregistration_timeoutz#IRC server did not send RPL_WELCOMEPRIVMSG NickServ :IDENTIFY    JOIN z(IRC: connected to %s:%s as %s, joined %s)$r.   r6   loggererror_set_fatal_errorgateway.statusrj   rk   r3   	_lock_keyImportErrorr;   rn   create_default_contextr[   wait_foropen_connectionr0   rV   rW   rU   rD   r=   	_send_rawcreate_task_receive_looprX   r]   waitTimeoutError
disconnectr?   sleep_mark_connectedinforY   )r^   rj   rk   lock_keyssl_ctxes         r    connectzIRCAdapter.connect   sB     { 	$, 	LLEFFF!! 8 "   
 5		"OOOOOOOO+7777H&&uh77 KT]\`\ghhh%%o7_kp%qqqu%DNN 	" 	" 	"!DNNN	"	G| 7466/6/?'TYGLLL0 0 0 * * * * * *&DL$,,  	 	 	LLA4;PTPY[\]]]!!"2CFFd!KKK55555	  	A..!?)=!?!?@@@@@@@@@nn4T]44555555555nnFT]FFFGGGGGGGGG "-d.@.@.B.BCC	"4#;#@#@#B#BDQQQQQQQQQQQ# 	 	 	LL6777//#########!!"8:_ko!ppp55		 ! 	#..!Wt?U!W!WXXXXXXXXX-""""""""" nn3T\33444444444>TYX\XjlplxyyytsF   A#B3 +B3 3CCA$D0 0
F:AFF13I% %AKKNc                   K   t          | dd          r)	 ddlm}  |d| j                   n# t          $ r Y nw xY w|                                  | j        r| j                                        s	 |                     d           d{V  t          j
        d           d{V  n# t          $ r Y nw xY w	 | j                                         | j                                         d{V  n# t          $ r Y nw xY w| j        rV| j                                        s=| j                                         	 | j         d{V  n# t          j        $ r Y nw xY wd| _        d| _        d| _        | j                                         dS )	zQuit and close the connection.r}   Nr   )rk   r)   z QUIT :Hermes Agent shutting downg      ?F)rN   r|   rk   r}   rU   _mark_disconnectedrW   
is_closingr   r[   r   closewait_closedrX   donecancelCancelledErrorrV   rZ   r]   clear)r^   rk   s     r    r   zIRCAdapter.disconnect   s      4d++ 	>>>>>>##E4>::::   !!!< 
	 7 7 9 9 
	nn%GHHHHHHHHHmC((((((((((   ""$$$l..0000000000    ? 	4?#7#7#9#9 	O""$$$o%%%%%%%%)      &&(((((sB   - 
::25B( (
B54B598C2 2
C?>C?<E
 
EEchat_idcontentreply_tometadatac           	        K   | j         r| j                                         rt          dd          S |}|                     ||          }|D ]u}	 |                     d| d|            d {V  t          j        d           d {V  ?# t          $ r*}t          dt          |                    cY d }~c S d }~ww xY wt          dt          t          t          j
                    dz                      	          S )
NFzNot connected)successrz   PRIVMSG r   333333?T  r   
message_id)rW   r   r   _split_messager   r[   r   rU   rD   rR   time)	r^   r   r   r   r   targetlinesliner   s	            r    sendzIRCAdapter.send   sA      | 	Dt|6688 	De?CCCC##GV44 	? 	?D?nn%@%@%@$%@%@AAAAAAAAAmC(((((((((( ? ? ?!%s1vv>>>>>>>>>>>? $3s49;;;M7N7N3O3OPPPPs   ;B
CB<4C<Cc                 
   K   dS )u&   IRC has no typing indicator — no-op.NrB   )r^   r   r   s      r    send_typingzIRCAdapter.send_typing  s      r%   c                 l   K   |                     d          p|                     d          }||rdnddS )N#&groupdm)re   type)r   )r^   r   
is_channels      r    get_chat_infozIRCAdapter.get_chat_info  sJ      '',,G0B0B30G0G
)3GGt
 
 	
r%   r   c                 r   |                      |          }t          d| d                    d                    dz   }d|z
  }| j        }g }|                    d          D ]N}|                                s	 |                    d          }t          ||          }	t          |          |	k    r*|                                r|                    |           ndt          |          }}
d	}|
|k    rI|
|z   dz  }t          |d
|                             d                    |	k    r|}|dz   }
n|dz
  }|
|k    I|}|                    dd	|          }||dz  k    r|}|                    |d
|         	                                           ||d
         
                                }5P|r|ndgS )zSplit a long message into IRC-safe chunks.

        IRC has a ~512 byte line limit.  After accounting for protocol
        overhead (``PRIVMSG <target> :``), we split content into chunks.
        r   r   utf-8rw     
Tr   r   Nr      r   )_strip_markdownr   encoderI   r   stripminr   rfindrstriplstrip)r^   r   r   overhead	max_bytes
user_limitr   	paragraph
para_byteslimitlowhighbestmidsplit_atspaces                   r    r   zIRCAdapter._split_message$  s    &&w//,&,,,33G<<==A(N	,
 t,, 	: 	:I??$$ :&--g66
J	22z??e++ (( 0Y///s9~~TTkk:!+C9TcT?11'::;;uDD"!Ag"Qw Tkk  !Q998q=(($HYyy188::;;;%hii07799	/: & 'uuB4'r%   textc                 f   t          j        dd|           } t          j        dd|           } t          j        dd|           } t          j        dd|           } t          j        dd|           } t          j        dd|           } t          j        d	d
|           } t          j        dd|           } | S )z-Convert basic markdown to plain text for IRC.z\*\*(.+?)\*\*z\1z	__(.+?)__z	\*(.+?)\*z(?<!\w)_(.+?)_(?!\w)z`(.+?)`z	```\w*\n?r   z!\[([^\]]*)\]\(([^)]+)\)z\2z\[([^\]]+)\]\(([^)]+)\)z\1 (\2))resubr   s    r    r   zIRCAdapter._strip_markdownP  s     v&t44vlE400vlE400v-ud;;vj%..vlB--v15$??v0*dCCr%   r   c                    K   | j         r| j                                         rdS |dz                       d          }| j                             |           | j                                          d{V  dS )zSend a raw IRC protocol line.N
r   )rW   r   r   writedrain)r^   r   encodeds      r    r   zIRCAdapter._send_rawe  s      | 	t|6688 	F&=((117###l  """""""""""r%   c                   K   d}	 | j         r| j                                         s| j                             d           d{V }|sn||z  }d|v r|                    dd          \  }}	 |                    dd          }|                     |           d{V  n2# t          $ r%}t                              d	|           Y d}~nd}~ww xY wd|v | j         r| j                                         nA# t          j
        $ r  t          $ r%}t                              d
|           Y d}~nd}~ww xY w| j        rNt                              d           |                     ddd           |                                  d{V  dS dS # | j        rMt                              d           |                     ddd           |                                  d{V  w w xY w)u6   Main receive loop — reads lines and dispatches them.r%   i   N   
r   r   replaceerrorszIRC: error handling line: %szIRC: receive loop error: %sz*IRC: connection lost, marking disconnectedconnection_lostz"IRC connection closed unexpectedlyTrh   )rV   at_eofreadr   decode_handle_linerU   ry   warningr[   r   rz   is_connectedr{   _notify_fatal_error)r^   bufferdatar   decodedr   s         r    r   zIRCAdapter._receive_loopm  s     	1, Jt|':':'<'< J!\..t44444444 $''#)<<#;#;LD&J"&++gi+"H"H"//8888888888$ J J J'EqIIIIIIIIJ '' , Jt|':':'<'< J % 	 	 	 	; 	; 	;LL6::::::::	;   1KLLL%%&79]im%nnn..000000000001 1t  1KLLL%%&79]im%nnn..00000000001s[   A%C6 ,2B C6 
C)C	C6 	C'C6 5F 6D4D/*F /D44F AG$r   c                 "  K   t          |          }|d         }|d         }|dk    r,|r|d         nd}|                     d|            d{V  dS |dk    r1d	| _        | j                                         |r|d         | _        dS |d
k    r| j                            d          }t          j	        d| j                  }|r2t          |                    d                    dz   }| d| | _        n/| j        | j        k    r| j        dz   | _        n| j        dz   | _        |                     d| j                    d{V  dS |dk    rt          |          dk    rt          |d                   }	|d         }
|d         }|	                                | j                                        k    rdS |                    d          r%|                    d          rd|	 d|dd          }|                    d          rdS |
                    d          p|
                    d          }|r|
n|	}|rdnd}|rd}| j         d| j         d | j         dfD ]h}|                                                    |                                          r-|t          |          d                                         }d	} ni|sdS | j        r8|	                                | j        vrt&                              d!|	           dS |                     ||||	|	"           d{V  |d#k    rSt          |d                                                   | j                                        k    r|r|d         | _        dS dS dS dS )$z$Dispatch a single IRC protocol line.r   r   PINGr   r   PONG :N001T433_0123456789z_(\d+)$r   __1rs   PRIVMSGrw   r   zACTION z* r      r   r   r   r   Fr   ,z/IRC: ignoring message from unauthorized user %s)r   r   	chat_typeuser_id	user_nameNICK)r!   r   rZ   r]   setrY   r3   r   r   searchrR   r   r   r$   rE   r   endswithr   rS   ry   debug_dispatch_message)r^   r   msgr   r   payloadbasesuffix_matchnext_numsender_nickr   r   r   r   r   	addressedr   s                    r    r   zIRCAdapter._handle_line  sS      %%i.X f#)1fQiirG..!3'!3!3444444444F e#D$((*** /%+AY"F e=''66D9Z1CDDL :|11!44559(,%9%9x%9%9""#t}44%)]S%8""%)]T%9"..!=);!=!=>>>>>>>>>F iCKK1$4$4'H66KAYF!9D   ""d&8&>&>&@&@@@ }-- 7$--2G2G 76K66$qt*66 v&&   **3//I63D3DS3I3IJ *;ffG#-74I  	!	"&"4777D<N9Q9Q9Q"&"47779  Fzz||..v||~~>> #CKKLL17799$(	 ! F ( [->->-@-@Ha-a-aNP[\\\((##% )          fs8}!=!=!C!C!E!EI[IaIaIcIc!c!c /%+AY""" !c!c/ /r%   r   r   r   c                 f  K   | j         sdS |                     |||||          }t          |t          j        |t          t          t          j                    dz                      t          d          j	        
                                          }|                     |           d{V  dS )z;Build a MessageEvent and hand it to the base class handler.N)r   	chat_namer   r   r   r   datetime)r   message_typesourcer   	timestamp)_message_handlerbuild_sourcer	   r
   TEXTrD   rR   r   
__import__r  nowhandle_message)r^   r   r   r   r   r   r  events           r    r   zIRCAdapter._dispatch_message  s       $ 	F"" # 
 
 $)3ty{{T12233 ,,599;;
 
 
 !!%(((((((((((r%   r   N)NNN)__name__
__module____qualname____doc__rM   propertyrD   re   boolr   r   r   r   r   r   r   r   r   r   staticmethodr   r   r   r   r   __classcell__)rb   s   @r    r'   r'   `   s[        *3 *3 *3 *3 *3X c    X
At A A A AF )  )  )  )P #'-1Q QQ Q 3-	Q
 4S>*Q Q Q Q.      
3 
4S> 
 
 
 
*(c *(3 *(49 *( *( *( *(X c c    \(#C #D # # # #1 1 1 14W/c W/d W/ W/ W/ W/r)) ) 	)
 ) ) 
) ) ) ) ) ) ) )r%   r'   c                  x    t          j        dd          } t          j        dd          }t          | o|          S )uo   Check if IRC is configured.

    Only requires the server and channel — no external pip packages needed.
    r-   r   r5   )rO   rP   r  r.   r6   s     r    check_requirementsr    s;    
 Y|R((Fir**G "7###r%   c                     t          | di           pi }t          j        d          p|                    dd          }t          j        d          p|                    dd          }t	          |o|          S )z=Validate that the platform config has enough info to connect.r,   r-   r.   r   r5   r6   rN   rO   rP   rQ   r  r*   r,   r.   r6   s       r    validate_configr    o    FGR((.BEY|$$?		(B(?(?Fi&&B%))Ir*B*BG"7###r%   c                  |   ddl m} m}m}m}m}m}m}m}  |d            |d          }|r |d| d            |dd          sd	S  |d
            |d           t                        | d|pd          }	|	s |d           d	S  |d|	
                                            |dd          }
 |d|
rdnd           |
rdnd} | d| d |d          pd          }|rF	  |dt          t          |                               n5# t          $ r  |d|            Y nw xY w |d          r |dd            | d |d          pd          }|s |d           d	S  |d|
                                            | d |d          pd          }|s |d           d	S  |d|
                                           t                        |d             |d!            |d"d          r | d#d$          }|r |d%|            |d&d          r | d'd$          }|r |d(|           t                        |d)            |d*            |d+            |d,            |d-d          }|r$ |d.d            |d/d            |d0           ni |d.d            | d1 |d/          pd          }|r, |d/|                    d2d                      |d3           n |d/d            |d4           t                        |d5            |d6           d	S )7zInteractive `hermes gateway setup` flow for the IRC platform.

    Lazy-imports ``hermes_cli.setup`` helpers so the plugin stays importable
    in non-CLI contexts (gateway runtime, tests).
    r   )promptprompt_yes_nosave_env_valueget_env_valueprint_header
print_infoprint_warningprint_successrd   r-   z!IRC: already configured (server: )zReconfigure IRC?FNuR   Connect Hermes to an IRC network. Uses Python stdlib — no extra packages needed.z<   Works with Libera.Chat, OFTC, your own ZNC/InspIRCd, etc.z*IRC server hostname (e.g. irc.libera.chat)r   )defaultu)   Server is required — skipping IRC setupzUse TLS (recommended)?Tr7   r:   false66976667zPort (default r/   u   Invalid port — using default zBot nickname (e.g. hermes-bot)r2   u+   Nickname is required — skipping IRC setupu>   Channel to join (e.g. #hermes — comma-separate for multiple)r5   u*   Channel is required — skipping IRC setupu   🔑 Optional authenticationz   Leave blank to skip.z+Configure a server password (PASS command)?zServer password)passwordr<   z"Identify with NickServ on connect?zNickServ passwordr>   u5   🔒 Access control: restrict who can message the botuA      IRC nicks are not authenticated — anyone can claim any nick.zD   For public channels, pair with NickServ-only mode on your networkz,   if you want stronger identity guarantees.z2Allow all users in the channel to talk to the bot?IRC_ALLOW_ALL_USERSIRC_ALLOWED_USERSuD   ⚠️  Open access — any nick in the channel can command the bot.z=Allowed nicks (comma-separated, leave empty to deny everyone)r   zAllowlist configureduJ   No nicks allowed — the bot will ignore all messages until you add nicks.z)IRC configuration saved to ~/.hermes/.envzFRestart the gateway for changes to take effect: hermes gateway restart)hermes_cli.setupr"  r#  r$  r%  r&  r'  r(  r)  printr   rD   rR   r   r   )r"  r#  r$  r%  r&  r'  r(  r)  existing_serverr.   r;   default_portr0   r3   r6   r=   nickserv	allow_allalloweds                      r    interactive_setupr9    s9   	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 L#mL11O 
IIIIJJJ}/77 	FJcdddJMNNN	GGGV@/J_]_```F ABBBN<000m4d;;GN=G"@&&AAA$066&L62<222MM*<U<U<[Y[\\\D '	LN:s3t99~~6666 	L 	L 	LMJLJJKKKKK	L	z	"	" 'z2&&&v(n--3  H  CDDDN>8>>#3#3444fHm,,2  G  BCCCN='--//222	GGGJ-...J()))}BEJJ C &!2TBBB 	CN0/BBB}95AA >6-=== 	>N2H===	GGGJFGGGJRSSSJUVVVJ=>>>RTYZZI e,f555*B///\]]]],g666&K!M"566<"
 
 
  	eN.R0H0HIIIM01111N.333Jcddd	GGGM=>>>JWXXXXXs   +&D D-,D-c                     t          | di           pi }t          j        d          p|                    dd          }t          j        d          p|                    dd          }t	          |o|          S )z5Check whether IRC is configured (env or config.yaml).r,   r-   r.   r   r5   r6   r  r  s       r    r   r     r   r%   c                  D   t          j        dd                                          } t          j        dd                                          }| r|sdS | |d}t          j        dd                                          }|r$	 t          |          |d<   n# t          $ r Y nw xY wt          j        dd                                          }|r||d	<   t          j        d
d                                                                          }|r|dv |d<   t          j        d          rt          j        d          |d<   t          j        d          rt          j        d          |d<   t          j        d          p|}|r|t          j        d|          d|d<   |S )u  Seed ``PlatformConfig.extra`` from env vars during gateway config load.

    Called by the platform registry's env-enablement hook (landed in the
    generic-plugin-interface migration) BEFORE adapter construction, so
    ``gateway status`` and ``get_connected_platforms()`` reflect env-only
    configuration without instantiating the IRC client.  Returns ``None``
    when IRC isn't minimally configured; the caller skips auto-enabling.

    The special ``home_channel`` key in the returned dict is handled by
    the core hook — it becomes a proper ``HomeChannel`` dataclass on the
    ``PlatformConfig`` rather than being merged into ``extra``.
    r-   r   r5   Nr  r/   r0   r2   r3   r7   >   r8   r9   r:   r;   r<   r=   r>   r?   IRC_HOME_CHANNELIRC_HOME_CHANNEL_NAME)r   re   home_channel)rO   rP   r   rR   r   rE   )r.   r6   seedr0   r3   r;   homes          r    _env_enablementrA    s    Y|R((..00Fir**0022G w t D 9Z$$**,,D 	t99DLL 	 	 	D	y,,2244H $#Zir**002288::G :!%99Y 
y&'' C"$),A"B"B	y()) G$&I.E$F$F ! 9'((3GD 
I5t<< 
  
^ Ks   B 
B$#B$r   c                 ~    |                      dd                               dd                               dd          S )a1  Strip IRC line terminators and the NUL byte from ``text``.

    IRC commands are CRLF-delimited; a bare ``\r`` or ``\n`` in user
    content lets an attacker inject arbitrary IRC commands (CTCP, JOIN,
    KICK).  ``\x00`` is a protocol-illegal byte.  Everything else is
    valid in PRIVMSG payloads.
    r   r    r   )r   r   s    r    _strip_irc_control_charsrE    s8     <<c""**455==fbIIIr%   r   c                 4    t          |           o	| d         dv S )Nr   z#&+!)r  )r   s    r    _is_irc_channelrG    s    <</F1I//r%   F)	thread_idmedia_filesforce_documentr   messagerH  rI  rJ  c          	      B  23K   t          | di           pi }t          j        d          p|                    dd          }t          j        d          p|                    dd          }|r|sddiS t          j        d	          p|                    d
d          }		 t	          |	          }
n# t
          t          f$ r
 dd|	icY S w xY wt          j        d          p|                    dd          }t          j        d          }||                                dv }n#t          |                    dd                    }t          j        d          p|                    dd          }t          j        d          p|                    dd          }|p|2t          2fddD                       rddiS 2}|
                    d          dd         pd}| ddd         }t                              |          }|rt          j                    nd}	 t          j        t          j        ||
|           d!"           d{V \  }3n-# t          j        $ r  t&          $ r}dd#| icY d}~S d}~ww xY wd$t(          d%df3fd&}d'}d(}	 |r! |d)t+          |                      d{V   |d*|            d{V   |d+| d,           d{V  t          j                    }|                                d!z   }d-}|s||                                z
  }|d'k    rcdd.i	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY w	 t          j        |                    d0          |"           d{V }n# t          j        $ rf dd.icY 	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY wt          j        $ rf dd1icY 	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY ww xY w|                    d2d34          
                    d5          }t=          |          } | d6         }!|!d7k    r.| d8         r| d8         d'         nd}" |d9|"            d{V  n|!d:k    rd}n|!d;v r|d<z  }||k    rcdd=i	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY w| d>| dd         } |d*|            d{V  nk|!d?v rgdd@|! dAi	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY w||r; |dBt+          |                      d{V  t          j        dC           d{V  tA          |          r |dD|            d{V  |                                d/z   }#d-}$|$s]|#|                                z
  }|d'k    rn>	 t          j        |                    d0          |"           d{V }n!# t          j        t          j        f$ r Y nw xY w|                    d2d34          
                    d5          }t=          |          }%|%d6         }&|&d7k    r-|%d8         r|%d8         d'         nd}" |d9|"            d{V  nu|&dEv rd}$nn|&dFv rjddG| dH|& dAi	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY w|$]tC          dI| dJ"                    d2                    dCz   }'dK|'z
  }(d-})|#                    dL          D ]x}*t+          |*          
                                }*|*s'|*rN|*"                    d2          }+tC          |+          |(k    r4 |dI| dJ|*            d{V  t          j        dM           d{V  d})nd<tC          |*          d'}.}-},|,|-k    rI|,|-z   dCz  }/tC          |*d|/         "                    d2                    |(k    r|/}.|/d<z   },n|/d<z
  }-|,|-k    I|.}0|*$                    dNd'|0          }1|1|0dOz  k    r|1}0 |dI| dJ|*d|0         
                                            d{V  t          j        dM           d{V  d})|*|0d         %                                }*|*Nz|)scddPi	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY w |dQ           d{V  	 t          j        |&                    dR          dS"           d{V  n# t          j        $ r Y nw xY wdt)          t	          t/          j                    dTz                      dU	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY w# t          j        $ r  t&          $ r}tN          (                    dVdW           ddX| icY d}~	 3                                 t          j        3                                d/"           d{V  S # t          j        t&          f$ r Y S w xY wd}~ww xY w# 	 3                                 t          j        3                                d/"           d{V  w # t          j        t&          f$ r Y w w xY wxY w)Ya  Open an ephemeral IRC connection, send a PRIVMSG, and quit.

    Used by ``tools/send_message_tool._send_via_adapter`` when the gateway
    runner is not in this process (e.g. ``hermes cron`` running as a
    separate process from ``hermes gateway``).  Without this hook,
    ``deliver=irc`` cron jobs fail with ``No live adapter for platform``.

    The standalone client uses a distinct nick suffix (``-cron``) so it
    does not collide with the long-running gateway adapter that may already
    be holding the configured nickname on the same network.  When the
    target is a channel, the client JOINs it before sending PRIVMSG so
    networks with the default ``+n`` (no external messages) channel mode
    accept the delivery.

    ``thread_id`` and ``media_files`` are accepted for signature parity but
    are not meaningful on IRC: IRC has no native thread or attachment
    primitive.
    r,   r-   r.   r   r5   r6   rz   zBIRC standalone send: IRC_SERVER and IRC_CHANNEL must be configuredr/   r0   r1   z"IRC standalone send: invalid port r2   r3   r4   r7   N>   r8   r9   r:   r;   Tr<   r=   r>   r?   c              3       K   | ]}|v V  	d S r  rB   )rF   ch
raw_targets     r    	<genexpr>z#_standalone_send.<locals>.<genexpr>   s(      
@
@2
@
@
@
@
@
@r%   )rC  r   rD  r   z<IRC standalone send: chat_id contains illegal IRC charactersz_0123456789-   z-cron   rm   g      .@ro   zIRC standalone connect failed: r   r   c                    K                        | dz                       d                                                      d {V  d S )Nr   r   )r   r   r   )r   writers    r    _rawz_standalone_send.<locals>._raw  sQ      dVm++G44555llnnr%   r      rr   rs   rt   z 0 * :Hermes Agent (cron)Fz:IRC standalone send: registration timeout (no RPL_WELCOME)g      @r   zAIRC standalone send: server closed connection during registrationr   r   r   r   r   r   r   r   r   >   432r   r   z-IRC standalone send: too many nick collisionsz-cron->   464465z-IRC standalone send: server rejected client (r*  rv   rw   rx   >   366JOIN>   403405471473474475zIRC standalone send: JOIN z rejected (r   r   r   r   r   r   r   z2IRC standalone send: empty message after strippingzQUIT :deliveredi   g       @r   r   zIRC standalone send raised)exc_infozIRC standalone send failed: ))rN   rO   rP   rQ   rR   	TypeErrorr   rE   r  anyr   r'   r   rn   r   r[   r   r   r   rU   rD   rE  get_running_loopr   r   r   r   	readuntilIncompleteReadErrorr   r!   r   rG  r   r   r   r   r   r   ry   r   )4pconfigr   rK  rH  rI  rJ  r,   r.   r6   
port_valuer0   r3   use_tls_envr;   r=   r?   r   	nick_basestandalone_nickplainr   readerr   rU  nick_attemptsmax_nick_attemptsloopdeadline
registered	remainingraw_liner   r   cmdr   join_deadlinejoinedjmsgjcmdr   r   sent_anyr   r   r   r   r   r   r   r   rO  rT  s4                                                     @@r    _standalone_sendr|    s
     6 GWb))/REY|$$?		(B(?(?Fi&&B%))Ir*B*BG _ _]^^:&&A%))FD*A*AJN:z" N N NLjLLMMMMN y((OEIIj,,O,OH)M**K##%%)==uyyD1122i 566Z%))DUWY:Z:ZO	"9::`eiiH[]_>`>` #GJ

@
@
@
@&?
@
@
@@@ YWXXF //4DI")))#2#.O&&w//E.5?c(***4G@&/#FDg>>> 
  
  
 
 
 
 
 
 
 !    @ @ @>1>>???????@        M 	L$J7HHJJKKKKKKKKKd,?,,---------dE?EEEFFFFFFFFF'))99;;%
 	Y 499;;.IA~~!]^^	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	cf!(!1&2B2B72K2KU^!_!_!_______' _ _ _!]^^^V	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	[ . f f f!deeeR	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	[foogio@@GGOOG$W--Ci.Cf}}.1(mC#h-**d-G--..........!

&&" #444#%TUz	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	{ &/"E"Em"E"Ecrc"Jd4?445555555555&&!WQT!W!W!WXn	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	k  	Y:  	#$b5MN_5`5`bbccccccccc-""""""""" 6"" 	^$'v''((((((((( IIKK#-MF ^)DIIKK7	>> %,%5f6F6Fw6O6OYb%c%c%cccccccHH,g.IJ   E"//')/DDKKFSS)'22I6>>37>Id8nQ//rG$1112222222222_,,!FFGGG#%\&%\%\UY%\%\%\]l	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	Y  ^2 ,&,,,33G<<==A(N	T** 	: 	:I0;;BBDDI  :#**733w<<9,,$?&??I??@@@@@@@@@!-,,,,,,,,,#H"#S^^Q4TTkk:!+C9TcT?11'::;;yHH"!Ag"Qw Tkk  !Q998q=(($HdOfOO	)8)0D0K0K0M0MOOPPPPPPPPPmC(((((((((%hii07799	/  :2  	SQR	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	! d$%%%%%%%%%	"6;;t#4#4cBBBBBBBBBBB# 	 	 	D	  s3ty{{T7I3J3J/K/KLL	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	 !    = = =1DAAA;;;<<<<<<	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	=	LLNNN"6#5#5#7#7EEEEEEEEEEE$i0 	 	 	D	s  B/ /C
	C
4I I=,I82I=8I=Bl 0AM33NN/O  ?l  R(l APP10P14R(l ARR$#R$'R((Bl =AV  VV/l AXX('X(+Bl /[; :l ;\l \Bl &A_))``Gl Ahh)(h),l >/i. -l .j =l ?j  5l 6Ak99lln:."n5n:n= Ann21n25n::n= =p?Appppppc                     |                      ddd t          t          t          g ddt          t
          dt          ddd	d
ddd           dS )z7Plugin entry point: called by the Hermes plugin system.r)   rd   c                      t          |           S r  )r'   )cfgs    r    <lambda>zregister.<locals>.<lambda>  s    JsOO r%   )r-   r5   r2   z&No extra packages needed (stdlib only)r<  r1  r0  rK   u   💬FTu  You are chatting via IRC. IRC does not support markdown formatting — use plain text only. Messages are limited to ~450 characters per line (long messages are automatically split). In channels, users address you by prefixing your nick. Keep responses concise and conversational.)re   labeladapter_factorycheck_fnr  r   required_envinstall_hintsetup_fnenv_enablement_fncron_deliver_env_varstandalone_sender_fnallowed_users_envallow_all_envrI   emojipii_safeallow_update_commandplatform_hintN)register_platformr  r  r   r9  rA  r|  )ctxs    r    registerr    st    33#'!BBB=" * 0 .-+!E  ( ( ( ( (r%   r  )(r  r[   loggingrO   r   rn   r   typingr   r   r   r   	getLoggerr  ry   gateway.platforms.baser   r   r	   r
   gateway.sessionr   gateway.configr   r   rD   dictr!   r$   r'   r  r  r  r9  r   rA  rE  rG  r|  r  rB   r%   r    <module>r     s   :   				 				 



  , , , , , , , , , , , ,		8	$	$            * ) ) ) ) ) 3 3 3 3 3 3 3 3DC DD D D D D6=# =# = = = =\) \) \) \) \)$ \) \) \)F
$D 
$ 
$ 
$ 
$$t $ $ $ $hY hY hY hYV$D $ $ $ $0 0 0 0 0fJ3 J3 J J J J0C 0D 0 0 0 0  $'+ O O OO O
 }O $s)$O O 
#s(^O O O Od* * * * *r%   