This commit is contained in:
Solvely-Colin 2026-01-29 21:53:23 -05:00 committed by GitHub
commit 06875c92a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 89 additions and 3 deletions

View File

@ -233,11 +233,16 @@ export async function handleDiscordMessagingAction(
const replyTo = readStringParam(params, "replyTo");
const embeds =
Array.isArray(params.embeds) && params.embeds.length > 0 ? params.embeds : undefined;
const components =
Array.isArray(params.components) && params.components.length > 0
? params.components
: undefined;
const result = await sendMessageDiscord(to, content, {
...(accountId ? { accountId } : {}),
mediaUrl,
replyTo,
embeds,
components,
});
return jsonResult({ ok: true, result });
}
@ -279,18 +284,31 @@ export async function handleDiscordMessagingAction(
const channelId = resolveChannelId();
const name = readStringParam(params, "name", { required: true });
const messageId = readStringParam(params, "messageId");
// Optional initial content (required for forum posts).
const content = readStringParam(params, "content") ?? undefined;
// Optional applied tag ids (forum posts).
const appliedTagIds = Array.isArray(params.appliedTagIds)
? params.appliedTagIds.filter((x) => typeof x === "string" && x.trim())
: undefined;
const autoArchiveMinutesRaw = params.autoArchiveMinutes;
const autoArchiveMinutes =
typeof autoArchiveMinutesRaw === "number" && Number.isFinite(autoArchiveMinutesRaw)
? autoArchiveMinutesRaw
: undefined;
const thread = accountId
? await createThreadDiscord(
channelId,
{ name, messageId, autoArchiveMinutes },
{ name, messageId, autoArchiveMinutes, content, appliedTagIds },
{ accountId },
)
: await createThreadDiscord(channelId, { name, messageId, autoArchiveMinutes });
: await createThreadDiscord(channelId, {
name,
messageId,
autoArchiveMinutes,
content,
appliedTagIds,
});
return jsonResult({ ok: true, thread });
}
case "threadList": {

View File

@ -87,6 +87,30 @@ function buildSendSchema(options: { includeButtons: boolean; includeCards: boole
},
),
),
embeds: Type.Optional(
Type.Array(
Type.Object(
{},
{
additionalProperties: true,
description:
"Provider-specific embed objects (Discord embeds, etc.). Passed through to the channel adapter when supported.",
},
),
),
),
components: Type.Optional(
Type.Array(
Type.Object(
{},
{
additionalProperties: true,
description:
"Provider-specific message components (Discord buttons/selects, etc.). Passed through when supported.",
},
),
),
),
};
if (!options.includeButtons) delete props.buttons;
if (!options.includeCards) delete props.card;

View File

@ -37,6 +37,7 @@ export async function handleDiscordMessageAction(
const mediaUrl = readStringParam(params, "media", { trim: false });
const replyTo = readStringParam(params, "replyTo");
const embeds = Array.isArray(params.embeds) ? params.embeds : undefined;
const components = Array.isArray(params.components) ? params.components : undefined;
return await handleDiscordAction(
{
action: "sendMessage",
@ -46,6 +47,7 @@ export async function handleDiscordMessageAction(
mediaUrl: mediaUrl ?? undefined,
replyTo: replyTo ?? undefined,
embeds,
components,
},
cfg,
);
@ -180,6 +182,10 @@ export async function handleDiscordMessageAction(
if (action === "thread-create") {
const name = readStringParam(params, "threadName", { required: true });
const messageId = readStringParam(params, "messageId");
// Optional initial post content (required for forum post creation).
const content = readStringParam(params, "message");
// Optional forum tag ids.
const appliedTagIds = readStringArrayParam(params, "appliedTagIds");
const autoArchiveMinutes = readNumberParam(params, "autoArchiveMin", {
integer: true,
});
@ -190,6 +196,8 @@ export async function handleDiscordMessageAction(
channelId: resolveChannelId(),
name,
messageId,
content,
appliedTagIds,
autoArchiveMinutes,
},
cfg,

View File

@ -94,10 +94,30 @@ export async function createThreadDiscord(
) {
const rest = resolveDiscordRest(opts);
const body: Record<string, unknown> = { name: payload.name };
if (payload.autoArchiveMinutes) {
body.auto_archive_duration = payload.autoArchiveMinutes;
}
const route = Routes.threads(channelId, payload.messageId);
// Forum posts (channel type 15) require an initial message payload.
// If content is provided, create the thread with an initial post.
if (payload.content) {
body.message = { content: payload.content };
if (Array.isArray(payload.appliedTagIds) && payload.appliedTagIds.length) {
body.applied_tags = payload.appliedTagIds;
}
// NOTE: discord-api-types Routes doesn't currently expose a helper for
// POST /channels/{channel.id}/threads (it only exposes listing archived threads),
// so use the raw route string.
const route = `/channels/${channelId}/threads`;
return await rest.post(route, { body });
}
// Regular threads: create from an existing message when messageId is provided,
// otherwise create a standard thread in the channel.
const route = payload.messageId
? Routes.threads(channelId, payload.messageId)
: `/channels/${channelId}/threads`;
return await rest.post(route, { body });
}

View File

@ -29,6 +29,7 @@ type DiscordSendOpts = {
replyTo?: string;
retry?: RetryConfig;
embeds?: unknown[];
components?: unknown[];
};
export async function sendMessageDiscord(
@ -63,6 +64,7 @@ export async function sendMessageDiscord(
request,
accountInfo.config.maxLinesPerMessage,
opts.embeds,
opts.components,
chunkMode,
);
} else {
@ -74,6 +76,7 @@ export async function sendMessageDiscord(
request,
accountInfo.config.maxLinesPerMessage,
opts.embeds,
opts.components,
chunkMode,
);
}

View File

@ -277,6 +277,7 @@ async function sendDiscordText(
request: DiscordRequest,
maxLinesPerMessage?: number,
embeds?: unknown[],
components?: unknown[],
chunkMode?: ChunkMode,
) {
if (!text.trim()) {
@ -297,6 +298,7 @@ async function sendDiscordText(
content: chunks[0],
message_reference: messageReference,
...(embeds?.length ? { embeds } : {}),
...(components?.length ? { components } : {}),
},
}) as Promise<{ id: string; channel_id: string }>,
"text",
@ -313,6 +315,7 @@ async function sendDiscordText(
content: chunk,
message_reference: isFirst ? messageReference : undefined,
...(isFirst && embeds?.length ? { embeds } : {}),
...(isFirst && components?.length ? { components } : {}),
},
}) as Promise<{ id: string; channel_id: string }>,
"text",
@ -334,6 +337,7 @@ async function sendDiscordMedia(
request: DiscordRequest,
maxLinesPerMessage?: number,
embeds?: unknown[],
components?: unknown[],
chunkMode?: ChunkMode,
) {
const media = await loadWebMedia(mediaUrl);
@ -354,6 +358,7 @@ async function sendDiscordMedia(
content: caption || undefined,
message_reference: messageReference,
...(embeds?.length ? { embeds } : {}),
...(components?.length ? { components } : {}),
files: [
{
data: media.buffer,
@ -374,6 +379,7 @@ async function sendDiscordMedia(
request,
maxLinesPerMessage,
undefined,
undefined,
chunkMode,
);
}

View File

@ -67,9 +67,16 @@ export type DiscordMessageEdit = {
};
export type DiscordThreadCreate = {
/** Optional message id to start a thread from (for classic threads). */
messageId?: string;
/** Thread / forum post title. */
name: string;
/** Auto-archive duration in minutes. */
autoArchiveMinutes?: number;
/** Optional initial post content (required for forum post creation). */
content?: string;
/** Optional forum tag ids (applied tags). */
appliedTagIds?: string[];
};
export type DiscordThreadList = {