@zhin.js/adapter-discord 1.1.18 → 1.1.20
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 +46 -0
- package/README.md +10 -3
- package/adapters/discord/index.js +3 -23
- package/adapters/discord/index.ts +2 -27
- package/agents/discord/skills/discord/tools/send_embed/index.js +1 -1
- package/agents/discord/skills/discord/tools/send_embed/index.ts +1 -1
- package/agents/discord/skills/discord-guild/tools/create_thread/index.js +6 -2
- package/agents/discord/skills/discord-guild/tools/create_thread/index.ts +5 -2
- package/agents/discord/skills/discord-guild/tools/forum_post/index.js +1 -1
- package/agents/discord/skills/discord-guild/tools/forum_post/index.ts +1 -1
- package/commands/discord/endpoint/definition.js +0 -2
- package/commands/discord/endpoint/definition.ts +0 -2
- package/lib/adapter.d.ts +14 -0
- package/lib/adapter.js +55 -0
- package/lib/client.d.ts +9 -9
- package/lib/client.js +0 -2
- package/lib/console-spec.js +9 -0
- package/lib/console.d.ts +1 -7
- package/lib/console.js +2 -5
- package/lib/endpoint.d.ts +36 -32
- package/lib/endpoint.js +174 -170
- package/lib/gateway.d.ts +6 -124
- package/lib/gateway.js +88 -63
- package/lib/index.d.ts +4 -3
- package/lib/index.js +3 -2
- package/lib/protocol.d.ts +4 -0
- package/lib/protocol.js +1 -0
- package/lib/webhook.d.ts +2 -3
- package/lib/webhook.js +42 -30
- package/package.json +18 -18
- package/plugin.js +1 -4
- package/src/adapter.ts +38 -0
- package/src/client.ts +6 -12
- package/src/console-spec.ts +9 -0
- package/src/console.ts +2 -6
- package/src/endpoint.ts +197 -172
- package/src/gateway.ts +103 -151
- package/src/index.ts +2 -3
- package/src/protocol.ts +5 -0
- package/src/webhook.ts +42 -32
- package/lib/discord-runtime-state.d.ts +0 -1
- package/lib/discord-runtime-state.js +0 -6
- package/src/discord-runtime-state.ts +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @zhin.js/adapter-discord
|
|
2
2
|
|
|
3
|
+
## 1.1.20
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [5e878ba]
|
|
8
|
+
- @zhin.js/page@1.1.12
|
|
9
|
+
|
|
10
|
+
## 1.1.19
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 2c3610a: Move account lifecycle ownership into the generic Adapter base and let native Endpoints retain their SDK Client inheritance. Infer platform-specific operation Sessions from a single adapter registry while preserving command parameter, option, configuration and factory inference.
|
|
15
|
+
|
|
16
|
+
Bind message replies, management ports, login challenges and Agent cancellation to the captured account incarnation. Persist runtime account intent with encrypted, revision-checked Host storage; keep account management independent of Root process restarts. Require explicit operator confirmation before importing existing accounts, and give replacement accounts a new identity without inheriting previous Sessions.
|
|
17
|
+
|
|
18
|
+
Expose account save, start, stop, update and replacement through the existing adapter Console, with exact account ownership, truthful pending/unknown outcomes and no startup configuration or environment-file rewrites.
|
|
19
|
+
|
|
20
|
+
Complete the target migration across every adapter, the public authoring API, generators, examples and Console. This intentionally removes the old create factory, inheritable Endpoint/ClientEndpoint classes, Client descriptors and file-rewrite account management; plugin authors must use createAdapter, native Client inheritance and the single AdapterRegistry. Satori and OneBot12 require explicit platform/userId selection, and WeCom requires a separate agentId.
|
|
21
|
+
|
|
22
|
+
Verify opaque credential changes before the same configuration snapshot is committed. Drain admitted native callbacks across close/rollback, cancel target work on stop, and keep raw transport parsing, platform ACK timing and SDK native methods under their existing owners. Account metadata conceals credentials carried in URLs.
|
|
23
|
+
|
|
24
|
+
- a9f4e48: PATCH COMPATIBILITY CUTOVER: this batch intentionally changes plugin APIs. The maintainer explicitly chose patch releases with declared version boundaries. Upgrade the complete tested dependency set; patch numbering does not promise compatibility with the removed interfaces.
|
|
25
|
+
|
|
26
|
+
The first planned floors are zhin.js 1.1.19, Core 1.1.54, Adapter 1.1.26, FeatureKit 1.1.10, Plugin Runtime 1.1.18, Runtime/Isolate 1.1.16 and Agent 1.1.45. Keep old-interface plugins with the earlier locked stack until they are migrated. Adapter's independent Feature API changes from 1.0.0 to 2.0.0; official consumers declare ^2.0.0. Existing Feature API admission rejects explicitly incompatible ranges; omitted API metadata keeps its existing behavior and does not certify compatibility. Runtime engine and patch-only release gates remain unchanged.
|
|
27
|
+
|
|
28
|
+
Use the native-Client Adapter target contract, canonical FeatureKit AdapterRegistry augmentation and named Endpoint control/management requests. Route message controls from canonical message/conversation refs; retain exact source authority and propagate caller/account/explicit-Root-stop cancellation. Replace removed Agent activity feedback exports with source-bound feedback. HMR pause/resume belongs to concrete plugin instances; plugins choose accepted-work policy without changing Controller commit/rollback authority. Cooperative worker/process control cancellation does not kill an isolate or reopen retired business admission.
|
|
29
|
+
|
|
30
|
+
Provide authenticated historical-data preview, exact selected binding, transactional application, authoritative inspection and explicit resumption, including active games. Preserve before-images and journals; never replay an unconfirmed effect, infer an account from an alias, or reuse historical permission grants. Owner V2 approvals remain inactive legacy data, while new writes use V3. Rollback requires matching software, lockfile and verified data; installing old code alone cannot read a new V3 approval file. Conflicting after-images are never overwritten.
|
|
31
|
+
|
|
32
|
+
See docs/plans/adapter-operation-version-boundaries.md and the Chinese/English upgrade guide for the complete version inventory, custom-plugin changes, acceptance and recovery sequence. These code and metadata changes do not authorize merging, publication, deployment, login, production migration or removal of installed workspace patches.
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [2c3610a]
|
|
35
|
+
- Updated dependencies [a9f4e48]
|
|
36
|
+
- Updated dependencies [f8505b0]
|
|
37
|
+
- Updated dependencies [2c3610a]
|
|
38
|
+
- @zhin.js/adapter@1.1.26
|
|
39
|
+
- @zhin.js/feature-kit@1.1.10
|
|
40
|
+
- @zhin.js/core@1.1.54
|
|
41
|
+
- @zhin.js/host-http@1.1.12
|
|
42
|
+
- @zhin.js/client@1.1.22
|
|
43
|
+
- @zhin.js/page@1.1.11
|
|
44
|
+
- @zhin.js/agent-feature@1.1.11
|
|
45
|
+
- @zhin.js/skill@1.1.11
|
|
46
|
+
- @zhin.js/tool@1.1.13
|
|
47
|
+
- @zhin.js/permission@1.1.10
|
|
48
|
+
|
|
3
49
|
## 1.1.18
|
|
4
50
|
|
|
5
51
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -20,12 +20,13 @@ pnpm add @zhin.js/adapter-discord discord.js
|
|
|
20
20
|
## Plugin Runtime
|
|
21
21
|
|
|
22
22
|
- `@zhin.js/adapter` — 约定式 `adapters/discord/index.ts`(`defineAdapter`)
|
|
23
|
-
- `@zhin.js/core` — `
|
|
23
|
+
- `@zhin.js/core` — `endpoint[endpointSpec].events` 入站、`outboundMessageToken` 出站
|
|
24
24
|
- `zhin.js` — `plugin.ts`(`definePlugin`)
|
|
25
25
|
- 配置经插件 `schema.json` 落到顶层 `<instanceKey>`
|
|
26
26
|
- **无需** `@zhin.js/host-http` / `@zhin.js/host-router`(Gateway 路径)
|
|
27
27
|
|
|
28
|
-
入站:`
|
|
28
|
+
入站:`endpoint[endpointSpec].events.emit('message.receive', { conversation, message, content: text, sender, metadata })`(`conversation` 为 `ConversationRef`,guild 频道带 `parent` guild 容器)
|
|
29
|
+
|
|
29
30
|
出站:`send({ conversation, payload })` → discord.js channel.send
|
|
30
31
|
|
|
31
32
|
### 平台权限(platform permit)
|
|
@@ -92,11 +93,17 @@ discord:
|
|
|
92
93
|
启动后打开 Console 的「Discord」页(地址 `/discord`),可以:
|
|
93
94
|
|
|
94
95
|
- 查看运行中的连接、点「测试连接」做连通性诊断;
|
|
95
|
-
-
|
|
96
|
+
- 在账号库保存、修改、启动、停止、替换或删除该 Adapter 的账号;新增仅保存,不创建 SDK Client,需手动启动;
|
|
96
97
|
- 如果适配器要求登录,页面会显示 LoginAssist 的扫码 / 滑块 / 设备确认待办;
|
|
97
98
|
|
|
98
99
|
读取需要 `discord:read`,写操作需要 `discord:write`;管理员 full token 不受限。页面里的「添加 endpoint」等价于原来的 `discord endpoint add`。
|
|
99
100
|
|
|
101
|
+
首次修改启动配置里的既有账号前,管理员必须核对清单并确认一次迁移。原 canonical 身份与历史保留,完整账号配置加密进入 Host 数据库;此后账号管理只写数据库,不修改启动文件或 `.env`,不重启进程。没有持久化账号端口的环境可使用静态配置连接,动态写操作明确不可用。
|
|
102
|
+
|
|
103
|
+
启停意图在进程重启后恢复。停止或删除会取消并关闭目标账号的在途操作,其他账号继续运行;删除保留历史。已启动账号的连接配置更新先关闭旧 incarnation,再仅重连该账号;平台身份变化按替换处理,分配新内部身份、保留旧历史,新账号不继承旧 Session、个人设置或启动意图。提交结果未知时先刷新核实,不自动重复 CAS 或平台操作。
|
|
104
|
+
|
|
105
|
+
Discord 的 token / publicKey 更新通过只读 GET `/applications/@me` 核验真实应用 ID 与 `verify_key`;Interactions 模式必须与配置的 applicationId / publicKey 一致。HTTP 入站先验签,再核对必填 application_id,错误应用、缺失身份或验证失败都不能进入任何平台或业务事件。
|
|
106
|
+
|
|
100
107
|
## 故障排查
|
|
101
108
|
|
|
102
109
|
| 现象 | 排查 |
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/discord/index.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { resolveDiscordConfig, } from "../../lib/protocol.js";
|
|
9
|
-
import { discordRuntimeStateToken } from "../../lib/discord-runtime-state.js";
|
|
6
|
+
import { DiscordAdapter } from "../../lib/adapter.js";
|
|
7
|
+
import {} from "../../lib/protocol.js";
|
|
10
8
|
export { DiscordGatewayEndpoint, DiscordInteractionsEndpoint, } from "../../lib/endpoint.js";
|
|
11
9
|
export default defineAdapter({
|
|
12
10
|
capabilities: ['inbound', 'outbound'],
|
|
@@ -19,23 +17,5 @@ export default defineAdapter({
|
|
|
19
17
|
interactive: 'native',
|
|
20
18
|
markdown: 'native',
|
|
21
19
|
},
|
|
22
|
-
|
|
23
|
-
const config = resolveDiscordConfig(context.config);
|
|
24
|
-
// 注册到插件运行时状态(discord endpoint list 的"运行中"数据源)
|
|
25
|
-
context.use(discordRuntimeStateToken).endpoints.set(config.id, {
|
|
26
|
-
id: config.id,
|
|
27
|
-
mode: config.connection,
|
|
28
|
-
});
|
|
29
|
-
if (config.connection === 'interactions') {
|
|
30
|
-
return new DiscordInteractionsEndpoint({
|
|
31
|
-
id: context.id,
|
|
32
|
-
http: context.use(httpHostToken),
|
|
33
|
-
config,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return new DiscordGatewayEndpoint({
|
|
37
|
-
id: context.id,
|
|
38
|
-
config,
|
|
39
|
-
});
|
|
40
|
-
},
|
|
20
|
+
createAdapter: context => new DiscordAdapter(context),
|
|
41
21
|
});
|
|
@@ -2,23 +2,16 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/discord/index.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import {
|
|
5
|
+
import { DiscordAdapter } from '../../src/adapter.js';
|
|
6
6
|
import {
|
|
7
|
-
DiscordGatewayEndpoint,
|
|
8
|
-
DiscordInteractionsEndpoint,
|
|
9
|
-
} from '../../src/endpoint.js';
|
|
10
|
-
import {
|
|
11
|
-
resolveDiscordConfig,
|
|
12
7
|
type DiscordEndpointConfig,
|
|
13
8
|
} from '../../src/protocol.js';
|
|
14
|
-
import { discordRuntimeStateToken } from '../../src/discord-runtime-state.js';
|
|
15
9
|
|
|
16
10
|
export {
|
|
17
11
|
DiscordGatewayEndpoint,
|
|
18
12
|
DiscordInteractionsEndpoint,
|
|
19
13
|
} from '../../src/endpoint.js';
|
|
20
14
|
export type {
|
|
21
|
-
CreateDiscordClient,
|
|
22
15
|
DiscordClientTransport,
|
|
23
16
|
DiscordEndpointOptions,
|
|
24
17
|
DiscordInteractionsEndpointOptions,
|
|
@@ -35,23 +28,5 @@ export default defineAdapter<DiscordEndpointConfig>({
|
|
|
35
28
|
interactive: 'native',
|
|
36
29
|
markdown: 'native',
|
|
37
30
|
},
|
|
38
|
-
|
|
39
|
-
const config = resolveDiscordConfig(context.config);
|
|
40
|
-
// 注册到插件运行时状态(discord endpoint list 的"运行中"数据源)
|
|
41
|
-
context.use(discordRuntimeStateToken).endpoints.set(config.id, {
|
|
42
|
-
id: config.id,
|
|
43
|
-
mode: config.connection,
|
|
44
|
-
});
|
|
45
|
-
if (config.connection === 'interactions') {
|
|
46
|
-
return new DiscordInteractionsEndpoint({
|
|
47
|
-
id: context.id,
|
|
48
|
-
http: context.use(httpHostToken),
|
|
49
|
-
config,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
return new DiscordGatewayEndpoint({
|
|
53
|
-
id: context.id,
|
|
54
|
-
config,
|
|
55
|
-
});
|
|
56
|
-
},
|
|
31
|
+
createAdapter: context => new DiscordAdapter(context),
|
|
57
32
|
});
|
|
@@ -35,7 +35,7 @@ export default defineAgentTool({
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
const channel = await client.channels.fetch(channel_id);
|
|
38
|
-
if (!channel?.
|
|
38
|
+
if (!channel?.isSendable()) {
|
|
39
39
|
throw new Error(t(context.locale, 'notTextChannel', { p0: channel_id }));
|
|
40
40
|
}
|
|
41
41
|
const msg = await channel.send({ embeds: [embedData] });
|
|
@@ -30,7 +30,7 @@ export default defineAgentTool<{ channel_id: string; title?: string; description
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
const channel = await client.channels.fetch(channel_id);
|
|
33
|
-
if (!channel?.
|
|
33
|
+
if (!channel?.isSendable()) {
|
|
34
34
|
throw new Error(t(context.locale, 'notTextChannel', { p0: channel_id }));
|
|
35
35
|
}
|
|
36
36
|
const msg = await channel.send({ embeds: [embedData] } as never);
|
|
@@ -4,6 +4,7 @@ import { defineAgentTool } from '@zhin.js/tool';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { requireDiscordGatewayClient } from '@zhin.js/adapter-discord';
|
|
6
6
|
import { platformPermission } from '@zhin.js/permission';
|
|
7
|
+
import { ThreadAutoArchiveDuration } from 'discord.js';
|
|
7
8
|
export default defineAgentTool({
|
|
8
9
|
description: '在 Discord 频道中创建帖子/子线程',
|
|
9
10
|
inputSchema: z.object({
|
|
@@ -19,11 +20,14 @@ export default defineAgentTool({
|
|
|
19
20
|
async execute({ channel_id, name, message_id, auto_archive_duration }, context) {
|
|
20
21
|
const client = requireDiscordGatewayClient(context.$client);
|
|
21
22
|
const channel = await client.channels.fetch(channel_id);
|
|
22
|
-
if (!channel
|
|
23
|
+
if (!channel || !('threads' in channel) || channel.isThreadOnly())
|
|
23
24
|
throw new Error(t(context.locale, 'threadsUnsupported', { p0: channel_id }));
|
|
25
|
+
const duration = auto_archive_duration || 1440;
|
|
26
|
+
if (!Object.values(ThreadAutoArchiveDuration).includes(duration))
|
|
27
|
+
throw new Error('Unsupported Discord thread auto archive duration');
|
|
24
28
|
const thread = await channel.threads.create({
|
|
25
29
|
name,
|
|
26
|
-
autoArchiveDuration:
|
|
30
|
+
autoArchiveDuration: duration,
|
|
27
31
|
...(message_id ? { startMessage: message_id } : {}),
|
|
28
32
|
});
|
|
29
33
|
return { success: true, thread_id: thread.id, message: t(context.locale, 'threadCreated', { p0: name }) };
|
|
@@ -3,6 +3,7 @@ import { defineAgentTool } from '@zhin.js/tool';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { requireDiscordGatewayClient } from '@zhin.js/adapter-discord';
|
|
5
5
|
import { platformPermission } from '@zhin.js/permission';
|
|
6
|
+
import { ThreadAutoArchiveDuration } from 'discord.js';
|
|
6
7
|
|
|
7
8
|
export default defineAgentTool<{ channel_id: string; name: string; message_id?: string; auto_archive_duration?: number }>({
|
|
8
9
|
description: '在 Discord 频道中创建帖子/子线程',
|
|
@@ -19,10 +20,12 @@ export default defineAgentTool<{ channel_id: string; name: string; message_id?:
|
|
|
19
20
|
async execute({ channel_id, name, message_id, auto_archive_duration }: { channel_id: string; name: string; message_id?: string; auto_archive_duration?: number }, context) {
|
|
20
21
|
const client = requireDiscordGatewayClient(context.$client);
|
|
21
22
|
const channel = await client.channels.fetch(channel_id);
|
|
22
|
-
if (!channel
|
|
23
|
+
if (!channel || !('threads' in channel) || channel.isThreadOnly()) throw new Error(t(context.locale, 'threadsUnsupported', { p0: channel_id }));
|
|
24
|
+
const duration = auto_archive_duration || 1440;
|
|
25
|
+
if (!Object.values(ThreadAutoArchiveDuration).includes(duration)) throw new Error('Unsupported Discord thread auto archive duration');
|
|
23
26
|
const thread = await channel.threads.create({
|
|
24
27
|
name,
|
|
25
|
-
autoArchiveDuration:
|
|
28
|
+
autoArchiveDuration: duration,
|
|
26
29
|
...(message_id ? { startMessage: message_id } : {}),
|
|
27
30
|
});
|
|
28
31
|
return { success: true, thread_id: thread.id, message: t(context.locale, 'threadCreated', { p0: name }) };
|
|
@@ -19,7 +19,7 @@ export default defineAgentTool({
|
|
|
19
19
|
const client = requireDiscordGatewayClient(context.$client);
|
|
20
20
|
const tagList = tags ? tags.split(',').map((t) => t.trim()) : undefined;
|
|
21
21
|
const channel = await client.channels.fetch(channel_id);
|
|
22
|
-
if (!channel
|
|
22
|
+
if (!channel || channel.type !== ChannelType.GuildForum) {
|
|
23
23
|
throw new Error(t(context.locale, 'notForumChannel', { p0: channel_id }));
|
|
24
24
|
}
|
|
25
25
|
const tagIds = tagList?.length
|
|
@@ -19,7 +19,7 @@ export default defineAgentTool<{ channel_id: string; name: string; content: stri
|
|
|
19
19
|
const client = requireDiscordGatewayClient(context.$client);
|
|
20
20
|
const tagList = tags ? tags.split(',').map((t: string) => t.trim()) : undefined;
|
|
21
21
|
const channel = await client.channels.fetch(channel_id);
|
|
22
|
-
if (!channel
|
|
22
|
+
if (!channel || channel.type !== ChannelType.GuildForum) {
|
|
23
23
|
throw new Error(t(context.locale, 'notForumChannel', { p0: channel_id }));
|
|
24
24
|
}
|
|
25
25
|
const tagIds = tagList?.length
|
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { createEndpointCommands } from 'zhin.js/adapter';
|
|
9
9
|
import { defineCommand } from 'zhin.js/command';
|
|
10
|
-
import { discordRuntimeStateToken } from "../../../lib/discord-runtime-state.js";
|
|
11
10
|
export const discordEndpointCommands = createEndpointCommands({
|
|
12
11
|
adapterKey: 'discord',
|
|
13
12
|
adapterDisplayName: 'Discord',
|
|
14
13
|
fields: [
|
|
15
14
|
{ key: 'token', required: true, env: true, description: 'Discord bot token' },
|
|
16
15
|
],
|
|
17
|
-
running: (use) => use(discordRuntimeStateToken).endpoints.values(),
|
|
18
16
|
describeEntry: (entry) => `token: ${String(entry.token)}`,
|
|
19
17
|
}, defineCommand);
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createEndpointCommands } from 'zhin.js/adapter';
|
|
8
8
|
import { defineCommand } from 'zhin.js/command';
|
|
9
|
-
import { discordRuntimeStateToken } from '../../../src/discord-runtime-state.js';
|
|
10
9
|
|
|
11
10
|
export const discordEndpointCommands = createEndpointCommands({
|
|
12
11
|
adapterKey: 'discord',
|
|
@@ -14,6 +13,5 @@ export const discordEndpointCommands = createEndpointCommands({
|
|
|
14
13
|
fields: [
|
|
15
14
|
{ key: 'token', required: true, env: true, description: 'Discord bot token' },
|
|
16
15
|
],
|
|
17
|
-
running: (use) => use(discordRuntimeStateToken).endpoints.values(),
|
|
18
16
|
describeEntry: (entry) => `token: ${String(entry.token)}`,
|
|
19
17
|
}, defineCommand);
|
package/lib/adapter.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Adapter, type AdapterContext, type EndpointCreationContext } from 'zhin.js/adapter';
|
|
2
|
+
import { DiscordGatewayEndpoint, DiscordInteractionsEndpoint } from './endpoint.js';
|
|
3
|
+
import { type DiscordEndpointConfig } from './protocol.js';
|
|
4
|
+
import type { DiscordClientEventMap } from './client.js';
|
|
5
|
+
export declare class DiscordAdapter extends Adapter<DiscordGatewayEndpoint | DiscordInteractionsEndpoint, DiscordEndpointConfig, DiscordClientEventMap> {
|
|
6
|
+
private readonly context;
|
|
7
|
+
constructor(context: AdapterContext<DiscordEndpointConfig>);
|
|
8
|
+
get configurationIdentityFields(): readonly string[];
|
|
9
|
+
get configurationIdentityProbeFields(): readonly string[];
|
|
10
|
+
get configurationUpdateFields(): readonly string[];
|
|
11
|
+
protected validateConfiguration(input: DiscordEndpointConfig): void;
|
|
12
|
+
protected identifyConfiguration(input: DiscordEndpointConfig, signal: AbortSignal): Promise<string>;
|
|
13
|
+
protected createEndpoint(input: DiscordEndpointConfig, execution: EndpointCreationContext): Promise<DiscordGatewayEndpoint | DiscordInteractionsEndpoint>;
|
|
14
|
+
}
|
package/lib/adapter.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Adapter } from 'zhin.js/adapter';
|
|
2
|
+
import { httpHostToken } from '@zhin.js/host-http';
|
|
3
|
+
import { REST, Routes } from 'discord.js';
|
|
4
|
+
import { DiscordGatewayEndpoint, DiscordInteractionsEndpoint } from './endpoint.js';
|
|
5
|
+
import { createDiscordRestApiAgent } from './rest-api-proxy.js';
|
|
6
|
+
import { resolveDiscordConfig } from './protocol.js';
|
|
7
|
+
export class DiscordAdapter extends Adapter {
|
|
8
|
+
context;
|
|
9
|
+
constructor(context) {
|
|
10
|
+
super();
|
|
11
|
+
this.context = context;
|
|
12
|
+
}
|
|
13
|
+
get configurationIdentityFields() { return ['id', 'applicationId']; }
|
|
14
|
+
get configurationIdentityProbeFields() { return ['token', 'publicKey']; }
|
|
15
|
+
get configurationUpdateFields() {
|
|
16
|
+
return ['token', 'connection', 'restApiProxy', 'restApiProxy.port',
|
|
17
|
+
'gatewayFaultProxyUrl', 'intents', 'defaultActivity', 'defaultActivity.name', 'defaultActivity.type', 'defaultActivity.url', 'publicKey', 'interactionsPath'];
|
|
18
|
+
}
|
|
19
|
+
validateConfiguration(input) { resolveDiscordConfig(input); }
|
|
20
|
+
async identifyConfiguration(input, signal) {
|
|
21
|
+
const config = resolveDiscordConfig(input);
|
|
22
|
+
const agent = config.connection === 'gateway' && config.restApiProxy ? createDiscordRestApiAgent(config.restApiProxy) : undefined;
|
|
23
|
+
const rest = new REST({ version: '10', retries: 0, rejectOnRateLimit: () => true, ...(agent ? { agent } : {}) }).setToken(config.token);
|
|
24
|
+
try {
|
|
25
|
+
const application = await rest.get(Routes.currentApplication(), { signal });
|
|
26
|
+
if (typeof application?.id !== 'string' || !application.id.trim())
|
|
27
|
+
throw new Error('Discord identity response is incomplete');
|
|
28
|
+
if (config.connection === 'interactions' && application.id !== config.applicationId)
|
|
29
|
+
throw new Error('Discord application identity configuration does not match');
|
|
30
|
+
if (config.connection === 'interactions' && (typeof application.verify_key !== 'string'
|
|
31
|
+
|| application.verify_key.toLowerCase() !== config.publicKey.toLowerCase())) {
|
|
32
|
+
throw new Error('Discord verification key does not match the application identity');
|
|
33
|
+
}
|
|
34
|
+
return JSON.stringify(['discord', application.id]);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
rest.clearHashSweeper();
|
|
38
|
+
rest.clearHandlerSweeper();
|
|
39
|
+
await agent?.destroyProxy();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async createEndpoint(input, execution) {
|
|
43
|
+
const config = resolveDiscordConfig(input);
|
|
44
|
+
if (config.connection === 'interactions')
|
|
45
|
+
return new DiscordInteractionsEndpoint({ id: execution.canonicalId, config, http: this.context.use(httpHostToken) });
|
|
46
|
+
const restApiAgent = config.restApiProxy ? createDiscordRestApiAgent(config.restApiProxy) : undefined;
|
|
47
|
+
try {
|
|
48
|
+
return new DiscordGatewayEndpoint({ id: execution.canonicalId, config, restApiAgent });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
await restApiAgent?.destroyProxy();
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/lib/client.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ClientEvents } from 'discord.js';
|
|
2
|
+
import type { DiscordAdapter } from './adapter.js';
|
|
3
|
+
import type { DiscordGatewayEndpoint, DiscordInteractionsEndpoint } from './endpoint.js';
|
|
2
4
|
import type { DiscordClientTransport } from './gateway.js';
|
|
3
5
|
/** Exact Client variants produced by the Gateway and Interactions Endpoints. */
|
|
4
|
-
export type DiscordClient =
|
|
5
|
-
export type DiscordClientEventMap =
|
|
6
|
+
export type DiscordClient = DiscordGatewayEndpoint | DiscordInteractionsEndpoint;
|
|
7
|
+
export type DiscordClientEventMap = {
|
|
8
|
+
readonly [K in keyof ClientEvents]: ClientEvents[K][0];
|
|
9
|
+
} & Record<string, unknown>;
|
|
6
10
|
/** Narrow an adapter Client for Gateway-only SDK operations. */
|
|
7
11
|
export declare function requireDiscordGatewayClient(client: DiscordClient): DiscordClientTransport;
|
|
8
12
|
declare module '@zhin.js/feature-kit' {
|
|
9
|
-
interface
|
|
10
|
-
readonly discord:
|
|
11
|
-
readonly client: DiscordClient;
|
|
12
|
-
readonly events: DiscordClientEventMap;
|
|
13
|
-
};
|
|
13
|
+
interface AdapterRegistry {
|
|
14
|
+
readonly discord: DiscordAdapter;
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
|
-
export declare const discordClient: import("@zhin.js/adapter").EndpointClientToken<DiscordClient, DiscordClientEventMap>;
|
package/lib/client.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { defineEndpointClient } from 'zhin.js/adapter';
|
|
2
1
|
/** Narrow an adapter Client for Gateway-only SDK operations. */
|
|
3
2
|
export function requireDiscordGatewayClient(client) {
|
|
4
3
|
if ('guilds' in client && 'channels' in client)
|
|
5
4
|
return client;
|
|
6
5
|
throw new Error('This Discord tool requires a Gateway Endpoint Client');
|
|
7
6
|
}
|
|
8
|
-
export const discordClient = defineEndpointClient('discord');
|
package/lib/console-spec.js
CHANGED
|
@@ -3,6 +3,15 @@ export const discordConsoleSpec = {
|
|
|
3
3
|
adapterDisplayName: 'Discord',
|
|
4
4
|
fields: [
|
|
5
5
|
{ key: 'token', required: true, env: true, description: 'Discord bot token' },
|
|
6
|
+
{ key: 'connection', description: 'gateway or interactions' },
|
|
7
|
+
{ key: 'applicationId', description: 'Application ID for Interactions; identity changes replace the account' },
|
|
8
|
+
{ key: 'publicKey', description: 'Application Ed25519 public key' },
|
|
9
|
+
{ key: 'interactionsPath', description: 'Local Interactions route' },
|
|
10
|
+
{ key: 'restApiProxy.port', type: 'number', description: 'Configured REST loopback proxy port' },
|
|
11
|
+
{ key: 'gatewayFaultProxyUrl', description: 'Configured local Gateway fault proxy URL' },
|
|
12
|
+
{ key: 'defaultActivity.name', description: 'SDK activity name' },
|
|
13
|
+
{ key: 'defaultActivity.type', description: 'PLAYING, STREAMING, LISTENING, WATCHING or COMPETING' },
|
|
14
|
+
{ key: 'defaultActivity.url', description: 'SDK activity URL' },
|
|
6
15
|
],
|
|
7
16
|
docsUrl: 'https://zhin.js.org/adapters/discord',
|
|
8
17
|
readPermission: 'discord:read',
|
package/lib/console.d.ts
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discord Console RPC:运行状态 + endpoint 增删(原 `discord endpoint` 聊天命令)。
|
|
3
|
-
*
|
|
4
|
-
* 页面绑定 `pages/index`,读取 / 写入分别要求 `<adapter>:read` / `<adapter>:write`。
|
|
5
|
-
*/
|
|
6
|
-
import { type AdapterConsoleResources } from 'zhin.js/adapter';
|
|
7
1
|
import type { PluginConsoleRpc } from 'zhin.js';
|
|
8
|
-
export declare function createDiscordConsoleRpc(
|
|
2
|
+
export declare function createDiscordConsoleRpc(getConfig: () => unknown): PluginConsoleRpc;
|
package/lib/console.js
CHANGED
|
@@ -4,13 +4,10 @@
|
|
|
4
4
|
* 页面绑定 `pages/index`,读取 / 写入分别要求 `<adapter>:read` / `<adapter>:write`。
|
|
5
5
|
*/
|
|
6
6
|
import { createAdapterConsoleRpc } from 'zhin.js/adapter';
|
|
7
|
-
import { discordRuntimeStateToken } from './discord-runtime-state.js';
|
|
8
7
|
import { discordConsoleSpec } from './console-spec.js';
|
|
9
|
-
export function createDiscordConsoleRpc(
|
|
10
|
-
const state = resources.use(discordRuntimeStateToken);
|
|
8
|
+
export function createDiscordConsoleRpc(getConfig) {
|
|
11
9
|
return createAdapterConsoleRpc({
|
|
12
10
|
...discordConsoleSpec,
|
|
13
|
-
running: () => state.endpoints.values(),
|
|
14
11
|
getConfig,
|
|
15
|
-
}
|
|
12
|
+
});
|
|
16
13
|
}
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createDiscordRestApiAgent } from './rest-api-proxy.js';
|
|
2
|
+
/**
|
|
3
|
+
* DiscordEndpoint — lifecycle, outbound, admit, gateway / interactions modes, agent tool surface.
|
|
4
|
+
*/
|
|
5
|
+
import { Client } from 'discord.js';
|
|
6
|
+
import { endpointSpec, type Endpoint, type EndpointSpecification, type EndpointManagement } from 'zhin.js/adapter';
|
|
2
7
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
3
|
-
import { type MessageRef } from '@zhin.js/im-contract';
|
|
4
8
|
import type { CapabilityId } from 'zhin.js';
|
|
5
|
-
import { type
|
|
6
|
-
import { type DiscordButtonInbound, type DiscordInboundMessage, type ResolvedDiscordGatewayConfig, type ResolvedDiscordInteractionsConfig } from './protocol.js';
|
|
7
|
-
export type {
|
|
9
|
+
import { type DiscordClientTransport } from './gateway.js';
|
|
10
|
+
import { type DiscordButtonInbound, type DiscordInboundMessage, type DiscordOutboundBody, type ResolvedDiscordGatewayConfig, type ResolvedDiscordInteractionsConfig } from './protocol.js';
|
|
11
|
+
export type { DiscordClientTransport, } from './gateway.js';
|
|
8
12
|
export interface DiscordEndpointOptions {
|
|
9
13
|
readonly id: CapabilityId;
|
|
10
14
|
readonly config: ResolvedDiscordGatewayConfig;
|
|
11
|
-
readonly
|
|
15
|
+
readonly restApiAgent?: ReturnType<typeof createDiscordRestApiAgent>;
|
|
12
16
|
readonly fetch?: typeof globalThis.fetch;
|
|
13
17
|
}
|
|
14
|
-
export declare class DiscordGatewayEndpoint extends Endpoint
|
|
18
|
+
export declare class DiscordGatewayEndpoint extends Client implements Endpoint {
|
|
15
19
|
#private;
|
|
16
|
-
readonly
|
|
17
|
-
readonly control: EndpointControl;
|
|
18
|
-
readonly content: EndpointContentPort;
|
|
20
|
+
readonly [endpointSpec]: EndpointSpecification;
|
|
19
21
|
constructor(options: DiscordEndpointOptions);
|
|
20
|
-
/** The actual discord.js-compatible client used by this connection. */
|
|
21
|
-
get client(): DiscordClientTransport;
|
|
22
|
-
get transportState(): EndpointTransportState | undefined;
|
|
23
|
-
start(): Promise<void>;
|
|
24
|
-
open(): void;
|
|
25
|
-
close(): void;
|
|
26
|
-
stop(): Promise<void>;
|
|
27
|
-
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
28
|
-
recallMessage(message: MessageRef): Promise<void>;
|
|
29
22
|
/** Test / internal: admit a message when open. */
|
|
30
23
|
admit(msg: DiscordInboundMessage): void;
|
|
31
24
|
/** Test / internal: admit a button interaction when open. */
|
|
@@ -37,31 +30,42 @@ export interface DiscordInteractionsEndpointOptions {
|
|
|
37
30
|
readonly config: ResolvedDiscordInteractionsConfig;
|
|
38
31
|
readonly fetch?: typeof globalThis.fetch;
|
|
39
32
|
}
|
|
33
|
+
/** One Discord REST request retains the caller lifetime through response-body consumption. */
|
|
34
|
+
export interface DiscordRestRequest {
|
|
35
|
+
readonly method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
|
36
|
+
readonly path: string;
|
|
37
|
+
readonly body?: unknown;
|
|
38
|
+
readonly signal?: AbortSignal;
|
|
39
|
+
}
|
|
40
|
+
/** Protocol target and payload for a single message publication. */
|
|
41
|
+
export interface DiscordCreateMessageRequest {
|
|
42
|
+
readonly channelId: string;
|
|
43
|
+
readonly body: DiscordOutboundBody;
|
|
44
|
+
readonly signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
/** Protocol target for a single existing-message deletion. */
|
|
47
|
+
export interface DiscordDeleteMessageRequest {
|
|
48
|
+
readonly channelId: string;
|
|
49
|
+
readonly messageId: string;
|
|
50
|
+
readonly signal?: AbortSignal;
|
|
51
|
+
}
|
|
40
52
|
/** Minimal Discord REST client used when Gateway is intentionally disabled. */
|
|
41
53
|
export declare class DiscordRestClient {
|
|
42
54
|
readonly token: string;
|
|
43
55
|
readonly fetch: typeof globalThis.fetch;
|
|
44
56
|
constructor(token: string, fetch?: typeof globalThis.fetch);
|
|
45
|
-
request<T = unknown>(method
|
|
46
|
-
createMessage(channelId
|
|
57
|
+
request<T = unknown>({ method, path, body, signal }: DiscordRestRequest): Promise<T>;
|
|
58
|
+
createMessage({ channelId, body, signal }: DiscordCreateMessageRequest): Promise<{
|
|
47
59
|
id?: string;
|
|
48
60
|
}>;
|
|
49
|
-
deleteMessage(channelId
|
|
61
|
+
deleteMessage({ channelId, messageId, signal }: DiscordDeleteMessageRequest): Promise<void>;
|
|
50
62
|
}
|
|
51
|
-
export declare class DiscordInteractionsEndpoint extends
|
|
63
|
+
export declare class DiscordInteractionsEndpoint extends DiscordRestClient implements Endpoint {
|
|
52
64
|
#private;
|
|
53
|
-
readonly
|
|
54
|
-
readonly control: EndpointControl;
|
|
55
|
-
readonly content: EndpointContentPort;
|
|
65
|
+
readonly [endpointSpec]: EndpointSpecification;
|
|
56
66
|
constructor(options: DiscordInteractionsEndpointOptions);
|
|
57
67
|
get isOpen(): boolean;
|
|
58
68
|
get config(): ResolvedDiscordInteractionsConfig;
|
|
59
|
-
start(): Promise<void>;
|
|
60
|
-
open(): void;
|
|
61
|
-
close(): void;
|
|
62
|
-
stop(): Promise<void>;
|
|
63
|
-
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
64
|
-
recallMessage(message: MessageRef): Promise<void>;
|
|
65
69
|
admit(msg: DiscordInboundMessage): void;
|
|
66
70
|
/** Test / internal: admit a button interaction when open. */
|
|
67
71
|
admitButton(interaction: DiscordButtonInbound): void;
|