mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 21:58:26 +00:00
fix(outbound): unify resolved cfg threading across send paths (#33987)
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
type RequestClient,
|
||||
} from "@buape/carbon";
|
||||
import { ChannelType, Routes } from "discord-api-types/v10";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { loadConfig, type OpenClawConfig } from "../config/config.js";
|
||||
import { recordChannelActivity } from "../infra/channel-activity.js";
|
||||
import { loadWebMedia } from "../web/media.js";
|
||||
import { resolveDiscordAccount } from "./accounts.js";
|
||||
@@ -41,6 +41,7 @@ function extractComponentAttachmentNames(spec: DiscordComponentMessageSpec): str
|
||||
}
|
||||
|
||||
type DiscordComponentSendOpts = {
|
||||
cfg?: OpenClawConfig;
|
||||
accountId?: string;
|
||||
token?: string;
|
||||
rest?: RequestClient;
|
||||
@@ -58,10 +59,10 @@ export async function sendDiscordComponentMessage(
|
||||
spec: DiscordComponentMessageSpec,
|
||||
opts: DiscordComponentSendOpts = {},
|
||||
): Promise<DiscordSendResult> {
|
||||
const cfg = loadConfig();
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const accountInfo = resolveDiscordAccount({ cfg, accountId: opts.accountId });
|
||||
const { token, rest, request } = createDiscordClient(opts, cfg);
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId, cfg);
|
||||
const { channelId } = await resolveChannelId(rest, recipient, request);
|
||||
|
||||
const channelType = await resolveDiscordChannelType(rest, channelId);
|
||||
|
||||
@@ -4,7 +4,7 @@ import path from "node:path";
|
||||
import { serializePayload, type MessagePayloadObject, type RequestClient } from "@buape/carbon";
|
||||
import { ChannelType, Routes } from "discord-api-types/v10";
|
||||
import { resolveChunkMode } from "../auto-reply/chunk.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { loadConfig, type OpenClawConfig } from "../config/config.js";
|
||||
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
||||
import { recordChannelActivity } from "../infra/channel-activity.js";
|
||||
import type { RetryConfig } from "../infra/retry.js";
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
} from "./voice-message.js";
|
||||
|
||||
type DiscordSendOpts = {
|
||||
cfg?: OpenClawConfig;
|
||||
token?: string;
|
||||
accountId?: string;
|
||||
mediaUrl?: string;
|
||||
@@ -121,9 +122,9 @@ async function resolveDiscordSendTarget(
|
||||
to: string,
|
||||
opts: DiscordSendOpts,
|
||||
): Promise<{ rest: RequestClient; request: DiscordClientRequest; channelId: string }> {
|
||||
const cfg = loadConfig();
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const { rest, request } = createDiscordClient(opts, cfg);
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId, cfg);
|
||||
const { channelId } = await resolveChannelId(rest, recipient, request);
|
||||
return { rest, request, channelId };
|
||||
}
|
||||
@@ -133,7 +134,7 @@ export async function sendMessageDiscord(
|
||||
text: string,
|
||||
opts: DiscordSendOpts = {},
|
||||
): Promise<DiscordSendResult> {
|
||||
const cfg = loadConfig();
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const accountInfo = resolveDiscordAccount({
|
||||
cfg,
|
||||
accountId: opts.accountId,
|
||||
@@ -149,7 +150,7 @@ export async function sendMessageDiscord(
|
||||
accountId: accountInfo.accountId,
|
||||
});
|
||||
const { token, rest, request } = createDiscordClient(opts, cfg);
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId, cfg);
|
||||
const { channelId } = await resolveChannelId(rest, recipient, request);
|
||||
|
||||
// Forum/Media channels reject POST /messages; auto-create a thread post instead.
|
||||
@@ -310,6 +311,7 @@ export async function sendMessageDiscord(
|
||||
}
|
||||
|
||||
type DiscordWebhookSendOpts = {
|
||||
cfg?: OpenClawConfig;
|
||||
webhookId: string;
|
||||
webhookToken: string;
|
||||
accountId?: string;
|
||||
@@ -385,7 +387,7 @@ export async function sendWebhookMessageDiscord(
|
||||
};
|
||||
try {
|
||||
const account = resolveDiscordAccount({
|
||||
cfg: loadConfig(),
|
||||
cfg: opts.cfg ?? loadConfig(),
|
||||
accountId: opts.accountId,
|
||||
});
|
||||
recordChannelActivity({
|
||||
@@ -464,6 +466,7 @@ export async function sendPollDiscord(
|
||||
}
|
||||
|
||||
type VoiceMessageOpts = {
|
||||
cfg?: OpenClawConfig;
|
||||
token?: string;
|
||||
accountId?: string;
|
||||
verbose?: boolean;
|
||||
@@ -509,7 +512,7 @@ export async function sendVoiceMessageDiscord(
|
||||
let channelId: string | undefined;
|
||||
|
||||
try {
|
||||
const cfg = loadConfig();
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const accountInfo = resolveDiscordAccount({
|
||||
cfg,
|
||||
accountId: opts.accountId,
|
||||
@@ -518,7 +521,7 @@ export async function sendVoiceMessageDiscord(
|
||||
token = client.token;
|
||||
rest = client.rest;
|
||||
const request = client.request;
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId);
|
||||
const recipient = await parseAndResolveRecipient(to, opts.accountId, cfg);
|
||||
channelId = (await resolveChannelId(rest, recipient, request)).channelId;
|
||||
|
||||
// Convert to OGG/Opus if needed
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
createDiscordClient,
|
||||
formatReactionEmoji,
|
||||
normalizeReactionEmoji,
|
||||
resolveDiscordRest,
|
||||
} from "./send.shared.js";
|
||||
import type { DiscordReactionSummary, DiscordReactOpts } from "./send.types.js";
|
||||
|
||||
@@ -15,7 +14,7 @@ export async function reactMessageDiscord(
|
||||
emoji: string,
|
||||
opts: DiscordReactOpts = {},
|
||||
) {
|
||||
const cfg = loadConfig();
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const { rest, request } = createDiscordClient(opts, cfg);
|
||||
const encoded = normalizeReactionEmoji(emoji);
|
||||
await request(
|
||||
@@ -31,7 +30,8 @@ export async function removeReactionDiscord(
|
||||
emoji: string,
|
||||
opts: DiscordReactOpts = {},
|
||||
) {
|
||||
const rest = resolveDiscordRest(opts);
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const { rest } = createDiscordClient(opts, cfg);
|
||||
const encoded = normalizeReactionEmoji(emoji);
|
||||
await rest.delete(Routes.channelMessageOwnReaction(channelId, messageId, encoded));
|
||||
return { ok: true };
|
||||
@@ -42,7 +42,8 @@ export async function removeOwnReactionsDiscord(
|
||||
messageId: string,
|
||||
opts: DiscordReactOpts = {},
|
||||
): Promise<{ ok: true; removed: string[] }> {
|
||||
const rest = resolveDiscordRest(opts);
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const { rest } = createDiscordClient(opts, cfg);
|
||||
const message = (await rest.get(Routes.channelMessage(channelId, messageId))) as {
|
||||
reactions?: Array<{ emoji: { id?: string | null; name?: string | null } }>;
|
||||
};
|
||||
@@ -73,7 +74,8 @@ export async function fetchReactionsDiscord(
|
||||
messageId: string,
|
||||
opts: DiscordReactOpts & { limit?: number } = {},
|
||||
): Promise<DiscordReactionSummary[]> {
|
||||
const rest = resolveDiscordRest(opts);
|
||||
const cfg = opts.cfg ?? loadConfig();
|
||||
const { rest } = createDiscordClient(opts, cfg);
|
||||
const message = (await rest.get(Routes.channelMessage(channelId, messageId))) as {
|
||||
reactions?: Array<{
|
||||
count: number;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PollLayoutType } from "discord-api-types/payloads/v10";
|
||||
import type { RESTAPIPoll } from "discord-api-types/rest/v10";
|
||||
import { Routes, type APIChannel, type APIEmbed } from "discord-api-types/v10";
|
||||
import type { ChunkMode } from "../auto-reply/chunk.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { loadConfig, type OpenClawConfig } from "../config/config.js";
|
||||
import type { RetryRunner } from "../infra/retry-policy.js";
|
||||
import { buildOutboundMediaLoadOptions } from "../media/load-options.js";
|
||||
import { normalizePollDurationHours, normalizePollInput, type PollInput } from "../polls.js";
|
||||
@@ -80,9 +80,10 @@ function parseRecipient(raw: string): DiscordRecipient {
|
||||
export async function parseAndResolveRecipient(
|
||||
raw: string,
|
||||
accountId?: string,
|
||||
cfg?: OpenClawConfig,
|
||||
): Promise<DiscordRecipient> {
|
||||
const cfg = loadConfig();
|
||||
const accountInfo = resolveDiscordAccount({ cfg, accountId });
|
||||
const resolvedCfg = cfg ?? loadConfig();
|
||||
const accountInfo = resolveDiscordAccount({ cfg: resolvedCfg, accountId });
|
||||
|
||||
// First try to resolve using directory lookup (handles usernames)
|
||||
const trimmed = raw.trim();
|
||||
@@ -93,7 +94,7 @@ export async function parseAndResolveRecipient(
|
||||
const resolved = await resolveDiscordTarget(
|
||||
raw,
|
||||
{
|
||||
cfg,
|
||||
cfg: resolvedCfg,
|
||||
accountId: accountInfo.accountId,
|
||||
},
|
||||
parseOptions,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { RequestClient } from "@buape/carbon";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { RetryConfig } from "../infra/retry.js";
|
||||
|
||||
export class DiscordSendError extends Error {
|
||||
@@ -28,6 +29,7 @@ export type DiscordSendResult = {
|
||||
};
|
||||
|
||||
export type DiscordReactOpts = {
|
||||
cfg?: OpenClawConfig;
|
||||
token?: string;
|
||||
accountId?: string;
|
||||
rest?: RequestClient;
|
||||
|
||||
@@ -2,6 +2,15 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { sendWebhookMessageDiscord } from "./send.js";
|
||||
|
||||
const recordChannelActivityMock = vi.hoisted(() => vi.fn());
|
||||
const loadConfigMock = vi.hoisted(() => vi.fn(() => ({ channels: { discord: {} } })));
|
||||
|
||||
vi.mock("../config/config.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/config.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadConfig: () => loadConfigMock(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../infra/channel-activity.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../infra/channel-activity.js")>();
|
||||
@@ -14,6 +23,7 @@ vi.mock("../infra/channel-activity.js", async (importOriginal) => {
|
||||
describe("sendWebhookMessageDiscord activity", () => {
|
||||
beforeEach(() => {
|
||||
recordChannelActivityMock.mockClear();
|
||||
loadConfigMock.mockClear();
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => {
|
||||
@@ -30,7 +40,15 @@ describe("sendWebhookMessageDiscord activity", () => {
|
||||
});
|
||||
|
||||
it("records outbound channel activity for webhook sends", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
discord: {
|
||||
token: "resolved-token",
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = await sendWebhookMessageDiscord("hello world", {
|
||||
cfg,
|
||||
webhookId: "wh-1",
|
||||
webhookToken: "tok-1",
|
||||
accountId: "runtime",
|
||||
@@ -46,5 +64,6 @@ describe("sendWebhookMessageDiscord activity", () => {
|
||||
accountId: "runtime",
|
||||
direction: "outbound",
|
||||
});
|
||||
expect(loadConfigMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user