@zhin.js/adapter-slack 6.0.13 → 7.0.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 +47 -0
- package/adapters/slack.js +4 -1
- package/adapters/slack.ts +4 -1
- package/lib/endpoint.d.ts +4 -2
- package/lib/endpoint.js +36 -1
- package/lib/side-event-dispatch.d.ts +4 -0
- package/lib/side-event-dispatch.js +45 -0
- package/package.json +14 -14
- package/src/endpoint.ts +41 -2
- package/src/side-event-dispatch.ts +54 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f2c532f: Expose exact per-Endpoint message operations through one validated Adapter capability model, route Core control calls through declared active capabilities, and connect existing recall, edit, reaction, and typing implementations across platform adapters.
|
|
8
|
+
- Updated dependencies [b10d058]
|
|
9
|
+
- Updated dependencies [f2c532f]
|
|
10
|
+
- Updated dependencies [3dbf990]
|
|
11
|
+
- @zhin.js/host-http@1.0.12
|
|
12
|
+
- @zhin.js/adapter@1.2.0
|
|
13
|
+
- @zhin.js/core@1.5.13
|
|
14
|
+
- @zhin.js/agent@1.1.15
|
|
15
|
+
- zhin.js@6.0.13
|
|
16
|
+
|
|
17
|
+
## 6.0.14
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 5969c5b: Add SideEventGateway so adapters forward notice/request/system into HandlerIndex. HandlerContext now exposes only generation-safe capabilities and prompt ports; live Endpoint escape hatches are removed.
|
|
22
|
+
- Updated dependencies [5969c5b]
|
|
23
|
+
- Updated dependencies [d336a3f]
|
|
24
|
+
- Updated dependencies [0c82a7e]
|
|
25
|
+
- Updated dependencies [b9217e4]
|
|
26
|
+
- Updated dependencies [5969c5b]
|
|
27
|
+
- Updated dependencies [5969c5b]
|
|
28
|
+
- Updated dependencies [974772e]
|
|
29
|
+
- Updated dependencies [5969c5b]
|
|
30
|
+
- Updated dependencies [5969c5b]
|
|
31
|
+
- Updated dependencies [2f786bd]
|
|
32
|
+
- Updated dependencies [63d89f9]
|
|
33
|
+
- Updated dependencies [71c7cdd]
|
|
34
|
+
- Updated dependencies [3cca0ea]
|
|
35
|
+
- Updated dependencies [1312ca0]
|
|
36
|
+
- Updated dependencies [985fa22]
|
|
37
|
+
- Updated dependencies [04b861d]
|
|
38
|
+
- Updated dependencies [a23d544]
|
|
39
|
+
- Updated dependencies [8cddabf]
|
|
40
|
+
- Updated dependencies [dbe5081]
|
|
41
|
+
- @zhin.js/im-contract@1.0.4
|
|
42
|
+
- @zhin.js/core@1.5.12
|
|
43
|
+
- @zhin.js/adapter@1.1.11
|
|
44
|
+
- @zhin.js/agent@1.1.14
|
|
45
|
+
- @zhin.js/host-http@1.0.11
|
|
46
|
+
- @zhin.js/command@1.0.15
|
|
47
|
+
- zhin.js@6.0.12
|
|
48
|
+
- @zhin.js/permission@1.0.3
|
|
49
|
+
|
|
3
50
|
## 6.0.13
|
|
4
51
|
|
|
5
52
|
### Patch Changes
|
package/adapters/slack.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/slack.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
6
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
7
7
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
8
|
import { SlackEndpoint } from "../lib/endpoint.js";
|
|
9
9
|
import { resolveSlackConfig, } from "../lib/protocol.js";
|
|
@@ -11,6 +11,7 @@ import { slackRuntimeStateToken } from "../lib/slack-runtime-state.js";
|
|
|
11
11
|
export { SlackEndpoint } from "../lib/endpoint.js";
|
|
12
12
|
export default defineAdapter({
|
|
13
13
|
capabilities: ['inbound', 'outbound'],
|
|
14
|
+
operations: ['recall', 'edit', 'reaction'],
|
|
14
15
|
// 媒体段(canonical MediaRef):kind=url 直发(image 走 attachment image_url,
|
|
15
16
|
// 其余拉取后上传);kind=base64 解码后经 files.uploadV2 上传;kind=path 读盘上传;
|
|
16
17
|
// kind=file 无 Slack 对应物,丢弃留痕。Block Kit 原生按钮承载交互段。
|
|
@@ -29,6 +30,7 @@ export default defineAdapter({
|
|
|
29
30
|
return new SlackEndpoint({
|
|
30
31
|
id: context.id,
|
|
31
32
|
gateway: context.use(messageGatewayToken),
|
|
33
|
+
sideEvents: context.use(sideEventGatewayToken),
|
|
32
34
|
http: context.use(httpHostToken),
|
|
33
35
|
config,
|
|
34
36
|
});
|
|
@@ -36,6 +38,7 @@ export default defineAdapter({
|
|
|
36
38
|
return new SlackEndpoint({
|
|
37
39
|
id: context.id,
|
|
38
40
|
gateway: context.use(messageGatewayToken),
|
|
41
|
+
sideEvents: context.use(sideEventGatewayToken),
|
|
39
42
|
config,
|
|
40
43
|
});
|
|
41
44
|
},
|
package/adapters/slack.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/slack.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
5
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
6
6
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
7
|
import { SlackEndpoint } from '../src/endpoint.js';
|
|
8
8
|
import {
|
|
@@ -16,6 +16,7 @@ export type { SlackEndpointOptions, SlackSocketLike, SlackWebClientLike } from '
|
|
|
16
16
|
|
|
17
17
|
export default defineAdapter<SlackAdapterConfig>({
|
|
18
18
|
capabilities: ['inbound', 'outbound'],
|
|
19
|
+
operations: ['recall', 'edit', 'reaction'],
|
|
19
20
|
// 媒体段(canonical MediaRef):kind=url 直发(image 走 attachment image_url,
|
|
20
21
|
// 其余拉取后上传);kind=base64 解码后经 files.uploadV2 上传;kind=path 读盘上传;
|
|
21
22
|
// kind=file 无 Slack 对应物,丢弃留痕。Block Kit 原生按钮承载交互段。
|
|
@@ -34,6 +35,7 @@ export default defineAdapter<SlackAdapterConfig>({
|
|
|
34
35
|
return new SlackEndpoint({
|
|
35
36
|
id: context.id,
|
|
36
37
|
gateway: context.use(messageGatewayToken),
|
|
38
|
+
sideEvents: context.use(sideEventGatewayToken),
|
|
37
39
|
http: context.use(httpHostToken),
|
|
38
40
|
config,
|
|
39
41
|
});
|
|
@@ -41,6 +43,7 @@ export default defineAdapter<SlackAdapterConfig>({
|
|
|
41
43
|
return new SlackEndpoint({
|
|
42
44
|
id: context.id,
|
|
43
45
|
gateway: context.use(messageGatewayToken),
|
|
46
|
+
sideEvents: context.use(sideEventGatewayToken),
|
|
44
47
|
config,
|
|
45
48
|
});
|
|
46
49
|
},
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
|
|
2
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
1
|
+
import type { EndpointControl, EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
|
|
2
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
4
|
import type { CapabilityId } from 'zhin.js';
|
|
5
5
|
import { type ResolvedSlackConfig, type SlackEvent, type SlackInteractionPayload, type SlackMessageEvent, type SlackSlashCommand } from './protocol.js';
|
|
@@ -118,6 +118,7 @@ export interface SlackWebClientLike extends SlackChatClient {
|
|
|
118
118
|
export interface SlackEndpointOptions {
|
|
119
119
|
readonly id: CapabilityId;
|
|
120
120
|
readonly gateway: MessageGateway;
|
|
121
|
+
readonly sideEvents?: SideEventGateway;
|
|
121
122
|
readonly config: ResolvedSlackConfig;
|
|
122
123
|
readonly http?: HttpHost;
|
|
123
124
|
readonly createClient?: (token: string) => SlackWebClientLike;
|
|
@@ -129,6 +130,7 @@ export interface SlackEndpointOptions {
|
|
|
129
130
|
export declare class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
|
|
130
131
|
#private;
|
|
131
132
|
readonly management: EndpointManagement;
|
|
133
|
+
readonly control: EndpointControl;
|
|
132
134
|
constructor(options: SlackEndpointOptions);
|
|
133
135
|
/** Console 展示 / AdapterIndex live name(多 endpoint 时与 entry name 一致)。 */
|
|
134
136
|
get name(): string;
|
package/lib/endpoint.js
CHANGED
|
@@ -12,6 +12,7 @@ import { normalizeSlackReactionName } from './slack-reaction.js';
|
|
|
12
12
|
import { postSlackEphemeral } from './slack-response-url.js';
|
|
13
13
|
import { editSlackContent, sendSlackContent } from './slack-outbound.js';
|
|
14
14
|
import { registerSlackWebhookRoutes } from './webhook.js';
|
|
15
|
+
import { receiveSlackSideEvent } from './side-event-dispatch.js';
|
|
15
16
|
export class SlackEndpoint {
|
|
16
17
|
#logger;
|
|
17
18
|
#options;
|
|
@@ -25,6 +26,35 @@ export class SlackEndpoint {
|
|
|
25
26
|
#started = false;
|
|
26
27
|
#unregisterAgent;
|
|
27
28
|
management = createSlackEndpointManagement(this);
|
|
29
|
+
control = Object.freeze({
|
|
30
|
+
recall: async (message) => {
|
|
31
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
32
|
+
if (!ref || !this.#client)
|
|
33
|
+
return;
|
|
34
|
+
await this.#client.chat.delete(ref);
|
|
35
|
+
},
|
|
36
|
+
edit: async (message, content) => {
|
|
37
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
38
|
+
if (!ref)
|
|
39
|
+
return null;
|
|
40
|
+
await this.editMessage(ref.channel, ref.ts, content);
|
|
41
|
+
return message.id;
|
|
42
|
+
},
|
|
43
|
+
addReaction: async (message, emoji) => {
|
|
44
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
45
|
+
if (!ref)
|
|
46
|
+
return null;
|
|
47
|
+
const reaction = normalizeSlackReactionName(emoji);
|
|
48
|
+
await this.addReaction(ref.channel, ref.ts, reaction);
|
|
49
|
+
return reaction;
|
|
50
|
+
},
|
|
51
|
+
removeReaction: async (message, reactionId) => {
|
|
52
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
53
|
+
if (!ref)
|
|
54
|
+
return;
|
|
55
|
+
await this.removeReaction(ref.channel, ref.ts, reactionId);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
28
58
|
constructor(options) {
|
|
29
59
|
this.#logger = getAdapterLogger('slack', options.config.id);
|
|
30
60
|
this.#options = options;
|
|
@@ -216,7 +246,12 @@ export class SlackEndpoint {
|
|
|
216
246
|
handleEnvelope(body) {
|
|
217
247
|
const envelope = body;
|
|
218
248
|
if (envelope?.type === 'event_callback' && envelope.event) {
|
|
219
|
-
|
|
249
|
+
const event = envelope.event;
|
|
250
|
+
if (event.type !== 'message' && event.type !== 'app_mention') {
|
|
251
|
+
receiveSlackSideEvent(this.#options.sideEvents, String(this.#options.id), this.#options.config.id, event, this.#logger);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this.admit(event);
|
|
220
255
|
}
|
|
221
256
|
}
|
|
222
257
|
trackMessageChannel(ts, channel) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import { type getAdapterLogger } from '@zhin.js/logger';
|
|
3
|
+
import type { SlackEvent } from './protocol.js';
|
|
4
|
+
export declare function receiveSlackSideEvent(sideEvents: SideEventGateway | undefined, endpointKey: string, configId: string, event: SlackEvent, logger: ReturnType<typeof getAdapterLogger>): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { buildNotice, mapNoticeParts, senderFromId, SLACK_NOTICE_PARTS_MAP } from '@zhin.js/core';
|
|
2
|
+
import { formatCompact } from '@zhin.js/logger';
|
|
3
|
+
export function receiveSlackSideEvent(sideEvents, endpointKey, configId, event, logger) {
|
|
4
|
+
if (!sideEvents)
|
|
5
|
+
return;
|
|
6
|
+
const eventType = String(event.type ?? '');
|
|
7
|
+
if (!Object.prototype.hasOwnProperty.call(SLACK_NOTICE_PARTS_MAP, eventType))
|
|
8
|
+
return;
|
|
9
|
+
const record = event;
|
|
10
|
+
const parts = mapNoticeParts('slack', eventType);
|
|
11
|
+
const channel = typeof record.channel === 'string' ? record.channel : undefined;
|
|
12
|
+
const user = typeof record.user === 'string' ? record.user : undefined;
|
|
13
|
+
const sceneId = channel ?? user ?? configId;
|
|
14
|
+
const dedupeKey = [
|
|
15
|
+
eventType,
|
|
16
|
+
channel ?? '',
|
|
17
|
+
user ?? '',
|
|
18
|
+
String(record.ts ?? record.event_ts ?? ''),
|
|
19
|
+
].join(':');
|
|
20
|
+
void sideEvents.receiveNotice(buildNotice(record, {
|
|
21
|
+
$id: `slack:${dedupeKey}`,
|
|
22
|
+
$adapter: 'slack',
|
|
23
|
+
$endpoint: configId,
|
|
24
|
+
$type: 'notice',
|
|
25
|
+
$scene_id: sceneId,
|
|
26
|
+
$scene_type: parts.scene_type,
|
|
27
|
+
$sub_type: parts.sub_type,
|
|
28
|
+
$actor: senderFromId(user),
|
|
29
|
+
$target: senderFromId(typeof record.item_user === 'string' ? record.item_user : undefined),
|
|
30
|
+
$timestamp: toMillis(record.event_ts ?? record.ts),
|
|
31
|
+
})).catch((err) => {
|
|
32
|
+
logger.warn(formatCompact({
|
|
33
|
+
op: 'slack_side_event_failed',
|
|
34
|
+
endpoint: configId,
|
|
35
|
+
event: eventType,
|
|
36
|
+
error: err instanceof Error ? err.message : String(err),
|
|
37
|
+
}));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function toMillis(time) {
|
|
41
|
+
const value = Number(time);
|
|
42
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
43
|
+
return Date.now();
|
|
44
|
+
return value < 1e12 ? Math.round(value * 1000) : Math.round(value);
|
|
45
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-slack",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Zhin.js Slack adapter for Plugin Runtime (Socket Mode / HTTP Events)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -34,21 +34,21 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@slack/socket-mode": "^2.0.7",
|
|
36
36
|
"@slack/web-api": "^7.19.0",
|
|
37
|
-
"@zhin.js/adapter": "1.
|
|
38
|
-
"@zhin.js/core": "1.5.
|
|
39
|
-
"@zhin.js/host-http": "1.0.
|
|
40
|
-
"@zhin.js/im-contract": "1.0.
|
|
37
|
+
"@zhin.js/adapter": "1.2.0",
|
|
38
|
+
"@zhin.js/core": "1.5.13",
|
|
39
|
+
"@zhin.js/host-http": "1.0.12",
|
|
40
|
+
"@zhin.js/im-contract": "1.0.4",
|
|
41
41
|
"@zhin.js/logger": "1.0.76"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"zod": "^4.0.0",
|
|
45
|
-
"@zhin.js/adapter": "1.
|
|
46
|
-
"@zhin.js/agent": "1.1.
|
|
47
|
-
"@zhin.js/command": "1.0.
|
|
48
|
-
"@zhin.js/core": "1.5.
|
|
49
|
-
"@zhin.js/host-http": "1.0.
|
|
50
|
-
"@zhin.js/permission": "1.0.
|
|
51
|
-
"zhin.js": "6.0.
|
|
45
|
+
"@zhin.js/adapter": "1.2.0",
|
|
46
|
+
"@zhin.js/agent": "1.1.15",
|
|
47
|
+
"@zhin.js/command": "1.0.15",
|
|
48
|
+
"@zhin.js/core": "1.5.13",
|
|
49
|
+
"@zhin.js/host-http": "1.0.12",
|
|
50
|
+
"@zhin.js/permission": "1.0.3",
|
|
51
|
+
"zhin.js": "6.0.13"
|
|
52
52
|
},
|
|
53
53
|
"peerDependenciesMeta": {
|
|
54
54
|
"@zhin.js/agent": {
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"typescript": "^6.0.3",
|
|
70
70
|
"vitest": "^4.1.10",
|
|
71
71
|
"zod": "^4.4.3",
|
|
72
|
-
"@zhin.js/agent": "1.1.
|
|
73
|
-
"zhin.js": "6.0.
|
|
72
|
+
"@zhin.js/agent": "1.1.15",
|
|
73
|
+
"zhin.js": "6.0.13"
|
|
74
74
|
},
|
|
75
75
|
"files": [
|
|
76
76
|
"adapters",
|
package/src/endpoint.ts
CHANGED
|
@@ -6,11 +6,12 @@ import { WebClient } from '@slack/web-api';
|
|
|
6
6
|
import type {
|
|
7
7
|
EndpointFriend,
|
|
8
8
|
EndpointGroup,
|
|
9
|
+
EndpointControl,
|
|
9
10
|
EndpointInstance,
|
|
10
11
|
EndpointManagement,
|
|
11
12
|
EndpointSendRequest,
|
|
12
13
|
} from 'zhin.js/adapter';
|
|
13
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
14
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
14
15
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
15
16
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
16
17
|
import type { CapabilityId } from 'zhin.js';
|
|
@@ -37,6 +38,7 @@ import { normalizeSlackReactionName } from './slack-reaction.js';
|
|
|
37
38
|
import { postSlackEphemeral } from './slack-response-url.js';
|
|
38
39
|
import { editSlackContent, sendSlackContent, type SlackChatClient } from './slack-outbound.js';
|
|
39
40
|
import { registerSlackWebhookRoutes, type SlackWebhookHandler } from './webhook.js';
|
|
41
|
+
import { receiveSlackSideEvent } from './side-event-dispatch.js';
|
|
40
42
|
|
|
41
43
|
export interface SlackSocketLike {
|
|
42
44
|
on(
|
|
@@ -91,6 +93,7 @@ export interface SlackWebClientLike extends SlackChatClient {
|
|
|
91
93
|
export interface SlackEndpointOptions {
|
|
92
94
|
readonly id: CapabilityId;
|
|
93
95
|
readonly gateway: MessageGateway;
|
|
96
|
+
readonly sideEvents?: SideEventGateway;
|
|
94
97
|
readonly config: ResolvedSlackConfig;
|
|
95
98
|
readonly http?: HttpHost;
|
|
96
99
|
readonly createClient?: (token: string) => SlackWebClientLike;
|
|
@@ -114,6 +117,31 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
|
|
|
114
117
|
#started = false;
|
|
115
118
|
#unregisterAgent?: () => void;
|
|
116
119
|
readonly management: EndpointManagement = createSlackEndpointManagement(this);
|
|
120
|
+
readonly control: EndpointControl = Object.freeze<EndpointControl>({
|
|
121
|
+
recall: async (message) => {
|
|
122
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
123
|
+
if (!ref || !this.#client) return;
|
|
124
|
+
await this.#client.chat.delete(ref);
|
|
125
|
+
},
|
|
126
|
+
edit: async (message, content) => {
|
|
127
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
128
|
+
if (!ref) return null;
|
|
129
|
+
await this.editMessage(ref.channel, ref.ts, content);
|
|
130
|
+
return message.id;
|
|
131
|
+
},
|
|
132
|
+
addReaction: async (message, emoji) => {
|
|
133
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
134
|
+
if (!ref) return null;
|
|
135
|
+
const reaction = normalizeSlackReactionName(emoji);
|
|
136
|
+
await this.addReaction(ref.channel, ref.ts, reaction);
|
|
137
|
+
return reaction;
|
|
138
|
+
},
|
|
139
|
+
removeReaction: async (message, reactionId) => {
|
|
140
|
+
const ref = this.resolveMessageRef(message.id, message.conversation.id);
|
|
141
|
+
if (!ref) return;
|
|
142
|
+
await this.removeReaction(ref.channel, ref.ts, reactionId);
|
|
143
|
+
},
|
|
144
|
+
});
|
|
117
145
|
|
|
118
146
|
constructor(options: SlackEndpointOptions) {
|
|
119
147
|
this.#logger = getAdapterLogger('slack', options.config.id);
|
|
@@ -316,7 +344,18 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
|
|
|
316
344
|
handleEnvelope(body: unknown): void {
|
|
317
345
|
const envelope = body as SlackEventEnvelope;
|
|
318
346
|
if (envelope?.type === 'event_callback' && envelope.event) {
|
|
319
|
-
|
|
347
|
+
const event = envelope.event;
|
|
348
|
+
if (event.type !== 'message' && event.type !== 'app_mention') {
|
|
349
|
+
receiveSlackSideEvent(
|
|
350
|
+
this.#options.sideEvents,
|
|
351
|
+
String(this.#options.id),
|
|
352
|
+
this.#options.config.id,
|
|
353
|
+
event,
|
|
354
|
+
this.#logger,
|
|
355
|
+
);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
this.admit(event);
|
|
320
359
|
}
|
|
321
360
|
}
|
|
322
361
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { buildNotice, mapNoticeParts, senderFromId, SLACK_NOTICE_PARTS_MAP } from '@zhin.js/core';
|
|
2
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
|
+
import { formatCompact, type getAdapterLogger } from '@zhin.js/logger';
|
|
4
|
+
import type { SlackEvent } from './protocol.js';
|
|
5
|
+
|
|
6
|
+
export function receiveSlackSideEvent(
|
|
7
|
+
sideEvents: SideEventGateway | undefined,
|
|
8
|
+
endpointKey: string,
|
|
9
|
+
configId: string,
|
|
10
|
+
event: SlackEvent,
|
|
11
|
+
logger: ReturnType<typeof getAdapterLogger>,
|
|
12
|
+
): void {
|
|
13
|
+
if (!sideEvents) return;
|
|
14
|
+
const eventType = String(event.type ?? '');
|
|
15
|
+
if (!Object.prototype.hasOwnProperty.call(SLACK_NOTICE_PARTS_MAP, eventType)) return;
|
|
16
|
+
const record = event as Record<string, unknown>;
|
|
17
|
+
const parts = mapNoticeParts('slack', eventType);
|
|
18
|
+
const channel = typeof record.channel === 'string' ? record.channel : undefined;
|
|
19
|
+
const user = typeof record.user === 'string' ? record.user : undefined;
|
|
20
|
+
const sceneId = channel ?? user ?? configId;
|
|
21
|
+
const dedupeKey = [
|
|
22
|
+
eventType,
|
|
23
|
+
channel ?? '',
|
|
24
|
+
user ?? '',
|
|
25
|
+
String(record.ts ?? record.event_ts ?? ''),
|
|
26
|
+
].join(':');
|
|
27
|
+
void sideEvents.receiveNotice(buildNotice(record, {
|
|
28
|
+
$id: `slack:${dedupeKey}`,
|
|
29
|
+
$adapter: 'slack' as never,
|
|
30
|
+
$endpoint: configId,
|
|
31
|
+
$type: 'notice',
|
|
32
|
+
$scene_id: sceneId,
|
|
33
|
+
$scene_type: parts.scene_type,
|
|
34
|
+
$sub_type: parts.sub_type,
|
|
35
|
+
$actor: senderFromId(user),
|
|
36
|
+
$target: senderFromId(
|
|
37
|
+
typeof record.item_user === 'string' ? record.item_user : undefined,
|
|
38
|
+
),
|
|
39
|
+
$timestamp: toMillis(record.event_ts ?? record.ts),
|
|
40
|
+
})).catch((err) => {
|
|
41
|
+
logger.warn(formatCompact({
|
|
42
|
+
op: 'slack_side_event_failed',
|
|
43
|
+
endpoint: configId,
|
|
44
|
+
event: eventType,
|
|
45
|
+
error: err instanceof Error ? err.message : String(err),
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function toMillis(time: unknown): number {
|
|
51
|
+
const value = Number(time);
|
|
52
|
+
if (!Number.isFinite(value) || value <= 0) return Date.now();
|
|
53
|
+
return value < 1e12 ? Math.round(value * 1000) : Math.round(value);
|
|
54
|
+
}
|