This commit is contained in:
Ryahn 2025-05-12 12:57:59 -05:00
commit ddd5972f72
58 changed files with 4495 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/node_modules
/.babelrc
/yarn.lock
/botconfig.json

38
botconfig.sample.json Normal file
View File

@ -0,0 +1,38 @@
{
"token": "",
"api": "https://chat.f95.io/api",
"mongoDB": "mongodb://127.0.0.1:27017/zonies",
"defaultGuildId": "01HAT8SAMPMRD554Z9942EEHJ7",
"owners": ["01HATCHHMPTJBHH534H6VFYKK3"],
"prefix": "!",
"roles": [
{
"mod": "01HB2G8GJHJS1ZA8XSQ6PECG12",
"staff": "01HATA3EAVF0H5T9QSD89Z4C9K",
"jrmod": "01HB2H2JFY9PWDW7GXRC1SBY4C",
"uploader": "01HF78H660P67QXEZPZ9MD1HBZ",
"jruploader": "01HF78RZDE32E5CXKS4P85PY54"
}
],
"rule7": {
"apiKey": ""
},
"emojis": {
"one": "1⃣",
"two": "2⃣",
"three": "3⃣",
"four": "4⃣",
"five": "5⃣",
"six": "6⃣",
"seven": "7⃣",
"eight": "8⃣",
"nine": "9⃣",
"ten": "🔟",
"confetti": "🎊",
"stop": "🛑",
"check": "✅",
"cross": "❌"
},
"tenor_api_key": ""
}

26
commands/avatar.js Normal file
View File

@ -0,0 +1,26 @@
const Embed = require("../functions/embed")
module.exports = {
config: {
name: "avatar",
usage: true,
cooldown: 5000,
available: true,
permissions: [],
aliases: ['av']
},
run: async (client, message, args, db) => {
if (message.mentions?.length <= 0) return message.reply('Must mention a user!');
const targetName = (message.mentions?.length >= 1 ? message.mentions[0]?.username : message.author.username)
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(message.mentionIds[0]);
const target = message.mentions?.length >= 1 ? member.user.avatar.createFileURL({ max_side: 4096 }, true) ? member.user.avatar.createFileURL({ max_side: 4096 }, true) : message.mentions[0]?.defaultAvatarURL : member.user.avatar.createFileURL({ size: 4096 }, true) ? member.user.avatar.createFileURL({ size: 4096 }, true) : member.user.avatar.createFileURL({ size: 4096 }, true) ? message.author.avatarURL({ size: 4096 }, true) : message.author.avatarURL({ size: 4096 }, true) ? message.author.avatarURL({ size: 4096 }, true) : message.member.user.defaultAvatarURL
const embed = { description: "Avatar for [" + targetName + "](" + target + ")", colour: "#00FFFF", iconURL: target }
await message.channel.sendMessage({ content: "[ ](" + target + ")", embeds: [embed] }).catch(err => {
console.log(`${Date(Date.now().toString()).slice(0, 25)}`);
console.log("User: " + message.author.username + ` [${message.authorId}] ` + " | Command: avatar | Args: " + (args?.join(" ") || "NONE"))
console.log(err.message);
return;
});
},
};

90
commands/command.js Normal file
View File

@ -0,0 +1,90 @@
const Embed = require("../functions/embed")
const CommandsDB = require('../models/commands');
const { random } = require('../functions/randomStr');
const moment = require('moment');
module.exports = {
config: {
name: "command",
usage: true,
cooldown: 0,
available: true,
permissions: ['ManageServer'],
aliases: ['cmd']
},
run: async (client, message, args, db) => {
/*************************************************
* HELP
*************************************************/
if (args[0] === 'help') {
let embed = new Embed()
.setDescription(`### Custom Commands\n**add**: \`add [name] [content]\`\nEx: \`add soul Fucks his cousin|Is swedish\` or \`add zemax Is the punching bag for uploaders\`\nIf you want to add multiple options to a command use a pipe \`|\`\n\n**delete**: \`delete [name/id]\`\n\n**edit**: \`edit [name/id]\`\n\n**show**: \`show [name/id]\`\n\n`)
.setColor(`#A52F05`);
return message.reply({ embeds: [embed] }, false);
/*************************************************
* ADD
*************************************************/
} else if (args[0] === 'add') {
let name = args[1];
let content = args[2];
const cmd = new CommandsDB();
let check = await CommandsDB.findOne({ name: name }).select("name").lean();
if (check) return message.reply('Command already exists with that name!');
if (content.includes('|')) {
cmd.id = random(5);
cmd.name = name;
cmd.content = JSON.stringify(content.split('|'));
cmd.createdBy = message.member.user.username;
cmd.save();
return message.reply('Command saved!');
}
cmd.id = random(5);
cmd.name = name;
cmd.content = args.slice(2).join(' ');
cmd.createdBy = message.member.user.username;
cmd.save();
return message.reply('Command saved!');
/*************************************************
* FIND
*************************************************/
} else if (args[0] === 'find') {
let check = await (CommandsDB.findOne({ name: args[1] }).select("id name").lean() || CommandsDB.findOne({ id: args[1] }).select("id name").lean());
if (!check) return message.reply('Command doesn\'t exists with that name or ID!');
CommandsDB.findOne({ name: args[1] }).then((data) => {
let embed = new Embed()
.setDescription(`### Custom Commands: ${data.name}\n**ID**: ${data.id}\n**Content**: ${data.content}\n**Created by**: ${data.createdBy}\n**Created at**: ${moment.unix(data.created_at).format('YYYY/MMM/DD HH:MM:ss')} (UTC)`)
.setColor(`#A52F05`);
return message.reply({ embeds: [embed] }, false);
});
/*************************************************
* DELETE
*************************************************/
} else if (args[0] === 'delete') {
let check = await (CommandsDB.findOne({ name: args[1] }).select("id name").lean() || CommandsDB.findOne({ id: args[1] }).select("id name").lean());
console.log(check)
if (!check) return message.reply('Command doesn\'t exists with that name or id!');
CommandsDB.deleteMany({ id: check.id }).exec();
return message.reply('Command Removed!');
/*************************************************
* SHOW
*************************************************/
} else if (args[0] === 'show') {
// let cmds = await CommandsDB.find({}).select('name').lean();
// console.log(cmds.toString().join(','))
let data = CommandsDB.find({}).select('name').then((data) => {
let names = [];
data.forEach((item) => { names.push(item.name) });
message.reply('DM Sent with all available custom commands.');
message.member.user.openDM().then((dm) => { dm.sendMessage(names.join(', ')) });
});
/*************************************************
* NO ARGS
*************************************************/
} else {
return message.reply(`First argument must be add, delete, edit, show, find or help. You put ${args[0]}`, false)
}
},
};

40
commands/gaydar.js Normal file
View File

@ -0,0 +1,40 @@
const Embed = require("../functions/embed")
// const { } = require('revolt.js')
module.exports = {
config: {
name: "gaydar",
usage: true,
cooldown: 5000,
available: true,
permissions: [],
aliases: ['gd']
},
run: async (client, message, args, db) => {
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();
});
// 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'
// }
// 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;
// });
},
};

29
commands/gif.js Normal file
View File

@ -0,0 +1,29 @@
const Embed = require("../functions/embed")
const fetch = require('node-fetch-commonjs')
const config = require('../config')
module.exports = {
config: {
name: "gif",
usage: true,
cooldown: 5000,
available: true,
permissions: [],
aliases: ['tenor', 'giphy', 'gify']
},
run: async (client, message, args, db) => {
if (!args[0]) return message.reply('Usage: !gif SEARCHTERM | Aliases: gif, tneor, giphy, gify');
async function get_gif_url(query) {
// Fetches the gif url
const gif_response = await fetch(`https://tenor.googleapis.com/v2/search?q=${query}&key=${config.tenor_api_key}&limit=1&media_filter=gif`);
// Converts it to json
const gif_data = await gif_response.json();
// Returns the url
return gif_data.results[0].media_formats.gif.url;
}
let url = await get_gif_url(args.join(' '));
return message.reply(url);
},
};

30
commands/help.js Normal file
View File

@ -0,0 +1,30 @@
const Embed = require("../functions/embed")
module.exports = {
config: {
name: "help",
usage: true,
cooldown: 5000,
available: true,
permissions: [],
aliases: ['h']
},
run: async (client, message, args, db) => {
const embed = {
description: `**!avatar**: \`!avatar @USER\`\n**Aliases**: \`!av\`\n
**!command**: \`!command add NAME CONTENT\`\n**Aliases**: \`!cmd\`\n
**!gif**: \`!gif TERM\`\n**Aliases**: \`!gify, !tenor, !giphy\`\n
**!polls**: \`!polls TIME | How are you? | Good | Bad\`\n**Aliases**: \`!poll\`\n
**!urban**: \`!urban TERM\`\n**Aliases**: \`!ub\`\n\n
**Tip**: You can run each command without an argument and it will return how to use it.`, colour: "#00FFFF", title: "Commands Help"
}
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: help | Args: " + (args?.join(" ") || "NONE"))
console.log(err.message);
return;
});
},
};

26
commands/info.js Normal file
View File

@ -0,0 +1,26 @@
const Embed = require("../functions/embed");
const Giveaway = require("../models/giveaways");
const SavedPolls = require("../models/savedPolls");
const { dependencies } = require("../package.json");
module.exports = {
config: {
name: "info",
usage: false,
cooldown: 10000,
available: true,
permissions: [],
aliases: ["stats"]
},
run: async (client, message, args, db) => {
const unixstamp = client.functions.get("fetchTime")(Math.floor(process.uptime() * 1000), client, db.language)
let beforeCall = Date.now();
const polls = await SavedPolls.find();
let dbPing = Date.now() - beforeCall;
const embed = new Embed()
.setDescription(`### ${client.translate.get(db.language, 'Commands.info.start')}\n${client.translate.get(db.language, 'Commands.info.servers')}: ${client.servers.size().toLocaleString()}\n${client.translate.get(db.language, 'Commands.info.giveaways')}: ${(await Giveaway.find()).length.toLocaleString()}\n${client.translate.get(db.language, 'Commands.info.polls')}: ${polls.length.toLocaleString()}\n${client.translate.get(db.language, 'Commands.info.uptime')}: ${unixstamp}\n\n${client.translate.get(db.language, 'Commands.info.ping')}: ${client.events.ping()}ms\n${client.translate.get(db.language, 'Commands.info.database')}: ${dbPing}ms\n${client.translate.get(db.language, 'Commands.info.library')}: Revolt.js | ${dependencies["revolt.js"]}`)
.setColor(`#A52F05`);
message.reply({ embeds: [embed] }, false)
},
};

55
commands/polls.js Normal file
View File

@ -0,0 +1,55 @@
const Embed = require(`../functions/embed`)
const Polls = require(`../functions/poll`)
const dhms = require(`../functions/dhms`);
const PollDB = require("../models/polls");
const SavedPolls = require(`../models/savedPolls`)
module.exports = {
config: {
name: `polls`,
usage: true,
cooldown: 10000,
available: true,
permissions: ['ManageServer'],
aliases: ["poll"]
},
run: async (client, message, args, db) => {
// const check = await PollDB.find({ owner: message.authorId })
// if (check.length === 5) return message.reply({ embeds: [new Embed().setDescription(client.translate.get(db.language, "Commands.polls.tooMany")).setColor(`#FF0000`)] }, false);
const options = args.join(` `).split(`|`).map(x => x.trim()).filter(x => x);
if (!options[0]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validTime")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
const time = dhms(options[0]);
if (!time) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validTime")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (time === 0) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validTime")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (time < 30000) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.longerThan")} \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (time > 2592000000) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.shorterThan")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (!options[1]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validQuestion")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (!options[2]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validOption")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (!options[3]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validOption2")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (options.length >= 13) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.maxOptions")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
const names = [options[2], options[3], options[4] ? options[4] : null, options[5] ? options[5] : null, options[6] ? options[6] : null, options[7] ? options[7] : null, options[8] ? options[8] : null, options[9] ? options[9] : null, options[10] ? options[10] : null, options[11] ? options[11] : null];
const reactions = [client.config.emojis.one, client.config.emojis.two, options[4] ? client.config.emojis.three : null, options[5] ? client.config.emojis.four : null, options[6] ? client.config.emojis.five : null, options[7] ? client.config.emojis.six : null, options[8] ? client.config.emojis.seven : null, options[9] ? client.config.emojis.eight : null, options[10] ? client.config.emojis.nine : null, options[11] ? client.config.emojis.ten : null, client.config.emojis.stop];
const poll = new Polls({ time, client, name: { name: client.translate.get(db.language, "Commands.polls.polls"), description: options[1] }, options: { name: names.filter(a => a) }, owner: message.authorId, lang: db.language })
await poll.update();
let tooMuch = [];
if (options[1].length > 75) tooMuch.push(`**${client.translate.get(db.language, "Commands.polls.title")}**: ${options[1]}`)
names.filter(e => e).forEach((e, i) => {
i++
if (e.length > 70) {
tooMuch.push(`**${i}.** ${e}`)
}
});
message.channel.sendMessage({ embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(poll.canvas.toBuffer(), `Poll.png`)).setColor(`#A52F05`)], interactions: [reactions.filter(e => e)] }).then((msg) => {
poll.start(msg, poll);
});
await (new SavedPolls({
owner: message.authorId,
desc: options[1],
options: { name: names }
}).save());
},
};

22
commands/prefix.js Normal file
View File

@ -0,0 +1,22 @@
const Embed = require("../functions/embed")
module.exports = {
config: {
name: "prefix",
cooldown: 5000,
available: true,
usage: true,
permissions: ["ManageServer"],
aliases: []
},
run: async (client, message, args, db) => {
const embed = new Embed()
.setDescription(`${client.translate.get(db.language, "Commands.prefix.prefix")}: \`${db.prefix}\`\n\n${client.translate.get(db.language, "Commands.prefix.change")} \`${db.prefix}prefix <${client.translate.get(db.language, "Commands.prefix.new")}>\``)
if (!args[0]) return message.reply({ embeds: [embed] }, false);
if (args[0] === db.prefix) return message.reply({ embeds: [embed] }, false);
if (args[0].length > 8) return message.reply(client.translate.get(db.language, "Commands.prefix.tooMany"), false);
await client.database.updateGuild(message.server.id, { prefix: args[0] });
return message.reply(`${client.translate.get(db.language, "Commands.prefix.success")} \`${args[0]}\``, false);
},
};

170
commands/register.js Normal file
View File

@ -0,0 +1,170 @@
const axios = require(`axios`);
const botConfig = require('../botconfig.json');
const { generateUniqueId } = require('../functions/randomStr');
const https = require('https');
const Invites = require('../models/registerInvite');
const Embed = require('../functions/embed');
// Create axios instance outside of module.exports
const makeRequest = axios.create({
baseURL: `https://rule7.zonies.xyz/api/v1`,
headers: {
"Authorization": `Bearer ${botConfig.rule7.apiKey}`
},
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
});
module.exports = {
config: {
name: `register`,
usage: true,
cooldown: 10000,
available: true,
permissions: [''],
roles: ['staff', 'uploader', 'jrmod', 'mod'],
aliases: ["reg"],
dm: true
},
run: async (client, message, args, db) => {
try {
// If not in DM, check for permissions and roles
if (message.channel.type !== 'DirectMessage') {
// Check if user has any of the required roles
const hasRequiredRole = message.member.roles.some(role =>
module.exports.config.roles.includes(role.toLowerCase())
);
if (!hasRequiredRole && !client.config.owners.includes(message.authorId)) {
return message.reply({
embeds: [
new Embed()
.setColor("#FF0000")
.setDescription(`You don't have the required roles to use this command.`)
]
});
}
}
// Check if the command is used in a DM
if (message.channel.type !== 'DirectMessage') {
const inviteCode = generateUniqueId();
const staffRoles = {
is_staff: 0,
is_mod: 0,
is_jrmod: 0,
is_uploader: 0,
is_jruploader: 0
}
let memberRoles = message.member.roles;
// Set staff role values based on member's roles
if (memberRoles.includes(client.botConfig.roles[0].staff)) staffRoles.is_staff = 1;
if (memberRoles.includes(client.botConfig.roles[0].mod)) staffRoles.is_mod = 1;
if (memberRoles.includes(client.botConfig.roles[0].jrmod)) staffRoles.is_jrmod = 1;
if (memberRoles.includes(client.botConfig.roles[0].uploader)) staffRoles.is_uploader = 1;
if (memberRoles.includes(client.botConfig.roles[0].jruploader)) staffRoles.is_jruploader = 1;
const registerInvite = new Invites({
userId: message.author.id,
invite: inviteCode,
staffRoles,
createdAt: Date.now()
});
await registerInvite.save();
return message.reply({
content: `Run this command in a DM to the bot and use this invite code: \`${inviteCode}\`\n
**Usage:** \`!register ${inviteCode} <username> <email> <f95zone profile url>\`\n
**Note: This invite code will expire in 10 minutes.**`
});
}
const [invite, username, email, profileUrl] = args;
if (!invite || !username || !email || !profileUrl) {
return message.reply({
content: `[REG3] Please provide a invite code, username, email, and f95zone profile url. \n\n**Usage:** \`${client.prefix}register <invite> <username> <email> <f95zone profile url>\``
});
}
// DM flow - Use client.database instead of direct model
const registerInvite = await Invites.findOne({ invite: invite });
if (!registerInvite) {
return message.reply({
content: `[REG1] You don't have an invite code. Please run the command outside of DMs.`
});
}
if (registerInvite.createdAt + 10 * 60 * 1000 < Date.now()) {
await registerInvite.deleteOne({ invite: invite });
return message.reply({
content: `[REG2] Your invite code has expired. Please run the command outside of DMs.`
});
}
if (invite !== registerInvite.invite) {
return message.reply({
content: `[REG4] Invalid invite code. Please run the command outside of DMs.`
});
}
let f95zoneId = profileUrl.split('/')[4];
f95zoneId = f95zoneId.split('.')[1];
try {
const { data } = await makeRequest.post(`/bot/register`, {
username,
email,
f95_id: f95zoneId,
is_staff: registerInvite.staffRoles.is_staff,
is_mod: registerInvite.staffRoles.is_mod,
is_jrmod: registerInvite.staffRoles.is_jrmod,
is_uploader: registerInvite.staffRoles.is_uploader,
is_jruploader: registerInvite.staffRoles.is_jruploader
});
if (data.status === 'success') {
await registerInvite.deleteOne({ invite: invite });
return message.reply({
content: `[REG5] You have been registered. Please check your email for verification.`
});
} else if (data.status === 'invalid_email') {
return message.reply({
content: `[REG5.1] An error occurred during registration. ${data.msg}`
});
} else if (data.status === 'username_exists') {
return message.reply({
content: `[REG5.2] An error occurred during registration. ${data.msg}`
});
} else if (data.status === 'invalid_f95_id') {
return message.reply({
content: `[REG5.3] An error occurred during registration. ${data.msg}`
});
} else if (data.status === 'email_exists') {
return message.reply({
content: `[REG5.4] An error occurred during registration. ${data.msg}`
});
} else if (data.status === 'error') {
return message.reply({
content: `[REG5.5] An error occurred during registration. ${data.msg}`
});
}
} catch (apiError) {
return message.reply({
content: `[REG6] An error occurred during registration. Please try again later.\n\`${apiError}\``
});
}
} catch (error) {
console.error('Registration Error:', error);
return message.reply({
content: `[REG7] An unexpected error occurred. Please try again later.\n\`${error}\``
});
}
},
};

31
commands/reload.js Normal file
View File

@ -0,0 +1,31 @@
const Reload = require("../functions/reload")
module.exports = {
config: {
name: "reload",
cooldown: 0,
available: "Owner",
permissions: [],
aliases: ["r"]
},
run: async (client, message, args) => {
if (!client.config.owners.includes(message.authorId)) return;
if (!args[0]) return message.reply("Provide either a category or a command to reload.", false)
if (args[0] === "category") {
let error = [];
let success = [];
if (!args[1]) return message.reply("Provide a category's name to reload it.", false)
client.commands.filter(c => c.config.category === args[1]).map(cc => {
Reload(client, cc.config.category, cc.config.name, args[2])
let check = client.reloadCommand(args[1], cc.config.name)
if (check.includes("Error")) return error.push(check)
else if (check.includes("Reloaded command:")) return success.push("1")
})
return message.reply(`\`\`\`css\nSuccessful Commands: ${success.length}\nErrored Commands: ${error.length} ${error.length > 0 ? "\n" + error.map(c => c).join("\n") : " "}`, false)
}
message.reply(Reload(client, args[0], args[1], args[2]), false)
}
}

55
commands/rule7.js Normal file
View File

@ -0,0 +1,55 @@
const Embed = require(`../functions/embed`)
const Polls = require(`../functions/poll`)
const dhms = require(`../functions/dhms`);
const PollDB = require("../models/rule7");
const SavedPolls = require(`../models/savedPolls`)
module.exports = {
config: {
name: `rule7`,
usage: true,
cooldown: 10000,
available: true,
permissions: ['ManageServer'],
aliases: ["r7"]
},
run: async (client, message, args, db) => {
// const check = await PollDB.find({ owner: message.authorId })
// if (check.length === 5) return message.reply({ embeds: [new Embed().setDescription(client.translate.get(db.language, "Commands.polls.tooMany")).setColor(`#FF0000`)] }, false);
const options = args.join(` `).split(`|`).map(x => x.trim()).filter(x => x);
if (!options[0]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validTime")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
const time = dhms(options[0]);
if (!time) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validTime")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (time === 0) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validTime")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (time < 30000) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.longerThan")} \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (time > 2592000000) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.shorterThan")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (!options[1]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validQuestion")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (!options[2]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validOption")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (!options[3]) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.validOption2")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
if (options.length >= 13) return message.reply({ embeds: [new Embed().setDescription(`${client.translate.get(db.language, "Commands.polls.maxOptions")}: \`${client.config.prefix}polls 5m | ${client.translate.get(db.language, "Commands.polls.example")}\``).setColor(`#FF0000`)] }, false);
const names = [options[2], options[3], options[4] ? options[4] : null, options[5] ? options[5] : null, options[6] ? options[6] : null, options[7] ? options[7] : null, options[8] ? options[8] : null, options[9] ? options[9] : null, options[10] ? options[10] : null, options[11] ? options[11] : null];
const reactions = [client.config.emojis.one, client.config.emojis.two, options[4] ? client.config.emojis.three : null, options[5] ? client.config.emojis.four : null, options[6] ? client.config.emojis.five : null, options[7] ? client.config.emojis.six : null, options[8] ? client.config.emojis.seven : null, options[9] ? client.config.emojis.eight : null, options[10] ? client.config.emojis.nine : null, options[11] ? client.config.emojis.ten : null, client.config.emojis.stop];
const poll = new Polls({ time, client, name: { name: client.translate.get(db.language, "Commands.polls.polls"), description: options[1] }, options: { name: names.filter(a => a) }, owner: message.authorId, lang: db.language })
await poll.update();
let tooMuch = [];
if (options[1].length > 75) tooMuch.push(`**${client.translate.get(db.language, "Commands.polls.title")}**: ${options[1]}`)
names.filter(e => e).forEach((e, i) => {
i++
if (e.length > 70) {
tooMuch.push(`**${i}.** ${e}`)
}
});
message.channel.sendMessage({ embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(poll.canvas.toBuffer(), `Poll.png`)).setColor(`#A52F05`)], interactions: [reactions.filter(e => e)] }).then((msg) => {
poll.start(msg, poll);
});
await (new SavedPolls({
owner: message.authorId,
desc: options[1],
options: { name: names }
}).save());
},
};

19
commands/template.txt Normal file
View File

@ -0,0 +1,19 @@
const Embed = require("../functions/embed")
module.exports = {
config: {
name: "",
usage: true,
cooldown: 5000,
available: true,
permissions: [],
aliases: []
},
run: async (client, message, args, db) => {
new Paginator([], { timeout: 5 * 2e4, user: message.authorId, client: client })
.add([embed, embed2])
.start(message.channel);
},
};

36
commands/urban.js Normal file
View File

@ -0,0 +1,36 @@
const Embed = require("../functions/embed");
const axios = require('axios');
module.exports = {
config: {
name: "urban",
usage: true,
cooldown: 5000,
available: true,
permissions: [],
aliases: ['ub']
},
run: async (client, message, args, db) => {
let query = args.join(' ');
const url = ('https://api.urbandictionary.com/v0/define?term=' + query)
const response = await axios.get(url);
const data = response.data;
const def = data.list[0];
var length = data.list.length
if (parseInt(length) < 1) {
message.reply("No results")
return;
}
const embed = {
description: `### Definition for ${query}\n\n${def.definition}\n\n**Example**:\n\`\`\`${def.example}\`\`\` \n\n:thumbsup: ${def.thumbs_up} :thumbsdown: ${def.thumbs_down}\n\n**Link**: [${query}](${def.permalink})`,
colour: "#00FFFF"
}
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: urban | Args: " + (args?.join(" ") || "NONE"))
console.log(err.message);
return;
});
},
};

23
config.js Normal file
View File

@ -0,0 +1,23 @@
const config = {
"owners": ["01HATCHHMPTJBHH534H6VFYKK3"],
"prefix": "!",
"emojis": {
"one": "1⃣",
"two": "2⃣",
"three": "3⃣",
"four": "4⃣",
"five": "5⃣",
"six": "6⃣",
"seven": "7⃣",
"eight": "8⃣",
"nine": "9⃣",
"ten": "🔟",
"confetti": "🎊",
"stop": "🛑",
"check": "✅",
"cross": "❌",
},
"tenor_api_key": "AIzaSyCBUnqtNWMPn3f7F4Iu09KewGS8BfwUkDE",
};
module.exports = config;

7
ecosystem.config.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
apps : [{
name : "Zonies",
script : "./index.js",
node_args: "--experimental-specifier-resolution=node"
}]
}

145
events/messageCreate.js Normal file
View File

@ -0,0 +1,145 @@
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');
module.exports = async (client, message) => {
// Early return checks
if (!message || !message.content || message.author.bot) return;
const isDM = message.channel.type === "DirectMessage";
// Get guild settings (if not DM)
const db = isDM ? { prefix: client.botConfig.prefix, language: 'en' } : await client.database.getGuild(message.server.id, true);
// Check if message starts with prefix
if (!message.content.startsWith(db.prefix)) {
// Handle bot mention
if (message.content && (new RegExp(`^(<@!?${client.user.id}>)`)).test(message.content)) {
const mention = new Embed()
.setColor("#A52F05")
.setTitle(client.user.username)
.setDescription(`${client.translate.get(db.language, "Events.messageCreate.prefix")} \`${db.prefix}\`\n${client.translate.get(db.language, "Events.messageCreate.prefix2")} \`${db.prefix}help\``)
return message.reply({
embeds: [mention]
}, false).catch(() => { return });
}
return;
}
let args = message.content.slice(db.prefix.length).trim().split(/ +/g);
let cmd = args.shift().toLowerCase();
// Handle collectors
if (client.messageCollector.has(message.authorId) && client.messageCollector.get(message.authorId).channelId === message.channelId && !client.messageCollector.get(message.authorId).messageId) return await Collector(client, message, db);
if (client.messageEdit.has(message.authorId) && client.messageEdit.get(message.authorId).channelId === message.channelId && !client.messageEdit.get(message.authorId).messageId) return await EditCollector(client, message, db);
// Handle custom commands
let check = await CommandDB.findOne({ name: cmd }).select("name").lean();
if (check) {
CommandDB.findOne({ name: cmd }).then((data) => {
if (isJson(data.content)) {
let items = JSON.parse(data.content);
let item = items[Math.floor(Math.random() * items.length)];
return message.reply(item);
}
return message.reply(data.content);
});
}
// Command handling
let commandfile = client.commands.get(cmd) || client.commands.get(client.aliases.get(cmd));
if (commandfile) {
// DM Check - if command doesn't allow DMs and we're in a DM, return
if (isDM && !commandfile.config.dm) {
return message.reply({
embeds: [new Embed()
.setColor("#FF0000")
.setDescription(client.translate.get(db.language, "Events.messageCreate.noDM"))]
}, false).catch(() => { return });
}
// Permission checks for non-DM messages
if (!isDM) {
// Check bot permissions first
const canSendMessage = message.channel.havePermission("SendMessage");
const canReact = message.channel.havePermission("React");
if (!canSendMessage) {
return message.member.user.openDM().then((dm) => {
dm.sendMessage(`${client.translate.get(db.language, "Events.messageCreate.unable")} <#${message.channelId}>. ${client.translate.get(db.language, "Events.messageCreate.contact")}.`)
}).catch(() => { return });
}
if (!canReact) {
return message.reply(`${client.translate.get(db.language, "Events.messageCreate.noPerms")}. ${client.translate.get(db.language, "Events.messageCreate.contact")}.`, false).catch(() => { return });
}
// Check if user has required roles
const hasRequiredRole = commandfile.config.roles && message.member.roles && client.botConfig.roles && client.botConfig.roles.length > 0 ?
message.member.roles.some(role => {
const whitelistedRoles = Object.values(client.botConfig.roles[0] || {});
return whitelistedRoles.includes(role);
}) : false;
// Command availability check (skip for DMs)
if (!commandfile.config.available && !hasRequiredRole && !client.botConfig.owners.includes(message.authorId)) {
return message.reply({
embeds: [new Embed()
.setColor("#FF0000")
.setDescription(client.translate.get(db.language, "Events.messageCreate.unavail"))]
}, false).catch(() => { return });
}
// Permission and role checks (skip for DMs)
const needsPermissionCheck = commandfile.config.permissions && commandfile.config.permissions.length > 0;
const needsRoleCheck = commandfile.config.roles && commandfile.config.roles.length > 0;
if (needsPermissionCheck || needsRoleCheck) {
let hasPermission = true;
if (needsPermissionCheck) {
try {
hasPermission = message.member.hasPermission(commandfile.config.permissions[0]);
} catch (err) {
hasPermission = false;
}
}
if (!hasPermission && !hasRequiredRole && !client.botConfig.owners.includes(message.authorId)) {
return message.reply({
embeds: [new Embed()
.setColor("#FF0000")
.setDescription(`${client.translate.get(db.language, "Events.messageCreate.perms")}.\n${needsPermissionCheck ? `${client.translate.get(db.language, "Events.messageCreate.perms2")}: [${commandfile.config.permissions[0]}]` : ''}`)]
}, false).catch(() => { return });
}
}
}
// Cooldown handling
const used = client.used.get(`${message.authorId}-${cmd.toLowerCase()}`);
if (used) {
if (client.timeout.get(`${message.authorId}-${cmd.toLowerCase()}`)) return;
client.timeout.set(`${message.authorId}-${cmd.toLowerCase()}`, used);
setTimeout(() => client.timeout.delete(`${message.authorId}-${cmd.toLowerCase()}`), used);
const uremaining = client.functions.get("fetchTime")(used, client, db.language)
const embed = new Embed()
.setColor("#A52F05")
.setDescription(`<@${message.authorId}>, ${client.translate.get(db.language, "Events.messageCreate.wait")} \`${uremaining}\` ${client.translate.get(db.language, "Events.messageCreate.wait2")} \`${cmd.toLowerCase()}\` ${client.translate.get(db.language, "Events.messageCreate.wait3")}.`)
return message.reply({ embeds: [embed] }, false).catch(() => { return })
} else {
let cooldown = commandfile.config.cooldown;
client.used.set(`${message.authorId}-${cmd.toLowerCase()}`, cooldown);
setTimeout(() => client.used.delete(`${message.authorId}-${cmd.toLowerCase()}`), cooldown);
return commandfile.run(client, message, args, db);
}
}
}

25
events/messageDelete.js Normal file
View File

@ -0,0 +1,25 @@
const PollDB = require("../models/polls");
const Giveaways = require("../models/giveaways");
const GuildDB = require("../models/guilds");
module.exports = async (client, msg) => {
const paginateCheck = client.paginate.get(msg.authorId);
const pollCheck = client.polls.get(msg.id);
if (paginateCheck) {
client.paginate.delete(msg.authorId);
} else if (pollCheck) {
client.polls.delete(msg.id);
await PollDB.findOneAndDelete({ messageId: msg.id });
} else {
const db = await Giveaways.findOne({ messageId: msg.id });
if (db) {
await db.updateOne({ ended: true, endDate: Date.now() })
await db.save();
} else {
const db2 = await GuildDB.findOne({ "roles": { $elemMatch: { msgId: msg.id } } });
if (db2) {
db2.roles = db2.roles.filter(e => e.msgId !== msg.id);
await db2.save();
}
}
}
}

View File

@ -0,0 +1,220 @@
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]+)/;
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);
if (collector && collector.messageId === message.id || collector?.oldMessageId && collector?.oldMessageId === message.id && collector.channelId === message.channelId) {
if (emojiId === client.config.emojis.check) {
if (collector.roles.length === 0) {
const db = await client.database.getGuild(message.server.id);
message.delete().catch(() => { });
client.messages.get(collector?.oldMessageId)?.delete().catch(() => { })
const reactions = [...collector.rolesDone.map(e => e.emoji)];
message.channel.sendMessage(collector.type === "content" ? { content: `${message.content}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`, interactions: [reactions] } : { embeds: [new Embed().setColor("#A52F05").setDescription(`${client.messages.get(message.id).embeds[0].description}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`)], interactions: [reactions] }).then(async (msg) => {
db.roles.push({ msgId: msg.id, chanId: msg.channelId, roles: [...collector.rolesDone] });
await client.database.updateGuild(msg.server.id, { roles: db.roles });
});
clearTimeout(client.messageCollector.get(userId).timeout);
return client.messageCollector.delete(userId);
} else return;
} else if (emojiId === client.config.emojis.cross) {
const db = await client.database.getGuild(message.server.id);
client.messageCollector.delete(userId);
return message.reply({ embeds: [new Embed().setColor("#A52F05").setDescription(client.translate.get(db.language, "Events.messageReactionAdd.deleteCollector"))] },);
} else {
if (collector.roles.length === 0) return;
let emote;
if (colors.test(emojiId)) emote = `:${emojiId}:`;
else if (!colors.test(emojiId)) emote = emojiId
collector.rolesDone.push({ emoji: emojiId, role: collector.roles[0][0], name: collector.roles[0][1].name, color: collector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : collector.roles[0][1].colour });
message.edit(collector.type === "content" ? { content: message.content.replace(`{role:${collector.regex[0]}}`, `${emote} $\\text{\\textcolor{${collector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : collector.roles[0][1].colour}}{${collector.roles[0][1].name}}}$`) } : { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${collector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${collector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : collector.roles[0][1].colour}}{${collector.roles[0][1].name}}}$`))] })
collector.roles.shift();
return collector.regex.shift();
}
} else if (editCollector && editCollector.messageId === message.id || editCollector?.botMessage && editCollector?.botMessage === message.id && editCollector.channelId === message.channelId) {
if (emojiId === client.config.emojis.check) {
if (editCollector.roles.length === 0) {
const db = await client.database.getGuild(message.server.id);
message.delete().catch(() => { });
client.messages.get(editCollector?.oldMessageId)?.delete().catch(() => { })
client.messages.get(editCollector?.botMessage)?.delete().catch(() => { })
const reactions = [...editCollector.rolesDone.map(e => e.emoji)];
message.channel.sendMessage(editCollector.type === "content" ? { content: `${message.content}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`, interactions: [reactions] } : { embeds: [new Embed().setColor("#A52F05").setDescription(`${client.messages.get(message.id).embeds[0].description}\n\n##### ${client.translate.get(db.language, "Events.messageReactionAdd.cooldown")}`)], interactions: [reactions] }).then(async (msg) => {
db.roles.push({ msgId: msg.id, chanId: msg.channelId, roles: [...editCollector.rolesDone] });
await client.database.updateGuild(msg.server.id, { roles: db.roles.filter(e => e.msgId !== editCollector.oldMessageId) });
});
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;
switch (emojiId) {
case "⏪":
if (page !== 0) {
message.edit({
embeds: [pages[0]]
}).catch(() => { });
return paginateCheck.page = 0
} else {
return;
}
case "⬅️":
if (pages[page - 1]) {
message.edit({
embeds: [pages[--page]]
}).catch(() => { });
return paginateCheck.page = paginateCheck.page - 1
} else {
return;
}
case "➡️":
if (pages[page + 1]) {
message.edit({
embeds: [pages[++page]]
}).catch(() => { });
return paginateCheck.page = paginateCheck.page + 1
} else {
return;
}
case "⏩":
if (page !== pages.length) {
message.edit({
embeds: [pages[pages.length - 1]]
}).catch(() => { });
return paginateCheck.page = pages.length - 1
} else {
return;
}
}
} 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 (convert === 10 && pollCheck.owner === userId) {
await PollDB.findOneAndDelete({ messageId: message.id });
await pollCheck.poll.update();
message.edit({ content: `${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.owner")} (<@${pollCheck.owner}>) ${client.translate.get(pollCheck.lang, "Events.messageReactionAdd.end")}:`, embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`)).setColor("#F24646")] }).catch(() => { });
return client.polls.delete(message.id);
} else if (convert === 0 && convert !== 10 || convert !== -1 && convert !== 10) {
if (client.reactions.get(userId)) return client.users.get(userId)?.openDM().then(dm => dm.sendMessage(client.translate.get(pollCheck.lang, "Events.messageReactionAdd.tooFast"))).catch(() => { });
if (pollCheck.users.includes(userId)) return;
pollCheck.users.push(userId);
const user = (client.users.get(userId)) || await client.users.fetch(userId);
// console.log(user.avatar.id ? user.avatar.createFileURL() : 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar')
await pollCheck.poll.addVote(convert, userId, 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar', message.id);
message.edit({ embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`)).setColor("#A52F05")] }).catch(() => { });
client.reactions.set(userId, Date.now() + 3000)
return setTimeout(() => client.reactions.delete(userId), 3000)
} else return;
} else {
const db = await Giveaways.findOne({ messageId: message.id });
if (db) {
if (emojiId === client.config.emojis.confetti && db && !db.ended) {
if (client.reactions.get(userId)) return;
if (db.users.find(u => u.userID === userId)) return;
db.users.push({ userID: userId });
db.picking.push({ userID: userId });
db.save();
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();
if (db.users.length === 0) {
const noUsers = new Embed()
.setColor("#A52F05")
.setTitle(client.translate.get(db.lang, "Events.messageReactionAdd.giveaway"))
.setDescription(`${client.translate.get(db.lang, "Events.messageReactionAdd.owner")} (<@${userId}>) ${client.translate.get(db.lang, "Events.messageReactionAdd.early")}\n${client.translate.get(db.lang, "Events.messageReactionAdd.endNone")}!\n\n${client.translate.get(db.lang, "Events.messageReactionAdd.ended")}: <t:${Math.floor((endDate) / 1000)}:R>\n${client.translate.get(db.lang, "Events.messageReactionAdd.prize")}: ${db.prize}\n${client.translate.get(db.lang, "Events.messageReactionAdd.winnersNone")}${db.requirement ? `\n${client.translate.get(db.lang, "Events.messageReactionAdd.reqs")}: ${db.requirement}` : ``}`)
await db.updateOne({ ended: true, endDate: endDate })
await db.save();
return await client.api.patch(`/channels/${db.channelId}/messages/${db.messageId}`, { "embeds": [noUsers] });
}
for (let i = 0; i < db.winners; i++) {
let winner = db.picking[Math.floor(Math.random() * db.picking.length)];
if (winner) {
const filtered = db.picking.filter(object => object.userID != winner.userID)
db.picking = filtered;
db.pickedWinners.push({ id: winner.userID })
}
}
await db.updateOne({ ended: true, endDate: endDate })
await db.save();
const noUsers = new Embed()
.setColor("#A52F05")
.setTitle(client.translate.get(db.lang, "Events.messageReactionAdd.giveaway"))
.setDescription(`${client.translate.get(db.lang, "Events.messageReactionAdd.owner")} (<@${userId}>) ${client.translate.get(db.lang, "Events.messageReactionAdd.early")}\n${client.translate.get(db.lang, "Events.messageReactionAdd.partici")}: ${db.users.length}\n\n${client.translate.get(db.lang, "Events.messageReactionAdd.ended")}: <t:${Math.floor((endDate) / 1000)}:R>\n${client.translate.get(db.lang, "Events.messageReactionAdd.prize")}: ${db.prize}\n${client.translate.get(db.lang, "Events.messageReactionAdd.winners")}: ${db.pickedWinners.length > 0 ? db.pickedWinners.map(w => `<@${w.id}>`).join(", ") : client.translate.get(db.lang, "Events.messageReactionAdd.none")}${db.requirement ? `\n${client.translate.get(db.lang, "Events.messageReactionAdd.reqs")}: ${db.requirement}` : ``}`)
message.edit({ embeds: [noUsers] }).catch(() => { });
await client.api.post(`/channels/${db.channelId}/messages`, { "content": `${client.translate.get(db.lang, "Events.messageReactionAdd.congrats")} ${db.pickedWinners.map(w => `<@${w.id}>`).join(", ")}! ${client.translate.get(db.lang, "Events.messageReactionAdd.youWon")} **[${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})**!` }).catch(() => { });
client.reactions.set(userId, Date.now() + 3000)
setTimeout(() => client.reactions.delete(userId), 3000)
}
} else {
const db2 = await client.database.getGuild(message.server.id, true)
if (db2 && db2.roles.find(e => e.msgId === message.id) && db2.roles.find(e => e.roles.find(e => e.emoji === emojiId))) {
if (client.reactions.get(userId)) return;
const roles = [];
db2.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
const role = roles.find(e => e.emoji === emojiId);
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(userId);
if (!member) return;
let error = false;
let dataRoles = [];
if (member.roles) member.roles.map(e => dataRoles.push(e));
if (dataRoles.includes(role.role)) return;
client.reactions.set(userId, Date.now() + 3000);
setTimeout(() => client.reactions.delete(userId), 3000);
dataRoles.push(role.role);
await member.edit({ roles: dataRoles }).catch(() => { error = true })
if (error && db2.dm) {
member?.user?.openDM().then((dm) => { dm.sendMessage(`${client.translate.get(db2.language, "Events.messageReactionAdd.noPerms").replace("{role}", `**${role.name}**`)}!`) }).catch(() => { });
} else if (db2.dm) {
member?.user?.openDM().then((dm) => { dm.sendMessage(`${client.translate.get(db2.language, "Events.messageReactionAdd.success").replace("{role}", `**${role.name}**`)}!`) }).catch(() => { });
}
}
}
}
}

View File

@ -0,0 +1,100 @@
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]+)/;
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);
if (collector && collector.messageId === message.id && collector.channelId === message.channelId) {
const emoji = collector.rolesDone.find(e => e.emoji === emojiId);
if (emoji) {
collector.rolesDone = collector.rolesDone.filter(object => object.emoji != emojiId);
collector.roles.push([emoji.role, { name: emoji.name, colour: emoji.color }]);
collector.regex.push(emoji.name);
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);
if (colors.test(emojiId)) emote = `:${emojiId}:`;
else if (!colors.test(emojiId)) emote = emojiId
return message.edit(editCollector.type === "content" ? { content: message.content.replace(`${emote} $\\text{\\textcolor{${emoji.color}}{${emoji.name}}}$`, `{role:${emoji.name}}`) } : { embeds: [new Embed().setColor("#A52F05").setDescription(client.messages.get(message.id).embeds[0].description.replace(`{role:${editCollector.regex[0]}}`, `:${emojiId}: $\\text{\\textcolor{${editCollector.roles[0][1].colour?.includes("linear-gradient") ? '#000000' : editCollector.roles[0][1].colour}}{${editCollector.roles[0][1].name}}}$`))] }).catch(() => { });
}
} else if (pollCheck) {
if (client.reactions.get(userId)) return client.users.get(userId)?.openDM().then(dm => dm.sendMessage(client.translate.get(pollCheck.language, "Events.messageReactionRemove.tooFast"))).catch(() => { });
let convert = emojis.findIndex(e => e.name === emojiId);
if (convert === 0 && convert !== 10 || convert !== -1 && convert !== 10) {
if (!pollCheck.users.includes(userId)) return;
let tooMuch = [];
if (pollCheck.poll.options.description.length > 80) tooMuch.push(`**${client.translate.get(pollCheck.language, "Events.messageReactionRemove.title")}**: ${pollCheck.poll.options.description}`)
pollCheck.poll.voteOptions.name.filter(e => e).forEach((e, i) => {
i++
if (e.length > 70) {
tooMuch.push(`**${i}.** ${e}`)
}
});
pollCheck.users = pollCheck.users.filter(object => object != userId);
const user = (client.users.get(userId)) || await client.users.fetch(userId);
await pollCheck.poll.removeVote(convert, userId, 'https://chat.f95.io/api/users/01HATCWS7XZ7KEHW64AV20SMKR/default_avatar', message.id);
message.edit({ embeds: [new Embed().setDescription(tooMuch.length > 0 ? tooMuch.map(e => e).join("\n") : null).setMedia(await client.Uploader.upload(pollCheck.poll.canvas.toBuffer(), `Poll.png`)).setColor("#A52F05")] }).catch(() => { });
client.reactions.set(userId, Date.now() + 3000)
return setTimeout(() => client.reactions.delete(userId), 3000)
} else return;
} else {
if (client.reactions.get(userId)) return;
const db = await Giveaways.findOne({ messageId: message.id });
if (db && !db.ended) {
if (emojiId === client.config.emojis.confetti) {
if (!db.users.find(u => u.userID === userId)) return;
const filtered = db.users.filter(object => object.userID != userId)
db.users = filtered;
const filtered2 = db.picking.filter(object => object.userID != userId)
db.picking = filtered2;
db.save();
client.reactions.set(userId, Date.now() + 3000)
setTimeout(() => client.reactions.delete(userId), 3000)
client.users.get(userId)?.openDM().then(dm => dm.sendMessage(`${client.translate.get(pollCheck.language, "Events.messageReactionRemove.left")} [${db.prize}](https://chat.f95.io/server/${db.serverId}/channel/${db.channelId}/${db.messageId})!\n${client.translate.get(pollCheck.language, "Events.messageReactionRemove.left2")} **${db.users.length}** ${client.translate.get(pollCheck.language, "Events.messageReactionRemove.left")}!`)).catch(() => { });
}
} else {
const db2 = await client.database.getGuild(message.server.id, true)
if (db2 && db2.roles.find(e => e.msgId === message.id) && db2.roles.find(e => e.roles.find(e => e.emoji === emojiId))) {
const roles = [];
db2.roles.find(e => e.msgId === message.id).roles.map(e => roles.push(e));
const role = roles.find(e => e.emoji === emojiId);
const member = await (client.servers.get(message.server.id) || await client.servers.fetch(message.server.id))?.fetchMember(userId);
if (!member) return;
let error = false;
let dataRoles = [];
if (member.roles) member.roles.map(e => dataRoles.push(e));
if (!dataRoles.includes(role.role)) return;
client.reactions.set(userId, Date.now() + 3000);
setTimeout(() => client.reactions.delete(userId), 3000);
dataRoles = dataRoles.filter(object => object != role.role);
await member.edit({ roles: dataRoles }).catch(() => { error = true })
if (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(() => { });
}
}
}
}
}

12
events/serverCreate.js Normal file
View File

@ -0,0 +1,12 @@
const Embed = require("../functions/embed")
module.exports = async (client, server) => {
//console.log("join", server)
// await client.database.getGuild(server.id, true)
// const embed = new Embed()
// .setColor("#A52F05")
// .setTitle(server.name || "Unknown")
// .setDescription(`**Owner**: ${server.ownerId}\n**Discoverable**: ${server.discoverable ? "true" : "false"}\n**Created**: <t:${Math.floor((server.createdAt) / 1000)}:R>\n**ID**: ${server.id}`)
// await client.api.post(`/channels/01H1KQ4GVP96MEHA95YT2F0NT4/messages`, { embeds: [embed] }).catch(() => { })
}

13
events/serverDelete.js Normal file
View File

@ -0,0 +1,13 @@
const Embed = require("../functions/embed")
module.exports = async (client, server) => {
//console.log("delete",server)
// await client.database.deleteGuild(server.id)
// const embed = new Embed()
// .setColor("#FF0000")
// .setTitle(server.name || "Unknown")
// .setIcon(server.icon ? `https://autumn.revolt.chat/icons/${server.icon._id}/${server.icon.filename}` : null)
// .setDescription(`**Owner**: ${server.ownerId}\n**Discoverable**: ${server.discoverable ? "true" : "false"}\n**Created**: <t:${Math.floor((server.createdAt) / 1000)}:R>\n**ID**: ${server.id}`)
// await client.api.post(`/channels/01H1KQ4GVP96MEHA95YT2F0NT4/messages`, { embeds: [embed] })
}

View File

@ -0,0 +1,17 @@
let type;
module.exports = async (client, member, memberOld) => {
// Work in progress
if (member.roles > memberOld.roles && member.id.user === client.user.id) type = "add"
else if (member.roles < memberOld.roles) type = "remove"
else return;
switch (type) {
case "add":
let channel = await client.channels.fetch()
break;
case "remove":
break;
}
}

12
functions/audit.js Normal file
View File

@ -0,0 +1,12 @@
const db = require("../models/logging");
async function audit(type, message, cmd) {
const audit = new db();
audit.ranBy = message.member.user.username;
audit.name = cmd;
audit.type = type;
audit.channel = message.channel.name;
audit.save()
}
module.exports = audit;

24
functions/checkPolls.js Normal file
View File

@ -0,0 +1,24 @@
const db = require("../models/polls");
const Polls = require("./poll");
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) { }
poll.deleteOne({ messageId: poll.messageId });
}, i * 700);
}
}
module.exports = checkPolls;

24
functions/checkRoles.js Normal file
View File

@ -0,0 +1,24 @@
const db = require("../models/guilds");
async function checkRoles(client) {
let rr = await db.find({ $expr: { $gt: [{ $size: "$roles" }, 0] } });
if (!rr || rr.length === 0) return;
let i = 0;
let ii = 0;
for (let r of rr) {
i++
setTimeout(async () => {
r.roles.map((role) => {
ii++
setTimeout(async () => {
if (!client.channels.get(role.chanId) && role.roles.length === 0) {
return await client.database.updateGuild(r.id, { roles: r.roles.filter(e => e.msgId !== role.msgId) });
}
await client.channels.get(role.chanId)?.fetchMessage(role.msgId).catch(() => { });
}, ii * 700);
});
}, i * 600);
}
}
module.exports = checkRoles;

26
functions/colorCodes.js Normal file
View File

@ -0,0 +1,26 @@
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}`] || '');
};
module.exports = colors;

11
functions/dhms.js Normal file
View File

@ -0,0 +1,11 @@
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);
};
module.exports = dhms;

69
functions/embed.js Normal file
View File

@ -0,0 +1,69 @@
class Embed {
url = null;
title = null;
description = null;
image = null;
icon_url = null;
color = null;
thumbnail = null;
media = null;
constructor(data) {
Object.assign(this, data)
}
setTitle(title) {
this.title = title
return this
}
setMedia(media) {
this.media = media
return this
}
setIcon(iconURL) {
this.icon_url = iconURL
return this
}
setColor(color) {
this.color = color
return this
}
setImage(image) {
this.image = image
return this
}
setThumbnail(thumbnail) {
this.thumbnail = thumbnail
return this
}
setDescription(description) {
this.description = description
return this
}
setURL(url) {
this.url = url
return this
}
toJSON() {
return {
title: this.title,
description: this.description,
url: this.url,
icon_url: this.icon_url,
image: this.image,
thumbnail: this.thumbnail,
colour: this.color,
media: this.media
}
}
}
module.exports = Embed;

16
functions/fetchTime.js Normal file
View File

@ -0,0 +1,16 @@
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);
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")}`;
}
module.exports = fetchTime;

85
functions/logger.js Normal file
View File

@ -0,0 +1,85 @@
/**
* base code taken from https://github.com/Mirasaki/logger
*/
const chalk = require('chalk'),
moment = require('moment');
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]')
};
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}`),
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(
!err.stack
? chalk.red(err)
: err.stack
.split('\n')
.map((msg, index) => {
if (index === 0) {
return chalk.red(msg);
}
const isFailedFunctionCall = index === 1;
const traceStartIndex = msg.indexOf('(');
const traceEndIndex = msg.lastIndexOf(')');
const hasTrace = traceStartIndex !== -1;
const functionCall = msg.slice(
msg.indexOf('at') + 3,
hasTrace ? traceStartIndex - 1 : msg.length
);
const trace = msg.slice(traceStartIndex, traceEndIndex + 1);
return ` ${chalk.grey('at')} ${isFailedFunctionCall
? `${chalk.redBright(functionCall)} ${chalk.red.underline(trace)}`
: `${chalk.greenBright(functionCall)} ${chalk.grey(trace)}`
}`;
})
.join('\n')
)
}
};

26
functions/members.js Normal file
View File

@ -0,0 +1,26 @@
async function Members(client, server) {
if (typeof client !== 'object') return {
error: true,
msg: 'First argument isn\'t available or a valid client!'
};
if (typeof server !== 'string') return {
error: true,
msg: 'Second argument isn\'t available or a valid serverID string!'
};
try {
const member = await client.api.get(`/servers/${server}/members`)
return {
error: false,
users: member.users,
members: member.members
};
} catch {
return {
error: true,
msg: "Unknown Server"
}
}
}
module.exports = Members;

View File

@ -0,0 +1,72 @@
const Embed = require("../functions/embed");
async function Collector(client, message, db) {
const regex = /{role:(.*?)}/;
const regexAll = /{role:(.*?)}/g;
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 });
}
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));
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 });
}
let duplicate = [];
roleIds.map((r, i) => {
i++
if (roleIds.filter(e => e[0] === r[0]).length > 1) duplicate.push(roleIds[i - 1]);
});
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 });
}
let 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 });
}
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 });
}
message.delete().catch(() => { });
collector.roles = roleIds;
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;
});
}
module.exports = Collector;

72
functions/messageEdit.js Normal file
View File

@ -0,0 +1,72 @@
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 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));
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 });
}
let duplicate = [];
roleIds.map((r, i) => {
i++
if (roleIds.filter(e => e[0] === r[0]).length > 1) duplicate.push(roleIds[i - 1]);
});
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 });
}
let 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 });
}
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 });
}
message.delete().catch(() => { });
collector.roles = roleIds;
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;
});
}
module.exports = Collector;

29
functions/pagination.js Normal file
View File

@ -0,0 +1,29 @@
class Paginator {
constructor({ user, client, timeout }) {
this.pages = [];
this.client = client;
this.user = user;
this.page = 0;
this.timeout = timeout;
}
add(page) {
if (page.length) {
page.forEach((x) => { this.pages.push(x) });
return this;
}
this.pages.push(page);
return this;
}
async start(channel) {
if (!this.pages.length) return;
const reactions = ["⏪", "⬅️", "➡️", "⏩"];
this.pages.forEach((e, i=0) => { e.description = `${e.description}\n\nPage ${i+1} / ${this.pages.length}` })
const message1 = await channel.sendMessage({ embeds: [this.pages[0]], interactions: { reactions } });
this.client.paginate.set(this.user, { pages: this.pages, page: this.page, message: message1.id });
setTimeout(() => { if (this.client.paginate.get(this.user)) this.client.paginate.delete(this.user) }, this.timeout);
}
}
module.exports = Paginator;

250
functions/poll.js Normal file
View File

@ -0,0 +1,250 @@
const PollDB = require("../models/polls");
const Embed = require("./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()}`;
class Polls {
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 };
}
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);
if (this.time < 0) return;
await (new PollDB({
owner: this.owner,
channelId: message.channelId,
messageId: message.id,
avatars: this.avatars,
users: this.users,
votes: this.votes,
name: this.options.name,
desc: this.options.description,
options: this.voteOptions,
time: this.time,
lang: this.lang,
now: Date.now(),
}).save());
}
textHeight(text, ctx, m) {
let metrics = m || ctx.measureText(text);
return metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
}
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];
}
}
ctx.beginPath();
ctx.moveTo(x + radius.tl, y);
ctx.lineTo(x + width - radius.tr, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
ctx.lineTo(x + width, y + height - radius.br);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
ctx.lineTo(x + radius.bl, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
ctx.lineTo(x, y + radius.tl);
ctx.quadraticCurveTo(x, y, x + radius.tl, y);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
async update() {
let roundRect = this.roundRect;
let textHeight = this.textHeight;
var width = 600, height = this.size.canvas, padding = 10;
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);
let description = this.options.description.length > 80 ? this.options.description.slice(0, 77) + "..." : this.options.description;
var descHeight = textHeight(description, ctx);
ctx.fillStyle = "#23272A";
roundRect(ctx, 0, 0, width, height, 5, true, false); // background
ctx.fillStyle = "#4E535A";
ctx.font = `normal 12px Sans-Serif`;
ctx.fillText(name, padding, padding + 2 + nameHeight / 2); // name
ctx.fillStyle = "#FFFFFF";
ctx.font = `normal 17px Sans-Serif`;
ctx.fillText(description, padding, padding + 15 + nameHeight + descHeight / 2); // description
var headerHeight = padding + descHeight + nameHeight + 15;
var dataWidth = width - padding * 2;
var barHeight = 40;
var votes = this.votes;
var names = this.voteOptions.name;
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);
}
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 this.update();
return this.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 this.update();
return this.canvas;
}
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);
ctx.save();
ctx.translate(padding, padding + headerHeight);
var barPadding = 5;
percentages.forEach((percentage, i) => {
if (!percentage) percentage = 0;
let paddingLeft = (vote != undefined) ? 30 : 0;
ctx.fillStyle = "#2C2F33";
let y = (height + 10) * i;
roundRect(ctx, 20, y, width, height, 5, true, false); // full bar
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);
ctx.fillStyle = "#4E535A";
let h = textHeight(i + 1, ctx);
ctx.fillText(i + 1, 0, y + height / 2 + h / 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);
if (vote != undefined) {
ctx.strokeStyle = "#FFFFFF"; // selection circle
ctx.fillStyle = "#717cf4";
ctx.beginPath();
ctx.arc(35, y + 10 + h * 0.75, 6, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
if (vote == i) {
ctx.beginPath();
ctx.arc(35, y + 10 + h * 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);
ctx.fillStyle = "#A52F05"; // percentage and vote count
ctx.fillText(percentage + "% (" + votes[i] + ")", width - barPadding - w, y);
});
ctx.restore();
}
async drawFooter(ctx, x, y, width, height, padding, users) {
ctx.save();
ctx.translate(10, this.size.bar);
var rad = 18;
ctx.fillStyle = "#4E535A";
ctx.lineWidth = 2;
ctx.strokeStyle = "#4E535A";
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);
// Avatars
var pos = rad * users.length + 10 + metrics.width;
var 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");
context.beginPath();
context.arc(rad, rad, rad, 0, Math.PI * 2, true);
context.closePath();
context.clip();
const avatar = await Canvas.loadImage(user);
context.drawImage(avatar, 0, 0, rad * 2, rad * 2);
ctx.drawImage(a, pos, yPos);
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);
ctx.restore();
}
}
module.exports = Polls;

60
functions/randomStr.js Normal file
View File

@ -0,0 +1,60 @@
const crypt = require("crypto");
// const FlakeId = require('flakeid');
module.exports = {
/**
* Generates a unique ID
* @returns {string} 12-character base64 ID
*/
generateUniqueId: () => crypt.randomBytes(9)
.toString('base64')
.replace(/[/+]/g, c => c === '/' ? '_' : '-')
.slice(0, 12),
/**
*
* @param {number} len How long to set character length
* @returns alphanumeric string
*/
random: function random(len) {
len = len || 64;
return crypt.randomBytes(len).toString("hex");
},
// snowflake: function snowflake() {
// const flake = new FlakeId({
// mid: 64,
// timeOffset: 1431669781,
// });
// return flake.gen();
// },
shortid: function shortid() {
let str = Buffer.from(crypt.randomUUID()).toString('base64');
let n = 12;
return (str.length > n) ? str.substr(0, n - 1) : str
},
truncate: function truncate(str, n) {
return (str.length > n) ? str.substr(0, n - 1) : str;
},
randomNum: function (len1) {
const len2 = len1 || 17;
const { customAlphabet } = require('nanoid')
const nanoid = customAlphabet('1234567890', len2)
return nanoid();
},
isJson: function (str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
};

49
functions/reload.js Normal file
View File

@ -0,0 +1,49 @@
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}`);
client.off(evtName, typeof client._events[evtName] == 'function' ? client._events[evtName] : client._events[evtName][0])
client.event.delete(evtName)
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`
}
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`
}
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}`
}
}
module.exports = Reload;

12
handlers/command.js Normal file
View File

@ -0,0 +1,12 @@
const { readdirSync } = require("fs")
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`));
}

103
handlers/database.js Normal file
View File

@ -0,0 +1,103 @@
const { connect } = require("mongoose").set('strictQuery', true);
const color = require("../functions/colorCodes")
module.exports = class DatabaseHandler {
constructor(connectionString) {
this.cache = new Map();
this.guildModel = require('../models/guilds');
this.connectionString = connectionString;
}
cacheSweeper(client) {
setInterval(() => {
const guilds = this.cache.values();
for (const g of guilds) {
if (!client?.servers?.has(g?.id)) {
this.cache.delete(g?.id);
}
}
}, 60 * 60 * 1000);
}
guildSweeper(client) {
setInterval(async () => {
const guilds = await this.getAll();
let i = 0;
for (const g of guilds) {
setTimeout(async () => {
i++;
if (!client?.servers?.has(g?.id)) {
this.cache.delete(g?.id)
this.deleteGuild(g?.id);
}
}, i * 600)
}
}, 180 * 180 * 1000);
}
async connectToDatabase() {
await connect(this.connectionString, {
useNewUrlParser: true,
}).catch((err) => {
console.log(color("%", `%4[Mongoose]%7 :: ${err}`));
}).then(() => console.log(
color("%", "%6[Mongoose]%7 :: Connected to MongoDB"),
));
}
async fetchGuild(guildId, createIfNotFound = false) {
const fetched = await this.guildModel.findOne({ id: guildId });
if (fetched) return fetched;
if (!fetched && createIfNotFound) {
await this.guildModel.create({
id: guildId,
language: 'en_EN',
botJoined: Date.now() / 1000 | 0,
});
return this.guildModel.findOne({ id: guildId });
} return null;
}
async getGuild(guildId, createIfNotFound = true, force = false) {
if (force) return this.fetchGuild(guildId, createIfNotFound);
if (this.cache.has(guildId)) {
return this.cache.get(guildId);
}
const fetched = await this.fetchGuild(guildId, createIfNotFound);
if (fetched) {
this.cache.set(guildId, fetched?.toObject() ?? fetched);
return this.cache.get(guildId);
} return null;
}
async deleteGuild(guildId, onlyCache = false) {
if (this.cache.has(guildId)) this.cache.delete(guildId);
return !onlyCache ? this.guildModel.deleteMany({ id: guildId }) : true;
}
async updateGuild(guildId, data = {}, createIfNotFound = false) {
let oldData = await this.getGuild(guildId, createIfNotFound);
if (oldData) {
if (oldData?._doc) oldData = oldData?._doc;
data = { ...oldData, ...data };
this.cache.set(guildId, data);
return this.guildModel.updateOne({
id: guildId,
}, data);
} return null;
}
async getAll() {
return this.guildModel.find();
}
};

12
handlers/event.js Normal file
View File

@ -0,0 +1,12 @@
const { readdirSync } = require("fs")
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`));
};

11
handlers/function.js Normal file
View File

@ -0,0 +1,11 @@
const { readdirSync } = require("fs")
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`);
};

98
handlers/translation.js Normal file
View File

@ -0,0 +1,98 @@
const color = require("../functions/colorCodes")
class TranslationHandler {
constructor(languages) {
this.availableLanguages = languages ?? [
'en_EN',
// 'es_ES',
//'ar_AR',
// 'pt_BR',
//'sk_SK',
];
this.translations = {};
for (const l of this.availableLanguages) {
const data = require(`../languages/${l}.json`);
this.initLanguage(l, data);
}
console.log(color("%", `%b[Translation_Handler]%7 :: Loaded %e${this.availableLanguages.length} %7languages`));
}
initLanguage(key, language) {
this.translations[key] = language;
}
checkRegex(value) {
return /^[a-z]{2}_[A-Z]{2}$/.test(value);
}
getLanguage(language) {
if (!this.checkRegex(language)) return this.translations['en_EN'];
return this.translations[language];
}
addLanguage(language) {
if (!this.checkRegex(language)) {
throw new Error('Invalid language format. Example: en_EN');
}
this.availableLanguages.push(language);
}
reload() {
this.translations = {};
for (const l of this.availableLanguages) {
try {
const d = require(`../languages/${l}.json`);
if (!d) continue;
this.initLanguage(l, d);
return "Success"
} catch (e) {
return e.message;
}
}
}
get(language, path, data = {}) {
if (!language) language = 'en_EN';
const l = this.getLanguage(language);
const p = path.split('.');
let c = null;
if (p.length > 0) {
for (const i of p) {
try {
if (!c) {
if (!l.hasOwnProperty(i)) break;
c = l[i];
} else {
if (!c.hasOwnProperty(i)) break;
c = c[i];
}
} catch (err) {
break;
}
}
} else {
return `Unknown translation: ${language} | ${path}`;;
}
if (!c) return `Unknown translation: ${language} | ${path}`;;
if (data) {
try {
return c.replace(/{(\w+)}/g, (match, key) => data[key] ?? match);
} catch (e) {
return `Unknown translation: ${language} | ${path}`;
}
}
return c;
}
};
module.exports = TranslationHandler;

58
index.js Normal file
View File

@ -0,0 +1,58 @@
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 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");
client.database = new DatabaseHandler(mongoDB);
client.database.connectToDatabase();
client.database.cacheSweeper(client);
client.database.guildSweeper(client);
["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));
client.once("ready", async () => {
logger.success('Bot Ready', `${client.user.username} is ready`);
//client.database.connectToDatabase();
//client.database.cacheSweeper(client);
//client.database.guildSweeper(client);
await checkPolls(client);
});
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)
});
client.loginBot(token);

257
languages/en_EN.json Normal file
View File

@ -0,0 +1,257 @@
{
"Events": {
"messageCreate": {
"wait": "wait",
"wait2": "before using",
"wait3": "again",
"unable": "Unable to send messages in",
"contact": "please contact a server administrator to get this settled",
"noPerms": "Need permission to react as all of my features use reactions",
"unavail": "This command is currently unavailable",
"perms": "You don't have the required permissions to use this command",
"perms2": "Permission needed",
"prefix": "My prefix is",
"prefix2": "To get started, type",
"noRoles": "There are no roles in your message, provide some by following this example",
"noReact": "I don't have permission to add reactions so this may not work correctly.",
"maxRoles": "You can only have a maximum of 20 roles in your message.",
"unknown": "There are unknown roles in your message, please remove them and try again.",
"duplicate": "There are duplicate roles in your message, please remove them and try again",
"positions": "There are roles with higher ranks (position) than my highest role in your message, please remove them and try again"
},
"messageReactionAdd": {
"title": "title",
"owner": "Owner",
"end": "ended the poll early. These are the final results",
"tooFast": "You are reacting too fast! Please wait a few seconds before reacting again.",
"joined": "You joined giveaway",
"joined2": "There's currently",
"joined3": "user(s) in this giveaway!",
"early": "ended the giveaway early",
"endNone": "No one entered into the giveaway, so there are no winners",
"ended": "Ended",
"prize": "Prize",
"winnersNone": "Winner(s): None",
"none": "None",
"reqs": "Requirement",
"giveaway": "Giveaway",
"partici": "Participants",
"winners": "Winner(s)",
"congrats": "Congratulations",
"youWon": "You won the giveaway for",
"cooldown": "Reaction cooldown is 3 seconds",
"success": "Successfully added role {role}",
"noPerms": "I do not have permission to give role {role}",
"role": "role",
"deleteCollector": "Successfully stopped reaction collector!"
},
"messageReactionRemove": {
"title": "Title",
"tooFast": "You are reacting too fast! Please wait a few seconds before reacting again.",
"left": "You left giveaway",
"left2": "There are now",
"left3": "user(s) in this giveaway",
"success": "Successfully removed role {role}",
"noPerms": "I do not have permission to remove role {role}"
}
},
"Commands": {
"roles": {
"description": "Setup a reaction role message for your server",
"usage": "help",
"already": "You already have an active message collector going on! Finish that first before starting a new one.",
"pick": "Provide what you would like the type of message to be sent. Options: `embed`, `content`",
"ended": "The message collector has ended after 25 minutes.",
"success": "Successfully sent message collector to",
"react": "Send a message you would like to be as your reaction role message. After that is done, react with emojis in order of the roles you want to be added to the message.",
"react2": "Once you're done, react with ✅ to complete it.\nTo cancel this reaction collector, react with ❌ to stop it!",
"noPerms": "I require the `AssignRoles` permission to add roles to users.",
"max": "You can only have a maximum of 12 reaction role messages in your server.",
"noRoles": "There are no reaction role messages setup currently. To add some, type",
"jump": "Jump",
"roles": "Roles",
"delete": "Provide an ID for the reaction role message to delete it",
"edit": "Provide an ID for the reaction role message to edit it",
"notFound": "I could not find a reaction role message with that ID.",
"deleted": "Successfully deleted that reaction role message.",
"view": "Viewing Reaction Role Messages",
"dms": "Successfully toggled DM messages",
"on": "On",
"off": "Off",
"create": "Creating",
"createExample": "create [Channel Mention] [Type: content | embed]",
"editing": "Editing",
"viewing": "Viewing",
"deleting": "Deleting",
"msgId": "Message",
"explain": "Explanation",
"explain2": "When you create a reaction message (`f!roles create`), you will need to provide it with roles (format: `{role:Blue}`) and I will copy your message. React to my message to automatically update the roles and once you are finished, react with ✅ to complete it.",
"dm": "Direct Messages"
},
"help": {
"description": "Help command",
"usage": "[Command (Optional)]",
"embeds": {
"first": {
"cmdName": "Command",
"cmdDesc": "Description",
"cmdAvail": "Availability",
"cmdAvail2": "Available",
"cmdAvail3": "Unavailable",
"cmdCool": "Cooldown",
"cmdCool2": "seconds",
"cmdPerms": "Permissions",
"cmdPerms2": "None",
"cmdAlias": "Aliases",
"cmdUsage": "Usage"
},
"second": {
"start": "Available Commands",
"middle": "Unavailable Commands",
"end": "To get more information about a command, type",
"end2": "command"
}
}
},
"info": {
"description": "Stats on the bot",
"start": "Random Information",
"servers": "Servers",
"giveaways": "Giveaways",
"polls": "Polls",
"uptime": "Uptime",
"ping": "Ping",
"database": "Database",
"library": "Library",
"links": "Links",
"links2": "Invite",
"links3": "Support"
},
"giveaway": {
"description": "Creates a giveaway. Reactions have 3 second cooldown.",
"usage": "[Time, E.g. 20m] | [Winners] | [Prize] | [Requirement (Optional)] [Channel (Optional), E.g. channel:#giveaways]",
"tooMany": "This server has 15 giveaways currently running. Wait until one finishes before making anymore!",
"validChannel": "Please provide a valid channel mention or channel ID.\nExample",
"validChannel2": "Note: If you are using a channel ID, provide it after the requirement text",
"validTime": "Please provide a valid time.\nExample",
"validWinners": "Please provide the amount of winners. Maximum: 5\nExample",
"validPrize": "Please provide the prize.\nExample",
"ormore": "Giveaways can only be 5 minutes or above in time!\nExample",
"orless": "Please provide a time shorter than 365 days.\nExample",
"format": "You failed to follow the giveaway format.\nExample",
"formatWinners": "You failed to properly specific how many winners there are\nExample",
"maxwins": "When making a giveaway, you can only have a maximum of 5 winners.\nExample",
"giveaway": "Giveaway",
"react": "React with",
"react2": "to enter!",
"hosted": "Hosted by",
"time": "Time",
"prize": "Prize",
"winners": "Winners",
"reqs": "Requirement",
"noperms": "I do not have permission to send messages in",
"noperms2": "I do not have permission to view",
"noperms3": "I do not have permission to add reactions in",
"success": "Successfully sent giveaway to"
},
"polls": {
"description": "Creates a poll. Reactions have 3 second cooldown.",
"usage": "[Time, E.g. 10m] | [Question] | [Option 1] | [Option 2] | [Options 3-10 (Optional)]",
"tooMany": "You already have 5 polls running. Wait for one to end before creating anymore.",
"validTime": "Please provide a valid time.\nExample",
"longerThan": "Please provide a time longer than 30 seconds.\nExample",
"shorterThan": "Please provide a time shorter than 30 days.\nExample",
"validQuestion": "Please provide a question.\nExample",
"validOption": "Please provide the first option for the question. Maximum of 10 options.\nExample",
"validOption2": "Please provide the second option for the question. Maximum of 10 options.\nExample",
"maxOptions": "Please provide a maximum of 10 options.\nExample",
"polls": "Polls",
"title": "Title",
"example": "How are you? | Good | Bad"
},
"reroll": {
"description": "Reroll winners from giveaways. Winner number is based on the order of the winners in the embed.",
"usage": "[Message ID] [Winner Number, E.g. 1, 2, all | Default: all]",
"validID": "Please provide a valid message ID.",
"notValid": "I could not find a giveaway message with that ID.",
"winners": "Please provide a winner number (E.g. 1, 2, 3) or use `all` to reroll all winners!",
"notEnded": "This giveaway hasn't ended yet!",
"notHosted": "You can't reroll this giveaway because you didn't host it!",
"noUsers": "There are no users in this giveaway to reroll!",
"noPicks": "There are no more users that can be picked for rerolling!",
"onlyWinners": "There are only",
"onlyWinners2": "winner(s) to pick from!\nExample",
"winnerNum": "Winner Number",
"giveaway": "Giveaway",
"owner": "Owner",
"rerolled": "rerolled",
"winners1": "all winners!",
"winners2": "winner position",
"partici": "Participants",
"ended": "Ended",
"prize": "Prize",
"winner": "Winner(s)",
"reqs": "Requirement",
"reroll": "The giveaway was rerolled! New winner(s)",
"reroll2": "won the giveaway for",
"error": "Unable to send message for new winners",
"error2": "Unable to edit embed"
},
"prefix": {
"success": "Prefix successfully changed to",
"tooMany": "Prefix cannot be longer than 8 characters.",
"prefix": "Prefix",
"change": "To change the prefix, use",
"new": "new prefix",
"description": "Changes default prefix to your own",
"usage": "<text>"
},
"credits": {
"thankyou": "As developer of Functious, I wanted to thank everyone who contributed toward the bot.",
"thankyou2": "The bot wouldn't be where it is without all of you, thank you!",
"translators": "Translators",
"title": "Credits",
"description": "Credits for everyone who helped"
},
"language": {
"description": "Changes the language of the bot",
"usage": "<language>",
"example": "Example",
"current": "Current",
"change": "You can change your server's language by picking an available language.",
"avail": "Available",
"notAvailable": "Chosen language is not supported! ",
"success": "Successfully changed the language to"
}
},
"Functions": {
"fetchTime": {
"years": "year(s)",
"days": "day(s)",
"hours": "hour(s)",
"minutes": "minute(s)",
"seconds": "second(s)"
},
"giveawaysEnd": {
"giveaway": "Giveaway",
"noUsers": "No one entered into the giveaway so I couldn't pick a winner",
"ended": "Ended",
"prize": "Prize",
"winnersNone": "Winner(s): None",
"winners": "Winner(s)",
"reqs": "Requirement",
"noOne": "No one entered into giveaway",
"warn": "[Channel Edit] Unable to edit message",
"warn1": "in channel",
"warn2": "[Channel Post] Unable to post to channel",
"givEnd": "Giveaway Ended",
"partici": "Participants",
"congrats": "Congratulations",
"youWon": "You won the giveaway for"
},
"poll": {
"end": "This poll has ended and these are the results"
}
},
"links4": "Vote"
}

13
models/commands.js Normal file
View File

@ -0,0 +1,13 @@
const { Schema, model } = require("mongoose");
const moment = require('moment');
const timestamp = moment().valueOf() / 1000;
const commands = new Schema({
id: { type: String },
name: { type: String },
content: { type: String },
createdBy: { type: String },
created_at: { type: String, default: timestamp }
});
module.exports = model("commands", commands);

21
models/giveaways.js Normal file
View File

@ -0,0 +1,21 @@
const { Schema, model } = require("mongoose");
const giveaways = new Schema({
owner: { type: String, required: true },
serverId: { type: String, required: true },
channelId: { type: String, required: true },
messageId: { type: String, required: true },
users: [{ type: Object }],
time: { type: String, required: true },
now: { type: String, required: true },
prize: { type: String, required: true },
winners: { type: Number, required: true },
pickedWinners: [{ type: Object }],
picking: [{ type: Object }],
ended: { type: Boolean, default: false },
lang: { type: String, required: true, default: "en_EN" },
requirement: { type: String },
endDate: { type: String }
});
module.exports = model("giveaways", giveaways);

12
models/guilds.js Normal file
View File

@ -0,0 +1,12 @@
const { Schema, model } = require("mongoose");
const guilds = new Schema({
id: { type: String },
prefix: { type: String, default: "f!" },
language: { type: String, default: "en_EN" },
joined: { type: String, default: Date.now() / 1000 | 0 },
dm: { type: Boolean, default: true },
roles: { type: Array, default: [] },
});
module.exports = model("guilds", guilds);

12
models/logging.js Normal file
View File

@ -0,0 +1,12 @@
const { Schema, model } = require("mongoose");
const logging = new Schema({
id: { type: String },
type: { type: String },
ranBy: { type: String },
name: { type: String },
channel: { type: String },
created_at: { type: String, default: Date.now() / 1000 }
});
module.exports = model("logging", logging);

17
models/polls.js Normal file
View File

@ -0,0 +1,17 @@
const { Schema, model } = require("mongoose");
const polls = new Schema({
owner: { type: String },
serverId: { type: String },
channelId: { type: String },
messageId: { type: String },
avatars: { type: Object },
votes: { type: Object },
users: { type: Object },
time: { type: Number },
now: { type: Number },
desc: { type: String },
options: { type: Object },
});
module.exports = model("polls", polls);

10
models/registerInvite.js Normal file
View File

@ -0,0 +1,10 @@
const { Schema, model } = require("mongoose");
const registerInvite = new Schema({
userId: { type: String },
invite: { type: String },
staffRoles: { type: Object },
createdAt: { type: Number },
});
module.exports = model("registerInvite", registerInvite);

19
models/rule7.js Normal file
View File

@ -0,0 +1,19 @@
const { Schema, model } = require("mongoose");
const rule7 = new Schema({
owner: { type: String },
serverId: { type: String },
channelId: { type: String },
messageId: { type: String },
avatars: { type: Object },
votes: { type: Object },
users: { type: Object },
time: { type: Number },
now: { type: Number },
desc: { type: String },
options: { type: Object },
threadId: { type: String },
reportId: { type: String },
});
module.exports = model("rule7", rule7);

9
models/savedPolls.js Normal file
View File

@ -0,0 +1,9 @@
const { Schema, model } = require("mongoose");
const savedPolls = new Schema({
owner: { type: String },
desc: { type: String },
options: { type: Object },
});
module.exports = model("savedPolls", savedPolls);

1715
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"license": "MIT",
"dependencies": {
"@discordjs/collection": "^1.5.1",
"axios": "^1.9.0",
"canvas": "^2.11.2",
"chalk": "^4.1.2",
"date-fns": "^4.1.0",
"fs": "^0.0.1-security",
"moment": "^2.29.4",
"mongoose": "^7.2.0",
"nanoid": "3.3.4",
"node-fetch-commonjs": "^3.3.2",
"revolt-uploader": "^1.1.1",
"revolt.js": "npm:revolt.js-update@^7.0.0-beta.9",
"screen": "^0.2.10",
"wumpfetch": "^0.3.1"
},
"information": {
"Developer": "Ryahn",
"Discord": "ainzooalgown",
"Revolt": "Ryahn#1337",
"GitHub": "https://github.com/Ryahn/zonies"
}
}

27
test.js Normal file
View File

@ -0,0 +1,27 @@
// const { token, mongoDB, api } = require("./botconfig.json");
// const DatabaseHandler = require('./handlers/database');
// const util = require('util');
// const { isJson } = require('./functions/randomStr')
// const database = new DatabaseHandler(mongoDB);
// database.connectToDatabase();
// const CommandDB = require('./models/commands');
// let data = CommandDB.find({}).select('name').then((data) => {
// let names = [];
// data.forEach((item) => { names.push(item.name) })
// console.log(names.join(', '))
// })
(async () => {
const axios = require("axios");
const query = 'yeet'
const url = ('https://api.urbandictionary.com/v0/define?term=' + query)
const response = await axios.get(url);
const data = response.data;
const def = data.list[0];
})();