tg-bot-ts/src/subcommands/char/borrow.ts

95 lines
No EOL
4 KiB
TypeScript

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<void> {
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}»**.`);
}