Refactor done
This commit is contained in:
parent
88d985bb1f
commit
2d0a6626a8
@ -1,8 +1,9 @@
|
||||
const Embed = require("../functions/embed");
|
||||
const Collector = require("../functions/messageCollector");
|
||||
const EditCollector = require("../functions/messageEdit");
|
||||
const CommandDB = require('../models/commands');
|
||||
const { isJson } = require('../functions/randomStr');
|
||||
const path = require("path");
|
||||
const Embed = require(path.join(__dirname, "../functions/embed"));
|
||||
const Collector = require(path.join(__dirname, "../functions/messageCollector"));
|
||||
const EditCollector = require(path.join(__dirname, "../functions/messageEdit"));
|
||||
const CommandDB = require(path.join(__dirname, "../models/commands"));
|
||||
const { isJson } = require(path.join(__dirname, "../functions/randomStr"));
|
||||
|
||||
module.exports = async (client, message) => {
|
||||
// Early return checks
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const PollDB = require("../models/polls");
|
||||
const Giveaways = require("../models/giveaways");
|
||||
const GuildDB = require("../models/guilds");
|
||||
const path = require("path");
|
||||
const PollDB = require(path.join(__dirname, "../models/polls"));
|
||||
const Giveaways = require(path.join(__dirname, "../models/giveaways"));
|
||||
const GuildDB = require(path.join(__dirname, "../models/guilds"));
|
||||
module.exports = async (client, msg) => {
|
||||
const paginateCheck = client.paginate.get(msg.authorId);
|
||||
const pollCheck = client.polls.get(msg.id);
|
||||
|
||||
@ -1,220 +1,362 @@
|
||||
const Embed = require("../functions/embed");
|
||||
const PollDB = require("../models/polls");
|
||||
const Giveaways = require("../models/giveaways");
|
||||
const emojis = [{ name: "1️⃣", id: 0 }, { name: "2️⃣", id: 1 }, { name: "3️⃣", id: 2 }, { name: "4️⃣", id: 3 }, { name: "5️⃣", id: 4 }, { name: "6️⃣", id: 5 }, { name: "7️⃣", id: 6 }, { name: "8️⃣", id: 7 }, { name: "9️⃣", id: 8 }, { name: "🔟", id: 9 }, { name: "🛑", id: "stop" }]
|
||||
const colors = /^([A-Z0-9]+)/;
|
||||
const path = require("path");
|
||||
const Embed = require(path.join(__dirname, "../functions/embed"));
|
||||
const PollDB = require(path.join(__dirname, "../models/polls"));
|
||||
const Giveaways = require(path.join(__dirname, "../models/giveaways"));
|
||||
|
||||
module.exports = async (client, message, userId, emojiId) => {
|
||||
const paginateCheck = client.paginate.get(userId);
|
||||
const pollCheck = client.polls.get(message.id);
|
||||
const collector = client.messageCollector.get(userId);
|
||||
const editCollector = client.messageEdit.get(userId);
|
||||
// Constants
|
||||
const EMOJIS = [
|
||||
{ name: "1️⃣", id: 0 }, { name: "2️⃣", id: 1 }, { name: "3️⃣", id: 2 },
|
||||
{ name: "4️⃣", id: 3 }, { name: "5️⃣", id: 4 }, { name: "6️⃣", id: 5 },
|
||||
{ name: "7️⃣", id: 6 }, { name: "8️⃣", id: 7 }, { name: "9️⃣", id: 8 },
|
||||
{ name: "🔟", id: 9 }, { name: "🛑", id: "stop" }
|
||||
];
|
||||
const COLORS_REGEX = /^([A-Z0-9]+)/;
|
||||
|
||||
if (collector && collector.messageId === message.id || collector?.oldMessageId && collector?.oldMessageId === message.id && collector.channelId === message.channelId) {
|
||||
if (emojiId === client.config.emojis.check) {
|
||||
if (collector.roles.length === 0) {
|
||||
const db = await client.database.getGuild(message.server.id);
|
||||
message.delete().catch(() => { });
|
||||
client.messages.get(collector?.oldMessageId)?.delete().catch(() => { })
|
||||
const reactions = [...collector.rolesDone.map(e => e.emoji)];
|
||||
message.channel.sendMessage(collector.type === "content" ? { content: `${message.content}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`, interactions: [reactions] } : { embeds: [new Embed().setColor("#A52F05").setDescription(`${client.messages.get(message.id).embeds[0].description}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`)], interactions: [reactions] }).then(async (msg) => {
|
||||
db.roles.push({ msgId: msg.id, chanId: msg.channelId, roles: [...collector.rolesDone] });
|
||||
await client.database.updateGuild(msg.server.id, { roles: db.roles });
|
||||
});
|
||||
|
||||
clearTimeout(client.messageCollector.get(userId).timeout);
|
||||
return client.messageCollector.delete(userId);
|
||||
} else return;
|
||||
} else if (emojiId === client.config.emojis.cross) {
|
||||
/**
|
||||
* Handles message collector reactions
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @param {Object} collector - The message collector
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleMessageCollector(client, message, userId, emojiId, collector) {
|
||||
if (emojiId === client.config.emojis.check) {
|
||||
if (collector.roles.length === 0) {
|
||||
const db = await client.database.getGuild(message.server.id);
|
||||
client.messageCollector.delete(userId);
|
||||
return message.reply({ embeds: [new Embed().setColor("#A52F05").setDescription(client.translate.get(db.language, "Events.messageReactionAdd.deleteCollector"))] },);
|
||||
} else {
|
||||
if (collector.roles.length === 0) return;
|
||||
let emote;
|
||||
if (colors.test(emojiId)) emote = `:${emojiId}:`;
|
||||
else if (!colors.test(emojiId)) emote = emojiId
|
||||
collector.rolesDone.push({ emoji: emojiId, role: collector.roles[0][0], name: collector.roles[0][1].name, color: collector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : collector.roles[0][1].colour });
|
||||
message.edit(collector.type === "content" ? { content: message.content.replace(`{role:${collector.regex[0]}}`, `${emote} $\\text{\\textcolor{${collector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : collector.roles[0][1].colour}}{${collector.roles[0][1].name}}}$`) } : { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${collector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${collector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : collector.roles[0][1].colour}}{${collector.roles[0][1].name}}}$`))] })
|
||||
collector.roles.shift();
|
||||
return collector.regex.shift();
|
||||
}
|
||||
} else if (editCollector && editCollector.messageId === message.id || editCollector?.botMessage && editCollector?.botMessage === message.id && editCollector.channelId === message.channelId) {
|
||||
if (emojiId === client.config.emojis.check) {
|
||||
if (editCollector.roles.length === 0) {
|
||||
const db = await client.database.getGuild(message.server.id);
|
||||
message.delete().catch(() => { });
|
||||
client.messages.get(editCollector?.oldMessageId)?.delete().catch(() => { })
|
||||
client.messages.get(editCollector?.botMessage)?.delete().catch(() => { })
|
||||
const reactions = [...editCollector.rolesDone.map(e => e.emoji)];
|
||||
message.channel.sendMessage(editCollector.type === "content" ? { content: `${message.content}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`, interactions: [reactions] } : { embeds: [new Embed().setColor("#A52F05").setDescription(`${client.messages.get(message.id).embeds[0].description}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`)], interactions: [reactions] }).then(async (msg) => {
|
||||
db.roles.push({ msgId: msg.id, chanId: msg.channelId, roles: [...editCollector.rolesDone] });
|
||||
await client.database.updateGuild(msg.server.id, { roles: db.roles.filter(e => e.msgId !== editCollector.oldMessageId) });
|
||||
});
|
||||
await Promise.all([
|
||||
message.delete().catch(() => {}),
|
||||
client.messages.get(collector?.oldMessageId)?.delete().catch(() => {})
|
||||
]);
|
||||
|
||||
clearTimeout(client.messageEdit.get(userId).timeout);
|
||||
return client.messageEdit.delete(userId);
|
||||
} else return;
|
||||
} else if (emojiId === client.config.emojis.cross) {
|
||||
const db = await client.database.getGuild(message.server.id);
|
||||
client.messageEdit.delete(userId);
|
||||
return message.reply({ embeds: [new Embed().setColor("#A52F05").setDescription(client.translate.get(db.language, "Events.messageReactionAdd.deleteCollector"))] },);
|
||||
} else {
|
||||
if (editCollector.roles.length === 0) return;
|
||||
let emote;
|
||||
if (colors.test(emojiId)) emote = `:${emojiId}:`;
|
||||
else if (!colors.test(emojiId)) emote = emojiId
|
||||
editCollector.rolesDone.push({ emoji: emojiId, role: editCollector.roles[0][0], name: editCollector.roles[0][1].name, color: editCollector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : editCollector.roles[0][1].colour });
|
||||
message.edit(editCollector.type === "content" ? { content: message.content.replace(`{role:${editCollector.regex[0]}}`, `${emote} $\\text{\\textcolor{${editCollector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : editCollector.roles[0][1].colour}}{${editCollector.roles[0][1].name}}}$`) } : { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${editCollector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${editCollector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : editCollector.roles[0][1].colour}}{${editCollector.roles[0][1].name}}}$`))] })
|
||||
editCollector.roles.shift();
|
||||
return editCollector.regex.shift();
|
||||
const reactions = [...collector.rolesDone.map(e => e.emoji)];
|
||||
const content = collector.type === "content"
|
||||
? { content: `${message.content}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`, interactions: [reactions] }
|
||||
: { embeds: [new Embed().setColor("#A52F05").setDescription(`${client.messages.get(message.id).embeds[0].description}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`)], interactions: [reactions] };
|
||||
|
||||
const msg = await message.channel.sendMessage(content);
|
||||
db.roles.push({ msgId: msg.id, chanId: msg.channelId, roles: [...collector.rolesDone] });
|
||||
await client.database.updateGuild(msg.server.id, { roles: db.roles });
|
||||
|
||||
clearTimeout(client.messageCollector.get(userId).timeout);
|
||||
return client.messageCollector.delete(userId);
|
||||
}
|
||||
} else if (paginateCheck && paginateCheck.message == message.id) {
|
||||
let pages = paginateCheck.pages;
|
||||
let page = paginateCheck.page;
|
||||
switch (emojiId) {
|
||||
case "⏪":
|
||||
if (page !== 0) {
|
||||
message.edit({
|
||||
embeds: [pages[0]]
|
||||
}).catch(() => { });
|
||||
return paginateCheck.page = 0
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
case "⬅️":
|
||||
if (pages[page - 1]) {
|
||||
message.edit({
|
||||
embeds: [pages[--page]]
|
||||
}).catch(() => { });
|
||||
return paginateCheck.page = paginateCheck.page - 1
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
case "➡️":
|
||||
if (pages[page + 1]) {
|
||||
message.edit({
|
||||
embeds: [pages[++page]]
|
||||
}).catch(() => { });
|
||||
return paginateCheck.page = paginateCheck.page + 1
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
case "⏩":
|
||||
if (page !== pages.length) {
|
||||
message.edit({
|
||||
embeds: [pages[pages.length - 1]]
|
||||
}).catch(() => { });
|
||||
return paginateCheck.page = pages.length - 1
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (emojiId === client.config.emojis.cross) {
|
||||
const db = await client.database.getGuild(message.server.id);
|
||||
client.messageCollector.delete(userId);
|
||||
return message.reply({
|
||||
embeds: [new Embed().setColor("#A52F05").setDescription(client.translate.get(db.language, "Events.messageReactionAdd.deleteCollector"))]
|
||||
});
|
||||
}
|
||||
|
||||
if (collector.roles.length === 0) return;
|
||||
|
||||
const emote = COLORS_REGEX.test(emojiId) ? `:${emojiId}:` : emojiId;
|
||||
const role = collector.roles[0];
|
||||
const roleColor = role[1].colour?.includes("linear-gradient") ? '#000000' : role[1].colour;
|
||||
|
||||
collector.rolesDone.push({
|
||||
emoji: emojiId,
|
||||
role: role[0],
|
||||
name: role[1].name,
|
||||
color: roleColor
|
||||
});
|
||||
|
||||
const content = collector.type === "content"
|
||||
? { content: message.content.replace(`{role:${collector.regex[0]}}`, `${emote} $\\text{\\textcolor{${roleColor}}{${role[1].name}}}$`) }
|
||||
: { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${collector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${roleColor}}{${role[1].name}}}$`))] };
|
||||
|
||||
await message.edit(content);
|
||||
collector.roles.shift();
|
||||
return collector.regex.shift();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles pagination reactions
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} paginateCheck - The pagination check object
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handlePagination(client, message, paginateCheck, emojiId) {
|
||||
const { pages, page } = paginateCheck;
|
||||
let newPage = page;
|
||||
|
||||
switch (emojiId) {
|
||||
case "⏪":
|
||||
if (page !== 0) {
|
||||
await message.edit({ embeds: [pages[0]] }).catch(() => {});
|
||||
newPage = 0;
|
||||
}
|
||||
break;
|
||||
case "⬅️":
|
||||
if (pages[page - 1]) {
|
||||
await message.edit({ embeds: [pages[--newPage]] }).catch(() => {});
|
||||
}
|
||||
break;
|
||||
case "➡️":
|
||||
if (pages[page + 1]) {
|
||||
await message.edit({ embeds: [pages[++newPage]] }).catch(() => {});
|
||||
}
|
||||
break;
|
||||
case "⏩":
|
||||
if (page !== pages.length) {
|
||||
await message.edit({ embeds: [pages[pages.length - 1]] }).catch(() => {});
|
||||
newPage = pages.length - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (newPage !== page) {
|
||||
paginateCheck.page = newPage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles poll reactions
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} pollCheck - The poll check object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handlePoll(client, message, pollCheck, userId, emojiId) {
|
||||
const convert = EMOJIS.findIndex(e => e.name === emojiId);
|
||||
|
||||
if (convert === 10 && pollCheck.owner === userId) {
|
||||
await PollDB.findOneAndDelete({ messageId: message.id });
|
||||
await pollCheck.poll.update();
|
||||
|
||||
const tooMuch = [];
|
||||
if (pollCheck.poll.options.description.length > 80) {
|
||||
tooMuch.push(`**${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.title")}**: ${pollCheck.poll.options.description}`);
|
||||
}
|
||||
} else if (pollCheck) {
|
||||
let tooMuch = [];
|
||||
if (pollCheck.poll.options.description.length > 80) tooMuch.push(`**${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.title")}**: ${pollCheck.poll.options.description}`)
|
||||
pollCheck.poll.voteOptions.name.filter(e => e).forEach((e, i) => {
|
||||
i++
|
||||
if (e.length > 70) {
|
||||
tooMuch.push(`**${i}.** ${e}`)
|
||||
tooMuch.push(`**${i + 1}.** ${e}`);
|
||||
}
|
||||
});
|
||||
|
||||
let convert = emojis.findIndex(e => e.name === emojiId);
|
||||
if (convert === 10 && pollCheck.owner === userId) {
|
||||
await PollDB.findOneAndDelete({ messageId: message.id });
|
||||
await pollCheck.poll.update();
|
||||
message.edit({ content: `${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.owner")} (<@${pollCheck.owner}>) ${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.end")}:`, embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`)).setColor("#F24646")] }).catch(() => { });
|
||||
return client.polls.delete(message.id);
|
||||
} else if (convert === 0 && convert !== 10 || convert !== -1 && convert !== 10) {
|
||||
if (client.reactions.get(userId)) return client.users.get(userId)?.openDM().then(dm => dm.sendMessage(client.translate.get(pollCheck.lang, "Events.messageReactionAdd.tooFast"))).catch(() => { });
|
||||
if (pollCheck.users.includes(userId)) return;
|
||||
pollCheck.users.push(userId);
|
||||
const user = (client.users.get(userId)) || await client.users.fetch(userId);
|
||||
// console.log(user.avatar.id ? user.avatar.createFileURL() : 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar')
|
||||
await pollCheck.poll.addVote(convert, userId, 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar', message.id);
|
||||
message.edit({ embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`)).setColor("#A52F05")] }).catch(() => { });
|
||||
client.reactions.set(userId, Date.now() + 3000)
|
||||
return setTimeout(() => client.reactions.delete(userId), 3000)
|
||||
} else return;
|
||||
} else {
|
||||
const db = await Giveaways.findOne({ messageId: message.id });
|
||||
if (db) {
|
||||
if (emojiId === client.config.emojis.confetti && db && !db.ended) {
|
||||
if (client.reactions.get(userId)) return;
|
||||
if (db.users.find(u => u.userID === userId)) return;
|
||||
db.users.push({ userID: userId });
|
||||
db.picking.push({ userID: userId });
|
||||
db.save();
|
||||
await message.edit({
|
||||
content: `${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.owner")} (<@${pollCheck.owner}>) ${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.end")}:`,
|
||||
embeds: [new Embed()
|
||||
.setDescription(tooMuch.length > 0 ? tooMuch.join("\n") : null)
|
||||
.setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`))
|
||||
.setColor("#F24646")]
|
||||
}).catch(() => {});
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000)
|
||||
setTimeout(() => client.reactions.delete(userId), 3000)
|
||||
return client.polls.delete(message.id);
|
||||
}
|
||||
|
||||
client.users.get(userId)?.openDM().then(dm => dm.sendMessage(`${client.translate.get(db.lang, "Events.messageReactionAdd.joined")} [${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})!\n${client.translate.get(db.lang, "Events.messageReactionAdd.joined2")} **${db.users.length}** ${client.translate.get(db.lang, "Events.messageReactionAdd.joined3")}`)).catch(() => { });
|
||||
} else if (emojiId === client.config.emojis.stop && db && db.owner === userId && !db.ended) {
|
||||
let endDate = Date.now();
|
||||
|
||||
if (db.users.length === 0) {
|
||||
const noUsers = new Embed()
|
||||
.setColor("#A52F05")
|
||||
.setTitle(client.translate.get(db.lang, "Events.messageReactionAdd.giveaway"))
|
||||
.setDescription(`${client.translate.get(db.lang, "Events.messageReactionAdd.owner")} (<@${userId}>) ${client.translate.get(db.lang, "Events.messageReactionAdd.early")}\n${client.translate.get(db.lang, "Events.messageReactionAdd.endNone")}!\n\n${client.translate.get(db.lang, "Events.messageReactionAdd.ended")}: <t:${Math.floor((endDate) / 1000)}:R>\n${client.translate.get(db.lang, "Events.messageReactionAdd.prize")}: ${db.prize}\n${client.translate.get(db.lang, "Events.messageReactionAdd.winnersNone")}${db.requirement ? `\n${client.translate.get(db.lang, "Events.messageReactionAdd.reqs")}: ${db.requirement}` : ``}`)
|
||||
|
||||
await db.updateOne({ ended: true, endDate: endDate })
|
||||
await db.save();
|
||||
return await client.api.patch(`/channels/${db.channelId}/messages/${db.messageId}`, { "embeds": [noUsers] });
|
||||
}
|
||||
|
||||
for (let i = 0; i < db.winners; i++) {
|
||||
let winner = db.picking[Math.floor(Math.random() * db.picking.length)];
|
||||
if (winner) {
|
||||
const filtered = db.picking.filter(object => object.userID != winner.userID)
|
||||
db.picking = filtered;
|
||||
db.pickedWinners.push({ id: winner.userID })
|
||||
}
|
||||
}
|
||||
|
||||
await db.updateOne({ ended: true, endDate: endDate })
|
||||
await db.save();
|
||||
|
||||
const noUsers = new Embed()
|
||||
.setColor("#A52F05")
|
||||
.setTitle(client.translate.get(db.lang, "Events.messageReactionAdd.giveaway"))
|
||||
.setDescription(`${client.translate.get(db.lang, "Events.messageReactionAdd.owner")} (<@${userId}>) ${client.translate.get(db.lang, "Events.messageReactionAdd.early")}\n${client.translate.get(db.lang, "Events.messageReactionAdd.partici")}: ${db.users.length}\n\n${client.translate.get(db.lang, "Events.messageReactionAdd.ended")}: <t:${Math.floor((endDate) / 1000)}:R>\n${client.translate.get(db.lang, "Events.messageReactionAdd.prize")}: ${db.prize}\n${client.translate.get(db.lang, "Events.messageReactionAdd.winners")}: ${db.pickedWinners.length > 0 ? db.pickedWinners.map(w => `<@${w.id}>`).join(", ") : client.translate.get(db.lang, "Events.messageReactionAdd.none")}${db.requirement ? `\n${client.translate.get(db.lang, "Events.messageReactionAdd.reqs")}: ${db.requirement}` : ``}`)
|
||||
|
||||
message.edit({ embeds: [noUsers] }).catch(() => { });
|
||||
await client.api.post(`/channels/${db.channelId}/messages`, { "content": `${client.translate.get(db.lang, "Events.messageReactionAdd.congrats")} ${db.pickedWinners.map(w => `<@${w.id}>`).join(", ")}! ${client.translate.get(db.lang, "Events.messageReactionAdd.youWon")} **[${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})**!` }).catch(() => { });
|
||||
client.reactions.set(userId, Date.now() + 3000)
|
||||
setTimeout(() => client.reactions.delete(userId), 3000)
|
||||
}
|
||||
} else {
|
||||
const db2 = await client.database.getGuild(message.server.id, true)
|
||||
if (db2 && db2.roles.find(e => e.msgId === message.id) && db2.roles.find(e => e.roles.find(e => e.emoji === emojiId))) {
|
||||
if (client.reactions.get(userId)) return;
|
||||
|
||||
const roles = [];
|
||||
db2.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
|
||||
const role = roles.find(e => e.emoji === emojiId);
|
||||
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(userId);
|
||||
if (!member) return;
|
||||
|
||||
let error = false;
|
||||
let dataRoles = [];
|
||||
if (member.roles) member.roles.map(e => dataRoles.push(e));
|
||||
if (dataRoles.includes(role.role)) return;
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
|
||||
dataRoles.push(role.role);
|
||||
await member.edit({ roles: dataRoles }).catch(() => { error = true })
|
||||
|
||||
if (error && db2.dm) {
|
||||
member?.user?.openDM().then((dm) => { dm.sendMessage(`${client.translate.get(db2.language, "Events.messageReactionAdd.noPerms").replace("{role}", `**${role.name}**`)}!`) }).catch(() => { });
|
||||
} else if (db2.dm) {
|
||||
member?.user?.openDM().then((dm) => { dm.sendMessage(`${client.translate.get(db2.language, "Events.messageReactionAdd.success").replace("{role}", `**${role.name}**`)}!`) }).catch(() => { });
|
||||
}
|
||||
}
|
||||
if (convert === 0 || (convert !== -1 && convert !== 10)) {
|
||||
if (client.reactions.get(userId)) {
|
||||
return client.users.get(userId)?.openDM()
|
||||
.then(dm => dm.sendMessage(client.translate.get(pollCheck.lang, "Events.messageReactionAdd.tooFast")))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
if (pollCheck.users.includes(userId)) return;
|
||||
|
||||
pollCheck.users.push(userId);
|
||||
const user = client.users.get(userId) || await client.users.fetch(userId);
|
||||
await pollCheck.poll.addVote(convert, userId, 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar', message.id);
|
||||
|
||||
const tooMuch = [];
|
||||
if (pollCheck.poll.options.description.length > 80) {
|
||||
tooMuch.push(`**${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.title")}**: ${pollCheck.poll.options.description}`);
|
||||
}
|
||||
pollCheck.poll.voteOptions.name.filter(e => e).forEach((e, i) => {
|
||||
if (e.length > 70) {
|
||||
tooMuch.push(`**${i + 1}.** ${e}`);
|
||||
}
|
||||
});
|
||||
|
||||
await message.edit({
|
||||
embeds: [new Embed()
|
||||
.setDescription(tooMuch.length > 0 ? tooMuch.join("\n") : null)
|
||||
.setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`))
|
||||
.setColor("#A52F05")]
|
||||
}).catch(() => {});
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles giveaway reactions
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} db - The giveaway database object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleGiveaway(client, message, db, userId, emojiId) {
|
||||
if (emojiId === client.config.emojis.confetti && !db.ended) {
|
||||
if (client.reactions.get(userId) || db.users.find(u => u.userID === userId)) return;
|
||||
|
||||
db.users.push({ userID: userId });
|
||||
db.picking.push({ userID: userId });
|
||||
await db.save();
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
|
||||
await client.users.get(userId)?.openDM()
|
||||
.then(dm => dm.sendMessage(
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.joined")} [${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})!\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.joined2")} **${db.users.length}** ${client.translate.get(db.lang, "Events.messageReactionAdd.joined3")}`
|
||||
))
|
||||
.catch(() => {});
|
||||
} else if (emojiId === client.config.emojis.stop && db.owner === userId && !db.ended) {
|
||||
const endDate = Date.now();
|
||||
|
||||
if (db.users.length === 0) {
|
||||
const noUsers = new Embed()
|
||||
.setColor("#A52F05")
|
||||
.setTitle(client.translate.get(db.lang, "Events.messageReactionAdd.giveaway"))
|
||||
.setDescription(
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.owner")} (<@${userId}>) ${client.translate.get(db.lang, "Events.messageReactionAdd.early")}\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.endNone")}!\n\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.ended")}: <t:${Math.floor(endDate / 1000)}:R>\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.prize")}: ${db.prize}\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.winnersNone")}` +
|
||||
(db.requirement ? `\n${client.translate.get(db.lang, "Events.messageReactionAdd.reqs")}: ${db.requirement}` : ``)
|
||||
);
|
||||
|
||||
await db.updateOne({ ended: true, endDate });
|
||||
await db.save();
|
||||
return await client.api.patch(`/channels/${db.channelId}/messages/${db.messageId}`, { embeds: [noUsers] });
|
||||
}
|
||||
|
||||
for (let i = 0; i < db.winners; i++) {
|
||||
const winner = db.picking[Math.floor(Math.random() * db.picking.length)];
|
||||
if (winner) {
|
||||
db.picking = db.picking.filter(object => object.userID !== winner.userID);
|
||||
db.pickedWinners.push({ id: winner.userID });
|
||||
}
|
||||
}
|
||||
|
||||
await db.updateOne({ ended: true, endDate });
|
||||
await db.save();
|
||||
|
||||
const noUsers = new Embed()
|
||||
.setColor("#A52F05")
|
||||
.setTitle(client.translate.get(db.lang, "Events.messageReactionAdd.giveaway"))
|
||||
.setDescription(
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.owner")} (<@${userId}>) ${client.translate.get(db.lang, "Events.messageReactionAdd.early")}\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.partici")}: ${db.users.length}\n\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.ended")}: <t:${Math.floor(endDate / 1000)}:R>\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.prize")}: ${db.prize}\n` +
|
||||
`${client.translate.get(db.lang, "Events.messageReactionAdd.winners")}: ${db.pickedWinners.length > 0 ? db.pickedWinners.map(w => `<@${w.id}>`).join(", ") : client.translate.get(db.lang, "Events.messageReactionAdd.none")}` +
|
||||
(db.requirement ? `\n${client.translate.get(db.lang, "Events.messageReactionAdd.reqs")}: ${db.requirement}` : ``)
|
||||
);
|
||||
|
||||
await message.edit({ embeds: [noUsers] }).catch(() => {});
|
||||
await client.api.post(`/channels/${db.channelId}/messages`, {
|
||||
content: `${client.translate.get(db.lang, "Events.messageReactionAdd.congrats")} ${db.pickedWinners.map(w => `<@${w.id}>`).join(", ")}! ${client.translate.get(db.lang, "Events.messageReactionAdd.youWon")} **[${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})**!`
|
||||
}).catch(() => {});
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles role reactions
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} db - The guild database object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleRoleReaction(client, message, db, userId, emojiId) {
|
||||
if (client.reactions.get(userId)) return;
|
||||
|
||||
const roles = [];
|
||||
db.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
|
||||
const role = roles.find(e => e.emoji === emojiId);
|
||||
|
||||
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(userId);
|
||||
if (!member) return;
|
||||
|
||||
let error = false;
|
||||
let dataRoles = [];
|
||||
if (member.roles) member.roles.map(e => dataRoles.push(e));
|
||||
if (dataRoles.includes(role.role)) return;
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
|
||||
dataRoles.push(role.role);
|
||||
await member.edit({ roles: dataRoles }).catch(() => { error = true });
|
||||
|
||||
if (db.dm) {
|
||||
const dmMessage = error
|
||||
? client.translate.get(db.language, "Events.messageReactionAdd.noPerms").replace("{role}", `**${role.name}**`)
|
||||
: client.translate.get(db.language, "Events.messageReactionAdd.success").replace("{role}", `**${role.name}**`);
|
||||
|
||||
await member?.user?.openDM()
|
||||
.then(dm => dm.sendMessage(dmMessage))
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main event handler for message reactions
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
module.exports = async (client, message, userId, emojiId) => {
|
||||
try {
|
||||
const paginateCheck = client.paginate.get(userId);
|
||||
const pollCheck = client.polls.get(message.id);
|
||||
const collector = client.messageCollector.get(userId);
|
||||
const editCollector = client.messageEdit.get(userId);
|
||||
|
||||
// Handle message collector
|
||||
if (collector && (collector.messageId === message.id || (collector?.oldMessageId === message.id && collector.channelId === message.channelId))) {
|
||||
return await handleMessageCollector(client, message, userId, emojiId, collector);
|
||||
}
|
||||
|
||||
// Handle edit collector
|
||||
if (editCollector && (editCollector.messageId === message.id || (editCollector?.botMessage === message.id && editCollector.channelId === message.channelId))) {
|
||||
return await handleMessageCollector(client, message, userId, emojiId, editCollector);
|
||||
}
|
||||
|
||||
// Handle pagination
|
||||
if (paginateCheck && paginateCheck.message === message.id) {
|
||||
return await handlePagination(client, message, paginateCheck, emojiId);
|
||||
}
|
||||
|
||||
// Handle poll
|
||||
if (pollCheck) {
|
||||
return await handlePoll(client, message, pollCheck, userId, emojiId);
|
||||
}
|
||||
|
||||
// Handle giveaway
|
||||
const giveaway = await Giveaways.findOne({ messageId: message.id });
|
||||
if (giveaway) {
|
||||
return await handleGiveaway(client, message, giveaway, userId, emojiId);
|
||||
}
|
||||
|
||||
// Handle role reaction
|
||||
const db = await client.database.getGuild(message.server.id, true);
|
||||
if (db?.roles.find(e => e.msgId === message.id)?.roles.find(e => e.emoji === emojiId)) {
|
||||
return await handleRoleReaction(client, message, db, userId, emojiId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in messageReactionAdd:', error);
|
||||
}
|
||||
};
|
||||
@ -1,100 +1,199 @@
|
||||
const Embed = require("../functions/embed");
|
||||
const Giveaways = require("../models/giveaways");
|
||||
const emojis = [{ name: "1️⃣", id: 0 }, { name: "2️⃣", id: 1 }, { name: "3️⃣", id: 2 }, { name: "4️⃣", id: 3 }, { name: "5️⃣", id: 4 }, { name: "6️⃣", id: 5 }, { name: "7️⃣", id: 6 }, { name: "8️⃣", id: 7 }, { name: "9️⃣", id: 8 }, { name: "🔟", id: 9 }, { name: "🛑", id: "stop" }]
|
||||
const colors = /^([A-Z0-9]+)/;
|
||||
const path = require("path");
|
||||
const Embed = require(path.join(__dirname, "../functions/embed"));
|
||||
const Giveaways = require(path.join(__dirname, "../models/giveaways"));
|
||||
|
||||
module.exports = async (client, message, userId, emojiId) => {
|
||||
const pollCheck = client.polls.get(message.id);
|
||||
const collector = client.messageCollector.get(userId);
|
||||
const editCollector = client.messageEdit.get(userId);
|
||||
// Constants
|
||||
const EMOJIS = [
|
||||
{ name: "1️⃣", id: 0 }, { name: "2️⃣", id: 1 }, { name: "3️⃣", id: 2 },
|
||||
{ name: "4️⃣", id: 3 }, { name: "5️⃣", id: 4 }, { name: "6️⃣", id: 5 },
|
||||
{ name: "7️⃣", id: 6 }, { name: "8️⃣", id: 7 }, { name: "9️⃣", id: 8 },
|
||||
{ name: "🔟", id: 9 }, { name: "🛑", id: "stop" }
|
||||
];
|
||||
const COLORS_REGEX = /^([A-Z0-9]+)/;
|
||||
|
||||
if (collector && collector.messageId === message.id && collector.channelId === message.channelId) {
|
||||
const emoji = collector.rolesDone.find(e => e.emoji === emojiId);
|
||||
if (emoji) {
|
||||
collector.rolesDone = collector.rolesDone.filter(object => object.emoji != emojiId);
|
||||
collector.roles.push([emoji.role, { name: emoji.name, colour: emoji.color }]);
|
||||
collector.regex.push(emoji.name);
|
||||
/**
|
||||
* Handles message collector reaction removal
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @param {Object} collector - The message collector
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleMessageCollector(client, message, emojiId, collector) {
|
||||
const emoji = collector.rolesDone.find(e => e.emoji === emojiId);
|
||||
if (!emoji) return;
|
||||
|
||||
if (colors.test(emojiId)) emote = `:${emojiId}:`;
|
||||
else if (!colors.test(emojiId)) emote = emojiId
|
||||
return message.edit(collector.type === "content" ? { content: message.content.replace(`${emote} $\\text{\\textcolor{${emoji.color}}{${emoji.name}}}$`, `{role:${emoji.name}}`) } : { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${editCollector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${editCollector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : editCollector.roles[0][1].colour}}{${editCollector.roles[0][1].name}}}$`))] }).catch(() => { });
|
||||
collector.rolesDone = collector.rolesDone.filter(object => object.emoji !== emojiId);
|
||||
collector.roles.push([emoji.role, { name: emoji.name, colour: emoji.color }]);
|
||||
collector.regex.push(emoji.name);
|
||||
|
||||
const emote = COLORS_REGEX.test(emojiId) ? `:${emojiId}:` : emojiId;
|
||||
const content = collector.type === "content"
|
||||
? { content: message.content.replace(`${emote} $\\text{\\textcolor{${emoji.color}}{${emoji.name}}}$`, `{role:${emoji.name}}`) }
|
||||
: { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${collector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${collector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : collector.roles[0][1].colour}}{${collector.roles[0][1].name}}}$`))] };
|
||||
|
||||
return message.edit(content).catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles poll reaction removal
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} pollCheck - The poll check object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handlePoll(client, message, pollCheck, userId, emojiId) {
|
||||
if (client.reactions.get(userId)) {
|
||||
return client.users.get(userId)?.openDM()
|
||||
.then(dm => dm.sendMessage(client.translate.get(pollCheck.language, "Events.messageReactionRemove.tooFast")))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
const convert = EMOJIS.findIndex(e => e.name === emojiId);
|
||||
if (convert !== 0 && convert === 10 || convert === -1) return;
|
||||
|
||||
if (!pollCheck.users.includes(userId)) return;
|
||||
|
||||
const tooMuch = [];
|
||||
if (pollCheck.poll.options.description.length > 80) {
|
||||
tooMuch.push(`**${client.translate.get(pollCheck.language, "Events.messageReactionRemove.title")}**: ${pollCheck.poll.options.description}`);
|
||||
}
|
||||
pollCheck.poll.voteOptions.name.filter(e => e).forEach((e, i) => {
|
||||
if (e.length > 70) {
|
||||
tooMuch.push(`**${i + 1}.** ${e}`);
|
||||
}
|
||||
} else if (editCollector && editCollector.messageId === message.id && editCollector.channelId === message.channelId) {
|
||||
const emoji = editCollector.rolesDone.find(e => e.emoji === emojiId);
|
||||
if (emoji) {
|
||||
editCollector.rolesDone = editCollector.rolesDone.filter(object => object.emoji != emojiId);
|
||||
editCollector.roles.push([emoji.role, { name: emoji.name, colour: emoji.color }]);
|
||||
editCollector.regex.push(emoji.name);
|
||||
});
|
||||
|
||||
if (colors.test(emojiId)) emote = `:${emojiId}:`;
|
||||
else if (!colors.test(emojiId)) emote = emojiId
|
||||
return message.edit(editCollector.type === "content" ? { content: message.content.replace(`${emote} $\\text{\\textcolor{${emoji.color}}{${emoji.name}}}$`, `{role:${emoji.name}}`) } : { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${editCollector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${editCollector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : editCollector.roles[0][1].colour}}{${editCollector.roles[0][1].name}}}$`))] }).catch(() => { });
|
||||
}
|
||||
} else if (pollCheck) {
|
||||
if (client.reactions.get(userId)) return client.users.get(userId)?.openDM().then(dm => dm.sendMessage(client.translate.get(pollCheck.language, "Events.messageReactionRemove.tooFast"))).catch(() => { });
|
||||
let convert = emojis.findIndex(e => e.name === emojiId);
|
||||
if (convert === 0 && convert !== 10 || convert !== -1 && convert !== 10) {
|
||||
if (!pollCheck.users.includes(userId)) return;
|
||||
pollCheck.users = pollCheck.users.filter(object => object !== userId);
|
||||
const user = client.users.get(userId) || await client.users.fetch(userId);
|
||||
await pollCheck.poll.removeVote(convert, userId, 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar', message.id);
|
||||
|
||||
let tooMuch = [];
|
||||
if (pollCheck.poll.options.description.length > 80) tooMuch.push(`**${client.translate.get(pollCheck.language, "Events.messageReactionRemove.title")}**: ${pollCheck.poll.options.description}`)
|
||||
pollCheck.poll.voteOptions.name.filter(e => e).forEach((e, i) => {
|
||||
i++
|
||||
if (e.length > 70) {
|
||||
tooMuch.push(`**${i}.** ${e}`)
|
||||
}
|
||||
});
|
||||
await message.edit({
|
||||
embeds: [new Embed()
|
||||
.setDescription(tooMuch.length > 0 ? tooMuch.join("\n") : null)
|
||||
.setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`))
|
||||
.setColor("#A52F05")]
|
||||
}).catch(() => {});
|
||||
|
||||
pollCheck.users = pollCheck.users.filter(object => object != userId);
|
||||
const user = (client.users.get(userId)) || await client.users.fetch(userId);
|
||||
await pollCheck.poll.removeVote(convert, userId, 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar', message.id);
|
||||
message.edit({ embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`)).setColor("#A52F05")] }).catch(() => { });
|
||||
client.reactions.set(userId, Date.now() + 3000)
|
||||
return setTimeout(() => client.reactions.delete(userId), 3000)
|
||||
} else return;
|
||||
} else {
|
||||
if (client.reactions.get(userId)) return;
|
||||
const db = await Giveaways.findOne({ messageId: message.id });
|
||||
if (db && !db.ended) {
|
||||
if (emojiId === client.config.emojis.confetti) {
|
||||
if (!db.users.find(u => u.userID === userId)) return;
|
||||
const filtered = db.users.filter(object => object.userID != userId)
|
||||
db.users = filtered;
|
||||
const filtered2 = db.picking.filter(object => object.userID != userId)
|
||||
db.picking = filtered2;
|
||||
db.save();
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
}
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000)
|
||||
setTimeout(() => client.reactions.delete(userId), 3000)
|
||||
/**
|
||||
* Handles giveaway reaction removal
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} db - The giveaway database object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleGiveaway(client, message, db, userId, emojiId) {
|
||||
if (client.reactions.get(userId)) return;
|
||||
|
||||
client.users.get(userId)?.openDM().then(dm => dm.sendMessage(`${client.translate.get(pollCheck.language, "Events.messageReactionRemove.left")} [${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})!\n${client.translate.get(pollCheck.language, "Events.messageReactionRemove.left2")} **${db.users.length}** ${client.translate.get(pollCheck.language, "Events.messageReactionRemove.left")}!`)).catch(() => { });
|
||||
}
|
||||
} else {
|
||||
const db2 = await client.database.getGuild(message.server.id, true)
|
||||
if (db2 && db2.roles.find(e => e.msgId === message.id) && db2.roles.find(e => e.roles.find(e => e.emoji === emojiId))) {
|
||||
const roles = [];
|
||||
db2.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
|
||||
const role = roles.find(e => e.emoji === emojiId);
|
||||
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(userId);
|
||||
if (!member) return;
|
||||
if (emojiId === client.config.emojis.confetti && !db.ended) {
|
||||
if (!db.users.find(u => u.userID === userId)) return;
|
||||
|
||||
let error = false;
|
||||
let dataRoles = [];
|
||||
if (member.roles) member.roles.map(e => dataRoles.push(e));
|
||||
if (!dataRoles.includes(role.role)) return;
|
||||
db.users = db.users.filter(object => object.userID !== userId);
|
||||
db.picking = db.picking.filter(object => object.userID !== userId);
|
||||
await db.save();
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
|
||||
dataRoles = dataRoles.filter(object => object != role.role);
|
||||
await member.edit({ roles: dataRoles }).catch(() => { error = true })
|
||||
|
||||
if (error) {
|
||||
if (db2.dm) member?.user?.openDM().then((dm) => { dm.sendMessage(`${client.translate.get(db2.language, "Events.messageReactionRemove.noPerms").replace("{role}", `**${role.name}**`)}!`) }).catch(() => { });
|
||||
} else {
|
||||
if (db2.dm) member?.user?.openDM().then((dm) => { dm.sendMessage(`${client.translate.get(db2.language, "Events.messageReactionRemove.success").replace("{role}", `**${role.name}**`)}!`) }).catch(() => { });
|
||||
}
|
||||
}
|
||||
}
|
||||
await client.users.get(userId)?.openDM()
|
||||
.then(dm => dm.sendMessage(
|
||||
`${client.translate.get(db.language, "Events.messageReactionRemove.left")} [${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})!\n` +
|
||||
`${client.translate.get(db.language, "Events.messageReactionRemove.left2")} **${db.users.length}** ${client.translate.get(db.language, "Events.messageReactionRemove.left")}!`
|
||||
))
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles role reaction removal
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} db - The guild database object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleRoleReaction(client, message, db, userId, emojiId) {
|
||||
if (client.reactions.get(userId)) return;
|
||||
|
||||
const roles = [];
|
||||
db.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
|
||||
const role = roles.find(e => e.emoji === emojiId);
|
||||
|
||||
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(userId);
|
||||
if (!member) return;
|
||||
|
||||
let error = false;
|
||||
let dataRoles = [];
|
||||
if (member.roles) member.roles.map(e => dataRoles.push(e));
|
||||
if (!dataRoles.includes(role.role)) return;
|
||||
|
||||
client.reactions.set(userId, Date.now() + 3000);
|
||||
setTimeout(() => client.reactions.delete(userId), 3000);
|
||||
|
||||
dataRoles = dataRoles.filter(object => object !== role.role);
|
||||
await member.edit({ roles: dataRoles }).catch(() => { error = true });
|
||||
|
||||
if (db.dm) {
|
||||
const dmMessage = error
|
||||
? client.translate.get(db.language, "Events.messageReactionRemove.noPerms").replace("{role}", `**${role.name}**`)
|
||||
: client.translate.get(db.language, "Events.messageReactionRemove.success").replace("{role}", `**${role.name}**`);
|
||||
|
||||
await member?.user?.openDM()
|
||||
.then(dm => dm.sendMessage(dmMessage))
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main event handler for message reaction removal
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {string} userId - The user ID
|
||||
* @param {string} emojiId - The emoji ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
module.exports = async (client, message, userId, emojiId) => {
|
||||
try {
|
||||
const pollCheck = client.polls.get(message.id);
|
||||
const collector = client.messageCollector.get(userId);
|
||||
const editCollector = client.messageEdit.get(userId);
|
||||
|
||||
// Handle message collector
|
||||
if (collector && collector.messageId === message.id && collector.channelId === message.channelId) {
|
||||
return await handleMessageCollector(client, message, emojiId, collector);
|
||||
}
|
||||
|
||||
// Handle edit collector
|
||||
if (editCollector && editCollector.messageId === message.id && editCollector.channelId === message.channelId) {
|
||||
return await handleMessageCollector(client, message, emojiId, editCollector);
|
||||
}
|
||||
|
||||
// Handle poll
|
||||
if (pollCheck) {
|
||||
return await handlePoll(client, message, pollCheck, userId, emojiId);
|
||||
}
|
||||
|
||||
// Handle giveaway
|
||||
const giveaway = await Giveaways.findOne({ messageId: message.id });
|
||||
if (giveaway && !giveaway.ended) {
|
||||
return await handleGiveaway(client, message, giveaway, userId, emojiId);
|
||||
}
|
||||
|
||||
// Handle role reaction
|
||||
const db = await client.database.getGuild(message.server.id, true);
|
||||
if (db?.roles.find(e => e.msgId === message.id)?.roles.find(e => e.emoji === emojiId)) {
|
||||
return await handleRoleReaction(client, message, db, userId, emojiId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in messageReactionRemove:', error);
|
||||
}
|
||||
};
|
||||
@ -1,4 +1,5 @@
|
||||
const Embed = require("../functions/embed")
|
||||
const path = require("path");
|
||||
const Embed = require(path.join(__dirname, "../functions/embed"));
|
||||
module.exports = async (client, server) => {
|
||||
//console.log("join", server)
|
||||
// await client.database.getGuild(server.id, true)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const Embed = require("../functions/embed")
|
||||
const path = require("path");
|
||||
const Embed = require(path.join(__dirname, "../functions/embed"));
|
||||
module.exports = async (client, server) => {
|
||||
//console.log("delete",server)
|
||||
// await client.database.deleteGuild(server.id)
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
const path = require("path");
|
||||
let type;
|
||||
module.exports = async (client, member, memberOld) => {
|
||||
// Work in progress
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const db = require("../models/logging");
|
||||
const path = require("path");
|
||||
const db = require(path.join(__dirname, "../models/logging"));
|
||||
|
||||
async function audit(type, message, cmd) {
|
||||
const audit = new db();
|
||||
|
||||
@ -1,23 +1,69 @@
|
||||
const db = require("../models/polls");
|
||||
const Polls = require("./poll");
|
||||
const path = require("path");
|
||||
const db = require(path.join(__dirname, "../models/polls"));
|
||||
const Polls = require(path.join(__dirname, "./poll"));
|
||||
|
||||
/**
|
||||
* Checks and processes all active polls in the database
|
||||
* @param {Object} client - The Discord client instance
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function checkPolls(client) {
|
||||
let polls = await db.find();
|
||||
if (!polls || polls.length === 0) return;
|
||||
let i = 0;
|
||||
for (let poll of polls) {
|
||||
i++
|
||||
setTimeout(async () => {
|
||||
const time = poll.now - (Date.now() - poll.time), users = poll.users, avatars = poll.avatars, votes = poll.votes, desc = poll.desc, name = poll.name, names = poll.options, owner = poll.owner, lang = poll.lang;
|
||||
const newPoll = new Polls({ time, client, name: { name: name, description: desc }, options: names, votes: votes, users: users, avatars: avatars, owner: owner, lang: lang })
|
||||
try {
|
||||
const polls = await db.find();
|
||||
if (!polls?.length) return;
|
||||
|
||||
// Process polls concurrently with a small delay between each
|
||||
await Promise.all(polls.map(async (poll, index) => {
|
||||
try {
|
||||
await client.channels.get(poll.channelId).fetchMessage(poll.messageId).catch(() => { return });
|
||||
const msg = await client.messages.get(poll.messageId);
|
||||
if (msg) newPoll.start(msg, newPoll);
|
||||
} catch (e) { }
|
||||
// Add small delay between processing each poll to prevent rate limiting
|
||||
await new Promise(resolve => setTimeout(resolve, index * 700));
|
||||
|
||||
poll.deleteOne({ messageId: poll.messageId });
|
||||
}, i * 700);
|
||||
const {
|
||||
now,
|
||||
time,
|
||||
users,
|
||||
avatars,
|
||||
votes,
|
||||
desc,
|
||||
name,
|
||||
options: names,
|
||||
owner,
|
||||
lang,
|
||||
channelId,
|
||||
messageId
|
||||
} = poll;
|
||||
|
||||
const timeRemaining = now - (Date.now() - time);
|
||||
|
||||
const newPoll = new Polls({
|
||||
time: timeRemaining,
|
||||
client,
|
||||
name: { name, description: desc },
|
||||
options: names,
|
||||
votes,
|
||||
users,
|
||||
avatars,
|
||||
owner,
|
||||
lang
|
||||
});
|
||||
|
||||
// Attempt to fetch and process the message
|
||||
const channel = client.channels.get(channelId);
|
||||
if (!channel) return;
|
||||
|
||||
const message = await channel.fetchMessage(messageId).catch(() => null);
|
||||
if (message) {
|
||||
await newPoll.start(message, newPoll);
|
||||
}
|
||||
|
||||
// Clean up the poll from database
|
||||
await poll.deleteOne({ messageId });
|
||||
} catch (error) {
|
||||
console.error(`Error processing poll ${poll.messageId}:`, error);
|
||||
}
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error in checkPolls:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,23 +1,72 @@
|
||||
const db = require("../models/guilds");
|
||||
async function checkRoles(client) {
|
||||
let rr = await db.find({ $expr: { $gt: [{ $size: "$roles" }, 0] } });
|
||||
if (!rr || rr.length === 0) return;
|
||||
let i = 0;
|
||||
let ii = 0;
|
||||
for (let r of rr) {
|
||||
i++
|
||||
setTimeout(async () => {
|
||||
r.roles.map((role) => {
|
||||
ii++
|
||||
setTimeout(async () => {
|
||||
if (!client.channels.get(role.chanId) && role.roles.length === 0) {
|
||||
return await client.database.updateGuild(r.id, { roles: r.roles.filter(e => e.msgId !== role.msgId) });
|
||||
}
|
||||
const path = require("path");
|
||||
const db = require(path.join(__dirname, "../models/guilds"));
|
||||
|
||||
await client.channels.get(role.chanId)?.fetchMessage(role.msgId).catch(() => { });
|
||||
}, ii * 700);
|
||||
});
|
||||
}, i * 600);
|
||||
/**
|
||||
* Checks and validates role reaction messages across all guilds
|
||||
* @param {Object} client - Discord client instance
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function checkRoles(client) {
|
||||
try {
|
||||
// Find all guilds with role reactions
|
||||
const guildsWithRoles = await db.find({
|
||||
$expr: { $gt: [{ $size: "$roles" }, 0] }
|
||||
});
|
||||
|
||||
if (!guildsWithRoles?.length) return;
|
||||
|
||||
// Process each guild sequentially to avoid rate limits
|
||||
for (const guild of guildsWithRoles) {
|
||||
try {
|
||||
await processGuildRoles(client, guild);
|
||||
} catch (error) {
|
||||
console.error(`Error processing guild ${guild.id}:`, error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in checkRoles:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process role reactions for a single guild
|
||||
* @param {Object} client - Discord client instance
|
||||
* @param {Object} guild - Guild data from database
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function processGuildRoles(client, guild) {
|
||||
const validRoles = [];
|
||||
const invalidRoles = [];
|
||||
|
||||
// Process each role reaction message
|
||||
for (const role of guild.roles) {
|
||||
try {
|
||||
const channel = client.channels.get(role.chanId);
|
||||
|
||||
// Skip if channel doesn't exist or role array is empty
|
||||
if (!channel || !role.roles?.length) {
|
||||
invalidRoles.push(role);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify message exists
|
||||
const message = await channel.fetchMessage(role.msgId).catch(() => null);
|
||||
if (message) {
|
||||
validRoles.push(role);
|
||||
} else {
|
||||
invalidRoles.push(role);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error processing role ${role.msgId} in guild ${guild.id}:`, error);
|
||||
invalidRoles.push(role);
|
||||
}
|
||||
}
|
||||
|
||||
// Update guild with only valid roles if there were any invalid ones
|
||||
if (invalidRoles.length > 0) {
|
||||
await client.database.updateGuild(guild.id, {
|
||||
roles: validRoles
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,26 +1,45 @@
|
||||
function colors(altColorChar, textToTranslate) {
|
||||
const colorMap = {
|
||||
[`${altColorChar}0`]: '\x1b[30m', // Black
|
||||
[`${altColorChar}1`]: '\x1b[34m', // Dark Blue
|
||||
[`${altColorChar}2`]: '\x1b[32m', // Dark Green
|
||||
[`${altColorChar}3`]: '\x1b[36m', // Dark Aqua
|
||||
[`${altColorChar}4`]: '\x1b[31m', // Dark Red
|
||||
[`${altColorChar}5`]: '\x1b[35m', // Dark Purple
|
||||
[`${altColorChar}6`]: '\x1b[33m', // Gold
|
||||
[`${altColorChar}7`]: '\x1b[37m', // Gray
|
||||
[`${altColorChar}8`]: '\x1b[90m', // Dark Gray
|
||||
[`${altColorChar}9`]: '\x1b[94m', // Blue
|
||||
[`${altColorChar}a`]: '\x1b[92m', // Green
|
||||
[`${altColorChar}b`]: '\x1b[96m', // Aqua
|
||||
[`${altColorChar}c`]: '\x1b[91m', // Red
|
||||
[`${altColorChar}d`]: '\x1b[95m', // Light Purple
|
||||
[`${altColorChar}e`]: '\x1b[93m', // Yellow
|
||||
[`${altColorChar}f`]: '\x1b[97m', // White
|
||||
[`${altColorChar}r`]: '\x1b[0m', // Reset
|
||||
};
|
||||
|
||||
const regex = new RegExp(`${altColorChar}([0-9a-fr])`, 'g');
|
||||
return textToTranslate.replace(regex, (match, code) => colorMap[`${altColorChar}${code}`] || '');
|
||||
/**
|
||||
* ANSI color codes mapping for terminal text coloring
|
||||
* @type {Object<string, string>}
|
||||
*/
|
||||
const ANSI_COLORS = {
|
||||
'0': '\x1b[30m', // Black
|
||||
'1': '\x1b[34m', // Dark Blue
|
||||
'2': '\x1b[32m', // Dark Green
|
||||
'3': '\x1b[36m', // Dark Aqua
|
||||
'4': '\x1b[31m', // Dark Red
|
||||
'5': '\x1b[35m', // Dark Purple
|
||||
'6': '\x1b[33m', // Gold
|
||||
'7': '\x1b[37m', // Gray
|
||||
'8': '\x1b[90m', // Dark Gray
|
||||
'9': '\x1b[94m', // Blue
|
||||
'a': '\x1b[92m', // Green
|
||||
'b': '\x1b[96m', // Aqua
|
||||
'c': '\x1b[91m', // Red
|
||||
'd': '\x1b[95m', // Light Purple
|
||||
'e': '\x1b[93m', // Yellow
|
||||
'f': '\x1b[97m', // White
|
||||
'r': '\x1b[0m', // Reset
|
||||
};
|
||||
|
||||
/**
|
||||
* Translates color codes in text to ANSI color sequences
|
||||
* @param {string} altColorChar - The character used to prefix color codes (e.g., '&' or '§')
|
||||
* @param {string} textToTranslate - The text containing color codes to translate
|
||||
* @returns {string} The text with color codes replaced by ANSI sequences
|
||||
* @throws {Error} If altColorChar is not a single character
|
||||
*/
|
||||
function colors(altColorChar, textToTranslate) {
|
||||
if (typeof altColorChar !== 'string' || altColorChar.length !== 1) {
|
||||
throw new Error('altColorChar must be a single character');
|
||||
}
|
||||
|
||||
if (typeof textToTranslate !== 'string') {
|
||||
throw new Error('textToTranslate must be a string');
|
||||
}
|
||||
|
||||
const regex = new RegExp(`${altColorChar}([0-9a-fr])`, 'g');
|
||||
return textToTranslate.replace(regex, (_, code) => ANSI_COLORS[code] || '');
|
||||
}
|
||||
|
||||
module.exports = colors;
|
||||
|
||||
@ -1,11 +1,40 @@
|
||||
function dhms(str, sec = false) {
|
||||
const x = sec ? 1 : 1000;
|
||||
if (typeof str !== 'string') return 0;
|
||||
const fixed = str.replace(/\s/g, '');
|
||||
const tail = +fixed.match(/-?\d+$/g) || 0;
|
||||
const parts = (fixed.match(/-?\d+[^-0-9]+/g) || [])
|
||||
.map(v => +v.replace(/[^-0-9]+/g, '') * ({ s: x, m: 60 * x, h: 3600 * x, d: 86400 * x }[v.replace(/[-0-9]+/g, '')] || 0));
|
||||
return [tail, ...parts].reduce((a, b) => a + b, 0);
|
||||
};
|
||||
/**
|
||||
* Converts a time string (e.g., "1d2h3m4s") into milliseconds or seconds
|
||||
* @param {string} timeStr - The time string to convert (e.g., "1d2h3m4s")
|
||||
* @param {boolean} [inSeconds=false] - If true, returns result in seconds instead of milliseconds
|
||||
* @returns {number} The converted time in milliseconds or seconds
|
||||
*/
|
||||
function dhms(timeStr, inSeconds = false) {
|
||||
// Return 0 for invalid input
|
||||
if (typeof timeStr !== 'string' || !timeStr.trim()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Define time unit multipliers
|
||||
const multipliers = {
|
||||
s: inSeconds ? 1 : 1000,
|
||||
m: inSeconds ? 60 : 60000,
|
||||
h: inSeconds ? 3600 : 3600000,
|
||||
d: inSeconds ? 86400 : 86400000
|
||||
};
|
||||
|
||||
// Remove whitespace and split into parts
|
||||
const cleanStr = timeStr.replace(/\s/g, '');
|
||||
|
||||
// Extract the numeric value at the end (if any)
|
||||
const tailMatch = cleanStr.match(/-?\d+$/);
|
||||
const tailValue = tailMatch ? parseInt(tailMatch[0], 10) : 0;
|
||||
|
||||
// Extract and convert time parts
|
||||
const timeParts = (cleanStr.match(/-?\d+[^-0-9]+/g) || [])
|
||||
.map(part => {
|
||||
const value = parseInt(part.replace(/[^-0-9]+/g, ''), 10);
|
||||
const unit = part.replace(/[-0-9]+/g, '');
|
||||
return value * (multipliers[unit] || 0);
|
||||
});
|
||||
|
||||
// Sum all parts including the tail
|
||||
return [tailValue, ...timeParts].reduce((sum, value) => sum + value, 0);
|
||||
}
|
||||
|
||||
module.exports = dhms;
|
||||
@ -1,16 +1,34 @@
|
||||
function fetchTime(ms, client, lang) {
|
||||
var totalSeconds = (ms / 1000);
|
||||
let years = Math.floor(totalSeconds / 31536000);
|
||||
totalSeconds %= 31536000;
|
||||
let days = Math.floor(totalSeconds / 86400);
|
||||
totalSeconds %= 86400;
|
||||
let hours = Math.floor(totalSeconds / 3600);
|
||||
totalSeconds %= 3600;
|
||||
let minutes = Math.floor(totalSeconds / 60);
|
||||
let seconds = totalSeconds % 60;
|
||||
seconds = Math.floor(seconds);
|
||||
/**
|
||||
* Converts milliseconds into a human-readable time string with localized units
|
||||
* @param {number} milliseconds - The time in milliseconds to convert
|
||||
* @param {Object} client - The client object containing translation functionality
|
||||
* @param {string} lang - The language code for translations
|
||||
* @returns {string} Formatted time string with localized units
|
||||
*/
|
||||
function fetchTime(milliseconds, client, lang) {
|
||||
if (!Number.isFinite(milliseconds) || milliseconds < 0) {
|
||||
throw new Error('Invalid milliseconds value provided');
|
||||
}
|
||||
|
||||
return `${years ? `${years} ${client.translate.get(lang, "Functions.fetchTime.years")},` : ""} ${days ? `${days} ${client.translate.get(lang, "Functions.fetchTime.days")},` : ""} ${hours ? `${hours} ${client.translate.get(lang, "Functions.fetchTime.hours")},` : ""} ${minutes ? `${minutes} ${client.translate.get(lang, "Functions.fetchTime.minutes")},` : ""} ${seconds} ${client.translate.get(lang, "Functions.fetchTime.seconds")}`;
|
||||
const timeUnits = [
|
||||
{ value: 31536000, key: 'years' },
|
||||
{ value: 86400, key: 'days' },
|
||||
{ value: 3600, key: 'hours' },
|
||||
{ value: 60, key: 'minutes' },
|
||||
{ value: 1, key: 'seconds' }
|
||||
];
|
||||
|
||||
let remainingSeconds = Math.floor(milliseconds / 1000);
|
||||
|
||||
return timeUnits
|
||||
.map(({ value, key }) => {
|
||||
const unitValue = Math.floor(remainingSeconds / value);
|
||||
remainingSeconds %= value;
|
||||
return unitValue ? `${unitValue} ${client.translate.get(lang, `Functions.fetchTime.${key}`)},` : '';
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
module.exports = fetchTime;
|
||||
@ -1,38 +1,105 @@
|
||||
/**
|
||||
* base code taken from https://github.com/Mirasaki/logger
|
||||
* Enhanced logger utility with date-fns for date formatting
|
||||
* Original code inspired by https://github.com/Mirasaki/logger
|
||||
*/
|
||||
|
||||
const chalk = require('chalk'),
|
||||
moment = require('moment');
|
||||
const chalk = require('chalk');
|
||||
const { format, formatInTimeZone } = require('date-fns-tz');
|
||||
|
||||
const tagList = {
|
||||
SYSLOG: chalk.greenBright('[SYSLOG]'),
|
||||
SYSERR: chalk.redBright('[SYSERR]'),
|
||||
SUCCESS: chalk.greenBright('[SUCCESS]'),
|
||||
INFO: chalk.blueBright('[INFO]'),
|
||||
DEBUG: chalk.magentaBright('[DEBUG]'),
|
||||
DATA: chalk.yellowBright('[DATA]'),
|
||||
COMMAND: chalk.whiteBright('[CMD]'),
|
||||
EVENT: chalk.cyanBright('[EVENT]'),
|
||||
ERROR: chalk.redBright('[EVENT]'),
|
||||
WARN: chalk.yellowBright('[WARN]')
|
||||
// Define log levels and their corresponding styles
|
||||
const LOG_LEVELS = {
|
||||
SYSLOG: { color: 'greenBright', prefix: '[SYSLOG]' },
|
||||
SYSERR: { color: 'redBright', prefix: '[SYSERR]' },
|
||||
SUCCESS: { color: 'greenBright', prefix: '[SUCCESS]' },
|
||||
INFO: { color: 'blueBright', prefix: '[INFO]' },
|
||||
DEBUG: { color: 'magentaBright', prefix: '[DEBUG]' },
|
||||
DATA: { color: 'yellowBright', prefix: '[DATA]' },
|
||||
COMMAND: { color: 'whiteBright', prefix: '[CMD]' },
|
||||
EVENT: { color: 'cyanBright', prefix: '[EVENT]' },
|
||||
ERROR: { color: 'redBright', prefix: '[ERROR]' },
|
||||
WARN: { color: 'yellowBright', prefix: '[WARN]' }
|
||||
};
|
||||
|
||||
// Create tag list with chalk styling
|
||||
const tagList = Object.entries(LOG_LEVELS).reduce((acc, [key, { color, prefix }]) => ({
|
||||
...acc,
|
||||
[key]: chalk[color](prefix)
|
||||
}), {});
|
||||
|
||||
// Calculate longest tag length for alignment
|
||||
const longestTagLength = Math.max(...Object.values(tagList).map(t => t.length));
|
||||
const getTag = (tag) => `${tagList[tag]}${''.repeat(longestTagLength - tagList[tag].length)}`;
|
||||
const timestamp = () => `${chalk.whiteBright.bold(`[${moment.utc().format('YYYY-MM-DD HH:mm:ss')}]`)}`;
|
||||
|
||||
/**
|
||||
* Get formatted tag with proper spacing
|
||||
* @param {string} tag - The log level tag
|
||||
* @returns {string} Formatted tag with spacing
|
||||
*/
|
||||
const getTag = (tag) => `${tagList[tag]}${' '.repeat(longestTagLength - tagList[tag].length)}`;
|
||||
|
||||
/**
|
||||
* Get formatted timestamp
|
||||
* @returns {string} Formatted timestamp with styling
|
||||
*/
|
||||
const timestamp = () => {
|
||||
const now = new Date();
|
||||
const formattedDate = formatInTimeZone(now, 'UTC', 'yyyy-MM-dd HH:mm:ss');
|
||||
return chalk.whiteBright.bold(`[${formattedDate}]`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a log message with proper formatting
|
||||
* @param {string} level - Log level
|
||||
* @param {string} type - Optional type identifier
|
||||
* @param {string} message - Log message
|
||||
* @returns {string} Formatted log message
|
||||
*/
|
||||
const createLogMessage = (level, type, message) => {
|
||||
const typeTag = type ? `${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : ':';
|
||||
return `${timestamp()} ${getTag(level)} ${typeTag} ${message}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format error stack trace with proper styling
|
||||
* @param {Error} err - Error object
|
||||
* @returns {string} Formatted error stack trace
|
||||
*/
|
||||
const formatErrorStack = (err) => {
|
||||
if (!err.stack) return chalk.red(err);
|
||||
|
||||
return err.stack
|
||||
.split('\n')
|
||||
.map((msg, index) => {
|
||||
if (index === 0) return chalk.red(msg);
|
||||
|
||||
const isFailedFunctionCall = index === 1;
|
||||
const traceStartIndex = msg.indexOf('(');
|
||||
const traceEndIndex = msg.lastIndexOf(')');
|
||||
const hasTrace = traceStartIndex !== -1;
|
||||
const functionCall = msg.slice(
|
||||
msg.indexOf('at') + 3,
|
||||
hasTrace ? traceStartIndex - 1 : msg.length
|
||||
);
|
||||
const trace = msg.slice(traceStartIndex, traceEndIndex + 1);
|
||||
|
||||
return ` ${chalk.grey('at')} ${isFailedFunctionCall
|
||||
? `${chalk.redBright(functionCall)} ${chalk.red.underline(trace)}`
|
||||
: `${chalk.greenBright(functionCall)} ${chalk.grey(trace)}`
|
||||
}`;
|
||||
})
|
||||
.join('\n');
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
syslog: (type, str) => console.info(`${timestamp()} ${type ? `${getTag('SYSLOG')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('SYSLOG')}:`} ${str}`),
|
||||
syserr: (type, str) => console.error(`${timestamp()} ${type ? `${getTag('SYSERR')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('SYSERR')} :`} ${str}`),
|
||||
success: (type, str) => console.log(`${timestamp()} ${type ? `${getTag('SUCCESS')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('SUCCESS')}:`} ${str}`),
|
||||
info: (type, str) => console.info(`${timestamp()} ${type ? `${getTag('INFO')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('INFO')}:`} ${str}`),
|
||||
debug: (type, str) => console.log(`${timestamp()} ${type ? `${getTag('DEBUG')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('DEBUG')}:`} ${str}`),
|
||||
data: (type, str) => console.log(`${timestamp()} ${type ? `${getTag('DATA')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('DATA')}:`} ${str}`),
|
||||
command: (type, str) => console.log(`${timestamp()} ${type ? `${getTag('COMMAND')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('COMMAND')}:`} ${str}`),
|
||||
event: (type, str) => console.log(`${timestamp()} ${type ? `${getTag('EVENT')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('EVENT')}:`} ${str}`),
|
||||
error: (type, str) => console.log(`${timestamp()} ${type ? `${getTag('ERROR')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('ERROR')}:`} ${str}`),
|
||||
warn: (type, str) => console.log(`${timestamp()} ${type ? `${getTag('WARN')} ${chalk.whiteBright.bgBlue.bold(`[${type}]`)}:` : `${getTag('WARN')}:`} ${str}`),
|
||||
syslog: (type, str) => console.info(createLogMessage('SYSLOG', type, str)),
|
||||
syserr: (type, str) => console.error(createLogMessage('SYSERR', type, str)),
|
||||
success: (type, str) => console.log(createLogMessage('SUCCESS', type, str)),
|
||||
info: (type, str) => console.info(createLogMessage('INFO', type, str)),
|
||||
debug: (type, str) => console.log(createLogMessage('DEBUG', type, str)),
|
||||
data: (type, str) => console.log(createLogMessage('DATA', type, str)),
|
||||
command: (type, str) => console.log(createLogMessage('COMMAND', type, str)),
|
||||
event: (type, str) => console.log(createLogMessage('EVENT', type, str)),
|
||||
error: (type, str) => console.log(createLogMessage('ERROR', type, str)),
|
||||
warn: (type, str) => console.log(createLogMessage('WARN', type, str)),
|
||||
|
||||
startLog: (identifier) => console.log(`${timestamp()} ${getTag('DEBUG')} ${chalk.greenBright('[START]')} ${identifier}`),
|
||||
endLog: (identifier) => console.log(`${timestamp()} ${getTag('DEBUG')} ${chalk.redBright('[ END ]')} ${identifier}`),
|
||||
@ -40,46 +107,17 @@ module.exports = {
|
||||
timestamp,
|
||||
getExecutionTime: (hrtime) => {
|
||||
const timeSinceHrMs = (
|
||||
process.hrtime(hrtime)[0] * 1000
|
||||
+ hrtime[1] / 1000000
|
||||
process.hrtime(hrtime)[0] * 1000 +
|
||||
hrtime[1] / 1000000
|
||||
).toFixed(2);
|
||||
return `${chalk.yellowBright(
|
||||
(timeSinceHrMs / 1000).toFixed(2))
|
||||
} seconds (${chalk.yellowBright(timeSinceHrMs)} ms)`;
|
||||
return `${chalk.yellowBright((timeSinceHrMs / 1000).toFixed(2))} seconds (${chalk.yellowBright(timeSinceHrMs)} ms)`;
|
||||
},
|
||||
|
||||
printErr: (err) => {
|
||||
if (!(err instanceof Error)) {
|
||||
console.error(err)
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(
|
||||
!err.stack
|
||||
? chalk.red(err)
|
||||
: err.stack
|
||||
.split('\n')
|
||||
.map((msg, index) => {
|
||||
if (index === 0) {
|
||||
return chalk.red(msg);
|
||||
}
|
||||
|
||||
const isFailedFunctionCall = index === 1;
|
||||
const traceStartIndex = msg.indexOf('(');
|
||||
const traceEndIndex = msg.lastIndexOf(')');
|
||||
const hasTrace = traceStartIndex !== -1;
|
||||
const functionCall = msg.slice(
|
||||
msg.indexOf('at') + 3,
|
||||
hasTrace ? traceStartIndex - 1 : msg.length
|
||||
);
|
||||
const trace = msg.slice(traceStartIndex, traceEndIndex + 1);
|
||||
|
||||
return ` ${chalk.grey('at')} ${isFailedFunctionCall
|
||||
? `${chalk.redBright(functionCall)} ${chalk.red.underline(trace)}`
|
||||
: `${chalk.greenBright(functionCall)} ${chalk.grey(trace)}`
|
||||
}`;
|
||||
})
|
||||
.join('\n')
|
||||
)
|
||||
console.error(formatErrorStack(err));
|
||||
}
|
||||
};
|
||||
@ -1,72 +1,129 @@
|
||||
const Embed = require("../functions/embed");
|
||||
const path = require("path");
|
||||
const Embed = require(path.join(__dirname, "../functions/embed"));
|
||||
|
||||
/**
|
||||
* Validates and processes role assignments from a message
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} db - Database instance
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function Collector(client, message, db) {
|
||||
const regex = /{role:(.*?)}/;
|
||||
const regexAll = /{role:(.*?)}/g;
|
||||
const ROLE_REGEX = /{role:(.*?)}/g;
|
||||
const MAX_ROLES = 20;
|
||||
|
||||
// Get collector for the message author
|
||||
const collector = client.messageCollector.get(message.authorId);
|
||||
if (!message.content.match(regexAll) || message.content.match(regexAll)?.length === 0) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.noRoles")}: \`{role:Red}\``)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
|
||||
// Validate message contains role tags
|
||||
const roleMatches = message.content.match(ROLE_REGEX);
|
||||
if (!roleMatches?.length) {
|
||||
await sendErrorResponse(message, client, db, "Events.messageCreate.noRoles", `\`{role:Red}\``);
|
||||
return;
|
||||
}
|
||||
|
||||
const roles = message.content.match(regexAll).map((r) => r?.match(regex)[1]);
|
||||
if (roles.length > 20) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(client.translate.get(db.language, "Events.messageCreate.maxRoles"))] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
}
|
||||
collector.regex = roles
|
||||
const roleIds = []
|
||||
let newRoles = roles.map((r) => {
|
||||
return [...message.server.roles].map((r) => r).find((role) => r.toLowerCase() === role[1]?.name?.toLowerCase());
|
||||
})
|
||||
newRoles.map((r) => roleIds.push(r));
|
||||
// Extract role names from message
|
||||
const roleNames = roleMatches.map(match => match.match(/{role:(.*?)}/)[1]);
|
||||
|
||||
if (roleIds.map((r) => !r).includes(true)) {
|
||||
let unknown = [];
|
||||
roleIds.map((r, i) => {
|
||||
i++
|
||||
if (!r) {
|
||||
unknown.push(roles[i - 1]);
|
||||
}
|
||||
});
|
||||
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.unknown")}\n${unknown.map(e => `\`{role:${e}}\``).join(", ")}`)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
// Check role count limit
|
||||
if (roleNames.length > MAX_ROLES) {
|
||||
await sendErrorResponse(message, client, db, "Events.messageCreate.maxRoles");
|
||||
return;
|
||||
}
|
||||
|
||||
let duplicate = [];
|
||||
roleIds.map((r, i) => {
|
||||
i++
|
||||
if (roleIds.filter(e => e[0] === r[0]).length > 1) duplicate.push(roleIds[i - 1]);
|
||||
});
|
||||
collector.regex = roleNames;
|
||||
|
||||
if (duplicate.length > 0) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.duplicate")}\n${duplicate.map(e => `\`{role:${e[1].name}}\``)}`)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
// Find matching server roles
|
||||
const serverRoles = [...message.server.roles].map(([id, role]) => ({ id, role }));
|
||||
const matchedRoles = roleNames.map(name =>
|
||||
serverRoles.find(({ role }) => role.name.toLowerCase() === name.toLowerCase())
|
||||
);
|
||||
|
||||
// Check for unknown roles
|
||||
const unknownRoles = roleNames.filter((name, index) => !matchedRoles[index]);
|
||||
if (unknownRoles.length > 0) {
|
||||
await sendErrorResponse(
|
||||
message,
|
||||
client,
|
||||
db,
|
||||
"Events.messageCreate.unknown",
|
||||
unknownRoles.map(role => `\`{role:${role}}\``).join(", ")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let positions = [];
|
||||
const botRole = message.channel.server.member.orderedRoles.reverse()[0]
|
||||
// Check for duplicate roles
|
||||
const roleIds = matchedRoles.map(({ id }) => id);
|
||||
const duplicates = roleIds.filter((id, index) => roleIds.indexOf(id) !== index);
|
||||
if (duplicates.length > 0) {
|
||||
const duplicateRoles = duplicates.map(id =>
|
||||
matchedRoles.find(({ id: roleId }) => roleId === id).role.name
|
||||
);
|
||||
await sendErrorResponse(
|
||||
message,
|
||||
client,
|
||||
db,
|
||||
"Events.messageCreate.duplicate",
|
||||
duplicateRoles.map(name => `\`{role:${name}}\``).join(", ")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check bot role permissions
|
||||
const botRole = message.channel.server.member.orderedRoles.reverse()[0];
|
||||
if (!botRole) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(client.translate.get(db.language, "Events.messageCreate.noBotRole"))] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
await sendErrorResponse(message, client, db, "Events.messageCreate.noBotRole");
|
||||
return;
|
||||
}
|
||||
|
||||
roleIds.map((r, i) => {
|
||||
i++
|
||||
if (r[1].rank <= botRole.rank) positions.push(roleIds[i - 1])
|
||||
});
|
||||
|
||||
if (positions.length > 0) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.positions")}\n${positions.map(e => `\`{role:${e[1].name}}\``)}`)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
// Check role positions
|
||||
const invalidPositions = matchedRoles.filter(({ role }) => role.rank <= botRole.rank);
|
||||
if (invalidPositions.length > 0) {
|
||||
await sendErrorResponse(
|
||||
message,
|
||||
client,
|
||||
db,
|
||||
"Events.messageCreate.positions",
|
||||
invalidPositions.map(({ role }) => `\`{role:${role.name}}\``).join(", ")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
message.delete().catch(() => { });
|
||||
collector.roles = roleIds;
|
||||
// Process valid role assignment
|
||||
await message.delete().catch(() => {});
|
||||
collector.roles = matchedRoles;
|
||||
|
||||
const react = [client.config.emojis.check];
|
||||
return message.channel.sendMessage(collector.type === "content" ? { content: message.content, interactions: [react] } : { embeds: [new Embed().setDescription(message.content).setColor("#A52F05")], interactions: [react] }).then((msg) => {
|
||||
collector.messageId = msg.id;
|
||||
});
|
||||
const messageContent = collector.type === "content"
|
||||
? { content: message.content, interactions: [react] }
|
||||
: {
|
||||
embeds: [new Embed().setDescription(message.content).setColor("#A52F05")],
|
||||
interactions: [react]
|
||||
};
|
||||
|
||||
const sentMessage = await message.channel.sendMessage(messageContent);
|
||||
collector.messageId = sentMessage.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to send error responses
|
||||
* @param {Object} message - The message object
|
||||
* @param {Object} client - The client instance
|
||||
* @param {Object} db - Database instance
|
||||
* @param {string} translationKey - Translation key for the error message
|
||||
* @param {string} [additionalInfo] - Additional information to append to the error message
|
||||
*/
|
||||
async function sendErrorResponse(message, client, db, translationKey, additionalInfo = "") {
|
||||
const errorMessage = additionalInfo
|
||||
? `${client.translate.get(db.language, translationKey)}\n${additionalInfo}`
|
||||
: client.translate.get(db.language, translationKey);
|
||||
|
||||
await message.reply(
|
||||
{ embeds: [new Embed().setColor("#FF0000").setDescription(errorMessage)] },
|
||||
false
|
||||
).catch(() => {});
|
||||
|
||||
await message.react(client.config.emojis.cross).catch(() => {});
|
||||
}
|
||||
|
||||
module.exports = Collector;
|
||||
@ -1,72 +1,157 @@
|
||||
const Embed = require("../functions/embed");
|
||||
async function Collector(client, message, db) {
|
||||
const regex = /{role:(.*?)}/;
|
||||
const regexAll = /{role:(.*?)}/g;
|
||||
const collector = client.messageEdit.get(message.authorId);
|
||||
if (!message.content.match(regexAll) || message.content.match(regexAll)?.length === 0) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.noRoles")}: \`{role:Red}\``)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
const path = require("path");
|
||||
const Embed = require(path.join(__dirname, "../functions/embed"));
|
||||
|
||||
/**
|
||||
* Validates role mentions in a message
|
||||
* @param {string} content - Message content to validate
|
||||
* @param {string} language - User's language setting
|
||||
* @param {Object} client - Discord client instance
|
||||
* @returns {Object} Validation result with roles and error message if any
|
||||
*/
|
||||
function validateRoleMentions(content, language, client) {
|
||||
const regex = /{role:(.*?)}/g;
|
||||
const matches = content.match(regex);
|
||||
|
||||
if (!matches || matches.length === 0) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: client.translate.get(language, "Events.messageCreate.noRoles")
|
||||
};
|
||||
}
|
||||
|
||||
const roles = message.content.match(regexAll).map((r) => r?.match(regex)[1]);
|
||||
const roles = matches.map(match => match.match(/{role:(.*?)}/)[1]);
|
||||
|
||||
if (roles.length > 20) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(client.translate.get(db.language, "Events.messageCreate.maxRoles"))] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
}
|
||||
collector.regex = roles
|
||||
const roleIds = []
|
||||
let newRoles = roles.map((r) => {
|
||||
return [...message.server.roles].map((r) => r).find((role) => r.toLowerCase() === role[1]?.name?.toLowerCase());
|
||||
})
|
||||
newRoles.map((r) => roleIds.push(r));
|
||||
|
||||
if (roleIds.map((r) => !r).includes(true)) {
|
||||
let unknown = [];
|
||||
roleIds.map((r, i) => {
|
||||
i++
|
||||
if (!r) {
|
||||
unknown.push(roles[i - 1]);
|
||||
}
|
||||
});
|
||||
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.unknown")}\n${unknown.map(e => `\`{role:${e}}\``).join(", ")}`)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
return {
|
||||
isValid: false,
|
||||
error: client.translate.get(language, "Events.messageCreate.maxRoles")
|
||||
};
|
||||
}
|
||||
|
||||
let duplicate = [];
|
||||
roleIds.map((r, i) => {
|
||||
i++
|
||||
if (roleIds.filter(e => e[0] === r[0]).length > 1) duplicate.push(roleIds[i - 1]);
|
||||
});
|
||||
return { isValid: true, roles };
|
||||
}
|
||||
|
||||
if (duplicate.length > 0) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.duplicate")}\n${duplicate.map(e => `\`{role:${e[1].name}}\``)}`)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
/**
|
||||
* Finds server roles by name
|
||||
* @param {Array} roles - Array of role names
|
||||
* @param {Object} server - Server object
|
||||
* @returns {Object} Result containing found roles and unknown roles
|
||||
*/
|
||||
function findServerRoles(roles, server) {
|
||||
const roleIds = roles.map(roleName =>
|
||||
[...server.roles].find(([_, role]) =>
|
||||
roleName.toLowerCase() === role.name.toLowerCase()
|
||||
)
|
||||
);
|
||||
|
||||
const unknown = roles.filter((_, index) => !roleIds[index]);
|
||||
|
||||
return { roleIds, unknown };
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for duplicate roles
|
||||
* @param {Array} roleIds - Array of role IDs
|
||||
* @returns {Array} Array of duplicate roles
|
||||
*/
|
||||
function findDuplicateRoles(roleIds) {
|
||||
return roleIds.filter((role, index) =>
|
||||
roleIds.findIndex(r => r[0] === role[0]) !== index
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks role positions against bot's role
|
||||
* @param {Array} roleIds - Array of role IDs
|
||||
* @param {Object} botRole - Bot's highest role
|
||||
* @returns {Array} Array of roles with invalid positions
|
||||
*/
|
||||
function checkRolePositions(roleIds, botRole) {
|
||||
return roleIds.filter(role => role[1].rank <= botRole.rank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main collector function for message editing
|
||||
* @param {Object} client - Discord client instance
|
||||
* @param {Object} message - Message object
|
||||
* @param {Object} db - Database object
|
||||
*/
|
||||
async function Collector(client, message, db) {
|
||||
const collector = client.messageEdit.get(message.authorId);
|
||||
|
||||
// Validate role mentions
|
||||
const validation = validateRoleMentions(message.content, db.language, client);
|
||||
if (!validation.isValid) {
|
||||
await message.reply({
|
||||
embeds: [new Embed().setColor("#FF0000").setDescription(validation.error)]
|
||||
}, false).catch(() => {});
|
||||
return message.react(client.config.emojis.cross).catch(() => {});
|
||||
}
|
||||
|
||||
let positions = [];
|
||||
const botRole = message.channel.server.member.orderedRoles.reverse()[0]
|
||||
// Find server roles
|
||||
const { roleIds, unknown } = findServerRoles(validation.roles, message.server);
|
||||
if (unknown.length > 0) {
|
||||
await message.reply({
|
||||
embeds: [new Embed().setColor("#FF0000").setDescription(
|
||||
`${client.translate.get(db.language, "Events.messageCreate.unknown")}\n${
|
||||
unknown.map(e => `\`{role:${e}}\``).join(", ")
|
||||
}`
|
||||
)]
|
||||
}, false).catch(() => {});
|
||||
return message.react(client.config.emojis.cross).catch(() => {});
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
const duplicates = findDuplicateRoles(roleIds);
|
||||
if (duplicates.length > 0) {
|
||||
await message.reply({
|
||||
embeds: [new Embed().setColor("#FF0000").setDescription(
|
||||
`${client.translate.get(db.language, "Events.messageCreate.duplicate")}\n${
|
||||
duplicates.map(e => `\`{role:${e[1].name}}\``)
|
||||
}`
|
||||
)]
|
||||
}, false).catch(() => {});
|
||||
return message.react(client.config.emojis.cross).catch(() => {});
|
||||
}
|
||||
|
||||
// Check role positions
|
||||
const botRole = message.channel.server.member.orderedRoles.reverse()[0];
|
||||
if (!botRole) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(client.translate.get(db.language, "Events.messageCreate.noBotRole"))] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
await message.reply({
|
||||
embeds: [new Embed().setColor("#FF0000").setDescription(
|
||||
client.translate.get(db.language, "Events.messageCreate.noBotRole")
|
||||
)]
|
||||
}, false).catch(() => {});
|
||||
return message.react(client.config.emojis.cross).catch(() => {});
|
||||
}
|
||||
|
||||
roleIds.map((r, i) => {
|
||||
i++
|
||||
if (r[1].rank <= botRole.rank) positions.push(roleIds[i - 1])
|
||||
});
|
||||
|
||||
if (positions.length > 0) {
|
||||
message.reply({ embeds: [new Embed().setColor("#FF0000").setDescription(`${client.translate.get(db.language, "Events.messageCreate.positions")}\n${positions.map(e => `\`{role:${e[1].name}}\``)}`)] }, false).catch(() => { return });
|
||||
return message.react(client.config.emojis.cross).catch(() => { return });
|
||||
const invalidPositions = checkRolePositions(roleIds, botRole);
|
||||
if (invalidPositions.length > 0) {
|
||||
await message.reply({
|
||||
embeds: [new Embed().setColor("#FF0000").setDescription(
|
||||
`${client.translate.get(db.language, "Events.messageCreate.positions")}\n${
|
||||
invalidPositions.map(e => `\`{role:${e[1].name}}\``)
|
||||
}`
|
||||
)]
|
||||
}, false).catch(() => {});
|
||||
return message.react(client.config.emojis.cross).catch(() => {});
|
||||
}
|
||||
|
||||
message.delete().catch(() => { });
|
||||
// Process valid message
|
||||
await message.delete().catch(() => {});
|
||||
collector.roles = roleIds;
|
||||
collector.regex = validation.roles;
|
||||
|
||||
const react = [client.config.emojis.check];
|
||||
return message.channel.sendMessage(collector.type === "content" ? { content: message.content, interactions: [react] } : { embeds: [new Embed().setDescription(message.content).setColor("#A52F05")], interactions: [react] }).then((msg) => {
|
||||
collector.messageId = msg.id;
|
||||
});
|
||||
const messageContent = collector.type === "content"
|
||||
? { content: message.content, interactions: [react] }
|
||||
: {
|
||||
embeds: [new Embed().setDescription(message.content).setColor("#A52F05")],
|
||||
interactions: [react]
|
||||
};
|
||||
|
||||
const msg = await message.channel.sendMessage(messageContent);
|
||||
collector.messageId = msg.id;
|
||||
}
|
||||
|
||||
module.exports = Collector;
|
||||
@ -1,34 +1,104 @@
|
||||
const PollDB = require("../models/polls");
|
||||
const Embed = require("./embed");
|
||||
const path = require("path");
|
||||
const PollDB = require(path.join(__dirname, "../models/polls"));
|
||||
const Embed = require(path.join(__dirname, "./embed"));
|
||||
const Canvas = require('canvas');
|
||||
const format = `${(new Date().getMonth() + 1) < 10 ? `0${new Date().getMonth() + 1}` : new Date().getMonth() + 1}/${new Date().getDate()}/${new Date().getFullYear()} ${new Date().getHours()}:${(new Date().getMinutes() < 10 ? '0' : '') + new Date().getMinutes()}`;
|
||||
|
||||
// Constants
|
||||
const CANVAS_CONFIG = {
|
||||
WIDTH: 600,
|
||||
PADDING: 10,
|
||||
BAR_HEIGHT: 40,
|
||||
FONT_SIZES: {
|
||||
SMALL: '12px',
|
||||
MEDIUM: '17px'
|
||||
},
|
||||
COLORS: {
|
||||
BACKGROUND: "#23272A",
|
||||
TEXT_PRIMARY: "#FFFFFF",
|
||||
TEXT_SECONDARY: "#4E535A",
|
||||
BAR_BACKGROUND: "#2C2F33",
|
||||
BAR_FILL: "#A52F05",
|
||||
BAR_INACTIVE: "#24282B",
|
||||
SELECTION: "#717cf4"
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to format date
|
||||
const getFormattedDate = () => {
|
||||
const now = new Date();
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
return `${month}/${day}/${now.getFullYear()} ${hours}:${minutes}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Polls class for managing and rendering polls
|
||||
*/
|
||||
class Polls {
|
||||
constructor({ time, client, name, options, users, avatars, votes, owner, lang }) {
|
||||
/**
|
||||
* @param {Object} options - Poll configuration options
|
||||
* @param {number} options.time - Duration of the poll in milliseconds
|
||||
* @param {Object} options.client - Discord client instance
|
||||
* @param {Object} options.name - Poll name and description
|
||||
* @param {Object} options.options - Poll options configuration
|
||||
* @param {string[]} [options.users] - Array of user IDs who voted
|
||||
* @param {string[]} [options.avatars] - Array of user avatar URLs
|
||||
* @param {number[]} [options.votes] - Array of vote counts
|
||||
* @param {string} options.owner - Poll owner ID
|
||||
* @param {string} options.lang - Language code
|
||||
*/
|
||||
constructor({ time, client, name, options, users = [], avatars = [], votes, owner, lang }) {
|
||||
this.client = client;
|
||||
this.time = time;
|
||||
if (votes) this.votes = votes;
|
||||
else this.votes = options.name.length === 2 ? [0, 0] : options.name.length === 3 ? [0, 0, 0] : options.name.length === 4 ? [0, 0, 0, 0] : options.name.length === 5 ? [0, 0, 0, 0, 0] : options.name.length === 6 ? [0, 0, 0, 0, 0, 0] : options.name.length === 7 ? [0, 0, 0, 0, 0, 0, 0] : options.name.length === 8 ? [0, 0, 0, 0, 0, 0, 0, 0] : options.name.length === 9 ? [0, 0, 0, 0, 0, 0, 0, 0, 0] : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
this.users = users || [];
|
||||
this.avatars = avatars || [];
|
||||
this.options = { name: name.name, description: name.description };
|
||||
this.voteOptions = options;
|
||||
this.owner = owner;
|
||||
this.lang = lang;
|
||||
this.size = { canvas: options.name.length === 2 ? 200 : options.name.length === 3 ? 250 : options.name.length === 4 ? 300 : options.name.length === 5 ? 350 : options.name.length === 6 ? 400 : options.name.length === 7 ? 450 : options.name.length === 8 ? 500 : options.name.length === 9 ? 550 : 600, bar: options.name.length === 2 ? 150 : options.name.length === 3 ? 200 : options.name.length === 4 ? 250 : options.name.length === 5 ? 300 : options.name.length === 6 ? 350 : options.name.length === 7 ? 400 : options.name.length === 8 ? 450 : options.name.length === 9 ? 500 : 550 };
|
||||
this.users = users;
|
||||
this.avatars = avatars;
|
||||
this.options = { name: name.name, description: name.description };
|
||||
this.voteOptions = options;
|
||||
|
||||
// Initialize votes array based on number of options
|
||||
this.votes = votes || new Array(options.name.length).fill(0);
|
||||
|
||||
// Calculate canvas dimensions based on number of options
|
||||
this.size = this.calculateCanvasSize(options.name.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate canvas dimensions based on number of options
|
||||
* @param {number} optionCount - Number of poll options
|
||||
* @returns {Object} Canvas and bar dimensions
|
||||
*/
|
||||
calculateCanvasSize(optionCount) {
|
||||
const baseSize = 200;
|
||||
const increment = 50;
|
||||
const size = baseSize + (optionCount - 2) * increment;
|
||||
return {
|
||||
canvas: size,
|
||||
bar: size - 50
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the poll
|
||||
* @param {Object} message - Discord message object
|
||||
* @param {Object} poll - Poll instance
|
||||
*/
|
||||
async start(message, poll) {
|
||||
this.client.polls.set(message.id, { poll, messageId: message.id, users: this.users, owner: this.owner, lang: this.lang })
|
||||
setTimeout(async () => {
|
||||
if (!this.client.polls.get(message.id)) return;
|
||||
await this.update();
|
||||
message.edit({ embeds: [new Embed().setMedia(await this.client.Uploader.upload(poll.canvas.toBuffer(), `Poll.png`)).setColor(`#F24646`)], content: this.client.translate.get(this.lang, "Functions.poll.end") }).catch(() => { })
|
||||
this.client.polls.delete(message.id);
|
||||
await PollDB.findOneAndDelete({ messageId: message.id });
|
||||
}, this.time);
|
||||
this.client.polls.set(message.id, {
|
||||
poll,
|
||||
messageId: message.id,
|
||||
users: this.users,
|
||||
owner: this.owner,
|
||||
lang: this.lang
|
||||
});
|
||||
|
||||
if (this.time < 0) return;
|
||||
await (new PollDB({
|
||||
|
||||
// Save poll to database
|
||||
await new PollDB({
|
||||
owner: this.owner,
|
||||
channelId: message.channelId,
|
||||
messageId: message.id,
|
||||
@ -41,208 +111,325 @@ class Polls {
|
||||
time: this.time,
|
||||
lang: this.lang,
|
||||
now: Date.now(),
|
||||
}).save());
|
||||
}
|
||||
}).save();
|
||||
|
||||
textHeight(text, ctx, m) {
|
||||
let metrics = m || ctx.measureText(text);
|
||||
return metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
|
||||
}
|
||||
// Set poll end timer
|
||||
setTimeout(async () => {
|
||||
if (!this.client.polls.get(message.id)) return;
|
||||
|
||||
roundRect(ctx, x, y, width, height, radius, fill, stroke) { // Credit to https://stackoverflow.com/users/227299/juan-mendes
|
||||
if (typeof stroke === 'undefined') {
|
||||
stroke = true;
|
||||
}
|
||||
if (typeof radius === 'undefined') {
|
||||
radius = 5;
|
||||
}
|
||||
if (typeof radius === 'number') {
|
||||
radius = { tl: radius, tr: radius, br: radius, bl: radius };
|
||||
} else {
|
||||
var defaultRadius = { tl: 0, tr: 0, br: 0, bl: 0 };
|
||||
for (var side in defaultRadius) {
|
||||
radius[side] = radius[side] || defaultRadius[side];
|
||||
await this.update();
|
||||
const endMessage = this.client.translate.get(this.lang, "Functions.poll.end");
|
||||
|
||||
try {
|
||||
await message.edit({
|
||||
embeds: [new Embed()
|
||||
.setMedia(await this.client.Uploader.upload(poll.canvas.toBuffer(), 'Poll.png'))
|
||||
.setColor('#F24646')],
|
||||
content: endMessage
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to edit poll message:', error);
|
||||
}
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + radius.tl, y);
|
||||
ctx.lineTo(x + width - radius.tr, y);
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
|
||||
ctx.lineTo(x + width, y + height - radius.br);
|
||||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
|
||||
ctx.lineTo(x + radius.bl, y + height);
|
||||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
|
||||
ctx.lineTo(x, y + radius.tl);
|
||||
ctx.quadraticCurveTo(x, y, x + radius.tl, y);
|
||||
ctx.closePath();
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
this.client.polls.delete(message.id);
|
||||
await PollDB.findOneAndDelete({ messageId: message.id });
|
||||
}, this.time);
|
||||
}
|
||||
|
||||
async update() {
|
||||
let roundRect = this.roundRect;
|
||||
let textHeight = this.textHeight;
|
||||
/**
|
||||
* Calculate text height for canvas
|
||||
* @param {string} text - Text to measure
|
||||
* @param {CanvasRenderingContext2D} ctx - Canvas context
|
||||
* @param {TextMetrics} [metrics] - Optional text metrics
|
||||
* @returns {number} Text height
|
||||
*/
|
||||
textHeight(text, ctx, metrics) {
|
||||
const m = metrics || ctx.measureText(text);
|
||||
return m.actualBoundingBoxAscent + m.actualBoundingBoxDescent;
|
||||
}
|
||||
|
||||
var width = 600, height = this.size.canvas, padding = 10;
|
||||
const canvas = Canvas.createCanvas(width, height);
|
||||
/**
|
||||
* Draw rounded rectangle on canvas
|
||||
* @param {CanvasRenderingContext2D} ctx - Canvas context
|
||||
* @param {number} x - X coordinate
|
||||
* @param {number} y - Y coordinate
|
||||
* @param {number} width - Rectangle width
|
||||
* @param {number} height - Rectangle height
|
||||
* @param {number|Object} radius - Corner radius
|
||||
* @param {boolean} fill - Whether to fill the rectangle
|
||||
* @param {boolean} stroke - Whether to stroke the rectangle
|
||||
*/
|
||||
roundRect(ctx, x, y, width, height, radius = 5, fill = true, stroke = true) {
|
||||
const r = typeof radius === 'number'
|
||||
? { tl: radius, tr: radius, br: radius, bl: radius }
|
||||
: { tl: 0, tr: 0, br: 0, bl: 0, ...radius };
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r.tl, y);
|
||||
ctx.lineTo(x + width - r.tr, y);
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + r.tr);
|
||||
ctx.lineTo(x + width, y + height - r.br);
|
||||
ctx.quadraticCurveTo(x + width, y + height, x + width - r.br, y + height);
|
||||
ctx.lineTo(x + r.bl, y + height);
|
||||
ctx.quadraticCurveTo(x, y + height, x, y + height - r.bl);
|
||||
ctx.lineTo(x, y + r.tl);
|
||||
ctx.quadraticCurveTo(x, y, x + r.tl, y);
|
||||
ctx.closePath();
|
||||
|
||||
if (fill) ctx.fill();
|
||||
if (stroke) ctx.stroke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the poll canvas
|
||||
*/
|
||||
async update() {
|
||||
const { WIDTH, PADDING, BAR_HEIGHT, COLORS, FONT_SIZES } = CANVAS_CONFIG;
|
||||
const height = this.size.canvas;
|
||||
|
||||
// Create canvas
|
||||
const canvas = Canvas.createCanvas(WIDTH, height);
|
||||
this.canvas = canvas;
|
||||
const ctx = this.canvas.getContext('2d');
|
||||
this.ctx = ctx;
|
||||
|
||||
let name = this.options.name?.length > 70 ? this.options?.name.slice(0, 67) + "..." : this.options.name;
|
||||
var nameHeight = textHeight(name, ctx);
|
||||
// Draw background
|
||||
ctx.fillStyle = COLORS.BACKGROUND;
|
||||
this.roundRect(ctx, 0, 0, WIDTH, height, 5, true, false);
|
||||
|
||||
let description = this.options.description.length > 80 ? this.options.description.slice(0, 77) + "..." : this.options.description;
|
||||
var descHeight = textHeight(description, ctx);
|
||||
// Draw title and description
|
||||
const name = this.truncateText(this.options.name, 70);
|
||||
const description = this.truncateText(this.options.description, 80);
|
||||
|
||||
ctx.fillStyle = "#23272A";
|
||||
roundRect(ctx, 0, 0, width, height, 5, true, false); // background
|
||||
const nameHeight = this.textHeight(name, ctx);
|
||||
const descHeight = this.textHeight(description, ctx);
|
||||
|
||||
ctx.fillStyle = "#4E535A";
|
||||
ctx.font = `normal 12px Sans-Serif`;
|
||||
ctx.fillText(name, padding, padding + 2 + nameHeight / 2); // name
|
||||
// Draw title
|
||||
ctx.fillStyle = COLORS.TEXT_SECONDARY;
|
||||
ctx.font = `normal ${FONT_SIZES.SMALL} Sans-Serif`;
|
||||
ctx.fillText(name, PADDING, PADDING + 2 + nameHeight / 2);
|
||||
|
||||
ctx.fillStyle = "#FFFFFF";
|
||||
ctx.font = `normal 17px Sans-Serif`;
|
||||
ctx.fillText(description, padding, padding + 15 + nameHeight + descHeight / 2); // description
|
||||
// Draw description
|
||||
ctx.fillStyle = COLORS.TEXT_PRIMARY;
|
||||
ctx.font = `normal ${FONT_SIZES.MEDIUM} Sans-Serif`;
|
||||
ctx.fillText(description, PADDING, PADDING + 15 + nameHeight + descHeight / 2);
|
||||
|
||||
var headerHeight = padding + descHeight + nameHeight + 15;
|
||||
var dataWidth = width - padding * 2;
|
||||
var barHeight = 40;
|
||||
var votes = this.votes;
|
||||
var names = this.voteOptions.name;
|
||||
const headerHeight = PADDING + descHeight + nameHeight + 15;
|
||||
const dataWidth = WIDTH - PADDING * 2;
|
||||
|
||||
this.drawVoteBars(ctx, dataWidth - 20, barHeight, votes, { pad: padding, hHeight: headerHeight }, names);
|
||||
await this.drawFooter(ctx, padding, padding + headerHeight + barHeight * 2 + 20, width, height, padding, this.avatars);
|
||||
// Draw vote bars
|
||||
this.drawVoteBars(ctx, dataWidth - 20, BAR_HEIGHT, this.votes,
|
||||
{ pad: PADDING, hHeight: headerHeight },
|
||||
this.voteOptions.name);
|
||||
|
||||
// Draw footer
|
||||
await this.drawFooter(ctx, PADDING, PADDING + headerHeight + BAR_HEIGHT * 2 + 20,
|
||||
WIDTH, height, PADDING, this.avatars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate text with ellipsis
|
||||
* @param {string} text - Text to truncate
|
||||
* @param {number} maxLength - Maximum length
|
||||
* @returns {string} Truncated text
|
||||
*/
|
||||
truncateText(text, maxLength) {
|
||||
return text?.length > maxLength ? text.slice(0, maxLength - 3) + "..." : text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a vote to the poll
|
||||
* @param {number} option - Option index
|
||||
* @param {string} user - User ID
|
||||
* @param {string} avatar - User avatar URL
|
||||
* @param {string} id - Message ID
|
||||
* @returns {Canvas} Updated canvas
|
||||
*/
|
||||
async addVote(option, user, avatar, id) {
|
||||
if (this.avatars.length === 6) this.avatars.shift();
|
||||
this.avatars.push(avatar);
|
||||
this.votes[option]++;
|
||||
await PollDB.findOneAndUpdate({ messageId: id }, { $push: { users: user, avatars: avatar }, $inc: { [`votes.${option}`]: 1 } });
|
||||
|
||||
await PollDB.findOneAndUpdate(
|
||||
{ messageId: id },
|
||||
{
|
||||
$push: { users: user, avatars: avatar },
|
||||
$inc: { [`votes.${option}`]: 1 }
|
||||
}
|
||||
);
|
||||
|
||||
await this.update();
|
||||
return this.canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a vote from the poll
|
||||
* @param {number} option - Option index
|
||||
* @param {string} user - User ID
|
||||
* @param {string} avatar - User avatar URL
|
||||
* @param {string} id - Message ID
|
||||
* @returns {Canvas} Updated canvas
|
||||
*/
|
||||
async removeVote(option, user, avatar, id) {
|
||||
this.avatars.splice(this.avatars.indexOf(avatar), 1);
|
||||
this.votes[option]--;
|
||||
await PollDB.findOneAndUpdate({ messageId: id }, { $pull: { users: user, avatars: avatar }, $inc: { [`votes.${option}`]: -1 } });
|
||||
|
||||
await PollDB.findOneAndUpdate(
|
||||
{ messageId: id },
|
||||
{
|
||||
$pull: { users: user, avatars: avatar },
|
||||
$inc: { [`votes.${option}`]: -1 }
|
||||
}
|
||||
);
|
||||
|
||||
await this.update();
|
||||
return this.canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw vote bars on canvas
|
||||
* @param {CanvasRenderingContext2D} ctx - Canvas context
|
||||
* @param {number} width - Bar width
|
||||
* @param {number} height - Bar height
|
||||
* @param {number[]} votes - Vote counts
|
||||
* @param {Object} vars - Position variables
|
||||
* @param {string[]} names - Option names
|
||||
* @param {number} [vote] - Selected vote index
|
||||
*/
|
||||
drawVoteBars(ctx, width, height, votes, vars, names, vote) {
|
||||
let roundRect = this.roundRect;
|
||||
let textHeight = this.textHeight;
|
||||
let padding = vars.pad;
|
||||
let headerHeight = vars.hHeight;
|
||||
let sum = votes.reduce((prev, curr) => prev + curr);
|
||||
let percentages = votes.map((v) => Math.floor(v / (sum / 100) * 10) / 10);
|
||||
const { PADDING, COLORS } = CANVAS_CONFIG;
|
||||
const { pad: padding, hHeight: headerHeight } = vars;
|
||||
|
||||
const sum = votes.reduce((prev, curr) => prev + curr, 0);
|
||||
const percentages = votes.map(v => Math.floor(v / (sum / 100) * 10) / 10);
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(padding, padding + headerHeight);
|
||||
|
||||
var barPadding = 5;
|
||||
const barPadding = 5;
|
||||
percentages.forEach((percentage, i) => {
|
||||
if (!percentage) percentage = 0;
|
||||
let paddingLeft = (vote != undefined) ? 30 : 0;
|
||||
const y = (height + 10) * i;
|
||||
const paddingLeft = vote !== undefined ? 30 : 0;
|
||||
|
||||
ctx.fillStyle = "#2C2F33";
|
||||
let y = (height + 10) * i;
|
||||
roundRect(ctx, 20, y, width, height, 5, true, false); // full bar
|
||||
// Draw bar background
|
||||
ctx.fillStyle = COLORS.BAR_BACKGROUND;
|
||||
this.roundRect(ctx, 20, y, width, height, 5, true, false);
|
||||
|
||||
if (vote == i || percentage) { ctx.fillStyle = "#A52F05"; }
|
||||
else { ctx.fillStyle = "#24282B"; } // percentage display
|
||||
roundRect(ctx, 20, y, width * (votes[i] / (sum / 100) / 100), height, 5, true, false);
|
||||
// Draw bar fill
|
||||
ctx.fillStyle = (vote === i || percentage) ? COLORS.BAR_FILL : COLORS.BAR_INACTIVE;
|
||||
this.roundRect(ctx, 20, y, width * (votes[i] / (sum / 100) / 100), height, 5, true, false);
|
||||
|
||||
ctx.fillStyle = "#4E535A";
|
||||
let h = textHeight(i + 1, ctx);
|
||||
ctx.fillText(i + 1, 0, y + height / 2 + h / 2);
|
||||
// Draw option number
|
||||
ctx.fillStyle = COLORS.TEXT_SECONDARY;
|
||||
const numHeight = this.textHeight(i + 1, ctx);
|
||||
ctx.fillText(i + 1, 0, y + height / 2 + numHeight / 2);
|
||||
|
||||
ctx.fillStyle = "#FFFFFF"; // Option names
|
||||
h = textHeight(names[i], ctx);
|
||||
ctx.fillText(names[i].length > 65 ? names[i].slice(0, 62) + "..." : names[i], 30 + paddingLeft, y + 13 + h);
|
||||
// Draw option name
|
||||
ctx.fillStyle = COLORS.TEXT_PRIMARY;
|
||||
const name = this.truncateText(names[i], 65);
|
||||
const nameHeight = this.textHeight(name, ctx);
|
||||
ctx.fillText(name, 30 + paddingLeft, y + 13 + nameHeight);
|
||||
|
||||
if (vote != undefined) {
|
||||
ctx.strokeStyle = "#FFFFFF"; // selection circle
|
||||
ctx.fillStyle = "#717cf4";
|
||||
// Draw selection circle if voting
|
||||
if (vote !== undefined) {
|
||||
ctx.strokeStyle = COLORS.TEXT_PRIMARY;
|
||||
ctx.fillStyle = COLORS.SELECTION;
|
||||
ctx.beginPath();
|
||||
ctx.arc(35, y + 10 + h * 0.75, 6, 0, 2 * Math.PI);
|
||||
ctx.arc(35, y + 10 + nameHeight * 0.75, 6, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
if (vote == i) {
|
||||
|
||||
if (vote === i) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(35, y + 10 + h * 0.75, 3, 0, 2 * Math.PI);
|
||||
ctx.arc(35, y + 10 + nameHeight * 0.75, 3, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillStyle = "#2C2F33"; // percentage and vote count background
|
||||
let metrics = ctx.measureText(percentage + "% (" + votes[i] + ")");
|
||||
let w = metrics.width;
|
||||
h = textHeight(percentage + "% (" + votes[i] + ")", ctx, metrics);
|
||||
y = y + (height - h - barPadding * 2) + barPadding * 2.6;
|
||||
if (vote == i || vote == undefined) roundRect(ctx, width - barPadding - w - 3, y - h - 4, w + 5, h + 12, 5, true, false);
|
||||
// Draw percentage and vote count
|
||||
const voteText = `${percentage}% (${votes[i]})`;
|
||||
const metrics = ctx.measureText(voteText);
|
||||
const textHeight = this.textHeight(voteText, ctx, metrics);
|
||||
const textY = y + (height - textHeight - barPadding * 2) + barPadding * 2.6;
|
||||
|
||||
ctx.fillStyle = "#A52F05"; // percentage and vote count
|
||||
ctx.fillText(percentage + "% (" + votes[i] + ")", width - barPadding - w, y);
|
||||
if (vote === i || vote === undefined) {
|
||||
ctx.fillStyle = COLORS.BAR_BACKGROUND;
|
||||
this.roundRect(ctx, width - barPadding - metrics.width - 3,
|
||||
textY - textHeight - 4, metrics.width + 5, textHeight + 12, 5, true, false);
|
||||
}
|
||||
|
||||
ctx.fillStyle = COLORS.BAR_FILL;
|
||||
ctx.fillText(voteText, width - barPadding - metrics.width, textY);
|
||||
});
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw footer on canvas
|
||||
* @param {CanvasRenderingContext2D} ctx - Canvas context
|
||||
* @param {number} x - X coordinate
|
||||
* @param {number} y - Y coordinate
|
||||
* @param {number} width - Canvas width
|
||||
* @param {number} height - Canvas height
|
||||
* @param {number} padding - Padding value
|
||||
* @param {string[]} users - Array of user avatar URLs
|
||||
*/
|
||||
async drawFooter(ctx, x, y, width, height, padding, users) {
|
||||
const { COLORS } = CANVAS_CONFIG;
|
||||
ctx.save();
|
||||
ctx.translate(10, this.size.bar);
|
||||
|
||||
var rad = 18;
|
||||
ctx.fillStyle = "#4E535A";
|
||||
const rad = 18;
|
||||
ctx.fillStyle = COLORS.TEXT_SECONDARY;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = "#4E535A";
|
||||
ctx.strokeStyle = COLORS.TEXT_SECONDARY;
|
||||
|
||||
// Draw separator line
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0 - padding, 0);
|
||||
ctx.lineTo(width, 0);
|
||||
ctx.stroke();
|
||||
|
||||
let votes = (this.votes.reduce((p, c) => p + c) == 1) ? `${this.votes.reduce((p, c) => p + c)} vote` : `${this.votes.reduce((p, c) => p + c)} votes`;
|
||||
let metrics = ctx.measureText(votes);
|
||||
let h = this.textHeight(votes, ctx, metrics);
|
||||
ctx.fillText(votes, 5, rad + h);
|
||||
// Draw vote count
|
||||
const totalVotes = this.votes.reduce((p, c) => p + c, 0);
|
||||
const voteText = totalVotes === 1 ? '1 vote' : `${totalVotes} votes`;
|
||||
const metrics = ctx.measureText(voteText);
|
||||
const textHeight = this.textHeight(voteText, ctx, metrics);
|
||||
ctx.fillText(voteText, 5, rad + textHeight);
|
||||
|
||||
// Avatars
|
||||
var pos = rad * users.length + 10 + metrics.width;
|
||||
var yPos = 6;
|
||||
// Draw avatars
|
||||
let pos = rad * users.length + 10 + metrics.width;
|
||||
const yPos = 6;
|
||||
users.reverse();
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
ctx.beginPath();
|
||||
let user = users[i];
|
||||
|
||||
const a = Canvas.createCanvas(rad * 2, rad * 2);
|
||||
const context = a.getContext("2d");
|
||||
for (const avatar of users) {
|
||||
const avatarCanvas = Canvas.createCanvas(rad * 2, rad * 2);
|
||||
const avatarCtx = avatarCanvas.getContext('2d');
|
||||
|
||||
context.beginPath();
|
||||
context.arc(rad, rad, rad, 0, Math.PI * 2, true);
|
||||
context.closePath();
|
||||
context.clip();
|
||||
avatarCtx.beginPath();
|
||||
avatarCtx.arc(rad, rad, rad, 0, Math.PI * 2, true);
|
||||
avatarCtx.closePath();
|
||||
avatarCtx.clip();
|
||||
|
||||
const avatar = await Canvas.loadImage(user);
|
||||
context.drawImage(avatar, 0, 0, rad * 2, rad * 2);
|
||||
ctx.drawImage(a, pos, yPos);
|
||||
try {
|
||||
const avatarImage = await Canvas.loadImage(avatar);
|
||||
avatarCtx.drawImage(avatarImage, 0, 0, rad * 2, rad * 2);
|
||||
ctx.drawImage(avatarCanvas, pos, yPos);
|
||||
} catch (error) {
|
||||
console.error('Failed to load avatar:', error);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
pos -= rad;
|
||||
}
|
||||
|
||||
// Date
|
||||
let date = format;
|
||||
metrics = ctx.measureText(date);
|
||||
h = this.textHeight(date, ctx, metrics);
|
||||
ctx.fillText(date, width - 15 - metrics.width, rad + h);
|
||||
// Draw date
|
||||
const date = getFormattedDate();
|
||||
const dateMetrics = ctx.measureText(date);
|
||||
const dateHeight = this.textHeight(date, ctx, dateMetrics);
|
||||
ctx.fillText(date, width - 15 - dateMetrics.width, rad + dateHeight);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
const crypt = require("crypto");
|
||||
// const FlakeId = require('flakeid');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
|
||||
@ -1,48 +1,109 @@
|
||||
/**
|
||||
* Reloads a module (event, function, or command) in the client
|
||||
* @param {Object} client - The Discord client instance
|
||||
* @param {string} category - The category of the module ('events', 'functions', or command name)
|
||||
* @param {string} [name] - The name of the module (required for events and functions)
|
||||
* @returns {string} Status message indicating success or failure
|
||||
*/
|
||||
function Reload(client, category, name) {
|
||||
if (category === "events") {
|
||||
if (!name) return 'Provide an event name to reload!'
|
||||
try {
|
||||
const evtName = name;
|
||||
delete require.cache[require.resolve(`../events/${name}.js`)];
|
||||
const pull = require(`../events/${name}`);
|
||||
// Input validation
|
||||
if (!client) throw new Error('Client instance is required');
|
||||
if (!category) return 'Provide a category/command name to reload!';
|
||||
|
||||
client.off(evtName, typeof client._events[evtName] == 'function' ? client._events[evtName] : client._events[evtName][0])
|
||||
client.event.delete(evtName)
|
||||
const reloaders = {
|
||||
events: () => reloadEvent(client, name),
|
||||
functions: () => reloadFunction(client, name),
|
||||
default: () => reloadCommand(client, category)
|
||||
};
|
||||
|
||||
client.on(evtName, pull.bind(null, client))
|
||||
client.event.set(evtName, pull.bind(null, client))
|
||||
} catch (e) {
|
||||
return `Couldn't reload: **${category}/${name}**\n**Error**: ${e.message}`
|
||||
}
|
||||
return `Reloaded event: **${name}**.js`
|
||||
}
|
||||
return (reloaders[category] || reloaders.default)();
|
||||
}
|
||||
|
||||
if (category === "functions") {
|
||||
if (!name) return 'Provide a function name to reload!'
|
||||
try {
|
||||
const evtName = name;
|
||||
delete require.cache[require.resolve(`../functions/${name}.js`)];
|
||||
const pull = require(`../functions/${name}`);
|
||||
|
||||
client.functions.delete(evtName)
|
||||
client.functions.set(evtName, pull)
|
||||
} catch (e) {
|
||||
return `Couldn't reload: **functions/${name}**\n**Error**: ${e.message}`
|
||||
}
|
||||
return `Reloaded function: **${name}**.js`
|
||||
}
|
||||
/**
|
||||
* Reloads an event module
|
||||
* @param {Object} client - The Discord client instance
|
||||
* @param {string} name - The name of the event
|
||||
* @returns {string} Status message
|
||||
*/
|
||||
function reloadEvent(client, name) {
|
||||
if (!name) return 'Provide an event name to reload!';
|
||||
|
||||
try {
|
||||
if (!category) return 'Provide a command name to reload!'
|
||||
delete require.cache[require.resolve(`../commands/${category}.js`)];
|
||||
const pull = require(`../commands/${category}.js`);
|
||||
if (client.commands.get(category).config.aliases) client.commands.get(category).config.aliases.forEach(a => client.aliases.delete(a));
|
||||
client.commands.delete(category);
|
||||
client.commands.set(category, pull);
|
||||
if (client.commands.get(category).config.aliases) client.commands.get(category).config.aliases.forEach(a => client.aliases.set(a, category));
|
||||
return `Reloaded command: **commands/${category}**.js`
|
||||
} catch (e) {
|
||||
return `Couldn't reload: **commands/${category}**\n**Error**: ${e.message}`
|
||||
const eventPath = `../events/${name}.js`;
|
||||
delete require.cache[require.resolve(eventPath)];
|
||||
const eventModule = require(eventPath);
|
||||
|
||||
// Remove existing event listener
|
||||
const existingHandler = client._events[name];
|
||||
if (existingHandler) {
|
||||
client.off(name, typeof existingHandler === 'function' ? existingHandler : existingHandler[0]);
|
||||
}
|
||||
client.event.delete(name);
|
||||
|
||||
// Add new event listener
|
||||
const boundHandler = eventModule.bind(null, client);
|
||||
client.on(name, boundHandler);
|
||||
client.event.set(name, boundHandler);
|
||||
|
||||
return `Reloaded event: **${name}**.js`;
|
||||
} catch (error) {
|
||||
return `Couldn't reload: **events/${name}**\n**Error**: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads a function module
|
||||
* @param {Object} client - The Discord client instance
|
||||
* @param {string} name - The name of the function
|
||||
* @returns {string} Status message
|
||||
*/
|
||||
function reloadFunction(client, name) {
|
||||
if (!name) return 'Provide a function name to reload!';
|
||||
|
||||
try {
|
||||
const functionPath = `../functions/${name}.js`;
|
||||
delete require.cache[require.resolve(functionPath)];
|
||||
const functionModule = require(functionPath);
|
||||
|
||||
client.functions.delete(name);
|
||||
client.functions.set(name, functionModule);
|
||||
|
||||
return `Reloaded function: **${name}**.js`;
|
||||
} catch (error) {
|
||||
return `Couldn't reload: **functions/${name}**\n**Error**: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads a command module
|
||||
* @param {Object} client - The Discord client instance
|
||||
* @param {string} commandName - The name of the command
|
||||
* @returns {string} Status message
|
||||
*/
|
||||
function reloadCommand(client, commandName) {
|
||||
try {
|
||||
const commandPath = `../commands/${commandName}.js`;
|
||||
delete require.cache[require.resolve(commandPath)];
|
||||
const commandModule = require(commandPath);
|
||||
|
||||
// Handle aliases
|
||||
const existingCommand = client.commands.get(commandName);
|
||||
if (existingCommand?.config?.aliases) {
|
||||
existingCommand.config.aliases.forEach(alias => client.aliases.delete(alias));
|
||||
}
|
||||
|
||||
// Update command
|
||||
client.commands.delete(commandName);
|
||||
client.commands.set(commandName, commandModule);
|
||||
|
||||
// Update aliases if they exist
|
||||
if (commandModule.config?.aliases) {
|
||||
commandModule.config.aliases.forEach(alias => client.aliases.set(alias, commandName));
|
||||
}
|
||||
|
||||
return `Reloaded command: **commands/${commandName}**.js`;
|
||||
} catch (error) {
|
||||
return `Couldn't reload: **commands/${commandName}**\n**Error**: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user