scoootscooob 5682ec37fa
refactor: move Discord channel implementation to extensions/ (#45660)
* refactor: move Discord channel implementation to extensions/discord/src/

Move all Discord source files from src/discord/ to extensions/discord/src/,
following the extension migration pattern. Source files in src/discord/ are
replaced with re-export shims. Channel-plugin files from
src/channels/plugins/*/discord* are similarly moved and shimmed.

- Copy all .ts source files preserving subdirectory structure (monitor/, voice/)
- Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues)
- Fix all relative imports to use correct paths from new location
- Create re-export shims at original locations for backward compatibility
- Delete test files from shim locations (tests live in extension now)
- Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate
  extension files outside src/
- Update write-plugin-sdk-entry-dts.ts to match new declaration output paths

* fix: add importOriginal to thread-bindings session-meta mock for extensions test

* style: fix formatting in thread-bindings lifecycle test
2026-03-14 02:53:57 -07:00

72 lines
2.4 KiB
TypeScript

import type { BaseTokenResolution } from "../../../src/channels/plugins/types.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { normalizeResolvedSecretInputString } from "../../../src/config/types.secrets.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../src/routing/session-key.js";
export type DiscordTokenSource = "env" | "config" | "none";
export type DiscordTokenResolution = BaseTokenResolution & {
source: DiscordTokenSource;
};
export function normalizeDiscordToken(raw: unknown, path: string): string | undefined {
const trimmed = normalizeResolvedSecretInputString({ value: raw, path });
if (!trimmed) {
return undefined;
}
return trimmed.replace(/^Bot\s+/i, "");
}
export function resolveDiscordToken(
cfg?: OpenClawConfig,
opts: { accountId?: string | null; envToken?: string | null } = {},
): DiscordTokenResolution {
const accountId = normalizeAccountId(opts.accountId);
const discordCfg = cfg?.channels?.discord;
const resolveAccountCfg = (id: string) => {
const accounts = discordCfg?.accounts;
if (!accounts || typeof accounts !== "object" || Array.isArray(accounts)) {
return undefined;
}
const direct = accounts[id];
if (direct) {
return direct;
}
const matchKey = Object.keys(accounts).find((key) => normalizeAccountId(key) === id);
return matchKey ? accounts[matchKey] : undefined;
};
const accountCfg = resolveAccountCfg(accountId);
const hasAccountToken = Boolean(
accountCfg &&
Object.prototype.hasOwnProperty.call(accountCfg as Record<string, unknown>, "token"),
);
const accountToken = normalizeDiscordToken(
(accountCfg as { token?: unknown } | undefined)?.token ?? undefined,
`channels.discord.accounts.${accountId}.token`,
);
if (accountToken) {
return { token: accountToken, source: "config" };
}
if (hasAccountToken) {
return { token: "", source: "none" };
}
const configToken = normalizeDiscordToken(
discordCfg?.token ?? undefined,
"channels.discord.token",
);
if (configToken) {
return { token: configToken, source: "config" };
}
const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
const envToken = allowEnv
? normalizeDiscordToken(opts.envToken ?? process.env.DISCORD_BOT_TOKEN, "DISCORD_BOT_TOKEN")
: undefined;
if (envToken) {
return { token: envToken, source: "env" };
}
return { token: "", source: "none" };
}