Utoljára aktív 1 month ago

Revízió ea57045197c0e23517557d20428d1a95a0de5767

interactions.ts Eredeti
1import { Interaction, ChatInputCommandInteraction, ButtonInteraction, TextChannel } from "discord.js";
2import { handleButton } from "@handlers/buttons";
3import { handleTgCommand } from "@commands/tg";
4import { handleTgConfigCommand } from "@commands/tgConfig";
5import { handleBorrowAcceptButton } from "@subcommands/char/accept";
6import { handleBorrowDeclineButton } from "@subcommands/char/decline";
7import { handleConflictButton } from "@systems/conflict";
8import { handleImpersonateButton } from "@subcommands/impersonate";
9import { handleAutocomplete } from "@handlers/autocomplete";
10import { setActiveCharacter, getCharacterByName, getCharacters } from "@systems/characters";
11import { setPersistentPreference, clearSessionBorrowForUser, getEffectiveCharacter } from "@systems/borrow";
12import { polls, updatePollMessage } from "@systems/poll";
13import { cfg } from "@systems/config";
14import { resolveMessage, nowFormatted } from "@systems/messages";
15import { format } from "@format";
16import fs from "fs";
17import path from "path";
18
19async 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
105export 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}