
    i                        U 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mZ ddlmZ  ej        e          ZdZdZdZdZd	Zd
ee         fdZ	 	 	 dxdedededee
eef                  dee
ee	f                  ded
e	fdZ G d de          Zddddddddddd 
Zd!ed
efd"Zi a e
ee
ee	f         f         e!d#<   d$d%ded&e"d
e
ee	f         fd'Z#dyd(Z$ded)e	d
efd*Z%ded+ed)e	d
efd,Z&ded+ed)e	d
efd-Z'ded.ed)e	d
efd/Z(ded+ed)e	d
efd0Z)ded+ed1ed)e	d
ef
d2Z*dzded+ed4ed5ed)e	d
efd6Z+	 	 d{ded.ed5ed8ee         d9ee         d)e	d
efd:Z,ded.ed)e	d
efd;Z-ded.ed<ed)e	d
ef
d=Z.ded.ed<ed)e	d
ef
d>Z/	 	 d|ded.ed@ed<ee         dAed)e	d
efdBZ0ded+ed1edCed)e	d
efdDZ1ded+ed1edCed)e	d
efdEZ2e%e&e'e(e)e*e+e,e-e.e/e0e1e2dFZ3 e4h dG          Z5 e4e36                                          e5z
  Z7dH e38                                D             Z9dI e38                                D             Z:g dJZ;eeeeef                  e!dK<    e4dLdMh          Z<d+gd+gd+gd+d1gd+d4gd.gd.gd.gd.d<gd.d<gd.d@gg dNg dNdOZ=e
eee         f         e!dP<   d
eee                  fdQZ>dRe
ee	f         dSeee                  d
ee         fdTZ?	 	 d}dVee         dRee
ee	f                  dWed
ee
ee	f                  fdXZ@dYe
ee	f         dWed
ee
ee	f                  fdZZAd
ee
ee	f                  fd[ZBd
ee
ee	f                  fd\ZCd
ee
ee	f                  fd]ZDd^d_d`dadbdcdcdddedfdg
ZEdheded
efdiZFd
e"fdjZG	 	 	 	 	 	 	 	 	 	 	 d~dhedle
ee	f         dmed+ed.ed1edCed<ed4ed@ed5ed8ed9edAed
efdnZHdhed
efdoZIdhed
efdpZJdkdkdkdkdkdkdkdkd7dkdkd?dqZKdr ZL e@ eMe96                                          dsd$idUt          ZN e@ eMe:6                                          dsd$idut          ZO ejP        dUdUeN eLeI          eGdvgw            ejP        dudueO eLeJ          eGdvgw           dS )u  Discord server introspection and management tool.

Provides the agent with the ability to interact with Discord servers
when running on the Discord gateway. Uses Discord REST API directly
with the bot token — no dependency on the gateway adapter's client.

Only included in the hermes-discord toolset, so it has zero cost
for users on other platforms.

The schema exposed to the model is filtered by two gates:

1. Privileged intents detected from GET /applications/@me at schema
   build time. Actions that require an intent the bot doesn't have
   (search_members / member_info → GUILD_MEMBERS intent) are hidden.
   fetch_messages is kept regardless of MESSAGE_CONTENT intent, but
   its description is annotated when the intent is missing.

2. User config allowlist at ``discord.server_actions``. If the user
   sets a comma-separated list (or YAML list) of action names, only
   those appear in the schema. Empty/unset means all intent-available
   actions are exposed.

Per-guild permissions (MANAGE_ROLES etc.) are NOT pre-checked — Discord
returns a 403 at call time and :func:`_enrich_403` maps it to
actionable guidance the model can relay to the user.
    N)AnyDictListOptionalTuple)registryzhttps://discord.com/api/v10i @  i   i   i   returnc                  T    t          j        dd                                          pdS )z/Resolve the Discord bot token from environment.DISCORD_BOT_TOKEN N)osgetenvstrip     7/home/ubuntu/.hermes/hermes-agent/tools/discord_tool.py_get_bot_tokenr   5   s&    9("--3355==r      methodpathtokenparamsbodytimeoutc           	      
   t            | }|r%|dt          j                            |          z   z  }d}|'t	          j        |                              d          }t          j                            ||| d| ddd          }	 t          j        	                    ||	          5 }	|	j
        d
k    r	 ddd           dS t	          j        |	                                                    d                    cddd           S # 1 swxY w Y   dS # t          j        j        $ rX}
d}	 |
                                                    dd          }n# t           $ r Y nw xY wt#          |
j        |          |
d}
~
ww xY w)z'Make a request to the Discord REST API.?Nzutf-8zBot zapplication/jsonz;Hermes-Agent (https://github.com/NousResearch/hermes-agent))AuthorizationzContent-Typez
User-Agent)datar   headersr      r   replace)errors)DISCORD_API_BASEurllibparse	urlencodejsondumpsencoderequestRequesturlopenstatusloadsreaddecodeerror	HTTPError	ExceptionDiscordAPIErrorcode)r   r   r   r   r   r   urlr   reqrespe
error_bodys               r   _discord_requestr<   :   s    
%t
%
%C 4sV\++F3333Dz$&&w//
.
 
 +E^^.W
 
	 ! 	 	C9^##C#99 	;T{c!!	; 	; 	; 	; 	; 	; 	; 	; :diikk0099::	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; 	; <! 9 9 9
	CCJJ 	 	 	D	afj11q89sl   	!D *D	7D 8D	<D 	DD DD F*E=-)EE=
E$!E=#E$$E==Fc                   ,     e Zd ZdZdedef fdZ xZS )r5   z%Raised when a Discord API call fails.r.   r   c                 r    || _         || _        t                                          d| d|            d S )NzDiscord API error z: )r.   r   super__init__)selfr.   r   	__class__s      r   r@   zDiscordAPIError.__init__f   s@    	>f>>>>?????r   )__name__
__module____qualname____doc__intstrr@   __classcell__)rB   s   @r   r5   r5   d   s]        //@s @# @ @ @ @ @ @ @ @ @ @r   r5   textvoicecategoryannouncementannouncement_threadpublic_threadprivate_threadstageforummedia)
r            
            r      type_idc                 @    t                               | d|  d          S )Nzunknown())_CHANNEL_TYPE_NAMESget)r\   s    r   _channel_type_namera   ~   s$    ""7,Aw,A,A,ABBBr   _capability_cacheF)forcerc   c                   | t           v r|st           |          S dddd}	 t          dd| d          }t          |                    dd	          pd	          }t	          |t
          t          z  z            |d
<   t	          |t          t          z  z            |d<   d|d<   n2# t          $ r%}t                              d|           Y d}~nd}~ww xY w|t           | <   |S )a  Detect the bot's app-wide capabilities via GET /applications/@me.

    Returns a dict with keys:

    - ``has_members_intent``: GUILD_MEMBERS intent is enabled
    - ``has_message_content``: MESSAGE_CONTENT intent is enabled
    - ``detected``: detection succeeded (False means exposing everything
      and letting runtime errors handle it)

    Cached in a module-global. Pass ``force=True`` to re-fetch.
    TF)has_members_intenthas_message_contentdetectedGETz/applications/@merV   r    flagsr   re   rf   rg   z?Discord capability detection failed (%s); exposing all actions.N)rb   r<   rG   r`   bool_FLAG_GATEWAY_GUILD_MEMBERS#_FLAG_GATEWAY_GUILD_MEMBERS_LIMITED_FLAG_GATEWAY_MESSAGE_CONTENT%_FLAG_GATEWAY_MESSAGE_CONTENT_LIMITEDr4   loggerinfo)r   rc   capsappri   excs         r   _detect_capabilitiesrt      s1    !!!%! '' ## D
u&95!LLLCGGGQ'',1--%)03VVW&
 &
!" '+25ZZ['
 '
"#  Z 
 
 
Ms	
 	
 	
 	
 	
 	
 	
 	


  $eKs   BB" "
C,CCc                  
    i a dS )z%Test hook: clear the detection cache.N)rb   r   r   r   _reset_capability_cacherv      s     r   _kwargsc           
      <   t          dd|           }g }|D ]c}|                    |d         |d         |                    d          |                    dd          |                    d          d	           dt          j        |t          |          d
          S )z'List all guilds the bot is a member of.rh   z/users/@me/guildsidnameiconownerFpermissions)ry   rz   r{   r|   r}   )guildscount)r<   appendr`   r(   r)   len)r   rw   r~   resultgs        r   _list_guildsr      s    e%8%@@FF  D'fIEE&MMUU7E**55//
 
 	 	 	 	 :#f++>>???r   guild_idc                    t          dd| | ddi          }t          j        |d         |d         |                    d          |                    d	          |                    d
          |                    d          |                    d          |                    dg           |                    d          |                    d          |                    d          d          S )z'Get detailed information about a guild.rh   /guilds/with_countstruer   ry   rz   descriptionr{   owner_idapproximate_member_countapproximate_presence_countfeaturespremium_tierpremium_subscription_countverification_level)ry   rz   r   r{   r   member_countonline_countr   r   r   r   r<   r(   r)   r`   )r   r   rw   r   s       r   _server_infor      s     58 5 5umU[E\]]]A:g&	uu]++fEE*%%899:;;EE*b))n--&'ee,H&I&Iee$899    r   c           
         t          dd| d|           }i }g }|D ]>}|d         dk    r0|d         |d         |                    dd	          g d
||d         <   ?|D ]}|d         dk    r|d         |                    dd          t          |d                   |                    dd	          |                    d          |                    dd          d}|                    d          }|r&||v r"||         d                             |           |                    |           t	          |                                d           }	|	D ]}
|
d                             d             |                    d            g }|r|                    d|d           |	D ]/}
|                    |
d         |
d         d|
d         d           0t          d |D                       }t          j	        ||d          S )z4List all channels in a guild, organized by category.rh   r   z	/channelstyperU   ry   rz   positionr   )ry   rz   r   channelsr   topicnsfwF)ry   rz   r   r   r   r   	parent_idr   c                     | d         S Nr   r   cs    r   <lambda>z _list_channels.<locals>.<lambda>  s
    AjM r   )keyc                     | d         S r   r   r   s    r   r   z _list_channels.<locals>.<lambda>  s
    1Z= r   c                     | d         S r   r   r   s    r   r   z _list_channels.<locals>.<lambda>  s
    Qz] r   N)rL   r   )ry   rz   c              3   @   K   | ]}t          |d                    V  dS )r   N)r   ).0groups     r   	<genexpr>z!_list_channels.<locals>.<genexpr>  s/      ;;5E*%&&;;;;;;r   )channel_groupstotal_channels)
r<   r`   ra   r   sortedvaluessortsumr(   r)   )r   r   rw   r   
categoriesuncategorizedchentryparentsorted_catscatr   totals                r   _list_channelsr      s}   'E('E'E'EuMMH 79J*,M   f:??h6
FF:q11	$ $Jr$x   ( (f:??T(FF62&&&r&z22z1--VVG__FF65))
 
 $$ 	(f
**vz*11%8888  '''' **,,2I2IJJJK : :J!8!8999922333#%F E4]CCDDD  "4y#f+>>J
 
 	 	 	 	
 ;;F;;;;;E:5IIJJJr   
channel_idc                    t          dd| |           }t          j        |d         |                    d          t	          |d                   |                    d          |                    d          |                    dd	          |                    d
          |                    d          |                    dd          |                    d          d
          S )z+Get detailed info about a specific channel.rh   
/channels/ry   rz   r   r   r   r   Fr   r   rate_limit_per_userr   last_message_id)
ry   rz   r   r   r   r   r   r   r   r   )r<   r(   r)   r`   ra   )r   r   rw   r   s       r   _channel_infor     s    	%!:j!:!:E	B	BB:hv"2f:..FF:&&vu%%FF:&&VVK((!vv&;Q??66"344    r   c                    t          dd| d|           }g }t          |d d          D ]}|                    |d         |d         |                    d	          rd
|                    d	d          dnd|                    dd          |                    dd          |                    dd          |                    d          |                    dd          d           t	          j        |t          |          d          S )zList all roles in a guild.rh   r   z/rolesc                 .    |                      dd          S )Nr   r   r`   )rs    r   r   z_list_roles.<locals>.<lambda>*  s    z1)=)= r   T)r   reversery   rz   color#r   06xNr   mentionableFmanagedr   hoist)ry   rz   r   r   r   r   r   r   )rolesr   )r<   r   r   r`   r(   r)   r   )r   r   rw   r   r   r   s         r   _list_rolesr   &  s   U$?x$?$?$?GGEFE==tLLL 
 
D'fI45EE'NNL0w**0000j!,,5566uuY..EE.11UU7E**	
 	
 		 		 		 		 :V==>>>r   user_idc                    t          dd| d| |           }|                    di           }t          j        |                    d          |                    d          |                    d          |                    d          |                    d	          |                    d
d          |                    dg           |                    d          |                    d          d	          S )z'Get info about a specific guild member.rh   r   	/members/userry   usernameglobal_namenickavatarbotFr   	joined_atpremium_since)	r   r   display_namenicknamer   r   r   r   r   )r<   r`   r(   r)   )r   r   r   rw   mr   s         r   _member_infor   8  s     G8 G Gg G GOOA55D:88D>>HHZ((//EE&MM((8$$xxu%%w##UU;''//
 
 
 
 
r      querylimitc                 f   	 t          |          }n# t          t          f$ r d}Y nw xY w|t          t	          |d                    d}t          dd| d| |          }g }|D ]}|                    di           }	|                    |	                    d	          |	                    d
          |	                    d          |                    d          |	                    dd          |                    dg           d           t          j	        |t          |          d          S )z!Search for guild members by name.r   d   )r   r   rh   r   z/members/searchr   r   ry   r   r   r   r   Fr   )r   r   r   r   r   r   )membersr   rG   	TypeError
ValueErrorrH   minr<   r`   r   r(   r)   r   )
r   r   r   r   rw   r   r   r   r   r   s
             r   _search_membersr   I  sC   E

z"   s3uc??';';<<Fu&J&J&J&JEZ`aaaGF 	 	uuVR  xx~~,, HH]33f88E5))UU7B''
 
 	 	 	 	 :&3v;;??@@@    ((2   beforeafterc                    	 t          |          }n# t          t          f$ r d}Y nw xY wdt          t	          |d                    i}|r||d<   |r||d<   t          dd| d| |	          }g }|D ]0}	|	                    d
i           }
|                    |	d         |	                    dd          |
                    d          |
                    d          |
                    d          |
                    dd          d|	                    d          |	                    d          d |	                    dg           D             |	                    d          r d |	                    dg           D             ng |	                    dd          d           2t          j	        |t          |          d          S )z%Fetch recent messages from a channel.r   r   r   r   r   rh   r   z	/messagesr   authorry   contentr   r   r   r   F)ry   r   r   r   	timestampedited_timestampc                     g | ]A}|                     d           |                     d          |                     d          dBS )filenamer7   size)r   r7   r   r   )r   as     r   
<listcomp>z#_fetch_messages.<locals>.<listcomp>}  sS        UU:..quuU||QUUSY]][[  r   attachments	reactionsc                     g | ]B}|                     d i                                d          |                     dd          dCS )emojirz   r   r   )r   r   r   )r   r   s     r   r   z#_fetch_messages.<locals>.<listcomp>  s[        %%,,0088155RSCTCTUU  r   pinned)ry   r   r   r   r   r   r   r   )messagesr   r   )r   r   r   r   r   rw   r   r   r   msgr   s              r   _fetch_messagesr   _  s   E

z"   %s3uc??';';<F "!x  w'IJ'I'I'I5Y_```HF  2&&d)wwy"--jj&&"JJz22 &

= 9 9zz%//	  -- #(: ; ; 33   %%.  b11    ,.ggh..'
 
 	 	 	 	* :6CKK@@AAAr   c           	      r   t          dd| d|           }g }|D ]z}|                    di           }|                    |d         |                    dd          dd	         |                    d
          |                    d          d           {t          j        |t          |          d          S )z"List pinned messages in a channel.rh   r   z/pinsr   ry   r   r   N   r   r   )ry   r   r   r   )pinned_messagesr   )r<   r`   r   r(   r)   r   )r   r   rw   r   r   r   r   s          r   
_list_pinsr     s    'EJ'E'E'EuMMHF  2&&d)wwy"--dsd3jj,,--	
 
 	 	 	 	 :&3v;;GGHHHr   
message_idc                 f    t          dd| d| |            t          j        dd| dd          S )zPin a message in a channel.PUTr   /pins/TMessage z pinned.successmessager<   r(   r)   r   r   r   rw   s       r   _pin_messager
    sI    UGGG:GGOOO:$3Rj3R3R3RSSTTTr   c                 f    t          dd| d| |            t          j        dd| dd          S )zUnpin a message from a channel.DELETEr   r  Tr  z
 unpinned.r  r  r	  s       r   _unpin_messager    sI    XJJJJjJJERRR:$3Tj3T3T3TUUVVVr     rz   auto_archive_durationc                     |rd| d| d}||d}nd| d}||dd}t          d|| |          }t          j        d	|d
         |                    d          d          S )zCreate a thread in a channel.r   z
/messages/z/threads)rz   r  rX   )rz   r  r   POST)r   Try   rz   )r  	thread_idrz   r   )	r   r   rz   r   r  rw   r   r   threads	            r   _create_threadr    s      
FJFF*FFF%: 
  
 1J000%:
 

 fdE===F:D\

6""    r   role_idc           	      r    t          dd| d| d| |            t          j        dd| d| dd	          S )
zAdd a role to a guild member.r  r   r   /roles/TRole z added to user .r  r  r   r   r   r  rw   s        r   	_add_roler    s[    USxSS'SS'SSUZ[[[:$3]73]3]SZ3]3]3]^^___r   c           	      r    t          dd| d| d| |            t          j        dd| d| dd	          S )
z"Remove a role from a guild member.r  r   r   r  Tr  z removed from user r  r  r  r  s        r   _remove_roler    s[    XV(VVWVVWVVX]^^^:$3a73a3aW^3a3a3abbcccr   )list_guildsserver_infolist_channelschannel_info
list_rolesmember_infosearch_membersfetch_messages	list_pinspin_messageunpin_messagecreate_threadadd_roleremove_role>   r)  r%  r$  c                 ,    i | ]\  }}|t           v ||S r   )_CORE_ACTION_NAMESr   kvs      r   
<dictcomp>r1    s)    NNN$!Qa;M6M6MA6M6M6Mr   c                 ,    i | ]\  }}|t           v ||S r   )_ADMIN_ACTION_NAMESr.  s      r   r1  r1    s)    PPP41aq<O7O7O!Q7O7O7Or   ))r  z()zlist servers the bot is in)r  
(guild_id)zserver details + member counts)r   r4  z all channels grouped by category)r!  (channel_id)zsingle channel details)r"  r4  zroles sorted by position)r#  z(guild_id, user_id)zlookup a specific member)r$  z(guild_id, query)zfind members by name prefix)r%  r5  z1recent messages; optional before/after snowflakes)r&  r5  zpinned messages in a channel)r'  (channel_id, message_id)zpin a message)r(  r6  zunpin a message)r)  z(channel_id, name)z2create a public thread; optional message_id anchor)r*  (guild_id, user_id, role_id)zassign a role)r+  r7  zremove a role_ACTION_MANIFESTr#  r$  )r   r   r  )r  r   r"  r#  r$  r!  r%  r&  r'  r(  r)  r*  r+  _REQUIRED_PARAMSc                     	 ddl m}   |             }n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY w|                    d          pi                     d          }||dk    rdS t          |t                    r d |                    d	          D             }nXt          |t          t          f          rd
 |D             }n/t                              dt          |          j                   dS d |D             }d |D             }|rYt                              dd                    |          d                    t                                                               |S )a/  Read ``discord.server_actions`` from user config.

    Returns a list of allowed action names, or ``None`` if the user
    hasn't restricted the set (default: all actions allowed).

    Accepts either a comma-separated string or a YAML list.
    Unknown action names are dropped with a log warning.
    r   )load_configz:discord: could not load config (%s); allowing all actions.Ndiscordserver_actionsr   c                 ^    g | ]*}|                                 |                                 +S r   )r   r   ns     r   r   z0_load_allowed_actions_config.<locals>.<listcomp>,  s-    @@@qaggii@@@@r   ,c                     g | ]D}t          |                                          #t          |                                          ES r   )rH   r   r?  s     r   r   z0_load_allowed_actions_config.<locals>.<listcomp>.  s9    ???AA?Q???r   z5discord.server_actions: unexpected type %s; ignoring.c                 $    g | ]}|t           v |S r   _ACTIONSr?  s     r   r   z0_load_allowed_actions_config.<locals>.<listcomp>5  s    ///1hQr   c                 $    g | ]}|t           v|S r   rD  r?  s     r   r   z0_load_allowed_actions_config.<locals>.<listcomp>6  s"    555Q1H#4#4q#4#4#4r   z@discord.server_actions: unknown action(s) ignored: %s. Known: %s, )hermes_cli.configr;  r4   ro   debugr`   
isinstancerH   splitlisttuplewarningr   rC   joinrE  keys)r;  cfgrs   rawnamesvalidinvalids          r   _load_allowed_actions_configrV    s   111111kmm   QSVWWWttttt 779#
(
()9
:
:C
{cRiit#s @@CIIcNN@@@	C$	'	' ?????CT#YYEW	
 	
 	
 t/////E55%555G 
IIg		(--// : :	
 	
 	

 Ls    
A>Arq   	allowlistc                     g }t           D ]>}|                     dd          s
|t          v r"|||vr)|                    |           ?|S )zCompute the visible action list from intents + config allowlist.

    Preserves the canonical order from :data:`_ACTIONS`.
    re   T)rE  r`   _INTENT_GATED_MEMBERSr   )rq   rW  actionsrz   s       r   _available_actionsr[  @  sj     G  xx,d33 	@U8U8U T%:%:tNr   r<  rZ  	tool_namec                     |pi } sdS  fdt           D             }d                    |          }d}ddht                     z  }|rT|                    d          r?|                    d          d	u r(d
                    t	          |                    }d| d}|dk    r	d| d| }nd| d| }d ddddddddddddddddddddddddddd dd!ddd"ddg d#d$d%d&}	||d'|	d(gd)d*S )+u   Build the tool schema for the given filtered action list.

    Returns ``None`` when *actions* is empty — callers should drop the
    tool from registration in that case.
    Nc                 4    g | ]\  }}}|v 
d | | d| S )z  u     — r   )r   rz   sigdescrZ  s       r   r   z!_build_schema.<locals>.<listcomp>g  sE       D#t7?? 	%T$3$$d$$??r   
r   r%  r&  rg   rf   Fz and zA

NOTE: Bot does NOT have the MESSAGE_CONTENT privileged intent. z will return message metadata (author, timestamps, attachments, reactions, pin state) but `content` will be empty for messages not sent as a direct mention to the bot or in DMs. Enable the intent in the Discord Developer Portal to see all content.discord_adminz>Manage a Discord server via the REST API.

Available actions:
z

Call list_guilds first to discover guild_ids, then list_channels for channel_ids. Runtime errors will tell you if the bot lacks a specific per-guild permission (e.g. MANAGE_ROLES for add_role).z>Read and participate in a Discord server.

Available actions:
zr

Use the channel_id from the current conversation context. Use search_members to look up user IDs by name prefix.string)r   enumzDiscord server (guild) ID.)r   r   zDiscord channel ID.zDiscord user ID.zDiscord role ID.zDiscord message ID.z2Member name prefix to search for (search_members).z New thread name (create_thread).integer   r   zDMax results (default 50). Applies to fetch_messages, search_members.)r   minimummaximumr   z5Snowflake ID for reverse pagination (fetch_messages).z5Snowflake ID for forward pagination (fetch_messages).)<   r  i  i`'  zAThread archive duration in minutes (create_thread, default 1440).)r   rd  r   actionr   r   r   r  r   r   rz   r   r   r   r  objectrk  )r   
propertiesrequired)rz   r   
parameters)r8  rO  setr`   r   )
rZ  rq   r\  manifest_linesmanifest_blockcontent_noteaffected_actionsrS  r   rm  s
   `         r   _build_schemaru  X  sZ    :2D t   /  N
 YY~..NL(+6WE 
DHHZ00 
TXX>S5T5TX]5]5]V$45566TT T T 	 O##    	 
   	 
 

 7
 

 0
 

 -
 

 -
 

 0
 

 O
 

 =
 

 a	
 
 R
 

 R
 

 +++^"
 "
_4" 4"Jn "$!

 
  r   action_subsetc                      t                      }|sdS t          |          }t                      } fdt          ||          D             }|sdS t	          |||          S )zHBuild a dynamic schema for *action_subset* filtered by intents + config.Nc                     g | ]}|v |	S r   r   )r   r   rv  s     r   r   z'_get_dynamic_schema.<locals>.<listcomp>  s#    TTTQmASASqASASASr   )r\  )r   rt   rV  r[  ru  )rv  r\  r   rq   rW  rZ  s   `     r   _get_dynamic_schemary    s{    
 E t&&D,..ITTTT,T9==TTTG t$)<<<<r   c                  ,    t          t          d          S Nr<  )ry  _CORE_ACTIONSr   r   r   get_dynamic_schema_corer}    s    }i888r   c                  ,    t          t          d          S )Nrb  )ry  _ADMIN_ACTIONSr   r   r   get_dynamic_schema_adminr    s    ~???r   c                      t                      S )u0   Backward-compat wrapper — returns core schema.)r}  r   r   r   get_dynamic_schemar    s    "$$$r   zBot lacks MANAGE_MESSAGES permission in this channel. Ask the server admin to grant the bot a role that has MANAGE_MESSAGES, or a per-channel overwrite.z5Bot lacks MANAGE_MESSAGES permission in this channel.zCBot lacks CREATE_PUBLIC_THREADS in this channel, or cannot view it.zEither the bot lacks MANAGE_ROLES, or the target role sits higher than the bot's highest role. Roles can only be assigned below the bot's own position in the role hierarchy.z^Either the bot lacks MANAGE_ROLES, or the target role sits higher than the bot's highest role.zLBot cannot view this channel (missing VIEW_CHANNEL or READ_MESSAGE_HISTORY).z4Bot cannot view this channel (missing VIEW_CHANNEL).u|   Likely missing the Server Members privileged intent — enable it in the Discord Developer Portal under your bot's settings.z]Bot cannot see this guild member (missing Server Members intent or insufficient permissions).)
r'  r(  r)  r*  r+  r%  r&  r!  r$  r#  rk  c                 l    t                               |           }d|  d}|r| d| d| dS | d| dS )z?Return a user-friendly guidance string for a 403 on ``action``.z Discord API 403 (forbidden) on 'z'. z (Raw: r^   )_ACTION_403_HINTr`   )rk  r   hintbases       r   _enrich_403r    sc    ''D8f888D .----d----""4""""r   c                  8    t          t                                S )z>Tool is available only when a Discord bot token is configured.)rj   r   r   r   r   check_discord_tool_requirementsr  &  s      !!!r   r   valid_actions
tool_labelc                     t                      }|st          j        ddi          S |                    |           }|s9t          j        d|  t	          |                                          d          S t                      }|7| |vr3t          j        dd|  d|rd                    |          nd	 i          S |||||||	d
fdt                              | g           D             }|r/t          j        dd|  dd                    |           i          S 	  |||||||||	|
|||          S # t          $ r}t                              d|| |           |j        dk    r.t          j        dt          | |j                  i          cY d}~S t          j        dt          |          i          cY d}~S d}~wt           $ r?}t                              d||            t          j        dd| i          cY d}~S d}~ww xY w)z,Shared handler logic for both discord tools.r2   z!DISCORD_BOT_TOKEN not configured.zUnknown action: )r2   available_actionsNzAction 'z;' is disabled by config (discord.server_actions). Allowed: rG  z<none>)r   r   r   r  r   r   rz   c                 >    g | ]}                     |          |S r   r   )r   p
local_varss     r   r   z'_run_discord_action.<locals>.<listcomp>a  s+    TTTQ*..QRBSBSTqTTTr   z!Missing required parameters for 'z': )r   r   r   r   r  r   r   rz   r   r   r   r  z'Discord API error in %s action '%s': %si  z"Unexpected error in %s action '%s'zUnexpected error: )r   r(   r)   r`   rL  rP  rV  rO  r9  r5   ro   rN  r.   r  r   rH   r4   	exception)rk  r  r  r   r   r   r  r   r   rz   r   r   r   r  r   	action_fnrW  missingr:   r  s                      @r   _run_discord_actionr  /  s   " E Jz7$GHIII!!&))I z000!%m&8&8&:&:!;!;
 
   	 -..Iy!8!8zN6 N N4=KDIIi0008N N
   	    J UTTT*..vr::TTTG zXXXDIIgDVDVXX
   	?y!!"7
 
 
 	
  - - -@*fVWXXX8s??:wFAF(C(CDEEEEEEEEz7CFF+,,,,,,,, ? ? ?=z6RRRz7$<$<$<=>>>>>>>>?s=   "D9 9
HAGH"G;HH4HHHc                 *    t          | t          dfi |S )zNExecute a core Discord action (fetch_messages, search_members, create_thread).r<  )r  r|  rk  kwargss     r   discord_corer    s    v}iJJ6JJJr   c                 *    t          | t          dfi |S )z3Execute a Discord admin action (server management).rb  )r  r  r  s     r   discord_admin_handlerr    s    v~QQ&QQQr   rj  c                       fdS )zBCreate a registry-compatible handler lambda for a discord handler.c                 \      di  fdt                                           D             S )Nc                 D    i | ]\  }}|                     ||          S r   r   )r   r/  r0  argss      r   r1  z3_make_handler.<locals>.<lambda>.<locals>.<dictcomp>  s+    
C
C
CA1dhhq!nn
C
C
Cr   r   )_HANDLER_DEFAULTSitems)r  kw
handler_fns   ` r   r   z_make_handler.<locals>.<lambda>  sB    jj  
C
C
C
C):)@)@)B)B
C
C
C  r   r   )r  s   `r   _make_handlerr    s        r   rg   )rq   r\  rb  r   )rz   toolsetschemahandlercheck_fnrequires_env)NNr   )r	   N)r   )r   NN)Nr  r{  )r   r   r   r   r   r   r   r   r   r   r  )QrF   r(   loggingr   urllib.errorr%   urllib.parseurllib.requesttypingr   r   r   r   r   tools.registryr   	getLoggerrC   ro   r$   rk   rl   rm   rn   rH   r   rG   r<   r4   r5   r_   ra   rb   __annotations__rj   rt   rv   r   r   r   r   r   r   r   r   r   r
  r  r  r  r  rE  	frozensetr-  rP  r3  r  r|  r  r8  rY  r9  rV  r[  ru  ry  r}  r  r  r  r  r  r  r  r  r  r  rL  _STATIC_CORE_SCHEMA_STATIC_ADMIN_SCHEMAregisterr   r   r   <module>r     s    6   				             3 3 3 3 3 3 3 3 3 3 3 3 3 3 # # # # # #		8	$	$0  & &- # ' (/ %> > > > > (,%)'9 '9'9
'9 '9 T#s(^$	'9
 4S>
"'9 '9 	'9 '9 '9 '9T@ @ @ @ @i @ @ @   C C C C C C 02 4T#s(^+, 1 1 1 7< & & & &t &S#X & & & &R   @ @ @ @ @ @ @ s s s    $4K# 4K 4K 4K 4K 4K 4K 4Kn # # #    "?s ?c ?c ?c ? ? ? ?$ s S S S    "A A3 A# Ac A# AWZ A_b A A A A. /19=(B (B(B(B(+(BSM(B)1#(B (B 		(B (B (B (BVIc Is Is Is I I I IU U U# U# URU U U U UW# W3 WC WC WTW W W W W !%!% '*  	
 	   <`S `C `# ` `PS `X[ ` ` ` `d ds dS d3 dSV d[^ d d d d  #!%%## " YTTTUU i003EE NN(.."2"2NNNPP8>>#3#3PPP
0 0 0 $uS#s]+,   $ "	=2B"CDD  < \,	*!7+!N#n ,/"L1"F+222555* * $sDI~&   *&htCy&9 & & & &R
sCx.S	" 
#Y   4 &*t t#Yt
4S>
"t t d38n	t t t tn=S>== d38n= = = = 9$sCx.!9 9 9 9 9@(4S>": @ @ @ @%HT#s(^4 % % % %	&
 	@ 	N	4
	' 	W 	W 	?	>	%G& & R# #3 #3 # # # #" " " " " !%N? N?N?S>N? N? 	N?
 N? N? N? N? N? N? N? N? N? N? 	N? N? N? N?bK K3 K K K K
R# RC R R R R brb"2T     $mD				j%%8I    %}D				z5&9_     	M,'',%&     	M/00,%&     r   