Última atividade 1 month ago

nuno revisou este gist 1 month ago. Ir para a revisão

1 file changed, 156 insertions

interactions.ts(arquivo criado)

@@ -0,0 +1,156 @@
1 + import { Interaction, ChatInputCommandInteraction, ButtonInteraction, TextChannel } from "discord.js";
2 + import { handleButton } from "@handlers/buttons";
3 + import { handleTgCommand } from "@commands/tg";
4 + import { handleTgConfigCommand } from "@commands/tgConfig";
5 + import { handleBorrowAcceptButton } from "@subcommands/char/accept";
6 + import { handleBorrowDeclineButton } from "@subcommands/char/decline";
7 + import { handleConflictButton } from "@systems/conflict";
8 + import { handleImpersonateButton } from "@subcommands/impersonate";
9 + import { handleAutocomplete } from "@handlers/autocomplete";
10 + import { setActiveCharacter, getCharacterByName, getCharacters } from "@systems/characters";
11 + import { setPersistentPreference, clearSessionBorrowForUser, getEffectiveCharacter } from "@systems/borrow";
12 + import { polls, updatePollMessage } from "@systems/poll";
13 + import { cfg } from "@systems/config";
14 + import { resolveMessage, nowFormatted } from "@systems/messages";
15 + import { format } from "@format";
16 + import fs from "fs";
17 + import path from "path";
18 +
19 + async function handleSwitchAfterReclaim(btn: ButtonInteraction): Promise<void> {
20 + const parts = btn.customId.split(":");
21 + const userKey = parts[1];
22 + const charName = parts[2];
23 + const prevVoteType = (parts[3] ?? "yes") as "yes" | "no";
24 +
25 + const chars = JSON.parse(
26 + fs.readFileSync(path.join(__dirname, "../../data/characters.json"), "utf8")
27 + );
28 +
29 + let resolvedChar: any = null;
30 + let borrowedFrom: string | null = null;
31 +
32 + // Try own char first
33 + const ownEntry = chars[userKey]?.characters?.find((c: any) => c.name === charName);
34 + if (ownEntry) {
35 + setActiveCharacter(userKey, charName);
36 + clearSessionBorrowForUser(userKey);
37 + resolvedChar = ownEntry;
38 + } else {
39 + // Try shared char
40 + for (const [ownerKey, data] of Object.entries(chars) as [string, any][]) {
41 + const char = data.characters?.find(
42 + (c: any) => c.name === charName && c.sharedWith?.includes(userKey)
43 + );
44 + if (char) {
45 + setPersistentPreference(userKey, ownerKey, charName);
46 + clearSessionBorrowForUser(userKey);
47 + resolvedChar = char;
48 + borrowedFrom = ownerKey;
49 + break;
50 + }
51 + }
52 + }
53 +
54 + if (!resolvedChar) {
55 + await btn.reply({ content: `❌ Could not switch to **${charName}**.`, ephemeral: true });
56 + return;
57 + }
58 +
59 + // Re-add to poll with previous vote type
60 + const slot = [...polls.keys()][0];
61 + const state = slot !== undefined ? polls.get(slot) : null;
62 +
63 + if (state && !state.locked && state.confirmed === null) {
64 + const { char } = getEffectiveCharacter(userKey);
65 + const now = nowFormatted();
66 + const publicMsg = resolveMessage("public", prevVoteType, 1, userKey, null, null);
67 + const voteEntry = {
68 + userKey,
69 + displayName: charName,
70 + characterName: char?.name ?? charName,
71 + characterClass: char?.class ?? resolvedChar.class,
72 + characterLevel: char?.level ?? resolvedChar.level,
73 + characterNation: char?.nation ?? resolvedChar.nation,
74 + borrowedFrom: borrowedFrom ?? undefined,
75 + discordId: btn.user.id,
76 + votedAt: now,
77 + publicMessage: publicMsg ?? undefined,
78 + };
79 +
80 + for (const [id, entry] of [...state.yes.entries(), ...state.no.entries()]) {
81 + if (entry.userKey === userKey) {
82 + state.yes.delete(id);
83 + state.no.delete(id);
84 + }
85 + }
86 +
87 + if (prevVoteType === "yes") {
88 + state.yes.set(`switch_reclaim:${userKey}`, voteEntry);
89 + } else {
90 + state.no.set(`switch_reclaim:${userKey}`, voteEntry);
91 + }
92 +
93 + const channel = await btn.client.channels.fetch(cfg("pollChannelId")) as TextChannel;
94 + await updatePollMessage(channel, slot!);
95 + }
96 +
97 + const charDisplay = resolvedChar ? format.char(resolvedChar) : charName;
98 + const borrowNote = borrowedFrom ? ` *(shared by ${borrowedFrom})*` : "";
99 + await btn.reply({
100 + content: `🔄 ${charDisplay}${borrowNote}${state ? ` — re-added to poll as **${prevVoteType}**.` : ""}`,
101 + ephemeral: true,
102 + });
103 + }
104 +
105 + export async function handleInteraction(interaction: Interaction): Promise<void> {
106 + try {
107 + if (interaction.isAutocomplete()) {
108 + await handleAutocomplete(interaction);
109 + return;
110 + }
111 +
112 + if (interaction.isButton()) {
113 + const btn = interaction as ButtonInteraction;
114 +
115 + if (btn.customId.startsWith("conflict_")) {
116 + return await handleConflictButton(btn);
117 + }
118 +
119 + if (btn.customId.startsWith("impersonate_")) {
120 + return await handleImpersonateButton(btn);
121 + }
122 +
123 + if (btn.customId.startsWith("switch_after_reclaim:")) {
124 + return await handleSwitchAfterReclaim(btn);
125 + }
126 +
127 + if (btn.customId.startsWith("borrow_accept:")) {
128 + const [, ownerKey, requesterKey] = btn.customId.split(":");
129 + return await handleBorrowAcceptButton(btn, ownerKey, requesterKey);
130 + }
131 +
132 + if (btn.customId.startsWith("borrow_decline:")) {
133 + const [, ownerKey, requesterKey] = btn.customId.split(":");
134 + return await handleBorrowDeclineButton(btn, ownerKey, requesterKey);
135 + }
136 +
137 + return await handleButton(btn);
138 + }
139 +
140 + if (interaction.isChatInputCommand()) {
141 + const cmd = interaction as ChatInputCommandInteraction;
142 + if (cmd.commandName === "tg") await handleTgCommand(cmd);
143 + if (cmd.commandName === "tg-config") await handleTgConfigCommand(cmd);
144 + }
145 + } catch (err) {
146 + console.error("Interaction error:", err);
147 + try {
148 + const msg = { content: "❌ An error occurred.", ephemeral: true };
149 + if ((interaction as any).replied || (interaction as any).deferred) {
150 + await (interaction as any).followUp(msg);
151 + } else {
152 + await (interaction as any).reply(msg);
153 + }
154 + } catch {}
155 + }
156 + }
Próximo Anterior