agent-messenger 2.2.0 → 2.4.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/README.md +16 -16
- package/.claude-plugin/marketplace.json +29 -29
- package/.claude-plugin/plugin.json +5 -5
- package/CONTRIBUTING.md +1 -1
- package/README.md +9 -6
- package/bun.lock +89 -105
- package/bunfig.toml +3 -0
- package/dist/package.json +13 -3
- package/dist/src/platforms/discordbot/client.js +2 -2
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.js +2 -1
- package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
- package/dist/src/platforms/kakaotalk/client.d.ts +2 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +52 -2
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.js +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts +3 -0
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js +19 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js.map +1 -0
- package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +1 -1
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +16 -0
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js +8 -0
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +32 -20
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.js +2 -0
- package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts +2 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +66 -23
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/auth.js +4 -0
- package/dist/src/platforms/webex/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/encryption.d.ts +10 -0
- package/dist/src/platforms/webex/encryption.d.ts.map +1 -0
- package/dist/src/platforms/webex/encryption.js +49 -0
- package/dist/src/platforms/webex/encryption.js.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.js +4 -0
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts +6 -5
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/webex/token-extractor.js +92 -43
- package/dist/src/platforms/webex/token-extractor.js.map +1 -1
- package/dist/src/platforms/webex/types.d.ts +4 -0
- package/dist/src/platforms/webex/types.d.ts.map +1 -1
- package/dist/src/platforms/webex/types.js +2 -0
- package/dist/src/platforms/webex/types.js.map +1 -1
- package/dist/src/platforms/wechatbot/cli.d.ts +5 -0
- package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/cli.js +18 -0
- package/dist/src/platforms/wechatbot/cli.js.map +1 -0
- package/dist/src/platforms/wechatbot/client.d.ts +36 -0
- package/dist/src/platforms/wechatbot/client.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/client.js +208 -0
- package/dist/src/platforms/wechatbot/client.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts +28 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.js +164 -0
- package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.js +5 -0
- package/dist/src/platforms/wechatbot/commands/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts +18 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.js +80 -0
- package/dist/src/platforms/wechatbot/commands/message.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts +9 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.js +13 -0
- package/dist/src/platforms/wechatbot/commands/shared.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts +19 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.js +76 -0
- package/dist/src/platforms/wechatbot/commands/template.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts +20 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.js +53 -0
- package/dist/src/platforms/wechatbot/commands/user.js.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts +17 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.js +121 -0
- package/dist/src/platforms/wechatbot/credential-manager.js.map +1 -0
- package/dist/src/platforms/wechatbot/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/index.js +4 -0
- package/dist/src/platforms/wechatbot/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/types.d.ts +94 -0
- package/dist/src/platforms/wechatbot/types.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/types.js +54 -0
- package/dist/src/platforms/wechatbot/types.js.map +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +27 -13
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.js +21 -18
- package/dist/src/platforms/whatsapp/commands/auth.js.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.js +14 -8
- package/dist/src/platforms/whatsapp/credential-manager.js.map +1 -1
- package/docs/content/docs/agent-skills.mdx +4 -4
- package/docs/content/docs/cli/channeltalk.mdx +1 -1
- package/docs/content/docs/cli/channeltalkbot.mdx +1 -1
- package/docs/content/docs/cli/discord.mdx +1 -1
- package/docs/content/docs/cli/discordbot.mdx +1 -1
- package/docs/content/docs/cli/instagram.mdx +1 -1
- package/docs/content/docs/cli/kakaotalk.mdx +1 -1
- package/docs/content/docs/cli/line.mdx +1 -1
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/slack.mdx +1 -1
- package/docs/content/docs/cli/slackbot.mdx +1 -1
- package/docs/content/docs/cli/teams.mdx +1 -1
- package/docs/content/docs/cli/webex.mdx +5 -3
- package/docs/content/docs/cli/wechatbot.mdx +179 -0
- package/docs/content/docs/cli/whatsapp.mdx +1 -1
- package/docs/content/docs/cli/whatsappbot.mdx +1 -1
- package/docs/content/docs/sdk/meta.json +1 -1
- package/docs/content/docs/sdk/wechatbot.mdx +282 -0
- package/docs/content/docs/tui.mdx +1 -1
- package/docs/src/app/page.tsx +5 -5
- package/package.json +13 -3
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +24 -1
- package/skills/agent-line/SKILL.md +7 -11
- package/skills/agent-line/references/authentication.md +13 -4
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webex/references/authentication.md +4 -3
- package/skills/agent-webex/references/common-patterns.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +385 -0
- package/skills/agent-whatsapp/SKILL.md +12 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/discord/credential-manager.test.ts +18 -1
- package/src/platforms/discordbot/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.test.ts +216 -0
- package/src/platforms/instagram/commands/chat.test.ts +127 -0
- package/src/platforms/instagram/commands/message.test.ts +178 -0
- package/src/platforms/kakaotalk/cli.ts +2 -1
- package/src/platforms/kakaotalk/client.test.ts +157 -0
- package/src/platforms/kakaotalk/client.ts +57 -3
- package/src/platforms/kakaotalk/commands/auth.test.ts +299 -0
- package/src/platforms/kakaotalk/commands/chat.test.ts +97 -0
- package/src/platforms/kakaotalk/commands/index.ts +1 -0
- package/src/platforms/kakaotalk/commands/message.test.ts +113 -0
- package/src/platforms/kakaotalk/commands/profile.test.ts +84 -0
- package/src/platforms/kakaotalk/commands/profile.ts +21 -0
- package/src/platforms/kakaotalk/index.test.ts +5 -0
- package/src/platforms/kakaotalk/index.ts +2 -0
- package/src/platforms/kakaotalk/protocol/session.ts +2 -0
- package/src/platforms/kakaotalk/types.ts +18 -0
- package/src/platforms/line/commands/auth.test.ts +141 -0
- package/src/platforms/line/commands/auth.ts +28 -19
- package/src/platforms/line/commands/chat.test.ts +110 -0
- package/src/platforms/line/commands/friend.test.ts +98 -0
- package/src/platforms/line/commands/message.test.ts +119 -0
- package/src/platforms/line/commands/profile.test.ts +85 -0
- package/src/platforms/slackbot/commands/channel.test.ts +139 -0
- package/src/platforms/slackbot/commands/message.test.ts +226 -0
- package/src/platforms/slackbot/commands/reaction.test.ts +90 -0
- package/src/platforms/slackbot/commands/user.test.ts +143 -0
- package/src/platforms/teams/commands/reaction.test.ts +45 -61
- package/src/platforms/teams/commands/reaction.ts +2 -0
- package/src/platforms/telegram/commands/chat.test.ts +125 -0
- package/src/platforms/telegram/commands/message.test.ts +92 -0
- package/src/platforms/webex/client.ts +98 -26
- package/src/platforms/webex/commands/auth.ts +4 -0
- package/src/platforms/webex/commands/member.test.ts +65 -58
- package/src/platforms/webex/commands/message.test.ts +78 -121
- package/src/platforms/webex/commands/snapshot.test.ts +59 -46
- package/src/platforms/webex/commands/space.test.ts +49 -48
- package/src/platforms/webex/encryption.ts +53 -0
- package/src/platforms/webex/ensure-auth.ts +4 -0
- package/src/platforms/webex/token-extractor.ts +107 -40
- package/src/platforms/webex/types.ts +4 -0
- package/src/platforms/webex/typings/node-jose.d.ts +27 -0
- package/src/platforms/wechatbot/cli.ts +24 -0
- package/src/platforms/wechatbot/client.test.ts +497 -0
- package/src/platforms/wechatbot/client.ts +268 -0
- package/src/platforms/wechatbot/commands/auth.test.ts +211 -0
- package/src/platforms/wechatbot/commands/auth.ts +203 -0
- package/src/platforms/wechatbot/commands/index.ts +4 -0
- package/src/platforms/wechatbot/commands/message.test.ts +155 -0
- package/src/platforms/wechatbot/commands/message.ts +104 -0
- package/src/platforms/wechatbot/commands/shared.ts +22 -0
- package/src/platforms/wechatbot/commands/template.test.ts +199 -0
- package/src/platforms/wechatbot/commands/template.ts +102 -0
- package/src/platforms/wechatbot/commands/user.test.ts +165 -0
- package/src/platforms/wechatbot/commands/user.ts +75 -0
- package/src/platforms/wechatbot/credential-manager.test.ts +255 -0
- package/src/platforms/wechatbot/credential-manager.ts +148 -0
- package/src/platforms/wechatbot/index.test.ts +49 -0
- package/src/platforms/wechatbot/index.ts +19 -0
- package/src/platforms/wechatbot/types.test.ts +223 -0
- package/src/platforms/wechatbot/types.ts +107 -0
- package/src/platforms/whatsapp/client.ts +24 -13
- package/src/platforms/whatsapp/commands/auth.test.ts +311 -0
- package/src/platforms/whatsapp/commands/auth.ts +21 -17
- package/src/platforms/whatsapp/commands/chat.test.ts +198 -0
- package/src/platforms/whatsapp/commands/message.test.ts +231 -0
- package/src/platforms/whatsapp/credential-manager.test.ts +20 -0
- package/src/platforms/whatsapp/credential-manager.ts +17 -8
- package/src/platforms/whatsappbot/commands/auth.test.ts +217 -0
- package/src/platforms/whatsappbot/commands/message.test.ts +198 -0
- package/src/platforms/whatsappbot/commands/template.test.ts +112 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
|
|
5
|
+
mock.module('@/shared/utils/error-handler', () => ({
|
|
6
|
+
handleError: (err: Error) => { throw err },
|
|
7
|
+
}))
|
|
8
|
+
|
|
9
|
+
const mockGetAccount = mock(() =>
|
|
10
|
+
Promise.resolve({
|
|
11
|
+
account_id: 'plus-12025551234',
|
|
12
|
+
phone_number: '+12025551234',
|
|
13
|
+
name: 'Test User',
|
|
14
|
+
created_at: '2024-01-01T00:00:00.000Z',
|
|
15
|
+
updated_at: '2024-01-01T00:00:00.000Z',
|
|
16
|
+
}),
|
|
17
|
+
)
|
|
18
|
+
const mockEnsureAccountPaths = mock(() =>
|
|
19
|
+
Promise.resolve({ account_dir: '/tmp/test', auth_dir: '/tmp/test/auth' }),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
mock.module('../credential-manager', () => ({
|
|
23
|
+
WhatsAppCredentialManager: class {
|
|
24
|
+
getAccount = mockGetAccount
|
|
25
|
+
ensureAccountPaths = mockEnsureAccountPaths
|
|
26
|
+
},
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
const mockGetMessages = mock(() =>
|
|
30
|
+
Promise.resolve([
|
|
31
|
+
{
|
|
32
|
+
id: 'msg-1',
|
|
33
|
+
text: 'Hello',
|
|
34
|
+
from: '12025551234@s.whatsapp.net',
|
|
35
|
+
timestamp: 1000,
|
|
36
|
+
fromMe: false,
|
|
37
|
+
},
|
|
38
|
+
]),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const mockSendMessage = mock(() =>
|
|
42
|
+
Promise.resolve({
|
|
43
|
+
id: 'msg-2',
|
|
44
|
+
text: 'Hi there',
|
|
45
|
+
from: 'me@s.whatsapp.net',
|
|
46
|
+
timestamp: 2000,
|
|
47
|
+
fromMe: true,
|
|
48
|
+
}),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const mockSendReaction = mock(() => Promise.resolve())
|
|
52
|
+
const mockConnect = mock(() => Promise.resolve())
|
|
53
|
+
const mockClose = mock(() => Promise.resolve())
|
|
54
|
+
|
|
55
|
+
mock.module('../client', () => ({
|
|
56
|
+
WhatsAppClient: class {
|
|
57
|
+
login = mock(function (this: unknown) { return Promise.resolve(this) })
|
|
58
|
+
connect = mockConnect
|
|
59
|
+
close = mockClose
|
|
60
|
+
getMessages = mockGetMessages
|
|
61
|
+
sendMessage = mockSendMessage
|
|
62
|
+
sendReaction = mockSendReaction
|
|
63
|
+
},
|
|
64
|
+
}))
|
|
65
|
+
|
|
66
|
+
import { messageCommand } from './message'
|
|
67
|
+
|
|
68
|
+
describe('message commands', () => {
|
|
69
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
70
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
mockGetAccount.mockReset()
|
|
74
|
+
mockEnsureAccountPaths.mockReset()
|
|
75
|
+
mockGetMessages.mockReset()
|
|
76
|
+
mockSendMessage.mockReset()
|
|
77
|
+
mockSendReaction.mockReset()
|
|
78
|
+
mockConnect.mockReset()
|
|
79
|
+
mockClose.mockReset()
|
|
80
|
+
|
|
81
|
+
mockGetAccount.mockImplementation(() =>
|
|
82
|
+
Promise.resolve({
|
|
83
|
+
account_id: 'plus-12025551234',
|
|
84
|
+
phone_number: '+12025551234',
|
|
85
|
+
name: 'Test User',
|
|
86
|
+
created_at: '2024-01-01T00:00:00.000Z',
|
|
87
|
+
updated_at: '2024-01-01T00:00:00.000Z',
|
|
88
|
+
}),
|
|
89
|
+
)
|
|
90
|
+
mockEnsureAccountPaths.mockImplementation(() =>
|
|
91
|
+
Promise.resolve({ account_dir: '/tmp/test', auth_dir: '/tmp/test/auth' }),
|
|
92
|
+
)
|
|
93
|
+
mockGetMessages.mockImplementation(() =>
|
|
94
|
+
Promise.resolve([
|
|
95
|
+
{
|
|
96
|
+
id: 'msg-1',
|
|
97
|
+
text: 'Hello',
|
|
98
|
+
from: '12025551234@s.whatsapp.net',
|
|
99
|
+
timestamp: 1000,
|
|
100
|
+
fromMe: false,
|
|
101
|
+
},
|
|
102
|
+
]),
|
|
103
|
+
)
|
|
104
|
+
mockSendMessage.mockImplementation(() =>
|
|
105
|
+
Promise.resolve({
|
|
106
|
+
id: 'msg-2',
|
|
107
|
+
text: 'Hi there',
|
|
108
|
+
from: 'me@s.whatsapp.net',
|
|
109
|
+
timestamp: 2000,
|
|
110
|
+
fromMe: true,
|
|
111
|
+
}),
|
|
112
|
+
)
|
|
113
|
+
mockSendReaction.mockImplementation(() => Promise.resolve())
|
|
114
|
+
mockConnect.mockImplementation(() => Promise.resolve())
|
|
115
|
+
mockClose.mockImplementation(() => Promise.resolve())
|
|
116
|
+
|
|
117
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
118
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => {
|
|
119
|
+
throw new Error(`process.exit(${_code})`)
|
|
120
|
+
})
|
|
121
|
+
processExitSpy.mockClear()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
afterEach(() => {
|
|
125
|
+
console.log = originalConsoleLog
|
|
126
|
+
processExitSpy.mockRestore()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('list', () => {
|
|
130
|
+
test('fetches messages for a chat', async () => {
|
|
131
|
+
await expect(
|
|
132
|
+
messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net'], { from: 'user' }),
|
|
133
|
+
).rejects.toThrow('process.exit(0)')
|
|
134
|
+
|
|
135
|
+
expect(mockGetMessages).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 25)
|
|
136
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
137
|
+
expect(output).toHaveLength(1)
|
|
138
|
+
expect(output[0].id).toBe('msg-1')
|
|
139
|
+
expect(output[0].text).toBe('Hello')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('respects --limit option', async () => {
|
|
143
|
+
await expect(
|
|
144
|
+
messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net', '--limit', '10'], { from: 'user' }),
|
|
145
|
+
).rejects.toThrow('process.exit(0)')
|
|
146
|
+
|
|
147
|
+
expect(mockGetMessages).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 10)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('passes account option to credential manager', async () => {
|
|
151
|
+
await expect(
|
|
152
|
+
messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net', '--account', 'my-account'], { from: 'user' }),
|
|
153
|
+
).rejects.toThrow('process.exit(0)')
|
|
154
|
+
|
|
155
|
+
expect(mockGetAccount).toHaveBeenCalledWith('my-account')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('exits with error when no account configured', async () => {
|
|
159
|
+
mockGetAccount.mockImplementation(() => Promise.resolve(null))
|
|
160
|
+
|
|
161
|
+
await expect(
|
|
162
|
+
messageCommand.parseAsync(['list', '12025551234@s.whatsapp.net'], { from: 'user' }),
|
|
163
|
+
).rejects.toThrow('process.exit(1)')
|
|
164
|
+
|
|
165
|
+
expect(processExitSpy).toHaveBeenCalledWith(1)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
describe('send', () => {
|
|
170
|
+
test('sends a message to a chat', async () => {
|
|
171
|
+
await expect(
|
|
172
|
+
messageCommand.parseAsync(['send', '12025551234@s.whatsapp.net', 'Hello world'], { from: 'user' }),
|
|
173
|
+
).rejects.toThrow('process.exit(0)')
|
|
174
|
+
|
|
175
|
+
expect(mockSendMessage).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 'Hello world')
|
|
176
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
177
|
+
expect(output.id).toBe('msg-2')
|
|
178
|
+
expect(output.text).toBe('Hi there')
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test('passes account option to credential manager', async () => {
|
|
182
|
+
await expect(
|
|
183
|
+
messageCommand.parseAsync(['send', '12025551234@s.whatsapp.net', 'Hi', '--account', 'my-account'], {
|
|
184
|
+
from: 'user',
|
|
185
|
+
}),
|
|
186
|
+
).rejects.toThrow('process.exit(0)')
|
|
187
|
+
|
|
188
|
+
expect(mockGetAccount).toHaveBeenCalledWith('my-account')
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('react', () => {
|
|
193
|
+
test('sends a reaction to a message', async () => {
|
|
194
|
+
await expect(
|
|
195
|
+
messageCommand.parseAsync(
|
|
196
|
+
['react', '12025551234@s.whatsapp.net', 'msg-1', '👍'],
|
|
197
|
+
{ from: 'user' },
|
|
198
|
+
),
|
|
199
|
+
).rejects.toThrow('process.exit(0)')
|
|
200
|
+
|
|
201
|
+
expect(mockSendReaction).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 'msg-1', '👍', undefined)
|
|
202
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
203
|
+
expect(output.success).toBe(true)
|
|
204
|
+
expect(output.chat).toBe('12025551234@s.whatsapp.net')
|
|
205
|
+
expect(output.message_id).toBe('msg-1')
|
|
206
|
+
expect(output.emoji).toBe('👍')
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
test('passes --from-me flag to sendReaction', async () => {
|
|
210
|
+
await expect(
|
|
211
|
+
messageCommand.parseAsync(
|
|
212
|
+
['react', '12025551234@s.whatsapp.net', 'msg-1', '❤️', '--from-me'],
|
|
213
|
+
{ from: 'user' },
|
|
214
|
+
),
|
|
215
|
+
).rejects.toThrow('process.exit(0)')
|
|
216
|
+
|
|
217
|
+
expect(mockSendReaction).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 'msg-1', '❤️', true)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
test('passes account option to credential manager', async () => {
|
|
221
|
+
await expect(
|
|
222
|
+
messageCommand.parseAsync(
|
|
223
|
+
['react', '12025551234@s.whatsapp.net', 'msg-1', '👍', '--account', 'my-account'],
|
|
224
|
+
{ from: 'user' },
|
|
225
|
+
),
|
|
226
|
+
).rejects.toThrow('process.exit(0)')
|
|
227
|
+
|
|
228
|
+
expect(mockGetAccount).toHaveBeenCalledWith('my-account')
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
@@ -198,6 +198,26 @@ describe('WhatsAppCredentialManager', () => {
|
|
|
198
198
|
|
|
199
199
|
expect(result).toBe(false)
|
|
200
200
|
})
|
|
201
|
+
|
|
202
|
+
test('deletes session directory even when account is not in config', async () => {
|
|
203
|
+
const manager = setup()
|
|
204
|
+
|
|
205
|
+
const paths = await manager.ensureAccountPaths('orphan-account')
|
|
206
|
+
expect(existsSync(paths.account_dir)).toBe(true)
|
|
207
|
+
|
|
208
|
+
const result = await manager.removeAccount('orphan-account')
|
|
209
|
+
|
|
210
|
+
expect(result).toBe(true)
|
|
211
|
+
expect(existsSync(paths.account_dir)).toBe(false)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
test('returns false when neither config entry nor directory exists', async () => {
|
|
215
|
+
const manager = setup()
|
|
216
|
+
|
|
217
|
+
const result = await manager.removeAccount('ghost-account')
|
|
218
|
+
|
|
219
|
+
expect(result).toBe(false)
|
|
220
|
+
})
|
|
201
221
|
})
|
|
202
222
|
|
|
203
223
|
describe('clearCredentials', () => {
|
|
@@ -91,19 +91,28 @@ export class WhatsAppCredentialManager {
|
|
|
91
91
|
const config = await this.loadConfig()
|
|
92
92
|
const account = config.accounts[accountId] ?? config.accounts[createAccountId(accountId)]
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
let removedFromConfig = false
|
|
95
|
+
|
|
96
|
+
if (account) {
|
|
97
|
+
delete config.accounts[account.account_id]
|
|
98
|
+
|
|
99
|
+
if (config.current === account.account_id) {
|
|
100
|
+
config.current = Object.keys(config.accounts)[0] ?? null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await this.saveConfig(config)
|
|
104
|
+
removedFromConfig = true
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
|
|
107
|
+
const resolvedId = account?.account_id ?? createAccountId(accountId)
|
|
108
|
+
const accountDir = this.getAccountPaths(resolvedId).account_dir
|
|
109
|
+
const dirExisted = existsSync(accountDir)
|
|
99
110
|
|
|
100
|
-
if (
|
|
101
|
-
|
|
111
|
+
if (dirExisted) {
|
|
112
|
+
await rm(accountDir, { recursive: true, force: true })
|
|
102
113
|
}
|
|
103
114
|
|
|
104
|
-
|
|
105
|
-
await rm(this.getAccountPaths(account.account_id).account_dir, { recursive: true, force: true })
|
|
106
|
-
return true
|
|
115
|
+
return removedFromConfig || dirExisted
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
async clearCredentials(): Promise<void> {
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
|
|
2
|
+
import { existsSync, rmSync } from 'node:fs'
|
|
3
|
+
import { mkdir } from 'node:fs/promises'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
|
|
7
|
+
const mockVerifyToken = mock(() =>
|
|
8
|
+
Promise.resolve({
|
|
9
|
+
verified_name: 'Test Business',
|
|
10
|
+
}),
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
mock.module('../client', () => ({
|
|
14
|
+
WhatsAppBotClient: class MockWhatsAppBotClient {
|
|
15
|
+
async login(_credentials?: { phoneNumberId: string; accessToken: string }) {
|
|
16
|
+
return this
|
|
17
|
+
}
|
|
18
|
+
verifyToken = mockVerifyToken
|
|
19
|
+
},
|
|
20
|
+
}))
|
|
21
|
+
|
|
22
|
+
import { WhatsAppBotCredentialManager } from '../credential-manager'
|
|
23
|
+
import { clearAction, listAction, removeAction, setAction, statusAction, useAction } from './auth'
|
|
24
|
+
|
|
25
|
+
describe('auth commands', () => {
|
|
26
|
+
let tempDir: string
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
tempDir = join(tmpdir(), `whatsappbot-auth-test-${Date.now()}`)
|
|
30
|
+
await mkdir(tempDir, { recursive: true })
|
|
31
|
+
mockVerifyToken.mockClear()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
if (existsSync(tempDir)) {
|
|
36
|
+
rmSync(tempDir, { recursive: true })
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('setAction', () => {
|
|
41
|
+
test('validates token and stores credentials', async () => {
|
|
42
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
43
|
+
|
|
44
|
+
const result = await setAction('12345678901', 'EAAtest-token', { _credManager: manager })
|
|
45
|
+
|
|
46
|
+
expect(result.success).toBe(true)
|
|
47
|
+
expect(result.phone_number_id).toBe('12345678901')
|
|
48
|
+
expect(result.account_name).toBe('Test Business')
|
|
49
|
+
|
|
50
|
+
const creds = await manager.getCredentials()
|
|
51
|
+
expect(creds?.phone_number_id).toBe('12345678901')
|
|
52
|
+
expect(creds?.access_token).toBe('EAAtest-token')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('returns error when client throws', async () => {
|
|
56
|
+
mockVerifyToken.mockImplementationOnce(() => Promise.reject(new Error('Invalid token')))
|
|
57
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
58
|
+
|
|
59
|
+
const result = await setAction('12345678901', 'bad-token', { _credManager: manager })
|
|
60
|
+
|
|
61
|
+
expect(result.error).toBeDefined()
|
|
62
|
+
expect(result.error).toContain('Invalid token')
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('statusAction', () => {
|
|
67
|
+
test('returns invalid status when no credentials set', async () => {
|
|
68
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
69
|
+
|
|
70
|
+
const result = await statusAction({ _credManager: manager })
|
|
71
|
+
|
|
72
|
+
expect(result.valid).toBe(false)
|
|
73
|
+
expect(result.error).toBeDefined()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('returns valid status when credentials are set', async () => {
|
|
77
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
78
|
+
await manager.setCredentials({
|
|
79
|
+
phone_number_id: '12345678901',
|
|
80
|
+
account_name: 'Test Business',
|
|
81
|
+
access_token: 'EAAtest-token',
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const result = await statusAction({ _credManager: manager })
|
|
85
|
+
|
|
86
|
+
expect(result.valid).toBe(true)
|
|
87
|
+
expect(result.phone_number_id).toBe('12345678901')
|
|
88
|
+
expect(result.account_name).toBe('Test Business')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('returns invalid status when verifyToken fails', async () => {
|
|
92
|
+
mockVerifyToken.mockImplementationOnce(() => Promise.reject(new Error('Token expired')))
|
|
93
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
94
|
+
await manager.setCredentials({
|
|
95
|
+
phone_number_id: '12345678901',
|
|
96
|
+
account_name: 'Test Business',
|
|
97
|
+
access_token: 'expired-token',
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const result = await statusAction({ _credManager: manager })
|
|
101
|
+
|
|
102
|
+
expect(result.valid).toBe(false)
|
|
103
|
+
expect(result.phone_number_id).toBe('12345678901')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('returns error for unknown account', async () => {
|
|
107
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
108
|
+
|
|
109
|
+
const result = await statusAction({ account: 'nonexistent', _credManager: manager })
|
|
110
|
+
|
|
111
|
+
expect(result.valid).toBe(false)
|
|
112
|
+
expect(result.error).toContain('nonexistent')
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('clearAction', () => {
|
|
117
|
+
test('removes all stored credentials', async () => {
|
|
118
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
119
|
+
await manager.setCredentials({
|
|
120
|
+
phone_number_id: '12345678901',
|
|
121
|
+
account_name: 'Test Business',
|
|
122
|
+
access_token: 'EAAtest-token',
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const result = await clearAction({ _credManager: manager })
|
|
126
|
+
|
|
127
|
+
expect(result.success).toBe(true)
|
|
128
|
+
expect(await manager.getCredentials()).toBeNull()
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('listAction', () => {
|
|
133
|
+
test('returns empty accounts when none set', async () => {
|
|
134
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
135
|
+
|
|
136
|
+
const result = await listAction({ _credManager: manager })
|
|
137
|
+
|
|
138
|
+
expect(result.accounts).toHaveLength(0)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test('returns all stored accounts', async () => {
|
|
142
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
143
|
+
await manager.setCredentials({
|
|
144
|
+
phone_number_id: '11111111111',
|
|
145
|
+
account_name: 'Account A',
|
|
146
|
+
access_token: 'token-a',
|
|
147
|
+
})
|
|
148
|
+
await manager.setCredentials({
|
|
149
|
+
phone_number_id: '22222222222',
|
|
150
|
+
account_name: 'Account B',
|
|
151
|
+
access_token: 'token-b',
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const result = await listAction({ _credManager: manager })
|
|
155
|
+
|
|
156
|
+
expect(result.accounts).toHaveLength(2)
|
|
157
|
+
expect(result.accounts?.find((a) => a.phone_number_id === '11111111111')?.account_name).toBe('Account A')
|
|
158
|
+
expect(result.accounts?.find((a) => a.phone_number_id === '22222222222')?.is_current).toBe(true)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
describe('useAction', () => {
|
|
163
|
+
test('switches current account', async () => {
|
|
164
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
165
|
+
await manager.setCredentials({
|
|
166
|
+
phone_number_id: '11111111111',
|
|
167
|
+
account_name: 'Account A',
|
|
168
|
+
access_token: 'token-a',
|
|
169
|
+
})
|
|
170
|
+
await manager.setCredentials({
|
|
171
|
+
phone_number_id: '22222222222',
|
|
172
|
+
account_name: 'Account B',
|
|
173
|
+
access_token: 'token-b',
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
const result = await useAction('11111111111', { _credManager: manager })
|
|
177
|
+
|
|
178
|
+
expect(result.success).toBe(true)
|
|
179
|
+
expect(result.phone_number_id).toBe('11111111111')
|
|
180
|
+
expect(result.account_name).toBe('Account A')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('returns error for unknown account', async () => {
|
|
184
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
185
|
+
|
|
186
|
+
const result = await useAction('nonexistent', { _credManager: manager })
|
|
187
|
+
|
|
188
|
+
expect(result.error).toBeDefined()
|
|
189
|
+
expect(result.error).toContain('nonexistent')
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
describe('removeAction', () => {
|
|
194
|
+
test('removes a stored account', async () => {
|
|
195
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
196
|
+
await manager.setCredentials({
|
|
197
|
+
phone_number_id: '12345678901',
|
|
198
|
+
account_name: 'Test Business',
|
|
199
|
+
access_token: 'EAAtest-token',
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const result = await removeAction('12345678901', { _credManager: manager })
|
|
203
|
+
|
|
204
|
+
expect(result.success).toBe(true)
|
|
205
|
+
expect(await manager.getCredentials('12345678901')).toBeNull()
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test('returns error for unknown account', async () => {
|
|
209
|
+
const manager = new WhatsAppBotCredentialManager(tempDir)
|
|
210
|
+
|
|
211
|
+
const result = await removeAction('nonexistent', { _credManager: manager })
|
|
212
|
+
|
|
213
|
+
expect(result.error).toBeDefined()
|
|
214
|
+
expect(result.error).toContain('nonexistent')
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
})
|