@openclaw/discord 2026.3.2 → 2026.3.7
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/index.ts +2 -2
- package/package.json +1 -1
- package/src/channel.test.ts +1 -1
- package/src/channel.ts +67 -57
- package/src/runtime.ts +1 -1
- package/src/subagent-hooks.test.ts +2 -2
- package/src/subagent-hooks.ts +2 -2
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/discord";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/discord";
|
|
3
3
|
import { discordPlugin } from "./src/channel.js";
|
|
4
4
|
import { setDiscordRuntime } from "./src/runtime.js";
|
|
5
5
|
import { registerDiscordSubagentHooks } from "./src/subagent-hooks.js";
|
package/package.json
CHANGED
package/src/channel.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/discord";
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { discordPlugin } from "./channel.js";
|
|
4
4
|
import { setDiscordRuntime } from "./runtime.js";
|
package/src/channel.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildAccountScopedDmSecurityPolicy,
|
|
3
|
+
collectOpenProviderGroupPolicyWarnings,
|
|
4
|
+
collectOpenGroupPolicyConfiguredRouteWarnings,
|
|
5
|
+
createScopedAccountConfigAccessors,
|
|
6
|
+
formatAllowFromLowercase,
|
|
7
|
+
} from "openclaw/plugin-sdk/compat";
|
|
1
8
|
import {
|
|
2
9
|
applyAccountNameToChannelSection,
|
|
10
|
+
buildComputedAccountStatusSnapshot,
|
|
3
11
|
buildChannelConfigSchema,
|
|
4
12
|
buildTokenChannelStatusSummary,
|
|
5
13
|
collectDiscordAuditChannelIds,
|
|
@@ -8,8 +16,8 @@ import {
|
|
|
8
16
|
deleteAccountFromConfigSection,
|
|
9
17
|
discordOnboardingAdapter,
|
|
10
18
|
DiscordConfigSchema,
|
|
11
|
-
formatPairingApproveHint,
|
|
12
19
|
getChatChannelMeta,
|
|
20
|
+
inspectDiscordAccount,
|
|
13
21
|
listDiscordAccountIds,
|
|
14
22
|
listDiscordDirectoryGroupsFromConfig,
|
|
15
23
|
listDiscordDirectoryPeersFromConfig,
|
|
@@ -19,17 +27,17 @@ import {
|
|
|
19
27
|
normalizeDiscordMessagingTarget,
|
|
20
28
|
normalizeDiscordOutboundTarget,
|
|
21
29
|
PAIRING_APPROVED_MESSAGE,
|
|
30
|
+
projectCredentialSnapshotFields,
|
|
31
|
+
resolveConfiguredFromCredentialStatuses,
|
|
22
32
|
resolveDiscordAccount,
|
|
23
33
|
resolveDefaultDiscordAccountId,
|
|
24
34
|
resolveDiscordGroupRequireMention,
|
|
25
35
|
resolveDiscordGroupToolPolicy,
|
|
26
|
-
resolveOpenProviderRuntimeGroupPolicy,
|
|
27
|
-
resolveDefaultGroupPolicy,
|
|
28
36
|
setAccountEnabledInConfigSection,
|
|
29
37
|
type ChannelMessageActionAdapter,
|
|
30
38
|
type ChannelPlugin,
|
|
31
39
|
type ResolvedDiscordAccount,
|
|
32
|
-
} from "openclaw/plugin-sdk";
|
|
40
|
+
} from "openclaw/plugin-sdk/discord";
|
|
33
41
|
import { getDiscordRuntime } from "./runtime.js";
|
|
34
42
|
|
|
35
43
|
const meta = getChatChannelMeta("discord");
|
|
@@ -48,6 +56,13 @@ const discordMessageActions: ChannelMessageActionAdapter = {
|
|
|
48
56
|
},
|
|
49
57
|
};
|
|
50
58
|
|
|
59
|
+
const discordConfigAccessors = createScopedAccountConfigAccessors({
|
|
60
|
+
resolveAccount: ({ cfg, accountId }) => resolveDiscordAccount({ cfg, accountId }),
|
|
61
|
+
resolveAllowFrom: (account: ResolvedDiscordAccount) => account.config.dm?.allowFrom,
|
|
62
|
+
formatAllowFrom: (allowFrom) => formatAllowFromLowercase({ allowFrom }),
|
|
63
|
+
resolveDefaultTo: (account: ResolvedDiscordAccount) => account.config.defaultTo,
|
|
64
|
+
});
|
|
65
|
+
|
|
51
66
|
export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
52
67
|
id: "discord",
|
|
53
68
|
meta: {
|
|
@@ -80,6 +95,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
80
95
|
config: {
|
|
81
96
|
listAccountIds: (cfg) => listDiscordAccountIds(cfg),
|
|
82
97
|
resolveAccount: (cfg, accountId) => resolveDiscordAccount({ cfg, accountId }),
|
|
98
|
+
inspectAccount: (cfg, accountId) => inspectDiscordAccount({ cfg, accountId }),
|
|
83
99
|
defaultAccountId: (cfg) => resolveDefaultDiscordAccountId(cfg),
|
|
84
100
|
setAccountEnabled: ({ cfg, accountId, enabled }) =>
|
|
85
101
|
setAccountEnabledInConfigSection({
|
|
@@ -104,58 +120,49 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
104
120
|
configured: Boolean(account.token?.trim()),
|
|
105
121
|
tokenSource: account.tokenSource,
|
|
106
122
|
}),
|
|
107
|
-
|
|
108
|
-
(resolveDiscordAccount({ cfg, accountId }).config.dm?.allowFrom ?? []).map((entry) =>
|
|
109
|
-
String(entry),
|
|
110
|
-
),
|
|
111
|
-
formatAllowFrom: ({ allowFrom }) =>
|
|
112
|
-
allowFrom
|
|
113
|
-
.map((entry) => String(entry).trim())
|
|
114
|
-
.filter(Boolean)
|
|
115
|
-
.map((entry) => entry.toLowerCase()),
|
|
116
|
-
resolveDefaultTo: ({ cfg, accountId }) =>
|
|
117
|
-
resolveDiscordAccount({ cfg, accountId }).config.defaultTo?.trim() || undefined,
|
|
123
|
+
...discordConfigAccessors,
|
|
118
124
|
},
|
|
119
125
|
security: {
|
|
120
126
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
:
|
|
126
|
-
|
|
127
|
-
policy: account.config.dm?.policy ?? "pairing",
|
|
127
|
+
return buildAccountScopedDmSecurityPolicy({
|
|
128
|
+
cfg,
|
|
129
|
+
channelKey: "discord",
|
|
130
|
+
accountId,
|
|
131
|
+
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
|
132
|
+
policy: account.config.dm?.policy,
|
|
128
133
|
allowFrom: account.config.dm?.allowFrom ?? [],
|
|
129
|
-
|
|
130
|
-
approveHint: formatPairingApproveHint("discord"),
|
|
134
|
+
allowFromPathSuffix: "dm.",
|
|
131
135
|
normalizeEntry: (raw) => raw.replace(/^(discord|user):/i, "").replace(/^<@!?(\d+)>$/, "$1"),
|
|
132
|
-
};
|
|
136
|
+
});
|
|
133
137
|
},
|
|
134
138
|
collectWarnings: ({ account, cfg }) => {
|
|
135
|
-
const warnings: string[] = [];
|
|
136
|
-
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
|
137
|
-
const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({
|
|
138
|
-
providerConfigPresent: cfg.channels?.discord !== undefined,
|
|
139
|
-
groupPolicy: account.config.groupPolicy,
|
|
140
|
-
defaultGroupPolicy,
|
|
141
|
-
});
|
|
142
139
|
const guildEntries = account.config.guilds ?? {};
|
|
143
140
|
const guildsConfigured = Object.keys(guildEntries).length > 0;
|
|
144
141
|
const channelAllowlistConfigured = guildsConfigured;
|
|
145
142
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
143
|
+
return collectOpenProviderGroupPolicyWarnings({
|
|
144
|
+
cfg,
|
|
145
|
+
providerConfigPresent: cfg.channels?.discord !== undefined,
|
|
146
|
+
configuredGroupPolicy: account.config.groupPolicy,
|
|
147
|
+
collect: (groupPolicy) =>
|
|
148
|
+
collectOpenGroupPolicyConfiguredRouteWarnings({
|
|
149
|
+
groupPolicy,
|
|
150
|
+
routeAllowlistConfigured: channelAllowlistConfigured,
|
|
151
|
+
configureRouteAllowlist: {
|
|
152
|
+
surface: "Discord guilds",
|
|
153
|
+
openScope: "any channel not explicitly denied",
|
|
154
|
+
groupPolicyPath: "channels.discord.groupPolicy",
|
|
155
|
+
routeAllowlistPath: "channels.discord.guilds.<id>.channels",
|
|
156
|
+
},
|
|
157
|
+
missingRouteAllowlist: {
|
|
158
|
+
surface: "Discord guilds",
|
|
159
|
+
openBehavior:
|
|
160
|
+
"with no guild/channel allowlist; any channel can trigger (mention-gated)",
|
|
161
|
+
remediation:
|
|
162
|
+
'Set channels.discord.groupPolicy="allowlist" and configure channels.discord.guilds.<id>.channels',
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
159
166
|
},
|
|
160
167
|
},
|
|
161
168
|
groups: {
|
|
@@ -302,10 +309,11 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
302
309
|
textChunkLimit: 2000,
|
|
303
310
|
pollMaxOptions: 10,
|
|
304
311
|
resolveTarget: ({ to }) => normalizeDiscordOutboundTarget(to),
|
|
305
|
-
sendText: async ({ to, text, accountId, deps, replyToId, silent }) => {
|
|
312
|
+
sendText: async ({ cfg, to, text, accountId, deps, replyToId, silent }) => {
|
|
306
313
|
const send = deps?.sendDiscord ?? getDiscordRuntime().channel.discord.sendMessageDiscord;
|
|
307
314
|
const result = await send(to, text, {
|
|
308
315
|
verbose: false,
|
|
316
|
+
cfg,
|
|
309
317
|
replyTo: replyToId ?? undefined,
|
|
310
318
|
accountId: accountId ?? undefined,
|
|
311
319
|
silent: silent ?? undefined,
|
|
@@ -313,6 +321,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
313
321
|
return { channel: "discord", ...result };
|
|
314
322
|
},
|
|
315
323
|
sendMedia: async ({
|
|
324
|
+
cfg,
|
|
316
325
|
to,
|
|
317
326
|
text,
|
|
318
327
|
mediaUrl,
|
|
@@ -325,6 +334,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
325
334
|
const send = deps?.sendDiscord ?? getDiscordRuntime().channel.discord.sendMessageDiscord;
|
|
326
335
|
const result = await send(to, text, {
|
|
327
336
|
verbose: false,
|
|
337
|
+
cfg,
|
|
328
338
|
mediaUrl,
|
|
329
339
|
mediaLocalRoots,
|
|
330
340
|
replyTo: replyToId ?? undefined,
|
|
@@ -333,8 +343,9 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
333
343
|
});
|
|
334
344
|
return { channel: "discord", ...result };
|
|
335
345
|
},
|
|
336
|
-
sendPoll: async ({ to, poll, accountId, silent }) =>
|
|
346
|
+
sendPoll: async ({ cfg, to, poll, accountId, silent }) =>
|
|
337
347
|
await getDiscordRuntime().channel.discord.sendPollDiscord(to, poll, {
|
|
348
|
+
cfg,
|
|
338
349
|
accountId: accountId ?? undefined,
|
|
339
350
|
silent: silent ?? undefined,
|
|
340
351
|
}),
|
|
@@ -386,19 +397,21 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
386
397
|
return { ...audit, unresolvedChannels };
|
|
387
398
|
},
|
|
388
399
|
buildAccountSnapshot: ({ account, runtime, probe, audit }) => {
|
|
389
|
-
const configured =
|
|
400
|
+
const configured =
|
|
401
|
+
resolveConfiguredFromCredentialStatuses(account) ?? Boolean(account.token?.trim());
|
|
390
402
|
const app = runtime?.application ?? (probe as { application?: unknown })?.application;
|
|
391
403
|
const bot = runtime?.bot ?? (probe as { bot?: unknown })?.bot;
|
|
392
|
-
|
|
404
|
+
const base = buildComputedAccountStatusSnapshot({
|
|
393
405
|
accountId: account.accountId,
|
|
394
406
|
name: account.name,
|
|
395
407
|
enabled: account.enabled,
|
|
396
408
|
configured,
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
409
|
+
runtime,
|
|
410
|
+
probe,
|
|
411
|
+
});
|
|
412
|
+
return {
|
|
413
|
+
...base,
|
|
414
|
+
...projectCredentialSnapshotFields(account),
|
|
402
415
|
connected: runtime?.connected ?? false,
|
|
403
416
|
reconnectAttempts: runtime?.reconnectAttempts,
|
|
404
417
|
lastConnectedAt: runtime?.lastConnectedAt ?? null,
|
|
@@ -406,10 +419,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
|
|
406
419
|
lastEventAt: runtime?.lastEventAt ?? null,
|
|
407
420
|
application: app ?? undefined,
|
|
408
421
|
bot: bot ?? undefined,
|
|
409
|
-
probe,
|
|
410
422
|
audit,
|
|
411
|
-
lastInboundAt: runtime?.lastInboundAt ?? null,
|
|
412
|
-
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
|
413
423
|
};
|
|
414
424
|
},
|
|
415
425
|
},
|
package/src/runtime.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/discord";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { registerDiscordSubagentHooks } from "./subagent-hooks.js";
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@ const hookMocks = vi.hoisted(() => ({
|
|
|
35
35
|
unbindThreadBindingsBySessionKey: vi.fn(() => []),
|
|
36
36
|
}));
|
|
37
37
|
|
|
38
|
-
vi.mock("openclaw/plugin-sdk", () => ({
|
|
38
|
+
vi.mock("openclaw/plugin-sdk/discord", () => ({
|
|
39
39
|
resolveDiscordAccount: hookMocks.resolveDiscordAccount,
|
|
40
40
|
autoBindSpawnedDiscordSubagent: hookMocks.autoBindSpawnedDiscordSubagent,
|
|
41
41
|
listThreadBindingsBySessionKey: hookMocks.listThreadBindingsBySessionKey,
|
package/src/subagent-hooks.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/discord";
|
|
2
2
|
import {
|
|
3
3
|
autoBindSpawnedDiscordSubagent,
|
|
4
4
|
listThreadBindingsBySessionKey,
|
|
5
5
|
resolveDiscordAccount,
|
|
6
6
|
unbindThreadBindingsBySessionKey,
|
|
7
|
-
} from "openclaw/plugin-sdk";
|
|
7
|
+
} from "openclaw/plugin-sdk/discord";
|
|
8
8
|
|
|
9
9
|
function summarizeError(err: unknown): string {
|
|
10
10
|
if (err instanceof Error) {
|