2020-04-20 04:52:50 +00:00
|
|
|
/* Copyright 2020 Emily J. / mudkipscience and contributors. Subject to the AGPLv3 license.
|
2020-04-20 03:02:10 +00:00
|
|
|
* Some code found in this file has been taken from Guidebot, which is licensed under the MIT license.
|
|
|
|
*/
|
|
|
|
|
2020-04-18 07:52:42 +00:00
|
|
|
// Functions in this file are bound to the client object, and available basically anywhere.
|
|
|
|
|
2020-04-11 09:30:51 +00:00
|
|
|
const { MessageEmbed } = require('discord.js')
|
2020-04-04 06:59:22 +00:00
|
|
|
const mongoose = require('mongoose')
|
|
|
|
const Guild = require('../models/guild')
|
2020-04-06 05:53:41 +00:00
|
|
|
const User = require('../models/user')
|
2020-04-04 06:59:22 +00:00
|
|
|
|
2020-03-29 07:45:09 +00:00
|
|
|
module.exports = client => {
|
2020-04-11 09:30:51 +00:00
|
|
|
// Creates an embed for when commands are used incorrectly
|
2020-04-13 09:29:47 +00:00
|
|
|
client.userError = (msg, cmd, err) => {
|
2020-04-11 09:30:51 +00:00
|
|
|
const embed = new MessageEmbed()
|
|
|
|
embed.setColor('#EF5350')
|
|
|
|
embed.setTitle(cmd.help.name + ':' + cmd.help.category.toLowerCase())
|
|
|
|
embed.setDescription(err)
|
|
|
|
embed.addField('**Usage**', cmd.help.usage)
|
|
|
|
embed.addField('**Parameters**', cmd.help.parameters)
|
|
|
|
embed.setFooter(`Run 'help ${cmd.help.name}' for more information.`)
|
2020-04-13 09:29:47 +00:00
|
|
|
msg.channel.send(embed).then(msg => {
|
|
|
|
msg.delete({ timeout: 60000 })
|
|
|
|
})
|
2020-04-11 09:30:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 07:52:42 +00:00
|
|
|
// Embed colour function
|
|
|
|
client.embedColour = function (guild) {
|
|
|
|
if (!guild || guild.member(client.user).displayHexColor === '#000000') {
|
|
|
|
return ['#ff9d68', '#ff97cb', '#d789ff', '#74FFFF'].random()
|
|
|
|
} else {
|
|
|
|
return guild.member(client.user).displayHexColor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 08:31:48 +00:00
|
|
|
// Clean up input to remove @everyone, token, etc
|
2020-04-03 06:26:16 +00:00
|
|
|
client.clean = async (client, text) => {
|
|
|
|
if (text && text.constructor.name === 'Promise') {
|
|
|
|
text = await text
|
|
|
|
}
|
|
|
|
if (typeof text !== 'string') {
|
|
|
|
text = require('util').inspect(text, { depth: 1 })
|
|
|
|
}
|
|
|
|
text = text
|
|
|
|
.replace(/`/g, '`' + String.fromCharCode(8203))
|
|
|
|
.replace(/@/g, '@' + String.fromCharCode(8203))
|
|
|
|
.replace(client.token, 'mfa.VkO_2G4Qv3T--NO--lWetW_tjND--TOKEN--QFTm6YGtzq9PH--4U--tG0')
|
|
|
|
|
|
|
|
return text
|
|
|
|
}
|
|
|
|
|
2020-04-03 08:31:48 +00:00
|
|
|
// Single line await messages
|
|
|
|
client.awaitReply = async (msg, question, limit = 60000) => {
|
|
|
|
const filter = m => m.author.id === msg.author.id
|
|
|
|
await msg.channel.send(question)
|
|
|
|
try {
|
|
|
|
const collected = await msg.channel.awaitMessages(filter, {
|
|
|
|
max: 1,
|
|
|
|
time: limit,
|
|
|
|
errors: ['time']
|
|
|
|
})
|
|
|
|
return collected.first().content
|
|
|
|
} catch (e) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2020-04-01 08:33:02 +00:00
|
|
|
|
2020-04-13 09:29:47 +00:00
|
|
|
// Capitalises the first letter of every word in a string
|
|
|
|
// eslint-disable-next-line no-extend-native
|
|
|
|
Object.defineProperty(String.prototype, 'toProperCase', {
|
|
|
|
value: function () {
|
|
|
|
return this.replace(/([^\W_]+[^\s-]*) */g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-04-04 16:13:18 +00:00
|
|
|
// Returns a random int between min and max
|
2020-04-03 08:31:48 +00:00
|
|
|
client.intBetween = function (min, max) {
|
2020-04-04 06:59:22 +00:00
|
|
|
return Math.floor((Math.random() * (max - min)) + min)
|
2020-04-01 08:33:02 +00:00
|
|
|
}
|
|
|
|
|
2020-04-03 14:55:35 +00:00
|
|
|
// Get random array object
|
|
|
|
// eslint-disable-next-line no-extend-native
|
2020-04-03 08:31:48 +00:00
|
|
|
Object.defineProperty(Array.prototype, 'random', {
|
2020-04-03 14:55:35 +00:00
|
|
|
value: function () {
|
2020-04-03 08:31:48 +00:00
|
|
|
return this[Math.floor(Math.random() * this.length)]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// `await client.wait(1000);` to 'pause' for 1 second.
|
|
|
|
client.wait = require('util').promisify(setTimeout)
|
|
|
|
|
|
|
|
// Find guild members
|
|
|
|
client.findMembers = function (guild, search) {
|
|
|
|
if (!search || typeof search !== 'string') return
|
2020-04-02 08:24:36 +00:00
|
|
|
|
2020-04-03 06:26:16 +00:00
|
|
|
const members = []
|
2020-04-03 08:31:48 +00:00
|
|
|
let member
|
|
|
|
|
2020-04-03 06:26:16 +00:00
|
|
|
// Try ID search
|
2020-04-03 08:31:48 +00:00
|
|
|
if (!isNaN(search) === true) {
|
|
|
|
members.push(guild.members.cache.get(search))
|
2020-04-03 06:26:16 +00:00
|
|
|
if (members[0]) {
|
|
|
|
return members[0]
|
|
|
|
}
|
2020-04-02 08:24:36 +00:00
|
|
|
}
|
2020-04-01 08:33:02 +00:00
|
|
|
|
2020-04-03 06:26:16 +00:00
|
|
|
// Try username search
|
2020-04-01 08:33:02 +00:00
|
|
|
try {
|
2020-04-03 08:31:48 +00:00
|
|
|
member = guild.members.cache.find(m => m.displayName.toLowerCase() === search)
|
|
|
|
if (!member) {
|
|
|
|
guild.members.cache.find(m => m.user.tag.toLowerCase() === search)
|
|
|
|
}
|
2020-04-01 08:33:02 +00:00
|
|
|
} catch (err) {}
|
2020-04-03 08:31:48 +00:00
|
|
|
if (member) {
|
|
|
|
members.push(member)
|
|
|
|
}
|
|
|
|
guild.members.cache.forEach(m => {
|
|
|
|
if (m.displayName.toLowerCase().startsWith(search) || m.user.tag.toLowerCase().startsWith(search)) {
|
|
|
|
members.push(m)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return members
|
|
|
|
}
|
|
|
|
|
2020-04-07 08:10:01 +00:00
|
|
|
// USER OBJECT FROM MENTION
|
|
|
|
client.getUserFromMention = mention => {
|
|
|
|
if (!mention) return
|
|
|
|
|
|
|
|
if (mention.startsWith('<@') && mention.endsWith('>')) {
|
|
|
|
mention = mention.slice(2, -1)
|
|
|
|
|
|
|
|
if (mention.startsWith('!')) {
|
|
|
|
mention = mention.slice(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return client.users.cache.get(mention)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 08:31:48 +00:00
|
|
|
client.findRole = function (guild, search) {
|
2020-04-04 16:13:18 +00:00
|
|
|
var role = guild.roles.cache.find(r => r.name.toLowerCase() === search.toLowerCase())
|
2020-04-03 08:31:48 +00:00
|
|
|
if (!role) {
|
|
|
|
role = guild.roles.cache.get(search.toLowerCase())
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!role) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return role
|
2020-04-01 08:33:02 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 07:52:42 +00:00
|
|
|
// Loads commands
|
|
|
|
client.loadCommand = (commandName) => {
|
|
|
|
try {
|
|
|
|
const props = require(`../commands/${commandName}`)
|
|
|
|
if (props.init) {
|
|
|
|
props.init(client)
|
|
|
|
}
|
|
|
|
client.commands.set(props.help.name, props)
|
|
|
|
// So commands can each have their own cooldown time
|
|
|
|
client.cooldown.set(props.help.name, new Map())
|
|
|
|
props.conf.aliases.forEach(alias => {
|
|
|
|
client.aliases.set(alias, props.help.name)
|
|
|
|
})
|
|
|
|
return false
|
|
|
|
} catch (e) {
|
|
|
|
return `Failed to load ${commandName}: ${e}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Permission level function
|
|
|
|
client.permlevel = (message, settings) => {
|
|
|
|
let permlvl = 0
|
|
|
|
|
|
|
|
const permOrder = client.config.permLevels.slice(0).sort((p, c) => p.level < c.level ? 1 : -1)
|
|
|
|
|
|
|
|
while (permOrder.length) {
|
|
|
|
const currentLevel = permOrder.shift()
|
|
|
|
if (message.guild && currentLevel.guildOnly) continue
|
|
|
|
if (currentLevel.check(message, settings)) {
|
|
|
|
permlvl = currentLevel.level
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return permlvl
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get guild settings
|
|
|
|
client.findOrCreateGuild = async (guild) => {
|
|
|
|
const data = await Guild.findOne({ guildID: guild.id })
|
|
|
|
if (data) {
|
|
|
|
return data
|
|
|
|
} else {
|
|
|
|
const merged = Object.assign({ _id: mongoose.Types.ObjectId() }, { guildID: guild.id })
|
|
|
|
const newGuild = await new Guild(merged)
|
|
|
|
return newGuild.save()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update guild settings
|
|
|
|
client.updateGuild = async (guild, settings) => {
|
|
|
|
let data = await client.findOrCreateGuild(guild)
|
|
|
|
|
|
|
|
if (typeof data !== 'object') data = {}
|
|
|
|
for (const key in settings) {
|
|
|
|
if (data[key] !== settings[key]) {
|
|
|
|
data[key] = settings[key]
|
|
|
|
} else return
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.updateOne(settings)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete guild settings
|
|
|
|
client.deleteGuild = async (guild) => {
|
|
|
|
const data = await client.findOrCreateGuild(guild)
|
|
|
|
if (data) {
|
|
|
|
data.deleteOne({ guildID: guild.id })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get user settings
|
|
|
|
client.findOrCreateUser = async (user) => {
|
|
|
|
const data = await User.findOne({ userID: user.id })
|
|
|
|
if (data) {
|
|
|
|
return data
|
|
|
|
} else {
|
|
|
|
const merged = Object.assign({ _id: mongoose.Types.ObjectId() }, { userID: user.id })
|
|
|
|
const newUser = await new User(merged)
|
|
|
|
return newUser.save()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update user settings
|
|
|
|
client.updateUser = async (user, settings) => {
|
|
|
|
let data = await client.findOrCreateUser(user)
|
|
|
|
|
|
|
|
if (typeof data !== 'object') data = {}
|
|
|
|
for (const key in settings) {
|
|
|
|
if (data[key] !== settings[key]) {
|
|
|
|
data[key] = settings[key]
|
|
|
|
} else return
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.updateOne(settings)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete user settings
|
|
|
|
client.deleteUser = async (user) => {
|
|
|
|
const data = await client.findOrCreateUser(user)
|
|
|
|
if (data) {
|
|
|
|
data.deleteOne({ userID: user.id })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Command unloader
|
|
|
|
client.unloadCommand = async (commandName) => {
|
|
|
|
let command
|
|
|
|
if (client.commands.has(commandName)) {
|
|
|
|
command = client.commands.get(commandName)
|
|
|
|
} else if (client.aliases.has(commandName)) {
|
|
|
|
command = client.commands.get(client.aliases.get(commandName))
|
|
|
|
}
|
|
|
|
if (!command) return `The command \`${commandName}\` doesn't seem to exist, nor is it an alias. Try again!`
|
|
|
|
|
|
|
|
if (command.shutdown) {
|
|
|
|
await command.shutdown(client)
|
|
|
|
}
|
|
|
|
const mod = require.cache[require.resolve(`../commands/${command.help.name}`)]
|
|
|
|
delete require.cache[require.resolve(`../commands/${command.help.name}.js`)]
|
|
|
|
for (let i = 0; i < mod.parent.children.length; i++) {
|
|
|
|
if (mod.parent.children[i] === mod) {
|
|
|
|
mod.parent.children.splice(i, 1)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-04-03 14:55:35 +00:00
|
|
|
// Both of these functions catch errors and log them (maybe we could use sentry?)
|
2020-03-31 07:59:09 +00:00
|
|
|
process.on('uncaughtException', (err) => {
|
|
|
|
const errorMsg = err.stack.replace(new RegExp(`${__dirname}/`, 'g'), './')
|
2020-04-18 14:23:50 +00:00
|
|
|
client.logger.error(`Uncaught Exception: ${errorMsg}`)
|
2020-03-31 07:59:09 +00:00
|
|
|
process.exit(1)
|
|
|
|
})
|
|
|
|
|
|
|
|
process.on('unhandledRejection', err => {
|
2020-04-08 08:12:01 +00:00
|
|
|
client.logger.error(`Unhandled rejection: ${err.stack}`)
|
2020-03-31 07:59:09 +00:00
|
|
|
})
|
2020-03-29 07:45:09 +00:00
|
|
|
}
|