
    epjR                       d Z ddlm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	 ddl
mZmZmZmZ ddlmZ 	 ddlmZ dZn# e$ r d	ZdZY nw xY wddlZddlZ	 ej                            d
          duZn# e$ r d
ej        v ZY nw xY wdZdZdZ dZ!dZ"dZ#dZ$dZ%dZ&dZ'dZ(e)Z*dZ+dZ,dZ-dZ.dZ/dZ0ddl1m2Z2m3Z3 ddl4m5Z5 ddl6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z< ddl=m>Z? ddl=m@ZA dAdZB ejC        eD          ZEdZFdZGdZHdZId	ddBdZJeFddCdZK G d d          ZL G d d           ZM G d! d"          ZNdDd#ZOdDd$ZPdDd%ZQdEd'ZRd(ZS eTd)d*h          ZUddlVZW eWjX        d+          ZYdFd/ZZddd	d0dGd8Z[e	dHd:            Z\dDd;Z] G d< d=e7          Z^dId?Z_dId@Z`dS )Ja  
Microsoft Teams platform adapter for Hermes Agent.

Uses the microsoft-teams-apps SDK for authentication and activity processing.
Runs an aiohttp webhook server to receive messages from Teams.
Proactive messaging (send, typing) uses the SDK's App.send() method.

Requires:
    pip install microsoft-teams-apps aiohttp
    TEAMS_CLIENT_ID, TEAMS_CLIENT_SECRET, and TEAMS_TENANT_ID env vars

Configuration in config.yaml:
    platforms:
      teams:
        enabled: true
        extra:
          client_id: "your-client-id"      # or TEAMS_CLIENT_ID env var
          client_secret: "your-secret"      # or TEAMS_CLIENT_SECRET env var
          tenant_id: "your-tenant-id"       # or TEAMS_TENANT_ID env var
          port: 3978                        # or TEAMS_PORT env var
    )annotationsN)contextmanager)AnyDictIteratorOptional)quotewebTFmicrosoft_teams)PlatformPlatformConfig)MessageDeduplicator)BasePlatformAdapterMessageEventMessageType
SendResultcache_image_from_urlcache_media_bytes)UnscopedSecretError)
get_secretc                z    	 t          | |          }n$# t          $ r t          j        |           }Y nw xY w||n|S )a  Scope-aware credential read with the default-profile startup fallback.

    Secondary profiles construct their adapters under a profile secret
    scope -- the scope is authoritative and a scoped miss returns ``default``
    (no cross-profile borrow from ``os.environ``, which may hold another
    profile's value). The DEFAULT profile's adapter constructs and sends
    *unscoped* under multiplexing, where a bare ``get_secret`` would raise
    ``UnscopedSecretError`` and crash this path; there ``os.environ`` is that
    profile's own value, so fall back to it. Same pattern as the Slack
    ``SLACK_APP_TOKEN`` read (#59739) and
    ``gateway/platforms/whatsapp_common.py::_get_wsecret``.
    )_scoped_get_secret_UnscopedSecretErrorosgetenv)namedefaultvals      E/home/thesage/.hermes/hermes-agent/plugins/platforms/teams/adapter.py_get_scoped_secretr!   b   sR     w//   ioo/33w.s    44i  i   z/api/messagesr   valuer   r   boolreturnc                   t          | t                    r| S t          | t                    r2|                                                                 }|dv rdS |dv rdS |S )N>   1onyestrueT>   0noofffalseF)
isinstancer$   strstriplower)r#   r   
normalizeds      r    _parse_boolr4      sk    % % [[]]((**
33344445N    intc               T    	 t          |           S # t          t          f$ r |cY S w xY wN)r6   	TypeError
ValueError)r#   r   s     r    _coerce_portr;      s<    5zzz"   s    ''c                  0    e Zd ZdZddZdddd
ZddZdS )_StaticAccessTokenProviderzSMinimal token-provider shim so outbound Graph delivery can reuse the shared client.access_tokenr0   c                V    t          |pd                                          | _        d S N )r0   r1   _access_token)selfr>   s     r    __init__z#_StaticAccessTokenProvider.__init__   s)     !344::<<r5   F)force_refreshrE   r$   r%   c               B   K   ~| j         st          d          | j         S )Nz=TEAMS_GRAPH_ACCESS_TOKEN is required for graph delivery mode.)rB   r:   )rC   rE   s     r    get_access_tokenz+_StaticAccessTokenProvider.get_access_token   s-      ! 	^\]]]!!r5   Nonec                    d S r8    rC   s    r    clear_cachez&_StaticAccessTokenProvider.clear_cache   s    tr5   N)r>   r0   )rE   r$   r%   r0   r%   rH   )__name__
__module____qualname____doc__rD   rG   rL   rJ   r5   r    r=   r=      sf        ]]= = = = ?D " " " " " "     r5   r=   c                      e Zd ZdZ	 d#dddd$dZ	 d#d%dZd&dZd'dZd'dZd(dZ	d)dZ
d)dZed)d            Zed*d            Zed+d"            ZdS ),TeamsSummaryWritera  Pipeline-facing Teams outbound delivery surface.

    This stays inside the existing Teams platform plugin so the meeting-pipeline
    PR can reuse one Teams integration surface instead of introducing a second
    adapter elsewhere in the gateway core.
    N)graph_client	transportplatform_configPlatformConfig | NonerT   
Any | NonerU   httpx.AsyncBaseTransport | Noner%   rH   c               0    || _         || _        || _        d S r8   )_platform_config_graph_client
_transport)rC   rV   rT   rU   s       r    rD   zTeamsSummaryWriter.__init__   s     !0)#r5   payloadr   configdict[str, Any] | Noneexisting_recordOptional[dict[str, Any]]dict[str, Any]c                  K   |                      |          }|r3t          |                    d          d          st          |          S t	          |                    d          p|                    d          pd                                                                          }|sY|                    d          rd}nA|                    d	          s*|                    d
          r|                    d          rd}|dk    r|                     ||           d {V S |dk    r|                     ||           d {V S t          d          )Nforce_resendFr"   delivery_modemoderA   incoming_webhook_urlincoming_webhookchat_idteam_id
channel_idgraphz:Teams delivery_mode must be 'incoming_webhook' or 'graph'.)
_resolve_delivery_configr4   getdictr0   r1   r2   #_write_summary_via_incoming_webhook_write_summary_via_graphr:   )rC   r^   r_   ra   mergedrg   s         r    write_summaryz TeamsSummaryWriter.write_summary   sz      ..v66 	);vzz./I/ISX#Y#Y#Y 	)(((6::o..J&**V2D2DJKKQQSSYY[[ 	zz011 )I&& 

9%%*0**\*B*B %%%AA'6RRRRRRRRR7??66wGGGGGGGGGH
 
 	
r5   c           	        i }| j         }|e|                    t          |j        pi                      |j        rd|vr
|j        |d<   |j        r |                    d|j        j                   |                    t          |pi                      t          j	        dd          t          j	        dd          t          dd          t          j	        dd          t          j	        dd          t          j	        d	d          d
}|                                D ]!\  }}|r|                    |          s|||<   "|S )Nr>   rl   TEAMS_DELIVERY_MODErA   TEAMS_INCOMING_WEBHOOK_URLTEAMS_GRAPH_ACCESS_TOKENTEAMS_TEAM_IDTEAMS_CHANNEL_IDTEAMS_CHAT_ID)rf   rh   r>   rk   rl   rj   )r[   updaterp   extratokenhome_channel
setdefaultrj   r   r   r!   itemsro   )rC   r_   rs   platform_cfgenv_defaultskeyr#   s          r    rn   z+TeamsSummaryWriter._resolve_delivery_config   sR   !#,#MM$|17R88999! <nF&B&B)5);~&( S!!,0I0QRRRd6<R(()))  Y'<bAA$&I.JB$O$O./I2NNy"55)$6;;y"55
 
 ',,.. 	$ 	$JC $VZZ__ $#sr5   c                  K   dd l }t          |                    d          pd                                          }|st	          d          d|                     |          i}|                    d| j                  4 d {V }|                    ||           d {V }|	                                 d d d           d {V  n# 1 d {V swxY w Y   d	||j
        d
dS )Nr   rh   rA   zATEAMS_INCOMING_WEBHOOK_URL is required for incoming_webhook mode.textg      4@)timeoutrU   )jsonri   T)rf   webhook_urlstatus_code	delivered)httpxr0   ro   r1   r:   _render_summary_markdownAsyncClientr]   postraise_for_statusr   )rC   r^   r_   r   r   bodyclientresponses           r    rq   z6TeamsSummaryWriter._write_summary_via_incoming_webhook   s      	&**%;<<BCCIIKK 	b`aaa55g>>?$$TT_$MM 	( 	( 	( 	( 	( 	( 	(QW#[[4[@@@@@@@@H%%'''	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 0&#/	
 
 	
s   2C
CCc                  K   |                      |          }t          |                    d          pd                                          }|r|dt	          |d           d}|                    |dd|                     |          di	           d {V }d
d||pi                     d          |pi                     d          dS t          |                    d          pd                                          }t          |                    d          pd                                          }|r|st          d          dt	          |d           dt	          |d           d}|                    |dd|                     |          di	           d {V }d
d|||pi                     d          |pi                     d          dS )Nrj   rA   z/chats/)safez	/messagesr   html)contentTypecontent)	json_bodyrm   chatidwebUrl)rf   target_typerj   
message_idweb_urlrk   rl   zEGraph delivery mode requires chat_id, or both team_id and channel_id.z/teams/z
/channels/channel)rf   r   rk   rl   r   r   )_build_graph_clientr0   ro   r1   r	   	post_json_render_summary_htmlr:   )	rC   r^   r_   rT   rj   pathr   rk   rl   s	            r    rr   z+TeamsSummaryWriter._write_summary_via_graph  sg     
 //77fjj++1r2288:: 	?U7444???D)33!6dF_F_`gFhFh#i#ij 4        H
 ")%"'~222488$N//99   fjj++1r2288::L117R88>>@@
 	j 	W  5eG"--- 5 5Zb)))5 5 5 	 &//v$B[B[\cBdBdeef 0 
 
 
 
 
 
 
 

 %$$#>r..t44 B++H55
 
 	
r5   c                ,   | j         | j         S ddlm} ddlm} t          |                    d          pd                                          }|r |t          |          | j	                  S  ||
                                | j	                  S )Nr   )MicrosoftGraphTokenProvider)MicrosoftGraphClientr>   rA   )rU   )r\   tools.microsoft_graph_authr   tools.microsoft_graph_clientr   r0   ro   r1   r=   r]   from_env)rC   r_   r   r   r>   s        r    r   z&TeamsSummaryWriter._build_graph_client0  s    )%%JJJJJJEEEEEE6::n55;<<BBDD 	''*<88/    $#'0022o
 
 
 	
r5   r0   c           
        d|                      |           ddd|                     t          |dd           d           ddg|                     t          |dd                     dd|                     t          |d	d                     dd
|                     t          |dd                     }d                    |          S )Nz**rA   z	Summary: summaryNo summary available.zKey decisions:key_decisionszAction items:action_itemszRisks:risks
)_title_textgetattr_bullet_linesjoin)rC   r^   liness      r    r   z+TeamsSummaryWriter._render_summary_markdownB  s    )W%%)))`

77It#D#DF]^^``
 $ G GHH
 
 
  F FGG
 
 
 $ ? ?@@
 yyr5   c                Z   d|                      t          |dd           d          gfdt          t          |dd           pg           fdt          t          |dd           pg           fdt          t          |d	d           pg           fg}d
t          j        |                     |                     dg}|D ]\  }}|                    dt          j        |           d           t          |          dk    rE|dk    r?|                    dt          j        t          |d                              d           |r=d	                    d |D                       }|                    |rd| dpd           |                    d           d	                    |          S )NSummaryr   r   zKey decisionsr   zAction itemsr   Risksr   z<h2>z</h2>z<h3>z</h3>   z<p>r   z</p>rA   c              3     K   | ]J}t          |                                          #d t          j        t          |                     dV  KdS )z<li>z</li>N)r0   r1   r   escape.0items     r    	<genexpr>z:TeamsSummaryWriter._render_summary_html.<locals>.<genexpr>a  sY      "o"oD]`ae]f]f]l]l]n]n"o#G$+c$ii*@*@#G#G#G"o"o"o"o"o"or5   z<ul>z</ul>z<p>None</p>)
r   r   listr   r   r   appendlenr0   r   )rC   r^   sectionsblocksheadingr   rendereds          r    r   z'TeamsSummaryWriter._render_summary_htmlS  s   GGY$E$EG^__`ad77OT#J#J#PbQQRT''>4"H"H"NBOOPd77GT::@bAAB	
 BT[[%9%9::AAAB& 		- 		-NGUMM<W!5!5<<<===5zzQ7i#7#7DDKE!H$>$>DDDEEE -77"o"oTY"o"o"ooohA+A(+A+A+AR]SSSSm,,,,wwvr5   c                    t          | dd           }|rt          |          S t          | dd           }|rt          |dd           nd }d|pd S )Ntitlemeeting_ref
meeting_idzMeeting r   )r   r0   )r^   r   r   r   s       r    r   zTeamsSummaryWriter._titleg  sg    $// 	u::g}d;;ALVW[,===RV
3*1	333r5   r#   r   c                P    t          | pd                                          }|p|S r@   r0   r1   )r#   r   r   s      r    r   zTeamsSummaryWriter._textp  s)    5;B%%''wr5   values	list[str]c                <    d |pg D             }d |D             pdgS )Nc                    g | ]D}t          |                                          #t          |                                          ES rJ   r   r   s     r    
<listcomp>z4TeamsSummaryWriter._bullet_lines.<locals>.<listcomp>w  s=    SSStTARARST""SSSr5   c                    g | ]}d | S )z- rJ   r   s     r    r   z4TeamsSummaryWriter._bullet_lines.<locals>.<listcomp>x  s    ...T...r5   z- NonerJ   )clsr   r   s      r    r   z TeamsSummaryWriter._bullet_linesu  s4    SS"SSS.....<8*<r5   r8   )rV   rW   rT   rX   rU   rY   r%   rH   )r^   r   r_   r`   ra   rb   r%   rc   )r_   r`   r%   rc   )r^   r   r_   rc   r%   rc   )r_   rc   r%   r   )r^   r   r%   r0   )r#   r   r   r0   r%   r0   )r   r   r%   r   )rN   rO   rP   rQ   rD   rt   rn   rq   rr   r   r   r   staticmethodr   r   classmethodr   rJ   r5   r    rS   rS      sE         26	$ $(59	$ 	$ 	$ 	$ 	$ 	$ 59	
 
 
 
 
4   0
 
 
 
.*
 *
 *
 *
X
 
 
 
$       "   ( 4 4 4 \4    \ = = = [= = =r5   rS   c                  :    e Zd ZdZddZddZddZddZddZdS )_AiohttpBridgeAdaptera3  HttpServerAdapter that bridges the Teams SDK into an aiohttp server.

    Without a custom adapter, ``App()`` unconditionally imports fastapi/uvicorn
    and allocates a ``FastAPI()`` instance.  This bridge captures the SDK's
    route registrations and wires them into our own aiohttp ``Application``.
    aiohttp_app'web.Application'c                    || _         d S r8   )_aiohttp_app)rC   r   s     r    rD   z_AiohttpBridgeAdapter.__init__  s    'r5   method'HttpMethod'r   r0   handler'HttpRouteHandler'r%   rH   c                V    dfd}| j         j                            |||           dS )z2Register an SDK route handler as an aiohttp route.request'web.Request'r%   'web.Response'c                z  K   |                                   d {V }t          | j                  } t          ||                     d {V }|                    dd          }|                    d          }|)t          j        |t          j        |          d          S t          j        |          S )N)r   headersstatus   r   application/json)r   r   content_type)r   )r   rp   r   HttpRequestro   r   Responsedumps)r   r   r   resultr   	resp_bodyr   s         r    _aiohttp_handlerz>_AiohttpBridgeAdapter.register_route.<locals>._aiohttp_handler  s       ''''''D7?++G+27;DRY3Z3Z3Z+[+[%[%[%[%[%[%[FZZ#..F

6**I$|!I..!3   
 <v....r5   N)r   r   r%   r   )r   router	add_route)rC   r   r   r   r   s      ` r    register_routez$_AiohttpBridgeAdapter.register_route  sH    	/ 	/ 	/ 	/ 	/ 	/ 	 **649IJJJJJr5   	directoryc                    d S r8   rJ   )rC   r   r   s      r    serve_staticz"_AiohttpBridgeAdapter.serve_static  s    r5   portr6   c                $   K   t          d          )Nz(aiohttp server is managed by the adapter)NotImplementedError)rC   r   s     r    startz_AiohttpBridgeAdapter.start  s      !"LMMMr5   c                
   K   d S r8   rJ   rK   s    r    stopz_AiohttpBridgeAdapter.stop  s      r5   N)r   r   )r   r   r   r0   r   r   r%   rH   )r   r0   r   r0   r%   rH   )r   r6   r%   rH   rM   )	rN   rO   rP   rQ   rD   r   r   r   r   rJ   r5   r    r   r   {  s         ( ( ( (K K K K&   N N N N     r5   r   c                     t           ot          S )zDReturn True when all Teams dependencies and credentials are present.)TEAMS_SDK_AVAILABLEAIOHTTP_AVAILABLErJ   r5   r    check_requirementsr    s    4#44r5   c                @   t          | di           pi }t          j        d          p|                    dd          }t	          d          p|                    dd          }t          j        d          p|                    dd          }t          |o|o|          S )	zAReturn True when the config has the minimum required credentials.r}   TEAMS_CLIENT_ID	client_idrA   TEAMS_CLIENT_SECRETclient_secretTEAMS_TENANT_ID	tenant_id)r   r   r   ro   r!   r$   )r_   r}   r  r  r  s        r    validate_configr	    s    FGR((.BE	+,,J		+r0J0JI&'<==_?\^A_A_M	+,,J		+r0J0JI	9m9	:::r5   c                     t          |           S )z7Check whether Teams is configured (env or config.yaml).)r	  )r_   s    r    is_connectedr    s    6"""r5   dict | Nonec                    t          j        dd                                          } t          dd                                          }t          j        dd                                          }| r|r|sdS | ||d}t          j        dd                                          }|r$	 t	          |          |d<   n# t
          $ r Y nw xY wt          j        d	d                                          }|r||d
<   t          j        dd                                          }|r|t          j        dd          d|d<   |S )a  Seed ``PlatformConfig.extra`` from env vars during gateway config load.

    Called by the platform registry's env-enablement hook BEFORE adapter
    construction, so ``gateway status`` and ``get_connected_platforms()``
    reflect env-only configuration without instantiating the Teams SDK.
    Returns ``None`` when Teams isn't minimally configured.

    The special ``home_channel`` key in the returned dict becomes a proper
    ``HomeChannel`` dataclass on the ``PlatformConfig`` via the core hook.
    r  rA   r  r  N)r  r  r  
TEAMS_PORTr   TEAMS_SERVICE_URLservice_urlTEAMS_HOME_CHANNELTEAMS_HOME_CHANNEL_NAMEHome)rj   r   r   )r   r   r1   r!   r6   r:   )r  r  r  seedr   r  homes          r    _env_enablementr    sj    	+R006688I&'<bAAGGIIM	+R006688I - I t& D
 9\2&&,,..D 	t99DLL 	 	 	D	)/44::<<K *)]9)2..4466D 
I7@@ 
  
^ Ks   )B< <
C	C	z&https://smba.trafficmanager.net/teams/zsmba.trafficmanager.netz!smba.infra.gov.teams.microsoft.usz^[A-Za-z0-9:@\-_.]+$rawr0   Optional[str]c                    | sdS 	 ddl m}  ||           }n# t          $ r Y dS w xY w|j        dk    rdS |j        t
          vrdS |                     d          r| n| dz   }|S )a  Return a normalized service URL or ``None`` if it is not allowed.

    Requires ``https://`` and a host in ``_ALLOWED_TEAMS_SERVICE_HOSTS``.
    The trailing slash is added if absent so callers can append
    ``v3/conversations/...`` without double slashes.
    Nr   )urlparsehttps/)urllib.parser  	Exceptionschemehostname_ALLOWED_TEAMS_SERVICE_HOSTSendswith)r  r  parsedr3   s       r    _validate_teams_service_urlr$    s      t))))))#   tt}t:::tS))8sSyJs    
&&)	thread_idmedia_filesforce_documentrj   messager%  r&  Optional[list]r'  Dict[str, Any]c          	       K   t          | di           pi }t          j        d          p|                    dd          }t	          d          p|                    dd          }t          j        d          p|                    dd          }	|r|r|	sd	d
iS t          j        d          p|                    dd          pt
          }
t          |
          }|d	dt          t                     iS |sd	diS t          
                    |          sd	diS t          
                    |	          sd	diS d|	 d}| d| d}t          sd	diS 	 ddl}|                    d          }|                    d          4 d{V }|                    |d||dddd i|!          4 d{V 	 }|j        d"k    rU|                                 d{V }d	d#|j         d$|dd%          icddd          d{V  cddd          d{V  S |                                 d{V }ddd          d{V  n# 1 d{V swxY w Y   |                    d&          }|sd	d'icddd          d{V  S d(|d)d*}|                    ||d+| d,d-|.          4 d{V 	 }|j        d"k    rU|                                 d{V }d	d/|j         d$|dd%          icddd          d{V  cddd          d{V  S |                                 d{V }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   d|                    d0          d1S # t&          j        $ r  t*          $ r-}t,                              d2d3           d	d4| icY d}~S d}~ww xY w)5a  Acquire a Bot Framework bearer token and POST a single message activity.

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

    Configuration: requires ``TEAMS_CLIENT_ID``, ``TEAMS_CLIENT_SECRET``,
    ``TEAMS_TENANT_ID``, ``TEAMS_HOME_CHANNEL`` (the conversation ID), and
    optionally ``TEAMS_SERVICE_URL`` (Bot Framework service host; must be
    a known Bot Framework endpoint, see ``_ALLOWED_TEAMS_SERVICE_HOSTS``).

    Security: ``service_url`` is validated against an allowlist of known
    Bot Framework hosts to block SSRF / token-exfiltration via a tampered
    env var.  ``chat_id`` is validated to match the documented Bot
    Framework ID character set so it cannot escape the URL path.

    ``media_files`` and ``force_document`` are accepted for signature
    parity but not implemented for the standalone path; messages with
    attachments will send as text-only.  The live adapter handles
    attachments via the SDK.
    r}   r  r  rA   r  r  r  r  errorzaTeams standalone send: TEAMS_CLIENT_ID, TEAMS_CLIENT_SECRET, and TEAMS_TENANT_ID are all requiredr  r  NzeTeams standalone send: TEAMS_SERVICE_URL host is not on the Bot Framework allowlist; expected one of z<Teams standalone send: chat_id (conversation ID) is requiredz`Teams standalone send: chat_id contains characters outside the Bot Framework conversation ID setzSTeams standalone send: TEAMS_TENANT_ID contains characters outside the expected setz"https://login.microsoftonline.com/z/oauth2/v2.0/tokenzv3/conversations/z/activitiesz,Teams standalone send: aiohttp not installedr   g      .@)totalT)	trust_envclient_credentialsz%https://api.botframework.com/.default)
grant_typer  r  scopeContent-Typez!application/x-www-form-urlencoded)datar   r   i  z-Teams standalone send: token request failed (z): i,  r>   z:Teams standalone send: token response missing access_tokenr(  markdown)typer   
textFormatzBearer r   )Authorizationr2  )r   r   r   z-Teams standalone send: activity post failed (r   successr   zTeams standalone send raisedexc_infozTeams standalone send failed: )r   r   r   ro   r!   _DEFAULT_TEAMS_SERVICE_URLr$  sortedr!  _TEAMS_CONV_ID_REmatchr   aiohttpClientTimeoutClientSessionr   r   r   r   asyncioCancelledErrorr  loggerdebug)pconfigrj   r(  r%  r&  r'  r}   r  r  r  raw_service_urlr  	token_urlactivities_url_aiohttpper_request_timeoutsession
token_respr   token_payloadr>   activity	send_respsend_payloades                            r    _standalone_sendrT  
  s     > GWb))/RE	+,,J		+r0J0JI&'<==_?\^A_A_M	+,,J		+r0J0JI ~- ~I ~|}} 		%&& 	&99]B''	&% 
 .o>>K62336 6 	  YWXX""7++ }{||""9-- pnooRYRRRI#JJgJJJN IGHH4?"""" '4444@@))D)99 %	6 %	6 %	6 %	6 %	6 %	6 %	6W||"6!*%2D	  ()LM+ $ 
 
 8 8 8 8 8 8 8 8 $++!+!2!2222222D#%wU_Uf%w%wkoptqtptku%w%wx8 8 8 8 8 8 8 8 8 8 8 8 8%	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 '1oo&7&7 7 7 7 7 7 78 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 ),,^<<L _!]^%%	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6* "( H
 ||%=|%=%=$6  , $   6 6 6 6 6 6 6 6 #s**!*!1!1111111D#%vU^Ue%v%vjnospsosjt%v%vw6 6 6 6 6 6 6 6 6 6 6 6 63%	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6J &/^^%5%55555556 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 63%	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6 %	6N &**400
 
 	
 !    ? ? ?3dCCC=!==>>>>>>>?s   6M> 'M.<H;*M<M> H;)M;
I	MI		M&M> 9+M%<L2!M3M> L2 M2
L<	<M?L<	 MM> 
MM> "M#M> >O"N?9O?OIterator[None]c               #     K   	 ddl } n# t          $ r dV  Y dS w xY wt          | dd          }|dV  dS d | _        	 dV  || _        dS # || _        w xY w)u}  No-op ``dotenv.load_dotenv`` while importing the Teams SDK (#62935).

    ``microsoft_teams.apps.app`` calls ``load_dotenv(find_dotenv(usecwd=True))``
    at module import time. That mutates process-global ``os.environ`` from
    whatever ``.env`` sits above cwd — typically a root profile's secrets.
    Hermes owns dotenv loading; third-party import side effects must not.
    r   Nload_dotenvc                     dS )NFrJ   )argskwargss     r    <lambda>z._suppress_third_party_dotenv.<locals>.<lambda>  s    % r5   )dotenvImportErrorr   rW  )_dotenvoriginals     r    _suppress_third_party_dotenvr`    s               wt44H77G'&h&&&&s   	  A 	Ac                 l    t           	t          rdS dd} ddlm}  |d| t	                      d	
          S )u  Ensure the Teams SDK is importable, lazy-installing it on first use.

    Lazy-installs ``microsoft-teams-apps`` via
    ``tools.lazy_deps.ensure("platform.teams")`` if not present, then rebinds
    all module-level SDK globals on success. Returns True once the SDK (and
    aiohttp) are importable, False if they couldn't be installed/imported.

    ``App is not None`` means symbols are already bound — ``TEAMS_SDK_AVAILABLE``
    alone can be True from ``find_spec`` without an import having run yet.
    NTr%   rp   c                 b   ddl m}  t                      5  ddlm}m} ddlm} ddlm	}m
} ddlm} ddlm} ddlm}m}	 dd	lm}
m} dd
lm}m}m}m} ddlm}m}m} d d d            n# 1 swxY w Y   i d| ddd|d|d|d|d|d|d|d|d|	d|
d|d|d|d|d||||ddS )Nr   r
   )AppActivityContext)ClientOptions)MessageActivityConversationReference)TypingActivityInput)AdaptiveCardInvokeActivity)AdaptiveCardActionCardResponse!AdaptiveCardActionMessageResponse)InvokeResponseAdaptiveCardInvokeResponse)
HttpMethodr   HttpResponseHttpRouteHandler)AdaptiveCardExecuteAction	TextBlockr   r   Trc  rd  re  rf  rg  rh  ri  rj  rk  rl  rm  rn  r   ro  rp  )rq  rr  rs  r   )r@  r   r`  microsoft_teams.appsrc  rd  "microsoft_teams.common.http.clientre  microsoft_teams.apirf  rg  %microsoft_teams.api.activities.typingrh  3microsoft_teams.api.activities.invoke.adaptive_cardri  (microsoft_teams.api.models.adaptive_cardrj  rk  *microsoft_teams.api.models.invoke_responserl  rm  !microsoft_teams.apps.http.adapterrn  r   ro  rp  microsoft_teams.cardsrq  rr  rs  )_webrc  rd  re  rf  rg  rh  ri  rj  rk  rl  rm  rn  r   ro  rp  rq  rr  rs  s                      r    _importz)check_teams_requirements.<locals>._import  s   '''''')++ 	U 	UAAAAAAAAHHHHHHRRRRRRRRQQQQQQ                               UTTTTTTTTT-	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U0
4

 3
 	

 ]
 
 $%:
 "#6
 )*D
 -.L
 01R
 n
 )*D
 *
 ;
  L!
"  0#
$ )*"#'+
 
 
 	
s   A	A**A.1A.r   )ensure_and_bindzplatform.teamsF)prompt)r%   rp   )rc  r   tools.lazy_depsr  globals)r~  r  s     r    check_teams_requirementsr    sZ     ,t1
 1
 1
 1
f 0/////?+WgiiNNNNr5   c                       e Zd ZdZdZdZdE fdZdddFdZdGdZdHdIdZ	dJdZ
dKdZdLd Z	 	 	 	 	 dMdNd,Z	 	 dOdPd0ZdQdRd1Z	 	 dSdTd7Z	 	 	 dUdVd9Z	 	 dOdWd;Z	 	 	 dUdXd=Z	 	 	 dUdYd?Z	 	 	 	 dZd[dBZd\dDZ xZS )]TeamsAdapterz;Microsoft Teams adapter using the microsoft-teams-apps SDK.`m  Tr_   r   c                
   t                                          |t          d                     |j        pi }|                    d          pt          j        dd          | _        |                    d          pt          dd          | _	        |                    d          pt          j        dd          | _
        t          |                    d	          p&t          j        d
t          t                                        | _        |                    d          pt          j        dd          pt          }|rt          |          nd | _        d | _        d | _        t'          d          | _        i | _        d S )Nteamsr  r  rA   r  r  r  r  r   r  host
TEAMS_HOSTi  )max_size)superrD   r   r}   ro   r   r   
_client_idr!   _client_secret
_tenant_idr;   r0   _DEFAULT_PORT_port_DEFAULT_HOST_host_app_runnerr   _dedup
_conv_refs)rC   r_   r}   	_raw_host	__class__s       r    rD   zTeamsAdapter.__init__  sH   '!2!2333"))K00TBI>OQS4T4T#ii88i<NOdfh<i<i))K00TBI>OQS4T4T!IIfL<]9K9K!L!L
 

 IIf%%U<)D)DU	6?$IC	NNNT
%)	26)4888 +-r5   F)is_reconnectr  r$   r%   c          
        K   t                       t          s                     ddd           dS t          s                     ddd           dS  j        r j        r j        s                     ddd           dS 	 t          j        t                    }|j
                            d	d
            t           j         j         j        t          |          t          ddi                     _         j        j        d fd            } j        j        d fd            } j                                         d {V  t          j        |           _         j                                         d {V  t          j         j         j         j                  }|                                 d {V  d _                                          t8                              d j        pd j        t<                     dS # t>          $ rA}                     dd| d           t8                               d|           Y d }~dS d }~ww xY w)NMISSING_SDKzRmicrosoft-teams-apps could not be installed. Run: pip install microsoft-teams-appsF)	retryablez/aiohttp not installed. Run: pip install aiohttpMISSING_CREDENTIALSzJTEAMS_CLIENT_ID, TEAMS_CLIENT_SECRET, and TEAMS_TENANT_ID are all required)client_max_sizez/healthc                ,    t          j        d          S )Nok)r   )r   r   )_s    r    r[  z&TeamsAdapter.connect.<locals>.<lambda>%  s    CLd<S<S<S r5   
User-AgentHermesr   )r  r  r  http_server_adapterr   ctx ActivityContext[MessageActivity]c                B   K                        |            d {V  d S r8   )_on_messager  rC   s    r    _handle_messagez-TeamsAdapter.connect.<locals>._handle_message0  s3      &&s+++++++++++r5   +ActivityContext[AdaptiveCardInvokeActivity]r%   1InvokeResponse[AdaptiveCardActionMessageResponse]c                >   K                        |            d {V S r8   )_on_card_actionr  s    r    _handle_card_actionz1TeamsAdapter.connect.<locals>._handle_card_action4  s/       "11#666666666r5   Tz+[teams] Webhook server listening on %s:%d%sz* (all interfaces, IPv4+IPv6)CONNECT_FAILEDzTeams connection failed: z[teams] Failed to connect: %s)r  r  )r  r  r%   r  )!r  r   _set_fatal_errorr   r  r  r  r   Application_MAX_BODY_BYTESr   add_getrc  r   re  r  
on_messageon_card_action
initialize	AppRunnerr  setupTCPSiter  r  r   _running_mark_connectedrE  info_WEBHOOK_PATHr  r,  )rC   r  r   r  r  siterS  s   `      r    connectzTeamsAdapter.connect  s      	!"""" 	!!d "   
 5  	!!A "   
 5 	d&9 	 	!!%\ "   
 56	 //JJJK&&y2S2STTT/"1/$9+$F$F$lH-EFFF  DI Y!, , , , , "!, Y%7 7 7 7 7 &%7 )&&(((((((((=55DL,$$&&&&&&&&&;t|TZDDD**,, DM  """KK=
==
	   4 	 	 	!! /A// "   
 LL8!<<<55555	s   F
H 
I6IIrH   c                   K   d| _         | j        r&| j                                         d {V  d | _        d | _        |                                  t
                              d           d S )NFz[teams] Disconnected)r  r  cleanupr  _mark_disconnectedrE  r  rK   s    r    
disconnectzTeamsAdapter.disconnectV  sv      < 	 ,&&(((((((((DL	!!!*+++++r5         >@urlr0   r   floatbytesc                F  K   ddl m}m} ddlm}  ||          st          d           ||dd|gi          4 d{V 	 }|                    |d	d
i           d{V }|                                 |j        cddd          d{V  S # 1 d{V swxY w Y   dS )ac  Download attachment bytes with SSRF protection.

        Teams file attachments carry pre-authenticated SharePoint download
        URLs (no extra auth header needed). Validates the URL against the
        SSRF guard and follows redirects through the shared redirect guard,
        matching the cache_*_from_url helpers in gateway.platforms.base.
        r   )create_ssrf_safe_async_clientis_safe_url)_ssrf_redirect_guardz/Blocked unsafe attachment URL (SSRF protection)Tr   )r   follow_redirectsevent_hooksNr  z)Mozilla/5.0 (compatible; HermesAgent/1.0)r  )	tools.url_safetyr  r  gateway.platforms.baser  r:   ro   r   r   )rC   r  r   r  r  r  r   r   s           r    _fetch_attachment_bytesz$TeamsAdapter._fetch_attachment_bytes_  s      	POOOOOOO??????{3 	PNOOO00!#&:%;<
 
 
 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ #ZZ%'RS (        H %%'''#
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$ 
	$s   :B
BBr  r  c                *  K   |j         }| j        r| j        j        nd}|rt          |j        dd          |k    rdS t          |dd          }|r| j                            |          rdS t          |j        dd          }|r|j        | j	        |<   d}t          |d          r|j        r|j        }d|v r-ddl}|                    dd|                                          }|j        }t          |dd          pd}	|	d	k    rd
}
n|	dk    rd}
n|	dk    rd}
nd
}
|j        }t          |dd          pt          |dd          }t          |dd          pd}|                     |j        t          |dd          pd|
t!          |          |t          |dd          p| j                  }g }g }g }t          |dd          pg D ]M}t          |dd          }t          |dd          pd                                }t          |dd          pd}|dv r|sS|                    d          ri|dk    rt          |dd          }t)          |t*                    st          |dd          pi }|                    d          p|                    d          }|                    d          p|                    d          pd                    d          }|s|p|rd| nd }	 |                     |           d{V }t3          ||d!          }|rO|                    |j                   |                    |j                   |                    |j                   nt<                              d"|           n3# t@          $ r&}t<                              d#||           Y d}~nd}~ww xY w|r|                    d$          r	 tC          |           d{V }|r?|                    |           |                    |           |                    d%           n2# t@          $ r%}t<                              d&|           Y d}~nd}~ww xY w|r	 |                     |           d{V }t3          |||!          }|rN|                    |j                   |                    |j                   |                    |j                   # t@          $ r*}t<                              d'|p|||           Y d}~Ed}~ww xY wOd |v rtD          j#        }n?d%|v rtD          j$        }n.d(|v rtD          j%        }nd)|v rtD          j&        }ntD          j'        }tQ          ||||||*          }| )                    |           d{V  dS )+z>Process an incoming Teams message and dispatch to the gateway.Nr   rA   r   z<at>r   z<at>[^<]*</at>\s*conversation_typepersonaldm	groupChatgroupr   aad_object_idr   r  )rj   	chat_name	chat_typeuser_id	user_nameguild_idattachmentscontent_urlr   )z	text/htmlz
text/plainzapplication/vnd.microsoft.cardz2application/vnd.microsoft.teams.file.download.infor   __dict__downloadUrldownload_urlfileType	file_type.z	document.document)filename	mime_typez?[teams] Unsupported document type for attachment '%s', skippingz0[teams] Failed to cache file attachment '%s': %szimage/imagez,[teams] Failed to cache image attachment: %sz0[teams] Failed to cache attachment '%s' (%s): %svideoaudio)r   sourcemessage_type
media_urlsmedia_typesr   )*rP  r  r   r   from_r  is_duplicateconversationconversation_refr  hasattrr   resubr1   build_sourcer0   r  r2   
startswithr/   rp   ro   lstripr  r   r   r   
media_typekindrE  warningr  r   r   DOCUMENTPHOTOVIDEOAUDIOTEXTr   handle_message)rC   r  rP  bot_idmsg_idconv_idr   r  conv	conv_typer  from_accountr  r  r  r  r  media_kindsattr  r   att_namer   r  r  r  r3  cachedrS  msg_typeevents                                  r    r  zTeamsAdapter._on_messagey  s     < "&4 	ghndD99VCCF 4.. 	dk..v66 	F (/t<< 	<'*';DOG$ 8V$$ 	! 	!=DT>>III66.D99??AAD $D"5t<<B	
""II+%%II)##!III  ~,>>a',X\^`BaBaL&$77=2	""GdFD117RLLT;55H # 
 
 
8]D99?R B	 B	C!#}d;;K#C>>D"KKMMLsFD117RH
 :::;:&&'GHH SSS "#y$77!'400 G%gz4@@FBG&{{=99XW[[=X=X$[[44VK8P8PVTV^^_bcc	# #]9(\(?I(?(?(?R\d!%!=!=l!K!KKKKKKKD.thRTUUUF "))&+666#**6+<===#**6;7777]$   ! d d dNN#UW_abccccccccd 	|66x@@ 	V#7#D#DDDDDDDF 4"))&111#**<888#**7333  V V VNN#QSTUUUUUUUUV !%!=!=k!J!JJJJJJJD.x<  F  8"))&+666#**6+<===#**6;777    NNJ /Kq       , $$"+HH##"(HH##"(HH##"(HH"'H!!#
 
 
 !!%(((((((((((sK   &BN  
N0
N++N0AP$$
Q.QQA=S
T$T		Trj   card'AdaptiveCard''Any'c                2  K   ddl m} | j                            |          }|rJ| j        rC |                                |          }| j        j                            ||           d{V S | j        r!| j                            ||           d{V S dS )zJSend an AdaptiveCard, using a stored ConversationReference when available.r   )MessageActivityInputN)rv  r  r  ro   r  add_cardactivity_sendersend)rC   rj   r  r  conv_refrP  s         r    
_send_cardzTeamsAdapter._send_card  s      <<<<<<?&&w// 	7	 	7++--66t<<H277(KKKKKKKKKY 	7666666666tr5   -'ActivityContext[AdaptiveCardInvokeActivity]'3'InvokeResponse[AdaptiveCardActionMessageResponse]'c                  K   ddl m}m} |j        j        j        }|j        pi }|                    dd          }|                    dd          }|r|st          dt          d          	          S t          j        d
d                                          }t          j        dd                                                                          dv }	|	s|s9t                              d           t          dt          d          	          S |j        j        }
t#          |
dd          pt#          |
dd          }d |                    d          D             }d|vr>||vr:t                              d|           t          dt          d          	          S ddddd}|                    |          }|st          dt          d          	          S  ||          sat          dt'          t)                                          d                              t/          dd          g                    	          S  |||           d d!d"d#d$}|                    d%d          }|                    d&d          }g }|rM|                    t/          d'dd()                     |                    t/          d*| d+d                     |r(|                    t/          d,| dd-                     |                    t/          ||         dd()                     t          dt'          t)                                          d                              |                    	          S ).z4Handle an Adaptive Card Action.Execute button click.r   )resolve_gateway_approvalhas_blocking_approvalhermes_actionrA   session_keyr   zUnknown action.)r#   )r   r   TEAMS_ALLOWED_USERSTEAMS_ALLOW_ALL_USERS>   r'   r)   r*   us   [teams] card action rejected: TEAMS_ALLOWED_USERS not configured and TEAMS_ALLOW_ALL_USERS not set — default denyuB   ⛔ Approval buttons require TEAMS_ALLOWED_USERS to be configured.r  Nr   c                ^    h | ]*}|                                 |                                 +S rJ   )r1   )r   uids     r    	<setcomp>z/TeamsAdapter._on_card_action.<locals>.<setcomp>D  s-    XXX3CIIKKX399;;XXXr5   ,*u3   [teams] Unauthorized card action by %s — ignoringu   ⛔ Not authorized.oncerM  alwaysdeny)approve_onceapprove_sessionapprove_alwaysr+  1.4u,   ⚠️ Approval already resolved or expired.Tr   wrapu   ✅ Allowed (once)u   ✅ Allowed (session)u   ✅ Always allowedu
   ❌ Denied)r)  rM  r*  r+  cmddesc    ⚠️ Command Approval RequiredBolderr   r1  weight```

```Reason: r   r1  isSubtle)tools.approvalr  r  rP  r#   actionr3  ro   rl  rk  r   r   r1   r2   rE  r  r  r   splitrj  rq  with_version	with_bodyrs  r   )rC   r  r  r  r>  r3  r   r!  allowed_csv	allow_allr
  
clicker_idallowed_ids
choice_mapchoice	label_mapr2  r3  r   s                      r    r  zTeamsAdapter._on_card_action  s      	SRRRRRRR#*{ b"55hh}b11 	K 	!6=NOOO    i 5r::@@BBI5r::@@BBHHJJNbb	 	 
I   &:b      <-L EEhQ]_cegIhIhJXX+2C2CC2H2HXXXK+%%*K*G*GTV`aaa%:AVWWW    #(&	
 

 .. 	!6=NOOO   
 %$[11 	!3&..!\%((Y	/]dh i i ijkk      	! f555 ).* 	
 
	 hhub!!xx## 	GKK	'IPT]efffgggKK	'9s'9'9'9EEEFFF 	UKK	'8$'8'8tdSSSTTTI9V#44QQQRRR/"nn11%88BB4HH  
 
 
 	
r5   dangerous commandNcommandr!  descriptionmetadataOptional[Dict[str, Any]]allow_permanentallow_sessionsmart_deniedr   c	           	       K   | j         st          dd          S t          |          dk    r|dd         dz   n|}	|t          |          dk    r|dd         dz   n||d}
t          d	d
i |
ddid          g}|sZ|rX|                    t          dd
i |
ddi                     |r+|                    t          dd
i |
ddi                     |                    t          dd
i |
ddid                     t          ddd          t          d|	 dd          t          d| dd          g}|r$|                    t          d d                     t                                          d!                              |          	                    |          }	 | 
                    ||           d{V }|rt          |d"d          nd}t          d|#          S # t          $ rF}t                              d$|d%           t          dt          |          d&          cY d}~S d}~ww xY w)'z>Send an Adaptive Card approval prompt with Allow/Deny buttons.FTeams app not initializedr9  r,  i  Nz...r   )r!  r2  r3  z
Allow Oncehermes_approver   r,  positive)r   verbr3  stylezAllow Sessionr-  )r   rV  r3  zAlways Allowr.  Denyr+  destructiver4  Tr5  r6  r8  r9  r0  r:  r;  z>Smart DENY: owner override applies to this one operation only.r/  r   r8  z%[teams] send_exec_approval failed: %sr:  r9  r,  r  )r  r   r   rr  r   rs  rq  r@  rA  with_actionsr  r   r  rE  r,  r0   )rC   rj   rJ  r!  rK  rL  rN  rO  rP  cmd_previewbtn_data_baseactionsr   r  r   r   rS  s                    r    send_exec_approvalzTeamsAdapter.send_exec_approval|  s      y 	Pe3NOOOO03Gt0C0Cgetenu,, ',/LL3,>,>74C4=5((G
 
 !%5CMC?NCC:
 
 
   		 		NN=%,<JJ8IJJ       }(/?MMM?<LMM         	}/;M;?F;;=
 
 
 	 	 	
 =DQYZZZ5;555DAAA3k33$NNN

  	KK	U\`      ~~**511;;DAANNwWW	K??7D99999999F8>Ht444DJdzBBBB 	K 	K 	KLL@!dLSSSe3q66TJJJJJJJJJ	Ks   .AG0 0
I :;H;5I ;I r   reply_tor  c                  K   | j         st          dd          S |                     |          }|                     |          }d }|D ] }	 |r|                                r}|dk    rw	 | j                             |||           d {V }	nt# t          $ rF}
t                              d|
           | j         	                    ||           d {V }	Y d }
~
n)d }
~
ww xY w| j         	                    ||           d {V }	t          |	dd           }# t          $ r+}t          dt          |          d          cY d }~c S d }~ww xY wt          d|	          S )
NFrR  rS  r+   z3Teams reply() failed, falling back to flat send: %sr   TrZ  r8  )r  r   format_messagetruncate_messageisdigitreplyr  rE  rF  r  r   r0   )rC   rj   r   r`  rL  	formattedchunkslast_message_idchunkr   	reply_errrS  s               r    r  zTeamsAdapter.send  s      y 	Pe3NOOOO''00	&&y11 	O 	OEO B 0 0 2 2 Bx3
F'+yw%'P'P!P!P!P!P!P!P$ F F F Q%   (,y~~gu'E'E!E!E!E!E!E!EF $(9>>'5#A#AAAAAAAF")&$"="= O O O!%s1vvNNNNNNNNNNNO $?CCCCsH   D*"BD
C<CDC5D
EE;EEc                   K   | j         sd S 	 | j                             |t                                 d {V  d S # t          $ r Y d S w xY wr8   )r  r  rh  r  )rC   rj   rL  s      r    send_typingzTeamsAdapter.send_typing  sq      y 	F	)..*=*?*?@@@@@@@@@@@ 	 	 	DD	s   -< 
A
	A
mediar  default_mimecaptionmedia_labelc                  K   | j         st          dd          S 	 ddl}ddl}ddlm}m}	 |                    d          s|                    d          r9|}
|                    |	                    d	          d                   d         p|}n|
                    d
          }|                    |          d         p|}t          |d          5 }d| d|                    |                                                                           }
ddd           n# 1 swxY w Y    |||
          } |	                                |          }|r|                    |          }| j                            |          }|r'| j         j                            ||           d{V }n!| j                             ||           d{V }t          dt+          |dd                    S # t,          $ rG}t.                              d||d           t          dt3          |          d          cY d}~S d}~ww xY w)a  Send any media file/URL as a Teams attachment.

        Remote ``http(s)://`` URLs are attached by reference; local paths
        (with optional ``file://`` prefix) are base64-encoded into a data
        URI. MIME type is guessed from the path/extension, falling back to
        ``default_mime``. Shared by send_image / send_video / send_voice /
        send_document so every media kind uses the same Attachment path.
        FrR  rS  r   N)
Attachmentr  zhttp://zhttps://?zfile://rbzdata:z;base64,)r   r  Tr   r8  z[teams] send_%s failed: %sr:  rZ  )r  r   base64	mimetypesrv  rr  r  r  
guess_typer?  removeprefixopen	b64encodereaddecodeadd_attachmentsadd_textr  ro   r  r  r   r  rE  r,  r0   )rC   rj   r  rn  ro  rp  ru  rv  rr  r  r  r  r   f
attachmentrP  r  r   rS  s                      r    _send_media_attachmentz#TeamsAdapter._send_media_attachment  s       y 	Pe3NOOOO	KMMMLLLLLLLL  ++ cv/@/@/L/L c$%00c1B1B11EFFqIY\		 **955%0066q9I\	$%% c"b)"b"bV=M=Maffhh=W=W=^=^=`=`"b"bKc c c c c c c c c c c c c c c $TTTJ++--==jIIH 6#,,W55**733H A#y8==hQQQQQQQQ#y~~gx@@@@@@@@dwvtT7R7RSSSS 	K 	K 	KLL5{APTLUUUe3q66TJJJJJJJJJ	KsD   B5G- A DG- D!!G- $D!%CG- -
H>7<H93H>9H>	image_urlc                F   K   |                      ||d|d           d {V S )Nz	image/pngr  rj   r  rn  ro  rp  r  )rC   rj   r  ro  r`  rL  s         r    
send_imagezTeamsAdapter.send_image  sR       00$ 1 
 
 
 
 
 
 
 
 	
r5   
image_pathc                D   K   |                      ||||           d {V S )N)rj   r  ro  r`  )r  )rC   rj   r  ro  r`  rZ  s         r    send_image_filezTeamsAdapter.send_image_file&  sM       __ 	 % 
 
 
 
 
 
 
 
 	
r5   
video_pathc                F   K   |                      ||d|d           d {V S )Nz	video/mp4r  r  r  )rC   rj   r  ro  r`  rL  rZ  s          r    
send_videozTeamsAdapter.send_video5  sR       00$ 1 
 
 
 
 
 
 
 
 	
r5   
audio_pathc                F   K   |                      ||d|d           d {V S )Nz
audio/mpegvoicer  r  )rC   rj   r  ro  r`  rL  rZ  s          r    
send_voicezTeamsAdapter.send_voiceF  sR       00% 1 
 
 
 
 
 
 
 
 	
r5   	file_path	file_namec                F   K   |                      ||d|d           d {V S )Nzapplication/octet-streamr  r  r  )rC   rj   r  ro  r  r`  rL  rZ  s           r    send_documentzTeamsAdapter.send_documentW  sR       003" 1 
 
 
 
 
 
 
 
 	
r5   rp   c                   K   |d|dS )Nunknown)r   r5  rj   rJ   )rC   rj   s     r    get_chat_infozTeamsAdapter.get_chat_infoi  s      wGGGr5   )r_   r   )r  r$   r%   r$   rM   )r  )r  r0   r   r  r%   r  )r  r  r%   rH   )rj   r0   r  r  r%   r  )r  r  r%   r  )rI  NTTF)rj   r0   rJ  r0   r!  r0   rK  r0   rL  rM  rN  r$   rO  r$   rP  r$   r%   r   )NN)
rj   r0   r   r0   r`  r  rL  rM  r%   r   r8   )rj   r0   rL  rM  r%   rH   )Nrm  )rj   r0   r  r0   rn  r0   ro  r  rp  r0   r%   r   )NNN)rj   r0   r  r0   ro  r  r`  r  rL  rM  r%   r   )
rj   r0   r  r0   ro  r  r`  r  r%   r   )rj   r0   r  r0   ro  r  r`  r  rL  rM  r%   r   )rj   r0   r  r0   ro  r  r`  r  rL  rM  r%   r   )NNNN)rj   r0   r  r0   ro  r  r  r  r`  r  rL  rM  r%   r   )rj   r0   r%   rp   )rN   rO   rP   rQ   MAX_MESSAGE_LENGTHsplits_long_messagesrD   r  r  r  r  r  r  r_  r  rl  r  r  r  r  r  r  r  __classcell__)r  s   @r    r  r    sS       EE- - - - - -& 5: R R R R R Rh, , , ,$ $ $ $ $4V) V) V) V)p
 
 
 
]
 ]
 ]
 ]
H /-1 $"":K :K :K :K :K@ #'-1"D "D "D "D "DH     "&"0K 0K 0K 0K 0Kl "&"&-1
 
 
 
 
( "&"&
 
 
 
 
& "&"&-1
 
 
 
 
* "&"&-1
 
 
 
 
* "&#'"&-1
 
 
 
 
$H H H H H H H Hr5   r  rH   c                 ~   ddl m} m} ddlm}m}m}m}m}  | d          }|r |d| d            |dd          sd	S  |d
            |d            |d           t                        |d            |d            |d           t                        |d           t                        |d|pd          }|s |d           d	S  |d|
                                            |d | d          pdd          }	|	s |d           d	S  |d|	
                                            |d | d          pd          }
|
s |d           d	S  |d|

                                           t                        |d            |dd          rS |d | d           pd          }|r, |d |                    d!d                      |d"           n$ |d d           n |d#d$            |d%           t                        |d&            |d'            |d(           d	S ))z7Guide the user through Teams setup using the Teams CLI.r   )get_env_valuesave_env_value)r  prompt_yes_no
print_infoprint_successprint_warningr  z#Teams: already configured (app ID: )zReconfigure Teams?FNz2You'll need the Teams CLI. If you haven't already:z-  npm install -g @microsoft/teams.cli@previewz  teams loginzAThen expose port 3978 publicly (devtunnel / ngrok / cloudflared),zand create your bot:zM  teams app create --name "Hermes" --endpoint "https://<tunnel>/api/messages"zMThe CLI will print CLIENT_ID, CLIENT_SECRET, and TENANT_ID. Paste them below.z	Client IDrA   r"   u.   Client ID is required — skipping Teams setupzClient secretr  T)r   passwordu2   Client secret is required — skipping Teams setupz	Tenant IDr  u.   Tenant ID is required — skipping Teams setupzDTo find your AAD object ID for the allowlist: teams status --verbosez0Restrict access to specific users? (recommended)z(Allowed AAD object IDs (comma-separated)r"   zAllowlist configuredr#  r*   uF   ⚠️  Open access — anyone who can message the bot can command it.z+Teams configuration saved to ~/.hermes/.envz>Install the app in Teams:  teams app install --id <teamsAppId>z1Restart the gateway:       hermes gateway restart)hermes_cli.configr  r  hermes_cli.cli_outputr  r  r  r  r  printr1   replace)r  r  r  r  r  r  r  existing_idr  r  r  alloweds               r    interactive_setupr  o  s                         - 122K 
GGGGHHH}1599 	FJCDDDJ>???J	GGGJRSSSJ%&&&Jbccc	GGGJ^___	GGG{K,=2>>>I FGGGN$ioo&7&7888F?MMBW4X4X4^\^imnnnM JKKKN(-*=*=*?*?@@@{MM:K,L,L,RPRSSSI FGGGN$ioo&7&7888	GGGJUVVV}GNN `&6!M"788>B
 
 
  	6N0'//#r2J2JKKKM01111N0"5555.777^___	GGGM?@@@JOPPPJBCCCCCr5   c                    |                      ddd t          t          t          g ddt          t
          dt          ddd	d
dd           dS )u:   Plugin entry point — called by the Hermes plugin system.r  zMicrosoft Teamsc                     t          |           S r8   )r  )cfgs    r    r[  zregister.<locals>.<lambda>  s    L$5$5 r5   )r  r  r  z(pip install microsoft-teams-apps aiohttpr  r"  r#  r  u   💼Tu   You are chatting via Microsoft Teams. Teams renders a subset of markdown — bold (**text**), italic (*text*), and inline code (`code`) work, but complex tables or raw HTML do not. Keep responses clear and professional.)r   labeladapter_factorycheck_fnr	  r  required_envinstall_hintsetup_fnenv_enablement_fncron_deliver_env_varstandalone_sender_fnallowed_users_envallow_all_envmax_message_lengthemojiallow_update_commandplatform_hintN)register_platformr  r	  r  r  r  rT  )r  s    r    registerr    sq    55#'!RRR?" * 2 ./- !0A  % % % % %r5   r8   )r#   r   r   r$   r%   r$   )r#   r   r   r6   r%   r6   )r%   r$   )r%   r  )r  r0   r%   r  )rj   r0   r(  r0   r%  r  r&  r)  r'  r$   r%   r*  )r%   rU  rM   )arQ   
__future__r   rC  r   r   loggingr   
contextlibr   typingr   r   r   r   r  r	   r@  r   r   r]  importlib.util	importlibsys_sysutil	find_specr   r:   modulesre  rc  rd  rf  rg  rh  ri  rj  rk  rm  rl  r0   rn  r   ro  rp  rq  rr  rs  gateway.configr   r   gateway.platforms.helpersr   r  r   r   r   r   r   r   agent.secret_scoper   r   r   r   r!   	getLoggerrN   rE  r  r  r  r  r4   r;   r=   rS   r   r  r	  r  r  r<  	frozensetr!  r  	_re_teamscompiler>  r$  rT  r`  r  r  r  r  rJ   r5   r    <module>r     s   , # " " " " "     				 % % % % % % 0 0 0 0 0 0 0 0 0 0 0 0         
CCC        <#.223DEETQ < < <+t|;< 
  ! !% $( !! 
 	 3 3 3 3 3 3 3 3 9 9 9 9 9 9                K J J J J J ? ? ? ? ? ?/ / / /( 
	8	$	$   05 	 	 	 	 	 	 0=              P= P= P= P= P= P= P= P=f% % % % % % % %P5 5 5 5
; ; ; ;# # # #
$ $ $ $V F 
  )y'*         %I%&=>>    8  $"& w? w? w? w? w? w?@ ' ' ' '0CO CO CO COLA
H A
H A
H A
H A
H& A
H A
H A
HLDD DD DD DDR' ' ' ' ' 's!   ? 	A
AA4 4BB