├── LICENSE ├── README.md ├── assets └── help │ └── helpguide.png ├── bot.js ├── commands ├── developer │ ├── admin.js │ ├── eval.js │ ├── exec.js │ ├── reload.js │ └── throw.js ├── economy │ ├── balance.js │ ├── baltop.js │ ├── blackjack.js │ ├── buy.js │ ├── daily.js │ ├── earn.js │ ├── economy.js │ ├── gamble.js │ ├── give.js │ ├── redeem.js │ ├── sell.js │ ├── shop.js │ ├── slots.js │ └── work.js ├── fun │ ├── 8ball.js │ ├── ascii.js │ ├── challenge.js │ ├── choose.js │ ├── dadjoke.js │ ├── dice.js │ ├── league.js │ ├── magik.js │ ├── overwatch.js │ ├── owoify.js │ ├── passbeer.js │ └── ship.js ├── general │ ├── afk.js │ ├── calculate.js │ ├── credits.js │ ├── help.js │ ├── info.js │ ├── invite.js │ ├── links.js │ ├── patreon.js │ ├── ping.js │ ├── playlist.js │ ├── reminder.js │ ├── say.js │ ├── search.js │ ├── tag.js │ ├── translate.js │ └── upvote.js ├── management │ ├── autoresponse.js │ ├── autorole.js │ ├── config.js │ ├── disable.js │ ├── enable.js │ ├── experience.js │ ├── filter.js │ ├── ignore.js │ ├── log.js │ └── prefix.js ├── misc │ ├── cat.js │ ├── dog.js │ ├── flip.js │ ├── history.js │ ├── hug.js │ ├── mods.js │ ├── pfp.js │ ├── reverse.js │ ├── serverinfo.js │ └── xkcd.js ├── moderator │ ├── addrole.js │ ├── ban.js │ ├── find.js │ ├── hackban.js │ ├── kick.js │ ├── lock.js │ ├── mute.js │ ├── nick.js │ ├── purge.js │ ├── reason.js │ ├── removerole.js │ ├── renamechannel.js │ ├── softban.js │ ├── uinfo.js │ ├── unban.js │ ├── unhoist.js │ ├── unmute.js │ ├── voicekick.js │ └── warn.js ├── music │ ├── nowplaying.js │ ├── pause.js │ ├── play.js │ ├── queue.js │ ├── remove.js │ ├── repeat.js │ ├── resume.js │ ├── skip.js │ ├── stop.js │ └── time.js ├── nsfw │ ├── ass.js │ ├── boobs.js │ ├── lewdneko.js │ └── neko.js └── social │ ├── badges.js │ ├── equip.js │ ├── items.js │ ├── leaderboard.js │ ├── profile.js │ └── stats.js ├── data └── main │ ├── errorHandle │ ├── errorFooter.txt │ └── errorMessage.txt │ └── keys │ └── keys.js ├── events ├── GuildBanAdd.js ├── GuildBanRemove.js ├── GuildCreate.js ├── GuildDelete.js ├── GuildMemberAdd │ ├── index.js │ └── processors │ │ ├── autorole.js │ │ ├── index.js │ │ ├── memberlog.js │ │ └── moderationlog.js ├── GuildMemberRemove │ ├── index.js │ └── processors │ │ ├── index.js │ │ ├── memberlog.js │ │ ├── moderationlog.js │ │ └── punishment.js ├── GuildMemberUpdate │ ├── index.js │ └── processors │ │ ├── index.js │ │ ├── nickname.js │ │ └── punishment.js ├── Message │ ├── index.js │ └── processors │ │ ├── afk.js │ │ ├── autoresponse.js │ │ ├── autorole.js │ │ ├── experience.js │ │ └── index.js ├── MessageDelete.js ├── MessageUpdate.js ├── Ready │ ├── index.js │ ├── jobs │ │ ├── index.js │ │ ├── punishment.js │ │ ├── timer.js │ │ └── updateStats.js │ └── processors │ │ ├── commandLoader.js │ │ ├── extensionLoader.js │ │ ├── index.js │ │ ├── lavalink.js │ │ ├── postStats.js │ │ └── vacuum.js └── VoiceStateUpdate.js ├── extenders ├── Guild.js ├── GuildMember.js ├── Message.js ├── TextChannel.js └── User.js ├── models ├── general │ ├── Moderation.js │ ├── Playlist.js │ ├── Settings.js │ ├── Shop.js │ ├── Tags.js │ └── Timer.js ├── guild │ └── Guild.js └── user │ └── User.js ├── structures ├── PostgreSQL.js ├── games │ ├── Challenge.js │ └── Weapon.js ├── general │ ├── Google.js │ ├── LavalinkClient.js │ ├── Logs.js │ ├── Music.js │ ├── Playlist.js │ ├── Settings.js │ ├── Song.js │ ├── Tags.js │ └── Timers.js ├── internal │ ├── CommandHandler.js │ ├── Find.js │ ├── Interface.js │ ├── Permissions.js │ └── Statistics.js ├── moderation │ ├── Filter.js │ ├── Punish.js │ └── Spam.js └── user │ ├── Badges.js │ ├── Economy.js │ ├── Experience.js │ └── Inventory.js └── util ├── Constants.js ├── Duration.js ├── Emoji.js ├── GuildMemberAdd ├── index.js └── processors │ ├── autorole.js │ ├── index.js │ ├── memberlog.js │ └── moderationlog.js ├── GuildMemberRemove ├── index.js └── processors │ ├── index.js │ ├── memberlog.js │ ├── moderationlog.js │ └── punishment.js ├── GuildMemberUpdate ├── index.js └── processors │ ├── index.js │ ├── nickname.js │ └── punishment.js ├── Logger.js └── Message ├── index.js └── processors ├── afk.js ├── autoresponse.js ├── autorole.js ├── badges.js ├── experience.js └── index.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 zBlakee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zBot 2 | zBot is a highly advanced, mutli-purpose bot that can be used for moderation, games, profile customization and more! 3 | 4 | ...or at least it **was**. 5 | After running for over an entire year, with one main developer and 5 other contributors (as well as some extra help from others), zBot is being discontinued on the **20th of July, 2018**. 6 | This source code is purely for archival reasons. This code is not intended to be self-hosted or ran by anyone. 7 | I mean, you can **try**, but you probably won't get very far. 8 | 9 | You can find out more information about the discontinuation of zBot [at the website](https://zbot.me). 10 | Thank you for using zBot, I hope you enjoy this archive of code. 11 | 12 | PS. If you think my code sucks, good for you. Some of the code here isn't perfect, and I'm ready to admit that. 13 | It wasn't intended to be shown to other people. But, since zBot is being discontinued, I thought "why not?". 14 | This was my very first JavaScript project, and I've learned so much about JavaScript and programming in general from it that I am proud of the progress I made and I'm extremely proud of what I've created. 15 | -------------------------------------------------------------------------------- /assets/help/helpguide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nevvulo/zBot/72b22e51ff4aecddeede07572886d9674b882239/assets/help/helpguide.png -------------------------------------------------------------------------------- /commands/developer/exec.js: -------------------------------------------------------------------------------- 1 | exports.settings = { command: "exec", description: "Developer execution command.", usage: "exec **[code]**", permission: "dev" } 2 | exports.run = async (client, message, args) => { 3 | const Discord = require("discord.js") 4 | 5 | let silent = false 6 | let code = args.join(" ") 7 | if (!message.author.developer) return message.reply(`${client.util.emoji("nope", message.guild)} You don't have permission to use this command.`) 8 | require('child_process').exec(code, (err, out) => { 9 | if (!err) { 10 | const embed = new Discord.MessageEmbed() 11 | embed.setAuthor(`Execution » ${message.author.tag}`, client.user.displayAvatarURL()) 12 | embed.setDescription(`${client.util.emoji("ok", message.guild)} Here is the return value of your execution;`) 13 | embed.addField(`${client.util.emoji("edit", message.guild)} Input`, `\`\`\`js\n${code.length > 1010 ? `${code.substr(0, 1010)}...` : code}\`\`\``) 14 | embed.addField(`${client.util.emoji("info", message.guild)} Evaluation`, `\`\`\`js\n${out.length > 1010 ? `${out.substr(0, 1010)}...` : out}\`\`\``) 15 | embed.setColor("#7e57c2") 16 | message.channel.send({embed}).then(m => { 17 | m.react(client.util.emoji("ok", message.guild).replace("<:", "").replace(">", "")) 18 | }) 19 | } else { 20 | const embed = new Discord.MessageEmbed() 21 | embed.setAuthor(`Execution » ${message.author.tag}`, client.user.displayAvatarURL()) 22 | embed.setDescription(`${client.util.emoji("nope", message.guild)} An error occured whilst executing your code;`) 23 | embed.addField(`${client.util.emoji("edit", message.guild)} Input`, `\`\`\`js\n${code.length > 1010 ? `${code.substr(0, 1010)}...` : code}\`\`\``) 24 | embed.addField(`${client.util.emoji("nope", message.guild)} Error`, `\`\`\`js\n${err.length > 1010 ? `${err.substr(0, 1010)}...` : err}\`\`\``) 25 | embed.setColor("#ad1457") 26 | message.channel.send({embed}).then(m => { 27 | m.react(client.util.emoji("nope", message.guild).replace("<:", "").replace(">", "")) 28 | }) 29 | } 30 | }) 31 | } -------------------------------------------------------------------------------- /commands/developer/reload.js: -------------------------------------------------------------------------------- 1 | exports.run = (client, message, args) => { 2 | const type = args[0] 3 | if (type.includes("structure")) { 4 | delete require.cache[require.resolve(`./../../structures/general/${args[1]}.js`)] 5 | message.zreply(`${client.util.emoji("ok", message.guild)} \`${args[1]}\` was reloaded.`) 6 | } else { 7 | // the path is relative to the *current folder*, so just ./filename.js 8 | let category 9 | for (const [key, value] of client.categories) { if (value.includes(args[1])) category = key } 10 | if (!category) throw "That command doesn't exist." 11 | delete require.cache[require.resolve(`./../${category}/${args[1]}.js`)] 12 | client.commands.set(args[1], require(`./../${category}/${args[1]}.js`)) 13 | message.zreply(`${client.util.emoji("ok", message.guild)} \`${args[1]}\` was reloaded.`) 14 | } 15 | } 16 | 17 | let command = "reload" 18 | , description = "Reloads a module." 19 | , usage = "reload (module)" 20 | , throttle = {usages: 3, duration: 10} 21 | , permission = "dev" 22 | , category = "DEV" 23 | exports.settings = {command: command, description: description, usage: usage, throttle: throttle, permission: permission, category: category} 24 | -------------------------------------------------------------------------------- /commands/developer/throw.js: -------------------------------------------------------------------------------- 1 | exports.run = (client, message, args) => { if (args == "") return; throw args.slice(0).join(" ") } 2 | 3 | let command = "throw" 4 | , description = "Throws an error." 5 | , usage = "throw" 6 | , throttle = {usages: 4, duration: 10} 7 | , permission = "dev" 8 | , category = "DEV" 9 | exports.settings = {command: command, description: description, usage: usage, throttle: throttle, permission: permission, category: category} 10 | -------------------------------------------------------------------------------- /commands/economy/balance.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "balance", description: "Show your balance, or show another users balance.", usage: "balance [user]", aliases: ["bal"], throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const Constants = require("./../../util/Constants.js") 5 | const Economy = new (require("./../../structures/user/Economy.js")); 6 | if (!args[0]) args[0] = message.author.id; 7 | const member = Find.member(args.slice(0).join(' '), message.guild)[0]; 8 | if (!member) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a user under that search.`); 9 | const balance = (await member.profile).get("balance"); 10 | return message.zreply(`${client.util.emoji("ok", message.guild)} ${member.id == message.author.id ? "You have" : `${member.displayName} has`} \`${balance}\` ${balance == 1 ? 11 | Constants.CurrencySettings.name.singular : Constants.CurrencySettings.name.plural}.`) 12 | } 13 | -------------------------------------------------------------------------------- /commands/economy/baltop.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "baltop", description: "Displays a leaderboard of the users with the highest balance.", usage: ["baltop (list) [page]", "baltop (top) [number 1-10]"], 2 | throttle: {usages: 3, duration: 10} } 3 | exports.run = async (client, message, args) => { 4 | const User = require("./../../models/user/User.js") 5 | const Settings = require("./../../structures/general/Settings.js") 6 | const Constants = require("./../../util/Constants.js") 7 | const { Interface } = require("./../../structures/internal/Interface.js") 8 | const subcommand = args[0] || "top" 9 | let limit = (args[1] == undefined ? "" : args[1]) 10 | const memberCountnb = message.guild.members.filter(a => !a.user.bot).array().length 11 | 12 | if (subcommand == "top") { 13 | if (!limit) limit = 5 14 | if (memberCountnb < 6) limit = memberCountnb 15 | if (limit > 10) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't display more than 10 people at a time.`) 16 | 17 | const userProfile = await User.findAll({ where: { guildID: message.guild.id }, attributes: ["userID", "balance"], order: [["balance", "DESC"]], limit: Number(limit) }) 18 | const leaderboard = userProfile.sort((a, b) => b.balance - a.balance) 19 | .filter(user => client.users.has(user.userID)) 20 | .map((user, position) => `\`${position + 1}\`. **${(client.users.get(user.userID).tag)}** ─ \`${user.balance}\` ${user.balance == 1 ? 21 | Constants.CurrencySettings.name.singular : Constants.CurrencySettings.name.plural}\n`) 22 | 23 | new Interface().PaginationMenu(leaderboard, {title: `Highest Balance » ${message.guild.name}`, author: message.guild.iconURL(), type: "Top Balance", color: "ffb74d", description: `${client.util.emoji("ok", message.guild)} Here is ` + 24 | "the users with the highest balance in this guild.", pageLength: limit, delimiter: "\n"}, message, limit) 25 | } else if (subcommand == "list") { 26 | const userProfile = await User.findAll({ where: { guildID: message.guild.id }, attributes: ["userID", "balance"], order: [["balance", "DESC"]] }) 27 | const leaderboard = userProfile.sort((a, b) => b.balance - a.balance) 28 | .filter(user => client.users.has(user.userID)) 29 | .map((user, position) => `\`${position + 1}\`. **${(client.users.get(user.userID).tag)}** ─ \`${user.balance}\` ${user.balance == 1 ? 30 | Constants.CurrencySettings.name.singular : Constants.CurrencySettings.name.plural}\n`) 31 | new Interface().PaginationMenu(leaderboard, {title: `Balance Leaderboard » ${message.guild.name}`, author: message.guild.iconURL(), type: "Balance Leaderboard", color: "ffd54f", description: `${client.util.emoji("ok", message.guild)} Here is ` + 32 | `the balance leaderboard for this guild.\nTo see the next page, type \`${await message.guild.prefix}baltop list [page number]\`.`, pageLength: 10, delimiter: "\n"}, message, limit) 33 | } else { 34 | return message.zreply(`${client.util.emoji("nope", message.guild)} The syntax of the command is incorrect. For more information, type \`${await Settings.getValue(message.guild, "prefix")}help ${this.settings.command}\`.`) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /commands/economy/buy.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "buy", description: "Allows you to buy an item by it's name/ID from the shop.", usage: "buy [item name/id]", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const Constants = require("./../../util/Constants.js") 5 | const Shop = require("./../../models/general/Shop.js") 6 | const Economy = new (require("./../../structures/user/Economy.js")); 7 | const Sequelize = require('sequelize') 8 | const Op = Sequelize.Op 9 | const Inventory = new (require("./../../structures/user/Inventory.js"))(message.member); 10 | const item = await Shop.findOne({ where: { id: { [Op.like]: args.slice(0).join(" ") } } }); 11 | 12 | if (!item) return message.zreply(`${client.util.emoji("nope", message.guild)} That item doesn't exist. Try \`${await message.guild.prefix}shop\` \ 13 | to see the different items you can buy!`); 14 | if (await Inventory.has(item)) return message.zreply(`${client.util.emoji("nope", message.guild)} You already own this item.`); 15 | const balance = (await message.member.profile).get("balance"); 16 | if (item.cost > balance) return message.zreply(`${client.util.emoji("nope", message.guild)} You need \`${item.cost-balance}\` \ 17 | more ${Constants.CurrencySettings.name.plural} to purchase \`${item.name}\`.`); 18 | 19 | console.log(item.dataValues) 20 | await Inventory.add(item.dataValues); 21 | await Economy.remove(message.member, item.cost) 22 | message.zreply(`${client.util.emoji("ok", message.guild)} Great success! You've just purchased \`${item.name}\`.`) 23 | } 24 | -------------------------------------------------------------------------------- /commands/economy/earn.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "earn", description: "Shows all the possible ways to earn money in zBot.", usage: "earn", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Discord = require("discord.js") 4 | const Constants = require("./../../util/Constants.js") 5 | const prefix = await message.guild.prefix; 6 | const embed = new Discord.MessageEmbed() 7 | embed.setAuthor(`Earn Currency » ${message.author.tag}`, message.author.displayAvatarURL()) 8 | embed.setDescription(`**[${prefix}daily](https://zbot.me/documentation)** ─ \`earn ${Constants.CurrencySettings.symbol}100 (and bonus money for streaks!)\`\n\ 9 | **[${prefix}work](https://zbot.me/documentation)** ─ \`earn ${Constants.CurrencySettings.name.plural} as you work, the more you work, the more ${Constants.CurrencySettings.name.plural} you get!)\`\n\ 10 | **[Become a patron](https://patreon.com/zbot)** ─ \`earn ${Constants.CurrencySettings.symbol}1000 for every dollar you donate using ${prefix}redeem\`\n\ 11 | **[${prefix}blackjack](https://zbot.me/documentation)** ─ \`bet ${Constants.CurrencySettings.name.plural} to play a game of Blackjack, winners earn double of what they bet!\`\n\ 12 | **[${prefix}gamble](https://zbot.me/documentation)** ─ \`gamble your ${Constants.CurrencySettings.name.plural} away with dice rolls, winners earn double of what they bet!\`\n\ 13 | **[${prefix}sell](https://zbot.me/documentation)** ─ \`if you have any spare items in your inventory, you can sell them back for half of what they were worth\`\n`); 14 | embed.setColor("#009688") 15 | return message.channel.zend({ embed }); 16 | } 17 | -------------------------------------------------------------------------------- /commands/economy/gamble.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "gamble", description: "Allows you to bet money to either lose or earn more.", usage: "gamble [amount of money]", 2 | info: "You are betting with a 12-sided die. If you roll a higher amount than the bot, you win!", throttle: {usages: 4, duration: 10} } 3 | exports.run = async (client, message, args) => { 4 | const Discord = require("discord.js"); 5 | const Economy = new (require("./../../structures/user/Economy.js")); 6 | const Constants = require("./../../util/Constants.js") 7 | const profile = (await message.member.profile) 8 | let bet = Math.floor(Number(args.slice(0).join(" "))) || 50 9 | if (!bet) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to bet a valid amount of ${Constants.CurrencySettings.name.plural}`) 10 | if (bet < 1) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't bet a negative amount of ${Constants.CurrencySettings.name.plural}.`) 11 | if (profile.get("balance") < bet) return message.zreply(`${client.util.emoji("nope", message.guild)} You don't have enough \ 12 | ${Constants.CurrencySettings.name.plural} to bet this amount.`) 13 | const roll = { 14 | bot: Number(Math.floor(Math.random() * 12) + 1), 15 | user: Number(Math.floor(Math.random() * 12) + 1) 16 | } 17 | 18 | await Economy.remove(message.member, bet) 19 | 20 | const gambed = function () { 21 | const embed = new Discord.MessageEmbed() 22 | embed.setAuthor(`Gamble » ${message.author.tag}`, message.author.displayAvatarURL()) 23 | embed.setDescription(`${roll.user !== roll.bot ? (roll.user > roll.bot ? `You won \`${Constants.CurrencySettings.symbol}${bet*2}\`!` : 24 | `You lost \`${Constants.CurrencySettings.symbol}${bet}\`.`) : `You tied with ${client.user.username 25 | }, quadrupling your bet. You won \`${Constants.CurrencySettings.symbol}${bet*4}\`!`}`) 26 | embed.addField(`${client.util.emoji(`${roll.user == roll.bot ? "empty" : (roll.user > roll.bot ? "ok" : "nope")}`, 27 | message.guild)} ${message.author.tag}`, `Rolled \`${roll.user}\``, true) 28 | embed.addField(`${client.util.emoji(`${roll.user == roll.bot ? "empty" : (roll.user > roll.bot ? "nope" : "ok")}`, 29 | message.guild)} ${client.user.username}`, `Rolled \`${roll.bot}\``, true) 30 | embed.setColor("#ffa726") 31 | return message.channel.zend({ embed }) 32 | } 33 | 34 | gambed(); 35 | if (roll.user >= roll.bot) await Economy.add(message.member, bet*2) 36 | if (roll.user == roll.bot) await Economy.add(message.member, bet*2) 37 | } 38 | -------------------------------------------------------------------------------- /commands/economy/give.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "give", description: "Show your generosity and give another user some money!", usage: "give (user) (amount)", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const Constants = require("./../../util/Constants.js") 5 | const Economy = new (require("./../../structures/user/Economy.js")); 6 | const balance = (await message.member.profile).get("balance"); 7 | 8 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to provide a user to give money to.`); 9 | const member = Find.member(args[0], message.guild)[0]; 10 | if (message.member == member) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't give yourself money!`) 11 | if (!member) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a user under that search.`); 12 | 13 | const amount = Math.floor(Number(args[1])) || null 14 | if (!amount) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to provide an amount of money that you want to give.`) 15 | if (amount < 1) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't give negative amounts of money!`) 16 | if (amount > balance) return message.zreply(`${client.util.emoji("nope", message.guild)} You don't have that amount of money to give to this person!`) 17 | 18 | await Economy.add(member, amount); //add amount to receiver 19 | await Economy.remove(message.member, amount); //remove amount from giver 20 | return message.zreply(`${client.util.emoji("ok", message.guild)} You have successfully given \`${Constants.CurrencySettings.symbol}${amount}\` to \ 21 | \`${member.displayName}\`!`) 22 | } 23 | -------------------------------------------------------------------------------- /commands/economy/redeem.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "redeem", description: "Redeem your donator rewards if you are a patron/donator!", usage: "redeem", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const Constants = require("./../../util/Constants.js") 5 | const Discord = require("discord.js") 6 | const Economy = new (require("./../../structures/user/Economy.js")); 7 | if (!message.author.patron) return message.zreply(`${client.util.emoji("nope", message.guild)} This command is only available to those who have donated in \ 8 | the past or are a patron on the zBot patreon page. If you feel that this incorrect, please contact \`${client.developer.tag}\`.`) 9 | if ((await message.member.profile).get("donator").redeemed) return message.zreply(`${client.util.emoji("nope", message.guild)} You have already redeemed \ 10 | your reward on this guild. If you feel that this incorrect, please contact \`${client.developer.tag}\`.`) 11 | const balance = (await message.member.profile).get("balance"); 12 | 13 | const patronAmount = await Economy.patron(message.member, message.author.patron.level) 14 | const embed = new Discord.MessageEmbed(); 15 | embed.setAuthor(`Donator Redeem » ${message.author.tag}`, message.author.displayAvatarURL()) 16 | embed.setDescription(`${client.util.emoji("ok", message.guild)} Thank you for donating to zBot! It means a lot that you support the work that I (\`${ 17 | client.developer.tag}\`) put into zBot. Enjoy your rewards!`) 18 | embed.addField(message.author.patron.type, `\`\`\`diff\n+ ${Constants.CurrencySettings.symbol}${patronAmount}\`\`\``) 19 | embed.setColor("#4caf50") 20 | return message.channel.zend({ embed }) 21 | } 22 | -------------------------------------------------------------------------------- /commands/economy/sell.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "sell", description: "Allows you to sell an item that you have previously bought from the shop, and earn money back on it", 2 | usage: "sell (item)", aliases: ["trade"], throttle: {usages: 4, duration: 10} } 3 | exports.run = async (client, message, args) => { 4 | const Find = require("./../../structures/internal/Find.js") 5 | const Constants = require("./../../util/Constants.js") 6 | const Shop = require("./../../models/general/Shop.js") 7 | const Economy = new (require("./../../structures/user/Economy.js")); 8 | const Sequelize = require('sequelize') 9 | const Op = Sequelize.Op 10 | const Inventory = new (require("./../../structures/user/Inventory.js"))(message.member); 11 | const item = await Shop.findOne({ where: { id: { [Op.like]: args.slice(0).join(" ") } } }); 12 | 13 | if (!item) return message.zreply(`${client.util.emoji("nope", message.guild)} That item doesn't exist.`); 14 | if (item.type == "badge") return message.zreply(`${client.util.emoji("nope", message.guild)} You can't sell badges!`); 15 | if (!await Inventory.has(item)) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't sell an item you don't own.`); 16 | 17 | await Inventory.remove(item.dataValues); 18 | await Economy.add(message.member, item.cost/2) 19 | message.zreply(`${client.util.emoji("ok", message.guild)} You have successfully sold the item \`${item.name}\`. You no longer own this item, and you have earned \ 20 | ${item.cost/2} ${Constants.CurrencySettings.name.plural} back.`) 21 | } 22 | -------------------------------------------------------------------------------- /commands/economy/shop.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "shop", description: "Displays the shop and the available items you can buy in zBot.", usage: "shop [type | page]", aliases: ["store"], throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const Constants = require("./../../util/Constants.js") 5 | const { Interface } = require("./../../structures/internal/Interface.js") 6 | const Economy = new (require("./../../structures/user/Economy.js")); 7 | const Inventory = new (require("./../../structures/user/Inventory.js"))(message.member); 8 | if (!args[0]) args[0] = 1 9 | 10 | let backgrounds = []; 11 | for (let i in Object.values(Constants.Items.background)) { 12 | let current = Object.values(Constants.Items.background)[i]; 13 | if (await Inventory.has(current)) continue; 14 | backgrounds.push(`**${current.name}** ─ ${Constants.CurrencySettings.symbol}${current.cost}\n*ID* \`${current.id}\` ─ ${current.type.charAt(0).toUpperCase() 15 | }${current.type.slice(1)}\n`) 16 | } 17 | 18 | const shop = [backgrounds || null] 19 | if (!shop[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You've bought everything in the shop, there are no new items. Congratulations!`); 20 | new Interface().PaginationMenu(shop[0], {title: `zBot Shop`, author: client.user.displayAvatarURL(), 21 | type: "Shop Items", color: "424242", description: `${client.util.emoji("ok", message.guild)} G'day! Here is what is available in the shop right now; \ 22 | \nTo see the next page of the shop, type \`${await message.guild.prefix}shop [page number]\`.`, pageLength: 7, delimiter: "\n"}, message, args[0]) 23 | } 24 | -------------------------------------------------------------------------------- /commands/economy/work.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "work", description: "Quick way to earn money, work 8 hours and receive your salary!", usage: "work", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const Constants = require("./../../util/Constants.js") 5 | const Economy = new (require("./../../structures/user/Economy.js")); 6 | let info = (await message.member.profile).get("work"); 7 | 8 | const now = Date.now(); 9 | const last = info.lastWorked || 0; 10 | const diff = now - last; 11 | const next = 1800000 - diff; 12 | 13 | const hours = Math.floor(next / 3600000); 14 | const minutes = Math.floor((next / 60000) - (hours * 60)); 15 | const seconds = Math.floor((next / 1000) - ((hours * 3600) + (minutes * 60))); 16 | 17 | const timeArr = [{ type: {singular: "hour", plural: "hours"}, amount: hours }, 18 | { type: {singular: "minute", plural: "minutes"}, amount: minutes }, 19 | { type: {singular: "second", plural: "seconds"}, amount: seconds } ]; 20 | let properArr = []; 21 | 22 | for (let i in timeArr) { 23 | if (timeArr[i].amount < 1) continue; 24 | properArr.push(`${timeArr[i].amount} ${timeArr[i].amount == 1 ? timeArr[i].type.singular : timeArr[i].type.plural}`) 25 | } 26 | let timeLeft = properArr.slice(0, -2).join(', ') + (properArr.slice(0, -2).length ? ', ' : '') + properArr.slice(-2).join(' and '); 27 | const funkyQuotes = [ 28 | "What a long day!", 29 | "What a hard worker you are!", 30 | "Nothing like cracking a cold one open with the boys!", 31 | "Just another day in the office!", 32 | "Good work there!", 33 | "Working hard or hardly working?", 34 | "Time to get off the feet!", 35 | "Time to relax!", 36 | "Hard day!", 37 | "Money, money, money!", 38 | "Nothing can stop you now!", 39 | "Don't stop me now!", 40 | "No worries!", 41 | "Not a hassle!", 42 | "No stress at all!", 43 | "Flying through work like nothing!", 44 | "Too easy!", 45 | "I'm having such a good time!", 46 | "I'm having a ball!", 47 | "If only it were this easy in real life!", 48 | "No struggles here!" 49 | ] 50 | 51 | if (diff >= 1800000) { 52 | let type = "user"; 53 | const types = { 54 | "plus": message.author.patron.level == 1, 55 | "ultra": message.author.patron.level == 2, 56 | "developer": message.author.developer, 57 | "contributor": Object.keys(client.contributors).includes(message.author.id), 58 | "upvoter": message.author.upvoted, 59 | "user": true 60 | } 61 | 62 | for (let [key, value] of Object.entries(types)) { 63 | if (value) { 64 | type = key; 65 | break; 66 | } 67 | } 68 | const work = await Economy.work(message.member, type) 69 | return message.zreply(`${client.util.emoji("ok", message.guild)} ${funkyQuotes[Math.floor(Math.random()* 70 | funkyQuotes.length)]} You work long and hard to receive \`${Constants.CurrencySettings.symbol}${work.earned}\`. You have now worked a total of \`${work.hours}\` hours.`) 71 | } else { 72 | return message.zreply(`${client.util.emoji("nope", message.guild)} You need to wait \`${timeLeft}\` before you can work again.`) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /commands/fun/8ball.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "8ball", description: "Ask the 8-ball a question and get a random response.", usage: "8ball (question)", throttle: {usages: 3, duration: 7}} 2 | exports.run = (client, message, args) => { 3 | const responses = [ 4 | "It is certain.", 5 | "It is decidedly so.", 6 | "Without a doubt.", 7 | "Yes, definitely.", 8 | "You may rely on it.", 9 | "As I see it, yes.", 10 | "Most likely.", 11 | "Outlook is good.", 12 | "Yes.", 13 | "Signs point to yes.", 14 | "Reply hazy, try again later.", 15 | "Ask again later.", 16 | "Better not tell you now.", 17 | "Cannot predict now.", 18 | "Concentrate and ask again.", 19 | "Don't count on it.", 20 | "My reply is no.", 21 | "My sources say no.", 22 | "Outlook is not so good.", 23 | "Very doubtful.", 24 | ] 25 | 26 | args = args.slice(0).join(" ") 27 | if (!args) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to specify a question/message.`) 28 | message.channel.zend(`${client.util.emoji("offline", message.guild)} \`${args.replace(/`/g, "`").replace(/\n/g, " ")}\` ─ *Asked by \`${message.author.tag}\`.*\n:8ball: ${responses[Math.floor(Math.random() * responses.length)]}`) 29 | // replacing the codeblock character with a similar unicode character 30 | } 31 | -------------------------------------------------------------------------------- /commands/fun/ascii.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "ascii", description: "Turns your text into a large ASCII formation.", usage: "ascii (text)", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const snekfetch = require("snekfetch") 4 | const text = args.slice(0).join(" ") 5 | if (!text) return message.zreply(`${client.util.emoji("nope", message.guild)} Your message is too short to be converted to ASCII.`) 6 | if (text.length > 128) return message.zreply(`${client.util.emoji("nope", message.guild)} Your message is too long to be converted to ASCII.`) 7 | const res = await snekfetch.get(`http://artii.herokuapp.com/make?text=${text}`) 8 | .set("Accept", "text/plain") 9 | .catch(() => { 10 | message.zreply(`${client.util.emoji("nope", message.guild)} An error occured whilst ASCII-ifying the text. Please try again later.`) 11 | }) 12 | message.channel.zend(`${client.util.emoji("ok", message.guild)} Here is your ASCII art with the text \`${text}\`;\n\`\`\`${res.text}\`\`\``) 13 | } 14 | -------------------------------------------------------------------------------- /commands/fun/dadjoke.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "dadjoke", description: "Gives a random joke, for better or for worse.", usage: "dadjoke", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const snekfetch = require("snekfetch") 4 | snekfetch.get("https://icanhazdadjoke.com/") 5 | .set("Accept", "text/plain") 6 | .then(res => { 7 | message.channel.zend(`:rofl: **DAD JOKE**: *${res.text}*`) 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /commands/fun/dice.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "dice", description: "Rolls a dice.", usage: "dice [number [...]]", 2 | info: "Multiple die can be rolled by separating each die you want to roll with a space. You can roll any number from 1 - 2147483647.", throttle: {usages: 4, duration: 10} } 3 | exports.run = async (client, message, args) => { 4 | let dice = args.slice(0).join(" ") || 6 5 | 6 | if (args.length > 1) { dice = args } 7 | else { 8 | if (Number(args[0]) > 2147483647) return message.zreply(`${client.util.emoji("nope", message.guild)} The number you've provided is too high.`) 9 | dice = parseInt(args[0], 10) 10 | } 11 | 12 | if (dice.constructor === Array) { 13 | if (dice.length > 25) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't roll more than 25 die at a time.`) 14 | const diceArr = [] 15 | for (let i = 0; i < dice.length; i++) { 16 | if (Number(dice[i]) > 2147483647) return message.zreply(`${client.util.emoji("nope", message.guild)} The number you've provided is too high.`) 17 | const roll = Number(Math.floor(Math.random() * dice[i]) + 1) 18 | if (isNaN(roll)) return message.zreply(`${client.util.emoji("nope", message.guild)} You've provided an invalid number in one of your rolls.`) 19 | diceArr.push(roll) 20 | } 21 | dice = diceArr.join(", ") 22 | } else { 23 | if (isNaN(dice)) return message.zreply(`${client.util.emoji("nope", message.guild)} That's not a valid number.`) 24 | dice = Math.floor(Math.random() * dice) + 1 25 | } 26 | 27 | const randomPhrase = [`Looks like you've rolled a **${dice}** this time.`, `You've landed on **${dice}**.`, `Alright, it's landed on **${ 28 | dice}**.`, `Oh! Would you look at that? The dice landed on **${dice}**!`] 29 | message.zreply(`:game_die: **DICE**: ${randomPhrase[Math.floor(Math.random() * randomPhrase.length)]}`) 30 | } 31 | -------------------------------------------------------------------------------- /commands/fun/magik.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "magik", description: "Sprinkle some magic onto any image you want!", usage: "magik (attachment | url)", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const snekfetch = require("snekfetch") 4 | const text = args.slice(0).join(" ") 5 | if (!text && !message.attachments.size) return message.zreply(`${client.util.emoji("nope", message.guild)} No image/attachment or URL was provided.`) 6 | const res = await snekfetch.get(`https://discord.services/api/magik?url=${text || message.attachments.first().url}`) 7 | .catch(() => { 8 | return message.zreply(`${client.util.emoji("nope", message.guild)} Unfortunately an error occured whilst trying to improve your image.`) 9 | }) 10 | return message.channel.zend(`${client.util.emoji("ok", message.guild)} I've sprinkled some magic onto your image.`, {files: [res.body]}) 11 | } 12 | -------------------------------------------------------------------------------- /commands/fun/owoify.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "owoify", description: "Translate any text you want into OwO form!", usage: "owoify (text)", aliases: ["owo"], throttle: {usages: 3, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const api = new (require("./../../data/main/keys/keys.js"))() 4 | require('snekfetch').get(`https://dev.anidiots.guide/text/owoify`) 5 | .set("Authorization", api.idiot) 6 | .query({ text: args.slice(0).join(" ") }) 7 | .then(res => { 8 | return message.channel.zend(`${String.fromCharCode(0x200B)}${res.body.text}`) //zws 9 | }).catch(err => { throw err }); 10 | } 11 | -------------------------------------------------------------------------------- /commands/fun/passbeer.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "passbeer", description: "Had a long day at work? Get a beer, or pass a beer to someone else.", usage: "passbeer [user]", throttle: {usages: 3, duration: 7} } 2 | exports.run = (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | if (args.toString() == "") { 5 | message.zreply(`${client.util.emoji("ok", message.guild)} Here's a beer! :beer:`) 6 | const filter = m => m.author == message.author 7 | message.channel.awaitMessages(filter, { max: 1, time: 15000, errors: ["time"] }) 8 | .then(collected => { 9 | if (collected.first().content.toLowerCase().startsWith("make that")) { 10 | const number = collected.first().content.toLowerCase().split("make that").slice(1) 11 | message.zreply(`${client.util.emoji("ok", message.guild)} Here's ${number} more beers! :beers:`) 12 | } 13 | }) 14 | return 15 | } 16 | 17 | args = args.toString() 18 | args = Find.member(args, message.guild).shift() 19 | if (args == undefined) return `${client.util.emoji("nope", message.guild)} I couldn't find any users under that query.` 20 | message.guild.members.fetch(args.id).then(function(member) { 21 | message.channel.zend(`${client.util.emoji("ok", message.guild)} ${message.author} passed ${member.displayName} a beer! Cheers! :beers:`) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /commands/fun/ship.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "ship", description: "Ships you with another random user.", usage: "ship [user]", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const user = args.slice(0).join(" ") 5 | const userGrabbed = Find.member(user, message.guild)[0] 6 | if (!userGrabbed) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a user under that search.`) 7 | 8 | const properUser2 = (!user ? message.guild.members.random().displayName : userGrabbed.displayName) 9 | const properUser1 = message.member.displayName 10 | const choppedUser1 = properUser1.slice(0, Math.floor(properUser1.length / 2)) 11 | const choppedUser2 = properUser2.slice(Math.floor(properUser1.length / 2)) 12 | return message.channel.zend(`:ship: \`${choppedUser1}${choppedUser2}\` *(**${properUser1} **and** ${properUser2}**)*`) 13 | } 14 | -------------------------------------------------------------------------------- /commands/general/afk.js: -------------------------------------------------------------------------------- 1 | exports.settings = { command: "afk", description: "Toggle your AFK status in zBot.", usage: "afk [reason]", throttle: { usages: 3, duration: 10 } } 2 | exports.run = async (client, message, args) => { 3 | const reason = args.slice(0).join(" ") || null 4 | const user = (await message.member.profile); 5 | 6 | if (user.get("afk").afk) { 7 | await message.member.update("afk", { afk: true, reason: null }) 8 | return message.zreply(`${client.util.emoji("ok", message.guild)} You are no longer AFK.`) 9 | } else { 10 | await message.member.update("afk", { afk: true, reason: reason }) 11 | return message.zreply( 12 | `${client.util.emoji("ok", message.guild)} You are now marked as AFK.\nIf you are mentioned in a message, I'll let that user know that you are AFK${reason !== null ? ` with the reason \`${reason}\`.` : "."}` 13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /commands/general/credits.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "credits", description: "Shows credits and information relating to people who have contributed to zBot.", usage: "credits", throttle: {usages: 2, duration: 7}} 2 | exports.run = async (client, message) => { 3 | const contributors = { 4 | "286166184402092042": "providing hosting for zBot <3", 5 | "278805875978928128": "for providing a codebase for zBot", 6 | "236279900728721409": "help with lavalink and optimization", 7 | "284551391781978112": "support, testing and providing ideas", 8 | "272689325521502208": "big help with nsfw & music commands" 9 | } 10 | 11 | const Discord = require("discord.js") 12 | const embed = new Discord.MessageEmbed() 13 | embed.setAuthor(`Credits » zBot - created by ${client.developer.tag}`, client.user.avatarURL( {format: "png"} )) 14 | embed.setColor("#e64a19") 15 | let msg = "" 16 | for (const i in contributors) { 17 | const user = await client.users.fetch(i); 18 | msg += `**${user.tag}** ─ \`${contributors[i]}\`\n` 19 | } 20 | embed.setThumbnail(client.user.avatarURL( {format: "png"} )) 21 | embed.addField("Special Thanks", msg) 22 | msg = "Badge icons provided courtesy of [`twemoji`](https://github.com/twitter/twemoji)\nBackground images from profiles are primarily provided by [Unsplash](https://unsplash.com/) [(see the full list of background credits here)](https://pastebin.com/YhC7YFBR)" 23 | embed.addField("Images", msg) 24 | let thankMessage = "Thanks for using zBot!" 25 | 26 | const messages = { 27 | "developer": {message: "Thanks for creating zBot!", members: [client.developer.id]}, 28 | "contributor": {message: "Thanks for being involved with the development of zBot!", members: ["284551391781978112", "278805875978928128"]}, 29 | "meme": {message: "Thanks for being gay!", members: ["197532753556668417"]}, 30 | "special": {message: "Thanks for being special!", members: ["269273494733324308"]}, 31 | "patron": {message: "Thanks for being a patron of zBot! You are a sick lad!", members: [message.author.patron]}, 32 | "upvote": {message: "Thanks for upvoting zBot!", members: [message.author.upvoted]} 33 | } 34 | for (const i in messages) { 35 | if (messages[i].members.includes(message.author.id)) { 36 | thankMessage = messages[i].message 37 | break 38 | } 39 | } 40 | embed.setFooter(`Finally, thanks to you ${message.author.username}. ${thankMessage}`) 41 | message.channel.zend({ embed }) 42 | } 43 | -------------------------------------------------------------------------------- /commands/general/invite.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "invite", description: "Shows invite links and other places you can find zBot.", usage: "invite", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const Discord = require("discord.js") 4 | const embed = new Discord.MessageEmbed() 5 | const base = `https://discordapp.com/oauth2/authorize?client_id=${client.user.id}&scope=bot&permissions=` 6 | 7 | const admin = 8; 8 | const normal = 536210679; 9 | const minimal = 136364160; 10 | const none = 0; 11 | 12 | embed.setAuthor(`Invite ${client.user.username} » ${message.author.tag}`, message.author.displayAvatarURL()) 13 | embed.setDescription(`**[Click here](${base}${normal})** to \`invite ${client.user.username} to your server!\`\n\n\ 14 | [Administrator](${base}${admin}) ─ [Minimal](${base}${minimal}) ─ [No Permissions](${base}${none}) 15 | If you don't know which invite to choose,\n\`Administrator\` provides the best experience.`) 16 | embed.setColor("#388e3c") 17 | return message.channel.zend({ embed }) 18 | } 19 | -------------------------------------------------------------------------------- /commands/general/links.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "links", description: "Displays all the different links that are useful to zBot and different places you can go to learn more about zBot.", usage: "links", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const Discord = require("discord.js") 4 | const embed = new Discord.MessageEmbed() 5 | embed.setAuthor(`Invite Links » ${message.author.tag}`, message.author.displayAvatarURL()) 6 | embed.setDescription("**[Click here](https://discordapp.com/oauth2/authorize?client_id=345766303052857344&scope=bot&permissions=536210679)** to `invite zBot to your server!`\n" + 7 | "**[Click here](https://discord.gg/uQWCfXB)** to `join our support server!`\n" + 8 | "**[Click here](https://zbot.me)** to `check out the official zBot website!`\n" + 9 | "**[Click here](https://discordbots.org/bot/345766303052857344)** to `check out zBot on discordbots.org!`\n" + 10 | "**[Click here](https://trello.com/b/cxV37X4Y)** to `see the zBot Trello board!`\n") 11 | embed.setColor("#009688") 12 | return message.channel.zend({ embed }); 13 | } 14 | -------------------------------------------------------------------------------- /commands/general/patreon.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "patreon", description: "Shows patreon information for zBot.", usage: "patreon", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const Discord = require("discord.js") 4 | const embed = new Discord.MessageEmbed() 5 | embed.setAuthor(`Patreon » ${message.author.tag}`, message.author.displayAvatarURL()) 6 | embed.setDescription("**[Click here](https://patreon.com/zbot)** to `check out the zBot patreon page!`\n\nYou can check out the different rewards and perks\nyou can get from donating on the page as well!") 7 | embed.setColor("#ffcdd2") 8 | return message.channel.zend({ embed }) 9 | } 10 | -------------------------------------------------------------------------------- /commands/general/ping.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "ping", description: "Shows the ping time for zBot.", usage: "ping", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const now = Date.now(); 4 | message.channel.zend("Pinging...").then(sent => { 5 | const pingtime = sent.createdTimestamp - now; 6 | sent.edit(`${client.util.emoji((pingtime > 300 ? "dnd" : (pingtime > 150 ? "idle" : "online")), message.guild)} **PONG!** zBot *(message)* took **__${pingtime}ms__** to respond. Heartbeat acknowledgement *(client)* took **__${Math.round(client.ping)}ms__** to respond.`) 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /commands/general/say.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "say", description: "Say a message under the bot.", usage: "say (message)", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message, args) => { 3 | let includename = false 4 | let say = args.slice(0).join(" ") 5 | if (say.indexOf("--in") > -1) { 6 | say = say.replace("--in", "") 7 | includename = true 8 | } 9 | message.channel.zend((includename ? `${client.util.emoji("edit", message.guild)} **${message.author.tag}** ─ ` : `${String.fromCharCode(0x200B)}`) + say) 10 | } 11 | -------------------------------------------------------------------------------- /commands/general/search.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "search", description: "Searches a given query on Google.", usage: "search (query)", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Google = require("./../../structures/general/Google.js") 4 | const searchString = args.slice(0).join(" ") 5 | 6 | if (!searchString) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter something to search for. See \`${await message.guild.prefix} help search\` for more information.`) 7 | const randomPhrase = ["Right, here's what I've found under", "No problem! Here's what I've got for", "Searched the web for", "Alright, searching the web for", "Got that. Here's what I've got for", "Copy that, here's what I've found under", "Okay, I've found this for", "Here's what I've found under", "No problem boss. Found results for"] 8 | const phrase = randomPhrase[Math.floor(Math.random() * randomPhrase.length)] 9 | 10 | Google.search(searchString, message.channel && message.channel.nsfw) 11 | .then(({ card, results }) => { 12 | if (card) { 13 | message.zreply(`${client.util.emoji("ok", message.guild)} ${phrase} "**${searchString.length > 200 ? searchString.substr(0, 200) : searchString}**".\n${card}`) 14 | } else if (results.length) { 15 | const links = results.map((r) => r.link) 16 | message.zreply(`${client.util.emoji("ok", message.guild)} ${phrase} "**${searchString.length > 200 ? searchString.substr(0, 200) : searchString}**".\n\`1.\` ** ${links[0]} **` 17 | + `\n${links.slice(1, 3).map((l, t) => `\`${t+2}.\` <${l}>`).join("\n").trim()}`) 18 | } else { 19 | message.zreply(`${client.util.emoji("nope", message.guild)} I wasn't able to find any results under that search.`) 20 | } 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /commands/general/upvote.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "upvote", description: "Shows upvote information and perks for zBot.", usage: "upvote", throttle: {usages: 4, duration: 10}} 2 | exports.run = (client, message) => { 3 | const Discord = require("discord.js") 4 | const embed = new Discord.MessageEmbed() 5 | embed.setAuthor(`Upvoting » ${message.author.tag}`, message.author.displayAvatarURL()) 6 | embed.setDescription("**[Click here](https://discordbots.org/bot/z/vote)** to `vote for zBot on Discord Bot List!`\n\n**Upvoting** allows you to receive rewards such as;\n" 7 | + "─ Access to NSFW commands\n─ A profile badge in `profile` and `stats`\n─ 2 extra potions when fighting people in challenges!\n\nUpvoting also helps support the development of zBot \nand allows the bot to grow, so vote today!") 8 | embed.setColor("#66bb6a") 9 | return message.channel.zend({ embed }) 10 | } 11 | -------------------------------------------------------------------------------- /commands/management/prefix.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "prefix", description: "Allows you to see the current prefix for this guild and set a new one.", usage: "prefix [new prefix]", 2 | throttle: {usages: 3, duration: 10}, permission: {command: "admin", user: "MANAGE_GUILD"}, info: "Only people with `MANAGE_GUILD` or the server owner can change the prefix."} 3 | exports.run = async (client, message, args) => { 4 | const prefix = await message.guild.prefix 5 | const Settings = require("./../../structures/general/Settings.js") 6 | const isMod = await message.member.permission("MANAGE_GUILD") 7 | args = args.slice(0).join(" ") 8 | 9 | if (!args) return message.zreply(`${client.util.emoji("ok", message.guild)} The prefix for this guild is currently set as \`${prefix.replace(/`/g, "`").replace(/\n/g)}\`.\nTo change the prefix, type \`${prefix}${this.settings.command} [new prefix]\``) 10 | if (isMod > 2 || isMod) { 11 | if (args.includes("\"")) { 12 | args = args.split("\"")[1].split("\"")[0] 13 | } else { 14 | args = args.split(" ")[0] 15 | } 16 | await Settings.editSetting(message.guild, "prefix", args) 17 | return message.zreply(`${client.util.emoji("ok", message.guild)} Great success! The new prefix for this guild is \`${args.replace(/`/g, "`").replace(/\n/g)}\`.`) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /commands/misc/cat.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "cat", description: "Displays a random image of a cat.", usage: "cat", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const {get} = require("snekfetch") 4 | const base = "http://78.media.tumblr.com" 5 | get("http://thecatapi.com/api/images/get").then(response => { 6 | message.channel.zend(`${client.util.emoji("ok", message.guild)} :cat: **|** *Here is your random cat!*\n${base}${response.request.path}`) 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /commands/misc/dog.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "dog", description: "Displays a random image of a dog.", usage: "dog", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const {get} = require("snekfetch") 4 | get("https://random.dog/woof.json").then(res => { 5 | message.channel.zend(`${client.util.emoji("ok", message.guild)} :dog: **|** *Here is your random dog!*\n${res.body.url}`) 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /commands/misc/flip.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "flip", description: "Flips a coin.", usage: "flip", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | let coin = "" 4 | switch (Math.floor(Math.random() * 1000) % 2) { 5 | case 0: 6 | coin = "heads" 7 | break 8 | case 1: 9 | coin = "tails" 10 | break 11 | } 12 | const randomPhrase = [`Looks like it's **${coin}** this time.`, `It's **${coin}**.`, `Alright, it's landed on **${coin}**.`, `Oh! Would you look at that? The coin's landed on **${coin}**!`] 13 | message.zreply(`:white_circle: **COIN FLIP**: ${randomPhrase[Math.floor(Math.random() * randomPhrase.length)]}`) 14 | } 15 | -------------------------------------------------------------------------------- /commands/misc/hug.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "hug", description: "Give a hug to somebody!", usage: "hug [string]", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message, args) => { 3 | const Badges = new(require("./../../structures/user/Badges.js"))(message.member) 4 | const hug = args.slice(0).join(" ") 5 | const responses = ["Isn't that just lovely?", "That's so cute!", "Awh! <3", "Love is in the air!"] 6 | 7 | if (hug.includes("jar of dirt")) return message.channel.zend(`${client.util.emoji("nope", message.guild)} Are you silly? Don't hug a jar of dirt!`) 8 | 9 | if (!hug) { 10 | switch (Math.floor(Math.random() * 1000) % 4) { 11 | case 0: 12 | message.channel.zend(`${client.util.emoji("nope", message.guild)} What is love? Clearly you don't know what it is.`) 13 | break 14 | case 1: 15 | message.channel.zend(`${client.util.emoji("nope", message.guild)} Can't find anybody to hug? Here, I'll give you one.`) 16 | message.channel.zend(`:hugging: **HUG**: ${client.user} hugged ${message.author}. ${responses[Math.floor(Math.random()*responses.length)]}`) 17 | break 18 | case 2: 19 | message.channel.zend(`${client.util.emoji("nope", message.guild)} Come on. Surely someone out there loves you.`) 20 | break 21 | case 3: 22 | message.channel.zend(`${client.util.emoji("nope", message.guild)} You can't hug nothing.`) 23 | break 24 | } 25 | return 26 | } 27 | 28 | message.channel.zend(`:hugging: ${message.author.toString()} hugged ${hug}. ${responses[Math.floor(Math.random()*responses.length)]}`) 29 | // Friendship - badge progress 30 | Badges.incrementProgress("friendship") 31 | } 32 | -------------------------------------------------------------------------------- /commands/misc/mods.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "mods", description: "Shows all of the available moderators.", usage: "mods [online | offline | idle | dnd] [page]", throttle: {usages: 3, duration: 10}} 2 | exports.run = async (client, message, args) => { 3 | const Settings = require("./../../structures/general/Settings.js") 4 | const possibleStatuses = ["offline", "online", "idle", "dnd"] 5 | const moderatorRole = await Settings.getValue(message.guild, "moderatorRole") 6 | const { Interface } = require("./../../structures/internal/Interface.js") 7 | const tosend = [] 8 | let onlineCount = 0 9 | const moderators = [] 10 | const status = args[0] 11 | let page = args[1] 12 | if (page === undefined && status !== undefined) { 13 | page = status 14 | } 15 | 16 | if (!message.guild.roles.has(moderatorRole)) return message.zreply(`${client.util.emoji("nope", message.guild)} The owner of this guild hasn't set up a moderator role in the config.`) 17 | const moderatorAmount = message.guild.roles.get(moderatorRole).members.array().length 18 | if (moderatorAmount < 1) return message.zreply(`${client.util.emoji("nope", message.guild)} There are no users with the moderator role.`) 19 | 20 | message.guild.members.forEach(member => { 21 | if (member.roles.highest.comparePositionTo(moderatorRole) >= 0 && !member.user.bot) { 22 | moderators.push(member) 23 | } 24 | }) 25 | 26 | for (const i in moderators) { 27 | const member = moderators[i] 28 | if (member.presence.status == "online") onlineCount++ 29 | if (possibleStatuses.indexOf(status) > -1) { 30 | if (member.presence.status == status) { 31 | tosend.push(`${client.util.emoji(member.presence.status, message.guild)} \`${member.displayName}\``) 32 | } 33 | } else { 34 | tosend.push(`${client.util.emoji(member.presence.status, message.guild)} \`${member.displayName}\``) 35 | } 36 | } 37 | 38 | if (tosend.length < 1) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find any moderators under that search.`) 39 | const mode = (possibleStatuses.indexOf(status) > -1 ? (status == "dnd" ? "in do not disturb mode" : status) : undefined) 40 | new Interface().PaginationMenu(tosend, {title: `Moderators » ${message.author.tag}`, author: message.author.displayAvatarURL(), type: "Moderators", color: message.guild.roles.get(moderatorRole).hexColor, description: `${client.util.emoji("ok", message.guild)} Here is a list of all of the moderators${!mode ? ".\n" : ` **that are ${mode}**;\n` 41 | }${client.util.emoji("online", message.guild)} \`${onlineCount}\` out of \`${moderatorAmount}\` moderators are online`, pageLength: 8, delimiter: "\n"}, message, page) 42 | } 43 | -------------------------------------------------------------------------------- /commands/misc/pfp.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "pfp", description: "Displays yours or another users profile picture.", usage: "pfp [user]", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message, args) => { 3 | const Discord = require("discord.js") 4 | const Find = require("./../../structures/internal/Find.js") 5 | 6 | let user = args.slice(0).join(" ") 7 | 8 | if (user == "" || user == undefined) { 9 | user = message.author 10 | } else { 11 | user = Find.member(user, message.guild)[0] 12 | if (user == undefined) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a user under that query.`) 13 | } 14 | 15 | message.guild.members.fetch(user.id).then(function (member) { 16 | const embed = new Discord.MessageEmbed() 17 | embed.setAuthor(`Profile Picture » ${member.user.tag}`, (member.user.displayAvatarURL().toString().endsWith(".gif") ? `${member.user.displayAvatarURL({size: 2048})}&.gif` : member.user.displayAvatarURL({size: 2048, format: "png"}))) 18 | embed.setColor(member.roles.highest.hexColor) 19 | embed.setImage((member.user.displayAvatarURL().toString().endsWith(".gif") ? `${member.user.displayAvatarURL({size: 2048})}&.gif` : member.user.displayAvatarURL({size: 2048, format: "png"}))) 20 | message.channel.zend({ embed }) 21 | return 22 | }).catch(err => { 23 | throw err 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /commands/misc/reverse.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "reverse", description: "Reverses a string.", usage: "reverse (string)", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message, args) => { 3 | const original = args.slice(0).join(" ") 4 | const reverse = original.split("").reverse().join("") 5 | let msg = "" 6 | 7 | function cleanString(string) { 8 | return string 9 | .replace(/@(everyone|here)/g, "@\u200b$1") 10 | .replace(/<@!?[0-9]+>/g, input => { 11 | const id = input.replace(/<|!|>|@/g, "") 12 | if (message.channel.type === "dm" || message.channel.type === "group") { 13 | return client.users.has(id) ? `@${client.users.get(id).username}` : input 14 | } 15 | 16 | const member = message.channel.guild.members.get(id) 17 | if (member) { 18 | if (member.nickname) return `@${member.nickname}` 19 | return `@${member.user.username}` 20 | } else { 21 | const user = client.users.get(id) 22 | if (user) return `@${user.username}` 23 | return input 24 | } 25 | }) 26 | .replace(/<#[0-9]+>/g, input => { 27 | const channel = client.channels.get(input.replace(/<|#|>/g, "")) 28 | if (channel) return `#${channel.name}` 29 | return input 30 | }) 31 | .replace(/<@&[0-9]+>/g, input => { 32 | if (message.channel.type === "dm" || message.channel.type === "group") return input 33 | const role = message.guild.roles.get(input.replace(/<|@|>|&/g, "")) 34 | if (role) return `@${role.name}` 35 | return input 36 | }) 37 | } 38 | 39 | if (reverse === original) msg = `\n${client.util.emoji("info", message.guild)} **FUN FACT**: Your phrase is a palindrome, meaning it's spelt the same way forward as it is backwards!` 40 | message.zreply(`${client.util.emoji("edit", message.guild)} **REVERSE**: ${cleanString(reverse)}${msg}`) 41 | } 42 | -------------------------------------------------------------------------------- /commands/misc/xkcd.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "xkcd", description: "Select an xkcd comic to view, or see the latest one.", usage: "xkcd [number]", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message, args) => { 3 | const snekfetch = require("snekfetch") 4 | const Discord = require("discord.js") 5 | const moment = require("moment") 6 | args = args.slice(0).join(" ") 7 | 8 | snekfetch.get(args ? `http://xkcd.com/${args}/info.0.json` : "http://xkcd.com/info.0.json") 9 | .set("Accept", "application/json") 10 | .then(res => { 11 | res = JSON.parse(res.text) 12 | const embed = new Discord.MessageEmbed() 13 | embed.setAuthor(`xkcd » ${res.title}`, "https://pbs.twimg.com/profile_images/602808103281692673/8lIim6cB_400x400.png") 14 | embed.setColor("#64b5f6") 15 | embed.setDescription(`${client.util.emoji("ok", message.guild)} Here is xkcd comic \`#${res.num}\`.\n\n${res.alt}`) 16 | embed.setImage(res.img) 17 | embed.setFooter(`Comic created on the ${moment(`${res.day}-${res.month}-${res.year}`, "DD-MM-YYYY").format("Do [of] MMMM, YYYY")}`) 18 | message.channel.zend({ embed }) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /commands/moderator/addrole.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "addrole", description: "Add a role to a specified memmber in this guild.", usage: "addrole (user) (role)", aliases: ["ar", "arole", "addr"], throttle: {usages: 3, duration: 10}, 2 | permission: {command: "mod", user: "MANAGE_ROLES"} } 3 | exports.run = async (client, message, args) => { 4 | const Find = require("./../../structures/internal/Find.js") 5 | const role = Find.role(args.slice(1).join(" "), message.guild)[0]; 6 | const member = Find.member(args[0], message.guild)[0]; 7 | const prefix = await message.guild.prefix 8 | 9 | if (!role) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a role under that query.`) 10 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter a user to add a role to. See \`${prefix}help ${this.settings.command}\` for more information.`) 11 | if (!role.calculatedPosition >= message.guild.me.roles.highest.calculatedPosition || !message.guild.me.hasPermission("MANAGE_ROLES")) return message.zreply(`\ 12 | ${client.util.emoji("nope", message.guild)} I couldn't add that role to this user. Make sure that my role is higher than the one you are trying \ 13 | to add to this user.`) 14 | if (member.roles.has(role.id)) return message.zreply(`${client.util.emoji("nope", message.guild)} \`${member.user.tag}\` already has the \`${role.name}\` role.`) 15 | member.roles.add(role, `[ ${message.author.tag} ] Using \`addrole\``).catch(c => { 16 | throw c; 17 | }).then(t => { 18 | message.zreply(`${client.util.emoji("ok", message.guild)} \`${member.user.tag}\` now has the \`${role.name}\` role.`) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /commands/moderator/find.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "find", description: "Find a user by providing a search query.", usage: "find (user) [page]", permission: {command: "mod", user: "MANAGE_MESSAGES"} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} The syntax of the command is incorrect. For more information, type \`${await message.guild.prefix}help ${this.settings.command}\`.`) 5 | const hasPageNum = /^\d+$/.test(args[args.length-1]) 6 | let search = args.join(" ") 7 | if (hasPageNum) search = args.slice(0, -1).join(" ") 8 | const grabUsers = Find.user(search) 9 | const users = [] 10 | const pageno = hasPageNum ? args[args.length-1] : 1 11 | 12 | for (const i in grabUsers) { 13 | users.push(`**${grabUsers[i].tag}** (${grabUsers[i].id}) ${grabUsers[i].bot ? client.util.emoji("bot", message.guild) : ""}`) 14 | } 15 | 16 | new (require("./../../structures/internal/Interface.js")).Interface().PaginationMenu(users, {title: `Find » ${message.author.tag}`, author: message.author.displayAvatarURL(), type: `Members found under "${search}"`, color: "66bb6a", description: `${client.util.emoji("ok", message.guild)} I found ${users.length 17 | } members under the query \`${search}\`.`, pageLength: 10, delimiter: "\n"}, message, pageno) 18 | } 19 | -------------------------------------------------------------------------------- /commands/moderator/hackban.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "hackban", description: "Bans a user who isn't present in this guild.", usage: "hackban (user id)", throttle: {usages: 3, duration: 10}, 2 | permission: {command: "mod", user: "BAN_MEMBERS"} } 3 | exports.run = async (client, message, args) => { 4 | const Find = require("./../../structures/internal/Find.js") 5 | const id = args.slice(0).join(" ") 6 | const user = await client.users.fetch(id); 7 | const prefix = await message.guild.prefix 8 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter the ID of a user to hackban. See \`${prefix}help hackban\` for more information.`) 9 | if (!message.guild.member(client.user).hasPermission("BAN_MEMBERS")) return message.zreply(`${client.util.emoji("nope", message.guild)} I don't have permission to hackban this person. Make sure I have the \`BAN_MEMBERS\` permission and try again.`) 10 | const banList = await message.guild.fetchBans() 11 | if (!banList) throw "Ban list not retrievable" 12 | const banned = banList.has(user.id) 13 | if (banned) return message.zreply(`${client.util.emoji("nope", message.guild)} That user is already banned!`) 14 | message.guild.members.ban(user, { days: 7, reason: `Hackbanned by ${message.author.tag}`}).catch(c => { 15 | throw c; 16 | }).then(t => { 17 | message.zreply(`${client.util.emoji("ok", message.guild)} \`${user.tag}\` has been \`banned\` from this server.`) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /commands/moderator/lock.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "lock", description: "Lockdown a specific channel in this server.", usage: "kick (user) [reason]", throttle: {usages: 2, duration: 10}, 2 | permission: {command: "mod", user: "KICK_MEMBERS"} } 3 | exports.run = async (client, message, args) => { 4 | const Find = require("./../../structures/internal/Find.js") 5 | const defaultRole = Find.role(message.guild.id, message.guild)[0]; 6 | const channel = Find.channel(args.slice(0).join(' ') || message.channel.id, message.guild)[0]; 7 | const channelType = (channel.type === "text" ? "SEND_MESSAGES" : (channel.type === "category" ? "SEND_MESSAGES" : "CONNECT")); 8 | if (message.channel.permissionsFor(message.guild.me).has("SEND_MESSAGES") === false) return true; 9 | 10 | const permOverwrite = channel.permissionOverwrites.get(defaultRole.id); 11 | const locked = permOverwrite ? permOverwrite.denied.has(channelType) : false; 12 | client.util.log(defaultRole.name); 13 | client.util.log(channel.name) 14 | channel.overwritePermissions(defaultRole, { [channelType]: locked }, locked ? `[ ${message.author.tag} ] Channel unlocked.` : `[ ${message.author.tag} ] Channel locked.`) 15 | .then(() => !locked) 16 | .catch(() => message.zreply(`${client.util.emoji("nope", message.guild)} I don't have permission to change channel overrides on ${channel == message.channel ? "this" : "that"} channel.`)); 17 | console.log(locked) 18 | return message.channel.zend(`${client.util.emoji("ok", message.guild)} \`@everyone\` ${locked ? "can now" : "no longer has access to"}` + 19 | ` ${channelType == "CONNECT" ? "connect to " : "chat in"} \`${channel.name}\`.`) 20 | } 21 | -------------------------------------------------------------------------------- /commands/moderator/nick.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "nick", description: "Change the nickname of a specified member.", usage: "nick (user) (new nickname | clear)", throttle: {usages: 3, duration: 10}, 2 | permission: {command: "mod", user: "MANAGE_NICKNAMES"} } 3 | exports.run = async (client, message, args) => { 4 | const prefix = await message.guild.prefix 5 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter the name of the member you want to unmute. Type \`${prefix}help ${this.settings.command}\` for more information.`) 6 | const Find = require("./../../structures/internal/Find.js") 7 | const member = Find.member(args[0], message.guild)[0] 8 | let nickname = args.slice(1).join(" ") 9 | 10 | if (!member) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a user under that search.`) 11 | if (nickname.length > 32) return message.zreply(`${client.util.emoji("nope", message.guild)} The nickname you provided is more than 32 characters.`) 12 | if (nickname.length < 2) return message.zreply(`${client.util.emoji("nope", message.guild)} The nickname you provided is less than 2 characters.`) 13 | if (nickname === "clear") nickname = "" 14 | member.setNickname(nickname).then(success => { 15 | if (!success.nickname) return message.zreply(`${client.util.emoji("ok", message.guild)} \`${member.user.tag}\`'s nickname has been cleared.`) 16 | return message.zreply(`${client.util.emoji("ok", message.guild)} \`${member.user.tag}\`'s nickname is now \`${success.nickname}\`.`) 17 | }).catch(() => { 18 | return message.zreply(`${client.util.emoji("nope", message.guild)} I don't have permission to change this user's nickname.`) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /commands/moderator/reason.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "reason", description: "When you perform a punishment on someone, a case number is assigned to that punishment. Using this command, you can edit a punishment case's reason after you've created it.", 2 | usage: "reason (case number) (reason)", throttle: {usages: 3, duration: 10}, aliases: ["r", "re", "rsn"], permission: {command: "mod", user: "MANAGE_MESSAGES"}} 3 | exports.run = async (client, message, args) => { 4 | const Discord = require("discord.js") 5 | const Settings = require("./../../structures/general/Settings.js") 6 | const Punish = require("./../../structures/moderation/Punish.js") 7 | 8 | const prefix = await Settings.getValue(message.guild, "prefix") 9 | const CASE = args[0]; 10 | const reason = args.slice(1).join(' ') 11 | 12 | if (!args) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter a punishment case to modify. See \`${prefix}help ${this.settings.command}\` for more information.`) 13 | if (!reason) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter a reason for this case. See \`${prefix}help ${this.settings.command}\` for more information.`) 14 | 15 | let caseObj; 16 | 17 | Punish.editPunish(CASE, message.guild, message.author, (reason.length > 1024 ? `${reason.substr(0, 1019)}...` : reason)) 18 | message.zreply(`${client.util.emoji("ok", message.guild)} ${caseObj ? `Cases \`${caseObj[0]}\` to \`${caseObj[caseObj.length-1]}\` were` : `Case \`${CASE}\` was`} successfully edited.`) 19 | } 20 | -------------------------------------------------------------------------------- /commands/moderator/removerole.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "removerole", description: "Remove a role from a specified memmber in this guild.", usage: "removerole (user) (role)", 2 | aliases: ["rr", "rmrole", "rrole", "remover"], throttle: {usages: 3, duration: 10}, permission: {command: "mod", user: "MANAGE_ROLES"} } 3 | exports.run = async (client, message, args) => { 4 | const Find = require("./../../structures/internal/Find.js") 5 | const role = Find.role(args.slice(1).join(" "), message.guild)[0]; 6 | const member = Find.member(args[0], message.guild)[0]; 7 | const prefix = await message.guild.prefix 8 | 9 | if (!role) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a role under that query.`) 10 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter a user to remove a role from. See \`${prefix}help ${this.settings.command}\` for more information.`) 11 | if (!role.calculatedPosition >= message.guild.me.roles.highest.calculatedPosition || !message.guild.me.hasPermission("MANAGE_ROLES")) return message.zreply(`\ 12 | ${client.util.emoji("nope", message.guild)} I couldn't remove that role from that user. Make sure that my role is higher than the one you are trying \ 13 | to remove from this user.`) 14 | if (!member.roles.has(role.id)) return message.zreply(`${client.util.emoji("nope", message.guild)} \`${member.user.tag}\` doesn't have the \`${role.name}\` role.`) 15 | member.roles.remove(role, `[ ${message.author.tag} ] Using \`removerole\``).catch(c => { 16 | throw c; 17 | }).then(t => { 18 | message.zreply(`${client.util.emoji("ok", message.guild)} \`${member.user.tag}\` no longer has the \`${role.name}\` role.`) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /commands/moderator/renamechannel.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "renamechannel", description: "Renames a channel, text or voice.", usage: "renamechannel (voice channel)", 2 | throttle: {usages: 3, duration: 10}, permission: {command: "mod", user: "MANAGE_CHANNELS"}, info: "`voice channel` parameter can be a channel mention, id or name."} 3 | exports.run = async (client, message, args) => { 4 | const Find = require("./../../structures/internal/Find.js") 5 | const name = args.slice(1).join(" ") 6 | const prefix = await message.guild.prefix 7 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter a channel to rename. See \`${prefix}help renamechannel\` for more information.`) 8 | const channel = Find.channel(args[0], message.guild)[0] 9 | if (!channel) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a channel under that search.`) 10 | 11 | if (!message.guild.member(client.user).hasPermission("MANAGE_CHANNELS")) return message.zreply(`${client.util.emoji("nope", message.guild)} I don't have permission to rename this channel. Make sure I have the \`MANAGE_CHANNELS\` permission and try again.`) 12 | channel.setName(name) 13 | .then(res => { 14 | message.channel.zend(`${client.util.emoji("ok", message.guild)} \`${channel.name}\` was succesfully renamed to \`${res.name}\`.`) 15 | }) 16 | .catch(err => { 17 | client.util.log(`Error at renamechannel whilst renaming a channel; ${err}`, "warn") 18 | message.zreply(`${client.util.emoji("nope", message.guild)} An error occured whilst renaming the channel. Check that I have sufficient permissions to modify channels, and try again.`) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /commands/moderator/unban.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "unban", description: "Unban a specified user from this server.", usage: "unban (user)", throttle: {usages: 3, duration: 10}, permission: {command: "mod", user: "BAN_MEMBERS"} } 2 | exports.run = async (client, message, args) => { 3 | const Find = require("./../../structures/internal/Find.js") 4 | const user = Find.user(args.slice(0).join(" ")) 5 | const prefix = await message.guild.prefix 6 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter the ID of a user to unban. See \`${prefix}help unban\` for more information.`) 7 | if (!message.guild.member(client.user).hasPermission("BAN_MEMBERS")) return message.zreply(`${client.util.emoji("nope", message.guild)} I don't have permission to unban this person. Make sure I have the \`BAN_MEMBERS\` permission and try again.`) 8 | const banList = await message.guild.fetchBans() 9 | if (!banList) return message.zreply(`${client.util.emoji("nope", message.guild)} I need the \`BAN_MEMBERS\` permission in order to see the ban list.`) 10 | const banned = banList.has(user.id) 11 | if (!banned) return message.zreply(`${client.util.emoji("nope", message.guild)} That user isn't banned!`) 12 | message.guild.unban(user, `Unbanned by ${message.author.tag} by using ${prefix}unban`) 13 | message.zreply(`${client.util.emoji("ok", message.guild)} \`${user.username}\` (${user.id}) has been **unbanned**.`) 14 | } 15 | -------------------------------------------------------------------------------- /commands/moderator/unhoist.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "unhoist", description: "Removes any special characters from everyone in the server.", usage: "unhoist", 2 | throttle: {usages: 3, duration: 10}, permission: {command: "mod", user: "MANAGE_NICKNAMES"}} 3 | exports.run = async (client, message, args) => { 4 | const prefix = await message.guild.prefix 5 | const Discord = require("discord.js") 6 | const Punish = require("./../../structures/moderation/Punish.js") 7 | const Find = require("./../../structures/internal/Find.js") 8 | const Settings = require("./../../structures/general/Settings.js") 9 | const muteRole = await Settings.getValue(message.guild, "muteRole") 10 | 11 | function sleep(ms) { 12 | return new Promise(resolve => setTimeout(resolve, ms)); 13 | } 14 | 15 | await message.guild.members.fetch(); 16 | const hoisters = message.guild.members.filter(u => { 17 | const username = u.nickname || u.user.username; 18 | return username.toLowerCase().codePointAt(0) < 48; // codepoint of '0' 19 | }).array(); 20 | 21 | if (!hoisters[0]) return message.zreply(`${client.util.emoji("empty", message.guild)} I couldn't find any hoisters.`) 22 | const failed = []; 23 | let done; 24 | const next = Number(1100*hoisters.length); 25 | const hours = Math.floor(next / 3600000); 26 | const minutes = Math.floor((next / 60000) - (hours * 60)); 27 | const seconds = Math.floor((next / 1000) - ((hours * 3600) + (minutes * 60))); 28 | 29 | const timeArr = [{ type: {singular: "hour", plural: "hours"}, amount: hours }, 30 | { type: {singular: "minute", plural: "minutes"}, amount: minutes }, 31 | { type: {singular: "second", plural: "seconds"}, amount: seconds } ]; 32 | let properArr = []; 33 | for (let i in timeArr) { 34 | if (timeArr[i].amount < 1) continue; 35 | properArr.push(`${timeArr[i].amount} ${timeArr[i].amount == 1 ? timeArr[i].type.singular : timeArr[i].type.plural}`) 36 | } 37 | let timeLeft = properArr.slice(0, -2).join(', ') + (properArr.slice(0, -2).length ? ', ' : '') + properArr.slice(-2).join(' and '); 38 | 39 | message.channel.zend(`${client.util.emoji("info", message.guild)} Unhoisting \`${hoisters.length}\` members. \ 40 | Please be patient, this may take some time. I will ping you once I'm finished! 41 | \`ETA ─ ${timeLeft}\``).then(m => { done = m }); 42 | for (const hoister of hoisters) { 43 | hoister.setNickname("Hoister").catch(e => { 44 | console.log(e) 45 | failed.push(hoister.username) 46 | }); 47 | await sleep(1100); 48 | } 49 | 50 | done.edit(`${client.util.emoji("ok", message.guild)} ${message.author} I've finished unhoisting \`${hoisters.length}\` members!`) 51 | if (failed.length > 0) message.channel.zend(`${client.util.emoji("empty", message.guild)} I failed to unhoist \`${failed.length}\` members.`) 52 | } 53 | -------------------------------------------------------------------------------- /commands/moderator/unmute.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "unmute", description: "Unmute a user with the muted role from this server.", usage: "unmute (user)", throttle: {usages: 3, duration: 10}, 2 | permission: {command: "mod", user: "MUTE_MEMBERS"}, info: "The muted role is the role you set as `muteRole` in your configuration."} 3 | exports.run = async (client, message, args) => { 4 | const prefix = await message.guild.prefix 5 | if (!args[0]) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter the name of the member you want to unmute. Type \`${prefix}help ${this.settings.command}\` for more information.`) 6 | const Find = require("./../../structures/internal/Find.js") 7 | const Settings = require("./../../structures/general/Settings.js") 8 | const muteRole = await Settings.getValue(message.guild, "muteRole") 9 | const member = Find.member(args.slice(0).join(" "), message.guild)[0] 10 | 11 | if (!member) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a user under that search.`) 12 | if (!message.guild.roles.has(muteRole) || !muteRole) return message.zreply(`${client.util.emoji("nope", message.guild)} The server owner hasn't set the \`muteRole\` setting to a valid role. You can use \`${prefix}config set muteRole [role name]\` to set the muted role.` ) 13 | if (!member.roles.has(muteRole)) return message.zreply(`${client.util.emoji("nope", message.guild)} This user isn't muted!`) 14 | member.roles.remove(muteRole).catch(() => { 15 | return message.zreply(`${client.util.emoji("nope", message.guild)} I don't have permission to unmute this person.`) 16 | }) 17 | return message.zreply(`${client.util.emoji("ok", message.guild)} \`${member.user.tag}\` (${member.user.id}) has been **unmuted**.`) 18 | } 19 | -------------------------------------------------------------------------------- /commands/moderator/voicekick.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "voicekick", description: "Kicks all members from a specific voice channel by deleting then re-creating the channel.", usage: "voicekick (voice channel)", 2 | throttle: {usages: 3, duration: 10}, permission: {command: "mod", user: "MANAGE_CHANNELS"}, info: "`voice channel` parameter can be a channel mention, id or name."} 3 | exports.run = async (client, message, args) => { 4 | const Find = require("./../../structures/internal/Find.js") 5 | args = args.slice(0).join(" ") 6 | const prefix = await message.guild.prefix 7 | if (!args) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to enter a channel to kick members from. See \`${prefix}help voicekick\` for more information.`) 8 | const channel = Find.channel(args, message.guild)[0] 9 | if (!channel) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a channel under that search.`) 10 | if (channel.type !== "voice") return message.zreply(`${client.util.emoji("nope", message.guild)} That channel is not a voice channel.`) 11 | 12 | if (!message.guild.member(client.user).hasPermission("MANAGE_CHANNELS")) return message.zreply(`${client.util.emoji("nope", message.guild)} I don't have permission to re-create this channel. Make sure I have the \`MANAGE_CHANNELS\` permission and try again.`) 13 | const position = channel.position 14 | channel.delete().catch(() => { 15 | message.zreply(`${client.util.emoji("nope", message.guild)} An error occured whilst deleting the channel. Check that I have sufficient permissions to delete/modify channels, and try again.`) 16 | }) 17 | channel.clone().then(res => { 18 | res.setPosition(position) 19 | message.channel.zend(`${client.util.emoji("ok", message.guild)} All members within \`${channel.name}\` were kicked.`) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /commands/music/nowplaying.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "nowplaying", description: "Shows detailed information on the song that is currently playing.", usage: "nowplaying", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message) => { 3 | function convertTime(milliseconds) { 4 | milliseconds = milliseconds / 1000 5 | const truncSeconds = Math.floor(milliseconds % 60) 6 | const min = Math.floor(milliseconds / 60) 7 | let timeString 8 | if (truncSeconds < 10) { 9 | timeString = `${min}:0${truncSeconds}` 10 | } else { 11 | timeString = `${min}:${truncSeconds}` 12 | } 13 | return timeString 14 | } 15 | 16 | if (!message.guild.music.playing) return message.zreply(`${client.util.emoji("nope", message.guild)} There's nothing that's currently playing.`) 17 | return message.channel.zend(`${client.util.emoji("info", message.guild)} Here's some information on the song that's currently playing;\n` + 18 | `**NOW PLAYING** ─ \`${message.guild.music.currentSong.title}\`\n**REQUESTED BY** ─ \`${message.guild.music.currentSong.requester == undefined ? "???" : message.guild.music.currentSong.requester.tag}\`\n**LENGTH** ─ \`${convertTime(message.guild.music.currentSong.duration)}\``) 19 | } 20 | -------------------------------------------------------------------------------- /commands/music/pause.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "pause", description: "Automatically joins your voice channel and plays a specified song.", usage: "play (query | playlist:[playlist id])", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message) => { 3 | if (!await message.guild.music.DJ(message.member)) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to be a DJ to use this command.`) 4 | if (!message.guild.music.currentSong || !message.guild.music.playing) return message.zreply(`${client.util.emoji("nope", message.guild)} There's no song playing to pause.`) 5 | if (message.guild.music.paused) return message.zreply(`${client.util.emoji("nope", message.guild)} Playback is already paused.`) 6 | message.channel.send(`${client.util.emoji("ok", message.guild)} Audio playback was \`paused\` by **${message.author.username}**.`) 7 | return message.guild.music.pause(); 8 | } 9 | -------------------------------------------------------------------------------- /commands/music/queue.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "queue", description: "Shows the songs that are currently queued in this guild.", usage: "queue [page]", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const page = args[0] || "" 4 | 5 | function convertTime(milliseconds) { 6 | milliseconds = milliseconds / 1000 7 | const truncSeconds = Math.floor(milliseconds % 60) 8 | const min = Math.floor(milliseconds / 60) 9 | let timeString 10 | if (truncSeconds < 10) { 11 | timeString = `${min}:0${truncSeconds}` 12 | } else { 13 | timeString = `${min}:${truncSeconds}` 14 | } 15 | return timeString 16 | } 17 | 18 | if (!message.guild.music.queue || message.guild.music.queue.length < 1) return message.zreply(`${client.util.emoji("empty", message.guild)} There are currently no songs \ 19 | in the queue. You can add something to the queue by using \`${await message.guild.prefix}music (search query | url | playlist:(playlist name/id))\`.`) 20 | const queueArr = [] 21 | message.guild.music.queue.forEach((song, i) => { 22 | queueArr.push(`\`${i+1}.\` **${song.title}**\n*Requested by \`${song.requester.tag}\`*${song.duration > 0 ? ` ─ \`${convertTime(song.duration)}\`` : ""}`) 23 | }) 24 | if (queueArr.length < 1) return message.zreply(`${client.util.emoji("empty", message.guild)} There are currently no songs in the queue. You can add something to the queue by using \`${await message.guild.prefix}music (search query | url | playlist:(playlist name/id))\`.`) 25 | new (require("./../../structures/internal/Interface.js")).Interface().PaginationMenu(queueArr, {title: `Queue » ${message.guild.name}`, author: message.guild.iconURL(), type: "Queued Songs", color: "66bb6a", description: `${client.util.emoji("ok", message.guild)} These are the songs that are currently queued in **${message.guild.name}**.\nYou can use \`${ 26 | await message.guild.prefix}${this.settings.command} [page]\` to see another page of the queue.`, pageLength: 5, delimiter: "\n\n", footer: {text: "zBot Music Player", avatar: "https://cdn1.iconfinder.com/data/icons/google_jfk_icons_by_carlosjj/512/music.png"}}, message, page) 27 | } 28 | -------------------------------------------------------------------------------- /commands/music/remove.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "remove", description: "Remove a song or a group of songs from the queue.", usage: "remove (index or range)", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | const Music = require("./../../structures/general/Music.js") 4 | const number = args[0] || "" 5 | 6 | if (!await message.guild.music.DJ(message.member)) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to be a DJ to use this command.`) 7 | if (!number) return message.zreply(`${client.util.emoji("nope", message.guild)} That's not a valid song index.`) 8 | if (message.guild.music.queue.length < 1) return message.zreply(`${client.util.emoji("nope", message.guild)} There's nothing in the queue to remove.`) 9 | if (number.includes("-")) { 10 | const caseObj = [] 11 | if (isNaN(parseInt(number.split("-")[0])) || isNaN(parseInt(number.split("-")[1]))) return message.zreply(`${client.util.emoji("nope", message.guild)} Your selection contains an invalid number.`) 12 | for (let i=parseInt(number.split("-")[0]); i <= parseInt(number.split("-")[1]); i++) { 13 | caseObj.push(i) 14 | } 15 | if (parseInt(number.split("-")[0]) > message.guild.music.queue.length || parseInt(number.split("-")[1]) > message.guild.music.queue.length) return message.zreply(`${client.util.emoji("nope", message.guild)} You've provided a song index that doesn't exist in the queue.`) 16 | message.guild.music.queue.splice(Number(Number(caseObj[0]) - 1), Number(Number(caseObj[caseObj.length-1]) - 1)) 17 | return message.zreply(`${client.util.emoji("ok", message.guild)} You have removed \`songs ${caseObj[0]} to ${caseObj[caseObj.length-1]}\` from the queue.`) 18 | } else { 19 | if (Number(number) > message.guild.music.queue.length) return message.zreply(`${client.util.emoji("nope", message.guild)} You've provided a song index that doesn't exist in the queue.`) 20 | const song = message.guild.music.queue[Number(number) - 1] 21 | message.guild.music.queue.splice(Number(number) - 1, 1) 22 | return message.zreply(`${client.util.emoji("ok", message.guild)} **${song}** was removed from the queue.`) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /commands/music/repeat.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "repeat", description: "Repeat one song to play indefinitely, or repeat the entire queue.", usage: "repeat [queue]", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message, args) => { 3 | if (!await message.guild.music.DJ(message.member)) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to be a DJ to use this command.`) 4 | if (!message.guild.music.playing) return message.zreply(`${client.util.emoji("nope", message.guild)} There's no song playing to repeat.`) 5 | if (args.join(" ") == "queue") { 6 | if (message.guild.music.repeat.queue) { 7 | message.guild.music.repeat.queue = false 8 | message.channel.send(`${client.util.emoji("ok", message.guild)} Queue repeat is now turned off.`) 9 | } else { 10 | message.channel.send(`${client.util.emoji("ok", message.guild)} The queue is now on repeat. When a song ends, it will be added into the queue again. Type \`${await message.guild.prefix}music repeat queue\` again to toggle off.`) 11 | message.guild.music.repeat.queue = true 12 | } 13 | } else if (message.guild.music.repeat.single) { 14 | message.guild.music.repeat.single = false 15 | message.channel.send(`${client.util.emoji("ok", message.guild)} Repeat is now turned off.`) 16 | } else { 17 | message.channel.send(`${client.util.emoji("ok", message.guild)} **${message.guild.music.currentSong.title}** is now on repeat. Type \`${await message.guild.prefix}repeat\` again to toggle off.`) 18 | message.guild.music.repeat.single = true 19 | } 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /commands/music/resume.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "resume", description: "Automatically joins your voice channel and plays a specified song.", usage: "play (query | playlist:[playlist id])", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message) => { 3 | if (!await message.guild.music.DJ(message.member)) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to be a DJ to use this command.`) 4 | if (!message.guild.music.currentSong || !message.guild.music.playing) return message.zreply(`${client.util.emoji("nope", message.guild)} There's no song playing to resume.`) 5 | if (!message.guild.music.paused) return message.zreply(`${client.util.emoji("nope", message.guild)} The player isn't paused.`) 6 | message.channel.send(`${client.util.emoji("ok", message.guild)} Audio playback was \`resumed\` by **${message.author.username}**.`) 7 | return message.guild.music.resume(); 8 | } 9 | -------------------------------------------------------------------------------- /commands/music/skip.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "skip", description: "Skips the current song if there is one playing.", usage: "skip", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message) => { 3 | const voiceChannel = message.member.voiceChannel 4 | const isMod = await message.member.permission("MANAGE_MESSAGES") 5 | 6 | if (!await message.guild.music.DJ(message.member)) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to be a DJ to use this command.`) 7 | if (!message.guild.music.playing) return message.zreply(`${client.util.emoji("nope", message.guild)} There's nothing to skip.`) 8 | if (!voiceChannel) return message.zreply(`${client.util.emoji("nope", message.guild)} You aren't currently in a voice channel.`) 9 | if (voiceChannel.id !== message.guild.me.voiceChannel.id) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to be in the same voice channel as me to skip songs!`) 10 | const memberCountnb = message.guild.music.voiceChannel.members.filter(a => !a.user.bot).array().length 11 | 12 | const song = message.guild.music.queue[0]; 13 | if (isMod || await message.guild.music.DJ(message.member)) { 14 | message.channel.send(`${client.util.emoji("ok", message.guild)} \`${message.member.displayName}\` skipped \`${message.guild.music.currentSong.title}\`.`) 15 | return message.guild.music.skip(true); 16 | } 17 | 18 | if (song.skips.size < Math.round(memberCountnb / 2)) { 19 | if (song.skips.has(message.author.id)) return message.zreply(`${client.util.emoji("nope", message.guild)} You've already voted to skip!`) 20 | song.skips.add(message.author.id); 21 | return message.channel.send(`${client.util.emoji("ok", message.guild)} \`${message.member.displayName}\` has voted to skip the current song. \ 22 | \`${Math.round((memberCountnb / 2) - song.skips.size)}\` more vote(s) are required in order to skip.`) 23 | } else if (song.skips.size >= Math.round(memberCountnb / 2)) { 24 | message.channel.send(`${client.util.emoji("ok", message.guild)} The skip vote passed, and \`${message.guild.music.currentSong.title}\` was skipped.`) 25 | return message.guild.music.skip(true); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /commands/music/stop.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "stop", description: "Stops playing the current song and leaves your voice channel.", usage: "stop", throttle: {usages: 4, duration: 10}} 2 | exports.run = async (client, message) => { 3 | const isMod = await message.member.permission("MANAGE_MESSAGES") 4 | 5 | if (!await message.guild.music.DJ(message.member)) return message.zreply(`${client.util.emoji("nope", message.guild)} You need to be a DJ to use this command.`) 6 | if (!message.guild.music.playing) return message.zreply(`${client.util.emoji("nope", message.guild)} There's nothing playing to end.`) 7 | const player = await client.lavalink.get(message.guild.id); 8 | const memberCountnb = message.guild.channels.get(player.channel).members.filter(a => !a.user.bot).array().length 9 | if (memberCountnb < 2 || isMod || await message.guild.music.DJ(message.member)) { 10 | message.guild.music.destroy() 11 | return message.channel.send(`${client.util.emoji("info", message.guild)} **MUSIC**: Music has stopped because **${message.author.tag}** has cleared the queue.`) 12 | } else { 13 | return message.zreply(`${client.util.emoji("nope", message.guild)} You can't do that, there are other people in this channel listening to the music!`) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /commands/music/time.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "time", description: "View the amount of time this song has currently been playing for.", usage: "time", throttle: {usages: 4, duration: 10} } 2 | exports.run = async (client, message) => { 3 | function convertTime(milliseconds) { 4 | milliseconds = milliseconds / 1000 5 | const truncSeconds = Math.floor(milliseconds % 60) 6 | const min = Math.floor(milliseconds / 60) 7 | let timeString 8 | if (truncSeconds < 10) { 9 | timeString = `${min}:0${truncSeconds}` 10 | } else { 11 | timeString = `${min}:${truncSeconds}` 12 | } 13 | return timeString 14 | } 15 | 16 | if (!message.guild.music.playing) return message.zreply(`${client.util.emoji("nope", message.guild)} There's no song playing at the moment.`) 17 | if (message.guild.music.currentSong.stream) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't view the time on a livestream.`) 18 | return message.zreply(`${client.util.emoji("ok", message.guild)} __**${message.guild.music.currentSong.title}**__ | \`${convertTime(message.guild.music.player.state.position)} / \ 19 | ${convertTime(message.guild.music.currentSong.duration)}\``) 20 | } 21 | -------------------------------------------------------------------------------- /commands/nsfw/ass.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "ass", description: "Shows a picture of ass from obutts.ru", usage: "ass", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message) => { 3 | const random = Math.floor(Math.random() * (5600 - 1 + 1)) + 1 4 | const assid = random.toString().padStart(5, "0") 5 | const link = `http://media.obutts.ru/butts_preview/${assid}.jpg` 6 | message.channel.zend(`${client.util.emoji("loading", message.guild)} Here's a random picture of some ass! ${link}`) 7 | } 8 | -------------------------------------------------------------------------------- /commands/nsfw/boobs.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "boobs", description: "Shows a picture of boobs from oboobs.ru", usage: "boobs", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message) => { 3 | const random = Math.floor(Math.random() * (12500 - 1 + 1)) + 1 4 | const boobid = random.toString().padStart(5, "0") 5 | const link = `http://media.oboobs.ru/boobs_preview/${boobid}.jpg` 6 | const word = ["boobs", "breasts", "bazookas", "bongos", "bobs"] 7 | message.channel.zend(`${client.util.emoji("loading", message.guild)} Here's some ${word[Math.floor(Math.random()*word.length)]}! ${link}`) 8 | } 9 | -------------------------------------------------------------------------------- /commands/nsfw/lewdneko.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "lewdneko", description: "Shows a picture of a neko from `nekos.life`", usage: "lewdneko", throttle: {usages: 3, duration: 10} } 2 | exports.run = (client, message) => { 3 | const loading = ["Just a moment...", "Lewding it up...", "Getting prepared...", "One second...", "Grabbing image...", "Getting lewd..."] 4 | message.channel.zend(`${client.util.emoji("loading", message.guild)} ${loading[Math.floor(Math.random()*loading.length)]}`).then(mes => { 5 | const snekfetch = require("snekfetch") 6 | snekfetch.get("https://nekos.life/api/v2/img/lewd") 7 | .set("Accept", "application/json") 8 | .then(res => { 9 | mes.edit(`${client.util.emoji("ok", message.guild)} Here's your ~lewd~ neko!\n${JSON.parse(res.text).url}`) 10 | }) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /commands/nsfw/neko.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "neko", description: "Shows a picture of a neko from `nekos.life`", usage: "neko", throttle: {usages: 4, duration: 10} } 2 | exports.run = (client, message) => { 3 | const loading = ["Just a moment...", "Lewding it up...", "Getting prepared...", "One second...", "Grabbing image...", "Getting lewd..."] 4 | message.channel.zend(`${client.util.emoji("loading", message.guild)} ${loading[Math.floor(Math.random()*loading.length)]}`).then(mes => { 5 | const snekfetch = require("snekfetch") 6 | snekfetch.get("https://nekos.life/api/v2/img/neko") 7 | .set("Accept", "application/json") 8 | .then(res => { 9 | mes.edit(`${client.util.emoji("ok", message.guild)} Here's your neko!\n${JSON.parse(res.text).url}`) 10 | }) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /commands/social/badges.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "badges", description: "View all the badges that are obtainable in zBot as well as the badges you own.", 2 | usage: ["badges [user]", "badges [info] (badge id)"], throttle: {usages: 3, duration: 10} } 3 | exports.run = async (client, message, args) => { 4 | const Discord = require("discord.js") 5 | const Find = require ("./../../structures/internal/Find.js") 6 | const BadgeStructure = new(require("./../../structures/user/Badges.js"))(message.member) 7 | 8 | if (args[0] == "info") { 9 | const badgetext = args[1] 10 | const badge = BadgeStructure.get(badgetext) 11 | if (!badge) return message.zreply(`${client.util.emoji("nope", message.guild)} That is not a valid badge nI.`) 12 | const embed = new Discord.MessageEmbed() 13 | embed.setAuthor(`Badges » ${message.author.tag}`, message.author.avatarURL()) 14 | embed.setDescription(`${client.util.emoji("info", message.guild)} Information about \`${badge.name}\``) 15 | embed.setColor("#ffb300") 16 | embed.addField("Description", badge.description) 17 | if (badge.levels) { 18 | let msg = "" 19 | for (const key in badge.levels) { 20 | msg += `\`Level ${key}\` ─ \`${badge.levels[key]}\`\n` 21 | } 22 | const level = await BadgeStructure.getLevel(badge.id) 23 | embed.addField("Levels", `${msg}${level ? `\n*You are currently at level \`${level}\` on this badge.*` : ""}`) 24 | } 25 | return message.channel.zend({ embed }) 26 | } 27 | 28 | let search = args.join(" ") 29 | if (!search) search = message.author.id 30 | const member = (search == message.author.id ? message.member : Find.member(args.join(" "), message.guild)[0]) 31 | if (!member) return message.zreply(`${client.util.emoji("nope", message.guild)} I couldn't find a member under that query.`) 32 | const page = (member.id == message.author.id ? args[0] : args[1]) 33 | 34 | const tosend = [] 35 | const badges = BadgeStructure.all 36 | for (const i in badges) { 37 | const value = badges[i] 38 | let msg = ""; 39 | msg += client.util.emoji(await BadgeStructure.has(value.id) ? "ok" : "nope", message.guild) 40 | msg += ` \`${value.name}\` ─ *ID* \`${value.id}\`` 41 | tosend.push(msg) 42 | } 43 | if (tosend.length < 1) return message.zreply(`${client.util.emoji("incomplete", message.guild)} You don't own any badges.`) 44 | new (require("./../../structures/internal/Interface.js")).Interface().PaginationMenu(tosend, {title: `Badges » ${member.user.tag}`, author: member.user.displayAvatarURL(), type: "Badges", color: "66bb6a", description: `${client.util.emoji("ok", message.guild)} Here is a list of all the collectable badges within zBot.\n` + 45 | `You can use \`${await message.guild.prefix}badges list [page number] to see the next page.\``, pageLength: 10, delimiter: "\n"}, message, page) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /commands/social/equip.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "equip", description: "Allows you to equip specific items to your profile.", usage: ["equip (item name/id) [slot]", "equip clear", "equip (list (badges | backgrounds))"], 2 | throttle: {usages: 3, duration: 10}, example: "equip **background** **polymountains**", info: "Parameter **slot** can be \"background\", \"1-6\" or \"weapon\"."} 3 | exports.run = async (client, message, args) => { 4 | const BadgeStructure = new(require("./../../structures/user/Badges.js"))(message.member) 5 | const Inventory = new(require("./../../structures/user/Inventory.js"))(message.member) 6 | const profile = (await message.member.profile) 7 | const prefix = await message.guild.prefix 8 | const Shop = require("./../../models/general/Shop.js") 9 | const Sequelize = require('sequelize') 10 | const Op = Sequelize.Op 11 | const item = await Shop.findOne({ where: { id: { [Op.like]: args[0] } } }); 12 | const slot = args[1] || null; 13 | 14 | if (!item) return message.zreply(`${client.util.emoji("nope", message.guild)} That item doesn't exist. \ 15 | You can use \`${prefix}items\` to see what items you own.`) 16 | 17 | if (await Inventory.has(item)) { 18 | switch (item.type) { 19 | case "background": 20 | await message.member.update("background", item.id); 21 | break; 22 | 23 | case "weapon": 24 | await message.member.update("weapon", item.id); 25 | break; 26 | 27 | case "badge": 28 | const profile = (await message.member.profile) 29 | const inventory = profile.get("inventory"); 30 | let slots = inventory.slots; 31 | console.log(slots) 32 | const index = slots.findIndex(e => !e) > 6 || slots.findIndex(e => !e) < 0 33 | ? 0 34 | : slots.findIndex(e => !e); 35 | slots[index] = item.id 36 | await message.member.update("slots", Object.assign([null, null, null, null, null, null], slots)); 37 | break; 38 | 39 | default: 40 | throw "Unknown item type" 41 | } 42 | return message.zreply(`${client.util.emoji("ok", message.guild)} You have successfully equipped \ 43 | \`${item.name}\`!`) 44 | } else { 45 | return message.zreply(`${client.util.emoji("nope", message.guild)} You don't own this item. \ 46 | You can use \`${prefix}items\` to see what items you own.`) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /commands/social/items.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "items", description: "View all of the items that you own on this guild.", usage: ["items [page]", "items [category] [page]"], 2 | throttle: {usages: 3, duration: 10} } 3 | exports.run = async (client, message, args) => { 4 | const Inventory = new(require("./../../structures/user/Inventory.js"))(message.member) 5 | const { Interface } = require("./../../structures/internal/Interface.js") 6 | const Constants = require("./../../util/Constants.js") 7 | if (!args[0]) args[0] = 1 8 | const userItems = await Inventory.all; 9 | const items = []; 10 | console.log(userItems) 11 | for (let i of userItems) { 12 | items.push(`**${i.name}** ─ *ID* \`${i.id}\` ─ ${i.type.charAt(0).toUpperCase()}${i.type.slice(1)}\n`) 13 | } 14 | 15 | if (items < 1) return message.zreply(`${client.util.emoji("empty", message.guild)} You have no items to show on this guild. Try \`${await message.guild.prefix}shop\` \ 16 | to see the different items you can buy!`) 17 | new Interface().PaginationMenu(items, {title: `Items » ${message.author.tag}`, author: message.author.displayAvatarURL(), type: "Owned Items", color: "ffb74d", 18 | description: `${client.util.emoji("ok", message.guild)} Here is a list of all the items that you own.`, pageLength: 7, delimiter: "\n"}, message, args[0]) 19 | } 20 | -------------------------------------------------------------------------------- /commands/social/leaderboard.js: -------------------------------------------------------------------------------- 1 | exports.settings = {command: "leaderboard", description: "Displays a leaderboard of the users with the highest ranking experience.", usage: ["leaderboard (list) [page]", "leaderboard (top) [number 1-10]"], 2 | throttle: {usages: 3, duration: 10} } 3 | exports.run = async (client, message, args) => { 4 | const User = require("./../../models/user/User.js") 5 | const Settings = require("./../../structures/general/Settings.js") 6 | const Experience = require("./../../structures/user/Experience.js") 7 | const { Interface } = require("./../../structures/internal/Interface.js") 8 | const subcommand = args[0] || "top" 9 | let limit = (args[1] == undefined ? "" : args[1]) 10 | const memberCountnb = message.guild.members.filter(a => !a.user.bot).array().length 11 | 12 | if (subcommand == "top") { 13 | if (!limit) limit = 5 14 | if (memberCountnb < 6) limit = memberCountnb 15 | if (limit > 10) return message.zreply(`${client.util.emoji("nope", message.guild)} You can't display more than 10 people at a time.`) 16 | 17 | const userProfile = await User.findAll({ where: { guildID: message.guild.id }, attributes: ["userID", "experience"], order: [["experience", "DESC"]], limit: Number(limit) }) 18 | const leaderboard = userProfile.sort((a, b) => b.experience - a.experience) 19 | .filter(user => client.users.has(user.userID)) 20 | .map((user, position) => `\`${position + 1}\`. **${(client.users.get(user.userID).tag)}** ─ ${user.experience} experience\nLevel ${Experience.getLevelFromExp(user.experience)} ─ ` + 21 | `*${Experience.getLevelBounds(Experience.getLevelFromExp(user.experience)).upperBound - user.experience} exp. until next level*\n`) 22 | 23 | new Interface().PaginationMenu(leaderboard, {title: `Top Experience » ${message.guild.name}`, author: message.guild.iconURL(), type: "Top Experience", color: "ffb74d", description: `${client.util.emoji("ok", message.guild)} Here is ` + 24 | "the users with the most experience in this guild.", pageLength: limit, delimiter: "\n"}, message, limit) 25 | } else if (subcommand == "list") { 26 | const userProfile = await User.findAll({ where: { guildID: message.guild.id }, attributes: ["userID", "experience"], order: [["experience", "DESC"]] }) 27 | const leaderboard = userProfile.sort((a, b) => b.experience - a.experience) 28 | .filter(user => client.users.has(user.userID)) 29 | .map((user, position) => `\`${position + 1}\`. **${(client.users.get(user.userID).tag)}** ─ ${user.experience} experience\n`) 30 | new Interface().PaginationMenu(leaderboard, {title: `Leaderboard » ${message.guild.name}`, author: message.guild.iconURL(), type: "Experience Leaderboard", color: "ffd54f", description: `${client.util.emoji("ok", message.guild)} Here is ` + 31 | `the experience leaderboard for this guild.\nTo see the next page, type \`${await message.guild.prefix}leaderboard list [page number]\`.`, pageLength: 10, delimiter: "\n"}, message, limit) 32 | } else { 33 | return message.zreply(`${client.util.emoji("nope", message.guild)} The syntax of the command is incorrect. For more information, type \`${await Settings.getValue(message.guild, "prefix")}help ${this.settings.command}\`.`) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /data/main/errorHandle/errorFooter.txt: -------------------------------------------------------------------------------- 1 | Don't fret! 2 | Don't worry! 3 | Don't freak out! 4 | Don't panic! 5 | Don't have a heart attack! 6 | Don't hyperventilate! 7 | Don't die! 8 | Don't get scared! 9 | Don't scream! 10 | Don't get angry! 11 | Don't start breaking stuff! 12 | Don't break anything! 13 | Stop, drop and roll. 14 | Why panic? 15 | No need to worry. 16 | No need to panic. 17 | Just relax. 18 | Just grab a drink and sit back. 19 | No need to stress yourself. 20 | We'll get this sorted out. 21 | Sorry about that! 22 | That was silly. 23 | Oops! 24 | Gah! 25 | Ahh! 26 | Oh no! 27 | Anyways. 28 | Regardless. 29 | Well, that was bad. 30 | Well, that was interesting. 31 | Well, that didn't turn out too well. -------------------------------------------------------------------------------- /data/main/errorHandle/errorMessage.txt: -------------------------------------------------------------------------------- 1 | Looks like we've hit the end of the road. 2 | ... now what? 3 | Crap. 4 | Did you do this? 5 | It's not my fault! 6 | I'm innocent. 7 | This can't be happening! 8 | What have you done? 9 | I'm blaming this on you! 10 | I blame JavaScript. 11 | I blame poor documentation. 12 | I blame this on Windows. 13 | I blame Linux. 14 | Dammit, Blake! You had one job! 15 | You did this! 16 | I can make up for it. 17 | I'm sorry. 18 | An error occured whilst displaying this error. 19 | Can we come to an agreement? 20 | We can just blame it on quantum physics. 21 | We can just blame it on Obama. 22 | Ow, that hurt. 23 | Quite honestly, I wouldn't worry myself about that. 24 | Hi. I'm zBot, and I'm a crashaholic. 25 | Can we try not to do that? 26 | This is all planned. 27 | This was supposed to happen, I swear! 28 | I'll give you $10 if you keep it on the low. 29 | What just happened? 30 | What went wrong? 31 | Everything was going so well... 32 | This doesn't make any sense! 33 | I'm sad now :( 34 | Please call technical support. 35 | I really need to stop doing that... 36 | I'm just an innocent bot, man! 37 | I honestly didn't mean to do that. 38 | Whoops. 39 | Whoa. 40 | Woah. 41 | Why?! 42 | That was weird... 43 | Whoops. My mistake. 44 | I can't believe you've done this. 45 | That wasn't intended. 46 | I thought I fixed this! 47 | Something cool was supposed to happen, not this! 48 | Oof, that didn't go right. 49 | That wasn't supposed to happen... 50 | Why'd that happen? 51 | Everybody stay calm! 52 | I was having such a good day until this. 53 | I'm going down! 54 | Man down, man down! 55 | I'm wounded! 56 | I've been shot! 57 | Send backup, I repeat, send backup! 58 | Is there a doctor anywhere? 59 | Ridiculous. 60 | This is an emergency. 61 | Someone call an ambulance! 62 | Score 500 or sacrifice child. 63 | I'm done. 64 | Stop that! 65 | :thinking: 66 | Come on. 67 | GOD! 68 | -------------------------------------------------------------------------------- /data/main/keys/keys.js: -------------------------------------------------------------------------------- 1 | module.exports = class Keys { 2 | get token() { 3 | return "[redacted]"; 4 | }; 5 | 6 | get beta() { 7 | return "[redacted]"; 8 | }; 9 | 10 | get lavalink() { 11 | return "[redacted]"; 12 | }; 13 | 14 | get zbot() { 15 | return "[redacted]"; 16 | } 17 | 18 | get dbot() { 19 | return "[redacted]"; 20 | }; 21 | 22 | get dbotuser() { 23 | return "[redacted]"; 24 | } 25 | 26 | get dbpw() { 27 | return "[redacted]"; 28 | }; 29 | 30 | get lst() { 31 | return "[redacted]"; 32 | }; 33 | 34 | get bfd() { 35 | return "[redacted]"; 36 | }; 37 | 38 | get ds() { 39 | return "[redacted]"; 40 | }; 41 | 42 | get bls() { 43 | return "[redacted]"; 44 | }; 45 | 46 | get lol() { 47 | return "[redacted]"; 48 | }; 49 | 50 | get idiot() { 51 | return "[redacted]"; 52 | } 53 | 54 | get genius() { 55 | return "[redacted]"; 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /events/GuildBanAdd.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../structures/general/Settings.js") 2 | const Punish = require("./../structures/moderation/Punish.js") 3 | const Discord = require("discord.js") 4 | 5 | module.exports = class { 6 | constructor(client) { 7 | this.client = client; 8 | } 9 | 10 | async run(guild, user) { 11 | let executor = this.client.user 12 | let reason = "*No reason supplied*." 13 | if (guild.me.hasPermission("VIEW_AUDIT_LOG")) { 14 | let auditLog = await guild.fetchAuditLogs({limit: 1}) 15 | if (auditLog) { 16 | auditLog = auditLog.entries.array()[0]; 17 | executor = auditLog.executor; 18 | reason = auditLog.reason; 19 | } 20 | } 21 | 22 | await Punish.performPunish(guild, "ban", executor, user, reason, null) 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /events/GuildBanRemove.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../structures/general/Settings.js") 2 | const Punish = require("./../structures/moderation/Punish.js") 3 | const Discord = require("discord.js") 4 | 5 | module.exports = class { 6 | constructor(client) { 7 | this.client = client; 8 | } 9 | 10 | async run(guild, user) { 11 | let executor = this.client.user 12 | let reason = "*No reason supplied*." 13 | if (guild.me.hasPermission("VIEW_AUDIT_LOG")) { 14 | let auditLog = await guild.fetchAuditLogs({limit: 1}) 15 | if (auditLog) { 16 | auditLog = auditLog.entries.array()[0]; 17 | executor = auditLog.executor; 18 | reason = auditLog.reason; 19 | } 20 | } 21 | 22 | await Punish.performPunish(guild, "unban", executor, user, reason, null) 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /events/GuildCreate.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../structures/general/Settings.js") 2 | const colors = require("colors") 3 | 4 | module.exports = class { 5 | constructor(client) { 6 | this.client = client; 7 | } 8 | 9 | async run(guild) { 10 | const client = this.client; 11 | client.util.log(colors.bold(colors.green("A new guild was added!")) + colors.blue(" (ID ▪ " + guild.id + ")")); 12 | client.util.log(colors.bold(colors.green("zBot is now at " + colors.yellow(require("./../bot.js").broadcastEval("client.guilds.size").join()) + " guilds!"))) 13 | await Settings.newGuild(guild); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /events/GuildDelete.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../structures/general/Settings.js") 2 | const colors = require("colors") 3 | 4 | module.exports = class { 5 | constructor(client) { 6 | this.client = client; 7 | } 8 | 9 | async run(guild) { 10 | const client = this.client; 11 | client.util.log(colors.bold(colors.red("A guild was removed.")) + colors.blue(" (ID ▪ " + guild.id + ")")); 12 | client.util.log(colors.bold(colors.red("zBot is now at " + colors.yellow(require("./../bot.js").broadcastEval("client.guilds.size").join()) + " guilds."))) 13 | await Settings.removeGuild(guild); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /events/GuildMemberAdd/index.js: -------------------------------------------------------------------------------- 1 | const processors = require(`${__dirname}/processors`); 2 | 3 | module.exports = class { 4 | constructor(client) { 5 | this.client = client; 6 | } 7 | 8 | async run(guildMember) { 9 | const client = this.client; 10 | if (guildMember == guildMember.guild.me) return; 11 | if (!guildMember) return; 12 | 13 | for (const processor of Object.values(processors)) { 14 | processor.call(this, guildMember); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /events/GuildMemberAdd/processors/autorole.js: -------------------------------------------------------------------------------- 1 | const Guild = require("./../../../models/guild/Guild.js"); 2 | 3 | module.exports = async function autorole(guildMember) { 4 | const guild = await guildMember.guild.config; 5 | 6 | //AUTOROLE MANAGER 7 | if (!guild) return; 8 | const guildroles = guild.get("autoroles"); 9 | if (guildroles) { 10 | const manageAutorole = guildroles.roles 11 | if (!manageAutorole) return; 12 | if (manageAutorole.length > 0) autorole(); 13 | function autorole() { 14 | manageAutorole.map((level, i) => { 15 | let autoroleObject = manageAutorole[i] 16 | if (autoroleObject.onJoin) { 17 | if (autoroleObject.removeOnLevelChange == null) autoroleObject.removeOnLevelChange = false; 18 | if (!guildMember.guild.roles.has(autoroleObject.role.id)) return; 19 | if (guildMember.roles.has(autoroleObject.role.id)) return; 20 | guildMember.roles.add(autoroleObject.role.id); 21 | } 22 | }); 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /events/GuildMemberAdd/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /events/GuildMemberAdd/processors/memberlog.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Discord = require("discord.js"); 3 | 4 | module.exports = async function memberlog(guildMember) { 5 | //Grab the channels 6 | const channelsMember = await Settings.grabLoggingChannels(guildMember.guild, "member", "join"); 7 | if (channelsMember) performJoinMessage(channelsMember); 8 | 9 | async function performJoinMessage(channels) { 10 | let randomjoin = ""; 11 | switch (Math.floor(Math.random() * 1000) % 7) { 12 | case 0: 13 | randomjoin = "Please give them a warm welcome!"; 14 | break; 15 | case 1: 16 | randomjoin = "Thanks for joining, and we hope you enjoy your stay!"; 17 | break; 18 | case 2: 19 | randomjoin = "Thanks for joining us!"; 20 | break; 21 | case 3: 22 | randomjoin = "It's great to have you here!"; 23 | break; 24 | case 4: 25 | randomjoin = "It's a pleasure to have you here."; 26 | break; 27 | case 5: 28 | randomjoin = "Hope you have had a great day so far!"; 29 | break; 30 | case 6: 31 | randomjoin = "Nice to see you!"; 32 | break; 33 | } 34 | 35 | let msg = ""; 36 | if (!await Settings.getValue(guildMember.guild, "joinMessage")) { 37 | msg = "**" + guildMember + "** has joined our awesome server! *" + randomjoin + "*"; 38 | } else { 39 | let joinMessage = await Settings.getValue(guildMember.guild, "joinMessage") 40 | let translate = { 41 | "%{user}": guildMember, 42 | "%{username}": guildMember.user.username, 43 | "%{tag}": guildMember.user.tag, 44 | "%{displayname}": guildMember.user.displayName, 45 | "%{discriminator}": guildMember.user.discriminator, 46 | "%{userid}": guildMember.id, 47 | "%{avatar}": guildMember.user.displayAvatarURL() 48 | }; 49 | for (let key in translate) { 50 | joinMessage = joinMessage.replace(new RegExp(key, "ig"), translate[key]); 51 | } 52 | msg = "​" + joinMessage.toString(); 53 | } 54 | 55 | //Send the log to all the channel(s) 56 | for (let i in channels) { 57 | if (await Settings.checkIgnore(guildMember.guild, channels[i], "logs")) continue; 58 | if (!guildMember.guild.channels.has(channels[i])) continue; 59 | guildMember.guild.channels.get(channels[i]).send({ msg }); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /events/GuildMemberAdd/processors/moderationlog.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Discord = require("discord.js"); 3 | const moment = require("moment") 4 | 5 | module.exports = async function moderationlog(guildMember) { 6 | const channelsMod = await Settings.grabLoggingChannels(guildMember.guild, "moderation", "member_join"); 7 | if (channelsMod) { 8 | const embed = new Discord.MessageEmbed(); 9 | embed.setAuthor("Member Joined » " + guildMember.user.tag, (guildMember.user.displayAvatarURL().toString().endsWith(".gif") ? guildMember.user.displayAvatarURL() : guildMember.user.displayAvatarURL({size: 2048}))); 10 | embed.setColor("#39cc45"); 11 | embed.setDescription(":wave: <@" + guildMember.id + "> has joined " + guildMember.guild.name + ".\n"); 12 | let msg = moment(`${guildMember.user.createdAt.toLocaleDateString()} ${guildMember.user.createdAt.toLocaleTimeString()}`, "YYYY-MM-DD h:mm:ss a").format("Do [of] MMMM, YYYY [at] h:mm:ss a") + " (" + moment(`${guildMember.user.createdAt.toLocaleDateString()} ${guildMember.user.createdAt.toLocaleTimeString()}`).fromNow() + ")"; 13 | embed.addField("**User Created**", msg); 14 | embed.setFooter("For more information on this user, type " + await Settings.getValue(guildMember.guild, "prefix") + "uinfo " + guildMember.user.username + "."); 15 | 16 | //Send the log to all the channel(s) 17 | for (let i in channelsMod) { 18 | if (await Settings.checkIgnore(guildMember.guild, channelsMod[i], "logs")) continue; 19 | if (!guildMember.guild.channels.has(channelsMod[i])) continue; 20 | guildMember.guild.channels.get(channelsMod[i]).send({ embed }); 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /events/GuildMemberRemove/index.js: -------------------------------------------------------------------------------- 1 | const processors = require(`${__dirname}/processors`); 2 | 3 | module.exports = class { 4 | constructor(client) { 5 | this.client = client; 6 | } 7 | 8 | async run(guildMember) { 9 | const client = this.client; 10 | if (guildMember == guildMember.guild.me) return; 11 | if (!guildMember) return; 12 | 13 | for (const processor of Object.values(processors)) { 14 | processor.call(this, guildMember); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /events/GuildMemberRemove/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /events/GuildMemberRemove/processors/memberlog.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Discord = require("discord.js"); 3 | 4 | module.exports = async function memberlog(guildMember) { 5 | //Grab the channels 6 | const channelsMember = await Settings.grabLoggingChannels(guildMember.guild, "member", "leave"); 7 | if (channelsMember) performLeaveMessage(); 8 | 9 | async function performLeaveMessage() { 10 | let randomleave = ""; 11 | switch (Math.floor(Math.random() * 1000) % 4) { 12 | case 0: 13 | randomleave = "See you later!"; 14 | break; 15 | case 1: 16 | randomleave = "Goodbye!"; 17 | break; 18 | case 2: 19 | randomleave = "Bye!"; 20 | break; 21 | case 3: 22 | randomleave = "Hope to see you again!"; 23 | break; 24 | } 25 | 26 | let msg = ""; 27 | if (!await Settings.getValue(guildMember.guild, "leaveMessage")) { 28 | msg = "**" + guildMember + "** has left the server. *" + randomleave + "*"; 29 | } else { 30 | let leaveMessage = await Settings.getValue(guildMember.guild, "leaveMessage") 31 | if (!leaveMessage) return; 32 | let translate = { 33 | "%{user}": guildMember, 34 | "%{username}": guildMember.user.username, 35 | "%{tag}": guildMember.user.tag, 36 | "%{displayname}": guildMember.user.displayName, 37 | "%{discriminator}": guildMember.user.discriminator, 38 | "%{userid}": guildMember.id, 39 | "%{avatar}": guildMember.user.displayAvatarURL() 40 | }; 41 | for (let key in translate) { 42 | leaveMessage = leaveMessage.replace(new RegExp(key, "ig"), translate[key]); 43 | } 44 | msg = "​" + leaveMessage.toString(); 45 | } 46 | 47 | //Send the log to all the channel(s) 48 | for (let i in channelsMember) { 49 | if (await Settings.checkIgnore(guildMember.guild, channelsMember[i], "logs")) continue; 50 | if (!guildMember.guild.channels.has(channelsMember[i])) continue; 51 | guildMember.guild.channels.get(channelsMember[i]).send({ msg }); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /events/GuildMemberRemove/processors/moderationlog.js: -------------------------------------------------------------------------------- 1 | const Punish = require("./../../../structures/moderation/Punish.js") 2 | const Settings = require("./../../../structures/general/Settings.js") 3 | const Discord = require("discord.js"); 4 | const moment = require("moment") 5 | 6 | module.exports = async function moderationlog(guildMember) { 7 | //Grab the channels 8 | const channelsMod = await Settings.grabLoggingChannels(guildMember.guild, "moderation", "member_leave"); 9 | 10 | async function leave() { 11 | const embed = new Discord.MessageEmbed(); 12 | embed.setAuthor("Member Left » " + guildMember.user.tag, guildMember.user.displayAvatarURL()); 13 | embed.setColor("#f44336"); 14 | embed.setDescription(":wave: <@" + guildMember.id + "> has left " + guildMember.guild.name + ".\n"); 15 | let msg = 0; 16 | if (guildMember.joinedAt) { 17 | msg = moment(`${guildMember.joinedAt.toLocaleDateString()} ${guildMember.joinedAt.toLocaleTimeString()}`, "YYYY-MM-DD h:mm:ss a").format("Do [of] MMMM, YYYY [at] h:mm:ss a") + " (" + moment(`${guildMember.joinedAt.toLocaleDateString()} ${guildMember.joinedAt.toLocaleTimeString()}`).fromNow() + ")"; 18 | if (msg === "Invalid date") { msg = `${guildMember.joinedAt.toLocaleDateString()} at ${guildMember.joinedAt.toLocaleTimeString()}`}; 19 | } 20 | embed.addField("User Joined", msg || "Unknown"); 21 | embed.setTimestamp(new Date()); 22 | 23 | //Send the log to all the channel(s) 24 | for (let i in channelsMod) { 25 | if (await Settings.checkIgnore(guildMember.guild, channelsMod[i], "logs")) continue; 26 | if (!guildMember.guild.channels.has(channelsMod[i])) continue; 27 | guildMember.guild.channels.get(channelsMod[i]).send({ embed }); 28 | } 29 | 30 | Settings.removeMember(guildMember.guild, guildMember) 31 | return embed; 32 | } 33 | } -------------------------------------------------------------------------------- /events/GuildMemberRemove/processors/punishment.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Punish = require("./../../../structures/moderation/Punish.js") 3 | const Discord = require("discord.js") 4 | 5 | module.exports = async function punishment(guildMember) { 6 | let client = guildMember.guild.client 7 | let executor = client.user 8 | let reason = "*No reason supplied*." 9 | if (guildMember.guild.me.hasPermission("VIEW_AUDIT_LOG")) { 10 | let auditLog = await guildMember.guild.fetchAuditLogs({limit: 1}) 11 | if (auditLog) { 12 | auditLog = auditLog.entries.array()[0]; 13 | executor = auditLog.executor; 14 | reason = auditLog.reason; 15 | 16 | if (auditLog.action === "MEMBER_KICK" && auditLog.target.id === guildMember.id) { 17 | //kick 18 | await Punish.performPunish(guildMember.guild, "kick", executor, guildMember, reason, null) 19 | } 20 | } 21 | } 22 | 23 | const channelsMod = await Settings.grabLoggingChannels(guildMember.guild, "moderation", "member_leave"); 24 | 25 | let recentCase = await Punish.grabCases(guildMember.guild); 26 | let recentCaseObj = await Punish.getCase(recentCase, guildMember.guild) 27 | if (recentCaseObj) { 28 | if ((recentCaseObj.type == "ban" || recentCaseObj.type == "softban" || recentCaseObj.type == "kick") && recentCaseObj.userID == guildMember.id) { 29 | if (channelsMod) { 30 | let type = recentCaseObj.type + "ed"; 31 | if (recentCaseObj.type.includes("ban")) { 32 | type = recentCaseObj.type + "ned" 33 | } 34 | performPunishLeave(type); 35 | } 36 | } 37 | } 38 | 39 | async function performPunishLeave(type) { 40 | const embed = new Discord.MessageEmbed(); 41 | embed.setAuthor("Member Removed » " + guildMember.user.tag, guildMember.user.displayAvatarURL()); 42 | embed.setColor("#f44336"); 43 | embed.setDescription(":wave: <@" + guildMember.id + "> was " + type + " from " + guildMember.guild.name + 44 | (guildMember.guild.members.get(recentCaseObj.punishedBy) ? " by <@" + recentCaseObj.punishedBy + ">.\n" : ".")); 45 | embed.addField("Reason", recentCaseObj.reason); 46 | embed.setTimestamp(new Date()); 47 | 48 | //Send the log to all the channel(s) 49 | for (let i in channelsMod) { 50 | if (await Settings.checkIgnore(guildMember.guild, channelsMod[i], "logs")) continue; 51 | if (!guildMember.guild.channels.has(channelsMod[i])) continue; 52 | guildMember.guild.channels.get(channelsMod[i]).send({ embed }); 53 | } 54 | 55 | Settings.removeMember(guildMember.guild, guildMember) 56 | return; 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /events/GuildMemberUpdate/index.js: -------------------------------------------------------------------------------- 1 | const processors = require(`${__dirname}/processors`); 2 | 3 | module.exports = class { 4 | constructor(client) { 5 | this.client = client; 6 | } 7 | 8 | async run(oldMember, newMember) { 9 | const client = this.client; 10 | if (oldMember == newMember) return; 11 | if (!oldMember || !newMember) return; 12 | 13 | for (const processor of Object.values(processors)) { 14 | processor.call(this, oldMember, newMember); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /events/GuildMemberUpdate/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /events/GuildMemberUpdate/processors/nickname.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Guild = require("./../../../models/guild/Guild.js") 3 | const Discord = require("discord.js") 4 | 5 | module.exports = async function nickname(oldMember, newMember) { 6 | const client = oldMember.guild.client 7 | if (oldMember.user.bot) return; 8 | 9 | let moderator = oldMember; 10 | if (newMember.guild.me.hasPermission("VIEW_AUDIT_LOG")) { 11 | let auditLog = await oldMember.guild.fetchAuditLogs({limit: 1}) 12 | if (auditLog) { 13 | auditLog = auditLog.entries.array()[0]; 14 | moderator = auditLog.executor; 15 | } 16 | } 17 | 18 | if (newMember.nickname != oldMember.nickname) { 19 | //Grab the channels 20 | const channels = await Settings.grabLoggingChannels(oldMember.guild, "moderation", "member_update"); 21 | if (!channels) return; 22 | 23 | const embed = new Discord.MessageEmbed(); 24 | embed.setAuthor("Nickname Change » " + oldMember.user.tag, oldMember.user.displayAvatarURL()); 25 | embed.setColor("#42b3f4"); 26 | if (newMember.nickname != null) { 27 | embed.setDescription(":label: " + (moderator.id !== oldMember.id ? "<@" + moderator.id + ">" + " changed " + "<@" + oldMember.id + ">'s nickname." : "<@" + oldMember.id + "> has changed their nickname.")); 28 | } else { 29 | embed.setDescription(":label: " + (moderator.id !== oldMember.id ? "<@" + moderator.id + ">" + " cleared " + "<@" + oldMember.id + ">'s nickname." : "<@" + oldMember.id + "> has cleared their nickname.") + "\nIt has defaulted to their username.\n"); 30 | } 31 | embed.addField("Previous Nickname", oldMember.displayName); 32 | embed.addField("New Nickname", newMember.displayName); 33 | embed.setTimestamp(new Date()); 34 | 35 | //Send the log to all the channel(s) 36 | for (let i in channels) { 37 | if (await Settings.checkIgnore(oldMember.guild, channels[i], "logs")) continue; 38 | if (!oldMember.guild.channels.has(channels[i])) continue; 39 | oldMember.guild.channels.get(channels[i]).send({ embed }); 40 | } 41 | return; 42 | } 43 | } -------------------------------------------------------------------------------- /events/GuildMemberUpdate/processors/punishment.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Punish = require("./../../../structures/moderation/Punish.js") 3 | 4 | module.exports = async function punishment(oldMember, newMember) { 5 | if (oldMember.roles !== newMember.roles) { 6 | let executor = oldMember.guild.client.user 7 | let reason = "*No reason supplied*." 8 | if (newMember.guild.me.hasPermission("VIEW_AUDIT_LOG")) { 9 | let auditLog = await oldMember.guild.fetchAuditLogs({limit: 1}) 10 | if (auditLog) { 11 | auditLog = auditLog.entries.array()[0]; 12 | executor = auditLog.executor; 13 | reason = auditLog.reason; 14 | } 15 | } 16 | 17 | const muteRole = await Settings.getValue(oldMember.guild, "muteRole") 18 | if (!oldMember.roles.has(muteRole) && newMember.roles.has(muteRole)) { 19 | //muted 20 | await Punish.performPunish(oldMember.guild, "mute", executor, newMember, reason, null) 21 | } 22 | if (oldMember.roles.has(muteRole) && !newMember.roles.has(muteRole)) { 23 | //unmuted 24 | await Punish.performPunish(oldMember.guild, "unmute", executor, newMember, reason, null) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /events/Message/index.js: -------------------------------------------------------------------------------- 1 | const Settings = require('./../../structures/general/Settings.js'); 2 | const Guild = require("./../../models/guild/Guild.js") 3 | const Discord = require("discord.js") 4 | const colors = require('colors'); 5 | const processors = require(`${__dirname}/processors`); 6 | const Interface = new(require("./../../structures/internal/Interface.js").Interface)(); 7 | const CommandHandler = require("./../../structures/internal/CommandHandler.js") 8 | const Filter = require("./../../structures/moderation/Filter.js").Expletive 9 | 10 | module.exports = class { 11 | constructor(client) { 12 | this.client = client; 13 | } 14 | 15 | async run(message) { 16 | if (message.author.bot) return; 17 | 18 | if (!message.guild) { 19 | this.client.util.log("[DM] " + message.content) 20 | new(require("./../../structures/games/Challenge.js"))(this.client).run(message); 21 | return; 22 | } 23 | 24 | //Ignore 25 | if (await Settings.checkIgnore(message.guild, message.channel, "everything")) return; 26 | 27 | const guild = await message.guild.config; 28 | if (!guild) { //create a new guild if one is not present 29 | this.client.util.log(colors.bold(colors.green("A new guild was added!")) + colors.blue(" (ID ▪ " + message.guild.id + ")")); 30 | this.client.util.log(colors.bold(colors.green("zBot is now at " + colors.yellow(this.client.guilds.size) + " guilds!"))) 31 | await Settings.fixGuild(this.client.guilds.get(message.guild.id)) 32 | } 33 | 34 | for (let processor of Object.values(processors)) { 35 | processor.call(this, message, guild); 36 | } 37 | 38 | if (message.channel.type === 'text' && !message.channel.permissionsFor(this.client.user).has("SEND_MESSAGES")) return; 39 | this.client.messages.total++ 40 | this.client.messages.characters += message.content.length 41 | 42 | //If not ignored, run a command. 43 | new CommandHandler(this.client).run(message) //CommandHandler 44 | new Filter(this.client).run(message) //Filter 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /events/Message/processors/afk.js: -------------------------------------------------------------------------------- 1 | const cooldowns = new Map(); 2 | 3 | module.exports = async function afk(message) { 4 | //COOLDOWN SYSTEM 5 | if (!cooldowns.has(message.guild.id)) cooldowns.set(message.guild.id, new Map()); 6 | const now = Date.now(); 7 | const timestamps = cooldowns.get(message.guild.id); 8 | const cooldownAmount = 30000; 9 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 10 | if (now < expirationTime) return; 11 | timestamps.set(message.author.id, now); 12 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 13 | //END COOLDOWN 14 | 15 | if (message.mentions.members.first()) { 16 | const user = (await message.mentions.members.first().profile).get("afk") 17 | if (message.author.id == message.mentions.members.first().id) return; 18 | if (user.afk) message.zreply(this.client.util.emoji("info", message.guild) + " **AFK**: " + message.mentions.members.first() 19 | .username + " has marked themselves as AFK" + (user.reason ? " with the reason `" + user.reason + "`." : ".")) 20 | } 21 | }; -------------------------------------------------------------------------------- /events/Message/processors/autoresponse.js: -------------------------------------------------------------------------------- 1 | const cooldowns = new Map(); 2 | 3 | module.exports = async function autoresponse(message, guild) { 4 | const client = message.guild.client; 5 | //COOLDOWN SYSTEM 6 | if (!cooldowns.has(message.guild.id)) cooldowns.set(message.guild.id, new Map()); 7 | const now = Date.now(); 8 | const timestamps = cooldowns.get(message.guild.id); 9 | const cooldownAmount = 2100; 10 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 11 | if (now < expirationTime) return; 12 | timestamps.set(message.author.id, now); 13 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 14 | //END COOLDOWN 15 | 16 | const autoresponses = guild.get("autoresponses"); 17 | if (guild && autoresponses) { 18 | for (let i in autoresponses.responses) { 19 | if (autoresponses.responses[i].trigger === message.content) { 20 | const Tags = require("./../../../structures/general/Tags.js") 21 | const variables = await Tags.autoresponseVariables(message, autoresponses.responses[i].response) 22 | function translate(txt) { 23 | for (let key in variables) { 24 | txt = txt.replace(new RegExp(key, "ig"), variables[key]); 25 | } 26 | return (txt.charAt(0) === txt.charAt(0).toUpperCase() ? txt.charAt(0).toUpperCase() + txt.slice(1) : txt.charAt(0) + txt.slice(1)) 27 | } 28 | this.client.util.log(`[AUTORESPONSE] Reacting to autoresponse trigger ${autoresponses.responses[i].trigger} in ${message.guild.name} (${message.guild.id})`, "success") 29 | message.channel.zend(await Tags.cleanString(translate(autoresponses.responses[i].response.toString()), message)) 30 | return; 31 | } 32 | } 33 | } 34 | 35 | }; -------------------------------------------------------------------------------- /events/Message/processors/autorole.js: -------------------------------------------------------------------------------- 1 | const cooldowns = new Map(); 2 | const Settings = require('./../../../structures/general/Settings.js'); 3 | const Experience = require('./../../../structures/user/Experience.js') 4 | 5 | module.exports = async function autorole(message, guild) { 6 | //COOLDOWN SYSTEM 7 | if (!cooldowns.has(message.guild.id)) cooldowns.set(message.guild.id, new Map()); 8 | const now = Date.now(); 9 | const timestamps = cooldowns.get(message.guild.id); 10 | const cooldownAmount = 2100; 11 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 12 | if (now < expirationTime) return; 13 | timestamps.set(message.author.id, now); 14 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 15 | //END COOLDOWN 16 | 17 | //AUTOROLE MANAGER 18 | const Experience = require('./../../../structures/user/Experience.js') 19 | let userLevel = await Experience.getLevel(message.member) 20 | const autoroles = guild.get("autoroles"); 21 | if (autoroles.roles.length && !timestamps.has(message.author.id)) autorole(); 22 | 23 | function autorole() { 24 | if (!autoroles.roles) return; 25 | autoroles.roles.map((level, i) => { 26 | let autoroleObject = autoroles.roles[i] 27 | if (!autoroleObject.onJoin) { 28 | if (autoroleObject.level !== userLevel && (autoroleObject.removeOnLevelChange == null ? false : autoroleObject.removeOnLevelChange) == true) { 29 | if (message.guild.roles.has(autoroleObject.role.id) && !message.member.roles.has(autoroleObject.role.id)) { 30 | this.client.util.log(`[LEVEL] Removing autorole ${autoroleObject.role.name} (${autoroleObject.role.id}) (level ${autoroleObject.level} - user level is ${userLevel}) in ${message.guild.name} (${message.guild.id})`, "success") 31 | message.member.roles.remove(autoroleObject.role.id); 32 | } 33 | } 34 | } 35 | 36 | if (autoroleObject.level <= userLevel) { 37 | if (autoroleObject.removeOnLevelChange == null) autoroleObject.removeOnLevelChange = false; 38 | if (message.guild.roles.has(autoroleObject.role.id) && !message.member.roles.has(autoroleObject.role.id)) { 39 | this.client.util.log(`[LEVEL] Adding autorole ${autoroleObject.role.name} (${autoroleObject.role.id}) (level ${autoroleObject.level} - user level is ${userLevel}) in ${message.guild.name} (${message.guild.id})`, "success") 40 | message.member.roles.add(autoroleObject.role.id); 41 | } 42 | } 43 | }); 44 | } 45 | 46 | }; -------------------------------------------------------------------------------- /events/Message/processors/experience.js: -------------------------------------------------------------------------------- 1 | const cooldowns = new Map(); 2 | const Settings = require('./../../../structures/general/Settings.js'); 3 | const Experience = require('./../../../structures/user/Experience.js') 4 | const Badges = require('./../../../structures/user/Badges.js'); 5 | 6 | module.exports = async function experience(message, guild) { 7 | //COOLDOWN SYSTEM 8 | if (!cooldowns.has(message.guild.id)) cooldowns.set(message.guild.id, new Map()); 9 | const now = Date.now(); 10 | const timestamps = cooldowns.get(message.guild.id); 11 | const cooldownAmount = 65000; 12 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 13 | if (now < expirationTime) return; 14 | timestamps.set(message.author.id, now); 15 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 16 | //END COOLDOWN 17 | 18 | let isExperience = await Settings.getValue(message.guild, "experienceTracking") 19 | if (message.guild == null) return; 20 | if (message.channel.type !== 'text') return; 21 | if (message.author.bot) return; 22 | 23 | let isIgnored = false; 24 | if (await Settings.checkIgnore(message.guild, message.channel, "experience")) isIgnored = true; 25 | 26 | //✦ Experience handler, including levels, badges and experience gained per message. ✦ 27 | const filter = message => message.author.id === member.user.id && member.user.bot == false; 28 | // Checks if they have talked recently 29 | 30 | if (isExperience && !isIgnored) { 31 | const levelMessages = await Settings.getValue(message.guild, "levelUpMessages"); 32 | let experienceGainMax = await Settings.getValue(message.guild, "experienceGainMax"); 33 | let experienceGainMin = await Settings.getValue(message.guild, "experienceGainMin"); 34 | if (!experienceGainMax) experienceGainMax = 11; 35 | if (!experienceGainMin) experienceGainMin = 4; 36 | 37 | let member = message.member 38 | if (!member) member = message.guild.members.fetch(message.author.id); 39 | let user = (await member.profile) 40 | 41 | const BadgeStructure = new Badges(member); 42 | BadgeStructure.check(); 43 | 44 | const level_before = await Experience.getLevel(member) 45 | let rand = Math.floor(Math.random() * (Number(experienceGainMax) - Number(experienceGainMin) + 1)) + Number(experienceGainMin); 46 | const experience = Number(user.get("experience")) 47 | 48 | await member.update("experience", experience+rand) //increment 49 | const level_after = await Experience.getLevel(member) 50 | if (level_after > level_before) (levelMessages ? function(){ 51 | const experience_to_next = Experience.getLevelBounds(level_after).lowerBound 52 | message.channel.zend(message.guild.client.util.emoji("info", message.guild) + " Congratulations **" + message.author.username + 53 | "**, you leveled up!\n**LEVEL** `" + level_before + "` ─ `" + level_after + "` | `" + experience_to_next + "` **EXP** *to next level*") 54 | }() : "") 55 | 56 | if (experience+rand > 99999999999999 || experience > 99999999999999) { 57 | await member.update("experience", 99999999999999) 58 | } 59 | } 60 | 61 | }; -------------------------------------------------------------------------------- /events/Message/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /events/MessageDelete.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../structures/general/Settings.js") 2 | const Guild = require("./../models/guild/Guild.js") 3 | const Discord = require("discord.js") 4 | 5 | module.exports = class { 6 | constructor(client) { 7 | this.client = client; 8 | } 9 | 10 | async run(message) { 11 | const client = this.client 12 | if (!message.guild) return; 13 | if (`${Date.now() - message.createdTimestamp}` < 1000) return; 14 | if (message.content.startsWith(await message.guild.prefix)) return; 15 | if (message.author.bot) return; 16 | 17 | client.messages.deleted++ 18 | 19 | const channels = await Settings.grabLoggingChannels(message.guild, "message", "delete"); 20 | if (!channels) return; 21 | if (await Settings.checkIgnore(message.guild, message.channel, "logs")) return; 22 | 23 | const embed = new Discord.MessageEmbed(); 24 | embed.setAuthor("Message Deleted » " + message.author.tag, message.author.displayAvatarURL()); 25 | embed.setColor("#e08743"); 26 | embed.setDescription(client.util.emoji("bin", message.guild) + " Message by <@" + message.author.id + "> in <#" + message.channel.id + "> was removed.\n"); 27 | let msg = "*No message provided*."; 28 | msg = message.cleanContent; 29 | if (message.cleanContent.length > 1020) msg = message.cleanContent.substr(0, 1019) + "..."; 30 | if (message.cleanContent.length < 1) msg = "*No message provided*."; 31 | if (message.attachments.size > 0) msg += " " + message.attachments.map(a => a.attachment).toString(); 32 | embed.addField("**Message**", msg); 33 | embed.setTimestamp(new Date()); 34 | 35 | //Send the log to all the channel(s) 36 | for (let i in channels) { 37 | if (!message.guild.channels.has(channels[i])) continue; 38 | message.guild.channels.get(channels[i]).send({ embed }); 39 | } 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /events/MessageUpdate.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../structures/general/Settings.js") 2 | const Guild = require("./../models/guild/Guild.js") 3 | const Discord = require("discord.js") 4 | const Expletive = require("./../structures/moderation/Filter.js").Expletive 5 | 6 | module.exports = class { 7 | constructor(client) { 8 | this.client = client; 9 | } 10 | 11 | async run(oldMessage, newMessage) { 12 | const client = this.client 13 | if (!oldMessage.guild) return; 14 | if (oldMessage.cleanContent == newMessage.cleanContent) return; //Ignore 15 | if (oldMessage.author.bot) return; 16 | 17 | client.messages.edited++ 18 | 19 | new Expletive(this.client).run(newMessage) //Filter 20 | //Grab the channels 21 | const channels = await Settings.grabLoggingChannels(oldMessage.guild, "message", "edit"); 22 | if (!channels) return; 23 | if (await Settings.checkIgnore(oldMessage.guild, oldMessage.channel, "ignoreLogs")) return; 24 | 25 | const embed = new Discord.MessageEmbed(); 26 | embed.setAuthor("Message Edited » " + oldMessage.author.tag, (oldMessage.author.displayAvatarURL().toString().endsWith(".gif") ? oldMessage.author.displayAvatarURL() : oldMessage.author.displayAvatarURL({size: 2048}))); 27 | embed.setColor("#f4c242"); 28 | embed.setDescription(client.util.emoji("edit", oldMessage.guild) + " Message by <@" + oldMessage.author.id + "> in <#" + oldMessage.channel.id + "> was edited.\n") 29 | let msg = ""; 30 | if (oldMessage.cleanContent.length > 1020) { 31 | msg = oldMessage.cleanContent.substr(0, 1019) + "..."; 32 | } else if (oldMessage.cleanContent.length < 1) { 33 | msg = "*No message provided*."; 34 | } else { 35 | msg = oldMessage.cleanContent; 36 | } 37 | if (oldMessage.attachments.size > 0) { 38 | let files = oldMessage.attachments.map(a => a.attachment) 39 | msg += " " + files.toString() 40 | } 41 | 42 | embed.addField("**Previous Content**", msg); 43 | 44 | msg = ""; 45 | if (newMessage.cleanContent.length > 1020) { 46 | msg = newMessage.cleanContent.substr(0, 1019) + "..."; 47 | } else if (newMessage.cleanContent.length < 1) { 48 | msg = "*No message provided*."; 49 | } else { 50 | msg = newMessage.cleanContent; 51 | } 52 | if (newMessage.attachments.size > 0) { 53 | let files = newMessage.attachments.map(a => a.attachment) 54 | msg += " " + files.toString() 55 | } 56 | embed.addField("**Updated Content**", msg); 57 | embed.setTimestamp(new Date()); 58 | 59 | //Send the log to all the channel(s) 60 | for (let i in channels) { 61 | if (!oldMessage.guild.channels.has(channels[i])) continue; 62 | oldMessage.guild.channels.get(channels[i]).send({ embed }); 63 | } 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /events/Ready/index.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../structures/general/Settings.js") 2 | const processors = require(`${__dirname}/processors`); 3 | const jobs = require(`${__dirname}/jobs`); 4 | 5 | module.exports = class { 6 | constructor(client) { 7 | this.client = client; 8 | } 9 | 10 | async run(client) { 11 | client = this.client 12 | client.commands = new Map() 13 | client.categories = new Map() 14 | client.aliases = new Map() 15 | client.messages = {characters: 0, total: 0, deleted: 0, edited: 0}; 16 | client.developer = client.users.get('246574843460321291'); 17 | client.contributors = { 18 | "286166184402092042": "providing hosting for zBot and contributing", 19 | "278805875978928128": "for providing a codebase for zBot and helping out", 20 | "236279900728721409": "help with lavalink and optimization", 21 | "284551391781978112": "support, testing and providing ideas", 22 | "272689325521502208": "big help with nsfw & music commands" 23 | } 24 | client.donators = { 25 | "246574843460321291": 0 26 | } 27 | 28 | for (const processor of Object.values(processors)) { 29 | processor.call(this, client); 30 | } 31 | 32 | for (const job of jobs) { 33 | job.func.call(this); 34 | setInterval(job.func.bind(this), job.interval); 35 | } 36 | 37 | client.util.log("> zBot is now online!", "success"); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /events/Ready/jobs/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(file => require(`${__dirname}/${file}`)); -------------------------------------------------------------------------------- /events/Ready/jobs/punishment.js: -------------------------------------------------------------------------------- 1 | const Timers = require("./../../../structures/general/Timers.js"); 2 | 3 | async function punishment() { 4 | Timers.pollPunishments(); 5 | } 6 | 7 | 8 | module.exports = { 9 | func: punishment, 10 | interval: 6e4 11 | }; 12 | 13 | -------------------------------------------------------------------------------- /events/Ready/jobs/timer.js: -------------------------------------------------------------------------------- 1 | const Timers = require("./../../../structures/general/Timers.js"); 2 | 3 | async function timer() { 4 | Timers.pollTimers(); 5 | } 6 | 7 | 8 | module.exports = { 9 | func: timer, 10 | interval: 1.5e4 11 | }; -------------------------------------------------------------------------------- /events/Ready/jobs/updateStats.js: -------------------------------------------------------------------------------- 1 | const Timers = require("./../../../structures/general/Timers.js"); 2 | const Statistics = require("./../../../structures/internal/Statistics.js"); 3 | 4 | async function updateStats(client) { 5 | Statistics.updateRates() 6 | if (process.argv[2] !== "--beta") { 7 | Statistics.sendBotListingData(client) 8 | } 9 | await Statistics.zBotUpvotes(); 10 | } 11 | 12 | 13 | module.exports = { 14 | func: updateStats, 15 | interval: 1e6 16 | }; -------------------------------------------------------------------------------- /events/Ready/processors/commandLoader.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs") 2 | 3 | module.exports = async function commandLoader(client) { 4 | cmdLoader(); 5 | async function cmdLoader() { 6 | const categories = await fs.readdirSync('./commands'); 7 | categories.forEach(x => { 8 | loadCategory(x); 9 | }); 10 | } 11 | 12 | async function loadCategory(name) { 13 | const files = await fs.readdirSync(`./commands/${name}`); 14 | const commands = []; 15 | files.forEach(x => { 16 | loadCmd(name, x); 17 | commands.push(x.split('.')[0]); 18 | }); 19 | client.categories.set(name, commands); 20 | } 21 | 22 | async function loadCmd(category, cmd) { 23 | try { 24 | const command = require(`./../../../commands/${category}/${cmd}`); 25 | client.commands.set(command.settings.command, command); 26 | if (!command.settings.aliases) return; 27 | command.settings.aliases.forEach(alias => { 28 | client.aliases.set(alias, command.settings.command); 29 | }); 30 | } catch (err) { 31 | client.util.log(`An error has occured trying to load command '${cmd.split('.')[0]}'`, "critical"); 32 | client.util.log(err.stack, "critical"); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /events/Ready/processors/extensionLoader.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs") 2 | 3 | module.exports = async function extensionLoader(client) { 4 | const extentions = []; 5 | const extenderFolder = "./extenders"; 6 | fs.readdir(extenderFolder, (err, files) => { 7 | files.map(file => { 8 | try { 9 | require(`./../../../${extenderFolder}/${file}`) 10 | extentions.push(file.split(".")[0]); 11 | } catch (error) { 12 | client.util.log(`Error loading ${file} extension: ${error}`, "critical"); 13 | } 14 | }); 15 | client.util.log(extentions.length + " extentions successfully loaded!", "success") 16 | }); 17 | } -------------------------------------------------------------------------------- /events/Ready/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /events/Ready/processors/lavalink.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const LavalinkClient = require("./../../../structures/general/LavalinkClient.js") 3 | 4 | module.exports = async function lavalink(client) { 5 | let nodes = [ 6 | { "host": '127.0.0.1', "port": 5959, "region": 'eu', "password": "[redacted]" } 7 | ]; 8 | let regions = { 9 | eu: ['eu', 'amsterdam', 'frankfurt', 'russia', 'hongkong', 'singapore', 'sydney'], 10 | us: ['us', 'brazil'], 11 | }; 12 | client.lavalink = new LavalinkClient(client, nodes, { 13 | user: client.user.id, 14 | shards: 1, 15 | region: 'us', 16 | rest: {url: 'http://127.0.0.1:2333', password: "[redacted]" } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /events/Ready/processors/postStats.js: -------------------------------------------------------------------------------- 1 | const Statistics = require("./../../../structures/internal/Statistics.js"); 2 | const snekfetch = require('snekfetch'); 3 | 4 | module.exports = async function postStats(client) { 5 | await Statistics.pollUpvotes(); 6 | snekfetch.post(`https://zbot.me/api/v1/commands`) 7 | .set('Authorization', api.zbot) 8 | .send({ commands: [...client.commands] }) 9 | .then(client.util.log('Updated zbot.me commands.', "success")) 10 | .catch(e => client.util.log('Unable to update zbot.me commands mate. ' + e, "critical")); 11 | 12 | snekfetch.post(`https://zbot.me/api/v1/categories`) 13 | .set('Authorization', api.zbot) 14 | .send({ categories: [...client.categories] }) 15 | .then(client.util.log('Updated zbot.me categories.', "success")) 16 | .catch(e => client.util.log('Unable to update zbot.me categories mate. ' + e, "critical")); 17 | } -------------------------------------------------------------------------------- /events/Ready/processors/vacuum.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | 3 | module.exports = async function vacuum(client) { 4 | await Settings.storeGuildConfigurations(); 5 | 6 | for (let [key, value] of client.guilds) { 7 | function sleep(ms) { 8 | return new Promise(resolve => setTimeout(resolve, ms)); 9 | } 10 | await sleep(350); 11 | await Settings.checkGuild(value); 12 | await Settings.checkGuildSettings(value); 13 | } 14 | } -------------------------------------------------------------------------------- /extenders/Guild.js: -------------------------------------------------------------------------------- 1 | const { Structures } = require("discord.js"); 2 | const Settings = require("./../structures/general/Settings.js"); 3 | const GuildStore = new Map(); 4 | const Guild = require('./../models/guild/Guild.js'); 5 | 6 | module.exports = Structures.extend("Guild", DiscordGuild => { 7 | return class zBotGuild extends DiscordGuild { 8 | 9 | constructor(...args) { 10 | super(...args); 11 | } 12 | 13 | get prefix() { 14 | return (async () => { 15 | return await Settings.getValue(this, "prefix"); 16 | })(); 17 | } 18 | 19 | async create() { 20 | return await Settings.fixGuild(this); 21 | } 22 | 23 | get fix() { 24 | return (async () => { 25 | let config = (await this.config); 26 | let changed = []; 27 | for (let i of Object.keys(this.defaultValues)) { 28 | try { 29 | if ((config.get(i) == null || config.get(i) == undefined)) { 30 | console.log("Guild fixed for " + i) 31 | const assign = !config.get(i) ? this.defaultValues[i] : Object.assign(this.defaultValues[i], config.get(i)) 32 | changed.push(i); 33 | await this.update(i, assign); //change this so `update` isn't called on every loop 34 | } 35 | } catch (e) { 36 | continue; 37 | } 38 | } 39 | return changed; 40 | })(); 41 | } 42 | 43 | get config() { //needs await 44 | return (async () => { 45 | let store = GuildStore.get(this.id); 46 | if (!store) { 47 | let guild = await Guild.findOne({ where: { id: this.id } }); 48 | if (!guild) guild = await this.create(); 49 | let fixed = false; 50 | GuildStore.set(this.id, new Map()); 51 | for (let [key, value] of Object.entries(guild.dataValues)) { 52 | if ((value == null || value == undefined) && key !== "id" && !fixed) { 53 | await this.fix; 54 | fixed = true; 55 | } 56 | GuildStore.get(this.id).set(key, value); 57 | } 58 | } 59 | return GuildStore.get(this.id); 60 | })(); 61 | } 62 | 63 | update(type, value) { 64 | return (async () => { 65 | try { 66 | GuildStore.get(this.id).set(type, value); 67 | await Guild.update({ [type]: value }, { where: { id: this.id } }); 68 | return value; 69 | } catch (err) { 70 | throw err; 71 | } 72 | })(); 73 | } 74 | 75 | get music() { 76 | return this.client.music.get(this.id) || this.client.music.add(this); 77 | } 78 | 79 | get defaultValues() { 80 | const obj = {}; 81 | for (let i of Object.keys(Guild._defaultValues)) { 82 | if (i == "id") continue; 83 | obj[i] = Guild._defaultValues[i](); 84 | } 85 | return obj 86 | } 87 | } 88 | }); 89 | -------------------------------------------------------------------------------- /extenders/Message.js: -------------------------------------------------------------------------------- 1 | const { Structures } = require("discord.js"); 2 | const Settings = require("./../structures/general/Settings.js"); 3 | const Interface = new(require("./../structures/internal/Interface.js").Interface)(); 4 | 5 | module.exports = Structures.extend("Message", DiscordMessage => { 6 | return class zBotMessage extends DiscordMessage { 7 | 8 | constructor(...args) { 9 | super(...args); 10 | } 11 | 12 | zreply(content, options) { 13 | const newContent = content.embed ? Interface.unembedify(content.embed) : content; 14 | return this.reply(newContent, options); 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /extenders/TextChannel.js: -------------------------------------------------------------------------------- 1 | const { Structures } = require("discord.js"); 2 | const Settings = require("./../structures/general/Settings.js"); 3 | const Interface = new(require("./../structures/internal/Interface.js").Interface)(); 4 | 5 | module.exports = Structures.extend("TextChannel", DiscordTextChannel => { 6 | return class zBotTextChannel extends DiscordTextChannel { 7 | 8 | constructor(...args) { 9 | super(...args); 10 | } 11 | 12 | zend(content, options) { 13 | let newContent; 14 | if (content.embed) newContent = (!this.guild.me.hasPermission("EMBED_LINKS") ? Interface.unembedify(content.embed) : content ) 15 | return this.send(newContent || content, options); 16 | } 17 | 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /extenders/User.js: -------------------------------------------------------------------------------- 1 | const { Structures } = require("discord.js"); 2 | const Experience = require("./../structures/user/Experience.js"); 3 | const Stats = require("./../structures/internal/Statistics.js"); 4 | const Permissions = require("./../structures/internal/Permissions.js"); 5 | const Timer = require('./../models/general/Timer.js'); 6 | const Statistics = require('./../structures/internal/Statistics.js'); 7 | 8 | module.exports = Structures.extend("User", DiscordUser => { 9 | return class zBotUser extends DiscordUser { 10 | 11 | constructor(...args) { 12 | super(...args); 13 | } 14 | 15 | get developer() { return Permissions.isDeveloper(this); } 16 | 17 | get guilds() { 18 | let amount = 0; 19 | for (const client of require("./../bot.js").broadcastEval("client").results) { 20 | amount += client.guilds.filter(guild => { 21 | if (guild.members.has(this.id)) return guild; 22 | }).size; 23 | } 24 | return amount; 25 | } 26 | 27 | get upvoted() { 28 | const id = this.id; 29 | return (async () => { 30 | let upvoters = []; 31 | for (const client of require("./../bot.js").broadcastEval("client").results) { 32 | if (!client.upvoters) return false; 33 | upvoters.concat(client.upvoters) 34 | } 35 | reduce = a => [...new Set(a)]; 36 | upvoters = reduce(upvoters) 37 | return (upvoters.includes(id) ? upvoters.includes(id) : 38 | async function() { const v = await Statistics.zBotUpvotes(); if (v.includes(id)) client.upvoters.push(id); return v.includes(id); }()) 39 | })(); 40 | } 41 | 42 | get patron() { //plus = 408590002100895744, ultra = 408589651566264332 43 | const client = require("./../bot.js").eval(0, "client") 44 | const donators = client.donators; 45 | const patrons = client.guilds.get('363516708062756886').members.filter(m => m.roles.has('408590204707012608')) 46 | const isPatron = patrons.has(this.id); 47 | const patronLevel = (isPatron ? (patrons.get(this.id).roles.has("408589651566264332") ? 2 : 1) : 0) 48 | const type = patronLevel == 1 ? "zBot+" : "zBot Ultra"; 49 | return isPatron ? { patron: isPatron, level: patronLevel, type: type, redeemed: false, amount: donators[this.id] ? 50 | donators[this.id] : (patronLevel == 1 ? 1 : 5) } : false; 51 | } 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /models/general/Moderation.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const Database = require('./../../structures/PostgreSQL.js'); 4 | 5 | const Moderation = Database.db.define('punishments', { 6 | case: Sequelize.INTEGER, 7 | guildID: Sequelize.STRING, 8 | userID: Sequelize.STRING, 9 | time: Sequelize.STRING, 10 | type: Sequelize.STRING, 11 | punishedBy: Sequelize.STRING, 12 | reason: Sequelize.STRING, 13 | duration: Sequelize.STRING, 14 | punishedByName: Sequelize.STRING, 15 | message: Sequelize.JSON 16 | }); 17 | 18 | module.exports = Moderation; 19 | -------------------------------------------------------------------------------- /models/general/Playlist.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const Database = require('./../../structures/PostgreSQL.js'); 4 | 5 | const Playlist = Database.db.define('playlists', { 6 | userID: Sequelize.STRING, 7 | guildID: Sequelize.STRING, 8 | playlistID: Sequelize.STRING, 9 | playlistName: Sequelize.STRING, 10 | playlistAvatar: Sequelize.STRING, 11 | playlistDescription: Sequelize.STRING, 12 | playlistPrivacy: { 13 | type: Sequelize.INTEGER, 14 | defaultValue: 0, 15 | allowNull: false, 16 | }, 17 | songs: Sequelize.JSON, 18 | stars: Sequelize.JSON, 19 | views: Sequelize.JSON, 20 | }); 21 | 22 | module.exports = Playlist; 23 | -------------------------------------------------------------------------------- /models/general/Settings.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const Database = require('./../../structures/PostgreSQL.js'); 3 | const SettingsModel = Database.db.define('settings', { 4 | guildID: Sequelize.STRING, 5 | prefix: { 6 | type: Sequelize.STRING, 7 | defaultValue: "+" 8 | }, 9 | expletiveFilter: { //NO LONGER AVAILABLE TODO 10 | type: Sequelize.BOOLEAN, 11 | defaultValue: false 12 | }, 13 | spamFilter: { //NO LONGER AVAILABLE TODO 14 | type: Sequelize.BOOLEAN, 15 | defaultValue: false 16 | }, 17 | moderatorRole: { 18 | type: Sequelize.STRING, 19 | defaultValue: "null" 20 | }, 21 | muteRole: { 22 | type: Sequelize.STRING, 23 | defaultValue: "null" 24 | }, 25 | experienceTracking: { //NO LONGER AVAILABLE TODO 26 | type: Sequelize.BOOLEAN, 27 | defaultValue: true 28 | }, 29 | musicDisplayNowPlaying: { 30 | type: Sequelize.BOOLEAN, 31 | defaultValue: false 32 | }, 33 | experienceGainMax: { //NO LONGER AVAILABLE TODO: implement `gain` subcommand in `experience` 34 | type: Sequelize.STRING, 35 | defaultValue: "0" 36 | }, 37 | experienceGainMin: { //NO LONGER AVAILABLE TODO: implement `gain` subcommand in `experience` 38 | type: Sequelize.STRING, 39 | defaultValue: "0" 40 | }, 41 | deleteUserCommand: { 42 | type: Sequelize.BOOLEAN, 43 | defaultValue: true 44 | }, 45 | allowMentions: { //CHANGED 46 | type: Sequelize.BOOLEAN, 47 | defaultValue: false 48 | }, 49 | levelUpMessages: { 50 | type: Sequelize.BOOLEAN, 51 | defaultValue: false 52 | }, 53 | leaveMessage: { //NO LONGER AVAILABLE TODO: create `leave` command 54 | type: Sequelize.STRING, 55 | defaultValue: "Goodbye!" 56 | }, 57 | joinMessage: { //NO LONGER AVAILABLE TODO: create `welcome` command 58 | type: Sequelize.STRING, 59 | defaultValue: "Hello!" 60 | }, 61 | //new stuff starts here 62 | punishConfirmation: { //CHANGED 63 | type: Sequelize.BOOLEAN, 64 | defaultValue: true 65 | }, 66 | dmPunishments: { //CHANGED 67 | type: Sequelize.BOOLEAN, 68 | defaultValue: false 69 | }, 70 | adminRole: { //CHANGED 71 | type: Sequelize.STRING, 72 | defaultValue: "null" 73 | }, 74 | djRole: { //CHANGED 75 | type: Sequelize.STRING, 76 | defaultValue: "null" 77 | } 78 | }); 79 | 80 | module.exports = SettingsModel; 81 | -------------------------------------------------------------------------------- /models/general/Shop.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const Database = require('./../../structures/PostgreSQL.js'); 4 | 5 | const Shop = Database.db.define('shop', { 6 | id: { 7 | type: Sequelize.STRING, 8 | primaryKey: true 9 | }, 10 | name: { 11 | type: Sequelize.STRING, 12 | unique: true, 13 | }, 14 | cost: { 15 | type: Sequelize.INTEGER, 16 | allowNull: false, 17 | default: 0 18 | }, 19 | description: Sequelize.STRING, 20 | type: Sequelize.STRING 21 | }, { 22 | timestamps: false, 23 | }); 24 | 25 | module.exports = Shop; 26 | -------------------------------------------------------------------------------- /models/general/Tags.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const Database = require('./../../structures/PostgreSQL.js'); 4 | 5 | const Tags = Database.db.define('tags', { 6 | name: Sequelize.STRING, 7 | description: Sequelize.STRING, 8 | userID: Sequelize.STRING, 9 | guildID: Sequelize.STRING, 10 | usage_count: { 11 | type: Sequelize.INTEGER, 12 | defaultValue: 0, 13 | allowNull: false, 14 | }, 15 | }); 16 | 17 | module.exports = Tags; 18 | -------------------------------------------------------------------------------- /models/general/Timer.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const Database = require('./../../structures/PostgreSQL.js'); 4 | 5 | const Timer = Database.db.define('timer', { 6 | timerID: Sequelize.STRING, 7 | userID: Sequelize.STRING, 8 | guildID: Sequelize.STRING, 9 | reason: { 10 | type: Sequelize.STRING, 11 | defaultValue: "" 12 | }, 13 | timerEnd: Sequelize.STRING, 14 | timeSpecified: Sequelize.STRING 15 | }); 16 | 17 | module.exports = Timer; 18 | -------------------------------------------------------------------------------- /models/guild/Guild.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const Database = require('./../../structures/PostgreSQL.js'); 4 | const Guild = Database.db.define('guild', { 5 | id: { 6 | type: Sequelize.STRING, 7 | primaryKey: true 8 | }, 9 | autoroles: { 10 | type: Sequelize.JSON, 11 | defaultValue: { 12 | roles: [] //{role: {id: STRING, name: STRING }, level: INTEGER, removeOnLevelChange: BOOLEAN, onJoin: BOOLEAN } 13 | } 14 | }, 15 | disable: { 16 | type: Sequelize.JSON, 17 | defaultValue: { 18 | commands: [] //{command: STRING, channelID: STRING, guildID: STRING} 19 | } 20 | }, 21 | filter: { 22 | type: Sequelize.JSON, 23 | defaultValue: { 24 | words: [], //just an array of STRINGS 25 | level: 0 26 | } 27 | }, 28 | logs: { 29 | type: Sequelize.JSON, 30 | defaultValue: { 31 | message: {}, 32 | member: {}, 33 | moderation: {}, 34 | other: {} 35 | } 36 | }, 37 | ignore: { 38 | type: Sequelize.JSON, 39 | defaultValue: { 40 | modules: [] //{module: STRING, channelID: STRING, guildID: STRING} 41 | } 42 | }, 43 | autoresponses: { 44 | type: Sequelize.JSON, 45 | defaultValue: { 46 | responses: [] //{trigger: STRING, response: STRING} 47 | } 48 | } 49 | }); 50 | 51 | module.exports = Guild; 52 | -------------------------------------------------------------------------------- /models/user/User.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const { CurrencySettings } = require('./../../util/Constants.js'); 3 | const Database = require('./../../structures/PostgreSQL.js'); 4 | const User = Database.db.define('user', { 5 | userID: Sequelize.STRING, 6 | username: Sequelize.STRING, 7 | discriminator: Sequelize.STRING, 8 | guildID: Sequelize.STRING, 9 | experience: { 10 | type: Sequelize.BIGINT(), // eslint-disable-line new-cap 11 | defaultValue: 0 12 | }, 13 | balance: { 14 | type: Sequelize.BIGINT(), // eslint-disable-line new-cap 15 | defaultValue: CurrencySettings.startingBalance, 16 | allowNull: false 17 | }, 18 | background: { 19 | type: Sequelize.STRING, 20 | defaultValue: "default" 21 | }, 22 | role: { 23 | type: Sequelize.JSON, 24 | defaultValue: { 25 | name: "@everyone", 26 | color: "000000" 27 | } 28 | }, 29 | weapon: { 30 | type: Sequelize.STRING, 31 | defaultValue: "wooden" 32 | }, 33 | afk: { 34 | type: Sequelize.JSON, 35 | defaultValue: { 36 | afk: false, 37 | reason: null 38 | } 39 | }, 40 | inventory: { 41 | type: Sequelize.JSON, 42 | defaultValue: { 43 | slots: [], 44 | badges: [], //whatever is in this array doesn't matter, in the badges structure we will check if any of the values match one of the badges made in the structure 45 | //be something like {name: "badge", stats: 0, level: 0} 46 | items: [] //this is just a string of an item (like "oceansunset" or "html") 47 | } 48 | }, 49 | daily: { 50 | type: Sequelize.JSON, 51 | defaultValue: { 52 | streak: 0, 53 | received: null 54 | } 55 | }, 56 | donator: { 57 | type: Sequelize.JSON, 58 | defaultValue: { 59 | patron: false, 60 | redeemed: false 61 | } 62 | }, 63 | work: { 64 | type: Sequelize.JSON, 65 | defaultValue: { 66 | hoursWorked: 0.5, 67 | salary: 11, 68 | lastWorked: null 69 | } 70 | } 71 | }); 72 | 73 | module.exports = User; 74 | -------------------------------------------------------------------------------- /structures/general/LavalinkClient.js: -------------------------------------------------------------------------------- 1 | const snekfetch = require("snekfetch"); 2 | const { PlayerManager } = require("discord.js-lavalink"); 3 | 4 | class LavalinkClient extends PlayerManager { 5 | 6 | constructor(...args) { 7 | super(...args); 8 | 9 | this.defaultRegions = { 10 | asia: ["sydney", "singapore", "japan", "hongkong"], 11 | eu: ["london", "frankfurt", "amsterdam", "russia", "eu-central", "eu-west"], 12 | us: ["us-central", "us-west", "us-east", "us-south", "brazil"] 13 | }; 14 | } 15 | 16 | /** 17 | * Search for tracks from lavalink rest api 18 | * @param {string} search Search query 19 | * @returns {Promise>} 20 | */ 21 | resolveTracks(search) { 22 | return snekfetch.get(`${this.options.rest.url}/loadtracks`) 23 | .set("Authorization", this.options.rest.password) 24 | .query({ identifier: search }) 25 | .then(res => res.body ? res.body : null) 26 | .catch(error => { 27 | this.client.util.log(error, "critical"); 28 | return null; 29 | }); 30 | } 31 | 32 | getIdealRegion(region) { 33 | region = region.replace("vip-", ""); 34 | for (const key in this.defaultRegions) { 35 | const nodes = this.nodes.filter(node => node.ready && node.region === key); 36 | if (!nodes) continue; 37 | for (const id of this.defaultRegions[key]) { 38 | if (id === region || region.startsWith(id) || region.includes(id)) return key; 39 | } 40 | } 41 | return this.nodes.first().region; 42 | } 43 | 44 | getIdealHost(region) { 45 | region = this.getIdealRegion(region); 46 | const foundNode = this.nodes.find(node => node.ready && node.region === region); 47 | return foundNode ? foundNode.host : this.nodes.first().host; 48 | } 49 | 50 | } 51 | 52 | module.exports = LavalinkClient; 53 | -------------------------------------------------------------------------------- /structures/general/Logs.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | const fs = require('fs'); 3 | const colors = require('colors'); 4 | const Settings = require('./Settings.js'); 5 | 6 | class Logs { 7 | static logging(guild, log) { 8 | 9 | } 10 | 11 | static get logObject() { 12 | return { 13 | message: { 14 | all: {enabled: false, channels: []}, 15 | modules: { 16 | edit: {enabled: true, channels: []}, 17 | delete: {enabled: true, channels: []}, 18 | expletive: {enabled: false, channels: []}, 19 | spam: {enabled: false, channels: []} 20 | } 21 | }, 22 | member: { 23 | all: {enabled: false, channels: []}, 24 | modules: { 25 | join: {enabled: false, channels: []}, 26 | leave: {enabled: false, channels: []} 27 | } 28 | }, 29 | moderation: { 30 | all: {enabled: false, channels: []}, 31 | modules: { 32 | voice: {enabled: false, channels: []}, 33 | punishments: {enabled: true, channels: []}, 34 | edit_punishments: {enabled: false, channels: []}, 35 | delete_punishments: {enabled: false, channels: []}, 36 | commands: {enabled: false, channels: []}, 37 | member_join: {enabled: false, channels: []}, 38 | member_leave: {enabled: false, channels: []}, 39 | member_update: {enabled: false, channels: []} 40 | } 41 | }, 42 | other: { 43 | enabled: {enabled: false, channels: []}, 44 | modules: {} 45 | } 46 | } 47 | } 48 | 49 | static grabLoggingSettings() { 50 | //this would be similar to how to we grab info using playlists and autoresponses 51 | } 52 | 53 | } 54 | 55 | module.exports = Logs; 56 | -------------------------------------------------------------------------------- /structures/general/Song.js: -------------------------------------------------------------------------------- 1 | class Song { 2 | constructor(data, requester) { 3 | Object.defineProperty(this, "data", { value: data }); 4 | 5 | this.requester = requester; 6 | this.track = data.track; 7 | this.title = data.info.title; 8 | this.url = data.info.uri; 9 | this.id = data.info.identifier; 10 | this.seekable = data.info.isSeekable; 11 | this.author = data.info.author; 12 | this.duration = data.info.isStream ? 0 : data.info.length; 13 | this.stream = data.info.isStream; 14 | this.position = data.info.position; 15 | this.skips = new Set(); 16 | } 17 | 18 | toString() { 19 | return `${this.title} (${this.url})`; 20 | } 21 | 22 | JSONify() { 23 | return { 24 | requester: this.requester ? this.requester.toJSON() : null, 25 | track: this.track, 26 | title: this.title, 27 | url: this.url, 28 | id: this.id, 29 | seekable: this.seekable, 30 | author: this.author, 31 | duration: this.duration, 32 | friendlyDuration: this.friendlyDuration, 33 | stream: this.stream, 34 | position: this.position 35 | }; 36 | } 37 | 38 | } 39 | 40 | module.exports = Song; 41 | -------------------------------------------------------------------------------- /structures/general/Tags.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | const fs = require('fs'); 3 | const colors = require('colors'); 4 | const Settings = require('./Settings.js'); 5 | const Find = require('./../internal/Find.js'); 6 | const Experience = require('./../user/Experience.js'); 7 | 8 | class Tags { 9 | static async autoresponseVariables(message, raw) { 10 | var obj = { 11 | "%{user}": message.author, 12 | "%{username}": message.author.username, 13 | "%{tag}": message.author.tag, 14 | "%{displayname}": message.member.displayName, 15 | "%{discriminator}": message.author.discriminator, 16 | "%{userid}": message.author.id, 17 | "%{avatar}": message.author.displayAvatarURL(), 18 | "%{bot}": message.author.bot, 19 | "%{totalexp}": await message.member.experience, 20 | "%{currentexp}": await Experience.getCurrentExperience(message.member), 21 | "%{level}": await Experience.getLevel(message.author.id, message.guild.id), 22 | "%{remainingexp}": Experience.getLevelBounds(await Experience.getLevel(message.member)).upperBound - Experience.getLevelBounds(await Experience.getLevel(message.member)).lowerBound, 23 | "%{text}": message.content 24 | }; 25 | if (raw.match(/(%{random:.+?})/gi)) { 26 | Tags.createRandom(raw) 27 | } 28 | return obj; 29 | } 30 | 31 | static createRandom(raw) { 32 | let dataArray = raw.split(/([^%{|}]+)/gi).slice(1) //random:ha yes|ha no 33 | if (dataArray[0].startsWith("random:")) dataArray[0] = dataArray[0].replace("random:", "") 34 | for (let i in dataArray) { 35 | if (i%2) dataArray.splice(i); 36 | if (dataArray.indexOf("{")) dataArray.splice(i, -1) 37 | } 38 | return raw.replace(/(%{random:.+?})/gi, dataArray[Math.floor(Math.random()*dataArray.length)]) 39 | } 40 | 41 | 42 | static async cleanString(string, message) { 43 | if (await Settings.getValue(message.guild, "allowMentions")) return string; 44 | if (!string) return string; 45 | return string 46 | .replace(/@(everyone|here)/g, '@\u200b$1') 47 | .replace(/<@!?[0-9]+>/g, input => { 48 | const id = input.replace(/<|!|>|@/g, ''); 49 | if (message.channel.type === 'dm' || message.channel.type === 'group') { 50 | return client.users.has(id) ? `@${client.users.get(id).username}` : input; 51 | } 52 | 53 | const member = message.channel.guild.members.get(id); 54 | if (member) { 55 | if (member.nickname) return `@${member.nickname}`; 56 | return `@${member.user.username}`; 57 | } else { 58 | const user = client.users.get(id); 59 | if (user) return `@${user.username}`; 60 | return input; 61 | } 62 | }) 63 | .replace(/<#[0-9]+>/g, input => { 64 | const channel = client.channels.get(input.replace(/<|#|>/g, '')); 65 | if (channel) return `#${channel.name}`; 66 | return input; 67 | }) 68 | .replace(/<@&[0-9]+>/g, input => { 69 | if (message.channel.type === 'dm' || message.channel.type === 'group') return input; 70 | const role = message.guild.roles.get(input.replace(/<|@|>|&/g, '')); 71 | if (role) return `@${role.name}`; 72 | return input; 73 | }); 74 | } 75 | } 76 | 77 | module.exports = Tags; 78 | -------------------------------------------------------------------------------- /structures/internal/Permissions.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | const fs = require('fs'); 3 | const colors = require('colors'); 4 | const Settings = require('./../general/Settings.js'); 5 | const User = require('./../../models/user/User.js') 6 | const Moderation = require('./../../models/general/Moderation.js') 7 | const Tags = require('./../../models/general/Tags.js') 8 | 9 | var developerOverrides = true; 10 | var dev = ["246574843460321291"] 11 | 12 | class Permissions { 13 | static isDeveloper(user) { 14 | return dev.includes(user.id) 15 | } 16 | 17 | static get developerOverrides() { 18 | return Settings.doesDeveloperOverride(); 19 | } 20 | 21 | static isOwner(user, guild) { 22 | return user.id == guild.ownerID 23 | } 24 | 25 | static async isModerator(member, guild) { 26 | let moderatorRole = await Settings.getValue(guild, "moderatorRole") 27 | const role = guild.roles.get(moderatorRole) 28 | if (!moderatorRole || !guild.roles.has(moderatorRole) || !role) return false; 29 | return member.roles.highest.comparePositionTo(role) >= 0; 30 | } 31 | 32 | static async isAdministrator(member, guild) { 33 | let adminRole = await Settings.getValue(guild, "adminRole") 34 | const role = guild.roles.get(adminRole) 35 | if (!adminRole || !guild.roles.has(adminRole) || !role) return false; 36 | return member.roles.highest.comparePositionTo(role) >= 0; 37 | } 38 | 39 | static async grabPermissionLevel(user, guild, permission) { 40 | let moderatorRole = await Settings.getValue(guild, "moderatorRole") 41 | let adminRole = await Settings.getValue(guild, "adminRole") 42 | let roles = { moderator: { role: moderatorRole, exists: guild.roles.has(moderatorRole)}, 43 | admin: { role: adminRole, exists: guild.roles.has(adminRole) }} 44 | 45 | if (user.developer) return 4; 46 | if (Permissions.isOwner(user, guild)) return 3; 47 | 48 | const member = await guild.members.fetch(user.id); 49 | if (!roles.admin.exists ? member.hasPermission(permission, true, true) : 50 | await Permissions.isModerator(member, guild)) return 2; 51 | if (!roles.moderator.exists ? member.hasPermission(permission, true, true) : await Permissions.isAdministrator(member, guild)) return 1; 52 | 53 | return 0; 54 | } 55 | } 56 | 57 | module.exports = Permissions; 58 | -------------------------------------------------------------------------------- /structures/moderation/Spam.js: -------------------------------------------------------------------------------- 1 | const Discord = require("discord.js"); 2 | const events = require('events'); 3 | const commandEmitter = new events.EventEmitter(); 4 | const Settings = require('./../general/Settings.js'); 5 | const Guild = require("./../../models/guild/Guild.js") 6 | 7 | let lastMessages = {}; 8 | let sameMessageCount = {}; 9 | 10 | /* 11 | 12 | When asking the question "what is the most efficient way to detect spammers without disrupting normal users", 13 | we need to think about what the best formula is for checking if someone is spamming. To do that, we need to ask 14 | what "spam" really is 15 | 16 | In zBot, "spamming" is defined as; 17 | * repeating a certain word, line or phrase (similar or exactly the same) multiple times over a short period of time 18 | 19 | Here are the rules for a spammer to be caught by zBot; 20 | [SPAM TYPE 1: Message Spam (for spamming the same or very similar messages)] 21 | [CHECK 1] User needs to have sent the same or similar message more than once 22 | [CHECK 2] Check 1 needs to have happened at least 3 times and within a time interval of 5 seconds 23 | 24 | [SPAM TYPE 2: Fast Spam (specifically designed for detecting users that are posting messages at an alarmingly quick rate, regardless of message content)] 25 | [CHECK 1] User needs to have sent at least 4 messages within the past 3.5 seconds 26 | 27 | [SPAM TYPE 3: Flood Spam (designed to catch spam that is intentionally trying to flood/cover a large amount of the chat)] 28 | [CHECK 1] If more than 80% of the message content 29 | */ 30 | 31 | module.exports = {}; 32 | -------------------------------------------------------------------------------- /structures/user/Experience.js: -------------------------------------------------------------------------------- 1 | class Experience { 2 | static async getTotalExperience(member) { 3 | const userProfile = (await member.profile) 4 | if (!userProfile) return 0; 5 | return userProfile.get("experience") || 0; 6 | } 7 | 8 | static async getCurrentExperience(member) { 9 | const level = await Experience.getLevel(member); 10 | let totalXP = await Experience.getTotalExperience(member) 11 | const { lowerBound } = Experience.getLevelBounds(level); 12 | return totalXP - lowerBound; 13 | } 14 | 15 | static getLevelBounds(level) { 16 | const upperBound = Math.ceil((level / 0.177) ** 2); 17 | const lowerBound = Math.ceil(((level - 1) / 0.177) ** 2); 18 | 19 | return { upperBound, lowerBound }; 20 | } 21 | 22 | static async getLevel(member) { 23 | const exp = await Experience.getTotalExperience(member) 24 | return Math.floor(0.177 * Math.sqrt(exp)) + 1; 25 | } 26 | 27 | static getLevelFromExp(exp) { 28 | return Math.floor(0.177 * Math.sqrt(exp)) + 1; 29 | } 30 | } 31 | 32 | module.exports = Experience; 33 | -------------------------------------------------------------------------------- /structures/user/Inventory.js: -------------------------------------------------------------------------------- 1 | const User = require("./../../models/user/User.js") 2 | const Constants = require("./../../util/Constants.js") 3 | const Weapon = require("./../games/Weapon.js") 4 | const Experience = require("./Experience.js") 5 | 6 | class Inventory { 7 | constructor(member) { 8 | this.member = member; 9 | } 10 | 11 | async check() { 12 | let user = (await this.member.profile); 13 | let inventory = user.get("inventory"); 14 | if (!inventory.items) await this.member.fix; 15 | } 16 | 17 | async exists(item) { 18 | for (let [key, value] of Object.values(Constants.Items)) { 19 | for (let i of key) { 20 | if (item.id == key[i].id) return true; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | async has(item) { 27 | let user = (await this.member.profile); 28 | let inventory = user.get("inventory"); 29 | if (!inventory.items) { 30 | await this.member.fix; 31 | user = (await this.member.profile); 32 | inventory = user.get("inventory"); 33 | } 34 | let items = inventory.items; 35 | for (let i in items) { 36 | if (items[i].id === item.id) { 37 | return true; 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | get all() { 44 | return (async () => { 45 | let user = (await this.member.profile); 46 | let inventory = user.get("inventory"); 47 | if (!inventory.items) { 48 | await this.member.fix; 49 | user = (await this.member.profile); 50 | inventory = user.get("inventory"); 51 | } 52 | let items = []; 53 | let update = false; 54 | for (let i in inventory.items) { 55 | if (inventory.items[i].dataValues) { 56 | update = true; 57 | inventory.items[i] = inventory.items[i].dataValues; 58 | } 59 | items.push(inventory.items[i]) 60 | } 61 | if (update) await this.member.update("inventory", items); 62 | return items 63 | })(); 64 | } 65 | 66 | async add(item) { 67 | if (await this.has(item)) return; 68 | let user = (await this.member.profile); 69 | let inventory = user.get("inventory"); 70 | if (!inventory.items) { 71 | await this.member.fix; 72 | user = (await this.member.profile); 73 | inventory = user.get("inventory"); 74 | } 75 | if (item.constructor === Array) { 76 | for (let i in item) { 77 | inventory.items.push(item[i]); 78 | } 79 | } else { 80 | inventory.items.push(item); 81 | } 82 | const changed = await this.member.update("inventory", inventory); 83 | return changed; 84 | } 85 | 86 | async remove(item) { 87 | if (await !this.has(item)) return; 88 | let user = (await this.member.profile); 89 | let inventory = user.get("inventory"); 90 | if (!inventory.items) { 91 | await this.member.fix; 92 | user = (await this.member.profile); 93 | inventory = user.get("inventory"); 94 | } 95 | if (item.constructor === Array) { 96 | for (let i in item) { 97 | inventory.items = inventory.items.filter(o => o.id !== item[i].id); 98 | } 99 | } else { 100 | inventory.items = inventory.items.filter(o => o.id !== item.id); 101 | } 102 | const changed = await this.member.update("inventory", inventory); 103 | return changed; 104 | } 105 | } 106 | 107 | module.exports = Inventory; 108 | -------------------------------------------------------------------------------- /util/Emoji.js: -------------------------------------------------------------------------------- 1 | module.exports = function(type, guild) { 2 | const emojis = { 3 | nope: {name: "nope", id: {external: "<:xmark:314349398824058880>", internal: ":no_entry_sign:"} }, 4 | ok: {name: "ok", id: {external: "<:check:314349398811475968>", internal: ":white_check_mark:"} }, 5 | success: {name: "success", id: {external: "<:check:314349398811475968>", internal: ":white_check_mark:"} }, 6 | info: {name: "info", id: {external: "<:info:374192576053379082>", internal: ":information_source:"} }, 7 | warning: {name: "warning", id: {external: "<:warning:401340421478809600>", internal: ":warning:"} }, 8 | empty: {name: "empty", id: {external: "<:empty:314349398723264512>", internal: ":large_orange_diamond:"} }, 9 | incomplete: {name: "incomplete", id: {external: "<:empty:314349398723264512>", internal: ":large_orange_diamond:"} }, 10 | bot: {name: "bot", id: {external: "<:botTag:230105988211015680>", internal: "[BOT]"} }, 11 | online: {name: "online", id: {external: "<:online:313956277808005120>", internal: ":white_small_square:"} }, 12 | offline: {name: "offline", id: {external: "<:offline:313956277237710868>", internal: ":black_small_square:"} }, 13 | idle: {name: "idle", id: {external: "<:away:313956277220802560>", internal: ":small_orange_diamond:"} }, 14 | dnd: {name: "dnd", id: {external: "<:dnd:313956276893646850>", internal: ":small_red_triangle_down:"} }, 15 | challenge: {name: "challenge", id: {external: "<:swordsemoji:373752217628442635>", internal: ":crossed_swords:"} }, 16 | edit: {name: "edit", id: {external: "<:edit:383748366817034241>", internal: ":pencil:"} }, 17 | bin: {name: "bin", id: {external: "<:bin:383899733128773635>", internal: ":wastebasket:"} }, 18 | nitro: {name: "nitro", id: {external: "<:nitro:314068430611415041>", internal: "[NITRO]"} }, 19 | loading: {name: "loading", id: {external: "", internal: "❖"} }, 20 | typing: {name: "typing", id: {external: "", internal: ":large_orange_diamond:"} }, 21 | donator: {name: "donator", id: {external: "<:donator:458907386204127244>", internal: ":moneybag:"}} 22 | } 23 | 24 | const useExternal = (process.argv[2] !== "--beta" ? (guild.me == null ? false : guild.me.hasPermission("USE_EXTERNAL_EMOJIS")) : false); 25 | return useExternal ? emojis[type].id.external : emojis[type].id.internal; 26 | } 27 | -------------------------------------------------------------------------------- /util/GuildMemberAdd/index.js: -------------------------------------------------------------------------------- 1 | const processors = require(`${__dirname}/processors`); 2 | 3 | module.exports = class { 4 | constructor(client) { 5 | this.client = client; 6 | } 7 | 8 | async run(guildMember) { 9 | const client = this.client; 10 | if (guildMember == guildMember.guild.me) return; 11 | if (!guildMember) return; 12 | 13 | for (const processor of Object.values(processors)) { 14 | processor.call(this, guildMember); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /util/GuildMemberAdd/processors/autorole.js: -------------------------------------------------------------------------------- 1 | const Guild = require("./../../../models/guild/Guild.js"); 2 | 3 | module.exports = async function autorole(guildMember) { 4 | const guild = Guild.findOne({where: {id: guildMember.guild.id}}) 5 | 6 | //AUTOROLE MANAGER 7 | if (!guild) return; 8 | const guildroles = guild.autoroles 9 | if (guildroles) { 10 | const manageAutorole = guildroles.roles 11 | if (manageAutorole.length > 0) autorole(); 12 | function autorole() { 13 | manageAutorole.map((level, i) => { 14 | let autoroleObject = manageAutorole[i] 15 | if (autoroleObject.onJoin) { 16 | if (autoroleObject.removeOnLevelChange == null) autoroleObject.removeOnLevelChange = false; 17 | if (!guildMember.guild.roles.has(autoroleObject.role.id)) return; 18 | if (guildMember.roles.has(autoroleObject.role.id)) return; 19 | guildMember.roles.add(autoroleObject.role.id); 20 | } 21 | }); 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /util/GuildMemberAdd/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /util/GuildMemberAdd/processors/memberlog.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Discord = require("discord.js"); 3 | 4 | module.exports = async function memberlog(guildMember) { 5 | //Grab the channels 6 | const channelsMember = await Settings.grabLoggingChannels(guildMember.guild, "member", "join"); 7 | if (channelsMember) performJoinMessage(channelsMember); 8 | 9 | async function performJoinMessage(channels) { 10 | let randomjoin = ""; 11 | switch (Math.floor(Math.random() * 1000) % 7) { 12 | case 0: 13 | randomjoin = "Please give them a warm welcome!"; 14 | break; 15 | case 1: 16 | randomjoin = "Thanks for joining, and we hope you enjoy your stay!"; 17 | break; 18 | case 2: 19 | randomjoin = "Thanks for joining us!"; 20 | break; 21 | case 3: 22 | randomjoin = "It's great to have you here!"; 23 | break; 24 | case 4: 25 | randomjoin = "It's a pleasure to have you here."; 26 | break; 27 | case 5: 28 | randomjoin = "Hope you have had a great day so far!"; 29 | break; 30 | case 6: 31 | randomjoin = "Nice to see you!"; 32 | break; 33 | } 34 | 35 | let msg = ""; 36 | if (!await Settings.getValue(guildMember.guild, "joinMessage")) { 37 | msg = "**" + guildMember + "** has joined our awesome server! *" + randomjoin + "*"; 38 | } else { 39 | let joinMessage = await Settings.getValue(guildMember.guild, "joinMessage") 40 | let translate = { 41 | "%{user}": guildMember, 42 | "%{username}": guildMember.user.username, 43 | "%{tag}": guildMember.user.tag, 44 | "%{displayname}": guildMember.user.displayName, 45 | "%{discriminator}": guildMember.user.discriminator, 46 | "%{userid}": guildMember.id, 47 | "%{avatar}": guildMember.user.displayAvatarURL() 48 | }; 49 | for (let key in translate) { 50 | joinMessage = joinMessage.replace(new RegExp(key, "ig"), translate[key]); 51 | } 52 | msg = "​" + joinMessage.toString(); 53 | } 54 | 55 | //Send the log to all the channel(s) 56 | for (let i in channels) { 57 | if (await Settings.checkIgnore(guildMember.guild, channels[i], "logs")) continue; 58 | if (!guildMember.guild.channels.has(channels[i])) continue; 59 | guildMember.guild.channels.get(channels[i]).send({ msg }); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /util/GuildMemberAdd/processors/moderationlog.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Discord = require("discord.js"); 3 | const moment = require("moment") 4 | 5 | module.exports = async function moderationlog(guildMember) { 6 | const channelsMod = await Settings.grabLoggingChannels(guildMember.guild, "moderation", "member_join"); 7 | if (channelsMod) { 8 | const embed = new Discord.MessageEmbed(); 9 | embed.setAuthor("Member Joined » " + guildMember.user.tag, (guildMember.user.displayAvatarURL().toString().endsWith(".gif") ? guildMember.user.displayAvatarURL() : guildMember.user.displayAvatarURL({size: 2048}))); 10 | embed.setColor("#39cc45"); 11 | embed.setDescription(":wave: <@" + guildMember.id + "> has joined " + guildMember.guild.name + ".\n"); 12 | let msg = moment(`${guildMember.user.createdAt.toLocaleDateString()} ${guildMember.user.createdAt.toLocaleTimeString()}`, "YYYY-MM-DD h:mm:ss a").format("Do [of] MMMM, YYYY [at] h:mm:ss a") + " (" + moment(`${guildMember.user.createdAt.toLocaleDateString()} ${guildMember.user.createdAt.toLocaleTimeString()}`).fromNow() + ")"; 13 | embed.addField("**User Created**", msg); 14 | embed.setFooter("For more information on this user, type " + await Settings.getValue(guildMember.guild, "prefix") + "uinfo " + guildMember.user.username + "."); 15 | 16 | //Send the log to all the channel(s) 17 | for (let i in channelsMod) { 18 | if (await Settings.checkIgnore(guildMember.guild, channelsMod[i], "logs")) continue; 19 | if (!guildMember.guild.channels.has(channelsMod[i])) continue; 20 | guildMember.guild.channels.get(channelsMod[i]).send({ embed }); 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /util/GuildMemberRemove/index.js: -------------------------------------------------------------------------------- 1 | const processors = require(`${__dirname}/processors`); 2 | 3 | module.exports = class { 4 | constructor(client) { 5 | this.client = client; 6 | } 7 | 8 | async run(guildMember) { 9 | const client = this.client; 10 | if (guildMember == guildMember.guild.me) return; 11 | if (!guildMember) return; 12 | 13 | for (const processor of Object.values(processors)) { 14 | processor.call(this, guildMember); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /util/GuildMemberRemove/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /util/GuildMemberRemove/processors/memberlog.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Discord = require("discord.js"); 3 | 4 | module.exports = async function memberlog(guildMember) { 5 | //Grab the channels 6 | const channelsMember = await Settings.grabLoggingChannels(guildMember.guild, "member", "leave"); 7 | if (channelsMember) performLeaveMessage(); 8 | 9 | async function performLeaveMessage() { 10 | let randomleave = ""; 11 | switch (Math.floor(Math.random() * 1000) % 4) { 12 | case 0: 13 | randomleave = "See you later!"; 14 | break; 15 | case 1: 16 | randomleave = "Goodbye!"; 17 | break; 18 | case 2: 19 | randomleave = "Bye!"; 20 | break; 21 | case 3: 22 | randomleave = "Hope to see you again!"; 23 | break; 24 | } 25 | 26 | let msg = ""; 27 | if (!await Settings.getValue(guildMember.guild, "leaveMessage")) { 28 | msg = "**" + guildMember + "** has left the server. *" + randomleave + "*"; 29 | } else { 30 | let leaveMessage = await Settings.getValue(guildMember.guild, "leaveMessage") 31 | if (!leaveMessage) return; 32 | let translate = { 33 | "%{user}": guildMember, 34 | "%{username}": guildMember.user.username, 35 | "%{tag}": guildMember.user.tag, 36 | "%{displayname}": guildMember.user.displayName, 37 | "%{discriminator}": guildMember.user.discriminator, 38 | "%{userid}": guildMember.id, 39 | "%{avatar}": guildMember.user.displayAvatarURL() 40 | }; 41 | for (let key in translate) { 42 | leaveMessage = leaveMessage.replace(new RegExp(key, "ig"), translate[key]); 43 | } 44 | msg = "​" + leaveMessage.toString(); 45 | } 46 | 47 | //Send the log to all the channel(s) 48 | for (let i in channelsMember) { 49 | if (await Settings.checkIgnore(guildMember.guild, channelsMember[i], "logs")) continue; 50 | if (!guildMember.guild.channels.has(channelsMember[i])) continue; 51 | guildMember.guild.channels.get(channelsMember[i]).send({ msg }); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /util/GuildMemberRemove/processors/moderationlog.js: -------------------------------------------------------------------------------- 1 | const Punish = require("./../../../structures/moderation/Punish.js") 2 | const Settings = require("./../../../structures/general/Settings.js") 3 | const Discord = require("discord.js"); 4 | const moment = require("moment") 5 | 6 | module.exports = async function moderationlog(guildMember) { 7 | //Grab the channels 8 | const channelsMod = await Settings.grabLoggingChannels(guildMember.guild, "moderation", "member_leave"); 9 | 10 | async function leave() { 11 | const embed = new Discord.MessageEmbed(); 12 | embed.setAuthor("Member Left » " + guildMember.user.tag, guildMember.user.displayAvatarURL()); 13 | embed.setColor("#f44336"); 14 | embed.setDescription(":wave: <@" + guildMember.id + "> has left " + guildMember.guild.name + ".\n"); 15 | let msg = 0; 16 | if (guildMember.joinedAt) { 17 | msg = moment(`${guildMember.joinedAt.toLocaleDateString()} ${guildMember.joinedAt.toLocaleTimeString()}`, "YYYY-MM-DD h:mm:ss a").format("Do [of] MMMM, YYYY [at] h:mm:ss a") + " (" + moment(`${guildMember.joinedAt.toLocaleDateString()} ${guildMember.joinedAt.toLocaleTimeString()}`).fromNow() + ")"; 18 | if (msg === "Invalid date") { msg = `${guildMember.joinedAt.toLocaleDateString()} at ${guildMember.joinedAt.toLocaleTimeString()}`}; 19 | } 20 | embed.addField("User Joined", msg || "Unknown"); 21 | embed.setTimestamp(new Date()); 22 | 23 | //Send the log to all the channel(s) 24 | for (let i in channelsMod) { 25 | if (await Settings.checkIgnore(guildMember.guild, channelsMod[i], "logs")) continue; 26 | if (!guildMember.guild.channels.has(channelsMod[i])) continue; 27 | guildMember.guild.channels.get(channelsMod[i]).send({ embed }); 28 | } 29 | 30 | Settings.removeMember(guildMember.guild, guildMember) 31 | return embed; 32 | } 33 | } -------------------------------------------------------------------------------- /util/GuildMemberRemove/processors/punishment.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Punish = require("./../../../structures/moderation/Punish.js") 3 | 4 | module.exports = async function punishment(guildMember) { 5 | let client = guildMember.guild.client 6 | let executor = client.user 7 | let reason = "*No reason supplied*." 8 | if (guildMember.guild.me.hasPermission("VIEW_AUDIT_LOG")) { 9 | let auditLog = await guildMember.guild.fetchAuditLogs({limit: 1}) 10 | if (auditLog) { 11 | auditLog = auditLog.entries.array()[0]; 12 | executor = auditLog.executor; 13 | reason = auditLog.reason; 14 | 15 | if (auditLog.action === "MEMBER_KICK" && auditLog.target.id === guildMember.id) { 16 | //kick 17 | await Punish.performPunish(guildMember.guild, "kick", executor, guildMember, reason, null) 18 | } 19 | } 20 | } 21 | 22 | const channelsMod = await Settings.grabLoggingChannels(guildMember.guild, "moderation", "member_leave"); 23 | 24 | let recentCase = await Punish.grabCases(guildMember.guild); 25 | let recentCaseObj = await Punish.getCase(recentCase, guildMember.guild) 26 | if (recentCaseObj) { 27 | if ((recentCaseObj.type == "ban" || recentCaseObj.type == "softban" || recentCaseObj.type == "kick") && recentCaseObj.userID == guildMember.id) { 28 | if (channelsMod) { 29 | let type = recentCaseObj.type + "ed"; 30 | if (recentCaseObj.type.includes("ban")) { 31 | type = recentCaseObj.type + "ned" 32 | } 33 | performPunishLeave(type); 34 | } 35 | } 36 | } 37 | 38 | async function performPunishLeave(type) { 39 | const embed = new Discord.MessageEmbed(); 40 | embed.setAuthor("Member Removed » " + guildMember.user.tag, guildMember.user.displayAvatarURL()); 41 | embed.setColor("#f44336"); 42 | embed.setDescription(":wave: <@" + guildMember.id + "> was " + type + " from " + guildMember.guild.name + 43 | (guildMember.guild.members.get(recentCaseObj.punishedBy) ? " by <@" + recentCaseObj.punishedBy + ">.\n" : ".")); 44 | embed.addField("Reason", recentCaseObj.reason); 45 | embed.setTimestamp(new Date()); 46 | 47 | //Send the log to all the channel(s) 48 | for (let i in channelsMod) { 49 | if (await Settings.checkIgnore(guildMember.guild, channelsMod[i], "logs")) continue; 50 | if (!guildMember.guild.channels.has(channelsMod[i])) continue; 51 | guildMember.guild.channels.get(channelsMod[i]).send({ embed }); 52 | } 53 | 54 | Settings.removeMember(guildMember.guild, guildMember) 55 | return; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /util/GuildMemberUpdate/index.js: -------------------------------------------------------------------------------- 1 | const processors = require(`${__dirname}/processors`); 2 | 3 | module.exports = class { 4 | constructor(client) { 5 | this.client = client; 6 | } 7 | 8 | async run(oldMember, newMember) { 9 | const client = this.client; 10 | if (oldMember == newMember) return; 11 | if (!oldMember || !newMember) return; 12 | 13 | for (const processor of Object.values(processors)) { 14 | processor.call(this, oldMember, newMember); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /util/GuildMemberUpdate/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); -------------------------------------------------------------------------------- /util/GuildMemberUpdate/processors/nickname.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Guild = require("./../../../models/guild/Guild.js") 3 | const Discord = require("discord.js") 4 | 5 | module.exports = async function nickname(oldMember, newMember) { 6 | const client = oldMember.guild.client 7 | if (oldMember.user.bot) return; 8 | 9 | let moderator = oldMember; 10 | if (newMember.guild.me.hasPermission("VIEW_AUDIT_LOG")) { 11 | let auditLog = await oldMember.guild.fetchAuditLogs({limit: 1}) 12 | if (auditLog) { 13 | auditLog = auditLog.entries.array()[0]; 14 | moderator = auditLog.executor; 15 | } 16 | } 17 | 18 | if (newMember.nickname != oldMember.nickname) { 19 | //Grab the channels 20 | const guild = Guild.findOne({where: {id: oldMember.guild.id}}) 21 | const channels = await Settings.grabLoggingChannels(oldMember.guild, "moderation", "member_update"); 22 | if (!channels) return; 23 | 24 | const embed = new Discord.MessageEmbed(); 25 | embed.setAuthor("Nickname Change » " + oldMember.user.tag, oldMember.user.displayAvatarURL()); 26 | embed.setColor("#42b3f4"); 27 | if (newMember.nickname != null) { 28 | embed.setDescription(":label: " + (moderator.id !== oldMember.id ? "<@" + moderator.id + ">" + " changed " + "<@" + oldMember.id + ">'s nickname." : "<@" + oldMember.id + "> has changed their nickname.")); 29 | } else { 30 | embed.setDescription(":label: " + (moderator.id !== oldMember.id ? "<@" + moderator.id + ">" + " cleared " + "<@" + oldMember.id + ">'s nickname." : "<@" + oldMember.id + "> has cleared their nickname.") + "\nIt has defaulted to their username.\n"); 31 | } 32 | embed.addField("Previous Nickname", oldMember.displayName); 33 | embed.addField("New Nickname", newMember.displayName); 34 | embed.setTimestamp(new Date()); 35 | 36 | //Send the log to all the channel(s) 37 | for (let i in channels) { 38 | if (await Settings.checkIgnore(oldMember.guild, channels[i], "logs")) continue; 39 | if (!oldMember.guild.channels.has(channels[i])) continue; 40 | oldMember.guild.channels.get(channels[i]).send({ embed }); 41 | } 42 | return; 43 | } 44 | } -------------------------------------------------------------------------------- /util/GuildMemberUpdate/processors/punishment.js: -------------------------------------------------------------------------------- 1 | const Settings = require("./../../../structures/general/Settings.js") 2 | const Punish = require("./../../../structures/moderation/Punish.js") 3 | 4 | module.exports = async function punishment(oldMember, newMember) { 5 | if (oldMember.roles !== newMember.roles) { 6 | let executor = oldMember.guild.client.user 7 | let reason = "*No reason supplied*." 8 | if (newMember.guild.me.hasPermission("VIEW_AUDIT_LOG")) { 9 | let auditLog = await oldMember.guild.fetchAuditLogs({limit: 1}) 10 | if (auditLog) { 11 | auditLog = auditLog.entries.array()[0]; 12 | executor = auditLog.executor; 13 | reason = auditLog.reason; 14 | } 15 | } 16 | 17 | const muteRole = await Settings.getValue(oldMember.guild, "muteRole") 18 | if (!oldMember.roles.has(muteRole) && newMember.roles.has(muteRole)) { 19 | //muted 20 | await Punish.performPunish(oldMember.guild, "mute", executor, newMember, reason, null) 21 | } 22 | if (oldMember.roles.has(muteRole) && !newMember.roles.has(muteRole)) { 23 | //unmuted 24 | await Punish.performPunish(oldMember.guild, "unmute", executor, newMember, reason, null) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /util/Logger.js: -------------------------------------------------------------------------------- 1 | const colors = require("colors"); 2 | 3 | module.exports = function(logMessage, type = "info") { 4 | let logString; 5 | let logFormatting; 6 | switch (type) { 7 | case "debug": 8 | logFormatting = colors.bgMagenta(colors.white("[ DEBUG ]")); 9 | break; 10 | case "info": 11 | logString = colors.white(logMessage); 12 | logFormatting = colors.white("[ INFO ]"); 13 | break; 14 | case "warn": 15 | logString = colors.yellow(logMessage); 16 | logFormatting = colors.bgYellow(colors.black("[ WARNING ]")); 17 | break; 18 | case "critical": 19 | logString = colors.bgRed(colors.white(logMessage)); 20 | logFormatting = colors.bgRed(colors.white("[ CRITICAL ]")); 21 | break; 22 | case "success": 23 | logString = colors.green(logMessage); 24 | logFormatting = colors.bgGreen(colors.black("[ SUCCESS ]")); 25 | break; 26 | default: 27 | logString = colors.white(logMessage); 28 | logFormatting = colors.white("[ INFO ]"); 29 | break; 30 | } 31 | console.log(logFormatting, logString); 32 | } 33 | -------------------------------------------------------------------------------- /util/Message/index.js: -------------------------------------------------------------------------------- 1 | const Settings = require('./../../structures/general/Settings.js'); 2 | const Guild = require("./../../models/guild/Guild.js") 3 | const Discord = require("discord.js") 4 | const colors = require('colors'); 5 | const processors = require(`${__dirname}/processors`); 6 | const Interface = new(require("./../../structures/internal/Interface.js").Interface)(); 7 | const CommandHandler = require("./../../structures/internal/CommandHandler.js") 8 | const Filter = require("./../../structures/moderation/Filter.js").Expletive 9 | 10 | module.exports = class { 11 | constructor(client) { 12 | this.client = client; 13 | } 14 | 15 | async run(message) { 16 | let msg = message.content; 17 | if (message.author.bot) return; 18 | 19 | if (!message.guild) { 20 | this.client.util.log("[DM] " + message.content) 21 | new(require("./../../structures/games/Challenge.js"))(this.client).run(message); 22 | return; 23 | } 24 | 25 | //Ignore 26 | if (await Settings.checkIgnore(message.guild, message.channel, "everything")) return; 27 | 28 | const guild = await Guild.findOne({ where: { id: message.guild.id } }); 29 | if (!guild) { //create a new guild if one is not present 30 | this.client.util.log(colors.bold(colors.green("A new guild was added!")) + colors.blue(" (ID ▪ " + message.guild.id + ")")); 31 | this.client.util.log(colors.bold(colors.green("zBot is now at " + colors.yellow(this.client.guilds.size) + " guilds!"))) 32 | await Settings.fixGuild(this.client.guilds.get(message.guild.id)) 33 | } 34 | 35 | for (const processor of Object.values(processors)) { 36 | processor.call(this, message, guild); 37 | } 38 | 39 | if (message.channel.type === 'text' && !message.channel.permissionsFor(this.client.user).has("SEND_MESSAGES")) return; 40 | this.client.messages.total++ 41 | this.client.messages.characters += message.content.length 42 | 43 | //If not ignored, run a command. 44 | new CommandHandler(this.client).run(message) //CommandHandler 45 | new Filter(this.client).run(message) //Filter 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /util/Message/processors/afk.js: -------------------------------------------------------------------------------- 1 | const cooldowns = new Map(); 2 | 3 | module.exports = async function afk(message) { 4 | //COOLDOWN SYSTEM 5 | if (!cooldowns.has(message.guild.id)) cooldowns.set(message.guild.id, new Map()); 6 | const now = Date.now(); 7 | const timestamps = cooldowns.get(message.guild.id); 8 | const cooldownAmount = 30000; 9 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 10 | if (now < expirationTime) return; 11 | timestamps.set(message.author.id, now); 12 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 13 | //END COOLDOWN 14 | 15 | if (message.mentions.members.first()) { 16 | const user = (await message.mentions.members.first().profile).get("afk") 17 | if (message.author.id == message.mentions.members.first().id) return; 18 | if (user.afk) message.zreply(this.client.util.emoji("info", message.guild) + " **AFK**: " + message.mentions.members.first() 19 | .username + " has marked themselves as AFK" + (user.reason ? " with the reason `" + user.reason + "`." : ".")) 20 | } 21 | }; -------------------------------------------------------------------------------- /util/Message/processors/autoresponse.js: -------------------------------------------------------------------------------- 1 | const cooldowns = new Map(); 2 | 3 | module.exports = async function autoresponse(message, guild) { 4 | const client = message.guild.client; 5 | //COOLDOWN SYSTEM 6 | if (!cooldowns.has(message.guild.id)) cooldowns.set(message.guild.id, new Map()); 7 | const now = Date.now(); 8 | const timestamps = cooldowns.get(message.guild.id); 9 | const cooldownAmount = 2100; 10 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 11 | if (now < expirationTime) return; 12 | timestamps.set(message.author.id, now); 13 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 14 | //END COOLDOWN 15 | 16 | if (guild && guild.autoresponses) { 17 | for (let i in guild.autoresponses.responses) { 18 | if (guild.autoresponses.responses[i].trigger === message.content) { 19 | const Tags = require("./../../../structures/general/Tags.js") 20 | const variables = await Tags.autoresponseVariables(message, guild.autoresponses.responses[i].response) 21 | function translate(txt) { 22 | for (let key in variables) { 23 | txt = txt.replace(new RegExp(key, "ig"), variables[key]); 24 | } 25 | return (txt.charAt(0) === txt.charAt(0).toUpperCase() ? txt.charAt(0).toUpperCase() + txt.slice(1) : txt.charAt(0) + txt.slice(1)) 26 | } 27 | this.client.util.log(`[AUTORESPONSE] Reacting to autoresponse trigger ${guild.autoresponses.responses[i].trigger} in ${message.guild.name} (${message.guild.id})`, "success") 28 | message.channel.zend(await Tags.cleanString(translate(guild.autoresponses.responses[i].response.toString()), message)) 29 | return; 30 | } 31 | } 32 | } 33 | 34 | }; -------------------------------------------------------------------------------- /util/Message/processors/autorole.js: -------------------------------------------------------------------------------- 1 | const cooldowns = new Map(); 2 | const Settings = require('./../../../structures/general/Settings.js'); 3 | const Experience = require('./../../../structures/user/Experience.js') 4 | 5 | module.exports = async function autorole(message, guild) { 6 | //COOLDOWN SYSTEM 7 | if (!cooldowns.has(message.guild.id)) cooldowns.set(message.guild.id, new Map()); 8 | const now = Date.now(); 9 | const timestamps = cooldowns.get(message.guild.id); 10 | const cooldownAmount = 2100; 11 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 12 | if (now < expirationTime) return; 13 | timestamps.set(message.author.id, now); 14 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 15 | //END COOLDOWN 16 | 17 | //AUTOROLE MANAGER 18 | const Experience = require('./../../../structures/user/Experience.js') 19 | let userLevel = await Experience.getLevel(message.author.id, message.guild.id) 20 | if (guild.autoroles.roles.length && !timestamps.has(message.author.id)) autorole(); 21 | 22 | function autorole() { 23 | if (!guild.autoroles.roles) return; 24 | guild.autoroles.roles.map((level, i) => { 25 | let autoroleObject = guild.autoroles.roles[i] 26 | if (!autoroleObject.onJoin) { 27 | if (autoroleObject.level !== userLevel && (autoroleObject.removeOnLevelChange == null ? false : autoroleObject.removeOnLevelChange) == true) { 28 | if (message.guild.roles.has(autoroleObject.role.id) && !message.member.roles.has(autoroleObject.role.id)) { 29 | this.client.util.log(`[LEVEL] Removing autorole ${autoroleObject.role.name} (${autoroleObject.role.id}) (level ${autoroleObject.level} - user level is ${userLevel}) in ${message.guild.name} (${message.guild.id})`, "success") 30 | message.member.roles.remove(autoroleObject.role.id); 31 | } 32 | } 33 | } 34 | 35 | if (autoroleObject.level <= userLevel) { 36 | if (autoroleObject.removeOnLevelChange == null) autoroleObject.removeOnLevelChange = false; 37 | if (message.guild.roles.has(autoroleObject.role.id) && !message.member.roles.has(autoroleObject.role.id)) { 38 | this.client.util.log(`[LEVEL] Adding autorole ${autoroleObject.role.name} (${autoroleObject.role.id}) (level ${autoroleObject.level} - user level is ${userLevel}) in ${message.guild.name} (${message.guild.id})`, "success") 39 | message.member.roles.add(autoroleObject.role.id); 40 | } 41 | } 42 | }); 43 | } 44 | 45 | }; -------------------------------------------------------------------------------- /util/Message/processors/badges.js: -------------------------------------------------------------------------------- 1 | const Experience = require('./../../../structures/user/Experience.js') 2 | 3 | module.exports = async function badges(message) { 4 | 5 | /*let member = message.member 6 | if (!member) member = message.guild.members.fetch(message.author.id); 7 | 8 | const BadgeStructure = new(require('./../../../structures/user/Badges.js'))(member); 9 | // Developer ✅ 10 | if (message.author.developer && !BadgeStructure.has("developer")) { 11 | BadgeStructure.add("developer"); 12 | } else if (!message.author.developer && BadgeStructure.has("developer")) { 13 | BadgeStructure.remove("developer"); 14 | } 15 | // Moderator ✅ 16 | const isMod = await member.moderator 17 | if (isMod && !BadgeStructure.has("moderator")) { 18 | BadgeStructure.add("moderator"); 19 | } else if (!isMod && BadgeStructure.has("moderator")) { 20 | BadgeStructure.remove("moderator"); 21 | } 22 | // Friendship - badge progress is handled in hug.js ✅ 23 | if (await BadgeStructure.progress("friendship") >= 10 && !BadgeStructure.has("friendship")) BadgeStructure.add("friendship"); 24 | // Warrior - badge progress is handled in Challenge.js (structure) 25 | if (await BadgeStructure.progress("warrior") >= 10 && !BadgeStructure.has("warrior")) BadgeStructure.add("warrior"); 26 | // Invictus - badge progress is handled in Challenge.js (structure) (gets reset to 0 on loss) 27 | if (await BadgeStructure.progress("invictus") >= 5 && !BadgeStructure.has("invictus")) BadgeStructure.add("invictus"); 28 | // Tagger - badge progress is handled in tag.js (set to Tags.count) ✅ 29 | if (await BadgeStructure.progress("tagger") >= 25 && !BadgeStructure.has("tagger")) BadgeStructure.add("tagger"); 30 | // Commando - badge progress is handled in CommandHandler.js (structure) ✅ 31 | if (await BadgeStructure.progress("commando") >= 100 && !BadgeStructure.has("commando")) BadgeStructure.add("commando"); 32 | // Multilingual - badge progress is handled in translate.js ✅ 33 | if (await BadgeStructure.progress("multilingual") >= 50 && !BadgeStructure.has("multilingual")) BadgeStructure.add("multilingual"); 34 | // Essay Writer ✅ 35 | const userLevel = await Experience.getLevel(message.author.id, message.guild.id) 36 | if (await BadgeStructure.progress("essaywriter") !== userLevel) { 37 | BadgeStructure.editProgress("essaywriter", userLevel); 38 | } 39 | if (await BadgeStructure.progress("essaywriter") >= 10 && !BadgeStructure.has("essaywriter")) { 40 | BadgeStructure.add("essaywriter"); 41 | } else if (await BadgeStructure.progress("essaywriter") < 10 && BadgeStructure.has("essaywriter")) { 42 | BadgeStructure.remove("essaywriter"); 43 | } 44 | */ 45 | }; -------------------------------------------------------------------------------- /util/Message/processors/index.js: -------------------------------------------------------------------------------- 1 | require('fs') 2 | .readdirSync(__dirname) 3 | .filter(file => file !== 'index.js') 4 | .map(filename => { 5 | const moduleName = filename.split('.')[0]; 6 | exports[moduleName] = require(`${__dirname}/${filename}`); 7 | }); --------------------------------------------------------------------------------