@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.
- package/CHANGELOG.md +731 -2
- package/PERMITS.md +24 -0
- package/README.md +111 -158
- package/adapters/slack/index.js +40 -0
- package/adapters/slack/index.ts +45 -0
- package/agents/slack/agent.json +20 -0
- package/agents/slack/boundaries.md +3 -0
- package/agents/slack/conventions.md +3 -0
- package/agents/slack/skills/slack-channels/SKILL.md +24 -0
- package/agents/slack/skills/slack-channels/tools/archive_channel/index.js +19 -0
- package/agents/slack/skills/slack-channels/tools/archive_channel/index.ts +21 -0
- package/agents/slack/skills/slack-channels/tools/invite_to_channel/index.js +20 -0
- package/agents/slack/skills/slack-channels/tools/invite_to_channel/index.ts +23 -0
- package/agents/slack/skills/slack-channels/tools/set_purpose/index.js +20 -0
- package/agents/slack/skills/slack-channels/tools/set_purpose/index.ts +23 -0
- package/agents/slack/skills/slack-channels/tools/set_topic/index.js +20 -0
- package/agents/slack/skills/slack-channels/tools/set_topic/index.ts +23 -0
- package/agents/slack/skills/slack-channels/tools/unarchive/index.js +19 -0
- package/agents/slack/skills/slack-channels/tools/unarchive/index.ts +21 -0
- package/agents/slack/skills/slack-channels/tools/user_info/index.js +27 -0
- package/agents/slack/skills/slack-channels/tools/user_info/index.ts +29 -0
- package/agents/slack/skills/slack-messages/SKILL.md +22 -0
- package/agents/slack/skills/slack-messages/tools/add_reaction/index.js +27 -0
- package/agents/slack/skills/slack-messages/tools/add_reaction/index.ts +29 -0
- package/agents/slack/skills/slack-messages/tools/edit_message/index.js +21 -0
- package/agents/slack/skills/slack-messages/tools/edit_message/index.ts +25 -0
- package/agents/slack/skills/slack-messages/tools/pin_message/index.js +20 -0
- package/agents/slack/skills/slack-messages/tools/pin_message/index.ts +23 -0
- package/agents/slack/skills/slack-messages/tools/remove_reaction/index.js +30 -0
- package/agents/slack/skills/slack-messages/tools/remove_reaction/index.ts +32 -0
- package/agents/slack/skills/slack-messages/tools/unpin_message/index.js +20 -0
- package/agents/slack/skills/slack-messages/tools/unpin_message/index.ts +23 -0
- package/agents/slack/system.md +3 -0
- package/commands/slack/endpoint/add/[id]/index.js +3 -0
- package/commands/slack/endpoint/add/[id]/index.ts +3 -0
- package/commands/slack/endpoint/definition.js +19 -0
- package/commands/slack/endpoint/definition.ts +19 -0
- package/commands/slack/endpoint/list/index.js +3 -0
- package/commands/slack/endpoint/list/index.ts +3 -0
- package/commands/slack/endpoint/remove/[id]/index.js +3 -0
- package/commands/slack/endpoint/remove/[id]/index.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 +144 -0
- package/lib/protocol.js +258 -0
- package/lib/side-event-dispatch.d.ts +4 -0
- package/lib/side-event-dispatch.js +45 -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 +80 -16
- package/plugin.js +19 -0
- package/schema.json +108 -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 +406 -0
- package/src/side-event-dispatch.ts +54 -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/skills/slack/SKILL.md +0 -91
- package/src/adapter.ts +0 -52
- package/src/bot.ts +0 -688
- package/src/types.ts +0 -18
package/src/index.ts
CHANGED
|
@@ -1,265 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
execute: async (args: Record<string, any>) => {
|
|
61
|
-
const bot = getBot(args.bot);
|
|
62
|
-
const success = await bot.inviteToChannel(args.channel, args.users.split(','));
|
|
63
|
-
return { success, message: success ? '已邀请用户加入频道' : '操作失败' };
|
|
64
|
-
},
|
|
65
|
-
}, plugin.name));
|
|
66
|
-
|
|
67
|
-
disposers.push(toolService.addTool({
|
|
68
|
-
name: 'slack_set_topic',
|
|
69
|
-
description: '设置 Slack 频道话题',
|
|
70
|
-
parameters: {
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: {
|
|
73
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
74
|
-
channel: { type: 'string', description: '频道 ID' },
|
|
75
|
-
topic: { type: 'string', description: '新话题' },
|
|
76
|
-
},
|
|
77
|
-
required: ['bot', 'channel', 'topic'],
|
|
78
|
-
},
|
|
79
|
-
platforms: ['slack'],
|
|
80
|
-
tags: ['slack'],
|
|
81
|
-
execute: async (args: Record<string, any>) => {
|
|
82
|
-
const bot = getBot(args.bot);
|
|
83
|
-
const success = await bot.setChannelTopic(args.channel, args.topic);
|
|
84
|
-
return { success, message: success ? '已设置频道话题' : '操作失败' };
|
|
85
|
-
},
|
|
86
|
-
}, plugin.name));
|
|
87
|
-
|
|
88
|
-
disposers.push(toolService.addTool({
|
|
89
|
-
name: 'slack_archive_channel',
|
|
90
|
-
description: '归档 Slack 频道',
|
|
91
|
-
parameters: {
|
|
92
|
-
type: 'object',
|
|
93
|
-
properties: {
|
|
94
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
95
|
-
channel: { type: 'string', description: '频道 ID' },
|
|
96
|
-
},
|
|
97
|
-
required: ['bot', 'channel'],
|
|
98
|
-
},
|
|
99
|
-
platforms: ['slack'],
|
|
100
|
-
tags: ['slack'],
|
|
101
|
-
execute: async (args: Record<string, any>) => {
|
|
102
|
-
const bot = getBot(args.bot);
|
|
103
|
-
const success = await bot.archiveChannel(args.channel);
|
|
104
|
-
return { success, message: success ? '已归档频道' : '操作失败' };
|
|
105
|
-
},
|
|
106
|
-
}, plugin.name));
|
|
107
|
-
|
|
108
|
-
disposers.push(toolService.addTool({
|
|
109
|
-
name: 'slack_pin_message',
|
|
110
|
-
description: '置顶 Slack 消息',
|
|
111
|
-
parameters: {
|
|
112
|
-
type: 'object',
|
|
113
|
-
properties: {
|
|
114
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
115
|
-
channel: { type: 'string', description: '频道 ID' },
|
|
116
|
-
timestamp: { type: 'string', description: '消息时间戳' },
|
|
117
|
-
},
|
|
118
|
-
required: ['bot', 'channel', 'timestamp'],
|
|
119
|
-
},
|
|
120
|
-
platforms: ['slack'],
|
|
121
|
-
tags: ['slack'],
|
|
122
|
-
execute: async (args: Record<string, any>) => {
|
|
123
|
-
const bot = getBot(args.bot);
|
|
124
|
-
const success = await bot.pinMessage(args.channel, args.timestamp);
|
|
125
|
-
return { success, message: success ? '已置顶消息' : '操作失败' };
|
|
126
|
-
},
|
|
127
|
-
}, plugin.name));
|
|
128
|
-
|
|
129
|
-
disposers.push(toolService.addTool({
|
|
130
|
-
name: 'slack_add_reaction',
|
|
131
|
-
description: '给 Slack 消息添加表情反应',
|
|
132
|
-
parameters: {
|
|
133
|
-
type: 'object',
|
|
134
|
-
properties: {
|
|
135
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
136
|
-
channel: { type: 'string', description: '频道 ID' },
|
|
137
|
-
timestamp: { type: 'string', description: '消息时间戳' },
|
|
138
|
-
emoji: { type: 'string', description: '表情名称(不含冒号)' },
|
|
139
|
-
},
|
|
140
|
-
required: ['bot', 'channel', 'timestamp', 'emoji'],
|
|
141
|
-
},
|
|
142
|
-
platforms: ['slack'],
|
|
143
|
-
tags: ['slack'],
|
|
144
|
-
execute: async (args: Record<string, any>) => {
|
|
145
|
-
const bot = getBot(args.bot);
|
|
146
|
-
const success = await bot.addReaction(args.channel, args.timestamp, args.emoji);
|
|
147
|
-
return { success, message: success ? `已添加反应 :${args.emoji}:` : '操作失败' };
|
|
148
|
-
},
|
|
149
|
-
}, plugin.name));
|
|
150
|
-
|
|
151
|
-
disposers.push(toolService.addTool({
|
|
152
|
-
name: 'slack_remove_reaction',
|
|
153
|
-
description: '移除 Slack 消息上的表情反应',
|
|
154
|
-
parameters: {
|
|
155
|
-
type: 'object',
|
|
156
|
-
properties: {
|
|
157
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
158
|
-
channel_id: { type: 'string', description: '频道 ID' },
|
|
159
|
-
timestamp: { type: 'string', description: '消息时间戳' },
|
|
160
|
-
name: { type: 'string', description: '表情名称(如 thumbsup、heart)' },
|
|
161
|
-
},
|
|
162
|
-
required: ['bot', 'channel_id', 'timestamp', 'name'],
|
|
163
|
-
},
|
|
164
|
-
platforms: ['slack'],
|
|
165
|
-
tags: ['slack'],
|
|
166
|
-
execute: async (args: Record<string, any>) => {
|
|
167
|
-
const bot = getBot(args.bot);
|
|
168
|
-
const success = await bot.removeReaction(args.channel_id, args.timestamp, args.name);
|
|
169
|
-
return { success, message: success ? `已移除反应 :${args.name}:` : '操作失败' };
|
|
170
|
-
},
|
|
171
|
-
}, plugin.name));
|
|
172
|
-
|
|
173
|
-
disposers.push(toolService.addTool({
|
|
174
|
-
name: 'slack_unpin_message',
|
|
175
|
-
description: '取消 Slack 频道中消息的置顶',
|
|
176
|
-
parameters: {
|
|
177
|
-
type: 'object',
|
|
178
|
-
properties: {
|
|
179
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
180
|
-
channel_id: { type: 'string', description: '频道 ID' },
|
|
181
|
-
timestamp: { type: 'string', description: '消息时间戳' },
|
|
182
|
-
},
|
|
183
|
-
required: ['bot', 'channel_id', 'timestamp'],
|
|
184
|
-
},
|
|
185
|
-
platforms: ['slack'],
|
|
186
|
-
tags: ['slack'],
|
|
187
|
-
execute: async (args: Record<string, any>) => {
|
|
188
|
-
const bot = getBot(args.bot);
|
|
189
|
-
const success = await bot.unpinMessage(args.channel_id, args.timestamp);
|
|
190
|
-
return { success, message: success ? '已取消置顶' : '操作失败' };
|
|
191
|
-
},
|
|
192
|
-
}, plugin.name));
|
|
193
|
-
|
|
194
|
-
disposers.push(toolService.addTool({
|
|
195
|
-
name: 'slack_user_info',
|
|
196
|
-
description: '查询 Slack 用户详细信息',
|
|
197
|
-
parameters: {
|
|
198
|
-
type: 'object',
|
|
199
|
-
properties: {
|
|
200
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
201
|
-
user_id: { type: 'string', description: '用户 ID' },
|
|
202
|
-
},
|
|
203
|
-
required: ['bot', 'user_id'],
|
|
204
|
-
},
|
|
205
|
-
platforms: ['slack'],
|
|
206
|
-
tags: ['slack'],
|
|
207
|
-
execute: async (args: Record<string, any>) => {
|
|
208
|
-
const bot = getBot(args.bot);
|
|
209
|
-
const user = await bot.getUserInfo(args.user_id);
|
|
210
|
-
return {
|
|
211
|
-
id: user.id,
|
|
212
|
-
name: user.name,
|
|
213
|
-
real_name: user.real_name,
|
|
214
|
-
display_name: user.profile?.display_name,
|
|
215
|
-
email: user.profile?.email,
|
|
216
|
-
is_admin: user.is_admin,
|
|
217
|
-
is_bot: user.is_bot,
|
|
218
|
-
status_text: user.profile?.status_text,
|
|
219
|
-
};
|
|
220
|
-
},
|
|
221
|
-
}, plugin.name));
|
|
222
|
-
|
|
223
|
-
disposers.push(toolService.addTool({
|
|
224
|
-
name: 'slack_set_purpose',
|
|
225
|
-
description: '设置 Slack 频道的用途/目的',
|
|
226
|
-
parameters: {
|
|
227
|
-
type: 'object',
|
|
228
|
-
properties: {
|
|
229
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
230
|
-
channel_id: { type: 'string', description: '频道 ID' },
|
|
231
|
-
purpose: { type: 'string', description: '频道用途描述' },
|
|
232
|
-
},
|
|
233
|
-
required: ['bot', 'channel_id', 'purpose'],
|
|
234
|
-
},
|
|
235
|
-
platforms: ['slack'],
|
|
236
|
-
tags: ['slack'],
|
|
237
|
-
execute: async (args: Record<string, any>) => {
|
|
238
|
-
const bot = getBot(args.bot);
|
|
239
|
-
const success = await bot.setChannelPurpose(args.channel_id, args.purpose);
|
|
240
|
-
return { success, message: success ? '频道用途已更新' : '操作失败' };
|
|
241
|
-
},
|
|
242
|
-
}, plugin.name));
|
|
243
|
-
|
|
244
|
-
disposers.push(toolService.addTool({
|
|
245
|
-
name: 'slack_unarchive',
|
|
246
|
-
description: '恢复已归档的 Slack 频道',
|
|
247
|
-
parameters: {
|
|
248
|
-
type: 'object',
|
|
249
|
-
properties: {
|
|
250
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
251
|
-
channel_id: { type: 'string', description: '频道 ID' },
|
|
252
|
-
},
|
|
253
|
-
required: ['bot', 'channel_id'],
|
|
254
|
-
},
|
|
255
|
-
platforms: ['slack'],
|
|
256
|
-
tags: ['slack'],
|
|
257
|
-
execute: async (args: Record<string, any>) => {
|
|
258
|
-
const bot = getBot(args.bot);
|
|
259
|
-
const success = await bot.unarchiveChannel(args.channel_id);
|
|
260
|
-
return { success, message: success ? '频道已恢复' : '操作失败' };
|
|
261
|
-
},
|
|
262
|
-
}, plugin.name));
|
|
263
|
-
|
|
264
|
-
return () => disposers.forEach(d => d());
|
|
265
|
-
});
|
|
1
|
+
export {
|
|
2
|
+
formatInboundContent,
|
|
3
|
+
formatInteractionContent,
|
|
4
|
+
formatOutboundWire,
|
|
5
|
+
formatSlashContent,
|
|
6
|
+
headerValue,
|
|
7
|
+
keyboardToBlockKitBlocks,
|
|
8
|
+
normalizeWebhookPath,
|
|
9
|
+
readTextBody,
|
|
10
|
+
resolveSlackChannelType,
|
|
11
|
+
resolveSlackConfig,
|
|
12
|
+
slackInboundConversation,
|
|
13
|
+
verifySlackSignature,
|
|
14
|
+
type ResolvedSlackConfig,
|
|
15
|
+
type SlackEndpointConfig,
|
|
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
|
+
slackClient,
|
|
41
|
+
type SlackClientEventMap,
|
|
42
|
+
type SlackUserInfo,
|
|
43
|
+
} from './client.js';
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
checkSlackPlatformPermit,
|
|
47
|
+
normalizeSlackSenderForPermit,
|
|
48
|
+
platformPermit,
|
|
49
|
+
slackGroupPermitResolver,
|
|
50
|
+
} from './platform-permit.js';
|
|
51
|
+
|
|
52
|
+
export { normalizeSlackReactionName } from './slack-reaction.js';
|
|
53
|
+
export { markdownToMrkdwn, mrkdwnToPlainFallback, splitMrkdwnText } from './markdown-to-mrkdwn.js';
|
|
54
|
+
export { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
|
|
55
|
+
export {
|
|
56
|
+
createSlackInboundFilterState,
|
|
57
|
+
shouldDropSlackInboundMessage,
|
|
58
|
+
} from './slack-inbound-filter.js';
|
|
59
|
+
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
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack platform permit — workspace + channel 权限
|
|
3
|
+
*/
|
|
4
|
+
import type { PermissionSubject } from '@zhin.js/permission';
|
|
5
|
+
|
|
6
|
+
const ADAPTER = 'slack';
|
|
7
|
+
|
|
8
|
+
export function platformPermit(perm: string): string {
|
|
9
|
+
return `platform(${ADAPTER},${perm})`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const FACTORY_PERM_MAP: Record<string, string> = {
|
|
13
|
+
scene_admin: 'channel_manager',
|
|
14
|
+
scene_owner: 'workspace_owner',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function slackGroupPermitResolver(logicalPerm: string): string {
|
|
18
|
+
return platformPermit(FACTORY_PERM_MAP[logicalPerm] ?? logicalPerm);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function normalizeSlackSenderForPermit(input: {
|
|
22
|
+
isWorkspaceOwner?: boolean;
|
|
23
|
+
isWorkspaceAdmin?: boolean;
|
|
24
|
+
isChannelManager?: boolean;
|
|
25
|
+
}): { role?: string; permissions?: string[] } {
|
|
26
|
+
const permissions: string[] = [];
|
|
27
|
+
if (input.isWorkspaceOwner) {
|
|
28
|
+
permissions.push('workspace_owner', 'workspace_admin', 'channel_manager');
|
|
29
|
+
return { role: 'owner', permissions };
|
|
30
|
+
}
|
|
31
|
+
if (input.isWorkspaceAdmin) {
|
|
32
|
+
permissions.push('workspace_admin', 'channel_manager');
|
|
33
|
+
return { role: 'admin', permissions };
|
|
34
|
+
}
|
|
35
|
+
if (input.isChannelManager) {
|
|
36
|
+
permissions.push('channel_manager');
|
|
37
|
+
return { role: 'channel_admin', permissions };
|
|
38
|
+
}
|
|
39
|
+
return { role: 'member', permissions };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function checkSlackPlatformPermit(perm: string, subject: PermissionSubject): boolean {
|
|
43
|
+
const role = subject.sender?.role?.[0];
|
|
44
|
+
const permissions = subject.sender?.permissions ?? [];
|
|
45
|
+
const has = (t: string) => permissions.includes(t);
|
|
46
|
+
|
|
47
|
+
switch (perm) {
|
|
48
|
+
case 'workspace_owner':
|
|
49
|
+
return role === 'owner' || has('workspace_owner');
|
|
50
|
+
case 'workspace_admin':
|
|
51
|
+
return role === 'owner' || role === 'admin' || has('workspace_admin') || has('workspace_owner');
|
|
52
|
+
case 'channel_manager':
|
|
53
|
+
return role === 'owner' || role === 'admin' || role === 'channel_admin'
|
|
54
|
+
|| has('channel_manager') || has('workspace_admin') || has('workspace_owner');
|
|
55
|
+
default:
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|