@zhin.js/adapter-slack 1.0.78 → 1.1.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +638 -2
  2. package/README.md +112 -158
  3. package/adapters/slack.js +40 -0
  4. package/adapters/slack.ts +45 -0
  5. package/agent/PERMITS.md +24 -0
  6. package/{skills/slack/SKILL.md → agent/skills/slack.md} +2 -0
  7. package/agent/tools/add_reaction.ts +29 -0
  8. package/agent/tools/archive_channel.ts +21 -0
  9. package/agent/tools/edit_message.ts +25 -0
  10. package/agent/tools/invite_to_channel.ts +23 -0
  11. package/agent/tools/pin_message.ts +23 -0
  12. package/agent/tools/remove_reaction.ts +32 -0
  13. package/agent/tools/set_purpose.ts +23 -0
  14. package/agent/tools/set_topic.ts +23 -0
  15. package/agent/tools/unarchive.ts +21 -0
  16. package/agent/tools/unpin_message.ts +23 -0
  17. package/agent/tools/user_info.ts +29 -0
  18. package/commands/endpoint/add/[id].js +3 -0
  19. package/commands/endpoint/add/[id].ts +3 -0
  20. package/commands/endpoint/list.js +3 -0
  21. package/commands/endpoint/list.ts +3 -0
  22. package/commands/endpoint/remove/[id].js +3 -0
  23. package/commands/endpoint/remove/[id].ts +3 -0
  24. package/lib/client.d.ts +25 -0
  25. package/lib/client.js +2 -0
  26. package/lib/endpoint.d.ts +153 -0
  27. package/lib/endpoint.js +425 -0
  28. package/lib/index.d.ts +10 -10
  29. package/lib/index.js +10 -242
  30. package/lib/markdown-to-mrkdwn.d.ts +9 -0
  31. package/lib/markdown-to-mrkdwn.js +121 -0
  32. package/lib/mrkdwn-to-markdown.d.ts +4 -0
  33. package/lib/mrkdwn-to-markdown.js +30 -0
  34. package/lib/platform-permit.d.ts +15 -0
  35. package/lib/platform-permit.js +43 -0
  36. package/lib/protocol.d.ts +153 -0
  37. package/lib/protocol.js +272 -0
  38. package/lib/side-event-dispatch.d.ts +4 -0
  39. package/lib/side-event-dispatch.js +45 -0
  40. package/lib/slack-endpoint-commands.d.ts +1 -0
  41. package/lib/slack-endpoint-commands.js +18 -0
  42. package/lib/slack-inbound-filter.d.ts +12 -0
  43. package/lib/slack-inbound-filter.js +46 -0
  44. package/lib/slack-message-ref.d.ts +7 -0
  45. package/lib/slack-message-ref.js +17 -0
  46. package/lib/slack-outbound.d.ts +24 -0
  47. package/lib/slack-outbound.js +111 -0
  48. package/lib/slack-reaction.d.ts +1 -0
  49. package/lib/slack-reaction.js +26 -0
  50. package/lib/slack-response-url.d.ts +5 -0
  51. package/lib/slack-response-url.js +16 -0
  52. package/lib/slack-runtime-state.d.ts +1 -0
  53. package/lib/slack-runtime-state.js +6 -0
  54. package/lib/webhook.d.ts +14 -0
  55. package/lib/webhook.js +72 -0
  56. package/package.json +67 -15
  57. package/plugin.js +19 -0
  58. package/schema.json +95 -0
  59. package/src/client.ts +30 -0
  60. package/src/endpoint.ts +526 -0
  61. package/src/index.ts +59 -265
  62. package/src/markdown-to-mrkdwn.ts +117 -0
  63. package/src/mrkdwn-to-markdown.ts +29 -0
  64. package/src/platform-permit.ts +59 -0
  65. package/src/protocol.ts +435 -0
  66. package/src/side-event-dispatch.ts +54 -0
  67. package/src/slack-endpoint-commands.ts +19 -0
  68. package/src/slack-inbound-filter.ts +60 -0
  69. package/src/slack-message-ref.ts +18 -0
  70. package/src/slack-outbound.ts +170 -0
  71. package/src/slack-reaction.ts +23 -0
  72. package/src/slack-response-url.ts +26 -0
  73. package/src/slack-runtime-state.ts +7 -0
  74. package/src/webhook.ts +106 -0
  75. package/lib/adapter.d.ts +0 -16
  76. package/lib/adapter.d.ts.map +0 -1
  77. package/lib/adapter.js +0 -43
  78. package/lib/adapter.js.map +0 -1
  79. package/lib/bot.d.ts +0 -106
  80. package/lib/bot.d.ts.map +0 -1
  81. package/lib/bot.js +0 -628
  82. package/lib/bot.js.map +0 -1
  83. package/lib/index.d.ts.map +0 -1
  84. package/lib/index.js.map +0 -1
  85. package/lib/types.d.ts +0 -17
  86. package/lib/types.d.ts.map +0 -1
  87. package/lib/types.js +0 -2
  88. package/lib/types.js.map +0 -1
  89. package/plugin.yml +0 -3
  90. package/src/adapter.ts +0 -52
  91. package/src/bot.ts +0 -688
  92. package/src/types.ts +0 -18
package/README.md CHANGED
@@ -1,192 +1,146 @@
1
1
  # @zhin.js/adapter-slack
2
2
 
3
- Slack adapter for zhin.js framework.
3
+ Zhin.js Slack 适配器(Plugin Runtime),优先 Socket Mode,也可经 Runtime Host HTTP Events API 收发消息。
4
4
 
5
- ## Installation
5
+ ## 功能
6
+
7
+ - **Socket Mode**(默认):WebSocket 长连接,无需公网 URL
8
+ - **HTTP Events API**:`httpHostToken` POST(签名验证),**非** legacy host-router/Koa
9
+ - 入站经 `Endpoint.emit(...)`;出站 `send({ conversation, payload })` → `chat.postMessage` / Block Kit
10
+ - 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
11
+ - Block Kit 按钮、斜杠命令、消息编辑、表情反应等(见 `agent/tools/`)
12
+
13
+ ## 安装
6
14
 
7
15
  ```bash
8
16
  pnpm add @zhin.js/adapter-slack
9
17
  ```
10
18
 
11
- ## Configuration
19
+ ## Plugin Runtime
20
+
21
+ - `@zhin.js/adapter` — 约定式 `adapters/slack.ts`(`defineAdapter`)
22
+ - `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
23
+ - `@zhin.js/host-http` — 仅 HTTP 模式需要 `httpHostToken` 注册 Events 路由
24
+ - `zhin.js` — `plugin.ts`(`definePlugin`)
25
+ - 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
12
26
 
13
- 通过 **`socketMode`** 选择连接方式(默认 `true` 时为 Socket Mode)。
27
+ 入站:`gateway.receive({ conversation, message: { conversation, id }, content: text, sender, metadata })`(`conversation` 为 ConversationRef:`channel_type: im` → kind `private`,其余 → kind `group`;线程根 ts 进 `threadId`)
28
+ 出站:`send({ conversation, payload })` → Web API(`conversation.id` 为 channel,`conversation.threadId` 为 thread_ts)
14
29
 
15
- ### 模式对比
30
+ ### 平台权限(platform permit)
31
+
32
+ - `plugin.ts` 已注册 checker,Runtime Tool 权限统一经 Core `canAccessTool()`;当前 Slack 入站没有可靠 sender role 时,受限工具按 fail-closed 拒绝,不会静默放行。
33
+
34
+ ## 模式对比
16
35
 
17
36
  | 模式 | `socketMode` | 适用场景 | 额外字段 |
18
37
  |------|--------------|----------|----------|
19
- | **Socket Mode** | `true`(推荐本地/内网) | 无需公网 URL,WebSocket 长连接 | `appToken`(`xapp-...`) |
20
- | **HTTP Events** | `false` | 生产环境,有公网 HTTPS | `port`(Bolt 监听端口) |
21
-
22
- #### HTTP 模式前置条件
23
-
24
- - Slack App 中配置 **Event Subscriptions** 的 Request URL(须公网 HTTPS 可达)
25
- - 启用对应 Bot Events(`message.*`、`app_mention` 等)
26
- - 防火墙 / 反向代理放行 `port`
27
- - **不需要** `@zhin.js/host-router`(由 `@slack/bolt` 自行监听)
28
-
29
- ### Socket Mode (Recommended for Development)
30
-
31
- ```typescript
32
- import { defineConfig } from 'zhin.js'
33
-
34
- export default defineConfig({
35
- bots: [
36
- {
37
- name: 'my-slack-bot',
38
- context: 'slack',
39
- token: 'xoxb-your-bot-token',
40
- signingSecret: 'your-signing-secret',
41
- appToken: 'xapp-your-app-token',
42
- socketMode: true
43
- }
44
- ]
45
- })
38
+ | **Socket Mode**(默认) | `true` | 本地/内网,无需公网 URL | `appToken`(`xapp-...`) |
39
+ | **HTTP Events** | `false` | 生产环境有公网 HTTPS | `signingSecret` + Runtime Host |
40
+
41
+ ## 前置条件
42
+
43
+ 1. 创建 Slack App,安装到 Workspace,并授予收发消息所需 OAuth scopes。
44
+ 2. Socket Mode 创建 `connections:write` App-Level Token;HTTP 模式配置 Signing Secret 与 Events URL。
45
+ 3. 订阅需要的 bot events,并把应用加入目标频道。
46
+
47
+ ## 最小配置(Socket Mode)
48
+
49
+ ```yaml
50
+ # zhin.config.yml(Plugin Runtime)
51
+ plugins:
52
+ slack:
53
+ socketMode: true # 默认 true,可省略
54
+ endpoints:
55
+ - name: my-slack-bot
56
+ token: ${SLACK_BOT_TOKEN}
57
+ appToken: ${SLACK_APP_TOKEN}
46
58
  ```
47
59
 
48
- ### HTTP Mode (For Production with Public URL)
49
-
50
- ```typescript
51
- import { defineConfig } from 'zhin.js'
52
-
53
- export default defineConfig({
54
- bots: [
55
- {
56
- name: 'my-slack-bot',
57
- context: 'slack',
58
- token: 'xoxb-your-bot-token',
59
- signingSecret: 'your-signing-secret',
60
- socketMode: false,
61
- port: 3000
62
- }
63
- ]
64
- })
60
+ 多 workspace:一个插件实例挂多个 endpoint(`endpoints` 数组逐项覆盖顶层字段,`name` 必填):
61
+
62
+ ```yaml
63
+ plugins:
64
+ slack:
65
+ endpoints:
66
+ - name: team-a
67
+ token: ${SLACK_BOT_TOKEN_A}
68
+ appToken: ${SLACK_APP_TOKEN_A}
69
+ - name: team-b
70
+ token: ${SLACK_BOT_TOKEN_B}
71
+ appToken: ${SLACK_APP_TOKEN_B}
65
72
  ```
66
73
 
67
- ## Features
68
-
69
- - ✅ Send and receive text messages
70
- - ✅ Support for rich media (images, files)
71
- - ✅ Message formatting (Slack mrkdwn)
72
- - ✅ Reply to messages and threads
73
- - ✅ Mentions (@user, #channel)
74
- - ✅ Links and attachments
75
- - ✅ Socket Mode and HTTP mode
76
- - ✅ Private messages and channels
77
-
78
- ## Setting Up Your Slack App
79
-
80
- 1. Go to [Slack API](https://api.slack.com/apps)
81
- 2. Create a new app
82
- 3. Add Bot Token Scopes:
83
- - `chat:write` - Send messages
84
- - `chat:write.public` - Send messages to public channels
85
- - `channels:read` - View basic channel info
86
- - `channels:history` - View messages in channels
87
- - `groups:read` - View basic private channel info
88
- - `groups:history` - View messages in private channels
89
- - `im:read` - View basic direct message info
90
- - `im:history` - View messages in direct messages
91
- - `mpim:read` - View basic group direct message info
92
- - `mpim:history` - View messages in group direct messages
93
- - `users:read` - View user info
94
- - `files:read` - View files
95
- - `files:write` - Upload files
96
- 4. Enable Socket Mode (if using Socket Mode):
97
- - Go to Socket Mode settings
98
- - Enable Socket Mode
99
- - Generate an app-level token with `connections:write` scope
100
- 5. Subscribe to events:
101
- - `message.channels` - Messages in public channels
102
- - `message.groups` - Messages in private channels
103
- - `message.im` - Direct messages
104
- - `message.mpim` - Group direct messages
105
- - `app_mention` - When the bot is mentioned
106
- 6. Install the app to your workspace
107
- 7. Copy the Bot User OAuth Token (`xoxb-...`)
108
- 8. Copy the Signing Secret
109
- 9. Copy the App-Level Token (`xapp-...`) if using Socket Mode
110
-
111
- ## Usage Examples
112
-
113
- ### Basic Message Handling
114
-
115
- ```typescript
116
- import { usePlugin, MessageCommand } from 'zhin.js'
117
-
118
- const { addCommand } = usePlugin()
119
-
120
- addCommand(new MessageCommand('hello')
121
- .action(async (message) => {
122
- return 'Hello from Slack!'
123
- })
124
- )
74
+ ## HTTP Events 配置
75
+
76
+ ```yaml
77
+ plugins:
78
+ slack:
79
+ socketMode: false
80
+ webhookPath: /slack/events # 可选,默认 /slack/events
81
+ endpoints:
82
+ - name: my-slack-bot
83
+ token: ${SLACK_BOT_TOKEN}
84
+ signingSecret: ${SLACK_SIGNING_SECRET}
125
85
  ```
126
86
 
127
- ### Send Rich Messages
128
-
129
- ```typescript
130
- addCommand(new MessageCommand('info')
131
- .action(async (message) => {
132
- return [
133
- { type: 'text', data: { text: '*Bold* and _italic_ text\n' } },
134
- { type: 'link', data: { url: 'https://slack.com', text: 'Visit Slack' } }
135
- ]
136
- })
137
- )
138
- ```
87
+ 根插件 `zhin.plugins`(或项目图)需引用 `@zhin.js/adapter-slack`(`instanceKey: slack`)。
88
+ HTTP 模式下 Runtime Host(`http`)须已 listen;Slack App 的 Event Subscriptions / Interactivity / Slash Commands Request URL 指向 `https://your-domain/slack/events`。
139
89
 
140
- ### Mention Users
141
-
142
- ```typescript
143
- addCommand(new MessageCommand('mention <userId:text>')
144
- .action(async (message, result) => {
145
- return [
146
- { type: 'at', data: { id: result.params.userId } },
147
- { type: 'text', data: { text: ' Hello!' } }
148
- ]
149
- })
150
- )
151
- ```
90
+ ## 环境变量
152
91
 
153
- ### Reply in Thread
92
+ | 变量 | 说明 |
93
+ |------|------|
94
+ | `SLACK_BOT_TOKEN` / `SLACK_TOKEN` | Bot User OAuth Token(`xoxb-...`) |
95
+ | `SLACK_APP_TOKEN` | App-Level Token(Socket Mode,`xapp-...`) |
96
+ | `SLACK_SIGNING_SECRET` | Signing Secret(HTTP 模式) |
97
+ | `SLACK_BOT_NAME` | 可选 endpoint 名称 |
154
98
 
155
- ```typescript
156
- addCommand(new MessageCommand('thread')
157
- .action(async (message) => {
158
- // Reply in a thread by passing the message timestamp
159
- await message.$reply('This is a threaded reply!', true)
160
- })
161
- )
162
- ```
99
+ ## 消息格式
100
+
101
+ ### 出站(Markdown → mrkdwn)
163
102
 
164
- ## Slack-Specific Features
103
+ 通用 Markdown(如 `**粗体**`)会转换为 Slack mrkdwn,并通过 Block Kit `section` 发送。
165
104
 
166
- ### Slack Formatting
105
+ ### 入站(mrkdwn → Markdown)
167
106
 
168
- Slack uses mrkdwn format:
169
- - `*bold*` for **bold**
170
- - `_italic_` for *italic*
171
- - `~strike~` for ~~strikethrough~~
172
- - `` `code` `` for `code`
173
- - `> quote` for blockquotes
107
+ | Slack mrkdwn | 通用 Markdown |
108
+ |--------------|---------------|
109
+ | `*bold*` | `**bold**` |
110
+ | `_italic_` | `*italic*` |
111
+ | `~strike~` | `~~strike~~` |
112
+ | `<url\|text>` | `[text](url)` |
174
113
 
175
- ### User and Channel Mentions
114
+ ## AI 工具
176
115
 
177
- The adapter automatically converts:
178
- - `<@U12345678>` to user mentions
179
- - `<#C12345678>` to channel mentions
116
+ | 类别 | 路径 |
117
+ |------|------|
118
+ | Permit 词汇 | `agent/PERMITS.md` |
119
+ | 平台工具 | `agent/tools/`(邀请、话题、反应、置顶、编辑等) |
120
+ | 技能说明 | `agent/skills/slack.md` |
180
121
 
181
- ### File Uploads
122
+ ## 限制
182
123
 
183
- Upload files using the `file` segment type with a local file path.
124
+ - 入站 mrkdwn → Markdown 为启发式转换
125
+ - Modals / Select menus — 暂不支持
126
+ - OAuth 安装流程 — 暂不支持
127
+ - 旧 `usePlugin` / `extends Adapter` / host-router 生产入口已删除
184
128
 
185
- ## Limitations
129
+ ## 故障排查
186
130
 
187
- - Message recall requires channel information (not available from message ID alone)
188
- - Some Slack features like interactive components require additional setup
131
+ | 现象 | 排查 |
132
+ | --- | --- |
133
+ | Socket Mode 无法连接 | 检查 `xapp-` Token、Socket Mode 与 `connections:write` scope |
134
+ | HTTP Events 返回 401 | 检查 Signing Secret、原始请求体、服务器时钟与反向代理 |
135
+ | 频道消息未触发 | 确认事件订阅、OAuth scopes,并邀请 App 进入频道 |
136
+ | 线程回复跑到主频道 | 检查入站 `thread_ts` 是否保留为 Conversation `threadId` |
189
137
 
190
- ## License
138
+ ## 许可证
191
139
 
192
140
  MIT
141
+
142
+ ## 发送失败与回执
143
+
144
+ 成功发送返回平台 `ts` 组成的消息引用,不以本地时间替代缺失回执。
145
+ 文件上传失败会让本次发送失败;分段消息中某段缺少有效回执时,停止后续分段。
146
+ 前面已成功的文件或分段不会自动回滚;发送失败可能表示部分已送达,请先核对目标会话再重试。
@@ -0,0 +1,40 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ /**
3
+ * Convention entry: discover `adapters/slack.ts` → defineAdapter.
4
+ */
5
+ import { defineAdapter } from 'zhin.js/adapter';
6
+ import { httpHostToken } from '@zhin.js/host-http';
7
+ import { SlackEndpoint } from "../lib/endpoint.js";
8
+ import { resolveSlackConfig, } from "../lib/protocol.js";
9
+ import { slackRuntimeStateToken } from "../lib/slack-runtime-state.js";
10
+ export { SlackEndpoint } from "../lib/endpoint.js";
11
+ export default defineAdapter({
12
+ capabilities: ['inbound', 'outbound'],
13
+ operations: ['recall', 'edit', 'reaction'],
14
+ // 媒体段(canonical MediaRef):kind=url 直发(image 走 attachment image_url,
15
+ // 其余拉取后上传);kind=base64 解码后经 files.uploadV2 上传;kind=path 读盘上传;
16
+ // kind=file 无 Slack 对应物,丢弃留痕。Block Kit 原生按钮承载交互段。
17
+ segments: {
18
+ outboundMedia: ['url', 'upload', 'path'],
19
+ interactive: 'native',
20
+ },
21
+ create(context) {
22
+ const config = resolveSlackConfig(context.config);
23
+ // 注册到插件运行时状态(slack.endpoint list 的"运行中"数据源)
24
+ context.use(slackRuntimeStateToken).endpoints.set(config.id, {
25
+ id: config.id,
26
+ mode: config.mode,
27
+ });
28
+ if (config.mode === 'http') {
29
+ return new SlackEndpoint({
30
+ id: context.id,
31
+ http: context.use(httpHostToken),
32
+ config,
33
+ });
34
+ }
35
+ return new SlackEndpoint({
36
+ id: context.id,
37
+ config,
38
+ });
39
+ },
40
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Convention entry: discover `adapters/slack.ts` → defineAdapter.
3
+ */
4
+ import { defineAdapter } from 'zhin.js/adapter';
5
+ import { httpHostToken } from '@zhin.js/host-http';
6
+ import { SlackEndpoint } from '../src/endpoint.js';
7
+ import {
8
+ resolveSlackConfig,
9
+ type SlackAdapterConfig,
10
+ } from '../src/protocol.js';
11
+ import { slackRuntimeStateToken } from '../src/slack-runtime-state.js';
12
+
13
+ export { SlackEndpoint } from '../src/endpoint.js';
14
+ export type { SlackEndpointOptions, SlackSocketLike, SlackWebClientLike } from '../src/endpoint.js';
15
+
16
+ export default defineAdapter<SlackAdapterConfig>({
17
+ capabilities: ['inbound', 'outbound'],
18
+ operations: ['recall', 'edit', 'reaction'],
19
+ // 媒体段(canonical MediaRef):kind=url 直发(image 走 attachment image_url,
20
+ // 其余拉取后上传);kind=base64 解码后经 files.uploadV2 上传;kind=path 读盘上传;
21
+ // kind=file 无 Slack 对应物,丢弃留痕。Block Kit 原生按钮承载交互段。
22
+ segments: {
23
+ outboundMedia: ['url', 'upload', 'path'],
24
+ interactive: 'native',
25
+ },
26
+ create(context) {
27
+ const config = resolveSlackConfig(context.config);
28
+ // 注册到插件运行时状态(slack.endpoint list 的"运行中"数据源)
29
+ context.use(slackRuntimeStateToken).endpoints.set(config.id, {
30
+ id: config.id,
31
+ mode: config.mode,
32
+ });
33
+ if (config.mode === 'http') {
34
+ return new SlackEndpoint({
35
+ id: context.id,
36
+ http: context.use(httpHostToken),
37
+ config,
38
+ });
39
+ }
40
+ return new SlackEndpoint({
41
+ id: context.id,
42
+ config,
43
+ });
44
+ },
45
+ });
@@ -0,0 +1,24 @@
1
+ # Slack Platform Permits
2
+
3
+ ## 入站字段
4
+
5
+ 频道消息异步 enrich(`users.info` + `conversations.info`,TTL 60s):
6
+
7
+ | 字段 | 来源 |
8
+ |------|------|
9
+ | `$sender.role` | `owner` · `admin` · `channel_admin` · `member` |
10
+ | `$sender.permissions` | `workspace_owner` · `workspace_admin` · `channel_manager` |
11
+
12
+ `channel_manager`:频道 `creator === user` 或 workspace 管理员。
13
+
14
+ ## Permit 词汇表
15
+
16
+ | `platform(slack,…)` | 含义 |
17
+ |---------------------|------|
18
+ | `workspace_owner` | Workspace 主 |
19
+ | `workspace_admin` | Workspace 管理员 |
20
+ | `channel_manager` | 频道管理者 |
21
+
22
+ ## 工厂映射
23
+
24
+ `scene_admin` → `channel_manager` · `scene_owner` → `workspace_owner`
@@ -34,6 +34,7 @@ tools:
34
34
  - slack_user_info
35
35
  - slack_set_purpose
36
36
  - slack_unarchive
37
+ - slack_edit_message
37
38
  - slack_kick_member
38
39
  - slack_set_group_name
39
40
  - slack_list_members
@@ -83,6 +84,7 @@ Slack 频道有两个元信息字段:
83
84
  | `slack_unpin_message` | 取消置顶 | — |
84
85
  | `slack_add_reaction` | 添加表情反应 | — |
85
86
  | `slack_remove_reaction` | 移除表情反应 | — |
87
+ | `slack_edit_message` | 编辑消息 | 通过 chat.update 修改已发消息 |
86
88
 
87
89
  ## 易错点
88
90
 
@@ -0,0 +1,29 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { normalizeSlackReactionName } from '../../src/slack-reaction.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel: string;
7
+ timestamp: string;
8
+ emoji: string;
9
+ }>({
10
+ description: '给 Slack 消息添加表情反应',
11
+ inputSchema: z.object({
12
+ channel: z.string().describe('频道 ID'),
13
+ timestamp: z.string().describe('消息时间戳'),
14
+ emoji: z.string().describe('表情名称(不含冒号)'),
15
+ }),
16
+ adapter: 'slack',
17
+ tags: ['slack'],
18
+ async execute({ channel, timestamp, emoji }, context) {
19
+ const client = context.$client;
20
+ const name = normalizeSlackReactionName(emoji);
21
+ try {
22
+ await client.reactions.add({ channel, timestamp, name });
23
+ } catch (error) {
24
+ if ((error as { data?: { error?: string } })?.data?.error !== 'already_reacted') throw error;
25
+ }
26
+ const success = true;
27
+ return { success, message: success ? `已添加反应 :${emoji}:` : '操作失败' };
28
+ },
29
+ });
@@ -0,0 +1,21 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { platformPermit } from '../../src/platform-permit.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel: string;
7
+ }>({
8
+ description: '归档 Slack 频道',
9
+ inputSchema: z.object({
10
+ channel: z.string().describe('频道 ID'),
11
+ }),
12
+ adapter: 'slack',
13
+ tags: ['slack'],
14
+ permissions: [platformPermit('workspace_admin')],
15
+ async execute({ channel }, context) {
16
+ const client = context.$client;
17
+ await client.conversations.archive({ channel });
18
+ const success = true;
19
+ return { success, message: success ? '已归档频道' : '操作失败' };
20
+ },
21
+ });
@@ -0,0 +1,25 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { editSlackContent } from '../../src/slack-outbound.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel: string;
7
+ message_ts: string;
8
+ text: string;
9
+ }>({
10
+ description: '编辑 Slack 消息',
11
+ inputSchema: z.object({
12
+ channel: z.string().describe('频道 ID'),
13
+ message_ts: z.string().describe('消息时间戳'),
14
+ text: z.string().describe('新的消息文本'),
15
+ }),
16
+ adapter: 'slack',
17
+ tags: ['slack'],
18
+ async execute({ channel, message_ts, text }, context) {
19
+ const client = context.$client;
20
+ await editSlackContent(client, channel, message_ts, [
21
+ { type: 'text', data: { text } },
22
+ ]);
23
+ return { success: true, message: '消息已编辑' };
24
+ },
25
+ });
@@ -0,0 +1,23 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { platformPermit } from '../../src/platform-permit.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel: string;
7
+ users: string;
8
+ }>({
9
+ description: '邀请用户加入 Slack 频道',
10
+ inputSchema: z.object({
11
+ channel: z.string().describe('频道 ID'),
12
+ users: z.string().describe('用户 ID 列表(逗号分隔)'),
13
+ }),
14
+ adapter: 'slack',
15
+ tags: ['slack'],
16
+ permissions: [platformPermit('channel_manager')],
17
+ async execute({ channel, users }, context) {
18
+ const client = context.$client;
19
+ await client.conversations.invite({ channel, users });
20
+ const success = true;
21
+ return { success, message: success ? '已邀请用户加入频道' : '操作失败' };
22
+ },
23
+ });
@@ -0,0 +1,23 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { platformPermit } from '../../src/platform-permit.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel: string;
7
+ timestamp: string;
8
+ }>({
9
+ description: '置顶 Slack 消息',
10
+ inputSchema: z.object({
11
+ channel: z.string().describe('频道 ID'),
12
+ timestamp: z.string().describe('消息时间戳'),
13
+ }),
14
+ adapter: 'slack',
15
+ tags: ['slack'],
16
+ permissions: [platformPermit('channel_manager')],
17
+ async execute({ channel, timestamp }, context) {
18
+ const client = context.$client;
19
+ await client.pins.add({ channel, timestamp });
20
+ const success = true;
21
+ return { success, message: success ? '已置顶消息' : '操作失败' };
22
+ },
23
+ });
@@ -0,0 +1,32 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { normalizeSlackReactionName } from '../../src/slack-reaction.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel_id: string;
7
+ timestamp: string;
8
+ name: string;
9
+ }>({
10
+ description: '移除 Slack 消息上的表情反应',
11
+ inputSchema: z.object({
12
+ channel_id: z.string().describe('频道 ID'),
13
+ timestamp: z.string().describe('消息时间戳'),
14
+ name: z.string().describe('表情名称(如 thumbsup、heart)'),
15
+ }),
16
+ adapter: 'slack',
17
+ tags: ['slack'],
18
+ async execute({ channel_id, timestamp, name }, context) {
19
+ const client = context.$client;
20
+ try {
21
+ await client.reactions.remove({
22
+ channel: channel_id,
23
+ timestamp,
24
+ name: normalizeSlackReactionName(name),
25
+ });
26
+ } catch (error) {
27
+ if ((error as { data?: { error?: string } })?.data?.error !== 'no_reaction') throw error;
28
+ }
29
+ const success = true;
30
+ return { success, message: success ? `已移除反应 :${name}:` : '操作失败' };
31
+ },
32
+ });
@@ -0,0 +1,23 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { platformPermit } from '../../src/platform-permit.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel_id: string;
7
+ purpose: string;
8
+ }>({
9
+ description: '设置 Slack 频道的用途/目的',
10
+ inputSchema: z.object({
11
+ channel_id: z.string().describe('频道 ID'),
12
+ purpose: z.string().describe('频道用途描述'),
13
+ }),
14
+ adapter: 'slack',
15
+ tags: ['slack'],
16
+ permissions: [platformPermit('channel_manager')],
17
+ async execute({ channel_id, purpose }, context) {
18
+ const client = context.$client;
19
+ await client.conversations.setPurpose({ channel: channel_id, purpose });
20
+ const success = true;
21
+ return { success, message: success ? '频道用途已更新' : '操作失败' };
22
+ },
23
+ });
@@ -0,0 +1,23 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { platformPermit } from '../../src/platform-permit.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel: string;
7
+ topic: string;
8
+ }>({
9
+ description: '设置 Slack 频道话题',
10
+ inputSchema: z.object({
11
+ channel: z.string().describe('频道 ID'),
12
+ topic: z.string().describe('新话题'),
13
+ }),
14
+ adapter: 'slack',
15
+ tags: ['slack'],
16
+ permissions: [platformPermit('channel_manager')],
17
+ async execute({ channel, topic }, context) {
18
+ const client = context.$client;
19
+ await client.conversations.setTopic({ channel, topic });
20
+ const success = true;
21
+ return { success, message: success ? '已设置频道话题' : '操作失败' };
22
+ },
23
+ });
@@ -0,0 +1,21 @@
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { platformPermit } from '../../src/platform-permit.js';
4
+
5
+ export default defineAgentTool<{
6
+ channel_id: string;
7
+ }>({
8
+ description: '恢复已归档的 Slack 频道',
9
+ inputSchema: z.object({
10
+ channel_id: z.string().describe('频道 ID'),
11
+ }),
12
+ adapter: 'slack',
13
+ tags: ['slack'],
14
+ permissions: [platformPermit('workspace_admin')],
15
+ async execute({ channel_id }, context) {
16
+ const client = context.$client;
17
+ await client.conversations.unarchive({ channel: channel_id });
18
+ const success = true;
19
+ return { success, message: success ? '频道已恢复' : '操作失败' };
20
+ },
21
+ });