@zhin.js/adapter-satori 5.0.0 → 5.0.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 +27 -0
- package/README.md +2 -2
- package/lib/endpoint.d.ts +3 -9
- package/lib/endpoint.js +17 -21
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +7 -2
- package/lib/protocol.js +14 -2
- package/package.json +12 -11
- package/src/endpoint.ts +18 -21
- package/src/index.ts +1 -3
- package/src/protocol.ts +19 -2
- /package/commands/endpoint/add/{[name:string].js → [name].js} +0 -0
- /package/commands/endpoint/add/{[name:string].ts → [name].ts} +0 -0
- /package/commands/endpoint/remove/{[name:string].js → [name].js} +0 -0
- /package/commands/endpoint/remove/{[name:string].ts → [name].ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @zhin.js/adapter-satori
|
|
2
2
|
|
|
3
|
+
## 5.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [afc0e66]
|
|
8
|
+
- Updated dependencies [2e41ad5]
|
|
9
|
+
- Updated dependencies [9f57124]
|
|
10
|
+
- @zhin.js/core@1.5.2
|
|
11
|
+
- @zhin.js/adapter@1.1.5
|
|
12
|
+
- @zhin.js/im-contract@1.0.1
|
|
13
|
+
- @zhin.js/plugin-runtime@1.1.3
|
|
14
|
+
- @zhin.js/command@1.0.7
|
|
15
|
+
- @zhin.js/host-http@1.0.6
|
|
16
|
+
- zhin.js@6.0.2
|
|
17
|
+
|
|
18
|
+
## 5.0.1
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [c8f4d45]
|
|
23
|
+
- @zhin.js/plugin-runtime@1.1.2
|
|
24
|
+
- @zhin.js/host-http@1.0.5
|
|
25
|
+
- @zhin.js/adapter@1.1.4
|
|
26
|
+
- @zhin.js/command@1.0.6
|
|
27
|
+
- @zhin.js/core@1.5.1
|
|
28
|
+
- zhin.js@6.0.1
|
|
29
|
+
|
|
3
30
|
## 5.0.0
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -27,8 +27,8 @@ pnpm add @zhin.js/adapter-satori
|
|
|
27
27
|
- `@zhin.js/host-http` — Webhook 模式需 `httpHostToken` 注册 POST 路由
|
|
28
28
|
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`(`baseUrl` / `token` / …)
|
|
29
29
|
|
|
30
|
-
入站:`gateway.receive({
|
|
31
|
-
出站:`send({
|
|
30
|
+
入站:`gateway.receive({ conversation, message, content, sender, metadata })`(`conversation` 为 ConversationRef:DIRECT 频道 → kind `private`,其余 → kind `group`、所属 guild 进 `parent`)
|
|
31
|
+
出站:`send({ conversation, payload })` → Satori `message.create`(`channel_id = conversation.id`;payload 已由 gateway/core 渲染;无 segment-mapper)
|
|
32
32
|
|
|
33
33
|
入站 `metadata.mentioned`:消息 content 中 `<at id="…"/>` 元素的 id 等于登录 selfId(READY/事件 `login.user.id`)时置 `true`。
|
|
34
34
|
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SatoriEndpoint — WebSocket and webhook lifecycle, outbound, admit.
|
|
3
3
|
*/
|
|
4
|
-
import { type EndpointInstance, type EndpointManagement } from '@zhin.js/adapter';
|
|
4
|
+
import { type EndpointInstance, type EndpointManagement, type EndpointSendRequest } from '@zhin.js/adapter';
|
|
5
5
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
6
6
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
7
7
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
@@ -23,10 +23,7 @@ export declare class SatoriWsEndpoint implements EndpointInstance {
|
|
|
23
23
|
open(): void;
|
|
24
24
|
close(): void;
|
|
25
25
|
stop(): Promise<void>;
|
|
26
|
-
send({
|
|
27
|
-
readonly target: string;
|
|
28
|
-
readonly payload: unknown;
|
|
29
|
-
}): Promise<string>;
|
|
26
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
30
27
|
/** Test / internal: admit a gateway event when the endpoint is open. */
|
|
31
28
|
admit(body: SatoriEventBody): void;
|
|
32
29
|
recall(id: string): Promise<void>;
|
|
@@ -51,10 +48,7 @@ export declare class SatoriWebhookEndpoint implements EndpointInstance {
|
|
|
51
48
|
open(): void;
|
|
52
49
|
close(): void;
|
|
53
50
|
stop(): Promise<void>;
|
|
54
|
-
send({
|
|
55
|
-
readonly target: string;
|
|
56
|
-
readonly payload: unknown;
|
|
57
|
-
}): Promise<string>;
|
|
51
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
58
52
|
admit(body: SatoriEventBody): void;
|
|
59
53
|
recall(id: string): Promise<void>;
|
|
60
54
|
/** Test helper: inject login without a live webhook push. */
|
package/lib/endpoint.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { createEndpointLifecycle, } from '@zhin.js/adapter';
|
|
5
5
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
6
|
-
import { SatoriOpcode, buildWsUrl, callSatoriApi, extractCreatedMessageId, formatInboundContent, formatMessageId, formatSatoriOutbound, isMessageEvent, isSelfMentioned, parseMessageRef, resolveInboundSender,
|
|
6
|
+
import { SatoriOpcode, buildWsUrl, callSatoriApi, extractCreatedMessageId, formatInboundContent, formatMessageId, formatSatoriOutbound, isMessageEvent, isSelfMentioned, parseMessageRef, resolveInboundSender, satoriInboundConversation, } from './protocol.js';
|
|
7
7
|
import { registerSatoriWebhookRoutes } from './webhook.js';
|
|
8
8
|
import { WS_OPEN, defaultCreateWebSocket, } from './ws.js';
|
|
9
9
|
const logger = getLogger('satori');
|
|
@@ -64,20 +64,20 @@ export class SatoriWsEndpoint {
|
|
|
64
64
|
await this.#lifecycle.stop();
|
|
65
65
|
this.#ws = null;
|
|
66
66
|
}
|
|
67
|
-
async send({
|
|
67
|
+
async send({ conversation, payload }) {
|
|
68
68
|
const content = formatSatoriOutbound(payload);
|
|
69
69
|
const result = await this.#api('message', 'create', {
|
|
70
|
-
channel_id:
|
|
70
|
+
channel_id: conversation.id,
|
|
71
71
|
content,
|
|
72
72
|
});
|
|
73
73
|
const msgId = extractCreatedMessageId(result);
|
|
74
74
|
logger.debug(formatCompact({
|
|
75
75
|
op: 'satori_send',
|
|
76
76
|
endpoint: this.#options.config.name,
|
|
77
|
-
|
|
77
|
+
channel: conversation.id,
|
|
78
78
|
messageId: msgId || undefined,
|
|
79
79
|
}));
|
|
80
|
-
return msgId ? formatMessageId(
|
|
80
|
+
return msgId ? formatMessageId(conversation.id, msgId) : '';
|
|
81
81
|
}
|
|
82
82
|
/** Test / internal: admit a gateway event when the endpoint is open. */
|
|
83
83
|
admit(body) {
|
|
@@ -87,18 +87,16 @@ export class SatoriWsEndpoint {
|
|
|
87
87
|
this.#login = body.login;
|
|
88
88
|
if (!isMessageEvent(body))
|
|
89
89
|
return;
|
|
90
|
-
const
|
|
90
|
+
const conversation = satoriInboundConversation(String(this.#options.id), body);
|
|
91
91
|
const content = formatInboundContent(body);
|
|
92
92
|
const sender = resolveInboundSender(body);
|
|
93
|
-
const messageId = formatMessageId(target, body.message.id);
|
|
94
93
|
const selfId = this.#login?.user?.id ?? body.login?.user?.id;
|
|
95
94
|
const mentioned = isSelfMentioned(body, selfId);
|
|
96
95
|
void this.#options.gateway.receive({
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
conversation,
|
|
97
|
+
message: { conversation, id: body.message.id },
|
|
99
98
|
content,
|
|
100
99
|
sender,
|
|
101
|
-
id: messageId,
|
|
102
100
|
metadata: Object.freeze({
|
|
103
101
|
type: body.type,
|
|
104
102
|
channelType: isPrivateChannelType(body) ? 'private' : 'group',
|
|
@@ -110,7 +108,7 @@ export class SatoriWsEndpoint {
|
|
|
110
108
|
}).catch((err) => {
|
|
111
109
|
logger.warn(formatCompact({
|
|
112
110
|
op: 'satori_gateway_receive_failed',
|
|
113
|
-
|
|
111
|
+
channel: conversation.id,
|
|
114
112
|
error: err instanceof Error ? err.message : String(err),
|
|
115
113
|
}));
|
|
116
114
|
});
|
|
@@ -295,20 +293,20 @@ export class SatoriWebhookEndpoint {
|
|
|
295
293
|
endpoint: this.#options.config.name,
|
|
296
294
|
}));
|
|
297
295
|
}
|
|
298
|
-
async send({
|
|
296
|
+
async send({ conversation, payload }) {
|
|
299
297
|
const content = formatSatoriOutbound(payload);
|
|
300
298
|
const result = await this.#api('message', 'create', {
|
|
301
|
-
channel_id:
|
|
299
|
+
channel_id: conversation.id,
|
|
302
300
|
content,
|
|
303
301
|
});
|
|
304
302
|
const msgId = extractCreatedMessageId(result);
|
|
305
303
|
logger.debug(formatCompact({
|
|
306
304
|
op: 'satori_send',
|
|
307
305
|
endpoint: this.#options.config.name,
|
|
308
|
-
|
|
306
|
+
channel: conversation.id,
|
|
309
307
|
messageId: msgId || undefined,
|
|
310
308
|
}));
|
|
311
|
-
return msgId ? formatMessageId(
|
|
309
|
+
return msgId ? formatMessageId(conversation.id, msgId) : '';
|
|
312
310
|
}
|
|
313
311
|
admit(body) {
|
|
314
312
|
if (!this.#open)
|
|
@@ -317,18 +315,16 @@ export class SatoriWebhookEndpoint {
|
|
|
317
315
|
this.#login = body.login;
|
|
318
316
|
if (!isMessageEvent(body))
|
|
319
317
|
return;
|
|
320
|
-
const
|
|
318
|
+
const conversation = satoriInboundConversation(String(this.#options.id), body);
|
|
321
319
|
const content = formatInboundContent(body);
|
|
322
320
|
const sender = resolveInboundSender(body);
|
|
323
|
-
const messageId = formatMessageId(target, body.message.id);
|
|
324
321
|
const selfId = this.#login?.user?.id ?? body.login?.user?.id;
|
|
325
322
|
const mentioned = isSelfMentioned(body, selfId);
|
|
326
323
|
void this.#options.gateway.receive({
|
|
327
|
-
|
|
328
|
-
|
|
324
|
+
conversation,
|
|
325
|
+
message: { conversation, id: body.message.id },
|
|
329
326
|
content,
|
|
330
327
|
sender,
|
|
331
|
-
id: messageId,
|
|
332
328
|
metadata: Object.freeze({
|
|
333
329
|
type: body.type,
|
|
334
330
|
channelType: isPrivateChannelType(body) ? 'private' : 'group',
|
|
@@ -340,7 +336,7 @@ export class SatoriWebhookEndpoint {
|
|
|
340
336
|
}).catch((err) => {
|
|
341
337
|
logger.warn(formatCompact({
|
|
342
338
|
op: 'satori_gateway_receive_failed',
|
|
343
|
-
|
|
339
|
+
channel: conversation.id,
|
|
344
340
|
error: err instanceof Error ? err.message : String(err),
|
|
345
341
|
}));
|
|
346
342
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { SatoriOpcode, buildWsUrl, callSatoriApi, extractCreatedMessageId, formatInboundContent,
|
|
1
|
+
export { SatoriOpcode, buildWsUrl, callSatoriApi, extractCreatedMessageId, formatInboundContent, formatSatoriOutbound, isMessageEvent, isPrivateChannel, resolveInboundSender, resolveSatoriConfig, satoriInboundConversation, type ResolvedSatoriWebhookConfig, type ResolvedSatoriWsConfig, type SatoriAdapterConfig, type SatoriApiOptions, type SatoriChannel, type SatoriEventBody, type SatoriLogin, type SatoriMessage, type SatoriSignal, type SatoriUser, type SatoriWireSegment, } from './protocol.js';
|
|
2
2
|
export { SatoriWebhookEndpoint, SatoriWsEndpoint, type CreateSatoriWebSocket, type SatoriApiCaller, type SatoriWebhookEndpointOptions, type SatoriWsEndpointOptions, type SatoriWsSocket, } from './endpoint.js';
|
|
3
3
|
export { handleSatoriWebhookRequest, readRequestBody, registerSatoriWebhookRoutes, resolveSatoriOpcode, verifySatoriToken, type SatoriWebhookHandler, } from './webhook.js';
|
|
4
4
|
export { WS_OPEN, defaultCreateWebSocket, } from './ws.js';
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { SatoriOpcode, buildWsUrl, callSatoriApi, extractCreatedMessageId, formatInboundContent,
|
|
1
|
+
export { SatoriOpcode, buildWsUrl, callSatoriApi, extractCreatedMessageId, formatInboundContent, formatSatoriOutbound, isMessageEvent, isPrivateChannel, resolveInboundSender, resolveSatoriConfig, satoriInboundConversation, } from './protocol.js';
|
|
2
2
|
export { SatoriWebhookEndpoint, SatoriWsEndpoint, } from './endpoint.js';
|
|
3
3
|
export { handleSatoriWebhookRequest, readRequestBody, registerSatoriWebhookRoutes, resolveSatoriOpcode, verifySatoriToken, } from './webhook.js';
|
|
4
4
|
export { WS_OPEN, defaultCreateWebSocket, } from './ws.js';
|
package/lib/protocol.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
4
|
* Spec: https://satori.chat/zh-CN/protocol/overview.html
|
|
5
5
|
*/
|
|
6
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
6
7
|
/** Opcode:EVENT=0, PING=1, PONG=2, IDENTIFY=3, READY=4, META=5 */
|
|
7
8
|
export declare const SatoriOpcode: {
|
|
8
9
|
readonly EVENT: 0;
|
|
@@ -124,9 +125,13 @@ export declare function callSatoriApi<T = unknown>(options: SatoriApiOptions, re
|
|
|
124
125
|
export declare function formatInboundContent(body: SatoriEventBody & {
|
|
125
126
|
message: SatoriMessage;
|
|
126
127
|
}): string;
|
|
127
|
-
|
|
128
|
+
/**
|
|
129
|
+
* 入站归一化 → ConversationRef:Channel.type 1 (DIRECT) → kind 'private';
|
|
130
|
+
* 其余频道消息 → kind 'group',所属 guild 容器进 `parent`(kind 'channel')。
|
|
131
|
+
*/
|
|
132
|
+
export declare function satoriInboundConversation(endpointId: string, body: SatoriEventBody & {
|
|
128
133
|
message: SatoriMessage;
|
|
129
|
-
}):
|
|
134
|
+
}): ConversationRef;
|
|
130
135
|
export declare function resolveInboundSender(body: SatoriEventBody & {
|
|
131
136
|
message: SatoriMessage;
|
|
132
137
|
}): string;
|
package/lib/protocol.js
CHANGED
|
@@ -113,9 +113,21 @@ export function formatInboundContent(body) {
|
|
|
113
113
|
const content = body.message.content ?? '';
|
|
114
114
|
return typeof content === 'string' ? content : String(content);
|
|
115
115
|
}
|
|
116
|
-
|
|
116
|
+
/**
|
|
117
|
+
* 入站归一化 → ConversationRef:Channel.type 1 (DIRECT) → kind 'private';
|
|
118
|
+
* 其余频道消息 → kind 'group',所属 guild 容器进 `parent`(kind 'channel')。
|
|
119
|
+
*/
|
|
120
|
+
export function satoriInboundConversation(endpointId, body) {
|
|
117
121
|
const channel = body.channel ?? body.message.channel;
|
|
118
|
-
|
|
122
|
+
const kind = isPrivateChannel(channel) ? 'private' : 'group';
|
|
123
|
+
return {
|
|
124
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
125
|
+
kind,
|
|
126
|
+
id: channel?.id ?? '',
|
|
127
|
+
...(kind === 'group' && body.guild?.id
|
|
128
|
+
? { parent: { kind: 'channel', id: body.guild.id } }
|
|
129
|
+
: {}),
|
|
130
|
+
};
|
|
119
131
|
}
|
|
120
132
|
export function resolveInboundSender(body) {
|
|
121
133
|
const user = body.user ?? body.message.user ?? body.message.member?.user;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-satori",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Zhin.js Satori adapter for Plugin Runtime (WebSocket client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -39,25 +39,26 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"ws": "^8.21.1",
|
|
42
|
-
"@zhin.js/adapter": "1.1.
|
|
43
|
-
"@zhin.js/command": "1.0.
|
|
44
|
-
"@zhin.js/core": "1.5.
|
|
45
|
-
"@zhin.js/host-http": "1.0.
|
|
42
|
+
"@zhin.js/adapter": "1.1.5",
|
|
43
|
+
"@zhin.js/command": "1.0.7",
|
|
44
|
+
"@zhin.js/core": "1.5.2",
|
|
45
|
+
"@zhin.js/host-http": "1.0.6",
|
|
46
|
+
"@zhin.js/im-contract": "1.0.1",
|
|
46
47
|
"@zhin.js/logger": "1.0.75",
|
|
47
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
48
|
+
"@zhin.js/plugin-runtime": "1.1.3"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^26.1.2",
|
|
51
52
|
"@types/ws": "^8.18.1",
|
|
52
53
|
"typescript": "^6.0.3",
|
|
53
54
|
"vitest": "^4.1.10",
|
|
54
|
-
"@zhin.js/host-http": "1.0.
|
|
55
|
+
"@zhin.js/host-http": "1.0.6"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
57
|
-
"@zhin.js/adapter": "1.1.
|
|
58
|
-
"@zhin.js/core": "1.5.
|
|
59
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
60
|
-
"zhin.js": "6.0.
|
|
58
|
+
"@zhin.js/adapter": "1.1.5",
|
|
59
|
+
"@zhin.js/core": "1.5.2",
|
|
60
|
+
"@zhin.js/plugin-runtime": "1.1.3",
|
|
61
|
+
"zhin.js": "6.0.2"
|
|
61
62
|
},
|
|
62
63
|
"peerDependenciesMeta": {
|
|
63
64
|
"zhin.js": {
|
package/src/endpoint.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type EndpointInstance,
|
|
10
10
|
type EndpointLifecycle,
|
|
11
11
|
type EndpointManagement,
|
|
12
|
+
type EndpointSendRequest,
|
|
12
13
|
} from '@zhin.js/adapter';
|
|
13
14
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
14
15
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
isSelfMentioned,
|
|
27
28
|
parseMessageRef,
|
|
28
29
|
resolveInboundSender,
|
|
29
|
-
|
|
30
|
+
satoriInboundConversation,
|
|
30
31
|
type ResolvedSatoriWebhookConfig,
|
|
31
32
|
type ResolvedSatoriWsConfig,
|
|
32
33
|
type SatoriApiOptions,
|
|
@@ -117,20 +118,20 @@ export class SatoriWsEndpoint implements EndpointInstance {
|
|
|
117
118
|
this.#ws = null;
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
async send({
|
|
121
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
121
122
|
const content = formatSatoriOutbound(payload);
|
|
122
123
|
const result = await this.#api('message', 'create', {
|
|
123
|
-
channel_id:
|
|
124
|
+
channel_id: conversation.id,
|
|
124
125
|
content,
|
|
125
126
|
});
|
|
126
127
|
const msgId = extractCreatedMessageId(result);
|
|
127
128
|
logger.debug(formatCompact({
|
|
128
129
|
op: 'satori_send',
|
|
129
130
|
endpoint: this.#options.config.name,
|
|
130
|
-
|
|
131
|
+
channel: conversation.id,
|
|
131
132
|
messageId: msgId || undefined,
|
|
132
133
|
}));
|
|
133
|
-
return msgId ? formatMessageId(
|
|
134
|
+
return msgId ? formatMessageId(conversation.id, msgId) : '';
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
/** Test / internal: admit a gateway event when the endpoint is open. */
|
|
@@ -138,18 +139,16 @@ export class SatoriWsEndpoint implements EndpointInstance {
|
|
|
138
139
|
if (!this.#open) return;
|
|
139
140
|
if (body.login && !this.#login) this.#login = body.login;
|
|
140
141
|
if (!isMessageEvent(body)) return;
|
|
141
|
-
const
|
|
142
|
+
const conversation = satoriInboundConversation(String(this.#options.id), body);
|
|
142
143
|
const content = formatInboundContent(body);
|
|
143
144
|
const sender = resolveInboundSender(body);
|
|
144
|
-
const messageId = formatMessageId(target, body.message.id);
|
|
145
145
|
const selfId = this.#login?.user?.id ?? body.login?.user?.id;
|
|
146
146
|
const mentioned = isSelfMentioned(body, selfId);
|
|
147
147
|
void this.#options.gateway.receive({
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
conversation,
|
|
149
|
+
message: { conversation, id: body.message.id },
|
|
150
150
|
content,
|
|
151
151
|
sender,
|
|
152
|
-
id: messageId,
|
|
153
152
|
metadata: Object.freeze({
|
|
154
153
|
type: body.type,
|
|
155
154
|
channelType: isPrivateChannelType(body) ? 'private' : 'group',
|
|
@@ -161,7 +160,7 @@ export class SatoriWsEndpoint implements EndpointInstance {
|
|
|
161
160
|
}).catch((err) => {
|
|
162
161
|
logger.warn(formatCompact({
|
|
163
162
|
op: 'satori_gateway_receive_failed',
|
|
164
|
-
|
|
163
|
+
channel: conversation.id,
|
|
165
164
|
error: err instanceof Error ? err.message : String(err),
|
|
166
165
|
}));
|
|
167
166
|
});
|
|
@@ -374,38 +373,36 @@ export class SatoriWebhookEndpoint implements EndpointInstance {
|
|
|
374
373
|
}));
|
|
375
374
|
}
|
|
376
375
|
|
|
377
|
-
async send({
|
|
376
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
378
377
|
const content = formatSatoriOutbound(payload);
|
|
379
378
|
const result = await this.#api('message', 'create', {
|
|
380
|
-
channel_id:
|
|
379
|
+
channel_id: conversation.id,
|
|
381
380
|
content,
|
|
382
381
|
});
|
|
383
382
|
const msgId = extractCreatedMessageId(result);
|
|
384
383
|
logger.debug(formatCompact({
|
|
385
384
|
op: 'satori_send',
|
|
386
385
|
endpoint: this.#options.config.name,
|
|
387
|
-
|
|
386
|
+
channel: conversation.id,
|
|
388
387
|
messageId: msgId || undefined,
|
|
389
388
|
}));
|
|
390
|
-
return msgId ? formatMessageId(
|
|
389
|
+
return msgId ? formatMessageId(conversation.id, msgId) : '';
|
|
391
390
|
}
|
|
392
391
|
|
|
393
392
|
admit(body: SatoriEventBody): void {
|
|
394
393
|
if (!this.#open) return;
|
|
395
394
|
if (body.login && !this.#login) this.#login = body.login;
|
|
396
395
|
if (!isMessageEvent(body)) return;
|
|
397
|
-
const
|
|
396
|
+
const conversation = satoriInboundConversation(String(this.#options.id), body);
|
|
398
397
|
const content = formatInboundContent(body);
|
|
399
398
|
const sender = resolveInboundSender(body);
|
|
400
|
-
const messageId = formatMessageId(target, body.message.id);
|
|
401
399
|
const selfId = this.#login?.user?.id ?? body.login?.user?.id;
|
|
402
400
|
const mentioned = isSelfMentioned(body, selfId);
|
|
403
401
|
void this.#options.gateway.receive({
|
|
404
|
-
|
|
405
|
-
|
|
402
|
+
conversation,
|
|
403
|
+
message: { conversation, id: body.message.id },
|
|
406
404
|
content,
|
|
407
405
|
sender,
|
|
408
|
-
id: messageId,
|
|
409
406
|
metadata: Object.freeze({
|
|
410
407
|
type: body.type,
|
|
411
408
|
channelType: isPrivateChannelType(body) ? 'private' : 'group',
|
|
@@ -417,7 +414,7 @@ export class SatoriWebhookEndpoint implements EndpointInstance {
|
|
|
417
414
|
}).catch((err) => {
|
|
418
415
|
logger.warn(formatCompact({
|
|
419
416
|
op: 'satori_gateway_receive_failed',
|
|
420
|
-
|
|
417
|
+
channel: conversation.id,
|
|
421
418
|
error: err instanceof Error ? err.message : String(err),
|
|
422
419
|
}));
|
|
423
420
|
});
|
package/src/index.ts
CHANGED
|
@@ -4,14 +4,12 @@ export {
|
|
|
4
4
|
callSatoriApi,
|
|
5
5
|
extractCreatedMessageId,
|
|
6
6
|
formatInboundContent,
|
|
7
|
-
formatMessageId,
|
|
8
7
|
formatSatoriOutbound,
|
|
9
8
|
isMessageEvent,
|
|
10
9
|
isPrivateChannel,
|
|
11
|
-
parseMessageRef,
|
|
12
10
|
resolveInboundSender,
|
|
13
|
-
resolveInboundTarget,
|
|
14
11
|
resolveSatoriConfig,
|
|
12
|
+
satoriInboundConversation,
|
|
15
13
|
type ResolvedSatoriWebhookConfig,
|
|
16
14
|
type ResolvedSatoriWsConfig,
|
|
17
15
|
type SatoriAdapterConfig,
|
package/src/protocol.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { pickCredential } from '@zhin.js/adapter';
|
|
8
8
|
import { isMediaRef } from '@zhin.js/core';
|
|
9
|
+
import type { ConversationKind, ConversationRef } from '@zhin.js/im-contract';
|
|
9
10
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
10
11
|
|
|
11
12
|
const logger = getLogger('satori');
|
|
@@ -227,9 +228,25 @@ export function formatInboundContent(body: SatoriEventBody & { message: SatoriMe
|
|
|
227
228
|
return typeof content === 'string' ? content : String(content);
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 入站归一化 → ConversationRef:Channel.type 1 (DIRECT) → kind 'private';
|
|
234
|
+
* 其余频道消息 → kind 'group',所属 guild 容器进 `parent`(kind 'channel')。
|
|
235
|
+
*/
|
|
236
|
+
export function satoriInboundConversation(
|
|
237
|
+
endpointId: string,
|
|
238
|
+
body: SatoriEventBody & { message: SatoriMessage },
|
|
239
|
+
): ConversationRef {
|
|
231
240
|
const channel = body.channel ?? body.message.channel;
|
|
232
|
-
|
|
241
|
+
const kind: ConversationKind = isPrivateChannel(channel) ? 'private' : 'group';
|
|
242
|
+
return {
|
|
243
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
244
|
+
kind,
|
|
245
|
+
id: channel?.id ?? '',
|
|
246
|
+
...(kind === 'group' && body.guild?.id
|
|
247
|
+
? { parent: { kind: 'channel' as const, id: body.guild.id } }
|
|
248
|
+
: {}),
|
|
249
|
+
};
|
|
233
250
|
}
|
|
234
251
|
|
|
235
252
|
export function resolveInboundSender(body: SatoriEventBody & { message: SatoriMessage }): string {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|