gistfile1.txt
· 4.8 KiB · Text
Raw
import {
ChatInputCommandInteraction,
ButtonInteraction,
ButtonBuilder,
ButtonStyle,
ActionRowBuilder,
EmbedBuilder,
} from "discord.js";
import { cfg } from "@systems/config";
import { hasOfficerRole } from "@systems/users";
import { getRegisteredUsers, setImpersonation, clearImpersonation, getImpersonation } from "@systems/impersonate";
import { replyAndDelete } from "@utils";
const PAGE_SIZE = 5; // users per page (1 button each + nav row)
function buildImpersonateEmbed(users: { userKey: string; aliases: string[] }[], page: number, currentImpersonation: string | null): EmbedBuilder {
const start = page * PAGE_SIZE;
const pageUsers = users.slice(start, start + PAGE_SIZE);
const lines = pageUsers.map((u, i) => {
const display = u.aliases[0] ?? u.userKey;
const current = u.userKey === currentImpersonation ? " ← *active*" : "";
return `${start + i + 1}. **${display}** (${u.userKey})${current}`;
});
return new EmbedBuilder()
.setTitle("🎭 Impersonate User")
.setDescription(
(currentImpersonation
? `Currently impersonating: **${currentImpersonation}**\n\n`
: "Not impersonating anyone.\n\n") +
lines.join("\n")
)
.setColor(0x5865f2)
.setFooter({ text: `Page ${page + 1} of ${Math.ceil(users.length / PAGE_SIZE)}` });
}
function buildImpersonateButtons(
users: { userKey: string; aliases: string[] }[],
page: number,
realDiscordId: string
): ActionRowBuilder<ButtonBuilder>[] {
const start = page * PAGE_SIZE;
const pageUsers = users.slice(start, start + PAGE_SIZE);
const hasNext = users.length > (page + 1) * PAGE_SIZE;
const hasPrev = page > 0;
const rows: ActionRowBuilder<ButtonBuilder>[] = [];
// One button per user on this page
const userButtons = pageUsers.map((u) =>
new ButtonBuilder()
.setCustomId(`impersonate_set:${u.userKey}:${page}`)
.setLabel(`${u.aliases[0] ?? u.userKey}`)
.setStyle(ButtonStyle.Primary)
);
if (userButtons.length > 0) {
rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...userButtons));
}
// Nav + release row
const navBtns: ButtonBuilder[] = [];
if (hasPrev) navBtns.push(new ButtonBuilder().setCustomId(`impersonate_page:${page - 1}`).setLabel("← Prev").setStyle(ButtonStyle.Secondary));
if (hasNext) navBtns.push(new ButtonBuilder().setCustomId(`impersonate_page:${page + 1}`).setLabel("Next →").setStyle(ButtonStyle.Secondary));
navBtns.push(new ButtonBuilder().setCustomId(`impersonate_release:${page}`).setLabel("🚫 Release").setStyle(ButtonStyle.Danger));
rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...navBtns));
return rows;
}
// Slash command handler
export async function handleImpersonate(interaction: ChatInputCommandInteraction): Promise<void> {
const member = await interaction.guild!.members.fetch(interaction.user.id);
if (!hasOfficerRole(member, cfg("officerRoles"))) {
return void replyAndDelete(interaction, "❌ You don't have permission to use this command.");
}
const users = getRegisteredUsers();
if (users.length === 0) return void replyAndDelete(interaction, "❌ No registered users found in usermap.json.");
const current = getImpersonation(interaction.user.id);
const embed = buildImpersonateEmbed(users, 0, current);
const buttons = buildImpersonateButtons(users, 0, interaction.user.id);
await interaction.reply({ embeds: [embed], components: buttons, ephemeral: true });
}
// Button handler
export async function handleImpersonateButton(interaction: ButtonInteraction): Promise<void> {
const { customId } = interaction;
const realId = interaction.user.id;
if (customId.startsWith("impersonate_set:")) {
const parts = customId.split(":");
const userKey = parts[1];
const page = parseInt(parts[2] ?? "0");
setImpersonation(realId, userKey);
const users = getRegisteredUsers();
const embed = buildImpersonateEmbed(users, page, userKey);
const buttons = buildImpersonateButtons(users, page, realId);
await interaction.update({ embeds: [embed], components: buttons });
return;
}
if (customId.startsWith("impersonate_page:")) {
const page = parseInt(customId.split(":")[1]);
const current = getImpersonation(realId);
const users = getRegisteredUsers();
const embed = buildImpersonateEmbed(users, page, current);
const buttons = buildImpersonateButtons(users, page, realId);
await interaction.update({ embeds: [embed], components: buttons });
return;
}
if (customId.startsWith("impersonate_release")) {
clearImpersonation(realId);
const users = getRegisteredUsers();
const embed = buildImpersonateEmbed(users, 0, null);
const buttons = buildImpersonateButtons(users, 0, realId);
await interaction.update({ embeds: [embed], components: buttons });
return;
}
}
| 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 | } |