import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js"; import { handleAdminUserMap, handleAdminPollFixVoter, handleAdminPollShowEntry, } from "@subcommands/admin/userMap"; import { UpdatesCommands } from "@subcommands/admin/updates"; import { ScoreInjectCommands } from "@subcommands/admin/score-inject"; import { ResultCommands } from "@subcommands/admin/result-post"; import { TestAlignCommands } from "@subcommands/admin/test-align"; import { ResetWeekCommands } from "../subcommands/admin/reset-week"; import { AnnouncementCommands } from "../subcommands/admin/announcement"; export function buildTgAdminCommand(): SlashCommandBuilder { const cmd = new SlashCommandBuilder() .setName("tg-admin") .setDescription("Administrative commands for TG bot management"); // ── user group ──────────────────────────────────────────────────────────────── cmd.addSubcommandGroup((g) => g .setName("user") .setDescription("Manage player registrations") .addSubcommand((s) => s .setName("map") .setDescription("Register a Discord user to a userKey") .addUserOption((o) => o .setName("user") .setDescription("Discord user to map") .setRequired(true) ) .addStringOption((o) => o .setName("userkey") .setDescription("The userKey to map this player to (must exist in characters.json)") .setRequired(true) .setAutocomplete(true) ) ) .addSubcommand((s) => s .setName("unmap") .setDescription("Remove a player's registration") .addUserOption((o) => o .setName("user") .setDescription("Discord user to unmap") .setRequired(true) ) ) .addSubcommand((s) => s .setName("list") .setDescription("List all registered player mappings") ) ); // ── poll group ───────────────────────────────────────────────────────────────── cmd.addSubcommandGroup((g) => g .setName("poll") .setDescription("Manage active poll entries") .addSubcommand((s) => s .setName("fix-voter") .setDescription("Re-resolve a voter's character in the active poll") .addUserOption((o) => o .setName("user") .setDescription("Discord user whose entry needs fixing") .setRequired(true) ) ) .addSubcommand((s) => s .setName("show-entry") .setDescription("Show raw poll entry for a user") .addUserOption((o) => o .setName("user") .setDescription("Discord user to inspect") .setRequired(true) ) ) ); cmd.addSubcommandGroup((g) => g .setName("updates") .setDescription("Manage update posts") .addSubcommand((s) => s .setName("post") .setDescription("Post or edit an update embed") .addStringOption((o) => o .setName("version") .setDescription("Version to post (defaults to latest)") .setRequired(false) .setAutocomplete(true) ) ) .addSubcommand((s) => s .setName("preview") .setDescription("Preview an update embed (ephemeral)") .addStringOption((o) => o .setName("version") .setDescription("Version to preview (defaults to latest)") .setRequired(false) .setAutocomplete(true) ) ) .addSubcommand((s) => s .setName("list") .setDescription("List all versions and their post status") ) ) // ── result group ───────────────────────────────────────────────────────────────── cmd.addSubcommandGroup((g) => g .setName("result") .setDescription("Manage TG results") .addSubcommand((s) => s .setName("post") .setDescription("Post or edit a TG result") .addStringOption((o) => o.setName("history_key").setDescription("TG to post").setRequired(true).setAutocomplete(true)) ) ) // ── leaderboard group ───────────────────────────────────────────────────────────────── cmd.addSubcommandGroup((g) => g .setName("leaderboard") .setDescription("Manage leaderboard") .addSubcommand((s) => s .setName("post") .setDescription("Post or edit the leaderboard") .addStringOption((o) => o.setName("week_key").setDescription("Week e.g. 2026-W24 (defaults current)").setAutocomplete(true)) ) .addSubcommand((s) => s .setName("post-highlights") .setDescription("Post or edit the leaderboard highlights") .addStringOption((o) => o.setName("week_key").setDescription("Week e.g. 2026-W24 (defaults current)").setAutocomplete(true)) ) ) cmd.addSubcommand((s) => s .setName("score-inject") .setDescription("Inject a score for any player (officer only)") .addStringOption((o) => o.setName("char_name").setDescription("Character").setRequired(true).setAutocomplete(true)) .addIntegerOption((o) => o.setName("pts").setDescription("Points").setRequired(true)) .addIntegerOption((o) => o.setName("slot").setDescription("TG slot hour").setRequired(true)) .addStringOption((o) => o.setName("date").setDescription("Date YYYY-MM-DD (defaults today)")) .addIntegerOption((o) => o.setName("k").setDescription("Kills")) .addIntegerOption((o) => o.setName("d").setDescription("Deaths")) .addIntegerOption((o) => o.setName("atk").setDescription("ATK damage")) .addIntegerOption((o) => o.setName("def").setDescription("DEF damage taken")) .addIntegerOption((o) => o.setName("heal").setDescription("Healing done")) .addStringOption((o) => o .setName("played_by") .setDescription("Userkey of who actually played (if different from character owner)") ) ) cmd.addSubcommand((s) => s .setName("test-align") .setDescription("[TEMP] Test embed text alignment calibration") .addStringOption((o) => o.setName("name_a").setDescription("First name").setRequired(true)) .addStringOption((o) => o.setName("name_b").setDescription("Second name").setRequired(true)) .addIntegerOption((o) => o.setName("fillers").setDescription("Filler count to add to name_a").setRequired(true)) .addStringOption((o) => o .setName("filler_type") .setDescription("Which invisible character to use") .addChoices( { name: "Hangul Filler", value: "hangul" }, { name: "Thin Space", value: "thin" }, { name: "Hair Space", value: "hair" }, ) ) ) cmd.addSubcommandGroup((g) => g .setName("announcement") .setDescription("Manage announcements") .addSubcommand((s) => s .setName("post") .setDescription("Post an announcement") .addStringOption((o) => o.setName("id").setDescription("Announcement ID").setRequired(true).setAutocomplete(true)) .addBooleanOption((o) => o.setName("force").setDescription("Repost even if already posted")) ) .addSubcommand((s) => s .setName("preview") .setDescription("Preview an announcement") .addStringOption((o) => o.setName("id").setDescription("Announcement ID").setRequired(true).setAutocomplete(true)) ) .addSubcommand((s) => s .setName("list") .setDescription("List all announcements") ) ) return cmd; } export async function handleTgAdminCommand(interaction: ChatInputCommandInteraction): Promise { const group = interaction.options.getSubcommandGroup(false); const sub = interaction.options.getSubcommand(); if (group === "user") { if (sub === "map" || sub === "unmap" || sub === "list") { return handleAdminUserMap(interaction); } } if (group === "poll") { if (sub === "fix-voter") return handleAdminPollFixVoter(interaction); if (sub === "show-entry") return handleAdminPollShowEntry(interaction); } if (group === "updates") { if (sub === "post") return UpdatesCommands.post(interaction); if (sub === "preview") return UpdatesCommands.preview(interaction); if (sub === "list") return UpdatesCommands.list(interaction); } if (group === null && sub === "score-inject") return ScoreInjectCommands.inject(interaction); if (group === "result" && sub === "post") return ResultCommands.post(interaction); if (group === "leaderboard" && sub === "post") return ResultCommands.leaderboardPost(interaction); if (group === "leaderboard" && sub === "post-highlights") return ResultCommands.leaderboardHighlights(interaction); if (group === "announcement" && sub === "post") return AnnouncementCommands.post(interaction); if (group === "announcement" && sub === "preview") return AnnouncementCommands.preview(interaction); if (group === "announcement" && sub === "list") return AnnouncementCommands.list(interaction); if (group === null && sub === "test-align") return TestAlignCommands.handle(interaction); if (group === null && sub === "reset-week") return ResetWeekCommands.handle(interaction); }