@zhin.js/adapter-discord 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -0
- package/README.md +7 -8
- package/adapters/{discord.js → discord/index.js} +6 -6
- package/adapters/{discord.ts → discord/index.ts} +9 -9
- package/agents/discord/agent.json +20 -0
- package/agents/discord/boundaries.md +3 -0
- package/agents/discord/conventions.md +3 -0
- package/agents/discord/skills/discord/SKILL.md +21 -0
- package/agents/discord/skills/discord/tools/add_role/index.js +24 -0
- package/{agent/tools/add_role.ts → agents/discord/skills/discord/tools/add_role/index.ts} +4 -4
- package/agents/discord/skills/discord/tools/create_thread/index.js +29 -0
- package/{agent/tools/create_thread.ts → agents/discord/skills/discord/tools/create_thread/index.ts} +4 -4
- package/agents/discord/skills/discord/tools/forum_post/index.js +33 -0
- package/{agent/tools/forum_post.ts → agents/discord/skills/discord/tools/forum_post/index.ts} +2 -2
- package/agents/discord/skills/discord/tools/list_roles/index.js +28 -0
- package/{agent/tools/list_roles.ts → agents/discord/skills/discord/tools/list_roles/index.ts} +4 -4
- package/agents/discord/skills/discord/tools/react/index.js +24 -0
- package/{agent/tools/react.ts → agents/discord/skills/discord/tools/react/index.ts} +2 -2
- package/agents/discord/skills/discord/tools/remove_role/index.js +24 -0
- package/{agent/tools/remove_role.ts → agents/discord/skills/discord/tools/remove_role/index.ts} +4 -4
- package/agents/discord/skills/discord/tools/send_embed/index.js +43 -0
- package/{agent/tools/send_embed.ts → agents/discord/skills/discord/tools/send_embed/index.ts} +2 -2
- package/agents/discord/system.md +3 -0
- package/commands/{endpoint/add/[id].js → discord/endpoint/add/[id]/index.js} +1 -1
- package/commands/discord/endpoint/add/[id]/index.ts +3 -0
- package/{lib/discord-endpoint-commands.js → commands/discord/endpoint/definition.js} +5 -4
- package/{src/discord-endpoint-commands.ts → commands/discord/endpoint/definition.ts} +4 -4
- package/commands/{endpoint/list.js → discord/endpoint/list/index.js} +1 -1
- package/commands/discord/endpoint/list/index.ts +3 -0
- package/commands/{endpoint/remove/[id].js → discord/endpoint/remove/[id]/index.js} +1 -1
- package/commands/discord/endpoint/remove/[id]/index.ts +3 -0
- package/lib/discord-runtime-state.js +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +15 -32
- package/lib/protocol.js +20 -33
- package/package.json +32 -21
- package/schema.json +66 -2
- package/src/discord-runtime-state.ts +1 -1
- package/src/index.ts +3 -1
- package/src/protocol.ts +37 -64
- package/agent/skills/discord.md +0 -104
- package/commands/endpoint/add/[id].ts +0 -3
- package/commands/endpoint/list.ts +0 -3
- package/commands/endpoint/remove/[id].ts +0 -3
- package/lib/discord-endpoint-commands.d.ts +0 -1
- /package/{agent/PERMITS.md → PERMITS.md} +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
1
2
|
/**
|
|
2
|
-
* `discord
|
|
3
|
-
* commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
|
|
3
|
+
* `discord endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
|
|
4
|
+
* commands/discord/endpoint/ 下的 list / add / remove 直接默认导出这三项。
|
|
4
5
|
* 注:applicationId / publicKey 为顶层共享字段(slash commands / interactions 用),
|
|
5
|
-
* endpoints[i] 仅需
|
|
6
|
+
* endpoints[i] 仅需 id + token(见 schema.json),故 add 只录入 token。
|
|
6
7
|
*/
|
|
7
8
|
import { createEndpointCommands } from 'zhin.js/adapter';
|
|
8
9
|
import { defineCommand } from 'zhin.js/command';
|
|
9
|
-
import { discordRuntimeStateToken } from
|
|
10
|
+
import { discordRuntimeStateToken } from "../../../lib/discord-runtime-state.js";
|
|
10
11
|
export const discordEndpointCommands = createEndpointCommands({
|
|
11
12
|
adapterKey: 'discord',
|
|
12
13
|
adapterDisplayName: 'Discord',
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `discord
|
|
3
|
-
* commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
|
|
2
|
+
* `discord endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
|
|
3
|
+
* commands/discord/endpoint/ 下的 list / add / remove 直接默认导出这三项。
|
|
4
4
|
* 注:applicationId / publicKey 为顶层共享字段(slash commands / interactions 用),
|
|
5
|
-
* endpoints[i] 仅需
|
|
5
|
+
* endpoints[i] 仅需 id + token(见 schema.json),故 add 只录入 token。
|
|
6
6
|
*/
|
|
7
7
|
import { createEndpointCommands } from 'zhin.js/adapter';
|
|
8
8
|
import { defineCommand } from 'zhin.js/command';
|
|
9
|
-
import { discordRuntimeStateToken } from '
|
|
9
|
+
import { discordRuntimeStateToken } from '../../../src/discord-runtime-state.js';
|
|
10
10
|
|
|
11
11
|
export const discordEndpointCommands = createEndpointCommands({
|
|
12
12
|
adapterKey: 'discord',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Discord 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
-
* 由 plugin.ts setup() provide,adapter create 与 `discord
|
|
3
|
+
* 由 plugin.ts setup() provide,adapter create 与 `discord endpoint` 命令共享(同一 owner generation)。
|
|
4
4
|
*/
|
|
5
5
|
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
6
|
export const discordRuntimeStateToken = defineEndpointRuntimeStateToken('discord');
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { activityTypeCode, formatButtonContent, formatInboundContent, formatOutboundBody, resolveChannelKind, resolveDiscordConfig, senderDisplayName, type
|
|
2
|
-
export { discordClient, type DiscordClient, type DiscordClientEventMap, } from './client.js';
|
|
1
|
+
export { activityTypeCode, formatButtonContent, formatInboundContent, formatOutboundBody, resolveChannelKind, resolveDiscordConfig, senderDisplayName, type DiscordActivity, type DiscordEndpointConfig, type DiscordButtonInbound, type DiscordInboundAttachment, type DiscordInboundMessage, type DiscordOutboundBody, type DiscordWireSegment, type ResolvedDiscordConfig, type ResolvedDiscordGatewayConfig, type ResolvedDiscordInteractionsConfig, } from './protocol.js';
|
|
2
|
+
export { discordClient, requireDiscordGatewayClient, type DiscordClient, type DiscordClientEventMap, } from './client.js';
|
|
3
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';
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { activityTypeCode, formatButtonContent, formatInboundContent, formatOutboundBody, resolveChannelKind, resolveDiscordConfig, senderDisplayName, } from './protocol.js';
|
|
2
|
-
export { discordClient, } from './client.js';
|
|
2
|
+
export { discordClient, requireDiscordGatewayClient, } from './client.js';
|
|
3
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';
|
package/lib/protocol.d.ts
CHANGED
|
@@ -1,43 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
|
-
*/
|
|
1
|
+
/** Discord Gateway protocol helpers. Canonicalization is owned by Core. */
|
|
2
|
+
import { type ConversationRef } from '@zhin.js/im-contract';
|
|
5
3
|
import type { Segment } from '@zhin.js/core/runtime';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
readonly
|
|
10
|
-
|
|
4
|
+
export interface DiscordActivity {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly type: 'PLAYING' | 'STREAMING' | 'LISTENING' | 'WATCHING' | 'COMPETING';
|
|
7
|
+
readonly url?: string;
|
|
8
|
+
}
|
|
9
|
+
/** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
|
|
10
|
+
export interface DiscordEndpointConfig {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly token: string;
|
|
11
13
|
/** Default `gateway`. `interactions` uses httpHostToken POST + Ed25519 verify. */
|
|
12
14
|
readonly connection?: 'gateway' | 'interactions';
|
|
13
15
|
readonly intents?: readonly number[];
|
|
14
16
|
readonly enableSlashCommands?: boolean;
|
|
15
17
|
readonly globalCommands?: boolean;
|
|
16
|
-
readonly defaultActivity?:
|
|
17
|
-
readonly name: string;
|
|
18
|
-
readonly type: 'PLAYING' | 'STREAMING' | 'LISTENING' | 'WATCHING' | 'COMPETING';
|
|
19
|
-
readonly url?: string;
|
|
20
|
-
};
|
|
18
|
+
readonly defaultActivity?: DiscordActivity;
|
|
21
19
|
readonly slashCommands?: readonly Record<string, unknown>[];
|
|
22
20
|
/** Interactions-only fields. */
|
|
23
21
|
readonly applicationId?: string;
|
|
24
22
|
readonly publicKey?: string;
|
|
25
23
|
readonly interactionsPath?: string;
|
|
26
|
-
/** Transitional: legacy root `endpoints[]` with `context: discord`. */
|
|
27
|
-
readonly endpoints?: ReadonlyArray<{
|
|
28
|
-
readonly context?: string;
|
|
29
|
-
readonly id?: string;
|
|
30
|
-
readonly token?: string;
|
|
31
|
-
readonly connection?: 'gateway' | 'interactions';
|
|
32
|
-
readonly intents?: readonly number[];
|
|
33
|
-
readonly enableSlashCommands?: boolean;
|
|
34
|
-
readonly globalCommands?: boolean;
|
|
35
|
-
readonly defaultActivity?: DiscordAdapterConfig['defaultActivity'];
|
|
36
|
-
readonly slashCommands?: readonly Record<string, unknown>[];
|
|
37
|
-
readonly applicationId?: string;
|
|
38
|
-
readonly publicKey?: string;
|
|
39
|
-
readonly interactionsPath?: string;
|
|
40
|
-
}>;
|
|
41
24
|
}
|
|
42
25
|
export interface ResolvedDiscordGatewayConfig {
|
|
43
26
|
readonly context: 'discord';
|
|
@@ -47,7 +30,7 @@ export interface ResolvedDiscordGatewayConfig {
|
|
|
47
30
|
readonly intents?: readonly number[];
|
|
48
31
|
readonly enableSlashCommands: boolean;
|
|
49
32
|
readonly globalCommands: boolean;
|
|
50
|
-
readonly defaultActivity?:
|
|
33
|
+
readonly defaultActivity?: DiscordActivity;
|
|
51
34
|
readonly slashCommands?: readonly Record<string, unknown>[];
|
|
52
35
|
}
|
|
53
36
|
export interface ResolvedDiscordInteractionsConfig {
|
|
@@ -121,7 +104,7 @@ export interface DiscordOutboundBody {
|
|
|
121
104
|
}>;
|
|
122
105
|
readonly components?: ReadonlyArray<DiscordOutboundActionRow>;
|
|
123
106
|
}
|
|
124
|
-
export declare function resolveDiscordConfig(config
|
|
107
|
+
export declare function resolveDiscordConfig(config: DiscordEndpointConfig): ResolvedDiscordConfig;
|
|
125
108
|
export declare function resolveChannelKind(channelType: number | string | undefined): 'private' | 'group' | 'channel';
|
|
126
109
|
/**
|
|
127
110
|
* 入站归一化 → ConversationRef:DM → 'private';GroupDM → 'group';
|
|
@@ -154,7 +137,7 @@ export declare function formatButtonSegments(interaction: DiscordButtonInbound):
|
|
|
154
137
|
* Segment canonicalization is intentionally not done here.
|
|
155
138
|
*/
|
|
156
139
|
export declare function formatOutboundBody(payload: unknown): DiscordOutboundBody;
|
|
157
|
-
export declare function activityTypeCode(type:
|
|
140
|
+
export declare function activityTypeCode(type: DiscordActivity['type']): number;
|
|
158
141
|
/** interactions 验签时间戳时效窗口(防重放)。 */
|
|
159
142
|
export declare const DISCORD_SIGNATURE_MAX_AGE_MS: number;
|
|
160
143
|
export declare function verifyDiscordInteractionSignature(publicKeyHex: string, body: string, signature: string, timestamp: string, nowMs?: number): boolean;
|
package/lib/protocol.js
CHANGED
|
@@ -1,33 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discord Gateway protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
|
|
3
|
-
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
|
-
*/
|
|
1
|
+
/** Discord Gateway protocol helpers. Canonicalization is owned by Core. */
|
|
5
2
|
import { createPublicKey, verify as cryptoVerify } from 'node:crypto';
|
|
6
|
-
import { isMediaRef } from '@zhin.js/
|
|
3
|
+
import { isMediaRef } from '@zhin.js/im-contract';
|
|
7
4
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
8
5
|
const logger = getLogger('discord');
|
|
9
|
-
export function resolveDiscordConfig(config
|
|
10
|
-
const
|
|
11
|
-
const token = (
|
|
12
|
-
|
|
13
|
-
|| process.env.DISCORD_BOT_TOKEN
|
|
14
|
-
|| '';
|
|
15
|
-
if (!token) {
|
|
16
|
-
throw new TypeError('Discord adapter requires token (plugins.<key>.token or endpoints with context: discord)');
|
|
17
|
-
}
|
|
18
|
-
const id = (typeof config.id === 'string' && config.id)
|
|
19
|
-
|| (typeof entry?.id === 'string' && entry.id)
|
|
20
|
-
|| process.env.DISCORD_BOT_NAME
|
|
21
|
-
|| 'discord-bot';
|
|
22
|
-
const connection = config.connection
|
|
23
|
-
?? entry?.connection
|
|
24
|
-
?? 'gateway';
|
|
6
|
+
export function resolveDiscordConfig(config) {
|
|
7
|
+
const id = requiredEndpointField(config.id, 'id');
|
|
8
|
+
const token = requiredEndpointField(config.token, 'token');
|
|
9
|
+
const connection = config.connection ?? 'gateway';
|
|
25
10
|
if (connection === 'interactions') {
|
|
26
|
-
const applicationId = config.applicationId
|
|
27
|
-
const publicKey = config.publicKey
|
|
28
|
-
if (!applicationId || !publicKey) {
|
|
29
|
-
throw new TypeError('Discord connection:interactions requires applicationId and publicKey');
|
|
30
|
-
}
|
|
11
|
+
const applicationId = requiredEndpointField(config.applicationId, 'applicationId');
|
|
12
|
+
const publicKey = requiredEndpointField(config.publicKey, 'publicKey');
|
|
31
13
|
return {
|
|
32
14
|
context: 'discord',
|
|
33
15
|
connection: 'interactions',
|
|
@@ -35,7 +17,7 @@ export function resolveDiscordConfig(config = {}) {
|
|
|
35
17
|
token,
|
|
36
18
|
applicationId,
|
|
37
19
|
publicKey,
|
|
38
|
-
interactionsPath: config.interactionsPath ||
|
|
20
|
+
interactionsPath: config.interactionsPath || '/discord/interactions',
|
|
39
21
|
};
|
|
40
22
|
}
|
|
41
23
|
return {
|
|
@@ -43,14 +25,19 @@ export function resolveDiscordConfig(config = {}) {
|
|
|
43
25
|
connection: 'gateway',
|
|
44
26
|
id,
|
|
45
27
|
token,
|
|
46
|
-
intents: config.intents
|
|
47
|
-
enableSlashCommands: config.enableSlashCommands === true
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
slashCommands: config.slashCommands ?? entry?.slashCommands,
|
|
28
|
+
intents: config.intents,
|
|
29
|
+
enableSlashCommands: config.enableSlashCommands === true,
|
|
30
|
+
globalCommands: config.globalCommands === true,
|
|
31
|
+
defaultActivity: config.defaultActivity,
|
|
32
|
+
slashCommands: config.slashCommands,
|
|
52
33
|
};
|
|
53
34
|
}
|
|
35
|
+
function requiredEndpointField(value, field) {
|
|
36
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
37
|
+
throw new TypeError(`Discord endpoint requires a non-empty ${field}`);
|
|
38
|
+
}
|
|
39
|
+
return value.trim();
|
|
40
|
+
}
|
|
54
41
|
export function resolveChannelKind(channelType) {
|
|
55
42
|
// discord.js ChannelType.DM = 1, GroupDM = 3
|
|
56
43
|
if (channelType === 1 || channelType === 'DM' || channelType === 'private')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-discord",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Zhin.js Discord adapter for Plugin Runtime (Gateway WebSocket)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -33,26 +33,25 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"discord.js": "^14.27.0",
|
|
36
|
-
"@zhin.js/adapter": "1.1.
|
|
37
|
-
"@zhin.js/
|
|
38
|
-
"@zhin.js/
|
|
39
|
-
"@zhin.js/
|
|
40
|
-
"@zhin.js/
|
|
41
|
-
"@zhin.js/
|
|
42
|
-
"@zhin.js/
|
|
36
|
+
"@zhin.js/adapter": "1.1.14",
|
|
37
|
+
"@zhin.js/agent-feature": "1.1.1",
|
|
38
|
+
"@zhin.js/core": "1.1.37",
|
|
39
|
+
"@zhin.js/feature-kit": "1.1.1",
|
|
40
|
+
"@zhin.js/host-http": "1.1.1",
|
|
41
|
+
"@zhin.js/im-contract": "1.1.1",
|
|
42
|
+
"@zhin.js/logger": "1.1.1",
|
|
43
|
+
"@zhin.js/permission": "1.1.1",
|
|
44
|
+
"@zhin.js/skill": "1.1.1",
|
|
45
|
+
"@zhin.js/tool": "1.1.1"
|
|
43
46
|
},
|
|
44
47
|
"peerDependencies": {
|
|
45
48
|
"zod": "^4.0.0",
|
|
46
|
-
"@zhin.js/adapter": "^1.1.
|
|
47
|
-
"@zhin.js/
|
|
48
|
-
"@zhin.js/
|
|
49
|
-
"
|
|
50
|
-
"zhin.js": "^1.1.0"
|
|
49
|
+
"@zhin.js/adapter": "^1.1.14",
|
|
50
|
+
"@zhin.js/command": "^1.1.1",
|
|
51
|
+
"@zhin.js/core": "^1.1.37",
|
|
52
|
+
"zhin.js": "^1.1.2"
|
|
51
53
|
},
|
|
52
54
|
"peerDependenciesMeta": {
|
|
53
|
-
"@zhin.js/agent": {
|
|
54
|
-
"optional": true
|
|
55
|
-
},
|
|
56
55
|
"@zhin.js/command": {
|
|
57
56
|
"optional": true
|
|
58
57
|
},
|
|
@@ -68,9 +67,8 @@
|
|
|
68
67
|
"typescript": "^6.0.3",
|
|
69
68
|
"vitest": "^4.1.10",
|
|
70
69
|
"zod": "^4.4.3",
|
|
71
|
-
"@zhin.js/
|
|
72
|
-
"
|
|
73
|
-
"zhin.js": "1.1.0"
|
|
70
|
+
"@zhin.js/host-http": "1.1.1",
|
|
71
|
+
"zhin.js": "1.1.2"
|
|
74
72
|
},
|
|
75
73
|
"files": [
|
|
76
74
|
"adapters",
|
|
@@ -79,9 +77,10 @@
|
|
|
79
77
|
"schema.json",
|
|
80
78
|
"src",
|
|
81
79
|
"lib",
|
|
82
|
-
"
|
|
80
|
+
"agents",
|
|
83
81
|
"README.md",
|
|
84
|
-
"CHANGELOG.md"
|
|
82
|
+
"CHANGELOG.md",
|
|
83
|
+
"PERMITS.md"
|
|
85
84
|
],
|
|
86
85
|
"publishConfig": {
|
|
87
86
|
"access": "public",
|
|
@@ -104,6 +103,18 @@
|
|
|
104
103
|
{
|
|
105
104
|
"package": "@zhin.js/command",
|
|
106
105
|
"api": "^1.0.0"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"package": "@zhin.js/tool",
|
|
109
|
+
"api": "^1.0.0"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"package": "@zhin.js/skill",
|
|
113
|
+
"api": "^1.0.0"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"package": "@zhin.js/agent-feature",
|
|
117
|
+
"api": "^1.0.0"
|
|
107
118
|
}
|
|
108
119
|
],
|
|
109
120
|
"plugins": []
|
package/schema.json
CHANGED
|
@@ -91,10 +91,11 @@
|
|
|
91
91
|
},
|
|
92
92
|
"endpoints": {
|
|
93
93
|
"type": "array",
|
|
94
|
-
"
|
|
94
|
+
"minItems": 1,
|
|
95
|
+
"description": "多账号:每项定义一个 endpoint,id/token 必填,其余字段覆盖实例默认值",
|
|
95
96
|
"items": {
|
|
96
97
|
"type": "object",
|
|
97
|
-
"additionalProperties":
|
|
98
|
+
"additionalProperties": false,
|
|
98
99
|
"properties": {
|
|
99
100
|
"master": {
|
|
100
101
|
"type": [
|
|
@@ -118,6 +119,69 @@
|
|
|
118
119
|
"type": "string",
|
|
119
120
|
"description": "Discord bot token"
|
|
120
121
|
},
|
|
122
|
+
"connection": {
|
|
123
|
+
"type": "string",
|
|
124
|
+
"enum": [
|
|
125
|
+
"gateway",
|
|
126
|
+
"interactions"
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
"intents": {
|
|
130
|
+
"type": "array",
|
|
131
|
+
"items": {
|
|
132
|
+
"type": "number"
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"enableSlashCommands": {
|
|
136
|
+
"type": "boolean"
|
|
137
|
+
},
|
|
138
|
+
"globalCommands": {
|
|
139
|
+
"type": "boolean"
|
|
140
|
+
},
|
|
141
|
+
"defaultActivity": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"additionalProperties": false,
|
|
144
|
+
"properties": {
|
|
145
|
+
"name": {
|
|
146
|
+
"type": "string"
|
|
147
|
+
},
|
|
148
|
+
"type": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"enum": [
|
|
151
|
+
"PLAYING",
|
|
152
|
+
"STREAMING",
|
|
153
|
+
"LISTENING",
|
|
154
|
+
"WATCHING",
|
|
155
|
+
"COMPETING"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"url": {
|
|
159
|
+
"type": "string"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"required": [
|
|
163
|
+
"name",
|
|
164
|
+
"type"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
"slashCommands": {
|
|
168
|
+
"type": "array",
|
|
169
|
+
"items": {
|
|
170
|
+
"type": "object"
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"applicationId": {
|
|
174
|
+
"type": "string"
|
|
175
|
+
},
|
|
176
|
+
"publicKey": {
|
|
177
|
+
"type": "string"
|
|
178
|
+
},
|
|
179
|
+
"interactionsPath": {
|
|
180
|
+
"type": "string"
|
|
181
|
+
},
|
|
182
|
+
"commandPrefix": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
},
|
|
121
185
|
"id": {
|
|
122
186
|
"type": "string",
|
|
123
187
|
"description": "Discord bot name"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Discord 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
-
* 由 plugin.ts setup() provide,adapter create 与 `discord
|
|
3
|
+
* 由 plugin.ts setup() provide,adapter create 与 `discord endpoint` 命令共享(同一 owner generation)。
|
|
4
4
|
*/
|
|
5
5
|
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
6
|
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,8 @@ export {
|
|
|
6
6
|
resolveChannelKind,
|
|
7
7
|
resolveDiscordConfig,
|
|
8
8
|
senderDisplayName,
|
|
9
|
-
type
|
|
9
|
+
type DiscordActivity,
|
|
10
|
+
type DiscordEndpointConfig,
|
|
10
11
|
type DiscordButtonInbound,
|
|
11
12
|
type DiscordInboundAttachment,
|
|
12
13
|
type DiscordInboundMessage,
|
|
@@ -19,6 +20,7 @@ export {
|
|
|
19
20
|
|
|
20
21
|
export {
|
|
21
22
|
discordClient,
|
|
23
|
+
requireDiscordGatewayClient,
|
|
22
24
|
type DiscordClient,
|
|
23
25
|
type DiscordClientEventMap,
|
|
24
26
|
} from './client.js';
|
package/src/protocol.ts
CHANGED
|
@@ -1,50 +1,33 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discord Gateway protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
|
|
3
|
-
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
|
-
*/
|
|
1
|
+
/** Discord Gateway protocol helpers. Canonicalization is owned by Core. */
|
|
5
2
|
|
|
6
3
|
import { createPublicKey, verify as cryptoVerify } from 'node:crypto';
|
|
7
|
-
import { isMediaRef } from '@zhin.js/
|
|
4
|
+
import { isMediaRef, type ConversationRef } from '@zhin.js/im-contract';
|
|
8
5
|
import type { Segment } from '@zhin.js/core/runtime';
|
|
9
|
-
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
10
6
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
11
7
|
|
|
12
8
|
const logger = getLogger('discord');
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
10
|
+
export interface DiscordActivity {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly type: 'PLAYING' | 'STREAMING' | 'LISTENING' | 'WATCHING' | 'COMPETING';
|
|
13
|
+
readonly url?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
|
|
17
|
+
export interface DiscordEndpointConfig {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly token: string;
|
|
18
20
|
/** Default `gateway`. `interactions` uses httpHostToken POST + Ed25519 verify. */
|
|
19
21
|
readonly connection?: 'gateway' | 'interactions';
|
|
20
22
|
readonly intents?: readonly number[];
|
|
21
23
|
readonly enableSlashCommands?: boolean;
|
|
22
24
|
readonly globalCommands?: boolean;
|
|
23
|
-
readonly defaultActivity?:
|
|
24
|
-
readonly name: string;
|
|
25
|
-
readonly type: 'PLAYING' | 'STREAMING' | 'LISTENING' | 'WATCHING' | 'COMPETING';
|
|
26
|
-
readonly url?: string;
|
|
27
|
-
};
|
|
25
|
+
readonly defaultActivity?: DiscordActivity;
|
|
28
26
|
readonly slashCommands?: readonly Record<string, unknown>[];
|
|
29
27
|
/** Interactions-only fields. */
|
|
30
28
|
readonly applicationId?: string;
|
|
31
29
|
readonly publicKey?: string;
|
|
32
30
|
readonly interactionsPath?: string;
|
|
33
|
-
/** Transitional: legacy root `endpoints[]` with `context: discord`. */
|
|
34
|
-
readonly endpoints?: ReadonlyArray<{
|
|
35
|
-
readonly context?: string;
|
|
36
|
-
readonly id?: string;
|
|
37
|
-
readonly token?: string;
|
|
38
|
-
readonly connection?: 'gateway' | 'interactions';
|
|
39
|
-
readonly intents?: readonly number[];
|
|
40
|
-
readonly enableSlashCommands?: boolean;
|
|
41
|
-
readonly globalCommands?: boolean;
|
|
42
|
-
readonly defaultActivity?: DiscordAdapterConfig['defaultActivity'];
|
|
43
|
-
readonly slashCommands?: readonly Record<string, unknown>[];
|
|
44
|
-
readonly applicationId?: string;
|
|
45
|
-
readonly publicKey?: string;
|
|
46
|
-
readonly interactionsPath?: string;
|
|
47
|
-
}>;
|
|
48
31
|
}
|
|
49
32
|
|
|
50
33
|
export interface ResolvedDiscordGatewayConfig {
|
|
@@ -55,7 +38,7 @@ export interface ResolvedDiscordGatewayConfig {
|
|
|
55
38
|
readonly intents?: readonly number[];
|
|
56
39
|
readonly enableSlashCommands: boolean;
|
|
57
40
|
readonly globalCommands: boolean;
|
|
58
|
-
readonly defaultActivity?:
|
|
41
|
+
readonly defaultActivity?: DiscordActivity;
|
|
59
42
|
readonly slashCommands?: readonly Record<string, unknown>[];
|
|
60
43
|
}
|
|
61
44
|
|
|
@@ -141,33 +124,14 @@ export interface DiscordOutboundBody {
|
|
|
141
124
|
readonly components?: ReadonlyArray<DiscordOutboundActionRow>;
|
|
142
125
|
}
|
|
143
126
|
|
|
144
|
-
export function resolveDiscordConfig(config:
|
|
145
|
-
const
|
|
146
|
-
const token = (
|
|
147
|
-
|
|
148
|
-
|| process.env.DISCORD_BOT_TOKEN
|
|
149
|
-
|| '';
|
|
150
|
-
if (!token) {
|
|
151
|
-
throw new TypeError(
|
|
152
|
-
'Discord adapter requires token (plugins.<key>.token or endpoints with context: discord)',
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
const id = (typeof config.id === 'string' && config.id)
|
|
156
|
-
|| (typeof entry?.id === 'string' && entry.id)
|
|
157
|
-
|| process.env.DISCORD_BOT_NAME
|
|
158
|
-
|| 'discord-bot';
|
|
159
|
-
const connection = config.connection
|
|
160
|
-
?? entry?.connection
|
|
161
|
-
?? 'gateway';
|
|
127
|
+
export function resolveDiscordConfig(config: DiscordEndpointConfig): ResolvedDiscordConfig {
|
|
128
|
+
const id = requiredEndpointField(config.id, 'id');
|
|
129
|
+
const token = requiredEndpointField(config.token, 'token');
|
|
130
|
+
const connection = config.connection ?? 'gateway';
|
|
162
131
|
|
|
163
132
|
if (connection === 'interactions') {
|
|
164
|
-
const applicationId = config.applicationId
|
|
165
|
-
const publicKey = config.publicKey
|
|
166
|
-
if (!applicationId || !publicKey) {
|
|
167
|
-
throw new TypeError(
|
|
168
|
-
'Discord connection:interactions requires applicationId and publicKey',
|
|
169
|
-
);
|
|
170
|
-
}
|
|
133
|
+
const applicationId = requiredEndpointField(config.applicationId, 'applicationId');
|
|
134
|
+
const publicKey = requiredEndpointField(config.publicKey, 'publicKey');
|
|
171
135
|
return {
|
|
172
136
|
context: 'discord',
|
|
173
137
|
connection: 'interactions',
|
|
@@ -175,7 +139,7 @@ export function resolveDiscordConfig(config: DiscordAdapterConfig = {}): Resolve
|
|
|
175
139
|
token,
|
|
176
140
|
applicationId,
|
|
177
141
|
publicKey,
|
|
178
|
-
interactionsPath: config.interactionsPath ||
|
|
142
|
+
interactionsPath: config.interactionsPath || '/discord/interactions',
|
|
179
143
|
};
|
|
180
144
|
}
|
|
181
145
|
|
|
@@ -184,15 +148,24 @@ export function resolveDiscordConfig(config: DiscordAdapterConfig = {}): Resolve
|
|
|
184
148
|
connection: 'gateway',
|
|
185
149
|
id,
|
|
186
150
|
token,
|
|
187
|
-
intents: config.intents
|
|
188
|
-
enableSlashCommands: config.enableSlashCommands === true
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
slashCommands: config.slashCommands ?? entry?.slashCommands,
|
|
151
|
+
intents: config.intents,
|
|
152
|
+
enableSlashCommands: config.enableSlashCommands === true,
|
|
153
|
+
globalCommands: config.globalCommands === true,
|
|
154
|
+
defaultActivity: config.defaultActivity,
|
|
155
|
+
slashCommands: config.slashCommands,
|
|
193
156
|
};
|
|
194
157
|
}
|
|
195
158
|
|
|
159
|
+
function requiredEndpointField(
|
|
160
|
+
value: unknown,
|
|
161
|
+
field: 'id' | 'token' | 'applicationId' | 'publicKey',
|
|
162
|
+
): string {
|
|
163
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
164
|
+
throw new TypeError(`Discord endpoint requires a non-empty ${field}`);
|
|
165
|
+
}
|
|
166
|
+
return value.trim();
|
|
167
|
+
}
|
|
168
|
+
|
|
196
169
|
export function resolveChannelKind(channelType: number | string | undefined): 'private' | 'group' | 'channel' {
|
|
197
170
|
// discord.js ChannelType.DM = 1, GroupDM = 3
|
|
198
171
|
if (channelType === 1 || channelType === 'DM' || channelType === 'private') return 'private';
|
|
@@ -431,7 +404,7 @@ export function formatOutboundBody(payload: unknown): DiscordOutboundBody {
|
|
|
431
404
|
}
|
|
432
405
|
|
|
433
406
|
export function activityTypeCode(
|
|
434
|
-
type:
|
|
407
|
+
type: DiscordActivity['type'],
|
|
435
408
|
): number {
|
|
436
409
|
const map = {
|
|
437
410
|
PLAYING: 0,
|