agent-messenger 2.10.2 → 2.11.1
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 +21 -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 +21 -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
|
@@ -6,11 +6,20 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
|
6
6
|
import { tmpdir } from 'node:os'
|
|
7
7
|
import { join } from 'node:path'
|
|
8
8
|
|
|
9
|
+
import { ChromiumCookieDecryptor } from '@/shared/chromium'
|
|
10
|
+
|
|
9
11
|
import { ExtractedWorkspace, TokenExtractor } from './token-extractor'
|
|
10
12
|
|
|
11
13
|
const tempDirs: string[] = []
|
|
14
|
+
const originalAgentBrowserProfile = process.env.AGENT_BROWSER_PROFILE
|
|
12
15
|
|
|
13
16
|
afterEach(() => {
|
|
17
|
+
if (originalAgentBrowserProfile) {
|
|
18
|
+
process.env.AGENT_BROWSER_PROFILE = originalAgentBrowserProfile
|
|
19
|
+
} else {
|
|
20
|
+
delete process.env.AGENT_BROWSER_PROFILE
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
for (const dir of tempDirs) {
|
|
15
24
|
rmSync(dir, { recursive: true, force: true })
|
|
16
25
|
}
|
|
@@ -802,6 +811,52 @@ describe('TokenExtractor browser fallback', () => {
|
|
|
802
811
|
expect(result).toEqual([])
|
|
803
812
|
})
|
|
804
813
|
|
|
814
|
+
it('resolves Local State from agent-browser profile root for encrypted cookies', async () => {
|
|
815
|
+
// given
|
|
816
|
+
const agentBrowserProfile = mkdtempSync(join(tmpdir(), 'agent-browser-slack-profile-'))
|
|
817
|
+
tempDirs.push(agentBrowserProfile)
|
|
818
|
+
process.env.AGENT_BROWSER_PROFILE = agentBrowserProfile
|
|
819
|
+
|
|
820
|
+
const hex64 = 'c'.repeat(64)
|
|
821
|
+
const token = `xoxc-1111111111-2222222222-3333333333-${hex64}`
|
|
822
|
+
const profileDir = join(agentBrowserProfile, 'Default')
|
|
823
|
+
const leveldbDir = join(profileDir, 'Local Storage', 'leveldb')
|
|
824
|
+
const networkDir = join(profileDir, 'Network')
|
|
825
|
+
mkdirSync(leveldbDir, { recursive: true })
|
|
826
|
+
mkdirSync(networkDir, { recursive: true })
|
|
827
|
+
writeFileSync(join(leveldbDir, '000001.log'), `"${token}"T12345678"name":"agent-browser-workspace"`)
|
|
828
|
+
writeFileSync(join(agentBrowserProfile, 'Local State'), '{}')
|
|
829
|
+
createCookiesDb(join(networkDir, 'Cookies'), [
|
|
830
|
+
{
|
|
831
|
+
name: 'd',
|
|
832
|
+
value: '',
|
|
833
|
+
encrypted_value: new Uint8Array([1, 2, 3]),
|
|
834
|
+
host_key: '.slack.com',
|
|
835
|
+
last_access_utc: 1,
|
|
836
|
+
},
|
|
837
|
+
])
|
|
838
|
+
const decryptSpy = spyOn(ChromiumCookieDecryptor.prototype, 'decryptCookie').mockReturnValue('xoxd-AgentBrowser')
|
|
839
|
+
|
|
840
|
+
try {
|
|
841
|
+
// when
|
|
842
|
+
const extractor = new TokenExtractor('darwin', join(agentBrowserProfile, 'missing-desktop'))
|
|
843
|
+
const result = await extractor.extractFromBrowsers()
|
|
844
|
+
|
|
845
|
+
// then
|
|
846
|
+
expect(result).toEqual([
|
|
847
|
+
{
|
|
848
|
+
workspace_id: 'T12345678',
|
|
849
|
+
workspace_name: 'agent-browser-workspace',
|
|
850
|
+
token,
|
|
851
|
+
cookie: 'xoxd-AgentBrowser',
|
|
852
|
+
},
|
|
853
|
+
])
|
|
854
|
+
expect(decryptSpy).toHaveBeenCalledWith(Buffer.from([1, 2, 3]), join(agentBrowserProfile, 'Local State'))
|
|
855
|
+
} finally {
|
|
856
|
+
decryptSpy.mockRestore()
|
|
857
|
+
}
|
|
858
|
+
})
|
|
859
|
+
|
|
805
860
|
it('extract tries desktop before browser profiles', async () => {
|
|
806
861
|
// given — slackDir with LevelDB token data
|
|
807
862
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-fallback-desktop-'))
|
|
@@ -826,6 +881,47 @@ describe('TokenExtractor browser fallback', () => {
|
|
|
826
881
|
extractFromBrowsersSpy.mockRestore()
|
|
827
882
|
})
|
|
828
883
|
|
|
884
|
+
it('custom browser profile can upgrade desktop token with a cookie', async () => {
|
|
885
|
+
// given
|
|
886
|
+
const slackDir = mkdtempSync(join(tmpdir(), 'slack-custom-cookie-upgrade-'))
|
|
887
|
+
tempDirs.push(slackDir)
|
|
888
|
+
|
|
889
|
+
const hex64 = 'd'.repeat(64)
|
|
890
|
+
const token = `xoxc-1111111111-2222222222-3333333333-${hex64}`
|
|
891
|
+
const leveldbDir = join(slackDir, 'Local Storage', 'leveldb')
|
|
892
|
+
mkdirSync(leveldbDir, { recursive: true })
|
|
893
|
+
writeFileSync(join(leveldbDir, '000001.log'), `"${token}"T12345678"name":"desktop-workspace"`)
|
|
894
|
+
|
|
895
|
+
const browserWorkspace: ExtractedWorkspace = {
|
|
896
|
+
workspace_id: 'T12345678',
|
|
897
|
+
workspace_name: 'browser-workspace',
|
|
898
|
+
token,
|
|
899
|
+
cookie: 'xoxd-browser-cookie',
|
|
900
|
+
}
|
|
901
|
+
const extractFromBrowsersSpy = spyOn(TokenExtractor.prototype as any, 'extractFromBrowsers').mockResolvedValue([
|
|
902
|
+
browserWorkspace,
|
|
903
|
+
])
|
|
904
|
+
|
|
905
|
+
try {
|
|
906
|
+
// when
|
|
907
|
+
const extractor = new TokenExtractor('darwin', slackDir, undefined, undefined, ['/tmp/custom-profile'])
|
|
908
|
+
const result = await extractor.extract()
|
|
909
|
+
|
|
910
|
+
// then
|
|
911
|
+
expect(extractFromBrowsersSpy).toHaveBeenCalled()
|
|
912
|
+
expect(result).toEqual([
|
|
913
|
+
{
|
|
914
|
+
workspace_id: 'T12345678',
|
|
915
|
+
workspace_name: 'desktop-workspace',
|
|
916
|
+
token,
|
|
917
|
+
cookie: 'xoxd-browser-cookie',
|
|
918
|
+
},
|
|
919
|
+
])
|
|
920
|
+
} finally {
|
|
921
|
+
extractFromBrowsersSpy.mockRestore()
|
|
922
|
+
}
|
|
923
|
+
})
|
|
924
|
+
|
|
829
925
|
it('extract falls back to browser profiles when desktop has no tokens', async () => {
|
|
830
926
|
// given — empty slackDir (no tokens)
|
|
831
927
|
const slackDir = mkdtempSync(join(tmpdir(), 'slack-fallback-browser-'))
|
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
ChromiumCookieDecryptor,
|
|
13
13
|
ChromiumCookieReader,
|
|
14
14
|
discoverBrowserProfileDirs,
|
|
15
|
+
findLocalStatePath,
|
|
15
16
|
getBrowserBasePath,
|
|
17
|
+
getAgentBrowserProfileDirs,
|
|
16
18
|
} from '@/shared/chromium'
|
|
17
19
|
import { DerivedKeyCache } from '@/shared/utils/derived-key-cache'
|
|
18
20
|
import { lookupLinuxKeyringPassword } from '@/shared/utils/linux-keyring'
|
|
@@ -63,12 +65,14 @@ export class TokenExtractor {
|
|
|
63
65
|
private debugLog: ((message: string) => void) | null
|
|
64
66
|
private browserDecryptor: ChromiumCookieDecryptor
|
|
65
67
|
private browserCookieReader: ChromiumCookieReader
|
|
68
|
+
private customBrowserProfileDirs: string[]
|
|
66
69
|
|
|
67
70
|
constructor(
|
|
68
71
|
platform?: NodeJS.Platform,
|
|
69
72
|
slackDir?: string,
|
|
70
73
|
keyCache?: DerivedKeyCache,
|
|
71
74
|
debugLog?: (message: string) => void,
|
|
75
|
+
customBrowserProfileDirs?: string[],
|
|
72
76
|
) {
|
|
73
77
|
this.platform = platform ?? process.platform
|
|
74
78
|
|
|
@@ -79,6 +83,7 @@ export class TokenExtractor {
|
|
|
79
83
|
this.slackDir = slackDir ?? this.getSlackDir()
|
|
80
84
|
this.keyCache = keyCache ?? new DerivedKeyCache()
|
|
81
85
|
this.debugLog = debugLog ?? null
|
|
86
|
+
this.customBrowserProfileDirs = customBrowserProfileDirs ?? []
|
|
82
87
|
this.browserDecryptor = new ChromiumCookieDecryptor({ platform: this.platform })
|
|
83
88
|
this.browserCookieReader = new ChromiumCookieReader()
|
|
84
89
|
}
|
|
@@ -188,6 +193,9 @@ export class TokenExtractor {
|
|
|
188
193
|
}
|
|
189
194
|
|
|
190
195
|
async extract(): Promise<ExtractedWorkspace[]> {
|
|
196
|
+
const results: ExtractedWorkspace[] = []
|
|
197
|
+
const seenTokens = new Set<string>()
|
|
198
|
+
|
|
191
199
|
if (existsSync(this.slackDir)) {
|
|
192
200
|
await this.getDerivedKeyAsync()
|
|
193
201
|
|
|
@@ -201,27 +209,55 @@ export class TokenExtractor {
|
|
|
201
209
|
if (this.cachedKey) {
|
|
202
210
|
await this.keyCache.set('slack', this.cachedKey)
|
|
203
211
|
const retryCookie = await this.extractCookieFromSQLite()
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
workspace_name: t.teamName,
|
|
207
|
-
token: t.token,
|
|
208
|
-
cookie: retryCookie,
|
|
209
|
-
}))
|
|
212
|
+
this.addExtractedWorkspaces(results, seenTokens, tokens, retryCookie)
|
|
213
|
+
return this.customBrowserProfileDirs.length > 0 ? this.mergeBrowserResults(results, seenTokens) : results
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
workspace_name: t.teamName,
|
|
216
|
-
token: t.token,
|
|
217
|
-
cookie: cookie,
|
|
218
|
-
}))
|
|
217
|
+
this.addExtractedWorkspaces(results, seenTokens, tokens, cookie)
|
|
218
|
+
return this.customBrowserProfileDirs.length > 0 ? this.mergeBrowserResults(results, seenTokens) : results
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
return this.extractFromBrowsers()
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
private async mergeBrowserResults(
|
|
226
|
+
results: ExtractedWorkspace[],
|
|
227
|
+
seenTokens: Set<string>,
|
|
228
|
+
): Promise<ExtractedWorkspace[]> {
|
|
229
|
+
for (const workspace of await this.extractFromBrowsers()) {
|
|
230
|
+
const existing = results.find((item) => item.token === workspace.token)
|
|
231
|
+
if (existing) {
|
|
232
|
+
if (!existing.cookie && workspace.cookie) {
|
|
233
|
+
existing.cookie = workspace.cookie
|
|
234
|
+
}
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
seenTokens.add(workspace.token)
|
|
238
|
+
results.push(workspace)
|
|
239
|
+
}
|
|
240
|
+
return results
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private addExtractedWorkspaces(
|
|
244
|
+
results: ExtractedWorkspace[],
|
|
245
|
+
seenTokens: Set<string>,
|
|
246
|
+
tokens: TokenInfo[],
|
|
247
|
+
cookie: string,
|
|
248
|
+
): void {
|
|
249
|
+
for (const token of tokens) {
|
|
250
|
+
if (seenTokens.has(token.token)) continue
|
|
251
|
+
seenTokens.add(token.token)
|
|
252
|
+
results.push({
|
|
253
|
+
workspace_id: token.teamId,
|
|
254
|
+
workspace_name: token.teamName,
|
|
255
|
+
token: token.token,
|
|
256
|
+
cookie,
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
225
261
|
async extractFromBrowsers(): Promise<ExtractedWorkspace[]> {
|
|
226
262
|
const results: ExtractedWorkspace[] = []
|
|
227
263
|
const seenTokens = new Set<string>()
|
|
@@ -259,6 +295,33 @@ export class TokenExtractor {
|
|
|
259
295
|
}
|
|
260
296
|
}
|
|
261
297
|
|
|
298
|
+
for (const profileDir of getAgentBrowserProfileDirs({ customProfileDirs: this.customBrowserProfileDirs })) {
|
|
299
|
+
const leveldbDir = join(profileDir, 'Local Storage', 'leveldb')
|
|
300
|
+
if (!existsSync(leveldbDir)) continue
|
|
301
|
+
|
|
302
|
+
let tokenInfos: TokenInfo[]
|
|
303
|
+
try {
|
|
304
|
+
tokenInfos = await this.extractFromLevelDB(leveldbDir, 'local-storage')
|
|
305
|
+
} catch {
|
|
306
|
+
continue
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (tokenInfos.length === 0) continue
|
|
310
|
+
|
|
311
|
+
const cookie = await this.extractCookieFromBrowserProfile(profileDir, profileDir)
|
|
312
|
+
|
|
313
|
+
for (const t of tokenInfos) {
|
|
314
|
+
if (seenTokens.has(t.token)) continue
|
|
315
|
+
seenTokens.add(t.token)
|
|
316
|
+
results.push({
|
|
317
|
+
workspace_id: t.teamId,
|
|
318
|
+
workspace_name: t.teamName,
|
|
319
|
+
token: t.token,
|
|
320
|
+
cookie,
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
262
325
|
return results
|
|
263
326
|
}
|
|
264
327
|
|
|
@@ -293,7 +356,7 @@ export class TokenExtractor {
|
|
|
293
356
|
if (row.value?.startsWith('xoxd-')) return row.value
|
|
294
357
|
|
|
295
358
|
if (row.encrypted_value && row.encrypted_value.length > 0) {
|
|
296
|
-
const localStatePath = join(browserBase, 'Local State')
|
|
359
|
+
const localStatePath = findLocalStatePath(cookiesPath) ?? join(browserBase, 'Local State')
|
|
297
360
|
const decrypted = this.browserDecryptor.decryptCookie(
|
|
298
361
|
Buffer.from(row.encrypted_value),
|
|
299
362
|
existsSync(localStatePath) ? localStatePath : undefined,
|
|
@@ -6,6 +6,7 @@ import pkg from '../../../package.json' with { type: 'json' }
|
|
|
6
6
|
import {
|
|
7
7
|
authCommand,
|
|
8
8
|
channelCommand,
|
|
9
|
+
fileCommand,
|
|
9
10
|
messageCommand,
|
|
10
11
|
reactionCommand,
|
|
11
12
|
userCommand,
|
|
@@ -18,7 +19,17 @@ program
|
|
|
18
19
|
.name('agent-slackbot')
|
|
19
20
|
.description('CLI tool for Slack bot integration using bot tokens (xoxb-)')
|
|
20
21
|
.version(pkg.version)
|
|
22
|
+
.option('--pretty', 'Pretty-print JSON output')
|
|
21
23
|
.option('--bot <id>', 'Use specific bot (default: current)')
|
|
24
|
+
.hook('preAction', (thisCmd, actionCmd) => {
|
|
25
|
+
for (const [key, value] of Object.entries(thisCmd.opts())) {
|
|
26
|
+
if (value === undefined) continue
|
|
27
|
+
const source = actionCmd.getOptionValueSource(key)
|
|
28
|
+
if (source === undefined || source === 'default') {
|
|
29
|
+
actionCmd.setOptionValue(key, value)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
})
|
|
22
33
|
|
|
23
34
|
program.addCommand(authCommand)
|
|
24
35
|
program.addCommand(whoamiCommand)
|
|
@@ -26,7 +37,8 @@ program.addCommand(messageCommand)
|
|
|
26
37
|
program.addCommand(channelCommand)
|
|
27
38
|
program.addCommand(userCommand)
|
|
28
39
|
program.addCommand(reactionCommand)
|
|
40
|
+
program.addCommand(fileCommand)
|
|
29
41
|
|
|
30
|
-
program.
|
|
42
|
+
program.parseAsync(process.argv)
|
|
31
43
|
|
|
32
44
|
export default program
|
|
@@ -65,6 +65,70 @@ const mockReactions = {
|
|
|
65
65
|
add: mock(() => Promise.resolve({ ok: true })),
|
|
66
66
|
remove: mock(() => Promise.resolve({ ok: true })),
|
|
67
67
|
}
|
|
68
|
+
const mockAssistant = {
|
|
69
|
+
threads: {
|
|
70
|
+
setStatus: mock(() => Promise.resolve({ ok: true })),
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
const mockFiles = {
|
|
74
|
+
uploadV2: mock(() =>
|
|
75
|
+
Promise.resolve({
|
|
76
|
+
ok: true,
|
|
77
|
+
files: [
|
|
78
|
+
{
|
|
79
|
+
ok: true,
|
|
80
|
+
files: [
|
|
81
|
+
{
|
|
82
|
+
id: 'F123',
|
|
83
|
+
name: 'test.txt',
|
|
84
|
+
title: 'test.txt',
|
|
85
|
+
mimetype: 'text/plain',
|
|
86
|
+
size: 12,
|
|
87
|
+
url_private: 'https://files.slack.com/files-pri/T123-F123/test.txt',
|
|
88
|
+
created: 1234567890,
|
|
89
|
+
user: 'U123',
|
|
90
|
+
channels: ['C123'],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
}),
|
|
96
|
+
),
|
|
97
|
+
list: mock(() =>
|
|
98
|
+
Promise.resolve({
|
|
99
|
+
ok: true,
|
|
100
|
+
files: [
|
|
101
|
+
{
|
|
102
|
+
id: 'F123',
|
|
103
|
+
name: 'test.txt',
|
|
104
|
+
title: 'test.txt',
|
|
105
|
+
mimetype: 'text/plain',
|
|
106
|
+
size: 1024,
|
|
107
|
+
url_private: 'https://files.slack.com/files-pri/T123-F123/test.txt',
|
|
108
|
+
created: 1234567890,
|
|
109
|
+
user: 'U123',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
}),
|
|
113
|
+
),
|
|
114
|
+
info: mock(() =>
|
|
115
|
+
Promise.resolve({
|
|
116
|
+
ok: true,
|
|
117
|
+
file: {
|
|
118
|
+
id: 'F123',
|
|
119
|
+
name: 'test.txt',
|
|
120
|
+
title: 'test.txt',
|
|
121
|
+
mimetype: 'text/plain',
|
|
122
|
+
size: 1024,
|
|
123
|
+
url_private: 'https://files.slack.com/files-pri/T123-F123/test.txt',
|
|
124
|
+
created: 1234567890,
|
|
125
|
+
user: 'U123',
|
|
126
|
+
channels: ['C123'],
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
),
|
|
130
|
+
delete: mock(() => Promise.resolve({ ok: true })),
|
|
131
|
+
}
|
|
68
132
|
const mockUsers = {
|
|
69
133
|
list: mock(() =>
|
|
70
134
|
Promise.resolve({
|
|
@@ -105,6 +169,8 @@ mock.module('@slack/web-api', () => ({
|
|
|
105
169
|
chat = mockChat
|
|
106
170
|
reactions = mockReactions
|
|
107
171
|
users = mockUsers
|
|
172
|
+
files = mockFiles
|
|
173
|
+
assistant = mockAssistant
|
|
108
174
|
},
|
|
109
175
|
}))
|
|
110
176
|
|
|
@@ -120,6 +186,11 @@ describe('SlackBotClient', () => {
|
|
|
120
186
|
mockReactions.remove.mockClear()
|
|
121
187
|
mockUsers.list.mockClear()
|
|
122
188
|
mockUsers.info.mockClear()
|
|
189
|
+
mockFiles.uploadV2.mockClear()
|
|
190
|
+
mockFiles.list.mockClear()
|
|
191
|
+
mockFiles.info.mockClear()
|
|
192
|
+
mockFiles.delete.mockClear()
|
|
193
|
+
mockAssistant.threads.setStatus.mockClear()
|
|
123
194
|
})
|
|
124
195
|
|
|
125
196
|
describe('login', () => {
|
|
@@ -224,6 +295,38 @@ describe('SlackBotClient', () => {
|
|
|
224
295
|
})
|
|
225
296
|
})
|
|
226
297
|
|
|
298
|
+
describe('setAssistantStatus', () => {
|
|
299
|
+
it('sets assistant typing status with channel_id and thread_ts', async () => {
|
|
300
|
+
// given
|
|
301
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
302
|
+
|
|
303
|
+
// when
|
|
304
|
+
await client.setAssistantStatus('C123', '1234567890.123456', 'is typing...')
|
|
305
|
+
|
|
306
|
+
// then
|
|
307
|
+
expect(mockAssistant.threads.setStatus).toHaveBeenCalledWith({
|
|
308
|
+
channel_id: 'C123',
|
|
309
|
+
thread_ts: '1234567890.123456',
|
|
310
|
+
status: 'is typing...',
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('clears status when given empty string', async () => {
|
|
315
|
+
// given
|
|
316
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
317
|
+
|
|
318
|
+
// when
|
|
319
|
+
await client.setAssistantStatus('C123', '1234567890.123456', '')
|
|
320
|
+
|
|
321
|
+
// then
|
|
322
|
+
expect(mockAssistant.threads.setStatus).toHaveBeenCalledWith({
|
|
323
|
+
channel_id: 'C123',
|
|
324
|
+
thread_ts: '1234567890.123456',
|
|
325
|
+
status: '',
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
|
|
227
330
|
describe('listChannels', () => {
|
|
228
331
|
it('returns list of channels', async () => {
|
|
229
332
|
// given
|
|
@@ -367,4 +470,175 @@ describe('SlackBotClient', () => {
|
|
|
367
470
|
expect(user.name).toBe('testuser')
|
|
368
471
|
})
|
|
369
472
|
})
|
|
473
|
+
|
|
474
|
+
describe('uploadFile', () => {
|
|
475
|
+
it('uploads file via files.uploadV2 and returns mapped file', async () => {
|
|
476
|
+
// given
|
|
477
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
478
|
+
|
|
479
|
+
// when
|
|
480
|
+
const file = await client.uploadFile('C123', Buffer.from('test content'), 'test.txt')
|
|
481
|
+
|
|
482
|
+
// then
|
|
483
|
+
expect(file.id).toBe('F123')
|
|
484
|
+
expect(file.name).toBe('test.txt')
|
|
485
|
+
expect(file.size).toBe(12)
|
|
486
|
+
expect(mockFiles.uploadV2).toHaveBeenCalledWith(
|
|
487
|
+
expect.objectContaining({ channel_id: 'C123', filename: 'test.txt' }),
|
|
488
|
+
)
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
it('forwards thread_ts, title, initial_comment to the SDK', async () => {
|
|
492
|
+
// given
|
|
493
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
494
|
+
|
|
495
|
+
// when
|
|
496
|
+
await client.uploadFile('C123', Buffer.from('x'), 'test.txt', {
|
|
497
|
+
thread_ts: '1234567890.123456',
|
|
498
|
+
title: 'My Title',
|
|
499
|
+
initial_comment: 'Here you go',
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
// then
|
|
503
|
+
expect(mockFiles.uploadV2).toHaveBeenCalledWith(
|
|
504
|
+
expect.objectContaining({
|
|
505
|
+
channel_id: 'C123',
|
|
506
|
+
filename: 'test.txt',
|
|
507
|
+
thread_ts: '1234567890.123456',
|
|
508
|
+
title: 'My Title',
|
|
509
|
+
initial_comment: 'Here you go',
|
|
510
|
+
}),
|
|
511
|
+
)
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
it('throws SlackBotError when API responds not ok', async () => {
|
|
515
|
+
// given
|
|
516
|
+
mockFiles.uploadV2.mockResolvedValueOnce({ ok: false, error: 'file_upload_failed' } as any)
|
|
517
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
518
|
+
|
|
519
|
+
// when/then
|
|
520
|
+
await expect(client.uploadFile('C123', Buffer.from('x'), 'test.txt')).rejects.toThrow(SlackBotError)
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
it('throws SlackBotError when completion has no inner files', async () => {
|
|
524
|
+
// given
|
|
525
|
+
mockFiles.uploadV2.mockResolvedValueOnce({ ok: true, files: [{ ok: true, files: [] }] } as any)
|
|
526
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
527
|
+
|
|
528
|
+
// when/then
|
|
529
|
+
await expect(client.uploadFile('C123', Buffer.from('x'), 'test.txt')).rejects.toThrow(SlackBotError)
|
|
530
|
+
})
|
|
531
|
+
})
|
|
532
|
+
|
|
533
|
+
describe('listFiles', () => {
|
|
534
|
+
it('returns mapped files', async () => {
|
|
535
|
+
// given
|
|
536
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
537
|
+
|
|
538
|
+
// when
|
|
539
|
+
const files = await client.listFiles()
|
|
540
|
+
|
|
541
|
+
// then
|
|
542
|
+
expect(files).toHaveLength(1)
|
|
543
|
+
expect(files[0].id).toBe('F123')
|
|
544
|
+
expect(files[0].mimetype).toBe('text/plain')
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
it('forwards channel/user/limit to the SDK', async () => {
|
|
548
|
+
// given
|
|
549
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
550
|
+
|
|
551
|
+
// when
|
|
552
|
+
await client.listFiles({ channel: 'C123', user: 'U123', limit: 50 })
|
|
553
|
+
|
|
554
|
+
// then
|
|
555
|
+
expect(mockFiles.list).toHaveBeenCalledWith({ channel: 'C123', user: 'U123', count: 50 })
|
|
556
|
+
})
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
describe('getFileInfo', () => {
|
|
560
|
+
it('returns file metadata', async () => {
|
|
561
|
+
// given
|
|
562
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
563
|
+
|
|
564
|
+
// when
|
|
565
|
+
const file = await client.getFileInfo('F123')
|
|
566
|
+
|
|
567
|
+
// then
|
|
568
|
+
expect(file.id).toBe('F123')
|
|
569
|
+
expect(file.url_private).toBe('https://files.slack.com/files-pri/T123-F123/test.txt')
|
|
570
|
+
})
|
|
571
|
+
|
|
572
|
+
it('throws on API failure', async () => {
|
|
573
|
+
// given
|
|
574
|
+
mockFiles.info.mockResolvedValueOnce({ ok: false, error: 'file_not_found' } as any)
|
|
575
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
576
|
+
|
|
577
|
+
// when/then
|
|
578
|
+
await expect(client.getFileInfo('F999')).rejects.toThrow(SlackBotError)
|
|
579
|
+
})
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
describe('downloadFile', () => {
|
|
583
|
+
it('downloads file content using the bot token', async () => {
|
|
584
|
+
// given
|
|
585
|
+
const originalFetch = globalThis.fetch
|
|
586
|
+
let capturedAuthHeader: string | null = null
|
|
587
|
+
globalThis.fetch = async (_url: any, init: any = {}) => {
|
|
588
|
+
capturedAuthHeader = init?.headers?.Authorization ?? null
|
|
589
|
+
return new Response(Buffer.from('downloaded content'), { status: 200 })
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
try {
|
|
593
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
594
|
+
|
|
595
|
+
// when
|
|
596
|
+
const result = await client.downloadFile('F123')
|
|
597
|
+
|
|
598
|
+
// then
|
|
599
|
+
expect(capturedAuthHeader).toBe('Bearer xoxb-test-token')
|
|
600
|
+
expect(result.file.id).toBe('F123')
|
|
601
|
+
expect(result.buffer.toString()).toBe('downloaded content')
|
|
602
|
+
} finally {
|
|
603
|
+
globalThis.fetch = originalFetch
|
|
604
|
+
}
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
it('throws SlackBotError when download HTTP response is not ok', async () => {
|
|
608
|
+
// given
|
|
609
|
+
const originalFetch = globalThis.fetch
|
|
610
|
+
globalThis.fetch = async () => new Response('forbidden', { status: 403, statusText: 'Forbidden' })
|
|
611
|
+
|
|
612
|
+
try {
|
|
613
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
614
|
+
|
|
615
|
+
// when/then
|
|
616
|
+
await expect(client.downloadFile('F123')).rejects.toThrow(SlackBotError)
|
|
617
|
+
} finally {
|
|
618
|
+
globalThis.fetch = originalFetch
|
|
619
|
+
}
|
|
620
|
+
})
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
describe('deleteFile', () => {
|
|
624
|
+
it('calls files.delete with the given file ID', async () => {
|
|
625
|
+
// given
|
|
626
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
627
|
+
|
|
628
|
+
// when
|
|
629
|
+
await client.deleteFile('F123')
|
|
630
|
+
|
|
631
|
+
// then
|
|
632
|
+
expect(mockFiles.delete).toHaveBeenCalledWith({ file: 'F123' })
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
it('throws on API failure', async () => {
|
|
636
|
+
// given
|
|
637
|
+
mockFiles.delete.mockResolvedValueOnce({ ok: false, error: 'cant_delete_file' } as any)
|
|
638
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
639
|
+
|
|
640
|
+
// when/then
|
|
641
|
+
await expect(client.deleteFile('F123')).rejects.toThrow(SlackBotError)
|
|
642
|
+
})
|
|
643
|
+
})
|
|
370
644
|
})
|