mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 13:10:43 +00:00
refactor(discord): centralize thread channel context
This commit is contained in:
@@ -41,6 +41,10 @@ export function resolveDiscordChannelNameSafe(channel: unknown): string | undefi
|
||||
return resolveDiscordChannelStringPropertySafe(channel, "name");
|
||||
}
|
||||
|
||||
export function resolveDiscordChannelIdSafe(channel: unknown): string | undefined {
|
||||
return resolveDiscordChannelStringPropertySafe(channel, "id");
|
||||
}
|
||||
|
||||
export function resolveDiscordChannelTopicSafe(channel: unknown): string | undefined {
|
||||
return resolveDiscordChannelStringPropertySafe(channel, "topic");
|
||||
}
|
||||
|
||||
@@ -32,14 +32,10 @@ import {
|
||||
resolveDiscordGuildEntry,
|
||||
shouldEmitDiscordReactionNotification,
|
||||
} from "./allow-list.js";
|
||||
import {
|
||||
resolveDiscordChannelInfoSafe,
|
||||
resolveDiscordChannelParentIdSafe,
|
||||
} from "./channel-access.js";
|
||||
import { formatDiscordReactionEmoji, formatDiscordUserTag } from "./format.js";
|
||||
import { resolveDiscordChannelInfo } from "./message-utils.js";
|
||||
import { setPresence } from "./presence-cache.js";
|
||||
import { isThreadArchived } from "./thread-bindings.discord-api.js";
|
||||
import { resolveFetchedDiscordThreadLikeChannelContext } from "./thread-channel-context.js";
|
||||
import { closeDiscordThreadSessions } from "./thread-session-close.js";
|
||||
import { normalizeDiscordListenerTimeoutMs, runDiscordTaskWithTimeout } from "./timeouts.js";
|
||||
|
||||
@@ -77,6 +73,11 @@ type DiscordReactionRoutingParams = {
|
||||
guildEntries?: Record<string, import("./allow-list.js").DiscordGuildEntryResolved>;
|
||||
};
|
||||
|
||||
type DiscordReactionMode = "off" | "own" | "all" | "allowlist";
|
||||
type DiscordReactionChannelConfig = ReturnType<typeof resolveDiscordChannelConfigWithFallback>;
|
||||
type DiscordReactionIngressAccess = Awaited<ReturnType<typeof authorizeDiscordReactionIngress>>;
|
||||
type DiscordFetchedReactionMessage = { author?: User } | null;
|
||||
|
||||
const DISCORD_SLOW_LISTENER_THRESHOLD_MS = 30_000;
|
||||
const discordEventQueueLog = createSubsystemLogger("discord/event-queue");
|
||||
|
||||
@@ -409,6 +410,117 @@ async function authorizeDiscordReactionIngress(
|
||||
return { allowed: true };
|
||||
}
|
||||
|
||||
async function handleDiscordThreadReactionNotification(params: {
|
||||
reactionMode: DiscordReactionMode;
|
||||
message: DiscordReactionEvent["message"];
|
||||
parentId?: string;
|
||||
resolveThreadChannelAccess: () => Promise<{
|
||||
access: DiscordReactionIngressAccess;
|
||||
channelConfig: DiscordReactionChannelConfig;
|
||||
}>;
|
||||
shouldNotifyReaction: (options: {
|
||||
mode: DiscordReactionMode;
|
||||
messageAuthorId?: string;
|
||||
channelConfig?: DiscordReactionChannelConfig;
|
||||
}) => boolean;
|
||||
resolveReactionBase: () => { baseText: string; contextKey: string };
|
||||
emitReaction: (text: string, parentPeerId?: string) => void;
|
||||
emitReactionWithAuthor: (message: DiscordFetchedReactionMessage) => void;
|
||||
}) {
|
||||
if (params.reactionMode === "off") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.reactionMode === "all" || params.reactionMode === "allowlist") {
|
||||
const { access, channelConfig } = await params.resolveThreadChannelAccess();
|
||||
if (
|
||||
!access.allowed ||
|
||||
!params.shouldNotifyReaction({ mode: params.reactionMode, channelConfig })
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { baseText } = params.resolveReactionBase();
|
||||
params.emitReaction(baseText, params.parentId);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = await params.message.fetch().catch(() => null);
|
||||
const { access, channelConfig } = await params.resolveThreadChannelAccess();
|
||||
const messageAuthorId = message?.author?.id ?? undefined;
|
||||
if (
|
||||
!access.allowed ||
|
||||
!params.shouldNotifyReaction({
|
||||
mode: params.reactionMode,
|
||||
messageAuthorId,
|
||||
channelConfig,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
params.emitReactionWithAuthor(message);
|
||||
}
|
||||
|
||||
async function handleDiscordChannelReactionNotification(params: {
|
||||
isGuildMessage: boolean;
|
||||
reactionMode: DiscordReactionMode;
|
||||
message: DiscordReactionEvent["message"];
|
||||
channelConfig: DiscordReactionChannelConfig;
|
||||
parentId?: string;
|
||||
authorizeReactionIngressForChannel: (
|
||||
channelConfig: DiscordReactionChannelConfig,
|
||||
) => Promise<DiscordReactionIngressAccess>;
|
||||
shouldNotifyReaction: (options: {
|
||||
mode: DiscordReactionMode;
|
||||
messageAuthorId?: string;
|
||||
channelConfig?: DiscordReactionChannelConfig;
|
||||
}) => boolean;
|
||||
resolveReactionBase: () => { baseText: string; contextKey: string };
|
||||
emitReaction: (text: string, parentPeerId?: string) => void;
|
||||
emitReactionWithAuthor: (message: DiscordFetchedReactionMessage) => void;
|
||||
}) {
|
||||
if (params.isGuildMessage) {
|
||||
const access = await params.authorizeReactionIngressForChannel(params.channelConfig);
|
||||
if (!access.allowed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.reactionMode === "off") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.reactionMode === "all" || params.reactionMode === "allowlist") {
|
||||
if (
|
||||
!params.shouldNotifyReaction({
|
||||
mode: params.reactionMode,
|
||||
channelConfig: params.channelConfig,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { baseText } = params.resolveReactionBase();
|
||||
params.emitReaction(baseText, params.parentId);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = await params.message.fetch().catch(() => null);
|
||||
const messageAuthorId = message?.author?.id ?? undefined;
|
||||
if (
|
||||
!params.shouldNotifyReaction({
|
||||
mode: params.reactionMode,
|
||||
messageAuthorId,
|
||||
channelConfig: params.channelConfig,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
params.emitReactionWithAuthor(message);
|
||||
}
|
||||
|
||||
async function handleDiscordReactionEvent(
|
||||
params: {
|
||||
data: DiscordReactionEvent;
|
||||
@@ -449,16 +561,17 @@ async function handleDiscordReactionEvent(
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
const channelInfo = resolveDiscordChannelInfoSafe(channel);
|
||||
const channelName = channelInfo.name;
|
||||
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
||||
const channelType = channelInfo.type;
|
||||
const channelContext = await resolveFetchedDiscordThreadLikeChannelContext({
|
||||
client,
|
||||
channel,
|
||||
channelIdFallback: data.channel_id,
|
||||
});
|
||||
const channelName = channelContext.channelName;
|
||||
const channelSlug = channelContext.channelSlug;
|
||||
const channelType = channelContext.channelType;
|
||||
const isDirectMessage = channelType === ChannelType.DM;
|
||||
const isGroupDm = channelType === ChannelType.GroupDM;
|
||||
const isThreadChannel =
|
||||
channelType === ChannelType.PublicThread ||
|
||||
channelType === ChannelType.PrivateThread ||
|
||||
channelType === ChannelType.AnnouncementThread;
|
||||
const isThreadChannel = channelContext.isThreadChannel;
|
||||
const memberRoleIds = Array.isArray(data.rawMember?.roles)
|
||||
? data.rawMember.roles.map((roleId: string) => roleId)
|
||||
: [];
|
||||
@@ -490,9 +603,9 @@ async function handleDiscordReactionEvent(
|
||||
return;
|
||||
}
|
||||
}
|
||||
let parentId = resolveDiscordChannelParentIdSafe(channel);
|
||||
let parentName: string | undefined;
|
||||
let parentSlug = "";
|
||||
const parentId = isThreadChannel ? channelContext.threadParentId : channelContext.parentId;
|
||||
const parentName = isThreadChannel ? channelContext.threadParentName : undefined;
|
||||
const parentSlug = isThreadChannel ? channelContext.threadParentSlug : "";
|
||||
let reactionBase: { baseText: string; contextKey: string } | null = null;
|
||||
const resolveReactionBase = () => {
|
||||
if (reactionBase) {
|
||||
@@ -535,9 +648,9 @@ async function handleDiscordReactionEvent(
|
||||
});
|
||||
};
|
||||
const shouldNotifyReaction = (options: {
|
||||
mode: "off" | "own" | "all" | "allowlist";
|
||||
mode: DiscordReactionMode;
|
||||
messageAuthorId?: string;
|
||||
channelConfig?: ReturnType<typeof resolveDiscordChannelConfigWithFallback>;
|
||||
channelConfig?: DiscordReactionChannelConfig;
|
||||
}) =>
|
||||
shouldEmitDiscordReactionNotification({
|
||||
mode: options.mode,
|
||||
@@ -557,14 +670,6 @@ async function handleDiscordReactionEvent(
|
||||
const text = authorLabel ? `${baseText} from ${authorLabel}` : baseText;
|
||||
emitReaction(text, parentId);
|
||||
};
|
||||
const loadThreadParentInfo = async () => {
|
||||
if (!parentId) {
|
||||
return;
|
||||
}
|
||||
const parentInfo = await resolveDiscordChannelInfo(client, parentId);
|
||||
parentName = parentInfo?.name;
|
||||
parentSlug = parentName ? normalizeDiscordSlug(parentName) : "";
|
||||
};
|
||||
const resolveThreadChannelConfig = () =>
|
||||
resolveDiscordChannelConfigWithFallback({
|
||||
guildInfo,
|
||||
@@ -577,77 +682,30 @@ async function handleDiscordReactionEvent(
|
||||
scope: "thread",
|
||||
});
|
||||
const authorizeReactionIngressForChannel = async (
|
||||
channelConfig: ReturnType<typeof resolveDiscordChannelConfigWithFallback>,
|
||||
channelConfig: DiscordReactionChannelConfig,
|
||||
) =>
|
||||
await authorizeDiscordReactionIngress({
|
||||
...reactionIngressBase,
|
||||
channelConfig,
|
||||
});
|
||||
const resolveThreadChannelAccess = async (channelInfo: { parentId?: string } | null) => {
|
||||
parentId = channelInfo?.parentId;
|
||||
await loadThreadParentInfo();
|
||||
const resolveThreadChannelAccess = async () => {
|
||||
const channelConfig = resolveThreadChannelConfig();
|
||||
const access = await authorizeReactionIngressForChannel(channelConfig);
|
||||
return { access, channelConfig };
|
||||
};
|
||||
|
||||
// Parallelize async operations for thread channels
|
||||
if (isThreadChannel) {
|
||||
const reactionMode = guildInfo?.reactionNotifications ?? "own";
|
||||
|
||||
// Early exit: skip fetching message if notifications are off
|
||||
if (reactionMode === "off") {
|
||||
return;
|
||||
}
|
||||
|
||||
const channelInfoPromise = parentId
|
||||
? Promise.resolve({ parentId })
|
||||
: resolveDiscordChannelInfo(client, data.channel_id);
|
||||
|
||||
// Fast path: for "all" and "allowlist" modes, we don't need to fetch the message
|
||||
if (reactionMode === "all" || reactionMode === "allowlist") {
|
||||
const channelInfo = await channelInfoPromise;
|
||||
const { access: threadAccess, channelConfig: threadChannelConfig } =
|
||||
await resolveThreadChannelAccess(channelInfo);
|
||||
if (!threadAccess.allowed) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!shouldNotifyReaction({
|
||||
mode: reactionMode,
|
||||
channelConfig: threadChannelConfig,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { baseText } = resolveReactionBase();
|
||||
emitReaction(baseText, parentId);
|
||||
return;
|
||||
}
|
||||
|
||||
// For "own" mode, we need to fetch the message to check the author
|
||||
const messagePromise = data.message.fetch().catch(() => null);
|
||||
|
||||
const [channelInfo, message] = await Promise.all([channelInfoPromise, messagePromise]);
|
||||
const { access: threadAccess, channelConfig: threadChannelConfig } =
|
||||
await resolveThreadChannelAccess(channelInfo);
|
||||
if (!threadAccess.allowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const messageAuthorId = message?.author?.id ?? undefined;
|
||||
if (
|
||||
!shouldNotifyReaction({
|
||||
mode: reactionMode,
|
||||
messageAuthorId,
|
||||
channelConfig: threadChannelConfig,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitReactionWithAuthor(message);
|
||||
await handleDiscordThreadReactionNotification({
|
||||
reactionMode,
|
||||
message: data.message,
|
||||
parentId,
|
||||
resolveThreadChannelAccess,
|
||||
shouldNotifyReaction,
|
||||
resolveReactionBase,
|
||||
emitReaction,
|
||||
emitReactionWithAuthor,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -662,39 +720,19 @@ async function handleDiscordReactionEvent(
|
||||
parentSlug,
|
||||
scope: "channel",
|
||||
});
|
||||
if (isGuildMessage) {
|
||||
const channelAccess = await authorizeReactionIngressForChannel(channelConfig);
|
||||
if (!channelAccess.allowed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const reactionMode = guildInfo?.reactionNotifications ?? "own";
|
||||
|
||||
// Early exit: skip fetching message if notifications are off
|
||||
if (reactionMode === "off") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path: for "all" and "allowlist" modes, we don't need to fetch the message
|
||||
if (reactionMode === "all" || reactionMode === "allowlist") {
|
||||
if (!shouldNotifyReaction({ mode: reactionMode, channelConfig })) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { baseText } = resolveReactionBase();
|
||||
emitReaction(baseText, parentId);
|
||||
return;
|
||||
}
|
||||
|
||||
// For "own" mode, we need to fetch the message to check the author
|
||||
const message = await data.message.fetch().catch(() => null);
|
||||
const messageAuthorId = message?.author?.id ?? undefined;
|
||||
if (!shouldNotifyReaction({ mode: reactionMode, messageAuthorId, channelConfig })) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitReactionWithAuthor(message);
|
||||
await handleDiscordChannelReactionNotification({
|
||||
isGuildMessage,
|
||||
reactionMode,
|
||||
message: data.message,
|
||||
channelConfig,
|
||||
parentId,
|
||||
authorizeReactionIngressForChannel,
|
||||
shouldNotifyReaction,
|
||||
resolveReactionBase,
|
||||
emitReaction,
|
||||
emitReactionWithAuthor,
|
||||
});
|
||||
} catch (err) {
|
||||
params.logger.error(danger(`discord reaction handler failed: ${String(err)}`));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { ChannelType, type Client } from "@buape/carbon";
|
||||
import { normalizeDiscordSlug } from "./allow-list.js";
|
||||
import {
|
||||
resolveDiscordChannelNameSafe,
|
||||
resolveDiscordChannelParentIdSafe,
|
||||
} from "./channel-access.js";
|
||||
import { resolveDiscordChannelInfo } from "./message-utils.js";
|
||||
import { resolveDiscordThreadParentInfo } from "./threading.js";
|
||||
import { resolveDiscordThreadLikeChannelContext } from "./thread-channel-context.js";
|
||||
|
||||
type DiscordInteractionChannel = {
|
||||
id?: string;
|
||||
@@ -31,48 +25,25 @@ export async function resolveDiscordNativeInteractionChannelContext(params: {
|
||||
hasGuild: boolean;
|
||||
channelIdFallback: string;
|
||||
}): Promise<DiscordNativeInteractionChannelContext> {
|
||||
const { channel } = params;
|
||||
const channelType = channel?.type;
|
||||
const channelContext = await resolveDiscordThreadLikeChannelContext({
|
||||
client: params.client,
|
||||
channel: params.channel,
|
||||
channelIdFallback: params.channelIdFallback,
|
||||
});
|
||||
const channelType = channelContext.channelType;
|
||||
const isDirectMessage = channelType === ChannelType.DM;
|
||||
const isGroupDm = channelType === ChannelType.GroupDM;
|
||||
const isThreadChannel =
|
||||
channelType === ChannelType.PublicThread ||
|
||||
channelType === ChannelType.PrivateThread ||
|
||||
channelType === ChannelType.AnnouncementThread;
|
||||
const channelName = resolveDiscordChannelNameSafe(channel);
|
||||
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
||||
const rawChannelId = channel?.id ?? params.channelIdFallback;
|
||||
|
||||
let threadParentId: string | undefined;
|
||||
let threadParentName: string | undefined;
|
||||
let threadParentSlug = "";
|
||||
if (params.hasGuild && channel && isThreadChannel && rawChannelId) {
|
||||
const channelInfo = await resolveDiscordChannelInfo(params.client, rawChannelId);
|
||||
const parentInfo = await resolveDiscordThreadParentInfo({
|
||||
client: params.client,
|
||||
threadChannel: {
|
||||
id: rawChannelId,
|
||||
name: channelName,
|
||||
parentId: resolveDiscordChannelParentIdSafe(channel),
|
||||
parent: undefined,
|
||||
},
|
||||
channelInfo,
|
||||
});
|
||||
threadParentId = parentInfo.id;
|
||||
threadParentName = parentInfo.name;
|
||||
threadParentSlug = threadParentName ? normalizeDiscordSlug(threadParentName) : "";
|
||||
}
|
||||
|
||||
return {
|
||||
channelType,
|
||||
isDirectMessage,
|
||||
isGroupDm,
|
||||
isThreadChannel,
|
||||
channelName,
|
||||
channelSlug,
|
||||
rawChannelId,
|
||||
threadParentId,
|
||||
threadParentName,
|
||||
threadParentSlug,
|
||||
isThreadChannel: channelContext.isThreadChannel,
|
||||
channelName: channelContext.channelName,
|
||||
channelSlug: channelContext.channelSlug,
|
||||
rawChannelId: channelContext.channelId,
|
||||
threadParentId: params.hasGuild ? channelContext.threadParentId : undefined,
|
||||
threadParentName: params.hasGuild ? channelContext.threadParentName : undefined,
|
||||
threadParentSlug: params.hasGuild ? channelContext.threadParentSlug : "",
|
||||
};
|
||||
}
|
||||
|
||||
108
extensions/discord/src/monitor/thread-channel-context.ts
Normal file
108
extensions/discord/src/monitor/thread-channel-context.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { ChannelType, type Client } from "@buape/carbon";
|
||||
import { normalizeDiscordSlug } from "./allow-list.js";
|
||||
import {
|
||||
resolveDiscordChannelIdSafe,
|
||||
resolveDiscordChannelInfoSafe,
|
||||
resolveDiscordChannelParentIdSafe,
|
||||
} from "./channel-access.js";
|
||||
import { resolveDiscordChannelInfo, type DiscordChannelInfo } from "./message-utils.js";
|
||||
import { resolveDiscordThreadParentInfo } from "./threading.js";
|
||||
|
||||
export type DiscordThreadLikeChannelContext = {
|
||||
channelType?: ChannelType;
|
||||
isThreadChannel: boolean;
|
||||
channelId: string;
|
||||
channelName?: string;
|
||||
channelSlug: string;
|
||||
parentId?: string;
|
||||
threadParentId?: string;
|
||||
threadParentName?: string;
|
||||
threadParentSlug: string;
|
||||
channelInfo: DiscordChannelInfo | null;
|
||||
};
|
||||
|
||||
export function isDiscordThreadChannelType(type: ChannelType | number | undefined): boolean {
|
||||
return (
|
||||
type === ChannelType.PublicThread ||
|
||||
type === ChannelType.PrivateThread ||
|
||||
type === ChannelType.AnnouncementThread
|
||||
);
|
||||
}
|
||||
|
||||
function buildFetchedChannelInfo(channel: unknown): DiscordChannelInfo | null {
|
||||
const channelInfo = resolveDiscordChannelInfoSafe(channel);
|
||||
if (channelInfo.type === undefined) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: channelInfo.type as ChannelType,
|
||||
name: channelInfo.name,
|
||||
topic: channelInfo.topic,
|
||||
parentId: channelInfo.parentId,
|
||||
ownerId: channelInfo.ownerId,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveDiscordThreadLikeChannelContext(params: {
|
||||
client: Client;
|
||||
channel: unknown;
|
||||
channelIdFallback?: string;
|
||||
channelInfo?: DiscordChannelInfo | null;
|
||||
}): Promise<DiscordThreadLikeChannelContext> {
|
||||
const safeChannelInfo = resolveDiscordChannelInfoSafe(params.channel);
|
||||
const channelId = resolveDiscordChannelIdSafe(params.channel) ?? params.channelIdFallback ?? "";
|
||||
const channelInfo =
|
||||
params.channelInfo !== undefined
|
||||
? params.channelInfo
|
||||
: channelId
|
||||
? await resolveDiscordChannelInfo(params.client, channelId)
|
||||
: null;
|
||||
const channelType = (safeChannelInfo.type as ChannelType | undefined) ?? channelInfo?.type;
|
||||
const channelName = safeChannelInfo.name ?? channelInfo?.name;
|
||||
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
||||
const parentId = resolveDiscordChannelParentIdSafe(params.channel) ?? channelInfo?.parentId;
|
||||
const isThreadChannel = isDiscordThreadChannelType(channelType);
|
||||
|
||||
let threadParentId: string | undefined;
|
||||
let threadParentName: string | undefined;
|
||||
let threadParentSlug = "";
|
||||
if (channelId && isThreadChannel) {
|
||||
const parentInfo = await resolveDiscordThreadParentInfo({
|
||||
client: params.client,
|
||||
threadChannel: {
|
||||
id: channelId,
|
||||
name: channelName,
|
||||
parentId,
|
||||
parent: undefined,
|
||||
},
|
||||
channelInfo,
|
||||
});
|
||||
threadParentId = parentInfo.id;
|
||||
threadParentName = parentInfo.name;
|
||||
threadParentSlug = threadParentName ? normalizeDiscordSlug(threadParentName) : "";
|
||||
}
|
||||
|
||||
return {
|
||||
channelType,
|
||||
isThreadChannel,
|
||||
channelId,
|
||||
channelName,
|
||||
channelSlug,
|
||||
parentId,
|
||||
threadParentId,
|
||||
threadParentName,
|
||||
threadParentSlug,
|
||||
channelInfo,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveFetchedDiscordThreadLikeChannelContext(params: {
|
||||
client: Client;
|
||||
channel: unknown;
|
||||
channelIdFallback?: string;
|
||||
}): Promise<DiscordThreadLikeChannelContext> {
|
||||
return await resolveDiscordThreadLikeChannelContext({
|
||||
...params,
|
||||
channelInfo: buildFetchedChannelInfo(params.channel),
|
||||
});
|
||||
}
|
||||
@@ -13,14 +13,9 @@ import {
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import type { DiscordAccountConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { formatMention } from "../mentions.js";
|
||||
import { normalizeDiscordSlug } from "../monitor/allow-list.js";
|
||||
import {
|
||||
resolveDiscordChannelNameSafe,
|
||||
resolveDiscordChannelParentIdSafe,
|
||||
} from "../monitor/channel-access.js";
|
||||
import { resolveDiscordChannelInfo } from "../monitor/message-utils.js";
|
||||
import { resolveDiscordChannelNameSafe } from "../monitor/channel-access.js";
|
||||
import { resolveDiscordSenderIdentity } from "../monitor/sender-identity.js";
|
||||
import { resolveDiscordThreadParentInfo } from "../monitor/threading.js";
|
||||
import { resolveDiscordThreadLikeChannelContext } from "../monitor/thread-channel-context.js";
|
||||
import { authorizeDiscordVoiceIngress } from "./access.js";
|
||||
import type { DiscordVoiceManager } from "./manager.js";
|
||||
|
||||
@@ -66,35 +61,12 @@ async function authorizeVoiceCommand(
|
||||
}
|
||||
|
||||
const channelId = channelOverride?.id ?? channel?.id ?? "";
|
||||
const rawChannelName = channelOverride?.name ?? resolveDiscordChannelNameSafe(channel);
|
||||
const rawParentId = channelOverride?.parentId ?? resolveDiscordChannelParentIdSafe(channel);
|
||||
const channelInfo = channelId
|
||||
? await resolveDiscordChannelInfo(interaction.client, channelId)
|
||||
: null;
|
||||
const channelName = rawChannelName ?? channelInfo?.name;
|
||||
const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
|
||||
const isThreadChannel =
|
||||
channelInfo?.type === CarbonChannelType.PublicThread ||
|
||||
channelInfo?.type === CarbonChannelType.PrivateThread ||
|
||||
channelInfo?.type === CarbonChannelType.AnnouncementThread;
|
||||
let parentId: string | undefined;
|
||||
let parentName: string | undefined;
|
||||
let parentSlug: string | undefined;
|
||||
if (isThreadChannel && channelId) {
|
||||
const parentInfo = await resolveDiscordThreadParentInfo({
|
||||
client: interaction.client,
|
||||
threadChannel: {
|
||||
id: channelId,
|
||||
name: channelName,
|
||||
parentId: rawParentId ?? channelInfo?.parentId,
|
||||
parent: undefined,
|
||||
},
|
||||
channelInfo,
|
||||
});
|
||||
parentId = parentInfo.id;
|
||||
parentName = parentInfo.name;
|
||||
parentSlug = parentName ? normalizeDiscordSlug(parentName) : undefined;
|
||||
}
|
||||
const channelContext = await resolveDiscordThreadLikeChannelContext({
|
||||
client: interaction.client,
|
||||
channel: channelOverride ?? channel,
|
||||
channelIdFallback: channelId,
|
||||
});
|
||||
const channelName = channelOverride?.name ?? channelContext.channelName;
|
||||
|
||||
const memberRoleIds = Array.isArray(interaction.rawData.member?.roles)
|
||||
? interaction.rawData.member.roles.map((roleId: string) => roleId)
|
||||
@@ -109,11 +81,11 @@ async function authorizeVoiceCommand(
|
||||
guildId: interaction.guild.id,
|
||||
channelId,
|
||||
channelName,
|
||||
channelSlug,
|
||||
parentId,
|
||||
parentName,
|
||||
parentSlug,
|
||||
scope: isThreadChannel ? "thread" : "channel",
|
||||
channelSlug: channelContext.channelSlug,
|
||||
parentId: channelOverride?.parentId ?? channelContext.threadParentId,
|
||||
parentName: channelContext.threadParentName,
|
||||
parentSlug: channelContext.threadParentSlug,
|
||||
scope: channelContext.isThreadChannel ? "thread" : "channel",
|
||||
channelLabel: channelId ? formatMention({ channelId }) : "This channel",
|
||||
memberRoleIds,
|
||||
sender: {
|
||||
@@ -200,7 +172,6 @@ export function createDiscordVoiceCommand(params: VoiceCommandContext): CommandW
|
||||
channelOverride: {
|
||||
id: channel.id,
|
||||
name: resolveDiscordChannelNameSafe(channel),
|
||||
parentId: resolveDiscordChannelParentIdSafe(channel),
|
||||
},
|
||||
});
|
||||
if (!access.ok) {
|
||||
|
||||
Reference in New Issue
Block a user