fix(discord): allow configured application ids · openclaw/openclaw@485b875
steipete
·
2026-04-30
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -60,7 +60,7 @@ Docs: https://docs.openclaw.ai
|
60 | 60 | - Ollama: keep explicit local model runs on target-provider runtime hooks when PI discovery is skipped, so one-shot Ollama calls no longer cold-load unrelated provider runtimes before streaming. Fixes #74078. Thanks @sakalaboator. |
61 | 61 | - Slack/prompts: rely on Slack `interactiveReplies` guidance instead of generic `inlineButtons` config hints so enabled Slack button directives are not contradicted. Fixes #46647. Thanks @jeremykoerber. |
62 | 62 | - Slack/reactions: treat duplicate `already_reacted` responses as idempotent success so repeated agent reaction adds no longer surface as tool failures. Fixes #69005. Thanks @shipitsteven and @martingarramon. |
63 | | -- Channels/Discord: cool down Cloudflare/Error 1015 HTML 429 REST failures during startup application lookup and gateway metadata fetches, sanitizing HTML bodies before logging and honoring Retry-After before falling back to a conservative cooldown. Fixes #38853. Thanks @djgeorg3 and @Garyko0730. |
| 63 | +- Channels/Discord: cool down Cloudflare/Error 1015 HTML 429 REST failures during startup application lookup and gateway metadata fetches, add `channels.discord.applicationId` as an app-id lookup bypass, sanitize HTML bodies before logging, and honor Retry-After before falling back to a conservative cooldown. Fixes #38853. Thanks @djgeorg3 and @Garyko0730. |
64 | 64 | - Slack/tools: expose `fileId` in the shared message tool schema so `download-file` can receive Slack attachment IDs from inbound placeholders. Fixes #45574. Thanks @chadvegas. |
65 | 65 | - Exec: reject invalid per-call `host` values instead of silently falling back to the default target, so hostname-like values fail before commands run. Fixes #74426. Thanks @scr00ge-00 and @vyctorbrzezowski. |
66 | 66 | - Google/Gemini: send non-empty placeholder content when a Gemini run is triggered with empty or filtered user content, avoiding `contents is not specified` API errors. Thanks @CaoYuhaoCarl. |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
1 | | -b095536aeeb273b029a7a96cd8406cbcbc315dfd67dc45f469782ce1ccbc9e08 config-baseline.json |
| 1 | +7bf720f6d9040c53323553b1bd351f688137c6b352c4cf2acfd7f7d252644b38 config-baseline.json |
2 | 2 | ab9a004ec78ed51e646be29eb10aa6700de1d47fee77331a85ca5e2cd15b6e93 config-baseline.core.json |
3 | | -fab66aa304db5697e87259165ad261006719eb6e6cdbd25f957fcba2b7b324e9 config-baseline.channel.json |
| 3 | +92712871defa92eeda8161b516db85574681f2b70678b940508a808b987aeae2 config-baseline.channel.json |
4 | 4 | c4231c2194206547af8ad94342dc00aadb734f43cb49cc79d4c46bdbb80c3f95 config-baseline.plugin.json |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -115,6 +115,7 @@ openclaw gateway
|
115 | 115 | |
116 | 116 | If OpenClaw is already running as a background service, restart it via the OpenClaw Mac app or by stopping and restarting the `openclaw gateway run` process. |
117 | 117 | For managed service installs, run `openclaw gateway install` from a shell where `DISCORD_BOT_TOKEN` is present, or store the variable in `~/.openclaw/.env`, so the service can resolve the env SecretRef after restart. |
| 118 | +If your host is blocked or rate-limited by Discord's startup application lookup, set `channels.discord.applicationId` to the application's client ID from the Developer Portal so startup can skip that REST call. |
118 | 119 | |
119 | 120 | </Step> |
120 | 121 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -76,6 +76,30 @@ describe("discord config schema", () => {
|
76 | 76 | expect(cfg.historyLimit).toBe(3); |
77 | 77 | }); |
78 | 78 | |
| 79 | +it("accepts Discord application IDs at top-level and account scope", () => { |
| 80 | +const cfg = expectValidDiscordConfig({ |
| 81 | +applicationId: "123456789012345678", |
| 82 | +accounts: { |
| 83 | +work: { |
| 84 | +applicationId: 234567890123456, |
| 85 | +}, |
| 86 | +}, |
| 87 | +}); |
| 88 | + |
| 89 | +expect(cfg.applicationId).toBe("123456789012345678"); |
| 90 | +expect(cfg.accounts?.work?.applicationId).toBe("234567890123456"); |
| 91 | +}); |
| 92 | + |
| 93 | +it("rejects unsafe numeric Discord application IDs", () => { |
| 94 | +const issues = expectInvalidDiscordConfig({ |
| 95 | +applicationId: 106232522769186816, |
| 96 | +}); |
| 97 | + |
| 98 | +expect( |
| 99 | +issues.some((issue) => issue.message.includes("not a valid non-negative safe integer")), |
| 100 | +).toBe(true); |
| 101 | +}); |
| 102 | + |
79 | 103 | it("loads guild map and dm group settings", () => { |
80 | 104 | const cfg = expectValidDiscordConfig({ |
81 | 105 | enabled: true, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -222,4 +222,8 @@ export const discordChannelConfigUiHints = {
|
222 | 222 | help: "Discord bot token used for gateway and REST API authentication for this provider account. Keep this secret out of committed config and rotate immediately after any leak.", |
223 | 223 | sensitive: true, |
224 | 224 | }, |
| 225 | +applicationId: { |
| 226 | +label: "Discord Application ID", |
| 227 | +help: "Optional Discord application/client ID. Set this when hosted environments cannot reach Discord's application lookup endpoint during startup.", |
| 228 | +}, |
225 | 229 | } satisfies Record<string, ChannelConfigUiHint>; |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -887,6 +887,30 @@ describe("monitorDiscordProvider", () => {
|
887 | 887 | expect(getConstructedClientOptions().clientId).toBe("123"); |
888 | 888 | }); |
889 | 889 | |
| 890 | +it("uses configured application id before token parsing or REST lookup", async () => { |
| 891 | +const fetchApplicationId = vi.fn(async () => "network-app"); |
| 892 | +providerTesting.setFetchDiscordApplicationId(fetchApplicationId); |
| 893 | +resolveDiscordAccountMock.mockReturnValue({ |
| 894 | +accountId: "default", |
| 895 | +token: "MTIz.abc.def", |
| 896 | +config: { |
| 897 | +applicationId: "987654321098765432", |
| 898 | +commands: { native: true, nativeSkills: false }, |
| 899 | +voice: { enabled: false }, |
| 900 | +agentComponents: { enabled: false }, |
| 901 | +execApprovals: { enabled: false }, |
| 902 | +}, |
| 903 | +}); |
| 904 | + |
| 905 | +await monitorDiscordProvider({ |
| 906 | +config: baseConfig(), |
| 907 | +runtime: baseRuntime(), |
| 908 | +}); |
| 909 | + |
| 910 | +expect(fetchApplicationId).not.toHaveBeenCalled(); |
| 911 | +expect(getConstructedClientOptions().clientId).toBe("987654321098765432"); |
| 912 | +}); |
| 913 | + |
890 | 914 | it("reports connected status on startup and shutdown", async () => { |
891 | 915 | const setStatus = vi.fn(); |
892 | 916 | clientGetPluginMock.mockImplementation((name: string) => |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -317,7 +317,11 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
317 | 317 | phase: "fetch-application-id:start", |
318 | 318 | startAt: startupStartedAt, |
319 | 319 | }); |
320 | | -const parsedApplicationId = parseApplicationIdFromToken(token); |
| 320 | +const configuredApplicationId = |
| 321 | +typeof discordCfg.applicationId === "string" && discordCfg.applicationId.trim() |
| 322 | + ? discordCfg.applicationId.trim() |
| 323 | + : undefined; |
| 324 | +const parsedApplicationId = configuredApplicationId ?? parseApplicationIdFromToken(token); |
321 | 325 | const applicationId = |
322 | 326 | parsedApplicationId ?? |
323 | 327 | (await (fetchDiscordApplicationIdForTesting ?? fetchDiscordApplicationId)( |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -789,6 +789,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
789 | 789 | }, |
790 | 790 | ], |
791 | 791 | }, |
| 792 | + applicationId: { |
| 793 | + type: "string", |
| 794 | + }, |
792 | 795 | proxy: { |
793 | 796 | type: "string", |
794 | 797 | }, |
@@ -2152,6 +2155,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
2152 | 2155 | }, |
2153 | 2156 | ], |
2154 | 2157 | }, |
| 2158 | + applicationId: { |
| 2159 | + type: "string", |
| 2160 | + }, |
2155 | 2161 | proxy: { |
2156 | 2162 | type: "string", |
2157 | 2163 | }, |
@@ -3622,6 +3628,10 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
3622 | 3628 | help: "Discord bot token used for gateway and REST API authentication for this provider account. Keep this secret out of committed config and rotate immediately after any leak.", |
3623 | 3629 | sensitive: true, |
3624 | 3630 | }, |
| 3631 | + applicationId: { |
| 3632 | + label: "Discord Application ID", |
| 3633 | + help: "Optional Discord application/client ID. Set this when hosted environments cannot reach Discord's application lookup endpoint during startup.", |
| 3634 | + }, |
3625 | 3635 | }, |
3626 | 3636 | unsupportedSecretRefSurfacePatterns: [ |
3627 | 3637 | "channels.discord.accounts.*.threadBindings.webhookToken", |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -241,6 +241,8 @@ export type DiscordAccountConfig = {
|
241 | 241 | /** If false, do not start this Discord account. Default: true. */ |
242 | 242 | enabled?: boolean; |
243 | 243 | token?: SecretInput; |
| 244 | +/** Optional Discord application/client ID. Set this when REST application lookup is blocked. */ |
| 245 | +applicationId?: string; |
244 | 246 | /** HTTP(S) proxy URL for Discord gateway WebSocket connections. */ |
245 | 247 | proxy?: string; |
246 | 248 | /** Timeout for Discord /gateway/bot metadata lookup before falling back to the default gateway URL. Default: 30000. */ |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -527,6 +527,7 @@ export const DiscordAccountSchema = z
|
527 | 527 | commands: ProviderCommandsSchema, |
528 | 528 | configWrites: z.boolean().optional(), |
529 | 529 | token: SecretInputSchema.optional().register(sensitive), |
| 530 | +applicationId: DiscordIdSchema.optional(), |
530 | 531 | proxy: z.string().optional(), |
531 | 532 | gatewayInfoTimeoutMs: z.number().int().positive().max(120_000).optional(), |
532 | 533 | allowBots: z.union([z.boolean(), z.literal("mentions")]).optional(), |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。