
    o;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Zddlm	Z	m
Z
 ddlmZmZmZmZmZ 	 ddlmZ ddlmZ ddlmZ ddlZdZn# e$ r d	ZeZeZeZY nw xY wddlZdd
lmZ ej                            d e  ee!          "                                j#        d                              ddl$m%Z%m&Z& ddl'm(Z( ddl)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3  ej4        e5          Z6 ej7        dd          Z8ej7        ee                   e9d<   e	 G d d                      Z:de;fdZ<de=de fdZ>d!de=de?de fdZ@dedee          ddfdZAdZBdee          fdZC G d d e*          ZDdS )"z
Slack platform adapter.

Uses slack-bolt (Python) with Socket Mode for:
- Receiving messages from channels and DMs
- Sending responses back
- Handling slash commands
- Thread support
    N)	dataclassfield)DictOptionalAnyTupleList)AsyncApp)AsyncSocketModeHandler)AsyncWebClientTF)Path   )PlatformPlatformConfig)MessageDeduplicator)
BasePlatformAdapterMessageEventMessageTypeProcessingOutcome
SendResultSUPPORTED_DOCUMENT_TYPESis_host_excluded_by_no_proxyresolve_proxy_urlsafe_url_for_logcache_document_from_bytes_slash_user_id)defaultc                   f    e Zd ZU dZeed<    eej                  Z	e
ed<   dZeed<   dZeed<   d	S )
_ThreadContextCachez'Cache entry for fetched thread context.content)default_factory
fetched_atr   message_count parent_textN)__name__
__module____qualname____doc__str__annotations__r   time	monotonicr"   floatr#   intr%        </home/ubuntu/.hermes/hermes-agent/gateway/platforms/slack.pyr   r   B   s`         11LLLdn===J===M3Kr1   r   returnc                      t           S )z*Check if Slack dependencies are available.)SLACK_AVAILABLEr0   r1   r2   check_slack_requirementsr6   K   s    r1   blocksc           	      ^   | sdS g dt           dt          fdddt          dt          dt          dd	ffd
ddt           dt          dt          dd	ffd| D ]<}|pi                     d          dk    r |                    dg                      =d                              S )au  Extract readable text from Slack Block Kit blocks, including quoted/forwarded content.

    Slack's modern WYSIWYG composer sends messages with a ``blocks`` array
    containing ``rich_text`` elements. When a user forwards or quotes another
    message, the quoted content appears as nested ``rich_text_quote`` elements
    that are *not* included in the plain ``text`` field of the event.

    This helper walks the rich-text tree recursively and returns readable lines,
    preserving quotes, list items, and preformatted blocks so the agent can see
    forwarded/quoted content instead of only the lossy plain-text field.
    r$   elementsr3   c                    g }| D ]}|                     dd          }|dk    r*|                    |                     dd                     I|dk    rJ|                     dd          }|                     dd          p|}|                    | d| d           |dk    r.|                    d	|                     d
d           d           |dk    r/|                    d|                     dd           d           |dk    r/|                    d|                     dd           d           7|dk    r/|                    d|                     dd           d           l|dk    r/|                    d|                     dd           d           |dk    r)|                    |                     dd                     d                    |          S )z@Render inline elements (text, link, channel, user, emoji, etc.).typer$   textlinkurlz ()channelz<#
channel_id>user<@user_id	usergroupz
<!subteam^usergroup_idemoji:name	broadcastz<!rangeheredatefallback)getappendjoin)r9   pieceselel_typer>   r<   s         r2   _render_inline_elementsz@_extract_text_from_slack_blocks.<locals>._render_inline_elementsa   s    	6 	6BffVR((G&  bffVR001111F""ffUB''vvfb))0S/////0000I%%>266,#;#;>>>????F"";266)R#8#8;;;<<<<K''H266."+E+EHHHIIIIG##7"&&"4"47778888K''=266'6#:#:===>>>>F""bffZ44555wwvr1   r   r<   quote_depthbulletNc                     | r|                                  sd S |rd|z  dz   nd}                    | | |                                             d S )NrB    r$   )striprQ   rstrip)r<   rW   rX   prefixpartss       r2   _append_linez5_extract_text_from_slack_blocks.<locals>._append_linez   sl     	4::<< 	F0;C3$++....557788888r1   c           	         | D ]}|                     dd          }|dk    r,  |                     dg                     ||           K|dk    r% |                     dg           |dz              v|d	k    r^|                     d
          }t          |                     dg                     D ]$\  }}|dk    rdn|dz    d} |g||           %|dk    rg }	|                     dg           D ]a}
|
                     dd          }|dk    r  |
                     dg                     }n |
g          }|r|	                    |           bd                    |	          }|r+|                     dd          } d| d| d||            |g          }|r |||           d S )Nr;   r$   rich_text_sectionr9   )rW   rX   rich_text_quote   )rW   rich_text_liststylerX   u   • . rich_text_preformatted
languagez```
```)rP   	enumeraterQ   rR   )r9   rW   rX   elem	elem_type
list_styleidxitemitem_bullet
code_lineschild
child_typerendered	code_textlangr_   rV   _walk_elementss                  r2   rx   z7_extract_text_from_slack_blocks.<locals>._walk_elements   sm    !	S !	SD,,I///++DHHZ,D,DEE +!    
 ///txx
B77[ST_UUUUU...!XXg..
!*488J+C+C!D!D X XIC,6(,B,B&&3QR7K"ND6{;WWWWWX 666(*
!XXj"55 4 4E!&62!6!6J!%888#:#:599ZQS;T;T#U#U#:#:E7#C#C 4"))(333 IIj11	 i88J33D L!?t!?!?y!?!?!?[aghhhh22D6:: S L{6RRRRC!	S !	Sr1   r;   	rich_textrh   )r   r$   )listr*   r/   rP   rR   )r7   blockr_   rV   rx   r^   s     @@@@r2   _extract_text_from_slack_blocksr|   P   s2     rE$ 3    29 93 9S 9c 94 9 9 9 9 9 9"S "S "SC "SS "SRV "S "S "S "S "S "S "S "SH  6 6KRV$$33N599Z4455599Ur1   p  	max_charsc                 \   | sdS t          d | D                       rdS h dh dfd	 t          j         |           dd          }n# t          $ r t	          |           }Y nw xY wt          |          |k    r"|d	|d
z
                                           dz   }d| dS )zPReturn a compact, redacted JSON view of the current message's Block Kit payload.r$   c              3   L   K   | ]}|pi                      d           dk    V   dS )r;   ry   N)rP   ).0r{   s     r2   	<genexpr>z4_serialize_slack_blocks_for_agent.<locals>.<genexpr>   s9      
H
HEKRV$$3
H
H
H
H
H
Hr1   >   r;   rH   re   block_idmultipleoptional	action_iddispatch_action>   hintr<   closelabeltitlefieldssubmitconfirmoptionsr9   	accessorydescriptionplaceholderoption_groupsc                    t          | t                    rd fd| D             D             S t          | t                    rDi }|                                 D ]+\  }}|v r|||<   |v r |          }|d i g dfvr|||<   ,|S t          | t          t
          t          t          f          s| | S t          |           S )Nc                 "    g | ]}|d i g dfv
|S )Nr$   r0   )r   rp   s     r2   
<listcomp>zH_serialize_slack_blocks_for_agent.<locals>._sanitize.<locals>.<listcomp>   s/    eeeTdSWY[]_acRdFdFdDFdFdFdr1   c              3   .   K   | ]} |          V  d S Nr0   )r   v	_sanitizes     r2   r   zG_serialize_slack_blocks_for_agent.<locals>._sanitize.<locals>.<genexpr>   s+      %B%Bqiill%B%B%B%B%B%Br1   r$   )	
isinstancerz   dictitemsr*   r/   r.   boolrepr)value	sanitizedkeyrp   cleanedr   recursive_allowlistscalar_allowlists        r2   r   z4_serialize_slack_blocks_for_agent.<locals>._sanitize   s    eT"" 	fee%B%B%B%BE%B%B%BeeeeeT"" 		I"[[]] 1 1	T***%)IcNN///'iooGtRR&888)0	#ec3t455 	LE{{r1   Fr   )ensure_asciiindentN   z
... [truncated]z3[Slack Block Kit payload for this message]
```json
rj   )alljsondumps	Exceptionr   lenr\   )r7   r~   payloadr   r   r   s      @@@r2   !_serialize_slack_blocks_for_agentr      s    r

H
H
H
H
HHH r	 	 	  "      "*YYv..U1MMM   v,, 7||i*IN*+22447JJQ7QQQQs    A A0/A0client	proxy_urlc                 8    t          | d          r	|| _        dS dS )zDApply a resolved proxy to a Slack SDK client or clear it explicitly.proxyN)hasattrr   )r   r   s     r2   _apply_slack_proxyr      s)    vw ! ! !r1   )z	slack.comzfiles.slack.comzwss-primary.slack.comc                  D   t                      } | sdS |                                 }|                    d          s*t                              dt          |                      dS t          d t          D                       rt                              d           dS | S )z:Resolve a proxy URL that Slack SDK clients can safely use.N)zhttp://zhttps://zA[Slack] Ignoring unsupported proxy scheme for Slack transport: %sc              3   4   K   | ]}t          |          V  d S r   )r   )r   hosts     r2   r   z+_resolve_slack_proxy_url.<locals>.<genexpr>  s+      
M
M$'--
M
M
M
M
M
Mr1   z3[Slack] NO_PROXY bypasses Slack proxy configuration)r   lower
startswithloggerinfor   any_SLACK_PROXY_HOSTS)r   
normalizeds     r2   _resolve_slack_proxy_urlr      s    !##I t""J  !899 OY''	
 	
 	
 t

M
M:L
M
M
MMM IJJJtr1   c                       e Zd ZdZdZdef fdZdddedee	e
ef                  d	ee
         fd
Zdddedee	e
ef                  d	ee
         fdZdZde
d	ee	e
ef                  fdZde	e
ef         de
d	dfdZd	efdZdjdZde
d	efdZ	 	 dkde
de
dee
         dee	e
ef                  d	ef
dZ	 	 dkde
de
de
dee
         dee	e
ef                  d	efdZddde
de
de
ded	ef
d Zdlde
d	dfd!Zdlde
d	dfd"Zd	efd#Z	 	 dkdee
         dee	e
ef                  d	ee
         fd$Z	 	 	 dmde
d%e
d&ee
         dee
         dee	e
ef                  d	efd'Z	 	 dnde
d)eee
e
f                  dee	e
ef                  d*e d	df
 fd+Z!de
d,ee
         d	dfd-Z"ded	efd.Z#de
d	e
fd/Z$d0e
d1e
d2e
d	efd3Z%d0e
d1e
d2e
d	efd4Z&d	efd5Z'd6e(d	dfd7Z)d6e(d8e*d	dfd9Z+dode
de
d	e
fd;Z,	 	 	 dmde
d<e
d&ee
         dee
         dee	e
ef                  d	efd=Z-	 	 	 dmde
d>e
d&ee
         dee
         dee	e
ef                  d	ef fd?Z.	 	 	 dmde
d@e
d&ee
         dee
         dee	e
ef                  d	efdAZ/	 	 	 dmde
dBe
d&ee
         dee
         dee	e
ef                  d	efdCZ0	 	 	 	 dpde
d%e
d&ee
         dDee
         dee
         dee	e
ef                  d	efdEZ1de
d	e	e
ef         fdFZ2dGe
d,e
d	eee
e
f                  fdHZ3d6e4d	e	e
e
f         fdIZ5de	e
e
f         d	dfdJZ6	 	 dqd6e4dGe
d,e
d	e	e
e
f         fdKZ7de	e
e
f         d	dfdLZ8d6e4d	dfdMZ9d6e4d	dfdNZ:	 	 drde
dPe
dQe
dRe
dee	e
ef                  d	efdSZ;	 dlde
dTe
dUe
dQe
dVe
dee	e
ef                  d	efdWZ<djdXZ=djdYZ>	 dsdGe
d,e
d[e
d\e
d]e?d	e
fd^Z@	 dodGe
d,e
d\e
d	e
fd_ZAdPe4d	dfd`ZBdGe
d,e
de
d	efdaZCdtdbe
dce
dded\e
d	e
f
deZDdodbe
d\e
d	eEfdfZFd	efdgZGd	efdhZHd	eIfdiZJ xZKS )uSlackAdaptera  
    Slack bot adapter using Socket Mode.

    Requires two tokens:
      - SLACK_BOT_TOKEN (xoxb-...) for API calls
      - SLACK_APP_TOKEN (xapp-...) for Socket Mode connection

    Features:
      - DMs and channel messages (mention-gated in channels)
      - Thread support
      - File/image/audio attachments
      - Slash commands (/hermes)
      - Typing indicators (not natively supported by Slack bots)
    iX  configc                    t                                          |t          j                   d | _        d | _        d | _        i | _        d | _        i | _	        i | _
        i | _        t                      | _        i | _        t                      | _        d| _        t                      | _        d| _        i | _        d| _        i | _        d| _        t                      | _        i | _        i | _        d S )Ni  g      N@)super__init__r   SLACK_app_handler_bot_user_id_user_name_cache_socket_mode_task_team_clients_team_bot_user_ids_channel_teamr   _dedup_approval_resolvedset_bot_message_ts_BOT_TS_MAX_mentioned_threads_MENTIONED_THREADS_MAX_assistant_threads_ASSISTANT_THREADS_MAX_thread_context_cache_THREAD_CACHE_TTL_reacting_message_ids_active_status_threads_slash_command_contexts)selfr   	__class__s     r2   r   zSlackAdapter.__init__  s    000#'	'++/029=-/24-/ *++ 46 %(EE (+uu&*#
 JL&*#EG"!%*-%%" 79#
 OQ$$$r1   Nfile_objresponser   r3   c                   |t          |d          sdS t          |                    dd          pd                                          }|sdS t          |pi                     d          p|pi                     d          pd          }t          |                    dd          pd                                          }t          |                    d	d          pd                                          }d
}|rd| dnd}|dk    r|rd| dnd}	d| d|	 | | S |dv r	d| d| dS |dv r	d| d| dS |dv r	d| d| dS dS )zLConvert Slack API auth/permission failures into actionable user-facing text.NrP   errorr$   rJ   idthis attachmentneededprovidedzM Update the Slack app scopes/settings and reinstall the app to the workspace.z Current bot scopes: .missing_scopezMissing scope: zMissing required Slack scope.#Slack attachment access failed for rf   >   
not_authedinvalid_authtoken_revokedaccount_inactivez* because the bot token is not authorized (z'). Refresh the token/reinstall the app.>   file_deletedfile_not_foundSlack attachment z is no longer available (z).>   access_deniedno_permissionrestricted_actionfile_access_deniednot_allowed_token_typez+ because the bot does not have permission (z>). Check workspace permissions/scopes and reinstall if needed.)r   r*   rP   r[   )
r   r   r   r   
file_labelr   r   reinstall_hintprovided_hintneeded_hints
             r2   _describe_slack_api_errorz&SlackAdapter._describe_slack_api_errorK  s   78U#;#;4HLL"--344::<< 	4(.b--f55h(.b9M9Md9S9ShWhii
X\\(B//5266<<>>x||J339r::@@BBh?GO;;;;;RO##9?d5F5555EdKsss{sTascqsssWWW ^  ^  ^ot  ^  ^  ^  ^666UzUUEUUUU{{{ v  v  vpu  v  v  v  vtr1   excc                   t          |pi                     d          p|pi                     d          pd          }t          |dd          }|                     ||          }|r|S 	 ddl}n# t
          $ r d}Y nw xY w|Et          ||j                  r0|j        j	        }|dk    rd	| d
S |dk    rd	| dS |dk    rd| dS t          |          }d|v sd|v rd	| dS dS )zLTranslate Slack download exceptions into user-facing attachment diagnostics.rJ   r   r   r   Nr   r   i  r   z> with HTTP 401. The bot token is not authorized for this file.i  zK with HTTP 403. The bot likely lacks permission or scope to read this file.i  r   z. returned HTTP 404 and is no longer reachable.z$Slack returned HTML instead of mediaznon-image datazs: Slack returned an HTML/login or non-media response. This usually means a scope, auth, or file-permission problem.)
r*   rP   getattrr   httpxr   r   HTTPStatusErrorr   status_code)	r   r   r   r   r   
api_detailr   statusmessages	            r2    _describe_slack_download_failurez-SlackAdapter._describe_slack_download_failuree  s   (.b--f55h(.b9M9Md9S9ShWhii
3
D1133Hx3PP
 		LLLL 	 	 	EEE	 C1F!G!G\-F}} HZ  H  H  H  H}} UZ  U  U  U  U}}e:eeeec((1W<<@PT[@[@[Pj P P P ts   +A0 0A?>A?g      ^@chat_idc                     t          j                     fd j                                        D             }|D ]} j                            |d           t
                                          }|r j                            ||fd          S d}t           j                  D ]}|d         |k    r|} n|dS  j                            |          S )u]  Return and remove the slash-command context for *chat_id*, if fresh.

        Contexts older than ``_SLASH_CTX_TTL`` seconds are silently discarded.

        Uses the ``_slash_user_id`` ContextVar (set in ``_handle_slash_command``)
        to match the exact ``(channel_id, user_id)`` key.  This prevents a
        concurrent slash command from a different user on the same channel from
        stealing another user's ephemeral context.  Falls back to a
        channel-only scan when the ContextVar is unset (e.g. send() called
        from a non-slash code path — should not match anything).
        c                 B    g | ]\  }}|d          z
  j         k    |S )ts)_SLASH_CTX_TTL)r   kr   nowr   s      r2   r   z3SlackAdapter._pop_slash_context.<locals>.<listcomp>  s=     
 
 
!QQtW}t222 222r1   Nr   )r,   r-   r   r   popr   rP   rz   )r   r  
stale_keysr  uid	match_keyr   r  s   `      @r2   _pop_slash_contextzSlackAdapter._pop_slash_context  s    n
 
 
 
 
6<<>>
 
 

  	6 	6A(,,Q5555   "" 	J/33WcNDIII 	455 	 	C1v  	 ! 4+//	:::r1   ctxr    r   c           	      L  K   |                      |          }|                     || j                  }|r|d         n|}dd|d}	 t          j                    4 d{V }|                    |d         |t          j        d          	          4 d{V 	 }|j        d
k    r5t          dd          cddd          d{V  cddd          d{V  S |	                                 d{V }	t                              d|j        |	dd
                    ddd          d{V  n# 1 d{V swxY w Y   ddd          d{V  n# 1 d{V swxY w Y   n2# t          $ r%}
t                              d|
           Y d}
~
nd}
~
ww xY wt          dd          S )u  Replace the initial ephemeral ack via ``response_url``.

        Slack's ``response_url`` accepts a POST with ``replace_original``
        for up to 30 minutes after the slash command was invoked.  This
        lets us swap the "Running /cmd…" placeholder with the real reply,
        and the message stays ephemeral ("Only visible to you").

        Falls back to a simple ``True`` SendResult if the POST fails —
        the user already saw the initial ack, so a delivery failure here
        is non-critical.
        r   	ephemeralT)response_typereplace_originalr<   Nresponse_url
   )total)r   timeout   success
message_idz)[Slack] response_url POST returned %s: %sz$[Slack] response_url POST failed: %s)format_messagetruncate_messageMAX_MESSAGE_LENGTHaiohttpClientSessionpostClientTimeoutr  r   r<   r   warningr   )r   r  r    	formattedchunksr<   r   sessionrespbodyes              r2   _send_slash_ephemeralz"SlackAdapter._send_slash_ephemeral  s^       ''00	
 &&y$2IJJ"1vayy	( $
 

	,..       '"<<' #1;;; (           {c)))$4HHH                          "&,,,,,,DNNCTcT
                                                        	 	 	NN6       	
 $48888ss   E# 8ED.4EE# AD.E.
D8	8E;D8	<E?E# 
EE# EE# #
F-FFc                    !K   t           st                              d           dS  j        j        }t          j        d          }|st                              d           dS |st                              d           dS t                      }|r(t                              dt          |                     d |
                    d          D             }d	d
lm}  |            dz  }|                                r	 t          j        |                    d                    }|                                D ]\  }}	t%          |	t&                    r|	                    dd          nd}
|
ra|
|vr]|                    |
           t%          |	t&                    r|	                    d|          n|}t                              d|           n3# t,          $ r&}t                              d||           Y d}~nd}~ww xY wd}	                      d|d          s$	 |r j        s                                  dS dS dS d} j        s	  j                                         d{V  n0# t,          $ r# t                              d j                   Y nw xY wd _        d _        n# d _        d _        w xY w|d	         }tA          |           _        tC           j        j"        |           |D ]}tG          |          }tC          ||           |$                                 d{V }|                    dd          }|                    dd          }|                    dd          }|                    dd          }| j%        |<   | j&        |<    j'        | _'        t                              d|||           ԉ j        (                    d           fd             } j        (                    d!           fd"            } j        (                    d#          d$             } j        (                    d%          d&             } j        (                    d'          d(             } j        (                    d)           fd*            } j        (                    d+           fd,            }d	d-l)m*} d	dl+!d.  |            D             }|r;!,                    d/d0-                    !fd1|D                       z   d2z             }n!,                    d3          } j        .                    |           fd4            }d5D ]*}   j        /                    |            j0                   +d6D ]*}   j        /                    |            j1                   +te           j        ||7           _        tC           j        j"        |           tg          j4         j        5                                           _6        d _        t                              d8to           j%                             	 |r j        s                                  dS dS dS # t,          $ rI}t                              d9|d:           Y d}~|r j        s                                  dS dS dS d}~ww xY w# |r j        s                                  w w w xY w);z!Connect to Slack via Socket Mode.z=[Slack] slack-bolt not installed. Run: pip install slack-boltFSLACK_APP_TOKENz[Slack] SLACK_BOT_TOKEN not setz[Slack] SLACK_APP_TOKEN not setz+[Slack] Using proxy for Slack transport: %sc                 ^    g | ]*}|                                 |                                 +S r0   r[   )r   ts     r2   r   z(SlackAdapter.connect.<locals>.<listcomp>  s-    KKKAKaggiiKKKr1   ,r   )get_hermes_homezslack_tokens.jsonutf-8)encodingtokenr$   	team_namez+[Slack] Loaded saved token for workspace %sz[Slack] Failed to read %s: %sNzslack-app-tokenzSlack app tokenTz+[%s] Failed to close previous Slack handler)r8  team_idrE   rC   unknownteamz7[Slack] Authenticated as @%s in workspace %s (team: %s)r  c                 B   K                        |            d {V  d S r   _handle_slack_messageeventsayr   s     r2   handle_message_eventz2SlackAdapter.connect.<locals>.handle_message_event>  3      0077777777777r1   app_mentionc                 B   K                        |            d {V  d S r   r>  r@  s     r2   handle_app_mentionz0SlackAdapter.connect.<locals>.handle_app_mentionI  rD  r1   file_sharedc                 
   K   d S r   r0   rA  rB  s     r2   handle_file_sharedz0SlackAdapter.connect.<locals>.handle_file_sharedP        r1   file_createdc                 
   K   d S r   r0   rJ  s     r2   handle_file_createdz1SlackAdapter.connect.<locals>.handle_file_createdT  rL  r1   file_changec                 
   K   d S r   r0   rJ  s     r2   handle_file_changez0SlackAdapter.connect.<locals>.handle_file_changeX  rL  r1   assistant_thread_startedc                 B   K                        |            d {V  d S r   (_handle_assistant_thread_lifecycle_eventr@  s     r2   handle_assistant_thread_startedz=SlackAdapter.connect.<locals>.handle_assistant_thread_started\  3      CCEJJJJJJJJJJJr1    assistant_thread_context_changedc                 B   K                        |            d {V  d S r   rU  r@  s     r2   'handle_assistant_thread_context_changedzESlackAdapter.connect.<locals>.handle_assistant_thread_context_changed`  rX  r1   )slack_native_slashesc                     g | ]\  }}}|	S r0   r0   )r   rJ   _d_hs       r2   r   z(SlackAdapter.connect.<locals>.<listcomp>t  s    LLL\T2rDLLLr1   z^/(?:|c              3   B   K   | ]}                     |          V  d S r   )escape)r   n_res     r2   r   z'SlackAdapter.connect.<locals>.<genexpr>w  s-      'L'L!

1'L'L'L'L'L'Lr1   z)$z	^/hermes$c                    K   |                     d          pd                    d          } | dd| d           d {V                      |           d {V  d S )Ncommandr$   /r  z
Running `/u   `…)r  r<   )rP   lstrip_handle_slash_command)ackrf  slashr   s      r2   handle_hermes_commandz3SlackAdapter.connect.<locals>.handle_hermes_command|  s       Y//52==cBBc"-1e111          0099999999999r1   hermes_approve_oncehermes_approve_sessionhermes_approve_alwayshermes_denyhermes_confirm_oncehermes_confirm_alwayshermes_confirm_cancel)r   z/[Slack] Socket Mode connected (%d workspace(s))z[Slack] Connection failed: %sexc_info)8r5   r   r   r   r8  osgetenvr   r   r   splithermes_constantsr5  existsr   loads	read_textr   r   r   rP   rQ   r   r'  _acquire_platform_lock_running_release_platform_lockr   close_asyncdebugrJ   r   r
   r   r   r   	auth_testr   r   r   rA  hermes_cli.commandsr\  recompilerR   rf  action_handle_approval_action_handle_slash_confirm_actionr   asynciocreate_taskstart_asyncr   r   )"r   	raw_token	app_tokenr   
bot_tokensr5  tokens_filesavedr:  entrytok
team_labelr-  lock_acquiredprimary_tokenr8  r   auth_responsebot_user_idbot_namer9  rC  rG  rK  rO  rR  rW  r[  r\  _slash_names_slash_patternrl  
_action_idrd  s"   `                                @r2   connectzSlackAdapter.connect  s      	LLO   5K%	I/00	 	LL:;;;5 	LL:;;;5,..	 	dKKEGWXaGbGbccc LK)=)=KKK
 	544444%o''*== 
	P	P
;#8#8'#8#J#JKK&+kkmm _ _NGU4>ud4K4KS%))GR000QSC _s*44"))#...HRSXZ^H_H_%lUYY{G%D%D%Del
$QS]^^^_  P P P>QOOOOOOOOP Z	.../@)M^__ n  .T] .++-----. . . .m !M }(%-335555555555  [ [ [LL!NPTPYZZZZZ[ %)DM $DII %)DM $DI$$$$ 'qMM }555DIty/;;; $  'e444"69555&,&6&6&8&8 8 8 8 8 8 8'++Ir::+//	2>>(,,VY??)--fi@@	.4"7+3>'0 $,(3D%Mi    Y__Y''8 8 8 8 ('8 Y__]++8 8 8 8 ,+8 Y__]++  ,+ Y__^,,  -, Y__]++  ,+ Y__788K K K K 98K Y__?@@K K K K A@K  A@@@@@LL5I5I5K5KLLLL ;!$sxx'L'L'L'L|'L'L'LLLLuT" " "%\!:!:Y~..: : : : /.: K K
 -	  ,,T-IJJJJ P P

 -	  ,,T-NOOOO 349iyYYYDMt}3Y???%,%89R9R9T9T%U%UD" DMKKAD&''     .T] .++-----. . . .	  	 	 	LL8!dLKKK555 .T] .++-----. . . .		  .T] .++----. .s   CG 
H!HHY; 	Y; I2 1J1 2*JJ1 JJ1 "Y; 1KNY; ;
[[	"[ 	[[ ![2c                 ,  K   | j         rU	 | j                                          d{V  n4# t          $ r'}t                              d|d           Y d}~nd}~ww xY wd| _        |                                  t                              d           dS )zDisconnect from Slack.Nz3[Slack] Error while closing Socket Mode handler: %sTrv  Fz[Slack] Disconnected)r   r  r   r   r'  r  r  r   )r   r-  s     r2   
disconnectzSlackAdapter.disconnect  s      = 	hhm//1111111111 h h hTVWbfggggggggh##%%%*+++++s   + 
AAAc                 ~    | j                             |          }|r|| j        v r| j        |         S | j        j        S )z6Return the workspace-specific WebClient for a channel.)r   rP   r   r   r   )r   r  r:  s      r2   _get_clientzSlackAdapter._get_client  sF    $((11 	/w$"444%g..yr1   reply_tometadatac                   K   | j         st          dd          S 	 |                     |          }|r|                     ||           d{V S |                     |          }|                     || j                  }|                     ||          }d}	| j        j	        
                    dd          }
t          |          D ]E\  }}||dd}|r||d<   |
r|d	k    rd|d<    |                     |          j        di | d{V }	F|r|                     |           d{V  |	r|	
                    d
          nd}|r| j                            |           |r| j                            |           t#          | j                  | j        k    rXt#          | j                  | j        dz  z
  }t'          | j                  d|         D ]}| j                            |           t          d||	          S # t*          $ rE}t,                              d|d           t          dt1          |                    cY d}~S d}~ww xY w)z(Send a message to a Slack channel or DM.FNot connectedr  r   Nreply_broadcastT)r@   r<   mrkdwn	thread_tsr   r
  r   r  r  raw_responsez[Slack] Send error: %srv  r0   )r   r   r  r.  r   r!  r"  _resolve_thread_tsr   extrarP   rk   r  chat_postMessagestop_typingr   addr   r   rz   discardr   r   r   r*   )r   r  r    r  r  	slash_ctxr(  r)  r  last_resultrK   ichunkkwargssent_tsexcessold_tsr-  s                     r2   sendzSlackAdapter.send  s      y 	De?CCCC@	; //88I !77w        
 ++G44I **9d6MNNF//(CCIK )--.?GGI%f-- Y Y5&!" 
  9*3F;'  9Q!VV4801$ND$4$4W$=$=$N$X$XQW$X$XXXXXXX  0&&w///////// 0;Dkood+++G =$((111 8(,,Y777t+,,t/??? !5669IQ9NNF"&t';"<"<WfW"E = =,44V<<<<"(     	; 	; 	;LL11tLDDDe3q66:::::::::	;s$   2H F1H 
I:IIIrE   c                 8  K   | j         st          dd          S |r|st          dd          S 	 |                     |          }|                     ||          }|||dd}|r||d<    |                     |          j        di | d{V }	t          d|	                    d	          p|	                    d
          |	          S # t          $ rE}
t          	                    d|
d           t          dt          |
                    cY d}
~
S d}
~
ww xY w)z8Send a Slack ephemeral message visible only to one user.Fr  r  z chat_id and user_id are requiredT)r@   rC   r<   r  r  N
message_tsr
  r  z [Slack] Ephemeral send error: %srv  r0   )r   r   r   r  r  chat_postEphemeralrP   r   r   r   r*   )r   r  rE   r    r  r  r(  r  r  resultr-  s              r2   send_private_noticez SlackAdapter.send_private_notice  s{      y 	De?CCCC 	Wg 	We3UVVVV	;++G44I//(CCI"!	 F  0&/{#G4++G44GQQ&QQQQQQQQF!::l33Gvzz$7G7G#   
  	; 	; 	;LL;QLNNNe3q66:::::::::	;s   BC
 

D:DDDF)finalizer  r  c                  K   | j         st          dd          S 	 |                     |          }|                     |                              |||           d{V  |r|                     |           d{V  t          d|          S # t          $ rG}t                              d|||d	           t          dt          |                    cY d}~S d}~ww xY w)
z%Edit a previously sent Slack message.Fr  r  )r@   r
  r<   NTr  z3[Slack] Failed to edit message %s in channel %s: %srv  )
r   r   r   r  chat_updater  r   r   r   r*   )r   r  r  r    r  r(  r-  s          r2   edit_messagezSlackAdapter.edit_message/  sF      y 	De?CCCC	;++G44I""7++77 8         
  0&&w/////////dzBBBB 	; 	; 	;LLE     e3q66:::::::::	;s   A3B 
C!<CC!C!c                 ^  K   | j         sdS d}|r*|                    d          p|                    d          }|sdS || j        |<   	 |                     |                              ||d           d{V  dS # t
          $ r&}t                              d|           Y d}~dS d}~ww xY w)a  Show a typing/status indicator using assistant.threads.setStatus.

        Displays "is thinking..." next to the bot name in a thread.
        Requires the assistant:write or chat:write scope.
        Auto-clears when the bot sends a reply to the thread.
        N	thread_idr  zis thinking...rA   r  r  z.[Slack] assistant.threads.setStatus failed: %s)r   rP   r   r  assistant_threads_setStatusr   r   r  r   r  r  r  r-  s        r2   send_typingzSlackAdapter.send_typingN  s      y 	F	 	O [11NX\\+5N5NI 	F/8#G,		N""7++GG"#' H           
  	N 	N 	N LLI1MMMMMMMMM	Ns   	1A< <
B,B''B,c                 $  K   | j         sdS | j                            |d          }|sdS 	 |                     |                              ||d           d{V  dS # t
          $ r&}t                              d|           Y d}~dS d}~ww xY w)z,Clear the assistant thread status indicator.Nr$   r  z4[Slack] assistant.threads.setStatus clear failed: %s)r   r   r  r  r  r   r   r  r  s        r2   r  zSlackAdapter.stop_typingk  s      y 	F/33GTBB	 	F	T""7++GG"# H           
  	T 	T 	TLLOQRSSSSSSSSS	Ts   1A 
B)B

Bc                     | j         j                            d          }|dS t          |                                                                          dv S )u  Whether top-level Slack DMs get per-message session threads.

        Defaults to ``True`` so each visible DM reply thread is isolated as its
        own Hermes session — matching the per-thread behavior channels already
        have.  Set ``platforms.slack.extra.dm_top_level_threads_as_sessions``
        to ``false`` in config.yaml to revert to the legacy behavior where all
        top-level DMs share one continuous session.
         dm_top_level_threads_as_sessionsNT)1trueyeson)r   r  rP   r*   r[   r   )r   raws     r2   !_dm_top_level_threads_as_sessionsz.SlackAdapter._dm_top_level_threads_as_sessions{  sO     k##$FGG;43xx~~%%''+EEEr1   c                 :   | j         j                            dd          s>|pi }|                    d          p|                    d          }|r
|r||k    rd}|pdS |r:|                    d          r|d         S |                    d          r|d         S |S )a  Resolve the correct thread_ts for a Slack API call.

        Prefers metadata thread_id (the thread parent's ts, set by the
        gateway) over reply_to (which may be a child message's ts).

        When ``reply_in_thread`` is ``false`` in the platform extra config,
        top-level channel messages receive direct channel replies instead of
        thread replies.  Messages that originate inside an existing thread are
        always replied to in-thread to preserve conversation context.
        reply_in_threadTr  r  N)r   r  rP   )r   r  r  mdexisting_threads        r2   r  zSlackAdapter._resolve_thread_ts  s    0 { $$%6== 	+RB ff[11HRVVK5H5HO '8 '80K0K"&"*d* 	-||K(( -,,||K(( -,,r1   	file_pathcaptionc                   K   | j         st          dd          S t          j                            |          st          d|           |                     ||          }d}t          d          D ]}	 |                     |          	                    ||t          j        
                    |          |pd|           d{V }	|                     ||           t          d	|	
          c S # t          $ rh}
|
}|                     |
          r|dk    r t                              d|dz   ||
           t!          j        d|dz   z             d{V  Y d}
~
d}
~
ww xY w|)zUpload a local file to Slack.Fr  r  File not found: N   r$   r@   filefilenameinitial_commentr  Tr  r  r   z$[Slack] Upload retry %d/2 for %s: %src         ?)r   r   rx  pathr|  FileNotFoundErrorr  rL   r  files_upload_v2basename_record_uploaded_file_threadr   _is_retryable_upload_errorr   r  r  sleep)r   r  r  r  r  r  r  last_excattemptr  r   s              r2   _upload_filezSlackAdapter._upload_file  s      y 	De?CCCCw~~i(( 	D#$By$B$BCCC++Hh??	Qxx 	9 	9G9#//88HH#"W--i88$+Mr'  I           11'9EEE!$VDDDDDD 
9 
9 
966s;; w!||:aK	   mC7Q;$788888888888888
9 s   6A8C11
E#;AEE#        imageshuman_delayc           
        K   | j         sdS sdS 	 ddl}ddlm} ddlm} n;# t          $ r. t                                          |||           d{V  Y dS w xY w| 	                    d|          }dfdt          dt                              D             }	t          |	          D ]<\  }
}|dk    r |
dk    rt          j        |           d{V  g }g }	 |                    dd	          4 d{V }|D ]\  }}|r|                    |           |                    d
          r ||dd                   }t$          j                            |          st*                              d|           |                    |t$          j                            |          d            ||          st*                              d           	 |                    |           d{V }|                                 d}|j                            dd          }d|v sd|v rd}nd|v rd}nd|v rd}|                    |j        dt          |           d| d           s# t          $ r4}t*                              dt9          |          |           Y d}~d}~ww xY w	 ddd          d{V  n# 1 d{V swxY w Y   |s-|rd                    |          nd}t*                              dt          |          |
dz   t          |	                     |                     |                               ||||           d{V }| !                    ||           |}# t          $ re}t*                              d|
dz   t          |	          |d           t                                          ||||            d{V  Y d}~6d}~ww xY wdS )!a  Send a batch of images as a single Slack message with multiple file uploads.

        Uses ``files_upload_v2`` with its ``file_uploads`` parameter so all
        images show up attached to one ``initial_comment`` message instead
        of N separate messages. Falls back to the base per-image loop on
        any failure.

        The batch limit is 10 file uploads per call (Slack server-side cap).
        Nr   )unquoteis_safe_urlr  c                 *    g | ]}||z            S r0   r0   )r   r  CHUNKr  s     r2   r   z5SlackAdapter.send_multiple_images.<locals>.<listcomp>  s&    LLL!&1u9%LLLr1         >@Tr  follow_redirectszfile://   z"[Slack] Skipping missing image: %s)r  r  z)[Slack] Blocked unsafe image URL in batchpngcontent-typer$   jpegjpggifwebpimage_r   )r    r  z"[Slack] Download failed for %s: %srh   zC[Slack] Sending %d image(s) in single files_upload_v2 (chunk %d/%d)rc   )r@   file_uploadsr  r  zW[Slack] Multi-image files_upload_v2 failed (chunk %d/%d), falling back to per-image: %srv  )r  )"r   r   urllib.parser  tools.url_safetyr  r   r   send_multiple_imagesr  rL   r   rk   r  r  AsyncClientrQ   r   rx  r  r|  r   r'  r  rP   raise_for_statusheadersr    r   rR   r   r  r  r  )r   r  r  r  r  _httpx_unquote_is_safe_urlr  r)  	chunk_idxr  r  initial_comment_partshttp_client	image_urlalt_text
local_pathr   extctdl_errr  r  _r-  r  r   s     `                       @r2   r  z!SlackAdapter.send_multiple_images  s       y 	F 	F	""""888888DDDDDDD 	 	 	''..w+VVVVVVVVVFF	 ++D(;;	LLLLLuQFU/K/KLLL )& 1 1 E	f E	fIuQ9q==mK00000000013L/1!?f!--dT-RR &) &) &) &) &) &) &)Va/4 %) %)+	8# C188BBB$//	:: !))1)ABB-)@)@J#%7>>*#=#= ) &/SU_ ` ` ` ((//(2,.G,<,<Z,H,H1 1    
 $0<	#:#: ) &/Z [ [ [ ()1<1K1K+K+K+K+K+K+K ( 9 9 ; ; ;&+%-%5%9%9."%M%M#)R<<5B;;*/CC%*b[[*/CC%+r\\*0C , 3 3/7/?0R\9J9J0R0RS0R0R5" 5" !# !# !# !# $- ) ) ) &$H$4Y$?$?!" !" !" !))A%)&) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &) &)P $ F["c$)),A"B"B"BacY%%y1}c&kk    $//88HH#!-$3'	  I           11'9EEE f f fmM3v;;!    
 gg227E8Yd2eeeeeeeeeeeeeefE	f E	fs   % 4AA#M> CK
 BI64K
6
J4	 )J/	)K
/J4	4K
8M>

K	M>K	M>BM>>
O-AO((O-r  c                 0   |sdS | j                             |           t          | j                   | j        k    rXt          | j                   | j        dz  z
  }t	          | j                   d|         D ]}| j                             |           dS dS )z?Treat successful file uploads as bot participation in a thread.Nr   )r   r  r   r   rz   r  )r   r  r  r  r  s        r2   r  z)SlackAdapter._record_uploaded_file_threadB  s     	F  +++t#$$t'777-..1AQ1FFFt344WfW= 5 5$,,V4444 875 5r1   c           
      d   t          t          |dd          dd          }||dk    p|dk    S d                    d |t          |dd	          t          |dd          fD                                                       }d
|v sd|v sd|v rdS d|v sd|v sd|v rdS |                     |          S )z:Best-effort detection for transient Slack upload failures.r   Nr      rZ   c              3   8   K   | ]}|t          |          V  d S r   r*   r   parts     r2   r   z:SlackAdapter._is_retryable_upload_error.<locals>.<genexpr>R  sD       
 
 
II
 
 
 
 
 
r1   r  r$   rate_limitedratelimited429Tzconnection resetzservice unavailableztemporarily unavailable)r   rR   r   _is_retryable_error)r   r   r  r,  s       r2   r  z'SlackAdapter._is_retryable_upload_errorL  s    gc:t<<mTRR"#%;);;xx 
 
Y++Z..#
 
 
 
 
 %'' 	 T!!]d%:%:etmm4%%)>$)F)FJcgkJkJk4''---r1   c                    |s|S i dgdt           dt           ffd|}t          j        dfd|          }t          j        dfd|          }fd	}t          j        d
||          }t          j        dfd|          }t          j        dfd|t          j                  }|                    dd                              dd                              dd          }|                    dd                              dd                              dd          }fd}t          j        d||t          j                  }t          j        dfd|          }t          j        dfd|          }t          j        dfd|          }t          j        dfd|          }t                    D ]}|                    ||                   }|S ) a  Convert standard markdown to Slack mrkdwn format.

        Protected regions (code blocks, inline code) are extracted first so
        their contents are never modified.  Standard markdown constructs
        (headers, bold, italic, links) are translated to mrkdwn syntax.
        r   r   r3   c                 J    dd          d}dxx         dz  cc<   | |<   |S )z<Stash value behind a placeholder that survives later passes.z SLr    rc   r0   )r   r   counterplaceholderss     r2   _phz(SlackAdapter.format_message.<locals>._phn  s;    +71:+++CAJJJ!OJJJ %LJr1   z(```(?:[^\n]*\n)?[\s\S]*?```)c                 @     |                      d                    S Nr   groupmr&  s    r2   <lambda>z-SlackAdapter.format_message.<locals>.<lambda>z      cc!''!**oo r1   z	(`[^`]+`)c                 @     |                      d                    S r(  r)  r+  s    r2   r-  z-SlackAdapter.format_message.<locals>.<lambda>  s    cc!''!**oo r1   c                 ,   |                      d          }|                      d                                          }|                    d          r1|                    d          r|dd                                         } d| d| d          S )Nrc   r   <rB   r`  )r*  r[   r   endswith)r,  r   r>   r&  s      r2   _convert_markdown_linkz;SlackAdapter.format_message.<locals>._convert_markdown_link  s    GGAJJE''!**""$$C~~c"" (s||C'8'8 (!B$ioo''3)3)))))***r1   z3(?<!!)\[([^\]]+)\]\(([^()]*(?:\([^()]*\)[^()]*)*)\)z+(<(?:[@#!]|(?:https?|mailto|tel):)[^>\n]+>)c                 @     |                      d                    S )Nrc   r)  r+  s    r2   r-  z-SlackAdapter.format_message.<locals>.<lambda>  r.  r1   z^(>+\s)c                 @     |                      d                    S r(  r)  r+  s    r2   r-  z-SlackAdapter.format_message.<locals>.<lambda>  s    CC

OO r1   )flagsz&amp;&z&lt;r1  z&gt;rB   c                     |                      d                                          }t          j        dd|          } d| d          S )Nrc   \*\*(.+?)\*\*z\1*)r*  r[   r  sub)r,  innerr&  s     r2   _convert_headerz4SlackAdapter.format_message.<locals>._convert_header  sJ    GGAJJ$$&&EF+UE::E3|5|||$$$r1   z^#{1,6}\s+(.+)$z\*\*\*(.+?)\*\*\*c                 H     d|                      d           d          S )Nz*_rc   z_*r)  r+  s    r2   r-  z-SlackAdapter.format_message.<locals>.<lambda>  s'    cc-qwwqzz---.. r1   r:  c                 H     d|                      d           d          S )Nr;  rc   r)  r+  s    r2   r-  z-SlackAdapter.format_message.<locals>.<lambda>  '    cc+aggajj+++,, r1   z$(?<!\*)\*(\S(?:[^*\n]*?\S)?)\*(?!\*)c                 H     d|                      d           d          S )Nr  rc   r)  r+  s    r2   r-  z-SlackAdapter.format_message.<locals>.<lambda>  rA  r1   z	~~(.+?)~~c                 H     d|                      d           d          S )N~rc   r)  r+  s    r2   r-  z-SlackAdapter.format_message.<locals>.<lambda>  rA  r1   )r*   r  r<  	MULTILINEreplacereversed)	r   r    r<   r4  r>  r   r&  r$  r%  s	         @@@r2   r   zSlackAdapter.format_messagea  s     	N#	s 	s 	 	 	 	 	 	 	  v,%%%%
 
 vl$=$=$=$=tDD	+ 	+ 	+ 	+ 	+ vB"
 
 v:%%%%
 
 vj";";";";TVVV ||GS))11&#>>FFvsSS||C))11#v>>FFsFSS	% 	% 	% 	% 	% vR\
 
 

 v ....
 
 v,,,,
 
 v3,,,,
 
 v,,,,
 
 L)) 	8 	8C<<\#%677DDr1   r@   	timestamprH   c                    K   | j         sdS 	 |                     |                              |||           d{V  dS # t          $ r'}t                              d||           Y d}~dS d}~ww xY w)z<Add an emoji reaction to a message. Returns True on success.Fr@   rH  rJ   NTz%[Slack] reactions.add failed (%s): %s)r   r  reactions_addr   r   r  r   r@   rH  rH   r-  s        r2   _add_reactionzSlackAdapter._add_reaction  s       y 	5	""7++9995 :          4 	 	 	LL@%KKK55555	   1A   
A1
A,,A1c                    K   | j         sdS 	 |                     |                              |||           d{V  dS # t          $ r'}t                              d||           Y d}~dS d}~ww xY w)zARemove an emoji reaction from a message. Returns True on success.FrJ  NTz([Slack] reactions.remove failed (%s): %s)r   r  reactions_remover   r   r  rL  s        r2   _remove_reactionzSlackAdapter._remove_reaction  s       y 	5	""7++<<95 =          4 	 	 	LLCUANNN55555	rN  c                 T    t          j        dd                                          dvS )z6Check if message reactions are enabled via config/env.SLACK_REACTIONSr  )false0no)rx  ry  r   )r   s    r2   _reactions_enabledzSlackAdapter._reactions_enabled  s'    y*F3399;;CWWWr1   rA  c                    K   |                                  sdS t          |dd          }|r	|| j        vrdS t          |j        dd          }|r|                     ||d           d{V  dS dS )z;Add an in-progress reaction when message processing begins.Nr  r  eyes)rW  r   r   sourcerM  )r   rA  r
  rA   s       r2   on_processing_startz SlackAdapter.on_processing_start  s      &&(( 	FUL$// 	Rt999FU\9d;;
 	=$$ZV<<<<<<<<<<<	= 	=r1   outcomec                   K   |                                  sdS t          |dd          }|r	|| j        vrdS | j                            |           t          |j        dd          }|sdS |                     ||d           d{V  |t          j        k    r|                     ||d           d{V  dS |t          j	        k    r|                     ||d           d{V  dS dS )zCSwap the in-progress reaction for a final success/failure reaction.Nr  r  rY  white_check_markx)
rW  r   r   r  rZ  rQ  r   SUCCESSrM  FAILURE)r   rA  r\  r
  rA   s        r2   on_processing_completez#SlackAdapter.on_processing_complete  s2     &&(( 	FUL$// 	Rt999F"**2...U\9d;;
 	F##JF;;;;;;;;;'///$$Z5GHHHHHHHHHHH)111$$ZS99999999999 21r1   r$   c                 f  K   |sdS || j         v r| j         |         S | j        s|S 	 |r|                     |          n| j        j        }|                    |           d{V }|                    di           }|                    di           }|                    d          p@|                    d          p+|                    d          p|                    d          p|}|| j         |<   |S # t          $ r2}t                              d	||           || j         |<   |cY d}~S d}~ww xY w)
z8Resolve a Slack user ID to a display name, with caching.r$   )rC   NrC   profiledisplay_name	real_namerJ   z$[Slack] users.info failed for %s: %s)	r   r   r  r   
users_inforP   r   r   r  )	r   rE   r  r   r  rC   rd  rJ   r-  s	            r2   _resolve_user_namezSlackAdapter._resolve_user_name  su      	2d+++(11y 	N	29OT%%g...ty?OF!,,',::::::::F::fb))Dhhy"--GN++ ;;{++88K(( 88F##   .2D!'*K 	 	 	LL?!LLL-4D!'*NNNNNN	s   CC4 4
D0>'D+%D0+D0
image_pathc                 T  K   	 |                      |||||           d{V S # t          $ r t          dd|           cY S t          $ r[}t                              d| j        ||d           d| }|r| d	| }|                     ||||
           d{V cY d}~S d}~ww xY w)z1Send a local image file to Slack by uploading it.NFzImage file not found: r  z,[%s] Failed to send local Slack image %s: %sTrv  u   🖼️ Image: rh   r  r  )r  r  r   r   r   r   rJ   r  )r   r  ri  r  r  r  r-  r<   s           r2   send_image_filezSlackAdapter.send_image_file2  s&     	X**7JS[\\\\\\\\\  	Z 	Z 	Ze3XJ3X3XYYYYYY 	X 	X 	XLL>	     2Z11D ,!++T++7D8hWWWWWWWWWWWWWW	Xs"   # B'	B'AB"B'"B'r  c                   K   | j         st          dd          S ddlm  |          sFt                              d           t                                          |||||           d{V S 	 ddl}fd	}|	                    d
dd|gi          4 d{V 	 }|
                    |           d{V }	|	                                 ddd          d{V  n# 1 d{V swxY w Y   |                     ||          }
|                     |                              ||	j        d|pd|
           d{V }|                     ||
           t          d|          S # t"          $ r_}t                              dt%          |          |d           |r| d| n|}|                     ||||           d{V cY d}~S d}~ww xY w)z6Send an image to Slack by uploading the URL as a file.Fr  r  r   r  z2[Slack] Blocked unsafe image URL (SSRF protection))r  Nc                    K   | j         r:| j        r5t          | j        j                  } |          st	          d          dS dS dS )zHRe-check redirect targets so public URLs cannot bounce into private IPs.z,Blocked redirect to private/internal addressN)is_redirectnext_requestr*   r>   
ValueError)r   redirect_urlr  s     r2   _ssrf_redirect_guardz5SlackAdapter.send_image.<locals>._ssrf_redirect_guard`  sw      ' YH,A Y#&x'<'@#A#AL&;|44 Y()WXXXY Y Y YY Yr1   r  Tr   )r  r  event_hooksz	image.pngr$   )r@   r    r  r  r  r  zD[Slack] Failed to upload image from URL %s, falling back to text: %srv  rh   )r  r    r  r  )r   r   r  r  r   r'  r   
send_imager   r  rP   r  r  r  r  r    r  r   r   r  )r   r  r  r  r  r  r   rs  r   r   r  r  r-  r<   r  r   s                 @r2   ru  zSlackAdapter.send_imageL  sD      y 	De?CCCC000000{9%% 	fNNOPPP++GY\d+eeeeeeeee-	LLLY Y Y Y Y ((!%'*>)?@ )   , , , , , , , , !'I!6!6666666))+++, , , , , , , , , , , , , , , , , , , , , , , , , , , //(CCI++G44DD ($ '2# E        F --gyAAAd@@@@ 	 	 	NNV ++	     18Fg,,,,,YD!!	 #              	sD   5*E*  0C"E* "
C,,E* /C,0A9E* *
G4AGGG
audio_pathc                 *  K   	 |                      |||||           d{V S # t          $ r t          dd|           cY S t          $ rF}t                              d||d           t          dt          |                    cY d}~S d}~ww xY w)zSend an audio file to Slack.NFzAudio file not found: r  z([Slack] Failed to send audio file %s: %sTrv  )r  r  r   r   r   r   r*   )r   r  rv  r  r  r  r  r-  s           r2   
send_voicezSlackAdapter.send_voice  s      	;**7JS[\\\\\\\\\  	Z 	Z 	Ze3XJ3X3XYYYYYY 	; 	; 	;LL:	     e3q66:::::::::	;s!   # B	B;BBB
video_pathc                   K   | j         st          dd          S t          j                            |          st          dd|           S 	 |                     ||          }d}t          d          D ]}	 |                     |                              ||t          j        	                    |          |pd|           d{V }	| 
                    ||           t          d	|	
          c S # t          $ rh}
|
}|                     |
          r|dk    r t                              d|dz   ||
           t          j        d|dz   z             d{V  Y d}
~
d}
~
ww xY w|# t          $ r[}t                              d| j        ||d	           d| }|r| d| }|                     ||||           d{V cY d}~S d}~ww xY w)zSend a video file to Slack.Fr  r  zVideo file not found: Nr  r$   r  Tr  r   z*[Slack] Video upload retry %d/2 for %s: %src   r  z [%s] Failed to send video %s: %srv  u   🎬 Video: rh   rk  )r   r   rx  r  r|  r  rL   r  r  r  r  r   r  r   r  r  r  r   rJ   r  )r   r  ry  r  r  r  r  r  r  r  r   r-  r<   s                r2   
send_videozSlackAdapter.send_video  s      y 	De?CCCCw~~j)) 	Ze3XJ3X3XYYYY'	X//(CCIH 88 = ==#'#3#3G#<#<#L#L ''!#!1!1*!=!=(/2"+ $M $ $      F 55gyIII%dHHHHHH  
= 
= 
="H::3?? 7a<<LLD!"	   "-w{(;<<<<<<<<<<<<<<
= N 	X 	X 	XLL2	     /*..D ,!++T++7D8hWWWWWWWWWWWWWW	XsK   )E+ 9A8C41E+ 4
E&>AE!E+ !E&&E+ +
G5AGGG	file_namec                   K   | j         st          dd          S t          j                            |          st          dd|           S |pt          j                            |          }|                     ||          }	 d}	t          d          D ]}
	 |                     |          	                    ||||pd|           d{V }| 
                    ||           t          d	|
          c S # t          $ rh}|}	|                     |          r|
dk    r t                              d|
dz   ||           t          j        d|
dz   z             d{V  Y d}~d}~ww xY w|	# t          $ r[}t                              d| j        ||d	           d| }|r| d| }|                     ||||           d{V cY d}~S d}~ww xY w)z)Send a document/file attachment to Slack.Fr  r  r  Nr  r$   r  Tr  r   z-[Slack] Document upload retry %d/2 for %s: %src   r  z#[%s] Failed to send document %s: %srv  u   📎 File: rh   rk  )r   r   rx  r  r|  r  r  rL   r  r  r  r   r  r   r  r  r  r   rJ   r  )r   r  r  r  r|  r  r  re  r  r  r  r  r   r-  r<   s                  r2   send_documentzSlackAdapter.send_document  s      y 	De?CCCCw~~i(( 	Se3Qi3Q3QRRRR ?BG$4$4Y$?$?++Hh??	&	XH 88 = ==#'#3#3G#<#<#L#L '&!-(/2"+ $M $ $      F 55gyIII%dHHHHHH  
= 
= 
="H::3?? 7a<<LLG!!	   "-w{(;<<<<<<<<<<<<<<
= N 	X 	X 	XLL5	     -,,D ,!++T++7D8hWWWWWWWWWWWWWW	XsK   E/ AC85E/ 8
E*AE% E/ %E**E/ /
G9AG	GGc                   K   | j         s|ddS 	 |                     |                              |           d{V }|                    di           }|                    dd          }|                    d|          |rd	nd
dS # t          $ r-}t
                              d||d           |ddcY d}~S d}~ww xY w)z&Get information about a Slack channel.r;  )rJ   r;   )r@   Nr@   is_imFrJ   dmr*  z,[Slack] Failed to fetch chat info for %s: %sTrv  )r   r  conversations_inforP   r   r   r   )r   r  r  r@   is_dmr-  s         r2   get_chat_infozSlackAdapter.get_chat_info  s     y 	8#Y777	8++G44GGPWGXXXXXXXXFjjB//GKK//EFG44 %27    	8 	8 	8LL>	     $Y77777777	8s   A7B 
B?"B:4B?:B?rA   c                 J    |r|sdS t          |          t          |          fS )z>Return a stable cache key for Slack assistant thread metadata.Nr  )r   rA   r  s      r2   _assistant_thread_keyz"SlackAdapter._assistant_thread_key.  s-     	 	4JY00r1   c                 x   |                     d          pi }|                     d          p|                     d          pi }|                     d          p+|                     d          p|                     d          pd}|                     d          p+|                     d          p|                     d          pd}|                     d          p+|                     d	          p|                     d          pd}|                     d
          p+|                     d          p|                     d          pd}|                     d          pd}|rt          |          nd|rt          |          nd|rt          |          nd|rt          |          nd|rt          |          nddS )zCExtract Slack Assistant thread identity data from an event payload.assistant_threadcontextrA   r@   r$   r  r  rE   rC   r<  r:  )rA   r  rE   r:  context_channel_id)rP   r*   )	r   rA  r  r  rA   r  rE   r:  r  s	            r2   "_extract_assistant_thread_metadataz/SlackAdapter._extract_assistant_thread_metadata4  s    99%788>B"&&y11OUYYy5I5IOR   .. yy##{{<(( 	 	   -- yy%%yy&& 	 	   ++ yy  {{9%% 	 	 IIf yy####I.. 	 	 %[[66<" .8?#j///R+4<Y"'.6s7|||B'.6s7|||B=O"W#&8"9"9"9UW
 
 	
r1   c                 r   |                     dd          }|                     dd          }|                     ||          }|sdS | j                             |i           }t          |          }|                    d |                                D                        || j        |<   t          | j                  | j        k    rFt          | j                  | j        dz  z
  }t          | j                  d|         D ]
}| j        |= |                     dd          }	|	r|r|	| j	        |<   dS dS dS )zARemember assistant thread identity data for later message events.rA   r$   r  Nc                     i | ]
\  }}|||S r0   r0   r   r  r   s      r2   
<dictcomp>zASlackAdapter._cache_assistant_thread_metadata.<locals>.<dictcomp>e  s#    >>>1A>q!>>>r1   r   r:  )
rP   r  r   r   updater   r   r   rz   r   )
r   r  rA   r  r   existingmergedr  old_keyr:  s
             r2    _cache_assistant_thread_metadataz-SlackAdapter._cache_assistant_thread_metadata[  sX   \\,33
LLb11	((Y?? 	F*..sB77h>>(8(8>>>???'-$ t&''$*EEE011D4OST4TTF 788&A 5 5+G44**Y++ 	5z 	5-4Dz***	5 	5 	5 	5r1   c                    |                      |          }|r|                    d          s||d<   |r|                    d          s||d<   |                     |                    dd          |                    dd                    }|r| j                            |i           ni }|rBt	          |          }|                    d |                                D                        |S |S )zELoad cached assistant-thread metadata that matches the current event.rA   r  r$   c                     i | ]
\  }}|||S r0   r0   r  s      r2   r  zBSlackAdapter._lookup_assistant_thread_metadata.<locals>.<dictcomp>  s#    BBBDAqB1aBBBr1   )r  rP   r  r   r   r  r   )r   rA  rA   r  r  r   cachedr  s           r2   !_lookup_assistant_thread_metadataz.SlackAdapter._lookup_assistant_thread_metadatar  s    ::5AA 	0hll<88 	0%/H\" 	.X\\+66 	.$-H[!((LLr**LLb))
 
 :=D(,,S"555" 	&\\FMMBBHNN,<,<BBBCCCMr1   c           
         t          | dd          }|sdS |                    dd          }|                    dd          }|                    dd          }|r|r|sdS |                     ||d|||                    d          pd	          }	 |                    |           dS # t          $ r" t
                              d
||d           Y dS w xY w)zEPrime the session store so assistant threads get stable user scoping._session_storeNrA   r$   r  rE   r  r  )r  	chat_name	chat_typerE   r  
chat_topicz9[Slack] Failed to seed assistant thread session for %s/%sTrv  )r   rP   build_sourceget_or_create_sessionr   r   r  )r   r  session_storerA   r  rE   rZ  s          r2   _seed_assistant_thread_sessionz+SlackAdapter._seed_assistant_thread_session  s#   &6== 	F\\,33
LLb11	,,y"-- 	 	' 	F"" ||$899AT # 
 
	//77777 	 	 	LLK	       	s   B( ((CCc                    K   |                      |          }|                     |           |                     |           dS )zHHandle Slack Assistant lifecycle events that carry user/thread identity.N)r  r  r  )r   rA  r  s      r2   rV  z5SlackAdapter._handle_assistant_thread_lifecycle_event  sG      ::5AA--h777++H55555r1   c                   K   |                     dd          }|r| j                            |          rdS |                     d          s|                     d          dk    r| j        j                             dd          }|st          j        dd	          }t          |                                          	                                }|d	k    rdS |d
k    r,|                     dd          }| j
        rd| j
         d|vrdS |                     dd          }|r| j
        r|| j
        k    rdS |                     d          }|dv rdS |                     dd          }|}|                     d          }	|	rt          |	          }
|
r{|
	                                }|re||	                                vrOt                              d|dd                    |	                                dz   |z   	                                }t          |	          }|r,|	                                dz   |z   	                                }|                     d          pg }|rg }|D ]9}|                     dd          }|                     dd          p|                     dd          }|                     dd          }|                     dd          }|                     dd          }|                     d          r|r|r
d| d| d}n|rd| }n
|rd| }nd}|p|pd}|r4|	                                }t          |          d k    r|dd!         d"z   }|r
|r| d#| }n|r|}n
|rd| }n||v r|r| d$| d%}|                    |           ;|rid                    |          }|	                                dz   |z   	                                }t                              d&t          |                     |                     d'd          }|                     dd          }|                     |||                     d(d          )          }|                     d          p|                     d*d          }|s|                     d+d          }|                     d,          p*|                     d-          p|                     d-d          }|r|r
|| j        |<   |                     d.d          }|s|                    d/          rd0}|d1v }|rC|                     d(          p|                     d(          } | s|                                 r|} n|                     d(          p|} | j                             || j
                  }!|pd}"|!od|! d|"v }#|                     d(          }$t/          |$o|$|k              }%|s|!r||                                 v rni|                                 snT|                                 r|#sdS |#s:|%o|$| j        v }&|$duo|$| j        v }'|%o|                     ||$|2          }(|&s|'s|(sdS |#r|                    d|! dd          	                                }|$r|                                 sz| j                            |$           t          | j                  | j         k    rCtC          | j                  d| j         d3z           })|)D ]}*| j        "                    |*           |%r>|                     ||$|2          s&| #                    ||$||4           d{V }+|+r|+|z   }tH          j%        },|pd                    d5          rtH          j&        },g }-g }.g }/|                     d6g           }0|0D ]a}1|1                     d7          d8k    rr|1                     d9          }2|2s5	 | '                    |          (                    |2:           d{V }3|3                     d;          r	|3d<         }1nz| )                    |3|1=          }4|4r1|/                    |4           t          *                    d>|4           n/t          *                    d?|2|3                     d@                     n# tV          $ r}5tY          |5dAd          }6| )                    |6|1=          }4|4r1|/                    |4           t          *                    d>|4           nt          *                    dB|2|5dCD           Y d}5~5d}5~5ww xY w|1                     dEdF          }7|1                     dG          p|1                     dHd          }8|7                    dI          r|8r	 dJ|7-                    d5          dK         -                    dL          dM         z   }9|9dNvrdO}9| .                    |8|9|P           d{V }:|-                    |:           |.                    |7           q# tV          $ rs}5| /                    |5|1=          }4|4r1|/                    |4           t          *                    d>|4           nt          *                    dQ|8|5dCD           Y d}5~5d}5~5ww xY w|7                    dR          r|8r		 dJ|7-                    d5          dK         -                    dL          dM         z   }9|9dSvrdT}9| .                    |8|9dC|U           d{V }:|-                    |:           |.                    |7           # tV          $ rs}5| /                    |5|1=          }4|4r1|/                    |4           t          *                    d>|4           nt          *                    dV|8|5dCD           Y d}5~5d}5~5ww xY w|8rK	 |1                     dWd          };d}9|;r6t
          j0        1                    |;          \  }<}9|9                                }9|9s5|7r3dX te          j3                    D             }=|=                     |7d          }9|9td          vr|1                     dYdM          }>dZ}?|>r|>|?k    rt          *                    d[|>           | 4                    |8|P           d{V }@tk          |@|;pd\|9           }Atd          |9         }B|-                    |A           |.                    |B           t                              d]|A           d^}Ch d_}D|9|Dv rkt          |@          |Ck    rX	 |@6                    d`          }E|;pd\|9 }Fto          j8        dad%|F          }Fdb|F dc|E }G|r|G d| }n|G}n# tr          $ r Y nw xY w# tV          $ rs}5| /                    |5|1=          }4|4r1|/                    |4           t          *                    d>|4           nt          *                    dd|8|5dCD           Y d}5~5Yd}5~5ww xY wc|/r-ded                    df |/D                       z   }H|r|H d| n|H}|,tH          j&        k    rZ|.rXtu          dg |.D                       rtH          j;        },n2tu          dh |.D                       rtH          j<        },ntH          j=        },| >                    ||i           d{V }I| ?                    |||rdjndk||I| l          }JdMdml@mA}KmB}L  |K| j        j        |d          }M |L| j        j        |d          }Nd}O| r:| |k    r4	 | C                    || |n           d{V pd}On# tV          $ r d}OY nw xY wt          ||,|J|||-|.| |k    r| nd|M|O|No          }P|s|#o| E                                }Q|Qr| jF                            |           | G                    |P           d{V  dS )pz'Handle an incoming Slack message event.r
  r$   Nbot_idsubtypebot_message
allow_botsSLACK_ALLOW_BOTSnonementionsr<   rD   rB   rC   )message_changedmessage_deletedr7   zRSlack: extracted additional text from blocks (likely quoted/forwarded content): %si,  rh   z

attachmentsr   
title_linkfrom_urlfooterrO   is_msg_unfurlu   📎 [z](r?   u   📎 r  i  ...z
   z
   _r  z1Slack: appended %d link unfurl(s) to message textr@   r  )rA   r  rE   rA   r<  r:  channel_typeDim)r  mpim)rA   r  rE   r   )rA   r  
current_tsr:  rg  filesfile_accesscheck_file_infor   )r  okr  r   z
[Slack] %sz$[Slack] files.info failed for %s: %sr   r   z#[Slack] files.info error for %s: %sTrv  mimetyper;  url_private_downloadurl_privateimage/r   r2  ;r   ).jpgz.jpegz.pngz.gifz.webpr  )r:  z)[Slack] Failed to cache image from %s: %saudio/).oggz.mp3z.wavz.webmz.m4ar  )audior:  z)[Slack] Failed to cache audio from %s: %srJ   c                     i | ]\  }}||	S r0   r0   r  s      r2   r  z6SlackAdapter._handle_slack_message.<locals>.<dictcomp>  s    &Y&Y&Y1q!&Y&Y&Yr1   sizei  @z.[Slack] Document too large or unknown size: %sdocumentz [Slack] Cached user document: %si  >   .md.cfg.csv.ini.log.txt.xml.yml.json.toml.yamlr6  z	[^\w.\- ]z[Content of z]:
z,[Slack] Failed to cache document from %s: %sz[Slack attachment notice]
c              3       K   | ]	}d | V  
dS )z- Nr0   )r   rc  s     r2   r   z5SlackAdapter._handle_slack_message.<locals>.<genexpr>&  s)      DjDjRSX!XXDjDjDjDjDjDjr1   c              3   @   K   | ]}|                     d           V  dS )r  Nr   r   r,  s     r2   r   z5SlackAdapter._handle_slack_message.<locals>.<genexpr>*  s.      ??a1<<))??????r1   c              3   @   K   | ]}|                     d           V  dS )r  Nr  r  s     r2   r   z5SlackAdapter._handle_slack_message.<locals>.<genexpr>,  s.      AAQ\\(++AAAAAAr1   r  r  r*  )r  r  r  rE   	user_namer  )resolve_channel_promptresolve_channel_skills)rA   r  r:  )r<   message_typerZ  raw_messager  
media_urlsmedia_typesreply_to_message_idchannel_promptreply_to_text
auto_skill)HrP   r   is_duplicater   r  rx  ry  r*   r   r[   r   r|   r   r  r   r   rQ   rR   r  r   r   r  r   r   _slack_free_response_channels_slack_require_mention_slack_strict_mentionr   r   _has_active_session_for_threadrF  r  r   rz   r  _fetch_thread_contextr   TEXTCOMMANDr  
files_infor   r'  r   r   rz  _download_slack_filer  r  splitextr   r   _download_slack_file_bytesr   decoder  r<  UnicodeDecodeErrorr   PHOTOVOICEDOCUMENTrh  r  gateway.platforms.baser  r  _fetch_thread_parent_textr   rW  r   handle_message)Rr   rA  event_tsr  
text_checkmsg_userr  original_textr<   r7   blocks_textstripped_blocksblocks_payloadslack_attachments	att_partsatt	att_titleatt_urlatt_text
att_footeratt_fallbackheaderr,  sectionattachment_textrA   r
  assistant_metarE   r:  r  r  r  bot_uidrouting_textis_mentionedevent_thread_tsis_thread_replyreply_to_bot_threadin_mentioned_threadhas_session	to_remover3  thread_contextmsg_typer  r  attachment_noticesr  ffile_id	info_respdetailr-  r   r  r>   r  r  original_filenamer  mime_to_ext	file_sizeMAX_DOC_BYTES	raw_bytescached_pathdoc_mimeMAX_TEXT_INJECT_BYTESTEXT_INJECT_EXTENSIONStext_contentre  	injectionnotice_blockr  rZ  r  r  _channel_prompt_auto_skillr  	msg_event_should_reactsR                                                                                     r2   r?  z"SlackAdapter._handle_slack_message  s      99T2&& 	00:: 	F 99X 	%))I"6"6-"G"G*..|R@@J CY'96BB
Z..006688JV##z))"YYvr22
$ )Bd.?)B)B)B*)T)TF yy,,H D- (d>O2O2O ))I&&<<<F		&"-- 8$$ 	H9&AAK 
K #."3"3"5"5" Kdjjll'J'JLL@'-  
 !JJLL4//AHHJJD>vFFN H

v->EEGG "IIm44: :	#%I( 0* 0*GGGR00	'',33Nswwz27N7N7762.. WWXr22
"wwz266 77?++   " "=i==7===FF "0Y00FF ".W..FF!F  5<52 2::<<D4yy3#DSDzE1 d !'44d44GG $GG ,dnnGG d?? >!(==
===G  )))) "(++i"8"8

v-?FFHHG	NN  
 YYy"--
YYtR  ??!iiR00 @ 
 

 ))F##H~'9'9)R'H'H 	>'++L"==JIIf 1yy##1!!)R00 	  	5z 	5-4Dz* yy44 	 
 5 5c : : 	 L.  	5		+..Q.2D2D[2Q2QI !G!G!I!I 			+..4"I )--gt7HII$*B????l#B))K00H?b3HII 	 	T??AAAA0022 ++-- l ! #O4;O(O $ $4/ C'4+BB $
 $ ;;#-"1 ' <    + 3F { F 	;<<W44::<<D
  ;t'A'A'C'C ;'++O<<<t.//$2MMM $T%< = =>_t?Z^_?_>_ `I& ; ;/77::::  	-4#F#F!% $G $
 $
 	-
 $(#=#=%)	 $> $ $      N  -%, #R++C00 	+"*H 
(*		'2&& @	n @	nA uu]##'888%%++ &*&6&6z&B&B&M&MSZ&M&[&[ [ [ [ [ [ [I }}T** !%f-!%!?!?	TU!?!V!V! .55f==="NN<@@@@"NN F 'w)?)?   !  !   &q*d;;H!;;Hq;QQF i*11&999|V<<<<'LgWXcghhhHHHH uuZ33H%%.//K1553K3KC""8,, [n [nks 3 3B 7 = =c B B1 EEC"LLL$#'#<#<S#w#<#W#WWWWWWWF%%f---&&x0000  k k k!BB1qBQQF k*11&999|V<<<<'RTWYZeijjjk $$X.. Kn3 Knks 3 3B 7 = =c B B1 EEC"KKK$#'#<#<S#T[b#<#c#cccccccF%%f---&&x0000  k k k!BB1qBQQF k*11&999|V<<<<'RTWYZeijjjk  <n:n()fb(9(9%C( *!#!1!12C!D!D3!iikk  <8 <&Y&Y8P8V8X8X&Y&Y&Y)ooh;;":::  !"fa 0 0I$4M$ !	M(A(A'WYbccc  '+&E&EcSZ&E&[&[ [ [ [ [ [ [I";!#4#H8H38H8H# #K  8<H%%k222&&x000LL!C[QQQ -7). . .* 444YK`9`9`
!+4+;+;G+D+DL+<+P@P3@P@PL+-6,\+R+RL(W|(W(W(W(WI# 1*3'?'?'?'?'01 ! ! ! D! ! n n n!BB1qBQQF n*11&999|V<<<<'UWZ\]hlmmmnm<n|  	I8499DjDjWiDjDjDj;j;jjL26Hl.....LD{***{*??;????? 0&,AA[AAAAA 0&,&/ 11':1NNNNNNNN	 "" #0dd # 
 
 	ZYYYYYYY00Kz4
 
 -,Kz4
 
  	%b%&*&D&D)'# 'E ' ' ! ! ! ! ! ! ! 	 
  % % % $% !!!#-6"__		$*'"
 
 
	" .,MD4K4K4M4M 	/&**2...!!),,,,,,,,,,,s   !Cb))
d73A9d22d7Bh
j&A(jj6Bl>>
n;A(n66n;Bv;vB!v3Au:9v:
vvvv
x	A(xx	$ } }}dangerous commandrf  session_keyr   c                   K   | j         st          dd          S 	 t          |          dk    r|dd         dz   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gdg}|d|dd          |d}	|r||	d<    |                     |          j        d&i |	 d{V }
|
                    d d!          }|r
d| j        |<   t          d"||
#          S # t          $ rE}t          
                    d$|d"%           t          dt          |                    cY d}~S d}~ww xY w)'u   Send a Block Kit approval prompt with interactive buttons.

        The buttons call ``resolve_gateway_approval()`` to unblock the waiting
        agent thread — same mechanism as the text ``/approve`` flow.
        Fr  r  T  Nr  r  r  z):warning: *Command Approval Required*
```z```
Reason: r;   r<   actionsbutton
plain_textz
Allow Onceprimaryrn  r;   r<   re   r   r   zAllow Sessionro  r;   r<   r   r   zAlways Allowrp  Denydangerrq  r;   r9   u"   ⚠️ Command approval required: d   r@   r<   r7   r  r
  r$   Tr  z%[Slack] send_exec_approval failed: %srv  r0   )r   r   r   r  r  r  rP   r   r   r   r   r*   )r   r  rf  r,  r   r  cmd_previewr  r7   r  r  msg_tsr-  s                r2   send_exec_approvalzSlackAdapter.send_exec_approvalp  sV      y 	De?CCCCA	;47LL44G4G'%4%.500WK//h??I & (5"-5 5'25 5 
 
 & %--9<$P$P%.)>%0  %--9?$S$S)A%0	  %--9>$R$R)@%0	  %--96$J$J%-)6%0 )! +F\ #P[#=NPP & &F
  0&/{#E4++G44EOOOOOOOOOOFZZb))F 827'/dvFSSSS 	; 	; 	;LL@!dLSSSe3q66:::::::::	;s   C"C? ?
E	:E	E	Er   r  
confirm_idc           
        K   | j         st          dd          S 	 t          |          dk    r|dd         dz   n|}|                     d|          }| d| }	dd	d
|p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g}
||pd d|dd          |
d}|r||d<    |                     |          j        d&i | d{V }t          d |                    d!d"          |#          S # t          $ rE}t          	                    d$|d %           t          dt          |                    cY d}~S d}~ww xY w)'z@Send a Block Kit three-option slash-command confirmation prompt.Fr  r  r.  Nr  r`  r  r  r;  Confirmz*

r/  r0  r1  r2  zApprove Oncer3  rs  r4  zAlways Approvert  r5  Cancelr7  ru  r8  : r9  r:  r  Tr
  r$   r  z%[Slack] send_slash_confirm failed: %srv  r0   )r   r   r   r  r  r  rP   r   r   r   r*   )r   r  r   r  r,  r>  r  r,  r  r   r7   r  r  r-  s                 r2   send_slash_confirmzSlackAdapter.send_slash_confirm  s5     
 y 	De?CCCC6	;-0\\D-@-@75D5>E))gD//h??I #11Z11E & ( CE$6Y C CT C C   & %--9>$R$R%.)>%*  %--9CS$T$T)@%*	  %--98$L$L%-)@%* ! !FH # -I==dsd== & &F
  0&/{#E4++G44EOOOOOOOOOOFdvzz$7K7KZ`aaaa 	; 	; 	;LL@!dLSSSe3q66:::::::::	;s   CC5 5
E?:D?9E?Ec                   K    |             d{V  |                     dd          }|                     dd          }|                     di           }|                     dd          }|                     di                                dd          }|                     d	i                                d
d          }	|                     d	i                                dd          }
t          j        dd                                          }|rEd |                    d          D             }d|vr"|
|vrt
                              d|	|
           dS d|vrt
                              d|           dS |                    dd          \  }}dddd}|                     |d          }d|	 d|	 d|	 d}|                     |d|	           }d}|                     dg           D ]G}|                     d          dk    r,|                     d i                                d d          } nHdd!|pd"d#d#d$d!|d#gd%g}	 |                     |                              ||||&           d{V  n2# t          $ r%}t
                              d'|           Y d}~nd}~ww xY w	 d(d)l
m} |                    |||           d{V }|rI||d*}|                     d+          p|}|r||d+<    |                     |          j        d0i | d{V  t
                              d,|||	           dS # t          $ r(}t
                              d-|d./           Y d}~dS d}~ww xY w)1z3Handle a slash-confirm button click from Block Kit.Nr   r$   r   r  r
  r@   r   rC   rJ   r;  SLACK_ALLOWED_USERSc                 ^    h | ]*}|                                 |                                 +S r0   r2  r   r  s     r2   	<setcomp>z<SlackAdapter._handle_slash_confirm_action.<locals>.<setcomp>	  -    XXX3CIIKKX399;;XXXr1   r4  r;  u@   [Slack] Unauthorized slash-confirm click by %s (%s) — ignoringr`  z)[Slack] Malformed slash-confirm value: %src   oncealwayscancelrr     ✅ Approved once by u   🔒 Always approved by u   ❌ Cancelled by )rJ  rK  rL  Resolved by r7   r;   r  r<   r  zConfirmation promptr/  r  r8  r@   r
  r<   r7   z2[Slack] Failed to update slash-confirm message: %sr   )slash_confirm)r@   r<   r  zGSlack button resolved slash-confirm for session %s (choice=%s, user=%s)z5Failed to resolve slash-confirm from Slack button: %sTrv  r0   )rP   rx  ry  r[   rz  r   r'  r  r  r   toolsrP  resolver  r   r   )r   rj  r,  r  r   r   r  r<  rA   r  rE   allowed_csvallowed_idsr,  r>  
choice_mapchoice	label_mapdecision_textr  r{   updated_blocksr-  _slash_confirm_modresult_textpost_kwargsr  r   s                               r2   r  z)SlackAdapter._handle_slash_confirm_action 	  s     ceeJJ{B//	

7B''((9b))T2&&XXi,,00r::
HHVR((,,VY??	((62&&**444 i 5r::@@BB 	XX+2C2CC2H2HXXXK+%%'*D*DVw    eNNFNNNF"'++c1"5"5Z $*%-%-
 


 	844 8I77<<<5)55
 
	
 "f.HY.H.HII [[2.. 	 	Eyy  I-- %		&" 5 5 9 9&" E E . "$)B-B   "%}== 
 	T"":..::""%	 ;            	T 	T 	TNNOQRSSSSSSSS	T	fAAAAAA 2 : :;
TZ [ [[[[[[[K 	S)'/ /
 $KK44>	 9/8K,Cd&&z22CRRkRRRRRRRRRKKYVY      	f 	f 	fLLPRU`dLeeeeeeeee	fs1   	2I< <
J+J&&J+/BL< <
M.M))M.c                   K    |             d{V  |                     dd          }|                     dd          }|                     di           }|                     dd          }|                     di                                dd          }|                     d	i                                d
d          }	|                     d	i                                dd          }
t          j        dd                                          }|rEd |                    d          D             }d|vr"|
|vrt
                              d|	|
           dS ddddd}|                     |d          }| j                            |d          rdS d|	 d|	 d|	 d|	 d}|                     |d|	           }d}|                     dg           D ]G}|                     d          dk    r,|                     d i                                d d          } nHdd!|pd"d#d#d$d!|d#gd%g}	 | 	                    |          
                    ||||&           d{V  n2# t          $ r%}t
                              d'|           Y d}~nd}~ww xY w	 d(d)lm}  |||          }t
                              d*||||	           dS # t          $ r&}t
                              d+|           Y d}~dS d}~ww xY w),z/Handle an approval button click from Block Kit.Nr   r$   r   r  r
  r@   r   rC   rJ   r;  rE  c                 ^    h | ]*}|                                 |                                 +S r0   r2  rG  s     r2   rH  z7SlackAdapter._handle_approval_action.<locals>.<setcomp>s	  rI  r1   r4  r;  u;   [Slack] Unauthorized approval click by %s (%s) — ignoringrJ  r*  rK  denyrm  TrM  u   ✅ Approved for session by u   ✅ Approved permanently by u   ❌ Denied by )rJ  r*  rK  r_  rN  r7   r;   r  r<   r  zCommand approval requestr/  r  r8  rO  z-[Slack] Failed to update approval message: %sr   )resolve_gateway_approvalzHSlack button resolved %d approval(s) for session %s (choice=%s, user=%s)z8Failed to resolve gateway approval from Slack button: %s)rP   rx  ry  r[   rz  r   r'  r   r  r  r  r   tools.approvalr`  r   r   )r   rj  r,  r  r   r,  r  r<  rA   r  rE   rS  rT  rU  rV  rW  rX  r  r{   rY  r-  r`  countr   s                           r2   r  z$SlackAdapter._handle_approval_actionb	  s     ceeJJ{B//	jj"--((9b))T2&&XXi,,00r::
HHVR((,,VY??	((62&&**444
 i 5r::@@BB 	XX+2C2CC2H2HXXXK+%%'*D*DQw    $*&/%-!	
 

 	622 "&&vt44 	F 8I77AiAA@Y@@0Y00	
 
	 "f.HY.H.HII [[2.. 	 	Eyy  I-- %		&" 5 5 9 9&" E E . "$)G-G   "%}== 
 	O"":..::""%	 ;            	O 	O 	ONNJANNNNNNNN	O	Z??????,,[&AAEKKZ{FI      	Z 	Z 	ZLLSUXYYYYYYYYY	Zs0   12I$ $
J.JJ0K	 	
K9K44K9   r  r:  limitc                   K   | d| d| }t          j                    }| j                            |          }|r||j        z
  | j        k     r|j        S 	 |                     |          }	d}
t          d          D ]}	 |		                    |||dz   d           d{V }
 n# t          $ r}t          |                                          }d|v pd|v pd	|v }|rL|d
k     rFdd
|z  z  }t                              d||dz              t          j        |           d{V  Y d}~ d}~ww xY w|
dS |
                    dg           }|sdS | j                            || j                  }g }d}|D ]{}|                    dd          }||k    r ||k    }t'          |                    d                    p|                    d          dk    }|                    dd          }|                    d          p|}|r| j                            |          ndp| j        }|r|s	|r||k    r|                    dd                                          }|s|r,|                    d| dd                                          }|rdnd}|pd}|r|s|                    d          pd}|                     ||           d{V }|                    | | d|            |r|}}d}|rdd                    |          z   d z   }t3          ||t5          |          |!          | j        |<   |S # t          $ r&} t                              d"|            Y d} ~ dS d} ~ ww xY w)#u  Fetch recent thread messages to provide context when the bot is
        mentioned mid-thread for the first time.

        This method is only called when there is NO active session for the
        thread (guarded at the call site by _has_active_session_for_thread).
        That guard ensures thread messages are prepended only on the very
        first turn — after that the session history already holds them, so
        there is no duplication across subsequent turns.

        Results are cached for _THREAD_CACHE_TTL seconds per thread to avoid
        hammering conversations.replies (Tier 3, ~50 req/min).

        Returns a formatted string with prior thread history, or empty string
        on failure or if the thread has no prior messages.
        rI   Nr  rc   Tr@   r
  rd  	inclusiver  r  r  r   g      ?zL[Slack] conversations.replies rate limited; retrying in %.1fs (attempt %d/3)r$   messagesr
  r  r  r  rC   r<  r<   rD   rB   z[thread parent] r;  usernamebotr  rB  uV   [Thread context — prior messages in this thread (not yet in conversation history):]
rh   z
[End of thread context]

)r    r"   r#   r%   z*[Slack] Failed to fetch thread context: %s)r,   r-   r   rP   r"   r   r    r  rL   conversations_repliesr   r*   r   r   r'  r  r  r   r   r   r[   rF  rh  rQ   rR   r   r   )!r   rA   r  r  r:  rd  	cache_keyr  r  r   r  r  r   err_stris_rate_limitretry_afterrh  r  context_partsr%   msgr<  	is_parentis_botr  msg_teamself_bot_uidmsg_textr]   display_userrJ   r    r-  s!                                    r2   r  z"SlackAdapter._fetch_thread_context	  s     & "99I9999	n+//	:: 	"sV..$2HHH>!n	%%j11F F 88  #)#?#? *$#ai"&	 $@ $ $      F E    !#hhnn..G%0 5 G+5)W4 "
 % !1&)Q'\&:j'1   &mK888888888 !$ ~rzz*b11H r-11'4;LMMGMK 0+ 0+r** Z''"i/	cggh//00WCGGI4F4F-4W7762.. 776??5g  D+//999 ' &	  % % !L007762..4466   M'//WDDJJLLH/8@++b'49 @, @#&77:#6#6#?%L!44\:4VVVVVVVV$$%B%B%B%B%BCCC +"*KG mii../56  5H!-00'	5 5 5D&y1 N 	 	 	NNGKKK22222	sV   (L<  "B$"L< $
D/.A6D*$L< )D**D//L< 6L< G+L< <
M,M''M,c                   K   | d| d| }t          j                    }| j                            |          }|r||j        z
  | j        k     r|j        S 	 |                     |          }|                    ||dd           d{V }|r|                    dg           ng }	|	sdS |	d         }
|
                    d	d          |k    rdS | j	                            || j
                  }|
                    d
          pd                                }|r,|                    d| dd                                          }|S # t          $ r&}t                              d|           Y d}~dS d}~ww xY w)u  Return the raw text of the thread parent message (for reply_to_text).

        Uses the same per-thread cache as :meth:`_fetch_thread_context` to avoid
        hitting ``conversations.replies`` twice. Falls back to a cheap single-
        message fetch (``limit=1, inclusive=True``) when the cache is cold.

        Returns empty string on any failure — callers should treat an empty
        return as "no parent context to inject".
        rI   rc   Trf  Nrh  r$   r   r
  r<   rD   rB   z.[Slack] Failed to fetch thread parent text: %s)r,   r-   r   rP   r"   r   r%   r  rk  r   r   r[   rF  r   r   r  )r   rA   r  r:  rl  r  r  r   r  rh  parentr  r<   r   s                 r2   r  z&SlackAdapter._fetch_thread_parent_textJ
  s      "99I9999	n+//	:: 	&sV..$2HHH%%	%%j11F!77"	 8        F 6<Cvzz*b111H ra[Fzz$##y00r-11'4;LMMGJJv&&,"3355D A||OOOOR88>>@@K 	 	 	LLI3OOO22222	s&   AE )"E A8E 
E6E11E6c                 J  K   |                     d          pd                    d                                          }|                     dd                                          }|                     dd          }|                     dd          }|                     dd          }|r|r
|| j        |<   |dv rd	d
lm}  |            }d|d<   |r|                                d	         nd}	|	|v rS|t          |	          d                                         }
|
r||	          d|
                                 n||	         }n |rnd}nd| d|                                 }t          |          	                    d          }| 
                    ||rdnd|          }t          ||	                    d          rt          j        nt          j        ||          }|                     dd          }|r9|r7|r5|	                    d          r |t          j                    d| j        ||f<   t$                              |pd          }	 |                     |           d{V  t$                              |           dS # t$                              |           w xY w)uo  Handle Slack slash commands.

        Every gateway command in COMMAND_REGISTRY is registered as a native
        Slack slash (``/btw``, ``/stop``, ``/model``, etc.), matching the
        Discord and Telegram model. The slash name itself is the command;
        any text after it is the argument list.

        The legacy ``/hermes <subcommand> [args]`` form is preserved for
        backward compatibility with older workspace manifests and for users
        who want a single entry point for free-form questions (``/hermes
        what's the weather`` — non-slash text is treated as a regular
        message).
        rf  r$   rg  r<   rE   rA   r:  )hermesr$   r   )slack_subcommand_mapz	/compresscompactNrZ   z/helpr  r  r*  )r  r  rE   )r<   r  rZ  r  r  )r  r
  )rP   rh  r[   r   r  r|  rz  r   r*   r   r  r   r   r  r  r,   r-   r   r   r   r  reset)r   rf  
slash_namer<   rE   rA   r:  r|  subcommand_map
first_wordrestr  rZ  rA  r  _slash_user_id_tokens                   r2   ri  z"SlackAdapter._handle_slash_commands
  s       kk),,2::3??EEGG
{{62&&,,..++i,,[[r22
++i,,  	5z 	5-4Dz*'' A@@@@@1133N(3N9%,08abJ^++C
OO,,-3355IMm.4==t==CCEEESablSm  +z**D**0022D J**3//""#0dd # 
 
 040D0DZ,,+JZ	
 
 
 {{>266 	G 	
 	ts7K7K 	 ,n&&C CD(*g)>?  .11'/TBB	7%%e,,,,,,,,,  !566666N  !56666s   J J"c                 `   t          | dd          }|sdS 	 ddlm}m}  |t          j        |d||          }t          |dd          }|rt          |d	d
          nd
}	|rt          |dd          nd}
 |||	|
          }|                                 ||j        v S # t          $ r Y dS w xY w)u  Check if there's an active session for a thread.

        Used to determine if thread replies without @mentions should be
        processed (they should if there's an active session).

        Uses ``build_session_key()`` as the single source of truth for key
        construction — avoids the bug where manual key building didn't
        respect ``thread_sessions_per_user`` and ``group_sessions_per_user``
        settings correctly.
        r  NFr   )SessionSourcebuild_session_keyr*  )platformr  r  rE   r  r   group_sessions_per_userTthread_sessions_per_user)r  r  )	r   gateway.sessionr  r  r   r   _ensure_loaded_entriesr   )r   rA   r  rE   r  r  r  rZ  	store_cfggsputspur,  s               r2   r  z+SlackAdapter._has_active_session_for_thread
  s      &6== 	5	HHHHHHHH"]!"!#  F  x>>IJS]79&?FFFY]DLU`79&@%HHH[`D++(,)-  K ((***-"888 	 	 	55	s   BB 
B-,B-r>   r  r  c           
        K   ddl }|r|| j        v r| j        |         j        n| j        j        }|                    dd          4 d{V }t          d          D ]V}	 |                    |dd| i	           d{V }	|	                                 |	j                            d
d          }
d|
v rt          d|
 d          |r+ddl
m}  ||	j        |          c cddd          d{V  S ddl
m}  ||	j        |          c cddd          d{V  S # |j        |j        f$ r}t!          ||j                  r|j        j        dk     r |dk     rNt&                              d|dz   |dd         |           t+          j        d|dz   z             d{V  Y d}~O d}~ww xY w	 ddd          d{V  dS # 1 d{V swxY w Y   dS )z?Download a Slack file using the bot token for auth, with retry.r   Nr  Tr  r  AuthorizationBearer r  r  r$   	text/htmlz4Slack returned HTML instead of media (content-type: .); check bot token scopes and file permissions)cache_audio_from_bytes)cache_image_from_bytesr  r   )Slack file download retry %d/2 for %s: %src   P   r  )r   r   r8  r   r  rL   rP   r  r  rq  r  r  r    r  TimeoutExceptionr  r   r   r  r   r  r  r  )r   r>   r  r  r:  r   	bot_tokenr   r  r   r  r  r  r   s                 r2   r  z!SlackAdapter._download_slack_file
  s     9@yWPTPbEbEbD&w/55hlhshy	$$TD$II #	 #	 #	 #	 #	 #	 #	V 88 " "!%+ZZ!02GI2G2G H &0 & &            H --/// ")--nbAAB"b(((J.0J J J    MQQQQQQ55h6FLLLL/#	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	2 RQQQQQ55h6FLLLL5#	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	6 .0EF   !#u'<== #,BZ]`B`B`{{%P%,q[#crc(CA A A%mC7Q;,?@@@@@@@@@ 5"#	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	 #	sP   G	!B D!!G	6D!G	!F22A4F-&G	,F--F22G		
GGc           
      v  K   ddl }|r|| j        v r| j        |         j        n| j        j        }|                    dd          4 d{V }t          d          D ]5}	 |                    |dd| i	           d{V }|                                 |j                            d
d          }d|v rt          d| d          |j
        c cddd          d{V  S # |j        |j        t          f$ r}	t          |	|j                  r|	j        j        dk     r t          |	t                    r |dk     rNt                               d|dz   |dd         |	           t%          j        d|dz   z             d{V  Y d}	~	. d}	~	ww xY w	 ddd          d{V  dS # 1 d{V swxY w Y   dS )z7Download a Slack file and return raw bytes, with retry.r   Nr  Tr  r  r  r  r  r  r$   r  z9Slack returned HTML instead of file bytes (content-type: r  r  r   r  rc   r  r  )r   r   r8  r   r  rL   rP   r  r  rq  r    r  r  r   r   r  r   r  r  r  )
r   r>   r:  r   r  r   r  r   r  r   s
             r2   r  z'SlackAdapter._download_slack_file_bytes   s     9@yWPTPbEbEbD&w/55hlhshy	$$TD$II 	 	 	 	 	 	 	V 88  %+ZZ!02GI2G2G H &0 & &            H --///!)--nbAAB"b(((J.0J J J  
 $+++	 	 	 	 	 	 	 	 	 	 	 	 	 	  .0EzR 
 
 
!#u'<== #,BZ]`B`B`!#z22 {{%P%,q[#crc(CA A A%mC7Q;,?@@@@@@@@@ 
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	sD   F(!A.C$F($F;B
FF(FFF((
F25F2c                 
   | j         j                            d          }|:t          |t                    r|                                dvS t          |          S t          j        dd                                          dvS )a  Return whether channel messages require an explicit bot mention.

        Uses explicit-false parsing (like Discord/Matrix) rather than
        truthy parsing, since the safe default is True (gating on).
        Unrecognised or empty values keep gating enabled.
        require_mentionN)rT  rU  rV  offSLACK_REQUIRE_MENTIONr  	r   r  rP   r   r*   r   r   rx  ry  r   
configureds     r2   r  z#SlackAdapter._slack_require_mentionD  s     [&**+<==
!*c** M!''))1LLL
###y0&99??AAIdddr1   c                 
   | j         j                            d          }|:t          |t                    r|                                dv S t          |          S t          j        dd                                          dv S )zWhen true, channel threads require an explicit @-mention on every
        message. Disables all auto-triggers (mentioned-thread memory,
        bot-message follow-up, session-presence). Defaults to False.
        strict_mentionN)r  r  r  r  SLACK_STRICT_MENTIONrT  r  r  s     r2   r  z"SlackAdapter._slack_strict_mentionR  s    
 [&**+;<<
!*c** H!''))-GGG
###y/99??AAE___r1   c                 X   | j         j                            d          }|t          j        dd          }t          |t                    rd |D             S |!t          |                                          nd}|rd |	                    d          D             S t                      S )z1Return channel IDs where no @mention is required.free_response_channelsNSLACK_FREE_RESPONSE_CHANNELSr$   c                     h | ]D}t          |                                          #t          |                                          ES r0   )r*   r[   r  s     r2   rH  z=SlackAdapter._slack_free_response_channels.<locals>.<setcomp>d  s=    KKK$T9J9JKCIIOO%%KKKr1   c                 ^    h | ]*}|                                 |                                 +S r0   r2  r  s     r2   rH  z=SlackAdapter._slack_free_response_channels.<locals>.<setcomp>m  s-    JJJTTZZ\\JDJJLLJJJr1   r4  )r   r  rP   rx  ry  r   rz   r*   r[   rz  r   )r   r  ss      r2   r  z*SlackAdapter._slack_free_response_channels^  s    k##$<==;):B??Cc4   	LKK#KKKK !$CHHNNR 	KJJQWWS\\JJJJuur1   )r3   N)NNr   )NNN)Nr  )r$   )NNNN)r$   r$   )r+  N)r$   rc  )Fr$   )Lr&   r'   r(   r)   r"  r   r   r   r   r   r*   r   r   r  r  r  r.  r   r  r  r  r   r  r  r  r  r  r  r  r  r	   r   r.   r  r  r  r   rM  rQ  rW  r   r[  r   rb  rh  rl  ru  rx  r{  r~  r  r  r   r  r  r  r  rV  r?  r=  rC  r  r  r/   r  r  ri  r  r  bytesr  r  r  r   r  __classcell__)r   s   @r2   r   r     s         +Q~ +Q +Q +Q +Q +Q +QZ `d   # HTRUWZRZ^D\ hpqthu    4 hl   I HUYZ]_bZbUcLd pxy|p}    F N%;%;	$sCx.	!%; %; %; %;N09#s(^09 09 
	09 09 09 09dC.t C. C. C. C.J, , , , 3  3         #'-1K; K;K; K; 3-	K;
 4S>*K; 
K; K; K; K;d #'-1"; ";"; "; 	";
 3-"; 4S>*"; 
"; "; "; ";T ; ; ;; ; 	; ; 
; ; ; ;>N N N N N N N:T T T T T T T F4 F F F F  #'-1$ $3-$ 4S>*$ 
#	$ $ $ $T "&"&-1( (( ( #	(
 3-( 4S>*( 
( ( ( (\ .2 gf gfgf U38_%gf 4S>*	gf
 gf 
gf gf gf gf gf gfR5C 5HSM 5VZ 5 5 5 5.i .D . . . .*nc nc n n n nd'*36	    '*36	   XD X X X X	=| 	= 	= 	= 	= 	=:, :IZ :_c : : : :&  c 3    D "&"&-1X XX X #	X
 3-X 4S>*X 
X X X X< "&"&-1> >> > #	>
 3-> 4S>*> 
> > > > > >H "&"&-1; ;; ; #	;
 3-; 4S>*; 
; ; ; ;6 "&"&-16X 6X6X 6X #	6X
 3-6X 4S>*6X 
6X 6X 6X 6Xx "&#'"&-19X 9X9X 9X #	9X
 C=9X 3-9X 4S>*9X 
9X 9X 9X 9Xv83 84S> 8 8 8 801 1 1QVWZ\_W_Q`Ha 1 1 1 1%
 %
c3h %
 %
 %
 %
N5c3h 5D 5 5 5 54 	   	
 
c3h   0tCH~ $    >6D 6T 6 6 6 6}- }-$ }- }- }- }-F /-1N; N;N;%(N;7:N;N; 4S>*N; 
	N; N; N; N;d ?C>; >;>;#&>;14>;CF>;>;#+DcN#;>; 
>; >; >; >;@`f `f `f `fDYZ YZ YZ YZB )+G GG*-G;>GG"%G 
G G G GT ?A' ''*-'8;'	' ' ' 'RQ74 Q7D Q7 Q7 Q7 Q7f-- - 	-
 
- - - -^) )c ) )D )[^ )hk ) ) ) )V   C  #  u        He e e e e
`t 
` 
` 
` 
`s        r1   r   )r}   )Er)   r  contextvarsr   loggingrx  r  r,   dataclassesr   r   typingr   r   r   r   r	   slack_bolt.async_appr
   ,slack_bolt.adapter.socket_mode.async_handlerr   slack_sdk.web.async_clientr   r#  r5   ImportErrorsyspathlibr   _Pathr  insertr*   __file__rR  parentsgateway.configr   r   gateway.platforms.helpersr   r  r   r   r   r   r   r   r   r   r   r   	getLoggerr&   r   
ContextVarr   r+   r   r   r6   rz   r|   r/   r   r   r   r   r   r0   r1   r2   <module>r     s            				 				  ( ( ( ( ( ( ( ( 3 3 3 3 3 3 3 3 3 3 3 3 3 3
------SSSSSS999999NNNOO   OH NNN	 


 ! ! ! ! ! ! 33uuX..008;<< = = = 3 3 3 3 3 3 3 3 9 9 9 9 9 9                        
	8	$	$ 9O8Nd9 9 9&x}5   
        $    
XD XS X X X Xv<R <Rd <Rs <Rc <R <R <R <R~!s !x} ! ! ! ! ! (3-    *b) b) b) b) b)& b) b) b) b) b)s   A A A 