+
    kHàjS6  ã                  óT  € R t ^ RIHt ^ RIHt ^ RIHt ^ RIHt R^R^
R^R^R	^R
^R^R^R^#R^&/
t	]
! R ]	P                  4        4       4      t^t]! RR7       ! R R4      4       tR R ltR R ltR R ltR R ltR R ltR R ltR  R! ltR" R# ltR$ R% ltR& R' ltR( R) ltR*# )+u–  Discord onboarding: check a bot token against Discord and walk the user through setup.

The Developer Portal is where Discord setup goes wrong: a token copied from the wrong page, the
Message Content Intent left off (Discord then refuses the bot's connection), an invite URL built
by hand, and Developer Mode just to learn your own user ID. ``GET /applications/@me`` with the
bot token answers all of it at once â€” the token works, which intents are on, how many servers the
bot is in, and who owns the application â€” so the CLI wizard and the dashboard/Desktop check route
both read :func:`check_bot_token` instead of asking the user to verify those by hand.
)Úannotations)Ú	dataclass)ÚOptional)Ú	urlencodezAdd ReactionszView ChannelszSend MessageszEmbed LinkszAttach FileszRead Message HistoryÚConnectÚSpeakzCreate Public ThreadszSend Messages in Threadsc              #  ó4   "  € T F  p^V,          x € K  	  R# 5i)é   N© )Ú.0Úbits   & ÚI/home/ubuntu/.hermes/hermes-agent/plugins/platforms/discord/onboarding.pyÚ	<genexpr>r      s   é € ÐMÑ-L c˜˜cŸšÓ-Lùs   ‚T)Úfrozenc                  ó€   € ] tR t^#t$ R]R&   R]R&   R]R&   R]R&   R]R&   R	]R
&   ]R R l4       t]R R l4       tRtR# )ÚDiscordBotCheckÚstrÚapp_idÚbot_nameútuple[tuple[str, str], ...]ÚownersÚboolÚmessage_contentÚserver_memberszOptional[int]Úserver_countc               ó   € V ^8„  d   QhRR/# ©é   Úreturnr   r
   )Úformats   "r   Ú__annotate__ÚDiscordBotCheck.__annotate__.   s   € ÷ ?ñ ?˜Cñ ?ó    c           
     	óN   € \        R V P                  RRR\        R^ /4      pRV 2# )Ú	client_idÚscopezbot applications.commandsÚpermissionsÚintegration_typez%https://discord.com/oauth2/authorize?)r   r   ÚINVITE_PERMISSIONS)ÚselfÚquerys   & r   Ú
invite_urlÚDiscordBotCheck.invite_url-   s;   € ô ˜;¨¯©°WÐ>YØ(Ô*<Ð>PÐRSðUó Vˆà6°u°gÐ>Ð>r"   c               ó   € V ^8„  d   QhRR/# r   r
   )r   s   "r   r    r!   6   s   € ÷ Pñ P #ñ Pr"   c                	ó"   € R V P                    R2# )z,https://discord.com/developers/applications/z/bot)r   )r)   s   &r   Úbot_settings_urlÚ DiscordBotCheck.bot_settings_url5   s   € à=¸d¿k¹k¸]È$ÐOÐOr"   r
   N)	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__annotations__Úpropertyr+   r/   Ú__static_attributes__r
   r"   r   r   r   #   sL   ‡ àƒKØƒMà'Ó'ØÓØÓØÓàô?ó ð?ð ôPó ôPr"   r   c               ó    € V ^8„  d   QhRRRR/# )r   ÚappÚdictr   r   r
   )r   s   "r   r    r    :   s   € ÷ 	gñ 	g�ð 	gÐ5ñ 	gr"   c                óð  € V P                  R 4      p\        V\        4      '       d_   \        ;QJ d-    . R VP                  R4      ;'       g    .  4       F  NK  	  5# ! R VP                  R4      ;'       g    .  4       4      # V P                  R4      ;'       g    / pVP                  R4      '       d?   \	        VR,          4      \	        VP                  R4      ;'       g
    VR,          4      33# R# )Úteamc              3  óP  "  € T Fœ  pVP                  R 4      \        8X  g   K  VP                  R/ 4      P                  R4      '       g   KH  \        VR,          R,          4      \        VR,          P                  R4      ;'       g    VR,          R,          4      3x € Kž  	  R# 5i)Úmembership_stateÚuserÚidÚusernameN)ÚgetÚ_TEAM_MEMBER_ACCEPTEDr   )r   Úms   & r   r   Ú_owners.<locals>.<genexpr>=   s‚   é € ð 
á.�Ø�u‰uÐ'Ó(Ô,AÑAô VàFGÇeÁeÈFÐTVÓFW×F[ÑF[Ð\`×Faô VŒS��6•˜4•Ó!¤3 q¨¥y§}¡}°ZÓ'@×'SÐ'SÀAÀfÅIÈdÅOÓ#TÕUÛ.ùs   ‚B&£$B&Á;B&ÂB&ÚmembersÚownerr@   rA   r
   )rB   Ú
isinstancer:   Útupler   )r9   r<   rG   s   &  r   Ú_ownersrJ   :   sÒ   € Ø�7‰7�6‹?€DÜ�$œ×ÒßŒuñ 
à—X‘X˜iÓ(×.Ð.¨BÐ.ó
�uð 	
ˆuñ 
à—X‘X˜iÓ(×.Ð.¨BÐ.ó
ó 
ð 	
ð
 �G‰G�GÓ×"Ð" €EØOTÏyÉyÐY]ÏÊŒS��t•Óœs 5§9¡9¨ZÓ#8×#GÐ#G¸EÀ$½KÓHÐIÐKÐfÐdfÐfr"   c               ó    € V ^8„  d   QhRRRR/# )r   Útokenr   r   r   r
   )r   s   "r   r    r    F   s   € ÷ ñ ˜3ð  ?ñ r"   c                ó8  € ^ RI HpHpHp V! RRV P	                  4       ^
R7      p\        VP                  R4      ;'       g    ^ 4      pVP                  R4      ;'       g    / pVP                  R4      p\        \        VR,          4      \        VP                  R	4      ;'       g#    VP                  R
4      ;'       g
    VR,          4      \        V4      \        WR,          4      \        WQ,          4      \        V\
        4      '       d   VR7      # RR7      # )z„Read the application behind ``token``. Raises :class:`DiscordAPIError` (401 = bad token)
or ``OSError`` when Discord is unreachable.)Ú_FLAGS_GUILD_MEMBERSÚ_FLAGS_MESSAGE_CONTENTÚ_discord_requestÚGETz/applications/@me)ÚtimeoutÚflagsÚbotÚapproximate_guild_countr@   rA   ÚnameN)r   r   r   r   r   r   )Útools.discord_toolrN   rO   rP   ÚstripÚintrB   r   r   rJ   r   rH   )rL   rN   rO   rP   r9   rS   rT   Úcounts   &       r   Úcheck_bot_tokenr[   F   sá   € ÷ bÑaÙ
˜5Ð"5°u·{±{³}ÈbÔ
Q€CÜ�—‘˜Ó ×%Ð% AÓ&€EØ
�'‰'�%‹.×
Ð
˜B€CØ�G‰GÐ-Ó.€EÜÜ�3�t•9‹~Ü�S—W‘W˜ZÓ(×HÐH¨C¯G©G°F«O×HÐH¸sÀ4½yÓIÜ�s‹|Ü˜UÕ;Ó<Ü˜EÕ8Ó9Ü(¨´×4Ò4�Uôð ð ;?ôð r"   c               ó    € V ^8„  d   QhRRRR/# )r   Úrawr   r   Úlistr
   )r   s   "r   r    r    [   s   € ÷ ñ  ð ¨ñ r"   c                ó¨  € . pV P                  RR4      P                  R4       Fª  pVP                  4       pVP                  R4      '       d8   VP	                  R4      '       d!   VP                  R4      P                  R4      pVP                  4       P                  R4      '       d
   VR,          pV'       g   K™  VP                  V4       K¬  	  V# )	zGStrip common Discord mention prefixes from a comma-separated ID string.Ú Ú Ú,z<@Ú>z<@!zuser::é   NN)	ÚreplaceÚsplitrX   Ú
startswithÚendswithÚlstripÚrstripÚlowerÚappend)r]   ÚcleanedÚuids   &  r   Ú_clean_discord_user_idsro   [   s    € à€GØ�{‰{˜3 Ó#×)Ñ)¨#Ö.ˆØ�i‰i‹kˆØ�>‰>˜$×Ò C§L¡L°×$5Ò$5Ø—*‘*˜UÓ#×*Ñ*¨3Ó/ˆCØ�9‰9‹;×!Ñ! '×*Ò*Ø�b•'ˆCß‰3Ø�N‰N˜3Öñ /ð €Nr"   c               ó    € V ^8„  d   QhRRRR/# )r   rL   r   r   zOptional[str]r
   )r   s   "r   r    r    i   s   € ÷ ñ  cð ¨mñ r"   c                ób   € V '       d'   V P                  4       P                  4       '       d   R# R# )a4  Reject a Discord bot token that is really the numeric application ID.

Users routinely paste the application ID from the Developer Portal's General Information page
instead of the bot token (Bot page). A real bot token is dot-separated base64 and never purely
numeric, so this is a safe, narrow shape check.
z¯That looks like a numeric application ID, not a bot token. Paste the bot token from the Discord Developer Portal (Bot page), not the application ID (General Information page).N)rX   Úisdigit)rL   s   &r   Ú_discord_token_shape_errorrs   i   s&   € ÷ �—‘“×&Ñ&×(Ò(ñEñ r"   c               ó   € V ^8„  d   QhRR/# r   r
   )r   s   "r   r    r    w   s   € ÷ ñ ¨ñ r"   c                óš   € ^ RI Hp Rp\        ^4       F3  pV ! RRR7      pV'       g    R# \        V4      pVf   Vu # V! V4       K5  	  V# )zPPrompt for the bot token, re-prompting once when the answer is a numeric app ID.)Úprint_errorra   zDiscord bot tokenT)Úpassword)Úhermes_cli.cli_outputrv   Úrangers   )Úpromptrv   rL   Ú_attemptÚerrors   &    r   Ú_prompt_discord_bot_tokenr}   w   sN   € å1Ø€EÜ˜!–HˆÙÐ*°TÔ:ˆßÚÜ*¨5Ó1ˆØŠ=ØŠLÙ�EÖñ ð €Lr"   c               ó   € V ^8„  d   QhRR/# )r   r   z%tuple[str, Optional[DiscordBotCheck]]r
   )r   s   "r   r    r    ‡   s   € ÷ ñ Ð%Jñ r"   c                ó  € ^ RI HpHpHp ^ RIHp ^ RIHp \        ^4       FT  pV! R\        V 4      4      P                  4       pV'       g   Ru #  \        V4      pT! RTP                   R24       Yx3u # 	  V! R4       R#   \         d    T! R4        Kx  T dF   p	T	P                  R8X  d   T! R4        Rp	?	K�  T! R	T	P                   R
24       TR3u Rp	?	u # Rp	?	i\         d   p	T! RT	 R
24       TR3u Rp	?	u # Rp	?	ii ; i)z‚A token Discord accepted (with its check), an unverifiable one (offline: ``None`` check),
or ``("", None)`` when the user gave up.)rv   Úprint_successÚprint_warning)Ú_check_non_ascii_credential©ÚDiscordAPIErrorÚDISCORD_BOT_TOKENNzSThat isn't a bot token (it contains a line break). Copy it again from the Bot page.i‘  zeDiscord rejected that token. On the Bot page click Reset Token, copy the new token and paste it here.z,Couldn't verify the token (Discord answered z); saving it anyway.z,Couldn't reach Discord to verify the token (zToken works: this is the bot "Ú"z:Discord rejected three tokens in a row; nothing was saved.)ra   N)rx   rv   r€   r�   Úhermes_cli.configr‚   rW   r„   ry   r}   rX   r[   Ú
ValueErrorÚstatusÚOSErrorr   )
rz   rv   r€   r�   r‚   r„   r{   rL   ÚcheckÚexcs
   &         r   Ú_prompt_checked_tokenr�   ‡   s  € ÷ PÑOÝ=Ý2Ü˜!–Hˆá+Ð,?ÔAZÐ[aÓAbÓc×iÑiÓkˆßØŠOð	Ü# EÓ*ˆEñ 	Ð7¸¿¹Ð7GÀrÐJÔKØˆ|Òñ+ ñ, ÐLÔMØ€Oøô! ô 	ÙÐmÔnÚØô 	Ø�z‰z˜SÔ Ùð Dô EåÙÐHÈÏÉÈÐThÐiÔjØ˜$�;×ûÜô 	ÙÐHÈÈÐMaÐbÔcØ˜$�;×ûð	úsH   ÁBÂD
ÂD
ÂD
Â C Â?C ÃD
Ã D
Ã-D
Ã.DÃ=D
ÄD
c               ó$   € V ^8„  d   QhRRRRRR/# )r   rL   r   r‹   r   r   r
   )r   s   "r   r    r    §   s"   € ÷ ñ ¨#ð °oð ÐRañ r"   c                óÄ  € ^ RI HpHpHp ^ RIHp \        ^4       Fu  pVP                  '       d    MbV! R4       V! RVP                   24       V! R4       V! R4      P                  4       P                  4       R8X  d   Vu #  \        V 4      pKw  	  VP                  '       d	   V! R4       VP                  '       g	   V! R	4       V#   T\        3 d    Tu u # i ; i)
é    )Ú
print_infor€   r�   rƒ   zVMessage Content Intent is OFF: Discord will refuse the bot's connection until it's on.z   Turn it on here: uK      (Privileged Gateway Intents â†’ Message Content Intent â†’ Save Changes)z7Press Enter once it's saved to re-check, or type 'skip'ÚskipzMessage Content Intent is onz|Optional: turn on Server Members Intent on the same page if you want to allow people by username or role instead of user ID.)rx   r‘   r€   r�   rW   r„   ry   r   r/   rX   rk   r[   rŠ   r   )rL   r‹   rz   r‘   r€   r�   r„   r{   s   &&&     r   Ú_ensure_message_content_intentr“   §   sØ   € ßNÑNÝ2Ü˜!–HˆØ× × Ð ÙÙÐnÔoÙÐ)¨%×*@Ñ*@Ð)AÐBÔCÙÐ`ÔaÙÐKÓL×RÑRÓT×ZÑZÓ\Ð`fÔfØŠLð	Ü# EÓ*ŠEñ ð ××ÐÙÐ4Ô5Ø××ÐÙð Jô 	Kà€Løð  ¤Ð)ô 	ØŒLð	ús   ÂCÃCÃCc               ó    € V ^8„  d   QhRRRR/# )r   r‹   r   r   ÚNoner
   )r   s   "r   r    r    ¾   s   € ÷ Wñ W˜ð W¨Tñ Wr"   c                ó¤   € ^ RI Hp \        4        V P                  ^ 8X  d
   V! R4       MV! R4       V! RV P                   24       V! R4       R# )r�   )r‘   uH   ðŸ“¨ The bot isn't in any server yet. Open this link to add it to yours:uM   ðŸ“¨ Invite link (adds the bot to a server with the permissions Hermes uses):z   zD   Once you share a server with the bot you can also DM it directly.N)rx   r‘   Úprintr   r+   )r‹   r‘   s   & r   Ú_print_inviter˜   ¾   sI   € Ý0Ü	„GØ×Ñ˜QÔÙÐ]Õ^áÐbÔcÙ��U×%Ñ%Ð&Ð'Ô(ÙÐUÖVr"   c               ó    € V ^8„  d   QhRRRR/# )r   r‹   úOptional[DiscordBotCheck]r   r•   r
   )r   s   "r   r    r    É   s   € ÷  
ñ  
Ð6ð  
ÐRVñ  
r"   c                ó¬  a€ ^ RI HpHp ^ RIHpHp \        4        V! R4       \        V! R4      ;'       g    R4      oS'       d   V! RRP                  S4       24       V '       dî   V P                  '       dÜ   \        ;QJ d)    V3R lV P                   4       F  '       d   K   R	M	  R
M! V3R lV P                   4       4      '       gˆ   RP                  R V P                   4       4      p\        V P                  4      ^8X  d   RV R2MRV R2pV! VR
4      '       d1   SV P                   U	U
u. uF  w  ršV	S9  g   K  V	NK  	  up
p	,          oV! R4       V! R4       T! S'       d   RMR4      p\        T;'       g    R4      pSV U	u. uF  q™S9  g   K  V	NK  	  up	,          oV '       db   V P                  '       gP   \        ;QJ d    R V 4       F  '       g   K   R
M	  R	M! R V 4       4      '       d   V! RV P                   24       S'       d#   V! RRP                  S4      4       V! R4       R# V! R4       R# u up
p	i u up	i )r�   )r‘   r€   )Úget_env_valueÚsave_env_valueuC   ðŸ”’ Security: only allowlisted Discord users can talk to your bot.ÚDISCORD_ALLOWED_USERSra   z   Already allowed: z, c              3  ó2   <"  € T F  w  rVS9   x € K  	  R # 5i©Nr
   )r   rn   Ú_nameÚalloweds   &  €r   r   Ú$_prompt_allowlist.<locals>.<genexpr>Ò   s   øé € Ð)XÉ<¹Z¸S¨#°®.Ë<ùs   ƒFTc              3  ó0   "  € T F  w  rR V 2x € K  	  R# 5i)Ú@Nr
   )r   Ú_uidrV   s   &  r   r   r£   Ó   s   é € ÐB±\¡z t˜!˜D˜6�
³\ùs   ‚zAllow yourself (z) to talk to the bot?zAllow your team (uO      Others need their Discord user ID: Settings â†’ Advanced â†’ Developer Mode,uD      then right-click their name â†’ Copy User ID. Usernames work too.z4Other allowed users (comma-separated, Enter to skip)zLAllowed user IDs or usernames (comma-separated, leave empty for open access)c              3  óJ   "  € T F  qP                  4       '       * x € K  	  R # 5ir    )rr   )r   rn   s   & r   r   r£   Þ   s   é € Ð1UÉuÈ·k±k³m×2CÑ2CËuùs   ‚!#zX   Usernames are resolved when the gateway connects, which needs Server Members Intent: rb   zDiscord allowlist configuredu»   âš ï¸�  No allowlist set. Discord will deny messages until you set DISCORD_ALLOWED_USERS, DISCORD_ALLOWED_ROLES, DISCORD_ALLOWED_CHANNELS, or DISCORD_ALLOW_ALL_USERS=true for open access.N)rx   r‘   r€   r‡   rœ   r�   r—   ro   Újoinr   ÚallÚlenr   Úanyr/   )r‹   rz   Úprompt_yes_nor‘   r€   rœ   r�   ÚwhoÚquestionrn   r¡   r]   Úextrar¢   s   &&&          @r   Ú_prompt_allowlistr°   É   sÕ  ø€ ß?ß?Ü	„GÙÐTÔUä%¡mÐ4KÓ&L×&RÐ&RÐPRÓS€GßÙÐ)¨$¯)©)°GÓ*<Ð)=Ð>Ô?ß�——�§c£cÔ)XÈ5Ï<Ê<Ó)X§c§c¢cÔ)XÈ5Ï<Ê<Ó)X×&XÒ&XØ�i‰iÑB°U·\²\ÓBÓBˆÜEHÈÏÉÓEVÐZ[ÔE[Ð& s eÐ+@ÑAØ,¨S¨EÐ1FÐGð 	á˜ 4×(Ò(Ø¨e¯lªlÔQ©l¡
 ¸cÈÑ>PŸ˜©lÒQÕQˆGÙÐ`ÔaÙÐUÔVÙ
Ï7ÑGØdóf€Cä# C§I I¨2Ó.€EØ™uÓ;™u˜°7Ñ(:—�™uÑ;Õ;€Gß�U×)×)Ð)¯c«cÑ1UÉuÓ1U¯c¯cªcÑ1UÉuÓ1U×.UÒ.UÙÐmØ×,Ñ,Ð-ð/ô 	0çÙÐ.°·±¸Ó0AÔBÙÐ4Ö5áð?ö	
ùó Rùò <s   Ä6IÅIÆIÆIc               ó    € V ^8„  d   QhRRRR/# )r   rL   r   r   rš   r
   )r   s   "r   r    r    ì   s   € ÷ ñ ˜cð Ð&?ñ r"   c                ób   € ^ RI Hp  V '       d   \        V 4      # R#   T\        3 d     R# i ; i)r�   rƒ   N)rW   r„   r[   rŠ   )rL   r„   s   & r   Ú_check_saved_tokenr³   ì   s3   € Ý2ðß).Œ˜uÓ%Ð8°DÐ8øØœWÐ%ô Úðús   ˆ �
 › �.­.c               ó   € V ^8„  d   QhRR/# )r   r   r•   r
   )r   s   "r   r    r    ô   s   € ÷ 2,ñ 2,˜4ñ 2,r"   c                 ó˜  € ^ RI Hp HpHpHpHp ^ RIHpHpH	p ^ RI
Hp V ! R4       V! RRR4      '       dO   V! R4      '       g?   V! R4       V! R	R
4      '       d(   \        V! R4      ;'       g    R4      p	\        W“V4       R# R F  p
V! V
4       K  	  \        V4      w  r¼V'       g   R# V! RV4       V! R4       Vf
   V! R4       M\        W¼V4      p\!        V4       \        WÃV4       \#        4        R F  p
V! V
4       K  	  V! R4      P%                  4       pV'       d   V! RV4       R# V! R4      '       d   V! R4       R# R# )z¥Guide the user through Discord bot setup: token (checked live), intents, invite link,
allowlist (defaults to the bot's owner) and home channel. CLI imports are lazy.)Úprint_headerr‘   r€   rz   r¬   )rœ   Úremove_env_valuer�   )Údeclines_reconfigureÚDiscordzReconfigure Discord?r…   rž   uµ   âš ï¸�  Discord has no user allowlist. With the fail-closed default, messages are denied unless you configure allowed users, roles, or channels, or set DISCORD_ALLOW_ALL_USERS=true.zAdd allowed users now?Tra   NzDiscord token savedu~   Make sure Message Content Intent is on (Bot page â†’ Privileged Gateway Intents), or Discord will refuse the bot's connection.z9Home channel ID (leave empty to set later with /set-home)ÚDISCORD_HOME_CHANNELzHome channel cleared.)uG   1. Open https://discord.com/developers/applications â†’ New Applicationu7   2. Open the Bot page â†’ Reset Token â†’ copy the tokenzFHermes checks the token, the intents and the invite link for you next.zNGuide: https://hermes-agent.nousresearch.com/docs/user-guide/messaging/discord)u:   ðŸ“¬ Home Channel: where Hermes delivers cron job results,z.   cross-platform messages, and notifications.zH   Easiest: type /set-home in a Discord channel once the bot is running.uX      Or paste a channel ID (Developer Mode â†’ right-click a channel â†’ Copy Channel ID).)rx   r¶   r‘   r€   rz   r¬   r‡   rœ   r·   r�   Úhermes_cli.setup_platformsr¸   r³   r°   r�   r“   r˜   r—   rX   )r¶   r‘   r€   rz   r¬   rœ   r·   r�   r¸   ÚsavedÚlinerL   r‹   Úhome_channels                 r   Úinteractive_setupr¿   ô   sH  € ÷ eÕdßQÑQÝ?á�ÔÙ˜IÐ'=Ð?R×SÒSÙÐ4×5Ò5ÙðDôñ
 Ð5°t×<Ò<Ü*©=Ð9LÓ+M×+SÐ+SÐQSÓT�Ü! %°Ô?Ùóˆñ 	�4Öñô )¨Ó0�L€EßÙÙÐ&¨Ô.ÙÐ'Ô(Ø‚}Ùð Bõ 	Cô /¨u¸VÓDˆÜ�eÔÜ�e ]Ô3Ü	„Góˆñ 	�4Öññ ÐUÓV×\Ñ\Ó^€LßÙÐ-¨|Ö<Ù	Ð0×	1Ò	1ÙÐ*Ö+ñ 
2r"   N)Ú__doc__Ú
__future__r   Údataclassesr   Útypingr   Úurllib.parser   ÚINVITE_PERMISSION_BITSÚsumÚvaluesr(   rC   r   rJ   r[   ro   rs   r}   r�   r“   r˜   r°   r³   r¿   r
   r"   r   Ú<module>rÈ      sÓ   ðñõ #å !Ý Ý "ð
 �QØ�RØ�RØ�2Ø�BØ˜BØˆrØˆRØ˜RØ ðÐ ñ ÑMÐ-C×-JÑ-JÔ-LÓMÓMÐ ØÐ ñ �$Ô÷Pð Pó ðPõ,	gõõ*õõõ õ@õ.Wõ 
õF÷2,r"   