import { ChatInputCommandInteraction, TextChannel } from "discord.js"; import { cfg } from "../systems/config"; import { resolveUser, hasOfficerRole } from "../systems/users"; import { setActiveCharacter, getActiveCharacter, getCharacterByName } from "../systems/characters"; import { setSessionBorrow, getSessionBorrow, setPersistentPreference, clearPersistentPreference } from "../systems/borrow"; import { polls, updatePollMessage } from "../systems/poll"; import { replyAndDelete } from "../utils"; import fs from "fs"; import path from "path"; const CHARS_PATH = path.join(__dirname, "../../data/characters.json"); function findSharedChar(usermapKey: string, charName: string): { ownerKey: string; char: any } | null { try { const chars = JSON.parse(fs.readFileSync(CHARS_PATH, "utf8")); for (const [ownerKey, data] of Object.entries(chars) as [string, any][]) { if (ownerKey === usermapKey) continue; const char = data.characters?.find( (c: any) => c.name.toLowerCase() === charName.toLowerCase() && c.sharedWith?.includes(usermapKey) ); if (char) return { ownerKey, char }; } } catch {} return null; } // Reverse-lookup: find Discord userId for a usermapKey from current poll voters function findUserIdInPoll(state: any, usermapKey: string): string | null { for (const [id, entry] of [...state.yes.entries(), ...state.no.entries()]) { if (entry.usermapKey === usermapKey) return id; } return null; } export async function handleSwitch(interaction: ChatInputCommandInteraction): Promise { const member = await interaction.guild!.members.fetch(interaction.user.id); const isOfficer = hasOfficerRole(member, cfg("officerRoles")); const nameArg = interaction.options.getString("name"); const charName = interaction.options.getString("char_name", true); let usermapKey: string | null; if (nameArg) { if (!isOfficer) return void replyAndDelete(interaction, "❌ Only officers can switch other players' characters."); usermapKey = nameArg; } else { const user = await resolveUser(member); usermapKey = user.usermapKey; } if (!usermapKey) return void replyAndDelete(interaction, "❌ You are not registered in the system."); let resolvedChar: any = null; let borrowedFrom: string | null = null; // Try own characters first const set = setActiveCharacter(usermapKey, charName); if (set) { clearPersistentPreference(usermapKey); // Also clear any active session borrow so own char takes priority const { clearSessionBorrowForUser } = require("../systems/borrow"); clearSessionBorrowForUser(usermapKey); resolvedChar = getActiveCharacter(usermapKey); } else { // Fall back to shared characters const shared = findSharedChar(usermapKey, charName); if (shared) { setSessionBorrow(usermapKey, shared.ownerKey, shared.char.name); setPersistentPreference(usermapKey, shared.ownerKey, shared.char.name); resolvedChar = shared.char; borrowedFrom = shared.ownerKey; } } if (!resolvedChar) return void replyAndDelete(interaction, `❌ No character named **${charName}** found.`); // Update poll embed if user has voted const slot = [...polls.keys()][0]; if (slot !== undefined) { const state = polls.get(slot)!; const userId = nameArg ? findUserIdInPoll(state, usermapKey) : interaction.user.id; if (userId && (state.yes.has(userId) || state.no.has(userId))) { const updateEntry = (map: Map) => { const entry = map.get(userId); if (entry) { entry.characterName = resolvedChar.name; entry.characterClass = resolvedChar.class; entry.characterLevel = resolvedChar.level; entry.characterNation = resolvedChar.nation; entry.borrowedFrom = borrowedFrom ?? undefined; } }; updateEntry(state.yes); updateEntry(state.no); const channel = await interaction.client.channels.fetch(cfg("pollChannelId")) as TextChannel; await updatePollMessage(channel, slot); } } const borrowNote = borrowedFrom ? ` (shared by **${borrowedFrom}**)` : ""; return void replyAndDelete(interaction, `✅ Switched to **${charName}**${borrowNote}.`); }