Compare commits

..

3 Commits

Author SHA1 Message Date
Ryahn
2d0a6626a8 Refactor done 2025-05-12 15:20:40 -05:00
Ryahn
88d985bb1f Added additional config to commands 2025-05-12 15:10:29 -05:00
Ryahn
bc39fe3f7a Updated gaydar and refactor 2025-05-12 15:08:23 -05:00
37 changed files with 1908 additions and 841 deletions

BIN
assets/gaydar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -7,6 +7,8 @@ module.exports = {
cooldown: 5000,
available: true,
permissions: [],
roles: [],
dm: false,
aliases: ['av']
},
run: async (client, message, args, db) => {

View File

@ -1,5 +1,7 @@
const fs = require("fs");
const path = require("path");
const Embed = require("../functions/embed")
// const { } = require('revolt.js')
const Uploader = require("revolt-uploader");
module.exports = {
config: {
@ -8,33 +10,90 @@ module.exports = {
cooldown: 5000,
available: true,
permissions: [],
roles: [],
dm: false,
aliases: ['gd']
},
run: async (client, message, args, db) => {
try {
function randomInteger(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(message.mentionIds[0]).then((user) => {
return console.log();
let targetUser;
// Check if there's a mentioned user
if (message.mentionIds && message.mentionIds.length > 0) {
try {
const server = await client.servers.fetch(message.server.id);
const member = await server.fetchMember(message.mentionIds[0]);
if (!member || !member.id || !member.id.user) {
throw new Error("Invalid member structure");
}
// Fetch the user using the user ID from the member object
targetUser = await client.users.fetch(member.id.user);
if (!targetUser || !targetUser.username) {
throw new Error("Could not fetch user information");
}
} catch (fetchError) {
console.error("Fetch error details:", fetchError.message);
return message.reply({
embeds: [new Embed()
.setDescription("Could not find the mentioned user.")
.setColor(`#FF0000`)]
}, false);
}
} else {
// Use the command author if no user is mentioned
targetUser = message.author;
}
if (!targetUser) {
return message.reply({
embeds: [new Embed()
.setDescription("Could not determine the target user.")
.setColor(`#FF0000`)]
}, false);
}
const amount = randomInteger(1, 100);
const imagePath = path.join(__dirname, "../assets/gaydar.jpg");
// Check if file exists
if (!fs.existsSync(imagePath)) {
return message.reply({
embeds: [new Embed()
.setDescription("Error: Required image file not found.")
.setColor(`#FF0000`)]
}, false);
}
// Upload the image as an attachment
const attachment = await client.Uploader.uploadFile(imagePath, "gaydar.jpg");
// Send the message with the attachment
await message.channel.sendMessage({
content: `#### ${targetUser.username} is ${amount}% gay.`,
attachments: [attachment]
});
// const target = member.user.username;
// return console.log(member)
// let amount = randomInteger(1, 100);
// const embed = {
// description: `#### ${target} is ${amount}% gay.`,
// colour: "#ff8080",
// image: 'https://overlord.lordainz.xyz/f/gaydar.jpg'
// }
} catch (error) {
console.log(`${Date(Date.now().toString()).slice(0, 25)}`);
console.log("User: " + message.author.username + ` [${message.authorId}] ` + " | Command: gaydar | Args: " + (args?.join(" ") || "NONE"));
console.log(error.message);
// await message.channel.sendMessage({ content: "", embeds: [embed] }).catch(err => {
// console.log(`${Date(Date.now().toString()).slice(0, 25)}`);
// console.log("User: " + message.author.username + ` [${message.authorId}] ` + " | Command: gardar | Args: " + (args?.join(" ") || "NONE"))
// console.log(err.message);
// return;
// });
return message.reply({
embeds: [new Embed()
.setDescription("An error occurred while processing the command.")
.setColor(`#FF0000`)]
}, false);
}
},
};

View File

@ -9,6 +9,8 @@ module.exports = {
cooldown: 5000,
available: true,
permissions: [],
roles: [],
dm: false,
aliases: ['tenor', 'giphy', 'gify']
},
run: async (client, message, args, db) => {

View File

@ -7,6 +7,8 @@ module.exports = {
cooldown: 5000,
available: true,
permissions: [],
roles: [],
dm: false,
aliases: ['h']
},
run: async (client, message, args, db) => {

View File

@ -9,6 +9,8 @@ module.exports = {
cooldown: 10000,
available: true,
permissions: [],
roles: [],
dm: false,
aliases: ["stats"]
},
run: async (client, message, args, db) => {

View File

@ -11,6 +11,8 @@ module.exports = {
cooldown: 10000,
available: true,
permissions: ['ManageServer'],
roles: [],
dm: false,
aliases: ["poll"]
},
run: async (client, message, args, db) => {

View File

@ -7,7 +7,9 @@ module.exports = {
available: true,
usage: true,
permissions: ["ManageServer"],
aliases: []
aliases: [],
roles: [],
dm: false,
},
run: async (client, message, args, db) => {
const embed = new Embed()

View File

@ -5,7 +5,9 @@ module.exports = {
cooldown: 0,
available: "Owner",
permissions: [],
aliases: ["r"]
aliases: ["r"],
roles: [],
dm: false,
},
run: async (client, message, args) => {
if (!client.config.owners.includes(message.authorId)) return;

View File

@ -11,6 +11,8 @@ module.exports = {
cooldown: 10000,
available: true,
permissions: ['ManageServer'],
roles: [],
dm: false,
aliases: ["r7"]
},
run: async (client, message, args, db) => {

View File

@ -8,7 +8,9 @@ module.exports = {
cooldown: 5000,
available: true,
permissions: [],
aliases: ['ub']
aliases: ['ub'],
roles: [],
dm: false,
},
run: async (client, message, args, db) => {
let query = args.join(' ');

View File

@ -1,10 +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 logger = require('../functions/logger');
const audit = require('../functions/audit');
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

View File

@ -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);

View File

@ -1,200 +1,290 @@
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) {
/**
* 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);
message.delete().catch(() => { });
client.messages.get(collector?.oldMessageId)?.delete().catch(() => { })
await Promise.all([
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) => {
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 return;
} else if (emojiId === client.config.emojis.cross) {
}
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"))] },);
} 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();
return message.reply({
embeds: [new Embed().setColor("#A52F05").setDescription(client.translate.get(db.language, "Events.messageReactionAdd.deleteCollector"))]
});
}
} 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) });
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
});
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();
}
} else if (paginateCheck && paginateCheck.message == message.id) {
let pages = paginateCheck.pages;
let page = paginateCheck.page;
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) {
message.edit({
embeds: [pages[0]]
}).catch(() => { });
return paginateCheck.page = 0
} else {
return;
await message.edit({ embeds: [pages[0]] }).catch(() => {});
newPage = 0;
}
break;
case "⬅️":
if (pages[page - 1]) {
message.edit({
embeds: [pages[--page]]
}).catch(() => { });
return paginateCheck.page = paginateCheck.page - 1
} else {
return;
await message.edit({ embeds: [pages[--newPage]] }).catch(() => {});
}
break;
case "➡️":
if (pages[page + 1]) {
message.edit({
embeds: [pages[++page]]
}).catch(() => { });
return paginateCheck.page = paginateCheck.page + 1
} else {
return;
await message.edit({ embeds: [pages[++newPage]] }).catch(() => {});
}
break;
case "⏩":
if (page !== pages.length) {
message.edit({
embeds: [pages[pages.length - 1]]
}).catch(() => { });
return paginateCheck.page = pages.length - 1
} else {
return;
await message.edit({ embeds: [pages[pages.length - 1]] }).catch(() => {});
newPage = pages.length - 1;
}
break;
}
} 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}`)
}
});
let convert = emojis.findIndex(e => e.name === emojiId);
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();
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(() => { });
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({
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(() => {});
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 (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);
// console.log(user.avatar.id ? user.avatar.createFileURL() : 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar')
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);
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;
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 });
db.save();
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);
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();
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}` : ``}`)
.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.updateOne({ ended: true, endDate });
await db.save();
return await client.api.patch(`/channels/${db.channelId}/messages/${db.messageId}`, { "embeds": [noUsers] });
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)];
const 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 })
db.picking = db.picking.filter(object => object.userID !== winner.userID);
db.pickedWinners.push({ id: winner.userID });
}
}
await db.updateOne({ ended: true, endDate: endDate })
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}` : ``}`)
.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)
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);
}
} 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))) {
}
/**
* 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 = [];
db2.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
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;
@ -207,14 +297,66 @@ module.exports = async (client, message, userId, emojiId) => {
setTimeout(() => client.reactions.delete(userId), 3000);
dataRoles.push(role.role);
await member.edit({ roles: dataRoles }).catch(() => { error = true })
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 (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);
}
};

View File

@ -1,80 +1,133 @@
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) {
/**
* 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) {
collector.rolesDone = collector.rolesDone.filter(object => object.emoji != emojiId);
if (!emoji) return;
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);
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(() => { });
}
} 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);
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}}}$`))] };
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(() => { });
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(() => {});
}
} 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) {
const convert = EMOJIS.findIndex(e => e.name === emojiId);
if (convert !== 0 && convert === 10 || convert === -1) return;
if (!pollCheck.users.includes(userId)) return;
let tooMuch = [];
if (pollCheck.poll.options.description.length > 80) tooMuch.push(`**${client.translate.get(pollCheck.language, "Events.messageReactionRemove.title")}**: ${pollCheck.poll.options.description}`)
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) => {
i++
if (e.length > 70) {
tooMuch.push(`**${i}.** ${e}`)
tooMuch.push(`**${i + 1}.** ${e}`);
}
});
pollCheck.users = pollCheck.users.filter(object => object != userId);
const user = (client.users.get(userId)) || await client.users.fetch(userId);
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 {
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 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;
const db = await Giveaways.findOne({ messageId: message.id });
if (db && !db.ended) {
if (emojiId === client.config.emojis.confetti) {
if (emojiId === client.config.emojis.confetti && !db.ended) {
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)
db.users = db.users.filter(object => object.userID !== userId);
db.picking = db.picking.filter(object => object.userID !== userId);
await db.save();
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(() => { });
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.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(() => {});
}
} 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))) {
}
/**
* 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 = [];
db2.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
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;
@ -86,15 +139,61 @@ module.exports = async (client, message, userId, emojiId) => {
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 })
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(() => { });
}
}
}
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);
}
};

View File

@ -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)

View File

@ -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)

View File

@ -1,3 +1,4 @@
const path = require("path");
let type;
module.exports = async (client, member, memberOld) => {
// Work in progress

View File

@ -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();

View File

@ -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 {
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) { }
const polls = await db.find();
if (!polls?.length) return;
poll.deleteOne({ messageId: poll.messageId });
}, i * 700);
// Process polls concurrently with a small delay between each
await Promise.all(polls.map(async (poll, index) => {
try {
// Add small delay between processing each poll to prevent rate limiting
await new Promise(resolve => setTimeout(resolve, index * 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);
}
}

View File

@ -1,23 +1,72 @@
const db = require("../models/guilds");
const path = require("path");
const db = require(path.join(__dirname, "../models/guilds"));
/**
* Checks and validates role reaction messages across all guilds
* @param {Object} client - Discord client instance
* @returns {Promise<void>}
*/
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) });
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;
}
await client.channels.get(role.chanId)?.fetchMessage(role.msgId).catch(() => { });
}, ii * 700);
// 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
});
}, i * 600);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -1,68 +1,75 @@
/**
* 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')}]`)}`;
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}`),
/**
* 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)}`;
startLog: (identifier) => console.log(`${timestamp()} ${getTag('DEBUG')} ${chalk.greenBright('[START]')} ${identifier}`),
endLog: (identifier) => console.log(`${timestamp()} ${getTag('DEBUG')} ${chalk.redBright('[ END ]')} ${identifier}`),
/**
* 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}]`);
};
timestamp,
getExecutionTime: (hrtime) => {
const timeSinceHrMs = (
process.hrtime(hrtime)[0] * 1000
+ hrtime[1] / 1000000
).toFixed(2);
return `${chalk.yellowBright(
(timeSinceHrMs / 1000).toFixed(2))
} seconds (${chalk.yellowBright(timeSinceHrMs)} ms)`;
},
/**
* 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}`;
};
printErr: (err) => {
if (!(err instanceof Error)) {
console.error(err)
return;
}
/**
* 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);
console.error(
!err.stack
? chalk.red(err)
: err.stack
return err.stack
.split('\n')
.map((msg, index) => {
if (index === 0) {
return chalk.red(msg);
}
if (index === 0) return chalk.red(msg);
const isFailedFunctionCall = index === 1;
const traceStartIndex = msg.indexOf('(');
@ -79,7 +86,38 @@ module.exports = {
: `${chalk.greenBright(functionCall)} ${chalk.grey(trace)}`
}`;
})
.join('\n')
)
.join('\n');
};
module.exports = {
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}`),
timestamp,
getExecutionTime: (hrtime) => {
const timeSinceHrMs = (
process.hrtime(hrtime)[0] * 1000 +
hrtime[1] / 1000000
).toFixed(2);
return `${chalk.yellowBright((timeSinceHrMs / 1000).toFixed(2))} seconds (${chalk.yellowBright(timeSinceHrMs)} ms)`;
},
printErr: (err) => {
if (!(err instanceof Error)) {
console.error(err);
return;
}
console.error(formatErrorStack(err));
}
};

View File

@ -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;

View File

@ -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) => {
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;

View File

@ -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();
// Set poll end timer
setTimeout(async () => {
if (!this.client.polls.get(message.id)) return;
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);
}
textHeight(text, ctx, m) {
let metrics = m || ctx.measureText(text);
return metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
this.client.polls.delete(message.id);
await PollDB.findOneAndDelete({ messageId: message.id });
}, this.time);
}
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];
}
/**
* 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;
}
/**
* 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 + 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.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();
}
if (fill) ctx.fill();
if (stroke) ctx.stroke();
}
/**
* Update the poll canvas
*/
async update() {
let roundRect = this.roundRect;
let textHeight = this.textHeight;
const { WIDTH, PADDING, BAR_HEIGHT, COLORS, FONT_SIZES } = CANVAS_CONFIG;
const height = this.size.canvas;
var width = 600, height = this.size.canvas, padding = 10;
const canvas = Canvas.createCanvas(width, height);
// 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();
}
}

View File

@ -1,5 +1,4 @@
const crypt = require("crypto");
// const FlakeId = require('flakeid');
module.exports = {
/**

View File

@ -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}`;
}
}

View File

@ -1,12 +1,50 @@
const { readdirSync } = require("fs")
const { join } = require("path")
const color = require("../functions/colorCodes")
module.exports = (client) => {
const commands = readdirSync(`./commands/`).filter(d => d.endsWith('.js'));
for (let file of commands) {
let pull = require(`../commands/${file}`);
client.commands.set(pull.config.name, pull);
if (pull.config.aliases) pull.config.aliases.forEach(a => client.aliases.set(a, pull.config.name));
};
console.log(color("%", `%b[Command_Handler]%7 :: Loaded %e${client.commands.size} %7commands`));
/**
* Loads and registers all command files from the commands directory
* @param {Object} client - The Discord client instance
* @returns {Promise<void>}
*/
module.exports = async (client) => {
try {
const commandsPath = join(__dirname, "..", "commands")
const commandFiles = readdirSync(commandsPath).filter(file => file.endsWith(".js"))
let loadedCommands = 0
let failedCommands = 0
for (const file of commandFiles) {
try {
const command = require(join(commandsPath, file))
// Validate command structure
if (!command.config?.name) {
console.error(color("%", `%r[Command_Handler]%7 :: Command in ${file} is missing required config.name property`))
failedCommands++
continue
}
// Register command and aliases
client.commands.set(command.config.name, command)
if (command.config.aliases?.length) {
command.config.aliases.forEach(alias => client.aliases.set(alias, command.config.name))
}
loadedCommands++
} catch (error) {
console.error(color("%", `%r[Command_Handler]%7 :: Failed to load command ${file}: ${error.message}`))
failedCommands++
}
}
console.log(color("%", `%b[Command_Handler]%7 :: Successfully loaded %e${loadedCommands}%7 commands`))
if (failedCommands > 0) {
console.warn(color("%", `%y[Command_Handler]%7 :: Failed to load %r${failedCommands}%7 commands`))
}
} catch (error) {
console.error(color("%", `%r[Command_Handler]%7 :: Critical error: ${error.message}`))
throw error // Re-throw to handle at higher level
}
}

View File

@ -1,11 +1,24 @@
const { connect } = require("mongoose").set('strictQuery', true);
const color = require("../functions/colorCodes")
const fs = require('fs');
const path = require('path');
module.exports = class DatabaseHandler {
constructor(connectionString) {
this.cache = new Map();
this.guildModel = require('../models/guilds');
this.connectionString = connectionString;
this.models = {};
this.initializeModels();
}
initializeModels() {
const modelsPath = path.join(__dirname, '../models');
const modelFiles = fs.readdirSync(modelsPath).filter(file => file.endsWith('.js'));
for (const file of modelFiles) {
const modelName = path.parse(file).name;
this.models[modelName] = require(path.join(modelsPath, file));
}
}
cacheSweeper(client) {
@ -46,17 +59,17 @@ module.exports = class DatabaseHandler {
}
async fetchGuild(guildId, createIfNotFound = false) {
const fetched = await this.guildModel.findOne({ id: guildId });
const fetched = await this.models.guilds.findOne({ id: guildId });
if (fetched) return fetched;
if (!fetched && createIfNotFound) {
await this.guildModel.create({
await this.models.guilds.create({
id: guildId,
language: 'en_EN',
botJoined: Date.now() / 1000 | 0,
});
return this.guildModel.findOne({ id: guildId });
return this.models.guilds.findOne({ id: guildId });
} return null;
}
@ -78,7 +91,7 @@ module.exports = class DatabaseHandler {
async deleteGuild(guildId, onlyCache = false) {
if (this.cache.has(guildId)) this.cache.delete(guildId);
return !onlyCache ? this.guildModel.deleteMany({ id: guildId }) : true;
return !onlyCache ? this.models.guilds.deleteMany({ id: guildId }) : true;
}
async updateGuild(guildId, data = {}, createIfNotFound = false) {
@ -91,13 +104,13 @@ module.exports = class DatabaseHandler {
this.cache.set(guildId, data);
return this.guildModel.updateOne({
return this.models.guilds.updateOne({
id: guildId,
}, data);
} return null;
}
async getAll() {
return this.guildModel.find();
return this.models.guilds.find();
}
};

View File

@ -1,12 +1,44 @@
const { readdirSync } = require("fs")
const path = require("path")
const color = require("../functions/colorCodes")
module.exports = (client) => {
const events = readdirSync(`./events/`).filter(d => d.endsWith('.js'));
for (let file of events) {
let evt = require(`../events/${file}`);
client.event.set(file.split(".")[0], evt.bind(null, client));
client.on(file.split('.')[0], evt.bind(null, client));
};
console.log(color("%", `%b[Event_Handler]%7 :: Loaded %e${client.event.size} %7events`));
};
/**
* Loads and registers all event handlers for the client
* @param {Object} client - The Discord client instance
* @returns {Promise<void>}
*/
module.exports = async (client) => {
try {
const eventsPath = path.join(__dirname, "..", "events")
const eventFiles = readdirSync(eventsPath)
.filter(file => file.endsWith(".js"))
const loadedEvents = await Promise.all(
eventFiles.map(async (file) => {
try {
const eventName = path.parse(file).name
const event = require(path.join(eventsPath, file))
// Register the event
client.event.set(eventName, event.bind(null, client))
client.on(eventName, event.bind(null, client))
return eventName
} catch (error) {
console.error(color("%", `%r[Event_Handler]%7 :: Failed to load event %e${file}%7: ${error.message}`))
return null
}
})
)
const successfulEvents = loadedEvents.filter(Boolean)
console.log(color("%", `%b[Event_Handler]%7 :: Successfully loaded %e${successfulEvents.length}%7 events`))
if (successfulEvents.length < eventFiles.length) {
console.warn(color("%", `%y[Event_Handler]%7 :: %r${eventFiles.length - successfulEvents.length}%7 events failed to load`))
}
} catch (error) {
console.error(color("%", `%r[Event_Handler]%7 :: Critical error: ${error.message}`))
throw error // Re-throw to handle it in the main application
}
}

View File

@ -1,11 +1,40 @@
const { readdirSync } = require("fs")
const { join } = require("path")
const logger = require("../functions/logger")
module.exports = (client) => {
const functions = readdirSync(`./functions`).filter(d => d.endsWith('.js'));
for (let file of functions) {
let evt = require(`../functions/${file}`);
client.functions.set(file.split(".")[0], evt);
};
logger.event('Function_Handler', `Loaded ${client.functions.size} functions`);
/**
* Loads and registers all function modules from the functions directory
* @param {Object} client - The client instance
* @returns {Promise<void>}
*/
module.exports = async (client) => {
try {
if (!client || !client.functions) {
throw new Error('Invalid client object or missing functions collection');
}
const functionsDir = join(__dirname, '..', 'functions');
const functions = readdirSync(functionsDir)
.filter(file => file.endsWith('.js') && file !== 'logger.js');
const loadedFunctions = await Promise.all(
functions.map(async (file) => {
try {
const functionName = file.split('.')[0];
const functionModule = require(join(functionsDir, file));
client.functions.set(functionName, functionModule);
return functionName;
} catch (error) {
logger.error('Function_Handler', `Failed to load function ${file}: ${error.message}`);
return null;
}
})
);
const successfulLoads = loadedFunctions.filter(Boolean).length;
logger.event('Function_Handler', `Successfully loaded ${successfulLoads}/${functions.length} functions`);
} catch (error) {
logger.error('Function_Handler', `Failed to initialize function handler: ${error.message}`);
throw error;
}
};

125
index.js
View File

@ -2,57 +2,102 @@ const { Client } = require("revolt.js");
const { Collection } = require('@discordjs/collection');
const { token, mongoDB, api } = require("./botconfig.json");
const logger = require('./functions/logger');
const checkPolls = require('./functions/checkPolls')
const checkPolls = require('./functions/checkPolls');
const color = require("./functions/colorCodes");
const client = new Client({ baseURL: api });
const Uploader = require("revolt-uploader");
const fetch = require("wumpfetch");
const TranslationHandler = require('./handlers/translation');
const DatabaseHandler = require('./handlers/database');
client.Uploader = new Uploader(client);
client.config = require("./config");
client.translate = new TranslationHandler();
client.logger = require('./functions/logger');
client.botConfig = require("./botconfig.json");
class Bot {
constructor(config) {
this.config = config;
this.client = new Client({ baseURL: config.api });
this.initializeCore();
this.initializeCollections();
this.setupErrorHandling();
}
client.database = new DatabaseHandler(mongoDB);
client.database.connectToDatabase();
client.database.cacheSweeper(client);
client.database.guildSweeper(client);
initializeCore() {
this.client.Uploader = new Uploader(this.client);
this.client.config = require("./config");
this.client.translate = new TranslationHandler();
this.client.logger = logger;
this.client.botConfig = this.config;
}
["reactions", "paginate", "timeout", "polls", "used", "messageCollector", "messageEdit"].forEach(x => client[x] = new Map());
["aliases", "commands", "event", "functions"].forEach(x => client[x] = new Collection());
["command", "event", "function"].forEach(x => require(`./handlers/${x}`)(client));
initializeCollections() {
const collections = ["aliases", "commands", "event", "functions"];
const maps = ["reactions", "paginate", "timeout", "polls", "used", "messageCollector", "messageEdit"];
client.once("ready", async () => {
logger.success('Bot Ready', `${client.user.username} is ready`);
collections.forEach(x => this.client[x] = new Collection());
maps.forEach(x => this.client[x] = new Map());
}
//client.database.connectToDatabase();
//client.database.cacheSweeper(client);
//client.database.guildSweeper(client);
async initializeDatabase() {
try {
const db = new DatabaseHandler(this.config.mongoDB);
await db.connectToDatabase();
await checkPolls(client);
this.client.database = db;
this.client.models = db.models;
});
db.cacheSweeper(this.client);
db.guildSweeper(this.client);
logger.success('Database', 'Successfully connected to database');
} catch (error) {
logger.error('Database Error', error);
throw error;
}
}
process.on("unhandledRejection", (reason, p) => {
console.log(color("%", "%4[Error_Handling] :: Unhandled Rejection/Catch%c"));
console.log(reason);
console.log(p)
});
process.on("uncaughtException", (err, origin) => {
console.log(color("%", "%4[Error_Handling] :: Uncaught Exception/Catch%c"));
console.log(err);
console.log(origin)
});
process.on("uncaughtExceptionMonitor", (err, origin) => {
console.log(color("%", "%4[Error_Handling] :: Uncaught Exception/Catch (MONITOR)%c"));
console.log(err);
console.log(origin)
});
setupErrorHandling() {
const errorTypes = {
"unhandledRejection": "Unhandled Rejection/Catch",
"uncaughtException": "Uncaught Exception/Catch",
"uncaughtExceptionMonitor": "Uncaught Exception/Catch (MONITOR)"
};
client.loginBot(token);
Object.entries(errorTypes).forEach(([event, message]) => {
process.on(event, (error, origin) => {
logger.error('Error Handling', `${message}: ${error.message}`);
console.log(color("%", `%4[Error_Handling] :: ${message}%c`));
console.log(error);
if (origin) console.log(origin);
});
});
}
async initializeHandlers() {
try {
["command", "event", "function"].forEach(x => require(`./handlers/${x}`)(this.client));
logger.success('Handlers', 'Successfully initialized all handlers');
} catch (error) {
logger.error('Handler Error', error);
throw error;
}
}
setupEventListeners() {
this.client.once("ready", async () => {
logger.success('Bot Ready', `${this.client.user.username} is ready`);
await checkPolls(this.client);
});
}
async start() {
try {
await this.initializeDatabase();
await this.initializeHandlers();
this.setupEventListeners();
await this.client.loginBot(this.config.token);
} catch (error) {
logger.error('Startup Error', error);
process.exit(1);
}
}
}
// Start the bot
const bot = new Bot({ token, mongoDB, api });
bot.start();

10
package-lock.json generated
View File

@ -16,7 +16,7 @@
"mongoose": "^7.2.0",
"nanoid": "3.3.4",
"node-fetch-commonjs": "^3.3.2",
"revolt-uploader": "^1.1.1",
"revolt-uploader": "^1.1.5",
"revolt.js": "npm:revolt.js-update@^7.0.0-beta.9",
"screen": "^0.2.10",
"wumpfetch": "^0.3.1"
@ -1227,10 +1227,10 @@
}
},
"node_modules/revolt-uploader": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/revolt-uploader/-/revolt-uploader-1.1.4.tgz",
"integrity": "sha512-hnUCc1grg6Yq1J2q0HcsbfbWCvHIR6hPHzk3/75EjWEjP7bPYwAAVsHOwwtfdMtwMUPN9EAmBj3NvU4t+o1JJw==",
"license": "ISC",
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/revolt-uploader/-/revolt-uploader-1.1.6.tgz",
"integrity": "sha512-teMUhz/QJDqx/sXJFFReur8kR/KUSl2A5dYy9hLY/KLLgpaZw+el4bV+TgFb0vPtpM4hg7mLn44gLiuBR7kD7g==",
"license": "MIT",
"dependencies": {
"form-data": "^4.0.0",
"node-fetch": "^2.6.7"

View File

@ -15,7 +15,7 @@
"mongoose": "^7.2.0",
"nanoid": "3.3.4",
"node-fetch-commonjs": "^3.3.2",
"revolt-uploader": "^1.1.1",
"revolt-uploader": "^1.1.5",
"revolt.js": "npm:revolt.js-update@^7.0.0-beta.9",
"screen": "^0.2.10",
"wumpfetch": "^0.3.1"