Ultima attività 1 month ago

Revisione b974f9751f6eb43f7da192d420ff196bee127d76

gistfile1.txt Raw
1const {
2 Client,
3 GatewayIntentBits,
4 ButtonBuilder,
5 ButtonStyle,
6 ActionRowBuilder,
7 EmbedBuilder,
8 } = require("discord.js");
9 const cron = require("node-cron");
10
11 // ─── Config ────────────────────────────────────────────────────────────────
12 const TOKEN = process.env.DISCORD_TOKEN; // your bot token
13 const CHANNEL_ID = process.env.CHANNEL_ID; // TG planning channel ID
14 const TG_HOUR = 20; // TG starts at 20:00
15 const POST_HOUR = 10; // bot posts at 19:00 (1h before)
16 const TIMEZONE = "Etc/GMT-2"; // GMT+2 (change to e.g. "Europe/Lisbon" if needed)
17 // ────────────────────────────────────────────────────────────────────────────
18
19 const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers] });
20
21 // vote entry: { username, votedAt, previousYesAt? }
22 let votes = { yes: new Map(), no: new Map() };
23 let currentPollMessageId = null;
24
25 function nowFormatted() {
26 return new Date().toLocaleTimeString("en-GB", {
27 timeZone: TIMEZONE,
28 hour: "2-digit",
29 minute: "2-digit",
30 });
31 }
32
33 function formatYesVoters() {
34 if (votes.yes.size === 0) return "—";
35 return [...votes.yes.values()]
36 .map((v) => `${v.username} · ${v.votedAt}`)
37 .join("\n");
38 }
39
40 function formatNoVoters() {
41 if (votes.no.size === 0) return "—";
42 return [...votes.no.values()]
43 .map((v) => {
44 const switched = v.previousYesAt
45 ? ` (was Yes at ${v.previousYesAt})`
46 : "";
47 return `${v.username} · ${v.votedAt}${switched}`;
48 })
49 .join("\n");
50 }
51
52 function buildEmbed() {
53 return new EmbedBuilder()
54 .setTitle("⚔️ TG — Tonight?")
55 .setDescription(
56 `Is **TG happening tonight at ${TG_HOUR}:00**?\n`
57 )
58 .setColor(0xe8a317)
59 .addFields(
60 {
61 name: `✅ Yes (${votes.yes.size})`,
62 value: formatYesVoters(),
63 inline: true,
64 },
65 {
66 name: `❌ No (${votes.no.size})`,
67 value: formatNoVoters(),
68 inline: true,
69 }
70 )
71 .setFooter({ text: "Vote updates live • Anyone can vote • You can switch your vote" })
72 .setTimestamp();
73 }
74
75 function buildButtons() {
76 const yesBtn = new ButtonBuilder()
77 .setCustomId("tg_yes")
78 .setLabel("✅ Yes")
79 .setStyle(ButtonStyle.Success);
80
81 const noBtn = new ButtonBuilder()
82 .setCustomId("tg_no")
83 .setLabel("❌ No")
84 .setStyle(ButtonStyle.Danger);
85
86 return new ActionRowBuilder().addComponents(yesBtn, noBtn);
87 }
88
89 async function updatePollMessage(channel) {
90 if (!currentPollMessageId) return;
91 try {
92 const msg = await channel.messages.fetch(currentPollMessageId);
93 await msg.edit({ embeds: [buildEmbed()], components: [buildButtons()] });
94 } catch (err) {
95 console.error("Failed to update poll message:", err);
96 }
97 }
98
99 async function postDailyPoll() {
100 try {
101 const channel = await client.channels.fetch(CHANNEL_ID);
102 if (!channel) return console.error("Channel not found.");
103
104 votes = { yes: new Map(), no: new Map() };
105 currentPollMessageId = null;
106
107 const msg = await channel.send({
108 embeds: [buildEmbed()],
109 components: [buildButtons()],
110 });
111 currentPollMessageId = msg.id;
112 console.log(`[${new Date().toISOString()}] Poll posted.`);
113 } catch (err) {
114 console.error("Failed to post poll:", err);
115 }
116 }
117
118 client.on("interactionCreate", async (interaction) => {
119 if (!interaction.isButton()) return;
120 if (!["tg_yes", "tg_no"].includes(interaction.customId)) return;
121
122 const userId = interaction.user.id;
123 const member = await interaction.guild.members.fetch(userId);
124 const username = member.nickname ?? interaction.user.username;
125 const votedYes = interaction.customId === "tg_yes";
126 const now = nowFormatted();
127
128 // Ignore if already voted the same option
129 if (votedYes && votes.yes.has(userId)) return interaction.deferUpdate();
130 if (!votedYes && votes.no.has(userId)) return interaction.deferUpdate();
131
132 if (votedYes) {
133 // Switching from No → Yes
134 votes.no.delete(userId);
135 votes.yes.set(userId, { username, votedAt: now });
136 } else {
137 // Switching from Yes → No — track when they were Yes
138 const previousYes = votes.yes.get(userId);
139 votes.yes.delete(userId);
140 votes.no.set(userId, {
141 username,
142 votedAt: now,
143 previousYesAt: previousYes ? previousYes.votedAt : null,
144 });
145 }
146
147 await interaction.reply({
148 content: votedYes
149 ? `✅ You voted **Yes** for TG tonight!`
150 : `❌ You voted **No** for TG tonight, you roaching out?`,
151 ephemeral: true,
152 });
153
154 const channel = await client.channels.fetch(CHANNEL_ID);
155 await updatePollMessage(channel);
156 });
157
158 client.once("clientReady", () => {
159 console.log(`Logged in as ${client.user.tag}`);
160
161 cron.schedule(`0 ${POST_HOUR} * * *`, () => postDailyPoll());
162 postDailyPoll()
163 console.log(`Poll scheduled daily at ${POST_HOUR}:00.`);
164 });
165
166 client.login(TOKEN);