import { ChatInputCommandInteraction, TextChannel } from "discord.js"; import { Config } from "../../systems/config"; import { resolveUser, hasOfficerRole } from "../../systems/users"; import { getCharacterByName, getActiveCharacter } from "../../systems/characters"; import { addPendingRequest, setSessionBorrow, sendBorrowRequestDM, canUseCharacter } from "../../systems/borrow"; import { polls, updatePollMessage } from "../../systems/poll"; import { replyAndDelete } from "../../utils"; import { getUsermapEntry, getUsermapEntryById } from "@src/systems/messages"; export async function handleCharBorrow(interaction: ChatInputCommandInteraction): Promise { const member = await interaction.guild!.members.fetch(interaction.user.id); const isOfficer = hasOfficerRole(member, Config.get({ section: "roles", key: "officer" })); const requester = await resolveUser(member); // Args: owner, charname, [username] (officer only — grants directly) const ownerArg = interaction.options.getString("owner", true); const charName = interaction.options.getString("char_name", true); const targetArg = interaction.options.getString("name"); // officer: grant to this user if (targetArg && !isOfficer) { return void replyAndDelete(interaction, "❌ Only officers can grant borrows directly."); } const requesterKey = targetArg ?? requester.userKey; if (!requesterKey) return void replyAndDelete(interaction, "❌ You are not registered in the system."); const char = getCharacterByName(ownerArg, charName); if (!char) return void replyAndDelete(interaction, `❌ Character **«${charName}»** not found for **${ownerArg}**.`); // Already has access? if (canUseCharacter(requesterKey, ownerArg, charName)) { return void replyAndDelete(interaction, `❌ **${requesterKey}** already has access to **«${charName}»**.`); } // Officer bypasses request — grant directly if (isOfficer && targetArg) { setSessionBorrow(requesterKey, ownerArg, charName); // Update poll if the user has already voted const slot = [...polls.keys()][0]; if (slot !== undefined) { const state = polls.get(slot)!; const channel = await interaction.client.channels.fetch(Config.get({ section: "channels", key: "poll" })) as TextChannel; // Find the voter entry and update their character for (const [id, entry] of [...state.yes.entries(), ...state.no.entries()]) { if (entry.userKey === requesterKey) { entry.characterName = char.name; entry.characterClass = char.class.key; entry.characterLevel = char.level; entry.characterNation = char.nation; entry.publicMessage = undefined; } } await updatePollMessage(channel, slot); } return void replyAndDelete(interaction, `✅ **${requesterKey}** can now borrow **«${charName}»** for this session.`); } // Regular player — send request to owner addPendingRequest({ requesterKey, ownerKey: ownerArg, charName, requestedAt: Date.now(), }); // Find owner's Discord ID from guild members // We need to reverse-lookup: find the guild member whose discord username maps to ownerArg const guild = interaction.guild!; await guild.members.fetch(); const ownerMember = guild.members.cache.find((m) => { const entry = getUsermapEntryById(m.user.id, m.user.username); return entry?.file === ownerArg; }); if (!ownerMember) { return void replyAndDelete(interaction, `✅ Borrow request sent — but **${ownerArg}** is not currently in the server to be notified.`); } const fallbackChannel = await interaction.client.channels.fetch(Config.get({ section: "channels", key: "poll" })) as TextChannel; await sendBorrowRequestDM( interaction.client, ownerMember.user.id, requester.displayName, ownerArg, requesterKey, char.name, char.class.key, char.level, fallbackChannel ); return void replyAndDelete(interaction, `✅ Borrow request sent to **${ownerArg}** for **«${charName}»**.`); }