Discord: add fetch message action

This commit is contained in:
Shadow
2026-01-10 16:38:02 -06:00
parent 786eac1d6f
commit c731a87d07
5 changed files with 87 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ Use `discord` to manage messages, reactions, threads, polls, and moderation. You
## Inputs to collect
- For reactions: `channelId`, `messageId`, and an `emoji`.
- For fetchMessage: `guildId`, `channelId`, `messageId`, or a `messageLink` like `https://discord.com/channels/<guildId>/<channelId>/<messageId>`.
- For stickers/polls/sendMessage: a `to` target (`channel:<id>` or `user:<id>`). Optional `content` text.
- Polls also need a `question` plus 210 `answers`.
- For media: `mediaUrl` with `file:///path` for local files or `https://...` for remote.
@@ -21,6 +22,7 @@ Use `discord` to manage messages, reactions, threads, polls, and moderation. You
Message context lines include `discord message id` and `channel` fields you can reuse directly.
**Note:** `sendMessage` uses `to: "channel:<id>"` format, not `channelId`. Other actions like `react`, `readMessages`, `editMessage` use `channelId` directly.
**Note:** `fetchMessage` accepts message IDs or full links like `https://discord.com/channels/<guildId>/<channelId>/<messageId>`.
## Actions
@@ -144,6 +146,24 @@ Use `discord.actions.*` to disable action groups:
}
```
### Fetch a single message
```json
{
"action": "fetchMessage",
"guildId": "999",
"channelId": "123",
"messageId": "456"
}
```
```json
{
"action": "fetchMessage",
"messageLink": "https://discord.com/channels/999/123/456"
}
```
### Send/edit/delete a message
```json

View File

@@ -5,6 +5,7 @@ import {
deleteMessageDiscord,
editMessageDiscord,
fetchChannelPermissionsDiscord,
fetchMessageDiscord,
fetchReactionsDiscord,
listPinsDiscord,
listThreadsDiscord,
@@ -53,6 +54,23 @@ function formatDiscordTimestamp(ts?: string | null): string | undefined {
return `${yyyy}-${mm}-${dd}T${hh}:${min}${sign}${offsetH}:${offsetM}${tzSuffix}`;
}
function parseDiscordMessageLink(link: string) {
const normalized = link.trim();
const match = normalized.match(
/^(?:https?:\/\/)?(?:ptb\.|canary\.)?discord(?:app)?\.com\/channels\/(\d+)\/(\d+)\/(\d+)(?:\/?|\?.*)$/i,
);
if (!match) {
throw new Error(
"Invalid Discord message link. Expected https://discord.com/channels/<guildId>/<channelId>/<messageId>.",
);
}
return {
guildId: match[1],
channelId: match[2],
messageId: match[3],
};
}
export async function handleDiscordMessagingAction(
action: string,
params: Record<string, unknown>,
@@ -157,6 +175,28 @@ export async function handleDiscordMessagingAction(
const permissions = await fetchChannelPermissionsDiscord(channelId);
return jsonResult({ ok: true, permissions });
}
case "fetchMessage": {
if (!isActionEnabled("messages")) {
throw new Error("Discord message reads are disabled.");
}
const messageLink = readStringParam(params, "messageLink");
let guildId = readStringParam(params, "guildId");
let channelId = readStringParam(params, "channelId");
let messageId = readStringParam(params, "messageId");
if (messageLink) {
const parsed = parseDiscordMessageLink(messageLink);
guildId = parsed.guildId;
channelId = parsed.channelId;
messageId = parsed.messageId;
}
if (!guildId || !channelId || !messageId) {
throw new Error(
"Discord message fetch requires guildId, channelId, and messageId (or a valid messageLink).",
);
}
const message = await fetchMessageDiscord(channelId, messageId);
return jsonResult({ ok: true, message, guildId, channelId, messageId });
}
case "readMessages": {
if (!isActionEnabled("messages")) {
throw new Error("Discord message reads are disabled.");

View File

@@ -11,6 +11,7 @@ const messagingActions = new Set([
"sticker",
"poll",
"permissions",
"fetchMessage",
"readMessages",
"sendMessage",
"editMessage",

View File

@@ -35,6 +35,21 @@ export const DiscordToolSchema = Type.Union([
action: Type.Literal("permissions"),
channelId: Type.String(),
}),
Type.Union([
Type.Object({
action: Type.Literal("fetchMessage"),
messageLink: Type.String(),
guildId: Type.Optional(Type.String()),
channelId: Type.Optional(Type.String()),
messageId: Type.Optional(Type.String()),
}),
Type.Object({
action: Type.Literal("fetchMessage"),
guildId: Type.String(),
channelId: Type.String(),
messageId: Type.String(),
}),
]),
Type.Object({
action: Type.Literal("readMessages"),
channelId: Type.String(),

View File

@@ -903,6 +903,17 @@ export async function readMessagesDiscord(
)) as APIMessage[];
}
export async function fetchMessageDiscord(
channelId: string,
messageId: string,
opts: DiscordReactOpts = {},
): Promise<APIMessage> {
const rest = resolveDiscordRest(opts);
return (await rest.get(
Routes.channelMessage(channelId, messageId),
)) as APIMessage;
}
export async function editMessageDiscord(
channelId: string,
messageId: string,