mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-18 05:20:48 +00:00
* 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
170 lines
5.1 KiB
TypeScript
170 lines
5.1 KiB
TypeScript
import {
|
|
appendMatchMetadata,
|
|
asString,
|
|
isRecord,
|
|
resolveEnabledConfiguredAccountId,
|
|
} from "../../../src/channels/plugins/status-issues/shared.js";
|
|
import type {
|
|
ChannelAccountSnapshot,
|
|
ChannelStatusIssue,
|
|
} from "../../../src/channels/plugins/types.js";
|
|
|
|
type DiscordIntentSummary = {
|
|
messageContent?: "enabled" | "limited" | "disabled";
|
|
};
|
|
|
|
type DiscordApplicationSummary = {
|
|
intents?: DiscordIntentSummary;
|
|
};
|
|
|
|
type DiscordAccountStatus = {
|
|
accountId?: unknown;
|
|
enabled?: unknown;
|
|
configured?: unknown;
|
|
application?: unknown;
|
|
audit?: unknown;
|
|
};
|
|
|
|
type DiscordPermissionsAuditSummary = {
|
|
unresolvedChannels?: number;
|
|
channels?: Array<{
|
|
channelId: string;
|
|
ok?: boolean;
|
|
missing?: string[];
|
|
error?: string | null;
|
|
matchKey?: string;
|
|
matchSource?: string;
|
|
}>;
|
|
};
|
|
|
|
function readDiscordAccountStatus(value: ChannelAccountSnapshot): DiscordAccountStatus | null {
|
|
if (!isRecord(value)) {
|
|
return null;
|
|
}
|
|
return {
|
|
accountId: value.accountId,
|
|
enabled: value.enabled,
|
|
configured: value.configured,
|
|
application: value.application,
|
|
audit: value.audit,
|
|
};
|
|
}
|
|
|
|
function readDiscordApplicationSummary(value: unknown): DiscordApplicationSummary {
|
|
if (!isRecord(value)) {
|
|
return {};
|
|
}
|
|
const intentsRaw = value.intents;
|
|
if (!isRecord(intentsRaw)) {
|
|
return {};
|
|
}
|
|
return {
|
|
intents: {
|
|
messageContent:
|
|
intentsRaw.messageContent === "enabled" ||
|
|
intentsRaw.messageContent === "limited" ||
|
|
intentsRaw.messageContent === "disabled"
|
|
? intentsRaw.messageContent
|
|
: undefined,
|
|
},
|
|
};
|
|
}
|
|
|
|
function readDiscordPermissionsAuditSummary(value: unknown): DiscordPermissionsAuditSummary {
|
|
if (!isRecord(value)) {
|
|
return {};
|
|
}
|
|
const unresolvedChannels =
|
|
typeof value.unresolvedChannels === "number" && Number.isFinite(value.unresolvedChannels)
|
|
? value.unresolvedChannels
|
|
: undefined;
|
|
const channelsRaw = value.channels;
|
|
const channels = Array.isArray(channelsRaw)
|
|
? (channelsRaw
|
|
.map((entry) => {
|
|
if (!isRecord(entry)) {
|
|
return null;
|
|
}
|
|
const channelId = asString(entry.channelId);
|
|
if (!channelId) {
|
|
return null;
|
|
}
|
|
const ok = typeof entry.ok === "boolean" ? entry.ok : undefined;
|
|
const missing = Array.isArray(entry.missing)
|
|
? entry.missing.map((v) => asString(v)).filter(Boolean)
|
|
: undefined;
|
|
const error = asString(entry.error) ?? null;
|
|
const matchKey = asString(entry.matchKey) ?? undefined;
|
|
const matchSource = asString(entry.matchSource) ?? undefined;
|
|
return {
|
|
channelId,
|
|
ok,
|
|
missing: missing?.length ? missing : undefined,
|
|
error,
|
|
matchKey,
|
|
matchSource,
|
|
};
|
|
})
|
|
.filter(Boolean) as DiscordPermissionsAuditSummary["channels"])
|
|
: undefined;
|
|
return { unresolvedChannels, channels };
|
|
}
|
|
|
|
export function collectDiscordStatusIssues(
|
|
accounts: ChannelAccountSnapshot[],
|
|
): ChannelStatusIssue[] {
|
|
const issues: ChannelStatusIssue[] = [];
|
|
for (const entry of accounts) {
|
|
const account = readDiscordAccountStatus(entry);
|
|
if (!account) {
|
|
continue;
|
|
}
|
|
const accountId = resolveEnabledConfiguredAccountId(account);
|
|
if (!accountId) {
|
|
continue;
|
|
}
|
|
|
|
const app = readDiscordApplicationSummary(account.application);
|
|
const messageContent = app.intents?.messageContent;
|
|
if (messageContent === "disabled") {
|
|
issues.push({
|
|
channel: "discord",
|
|
accountId,
|
|
kind: "intent",
|
|
message: "Message Content Intent is disabled. Bot may not see normal channel messages.",
|
|
fix: "Enable Message Content Intent in Discord Dev Portal → Bot → Privileged Gateway Intents, or require mention-only operation.",
|
|
});
|
|
}
|
|
|
|
const audit = readDiscordPermissionsAuditSummary(account.audit);
|
|
if (audit.unresolvedChannels && audit.unresolvedChannels > 0) {
|
|
issues.push({
|
|
channel: "discord",
|
|
accountId,
|
|
kind: "config",
|
|
message: `Some configured guild channels are not numeric IDs (unresolvedChannels=${audit.unresolvedChannels}). Permission audit can only check numeric channel IDs.`,
|
|
fix: "Use numeric channel IDs as keys in channels.discord.guilds.*.channels (then rerun channels status --probe).",
|
|
});
|
|
}
|
|
for (const channel of audit.channels ?? []) {
|
|
if (channel.ok === true) {
|
|
continue;
|
|
}
|
|
const missing = channel.missing?.length ? ` missing ${channel.missing.join(", ")}` : "";
|
|
const error = channel.error ? `: ${channel.error}` : "";
|
|
const baseMessage = `Channel ${channel.channelId} permission check failed.${missing}${error}`;
|
|
issues.push({
|
|
channel: "discord",
|
|
accountId,
|
|
kind: "permissions",
|
|
message: appendMatchMetadata(baseMessage, {
|
|
matchKey: channel.matchKey,
|
|
matchSource: channel.matchSource,
|
|
}),
|
|
fix: "Ensure the bot role can view + send in this channel (and that channel overrides don't deny it).",
|
|
});
|
|
}
|
|
}
|
|
return issues;
|
|
}
|