@zhin.js/adapter-slack 1.0.77 → 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.
- package/CHANGELOG.md +642 -0
- package/README.md +112 -158
- package/adapters/slack.js +40 -0
- package/adapters/slack.ts +45 -0
- package/agent/PERMITS.md +24 -0
- package/{skills/slack/SKILL.md → agent/skills/slack.md} +2 -0
- package/agent/tools/add_reaction.ts +29 -0
- package/agent/tools/archive_channel.ts +21 -0
- package/agent/tools/edit_message.ts +25 -0
- package/agent/tools/invite_to_channel.ts +23 -0
- package/agent/tools/pin_message.ts +23 -0
- package/agent/tools/remove_reaction.ts +32 -0
- package/agent/tools/set_purpose.ts +23 -0
- package/agent/tools/set_topic.ts +23 -0
- package/agent/tools/unarchive.ts +21 -0
- package/agent/tools/unpin_message.ts +23 -0
- package/agent/tools/user_info.ts +29 -0
- package/commands/endpoint/add/[id].js +3 -0
- package/commands/endpoint/add/[id].ts +3 -0
- package/commands/endpoint/list.js +3 -0
- package/commands/endpoint/list.ts +3 -0
- package/commands/endpoint/remove/[id].js +3 -0
- package/commands/endpoint/remove/[id].ts +3 -0
- package/lib/client.d.ts +25 -0
- package/lib/client.js +2 -0
- package/lib/endpoint.d.ts +153 -0
- package/lib/endpoint.js +425 -0
- package/lib/index.d.ts +10 -10
- package/lib/index.js +10 -242
- package/lib/markdown-to-mrkdwn.d.ts +9 -0
- package/lib/markdown-to-mrkdwn.js +121 -0
- package/lib/mrkdwn-to-markdown.d.ts +4 -0
- package/lib/mrkdwn-to-markdown.js +30 -0
- package/lib/platform-permit.d.ts +15 -0
- package/lib/platform-permit.js +43 -0
- package/lib/protocol.d.ts +153 -0
- package/lib/protocol.js +272 -0
- package/lib/side-event-dispatch.d.ts +4 -0
- package/lib/side-event-dispatch.js +45 -0
- package/lib/slack-endpoint-commands.d.ts +1 -0
- package/lib/slack-endpoint-commands.js +18 -0
- package/lib/slack-inbound-filter.d.ts +12 -0
- package/lib/slack-inbound-filter.js +46 -0
- package/lib/slack-message-ref.d.ts +7 -0
- package/lib/slack-message-ref.js +17 -0
- package/lib/slack-outbound.d.ts +24 -0
- package/lib/slack-outbound.js +111 -0
- package/lib/slack-reaction.d.ts +1 -0
- package/lib/slack-reaction.js +26 -0
- package/lib/slack-response-url.d.ts +5 -0
- package/lib/slack-response-url.js +16 -0
- package/lib/slack-runtime-state.d.ts +1 -0
- package/lib/slack-runtime-state.js +6 -0
- package/lib/webhook.d.ts +14 -0
- package/lib/webhook.js +72 -0
- package/package.json +67 -15
- package/plugin.js +19 -0
- package/schema.json +95 -0
- package/src/client.ts +30 -0
- package/src/endpoint.ts +526 -0
- package/src/index.ts +59 -265
- package/src/markdown-to-mrkdwn.ts +117 -0
- package/src/mrkdwn-to-markdown.ts +29 -0
- package/src/platform-permit.ts +59 -0
- package/src/protocol.ts +435 -0
- package/src/side-event-dispatch.ts +54 -0
- package/src/slack-endpoint-commands.ts +19 -0
- package/src/slack-inbound-filter.ts +60 -0
- package/src/slack-message-ref.ts +18 -0
- package/src/slack-outbound.ts +170 -0
- package/src/slack-reaction.ts +23 -0
- package/src/slack-response-url.ts +26 -0
- package/src/slack-runtime-state.ts +7 -0
- package/src/webhook.ts +106 -0
- package/lib/adapter.d.ts +0 -16
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js +0 -43
- package/lib/adapter.js.map +0 -1
- package/lib/bot.d.ts +0 -106
- package/lib/bot.d.ts.map +0 -1
- package/lib/bot.js +0 -628
- package/lib/bot.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/types.d.ts +0 -17
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/plugin.yml +0 -3
- package/src/adapter.ts +0 -52
- package/src/bot.ts +0 -688
- package/src/types.ts +0 -18
package/README.md
CHANGED
|
@@ -1,192 +1,146 @@
|
|
|
1
1
|
# @zhin.js/adapter-slack
|
|
2
2
|
|
|
3
|
-
Slack
|
|
3
|
+
Zhin.js Slack 适配器(Plugin Runtime),优先 Socket Mode,也可经 Runtime Host HTTP Events API 收发消息。
|
|
4
4
|
|
|
5
|
-
##
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
|
20
|
-
| **HTTP Events** | `false` |
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
##
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
103
|
+
通用 Markdown(如 `**粗体**`)会转换为 Slack mrkdwn,并通过 Block Kit `section` 发送。
|
|
165
104
|
|
|
166
|
-
###
|
|
105
|
+
### 入站(mrkdwn → Markdown)
|
|
167
106
|
|
|
168
|
-
Slack
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
107
|
+
| Slack mrkdwn | 通用 Markdown |
|
|
108
|
+
|--------------|---------------|
|
|
109
|
+
| `*bold*` | `**bold**` |
|
|
110
|
+
| `_italic_` | `*italic*` |
|
|
111
|
+
| `~strike~` | `~~strike~~` |
|
|
112
|
+
| `<url\|text>` | `[text](url)` |
|
|
174
113
|
|
|
175
|
-
|
|
114
|
+
## AI 工具
|
|
176
115
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
116
|
+
| 类别 | 路径 |
|
|
117
|
+
|------|------|
|
|
118
|
+
| Permit 词汇 | `agent/PERMITS.md` |
|
|
119
|
+
| 平台工具 | `agent/tools/`(邀请、话题、反应、置顶、编辑等) |
|
|
120
|
+
| 技能说明 | `agent/skills/slack.md` |
|
|
180
121
|
|
|
181
|
-
|
|
122
|
+
## 限制
|
|
182
123
|
|
|
183
|
-
|
|
124
|
+
- 入站 mrkdwn → Markdown 为启发式转换
|
|
125
|
+
- Modals / Select menus — 暂不支持
|
|
126
|
+
- OAuth 安装流程 — 暂不支持
|
|
127
|
+
- 旧 `usePlugin` / `extends Adapter` / host-router 生产入口已删除
|
|
184
128
|
|
|
185
|
-
##
|
|
129
|
+
## 故障排查
|
|
186
130
|
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
##
|
|
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
|
+
});
|
package/agent/PERMITS.md
ADDED
|
@@ -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
|
+
});
|