
    `gj                       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	m
Z
mZ ddlmZ 	 ddlmZ dZ	 dd	lmZmZ dd
lmZ ddlmZmZ ddlmZ ddlmZ ddlmZm Z  ddl!m"Z"m#Z# ddl$m%Z%m&Z&m'Z'm(Z( ddl)m*Z*m+Z+m,Z, dZ-ddl/m0Z0m1Z1 ddl2m3Z3 ddl4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:  ejv                  e<      Z=dZ>dZ?dZ@ddd1dZAe>dd2dZB G d d      ZC G d d      ZD G d d       ZEd3d!ZFd3d"ZGd3d#ZHd4d$ZId%ZJ eKd&d'h      ZLddlMZN eNj                  d(      ZPd5d)ZQdddd*	 	 	 	 	 	 	 	 	 	 	 d6d+ZRd3d,ZS G d- d.e5      ZTd7d/ZUd7d0ZVy# e$ r dZdZY w xY w# e$ r* dZ-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Z,Y w xY w)8a  
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)AnyDictOptional)quotewebTFAppActivityContextClientOptionsMessageActivityConversationReferenceTypingActivityInputAdaptiveCardInvokeActivityAdaptiveCardActionCardResponse!AdaptiveCardActionMessageResponseInvokeResponseAdaptiveCardInvokeResponse
HttpMethodHttpRequestHttpResponseHttpRouteHandlerAdaptiveCardExecuteAction	TextBlock)PlatformPlatformConfig)MessageDeduplicator)BasePlatformAdapterMessageEventMessageType
SendResultcache_image_from_urlcache_media_bytesi  i   z/api/messagesdefaultc                   t        | t              r| S t        | t              r(| j                         j	                         }|dv ry|dv ry|S )N>   1onyestrueT>   0noofffalseF)
isinstanceboolstrstriplower)valuer/   
normalizeds      R/root/.hermes/venv/lib/python3.12/site-packages/plugins/platforms/teams/adapter.py_parse_boolrA   o   sL    %%[[]((*
3344N    c               H    	 t        |       S # t        t        f$ r |cY S w xY wN)int	TypeError
ValueError)r>   r/   s     r@   _coerce_portrH   {   s*    5zz" s   
 !!c                  .    e Zd ZdZddZddd	dZd
dZy)_StaticAccessTokenProviderzSMinimal token-provider shim so outbound Graph delivery can reuse the shared client.c                H    t        |xs d      j                         | _        y N )r;   r<   _access_token)selfaccess_tokens     r@   __init__z#_StaticAccessTokenProvider.__init__   s     !34::<rB   F)force_refreshc               R   K   ~| j                   st        d      | j                   S w)Nz=TEAMS_GRAPH_ACCESS_TOKEN is required for graph delivery mode.)rN   rG   )rO   rR   s     r@   get_access_tokenz+_StaticAccessTokenProvider.get_access_token   s*     !!\]]!!!s   %'c                     y rD    rO   s    r@   clear_cachez&_StaticAccessTokenProvider.clear_cache   s    rB   N)rP   r;   )rR   r:   returnr;   rY   None)__name__
__module____qualname____doc__rQ   rT   rX   rV   rB   r@   rJ   rJ      s    ]= ?D "rB   rJ   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y)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	transportc               .    || _         || _        || _        y rD   )_platform_config_graph_client
_transport)rO   platform_configrb   rc   s       r@   rQ   zTeamsSummaryWriter.__init__   s     !0)#rB   c                H  K   | j                  |      }|r't        |j                  d      d      st        |      S t	        |j                  d      xs |j                  d      xs d      j                         j                         }|sI|j                  d      rd}n5|j                  d	      s"|j                  d
      r|j                  d      rd}|dk(  r| j                  ||       d {   S |dk(  r| j                  ||       d {   S t        d      7 .7 w)Nforce_resendFr.   delivery_modemoderM   incoming_webhook_urlincoming_webhookchat_idteam_id
channel_idgraphz:Teams delivery_mode must be 'incoming_webhook' or 'graph'.)
_resolve_delivery_configrA   getdictr;   r<   r=   #_write_summary_via_incoming_webhook_write_summary_via_graphrG   )rO   payloadconfigexisting_recordmergedrl   s         r@   write_summaryz TeamsSummaryWriter.write_summary   s     ..v6;vzz./ISX#Y((6::o.J&**V2DJKQQSYY[zz01)I&

9%&**\*B%%AA'6RRR7?66wGGGH
 	
 SGs$   C-D"/D0D"D D" D"c           	        i }| j                   }|y|j                  t        |j                  xs i              |j                  rd|vr|j                  |d<   |j
                  r&|j                  d|j
                  j                         |j                  t        |xs i              t        j                  dd      t        j                  dd      t        j                  dd      t        j                  dd      t        j                  dd      t        j                  d	d      d
}|j                         D ]  \  }}|s	|j                  |      r|||<   ! |S )NrP   rq   TEAMS_DELIVERY_MODErM   TEAMS_INCOMING_WEBHOOK_URLTEAMS_GRAPH_ACCESS_TOKENTEAMS_TEAM_IDTEAMS_CHANNEL_IDTEAMS_CHAT_ID)rk   rm   rP   rp   rq   ro   )re   updateru   extratokenhome_channel
setdefaultro   osgetenvitemsrt   )rO   ry   r{   platform_cfgenv_defaultskeyr>   s          r@   rs   z+TeamsSummaryWriter._resolve_delivery_config   s*   !#,,#MM$|117R89!!nF&B)5););~&((!!,0I0I0Q0QRd6<R()  YY'<bA$&II.JB$OII&@"Eyy"5))$6;yy"5
 ',,. 	$JCVZZ_#s	$ rB   c                  K   dd l }t        |j                  d      xs d      j                         }|st	        d      d| j                  |      i}|j                  d| j                        4 d {   }|j                  ||       d {   }|j                          d d d       d {    d	|j                  d
dS 7 P7 77 # 1 d {  7  sw Y   )xY ww)Nr   rm   rM   zATEAMS_INCOMING_WEBHOOK_URL is required for incoming_webhook mode.textg      4@)timeoutrc   )jsonrn   T)rk   webhook_urlstatus_code	delivered)httpxr;   rt   r<   rG   _render_summary_markdownAsyncClientrg   postraise_for_statusr   )rO   rx   ry   r   r   bodyclientresponses           r@   rv   z6TeamsSummaryWriter._write_summary_via_incoming_webhook   s      	&**%;<BCIIK`aa55g>?$$TT__$M 	( 	(QW#[[4[@@H%%'	( 	( 0&#//	
 	
	(@	( 	( 	( 	(sZ   A2C 4C5C 8CCC$C /C	0C C	C CCCC c                6  K   | j                  |      }t        |j                  d      xs d      j                         }|rndt	        |d       d}|j                  |dd| j                  |      di	       d {   }d
d||xs i j                  d      |xs i j                  d      dS t        |j                  d      xs d      j                         }t        |j                  d      xs d      j                         }|r|st        d      dt	        |d       dt	        |d       d}|j                  |dd| j                  |      di	       d {   }d
d|||xs i j                  d      |xs i j                  d      dS 7 7 6w)Nro   rM   z/chats/)safez	/messagesr   html)contentTypecontent)	json_bodyrr   chatidwebUrl)rk   target_typero   
message_idweb_urlrp   rq   zEGraph delivery mode requires chat_id, or both team_id and channel_id.z/teams/z
/channels/channel)rk   r   rp   rq   r   r   )_build_graph_clientr;   rt   r<   r   	post_json_render_summary_htmlrG   )	rO   rx   ry   rb   ro   pathr   rp   rq   s	            r@   rw   z+TeamsSummaryWriter._write_summary_via_graph   s    
 //7fjj+1r288:U745Y?D)33!6dF_F_`gFh#ij 4  H
 ")%"'~22248$N//9  fjj+1r288:L17R8>>@
jW  eG"-.jZb)*)5 	 &//v$B[B[\cBdef 0 
 

 %$$#>r..t4 B++H5
 	
5,
s%   A;F=F>C"F F!4FFc                $   | j                   | j                   S ddlm} ddlm} t        |j                  d      xs d      j                         }|r |t        |      | j                        S  ||j                         | j                        S )Nr   )MicrosoftGraphTokenProvider)MicrosoftGraphClientrP   rM   )rc   )rf   tools.microsoft_graph_authr   tools.microsoft_graph_clientr   r;   rt   r<   rJ   rg   from_env)rO   ry   r   r   rP   s        r@   r   z&TeamsSummaryWriter._build_graph_client  s    )%%%JE6::n5;<BBD'*<8//  $'002oo
 	
rB   c           
     L   d| j                  |       ddd| j                  t        |dd       d       ddg| j                  t        |dd             dd| j                  t        |d	d             dd
| j                  t        |dd             }dj	                  |      S )Nz**rM   z	Summary: summaryNo summary available.zKey decisions:key_decisionszAction items:action_itemszRisks:risks
)_title_textgetattr_bullet_linesjoin)rO   rx   liness      r@   r   z+TeamsSummaryWriter._render_summary_markdown,  s    W%&b)

77It#DF]^_`
 $ GH
 
 
  FG
 
 
 $ ?@
 yyrB   c                   d| j                  t        |dd       d      gfdt        t        |dd       xs g       fdt        t        |dd       xs g       fdt        t        |d	d       xs g       fg}d
t        j                  | j                  |             dg}|D ]  \  }}|j                  dt        j                  |       d       t        |      dk(  r:|dk(  r5|j                  dt        j                  t        |d                d       v|r6dj                  d |D              }|j                  |xr d| dxs d       |j                  d        dj                  |      S )NSummaryr   r   zKey decisionsr   zAction itemsr   Risksr   z<h2>z</h2>z<h3>z</h3>   z<p>r   z</p>rM   c              3     K   | ]@  }t        |      j                         sd t        j                  t        |             d B yw)z<li>z</li>N)r;   r<   r   escape).0items     r@   	<genexpr>z:TeamsSummaryWriter._render_summary_html.<locals>.<genexpr>K  s9     "oD]`ae]f]l]l]nT$++c$i*@)A#G"os
   A'Az<ul>z</ul>z<p>None</p>)
r   r   listr   r   r   appendlenr;   r   )rO   rx   sectionsblocksheadingr   rendereds          r@   r   z'TeamsSummaryWriter._render_summary_html=  sa   GGY$EG^_`ad77OT#J#PbQRT''>4"H"NBOPd77GT:@bAB	
 T[[%9:;5AB& 		-NGUMMDW!5 6e<=5zQ7i#7DKKE!H$>#?tDE77"oTY"oohAT(5+AR]Sm,		- wwvrB   c                    t        | dd       }|rt        |      S t        | dd       }|rt        |dd       nd }d|xs d S )Ntitlemeeting_ref
meeting_idzMeeting r   )r   r;   )rx   r   r   r   s       r@   r   zTeamsSummaryWriter._titleQ  sQ    $/u:g}d;ALW[,=RV
*1	233rB   c                H    t        | xs d      j                         }|xs |S rL   r;   r<   )r>   r/   r   s      r@   r   zTeamsSummaryWriter._textZ  s#    5;B%%'wrB   c                    |xs g D cg c]5  }t        |      j                         st        |      j                         7 }}|D cg c]  }d| 	 c}xs dgS c c}w c c}w )Nz- z- Noner   )clsvaluesr   r   s       r@   r   z TeamsSummaryWriter._bullet_lines_  sX    06"StTART"SS(-."TF.<8*< T.s   AA	A"rD   )rh   zPlatformConfig | Nonerb   z
Any | Nonerc   zhttpx.AsyncBaseTransport | NonerY   r[   )rx   r   ry   dict[str, Any] | Nonerz   zOptional[dict[str, Any]]rY   dict[str, Any])ry   r   rY   r   )rx   r   ry   r   rY   r   )ry   r   rY   r   )rx   r   rY   r;   )r>   r   r/   r;   rY   r;   )r   r   rY   z	list[str])r\   r]   r^   r_   rQ   r|   rs   rv   rw   r   r   r   staticmethodr   r   classmethodr   rV   rB   r@   ra   ra      s    26	$ $(59	$.	$ !		$
 3	$ 
	$ 59	

 &
 2	

 

40

 
 
	
.*
*
 *
 
	*
X
$ "( 4 4   = =rB   ra   c                  8    e Zd ZdZddZd	dZd
dZddZddZy)_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``.
    c                    || _         y rD   )_aiohttp_app)rO   aiohttp_apps     r@   rQ   z_AiohttpBridgeAdapter.__init__m  s
    'rB   c                `    dfd}| j                   j                  j                  |||       y)z2Register an SDK route handler as an aiohttp route.c                p  K   | j                          d {   }t        | j                        } t        ||             d {   }|j	                  dd      }|j	                  d      }|+t        j                  |t        j                  |      d      S t        j                  |      S 7 7 lw)N)r   headersstatus   r   application/json)r   r   content_type)r   )r   ru   r   r   rt   r	   Responsedumps)requestr   r   resultr   	resp_bodyhandlers         r@   _aiohttp_handlerz>_AiohttpBridgeAdapter.register_route.<locals>._aiohttp_handlers  s      'D7??+G+2;DRY3Z+[%[FZZ#.F

6*I$||!I.!3 
 <<v.. (%[s"   B6B2/B6B4A+B64B6N)r   z'web.Request'rY   z'web.Response')r   router	add_route)rO   methodr   r   r   s      ` r@   register_routez$_AiohttpBridgeAdapter.register_routep  s)    	/ 	  **649IJrB   c                     y rD   rV   )rO   r   	directorys      r@   serve_staticz"_AiohttpBridgeAdapter.serve_static  s    rB   c                    K   t        d      w)Nz(aiohttp server is managed by the adapter)NotImplementedError)rO   ports     r@   startz_AiohttpBridgeAdapter.start  s     !"LMMs   c                   K   y wrD   rV   rW   s    r@   stopz_AiohttpBridgeAdapter.stop  s	     s   N)r   z'web.Application')r   z'HttpMethod'r   r;   r   z'HttpRouteHandler'rY   r[   )r   r;   r   r;   rY   r[   )r   rE   rY   r[   rZ   )	r\   r]   r^   r_   rQ   r   r   r   r   rV   rB   r@   r   r   e  s"    (K&NrB   r   c                     t         xr t        S )zDReturn True when all Teams dependencies and credentials are present.)TEAMS_SDK_AVAILABLEAIOHTTP_AVAILABLErV   rB   r@   check_requirementsr    s    4#44rB   c                @   t        | di       xs i }t        j                  d      xs |j                  dd      }t        j                  d      xs |j                  dd      }t        j                  d      xs |j                  dd      }t	        |xr |xr |      S )	zAReturn True when the config has the minimum required credentials.r   TEAMS_CLIENT_ID	client_idrM   TEAMS_CLIENT_SECRETclient_secretTEAMS_TENANT_ID	tenant_id)r   r   r   rt   r:   )ry   r   r  r  r	  s        r@   validate_configr
    s    FGR(.BE		+,J		+r0JIII34V		/SU8VM		+,J		+r0JI	9m9	::rB   c                    t        |       S )z7Check whether Teams is configured (env or config.yaml).)r
  )ry   s    r@   is_connectedr    s    6""rB   c                 Z   t        j                  dd      j                         } t        j                  dd      j                         }t        j                  dd      j                         }| r|r|sy| ||d}t        j                  dd      j                         }|r	 t        |      |d<   t        j                  d	d      j                         }|r||d
<   t        j                  dd      j                         }|r|t        j                  dd      d|d<   |S # t        $ r Y zw xY w)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  rM   r  r  N)r  r  r	  
TEAMS_PORTr   TEAMS_SERVICE_URLservice_urlTEAMS_HOME_CHANNELTEAMS_HOME_CHANNEL_NAMEHome)ro   namer   )r   r   r<   rE   rG   )r  r  r	  seedr   r  homes          r@   _env_enablementr    s    		+R0668III3R8>>@M		+R0668I-I&D
 99\2&,,.D	t9DL ))/4::<K)]99)2.446DII7@ 
^ K  		s   !D 	D*)D*z&https://smba.trafficmanager.net/teams/zsmba.trafficmanager.netz!smba.infra.gov.teams.microsoft.usz^[A-Za-z0-9:@\-_.]+$c                    | sy	 ddl m}  ||       }|j                  dk7  ry|j                  t
        vry| j                  d      r| }|S | dz   }|S # t        $ r Y yw xY w)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)rawr  parsedr?   s       r@   _validate_teams_service_urlr$    sw     )# }}::S)J 03SyJ  s   A 	AA)	thread_idmedia_filesforce_documentc          	       K   t        | di       xs i }t        j                  d      xs |j                  dd      }t        j                  d      xs |j                  dd      }t        j                  d      xs |j                  dd      }	|r|r|	sd	d
iS t        j                  d      xs |j                  dd      xs t        }
t        |
      }|d	dt        t               iS |sd	diS t        j                  |      sd	diS t        j                  |	      sd	diS d|	 d}| d| d}t        sd	diS 	 ddl}|j                  d      }|j                  d      4 d{   }|j                  |d||dddd i|!      4 d{   }|j                  d"k\  rQ|j!                          d{   }d	d#|j                   d$|dd%  icddd      d{    cddd      d{    S |j#                          d{   }ddd      d{    j                  d&      }|sd	d'icddd      d{    S d(|d)d*}|j                  ||d+| d,d-|.      4 d{   }|j                  d"k\  rQ|j!                          d{   }d	d/|j                   d$|dd%  icddd      d{    cddd      d{    S |j#                          d{   }ddd      d{    ddd      d{    dj                  d0      d1S 7 7 7 j7 F7 87 "7 # 1 d{  7  sw Y   &xY w7 7 7 7 7 7 n7 `# 1 d{  7  sw Y   pxY w7 g# 1 d{  7  sw Y   wxY w# t$        j&                  $ r  t(        $ r(}t*        j-                  d2d3       d	d4| icY d}~S d}~ww xY w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  rM   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,  rP   z:Teams standalone send: token response missing access_tokenmessagemarkdown)typer   
textFormatzBearer r   )Authorizationr/  )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   rt   _DEFAULT_TEAMS_SERVICE_URLr$  sortedr   _TEAMS_CONV_ID_REmatchr  aiohttpClientTimeoutClientSessionr   r   r   r   asyncioCancelledErrorr  loggerdebug)pconfigro   r1  r%  r&  r'  r   r  r  r	  raw_service_urlr  	token_urlactivities_url_aiohttpper_request_timeoutsession
token_respr   token_payloadrP   activity	send_respsend_payloades                            r@   _standalone_sendrR    s\    > GWb)/RE		+,J		+r0JIII34V		/SU8VM		+,J		+r0JI-I|}} 			%& 	&99]B'	&% 
 .o>K82346 	 WXX""7+{||""9-noo4YK?QRI#}$5gYkJNGHH4?" '4444@))D)9 %	6 %	6W||"6!*%2D	 ()LM+ $ 
 8 8 $$+!+!22D#'TU_UfUfTggjkoptqtkujv%wx8 8 8%	6 %	6 %	6 '1oo&7 78 8 ),,^<L!]^%%	6 %	6 %	6* "(H
 ||'.|n%=$6 , $  6 6 ##s*!*!11D#'TU^UeUeTffijnospsjtiu%vw6 6 63%	6 %	6 %	6J &/^^%556 63%	6 %	6N &**40
 	
M%	68 38%	6 !88 8 8 8%	626 263%	6J  66 6 6 63%	6 %	6 %	6 %	6T !!  ?3dC9!=>>?s  D9O<,M> (L)M> ,!M)LM)#L-4L5L-M)L!M) M> ,L$-M> 1O2L-L'L-
M)L*M)0M> <M=M> O%M)'M(M)+#MMM)M)5M
6M):M> MM> OMM M$M)/M0M)4M> ?M' M> OM> M)L-!M)$M> 'L-*M)-M 	3L64M 	;M)M> M)M
M)M> MM)M$	MM$	 M)'M> )M;/M20M;7M> >ON=7O8O=OOc                 X    t         rt        rydd} ddlm}  |d| t	               d      S )	a^  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.
    Tc                    ddl m}  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} i d| ddd|d|d|d|d|d|d|d|d|	d|
d|d|d|d|d||||ddS )Nr   r   r
   r   r   r   r   r   r   r   r!   r	   r  Tr   r   r   r   r   r   r   r   r   r   r   r   r   r   r    )r"   r#   r$   r   )r>  r	   microsoft_teams.appsr   r   "microsoft_teams.common.http.clientr   microsoft_teams.apir   r   %microsoft_teams.api.activities.typingr   3microsoft_teams.api.activities.invoke.adaptive_cardr   (microsoft_teams.api.models.adaptive_cardr   r   *microsoft_teams.api.models.invoke_responser   r   !microsoft_teams.apps.http.adapterr   r   r   r    microsoft_teams.cardsr"   r#   r$   )_webr   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r"   r#   r$   s                      r@   _importz)check_teams_requirements.<locals>._import  s   '=DNM	
	
	
	
 	
 	QP
4

 3
 	

 ]
 
 $%:
 "#6
 )*D
 -.L
 01R
 n
 )*D
 *
 ;
  L!
"  0#
$ )*"#'+
 	
rB   r   )ensure_and_bindzplatform.teamsF)prompt)rY   ru   )r   r  tools.lazy_depsr`  globals)r_  r`  s     r@   check_teams_requirementsrd  t  s,     0/
b 0+WgiNNrB   c                      e Zd ZdZdZdZd fdZddddZddZddd	Z	dd
Z
ddZ	 	 	 	 ddZ	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d dZ	 	 d!	 	 	 	 	 	 	 	 	 d"dZd#d$dZ	 	 d%	 	 	 	 	 	 	 	 	 	 	 d&dZ	 	 	 d'	 	 	 	 	 	 	 	 	 	 	 d(dZ	 	 d!	 	 	 	 	 	 	 	 	 d)dZ	 	 	 d'	 	 	 	 	 	 	 	 	 	 	 d*dZ	 	 	 d'	 	 	 	 	 	 	 	 	 	 	 d+dZ	 	 	 	 d,	 	 	 	 	 	 	 	 	 	 	 	 	 d-dZd.dZ xZS )/TeamsAdapterz;Microsoft Teams adapter using the microsoft-teams-apps SDK.`m  Tc                @   t         |   |t        d             |j                  xs i }|j	                  d      xs t        j                  dd      | _        |j	                  d      xs t        j                  dd      | _        |j	                  d      xs t        j                  dd      | _	        t        |j	                  d	      xs# t        j                  d
t        t                          | _        d | _        d | _        t!        d      | _        i | _        y )Nteamsr  r  rM   r  r  r	  r  r   r  i  )max_size)superrQ   r%   r   rt   r   r   
_client_id_client_secret
_tenant_idrH   r;   _DEFAULT_PORT_port_app_runnerr'   _dedup
_conv_refs)rO   ry   r   	__class__s      r@   rQ   zTeamsAdapter.__init__  s    '!23"))K0TBII>OQS4T#ii8`BIIF[]_<`))K0TBII>OQS4T!IIfL<]9K!L

 &*	26)48 +-rB   F)is_reconnectc          
        K   t                t        s j                  ddd       yt        s j                  ddd       y j                  r j
                  r j                  s j                  ddd       y	 t        j                  t              }|j                  j                  d	d
        t         j                   j
                   j                  t        |      t        ddi             _         j                  j                   d fd       } j                  j"                  	 	 	 	 d fd       } j                  j%                          d {    t        j&                  |       _         j(                  j+                          d {    t        j,                   j(                  d j.                        }|j1                          d {    d _         j5                          t6        j9                  d j.                  t:               y7 7 7 E# t<        $ r7} j                  dd| d       t6        j?                  d|       Y d }~yd }~ww xY w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@   <lambda>z&TeamsAdapter.connect.<locals>.<lambda>  s    CLLd<S rB   
User-AgentHermesr   )r  r  r	  http_server_adapterr   c                D   K   j                  |        d {    y 7 wrD   )_on_messagectxrO   s    r@   _handle_messagez-TeamsAdapter.connect.<locals>._handle_message  s     &&s+++s     c                B   K   j                  |        d {   S 7 wrD   )_on_card_actionr  s    r@   _handle_card_actionz1TeamsAdapter.connect.<locals>._handle_card_action  s       "11#6666s   z0.0.0.0Tz0[teams] Webhook server listening on 0.0.0.0:%d%sCONNECT_FAILEDzTeams connection failed: z[teams] Failed to connect: %s)r   ActivityContext[MessageActivity])r  z+ActivityContext[AdaptiveCardInvokeActivity]rY   z1InvokeResponse[AdaptiveCardActionMessageResponse]) rd  r   _set_fatal_errorr  rl  rm  rn  r	   Application_MAX_BODY_BYTESr   add_getr   r   r   rq  
on_messageon_card_action
initialize	AppRunnerrr  setupTCPSiterp  r   _running_mark_connectedrC  info_WEBHOOK_PATHr  r)  )rO   rv  r   r  r  siterQ  s   `      r@   connectzTeamsAdapter.connect  s,     	!""!!d " 
  !!A " 
 d&9&9!!%\ " 
 5	 ///JK&&y2ST//"11//$9+$F$lH-EFDI YY!!, ", YY%%7@7B7 &7 ))&&(((==5DL,,$$&&&;;t||Y

CD**, DM  "KKB


  ) '  	!! +A3/ " 
 LL8!<	si   A:I&>CH# H;H# HAH# H!A H# I&H# H# !H# #	I#,-II&I##I&c                   K   d| _         | j                  r)| j                  j                          d {    d | _        d | _        | j	                          t
        j                  d       y 7 8w)NFz[teams] Disconnected)r  rr  cleanuprq  _mark_disconnectedrC  r  rW   s    r@   
disconnectzTeamsAdapter.disconnect  sV     <<,,&&(((DL	!*+	 )s   1A.A,9A.c                Z  K   ddl m} ddlm}  ||      st	        d      ddl}|j                  |dd|gi      4 d{   }|j                  |d	d
i       d{   }|j                          |j                  cddd      d{    S 7 N7 37 	# 1 d{  7  sw Y   yxY ww)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   )is_safe_url)_ssrf_redirect_guardz/Blocked unsafe attachment URL (SSRF protection)NTr   )r   follow_redirectsevent_hooksr  z)Mozilla/5.0 (compatible; HermesAgent/1.0)r  )
tools.url_safetyr  gateway.platforms.baser  rG   r   r   rt   r   r   )rO   urlr   r  r  r   r   r   s           r@   _fetch_attachment_bytesz$TeamsAdapter._fetch_attachment_bytes'  s      	1?3NOO$$!#&:%;< % 
 
	$ 
	$ #ZZ%'RS (  H %%'##
	$ 
	$ 
	$

	$ 
	$ 
	$ 
	$sY   ?B+BB+BBB>B+
BB+BB+B(BB($B+c                
  K   |j                   }| j                  r| j                  j                  nd}|rt        |j                  dd      |k(  ryt        |dd      }|r| j
                  j                  |      ryt        |j                  dd      }|r|j                  | j                  |<   d}t        |d      r|j                  r|j                  }d|v r%ddl}|j                  dd|      j                         }|j                  }t        |dd      xs d}	|	d	k(  rd
}
n|	dk(  rd}
n
|	dk(  rd}
nd
}
|j                  }t        |dd      xs t        |dd      }t        |dd      xs d}| j                  |j                  t        |dd      xs d|
t!        |      |t        |dd      xs | j"                        }g }g }g }t        |dd      xs g D ]i  }t        |dd      }t        |dd      xs dj%                         }t        |dd      xs d}|dv r|sH|j'                  d      rZ|dk(  r-t        |dd      }t)        |t*              st        |dd      xs i }|j-                  d      xs |j-                  d      }|j-                  d      xs |j-                  d      xs dj/                  d      }|s|xs	 |rd| nd }	 | j1                  |       d{   }t3        ||d!      }|rR|j5                  |j6                         |j5                  |j8                         |j5                  |j:                         nt<        j?                  d"|       |r\|j'                  d$      rK	 tC        |       d{   }|r3|j5                  |       |j5                  |       |j5                  d%       |s	 | j1                  |       d{   }t3        |||!      }|rQ|j5                  |j6                         |j5                  |j8                         |j5                  |j:                         l d |v rtD        jF                  }nOd%|v rtD        jH                  }n:d(|v rtD        jJ                  }n%d)|v rtD        jL                  }ntD        jN                  }tQ        ||||||*      }| jS                  |       d{    y7 # t@        $ r"}t<        j?                  d#||       Y d}~d}~ww xY w7 ~# t@        $ r!}t<        j?                  d&|       Y d}~ld}~ww xY w7 Y# t@        $ r'}t<        j?                  d'|xs |||       Y d}~d}~ww xY w7 w)+z>Process an incoming Teams message and dispatch to the gateway.Nr   rM   r   z<at>r   z<at>[^<]*</at>\s*conversation_typepersonaldm	groupChatgroupr   aad_object_idr  r	  )ro   	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   )*rN  rq  r   r   from_rs  is_duplicateconversationconversation_refrt  hasattrr   resubr<   build_sourcer;   rn  r=   
startswithr9   ru   rt   lstripr  r-   r   r   
media_typekindrC  warningr  r,   r*   DOCUMENTPHOTOVIDEOAUDIOTEXTr)   handle_message)rO   r  rN  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  r0  cachedrQ  msg_typeevents                                  r@   r  zTeamsAdapter._on_messageC  s>    << "&ghnndD9VC 4.dkk..v6 (//t<'*';';DOOG$ 8V$==DT>66.D9??AD $$D"5t<B	
"I+%I)#!II  ~~,>a',X\^`BaL&$7=2	""GGdFD17RLT;5H # 
 
8]D9?R B	C!#}d;K#C>D"KKMLsFD17RH
 ::;&&'GHSS "#y$7!'40%gz4@FBG&{{=9XW[[=X$[[4VK8PVTV^^_bc	##]9)I;(?R\d!%!=!=l!KKD.thRTUF"))&++6#**6+<+<=#**6;;7]$ |66x@V#7#DDF"))&1#**<8#**73 !%!=!=k!JJD.x<F "))&++6#**6+<+<=#**6;;7{B	R $"++H#"((H#"((H#"((H"''H!!#
 !!%(((G L ! dNN#UW_abccd E
 ! VNN#QSTUUV K ! NNJ /Kq < 	)s   KU*S#S$A<S U*6TT9T>U*T5T2A%T5?BU*U(U*S	S?S:4U*:S??U*T	T/T*$U**T//U*2T55	U%>U U* U%%U*c                Z  K   ddl m} | j                  j                  |      }|rP| j                  rD |       j                  |      }| j                  j                  j                  ||       d{   S | j                  r$| j                  j                  ||       d{   S y7 57 w)zJSend an AdaptiveCard, using a stored ConversationReference when available.r   )MessageActivityInputN)rW  r  rt  rt   rq  add_cardactivity_sendersend)rO   ro   cardr  conv_refrN  s         r@   
_send_cardzTeamsAdapter._send_card  s     <??&&w/		+-66t<H2277(KKKYY666 L6s$   A/B+1B'2/B+!B)"B+)B+c                F  K   ddl m}m} |j                  j                  j
                  }|j                  xs i }|j                  dd      }|j                  dd      }|r|st        dt        d      	      S t        j                  d
d      j                         }t        j                  dd      j                         j                         dv }	|	s|s,t        j                  d       t        dt        d      	      S |j                  j                   }
t#        |
dd      xs t#        |
dd      }|j%                  d      D ch c]#  }|j                         s|j                         % }}d|vr1||vr-t        j                  d|       t        dt        d      	      S ddddd}|j                  |      }|st        dt        d      	      S  ||      sIt        dt'        t)               j+                  d      j-                  t/        dd      g            	      S  |||       dd d!d"d#}|j                  d$d      }|j                  d%d      }g }|r=|j1                  t/        d&dd'(             |j1                  t/        d)| d*d             |r |j1                  t/        d+| dd,             |j1                  t/        ||   dd'(             t        dt'        t)               j+                  d      j-                  |            	      S c c}w w)-z4Handle an Adaptive Card Action.Execute button click.r   )resolve_gateway_approvalhas_blocking_approvalhermes_actionrM   session_keyr   zUnknown action.)r>   )r   r   TEAMS_ALLOWED_USERSTEAMS_ALLOW_ALL_USERS>   r1   r3   r4   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   ,*u3   [teams] Unauthorized card action by %s — ignoringu   ⛔ Not authorized.oncerK  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  rK  r  r  cmddesc    ⚠️ Command Approval RequiredBolderr   r  weight```

```Reason: r   r  isSubtle)tools.approvalr  r  rN  r>   actionr0  rt   r   r   r   r   r<   r=   rC  r  r  r   splitr   r"   with_version	with_bodyr$   r   )rO   r  r  r  r  r0  r  r  allowed_csv	allow_allr  
clicker_iduidallowed_ids
choice_mapchoice	label_mapr  r  r   s                       r@   r  zTeamsAdapter._on_card_action  s     	S##**{{ b"5hh}b1K!6=NO  ii 5r:@@BII5r:@@BHHJNbb	I &:b  <<--L EhQ]_cegIhJ2=2C2CC2HX3CIIK399;XKX+%*K*GTV`a%:AVW  #(&	

 .!6=NO 
 %[1!3&.!\%(Y	/]dh ijk  	!f5 ).* 	
	 hhub!xx#KK	'IPT]efgKK	se5'9EFKK	$'8tdSTI9V#44QR/"n11%8BB4H
 	
c Ys   EL!LL/F2L!c           	       K   | j                   st        dd      S t        |      dkD  r|dd dz   n|}|t        |      dkD  r|dd dz   n||d}	t        d	d
i |	ddid      g}
|sH|
j	                  t        dd
i |	ddi             |r#|
j	                  t        dd
i |	ddi             |
j	                  t        dd
i |	ddid             t        ddd      t        d| dd      t        d| dd      g}|r|j	                  t        d d             t               j                  d!      j                  |      j                  |
      }	 | j                  ||       d{   }|rt        |d"d      nd}t        d|#      S 7 "# t        $ r9}t        j                  d$|d%       t        dt        |      d&      cY d}~S d}~ww xY ww)'z>Send an Adaptive Card approval prompt with Allow/Deny buttons.FTeams app not initializedr7  r)  i  Nz...r   )r  r  r  z
Allow Oncehermes_approver  r  positive)r   verbr0  stylezAllow Sessionr  )r   r  r0  zAlways Allowr  Denyr  destructiver  Tr  r  r  r	  r   r
  r  z>Smart DENY: owner override applies to this one operation only.r  r   r6  z%[teams] send_exec_approval failed: %sr8  r7  r)  ry  )rq  r+   r   r#   r   r$   r"   r  r  with_actionsr  r   r  rC  r)  r;   )rO   ro   commandr  descriptionmetadataallow_permanentsmart_deniedcmd_previewbtn_data_baseactionsr   r  r   r   rQ  s                   r@   send_exec_approvalzTeamsAdapter.send_exec_approvalF  s     yye3NOO03Gt0Cgetnu, ',/L3,>74C=5(G
 !%5CMC?NC:
  NN=%,<JJ8IJ  }(/?MMM?<LM   	}/;M;?F;=
 	
 =DQYZU;-u5DAXk]3$N

 KK	U\`  ~**51;;DANNwW	K??7D99F8>t4DJdzBB :  	KLL@!dLSe3q6TJJ	KsH   EGF .F/!F GF 	G.G
GGGGc                  K   | j                   st        dd      S | j                  |      }| j                  |      }d }|D ]r  }	 |r<|j	                         r,|dk7  r'	 | j                   j                  |||       d {   }	n$| j                   j                  ||       d {   }	t        |	dd       }t t        d|	      S 7 E# t        $ rE}
t        j                  d|
       | j                   j                  ||       d {  7  }	Y d }
~
gd }
~
ww xY w7 s# t        $ r#}t        dt        |      d      cY d }~c S d }~ww xY ww)
NFr  r  r5   z3Teams reply() failed, falling back to flat send: %sr   Tr#  r6  )rq  r+   format_messagetruncate_messageisdigitreplyr  rC  rD  r  r   r;   )rO   ro   r   reply_tor'  	formattedchunkslast_message_idchunkr   	reply_errrQ  s               r@   r  zTeamsAdapter.send  s4     yye3NOO''0	&&y1 	OEO 0 0 2x3
F'+yyw%'P!P $(99>>'5#AAF")&$"=!	O( $?CC! "Q$ F Q% (,yy~~gu'E!E!EF B O!%s1vNNOs   AED C>C?C D#D$D5EC	D5DDD
DDD	E"E 8E9E EEc                   K   | j                   sy 	 | j                   j                  |t                      d {    y 7 # t        $ r Y y w xY wwrD   )rq  r  r   r  )rO   ro   r'  s      r@   send_typingzTeamsAdapter.send_typing  sB     yy	))..*=*?@@@ 		s5   A'A  >A  AA   	A	AAAc                   K   | j                   st        dd      S 	 ddl}ddl}ddlm}m}	 |j                  d      s|j                  d      r-|}
|j                  |j                  d	      d         d   xs |}nq|j                  d
      }|j                  |      d   xs |}t        |d      5 }d| d|j                  |j                               j                          }
ddd        ||
      } |	       j                  |      }|r|j!                  |      }| j"                  j%                  |      }|r/| j                   j&                  j)                  ||       d{   }n$| j                   j)                  ||       d{   }t        dt+        |dd            S # 1 sw Y   xY w7 M7 *# t,        $ r:}t.        j1                  d||d       t        dt3        |      d      cY d}~S d}~ww xY w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.
        Fr  r  r   N)
Attachmentr  zhttp://zhttps://?zfile://rbzdata:z;base64,)r   r  Tr   r6  z[teams] send_%s failed: %sr8  r#  )rq  r+   base64	mimetypesrW  r<  r  r  
guess_typer  removeprefixopen	b64encodereaddecodeadd_attachmentsadd_textrt  rt   r  r  r   r  rC  r)  r;   )rO   ro   r  default_mimecaptionmedia_labelr?  r@  r<  r  r  r  r   f
attachmentrN  r  r   rQ  s                      r@   _send_media_attachmentz#TeamsAdapter._send_media_attachment  s      yye3NOO	KL  +v/@/@/L$%00c1B11EFqIY\	 **95%006q9I\	$% c$))HV=M=Maffh=W=^=^=`<a"bKc $TJ+-==jIH#,,W5**73H#yy88==hQQ#yy~~gx@@dwvtT7RSSc c R@  	KLL5{APTLUe3q6TJJ	Ksl   G>BF8 14F(%BF8 &F4'$F8 F6F8 'G>(F1-F8 6F8 8	G;/G60G;1G>6G;;G>c                J   K   | j                  ||d|d       d {   S 7 w)Nz	image/pngr  ro   r  rI  rJ  rK  rN  )rO   ro   	image_urlrJ  r3  r'  s         r@   
send_imagezTeamsAdapter.send_image  s9      00$ 1 
 
 	
 
   #!#c                H   K   | j                  ||||       d {   S 7 w)N)ro   rR  rJ  r3  )rS  )rO   ro   
image_pathrJ  r3  kwargss         r@   send_image_filezTeamsAdapter.send_image_file  s4      __ 	 % 
 
 	
 
s   " "c                J   K   | j                  ||d|d       d {   S 7 w)Nz	video/mp4r  rP  rQ  )rO   ro   
video_pathrJ  r3  r'  rW  s          r@   
send_videozTeamsAdapter.send_video  s9      00$ 1 
 
 	
 
rT  c                J   K   | j                  ||d|d       d {   S 7 w)Nz
audio/mpegvoicerP  rQ  )rO   ro   
audio_pathrJ  r3  r'  rW  s          r@   
send_voicezTeamsAdapter.send_voice  s9      00% 1 
 
 	
 
rT  c                J   K   | j                  ||d|d       d {   S 7 w)Nzapplication/octet-streamr  rP  rQ  )rO   ro   	file_pathrJ  	file_namer3  r'  rW  s           r@   send_documentzTeamsAdapter.send_document   s9      003" 1 
 
 	
 
rT  c                   K   |d|dS w)Nunknown)r  r3  ro   rV   )rO   ro   s     r@   get_chat_infozTeamsAdapter.get_chat_info2  s     wGGs   	)ry   r&   )rv  r:   rY   r:   rZ   )g      >@)r  r;   r   floatrY   bytes)r  r  rY   r[   )ro   r;   r  z'AdaptiveCard'rY   z'Any')r  z-'ActivityContext[AdaptiveCardInvokeActivity]'rY   z3'InvokeResponse[AdaptiveCardActionMessageResponse]')zdangerous commandNTF)ro   r;   r%  r;   r  r;   r&  r;   r'  Optional[Dict[str, Any]]r(  r:   r)  r:   rY   r+   )NN)
ro   r;   r   r;   r3  Optional[str]r'  ri  rY   r+   rD   )ro   r;   r'  ri  rY   r[   )Nmedia)ro   r;   r  r;   rI  r;   rJ  rj  rK  r;   rY   r+   )NNN)ro   r;   rR  r;   rJ  rj  r3  rj  r'  ri  rY   r+   )
ro   r;   rV  r;   rJ  rj  r3  rj  rY   r+   )ro   r;   rZ  r;   rJ  rj  r3  rj  r'  ri  rY   r+   )ro   r;   r^  r;   rJ  rj  r3  rj  r'  ri  rY   r+   )NNNN)ro   r;   ra  r;   rJ  rj  rb  rj  r3  rj  r'  ri  rY   r+   )ro   r;   rY   ru   )r\   r]   r^   r_   MAX_MESSAGE_LENGTHsplits_long_messagesrQ   r  r  r  r  r  r  r-  r  r:  rN  rS  rX  r[  r_  rc  rf  __classcell__)ru  s   @r@   rf  rf    s   E-  5: Qf,$8V)p
]
@]
	<]
H /-1 $"9K9K 9K 	9K
 9K +9K 9K 9K 
9K~ #'-1"D"D "D  	"D
 +"D 
"DH "&"0K0K 0K 	0K
 0K 0K 
0Kl "&"&-1

 
 	

  
 +
 

( "&"&

 
 	

  
 

& "&"&-1

 
 	

  
 +
 

* "&"&-1

 
 	

  
 +
 

* "&#'"&-1

 
 	

 !
  
 +
 

$HrB   rf  c                 v   ddl m} m} ddlm}m}m}m}m}  | d      }|r |d| d        |dd      sy	 |d
        |d        |d       t                 |d        |d        |d       t                 |d       t                 |d|xs d      }|s	 |d       y	 |d|j                                 |d | d      xs dd      }	|	s	 |d       y	 |d|	j                                 |d | d      xs d      }
|
s	 |d       y	 |d|
j                                t                 |d        |dd      rB |d | d       xs d      }|r" |d |j                  d!d              |d"       n |d d       n |d#d$        |d%       t                 |d&        |d'        |d(       y	))z7Guide the user through Teams setup using the Teams CLI.r   )get_env_valuesave_env_value)ra  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 IDrM   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  r4   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.configrp  rq  hermes_cli.cli_outputra  rr  rs  rt  ru  printr<   replace)rp  rq  ra  rr  rs  rt  ru  existing_idr  r  r	  alloweds               r@   interactive_setupr  8  s       12K8QGH159CD>?	GRS%&bc	G^_	G{K,=2>IFG$ioo&78?MBW4X4^\^imnMJK(-*=*=*?@{M:K,L,RPRSIFG$ioo&78	GUVGN6!"78>B
 0'//#r2JK010"5.7^_	G?@OPBCrB   c                    | j                  ddd t        t        t        g ddt        t
        dt        ddd	d
dd       y)u:   Plugin entry point — called by the Hermes plugin system.ri  zMicrosoft Teamsc                    t        |       S rD   )rf  )cfgs    r@   r  zregister.<locals>.<lambda>  s    L$5 rB   )r  r  r  z(pip install microsoft-teams-apps aiohttpr  r  r  rg  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  rR  )r  s    r@   registerr    sT    5#'!R?" * 2 ./- !0A  %rB   )r>   r   r/   r:   rY   r:   )r>   r   r/   rE   rY   rE   )rY   r:   )rY   zdict | None)r"  r;   rY   rj  )ro   r;   r1  r;   r%  rj  r&  zOptional[list]r'  r:   rY   zDict[str, Any]rZ   )Wr_   
__future__r   rA  r   r   loggingr   typingr   r   r   r  r   r>  r	   r  ImportErrorrU  r   r   rV  r   rW  r   r   rX  r   rY  r   rZ  r   r   r[  r   r   r\  r   r   r   r    r]  r"   r#   r$   r   r;   gateway.configr%   r&   gateway.platforms.helpersr'   r  r(   r)   r*   r+   r,   r-   	getLoggerr\   rC  ro  r  r  rA   rH   rJ   ra   r   r  r
  r  r  r:  	frozensetr   r  	_re_teamscompiler<  r$  rR  rd  rf  r  r  rV   rB   r@   <module>r     s:  , #     	 & & 
'9@JI^ f  ML, 4 9  
		8	$  05 	 0=   P= P=f% %P5
;#
$V F 
  )'*    %I%%&=> 8  $"& w?w? w?
 w?  w? w? w?@>OB~	H& ~	HFDDR'g*  
C0  M
COO !%%)"(,%!%NJKLLMI's#   E A
E 	EE+FF