Spaces:
Paused
Paused
File size: 4,855 Bytes
ee826ee | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | /**
* polls.js — Encuestas y Votaciones v1.1
* FIX: restoreActivePolls maneja mensajes eliminados sin crashear
*/
import { EmbedBuilder } from 'discord.js';
import * as db from './db.js';
const activePoll = new Map();
const EMOJIS = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣'];
export async function createPoll(channel, question, options, durationMin = 60) {
if (options.length < 2 || options.length > 5) throw new Error('Entre 2 y 5 opciones');
const embed = new EmbedBuilder()
.setTitle(`🗳️ ${question}`)
.setColor(0x5865f2)
.setDescription(options.map((o, i) => `${EMOJIS[i]} ${o}`).join('\n'))
.setFooter({ text: `Termina en ${durationMin} minutos • Zelin` })
.setTimestamp(new Date(Date.now() + durationMin * 60000));
const msg = await channel.send({ embeds: [embed] });
for (let i = 0; i < options.length; i++) {
await msg.react(EMOJIS[i]);
}
const pollData = {
messageId: msg.id,
channelId: channel.id,
question,
options,
startedAt: Date.now(),
endsAt : Date.now() + durationMin * 60000,
};
activePoll.set(channel.id, pollData);
await db.memSet(`poll.active.${channel.id}`, pollData, 'polls').catch(() => {});
setTimeout(() => closePoll(channel, msg.id, pollData), durationMin * 60000);
return msg;
}
async function closePoll(channel, messageId, pollData) {
try {
// FIX: manejar mensaje eliminado
const msg = await channel.messages.fetch(messageId).catch(() => null);
if (!msg) {
// Mensaje eliminado — limpiar sin crashear
console.warn(`[Poll] Mensaje ${messageId} no encontrado (posiblemente eliminado), cerrando encuesta`);
activePoll.delete(pollData.channelId);
await db.db.execute({ sql: 'DELETE FROM zelin_memory WHERE key = ?', args: [`poll.active.${pollData.channelId}`] }).catch(() => {});
await channel.send(`La encuesta **"${pollData.question}"** finalizó pero su mensaje fue eliminado.`).catch(() => {});
return;
}
const results = await Promise.all(
pollData.options.map(async (opt, i) => {
const reaction = msg.reactions.cache.get(EMOJIS[i]);
const count = (reaction?.count ?? 1) - 1;
return { option: opt, votes: count };
})
);
results.sort((a, b) => b.votes - a.votes);
const winner = results[0];
const embed = new EmbedBuilder()
.setTitle(`🏆 Resultados: ${pollData.question}`)
.setColor(0x00ff00)
.setDescription(results.map((r, i) =>
`${i === 0 ? '🥇' : i === 1 ? '🥈' : '🥉'} ${r.option}: **${r.votes} votos**`
).join('\n'))
.setFooter({ text: `Ganador: ${winner.option} • Zelin` })
.setTimestamp();
await channel.send({ embeds: [embed] });
activePoll.delete(pollData.channelId);
await db.db.execute({ sql: 'DELETE FROM zelin_memory WHERE key = ?', args: [`poll.active.${pollData.channelId}`] }).catch(() => {});
} catch (err) {
console.error('[Poll] Error cerrando encuesta:', err.message);
// Limpiar de todas formas para no dejar basura
activePoll.delete(pollData.channelId);
}
}
export function getActivePoll(channelId) {
return activePoll.get(channelId) ?? null;
}
export async function restoreActivePolls(client) {
try {
const r = await db.db.execute({
sql : "SELECT key, value FROM zelin_memory WHERE category = 'polls' AND key LIKE 'poll.active.%'",
args: [],
});
for (const row of r.rows) {
try {
const pollData = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;
const remaining = pollData.endsAt - Date.now();
// FIX: obtener canal con manejo de errores
const ch = client.channels.cache.get(pollData.channelId);
if (!ch) {
// Canal no disponible — limpiar de DB
await db.db.execute({ sql: 'DELETE FROM zelin_memory WHERE key = ?', args: [row.key] }).catch(() => {});
continue;
}
if (remaining <= 0) {
// Ya expiró
await closePoll(ch, pollData.messageId, pollData).catch(() => {});
continue;
}
activePoll.set(pollData.channelId, pollData);
setTimeout(() => closePoll(ch, pollData.messageId, pollData), remaining);
console.log(`[Polls] Encuesta restaurada en #${ch.name}, cierra en ${Math.round(remaining/60000)}min`);
} catch (e) {
console.warn('[Polls] Error restaurando encuesta:', e.message);
}
}
} catch (e) {
console.error('[Polls] Error en restoreActivePolls:', e.message);
}
}
export function isPollRequest(content) {
const lower = content.toLowerCase();
return (lower.includes('encuesta') || lower.includes('votación') || lower.includes('vota')) &&
(lower.includes('haz') || lower.includes('crea') || lower.includes('pon') || lower.includes('hacer'));
}
|