async function handleSwitchAfterReclaim(btn: ButtonInteraction): Promise { const parts = btn.customId.split(":"); const userKey = parts[1]; const charName = parts[2]; const prevVoteType = (parts[3] ?? "yes") as "yes" | "no"; const chars = JSON.parse( fs.readFileSync(path.join(__dirname, "../../data/characters.json"), "utf8") ); let resolvedChar: any = null; let borrowedFrom: string | null = null; // Try own char first const ownEntry = chars[userKey]?.characters?.find((c: any) => c.name === charName); if (ownEntry) { setActiveCharacter(userKey, charName); clearSessionBorrowForUser(userKey); resolvedChar = ownEntry; } else { // Try shared char for (const [ownerKey, data] of Object.entries(chars) as [string, any][]) { const char = data.characters?.find( (c: any) => c.name === charName && c.sharedWith?.includes(userKey) ); if (char) { setPersistentPreference(userKey, ownerKey, charName); clearSessionBorrowForUser(userKey); resolvedChar = char; borrowedFrom = ownerKey; break; } } } if (!resolvedChar) { await btn.reply({ content: `❌ Could not switch to **${charName}**.`, ephemeral: true }); return; } // Re-add to poll with previous vote type const slot = [...polls.keys()][0]; const state = slot !== undefined ? polls.get(slot) : null; if (state && !state.locked && state.confirmed === null) { const { char } = getEffectiveCharacter(userKey); const now = nowFormatted(); const publicMsg = resolveMessage("public", prevVoteType, 1, userKey, null, null); const voteEntry = { userKey, displayName: charName, characterName: char?.name ?? charName, characterClass: char?.class ?? resolvedChar.class, characterLevel: char?.level ?? resolvedChar.level, characterNation: char?.nation ?? resolvedChar.nation, borrowedFrom: borrowedFrom ?? undefined, discordId: btn.user.id, votedAt: now, publicMessage: publicMsg ?? undefined, }; // Find and reuse existing vote ID — avoids duplicate entries let existingVoteId: string | null = null; for (const [id, entry] of [...state.yes.entries(), ...state.no.entries()]) { if (entry.userKey === userKey) { if (!existingVoteId) existingVoteId = id; state.yes.delete(id); state.no.delete(id); } } const voteId = existingVoteId ?? btn.user.id; if (prevVoteType === "yes") { state.yes.set(voteId, voteEntry); } else { state.no.set(voteId, voteEntry); } console.log(`[switch_reclaim] cleaning up for userKey=${userKey}`); console.log(`[switch_reclaim] yes keys:`, [...state.yes.entries()].map(([id, e]) => `${id}:${e.userKey}`)); console.log(`[switch_reclaim] no keys:`, [...state.no.entries()].map(([id, e]) => `${id}:${e.userKey}`)); const channel = await btn.client.channels.fetch(cfg("pollChannelId")) as TextChannel; await updatePollMessage(channel, slot!); } const charDisplay = resolvedChar ? format.char(resolvedChar) : charName; const borrowNote = borrowedFrom ? ` *(shared by ${borrowedFrom})*` : ""; await btn.reply({ content: `🔄 ${charDisplay}${borrowNote}${state ? ` — re-added to poll as **${prevVoteType}**.` : ""}`, ephemeral: true, }); }