Última atividade 1 month ago

nuno revisou este gist 1 month ago. Ir para a revisão

1 file changed, 146 insertions

switch.ts(arquivo criado)

@@ -0,0 +1,146 @@
1 + import { ChatInputCommandInteraction, TextChannel } from "discord.js";
2 + import { cfg } from "@systems/config";
3 + import { resolveUser, hasOfficerRole } from "@systems/users";
4 + import { setActiveCharacter, getActiveCharacter, getCharacterByName, getCharacters } from "@systems/characters";
5 + import {
6 + getEffectiveCharacter,
7 + setSessionBorrow,
8 + setPersistentPreference,
9 + clearPersistentPreference,
10 + clearSessionBorrowForUser,
11 + } from "@systems/borrow";
12 + import { polls, updatePollMessage } from "@systems/poll";
13 + import { getClassEmoji } from "@systems/emojis";
14 + import { replyAndDelete } from "@src/utils";
15 + import { format } from "@format";
16 + import fs from "fs";
17 + import path from "path";
18 +
19 + const CHARS_PATH = path.join(__dirname, "../../data/characters.json");
20 +
21 + function findSharedChar(userKey: string, charName: string): { ownerKey: string; char: any } | null {
22 + try {
23 + const chars = JSON.parse(fs.readFileSync(CHARS_PATH, "utf8"));
24 + for (const [ownerKey, data] of Object.entries(chars) as [string, any][]) {
25 + if (ownerKey === userKey) continue;
26 + const char = data.characters?.find(
27 + (c: any) => c.name.toLowerCase() === charName.toLowerCase() && c.sharedWith?.includes(userKey)
28 + );
29 + if (char) return { ownerKey, char };
30 + }
31 + } catch {}
32 + return null;
33 + }
34 +
35 + function findVoteIdInPoll(state: any, userKey: string): string | null {
36 + for (const [id, entry] of [...state.yes.entries(), ...state.no.entries()]) {
37 + if (entry.userKey === userKey) return id;
38 + }
39 + return null;
40 + }
41 +
42 + export async function handleSwitch(interaction: ChatInputCommandInteraction): Promise<void> {
43 + const member = await interaction.guild!.members.fetch(interaction.user.id);
44 + const isOfficer = hasOfficerRole(member, cfg("officerRoles"));
45 + const nameArg = interaction.options.getString("name");
46 + const charName = interaction.options.getString("char_name", true);
47 +
48 + let userKey: string | null;
49 + if (nameArg) {
50 + if (!isOfficer) return void replyAndDelete(interaction, "❌ Only officers can switch other players' characters.");
51 + userKey = nameArg;
52 + } else {
53 + const user = await resolveUser(member);
54 + userKey = user.userKey;
55 + }
56 +
57 + if (!userKey) return void replyAndDelete(interaction, "❌ You are not registered in the system.");
58 +
59 + // Resolve the target character without switching yet
60 + let resolvedChar: any = null;
61 + let borrowedFrom: string | null = null;
62 +
63 + const ownChar = getCharacterByName(userKey, charName);
64 + if (ownChar) {
65 + resolvedChar = ownChar;
66 + } else {
67 + const shared = findSharedChar(userKey, charName);
68 + if (shared) {
69 + resolvedChar = shared.char;
70 + borrowedFrom = shared.ownerKey;
71 + }
72 + }
73 +
74 + if (!resolvedChar) return void replyAndDelete(interaction, `❌ No character named **${charName}** found.`);
75 +
76 + // If already active — just show current state without switching
77 + const current = getEffectiveCharacter(userKey);
78 + if (current.char?.name === resolvedChar.name) {
79 + const classEmoji = getClassEmoji(resolvedChar.class) || resolvedChar.class;
80 + const borrowNote = current.borrowedFrom ? ` *(shared by ${current.borrowedFrom})*` : "";
81 + return void replyAndDelete(interaction, `${classEmoji} ${resolvedChar.level} ${resolvedChar.name}${borrowNote}`, true);
82 + }
83 +
84 + // Check if target character is already in the active poll by another player
85 + const slot = [...polls.keys()][0];
86 + if (slot !== undefined) {
87 + const state = polls.get(slot)!;
88 + for (const [id, entry] of state.yes.entries()) {
89 + const isOwnEntry = id === interaction.user.id || id === `impersonated:${userKey}`;
90 + if (!isOwnEntry && entry.characterName === resolvedChar.name && entry.userKey !== userKey) {
91 + const slotHour = state.slot;
92 + const charDisplay = format.char(resolvedChar);
93 + const isOwner = getCharacters(userKey).some((c) => c.name === resolvedChar.name);
94 + if (isOwner) {
95 + return void replyAndDelete(interaction,
96 + `⚠️ ${charDisplay} is already scheduled for TG at ${slotHour}:00. Vote with your character and use the reclaim button that appears.`,
97 + true
98 + );
99 + }
100 + return void replyAndDelete(interaction,
101 + `❌ ${charDisplay} is already scheduled for TG at ${slotHour}:00. Pick a different character.`,
102 + true
103 + );
104 + }
105 + }
106 + }
107 +
108 + // Now actually switch
109 + if (borrowedFrom) {
110 + setSessionBorrow(userKey, borrowedFrom, resolvedChar.name);
111 + setPersistentPreference(userKey, borrowedFrom, resolvedChar.name);
112 + } else {
113 + setActiveCharacter(userKey, charName);
114 + clearPersistentPreference(userKey);
115 + clearSessionBorrowForUser(userKey);
116 + resolvedChar = getActiveCharacter(userKey);
117 + }
118 +
119 + // Update poll embed if user has already voted
120 + if (slot !== undefined) {
121 + const state = polls.get(slot)!;
122 + const voteId = findVoteIdInPoll(state, userKey);
123 +
124 + if (voteId && (state.yes.has(voteId) || state.no.has(voteId))) {
125 + const updateEntry = (map: Map<string, any>) => {
126 + const entry = map.get(voteId);
127 + if (entry) {
128 + entry.characterName = resolvedChar.name;
129 + entry.characterClass = resolvedChar.class;
130 + entry.characterLevel = resolvedChar.level;
131 + entry.characterNation = resolvedChar.nation;
132 + entry.borrowedFrom = borrowedFrom ?? undefined;
133 + }
134 + };
135 + updateEntry(state.yes);
136 + updateEntry(state.no);
137 +
138 + const channel = await interaction.client.channels.fetch(cfg("pollChannelId")) as TextChannel;
139 + await updatePollMessage(channel, slot);
140 + }
141 + }
142 +
143 + const classEmoji = getClassEmoji(resolvedChar.class) || resolvedChar.class;
144 + const borrowNote = borrowedFrom ? ` *(shared by ${borrowedFrom})*` : "";
145 + return void replyAndDelete(interaction, `🔄 ${classEmoji} ${resolvedChar.level} ${resolvedChar.name}${borrowNote}`, true);
146 + }
Próximo Anterior