@zhin.js/adapter-slack 4.0.1 → 4.1.1

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 (80) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +77 -160
  3. package/adapters/slack.ts +34 -0
  4. package/{skills/slack/SKILL.md → agent/skills/slack.md} +2 -0
  5. package/agent/tools/add_reaction.ts +26 -0
  6. package/agent/tools/archive_channel.ts +24 -0
  7. package/agent/tools/edit_message.ts +28 -0
  8. package/agent/tools/invite_to_channel.ts +26 -0
  9. package/agent/tools/pin_message.ts +26 -0
  10. package/agent/tools/remove_reaction.ts +26 -0
  11. package/agent/tools/set_purpose.ts +26 -0
  12. package/agent/tools/set_topic.ts +26 -0
  13. package/agent/tools/unarchive.ts +24 -0
  14. package/agent/tools/unpin_message.ts +26 -0
  15. package/agent/tools/user_info.ts +31 -0
  16. package/lib/endpoint.d.ts +133 -94
  17. package/lib/endpoint.js +270 -612
  18. package/lib/index.d.ts +11 -10
  19. package/lib/index.js +11 -252
  20. package/lib/markdown-to-mrkdwn.d.ts +9 -0
  21. package/lib/markdown-to-mrkdwn.js +121 -0
  22. package/lib/mrkdwn-to-markdown.d.ts +4 -0
  23. package/lib/mrkdwn-to-markdown.js +30 -0
  24. package/lib/platform-permit.d.ts +1 -2
  25. package/lib/platform-permit.js +4 -2
  26. package/lib/protocol.d.ts +143 -0
  27. package/lib/protocol.js +225 -0
  28. package/lib/slack-agent-deps.d.ts +28 -0
  29. package/lib/slack-agent-deps.js +30 -0
  30. package/lib/slack-inbound-filter.d.ts +12 -0
  31. package/lib/slack-inbound-filter.js +46 -0
  32. package/lib/slack-message-ref.d.ts +7 -0
  33. package/lib/slack-message-ref.js +17 -0
  34. package/lib/slack-outbound.d.ts +24 -0
  35. package/lib/slack-outbound.js +109 -0
  36. package/lib/slack-reaction.d.ts +1 -0
  37. package/lib/slack-reaction.js +26 -0
  38. package/lib/slack-response-url.d.ts +5 -0
  39. package/lib/slack-response-url.js +16 -0
  40. package/lib/webhook.d.ts +14 -0
  41. package/lib/webhook.js +69 -0
  42. package/package.json +53 -14
  43. package/plugin.ts +13 -0
  44. package/schema.json +38 -0
  45. package/src/endpoint.ts +339 -647
  46. package/src/index.ts +65 -277
  47. package/src/markdown-to-mrkdwn.ts +117 -0
  48. package/src/mrkdwn-to-markdown.ts +29 -0
  49. package/src/platform-permit.ts +1 -2
  50. package/src/protocol.ts +380 -0
  51. package/src/slack-agent-deps.ts +57 -0
  52. package/src/slack-inbound-filter.ts +60 -0
  53. package/src/slack-message-ref.ts +18 -0
  54. package/src/slack-outbound.ts +167 -0
  55. package/src/slack-reaction.ts +23 -0
  56. package/src/slack-response-url.ts +26 -0
  57. package/src/webhook.ts +103 -0
  58. package/lib/adapter.d.ts +0 -19
  59. package/lib/adapter.d.ts.map +0 -1
  60. package/lib/adapter.js +0 -46
  61. package/lib/adapter.js.map +0 -1
  62. package/lib/endpoint.d.ts.map +0 -1
  63. package/lib/endpoint.js.map +0 -1
  64. package/lib/index.d.ts.map +0 -1
  65. package/lib/index.js.map +0 -1
  66. package/lib/platform-permit.d.ts.map +0 -1
  67. package/lib/platform-permit.js.map +0 -1
  68. package/lib/segment-mapper.d.ts +0 -2
  69. package/lib/segment-mapper.d.ts.map +0 -1
  70. package/lib/segment-mapper.js +0 -2
  71. package/lib/segment-mapper.js.map +0 -1
  72. package/lib/types.d.ts +0 -17
  73. package/lib/types.d.ts.map +0 -1
  74. package/lib/types.js +0 -2
  75. package/lib/types.js.map +0 -1
  76. package/plugin.yml +0 -3
  77. package/src/adapter.ts +0 -54
  78. package/src/segment-mapper.ts +0 -1
  79. package/src/types.ts +0 -18
  80. /package/{skills/slack → agent}/PERMITS.md +0 -0
package/src/index.ts CHANGED
@@ -1,280 +1,68 @@
1
- /**
2
- * Slack 适配器入口:类型扩展、导出、注册
3
- */
4
- import { usePlugin, type Plugin, type ISceneManagement, createSceneManagementTools, type ToolFeature } from "zhin.js";
5
- import { SlackAdapter } from "./adapter.js";
6
- import {
1
+ export {
2
+ formatInboundContent,
3
+ formatInteractionContent,
4
+ formatOutboundWire,
5
+ formatSlashContent,
6
+ headerValue,
7
+ inboundMessageId,
8
+ keyboardToBlockKitBlocks,
9
+ normalizeWebhookPath,
10
+ readTextBody,
11
+ resolveSlackChannelType,
12
+ resolveSlackConfig,
13
+ verifySlackSignature,
14
+ type ResolvedSlackConfig,
15
+ type SlackAdapterConfig,
16
+ type SlackBlockAction,
17
+ type SlackEvent,
18
+ type SlackEventEnvelope,
19
+ type SlackInteractionPayload,
20
+ type SlackMessageEvent,
21
+ type SlackSlashCommand,
22
+ type SlackUrlVerification,
23
+ type SlackWireSegment,
24
+ } from './protocol.js';
25
+
26
+ export {
27
+ SlackEndpoint,
28
+ type SlackEndpointOptions,
29
+ type SlackSocketLike,
30
+ type SlackWebClientLike,
31
+ } from './endpoint.js';
32
+
33
+ export {
34
+ registerSlackWebhookRoutes,
35
+ handleSlackWebhookRequest,
36
+ type SlackWebhookHandler,
37
+ } from './webhook.js';
38
+
39
+ export {
40
+ getSlackAgentDeps,
41
+ registerSlackAgentEndpoint,
42
+ setSlackAgentDeps,
43
+ type SlackAgentDeps,
44
+ type SlackAgentEndpoint,
45
+ } from './slack-agent-deps.js';
46
+
47
+ export {
48
+ checkSlackPlatformPermit,
49
+ normalizeSlackSenderForPermit,
7
50
  platformPermit,
8
51
  registerSlackPlatformPermitChecker,
9
52
  slackGroupPermitResolver,
10
- } from "./platform-permit.js";
11
-
12
- declare module "zhin.js" {
13
- interface Adapters {
14
- slack: SlackAdapter;
15
- }
16
- }
17
-
18
- export * from "./types.js";
19
- export { SlackEndpoint } from "./endpoint.js";
20
- export { SlackAdapter } from "./adapter.js";
21
-
22
- const plugin = usePlugin();
23
- const { provide, useContext } = plugin;
24
-
25
- provide({
26
- name: "slack",
27
- description: "Slack Endpoint Adapter",
28
- mounted: async (p: Plugin) => {
29
- const adapter = new SlackAdapter(p);
30
- await adapter.start();
31
- return adapter;
32
- },
33
- dispose: async (adapter: SlackAdapter) => {
34
- await adapter.stop();
35
- },
36
- });
37
-
38
- useContext('tool', 'slack', (toolService: ToolFeature, slack: SlackAdapter) => {
39
- const disposers: (() => void)[] = [];
40
- disposers.push(registerSlackPlatformPermitChecker());
41
- const sceneTools = createSceneManagementTools(
42
- slack as unknown as ISceneManagement,
43
- 'slack',
44
- { permitResolver: slackGroupPermitResolver, registerChecker: false },
45
- );
46
- disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
47
-
48
- function getEndpoint(endpointId: string) {
49
- const endpoint = slack.endpoints.get(endpointId);
50
- if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
51
- return endpoint;
52
- }
53
-
54
- disposers.push(toolService.addTool({
55
- name: 'slack_invite_to_channel',
56
- description: '邀请用户加入 Slack 频道',
57
- parameters: {
58
- type: 'object',
59
- properties: {
60
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
61
- channel: { type: 'string', description: '频道 ID' },
62
- users: { type: 'string', description: '用户 ID 列表(逗号分隔)' },
63
- },
64
- required: ['endpoint_id', 'channel', 'users'],
65
- },
66
- platforms: ['slack'],
67
- tags: ['slack'],
68
- permissions: [platformPermit('channel_manager')],
69
- execute: async (args: Record<string, any>) => {
70
- const endpoint = getEndpoint(args.endpoint_id);
71
- const success = await endpoint.inviteToChannel(args.channel, args.users.split(','));
72
- return { success, message: success ? '已邀请用户加入频道' : '操作失败' };
73
- },
74
- }, plugin.name));
75
-
76
- disposers.push(toolService.addTool({
77
- name: 'slack_set_topic',
78
- description: '设置 Slack 频道话题',
79
- parameters: {
80
- type: 'object',
81
- properties: {
82
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
83
- channel: { type: 'string', description: '频道 ID' },
84
- topic: { type: 'string', description: '新话题' },
85
- },
86
- required: ['endpoint_id', 'channel', 'topic'],
87
- },
88
- platforms: ['slack'],
89
- tags: ['slack'],
90
- permissions: [platformPermit('channel_manager')],
91
- execute: async (args: Record<string, any>) => {
92
- const endpoint = getEndpoint(args.endpoint_id);
93
- const success = await endpoint.setChannelTopic(args.channel, args.topic);
94
- return { success, message: success ? '已设置频道话题' : '操作失败' };
95
- },
96
- }, plugin.name));
97
-
98
- disposers.push(toolService.addTool({
99
- name: 'slack_archive_channel',
100
- description: '归档 Slack 频道',
101
- parameters: {
102
- type: 'object',
103
- properties: {
104
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
105
- channel: { type: 'string', description: '频道 ID' },
106
- },
107
- required: ['endpoint_id', 'channel'],
108
- },
109
- platforms: ['slack'],
110
- tags: ['slack'],
111
- permissions: [platformPermit('workspace_admin')],
112
- execute: async (args: Record<string, any>) => {
113
- const endpoint = getEndpoint(args.endpoint_id);
114
- const success = await endpoint.archiveChannel(args.channel);
115
- return { success, message: success ? '已归档频道' : '操作失败' };
116
- },
117
- }, plugin.name));
118
-
119
- disposers.push(toolService.addTool({
120
- name: 'slack_pin_message',
121
- description: '置顶 Slack 消息',
122
- parameters: {
123
- type: 'object',
124
- properties: {
125
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
126
- channel: { type: 'string', description: '频道 ID' },
127
- timestamp: { type: 'string', description: '消息时间戳' },
128
- },
129
- required: ['endpoint_id', 'channel', 'timestamp'],
130
- },
131
- platforms: ['slack'],
132
- tags: ['slack'],
133
- permissions: [platformPermit('channel_manager')],
134
- execute: async (args: Record<string, any>) => {
135
- const endpoint = getEndpoint(args.endpoint_id);
136
- const success = await endpoint.pinMessage(args.channel, args.timestamp);
137
- return { success, message: success ? '已置顶消息' : '操作失败' };
138
- },
139
- }, plugin.name));
140
-
141
- disposers.push(toolService.addTool({
142
- name: 'slack_add_reaction',
143
- description: '给 Slack 消息添加表情反应',
144
- parameters: {
145
- type: 'object',
146
- properties: {
147
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
148
- channel: { type: 'string', description: '频道 ID' },
149
- timestamp: { type: 'string', description: '消息时间戳' },
150
- emoji: { type: 'string', description: '表情名称(不含冒号)' },
151
- },
152
- required: ['endpoint_id', 'channel', 'timestamp', 'emoji'],
153
- },
154
- platforms: ['slack'],
155
- tags: ['slack'],
156
- execute: async (args: Record<string, any>) => {
157
- const endpoint = getEndpoint(args.endpoint_id);
158
- const success = await endpoint.addReaction(args.channel, args.timestamp, args.emoji);
159
- return { success, message: success ? `已添加反应 :${args.emoji}:` : '操作失败' };
160
- },
161
- }, plugin.name));
162
-
163
- disposers.push(toolService.addTool({
164
- name: 'slack_remove_reaction',
165
- description: '移除 Slack 消息上的表情反应',
166
- parameters: {
167
- type: 'object',
168
- properties: {
169
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
170
- channel_id: { type: 'string', description: '频道 ID' },
171
- timestamp: { type: 'string', description: '消息时间戳' },
172
- name: { type: 'string', description: '表情名称(如 thumbsup、heart)' },
173
- },
174
- required: ['endpoint_id', 'channel_id', 'timestamp', 'name'],
175
- },
176
- platforms: ['slack'],
177
- tags: ['slack'],
178
- execute: async (args: Record<string, any>) => {
179
- const endpoint = getEndpoint(args.endpoint_id);
180
- const success = await endpoint.removeReaction(args.channel_id, args.timestamp, args.name);
181
- return { success, message: success ? `已移除反应 :${args.name}:` : '操作失败' };
182
- },
183
- }, plugin.name));
184
-
185
- disposers.push(toolService.addTool({
186
- name: 'slack_unpin_message',
187
- description: '取消 Slack 频道中消息的置顶',
188
- parameters: {
189
- type: 'object',
190
- properties: {
191
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
192
- channel_id: { type: 'string', description: '频道 ID' },
193
- timestamp: { type: 'string', description: '消息时间戳' },
194
- },
195
- required: ['endpoint_id', 'channel_id', 'timestamp'],
196
- },
197
- platforms: ['slack'],
198
- tags: ['slack'],
199
- permissions: [platformPermit('channel_manager')],
200
- execute: async (args: Record<string, any>) => {
201
- const endpoint = getEndpoint(args.endpoint_id);
202
- const success = await endpoint.unpinMessage(args.channel_id, args.timestamp);
203
- return { success, message: success ? '已取消置顶' : '操作失败' };
204
- },
205
- }, plugin.name));
206
-
207
- disposers.push(toolService.addTool({
208
- name: 'slack_user_info',
209
- description: '查询 Slack 用户详细信息',
210
- parameters: {
211
- type: 'object',
212
- properties: {
213
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
214
- user_id: { type: 'string', description: '用户 ID' },
215
- },
216
- required: ['endpoint_id', 'user_id'],
217
- },
218
- platforms: ['slack'],
219
- tags: ['slack'],
220
- execute: async (args: Record<string, any>) => {
221
- const endpoint = getEndpoint(args.endpoint_id);
222
- const user = await endpoint.getUserInfo(args.user_id);
223
- return {
224
- id: user.id,
225
- name: user.name,
226
- real_name: user.real_name,
227
- display_name: user.profile?.display_name,
228
- email: user.profile?.email,
229
- is_admin: user.is_admin,
230
- is_bot: user.is_bot,
231
- status_text: user.profile?.status_text,
232
- };
233
- },
234
- }, plugin.name));
235
-
236
- disposers.push(toolService.addTool({
237
- name: 'slack_set_purpose',
238
- description: '设置 Slack 频道的用途/目的',
239
- parameters: {
240
- type: 'object',
241
- properties: {
242
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
243
- channel_id: { type: 'string', description: '频道 ID' },
244
- purpose: { type: 'string', description: '频道用途描述' },
245
- },
246
- required: ['endpoint_id', 'channel_id', 'purpose'],
247
- },
248
- platforms: ['slack'],
249
- tags: ['slack'],
250
- permissions: [platformPermit('channel_manager')],
251
- execute: async (args: Record<string, any>) => {
252
- const endpoint = getEndpoint(args.endpoint_id);
253
- const success = await endpoint.setChannelPurpose(args.channel_id, args.purpose);
254
- return { success, message: success ? '频道用途已更新' : '操作失败' };
255
- },
256
- }, plugin.name));
257
-
258
- disposers.push(toolService.addTool({
259
- name: 'slack_unarchive',
260
- description: '恢复已归档的 Slack 频道',
261
- parameters: {
262
- type: 'object',
263
- properties: {
264
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
265
- channel_id: { type: 'string', description: '频道 ID' },
266
- },
267
- required: ['endpoint_id', 'channel_id'],
268
- },
269
- platforms: ['slack'],
270
- tags: ['slack'],
271
- permissions: [platformPermit('workspace_admin')],
272
- execute: async (args: Record<string, any>) => {
273
- const endpoint = getEndpoint(args.endpoint_id);
274
- const success = await endpoint.unarchiveChannel(args.channel_id);
275
- return { success, message: success ? '频道已恢复' : '操作失败' };
276
- },
277
- }, plugin.name));
278
-
279
- return () => disposers.forEach(d => d());
280
- });
53
+ } from './platform-permit.js';
54
+
55
+ export {
56
+ formatSlackMessageRef,
57
+ parseSlackMessageRef,
58
+ slackMessageTs,
59
+ } from './slack-message-ref.js';
60
+
61
+ export { normalizeSlackReactionName } from './slack-reaction.js';
62
+ export { markdownToMrkdwn, mrkdwnToPlainFallback, splitMrkdwnText } from './markdown-to-mrkdwn.js';
63
+ export { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
64
+ export {
65
+ createSlackInboundFilterState,
66
+ shouldDropSlackInboundMessage,
67
+ } from './slack-inbound-filter.js';
68
+ export { editSlackContent, sendSlackContent } from './slack-outbound.js';
@@ -0,0 +1,117 @@
1
+ /**
2
+ * 常见 Markdown → Slack mrkdwn(Slack 粗体为 *text*,链接为 <url|text>)
3
+ */
4
+ export const SLACK_MRKDWN_TEXT_MAX = 2900;
5
+
6
+ export function markdownToMrkdwn(text: string): string {
7
+ let result = replaceMarkdownLinks(text);
8
+ result = result.replace(/\*\*([^*]+)\*\*/g, '*$1*');
9
+ result = result.replace(/__([^_]+)__/g, '*$1*');
10
+ result = result.replace(/~~([^~]+)~~/g, '~$1~');
11
+ result = result.replace(/^#{1,6}\s+(.+)$/gm, '*$1*');
12
+ return result;
13
+ }
14
+
15
+ function replaceMarkdownLinks(text: string): string {
16
+ let out = '';
17
+ let cursor = 0;
18
+ while (cursor < text.length) {
19
+ const labelStart = text.indexOf('[', cursor);
20
+ if (labelStart < 0) break;
21
+ const labelEnd = text.indexOf(']', labelStart + 1);
22
+ if (labelEnd < 0 || text[labelEnd + 1] !== '(') {
23
+ out += text.slice(cursor, labelStart + 1);
24
+ cursor = labelStart + 1;
25
+ continue;
26
+ }
27
+ const urlEnd = text.indexOf(')', labelEnd + 2);
28
+ if (urlEnd < 0) break;
29
+ out += text.slice(cursor, labelStart);
30
+ out += `<${text.slice(labelEnd + 2, urlEnd)}|${text.slice(labelStart + 1, labelEnd)}>`;
31
+ cursor = urlEnd + 1;
32
+ }
33
+ return out + text.slice(cursor);
34
+ }
35
+
36
+ /** 按 Slack section mrkdwn 上限切分(尽量在换行/空格处断开) */
37
+ export function splitMrkdwnText(text: string, maxLen = SLACK_MRKDWN_TEXT_MAX): string[] {
38
+ if (text.length <= maxLen) return [text];
39
+ const chunks: string[] = [];
40
+ let rest = text;
41
+ while (rest.length > maxLen) {
42
+ let cut = rest.lastIndexOf('\n', maxLen);
43
+ if (cut < maxLen * 0.4) cut = rest.lastIndexOf(' ', maxLen);
44
+ if (cut <= 0) cut = maxLen;
45
+ chunks.push(rest.slice(0, cut).trimEnd());
46
+ rest = rest.slice(cut).trimStart();
47
+ }
48
+ if (rest) chunks.push(rest);
49
+ return chunks.length > 0 ? chunks : [text];
50
+ }
51
+
52
+ /** 通知栏 / 无障碍 fallback 用的纯文本 */
53
+ export function mrkdwnToPlainFallback(text: string): string {
54
+ return stripMrkdwnMarkers(stripAngleBrackets(rewriteAngleLinks(text)));
55
+ }
56
+
57
+ /**
58
+ * `<url|text>` → `text (url)`。
59
+ * 线性扫描,语义等价于 `/<([^|>]+)\|([^>]+)>/g`(分组一不含 `|`,
60
+ * 因此切分点恒为首个 `|`),但避免量词重叠在超长 `<...` 输入上的
61
+ * 二次方回溯(js/polynomial-redos)。
62
+ */
63
+ function rewriteAngleLinks(text: string): string {
64
+ let out = '';
65
+ let i = 0;
66
+ while (i < text.length) {
67
+ const open = text.indexOf('<', i);
68
+ if (open < 0) break;
69
+ const close = text.indexOf('>', open + 1);
70
+ if (close < 0) break;
71
+ const inner = text.slice(open + 1, close);
72
+ const pipe = inner.indexOf('|');
73
+ if (pipe > 0 && pipe < inner.length - 1) {
74
+ out += text.slice(i, open);
75
+ out += `${inner.slice(pipe + 1)} (${inner.slice(0, pipe)})`;
76
+ i = close + 1;
77
+ continue;
78
+ }
79
+ // 非链接形态:本趟不匹配,原样保留 `<` 并继续(与原正则行为一致)。
80
+ out += text.slice(i, open + 1);
81
+ i = open + 1;
82
+ }
83
+ return out + text.slice(i);
84
+ }
85
+
86
+ /**
87
+ * 剥掉剩余 `<...>` 的尖括号。线性扫描,等价于 `/<([^>]+)>/g`
88
+ * (空内容 `<>` 不匹配、原样保留)。
89
+ */
90
+ function stripAngleBrackets(text: string): string {
91
+ let out = '';
92
+ let i = 0;
93
+ while (i < text.length) {
94
+ const open = text.indexOf('<', i);
95
+ if (open < 0) break;
96
+ const close = text.indexOf('>', open + 1);
97
+ if (close < 0) break;
98
+ if (close === open + 1) {
99
+ out += text.slice(i, open + 1);
100
+ i = open + 1;
101
+ continue;
102
+ }
103
+ out += text.slice(i, open);
104
+ out += text.slice(open + 1, close);
105
+ i = close + 1;
106
+ }
107
+ return out + text.slice(i);
108
+ }
109
+
110
+ /** 去掉 `*` `_` `~` `` ` `` 样式符号(等价于 `/[*_~`]/g`,逐字符线性)。 */
111
+ function stripMrkdwnMarkers(text: string): string {
112
+ let out = '';
113
+ for (const ch of text) {
114
+ if (ch !== '*' && ch !== '_' && ch !== '~' && ch !== '`') out += ch;
115
+ }
116
+ return out;
117
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Slack mrkdwn → 通用 Markdown(入站文本段)
3
+ */
4
+ export function mrkdwnToMarkdown(text: string): string {
5
+ let result = replaceSlackLinks(text);
6
+ result = result.replace(/\*([^*]+)\*/g, '**$1**');
7
+ result = result.replace(/_([^_]+)_/g, '*$1*');
8
+ result = result.replace(/~([^~]+)~/g, '~~$1~~');
9
+ return result;
10
+ }
11
+
12
+ function replaceSlackLinks(text: string): string {
13
+ let out = '';
14
+ let cursor = 0;
15
+ while (cursor < text.length) {
16
+ const start = text.indexOf('<', cursor);
17
+ if (start < 0) break;
18
+ const end = text.indexOf('>', start + 1);
19
+ if (end < 0) break;
20
+ out += text.slice(cursor, start);
21
+ const body = text.slice(start + 1, end);
22
+ const sep = body.indexOf('|');
23
+ const url = sep >= 0 ? body.slice(0, sep) : body;
24
+ const label = sep >= 0 ? body.slice(sep + 1) : url;
25
+ out += url.startsWith('http://') || url.startsWith('https://') ? `[${label}](${url})` : text.slice(start, end + 1);
26
+ cursor = end + 1;
27
+ }
28
+ return out + text.slice(cursor);
29
+ }
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Slack platform permit — workspace + channel 权限
3
3
  */
4
- import type { Message } from 'zhin.js';
5
- import { registerPlatformPermitChecker } from 'zhin.js';
4
+ import { registerPlatformPermitChecker, type Message } from '@zhin.js/core';
6
5
 
7
6
  const ADAPTER = 'slack';
8
7