feat(discord): support forum tag edits via channel-edit (#12070) (thanks @xiaoyaner0201)

This commit is contained in:
Shadow
2026-02-20 21:14:27 -06:00
committed by Shadow
parent b7644d61a2
commit b294342d7f
7 changed files with 96 additions and 4 deletions

View File

@@ -1,7 +1,7 @@
import fs from "node:fs/promises";
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
import { detectMime } from "../../media/mime.js";
import fs from "node:fs/promises";
import type { ImageSanitizationLimits } from "../image-sanitization.js";
import { detectMime } from "../../media/mime.js";
import { sanitizeToolResultImages } from "../tool-images.js";
// oxlint-disable-next-line typescript/no-explicit-any
@@ -273,3 +273,41 @@ export async function imageResultFromFile(params: {
imageSanitization: params.imageSanitization,
});
}
export type AvailableTag = {
id?: string;
name: string;
moderated?: boolean;
emoji_id?: string | null;
emoji_name?: string | null;
};
/**
* Validate and parse an `availableTags` parameter from untrusted input.
* Returns `undefined` when the value is missing or not an array.
* Entries that lack a string `name` are silently dropped.
*/
export function parseAvailableTags(raw: unknown): AvailableTag[] | undefined {
if (raw === undefined || raw === null) {
return undefined;
}
if (!Array.isArray(raw)) {
return undefined;
}
const result = raw
.filter(
(t): t is Record<string, unknown> =>
typeof t === "object" && t !== null && typeof t.name === "string",
)
.map((t) => ({
...(t.id !== undefined && typeof t.id === "string" ? { id: t.id } : {}),
name: t.name as string,
...(typeof t.moderated === "boolean" ? { moderated: t.moderated } : {}),
...(t.emoji_id === null || typeof t.emoji_id === "string" ? { emoji_id: t.emoji_id } : {}),
...(t.emoji_name === null || typeof t.emoji_name === "string"
? { emoji_name: t.emoji_name }
: {}),
}));
// Return undefined instead of empty array to avoid accidentally clearing all tags
return result.length ? result : undefined;
}