最後活躍 3 weeks ago

gistfile1.txt 原始檔案
1import {
2 ChatInputCommandInteraction,
3 ButtonInteraction,
4 ButtonBuilder,
5 ButtonStyle,
6 ActionRowBuilder,
7 EmbedBuilder,
8} from "discord.js";
9import { cfg } from "@systems/config";
10import { hasOfficerRole } from "@systems/users";
11import { getRegisteredUsers, setImpersonation, clearImpersonation, getImpersonation } from "@systems/impersonate";
12import { replyAndDelete } from "@utils";
13
14const PAGE_SIZE = 5; // users per page (1 button each + nav row)
15
16function buildImpersonateEmbed(users: { userKey: string; aliases: string[] }[], page: number, currentImpersonation: string | null): EmbedBuilder {
17 const start = page * PAGE_SIZE;
18 const pageUsers = users.slice(start, start + PAGE_SIZE);
19
20 const lines = pageUsers.map((u, i) => {
21 const display = u.aliases[0] ?? u.userKey;
22 const current = u.userKey === currentImpersonation ? " ← *active*" : "";
23 return `${start + i + 1}. **${display}** (${u.userKey})${current}`;
24 });
25
26 return new EmbedBuilder()
27 .setTitle("🎭 Impersonate User")
28 .setDescription(
29 (currentImpersonation
30 ? `Currently impersonating: **${currentImpersonation}**\n\n`
31 : "Not impersonating anyone.\n\n") +
32 lines.join("\n")
33 )
34 .setColor(0x5865f2)
35 .setFooter({ text: `Page ${page + 1} of ${Math.ceil(users.length / PAGE_SIZE)}` });
36}
37
38function buildImpersonateButtons(
39 users: { userKey: string; aliases: string[] }[],
40 page: number,
41 realDiscordId: string
42): ActionRowBuilder<ButtonBuilder>[] {
43 const start = page * PAGE_SIZE;
44 const pageUsers = users.slice(start, start + PAGE_SIZE);
45 const hasNext = users.length > (page + 1) * PAGE_SIZE;
46 const hasPrev = page > 0;
47 const rows: ActionRowBuilder<ButtonBuilder>[] = [];
48
49 // One button per user on this page
50 const userButtons = pageUsers.map((u) =>
51 new ButtonBuilder()
52 .setCustomId(`impersonate_set:${u.userKey}:${page}`)
53 .setLabel(`${u.aliases[0] ?? u.userKey}`)
54 .setStyle(ButtonStyle.Primary)
55 );
56 if (userButtons.length > 0) {
57 rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...userButtons));
58 }
59
60 // Nav + release row
61 const navBtns: ButtonBuilder[] = [];
62 if (hasPrev) navBtns.push(new ButtonBuilder().setCustomId(`impersonate_page:${page - 1}`).setLabel("← Prev").setStyle(ButtonStyle.Secondary));
63 if (hasNext) navBtns.push(new ButtonBuilder().setCustomId(`impersonate_page:${page + 1}`).setLabel("Next →").setStyle(ButtonStyle.Secondary));
64 navBtns.push(new ButtonBuilder().setCustomId(`impersonate_release:${page}`).setLabel("🚫 Release").setStyle(ButtonStyle.Danger));
65 rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...navBtns));
66
67 return rows;
68}
69
70// Slash command handler
71export async function handleImpersonate(interaction: ChatInputCommandInteraction): Promise<void> {
72 const member = await interaction.guild!.members.fetch(interaction.user.id);
73 if (!hasOfficerRole(member, cfg("officerRoles"))) {
74 return void replyAndDelete(interaction, "❌ You don't have permission to use this command.");
75 }
76
77 const users = getRegisteredUsers();
78 if (users.length === 0) return void replyAndDelete(interaction, "❌ No registered users found in usermap.json.");
79
80 const current = getImpersonation(interaction.user.id);
81 const embed = buildImpersonateEmbed(users, 0, current);
82 const buttons = buildImpersonateButtons(users, 0, interaction.user.id);
83
84 await interaction.reply({ embeds: [embed], components: buttons, ephemeral: true });
85}
86
87// Button handler
88export async function handleImpersonateButton(interaction: ButtonInteraction): Promise<void> {
89 const { customId } = interaction;
90 const realId = interaction.user.id;
91
92 if (customId.startsWith("impersonate_set:")) {
93 const parts = customId.split(":");
94 const userKey = parts[1];
95 const page = parseInt(parts[2] ?? "0");
96
97 setImpersonation(realId, userKey);
98 const users = getRegisteredUsers();
99 const embed = buildImpersonateEmbed(users, page, userKey);
100 const buttons = buildImpersonateButtons(users, page, realId);
101
102 await interaction.update({ embeds: [embed], components: buttons });
103 return;
104 }
105
106 if (customId.startsWith("impersonate_page:")) {
107 const page = parseInt(customId.split(":")[1]);
108 const current = getImpersonation(realId);
109 const users = getRegisteredUsers();
110 const embed = buildImpersonateEmbed(users, page, current);
111 const buttons = buildImpersonateButtons(users, page, realId);
112
113 await interaction.update({ embeds: [embed], components: buttons });
114 return;
115 }
116
117 if (customId.startsWith("impersonate_release")) {
118 clearImpersonation(realId);
119 const users = getRegisteredUsers();
120 const embed = buildImpersonateEmbed(users, 0, null);
121 const buttons = buildImpersonateButtons(users, 0, realId);
122
123 await interaction.update({ embeds: [embed], components: buttons });
124 return;
125 }
126}