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'));
}