最后活跃于 3 weeks ago

nuno 修订了这个 Gist 3 weeks ago. 转到此修订

1 file changed, 126 insertions

gistfile1.txt(文件已创建)

@@ -0,0 +1,126 @@
1 + import {
2 + ChatInputCommandInteraction,
3 + ButtonInteraction,
4 + ButtonBuilder,
5 + ButtonStyle,
6 + ActionRowBuilder,
7 + EmbedBuilder,
8 + } from "discord.js";
9 + import { cfg } from "@systems/config";
10 + import { hasOfficerRole } from "@systems/users";
11 + import { getRegisteredUsers, setImpersonation, clearImpersonation, getImpersonation } from "@systems/impersonate";
12 + import { replyAndDelete } from "@utils";
13 +
14 + const PAGE_SIZE = 5; // users per page (1 button each + nav row)
15 +
16 + function 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 +
38 + function 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
71 + export 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
88 + export 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 + }
上一页 下一页