@zhin.js/adapter-discord 7.0.3 → 7.0.5
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 +79 -0
- package/adapters/discord.js +2 -2
- package/adapters/discord.ts +2 -2
- package/lib/discord-agent-deps.d.ts +3 -3
- package/lib/discord-agent-deps.js +7 -7
- package/lib/endpoint.js +39 -34
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/platform-permit.d.ts +2 -3
- package/lib/platform-permit.js +3 -13
- package/lib/protocol.d.ts +5 -5
- package/lib/protocol.js +6 -6
- package/package.json +17 -16
- package/plugin.js +6 -4
- package/schema.json +41 -5
- package/src/discord-agent-deps.ts +9 -9
- package/src/endpoint.ts +41 -35
- package/src/index.ts +0 -1
- package/src/platform-permit.ts +4 -11
- package/src/protocol.ts +10 -10
- /package/commands/endpoint/add/{[name].js → [id].js} +0 -0
- /package/commands/endpoint/add/{[name].ts → [id].ts} +0 -0
- /package/commands/endpoint/remove/{[name].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name].ts → [id].ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# @zhin.js/adapter-discord
|
|
2
2
|
|
|
3
|
+
## 7.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 51015d6: refactor(adapters): migrate platform-permit to PermissionSubject
|
|
8
|
+
|
|
9
|
+
All adapter platform-permit checkers now accept PermissionSubject
|
|
10
|
+
(duck-typed from Message/CommandSession) instead of Message directly.
|
|
11
|
+
registerPlatformPermitChecker is replaced by host.registerPlatform in
|
|
12
|
+
the adapter plugin setup. activity-feedback service updated for new
|
|
13
|
+
PermissionHost integration.
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [6f9c366]
|
|
16
|
+
- Updated dependencies [373a56b]
|
|
17
|
+
- Updated dependencies [0de46a8]
|
|
18
|
+
- Updated dependencies [c106ecc]
|
|
19
|
+
- Updated dependencies [ca92e03]
|
|
20
|
+
- Updated dependencies [b0f37ae]
|
|
21
|
+
- Updated dependencies [ba08a2f]
|
|
22
|
+
- Updated dependencies [b08f7fe]
|
|
23
|
+
- Updated dependencies [daffd4c]
|
|
24
|
+
- Updated dependencies [36c7400]
|
|
25
|
+
- Updated dependencies [a9fa72e]
|
|
26
|
+
- Updated dependencies [c8de3ef]
|
|
27
|
+
- Updated dependencies [60f0fc8]
|
|
28
|
+
- Updated dependencies [574c990]
|
|
29
|
+
- Updated dependencies [d096f16]
|
|
30
|
+
- Updated dependencies [3f29623]
|
|
31
|
+
- Updated dependencies [2916852]
|
|
32
|
+
- Updated dependencies [d047869]
|
|
33
|
+
- Updated dependencies [162fa34]
|
|
34
|
+
- Updated dependencies [e62561e]
|
|
35
|
+
- Updated dependencies [3eeeb46]
|
|
36
|
+
- Updated dependencies [85d0f82]
|
|
37
|
+
- Updated dependencies [61bfc1c]
|
|
38
|
+
- Updated dependencies [04bad47]
|
|
39
|
+
- Updated dependencies [e40b048]
|
|
40
|
+
- Updated dependencies [5a5b1bb]
|
|
41
|
+
- Updated dependencies [f1708c3]
|
|
42
|
+
- Updated dependencies [d254a81]
|
|
43
|
+
- Updated dependencies [7123c47]
|
|
44
|
+
- Updated dependencies [05befc1]
|
|
45
|
+
- Updated dependencies [0663b6a]
|
|
46
|
+
- Updated dependencies [f28f9b3]
|
|
47
|
+
- Updated dependencies [9f340f7]
|
|
48
|
+
- Updated dependencies [e53444f]
|
|
49
|
+
- Updated dependencies [5eedd26]
|
|
50
|
+
- Updated dependencies [92b0dd7]
|
|
51
|
+
- Updated dependencies [f919b6f]
|
|
52
|
+
- Updated dependencies [098e411]
|
|
53
|
+
- Updated dependencies [e1b7c01]
|
|
54
|
+
- Updated dependencies [9b94f87]
|
|
55
|
+
- Updated dependencies [a7df753]
|
|
56
|
+
- @zhin.js/agent@1.1.5
|
|
57
|
+
- @zhin.js/host-http@1.0.8
|
|
58
|
+
- @zhin.js/permission@1.0.1
|
|
59
|
+
- @zhin.js/im-contract@1.0.3
|
|
60
|
+
- @zhin.js/adapter@1.1.7
|
|
61
|
+
- @zhin.js/plugin-runtime@1.1.5
|
|
62
|
+
- @zhin.js/command@1.0.9
|
|
63
|
+
- @zhin.js/core@1.5.4
|
|
64
|
+
- zhin.js@6.0.4
|
|
65
|
+
|
|
66
|
+
## 7.0.4
|
|
67
|
+
|
|
68
|
+
### Patch Changes
|
|
69
|
+
|
|
70
|
+
- f8c7a54: fix: im
|
|
71
|
+
- Updated dependencies [f8c7a54]
|
|
72
|
+
- @zhin.js/logger@1.0.76
|
|
73
|
+
- @zhin.js/host-http@1.0.7
|
|
74
|
+
- @zhin.js/adapter@1.1.6
|
|
75
|
+
- @zhin.js/agent@1.1.4
|
|
76
|
+
- @zhin.js/command@1.0.8
|
|
77
|
+
- @zhin.js/core@1.5.3
|
|
78
|
+
- @zhin.js/im-contract@1.0.2
|
|
79
|
+
- @zhin.js/plugin-runtime@1.1.4
|
|
80
|
+
- zhin.js@6.0.3
|
|
81
|
+
|
|
3
82
|
## 7.0.3
|
|
4
83
|
|
|
5
84
|
### Patch Changes
|
package/adapters/discord.js
CHANGED
|
@@ -21,8 +21,8 @@ export default defineAdapter({
|
|
|
21
21
|
const config = resolveDiscordConfig(context.config);
|
|
22
22
|
const gateway = context.use(messageGatewayToken);
|
|
23
23
|
// 注册到插件运行时状态(discord.endpoint list 的"运行中"数据源)
|
|
24
|
-
context.use(discordRuntimeStateToken).endpoints.set(config.
|
|
25
|
-
|
|
24
|
+
context.use(discordRuntimeStateToken).endpoints.set(config.id, {
|
|
25
|
+
id: config.id,
|
|
26
26
|
mode: config.connection,
|
|
27
27
|
});
|
|
28
28
|
if (config.connection === 'interactions') {
|
package/adapters/discord.ts
CHANGED
|
@@ -37,8 +37,8 @@ export default defineAdapter<DiscordAdapterConfig>({
|
|
|
37
37
|
const config = resolveDiscordConfig(context.config);
|
|
38
38
|
const gateway = context.use(messageGatewayToken);
|
|
39
39
|
// 注册到插件运行时状态(discord.endpoint list 的"运行中"数据源)
|
|
40
|
-
context.use(discordRuntimeStateToken).endpoints.set(config.
|
|
41
|
-
|
|
40
|
+
context.use(discordRuntimeStateToken).endpoints.set(config.id, {
|
|
41
|
+
id: config.id,
|
|
42
42
|
mode: config.connection,
|
|
43
43
|
});
|
|
44
44
|
if (config.connection === 'interactions') {
|
|
@@ -25,11 +25,11 @@ export interface DiscordAgentEndpoint {
|
|
|
25
25
|
getGuildInfo(guildId: string): Promise<unknown>;
|
|
26
26
|
}
|
|
27
27
|
export interface DiscordAgentDeps {
|
|
28
|
-
getEndpoint: (
|
|
28
|
+
getEndpoint: (endpointKey: string) => DiscordAgentEndpoint;
|
|
29
29
|
/** Alias kept for existing agent/tools that call getGatewayEndpoint. */
|
|
30
|
-
getGatewayEndpoint: (
|
|
30
|
+
getGatewayEndpoint: (endpointKey: string) => DiscordAgentEndpoint;
|
|
31
31
|
}
|
|
32
|
-
export declare function registerDiscordAgentEndpoint(
|
|
32
|
+
export declare function registerDiscordAgentEndpoint(endpointKey: string, endpoint: DiscordAgentEndpoint): () => void;
|
|
33
33
|
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
34
34
|
export declare function setDiscordAgentDeps(deps: DiscordAgentDeps | null): void;
|
|
35
35
|
export declare function getDiscordAgentDeps(): DiscordAgentDeps;
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const endpoints = new Map();
|
|
6
6
|
let override = null;
|
|
7
|
-
export function registerDiscordAgentEndpoint(
|
|
8
|
-
endpoints.set(
|
|
7
|
+
export function registerDiscordAgentEndpoint(endpointKey, endpoint) {
|
|
8
|
+
endpoints.set(endpointKey, endpoint);
|
|
9
9
|
return () => {
|
|
10
|
-
if (endpoints.get(
|
|
11
|
-
endpoints.delete(
|
|
10
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
11
|
+
endpoints.delete(endpointKey);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
}
|
|
@@ -16,10 +16,10 @@ export function registerDiscordAgentEndpoint(endpointId, endpoint) {
|
|
|
16
16
|
export function setDiscordAgentDeps(deps) {
|
|
17
17
|
override = deps;
|
|
18
18
|
}
|
|
19
|
-
function lookup(
|
|
20
|
-
const registered = endpoints.get(
|
|
19
|
+
function lookup(endpointKey) {
|
|
20
|
+
const registered = endpoints.get(endpointKey);
|
|
21
21
|
if (!registered)
|
|
22
|
-
throw new Error(`Endpoint ${
|
|
22
|
+
throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
23
23
|
return registered;
|
|
24
24
|
}
|
|
25
25
|
export function getDiscordAgentDeps() {
|
package/lib/endpoint.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* DiscordEndpoint — lifecycle, outbound, admit, gateway / interactions modes, agent tool surface.
|
|
3
3
|
*/
|
|
4
4
|
import { ChannelType } from 'discord.js';
|
|
5
|
-
import { formatLegacyConversationRef,
|
|
6
|
-
import { formatCompact,
|
|
5
|
+
import { formatLegacyConversationRef, formatLegacyMessageRef, nativeConversationId, parseLegacyMessageReference, } from '@zhin.js/im-contract';
|
|
6
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
7
7
|
import { registerDiscordAgentEndpoint } from './discord-agent-deps.js';
|
|
8
8
|
import { connectDiscordGatewayClient, defaultCreateClient, DEFAULT_INTENTS, resolveSenderRole, toMessageCreateOptions, } from './gateway.js';
|
|
9
9
|
import { discordInboundConversation, formatButtonContent, formatButtonSegments, formatInboundContent, formatInboundSegments, formatOutboundBody, senderDisplayName, } from './protocol.js';
|
|
@@ -11,8 +11,8 @@ import { registerDiscordInteractionRoutes } from './webhook.js';
|
|
|
11
11
|
const DISCORD_API = 'https://discord.com/api/v10';
|
|
12
12
|
/** 出站 HTTP 调用统一 30s 超时。 */
|
|
13
13
|
const OUTBOUND_TIMEOUT_MS = 30_000;
|
|
14
|
-
const logger = getLogger('discord');
|
|
15
14
|
export class DiscordGatewayEndpoint {
|
|
15
|
+
#logger;
|
|
16
16
|
#options;
|
|
17
17
|
#createClient;
|
|
18
18
|
#client = null;
|
|
@@ -37,6 +37,7 @@ export class DiscordGatewayEndpoint {
|
|
|
37
37
|
},
|
|
38
38
|
});
|
|
39
39
|
constructor(options) {
|
|
40
|
+
this.#logger = getAdapterLogger('discord', options.config.id);
|
|
40
41
|
this.#options = options;
|
|
41
42
|
this.#createClient = options.createClient ?? defaultCreateClient;
|
|
42
43
|
}
|
|
@@ -45,7 +46,7 @@ export class DiscordGatewayEndpoint {
|
|
|
45
46
|
return;
|
|
46
47
|
this.#started = true;
|
|
47
48
|
try {
|
|
48
|
-
this.#unregisterAgent = registerDiscordAgentEndpoint(this.#options.config.
|
|
49
|
+
this.#unregisterAgent = registerDiscordAgentEndpoint(this.#options.config.id, this);
|
|
49
50
|
const intents = this.#options.config.intents?.length
|
|
50
51
|
? [...this.#options.config.intents]
|
|
51
52
|
: DEFAULT_INTENTS;
|
|
@@ -54,16 +55,16 @@ export class DiscordGatewayEndpoint {
|
|
|
54
55
|
onMessage: (msg) => this.admit(msg),
|
|
55
56
|
onButton: (interaction) => this.admitButton(interaction),
|
|
56
57
|
});
|
|
57
|
-
logger.info(formatCompact({
|
|
58
|
+
this.#logger.info(formatCompact({
|
|
58
59
|
op: 'connect',
|
|
59
|
-
endpoint: this.#options.config.
|
|
60
|
+
endpoint: this.#options.config.id,
|
|
60
61
|
mode: 'gateway',
|
|
61
62
|
user: this.#client.user?.tag,
|
|
62
63
|
}));
|
|
63
64
|
}
|
|
64
65
|
catch (error) {
|
|
65
66
|
await this.stop();
|
|
66
|
-
logger.error('Failed to connect Discord gateway:', error);
|
|
67
|
+
this.#logger.error('Failed to connect Discord gateway:', error);
|
|
67
68
|
throw error;
|
|
68
69
|
}
|
|
69
70
|
}
|
|
@@ -88,18 +89,17 @@ export class DiscordGatewayEndpoint {
|
|
|
88
89
|
this.#client = null;
|
|
89
90
|
}
|
|
90
91
|
this.#started = false;
|
|
91
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
92
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
92
93
|
}
|
|
93
94
|
async send({ conversation, payload }) {
|
|
94
95
|
const body = formatOutboundBody(payload);
|
|
95
|
-
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
96
|
-
const target = formatLegacyConversationRef(conversation);
|
|
96
|
+
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
97
97
|
const snowflake = await this.#sendBody(conversation.id, body);
|
|
98
|
-
const messageId =
|
|
99
|
-
logger.debug(formatCompact({
|
|
98
|
+
const messageId = formatLegacyMessageRef({ conversation, id: snowflake });
|
|
99
|
+
this.#logger.debug(formatCompact({
|
|
100
100
|
op: 'discord_send',
|
|
101
|
-
endpoint: this.#options.config.
|
|
102
|
-
target,
|
|
101
|
+
endpoint: this.#options.config.id,
|
|
102
|
+
target: formatLegacyConversationRef(conversation),
|
|
103
103
|
messageId,
|
|
104
104
|
}));
|
|
105
105
|
return messageId;
|
|
@@ -130,18 +130,22 @@ export class DiscordGatewayEndpoint {
|
|
|
130
130
|
message: { conversation, id: msg.id },
|
|
131
131
|
content: formatInboundContent(msg),
|
|
132
132
|
segments: formatInboundSegments(msg),
|
|
133
|
-
sender:
|
|
133
|
+
sender: {
|
|
134
|
+
id: msg.authorId,
|
|
135
|
+
name: senderDisplayName(msg) || undefined,
|
|
136
|
+
...(resolveSenderRole(msg) ? { roles: [resolveSenderRole(msg)] } : {}),
|
|
137
|
+
},
|
|
138
|
+
endpointId: this.#options.config.id,
|
|
139
|
+
...(msg.mentionedBot ? { mentioned: true } : {}),
|
|
134
140
|
metadata: Object.freeze({
|
|
135
|
-
endpoint: this.#options.config.name,
|
|
136
141
|
channelKind: msg.channelKind,
|
|
137
142
|
userId: msg.authorId,
|
|
138
143
|
guildId: msg.guildId,
|
|
139
144
|
permissions: msg.permissionTokens,
|
|
140
145
|
role: resolveSenderRole(msg),
|
|
141
|
-
...(msg.mentionedBot ? { mentioned: true } : {}),
|
|
142
146
|
}),
|
|
143
147
|
}).catch((err) => {
|
|
144
|
-
logger.warn(formatCompact({
|
|
148
|
+
this.#logger.warn(formatCompact({
|
|
145
149
|
op: 'discord_gateway_receive_failed',
|
|
146
150
|
target: `${conversation.kind}:${conversation.id}`,
|
|
147
151
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -158,15 +162,15 @@ export class DiscordGatewayEndpoint {
|
|
|
158
162
|
message: { conversation, id: interaction.id },
|
|
159
163
|
content: formatButtonContent(interaction),
|
|
160
164
|
segments: formatButtonSegments(interaction),
|
|
161
|
-
sender: interaction.userName,
|
|
165
|
+
sender: { id: interaction.userId, name: interaction.userName },
|
|
166
|
+
endpointId: this.#options.config.id,
|
|
162
167
|
metadata: Object.freeze({
|
|
163
|
-
endpoint: this.#options.config.name,
|
|
164
168
|
eventType: 'button',
|
|
165
169
|
payload: interaction.customId,
|
|
166
170
|
sourceMessageId: interaction.sourceMessageId,
|
|
167
171
|
}),
|
|
168
172
|
}).catch((err) => {
|
|
169
|
-
logger.warn(formatCompact({
|
|
173
|
+
this.#logger.warn(formatCompact({
|
|
170
174
|
op: 'discord_gateway_receive_failed',
|
|
171
175
|
target: `${conversation.kind}:${conversation.id}`,
|
|
172
176
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -307,6 +311,7 @@ export class DiscordGatewayEndpoint {
|
|
|
307
311
|
}
|
|
308
312
|
}
|
|
309
313
|
export class DiscordInteractionsEndpoint {
|
|
314
|
+
#logger;
|
|
310
315
|
#options;
|
|
311
316
|
#fetch;
|
|
312
317
|
#routeReleases = [];
|
|
@@ -316,6 +321,7 @@ export class DiscordInteractionsEndpoint {
|
|
|
316
321
|
recall: (messageId) => this.recallMessage(messageId),
|
|
317
322
|
});
|
|
318
323
|
constructor(options) {
|
|
324
|
+
this.#logger = getAdapterLogger('discord', options.config.id);
|
|
319
325
|
this.#options = options;
|
|
320
326
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
321
327
|
}
|
|
@@ -330,9 +336,9 @@ export class DiscordInteractionsEndpoint {
|
|
|
330
336
|
return;
|
|
331
337
|
this.#started = true;
|
|
332
338
|
this.#routeReleases.push(...registerDiscordInteractionRoutes(this.#options.http, this));
|
|
333
|
-
logger.info(formatCompact({
|
|
339
|
+
this.#logger.info(formatCompact({
|
|
334
340
|
op: 'connect',
|
|
335
|
-
endpoint: this.#options.config.
|
|
341
|
+
endpoint: this.#options.config.id,
|
|
336
342
|
mode: 'interactions',
|
|
337
343
|
path: this.#options.config.interactionsPath,
|
|
338
344
|
}));
|
|
@@ -348,7 +354,7 @@ export class DiscordInteractionsEndpoint {
|
|
|
348
354
|
for (const release of this.#routeReleases.splice(0))
|
|
349
355
|
release();
|
|
350
356
|
this.#started = false;
|
|
351
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
357
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
352
358
|
}
|
|
353
359
|
async send({ conversation, payload }) {
|
|
354
360
|
const body = formatOutboundBody(payload);
|
|
@@ -368,13 +374,8 @@ export class DiscordInteractionsEndpoint {
|
|
|
368
374
|
}
|
|
369
375
|
const data = JSON.parse(text);
|
|
370
376
|
const snowflake = data.id ?? '';
|
|
371
|
-
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
372
|
-
return snowflake
|
|
373
|
-
? formatLegacyMessageReference({
|
|
374
|
-
target: formatLegacyConversationRef(conversation),
|
|
375
|
-
messageId: snowflake,
|
|
376
|
-
})
|
|
377
|
-
: '';
|
|
377
|
+
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
378
|
+
return snowflake ? formatLegacyMessageRef({ conversation, id: snowflake }) : '';
|
|
378
379
|
}
|
|
379
380
|
async recallMessage(messageId) {
|
|
380
381
|
if (!messageId)
|
|
@@ -403,16 +404,20 @@ export class DiscordInteractionsEndpoint {
|
|
|
403
404
|
message: { conversation, id: msg.id },
|
|
404
405
|
content: formatInboundContent(msg),
|
|
405
406
|
segments: formatInboundSegments(msg),
|
|
406
|
-
sender:
|
|
407
|
+
sender: {
|
|
408
|
+
id: msg.authorId,
|
|
409
|
+
name: senderDisplayName(msg) || undefined,
|
|
410
|
+
...(resolveSenderRole(msg) ? { roles: [resolveSenderRole(msg)] } : {}),
|
|
411
|
+
},
|
|
412
|
+
endpointId: this.#options.config.id,
|
|
407
413
|
metadata: Object.freeze({
|
|
408
|
-
endpoint: this.#options.config.name,
|
|
409
414
|
channelKind: msg.channelKind,
|
|
410
415
|
userId: msg.authorId,
|
|
411
416
|
guildId: msg.guildId,
|
|
412
417
|
eventType: 'application_command',
|
|
413
418
|
}),
|
|
414
419
|
}).catch((err) => {
|
|
415
|
-
logger.warn(formatCompact({
|
|
420
|
+
this.#logger.warn(formatCompact({
|
|
416
421
|
op: 'discord_gateway_receive_failed',
|
|
417
422
|
target: `${conversation.kind}:${conversation.id}`,
|
|
418
423
|
error: err instanceof Error ? err.message : String(err),
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { activityTypeCode, formatButtonContent, formatInboundContent, formatOutboundBody, resolveChannelKind, resolveDiscordConfig, senderDisplayName, type DiscordAdapterConfig, type DiscordButtonInbound, type DiscordInboundAttachment, type DiscordInboundMessage, type DiscordOutboundBody, type DiscordWireSegment, type ResolvedDiscordConfig, type ResolvedDiscordGatewayConfig, type ResolvedDiscordInteractionsConfig, } from './protocol.js';
|
|
2
2
|
export { getDiscordAgentDeps, registerDiscordAgentEndpoint, setDiscordAgentDeps, type DiscordAgentDeps, type DiscordAgentEndpoint, } from './discord-agent-deps.js';
|
|
3
|
-
export { checkDiscordPlatformPermit, discordGroupPermitResolver, normalizeDiscordSenderForPermit, platformPermit,
|
|
3
|
+
export { checkDiscordPlatformPermit, discordGroupPermitResolver, normalizeDiscordSenderForPermit, platformPermit, } from './platform-permit.js';
|
|
4
4
|
export { DiscordGatewayEndpoint, DiscordInteractionsEndpoint, type CreateDiscordClient, type DiscordClientTransport, type DiscordEndpointOptions, type DiscordInteractionsEndpointOptions, } from './endpoint.js';
|
|
5
5
|
export { connectDiscordGatewayClient, defaultCreateClient, normalizeDiscordMessage, resolveSenderRole, toMessageCreateOptions, type DiscordGatewayConnectHandlers, } from './gateway.js';
|
|
6
6
|
export { handleDiscordInteractionRequest, registerDiscordInteractionRoutes, type DiscordInteractionsHandler, } from './webhook.js';
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { activityTypeCode, formatButtonContent, formatInboundContent, formatOutboundBody, resolveChannelKind, resolveDiscordConfig, senderDisplayName, } from './protocol.js';
|
|
2
2
|
export { getDiscordAgentDeps, registerDiscordAgentEndpoint, setDiscordAgentDeps, } from './discord-agent-deps.js';
|
|
3
|
-
export { checkDiscordPlatformPermit, discordGroupPermitResolver, normalizeDiscordSenderForPermit, platformPermit,
|
|
3
|
+
export { checkDiscordPlatformPermit, discordGroupPermitResolver, normalizeDiscordSenderForPermit, platformPermit, } from './platform-permit.js';
|
|
4
4
|
export { DiscordGatewayEndpoint, DiscordInteractionsEndpoint, } from './endpoint.js';
|
|
5
5
|
export { connectDiscordGatewayClient, defaultCreateClient, normalizeDiscordMessage, resolveSenderRole, toMessageCreateOptions, } from './gateway.js';
|
|
6
6
|
export { handleDiscordInteractionRequest, registerDiscordInteractionRoutes, } from './webhook.js';
|
package/lib/platform-permit.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Discord platform permit — Guild 权限位
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import type { PermissionSubject } from '@zhin.js/permission';
|
|
5
5
|
export declare function platformPermit(perm: string): string;
|
|
6
6
|
export declare function discordGroupPermitResolver(logicalPerm: string): string;
|
|
7
7
|
export declare function normalizeDiscordSenderForPermit(input: {
|
|
@@ -11,5 +11,4 @@ export declare function normalizeDiscordSenderForPermit(input: {
|
|
|
11
11
|
role?: string;
|
|
12
12
|
permissions?: string[];
|
|
13
13
|
};
|
|
14
|
-
export declare function checkDiscordPlatformPermit(perm: string,
|
|
15
|
-
export declare function registerDiscordPlatformPermitChecker(): () => void;
|
|
14
|
+
export declare function checkDiscordPlatformPermit(perm: string, subject: PermissionSubject): boolean;
|
package/lib/platform-permit.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discord platform permit — Guild 权限位
|
|
3
|
-
*/
|
|
4
|
-
import { registerPlatformPermitChecker } from '@zhin.js/core';
|
|
5
1
|
const ADAPTER = 'discord';
|
|
6
2
|
export function platformPermit(perm) {
|
|
7
3
|
return `platform(${ADAPTER},${perm})`;
|
|
@@ -27,12 +23,9 @@ export function normalizeDiscordSenderForPermit(input) {
|
|
|
27
23
|
}
|
|
28
24
|
return { role: 'member', permissions };
|
|
29
25
|
}
|
|
30
|
-
function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
export function checkDiscordPlatformPermit(perm, message) {
|
|
35
|
-
const { role, permissions } = senderPermits(message);
|
|
26
|
+
export function checkDiscordPlatformPermit(perm, subject) {
|
|
27
|
+
const role = subject.sender?.role?.[0];
|
|
28
|
+
const permissions = subject.sender?.permissions ?? [];
|
|
36
29
|
const has = (t) => permissions.includes(t);
|
|
37
30
|
switch (perm) {
|
|
38
31
|
case 'guild_owner':
|
|
@@ -47,6 +40,3 @@ export function checkDiscordPlatformPermit(perm, message) {
|
|
|
47
40
|
return false;
|
|
48
41
|
}
|
|
49
42
|
}
|
|
50
|
-
export function registerDiscordPlatformPermitChecker() {
|
|
51
|
-
return registerPlatformPermitChecker(ADAPTER, checkDiscordPlatformPermit);
|
|
52
|
-
}
|
package/lib/protocol.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { Segment } from '@zhin.js/core/runtime';
|
|
|
6
6
|
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
7
7
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
8
8
|
export interface DiscordAdapterConfig {
|
|
9
|
-
readonly
|
|
9
|
+
readonly id?: string;
|
|
10
10
|
readonly token?: string;
|
|
11
11
|
/** Default `gateway`. `interactions` uses httpHostToken POST + Ed25519 verify. */
|
|
12
12
|
readonly connection?: 'gateway' | 'interactions';
|
|
@@ -26,7 +26,7 @@ export interface DiscordAdapterConfig {
|
|
|
26
26
|
/** Transitional: legacy root `endpoints[]` with `context: discord`. */
|
|
27
27
|
readonly endpoints?: ReadonlyArray<{
|
|
28
28
|
readonly context?: string;
|
|
29
|
-
readonly
|
|
29
|
+
readonly id?: string;
|
|
30
30
|
readonly token?: string;
|
|
31
31
|
readonly connection?: 'gateway' | 'interactions';
|
|
32
32
|
readonly intents?: readonly number[];
|
|
@@ -42,7 +42,7 @@ export interface DiscordAdapterConfig {
|
|
|
42
42
|
export interface ResolvedDiscordGatewayConfig {
|
|
43
43
|
readonly context: 'discord';
|
|
44
44
|
readonly connection: 'gateway';
|
|
45
|
-
readonly
|
|
45
|
+
readonly id: string;
|
|
46
46
|
readonly token: string;
|
|
47
47
|
readonly intents?: readonly number[];
|
|
48
48
|
readonly enableSlashCommands: boolean;
|
|
@@ -53,7 +53,7 @@ export interface ResolvedDiscordGatewayConfig {
|
|
|
53
53
|
export interface ResolvedDiscordInteractionsConfig {
|
|
54
54
|
readonly context: 'discord';
|
|
55
55
|
readonly connection: 'interactions';
|
|
56
|
-
readonly
|
|
56
|
+
readonly id: string;
|
|
57
57
|
readonly token: string;
|
|
58
58
|
readonly applicationId: string;
|
|
59
59
|
readonly publicKey: string;
|
|
@@ -128,7 +128,7 @@ export declare function resolveChannelKind(channelType: number | string | undefi
|
|
|
128
128
|
* guild 频道 → 'channel' + guild 容器进 parent(guild 是频道容器,kind 'channel')。
|
|
129
129
|
* endpoint.adapter = owner PluginId(CapabilityId 以 \0 分隔)。
|
|
130
130
|
*/
|
|
131
|
-
export declare function discordInboundConversation(
|
|
131
|
+
export declare function discordInboundConversation(endpointKey: string, msg: {
|
|
132
132
|
readonly channelId: string;
|
|
133
133
|
readonly channelKind: 'private' | 'group' | 'channel';
|
|
134
134
|
readonly guildId?: string;
|
package/lib/protocol.js
CHANGED
|
@@ -15,8 +15,8 @@ export function resolveDiscordConfig(config = {}) {
|
|
|
15
15
|
if (!token) {
|
|
16
16
|
throw new TypeError('Discord adapter requires token (plugins.<key>.token or endpoints with context: discord)');
|
|
17
17
|
}
|
|
18
|
-
const
|
|
19
|
-
|| (typeof entry?.
|
|
18
|
+
const id = (typeof config.id === 'string' && config.id)
|
|
19
|
+
|| (typeof entry?.id === 'string' && entry.id)
|
|
20
20
|
|| process.env.DISCORD_BOT_NAME
|
|
21
21
|
|| 'discord-bot';
|
|
22
22
|
const connection = config.connection
|
|
@@ -31,7 +31,7 @@ export function resolveDiscordConfig(config = {}) {
|
|
|
31
31
|
return {
|
|
32
32
|
context: 'discord',
|
|
33
33
|
connection: 'interactions',
|
|
34
|
-
|
|
34
|
+
id,
|
|
35
35
|
token,
|
|
36
36
|
applicationId,
|
|
37
37
|
publicKey,
|
|
@@ -41,7 +41,7 @@ export function resolveDiscordConfig(config = {}) {
|
|
|
41
41
|
return {
|
|
42
42
|
context: 'discord',
|
|
43
43
|
connection: 'gateway',
|
|
44
|
-
|
|
44
|
+
id,
|
|
45
45
|
token,
|
|
46
46
|
intents: config.intents ?? entry?.intents,
|
|
47
47
|
enableSlashCommands: config.enableSlashCommands === true
|
|
@@ -64,9 +64,9 @@ export function resolveChannelKind(channelType) {
|
|
|
64
64
|
* guild 频道 → 'channel' + guild 容器进 parent(guild 是频道容器,kind 'channel')。
|
|
65
65
|
* endpoint.adapter = owner PluginId(CapabilityId 以 \0 分隔)。
|
|
66
66
|
*/
|
|
67
|
-
export function discordInboundConversation(
|
|
67
|
+
export function discordInboundConversation(endpointKey, msg) {
|
|
68
68
|
return {
|
|
69
|
-
endpoint: { id:
|
|
69
|
+
endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
|
|
70
70
|
kind: msg.channelKind,
|
|
71
71
|
id: msg.channelId,
|
|
72
72
|
...(msg.guildId && msg.channelKind === 'channel'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-discord",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.5",
|
|
4
4
|
"description": "Zhin.js Discord adapter for Plugin Runtime (Gateway WebSocket)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -33,21 +33,22 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"discord.js": "^14.27.0",
|
|
36
|
-
"@zhin.js/adapter": "1.1.
|
|
37
|
-
"@zhin.js/command": "1.0.
|
|
38
|
-
"@zhin.js/core": "1.5.
|
|
39
|
-
"@zhin.js/host-http": "1.0.
|
|
40
|
-
"@zhin.js/im-contract": "1.0.
|
|
41
|
-
"@zhin.js/logger": "1.0.
|
|
42
|
-
"@zhin.js/
|
|
36
|
+
"@zhin.js/adapter": "1.1.7",
|
|
37
|
+
"@zhin.js/command": "1.0.9",
|
|
38
|
+
"@zhin.js/core": "1.5.4",
|
|
39
|
+
"@zhin.js/host-http": "1.0.8",
|
|
40
|
+
"@zhin.js/im-contract": "1.0.3",
|
|
41
|
+
"@zhin.js/logger": "1.0.76",
|
|
42
|
+
"@zhin.js/permission": "1.0.1",
|
|
43
|
+
"@zhin.js/plugin-runtime": "1.1.5"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"zod": "^4.0.0",
|
|
46
|
-
"@zhin.js/adapter": "1.1.
|
|
47
|
-
"@zhin.js/agent": "1.1.
|
|
48
|
-
"@zhin.js/core": "1.5.
|
|
49
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
50
|
-
"zhin.js": "6.0.
|
|
47
|
+
"@zhin.js/adapter": "1.1.7",
|
|
48
|
+
"@zhin.js/agent": "1.1.5",
|
|
49
|
+
"@zhin.js/core": "1.5.4",
|
|
50
|
+
"@zhin.js/plugin-runtime": "1.1.5",
|
|
51
|
+
"zhin.js": "6.0.4"
|
|
51
52
|
},
|
|
52
53
|
"peerDependenciesMeta": {
|
|
53
54
|
"zhin.js": {
|
|
@@ -65,9 +66,9 @@
|
|
|
65
66
|
"typescript": "^6.0.3",
|
|
66
67
|
"vitest": "^4.1.10",
|
|
67
68
|
"zod": "^4.4.3",
|
|
68
|
-
"@zhin.js/agent": "1.1.
|
|
69
|
-
"@zhin.js/host-http": "1.0.
|
|
70
|
-
"zhin.js": "6.0.
|
|
69
|
+
"@zhin.js/agent": "1.1.5",
|
|
70
|
+
"@zhin.js/host-http": "1.0.8",
|
|
71
|
+
"zhin.js": "6.0.4"
|
|
71
72
|
},
|
|
72
73
|
"files": [
|
|
73
74
|
"adapters",
|
package/plugin.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { createEndpointRuntimeState } from '@zhin.js/adapter';
|
|
3
3
|
import { definePlugin } from '@zhin.js/plugin-runtime';
|
|
4
|
-
import {
|
|
4
|
+
import { permissionHostToken } from '@zhin.js/permission';
|
|
5
|
+
import { checkDiscordPlatformPermit } from "./lib/platform-permit.js";
|
|
5
6
|
import { discordRuntimeStateToken } from "./lib/discord-runtime-state.js";
|
|
6
7
|
export default definePlugin({
|
|
7
8
|
name: 'discord',
|
|
@@ -9,9 +10,10 @@ export default definePlugin({
|
|
|
9
10
|
displayName: 'Discord Gateway Adapter',
|
|
10
11
|
},
|
|
11
12
|
setup(context) {
|
|
12
|
-
// 运行中 endpoint 注册表(discord.endpoint list 的"运行中"数据源)
|
|
13
13
|
context.resources.provide(discordRuntimeStateToken, createEndpointRuntimeState());
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
if (context.resources.has(permissionHostToken)) {
|
|
15
|
+
const host = context.resources.use(permissionHostToken);
|
|
16
|
+
return host.registerPlatform('discord', checkDiscordPlatformPermit);
|
|
17
|
+
}
|
|
16
18
|
},
|
|
17
19
|
});
|
package/schema.json
CHANGED
|
@@ -71,24 +71,60 @@
|
|
|
71
71
|
"default": "/discord/interactions",
|
|
72
72
|
"description": "POST path on httpHostToken when connection is interactions."
|
|
73
73
|
},
|
|
74
|
+
"master": {
|
|
75
|
+
"type": [
|
|
76
|
+
"string",
|
|
77
|
+
"number"
|
|
78
|
+
],
|
|
79
|
+
"description": "框架 master(Discord user snowflake;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
|
|
80
|
+
},
|
|
81
|
+
"trusted": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"items": {
|
|
84
|
+
"type": [
|
|
85
|
+
"string",
|
|
86
|
+
"number"
|
|
87
|
+
],
|
|
88
|
+
"description": "Trusted Discord user snowflake"
|
|
89
|
+
},
|
|
90
|
+
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
|
|
91
|
+
},
|
|
74
92
|
"endpoints": {
|
|
75
93
|
"type": "array",
|
|
76
|
-
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(
|
|
94
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
|
|
77
95
|
"items": {
|
|
78
96
|
"type": "object",
|
|
79
97
|
"additionalProperties": true,
|
|
80
98
|
"properties": {
|
|
81
|
-
"
|
|
82
|
-
"type":
|
|
83
|
-
|
|
99
|
+
"master": {
|
|
100
|
+
"type": [
|
|
101
|
+
"string",
|
|
102
|
+
"number"
|
|
103
|
+
],
|
|
104
|
+
"description": "本 endpoint 的框架 master(Discord user snowflake);覆盖顶层 master"
|
|
105
|
+
},
|
|
106
|
+
"trusted": {
|
|
107
|
+
"type": "array",
|
|
108
|
+
"items": {
|
|
109
|
+
"type": [
|
|
110
|
+
"string",
|
|
111
|
+
"number"
|
|
112
|
+
],
|
|
113
|
+
"description": "Trusted Discord user snowflake"
|
|
114
|
+
},
|
|
115
|
+
"description": "本 endpoint 的 trusted 列表"
|
|
84
116
|
},
|
|
85
117
|
"token": {
|
|
86
118
|
"type": "string",
|
|
87
119
|
"description": "Discord bot token"
|
|
120
|
+
},
|
|
121
|
+
"id": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "Discord bot name"
|
|
88
124
|
}
|
|
89
125
|
},
|
|
90
126
|
"required": [
|
|
91
|
-
"
|
|
127
|
+
"id",
|
|
92
128
|
"token"
|
|
93
129
|
]
|
|
94
130
|
}
|
|
@@ -39,22 +39,22 @@ export interface DiscordAgentEndpoint {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export interface DiscordAgentDeps {
|
|
42
|
-
getEndpoint: (
|
|
42
|
+
getEndpoint: (endpointKey: string) => DiscordAgentEndpoint;
|
|
43
43
|
/** Alias kept for existing agent/tools that call getGatewayEndpoint. */
|
|
44
|
-
getGatewayEndpoint: (
|
|
44
|
+
getGatewayEndpoint: (endpointKey: string) => DiscordAgentEndpoint;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const endpoints = new Map<string, DiscordAgentEndpoint>();
|
|
48
48
|
let override: DiscordAgentDeps | null = null;
|
|
49
49
|
|
|
50
50
|
export function registerDiscordAgentEndpoint(
|
|
51
|
-
|
|
51
|
+
endpointKey: string,
|
|
52
52
|
endpoint: DiscordAgentEndpoint,
|
|
53
53
|
): () => void {
|
|
54
|
-
endpoints.set(
|
|
54
|
+
endpoints.set(endpointKey, endpoint);
|
|
55
55
|
return () => {
|
|
56
|
-
if (endpoints.get(
|
|
57
|
-
endpoints.delete(
|
|
56
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
57
|
+
endpoints.delete(endpointKey);
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
}
|
|
@@ -64,9 +64,9 @@ export function setDiscordAgentDeps(deps: DiscordAgentDeps | null): void {
|
|
|
64
64
|
override = deps;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
function lookup(
|
|
68
|
-
const registered = endpoints.get(
|
|
69
|
-
if (!registered) throw new Error(`Endpoint ${
|
|
67
|
+
function lookup(endpointKey: string): DiscordAgentEndpoint {
|
|
68
|
+
const registered = endpoints.get(endpointKey);
|
|
69
|
+
if (!registered) throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
70
70
|
return registered;
|
|
71
71
|
}
|
|
72
72
|
|
package/src/endpoint.ts
CHANGED
|
@@ -14,11 +14,11 @@ import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
|
14
14
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
15
15
|
import {
|
|
16
16
|
formatLegacyConversationRef,
|
|
17
|
-
|
|
17
|
+
formatLegacyMessageRef,
|
|
18
18
|
nativeConversationId,
|
|
19
19
|
parseLegacyMessageReference,
|
|
20
20
|
} from '@zhin.js/im-contract';
|
|
21
|
-
import { formatCompact,
|
|
21
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
22
22
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
23
23
|
import { registerDiscordAgentEndpoint } from './discord-agent-deps.js';
|
|
24
24
|
import {
|
|
@@ -49,8 +49,6 @@ import { registerDiscordInteractionRoutes } from './webhook.js';
|
|
|
49
49
|
const DISCORD_API = 'https://discord.com/api/v10';
|
|
50
50
|
/** 出站 HTTP 调用统一 30s 超时。 */
|
|
51
51
|
const OUTBOUND_TIMEOUT_MS = 30_000;
|
|
52
|
-
const logger = getLogger('discord');
|
|
53
|
-
|
|
54
52
|
export type {
|
|
55
53
|
CreateDiscordClient,
|
|
56
54
|
DiscordClientTransport,
|
|
@@ -64,6 +62,8 @@ export interface DiscordEndpointOptions {
|
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
export class DiscordGatewayEndpoint implements EndpointInstance {
|
|
65
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
66
|
+
|
|
67
67
|
readonly #options: DiscordEndpointOptions;
|
|
68
68
|
readonly #createClient: CreateDiscordClient;
|
|
69
69
|
#client: DiscordClientTransport | null = null;
|
|
@@ -92,6 +92,7 @@ export class DiscordGatewayEndpoint implements EndpointInstance {
|
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
constructor(options: DiscordEndpointOptions) {
|
|
95
|
+
this.#logger = getAdapterLogger('discord', options.config.id);
|
|
95
96
|
this.#options = options;
|
|
96
97
|
this.#createClient = options.createClient ?? defaultCreateClient;
|
|
97
98
|
}
|
|
@@ -100,7 +101,7 @@ export class DiscordGatewayEndpoint implements EndpointInstance {
|
|
|
100
101
|
if (this.#started) return;
|
|
101
102
|
this.#started = true;
|
|
102
103
|
try {
|
|
103
|
-
this.#unregisterAgent = registerDiscordAgentEndpoint(this.#options.config.
|
|
104
|
+
this.#unregisterAgent = registerDiscordAgentEndpoint(this.#options.config.id, this);
|
|
104
105
|
const intents = this.#options.config.intents?.length
|
|
105
106
|
? [...this.#options.config.intents]
|
|
106
107
|
: DEFAULT_INTENTS;
|
|
@@ -109,15 +110,15 @@ export class DiscordGatewayEndpoint implements EndpointInstance {
|
|
|
109
110
|
onMessage: (msg) => this.admit(msg),
|
|
110
111
|
onButton: (interaction) => this.admitButton(interaction),
|
|
111
112
|
});
|
|
112
|
-
logger.info(formatCompact({
|
|
113
|
+
this.#logger.info(formatCompact({
|
|
113
114
|
op: 'connect',
|
|
114
|
-
endpoint: this.#options.config.
|
|
115
|
+
endpoint: this.#options.config.id,
|
|
115
116
|
mode: 'gateway',
|
|
116
117
|
user: this.#client.user?.tag,
|
|
117
118
|
}));
|
|
118
119
|
} catch (error) {
|
|
119
120
|
await this.stop();
|
|
120
|
-
logger.error('Failed to connect Discord gateway:', error);
|
|
121
|
+
this.#logger.error('Failed to connect Discord gateway:', error);
|
|
121
122
|
throw error;
|
|
122
123
|
}
|
|
123
124
|
}
|
|
@@ -144,19 +145,18 @@ export class DiscordGatewayEndpoint implements EndpointInstance {
|
|
|
144
145
|
this.#client = null;
|
|
145
146
|
}
|
|
146
147
|
this.#started = false;
|
|
147
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
148
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
151
152
|
const body = formatOutboundBody(payload);
|
|
152
|
-
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
153
|
-
const target = formatLegacyConversationRef(conversation);
|
|
153
|
+
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
154
154
|
const snowflake = await this.#sendBody(conversation.id, body);
|
|
155
|
-
const messageId =
|
|
156
|
-
logger.debug(formatCompact({
|
|
155
|
+
const messageId = formatLegacyMessageRef({ conversation, id: snowflake });
|
|
156
|
+
this.#logger.debug(formatCompact({
|
|
157
157
|
op: 'discord_send',
|
|
158
|
-
endpoint: this.#options.config.
|
|
159
|
-
target,
|
|
158
|
+
endpoint: this.#options.config.id,
|
|
159
|
+
target: formatLegacyConversationRef(conversation),
|
|
160
160
|
messageId,
|
|
161
161
|
}));
|
|
162
162
|
return messageId;
|
|
@@ -184,18 +184,22 @@ export class DiscordGatewayEndpoint implements EndpointInstance {
|
|
|
184
184
|
message: { conversation, id: msg.id },
|
|
185
185
|
content: formatInboundContent(msg),
|
|
186
186
|
segments: formatInboundSegments(msg),
|
|
187
|
-
sender:
|
|
187
|
+
sender: {
|
|
188
|
+
id: msg.authorId,
|
|
189
|
+
name: senderDisplayName(msg) || undefined,
|
|
190
|
+
...(resolveSenderRole(msg) ? { roles: [resolveSenderRole(msg)!] } : {}),
|
|
191
|
+
},
|
|
192
|
+
endpointId: this.#options.config.id,
|
|
193
|
+
...(msg.mentionedBot ? { mentioned: true } : {}),
|
|
188
194
|
metadata: Object.freeze({
|
|
189
|
-
endpoint: this.#options.config.name,
|
|
190
195
|
channelKind: msg.channelKind,
|
|
191
196
|
userId: msg.authorId,
|
|
192
197
|
guildId: msg.guildId,
|
|
193
198
|
permissions: msg.permissionTokens,
|
|
194
199
|
role: resolveSenderRole(msg),
|
|
195
|
-
...(msg.mentionedBot ? { mentioned: true } : {}),
|
|
196
200
|
}),
|
|
197
201
|
}).catch((err) => {
|
|
198
|
-
logger.warn(formatCompact({
|
|
202
|
+
this.#logger.warn(formatCompact({
|
|
199
203
|
op: 'discord_gateway_receive_failed',
|
|
200
204
|
target: `${conversation.kind}:${conversation.id}`,
|
|
201
205
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -212,15 +216,15 @@ export class DiscordGatewayEndpoint implements EndpointInstance {
|
|
|
212
216
|
message: { conversation, id: interaction.id },
|
|
213
217
|
content: formatButtonContent(interaction),
|
|
214
218
|
segments: formatButtonSegments(interaction),
|
|
215
|
-
sender: interaction.userName,
|
|
219
|
+
sender: { id: interaction.userId, name: interaction.userName },
|
|
220
|
+
endpointId: this.#options.config.id,
|
|
216
221
|
metadata: Object.freeze({
|
|
217
|
-
endpoint: this.#options.config.name,
|
|
218
222
|
eventType: 'button',
|
|
219
223
|
payload: interaction.customId,
|
|
220
224
|
sourceMessageId: interaction.sourceMessageId,
|
|
221
225
|
}),
|
|
222
226
|
}).catch((err) => {
|
|
223
|
-
logger.warn(formatCompact({
|
|
227
|
+
this.#logger.warn(formatCompact({
|
|
224
228
|
op: 'discord_gateway_receive_failed',
|
|
225
229
|
target: `${conversation.kind}:${conversation.id}`,
|
|
226
230
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -419,6 +423,8 @@ export interface DiscordInteractionsEndpointOptions {
|
|
|
419
423
|
}
|
|
420
424
|
|
|
421
425
|
export class DiscordInteractionsEndpoint implements EndpointInstance {
|
|
426
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
427
|
+
|
|
422
428
|
readonly #options: DiscordInteractionsEndpointOptions;
|
|
423
429
|
readonly #fetch: typeof globalThis.fetch;
|
|
424
430
|
#routeReleases: HttpRouteRegistration[] = [];
|
|
@@ -429,6 +435,7 @@ export class DiscordInteractionsEndpoint implements EndpointInstance {
|
|
|
429
435
|
});
|
|
430
436
|
|
|
431
437
|
constructor(options: DiscordInteractionsEndpointOptions) {
|
|
438
|
+
this.#logger = getAdapterLogger('discord', options.config.id);
|
|
432
439
|
this.#options = options;
|
|
433
440
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
434
441
|
}
|
|
@@ -445,9 +452,9 @@ export class DiscordInteractionsEndpoint implements EndpointInstance {
|
|
|
445
452
|
if (this.#started) return;
|
|
446
453
|
this.#started = true;
|
|
447
454
|
this.#routeReleases.push(...registerDiscordInteractionRoutes(this.#options.http, this));
|
|
448
|
-
logger.info(formatCompact({
|
|
455
|
+
this.#logger.info(formatCompact({
|
|
449
456
|
op: 'connect',
|
|
450
|
-
endpoint: this.#options.config.
|
|
457
|
+
endpoint: this.#options.config.id,
|
|
451
458
|
mode: 'interactions',
|
|
452
459
|
path: this.#options.config.interactionsPath,
|
|
453
460
|
}));
|
|
@@ -465,7 +472,7 @@ export class DiscordInteractionsEndpoint implements EndpointInstance {
|
|
|
465
472
|
this.#open = false;
|
|
466
473
|
for (const release of this.#routeReleases.splice(0)) release();
|
|
467
474
|
this.#started = false;
|
|
468
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
475
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
469
476
|
}
|
|
470
477
|
|
|
471
478
|
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
@@ -486,13 +493,8 @@ export class DiscordInteractionsEndpoint implements EndpointInstance {
|
|
|
486
493
|
}
|
|
487
494
|
const data = JSON.parse(text) as { id?: string };
|
|
488
495
|
const snowflake = data.id ?? '';
|
|
489
|
-
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
490
|
-
return snowflake
|
|
491
|
-
? formatLegacyMessageReference({
|
|
492
|
-
target: formatLegacyConversationRef(conversation),
|
|
493
|
-
messageId: snowflake,
|
|
494
|
-
})
|
|
495
|
-
: '';
|
|
496
|
+
// recall 链路仍是本端点编码的 legacy 引用,在边界内部从 conversation 派生
|
|
497
|
+
return snowflake ? formatLegacyMessageRef({ conversation, id: snowflake }) : '';
|
|
496
498
|
}
|
|
497
499
|
|
|
498
500
|
async recallMessage(messageId: string): Promise<void> {
|
|
@@ -520,16 +522,20 @@ export class DiscordInteractionsEndpoint implements EndpointInstance {
|
|
|
520
522
|
message: { conversation, id: msg.id },
|
|
521
523
|
content: formatInboundContent(msg),
|
|
522
524
|
segments: formatInboundSegments(msg),
|
|
523
|
-
sender:
|
|
525
|
+
sender: {
|
|
526
|
+
id: msg.authorId,
|
|
527
|
+
name: senderDisplayName(msg) || undefined,
|
|
528
|
+
...(resolveSenderRole(msg) ? { roles: [resolveSenderRole(msg)!] } : {}),
|
|
529
|
+
},
|
|
530
|
+
endpointId: this.#options.config.id,
|
|
524
531
|
metadata: Object.freeze({
|
|
525
|
-
endpoint: this.#options.config.name,
|
|
526
532
|
channelKind: msg.channelKind,
|
|
527
533
|
userId: msg.authorId,
|
|
528
534
|
guildId: msg.guildId,
|
|
529
535
|
eventType: 'application_command',
|
|
530
536
|
}),
|
|
531
537
|
}).catch((err) => {
|
|
532
|
-
logger.warn(formatCompact({
|
|
538
|
+
this.#logger.warn(formatCompact({
|
|
533
539
|
op: 'discord_gateway_receive_failed',
|
|
534
540
|
target: `${conversation.kind}:${conversation.id}`,
|
|
535
541
|
error: err instanceof Error ? err.message : String(err),
|
package/src/index.ts
CHANGED
package/src/platform-permit.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Discord platform permit — Guild 权限位
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import type { PermissionSubject } from '@zhin.js/permission';
|
|
5
5
|
|
|
6
6
|
const ADAPTER = 'discord';
|
|
7
7
|
|
|
@@ -36,13 +36,9 @@ export function normalizeDiscordSenderForPermit(input: {
|
|
|
36
36
|
return { role: 'member', permissions };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function checkDiscordPlatformPermit(perm: string, message: Message<any>): boolean {
|
|
45
|
-
const { role, permissions } = senderPermits(message);
|
|
39
|
+
export function checkDiscordPlatformPermit(perm: string, subject: PermissionSubject): boolean {
|
|
40
|
+
const role = subject.sender?.role?.[0];
|
|
41
|
+
const permissions = subject.sender?.permissions ?? [];
|
|
46
42
|
const has = (t: string) => permissions.includes(t);
|
|
47
43
|
|
|
48
44
|
switch (perm) {
|
|
@@ -59,6 +55,3 @@ export function checkDiscordPlatformPermit(perm: string, message: Message<any>):
|
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
|
|
62
|
-
export function registerDiscordPlatformPermitChecker(): () => void {
|
|
63
|
-
return registerPlatformPermitChecker(ADAPTER, checkDiscordPlatformPermit);
|
|
64
|
-
}
|
package/src/protocol.ts
CHANGED
|
@@ -13,7 +13,7 @@ const logger = getLogger('discord');
|
|
|
13
13
|
|
|
14
14
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
15
15
|
export interface DiscordAdapterConfig {
|
|
16
|
-
readonly
|
|
16
|
+
readonly id?: string;
|
|
17
17
|
readonly token?: string;
|
|
18
18
|
/** Default `gateway`. `interactions` uses httpHostToken POST + Ed25519 verify. */
|
|
19
19
|
readonly connection?: 'gateway' | 'interactions';
|
|
@@ -33,7 +33,7 @@ export interface DiscordAdapterConfig {
|
|
|
33
33
|
/** Transitional: legacy root `endpoints[]` with `context: discord`. */
|
|
34
34
|
readonly endpoints?: ReadonlyArray<{
|
|
35
35
|
readonly context?: string;
|
|
36
|
-
readonly
|
|
36
|
+
readonly id?: string;
|
|
37
37
|
readonly token?: string;
|
|
38
38
|
readonly connection?: 'gateway' | 'interactions';
|
|
39
39
|
readonly intents?: readonly number[];
|
|
@@ -50,7 +50,7 @@ export interface DiscordAdapterConfig {
|
|
|
50
50
|
export interface ResolvedDiscordGatewayConfig {
|
|
51
51
|
readonly context: 'discord';
|
|
52
52
|
readonly connection: 'gateway';
|
|
53
|
-
readonly
|
|
53
|
+
readonly id: string;
|
|
54
54
|
readonly token: string;
|
|
55
55
|
readonly intents?: readonly number[];
|
|
56
56
|
readonly enableSlashCommands: boolean;
|
|
@@ -62,7 +62,7 @@ export interface ResolvedDiscordGatewayConfig {
|
|
|
62
62
|
export interface ResolvedDiscordInteractionsConfig {
|
|
63
63
|
readonly context: 'discord';
|
|
64
64
|
readonly connection: 'interactions';
|
|
65
|
-
readonly
|
|
65
|
+
readonly id: string;
|
|
66
66
|
readonly token: string;
|
|
67
67
|
readonly applicationId: string;
|
|
68
68
|
readonly publicKey: string;
|
|
@@ -152,8 +152,8 @@ export function resolveDiscordConfig(config: DiscordAdapterConfig = {}): Resolve
|
|
|
152
152
|
'Discord adapter requires token (plugins.<key>.token or endpoints with context: discord)',
|
|
153
153
|
);
|
|
154
154
|
}
|
|
155
|
-
const
|
|
156
|
-
|| (typeof entry?.
|
|
155
|
+
const id = (typeof config.id === 'string' && config.id)
|
|
156
|
+
|| (typeof entry?.id === 'string' && entry.id)
|
|
157
157
|
|| process.env.DISCORD_BOT_NAME
|
|
158
158
|
|| 'discord-bot';
|
|
159
159
|
const connection = config.connection
|
|
@@ -171,7 +171,7 @@ export function resolveDiscordConfig(config: DiscordAdapterConfig = {}): Resolve
|
|
|
171
171
|
return {
|
|
172
172
|
context: 'discord',
|
|
173
173
|
connection: 'interactions',
|
|
174
|
-
|
|
174
|
+
id,
|
|
175
175
|
token,
|
|
176
176
|
applicationId,
|
|
177
177
|
publicKey,
|
|
@@ -182,7 +182,7 @@ export function resolveDiscordConfig(config: DiscordAdapterConfig = {}): Resolve
|
|
|
182
182
|
return {
|
|
183
183
|
context: 'discord',
|
|
184
184
|
connection: 'gateway',
|
|
185
|
-
|
|
185
|
+
id,
|
|
186
186
|
token,
|
|
187
187
|
intents: config.intents ?? entry?.intents,
|
|
188
188
|
enableSlashCommands: config.enableSlashCommands === true
|
|
@@ -206,7 +206,7 @@ export function resolveChannelKind(channelType: number | string | undefined): 'p
|
|
|
206
206
|
* endpoint.adapter = owner PluginId(CapabilityId 以 \0 分隔)。
|
|
207
207
|
*/
|
|
208
208
|
export function discordInboundConversation(
|
|
209
|
-
|
|
209
|
+
endpointKey: string,
|
|
210
210
|
msg: {
|
|
211
211
|
readonly channelId: string;
|
|
212
212
|
readonly channelKind: 'private' | 'group' | 'channel';
|
|
@@ -214,7 +214,7 @@ export function discordInboundConversation(
|
|
|
214
214
|
},
|
|
215
215
|
): ConversationRef {
|
|
216
216
|
return {
|
|
217
|
-
endpoint: { id:
|
|
217
|
+
endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
|
|
218
218
|
kind: msg.channelKind,
|
|
219
219
|
id: msg.channelId,
|
|
220
220
|
...(msg.guildId && msg.channelKind === 'channel'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|