@zhin.js/adapter-slack 1.0.78 → 1.1.2

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.
Files changed (108) hide show
  1. package/CHANGELOG.md +731 -2
  2. package/PERMITS.md +24 -0
  3. package/README.md +111 -158
  4. package/adapters/slack/index.js +40 -0
  5. package/adapters/slack/index.ts +45 -0
  6. package/agents/slack/agent.json +20 -0
  7. package/agents/slack/boundaries.md +3 -0
  8. package/agents/slack/conventions.md +3 -0
  9. package/agents/slack/skills/slack-channels/SKILL.md +24 -0
  10. package/agents/slack/skills/slack-channels/tools/archive_channel/index.js +19 -0
  11. package/agents/slack/skills/slack-channels/tools/archive_channel/index.ts +21 -0
  12. package/agents/slack/skills/slack-channels/tools/invite_to_channel/index.js +20 -0
  13. package/agents/slack/skills/slack-channels/tools/invite_to_channel/index.ts +23 -0
  14. package/agents/slack/skills/slack-channels/tools/set_purpose/index.js +20 -0
  15. package/agents/slack/skills/slack-channels/tools/set_purpose/index.ts +23 -0
  16. package/agents/slack/skills/slack-channels/tools/set_topic/index.js +20 -0
  17. package/agents/slack/skills/slack-channels/tools/set_topic/index.ts +23 -0
  18. package/agents/slack/skills/slack-channels/tools/unarchive/index.js +19 -0
  19. package/agents/slack/skills/slack-channels/tools/unarchive/index.ts +21 -0
  20. package/agents/slack/skills/slack-channels/tools/user_info/index.js +27 -0
  21. package/agents/slack/skills/slack-channels/tools/user_info/index.ts +29 -0
  22. package/agents/slack/skills/slack-messages/SKILL.md +22 -0
  23. package/agents/slack/skills/slack-messages/tools/add_reaction/index.js +27 -0
  24. package/agents/slack/skills/slack-messages/tools/add_reaction/index.ts +29 -0
  25. package/agents/slack/skills/slack-messages/tools/edit_message/index.js +21 -0
  26. package/agents/slack/skills/slack-messages/tools/edit_message/index.ts +25 -0
  27. package/agents/slack/skills/slack-messages/tools/pin_message/index.js +20 -0
  28. package/agents/slack/skills/slack-messages/tools/pin_message/index.ts +23 -0
  29. package/agents/slack/skills/slack-messages/tools/remove_reaction/index.js +30 -0
  30. package/agents/slack/skills/slack-messages/tools/remove_reaction/index.ts +32 -0
  31. package/agents/slack/skills/slack-messages/tools/unpin_message/index.js +20 -0
  32. package/agents/slack/skills/slack-messages/tools/unpin_message/index.ts +23 -0
  33. package/agents/slack/system.md +3 -0
  34. package/commands/slack/endpoint/add/[id]/index.js +3 -0
  35. package/commands/slack/endpoint/add/[id]/index.ts +3 -0
  36. package/commands/slack/endpoint/definition.js +19 -0
  37. package/commands/slack/endpoint/definition.ts +19 -0
  38. package/commands/slack/endpoint/list/index.js +3 -0
  39. package/commands/slack/endpoint/list/index.ts +3 -0
  40. package/commands/slack/endpoint/remove/[id]/index.js +3 -0
  41. package/commands/slack/endpoint/remove/[id]/index.ts +3 -0
  42. package/lib/client.d.ts +25 -0
  43. package/lib/client.js +2 -0
  44. package/lib/endpoint.d.ts +153 -0
  45. package/lib/endpoint.js +425 -0
  46. package/lib/index.d.ts +10 -10
  47. package/lib/index.js +10 -242
  48. package/lib/markdown-to-mrkdwn.d.ts +9 -0
  49. package/lib/markdown-to-mrkdwn.js +121 -0
  50. package/lib/mrkdwn-to-markdown.d.ts +4 -0
  51. package/lib/mrkdwn-to-markdown.js +30 -0
  52. package/lib/platform-permit.d.ts +15 -0
  53. package/lib/platform-permit.js +43 -0
  54. package/lib/protocol.d.ts +144 -0
  55. package/lib/protocol.js +258 -0
  56. package/lib/side-event-dispatch.d.ts +4 -0
  57. package/lib/side-event-dispatch.js +45 -0
  58. package/lib/slack-inbound-filter.d.ts +12 -0
  59. package/lib/slack-inbound-filter.js +46 -0
  60. package/lib/slack-message-ref.d.ts +7 -0
  61. package/lib/slack-message-ref.js +17 -0
  62. package/lib/slack-outbound.d.ts +24 -0
  63. package/lib/slack-outbound.js +111 -0
  64. package/lib/slack-reaction.d.ts +1 -0
  65. package/lib/slack-reaction.js +26 -0
  66. package/lib/slack-response-url.d.ts +5 -0
  67. package/lib/slack-response-url.js +16 -0
  68. package/lib/slack-runtime-state.d.ts +1 -0
  69. package/lib/slack-runtime-state.js +6 -0
  70. package/lib/webhook.d.ts +14 -0
  71. package/lib/webhook.js +72 -0
  72. package/package.json +80 -16
  73. package/plugin.js +19 -0
  74. package/schema.json +108 -0
  75. package/src/client.ts +30 -0
  76. package/src/endpoint.ts +526 -0
  77. package/src/index.ts +59 -265
  78. package/src/markdown-to-mrkdwn.ts +117 -0
  79. package/src/mrkdwn-to-markdown.ts +29 -0
  80. package/src/platform-permit.ts +59 -0
  81. package/src/protocol.ts +406 -0
  82. package/src/side-event-dispatch.ts +54 -0
  83. package/src/slack-inbound-filter.ts +60 -0
  84. package/src/slack-message-ref.ts +18 -0
  85. package/src/slack-outbound.ts +170 -0
  86. package/src/slack-reaction.ts +23 -0
  87. package/src/slack-response-url.ts +26 -0
  88. package/src/slack-runtime-state.ts +7 -0
  89. package/src/webhook.ts +106 -0
  90. package/lib/adapter.d.ts +0 -16
  91. package/lib/adapter.d.ts.map +0 -1
  92. package/lib/adapter.js +0 -43
  93. package/lib/adapter.js.map +0 -1
  94. package/lib/bot.d.ts +0 -106
  95. package/lib/bot.d.ts.map +0 -1
  96. package/lib/bot.js +0 -628
  97. package/lib/bot.js.map +0 -1
  98. package/lib/index.d.ts.map +0 -1
  99. package/lib/index.js.map +0 -1
  100. package/lib/types.d.ts +0 -17
  101. package/lib/types.d.ts.map +0 -1
  102. package/lib/types.js +0 -2
  103. package/lib/types.js.map +0 -1
  104. package/plugin.yml +0 -3
  105. package/skills/slack/SKILL.md +0 -91
  106. package/src/adapter.ts +0 -52
  107. package/src/bot.ts +0 -688
  108. package/src/types.ts +0 -18
package/src/bot.ts DELETED
@@ -1,688 +0,0 @@
1
- /**
2
- * Slack Bot 实现
3
- */
4
- import { App as SlackApp, LogLevel } from "@slack/bolt";
5
- import { WebClient, type ChatPostMessageArguments } from "@slack/web-api";
6
- import { formatCompact, Bot, Message, MessageSegment, segment, SendContent, SendOptions } from 'zhin.js';
7
- import type { SlackBotConfig, SlackMessageEvent } from "./types.js";
8
- import type { SlackAdapter } from "./adapter.js";
9
-
10
-
11
- export class SlackBot implements Bot<SlackBotConfig, SlackMessageEvent> {
12
- $connected: boolean;
13
- /** 延迟到 `$connect`,避免子类 mock `$connect` 时仍在构造函数里创建 Bolt/WebClient(会在后台触发 Slack API) */
14
- private app?: SlackApp;
15
- private client?: WebClient;
16
-
17
- get logger() {
18
- return this.adapter.plugin.logger;
19
- }
20
-
21
- get $id() {
22
- return this.$config.name;
23
- }
24
-
25
- constructor(public adapter: SlackAdapter, public $config: SlackBotConfig) {
26
- this.$connected = false;
27
- }
28
-
29
- #ensureSlackRuntime(): void {
30
- if (this.app && this.client) return;
31
- const $config = this.$config;
32
- if ($config.socketMode && $config.appToken) {
33
- this.app = new SlackApp({
34
- token: $config.token,
35
- signingSecret: $config.signingSecret,
36
- appToken: $config.appToken,
37
- socketMode: true,
38
- logLevel: $config.logLevel || LogLevel.INFO,
39
- });
40
- } else {
41
- this.app = new SlackApp({
42
- token: $config.token,
43
- signingSecret: $config.signingSecret,
44
- socketMode: false,
45
- logLevel: $config.logLevel || LogLevel.INFO,
46
- });
47
- }
48
- this.client = new WebClient($config.token);
49
- }
50
-
51
- async $connect(): Promise<void> {
52
- this.#ensureSlackRuntime();
53
- try {
54
- // Set up message event handler
55
- this.app!.message(async ({ message, say }) => {
56
- await this.handleSlackMessage(message as SlackMessageEvent);
57
- });
58
-
59
- // Set up app mention handler
60
- this.app!.event("app_mention", async ({ event, say }) => {
61
- await this.handleSlackMessage(event as any);
62
- });
63
-
64
- // Start the app
65
- const port = this.$config.port || 3000;
66
- if (this.$config.socketMode) {
67
- await this.app!.start();
68
- } else {
69
- await this.app!.start(port);
70
- }
71
-
72
- this.$connected = true;
73
-
74
- // Get bot info
75
- const authTest = await this.client!.auth.test();
76
- this.logger.info(formatCompact({ bot: this.$config.name, user: authTest.user }));
77
-
78
- if (!this.$config.socketMode) {
79
- this.logger.info(formatCompact( { op: "listen", port }));
80
- }
81
- } catch (error) {
82
- this.logger.error("Failed to connect Slack bot:", error);
83
- this.$connected = false;
84
- throw error;
85
- }
86
- }
87
-
88
- async $disconnect(): Promise<void> {
89
- if (!this.app) {
90
- this.$connected = false;
91
- return;
92
- }
93
- try {
94
- await this.app!.stop();
95
- this.$connected = false;
96
- this.logger.info(formatCompact( { op: "disconnect", bot: this.$config.name }));
97
- } catch (error) {
98
- this.logger.error("Error disconnecting Slack bot:", error);
99
- throw error;
100
- }
101
- }
102
-
103
- private async handleSlackMessage(msg: SlackMessageEvent): Promise<void> {
104
- // Ignore bot messages and message changes
105
- if ("subtype" in msg && (msg.subtype === "bot_message" || msg.subtype === "message_changed")) {
106
- return;
107
- }
108
-
109
- const message = this.$formatMessage(msg);
110
- this.adapter.emit("message.receive", message);
111
- this.logger.debug(
112
- `${this.$config.name} recv ${message.$channel.type}(${message.$channel.id}): ${segment.raw(message.$content)}`
113
- );
114
- }
115
-
116
- $formatMessage(msg: SlackMessageEvent): Message<SlackMessageEvent> {
117
- // Determine channel type based on channel ID
118
- const channelType = "channel_type" in msg && msg.channel_type === "im" ? "private" : "group";
119
- const channelId = msg.channel;
120
-
121
- // Parse message content
122
- const content = this.parseMessageContent(msg);
123
-
124
- // Extract user info safely
125
- const userId = ("user" in msg ? msg.user : "") || "";
126
- const userName = ("username" in msg ? msg.username : null) || userId || "Unknown";
127
- const messageText = ("text" in msg ? msg.text : "") || "";
128
-
129
- const result = Message.from(msg, {
130
- $id: msg.ts,
131
- $adapter: "slack",
132
- $bot: this.$config.name,
133
- $sender: {
134
- id: userId,
135
- name: userName,
136
- },
137
- $channel: {
138
- id: channelId,
139
- type: channelType,
140
- },
141
- $content: content,
142
- $raw: messageText,
143
- $timestamp: parseFloat(msg.ts) * 1000,
144
- $recall: async () => {
145
- try {
146
- await this.client!.chat.delete({
147
- channel: channelId,
148
- ts: result.$id,
149
- });
150
- } catch (error) {
151
- this.logger.error("Error recalling Slack message:", error);
152
- throw error;
153
- }
154
- },
155
- $reply: async (
156
- content: SendContent,
157
- quote?: boolean | string
158
- ): Promise<string> => {
159
- if (!Array.isArray(content)) content = [content];
160
-
161
- const sendOptions: Partial<ChatPostMessageArguments> = {
162
- channel: channelId,
163
- };
164
-
165
- // Handle thread reply
166
- if (quote) {
167
- const threadTs = typeof quote === "boolean" ? result.$id : quote;
168
- sendOptions.thread_ts = threadTs;
169
- }
170
-
171
- return await this.adapter.sendMessage({
172
- context: "slack",
173
- bot: this.$config.name,
174
- id: channelId,
175
- type: "channel",
176
- content: content,
177
- });
178
- },
179
- });
180
-
181
- return result;
182
- }
183
-
184
- private parseMessageContent(msg: SlackMessageEvent): MessageSegment[] {
185
- const segments: MessageSegment[] = [];
186
-
187
- // Handle text
188
- if ("text" in msg && msg.text) {
189
- // Parse Slack formatting
190
- segments.push(...this.parseSlackText(msg.text));
191
- }
192
-
193
- // Handle files
194
- if ("files" in msg && msg.files) {
195
- for (const file of msg.files) {
196
- if (file.mimetype?.startsWith("image/")) {
197
- segments.push({
198
- type: "image",
199
- data: {
200
- id: file.id,
201
- name: file.name,
202
- url: file.url_private || file.permalink,
203
- size: file.size,
204
- mimetype: file.mimetype,
205
- },
206
- });
207
- } else if (file.mimetype?.startsWith("video/")) {
208
- segments.push({
209
- type: "video",
210
- data: {
211
- id: file.id,
212
- name: file.name,
213
- url: file.url_private || file.permalink,
214
- size: file.size,
215
- mimetype: file.mimetype,
216
- },
217
- });
218
- } else if (file.mimetype?.startsWith("audio/")) {
219
- segments.push({
220
- type: "audio",
221
- data: {
222
- id: file.id,
223
- name: file.name,
224
- url: file.url_private || file.permalink,
225
- size: file.size,
226
- mimetype: file.mimetype,
227
- },
228
- });
229
- } else {
230
- segments.push({
231
- type: "file",
232
- data: {
233
- id: file.id,
234
- name: file.name,
235
- url: file.url_private || file.permalink,
236
- size: file.size,
237
- mimetype: file.mimetype,
238
- },
239
- });
240
- }
241
- }
242
- }
243
-
244
- // Handle attachments
245
- if ("attachments" in msg && msg.attachments) {
246
- for (const attachment of msg.attachments) {
247
- if (attachment.image_url) {
248
- segments.push({
249
- type: "image",
250
- data: {
251
- url: attachment.image_url,
252
- title: attachment.title,
253
- text: attachment.text,
254
- },
255
- });
256
- }
257
- }
258
- }
259
-
260
- return segments.length > 0
261
- ? segments
262
- : [{ type: "text", data: { text: "" } }];
263
- }
264
-
265
- private parseSlackText(text: string): MessageSegment[] {
266
- const segments: MessageSegment[] = [];
267
- let lastIndex = 0;
268
-
269
- // Match user mentions <@U12345678>
270
- const userMentionRegex = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
271
- // Match channel mentions <#C12345678|general>
272
- const channelMentionRegex = /<#([C][A-Z0-9]+)(?:\|([^>]+))?>/g;
273
- // Match links <http://example.com|Example>
274
- const linkRegex = /<(https?:\/\/[^|>]+)(?:\|([^>]+))?>/g;
275
-
276
- const allMatches: Array<{
277
- match: RegExpExecArray;
278
- type: "user" | "channel" | "link";
279
- }> = [];
280
-
281
- // Collect all matches
282
- let match;
283
- while ((match = userMentionRegex.exec(text)) !== null) {
284
- allMatches.push({ match, type: "user" });
285
- }
286
- while ((match = channelMentionRegex.exec(text)) !== null) {
287
- allMatches.push({ match, type: "channel" });
288
- }
289
- while ((match = linkRegex.exec(text)) !== null) {
290
- allMatches.push({ match, type: "link" });
291
- }
292
-
293
- // Sort by position
294
- allMatches.sort((a, b) => a.match.index! - b.match.index!);
295
-
296
- // Process matches
297
- for (const { match, type } of allMatches) {
298
- const matchStart = match.index!;
299
- const matchEnd = matchStart + match[0].length;
300
-
301
- // Add text before match
302
- if (matchStart > lastIndex) {
303
- const beforeText = text.slice(lastIndex, matchStart);
304
- if (beforeText.trim()) {
305
- segments.push({ type: "text", data: { text: beforeText } });
306
- }
307
- }
308
-
309
- // Add special segment
310
- switch (type) {
311
- case "user":
312
- segments.push({
313
- type: "at",
314
- data: {
315
- id: match[1],
316
- name: match[2] || match[1],
317
- text: match[0],
318
- },
319
- });
320
- break;
321
-
322
- case "channel":
323
- segments.push({
324
- type: "channel_mention",
325
- data: {
326
- id: match[1],
327
- name: match[2] || match[1],
328
- text: match[0],
329
- },
330
- });
331
- break;
332
-
333
- case "link":
334
- segments.push({
335
- type: "link",
336
- data: {
337
- url: match[1],
338
- text: match[2] || match[1],
339
- },
340
- });
341
- break;
342
- }
343
-
344
- lastIndex = matchEnd;
345
- }
346
-
347
- // Add remaining text
348
- if (lastIndex < text.length) {
349
- const remainingText = text.slice(lastIndex);
350
- if (remainingText.trim()) {
351
- segments.push({ type: "text", data: { text: remainingText } });
352
- }
353
- }
354
-
355
- return segments.length > 0
356
- ? segments
357
- : [{ type: "text", data: { text } }];
358
- }
359
-
360
- async $sendMessage(options: SendOptions): Promise<string> {
361
- try {
362
- const result = await this.sendContentToChannel(
363
- options.id,
364
- options.content
365
- );
366
- this.logger.debug(
367
- `${this.$config.name} send ${options.type}(${options.id}): ${segment.raw(options.content)}`
368
- );
369
- return result.ts || "";
370
- } catch (error) {
371
- this.logger.error("Failed to send Slack message:", error);
372
- throw error;
373
- }
374
- }
375
-
376
- private async sendContentToChannel(
377
- channel: string,
378
- content: SendContent,
379
- extraOptions: Partial<ChatPostMessageArguments> = {}
380
- ): Promise<any> {
381
- if (!Array.isArray(content)) content = [content];
382
-
383
- let textContent = "";
384
- const attachments: any[] = [];
385
-
386
- for (const segment of content) {
387
- if (typeof segment === "string") {
388
- textContent += segment;
389
- continue;
390
- }
391
-
392
- const { type, data } = segment;
393
-
394
- switch (type) {
395
- case "text":
396
- textContent += data.text || "";
397
- break;
398
-
399
- case "at":
400
- textContent += `<@${data.id}>`;
401
- break;
402
-
403
- case "channel_mention":
404
- textContent += `<#${data.id}>`;
405
- break;
406
-
407
- case "link":
408
- if (data.text && data.text !== data.url) {
409
- textContent += `<${data.url}|${data.text}>`;
410
- } else {
411
- textContent += `<${data.url}>`;
412
- }
413
- break;
414
-
415
- case "image":
416
- if (data.url) {
417
- attachments.push({
418
- image_url: data.url,
419
- title: data.name || data.title,
420
- });
421
- }
422
- break;
423
-
424
- case "file":
425
- // Files need to be uploaded separately
426
- if (data.file) {
427
- try {
428
- await this.client!.files.upload({
429
- channels: channel,
430
- file: data.file,
431
- filename: data.name,
432
- });
433
- } catch (error) {
434
- this.logger.error("Failed to upload file:", error);
435
- }
436
- }
437
- break;
438
-
439
- default:
440
- textContent += data.text || `[${type}]`;
441
- }
442
- }
443
-
444
- // Send message
445
- const messageOptions: any = {
446
- channel,
447
- text: textContent.trim() || "Message",
448
- ...extraOptions,
449
- };
450
-
451
- if (attachments.length > 0) {
452
- messageOptions.attachments = attachments;
453
- }
454
-
455
- const result = await this.client!.chat.postMessage(messageOptions as ChatPostMessageArguments);
456
- return result.message || {};
457
- }
458
-
459
- async $recallMessage(id: string): Promise<void> {
460
- // Slack requires both channel and ts (timestamp) to delete a message
461
- // The Bot interface only provides message ID (ts), making recall impossible
462
- // Users should use message.$recall() instead, which has the full context
463
- throw new Error(
464
- "SlackBot.$recallMessage: Message recall not supported without channel information. " +
465
- "Use message.$recall() method instead, which contains the required context."
466
- );
467
- }
468
-
469
- // ==================== 工作区管理 API ====================
470
-
471
- /**
472
- * 邀请用户到频道
473
- * @param channel 频道 ID
474
- * @param users 用户 ID 列表
475
- */
476
- async inviteToChannel(channel: string, users: string[]): Promise<boolean> {
477
- try {
478
- await this.client!.conversations.invite({ channel, users: users.join(',') });
479
- this.logger.debug(formatCompact( { op: "invite", bot: this.$id, channel, users: users.join(",") }));
480
- return true;
481
- } catch (error) {
482
- this.logger.error(`Slack Bot ${this.$id} 邀请用户失败:`, error);
483
- throw error;
484
- }
485
- }
486
-
487
- /**
488
- * 从频道踢出用户
489
- * @param channel 频道 ID
490
- * @param user 用户 ID
491
- */
492
- async kickFromChannel(channel: string, user: string): Promise<boolean> {
493
- try {
494
- await this.client!.conversations.kick({ channel, user });
495
- this.logger.debug(formatCompact( { op: "kick", bot: this.$id, channel, user }));
496
- return true;
497
- } catch (error) {
498
- this.logger.error(`Slack Bot ${this.$id} 踢出用户失败:`, error);
499
- throw error;
500
- }
501
- }
502
-
503
- /**
504
- * 设置频道话题
505
- * @param channel 频道 ID
506
- * @param topic 话题
507
- */
508
- async setChannelTopic(channel: string, topic: string): Promise<boolean> {
509
- try {
510
- await this.client!.conversations.setTopic({ channel, topic });
511
- this.logger.debug(formatCompact( { op: "set_topic", bot: this.$id, channel }));
512
- return true;
513
- } catch (error) {
514
- this.logger.error(`Slack Bot ${this.$id} 设置话题失败:`, error);
515
- throw error;
516
- }
517
- }
518
-
519
- /**
520
- * 设置频道目的
521
- * @param channel 频道 ID
522
- * @param purpose 目的
523
- */
524
- async setChannelPurpose(channel: string, purpose: string): Promise<boolean> {
525
- try {
526
- await this.client!.conversations.setPurpose({ channel, purpose });
527
- this.logger.debug(formatCompact( { op: "set_purpose", bot: this.$id, channel }));
528
- return true;
529
- } catch (error) {
530
- this.logger.error(`Slack Bot ${this.$id} 设置目的失败:`, error);
531
- throw error;
532
- }
533
- }
534
-
535
- /**
536
- * 归档频道
537
- * @param channel 频道 ID
538
- */
539
- async archiveChannel(channel: string): Promise<boolean> {
540
- try {
541
- await this.client!.conversations.archive({ channel });
542
- this.logger.debug(formatCompact( { op: "archive", bot: this.$id, channel }));
543
- return true;
544
- } catch (error) {
545
- this.logger.error(`Slack Bot ${this.$id} 归档频道失败:`, error);
546
- throw error;
547
- }
548
- }
549
-
550
- /**
551
- * 取消归档频道
552
- * @param channel 频道 ID
553
- */
554
- async unarchiveChannel(channel: string): Promise<boolean> {
555
- try {
556
- await this.client!.conversations.unarchive({ channel });
557
- this.logger.debug(formatCompact( { op: "unarchive", bot: this.$id, channel }));
558
- return true;
559
- } catch (error) {
560
- this.logger.error(`Slack Bot ${this.$id} 取消归档失败:`, error);
561
- throw error;
562
- }
563
- }
564
-
565
- /**
566
- * 重命名频道
567
- * @param channel 频道 ID
568
- * @param name 新名称
569
- */
570
- async renameChannel(channel: string, name: string): Promise<boolean> {
571
- try {
572
- await this.client!.conversations.rename({ channel, name });
573
- this.logger.debug(formatCompact( { op: "rename", bot: this.$id, channel, name }));
574
- return true;
575
- } catch (error) {
576
- this.logger.error(`Slack Bot ${this.$id} 重命名频道失败:`, error);
577
- throw error;
578
- }
579
- }
580
-
581
- /**
582
- * 获取频道成员列表
583
- * @param channel 频道 ID
584
- */
585
- async getChannelMembers(channel: string): Promise<string[]> {
586
- try {
587
- const result = await this.client!.conversations.members({ channel });
588
- return result.members || [];
589
- } catch (error) {
590
- this.logger.error(`Slack Bot ${this.$id} 获取成员列表失败:`, error);
591
- throw error;
592
- }
593
- }
594
-
595
- /**
596
- * 获取频道信息
597
- * @param channel 频道 ID
598
- */
599
- async getChannelInfo(channel: string): Promise<any> {
600
- try {
601
- const result = await this.client!.conversations.info({ channel });
602
- return result.channel;
603
- } catch (error) {
604
- this.logger.error(`Slack Bot ${this.$id} 获取频道信息失败:`, error);
605
- throw error;
606
- }
607
- }
608
-
609
- /**
610
- * 获取用户信息
611
- * @param user 用户 ID
612
- */
613
- async getUserInfo(user: string): Promise<any> {
614
- try {
615
- const result = await this.client!.users.info({ user });
616
- return result.user;
617
- } catch (error) {
618
- this.logger.error(`Slack Bot ${this.$id} 获取用户信息失败:`, error);
619
- throw error;
620
- }
621
- }
622
-
623
- /**
624
- * 添加消息反应
625
- * @param channel 频道 ID
626
- * @param timestamp 消息时间戳
627
- * @param name 表情名称
628
- */
629
- async addReaction(channel: string, timestamp: string, name: string): Promise<boolean> {
630
- try {
631
- await this.client!.reactions.add({ channel, timestamp, name });
632
- this.logger.debug(formatCompact( { op: "reaction_add", bot: this.$id, name }));
633
- return true;
634
- } catch (error) {
635
- this.logger.error(`Slack Bot ${this.$id} 添加反应失败:`, error);
636
- throw error;
637
- }
638
- }
639
-
640
- /**
641
- * 移除消息反应
642
- * @param channel 频道 ID
643
- * @param timestamp 消息时间戳
644
- * @param name 表情名称
645
- */
646
- async removeReaction(channel: string, timestamp: string, name: string): Promise<boolean> {
647
- try {
648
- await this.client!.reactions.remove({ channel, timestamp, name });
649
- this.logger.debug(formatCompact( { op: "reaction_remove", bot: this.$id, name }));
650
- return true;
651
- } catch (error) {
652
- this.logger.error(`Slack Bot ${this.$id} 移除反应失败:`, error);
653
- throw error;
654
- }
655
- }
656
-
657
- /**
658
- * 置顶消息
659
- * @param channel 频道 ID
660
- * @param timestamp 消息时间戳
661
- */
662
- async pinMessage(channel: string, timestamp: string): Promise<boolean> {
663
- try {
664
- await this.client!.pins.add({ channel, timestamp });
665
- this.logger.debug(formatCompact( { op: "pin", bot: this.$id, channel }));
666
- return true;
667
- } catch (error) {
668
- this.logger.error(`Slack Bot ${this.$id} 置顶消息失败:`, error);
669
- throw error;
670
- }
671
- }
672
-
673
- /**
674
- * 取消置顶消息
675
- * @param channel 频道 ID
676
- * @param timestamp 消息时间戳
677
- */
678
- async unpinMessage(channel: string, timestamp: string): Promise<boolean> {
679
- try {
680
- await this.client!.pins.remove({ channel, timestamp });
681
- this.logger.debug(formatCompact( { op: "unpin", bot: this.$id, channel }));
682
- return true;
683
- } catch (error) {
684
- this.logger.error(`Slack Bot ${this.$id} 取消置顶失败:`, error);
685
- throw error;
686
- }
687
- }
688
- }
package/src/types.ts DELETED
@@ -1,18 +0,0 @@
1
- /**
2
- * Slack 适配器类型定义
3
- */
4
- import type { LogLevel } from "@slack/bolt";
5
- import type { KnownEventFromType } from "@slack/bolt";
6
-
7
- export interface SlackBotConfig {
8
- context: "slack";
9
- token: string;
10
- name: string;
11
- signingSecret: string;
12
- appToken?: string;
13
- socketMode?: boolean;
14
- port?: number;
15
- logLevel?: LogLevel;
16
- }
17
-
18
- export type SlackMessageEvent = KnownEventFromType<"message">;