agent-messenger 1.2.0 → 1.3.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/README.md +1 -1
- package/dist/package.json +3 -2
- package/dist/src/platforms/slackbot/cli.d.ts +5 -0
- package/dist/src/platforms/slackbot/cli.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/cli.js +19 -0
- package/dist/src/platforms/slackbot/cli.js.map +1 -0
- package/dist/src/platforms/slackbot/client.d.ts +43 -0
- package/dist/src/platforms/slackbot/client.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/client.js +347 -0
- package/dist/src/platforms/slackbot/client.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/auth.d.ts +35 -0
- package/dist/src/platforms/slackbot/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/auth.js +185 -0
- package/dist/src/platforms/slackbot/commands/auth.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/channel.d.ts +3 -0
- package/dist/src/platforms/slackbot/commands/channel.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/channel.js +40 -0
- package/dist/src/platforms/slackbot/commands/channel.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/index.d.ts +6 -0
- package/dist/src/platforms/slackbot/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/index.js +6 -0
- package/dist/src/platforms/slackbot/commands/index.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/message.d.ts +3 -0
- package/dist/src/platforms/slackbot/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/message.js +135 -0
- package/dist/src/platforms/slackbot/commands/message.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/reaction.d.ts +3 -0
- package/dist/src/platforms/slackbot/commands/reaction.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/reaction.js +43 -0
- package/dist/src/platforms/slackbot/commands/reaction.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/shared.d.ts +9 -0
- package/dist/src/platforms/slackbot/commands/shared.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/shared.js +13 -0
- package/dist/src/platforms/slackbot/commands/shared.js.map +1 -0
- package/dist/src/platforms/slackbot/commands/user.d.ts +3 -0
- package/dist/src/platforms/slackbot/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/commands/user.js +40 -0
- package/dist/src/platforms/slackbot/commands/user.js.map +1 -0
- package/dist/src/platforms/slackbot/credential-manager.d.ts +18 -0
- package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/credential-manager.js +187 -0
- package/dist/src/platforms/slackbot/credential-manager.js.map +1 -0
- package/dist/src/platforms/slackbot/index.d.ts +4 -0
- package/dist/src/platforms/slackbot/index.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/index.js +4 -0
- package/dist/src/platforms/slackbot/index.js.map +1 -0
- package/dist/src/platforms/slackbot/types.d.ts +460 -0
- package/dist/src/platforms/slackbot/types.d.ts.map +1 -0
- package/dist/src/platforms/slackbot/types.js +114 -0
- package/dist/src/platforms/slackbot/types.js.map +1 -0
- package/docs/content/docs/integrations/meta.json +1 -1
- package/docs/content/docs/integrations/slackbot.mdx +204 -0
- package/docs/src/app/page.tsx +18 -1
- package/e2e/config.ts +26 -0
- package/e2e/helpers.ts +6 -1
- package/e2e/slackbot.e2e.test.ts +306 -0
- package/package.json +3 -2
- package/skills/agent-slackbot/SKILL.md +285 -0
- package/skills/agent-slackbot/references/authentication.md +253 -0
- package/skills/agent-slackbot/references/common-patterns.md +218 -0
- package/skills/agent-slackbot/templates/monitor-channel.sh +98 -0
- package/skills/agent-slackbot/templates/post-message.sh +107 -0
- package/skills/agent-slackbot/templates/workspace-summary.sh +113 -0
- package/src/platforms/slackbot/cli.ts +30 -0
- package/src/platforms/slackbot/client.test.ts +282 -0
- package/src/platforms/slackbot/client.ts +401 -0
- package/src/platforms/slackbot/commands/auth.test.ts +245 -0
- package/src/platforms/slackbot/commands/auth.ts +240 -0
- package/src/platforms/slackbot/commands/channel.ts +46 -0
- package/src/platforms/slackbot/commands/index.ts +5 -0
- package/src/platforms/slackbot/commands/message.ts +182 -0
- package/src/platforms/slackbot/commands/reaction.ts +59 -0
- package/src/platforms/slackbot/commands/shared.ts +23 -0
- package/src/platforms/slackbot/commands/user.ts +46 -0
- package/src/platforms/slackbot/credential-manager.test.ts +264 -0
- package/src/platforms/slackbot/credential-manager.ts +218 -0
- package/src/platforms/slackbot/index.ts +19 -0
- package/src/platforms/slackbot/types.test.ts +90 -0
- package/src/platforms/slackbot/types.ts +222 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterEach } from 'bun:test'
|
|
2
|
+
import { runCLI, parseJSON, generateTestId, waitForRateLimit } from './helpers'
|
|
3
|
+
import {
|
|
4
|
+
SLACKBOT_TEST_CHANNEL_ID,
|
|
5
|
+
SLACKBOT_TEST_CHANNEL,
|
|
6
|
+
validateSlackBotEnvironment,
|
|
7
|
+
} from './config'
|
|
8
|
+
import { SlackBotClient } from '../src/platforms/slackbot/client'
|
|
9
|
+
import { SlackBotCredentialManager } from '../src/platforms/slackbot/credential-manager'
|
|
10
|
+
|
|
11
|
+
let testMessages: string[] = []
|
|
12
|
+
let cleanupClient: SlackBotClient
|
|
13
|
+
|
|
14
|
+
async function getClient(): Promise<SlackBotClient> {
|
|
15
|
+
const credManager = new SlackBotCredentialManager()
|
|
16
|
+
const creds = await credManager.getCredentials()
|
|
17
|
+
if (!creds) throw new Error('No slackbot credentials')
|
|
18
|
+
return new SlackBotClient(creds.token)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function cleanupBotMessages(channel: string, timestamps: string[]) {
|
|
22
|
+
for (const ts of timestamps) {
|
|
23
|
+
try {
|
|
24
|
+
await cleanupClient.deleteMessage(channel, ts)
|
|
25
|
+
await waitForRateLimit(500)
|
|
26
|
+
} catch {
|
|
27
|
+
// best-effort cleanup
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('SlackBot E2E Tests', () => {
|
|
33
|
+
beforeAll(async () => {
|
|
34
|
+
await validateSlackBotEnvironment()
|
|
35
|
+
cleanupClient = await getClient()
|
|
36
|
+
|
|
37
|
+
// Bot must be in the channel to send messages
|
|
38
|
+
try {
|
|
39
|
+
await cleanupClient.joinChannel(SLACKBOT_TEST_CHANNEL_ID)
|
|
40
|
+
} catch {
|
|
41
|
+
// already in channel
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
afterEach(async () => {
|
|
46
|
+
if (testMessages.length > 0) {
|
|
47
|
+
await cleanupBotMessages(SLACKBOT_TEST_CHANNEL_ID, testMessages)
|
|
48
|
+
testMessages = []
|
|
49
|
+
}
|
|
50
|
+
await waitForRateLimit()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('auth', () => {
|
|
54
|
+
test('auth status returns valid bot info', async () => {
|
|
55
|
+
const result = await runCLI('slackbot', ['auth', 'status'])
|
|
56
|
+
expect(result.exitCode).toBe(0)
|
|
57
|
+
|
|
58
|
+
const data = parseJSON<{
|
|
59
|
+
valid: boolean
|
|
60
|
+
workspace_id: string
|
|
61
|
+
workspace_name: string
|
|
62
|
+
bot_id: string
|
|
63
|
+
}>(result.stdout)
|
|
64
|
+
expect(data).not.toBeNull()
|
|
65
|
+
expect(data?.valid).toBe(true)
|
|
66
|
+
expect(data?.workspace_id).toBeTruthy()
|
|
67
|
+
expect(data?.workspace_name).toBeTruthy()
|
|
68
|
+
expect(data?.bot_id).toBeTruthy()
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('message', () => {
|
|
73
|
+
test('message send creates message and returns ts', async () => {
|
|
74
|
+
const testId = generateTestId()
|
|
75
|
+
const result = await runCLI('slackbot', [
|
|
76
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Bot test ${testId}`,
|
|
77
|
+
])
|
|
78
|
+
expect(result.exitCode).toBe(0)
|
|
79
|
+
|
|
80
|
+
const data = parseJSON<{ ts: string; channel: string }>(result.stdout)
|
|
81
|
+
expect(data?.ts).toBeTruthy()
|
|
82
|
+
expect(data?.channel).toBe(SLACKBOT_TEST_CHANNEL_ID)
|
|
83
|
+
|
|
84
|
+
if (data?.ts) testMessages.push(data.ts)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('message list returns messages array', async () => {
|
|
88
|
+
const result = await runCLI('slackbot', [
|
|
89
|
+
'message', 'list', SLACKBOT_TEST_CHANNEL_ID, '--limit', '5',
|
|
90
|
+
])
|
|
91
|
+
expect(result.exitCode).toBe(0)
|
|
92
|
+
|
|
93
|
+
const data = parseJSON<Array<{ ts: string }>>(result.stdout)
|
|
94
|
+
expect(Array.isArray(data)).toBe(true)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('message get retrieves specific message', async () => {
|
|
98
|
+
const testId = generateTestId()
|
|
99
|
+
const sendResult = await runCLI('slackbot', [
|
|
100
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Get test ${testId}`,
|
|
101
|
+
])
|
|
102
|
+
const sent = parseJSON<{ ts: string }>(sendResult.stdout)
|
|
103
|
+
expect(sent?.ts).toBeTruthy()
|
|
104
|
+
if (sent?.ts) testMessages.push(sent.ts)
|
|
105
|
+
|
|
106
|
+
await waitForRateLimit()
|
|
107
|
+
|
|
108
|
+
const result = await runCLI('slackbot', [
|
|
109
|
+
'message', 'get', SLACKBOT_TEST_CHANNEL_ID, sent!.ts,
|
|
110
|
+
])
|
|
111
|
+
expect(result.exitCode).toBe(0)
|
|
112
|
+
|
|
113
|
+
const data = parseJSON<{ text: string; ts: string }>(result.stdout)
|
|
114
|
+
expect(data?.text).toContain(testId)
|
|
115
|
+
expect(data?.ts).toBe(sent!.ts)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('message update modifies message', async () => {
|
|
119
|
+
const testId = generateTestId()
|
|
120
|
+
const sendResult = await runCLI('slackbot', [
|
|
121
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Original ${testId}`,
|
|
122
|
+
])
|
|
123
|
+
const sent = parseJSON<{ ts: string }>(sendResult.stdout)
|
|
124
|
+
expect(sent?.ts).toBeTruthy()
|
|
125
|
+
if (sent?.ts) testMessages.push(sent.ts)
|
|
126
|
+
|
|
127
|
+
await waitForRateLimit()
|
|
128
|
+
|
|
129
|
+
const result = await runCLI('slackbot', [
|
|
130
|
+
'message', 'update', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, `Updated ${testId}`,
|
|
131
|
+
])
|
|
132
|
+
expect(result.exitCode).toBe(0)
|
|
133
|
+
|
|
134
|
+
await waitForRateLimit()
|
|
135
|
+
|
|
136
|
+
const getResult = await runCLI('slackbot', [
|
|
137
|
+
'message', 'get', SLACKBOT_TEST_CHANNEL_ID, sent!.ts,
|
|
138
|
+
])
|
|
139
|
+
const data = parseJSON<{ text: string }>(getResult.stdout)
|
|
140
|
+
expect(data?.text).toContain('Updated')
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('message delete removes message', async () => {
|
|
144
|
+
const testId = generateTestId()
|
|
145
|
+
const sendResult = await runCLI('slackbot', [
|
|
146
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Delete me ${testId}`,
|
|
147
|
+
])
|
|
148
|
+
const sent = parseJSON<{ ts: string }>(sendResult.stdout)
|
|
149
|
+
expect(sent?.ts).toBeTruthy()
|
|
150
|
+
|
|
151
|
+
await waitForRateLimit()
|
|
152
|
+
|
|
153
|
+
const result = await runCLI('slackbot', [
|
|
154
|
+
'message', 'delete', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, '--force',
|
|
155
|
+
])
|
|
156
|
+
expect(result.exitCode).toBe(0)
|
|
157
|
+
|
|
158
|
+
const data = parseJSON<{ deleted: string }>(result.stdout)
|
|
159
|
+
expect(data?.deleted).toBe(sent!.ts)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('message send with --thread creates reply', async () => {
|
|
163
|
+
const testId = generateTestId()
|
|
164
|
+
const sendResult = await runCLI('slackbot', [
|
|
165
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Parent ${testId}`,
|
|
166
|
+
])
|
|
167
|
+
const parent = parseJSON<{ ts: string }>(sendResult.stdout)
|
|
168
|
+
expect(parent?.ts).toBeTruthy()
|
|
169
|
+
if (parent?.ts) testMessages.push(parent.ts)
|
|
170
|
+
|
|
171
|
+
await waitForRateLimit()
|
|
172
|
+
|
|
173
|
+
const replyResult = await runCLI('slackbot', [
|
|
174
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Reply ${testId}`,
|
|
175
|
+
'--thread', parent!.ts,
|
|
176
|
+
])
|
|
177
|
+
expect(replyResult.exitCode).toBe(0)
|
|
178
|
+
|
|
179
|
+
const reply = parseJSON<{ ts: string; thread_ts: string }>(replyResult.stdout)
|
|
180
|
+
expect(reply?.thread_ts).toBe(parent!.ts)
|
|
181
|
+
|
|
182
|
+
if (reply?.ts) testMessages.push(reply.ts)
|
|
183
|
+
}, 30000)
|
|
184
|
+
|
|
185
|
+
test('message replies gets thread replies', async () => {
|
|
186
|
+
const testId = generateTestId()
|
|
187
|
+
const sendResult = await runCLI('slackbot', [
|
|
188
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Thread parent ${testId}`,
|
|
189
|
+
])
|
|
190
|
+
const parent = parseJSON<{ ts: string }>(sendResult.stdout)
|
|
191
|
+
expect(parent?.ts).toBeTruthy()
|
|
192
|
+
if (parent?.ts) testMessages.push(parent.ts)
|
|
193
|
+
|
|
194
|
+
await waitForRateLimit()
|
|
195
|
+
|
|
196
|
+
const replyResult = await runCLI('slackbot', [
|
|
197
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Thread reply ${testId}`,
|
|
198
|
+
'--thread', parent!.ts,
|
|
199
|
+
])
|
|
200
|
+
expect(replyResult.exitCode).toBe(0)
|
|
201
|
+
const reply = parseJSON<{ ts: string }>(replyResult.stdout)
|
|
202
|
+
if (reply?.ts) testMessages.push(reply.ts)
|
|
203
|
+
|
|
204
|
+
await waitForRateLimit()
|
|
205
|
+
|
|
206
|
+
const result = await runCLI('slackbot', [
|
|
207
|
+
'message', 'replies', SLACKBOT_TEST_CHANNEL_ID, parent!.ts,
|
|
208
|
+
])
|
|
209
|
+
expect(result.exitCode).toBe(0)
|
|
210
|
+
|
|
211
|
+
const data = parseJSON<Array<{ ts: string }>>(result.stdout)
|
|
212
|
+
expect(Array.isArray(data)).toBe(true)
|
|
213
|
+
expect(data!.length).toBeGreaterThanOrEqual(2)
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
describe('channel', () => {
|
|
218
|
+
test('channel list returns channels array', async () => {
|
|
219
|
+
const result = await runCLI('slackbot', ['channel', 'list', '--limit', '10'])
|
|
220
|
+
expect(result.exitCode).toBe(0)
|
|
221
|
+
|
|
222
|
+
const data = parseJSON<Array<{ id: string; name: string }>>(result.stdout)
|
|
223
|
+
expect(Array.isArray(data)).toBe(true)
|
|
224
|
+
expect(data!.length).toBeGreaterThan(0)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('channel info returns channel details', async () => {
|
|
228
|
+
const result = await runCLI('slackbot', ['channel', 'info', SLACKBOT_TEST_CHANNEL_ID])
|
|
229
|
+
expect(result.exitCode).toBe(0)
|
|
230
|
+
|
|
231
|
+
const data = parseJSON<{ id: string; name: string }>(result.stdout)
|
|
232
|
+
expect(data?.id).toBe(SLACKBOT_TEST_CHANNEL_ID)
|
|
233
|
+
expect(data?.name).toBe(SLACKBOT_TEST_CHANNEL)
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
describe('user', () => {
|
|
238
|
+
test('user list returns users array', async () => {
|
|
239
|
+
const result = await runCLI('slackbot', ['user', 'list', '--limit', '10'])
|
|
240
|
+
expect(result.exitCode).toBe(0)
|
|
241
|
+
|
|
242
|
+
const data = parseJSON<Array<{ id: string; name: string }>>(result.stdout)
|
|
243
|
+
expect(Array.isArray(data)).toBe(true)
|
|
244
|
+
expect(data!.length).toBeGreaterThan(0)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
test('user info returns user details', async () => {
|
|
248
|
+
// given: get the bot's own user_id via auth status
|
|
249
|
+
const statusResult = await runCLI('slackbot', ['auth', 'status'])
|
|
250
|
+
const status = parseJSON<{ bot_id: string; user: string }>(statusResult.stdout)
|
|
251
|
+
expect(status?.user).toBeTruthy()
|
|
252
|
+
|
|
253
|
+
await waitForRateLimit()
|
|
254
|
+
|
|
255
|
+
// when: list users and find the bot user
|
|
256
|
+
const listResult = await runCLI('slackbot', ['user', 'list', '--limit', '50'])
|
|
257
|
+
const users = parseJSON<Array<{ id: string; name: string }>>(listResult.stdout)
|
|
258
|
+
const botUser = users?.find((u) => u.name === status!.user)
|
|
259
|
+
expect(botUser).toBeTruthy()
|
|
260
|
+
|
|
261
|
+
await waitForRateLimit()
|
|
262
|
+
|
|
263
|
+
// then: user info returns matching data
|
|
264
|
+
const result = await runCLI('slackbot', ['user', 'info', botUser!.id])
|
|
265
|
+
expect(result.exitCode).toBe(0)
|
|
266
|
+
|
|
267
|
+
const data = parseJSON<{ id: string; name: string }>(result.stdout)
|
|
268
|
+
expect(data?.id).toBe(botUser!.id)
|
|
269
|
+
})
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
describe('reaction', () => {
|
|
273
|
+
test('reaction add and remove lifecycle', async () => {
|
|
274
|
+
// given: a message to react to
|
|
275
|
+
const testId = generateTestId()
|
|
276
|
+
const sendResult = await runCLI('slackbot', [
|
|
277
|
+
'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Reaction test ${testId}`,
|
|
278
|
+
])
|
|
279
|
+
const sent = parseJSON<{ ts: string }>(sendResult.stdout)
|
|
280
|
+
expect(sent?.ts).toBeTruthy()
|
|
281
|
+
if (sent?.ts) testMessages.push(sent.ts)
|
|
282
|
+
|
|
283
|
+
await waitForRateLimit(2000)
|
|
284
|
+
|
|
285
|
+
// when: add reaction
|
|
286
|
+
const addResult = await runCLI('slackbot', [
|
|
287
|
+
'reaction', 'add', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, 'thumbsup',
|
|
288
|
+
])
|
|
289
|
+
expect(addResult.exitCode).toBe(0)
|
|
290
|
+
|
|
291
|
+
const addData = parseJSON<{ success: boolean }>(addResult.stdout)
|
|
292
|
+
expect(addData?.success).toBe(true)
|
|
293
|
+
|
|
294
|
+
await waitForRateLimit(2000)
|
|
295
|
+
|
|
296
|
+
// then: remove reaction
|
|
297
|
+
const removeResult = await runCLI('slackbot', [
|
|
298
|
+
'reaction', 'remove', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, 'thumbsup',
|
|
299
|
+
])
|
|
300
|
+
expect(removeResult.exitCode).toBe(0)
|
|
301
|
+
|
|
302
|
+
const removeData = parseJSON<{ success: boolean }>(removeResult.stdout)
|
|
303
|
+
expect(removeData?.success).toBe(true)
|
|
304
|
+
}, 15000)
|
|
305
|
+
})
|
|
306
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "dist/cli.js",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"amsg": "./src/cli.ts",
|
|
11
11
|
"agent-slack": "./src/platforms/slack/cli.ts",
|
|
12
12
|
"agent-discord": "./src/platforms/discord/cli.ts",
|
|
13
|
-
"agent-teams": "./src/platforms/teams/cli.ts"
|
|
13
|
+
"agent-teams": "./src/platforms/teams/cli.ts",
|
|
14
|
+
"agent-slackbot": "./src/platforms/slackbot/cli.ts"
|
|
14
15
|
},
|
|
15
16
|
"scripts": {
|
|
16
17
|
"build": "tsc",
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-slackbot
|
|
3
|
+
description: Interact with Slack workspaces using bot tokens - send messages, read channels, manage reactions
|
|
4
|
+
allowed-tools: Bash(agent-slackbot:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Agent SlackBot
|
|
8
|
+
|
|
9
|
+
A TypeScript CLI tool that enables AI agents and humans to interact with Slack workspaces using bot tokens (xoxb-). Unlike agent-slack which extracts user tokens from the desktop app, agent-slackbot uses standard Slack Bot tokens for server-side and CI/CD integrations.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Set your bot token
|
|
15
|
+
agent-slackbot auth set xoxb-your-bot-token
|
|
16
|
+
|
|
17
|
+
# Or set with a custom bot identifier for multi-bot setups
|
|
18
|
+
agent-slackbot auth set xoxb-your-bot-token --bot deploy --name "Deploy Bot"
|
|
19
|
+
|
|
20
|
+
# Verify authentication
|
|
21
|
+
agent-slackbot auth status
|
|
22
|
+
|
|
23
|
+
# Send a message
|
|
24
|
+
agent-slackbot message send C0ACZKTDDC0 "Hello from bot!"
|
|
25
|
+
|
|
26
|
+
# List channels
|
|
27
|
+
agent-slackbot channel list
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Authentication
|
|
31
|
+
|
|
32
|
+
### Bot Token Setup
|
|
33
|
+
|
|
34
|
+
agent-slackbot uses Slack Bot tokens (xoxb-) which you get from the Slack App configuration:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Set bot token (validates against Slack API before saving)
|
|
38
|
+
agent-slackbot auth set xoxb-your-bot-token
|
|
39
|
+
|
|
40
|
+
# Set with a custom bot identifier
|
|
41
|
+
agent-slackbot auth set xoxb-your-bot-token --bot deploy --name "Deploy Bot"
|
|
42
|
+
|
|
43
|
+
# Check auth status
|
|
44
|
+
agent-slackbot auth status
|
|
45
|
+
|
|
46
|
+
# Clear stored credentials
|
|
47
|
+
agent-slackbot auth clear
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Multi-Bot Management
|
|
51
|
+
|
|
52
|
+
Store multiple bot tokens and switch between them:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Add multiple bots
|
|
56
|
+
agent-slackbot auth set xoxb-deploy-token --bot deploy --name "Deploy Bot"
|
|
57
|
+
agent-slackbot auth set xoxb-alert-token --bot alert --name "Alert Bot"
|
|
58
|
+
|
|
59
|
+
# List all stored bots
|
|
60
|
+
agent-slackbot auth list
|
|
61
|
+
|
|
62
|
+
# Switch active bot
|
|
63
|
+
agent-slackbot auth use deploy
|
|
64
|
+
|
|
65
|
+
# Use a specific bot for one command (without switching)
|
|
66
|
+
agent-slackbot message send C0ACZKTDDC0 "Alert!" --bot alert
|
|
67
|
+
|
|
68
|
+
# Remove a stored bot
|
|
69
|
+
agent-slackbot auth remove deploy
|
|
70
|
+
|
|
71
|
+
# Disambiguate bots with same ID across workspaces
|
|
72
|
+
agent-slackbot auth use T123456/deploy
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The `--bot <id>` flag is available on all commands to override the active bot for a single invocation.
|
|
76
|
+
|
|
77
|
+
### Getting a Bot Token
|
|
78
|
+
|
|
79
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps)
|
|
80
|
+
2. Create New App (or select existing)
|
|
81
|
+
3. Go to **OAuth & Permissions**
|
|
82
|
+
4. Add required bot token scopes (see below)
|
|
83
|
+
5. Install app to workspace
|
|
84
|
+
6. Copy the **Bot User OAuth Token** (starts with `xoxb-`)
|
|
85
|
+
|
|
86
|
+
### Required Bot Token Scopes
|
|
87
|
+
|
|
88
|
+
| Scope | Used For |
|
|
89
|
+
|-------|----------|
|
|
90
|
+
| `chat:write` | Sending messages |
|
|
91
|
+
| `channels:history` | Reading public channel messages |
|
|
92
|
+
| `channels:read` | Listing public channels |
|
|
93
|
+
| `channels:join` | Joining public channels |
|
|
94
|
+
| `groups:history` | Reading private channel messages |
|
|
95
|
+
| `groups:read` | Listing private channels |
|
|
96
|
+
| `users:read` | Listing users |
|
|
97
|
+
| `users:read.email` | Reading user email addresses |
|
|
98
|
+
| `reactions:write` | Adding/removing reactions |
|
|
99
|
+
| `reactions:read` | Listing reactions |
|
|
100
|
+
|
|
101
|
+
### Environment Variables (CI/CD)
|
|
102
|
+
|
|
103
|
+
For CI/CD pipelines, set these environment variables instead of using `auth set`:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
export E2E_SLACKBOT_TOKEN=xoxb-your-bot-token
|
|
107
|
+
export E2E_SLACKBOT_WORKSPACE_ID=T123456
|
|
108
|
+
export E2E_SLACKBOT_WORKSPACE_NAME="My Workspace"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Commands
|
|
112
|
+
|
|
113
|
+
### Message Commands
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Send a message
|
|
117
|
+
agent-slackbot message send <channel> <text>
|
|
118
|
+
agent-slackbot message send C0ACZKTDDC0 "Hello world"
|
|
119
|
+
|
|
120
|
+
# Send a threaded reply
|
|
121
|
+
agent-slackbot message send C0ACZKTDDC0 "Reply" --thread <ts>
|
|
122
|
+
|
|
123
|
+
# List messages
|
|
124
|
+
agent-slackbot message list <channel>
|
|
125
|
+
agent-slackbot message list C0ACZKTDDC0 --limit 50
|
|
126
|
+
|
|
127
|
+
# Get a single message by timestamp
|
|
128
|
+
agent-slackbot message get <channel> <ts>
|
|
129
|
+
|
|
130
|
+
# Get thread replies (includes parent message)
|
|
131
|
+
agent-slackbot message replies <channel> <thread_ts>
|
|
132
|
+
agent-slackbot message replies C0ACZKTDDC0 1234567890.123456 --limit 50
|
|
133
|
+
|
|
134
|
+
# Update a message (bot's own messages only)
|
|
135
|
+
agent-slackbot message update <channel> <ts> <new-text>
|
|
136
|
+
|
|
137
|
+
# Delete a message (bot's own messages only)
|
|
138
|
+
agent-slackbot message delete <channel> <ts> --force
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Channel Commands
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# List channels the bot can see
|
|
145
|
+
agent-slackbot channel list
|
|
146
|
+
agent-slackbot channel list --limit 50
|
|
147
|
+
|
|
148
|
+
# Get channel info
|
|
149
|
+
agent-slackbot channel info <channel>
|
|
150
|
+
agent-slackbot channel info C0ACZKTDDC0
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### User Commands
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# List users
|
|
157
|
+
agent-slackbot user list
|
|
158
|
+
agent-slackbot user list --limit 50
|
|
159
|
+
|
|
160
|
+
# Get user info
|
|
161
|
+
agent-slackbot user info <user-id>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Reaction Commands
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Add reaction
|
|
168
|
+
agent-slackbot reaction add <channel> <ts> <emoji>
|
|
169
|
+
agent-slackbot reaction add C0ACZKTDDC0 1234567890.123456 thumbsup
|
|
170
|
+
|
|
171
|
+
# Remove reaction
|
|
172
|
+
agent-slackbot reaction remove <channel> <ts> <emoji>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Output Format
|
|
176
|
+
|
|
177
|
+
### JSON (Default)
|
|
178
|
+
|
|
179
|
+
All commands output JSON by default for AI consumption:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"ts": "1234567890.123456",
|
|
184
|
+
"channel": "C0ACZKTDDC0",
|
|
185
|
+
"text": "Hello world"
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Pretty (Human-Readable)
|
|
190
|
+
|
|
191
|
+
Use `--pretty` flag for formatted output:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
agent-slackbot channel list --pretty
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Common Patterns
|
|
198
|
+
|
|
199
|
+
See `references/common-patterns.md` for typical AI agent workflows.
|
|
200
|
+
|
|
201
|
+
## Templates
|
|
202
|
+
|
|
203
|
+
See `templates/` directory for runnable examples:
|
|
204
|
+
- `post-message.sh` - Send messages with error handling
|
|
205
|
+
- `monitor-channel.sh` - Monitor channel for new messages
|
|
206
|
+
- `workspace-summary.sh` - Generate workspace summary
|
|
207
|
+
|
|
208
|
+
## Error Handling
|
|
209
|
+
|
|
210
|
+
All commands return consistent error format:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"error": "No credentials. Run \"auth set\" first."
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Common errors:
|
|
219
|
+
- `missing_token`: No credentials configured
|
|
220
|
+
- `invalid_token_type`: Token is not a bot token (must start with xoxb-)
|
|
221
|
+
- `not_in_channel`: Bot needs to join the channel first
|
|
222
|
+
- `slack_webapi_rate_limited_error`: Hit rate limit (auto-retries with backoff)
|
|
223
|
+
|
|
224
|
+
## Configuration
|
|
225
|
+
|
|
226
|
+
Credentials stored in: `~/.config/agent-messenger/slackbot-credentials.json`
|
|
227
|
+
|
|
228
|
+
Format:
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"current": {
|
|
232
|
+
"workspace_id": "T123456",
|
|
233
|
+
"bot_id": "deploy"
|
|
234
|
+
},
|
|
235
|
+
"workspaces": {
|
|
236
|
+
"T123456": {
|
|
237
|
+
"workspace_id": "T123456",
|
|
238
|
+
"workspace_name": "My Workspace",
|
|
239
|
+
"bots": {
|
|
240
|
+
"deploy": {
|
|
241
|
+
"bot_id": "deploy",
|
|
242
|
+
"bot_name": "Deploy Bot",
|
|
243
|
+
"token": "xoxb-..."
|
|
244
|
+
},
|
|
245
|
+
"alert": {
|
|
246
|
+
"bot_id": "alert",
|
|
247
|
+
"bot_name": "Alert Bot",
|
|
248
|
+
"token": "xoxb-..."
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Security**: File permissions set to 0600 (owner read/write only)
|
|
257
|
+
|
|
258
|
+
## Key Differences from agent-slack
|
|
259
|
+
|
|
260
|
+
| Feature | agent-slack | agent-slackbot |
|
|
261
|
+
|---------|------------|----------------|
|
|
262
|
+
| Token type | User token (xoxc-) | Bot token (xoxb-) |
|
|
263
|
+
| Token source | Auto-extracted from desktop app | Manual from Slack App config |
|
|
264
|
+
| Message search | Yes | No (requires user token) |
|
|
265
|
+
| File operations | Yes | No |
|
|
266
|
+
| Snapshot | Yes | No |
|
|
267
|
+
| Edit/delete messages | Any message | Bot's own messages only |
|
|
268
|
+
| Workspace management | Multi-workspace | Multi-bot, multi-workspace |
|
|
269
|
+
| CI/CD friendly | Requires desktop app | Yes (just set token) |
|
|
270
|
+
|
|
271
|
+
## Limitations
|
|
272
|
+
|
|
273
|
+
- No real-time events / Socket Mode
|
|
274
|
+
- No message search (requires user token scope)
|
|
275
|
+
- No file upload/download
|
|
276
|
+
- No workspace snapshot
|
|
277
|
+
- Bot can only edit/delete its own messages
|
|
278
|
+
- Bot must be invited to private channels
|
|
279
|
+
- No scheduled messages
|
|
280
|
+
- Plain text messages only (no blocks/formatting)
|
|
281
|
+
|
|
282
|
+
## References
|
|
283
|
+
|
|
284
|
+
- [Authentication Guide](references/authentication.md)
|
|
285
|
+
- [Common Patterns](references/common-patterns.md)
|