agent-messenger 2.10.2 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.env.template +4 -1
- package/README.md +77 -27
- package/bun.lock +26 -0
- package/dist/package.json +14 -1
- package/dist/src/platforms/channeltalk/commands/auth.d.ts +2 -1
- package/dist/src/platforms/channeltalk/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/commands/auth.js +5 -3
- package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts +2 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.js +22 -6
- package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/cli.js +11 -1
- package/dist/src/platforms/channeltalkbot/cli.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/auth.js +1 -5
- package/dist/src/platforms/channeltalkbot/commands/auth.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/bot.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/bot.js +1 -6
- package/dist/src/platforms/channeltalkbot/commands/bot.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/chat.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/chat.js +1 -6
- package/dist/src/platforms/channeltalkbot/commands/chat.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/group.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/group.js +1 -6
- package/dist/src/platforms/channeltalkbot/commands/group.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/manager.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/manager.js +1 -6
- package/dist/src/platforms/channeltalkbot/commands/manager.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/message.js +1 -6
- package/dist/src/platforms/channeltalkbot/commands/message.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/whoami.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/commands/whoami.js +1 -6
- package/dist/src/platforms/channeltalkbot/commands/whoami.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/credential-manager.d.ts +5 -0
- package/dist/src/platforms/channeltalkbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/credential-manager.js +34 -4
- package/dist/src/platforms/channeltalkbot/credential-manager.js.map +1 -1
- package/dist/src/platforms/discord/commands/auth.d.ts +1 -0
- package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/auth.js +3 -1
- package/dist/src/platforms/discord/commands/auth.js.map +1 -1
- package/dist/src/platforms/discord/listener.d.ts +2 -0
- package/dist/src/platforms/discord/listener.d.ts.map +1 -1
- package/dist/src/platforms/discord/listener.js +51 -21
- package/dist/src/platforms/discord/listener.js.map +1 -1
- package/dist/src/platforms/discord/token-extractor.d.ts +2 -1
- package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/discord/token-extractor.js +21 -6
- package/dist/src/platforms/discord/token-extractor.js.map +1 -1
- package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/cli.js +12 -1
- package/dist/src/platforms/discordbot/cli.js.map +1 -1
- package/dist/src/platforms/discordbot/client.d.ts +3 -0
- package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/client.js +3 -0
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- package/dist/src/platforms/discordbot/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/commands/auth.js +1 -5
- package/dist/src/platforms/discordbot/commands/auth.js.map +1 -1
- package/dist/src/platforms/discordbot/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/commands/message.js +1 -6
- package/dist/src/platforms/discordbot/commands/message.js.map +1 -1
- package/dist/src/platforms/discordbot/commands/server.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/commands/server.js +1 -4
- package/dist/src/platforms/discordbot/commands/server.js.map +1 -1
- package/dist/src/platforms/discordbot/commands/whoami.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/commands/whoami.js +1 -6
- package/dist/src/platforms/discordbot/commands/whoami.js.map +1 -1
- package/dist/src/platforms/discordbot/index.d.ts +3 -1
- package/dist/src/platforms/discordbot/index.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/index.js +2 -1
- package/dist/src/platforms/discordbot/index.js.map +1 -1
- package/dist/src/platforms/discordbot/listener.d.ts +43 -0
- package/dist/src/platforms/discordbot/listener.d.ts.map +1 -0
- package/dist/src/platforms/discordbot/listener.js +292 -0
- package/dist/src/platforms/discordbot/listener.js.map +1 -0
- package/dist/src/platforms/discordbot/types.d.ts +161 -0
- package/dist/src/platforms/discordbot/types.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/types.js +34 -0
- package/dist/src/platforms/discordbot/types.js.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.js +3 -1
- package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
- package/dist/src/platforms/instagram/token-extractor.d.ts +2 -1
- package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/instagram/token-extractor.js +11 -2
- package/dist/src/platforms/instagram/token-extractor.js.map +1 -1
- package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/auth.js +4 -2
- package/dist/src/platforms/slack/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/token-extractor.d.ts +4 -1
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/slack/token-extractor.js +64 -15
- package/dist/src/platforms/slack/token-extractor.js.map +1 -1
- package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/cli.js +15 -3
- package/dist/src/platforms/slackbot/cli.js.map +1 -1
- package/dist/src/platforms/slackbot/client.d.ts +22 -1
- package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/client.js +104 -1
- package/dist/src/platforms/slackbot/client.js.map +1 -1
- package/dist/src/platforms/slackbot/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/commands/auth.js +1 -5
- package/dist/src/platforms/slackbot/commands/auth.js.map +1 -1
- package/dist/src/platforms/slackbot/commands/file.d.ts +3 -0
- package/dist/src/platforms/slackbot/commands/file.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/file.js +164 -0
- package/dist/src/platforms/slackbot/commands/file.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/index.d.ts +1 -0
- package/dist/src/platforms/slackbot/commands/index.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/commands/index.js +1 -0
- package/dist/src/platforms/slackbot/commands/index.js.map +1 -1
- package/dist/src/platforms/slackbot/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/commands/message.js +19 -0
- package/dist/src/platforms/slackbot/commands/message.js.map +1 -1
- package/dist/src/platforms/slackbot/commands/whoami.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/commands/whoami.js +1 -6
- package/dist/src/platforms/slackbot/commands/whoami.js.map +1 -1
- package/dist/src/platforms/slackbot/credential-manager.d.ts +1 -0
- package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/credential-manager.js +30 -2
- package/dist/src/platforms/slackbot/credential-manager.js.map +1 -1
- package/dist/src/platforms/slackbot/index.d.ts +4 -1
- package/dist/src/platforms/slackbot/index.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/index.js +1 -0
- package/dist/src/platforms/slackbot/index.js.map +1 -1
- package/dist/src/platforms/slackbot/listener.d.ts +44 -0
- package/dist/src/platforms/slackbot/listener.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/listener.js +313 -0
- package/dist/src/platforms/slackbot/listener.js.map +1 -0
- package/dist/src/platforms/slackbot/types.d.ts +196 -1
- package/dist/src/platforms/slackbot/types.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/types.js +4 -1
- package/dist/src/platforms/slackbot/types.js.map +1 -1
- package/dist/src/platforms/teams/commands/auth.d.ts +1 -0
- package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/auth.js +37 -6
- package/dist/src/platforms/teams/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.js +31 -9
- package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts +4 -1
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js +71 -29
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/platforms/webex/commands/auth.d.ts +1 -0
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/auth.js +3 -1
- package/dist/src/platforms/webex/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts +3 -1
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/webex/token-extractor.js +16 -2
- package/dist/src/platforms/webex/token-extractor.js.map +1 -1
- package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/cli.js +11 -1
- package/dist/src/platforms/wechatbot/cli.js.map +1 -1
- package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/commands/auth.js +1 -5
- package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -1
- package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/commands/message.js +1 -6
- package/dist/src/platforms/wechatbot/commands/message.js.map +1 -1
- package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/commands/template.js +1 -6
- package/dist/src/platforms/wechatbot/commands/template.js.map +1 -1
- package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/commands/user.js +1 -6
- package/dist/src/platforms/wechatbot/commands/user.js.map +1 -1
- package/dist/src/platforms/wechatbot/commands/whoami.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/commands/whoami.js +1 -6
- package/dist/src/platforms/wechatbot/commands/whoami.js.map +1 -1
- package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/cli.js +11 -1
- package/dist/src/platforms/whatsappbot/cli.js.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/auth.js +1 -5
- package/dist/src/platforms/whatsappbot/commands/auth.js.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/message.js +1 -6
- package/dist/src/platforms/whatsappbot/commands/message.js.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/template.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/template.js +1 -6
- package/dist/src/platforms/whatsappbot/commands/template.js.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/whoami.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/commands/whoami.js +1 -6
- package/dist/src/platforms/whatsappbot/commands/whoami.js.map +1 -1
- package/dist/src/shared/chromium/browsers.d.ts +8 -0
- package/dist/src/shared/chromium/browsers.d.ts.map +1 -1
- package/dist/src/shared/chromium/browsers.js +58 -3
- package/dist/src/shared/chromium/browsers.js.map +1 -1
- package/dist/src/shared/chromium/cli-options.d.ts +5 -0
- package/dist/src/shared/chromium/cli-options.d.ts.map +1 -0
- package/dist/src/shared/chromium/cli-options.js +8 -0
- package/dist/src/shared/chromium/cli-options.js.map +1 -0
- package/dist/src/shared/chromium/index.d.ts +3 -1
- package/dist/src/shared/chromium/index.d.ts.map +1 -1
- package/dist/src/shared/chromium/index.js +2 -1
- package/dist/src/shared/chromium/index.js.map +1 -1
- package/dist/src/shared/utils/cli-output.d.ts +7 -0
- package/dist/src/shared/utils/cli-output.d.ts.map +1 -0
- package/dist/src/shared/utils/cli-output.js +7 -0
- package/dist/src/shared/utils/cli-output.js.map +1 -0
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +73 -20
- package/dist/src/tui/app.js.map +1 -1
- package/docs/content/docs/cli/channeltalk.mdx +4 -0
- package/docs/content/docs/cli/discord.mdx +5 -0
- package/docs/content/docs/cli/instagram.mdx +3 -0
- package/docs/content/docs/cli/slack.mdx +5 -0
- package/docs/content/docs/cli/slackbot.mdx +60 -22
- package/docs/content/docs/cli/teams.mdx +5 -0
- package/docs/content/docs/cli/webex.mdx +3 -0
- package/docs/content/docs/sdk/channeltalkbot.mdx +38 -1
- package/docs/content/docs/sdk/discordbot.mdx +501 -0
- package/docs/content/docs/sdk/meta.json +2 -0
- package/docs/content/docs/sdk/slackbot.mdx +576 -0
- package/e2e/README.md +1 -1
- package/e2e/config.ts +9 -4
- package/examples/discordbot-listen.ts +65 -0
- package/examples/slackbot-listen.ts +65 -0
- package/package.json +14 -1
- package/skills/agent-channeltalk/SKILL.md +5 -1
- package/skills/agent-channeltalk/references/authentication.md +5 -1
- package/skills/agent-channeltalkbot/SKILL.md +17 -3
- package/skills/agent-channeltalkbot/references/authentication.md +7 -5
- package/skills/agent-discord/SKILL.md +5 -1
- package/skills/agent-discord/references/authentication.md +7 -1
- package/skills/agent-discordbot/SKILL.md +13 -2
- package/skills/agent-discordbot/references/common-patterns.md +1 -1
- package/skills/agent-instagram/SKILL.md +7 -1
- package/skills/agent-instagram/references/authentication.md +6 -0
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +5 -1
- package/skills/agent-slack/references/authentication.md +7 -1
- package/skills/agent-slackbot/SKILL.md +56 -4
- package/skills/agent-slackbot/references/authentication.md +4 -0
- package/skills/agent-teams/SKILL.md +5 -1
- package/skills/agent-teams/references/authentication.md +7 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +7 -1
- package/skills/agent-webex/references/authentication.md +6 -0
- package/skills/agent-wechatbot/SKILL.md +16 -1
- package/skills/agent-wechatbot/references/authentication.md +219 -0
- package/skills/agent-wechatbot/references/common-patterns.md +358 -0
- package/skills/agent-wechatbot/templates/account-summary.sh +122 -0
- package/skills/agent-wechatbot/templates/post-message.sh +122 -0
- package/skills/agent-wechatbot/templates/send-template.sh +152 -0
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +30 -1
- package/src/platforms/channeltalk/commands/auth.test.ts +15 -3
- package/src/platforms/channeltalk/commands/auth.ts +15 -5
- package/src/platforms/channeltalk/token-extractor.ts +24 -5
- package/src/platforms/channeltalkbot/cli.ts +9 -0
- package/src/platforms/channeltalkbot/commands/auth.ts +1 -5
- package/src/platforms/channeltalkbot/commands/bot.ts +1 -6
- package/src/platforms/channeltalkbot/commands/chat.ts +1 -6
- package/src/platforms/channeltalkbot/commands/group.ts +1 -6
- package/src/platforms/channeltalkbot/commands/manager.ts +1 -6
- package/src/platforms/channeltalkbot/commands/message.ts +1 -6
- package/src/platforms/channeltalkbot/commands/whoami.test.ts +2 -0
- package/src/platforms/channeltalkbot/commands/whoami.ts +1 -6
- package/src/platforms/channeltalkbot/credential-manager.test.ts +96 -2
- package/src/platforms/channeltalkbot/credential-manager.ts +37 -4
- package/src/platforms/discord/commands/auth.ts +13 -2
- package/src/platforms/discord/listener.test.ts +59 -1
- package/src/platforms/discord/listener.ts +43 -19
- package/src/platforms/discord/token-extractor.ts +30 -6
- package/src/platforms/discordbot/cli.ts +10 -0
- package/src/platforms/discordbot/client.ts +4 -0
- package/src/platforms/discordbot/commands/auth.ts +1 -5
- package/src/platforms/discordbot/commands/message.ts +1 -6
- package/src/platforms/discordbot/commands/server.ts +1 -5
- package/src/platforms/discordbot/commands/whoami.ts +1 -6
- package/src/platforms/discordbot/index.test.ts +82 -0
- package/src/platforms/discordbot/index.ts +27 -9
- package/src/platforms/discordbot/listener.test.ts +1002 -0
- package/src/platforms/discordbot/listener.ts +321 -0
- package/src/platforms/discordbot/types.ts +163 -0
- package/src/platforms/instagram/commands/auth.ts +9 -1
- package/src/platforms/instagram/token-extractor.ts +13 -1
- package/src/platforms/slack/commands/auth.ts +11 -2
- package/src/platforms/slack/token-extractor.test.ts +96 -0
- package/src/platforms/slack/token-extractor.ts +76 -13
- package/src/platforms/slackbot/cli.ts +13 -1
- package/src/platforms/slackbot/client.test.ts +274 -0
- package/src/platforms/slackbot/client.ts +130 -2
- package/src/platforms/slackbot/commands/auth.ts +1 -5
- package/src/platforms/slackbot/commands/file.test.ts +201 -0
- package/src/platforms/slackbot/commands/file.ts +212 -0
- package/src/platforms/slackbot/commands/index.ts +1 -0
- package/src/platforms/slackbot/commands/message.ts +22 -0
- package/src/platforms/slackbot/commands/whoami.ts +1 -6
- package/src/platforms/slackbot/credential-manager.test.ts +62 -2
- package/src/platforms/slackbot/credential-manager.ts +32 -2
- package/src/platforms/slackbot/index.test.ts +59 -0
- package/src/platforms/slackbot/index.ts +31 -7
- package/src/platforms/slackbot/listener.test.ts +1012 -0
- package/src/platforms/slackbot/listener.ts +362 -0
- package/src/platforms/slackbot/types.ts +224 -1
- package/src/platforms/teams/commands/auth.test.ts +1 -1
- package/src/platforms/teams/commands/auth.ts +66 -7
- package/src/platforms/teams/ensure-auth.test.ts +56 -5
- package/src/platforms/teams/ensure-auth.ts +39 -11
- package/src/platforms/teams/token-extractor.test.ts +146 -24
- package/src/platforms/teams/token-extractor.ts +87 -29
- package/src/platforms/webex/commands/auth.ts +13 -2
- package/src/platforms/webex/token-extractor.ts +25 -3
- package/src/platforms/wechatbot/cli.ts +9 -0
- package/src/platforms/wechatbot/commands/auth.ts +1 -5
- package/src/platforms/wechatbot/commands/message.ts +1 -6
- package/src/platforms/wechatbot/commands/template.ts +1 -6
- package/src/platforms/wechatbot/commands/user.ts +1 -6
- package/src/platforms/wechatbot/commands/whoami.ts +1 -6
- package/src/platforms/whatsappbot/cli.ts +9 -0
- package/src/platforms/whatsappbot/commands/auth.ts +1 -5
- package/src/platforms/whatsappbot/commands/message.ts +1 -6
- package/src/platforms/whatsappbot/commands/template.ts +1 -6
- package/src/platforms/whatsappbot/commands/whoami.ts +1 -6
- package/src/shared/chromium/browsers.test.ts +80 -0
- package/src/shared/chromium/browsers.ts +72 -3
- package/src/shared/chromium/cli-options.test.ts +22 -0
- package/src/shared/chromium/cli-options.ts +12 -0
- package/src/shared/chromium/index.ts +3 -0
- package/src/shared/utils/cli-output.test.ts +57 -0
- package/src/shared/utils/cli-output.ts +8 -0
- package/src/tui/app.ts +129 -20
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Slack Bot
|
|
3
|
+
description: TypeScript SDK reference for Slack Bot — client, real-time Socket Mode listener, credential management, and types.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install agent-messenger
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { SlackBotClient, SlackBotCredentialManager, SlackBotError, SlackBotListener } from 'agent-messenger/slackbot'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Slack vs. Slack Bot
|
|
17
|
+
|
|
18
|
+
`agent-messenger/slack` and `agent-messenger/slackbot` are two distinct entry points for two distinct authentication models:
|
|
19
|
+
|
|
20
|
+
| Module | Token type | Source | Use when |
|
|
21
|
+
| -------------------------- | -------------- | ------------------------------- | ------------------------------------- |
|
|
22
|
+
| `agent-messenger/slack` | User (`xoxc-`) | Auto-extracted from desktop app | Acting as yourself, all features |
|
|
23
|
+
| `agent-messenger/slackbot` | Bot (`xoxb-`) | Slack App config (manual) | Server-side, CI/CD, multi-tenant bots |
|
|
24
|
+
|
|
25
|
+
This page documents `agent-messenger/slackbot`. For the user-token client, see the [Slack SDK reference](./slack).
|
|
26
|
+
|
|
27
|
+
## SlackBotClient
|
|
28
|
+
|
|
29
|
+
The main client for interacting with Slack's Web API using a bot token. Built on `@slack/web-api`. All methods include automatic retry with exponential backoff on `slack_webapi_rate_limited_error` (up to 3 retries, honoring Slack's `retryAfter` hint).
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { SlackBotClient } from 'agent-messenger/slackbot'
|
|
33
|
+
|
|
34
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-...' })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or use stored credentials — credentials are read from `~/.config/agent-messenger/slackbot-credentials.json` (managed by `agent-slackbot auth set`):
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const client = await new SlackBotClient().login()
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Authentication
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Verify bot credentials and resolve the bot's identity in the workspace
|
|
47
|
+
const me = await client.testAuth()
|
|
48
|
+
// → { user_id, team_id, bot_id?, user?, team? }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Messages
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Send a message to a channel
|
|
55
|
+
const msg = await client.postMessage('C0ACZKTDDC0', 'Hello world')
|
|
56
|
+
|
|
57
|
+
// Reply in a thread
|
|
58
|
+
const reply = await client.postMessage('C0ACZKTDDC0', 'Reply', { thread_ts: '1234567890.123456' })
|
|
59
|
+
// → SlackMessage { ts, text, type, user?, thread_ts? }
|
|
60
|
+
|
|
61
|
+
// Read a channel's recent messages (default limit: 20, supports cursor pagination)
|
|
62
|
+
const history = await client.getConversationHistory('C0ACZKTDDC0')
|
|
63
|
+
const limited = await client.getConversationHistory('C0ACZKTDDC0', { limit: 50 })
|
|
64
|
+
const next = await client.getConversationHistory('C0ACZKTDDC0', { cursor: 'dXNlcjpVMD...' })
|
|
65
|
+
// → SlackMessage[]
|
|
66
|
+
|
|
67
|
+
// Get a single message by timestamp
|
|
68
|
+
const message = await client.getMessage('C0ACZKTDDC0', '1234567890.123456')
|
|
69
|
+
// → SlackMessage | null
|
|
70
|
+
|
|
71
|
+
// Get all replies in a thread (parent message included as the first entry)
|
|
72
|
+
const replies = await client.getThreadReplies('C0ACZKTDDC0', '1234567890.123456')
|
|
73
|
+
const paginated = await client.getThreadReplies('C0ACZKTDDC0', '1234567890.123456', { limit: 100 })
|
|
74
|
+
// → SlackMessage[]
|
|
75
|
+
|
|
76
|
+
// Update a message (bot can only edit its own messages)
|
|
77
|
+
const edited = await client.updateMessage('C0ACZKTDDC0', '1234567890.123456', 'Updated text')
|
|
78
|
+
// → SlackMessage
|
|
79
|
+
|
|
80
|
+
// Delete a message (bot can only delete its own messages)
|
|
81
|
+
await client.deleteMessage('C0ACZKTDDC0', '1234567890.123456')
|
|
82
|
+
|
|
83
|
+
// Show a typing/status indicator in an AI Assistant thread (e.g. "Bot is typing...")
|
|
84
|
+
await client.setAssistantStatus('C0ACZKTDDC0', '1234567890.123456', 'is typing...')
|
|
85
|
+
await client.setAssistantStatus('C0ACZKTDDC0', '1234567890.123456', 'is analyzing your code...')
|
|
86
|
+
|
|
87
|
+
// Clear the status manually (status also auto-clears when the bot posts a message,
|
|
88
|
+
// or after a 2-minute timeout). Only works inside AI Assistant threads — requires `chat:write`.
|
|
89
|
+
await client.setAssistantStatus('C0ACZKTDDC0', '1234567890.123456', '')
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Reactions
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Add a reaction (emoji can be wrapped in colons or bare)
|
|
96
|
+
await client.addReaction('C0ACZKTDDC0', '1234567890.123456', 'thumbsup')
|
|
97
|
+
await client.addReaction('C0ACZKTDDC0', '1234567890.123456', ':white_check_mark:')
|
|
98
|
+
|
|
99
|
+
// Remove a reaction the bot added
|
|
100
|
+
await client.removeReaction('C0ACZKTDDC0', '1234567890.123456', 'thumbsup')
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Channels
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// List channels the bot can see (auto-paginates unless a `limit` is given)
|
|
107
|
+
const channels = await client.listChannels()
|
|
108
|
+
const firstPage = await client.listChannels({ limit: 50 })
|
|
109
|
+
const next = await client.listChannels({ limit: 50, cursor: 'dGVhbTpD...' })
|
|
110
|
+
// → SlackChannel[]
|
|
111
|
+
|
|
112
|
+
// Get a specific channel
|
|
113
|
+
const channel = await client.getChannelInfo('C0ACZKTDDC0')
|
|
114
|
+
// → SlackChannel { id, name, is_private, is_archived, created, creator, topic?, purpose? }
|
|
115
|
+
|
|
116
|
+
// Resolve a channel by ID or name — returns the channel ID
|
|
117
|
+
const id = await client.resolveChannel('C0ACZKTDDC0')
|
|
118
|
+
const sameId = await client.resolveChannel('#general')
|
|
119
|
+
const fromName = await client.resolveChannel('general')
|
|
120
|
+
|
|
121
|
+
// Join a public channel (required before posting in some workspaces)
|
|
122
|
+
await client.joinChannel('C0ACZKTDDC0')
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Users
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// List workspace members (auto-paginates unless a `limit` is given)
|
|
129
|
+
const users = await client.listUsers()
|
|
130
|
+
const firstPage = await client.listUsers({ limit: 100 })
|
|
131
|
+
// → SlackUser[]
|
|
132
|
+
|
|
133
|
+
// Get a single user
|
|
134
|
+
const user = await client.getUserInfo('U012ABC3DE')
|
|
135
|
+
// → SlackUser { id, name, real_name, is_admin, is_owner, is_bot, is_app_user, profile? }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Real-Time Events
|
|
139
|
+
|
|
140
|
+
`SlackBotListener` connects to Slack's [Socket Mode](https://api.slack.com/apis/socket-mode) WebSocket for instant event streaming — Events API events, slash commands, and interactive components — without running an HTTP webhook server.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
|
|
144
|
+
|
|
145
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-...' })
|
|
146
|
+
const listener = new SlackBotListener(client, {
|
|
147
|
+
appToken: process.env.SLACK_APP_TOKEN!, // xapp-...
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
listener.on('connected', (info) => {
|
|
151
|
+
console.log(`Connected to Slack (app ${info.app_id})`)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
listener.on('message', ({ ack, event }) => {
|
|
155
|
+
ack()
|
|
156
|
+
if (event.subtype || event.bot_id) return
|
|
157
|
+
console.log(`#${event.channel} <${event.user}>: ${event.text}`)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
listener.on('slash_commands', ({ ack, body }) => {
|
|
161
|
+
ack({ text: `Got \`${body.command} ${body.text}\`` })
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
listener.on('error', (err) => console.error(err))
|
|
165
|
+
|
|
166
|
+
await listener.start() // opens the Socket Mode WebSocket
|
|
167
|
+
// listener.stop() // clean shutdown
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Two tokens
|
|
171
|
+
|
|
172
|
+
Slack Socket Mode is the only place in this SDK where you need **two separate tokens** for one bot:
|
|
173
|
+
|
|
174
|
+
| Token | Purpose | Where to get it |
|
|
175
|
+
| ---------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
176
|
+
| Bot (`xoxb-...`) | Web API calls — `chat.postMessage`, `conversations.list`, etc. | Slack App config → **OAuth & Permissions** → Bot User OAuth Token |
|
|
177
|
+
| App (`xapp-...`) | Socket Mode WebSocket — opens `apps.connections.open`, receives events | Slack App config → **Basic Information** → App-Level Tokens, with the `connections:write` scope |
|
|
178
|
+
|
|
179
|
+
The bot token is passed to `SlackBotClient` (or stored via `agent-slackbot auth set`); the app token is passed to `SlackBotListener` via the `appToken` option. The two tokens are not interchangeable — calling `apps.connections.open` with a bot token returns `not_allowed_token_type`.
|
|
180
|
+
|
|
181
|
+
You also need to enable **Socket Mode** in the Slack App config (Settings → Socket Mode → On) and subscribe the app to the [Events API events](https://api.slack.com/apis/connections/events-api) you want to receive.
|
|
182
|
+
|
|
183
|
+
### Acknowledging envelopes
|
|
184
|
+
|
|
185
|
+
Every Socket Mode envelope except `hello` and `disconnect` carries an `envelope_id` that the client **must** acknowledge — otherwise Slack will retry delivery. The handler arguments include an `ack` callback for that purpose:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
listener.on('message', ({ ack, event }) => {
|
|
189
|
+
// Always ack first so Slack stops retrying. Do this even if you decide to
|
|
190
|
+
// ignore the event — failure to ack causes duplicate deliveries.
|
|
191
|
+
ack()
|
|
192
|
+
// ...your work
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// Slash commands and interactive components support a response payload that
|
|
196
|
+
// becomes the immediate user-visible reply (when accepts_response_payload is
|
|
197
|
+
// true on the envelope).
|
|
198
|
+
listener.on('slash_commands', ({ ack, body }) => {
|
|
199
|
+
ack({ text: 'Working on it...' })
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
listener.on('interactive', ({ ack, body }) => {
|
|
203
|
+
if (body.type === 'view_submission') {
|
|
204
|
+
ack({ response_action: 'clear' })
|
|
205
|
+
} else {
|
|
206
|
+
ack()
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The `ack` callback is **idempotent** (only the first call hits the wire) and a **no-op after the listener stops or reconnects** — so it is safe to hold across async work. Calling `ack()` with no argument sends `{ envelope_id }`; calling with a payload sends `{ envelope_id, payload }`.
|
|
212
|
+
|
|
213
|
+
### Available Events
|
|
214
|
+
|
|
215
|
+
| Event | Description |
|
|
216
|
+
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
217
|
+
| `message` | New message in a channel or DM |
|
|
218
|
+
| `app_mention` | Message that mentions the bot |
|
|
219
|
+
| `reaction_added` / `reaction_removed` | Reaction added or removed |
|
|
220
|
+
| `member_joined_channel` / `member_left_channel` | Member joins or leaves a channel |
|
|
221
|
+
| `channel_created` / `channel_deleted` / `channel_rename` / `channel_archive` / `channel_unarchive` | Channel lifecycle |
|
|
222
|
+
| `slash_commands` | Slash command invoked (envelope-level event, not Events API) |
|
|
223
|
+
| `interactive` | Block action, view submission, shortcut, or other interactive component |
|
|
224
|
+
| `slack_event` | Catch-all — fires for every Events API dispatch and every unknown envelope type |
|
|
225
|
+
| `connected` / `disconnected` | Socket Mode connection lifecycle |
|
|
226
|
+
| `error` | Connection or protocol error |
|
|
227
|
+
|
|
228
|
+
Any inner Events API event type that Slack delivers is forwarded by name (e.g., `team_join`, `user_change`, `pin_added`) and is also delivered to `slack_event`.
|
|
229
|
+
|
|
230
|
+
### Lifecycle Features
|
|
231
|
+
|
|
232
|
+
- **Auto-reconnect** with exponential backoff (1s → 30s, capped). On every reconnect the listener calls `apps.connections.open` to fetch a fresh single-use Socket Mode URL.
|
|
233
|
+
- **Server-requested reconnect** — when Slack sends a `disconnect` envelope with reason `warning` or `refresh_requested`, the listener reconnects immediately and resets backoff.
|
|
234
|
+
- **Terminal disconnect detection** — when the disconnect reason is `link_disabled`, the listener emits `error` and stops; reconnecting against a disabled app would loop forever.
|
|
235
|
+
- **`hello` timeout** — if the WebSocket opens but Slack does not send `hello` within 10 seconds, the listener force-closes the socket and reconnects.
|
|
236
|
+
- **WebSocket-level keepalive** — RFC 6455 ping/pong every 30 seconds; if no pong arrives within 10 seconds the connection is closed and re-established.
|
|
237
|
+
- **Rate-limit awareness** — if `apps.connections.open` returns a rate-limit error, the listener uses the server's `retryAfter` value as a floor on the next reconnect delay.
|
|
238
|
+
- **Stale-socket safety** — every async callback is generation-guarded so a stop+start cycle cannot leave the new connection in a broken state.
|
|
239
|
+
- **Fatal-error short-circuit** — `apps.connections.open` errors that indicate a permanently broken state (`not_authed`, `invalid_auth`, `account_inactive`, `user_removed_from_team`, `team_disabled`, `not_allowed_token_type`) emit `error` and stop without retrying.
|
|
240
|
+
|
|
241
|
+
### Debugging reconnects
|
|
242
|
+
|
|
243
|
+
To force frequent reconnects during development (Slack closes the socket every ~360s instead of the usual ~12 hours), enable `debugReconnects`:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const listener = new SlackBotListener(client, {
|
|
247
|
+
appToken: process.env.SLACK_APP_TOKEN!,
|
|
248
|
+
debugReconnects: true,
|
|
249
|
+
})
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
This appends `&debug_reconnects=true` to the WebSocket URL returned by `apps.connections.open`.
|
|
253
|
+
|
|
254
|
+
## SlackBotCredentialManager
|
|
255
|
+
|
|
256
|
+
Manages bot credentials stored at `~/.config/agent-messenger/slackbot-credentials.json` with `0o600` permissions. Supports multiple bots per workspace and multiple workspaces in the same config (e.g., a deploy bot and an alerts bot in two different workspaces). The environment variables `E2E_SLACKBOT_TOKEN`, `E2E_SLACKBOT_WORKSPACE_ID`, and `E2E_SLACKBOT_WORKSPACE_NAME` take precedence over file-based credentials.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { SlackBotCredentialManager } from 'agent-messenger/slackbot'
|
|
260
|
+
|
|
261
|
+
const manager = new SlackBotCredentialManager()
|
|
262
|
+
// Custom path: new SlackBotCredentialManager('/custom/config/dir')
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// Load full config from disk (returns defaults if file doesn't exist)
|
|
267
|
+
const config = await manager.load()
|
|
268
|
+
// → SlackBotConfig { current, workspaces }
|
|
269
|
+
|
|
270
|
+
// Save full config to disk (also enforces 0o600 permissions)
|
|
271
|
+
await manager.save(config)
|
|
272
|
+
|
|
273
|
+
// Get the credentials for the current bot, a specific bot ID, or
|
|
274
|
+
// "workspace_id/bot_id" to disambiguate a bot ID that exists in multiple
|
|
275
|
+
// workspaces.
|
|
276
|
+
const creds = await manager.getCredentials()
|
|
277
|
+
const specific = await manager.getCredentials('deploy')
|
|
278
|
+
const disambiguated = await manager.getCredentials('T0ABCDE/deploy')
|
|
279
|
+
// → SlackBotCredentials | null
|
|
280
|
+
|
|
281
|
+
// Store credentials for a bot (also marks it as the current bot)
|
|
282
|
+
await manager.setCredentials({
|
|
283
|
+
token: 'xoxb-...',
|
|
284
|
+
workspace_id: 'T0ABCDE',
|
|
285
|
+
workspace_name: 'Acme Corp',
|
|
286
|
+
bot_id: 'deploy',
|
|
287
|
+
bot_name: 'Deploy Bot',
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
// Switch the active bot — accepts the same lookup formats as getCredentials
|
|
291
|
+
await manager.setCurrent('alerts')
|
|
292
|
+
|
|
293
|
+
// List all stored bots (across workspaces) with current marker
|
|
294
|
+
const bots = await manager.listAll()
|
|
295
|
+
// → Array<SlackBotCredentials & { is_current: boolean }>
|
|
296
|
+
|
|
297
|
+
// Remove a stored bot (drops the workspace too if it was the last bot in it)
|
|
298
|
+
await manager.removeBot('alerts')
|
|
299
|
+
|
|
300
|
+
// Clear all stored credentials
|
|
301
|
+
await manager.clearCredentials()
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Types
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import type {
|
|
308
|
+
SlackBotConfig,
|
|
309
|
+
SlackBotCredentials,
|
|
310
|
+
SlackBotListenerEventMap,
|
|
311
|
+
SlackBotListenerOptions,
|
|
312
|
+
SlackChannel,
|
|
313
|
+
SlackFile,
|
|
314
|
+
SlackMessage,
|
|
315
|
+
SlackReaction,
|
|
316
|
+
SlackSocketModeAck,
|
|
317
|
+
SlackSocketModeAppMentionEvent,
|
|
318
|
+
SlackSocketModeChannelEvent,
|
|
319
|
+
SlackSocketModeDisconnectEnvelope,
|
|
320
|
+
SlackSocketModeDisconnectReason,
|
|
321
|
+
SlackSocketModeEnvelope,
|
|
322
|
+
SlackSocketModeEvent,
|
|
323
|
+
SlackSocketModeEventsApiArgs,
|
|
324
|
+
SlackSocketModeEventsApiEnvelope,
|
|
325
|
+
SlackSocketModeGenericEnvelope,
|
|
326
|
+
SlackSocketModeGenericEvent,
|
|
327
|
+
SlackSocketModeHelloEnvelope,
|
|
328
|
+
SlackSocketModeInteractiveArgs,
|
|
329
|
+
SlackSocketModeInteractiveEnvelope,
|
|
330
|
+
SlackSocketModeMemberChannelEvent,
|
|
331
|
+
SlackSocketModeMessageEvent,
|
|
332
|
+
SlackSocketModeReactionEvent,
|
|
333
|
+
SlackSocketModeSlashCommandArgs,
|
|
334
|
+
SlackSocketModeSlashCommandEnvelope,
|
|
335
|
+
SlackUser,
|
|
336
|
+
} from 'agent-messenger/slackbot'
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Zod Schemas
|
|
340
|
+
|
|
341
|
+
Runtime-validated schemas for parsing API responses and config files:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import {
|
|
345
|
+
SlackBotConfigSchema,
|
|
346
|
+
SlackBotCredentialsSchema,
|
|
347
|
+
SlackChannelSchema,
|
|
348
|
+
SlackFileSchema,
|
|
349
|
+
SlackMessageSchema,
|
|
350
|
+
SlackReactionSchema,
|
|
351
|
+
SlackUserSchema,
|
|
352
|
+
} from 'agent-messenger/slackbot'
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Errors
|
|
356
|
+
|
|
357
|
+
`SlackBotError` carries a stable `code` (e.g., `not_authed`, `missing_app_token`, `slack_webapi_rate_limited_error`, `disconnect_terminal`) and an optional `retryAfter` (seconds) when Slack rate-limits a request:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import { SlackBotError } from 'agent-messenger/slackbot'
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
await client.postMessage('C0ACZKTDDC0', 'Hello')
|
|
364
|
+
} catch (err) {
|
|
365
|
+
if (err instanceof SlackBotError && err.code === 'slack_webapi_rate_limited_error') {
|
|
366
|
+
console.log(`Rate limited; retry after ${err.retryAfter}s`)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Examples
|
|
372
|
+
|
|
373
|
+
### Auto-Reply Bot
|
|
374
|
+
|
|
375
|
+
Listen for `app_mention` events and reply in the same thread.
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
|
|
379
|
+
|
|
380
|
+
const client = await new SlackBotClient().login()
|
|
381
|
+
const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
|
|
382
|
+
|
|
383
|
+
listener.on('app_mention', async ({ ack, event }) => {
|
|
384
|
+
ack()
|
|
385
|
+
await client.postMessage(event.channel, `👋 Hi <@${event.user}>! How can I help?`, {
|
|
386
|
+
thread_ts: event.thread_ts ?? event.ts,
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
await listener.start()
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Reaction-Driven Approval Workflow
|
|
394
|
+
|
|
395
|
+
Post a request and watch for ✅ or ❌ reactions to advance state.
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
|
|
399
|
+
|
|
400
|
+
const client = await new SlackBotClient().login()
|
|
401
|
+
const me = await client.testAuth()
|
|
402
|
+
const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
|
|
403
|
+
|
|
404
|
+
const post = await client.postMessage('C0DEPLOY', '🚀 Deploy v2.1.0 to production?')
|
|
405
|
+
await client.addReaction('C0DEPLOY', post.ts, 'white_check_mark')
|
|
406
|
+
await client.addReaction('C0DEPLOY', post.ts, 'x')
|
|
407
|
+
|
|
408
|
+
listener.on('reaction_added', async ({ ack, event }) => {
|
|
409
|
+
ack()
|
|
410
|
+
if (event.item.channel !== 'C0DEPLOY' || event.item.ts !== post.ts) return
|
|
411
|
+
if (event.user === me.user_id) return
|
|
412
|
+
|
|
413
|
+
if (event.reaction === 'white_check_mark') {
|
|
414
|
+
await client.updateMessage('C0DEPLOY', post.ts, '🚀 Deploy v2.1.0 — *approved*')
|
|
415
|
+
} else if (event.reaction === 'x') {
|
|
416
|
+
await client.updateMessage('C0DEPLOY', post.ts, '🚀 Deploy v2.1.0 — *rejected*')
|
|
417
|
+
}
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
await listener.start()
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Slash Command Handler
|
|
424
|
+
|
|
425
|
+
React to `/deploy` slash command invocations in real time. The slash command must be registered in the Slack App config first.
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
|
|
429
|
+
|
|
430
|
+
const client = await new SlackBotClient().login()
|
|
431
|
+
const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
|
|
432
|
+
|
|
433
|
+
listener.on('slash_commands', async ({ ack, body }) => {
|
|
434
|
+
if (body.command !== '/deploy') {
|
|
435
|
+
ack()
|
|
436
|
+
return
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Reply immediately so Slack shows feedback to the user.
|
|
440
|
+
ack({ text: `Queued deploy of \`${body.text}\`...` })
|
|
441
|
+
|
|
442
|
+
// Then do the long-running work and post a follow-up.
|
|
443
|
+
await runDeploy(body.text)
|
|
444
|
+
await client.postMessage(body.channel_id, `✅ Deploy of \`${body.text}\` complete`)
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
await listener.start()
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Interactive Block Action Handler
|
|
451
|
+
|
|
452
|
+
Handle a button click from a Block Kit message.
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
|
|
456
|
+
|
|
457
|
+
const client = await new SlackBotClient().login()
|
|
458
|
+
const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
|
|
459
|
+
|
|
460
|
+
listener.on('interactive', async ({ ack, body }) => {
|
|
461
|
+
ack()
|
|
462
|
+
|
|
463
|
+
if (body.type !== 'block_actions') return
|
|
464
|
+
const action = body.actions?.[0] as { action_id?: string; value?: string } | undefined
|
|
465
|
+
if (action?.action_id !== 'approve_pr') return
|
|
466
|
+
|
|
467
|
+
await client.postMessage(
|
|
468
|
+
(body as { channel?: { id: string } }).channel!.id,
|
|
469
|
+
`<@${body.user!.id}> approved PR ${action.value}`,
|
|
470
|
+
)
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
await listener.start()
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### AI Assistant Thread with Typing Indicator
|
|
477
|
+
|
|
478
|
+
Show a "Bot is typing..." status while your agent is processing, then reply. Slack auto-clears the status when the bot posts its message. Requires the app to be configured as an AI Assistant (`assistant` scope, `assistant_thread_started` event) and a `chat:write` bot scope.
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
|
|
482
|
+
|
|
483
|
+
const client = await new SlackBotClient().login()
|
|
484
|
+
const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
|
|
485
|
+
|
|
486
|
+
listener.on('message', async ({ ack, event }) => {
|
|
487
|
+
ack()
|
|
488
|
+
// Only handle messages inside an AI Assistant thread (skip top-level posts and bot echoes).
|
|
489
|
+
if (!event.thread_ts || event.subtype === 'bot_message') return
|
|
490
|
+
|
|
491
|
+
// Show "Bot is typing..." while the agent thinks.
|
|
492
|
+
await client.setAssistantStatus(event.channel, event.thread_ts, 'is typing...')
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
const reply = await runAgent(event.text ?? '')
|
|
496
|
+
// Posting a reply auto-clears the status indicator.
|
|
497
|
+
await client.postMessage(event.channel, reply, { thread_ts: event.thread_ts })
|
|
498
|
+
} catch (err) {
|
|
499
|
+
// Clear the status on failure so it doesn't linger until the 2-minute timeout.
|
|
500
|
+
await client.setAssistantStatus(event.channel, event.thread_ts, '')
|
|
501
|
+
throw err
|
|
502
|
+
}
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
await listener.start()
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### CI Status Notifier
|
|
509
|
+
|
|
510
|
+
Post a status message at the start and finish of a CI job, with a reaction to indicate state.
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
import { SlackBotClient } from 'agent-messenger/slackbot'
|
|
514
|
+
|
|
515
|
+
const client = await new SlackBotClient().login()
|
|
516
|
+
const channel = 'C0BUILDS'
|
|
517
|
+
|
|
518
|
+
const start = await client.postMessage(channel, `🛠️ Build #${runId} started`)
|
|
519
|
+
await client.addReaction(channel, start.ts, 'hourglass_flowing_sand')
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
await runBuild()
|
|
523
|
+
await client.removeReaction(channel, start.ts, 'hourglass_flowing_sand')
|
|
524
|
+
await client.addReaction(channel, start.ts, 'white_check_mark')
|
|
525
|
+
await client.postMessage(channel, `✅ Build #${runId} succeeded`, { thread_ts: start.ts })
|
|
526
|
+
} catch (err) {
|
|
527
|
+
await client.removeReaction(channel, start.ts, 'hourglass_flowing_sand')
|
|
528
|
+
await client.addReaction(channel, start.ts, 'x')
|
|
529
|
+
await client.postMessage(channel, `❌ Build #${runId} failed: ${(err as Error).message}`, {
|
|
530
|
+
thread_ts: start.ts,
|
|
531
|
+
})
|
|
532
|
+
throw err
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Multi-Workspace, Multi-Bot Configuration
|
|
537
|
+
|
|
538
|
+
Use `SlackBotCredentialManager` to store and switch between bots across multiple workspaces.
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
import { SlackBotClient, SlackBotCredentialManager } from 'agent-messenger/slackbot'
|
|
542
|
+
|
|
543
|
+
const manager = new SlackBotCredentialManager()
|
|
544
|
+
|
|
545
|
+
// Two bots in the same workspace
|
|
546
|
+
await manager.setCredentials({
|
|
547
|
+
token: 'xoxb-DEPLOY',
|
|
548
|
+
workspace_id: 'T0ACME',
|
|
549
|
+
workspace_name: 'Acme Corp',
|
|
550
|
+
bot_id: 'deploy',
|
|
551
|
+
bot_name: 'Deploy Bot',
|
|
552
|
+
})
|
|
553
|
+
await manager.setCredentials({
|
|
554
|
+
token: 'xoxb-ALERTS',
|
|
555
|
+
workspace_id: 'T0ACME',
|
|
556
|
+
workspace_name: 'Acme Corp',
|
|
557
|
+
bot_id: 'alerts',
|
|
558
|
+
bot_name: 'Alerts Bot',
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
// A bot in a second workspace, with the same bot_id
|
|
562
|
+
await manager.setCredentials({
|
|
563
|
+
token: 'xoxb-DEPLOY-2',
|
|
564
|
+
workspace_id: 'T0WIDGET',
|
|
565
|
+
workspace_name: 'Widget Co',
|
|
566
|
+
bot_id: 'deploy',
|
|
567
|
+
bot_name: 'Widget Deploy Bot',
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
// Switch using "workspace_id/bot_id" to disambiguate
|
|
571
|
+
await manager.setCurrent('T0WIDGET/deploy')
|
|
572
|
+
const creds = await manager.getCredentials()
|
|
573
|
+
const client = await new SlackBotClient().login({ token: creds!.token })
|
|
574
|
+
|
|
575
|
+
await client.postMessage('C0RELEASES', '🚀 Widget Co release v3.0.0 deployed')
|
|
576
|
+
```
|
package/e2e/README.md
CHANGED
|
@@ -333,7 +333,7 @@ When triggering manually, you can select which platform to test:
|
|
|
333
333
|
| `bot` | list |
|
|
334
334
|
| `snapshot` | default |
|
|
335
335
|
|
|
336
|
-
> ⚠️ Channel Talk Bot tests require `E2E_CHANNELBOT_WORKSPACE_ID
|
|
336
|
+
> ⚠️ Channel Talk Bot tests require `E2E_CHANNELTALKBOT_WORKSPACE_ID` (legacy `E2E_CHANNELBOT_WORKSPACE_ID` still accepted as fallback).
|
|
337
337
|
|
|
338
338
|
## Troubleshooting
|
|
339
339
|
|
package/e2e/config.ts
CHANGED
|
@@ -149,16 +149,21 @@ export async function validateTeamsEnvironment(): Promise<boolean> {
|
|
|
149
149
|
return true
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
// ChannelBot Test Environment — requires
|
|
152
|
+
// ChannelBot Test Environment — requires E2E_CHANNELTALKBOT_WORKSPACE_ID to opt-in.
|
|
153
|
+
// E2E_CHANNELBOT_WORKSPACE_ID is also accepted as a legacy fallback.
|
|
153
154
|
// The E2E group is auto-discovered by name from the workspace's group list.
|
|
154
155
|
// Never run against a real business workspace automatically.
|
|
155
|
-
export const CHANNELBOT_TEST_WORKSPACE_ID =
|
|
156
|
-
|
|
156
|
+
export const CHANNELBOT_TEST_WORKSPACE_ID =
|
|
157
|
+
process.env.E2E_CHANNELTALKBOT_WORKSPACE_ID || process.env.E2E_CHANNELBOT_WORKSPACE_ID || ''
|
|
158
|
+
export const CHANNELBOT_TEST_WORKSPACE_NAME =
|
|
159
|
+
process.env.E2E_CHANNELTALKBOT_WORKSPACE_NAME || process.env.E2E_CHANNELBOT_WORKSPACE_NAME || ''
|
|
157
160
|
export const E2E_GROUP_NAME = 'E2E'
|
|
158
161
|
|
|
159
162
|
export async function validateChannelBotEnvironment(): Promise<{ groupId: string; groupName: string } | null> {
|
|
160
163
|
if (!CHANNELBOT_TEST_WORKSPACE_ID) {
|
|
161
|
-
console.warn(
|
|
164
|
+
console.warn(
|
|
165
|
+
'Skipping ChannelBot E2E: set E2E_CHANNELTALKBOT_WORKSPACE_ID to run against a dedicated test workspace.',
|
|
166
|
+
)
|
|
162
167
|
return null
|
|
163
168
|
}
|
|
164
169
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { DiscordBotClient } from '../src/platforms/discordbot/client'
|
|
3
|
+
import { DiscordBotListener } from '../src/platforms/discordbot/listener'
|
|
4
|
+
import { DiscordIntent } from '../src/platforms/discordbot/types'
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
const client = await new DiscordBotClient().login()
|
|
8
|
+
|
|
9
|
+
// MessageContent is privileged — enable it in the Discord Developer Portal first.
|
|
10
|
+
// Without it, `content` will be empty for messages that don't mention the bot.
|
|
11
|
+
const listener = new DiscordBotListener(client, {
|
|
12
|
+
intents:
|
|
13
|
+
DiscordIntent.Guilds |
|
|
14
|
+
DiscordIntent.GuildMessages |
|
|
15
|
+
DiscordIntent.GuildMessageReactions |
|
|
16
|
+
DiscordIntent.DirectMessages |
|
|
17
|
+
DiscordIntent.MessageContent,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
listener.on('connected', (info) => {
|
|
21
|
+
console.log(`Connected (bot: ${info.user.username} ${info.user.id}, session: ${info.sessionId})`)
|
|
22
|
+
console.log('Listening for events. Press Ctrl+C to stop.\n')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
listener.on('disconnected', () => {
|
|
26
|
+
console.log('[disconnected] reconnecting...')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
listener.on('message_create', (event) => {
|
|
30
|
+
if (event.author.bot) return
|
|
31
|
+
const time = new Date(event.timestamp).toLocaleTimeString()
|
|
32
|
+
console.log(`[${time}] message #${event.channel_id} <${event.author.username}>: ${event.content}`)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
listener.on('message_update', (event) => {
|
|
36
|
+
console.log(`[update] message ${event.id} in #${event.channel_id}`)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
listener.on('message_delete', (event) => {
|
|
40
|
+
console.log(`[delete] message ${event.id} in #${event.channel_id}`)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
listener.on('message_reaction_add', (event) => {
|
|
44
|
+
console.log(`[reaction] :${event.emoji.name}: by ${event.user_id} on ${event.channel_id}/${event.message_id}`)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
listener.on('interaction_create', (event) => {
|
|
48
|
+
const name = (event.data as { name?: string } | undefined)?.name ?? '(unknown)'
|
|
49
|
+
console.log(`[interaction] ${name} from ${event.user?.username ?? 'unknown'}`)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
listener.on('error', (err) => {
|
|
53
|
+
console.error(`[error] ${err.message}`)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
process.on('SIGINT', () => {
|
|
57
|
+
console.log('\nStopping...')
|
|
58
|
+
listener.stop()
|
|
59
|
+
process.exit(130)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
await listener.start()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main()
|