diff --git a/XiaoBot.js b/XiaoBot.js index cea5f467..2d59b1c5 100644 --- a/XiaoBot.js +++ b/XiaoBot.js @@ -16,7 +16,7 @@ const client = new CommandoClient({ messageCacheLifetime: 60, messageSweepInterval: 60 }); -const { carbon, dBots, dBotsOrg } = require('./structures/Util'); +const { carbon, dBots, dBotsOrg, parseTopic, parseTopicMsg } = require('./structures/Util'); client.registry .registerDefaultTypes() @@ -73,44 +73,25 @@ client.on('message', async msg => { }); client.on('guildMemberAdd', member => { - const channel = member.guild.channels.filter(c => { - const topic = c.topic || ''; - if (topic.includes('')) return true; - return false; - }).first() || member.guild.channels.find('name', 'member-log'); - if (!channel || !channel.permissionsFor(client.user).has('SEND_MESSAGES')) return; - const parseMsg = topic => { - if (!topic || !/.+<\/joinmessage>/gi.test(topic)) return ''; - const setting = topic.match(/.+<\/joinmessage>/gi)[0]; - return setting.slice(13, setting.length - 14) - .replace(/\(member\)/gi, member.user.username) - .replace(/\(server\)/gi, member.guild.name) - .replace(/\(mention\)/gi, member.toString()); - }; - const msg = channel.topic ? parseMsg(channel.topic) : ''; + const channel = parseTopic(member.guild.channels, 'memberlog', client.user).first(); + if (!channel) return; + const msg = parseTopicMsg(channel.topic, 'joinmessage') + .replace(/{{member}}/gi, member.user.username) + .replace(/{{server}}/gi, member.guild.name) + .replace(/{{mention}}/gi, member); channel.send(msg || `Welcome ${member.user.username}!`); }); client.on('guildMemberRemove', member => { - const channel = member.guild.channels.filter(c => { - const topic = c.topic || ''; - if (topic.includes('')) return true; - return false; - }).first() || member.guild.channels.find('name', 'member-log'); - if (!channel || !channel.permissionsFor(client.user).has('SEND_MESSAGES')) return; - const parseMsg = topic => { - if (!topic || !/.+<\/leavemessage>/gi.test(topic)) return ''; - const setting = topic.match(/.+<\/leavemessage>/gi)[0]; - return setting.slice(14, setting.length - 15) - .replace(/\(member\)/gi, member.user.username) - .replace(/\(server\)/gi, member.guild.name) - .replace(/\(mention\)/gi, member.toString()); - }; - const msg = channel.topic ? parseMsg(channel.topic) : ''; - channel.send(msg || `Bye ${member.user.username}...`); + const channel = parseTopic(member.guild.channels, 'memberlog', client.user).first(); + if (!channel) return; + const msg = parseTopicMsg(channel.topic, 'leavemessage') + .replace(/{{member}}/gi, member.user.username) + .replace(/{{server}}/gi, member.guild.name) + .replace(/{{mention}}/gi, member); + channel.send(msg || `Welcome ${member.user.username}!`); }); - client.on('guildCreate', async guild => { console.log(`[GUILD] I have joined ${guild.name}! (${guild.id})`); const guilds = await client.shard.fetchClientValues('guilds.size'); diff --git a/commands/moderation/ban.js b/commands/moderation/ban.js index db34c92a..9b0f0673 100644 --- a/commands/moderation/ban.js +++ b/commands/moderation/ban.js @@ -1,6 +1,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const { stripIndents } = require('common-tags'); +const { parseTopic, parseTopicMsg } = require('../../structures/Util'); module.exports = class BanCommand extends Command { constructor(client) { @@ -33,13 +34,13 @@ module.exports = class BanCommand extends Command { } async run(msg, args) { - const modlogs = msg.guild.channels.filter(c => { - const topic = c.topic || ''; - if (topic.includes('')) return true; - return false; - }).first() || msg.guild.channels.find('name', 'mod-log'); + const modlogs = parseTopic(msg.guild.channels, 'modlog', this.client.user).first(); const { member, reason } = args; if (!member.bannable) return msg.say('This member is not bannable. Perhaps they have a higher role than me?'); + if (member.highestRole.calculatedPosition > msg.member.highestRole.calculatedPosition) { + return msg.say('Your roles are too low to ban this member.'); + } + if (member.id === msg.author.id) return msg.say('I don\'t think you want to ban yourself...'); await msg.say(`Are you sure you want to ban ${member.user.tag} (${member.id})?`); const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, { max: 1, @@ -47,9 +48,13 @@ module.exports = class BanCommand extends Command { }); if (!msgs.size || !['y', 'yes'].includes(msgs.first().content.toLowerCase())) return msg.say('Aborting.'); try { + const message = parseTopicMsg(modlogs.topic, 'modmessage') + .replace(/{{action}}/gi, 'banned') + .replace(/{{moderator}}/gi, msg.author.tag) + .replace(/{{server}}/gi, msg.guild.name); await member.send(stripIndents` - You were banned from ${msg.guild.name}! - Reason: ${reason} + ${message || `You were banned from ${msg.guild.name} by ${msg.author.tag}!`} + **Reason:** ${reason} `); } catch (err) { await msg.say('Failed to Send DM.'); diff --git a/commands/moderation/kick.js b/commands/moderation/kick.js index 948c1694..00e7fae0 100644 --- a/commands/moderation/kick.js +++ b/commands/moderation/kick.js @@ -1,6 +1,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const { stripIndents } = require('common-tags'); +const { parseTopic, parseTopicMsg } = require('../../structures/Util'); module.exports = class KickCommand extends Command { constructor(client) { @@ -33,13 +34,13 @@ module.exports = class KickCommand extends Command { } async run(msg, args) { - const modlogs = msg.guild.channels.filter(c => { - const topic = c.topic || ''; - if (topic.includes('')) return true; - return false; - }).first() || msg.guild.channels.find('name', 'mod-log'); + const modlogs = parseTopic(msg.guild.channels, 'modlog', this.client.user).first(); const { member, reason } = args; if (!member.kickable) return msg.say('This member is not kickable. Perhaps they have a higher role than me?'); + if (member.highestRole.calculatedPosition > msg.member.highestRole.calculatedPosition) { + return msg.say('Your roles are too low to kick this member.'); + } + if (member.id === msg.author.id) return msg.say('I don\'t think you want to kick yourself...'); await msg.say(`Are you sure you want to kick ${member.user.tag} (${member.id})?`); const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, { max: 1, @@ -47,9 +48,13 @@ module.exports = class KickCommand extends Command { }); if (!msgs.size || !['y', 'yes'].includes(msgs.first().content.toLowerCase())) return msg.say('Aborting.'); try { + const message = parseTopicMsg(modlogs.topic, 'modmessage') + .replace(/{{action}}/gi, 'kicked') + .replace(/{{moderator}}/gi, msg.author.tag) + .replace(/{{server}}/gi, msg.guild.name); await member.send(stripIndents` - You were kicked from ${msg.guild.name}! - Reason: ${reason} + ${message || `You were kicked from ${msg.guild.name} by ${msg.author.tag}!`} + **Reason:** ${reason} `); } catch (err) { await msg.say('Failed to Send DM.'); diff --git a/commands/moderation/softban.js b/commands/moderation/softban.js index b5613d06..2dbf6b3c 100644 --- a/commands/moderation/softban.js +++ b/commands/moderation/softban.js @@ -1,6 +1,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const { stripIndents } = require('common-tags'); +const { parseTopic, parseTopicMsg } = require('../../structures/Util'); module.exports = class SoftbanCommand extends Command { constructor(client) { @@ -33,13 +34,13 @@ module.exports = class SoftbanCommand extends Command { } async run(msg, args) { - const modlogs = msg.guild.channels.filter(c => { - const topic = c.topic || ''; - if (topic.includes('')) return true; - return false; - }).first() || msg.guild.channels.find('name', 'mod-log'); + const modlogs = parseTopic(msg.guild.channels, 'modlog', this.client.user).first(); const { member, reason } = args; - if (!member.bannable) return msg.say('This member is not bannable. Perhaps they have a higher role than me?'); + if (!member.bannable) return msg.say('This member is not softbannable. Perhaps they have a higher role than me?'); + if (member.highestRole.calculatedPosition > msg.member.highestRole.calculatedPosition) { + return msg.say('Your roles are too low to softban this member.'); + } + if (member.id === msg.author.id) return msg.say('I don\'t think you want to softban yourself...'); await msg.say(`Are you sure you want to softban ${member.user.tag} (${member.id})?`); const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, { max: 1, @@ -47,9 +48,13 @@ module.exports = class SoftbanCommand extends Command { }); if (!msgs.size || !['y', 'yes'].includes(msgs.first().content.toLowerCase())) return msg.say('Aborting.'); try { + const message = parseTopicMsg(modlogs.topic, 'modmessage') + .replace(/{{action}}/gi, 'softbanned') + .replace(/{{moderator}}/gi, msg.author.tag) + .replace(/{{server}}/gi, msg.guild.name); await member.send(stripIndents` - You were softbanned from ${msg.guild.name}! - Reason: ${reason} + ${message || `You were softbanned from ${msg.guild.name} by ${msg.author.tag}!`} + **Reason:** ${reason} `); } catch (err) { await msg.say('Failed to Send DM.'); diff --git a/commands/moderation/unban.js b/commands/moderation/unban.js index 4e9e0e56..8b0349ca 100644 --- a/commands/moderation/unban.js +++ b/commands/moderation/unban.js @@ -1,6 +1,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const { stripIndents } = require('common-tags'); +const { parseTopic } = require('../../structures/Util'); module.exports = class UnbanCommand extends Command { constructor(client) { @@ -33,11 +34,7 @@ module.exports = class UnbanCommand extends Command { } async run(msg, args) { - const modlogs = msg.guild.channels.filter(c => { - const topic = c.topic || ''; - if (topic.includes('')) return true; - return false; - }).first() || msg.guild.channels.find('name', 'mod-log'); + const modlogs = parseTopic(msg.guild.channels, 'modlog', this.client.user).first(); const { id, reason } = args; const bans = await msg.guild.fetchBans(); if (!bans.has(id)) return msg.say('This ID is not in the Guild Banlist.'); diff --git a/commands/moderation/warn.js b/commands/moderation/warn.js index 297e5f38..1f3f4da8 100644 --- a/commands/moderation/warn.js +++ b/commands/moderation/warn.js @@ -1,6 +1,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const { stripIndents } = require('common-tags'); +const { parseTopic, parseTopicMsg } = require('../../structures/Util'); module.exports = class WarnCommand extends Command { constructor(client) { @@ -32,12 +33,13 @@ module.exports = class WarnCommand extends Command { } async run(msg, args) { - const modlogs = msg.guild.channels.filter(c => { - const topic = c.topic || ''; - if (topic.includes('')) return true; - return false; - }).first() || msg.guild.channels.find('name', 'mod-log'); + const modlogs = parseTopic(msg.guild.channels, 'modlog', this.client.user).first(); const { member, reason } = args; + if (!member.kickable) return msg.say('This member is not warnable. Perhaps they have a higher role than me?'); + if (member.highestRole.calculatedPosition > msg.member.highestRole.calculatedPosition) { + return msg.say('Your roles are too low to warn this member.'); + } + if (member.id === msg.author.id) return msg.say('I don\'t think you want to warn yourself...'); await msg.say(`Are you sure you want to warn ${member.user.tag} (${member.id})?`); const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, { max: 1, @@ -45,9 +47,13 @@ module.exports = class WarnCommand extends Command { }); if (!msgs.size || !['y', 'yes'].includes(msgs.first().content.toLowerCase())) return msg.say('Aborting.'); try { - await member.user.send(stripIndents` - You were warned in ${msg.guild.name}! - Reason: ${reason} + const message = parseTopicMsg(modlogs.topic, 'modmessage') + .replace(/{{action}}/gi, 'warn') + .replace(/{{moderator}}/gi, msg.author.tag) + .replace(/{{server}}/gi, msg.guild.name); + await member.send(stripIndents` + ${message || `You were warned in ${msg.guild.name} by ${msg.author.tag}!`} + **Reason:** ${reason} `); } catch (err) { await msg.say('Failed to Send DM.'); diff --git a/commands/random/portal-send.js b/commands/random/portal-send.js index 1f2a8dd5..330c742c 100644 --- a/commands/random/portal-send.js +++ b/commands/random/portal-send.js @@ -1,4 +1,5 @@ const Command = require('../../structures/Command'); +const { parseTopic } = require('../../structures/Util'); module.exports = class PortalSendCommand extends Command { constructor(client) { @@ -25,11 +26,7 @@ module.exports = class PortalSendCommand extends Command { async run(msg, args) { const { message } = args; - const channel = this.client.channels.filter(c => { - if (c.type !== 'text' || !c.topic || msg.guild.id === c.guild.id) return false; - else if (c.topic.includes('')) return true; - return false; - }).random(); + const channel = parseTopic(this.client.channels, 'portal', this.client.user).random(); if (!channel) return msg.say('Aww... No channel has an open portal...'); try { await channel.send(`**${msg.author.tag} (${msg.guild.name}):** ${message}`); diff --git a/commands/roleplay/evolve.js b/commands/roleplay/evolve.js new file mode 100644 index 00000000..b4dc4629 --- /dev/null +++ b/commands/roleplay/evolve.js @@ -0,0 +1,28 @@ +const Command = require('../../structures/Command'); +const { stripIndents } = require('common-tags'); + +module.exports = class EvolveCommand extends Command { + constructor(client) { + super(client, { + name: 'evolve', + group: 'roleplay', + memberName: 'evolve', + description: 'Evolves a user.', + args: [ + { + key: 'user', + prompt: 'What user do you want to roleplay with?', + type: 'user' + } + ] + }); + } + + run(msg, args) { + const { user } = args; + return msg.say(stripIndents` + **${user}** *is evolving!* + https://i.imgur.com/7bh8hoX.gif + `); + } +}; diff --git a/commands/util/setting-help.js b/commands/util/setting-help.js index 0d2656db..c9485809 100644 --- a/commands/util/setting-help.js +++ b/commands/util/setting-help.js @@ -15,12 +15,18 @@ module.exports = class SettingHelpCommand extends Command { run(msg) { return msg.say(stripIndents` + __**Settings**__ **Invite Guard:** Place \`\` in your default channel's topic. (${msg.guild.defaultChannel}) **Mod Log Channel:** Place \`\` in a channel's topic. **Portal Channel:** Place \`\` in a channel's topic. **Member Log Channel:** Place \`\` in a channel's topic. **Custom Join Message:** Place \`message\` in the Member Log's Channel. **Custom Leave Message:** Place \`message\` in the Member Log's Channel. + **Custom Mod DM:** Place \`message\` in the Mod Log's Channel. + + __**Placeholders**__ + **Join/Leave Message:** \`{{member}}\`, \`{{server}}\`, \`{{mention}}\` + **Mod DM:** \`{{action}}\`, \`{{server}}\`, \`{{moderator}}\` `); } }; diff --git a/package.json b/package.json index 475ae7fc..0aaaf7c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiaobot", - "version": "27.9.2", + "version": "28.0.0", "description": "Your personal server companion.", "main": "Shard.js", "scripts": { diff --git a/structures/Util.js b/structures/Util.js index 38021732..e5353189 100644 --- a/structures/Util.js +++ b/structures/Util.js @@ -40,6 +40,24 @@ class Util { .then(() => console.log('[DBOTSORG] Successfully posted to Discord Bots Org.')) .catch(err => console.error(`[DBOTSORG] Failed to post to Discord Bots Org. ${err}`)); } + + static parseTopic(channels, setting, user) { + const channelList = channels.filter(c => { + const topic = c.topic || ''; + if (topic.includes(`<${setting}>`) && c.type === 'text' && c.permissionsFor(user).has('SEND_MESSAGES')) return true; + return false; + }); + if (!channelList) return false; + return channelList; + } + + static parseTopicMsg(topic, setting) { + const regex = new RegExp(`<${setting}>.+`, 'gi'); + if (!regex.test(topic)) return ''; + const parsed = topic.match(regex)[0]; + const word = `<${setting}>`; + return parsed.slice(word.length, parsed.length - (word.length + 1)); + } } module.exports = Util;