From 716e6f5ce2ce334e424ecfbaaa8c3b58070e7ebe Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Wed, 17 Jan 2018 16:50:09 -0500 Subject: [PATCH 01/17] Add base command to roles file Added a class for a base command, the class helps standardizes commands, this class is currently in here temporarily, but should be extracted later. --- bot/controllers/roles.js | 96 +++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/bot/controllers/roles.js b/bot/controllers/roles.js index 04c65fe..6567963 100644 --- a/bot/controllers/roles.js +++ b/bot/controllers/roles.js @@ -1,59 +1,55 @@ module.exports = () => { const util = require('apex-util'); + // Base Command class, cleans up classes. + class BaseCommand { + constructor(cmd, example, title, desc, resType = 'reply', showWithHelp = true, allowInDM = false) { + this.cmd = cmd; + this.example = example; + this.title = title; + this.desc = desc; + this.showWithHelp = showWithHelp; + this.allowInDM = allowInDM; + this.resType = resType; + this.action = message => message; + } + } + const _run = (message) => { const disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; - const ctrls = [ - { - cmd: '!roles', - example: '!roles', - title: 'List All Roles', - desc: 'List all Armada Roles', - showWithHelp: true, - posTargetUser: null, - posSecondaryCmd: null, - regexSplitCharacter: null, - allowInDM: false, - resType: 'dm', - action: (message) => { - const roles = []; - util.log('help!', message); - message.guild.roles.map((role) => { - if (!disallowedRoles.includes(role.name.toLowerCase())) { - return roles.push(role.name); - } - return role.name; - }); - return 'List of all Armada Roles: \n\n' + roles.join('\n'); - }, - }, - { - cmd: '!addRole', - example: '!addRole ', - title: 'Add Role', - desc: 'Add a single role to yourself. Role is case-sensitive.', - showWithHelp: true, - posTargetUser: null, - posSecondaryCmd: null, - regexSplitCharacter: null, - allowInDM: false, - resType: 'reply', - action: (message, ctrl, msg) => { - const targetRole = message.guild.roles.find('name', msg.parsed[1]); - if (targetRole === null) { - util.log('No role matched', msg.parsed[1], 2); - return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; - } else if (disallowedRoles.includes(targetRole.name.toLowerCase())) { - util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); - return 'You are not worthy of the role ' + msg.parsed[1] + '.'; - } else { - util.log('Adding Role to user', targetRole.name, 2); - message.member.addRole(targetRole).catch(util.log); - return 'Added the role "' + targetRole.name + '".'; - } - }, - }, + const rolesCommand = new BaseCommand('!roles', '!roles', 'List All Roles', 'List all Armada Roles', 'dm'); + rolesCommand.action = (message) => { + const roles = []; + util.log('help!', message); + message.guild.roles.map((role) => { + if (!disallowedRoles.includes(role.name.toLowerCase())) { + return roles.push(role.name); + } + return role.name; + }); + return 'List of all Armada Roles: \n\n' + roles.join('\n'); + }; + + const addRoleCommand = new BaseCommand('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.'); + addRoleCommand.action = (message, ctrl, msg) => { + const targetRole = message.guild.roles.find('name', msg.parsed[1]); + if (targetRole === null) { + util.log('No role matched', msg.parsed[1], 2); + return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; + } else if (disallowedRoles.includes(targetRole.name.toLowerCase())) { + util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); + return 'You are not worthy of the role ' + msg.parsed[1] + '.'; + } else { + util.log('Adding Role to user', targetRole.name, 2); + message.member.addRole(targetRole).catch(util.log); + return 'Added the role "' + targetRole.name + '".'; + } + }; + + const ctrls = [ + rolesCommand, + addRoleCommand, { cmd: '!addRoles', example: '!addRoles ,,', From 0a645afdeabb6808b64c168efce8ee636fde8d50 Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Wed, 17 Jan 2018 19:11:42 -0500 Subject: [PATCH 02/17] Isolated base command class, cleaned up roles cmds Moved the BaseCommand class out of roles into separate file, renamed as Command in Role controller, cleaned up ctrl array. --- bot/baseCommand.js | 13 +++ bot/controllers/roles.js | 185 ++++++++++++++------------------------- 2 files changed, 78 insertions(+), 120 deletions(-) create mode 100644 bot/baseCommand.js diff --git a/bot/baseCommand.js b/bot/baseCommand.js new file mode 100644 index 0000000..7ccbc9a --- /dev/null +++ b/bot/baseCommand.js @@ -0,0 +1,13 @@ +class BaseCommand { + constructor(cmd, example, title, desc, action, resType = 'reply', showWithHelp = true, allowInDM = false) { + this.cmd = cmd; + this.example = example; + this.title = title; + this.desc = desc; + this.showWithHelp = showWithHelp; + this.allowInDM = allowInDM; + this.resType = resType; + this.action = action; + } +} +module.exports = BaseCommand; diff --git a/bot/controllers/roles.js b/bot/controllers/roles.js index 6567963..cbf8cc9 100644 --- a/bot/controllers/roles.js +++ b/bot/controllers/roles.js @@ -1,25 +1,11 @@ module.exports = () => { const util = require('apex-util'); - - // Base Command class, cleans up classes. - class BaseCommand { - constructor(cmd, example, title, desc, resType = 'reply', showWithHelp = true, allowInDM = false) { - this.cmd = cmd; - this.example = example; - this.title = title; - this.desc = desc; - this.showWithHelp = showWithHelp; - this.allowInDM = allowInDM; - this.resType = resType; - this.action = message => message; - } - } + const Command = require('../baseCommand.js'); const _run = (message) => { const disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; - const rolesCommand = new BaseCommand('!roles', '!roles', 'List All Roles', 'List all Armada Roles', 'dm'); - rolesCommand.action = (message) => { + const rolesAction = (message) => { const roles = []; util.log('help!', message); message.guild.roles.map((role) => { @@ -31,8 +17,7 @@ module.exports = () => { return 'List of all Armada Roles: \n\n' + roles.join('\n'); }; - const addRoleCommand = new BaseCommand('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.'); - addRoleCommand.action = (message, ctrl, msg) => { + const addRoleAction = (message, ctrl, msg) => { const targetRole = message.guild.roles.find('name', msg.parsed[1]); if (targetRole === null) { util.log('No role matched', msg.parsed[1], 2); @@ -47,111 +32,71 @@ module.exports = () => { } }; - const ctrls = [ - rolesCommand, - addRoleCommand, - { - cmd: '!addRoles', - example: '!addRoles ,,', - title: 'Add Multiple Specific Roles', - desc: 'Add multiple specific roles to yourself. Roles are case-sensitive.', - showWithHelp: true, - posTargetUser: null, - posSecondaryCmd: null, - regexSplitCharacter: null, - allowInDM: false, - resType: 'reply', - action: (message, ctrl, msg) => { - const roles = msg.parsed[1].split(','); - util.log('Multiple Roles Parsing', roles, 4); - - roles.map((role) => { - if (!disallowedRoles.includes(role.toLowerCase())) { - const targetRole = message.guild.roles.find('name', role); - util.log('Asking API for Role', targetRole, 4); - - if (targetRole === null) { - return '"' + role + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; - } - return message.member.addRole(targetRole).catch(util.log); - } - return role.name; - }); - - return 'All set!'; - }, - }, - { - cmd: '!removeRole', - example: '!removeRole ', - title: 'Remove a single role', - desc: 'Remove a single game role from yourself. Role is case-sensitive.', - showWithHelp: true, - posTargetUser: null, - posSecondaryCmd: null, - regexSplitCharacter: null, - allowInDM: false, - resType: 'reply', - action: (message, ctrl, msg) => { - const targetRole = message.guild.roles.find('name', msg.parsed[1]); + const addRolesAction = (message, ctrl, msg) => { + const roles = msg.parsed[1].split(','); + util.log('Multiple Roles Parsing', roles, 4); + + roles.map((role) => { + if (!disallowedRoles.includes(role.toLowerCase())) { + const targetRole = message.guild.roles.find('name', role); + util.log('Asking API for Role', targetRole, 4); + if (targetRole === null) { - util.log('No role matched', msg.parsed[1], 2); - return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; - } - if (disallowedRoles.includes(targetRole.name.toLowerCase())) { - util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); - return 'You have not the power or the will to control this power.'; + return '"' + role + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; } + return message.member.addRole(targetRole).catch(util.log); + } + return role.name; + }); + + return 'All set!'; + }; - util.log('Removing role from user', targetRole.name, 2); - message.member.removeRole(targetRole).catch(util.log); - return targetRole.name + ' removed.'; - }, - }, - { - cmd: '!addAllRoles', - example: '!addAllRoles', - title: 'Add All Roles', - desc: 'Add every game role to yourself.', - showWithHelp: true, - posTargetUser: null, - posSecondaryCmd: null, - regexSplitCharacter: null, - allowInDM: false, - resType: 'reply', - action: (message) => { - message.guild.roles.map((role) => { - if (!disallowedRoles.includes(role.name.toLowerCase())) { - return message.member.addRole(role).catch(util.log); - } - return role.name; - }); - - return 'Adding you to all Roles. You\'re going to be drinking from the firehose :sob:'; - }, - }, - { - cmd: '!removeAllRoles', - example: '!removeAllRoles', - title: 'Remove All Roles', - desc: 'Remove every game role from yourself.', - showWithHelp: true, - posTargetUser: null, - posSecondaryCmd: null, - regexSplitCharacter: null, - allowInDM: false, - resType: 'reply', - action: (message) => { - message.guild.roles.map((role) => { - if (!disallowedRoles.includes(role.name.toLowerCase())) { - return message.member.removeRole(role).catch(util.log); - } - return role.name; - }); - - return 'Removing all roles. Back to basics.'; - }, - }, + const removeRoleAction = (message, ctrl, msg) => { + const targetRole = message.guild.roles.find('name', msg.parsed[1]); + if (targetRole === null) { + util.log('No role matched', msg.parsed[1], 2); + return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; + } + if (disallowedRoles.includes(targetRole.name.toLowerCase())) { + util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); + return 'You have not the power or the will to control this power.'; + } + + util.log('Removing role from user', targetRole.name, 2); + message.member.removeRole(targetRole).catch(util.log); + return targetRole.name + ' removed.'; + }; + + const addAllRolesAction = (message) => { + message.guild.roles.map((role) => { + if (!disallowedRoles.includes(role.name.toLowerCase())) { + return message.member.addRole(role).catch(util.log); + } + return role.name; + }); + + return 'Adding you to all Roles. You\'re going to be drinking from the firehose :sob:'; + }; + + const removeAllRolesAction = (message) => { + message.guild.roles.map((role) => { + if (!disallowedRoles.includes(role.name.toLowerCase())) { + return message.member.removeRole(role).catch(util.log); + } + return role.name; + }); + + return 'Removing all roles. Back to basics.'; + }; + + const ctrls = [ + new Command('!roles', '!roles', 'List All Roles', 'List all Armada Roles', rolesAction, 'dm'), + new Command('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.', addRoleAction), + new Command('!addRoles', '!addRoles ,,', 'Add Multiple Specific Roles', 'Add a single role to yourself. Role is case-sensitive.', addRolesAction), + new Command('!removeRole', '!removeRole ', 'Remove a single role', 'Remove a single game role from yourself. Role is case-sensitive.', removeRoleAction), + new Command('!addAllRoles', '!addAllRoles', 'Add All Roles', 'Add every game role to yourself.', addAllRolesAction), + new Command('!removeAllRoles', '!removeAllRoles', 'Remove All Roles', 'Remove every game role from yourself.', removeAllRolesAction), ]; const onSuccess = (message, res, ctrl) => { From 33499737361a455d9a50d45391bb1a3c87cca5c2 Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Wed, 17 Jan 2018 21:59:17 -0500 Subject: [PATCH 03/17] Added BaseController, cleaned controllers Controllers now extend from BaseController. Most commands work. --- bot/baseController.js | 58 ++++++++ bot/client.js | 6 +- bot/controllers/roles.js | 116 +++++++++++++++- bot/controllers/verify.js | 273 ++++++++++++++++---------------------- 4 files changed, 289 insertions(+), 164 deletions(-) create mode 100644 bot/baseController.js diff --git a/bot/baseController.js b/bot/baseController.js new file mode 100644 index 0000000..a13ca25 --- /dev/null +++ b/bot/baseController.js @@ -0,0 +1,58 @@ +const util = require('apex-util'); + +class BaseController { + constructor(message) { + this.message = message; + this.ctrls = []; + } + + onSuccess(res, ctrl) { + if (res !== null) { + if (ctrl.resType === 'reply') { + return this.message.reply(res); + } else if (ctrl.resType === 'dm') { + return this.message.author.send(res); + } + } + // Fail Safe + return false; + } + + // Not sure why error method accepted 3 params in "Do Stuff". + onError() { + this.message.reply('I Broke... Beep...Boop...Beep'); + } + + messageMiddleware() { + const container = {}; + container.parsed = this.message.content.split(' '); + const msg = Object.assign(this.message, container); + return msg; + } + + run() { + this.ctrls.map((ctrl) => { + util.log('Running through controller', ctrl.cmd, 2); + + const msg = this.messageMiddleware(); + if (msg.parsed[0].toLowerCase() === ctrl.cmd.toLowerCase()) { + util.log('!!! Matched Ctrl to Cmd !!!', ctrl.cmd, 2); + + // Ensure the communication is happening on a server + if (!ctrl.allowInDM) { + if (!this.message.guild) return this.onError('Please don\'t use this command directly. Instead use it in a channel on a server. :beers:'); + } + + // Do Stuff + const res = ctrl.action(this.message, msg); + if (res) { + this.onSuccess(res, ctrl); + } else { + this.onError(); + } + } + return ctrl; + }); + } +} +module.exports = BaseController; diff --git a/bot/client.js b/bot/client.js index 6d5646f..e419518 100644 --- a/bot/client.js +++ b/bot/client.js @@ -24,8 +24,10 @@ client.on('message', (message) => { Object.keys(ctrls).forEach((ctrlKey) => { if (ctrlKey) { // initialize the controller - const ctrlInstance = ctrls[ctrlKey](); + const ctrlInstance = new ctrls[ctrlKey](message); + + /* // Determine the method names of each controller const controllerMethodKeys = Object.keys(ctrlInstance); util.log('Methods found in controller', controllerMethodKeys, 3); @@ -40,6 +42,8 @@ client.on('message', (message) => { // Pass the message to each method of each controller ctrlResponses.push(method(message)); }); + + */ } }); diff --git a/bot/controllers/roles.js b/bot/controllers/roles.js index cbf8cc9..fbb89a8 100644 --- a/bot/controllers/roles.js +++ b/bot/controllers/roles.js @@ -1,4 +1,117 @@ -module.exports = () => { +const BaseController = require('../baseController.js'); +const Command = require('../baseCommand.js'); +const util = require('apex-util'); + +class RoleController extends BaseController { + constructor(message) { + super(message); + this.ctrls = [ + new Command('!roles', '!roles', 'List All Roles', 'List all Armada Roles', this.rolesAction, 'dm'), + new Command('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.', this.addRoleAction), + new Command('!addRoles', '!addRoles ,,', 'Add Multiple Specific Roles', 'Add a single role to yourself. Role is case-sensitive.', this.addRolesAction), + new Command('!removeRole', '!removeRole ', 'Remove a single role', 'Remove a single game role from yourself. Role is case-sensitive.', this.removeRoleAction), + new Command('!addAllRoles', '!addAllRoles', 'Add All Roles', 'Add every game role to yourself.', this.addAllRolesAction), + new Command('!removeAllRoles', '!removeAllRoles', 'Remove All Roles', 'Remove every game role from yourself.', this.removeAllRolesAction), + ]; + this.disallowedRoles = []; + this.run(); + } + + rolesAction(message) { + this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + const roles = []; + util.log('help!', message); + message.guild.roles.map((role) => { + if (!this.disallowedRoles.includes(role.name.toLowerCase())) { + return roles.push(role.name); + } + return role.name; + }); + return 'List of all Armada Roles: \n\n' + roles.join('\n'); + } + + addRoleAction(message, msg) { + this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + const targetRole = message.guild.roles.find('name', msg.parsed[1]); + if (targetRole === null) { + util.log('No role matched', msg.parsed[1], 2); + return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; + } else if (this.disallowedRoles.includes(targetRole.name.toLowerCase())) { + util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); + return 'You are not worthy of the role ' + msg.parsed[1] + '.'; + } else { + util.log('Adding Role to user', targetRole.name, 2); + message.member.addRole(targetRole).catch(util.log); + return 'Added the role "' + targetRole.name + '".'; + } + } + + addRolesAction(message, msg) { + this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + const roles = msg.parsed[1].split(','); + util.log('Multiple Roles Parsing', roles, 4); + + roles.map((role) => { + if (!this.disallowedRoles.includes(role.toLowerCase())) { + const targetRole = message.guild.roles.find('name', role); + util.log('Asking API for Role', targetRole, 4); + + if (targetRole === null) { + return '"' + role + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; + } + return message.member.addRole(targetRole).catch(util.log); + } + return role.name; + }); + + return 'All set!'; + } + + removeRoleAction(message, msg) { + this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + const targetRole = message.guild.roles.find('name', msg.parsed[1]); + if (targetRole === null) { + util.log('No role matched', msg.parsed[1], 2); + return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; + } + if (this.disallowedRoles.includes(targetRole.name.toLowerCase())) { + util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); + return 'You have not the power or the will to control this power.'; + } + + util.log('Removing role from user', targetRole.name, 2); + message.member.removeRole(targetRole).catch(util.log); + return targetRole.name + ' removed.'; + } + + addAllRolesAction(message) { + this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + message.guild.roles.map((role) => { + if (!this.disallowedRoles.includes(role.name.toLowerCase())) { + return message.member.addRole(role).catch(util.log); + } + return role.name; + }); + + return 'Adding you to all Roles. You\'re going to be drinking from the firehose :sob:'; + } + + removeAllRolesAction(message) { + this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + message.guild.roles.map((role) => { + if (!this.disallowedRoles.includes(role.name.toLowerCase())) { + return message.member.removeRole(role).catch(util.log); + } + return role.name; + }); + + return 'Removing all roles. Back to basics.'; + } +} + +module.exports = RoleController; + +/* module.exports = () => { const util = require('apex-util'); const Command = require('../baseCommand.js'); @@ -148,3 +261,4 @@ module.exports = () => { run: _run, }; }; +*/ diff --git a/bot/controllers/verify.js b/bot/controllers/verify.js index 280b99e..c16c765 100644 --- a/bot/controllers/verify.js +++ b/bot/controllers/verify.js @@ -1,174 +1,123 @@ +const BaseController = require('../baseController.js'); +const Command = require('../baseCommand.js'); +const util = require('apex-util'); const models = require('../../db/models'); const uuidv4 = require('uuid/v4'); -const util = require('apex-util'); const nodemailer = require('nodemailer'); -// Method to generate random numeric verification code -// Modified to fit style guide from this SO answer: -// https://stackoverflow.com/a/39774334 -const generateCode = (n) => { - // Workaround method for Math.pow() and ** operator - const pow = (base, exp) => { - let result = 1; - for (let i = 0; i < exp; i += 1) { - result *= base; - } - return result; - }; - const add = 1; - let max = 12 - add; - let min = 0; - if (n > max) { - return generateCode(max) + generateCode(n - max); - } - max = pow(10, n + add); - min = max / 10; - const number = Math.floor(Math.random() * (max - (min + 1))) + min; - return ('' + number).substring(add); -}; - -module.exports = () => { - const _run = (message) => { - const ctrls = [{ - cmd: '!verify', - example: '!verify ', - title: 'Verify Email Address', - desc: 'Verify user\'s email address', - showWithHelp: true, - posTargetUser: null, - posSecondaryCmd: null, - regexSplitCharacter: null, - allowInDM: false, - resType: 'reply', - action: (message, ctrl, msg) => { - const targetVerifiedRoleName = 'Crew'; - const validDomains = ['student.fullsail.edu', 'fullsail.edu', 'fullsail.com']; - const timeoutInMiliseconds = 600000; - const email = msg.parsed[1].toLowerCase(); - const emailDomain = email.split('@').pop(); - // We can set `codeLength` to whatever length we want the verif code to be. - // Recommend ngt 8 digits. - if (validDomains.includes(emailDomain)) { - const codeLength = 6; - const code = generateCode(codeLength); - util.log('code', code, 3); - // TODO: Set `time` prop to 600000 (10min) - const collector = message.channel.createMessageCollector( - m => m.content.includes(code), - { time: timeoutInMiliseconds }); - collector.on('collect', (m) => { - const verifyUser = 'Welcome aboard, Crewmate!'; - const userAlredyOnSystem = 'This email has already been verified to a discord user.'; - models.Member.findOne({ where: { email } }).then((matchedUserData) => { - if (matchedUserData === null) { - // no existing record found - models.Member.create({ - discorduser: m.author.id, - email, - uuid: uuidv4(), - verified: 1, - }); - // mapping guild roles to find the crew role id - const targetRole = message.guild.roles.find('name', targetVerifiedRoleName); - message.member.addRole(targetRole).catch(util.log); - message.reply(verifyUser); - } else { - // existing record found - message.reply(userAlredyOnSystem); - } - }); - util.log('Collected', m.content, 3); - }); - collector.on('end', (collected) => { - const verificationTimeout = `!verify timeout. Clap ${collected.author.username} in irons! Let's see how well they dance on the plank!`; - util.log('Items', collected.size, 3); - if (collected.size === 0) { - // TODO: ping admin team on verification fail - message.reply(verificationTimeout); - } - }); - // Set up Nodemailer to send emails through gmail - const sendVerifyCode = nodemailer.createTransport({ - service: 'gmail', - auth: { - user: process.env.EMAIL_USERNAME, - pass: process.env.EMAIL_PASS, - }, - }); - // Nodemailer email recipient & message - // TODO: Build email template - const mailOptions = { - from: process.env.EMAIL_USERNAME, - to: email, - subject: 'Armada Verification Code', - html: `

Enter the code below into Discord, in the same channel on the Armada Server. Verification will timeout after ${(timeoutInMiliseconds / 1000) / 60} minutes from first entering the !verify command.

Verification Code: ${code}

`, - }; - // Call sendMail on sendVerifyCode - // Pass mailOptions & callback function - sendVerifyCode.sendMail(mailOptions, (err, info) => { - const errorMsg = 'Oops, looks like the email can not be sent. It\'s not you, it\'s me. Please reach out to a moderator to help you verify.'; - if (err) { - message.reply(errorMsg); - util.log('Email not sent', err, 3); - } else { - util.log('Email details', info, 3); - } - }); - - util.log('Code', code, 3); - return `...What's the passcode? \n\n *eyes you suspicously*\n\n I just sent it to your email, just respond back to this channel within ${(timeoutInMiliseconds / 1000) / 60} minutes, with the code, and I won't treat you like a scurvy cur!`; - } else { - return 'Sorry, I can only verify Full Sail University email addresses.'; - } - }, - }]; - - const onSuccess = (message, res, ctrl) => { - if (res !== null) { - if (ctrl.resType === 'reply') { - return message.reply(res); - } else if (ctrl.resType === 'dm') { - return message.author.send(res); +class VerifyController extends BaseController { + constructor(message) { + super(message); + // Method to generate random numeric verification code + // Modified to fit style guide from this SO answer: + // https://stackoverflow.com/a/39774334 + this.generateCode = (n) => { + // Workaround method for Math.pow() and ** operator + const pow = (base, exp) => { + let result = 1; + for (let i = 0; i < exp; i += 1) { + result *= base; } + return result; + }; + const add = 1; + let max = 12 - add; + let min = 0; + if (n > max) { + return this.generateCode(max) + this.generateCode(n - max); } - // Fail Safe - return false; - }; - - const onError = message => message.reply('I Broke... Beep...Boop...Beep'); - - const messageMiddleware = (message) => { - const container = {}; - container.parsed = message.content.split(' '); - const msg = Object.assign(message, container); - return msg; + max = pow(10, n + add); + min = max / 10; + const number = Math.floor(Math.random() * (max - (min + 1))) + min; + return ('' + number).substring(add); }; + this.ctrls = [ + new Command('!verify', '!verify ', 'Verify Email Address', 'Verify user\'s email address', this.verifyAction), + ]; + this.run(); + } - ctrls.map((ctrl) => { - util.log('Running through controller', ctrl.cmd, 2); - - const msg = messageMiddleware(message); - if (msg.parsed[0].toLowerCase() === ctrl.cmd.toLowerCase()) { - util.log('!!! Matched Ctrl to Cmd !!!', ctrl.cmd, 2); - - // Ensure the communication is happening on a server - if (!ctrl.allowInDM) { - if (!message.guild) return onError(message, 'Please don\'t use this command directly. Instead use it in a channel on a server. :beers:'); + verifyAction(message, msg) { + const targetVerifiedRoleName = 'Crew'; + const validDomains = ['student.fullsail.edu', 'fullsail.edu', 'fullsail.com']; + const timeoutInMiliseconds = 600000; + const email = msg.parsed[1].toLowerCase(); + const emailDomain = email.split('@').pop(); + // We can set `codeLength` to whatever length we want the verif code to be. + // Recommend ngt 8 digits. + if (validDomains.includes(emailDomain)) { + const codeLength = 6; + const code = this.generateCode(codeLength); + util.log('code', code, 3); + // TODO: Set `time` prop to 600000 (10min) + const collector = message.channel.createMessageCollector( + m => m.content.includes(code), + { time: timeoutInMiliseconds }); + collector.on('collect', (m) => { + const verifyUser = 'Welcome aboard, Crewmate!'; + const userAlredyOnSystem = 'This email has already been verified to a discord user.'; + models.Member.findOne({ where: { email } }).then((matchedUserData) => { + if (matchedUserData === null) { + // no existing record found + models.Member.create({ + discorduser: m.author.id, + email, + uuid: uuidv4(), + verified: 1, + }); + // mapping guild roles to find the crew role id + const targetRole = message.guild.roles.find('name', targetVerifiedRoleName); + message.member.addRole(targetRole).catch(util.log); + message.reply(verifyUser); + } else { + // existing record found + message.reply(userAlredyOnSystem); + } + }); + util.log('Collected', m.content, 3); + }); + collector.on('end', (collected) => { + const verificationTimeout = `!verify timeout. Clap ${collected.author.username} in irons! Let's see how well they dance on the plank!`; + util.log('Items', collected.size, 3); + if (collected.size === 0) { + // TODO: ping admin team on verification fail + message.reply(verificationTimeout); } - - // Do Stuff - const res = ctrl.action(message, ctrl, msg); - if (res) { - onSuccess(message, res, ctrl); + }); + // Set up Nodemailer to send emails through gmail + const sendVerifyCode = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL_USERNAME, + pass: process.env.EMAIL_PASS, + }, + }); + // Nodemailer email recipient & message + // TODO: Build email template + const mailOptions = { + from: process.env.EMAIL_USERNAME, + to: email, + subject: 'Armada Verification Code', + html: `

Enter the code below into Discord, in the same channel on the Armada Server. Verification will timeout after ${(timeoutInMiliseconds / 1000) / 60} minutes from first entering the !verify command.

Verification Code: ${code}

`, + }; + // Call sendMail on sendVerifyCode + // Pass mailOptions & callback function + sendVerifyCode.sendMail(mailOptions, (err, info) => { + const errorMsg = 'Oops, looks like the email can not be sent. It\'s not you, it\'s me. Please reach out to a moderator to help you verify.'; + if (err) { + message.reply(errorMsg); + util.log('Email not sent', err, 3); } else { - onError(message, res, ctrl); + util.log('Email details', info, 3); } - } - return ctrl; - }); - }; + }); + + util.log('Code', code, 3); + return `...What's the passcode? \n\n *eyes you suspicously*\n\n I just sent it to your email, just respond back to this channel within ${(timeoutInMiliseconds / 1000) / 60} minutes, with the code, and I won't treat you like a scurvy cur!`; + } else { + return 'Sorry, I can only verify Full Sail University email addresses.'; + } + } +} - return { - run: _run, - }; -}; +module.exports = VerifyController; From 75df9bc6566c742ec88818ac662b4032e8540e76 Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Wed, 17 Jan 2018 22:11:48 -0500 Subject: [PATCH 04/17] Remove code comments from client.js and roles ctrl Cleaned up excess code comments. Old code comments --- bot/client.js | 21 +----- bot/controllers/roles.js | 152 --------------------------------------- 2 files changed, 1 insertion(+), 172 deletions(-) diff --git a/bot/client.js b/bot/client.js index e419518..5bbe712 100644 --- a/bot/client.js +++ b/bot/client.js @@ -18,32 +18,13 @@ client.on('message', (message) => { if (message.content.charAt(0) === '!') { util.log('!Cmd Message Received', message.content, 0); - const ctrlResponses = []; - // Process message against every controller Object.keys(ctrls).forEach((ctrlKey) => { if (ctrlKey) { // initialize the controller const ctrlInstance = new ctrls[ctrlKey](message); - - /* - // Determine the method names of each controller - const controllerMethodKeys = Object.keys(ctrlInstance); - util.log('Methods found in controller', controllerMethodKeys, 3); - - // For each method on each controller - Object.keys(controllerMethodKeys).forEach((controllerMethodKey) => { - const methodName = controllerMethodKeys[controllerMethodKey]; - const method = ctrlInstance[methodName]; - - util.log('Looping Through Each Method within each Controller', method, 4); - - // Pass the message to each method of each controller - ctrlResponses.push(method(message)); - }); - - */ + util.log('Controller Instance', ctrlInstance, 5); } }); diff --git a/bot/controllers/roles.js b/bot/controllers/roles.js index fbb89a8..0428ea9 100644 --- a/bot/controllers/roles.js +++ b/bot/controllers/roles.js @@ -110,155 +110,3 @@ class RoleController extends BaseController { } module.exports = RoleController; - -/* module.exports = () => { - const util = require('apex-util'); - const Command = require('../baseCommand.js'); - - const _run = (message) => { - const disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; - - const rolesAction = (message) => { - const roles = []; - util.log('help!', message); - message.guild.roles.map((role) => { - if (!disallowedRoles.includes(role.name.toLowerCase())) { - return roles.push(role.name); - } - return role.name; - }); - return 'List of all Armada Roles: \n\n' + roles.join('\n'); - }; - - const addRoleAction = (message, ctrl, msg) => { - const targetRole = message.guild.roles.find('name', msg.parsed[1]); - if (targetRole === null) { - util.log('No role matched', msg.parsed[1], 2); - return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; - } else if (disallowedRoles.includes(targetRole.name.toLowerCase())) { - util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); - return 'You are not worthy of the role ' + msg.parsed[1] + '.'; - } else { - util.log('Adding Role to user', targetRole.name, 2); - message.member.addRole(targetRole).catch(util.log); - return 'Added the role "' + targetRole.name + '".'; - } - }; - - const addRolesAction = (message, ctrl, msg) => { - const roles = msg.parsed[1].split(','); - util.log('Multiple Roles Parsing', roles, 4); - - roles.map((role) => { - if (!disallowedRoles.includes(role.toLowerCase())) { - const targetRole = message.guild.roles.find('name', role); - util.log('Asking API for Role', targetRole, 4); - - if (targetRole === null) { - return '"' + role + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; - } - return message.member.addRole(targetRole).catch(util.log); - } - return role.name; - }); - - return 'All set!'; - }; - - const removeRoleAction = (message, ctrl, msg) => { - const targetRole = message.guild.roles.find('name', msg.parsed[1]); - if (targetRole === null) { - util.log('No role matched', msg.parsed[1], 2); - return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; - } - if (disallowedRoles.includes(targetRole.name.toLowerCase())) { - util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); - return 'You have not the power or the will to control this power.'; - } - - util.log('Removing role from user', targetRole.name, 2); - message.member.removeRole(targetRole).catch(util.log); - return targetRole.name + ' removed.'; - }; - - const addAllRolesAction = (message) => { - message.guild.roles.map((role) => { - if (!disallowedRoles.includes(role.name.toLowerCase())) { - return message.member.addRole(role).catch(util.log); - } - return role.name; - }); - - return 'Adding you to all Roles. You\'re going to be drinking from the firehose :sob:'; - }; - - const removeAllRolesAction = (message) => { - message.guild.roles.map((role) => { - if (!disallowedRoles.includes(role.name.toLowerCase())) { - return message.member.removeRole(role).catch(util.log); - } - return role.name; - }); - - return 'Removing all roles. Back to basics.'; - }; - - const ctrls = [ - new Command('!roles', '!roles', 'List All Roles', 'List all Armada Roles', rolesAction, 'dm'), - new Command('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.', addRoleAction), - new Command('!addRoles', '!addRoles ,,', 'Add Multiple Specific Roles', 'Add a single role to yourself. Role is case-sensitive.', addRolesAction), - new Command('!removeRole', '!removeRole ', 'Remove a single role', 'Remove a single game role from yourself. Role is case-sensitive.', removeRoleAction), - new Command('!addAllRoles', '!addAllRoles', 'Add All Roles', 'Add every game role to yourself.', addAllRolesAction), - new Command('!removeAllRoles', '!removeAllRoles', 'Remove All Roles', 'Remove every game role from yourself.', removeAllRolesAction), - ]; - - const onSuccess = (message, res, ctrl) => { - if (res !== null) { - if (ctrl.resType === 'reply') { - return message.reply(res); - } else if (ctrl.resType === 'dm') { - return message.author.send(res); - } - } - // Fail Safe - return false; - }; - - const onError = message => message.reply('I Broke... Beep...Boop...Beep'); - - const messageMiddleware = (message) => { - const container = {}; - container.parsed = message.content.split(' '); - const msg = Object.assign(message, container); - return msg; - }; - - ctrls.map((ctrl) => { - util.log('Running through controller', ctrl.cmd, 2); - - const msg = messageMiddleware(message); - if (msg.parsed[0].toLowerCase() === ctrl.cmd.toLowerCase()) { - util.log('!!! Matched Ctrl to Cmd !!!', ctrl.cmd, 2); - - // Ensure the communication is happening on a server - if (!ctrl.allowInDM) { - if (!message.guild) return onError(message, 'Please don\'t use this command directly. Instead use it in a channel on a server. :beers:'); - } - - // Do Stuff - const res = ctrl.action(message, ctrl, msg); - if (res) { - onSuccess(message, res, ctrl); - } else { - onError(message, res, ctrl); - } - } - return ctrl; - }); - }; - - return { - run: _run, - }; -}; -*/ From ad94440c037d4a86877234a8e3dd7f8aaeab3acf Mon Sep 17 00:00:00 2001 From: Nicole Cayouette Date: Thu, 18 Jan 2018 15:32:31 -0500 Subject: [PATCH 05/17] Controller Utility Class Added controllerUtils.js, contains a random code generation method. Aforementioned used in verify.js. --- bot/controllerUtils.js | 29 +++++++++++++++++++++++++++++ bot/controllers/verify.js | 32 ++++++++------------------------ 2 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 bot/controllerUtils.js diff --git a/bot/controllerUtils.js b/bot/controllerUtils.js new file mode 100644 index 0000000..378b28c --- /dev/null +++ b/bot/controllerUtils.js @@ -0,0 +1,29 @@ +class controllerUtils { + constructor() { + // Method to generate random numeric verification code + // Modified to fit style guide from this SO answer: + // https://stackoverflow.com/a/39774334 + this.generateCode = (n) => { + // Workaround method for Math.pow() and ** operator + const pow = (base, exp) => { + let result = 1; + for (let i = 0; i < exp; i += 1) { + result *= base; + } + return result; + }; + const add = 1; + let max = 12 - add; + let min = 0; + if (n > max) { + return this.generateCode(max) + this.generateCode(n - max); + } + max = pow(10, n + add); + min = max / 10; + const number = Math.floor(Math.random() * (max - (min + 1))) + min; + return ('' + number).substring(add); + }; + } +} + +module.exports = controllerUtils; diff --git a/bot/controllers/verify.js b/bot/controllers/verify.js index c16c765..27e5564 100644 --- a/bot/controllers/verify.js +++ b/bot/controllers/verify.js @@ -4,33 +4,11 @@ const util = require('apex-util'); const models = require('../../db/models'); const uuidv4 = require('uuid/v4'); const nodemailer = require('nodemailer'); +const controllerUtils = require('../controllerUtils.js'); class VerifyController extends BaseController { constructor(message) { super(message); - // Method to generate random numeric verification code - // Modified to fit style guide from this SO answer: - // https://stackoverflow.com/a/39774334 - this.generateCode = (n) => { - // Workaround method for Math.pow() and ** operator - const pow = (base, exp) => { - let result = 1; - for (let i = 0; i < exp; i += 1) { - result *= base; - } - return result; - }; - const add = 1; - let max = 12 - add; - let min = 0; - if (n > max) { - return this.generateCode(max) + this.generateCode(n - max); - } - max = pow(10, n + add); - min = max / 10; - const number = Math.floor(Math.random() * (max - (min + 1))) + min; - return ('' + number).substring(add); - }; this.ctrls = [ new Command('!verify', '!verify ', 'Verify Email Address', 'Verify user\'s email address', this.verifyAction), ]; @@ -43,11 +21,17 @@ class VerifyController extends BaseController { const timeoutInMiliseconds = 600000; const email = msg.parsed[1].toLowerCase(); const emailDomain = email.split('@').pop(); + // We can set `codeLength` to whatever length we want the verif code to be. // Recommend ngt 8 digits. if (validDomains.includes(emailDomain)) { const codeLength = 6; - const code = this.generateCode(codeLength); + + // Create instance of ControllerUtils + const contUtils = new controllerUtils(); + // code to equal value generated + const code = contUtils.generateCode(codeLength); + util.log('code', code, 3); // TODO: Set `time` prop to 600000 (10min) const collector = message.channel.createMessageCollector( From a098ce90ae41969aea663d8ccd4d917518e3ffd2 Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Thu, 18 Jan 2018 23:21:09 -0500 Subject: [PATCH 06/17] Fixed binding issue There was an issue accessing "this" from controller actions. This is now fixed by binding the controller. Cleaned up client.js and index.js to be more semantic and have better comments. --- bot/baseCommand.js | 4 +-- bot/baseController.js | 72 +++++++++++++++++++++------------------ bot/client.js | 27 ++++++++------- bot/controllers/index.js | 19 +++++------ bot/controllers/roles.js | 71 +++++++++++++++++++------------------- bot/controllers/verify.js | 11 +++--- 6 files changed, 105 insertions(+), 99 deletions(-) diff --git a/bot/baseCommand.js b/bot/baseCommand.js index 7ccbc9a..dcf1e40 100644 --- a/bot/baseCommand.js +++ b/bot/baseCommand.js @@ -1,6 +1,6 @@ class BaseCommand { - constructor(cmd, example, title, desc, action, resType = 'reply', showWithHelp = true, allowInDM = false) { - this.cmd = cmd; + constructor(command, example, title, desc, action, resType = 'reply', showWithHelp = true, allowInDM = false) { + this.command = command; this.example = example; this.title = title; this.desc = desc; diff --git a/bot/baseController.js b/bot/baseController.js index a13ca25..aec5414 100644 --- a/bot/baseController.js +++ b/bot/baseController.js @@ -2,56 +2,62 @@ const util = require('apex-util'); class BaseController { constructor(message) { - this.message = message; - this.ctrls = []; + // Add middleware to saved message + this.message = BaseController.messageMiddleware(message); + this.commands = []; } - onSuccess(res, ctrl) { - if (res !== null) { - if (ctrl.resType === 'reply') { - return this.message.reply(res); - } else if (ctrl.resType === 'dm') { - return this.message.author.send(res); + // Add extra information to message object + static messageMiddleware(message) { + // Creates an empty object container + const messageContainer = {}; + // Tokenizes the message by space characters, adds to container + messageContainer.parsed = message.content.split(' '); + // Adds message object to container + return Object.assign(message, messageContainer); + } + + onSuccess(commandResponse, command) { + if (commandResponse !== null) { + // Determine how to respond to message + if (command.resType === 'reply') { + return this.message.reply(commandResponse); + } else if (command.resType === 'dm') { + return this.message.author.send(commandResponse); } } - // Fail Safe + // Fail safe return false; } - // Not sure why error method accepted 3 params in "Do Stuff". - onError() { - this.message.reply('I Broke... Beep...Boop...Beep'); - } - - messageMiddleware() { - const container = {}; - container.parsed = this.message.content.split(' '); - const msg = Object.assign(this.message, container); - return msg; + onError(errorMessage = 'I Broke... Beep...Boop...Beep') { + return this.message.reply(errorMessage); } + // Execute the command's functionality run() { - this.ctrls.map((ctrl) => { - util.log('Running through controller', ctrl.cmd, 2); - - const msg = this.messageMiddleware(); - if (msg.parsed[0].toLowerCase() === ctrl.cmd.toLowerCase()) { - util.log('!!! Matched Ctrl to Cmd !!!', ctrl.cmd, 2); + // Loop through each command and map to key + util.log('Looping through controller commands', 0); + Object.keys(this.commands).map((key) => { + // If command matches message + if (this.message.parsed[0].toLowerCase() === this.commands[key].command.toLowerCase()) { + util.log('Matching command found', this.commands[key].command, 2); - // Ensure the communication is happening on a server - if (!ctrl.allowInDM) { - if (!this.message.guild) return this.onError('Please don\'t use this command directly. Instead use it in a channel on a server. :beers:'); + // If user messages the bot a channel-only command + if (!this.commands[key].allowInDM && !this.message.guild) { + return this.onError('Please don\'t use this command directly. Instead use it in a channel on a server. :beers:'); } - // Do Stuff - const res = ctrl.action(this.message, msg); - if (res) { - this.onSuccess(res, ctrl); + // Execute command's action + const commandResponse = this.commands[key].action(); + // Handle if command responds or breaks + if (commandResponse) { + this.onSuccess(commandResponse, this.commands[key]); } else { this.onError(); } } - return ctrl; + return this.commands[key]; }); } } diff --git a/bot/client.js b/bot/client.js index 5bbe712..5ed6a8d 100644 --- a/bot/client.js +++ b/bot/client.js @@ -1,31 +1,32 @@ const Discord = require('discord.js'); const util = require('apex-util'); -// If Production server default Debug mode to Production Setting +// If production server, set default debug mode to production setting if (process.env.NODE_ENV === 'production' && !process.env.DEBUG_MODE) process.env.DEBUG_MODE = 0; const client = new Discord.Client(); -// Pre Load Controllers -const ctrls = require('./controllers')(); +// Pre-load controllers +const controllers = require('./controllers')(); +// Alert when ready client.on('ready', () => { - util.log('Bot Online and Ready!', 0); + util.log('Bot Online and Ready', 0); }); +// Listen for messages client.on('message', (message) => { - // Check for ! Prefix on message to ensure it is a command + // Check for ! prefix on message to ensure it is a command if (message.content.charAt(0) === '!') { - util.log('!Cmd Message Received', message.content, 0); + util.log('Command message received', message.content, 0); // Process message against every controller - Object.keys(ctrls).forEach((ctrlKey) => { - if (ctrlKey) { - // initialize the controller - - const ctrlInstance = new ctrls[ctrlKey](message); - util.log('Controller Instance', ctrlInstance, 5); - } + Object.keys(controllers).forEach((key) => { + // Instantiate the controller + const controllerInstance = new controllers[key](message); + util.log('Controller instance', controllerInstance, 5); + // Runs commands after constructor is called + controllerInstance.run(); }); // TODO: Improve help message diff --git a/bot/controllers/index.js b/bot/controllers/index.js index eaa3c18..06947eb 100644 --- a/bot/controllers/index.js +++ b/bot/controllers/index.js @@ -2,13 +2,13 @@ const fs = require('fs'); const util = require('apex-util'); module.exports = () => { - const ctrls = []; + const controllers = []; const targetPath = './bot/controllers'; - const exts = ['js']; + const validExtensions = ['js']; - // const file = 'roles.js'; + // Auto-load controller files fs.readdirSync(targetPath).forEach((file) => { - // Skip Self Recursive Loading + // Skip self-recursive loading if (file === 'index.js') return false; // Split File name on "." @@ -18,14 +18,13 @@ module.exports = () => { const currentExt = splitFile[splitFile.length - 1]; // Check if extension is on approved array - if (exts.includes(currentExt)) { - ctrls.push(require(`./${file}`)); - // import/no-dynamic-require + if (validExtensions.includes(currentExt)) { + // Add controller to list + controllers.push(require(`./${file}`)); } - return true; }); - util.log('Return Controllers loaded within the target path of ' + targetPath, ctrls, 3); - return ctrls; + util.log('Controllers found within target path' + targetPath, controllers, 3); + return controllers; }; diff --git a/bot/controllers/roles.js b/bot/controllers/roles.js index 0428ea9..13c2f7b 100644 --- a/bot/controllers/roles.js +++ b/bot/controllers/roles.js @@ -5,24 +5,23 @@ const util = require('apex-util'); class RoleController extends BaseController { constructor(message) { super(message); - this.ctrls = [ - new Command('!roles', '!roles', 'List All Roles', 'List all Armada Roles', this.rolesAction, 'dm'), - new Command('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.', this.addRoleAction), - new Command('!addRoles', '!addRoles ,,', 'Add Multiple Specific Roles', 'Add a single role to yourself. Role is case-sensitive.', this.addRolesAction), - new Command('!removeRole', '!removeRole ', 'Remove a single role', 'Remove a single game role from yourself. Role is case-sensitive.', this.removeRoleAction), - new Command('!addAllRoles', '!addAllRoles', 'Add All Roles', 'Add every game role to yourself.', this.addAllRolesAction), - new Command('!removeAllRoles', '!removeAllRoles', 'Remove All Roles', 'Remove every game role from yourself.', this.removeAllRolesAction), + const controller = this; + this.commands = [ + new Command('!roles', '!roles', 'List All Roles', 'List all Armada Roles', this.rolesAction.bind(controller), 'dm'), + new Command('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.', this.addRoleAction.bind(controller)), + new Command('!addRoles', '!addRoles ,,', 'Add Multiple Specific Roles', 'Add a single role to yourself. Role is case-sensitive.', this.addRolesAction.bind(controller)), + new Command('!removeRole', '!removeRole ', 'Remove a single role', 'Remove a single game role from yourself. Role is case-sensitive.', this.removeRoleAction.bind(controller)), + new Command('!addAllRoles', '!addAllRoles', 'Add All Roles', 'Add every game role to yourself.', this.addAllRolesAction.bind(controller)), + new Command('!removeAllRoles', '!removeAllRoles', 'Remove All Roles', 'Remove every game role from yourself.', this.removeAllRolesAction.bind(controller)), ]; - this.disallowedRoles = []; - this.run(); + this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; } - rolesAction(message) { - this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + rolesAction() { + const { message, disallowedRoles } = this; const roles = []; - util.log('help!', message); message.guild.roles.map((role) => { - if (!this.disallowedRoles.includes(role.name.toLowerCase())) { + if (!disallowedRoles.includes(role.name.toLowerCase())) { return roles.push(role.name); } return role.name; @@ -30,15 +29,15 @@ class RoleController extends BaseController { return 'List of all Armada Roles: \n\n' + roles.join('\n'); } - addRoleAction(message, msg) { - this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; - const targetRole = message.guild.roles.find('name', msg.parsed[1]); + addRoleAction() { + const { message, disallowedRoles } = this; + const targetRole = message.guild.roles.find('name', message.parsed[1]); if (targetRole === null) { - util.log('No role matched', msg.parsed[1], 2); - return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; - } else if (this.disallowedRoles.includes(targetRole.name.toLowerCase())) { + util.log('No role matched', message.parsed[1], 2); + return '"' + message.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; + } else if (disallowedRoles.includes(targetRole.name.toLowerCase())) { util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); - return 'You are not worthy of the role ' + msg.parsed[1] + '.'; + return 'You are not worthy of the role ' + message.parsed[1] + '.'; } else { util.log('Adding Role to user', targetRole.name, 2); message.member.addRole(targetRole).catch(util.log); @@ -46,13 +45,13 @@ class RoleController extends BaseController { } } - addRolesAction(message, msg) { - this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; - const roles = msg.parsed[1].split(','); + addRolesAction() { + const { message, disallowedRoles } = this; + const roles = message.parsed[1].split(','); util.log('Multiple Roles Parsing', roles, 4); roles.map((role) => { - if (!this.disallowedRoles.includes(role.toLowerCase())) { + if (!disallowedRoles.includes(role.toLowerCase())) { const targetRole = message.guild.roles.find('name', role); util.log('Asking API for Role', targetRole, 4); @@ -67,14 +66,14 @@ class RoleController extends BaseController { return 'All set!'; } - removeRoleAction(message, msg) { - this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; - const targetRole = message.guild.roles.find('name', msg.parsed[1]); + removeRoleAction() { + const { message, disallowedRoles } = this; + const targetRole = message.guild.roles.find('name', message.parsed[1]); if (targetRole === null) { - util.log('No role matched', msg.parsed[1], 2); - return '"' + msg.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; + util.log('No role matched', message.parsed[1], 2); + return '"' + message.parsed[1] + '" is not a known role. Try `!roles` to get a list of all Roles (They are case-sensitive)'; } - if (this.disallowedRoles.includes(targetRole.name.toLowerCase())) { + if (disallowedRoles.includes(targetRole.name.toLowerCase())) { util.log('User Tried to add a restricted/dissalowed role', targetRole.name, 2); return 'You have not the power or the will to control this power.'; } @@ -84,10 +83,10 @@ class RoleController extends BaseController { return targetRole.name + ' removed.'; } - addAllRolesAction(message) { - this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + addAllRolesAction() { + const { message, disallowedRoles } = this; message.guild.roles.map((role) => { - if (!this.disallowedRoles.includes(role.name.toLowerCase())) { + if (!disallowedRoles.includes(role.name.toLowerCase())) { return message.member.addRole(role).catch(util.log); } return role.name; @@ -96,10 +95,10 @@ class RoleController extends BaseController { return 'Adding you to all Roles. You\'re going to be drinking from the firehose :sob:'; } - removeAllRolesAction(message) { - this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; + removeAllRolesAction() { + const { message, disallowedRoles } = this; message.guild.roles.map((role) => { - if (!this.disallowedRoles.includes(role.name.toLowerCase())) { + if (!disallowedRoles.includes(role.name.toLowerCase())) { return message.member.removeRole(role).catch(util.log); } return role.name; diff --git a/bot/controllers/verify.js b/bot/controllers/verify.js index 27e5564..14a19a9 100644 --- a/bot/controllers/verify.js +++ b/bot/controllers/verify.js @@ -9,17 +9,18 @@ const controllerUtils = require('../controllerUtils.js'); class VerifyController extends BaseController { constructor(message) { super(message); - this.ctrls = [ - new Command('!verify', '!verify ', 'Verify Email Address', 'Verify user\'s email address', this.verifyAction), + const controller = this; + this.commands = [ + new Command('!verify', '!verify ', 'Verify Email Address', 'Verify user\'s email address', this.verifyAction.bind(controller)), ]; - this.run(); } - verifyAction(message, msg) { + verifyAction() { + const { message } = this; const targetVerifiedRoleName = 'Crew'; const validDomains = ['student.fullsail.edu', 'fullsail.edu', 'fullsail.com']; const timeoutInMiliseconds = 600000; - const email = msg.parsed[1].toLowerCase(); + const email = message.parsed[1].toLowerCase(); const emailDomain = email.split('@').pop(); // We can set `codeLength` to whatever length we want the verif code to be. From 6d95c607c79f3dbf91b881828d3106d9ed01d263 Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Thu, 18 Jan 2018 23:45:46 -0500 Subject: [PATCH 07/17] Reorganized commands array, added comments Spread commands array out one item per line, added more semantic comments. --- bot/baseCommand.js | 6 ++-- bot/baseController.js | 4 +-- bot/controllers/roles.js | 67 +++++++++++++++++++++++++++++++++++---- bot/controllers/verify.js | 14 +++++++- 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/bot/baseCommand.js b/bot/baseCommand.js index dcf1e40..dd327de 100644 --- a/bot/baseCommand.js +++ b/bot/baseCommand.js @@ -1,12 +1,12 @@ class BaseCommand { - constructor(command, example, title, desc, action, resType = 'reply', showWithHelp = true, allowInDM = false) { + constructor(command, example, title, description, action, responseType = 'reply', showWithHelp = true, allowInDM = false) { this.command = command; this.example = example; this.title = title; - this.desc = desc; + this.description = description; this.showWithHelp = showWithHelp; this.allowInDM = allowInDM; - this.resType = resType; + this.responseType = responseType; this.action = action; } } diff --git a/bot/baseController.js b/bot/baseController.js index aec5414..8cb3d72 100644 --- a/bot/baseController.js +++ b/bot/baseController.js @@ -20,9 +20,9 @@ class BaseController { onSuccess(commandResponse, command) { if (commandResponse !== null) { // Determine how to respond to message - if (command.resType === 'reply') { + if (command.responseType === 'reply') { return this.message.reply(commandResponse); - } else if (command.resType === 'dm') { + } else if (command.responseType === 'dm') { return this.message.author.send(commandResponse); } } diff --git a/bot/controllers/roles.js b/bot/controllers/roles.js index 13c2f7b..af2130e 100644 --- a/bot/controllers/roles.js +++ b/bot/controllers/roles.js @@ -4,19 +4,67 @@ const util = require('apex-util'); class RoleController extends BaseController { constructor(message) { + // Call BaseController constructor super(message); + + // Aliasing 'this' as controller to allow for binding in actions const controller = this; + + // Array of all commands, see baseCommand.js for prototype this.commands = [ - new Command('!roles', '!roles', 'List All Roles', 'List all Armada Roles', this.rolesAction.bind(controller), 'dm'), - new Command('!addRole', '!addRole ', 'Add Role', 'Add a single role to yourself. Role is case-sensitive.', this.addRoleAction.bind(controller)), - new Command('!addRoles', '!addRoles ,,', 'Add Multiple Specific Roles', 'Add a single role to yourself. Role is case-sensitive.', this.addRolesAction.bind(controller)), - new Command('!removeRole', '!removeRole ', 'Remove a single role', 'Remove a single game role from yourself. Role is case-sensitive.', this.removeRoleAction.bind(controller)), - new Command('!addAllRoles', '!addAllRoles', 'Add All Roles', 'Add every game role to yourself.', this.addAllRolesAction.bind(controller)), - new Command('!removeAllRoles', '!removeAllRoles', 'Remove All Roles', 'Remove every game role from yourself.', this.removeAllRolesAction.bind(controller)), + new Command( + '!roles', + '!roles', + 'List All Roles', + 'List all Armada Roles', this.rolesAction.bind(controller), + 'dm', + ), + new Command( + '!addRole', + '!addRole ', + 'Add Role', + 'Add a single role to yourself. Role is case-sensitive.', + this.addRoleAction.bind(controller), + ), + new Command( + '!addRoles', + '!addRoles ,,', + 'Add Multiple Specific Roles', + 'Add a single role to yourself. Role is case-sensitive.', + this.addRolesAction.bind(controller), + ), + new Command( + '!removeRole', + '!removeRole ', + 'Remove a single role', + 'Remove a single game role from yourself. Role is case-sensitive.', + this.removeRoleAction.bind(controller), + ), + new Command( + '!addAllRoles', + '!addAllRoles', + 'Add All Roles', + 'Add every game role to yourself.', + this.addAllRolesAction.bind(controller), + ), + new Command( + '!removeAllRoles', + '!removeAllRoles', + 'Remove All Roles', + 'Remove every game role from yourself.', + this.removeAllRolesAction.bind(controller), + ), + ]; + + // User roles commands cannot change + this.disallowedRoles = [ + 'admin', 'armada officers', 'armada officer', + 'moderator', 'privateers', 'privateer', + 'tester', 'crew', 'fleet officer', '@everyone', ]; - this.disallowedRoles = ['admin', 'armada officers', 'armada officer', 'moderator', 'privateers', 'privateer', 'tester', 'crew', 'fleet officer', '@everyone']; } + // Lists all roles rolesAction() { const { message, disallowedRoles } = this; const roles = []; @@ -29,6 +77,7 @@ class RoleController extends BaseController { return 'List of all Armada Roles: \n\n' + roles.join('\n'); } + // Adds a role to the user addRoleAction() { const { message, disallowedRoles } = this; const targetRole = message.guild.roles.find('name', message.parsed[1]); @@ -45,6 +94,7 @@ class RoleController extends BaseController { } } + // Adds multiple roles to the user addRolesAction() { const { message, disallowedRoles } = this; const roles = message.parsed[1].split(','); @@ -66,6 +116,7 @@ class RoleController extends BaseController { return 'All set!'; } + // Removes role from user removeRoleAction() { const { message, disallowedRoles } = this; const targetRole = message.guild.roles.find('name', message.parsed[1]); @@ -83,6 +134,7 @@ class RoleController extends BaseController { return targetRole.name + ' removed.'; } + // Adds all roles to user addAllRolesAction() { const { message, disallowedRoles } = this; message.guild.roles.map((role) => { @@ -95,6 +147,7 @@ class RoleController extends BaseController { return 'Adding you to all Roles. You\'re going to be drinking from the firehose :sob:'; } + // Removes all roles from user removeAllRolesAction() { const { message, disallowedRoles } = this; message.guild.roles.map((role) => { diff --git a/bot/controllers/verify.js b/bot/controllers/verify.js index 14a19a9..5c4a994 100644 --- a/bot/controllers/verify.js +++ b/bot/controllers/verify.js @@ -8,13 +8,25 @@ const controllerUtils = require('../controllerUtils.js'); class VerifyController extends BaseController { constructor(message) { + // Call BaseController constructor super(message); + + // Aliasing 'this' as controller to allow for binding in actions const controller = this; + + // Array of all commands, see baseCommand.js for prototype this.commands = [ - new Command('!verify', '!verify ', 'Verify Email Address', 'Verify user\'s email address', this.verifyAction.bind(controller)), + new Command( + '!verify', + '!verify ', + 'Verify Email Address', + 'Verify user\'s email address', + this.verifyAction.bind(controller), + ), ]; } + // Verifies Full Sail email addresses verifyAction() { const { message } = this; const targetVerifiedRoleName = 'Crew'; From 70a292561871176e28938af7035e29f0326baa30 Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Fri, 19 Jan 2018 00:26:41 -0500 Subject: [PATCH 08/17] Cleaned up util file Fixed export for codeGenerator. --- bot/client.js | 15 +++++++++++-- bot/controllerUtils.js | 47 ++++++++++++++++----------------------- bot/controllers/verify.js | 7 ++---- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/bot/client.js b/bot/client.js index 5ed6a8d..2b4e28d 100644 --- a/bot/client.js +++ b/bot/client.js @@ -20,6 +20,9 @@ client.on('message', (message) => { if (message.content.charAt(0) === '!') { util.log('Command message received', message.content, 0); + // Build basic help string + let helpString = 'v1.3.2 Discovered Commands:'; + // Process message against every controller Object.keys(controllers).forEach((key) => { // Instantiate the controller @@ -27,11 +30,19 @@ client.on('message', (message) => { util.log('Controller instance', controllerInstance, 5); // Runs commands after constructor is called controllerInstance.run(); + + // Loop through commands if help command and add to string + if (message.content.toLowerCase() === '!help') { + Object.keys(controllerInstance.commands).forEach((commandKey) => { + const commandInstance = controllerInstance.commands[commandKey]; + helpString += `\n\n \`${commandInstance.example}\` \n\t ${commandInstance.description}`; + }); + } }); - // TODO: Improve help message + // If help command called, display string if (message.content.toLowerCase() === '!help') { - message.reply('v1.3.2 Discovered Commands: \n `!roles` \n\t List all Armada Roles \n\n `!addRole` RoleName \n\t Adds yourself to a role and the related text/voice rooms. \n\n `!removeRole` RoleName \n\t Removes a role from yourself. \n\n `!addAllRoles` \n\t Add all roles to yourself. \n\n `!removeAllRoles` \n\t Remove all roles from yourself. \n\n `!verify emailAddress@student.fullsail.com` \n\t Self verify that you are a Full Sail Student / Alumni via your Full Sail email account.'); + message.reply(helpString); } } }); diff --git a/bot/controllerUtils.js b/bot/controllerUtils.js index 378b28c..7251eec 100644 --- a/bot/controllerUtils.js +++ b/bot/controllerUtils.js @@ -1,29 +1,20 @@ -class controllerUtils { - constructor() { - // Method to generate random numeric verification code - // Modified to fit style guide from this SO answer: - // https://stackoverflow.com/a/39774334 - this.generateCode = (n) => { - // Workaround method for Math.pow() and ** operator - const pow = (base, exp) => { - let result = 1; - for (let i = 0; i < exp; i += 1) { - result *= base; - } - return result; - }; - const add = 1; - let max = 12 - add; - let min = 0; - if (n > max) { - return this.generateCode(max) + this.generateCode(n - max); - } - max = pow(10, n + add); - min = max / 10; - const number = Math.floor(Math.random() * (max - (min + 1))) + min; - return ('' + number).substring(add); - }; +exports.generateCode = (n) => { + // Workaround method for Math.pow() and ** operator + const pow = (base, exp) => { + let result = 1; + for (let i = 0; i < exp; i += 1) { + result *= base; + } + return result; + }; + const add = 1; + let max = 12 - add; + let min = 0; + if (n > max) { + return this.generateCode(max) + this.generateCode(n - max); } -} - -module.exports = controllerUtils; + max = pow(10, n + add); + min = max / 10; + const number = Math.floor(Math.random() * (max - (min + 1))) + min; + return ('' + number).substring(add); +}; diff --git a/bot/controllers/verify.js b/bot/controllers/verify.js index 5c4a994..2cc3091 100644 --- a/bot/controllers/verify.js +++ b/bot/controllers/verify.js @@ -4,7 +4,7 @@ const util = require('apex-util'); const models = require('../../db/models'); const uuidv4 = require('uuid/v4'); const nodemailer = require('nodemailer'); -const controllerUtils = require('../controllerUtils.js'); +const { generateCode } = require('../controllerUtils.js'); class VerifyController extends BaseController { constructor(message) { @@ -39,11 +39,8 @@ class VerifyController extends BaseController { // Recommend ngt 8 digits. if (validDomains.includes(emailDomain)) { const codeLength = 6; - - // Create instance of ControllerUtils - const contUtils = new controllerUtils(); // code to equal value generated - const code = contUtils.generateCode(codeLength); + const code = generateCode(codeLength); util.log('code', code, 3); // TODO: Set `time` prop to 600000 (10min) From 9badba71dd6853609d72eb377474d5d4c9433143 Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Fri, 19 Jan 2018 00:45:41 -0500 Subject: [PATCH 09/17] Added support to hide from help Set showWithHelp in command to false to hide command from help menu. --- bot/client.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/client.js b/bot/client.js index 2b4e28d..81036a2 100644 --- a/bot/client.js +++ b/bot/client.js @@ -35,7 +35,10 @@ client.on('message', (message) => { if (message.content.toLowerCase() === '!help') { Object.keys(controllerInstance.commands).forEach((commandKey) => { const commandInstance = controllerInstance.commands[commandKey]; - helpString += `\n\n \`${commandInstance.example}\` \n\t ${commandInstance.description}`; + // Check if command should be shown in help menu + if (commandInstance.showWithHelp) { + helpString += `\n\n \`${commandInstance.example}\` \n\t ${commandInstance.description}`; + } }); } }); From a7e6db6e1e5d460896288971122c6770eb70a718 Mon Sep 17 00:00:00 2001 From: Mark Langenhorst Date: Fri, 19 Jan 2018 00:56:19 -0600 Subject: [PATCH 10/17] adding channels.js for !announce functionality in new code format --- bot/controllers/channels.js | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 bot/controllers/channels.js diff --git a/bot/controllers/channels.js b/bot/controllers/channels.js new file mode 100644 index 0000000..e9028cf --- /dev/null +++ b/bot/controllers/channels.js @@ -0,0 +1,58 @@ +const BaseController = require('../baseController.js'); +const Command = require('../baseCommand.js'); +const util = require('apex-util'); + +class ChannelController extends BaseController { + constructor(message) { + super(message); + const controller = this; + this.commands = [ + new Command('!channels', '!channels', 'List All Channels', 'List all available Channels', this.channelsAction.bind(controller), 'dm'), + new Command('!announce', '!announce , ', 'Announce to other channels', 'Broadcast to multiple channels. Channels are case-sensitive.', this.announceAction.bind(controller)), + ]; + } + + channelsAction() { + const { message } = this; + const channels = []; + message.guild.channels.map((channel) => { + return channels.push(channel.name); + return channel.name; + }); + return 'List of all Armada Channels: \n\n' + channels.join('\n'); + } + + announceAction() { + const { message } = this; + const channels = message.parsed[1].split(','); + util.log('Multiple Channels Parsing', channels, 4); + + channels.map((channel) => { + const targetChannel = message.guild.channels.find('name', channel); + util.log('Asking API for Channel', targetChannel, 4); + + if (targetChannel === null) { + return '"' + channel + '" is not a known channel. Try `!channels` to get a list of all Channels (They are case-sensitive)'; + } + + // Set parsed value to 2 for message. + let msgParsedIndex = 2; + let preparedMessage = ''; + + // Loop through/join user message by space until end. + while (msgParsedIndex < message.parsed.length) { + // Add spaces after first word + if (msgParsedIndex !== 2) { + preparedMessage += ' '; + } + preparedMessage += message.parsed[msgParsedIndex]; + msgParsedIndex += 1; + } + return targetChannel.send(preparedMessage); + }); + + return 'Broadcast sent!'; + } +} + +module.exports = ChannelController; From 0dc4d7f0aac7eefc84c27bb7e7f41a06248d50c6 Mon Sep 17 00:00:00 2001 From: Mark Langenhorst Date: Fri, 19 Jan 2018 01:21:24 -0600 Subject: [PATCH 11/17] adding to channels.js --- bot/controllers/channels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/controllers/channels.js b/bot/controllers/channels.js index e9028cf..62988fc 100644 --- a/bot/controllers/channels.js +++ b/bot/controllers/channels.js @@ -8,7 +8,7 @@ class ChannelController extends BaseController { const controller = this; this.commands = [ new Command('!channels', '!channels', 'List All Channels', 'List all available Channels', this.channelsAction.bind(controller), 'dm'), - new Command('!announce', '!announce , ', 'Announce to other channels', 'Broadcast to multiple channels. Channels are case-sensitive.', this.announceAction.bind(controller)), + new Command('!announce', '!announce , ', 'Announce to other channels', 'Broadcast to multiple channels. Channels are case-sensitive.', this.announceAction.bind(controller), 'reply', false), ]; } From 5adcde291e2ce73ed011052edc8e9f4015c59f64 Mon Sep 17 00:00:00 2001 From: Mark Langenhorst Date: Fri, 19 Jan 2018 01:42:09 -0600 Subject: [PATCH 12/17] add username to announce message --- bot/controllers/channels.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/controllers/channels.js b/bot/controllers/channels.js index 62988fc..3f0d06d 100644 --- a/bot/controllers/channels.js +++ b/bot/controllers/channels.js @@ -29,6 +29,7 @@ class ChannelController extends BaseController { channels.map((channel) => { const targetChannel = message.guild.channels.find('name', channel); + const sender = message.author.username; util.log('Asking API for Channel', targetChannel, 4); if (targetChannel === null) { @@ -48,7 +49,7 @@ class ChannelController extends BaseController { preparedMessage += message.parsed[msgParsedIndex]; msgParsedIndex += 1; } - return targetChannel.send(preparedMessage); + return targetChannel.send(sender + " has an announcment: ```" + preparedMessage + "```"); }); return 'Broadcast sent!'; From 6186cfafdcba4c4930eab75aef04d14fddb8d53d Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Fri, 19 Jan 2018 03:21:13 -0500 Subject: [PATCH 13/17] Add admin-only option to commands Commands can now be admin-only. Admin roles are defined in baseController.js. --- bot/baseCommand.js | 7 +++- bot/baseController.js | 9 +++++ bot/controllers/channels.js | 66 ++++++++++++++++++++++--------------- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/bot/baseCommand.js b/bot/baseCommand.js index dd327de..224adc3 100644 --- a/bot/baseCommand.js +++ b/bot/baseCommand.js @@ -1,11 +1,16 @@ class BaseCommand { - constructor(command, example, title, description, action, responseType = 'reply', showWithHelp = true, allowInDM = false) { + constructor( + command, example, title, + description, action, responseType = 'reply', + showWithHelp = true, allowInDM = false, adminOnly = false, + ) { this.command = command; this.example = example; this.title = title; this.description = description; this.showWithHelp = showWithHelp; this.allowInDM = allowInDM; + this.adminOnly = adminOnly; this.responseType = responseType; this.action = action; } diff --git a/bot/baseController.js b/bot/baseController.js index 8cb3d72..691d0f4 100644 --- a/bot/baseController.js +++ b/bot/baseController.js @@ -5,6 +5,10 @@ class BaseController { // Add middleware to saved message this.message = BaseController.messageMiddleware(message); this.commands = []; + this.adminRoles = [ + 'admin', 'armada officers', 'armada officer', + 'moderator', 'tester', 'fleet officer', + ]; } // Add extra information to message object @@ -48,6 +52,11 @@ class BaseController { return this.onError('Please don\'t use this command directly. Instead use it in a channel on a server. :beers:'); } + // If non-admin enters admin command + if (this.commands[key].adminOnly && !this.adminRoles.some(role => this.message.member.roles.find('name', role))) { + return this.onError('Hey there, you don\'t have permissions to run this command.'); + } + // Execute command's action const commandResponse = this.commands[key].action(); // Handle if command responds or breaks diff --git a/bot/controllers/channels.js b/bot/controllers/channels.js index 3f0d06d..91d0a67 100644 --- a/bot/controllers/channels.js +++ b/bot/controllers/channels.js @@ -7,18 +7,32 @@ class ChannelController extends BaseController { super(message); const controller = this; this.commands = [ - new Command('!channels', '!channels', 'List All Channels', 'List all available Channels', this.channelsAction.bind(controller), 'dm'), - new Command('!announce', '!announce , ', 'Announce to other channels', 'Broadcast to multiple channels. Channels are case-sensitive.', this.announceAction.bind(controller), 'reply', false), + new Command( + '!channels', + '!channels', + 'List All Channels', + 'List all available Channels', + this.channelsAction.bind(controller), + 'dm', + ), + new Command( + '!announce', + '!announce , ', + 'Announce to other channels', + 'Broadcast to multiple channels. Channels are case-sensitive.', + this.announceAction.bind(controller), + 'reply', + false, + false, + true, + ), ]; } channelsAction() { const { message } = this; const channels = []; - message.guild.channels.map((channel) => { - return channels.push(channel.name); - return channel.name; - }); + message.guild.channels.map(channel => channels.push(channel.name)); return 'List of all Armada Channels: \n\n' + channels.join('\n'); } @@ -28,29 +42,29 @@ class ChannelController extends BaseController { util.log('Multiple Channels Parsing', channels, 4); channels.map((channel) => { - const targetChannel = message.guild.channels.find('name', channel); - const sender = message.author.username; - util.log('Asking API for Channel', targetChannel, 4); + const targetChannel = message.guild.channels.find('name', channel); + const sender = message.author.username; + util.log('Asking API for Channel', targetChannel, 4); - if (targetChannel === null) { - return '"' + channel + '" is not a known channel. Try `!channels` to get a list of all Channels (They are case-sensitive)'; - } + if (targetChannel === null) { + return '"' + channel + '" is not a known channel. Try `!channels` to get a list of all Channels (They are case-sensitive)'; + } - // Set parsed value to 2 for message. - let msgParsedIndex = 2; - let preparedMessage = ''; - - // Loop through/join user message by space until end. - while (msgParsedIndex < message.parsed.length) { - // Add spaces after first word - if (msgParsedIndex !== 2) { - preparedMessage += ' '; - } - preparedMessage += message.parsed[msgParsedIndex]; - msgParsedIndex += 1; + // Set parsed value to 2 for message. + let msgParsedIndex = 2; + let preparedMessage = ''; + + // Loop through/join user message by space until end. + while (msgParsedIndex < message.parsed.length) { + // Add spaces after first word + if (msgParsedIndex !== 2) { + preparedMessage += ' '; } - return targetChannel.send(sender + " has an announcment: ```" + preparedMessage + "```"); - }); + preparedMessage += message.parsed[msgParsedIndex]; + msgParsedIndex += 1; + } + return targetChannel.send(sender + ' has an announcment: ```' + preparedMessage + '```'); + }); return 'Broadcast sent!'; } From 4a9f52414072227dbc9f592aeb02aa2eac7c0c1c Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Fri, 19 Jan 2018 10:19:16 -0500 Subject: [PATCH 14/17] New isAdmin util, help hides admin cmds controllerUtils.js is now botUtils.js, a new isAdmin util can check if a guild member object is an admin. The help command now hides admin-only commands from non-admin users. --- bot/baseCommand.js | 2 +- bot/baseController.js | 9 +++------ bot/{controllerUtils.js => botUtils.js} | 12 ++++++++++++ bot/client.js | 9 ++++++++- bot/controllers/channels.js | 2 -- bot/controllers/verify.js | 2 +- 6 files changed, 25 insertions(+), 11 deletions(-) rename bot/{controllerUtils.js => botUtils.js} (61%) diff --git a/bot/baseCommand.js b/bot/baseCommand.js index 224adc3..2ccb006 100644 --- a/bot/baseCommand.js +++ b/bot/baseCommand.js @@ -2,7 +2,7 @@ class BaseCommand { constructor( command, example, title, description, action, responseType = 'reply', - showWithHelp = true, allowInDM = false, adminOnly = false, + adminOnly = false, showWithHelp = true, allowInDM = false, ) { this.command = command; this.example = example; diff --git a/bot/baseController.js b/bot/baseController.js index 691d0f4..4f1e8d2 100644 --- a/bot/baseController.js +++ b/bot/baseController.js @@ -1,14 +1,11 @@ const util = require('apex-util'); +const { isAdmin } = require('./botUtils.js'); class BaseController { constructor(message) { // Add middleware to saved message this.message = BaseController.messageMiddleware(message); this.commands = []; - this.adminRoles = [ - 'admin', 'armada officers', 'armada officer', - 'moderator', 'tester', 'fleet officer', - ]; } // Add extra information to message object @@ -53,8 +50,8 @@ class BaseController { } // If non-admin enters admin command - if (this.commands[key].adminOnly && !this.adminRoles.some(role => this.message.member.roles.find('name', role))) { - return this.onError('Hey there, you don\'t have permissions to run this command.'); + if (this.commands[key].adminOnly && !isAdmin(this.message.member)) { + return this.onError('You don\'t have permissions to run this command.'); } // Execute command's action diff --git a/bot/controllerUtils.js b/bot/botUtils.js similarity index 61% rename from bot/controllerUtils.js rename to bot/botUtils.js index 7251eec..1f46b34 100644 --- a/bot/controllerUtils.js +++ b/bot/botUtils.js @@ -18,3 +18,15 @@ exports.generateCode = (n) => { const number = Math.floor(Math.random() * (max - (min + 1))) + min; return ('' + number).substring(add); }; + +// Checks if person is an admin user, use GuildMember object +exports.isAdmin = (member) => { + const adminRoles = [ + 'admin', 'armada officers', 'armada officer', + 'moderator', 'tester', 'fleet officer', + ]; + if (adminRoles.some(role => member.roles.find('name', role))) { + return true; + } + return false; +}; diff --git a/bot/client.js b/bot/client.js index 81036a2..7c109b9 100644 --- a/bot/client.js +++ b/bot/client.js @@ -1,5 +1,6 @@ const Discord = require('discord.js'); const util = require('apex-util'); +const { isAdmin } = require('./botUtils.js'); // If production server, set default debug mode to production setting if (process.env.NODE_ENV === 'production' && !process.env.DEBUG_MODE) process.env.DEBUG_MODE = 0; @@ -37,7 +38,13 @@ client.on('message', (message) => { const commandInstance = controllerInstance.commands[commandKey]; // Check if command should be shown in help menu if (commandInstance.showWithHelp) { - helpString += `\n\n \`${commandInstance.example}\` \n\t ${commandInstance.description}`; + if (commandInstance.adminOnly && isAdmin(message.member)) { + helpString += `\n\n \`${commandInstance.example}\` - Admin Only \n\t ${commandInstance.description}`; + } else if (commandInstance.adminOnly && !isAdmin(message.member)) { + helpString += ''; + } else { + helpString += `\n\n \`${commandInstance.example}\` \n\t ${commandInstance.description}`; + } } }); } diff --git a/bot/controllers/channels.js b/bot/controllers/channels.js index 91d0a67..f950fd9 100644 --- a/bot/controllers/channels.js +++ b/bot/controllers/channels.js @@ -22,8 +22,6 @@ class ChannelController extends BaseController { 'Broadcast to multiple channels. Channels are case-sensitive.', this.announceAction.bind(controller), 'reply', - false, - false, true, ), ]; diff --git a/bot/controllers/verify.js b/bot/controllers/verify.js index 2cc3091..89d0bd5 100644 --- a/bot/controllers/verify.js +++ b/bot/controllers/verify.js @@ -4,7 +4,7 @@ const util = require('apex-util'); const models = require('../../db/models'); const uuidv4 = require('uuid/v4'); const nodemailer = require('nodemailer'); -const { generateCode } = require('../controllerUtils.js'); +const { generateCode } = require('../botUtils.js'); class VerifyController extends BaseController { constructor(message) { From 4415296e0c35864eb8e536f89a0b5b62a79e4aad Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Fri, 19 Jan 2018 10:39:32 -0500 Subject: [PATCH 15/17] Cleaned up !help command Made sure formatting was consistent across all command information. Added formatting and hints for required and optional items. --- bot/client.js | 4 ++-- bot/controllers/channels.js | 6 +++--- bot/controllers/roles.js | 13 +++++++------ bot/controllers/verify.js | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/bot/client.js b/bot/client.js index 7c109b9..f9d0802 100644 --- a/bot/client.js +++ b/bot/client.js @@ -22,7 +22,7 @@ client.on('message', (message) => { util.log('Command message received', message.content, 0); // Build basic help string - let helpString = 'v1.3.2 Discovered Commands:'; + let helpString = 'v1.3.2 Discovered Commands:\n\n\t**<> - Required Item\t\t[] - Optional Item**'; // Process message against every controller Object.keys(controllers).forEach((key) => { @@ -39,7 +39,7 @@ client.on('message', (message) => { // Check if command should be shown in help menu if (commandInstance.showWithHelp) { if (commandInstance.adminOnly && isAdmin(message.member)) { - helpString += `\n\n \`${commandInstance.example}\` - Admin Only \n\t ${commandInstance.description}`; + helpString += `\n\n \`${commandInstance.example}\` **- Admin Only** \n\t ${commandInstance.description}`; } else if (commandInstance.adminOnly && !isAdmin(message.member)) { helpString += ''; } else { diff --git a/bot/controllers/channels.js b/bot/controllers/channels.js index f950fd9..316060d 100644 --- a/bot/controllers/channels.js +++ b/bot/controllers/channels.js @@ -11,14 +11,14 @@ class ChannelController extends BaseController { '!channels', '!channels', 'List All Channels', - 'List all available Channels', + 'List all available Armada channels.', this.channelsAction.bind(controller), 'dm', ), new Command( '!announce', - '!announce , ', - 'Announce to other channels', + '!announce ,[channel_name] ', + 'Announce To Channels', 'Broadcast to multiple channels. Channels are case-sensitive.', this.announceAction.bind(controller), 'reply', diff --git a/bot/controllers/roles.js b/bot/controllers/roles.js index af2130e..19bd016 100644 --- a/bot/controllers/roles.js +++ b/bot/controllers/roles.js @@ -16,7 +16,8 @@ class RoleController extends BaseController { '!roles', '!roles', 'List All Roles', - 'List all Armada Roles', this.rolesAction.bind(controller), + 'List all available Armada roles.', + this.rolesAction.bind(controller), 'dm', ), new Command( @@ -28,16 +29,16 @@ class RoleController extends BaseController { ), new Command( '!addRoles', - '!addRoles ,,', - 'Add Multiple Specific Roles', - 'Add a single role to yourself. Role is case-sensitive.', + '!addRoles ,[role_name]', + 'Add Multiple Roles', + 'Add multiple roles to yourself. Rolea are case-sensitive.', this.addRolesAction.bind(controller), ), new Command( '!removeRole', '!removeRole ', - 'Remove a single role', - 'Remove a single game role from yourself. Role is case-sensitive.', + 'Remove Role', + 'Remove a single role from yourself. Role is case-sensitive.', this.removeRoleAction.bind(controller), ), new Command( diff --git a/bot/controllers/verify.js b/bot/controllers/verify.js index 89d0bd5..552661c 100644 --- a/bot/controllers/verify.js +++ b/bot/controllers/verify.js @@ -20,7 +20,7 @@ class VerifyController extends BaseController { '!verify', '!verify ', 'Verify Email Address', - 'Verify user\'s email address', + 'Verify your Full Sail email address. Must be @student.fullsail.edu or @fullsail.com.', this.verifyAction.bind(controller), ), ]; From c60258d73f18b7b4d49ace1bdec5bf84bcec63bc Mon Sep 17 00:00:00 2001 From: Luke Billington Date: Fri, 19 Jan 2018 12:59:14 -0500 Subject: [PATCH 16/17] Removed outdated roles unit test Test on `roles.js` checked for # of functions, controller file now uses a class, rendering test ineffective. --- test/__roles.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/__roles.js b/test/__roles.js index f14c2c9..46f5e18 100644 --- a/test/__roles.js +++ b/test/__roles.js @@ -1,4 +1,3 @@ -const roles = require('../bot/controllers/roles'); const expect = require('chai').expect; describe('Sanity Testing', () => { @@ -6,12 +5,3 @@ describe('Sanity Testing', () => { expect(1).to.be.equal(1); }); }); - - -describe('Roles Testing', () => { - it('roles() has >1 Methods attatched', () => { - // Instance the roles - const target = Object.keys(roles()); - expect(target.length).to.be.above(0); - }); -}); From 7b2c420484df12b37a5c55cec69329b336fac2bd Mon Sep 17 00:00:00 2001 From: Chris Chapman Date: Tue, 20 Feb 2018 14:40:55 -0500 Subject: [PATCH 17/17] Fixed Case issue with isAdmin() --- bot/botUtils.js | 4 ++-- bot/client.js | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/botUtils.js b/bot/botUtils.js index 1f46b34..6ce3997 100644 --- a/bot/botUtils.js +++ b/bot/botUtils.js @@ -22,8 +22,8 @@ exports.generateCode = (n) => { // Checks if person is an admin user, use GuildMember object exports.isAdmin = (member) => { const adminRoles = [ - 'admin', 'armada officers', 'armada officer', - 'moderator', 'tester', 'fleet officer', + 'Admin', 'Armada Officers', 'Armada Officer', 'Fleet Officer', + 'Moderator', 'Tester', ]; if (adminRoles.some(role => member.roles.find('name', role))) { return true; diff --git a/bot/client.js b/bot/client.js index f9d0802..18d9cc9 100644 --- a/bot/client.js +++ b/bot/client.js @@ -22,7 +22,7 @@ client.on('message', (message) => { util.log('Command message received', message.content, 0); // Build basic help string - let helpString = 'v1.3.2 Discovered Commands:\n\n\t**<> - Required Item\t\t[] - Optional Item**'; + let helpString = 'v1.4.0 Discovered Commands:\n\n\t**<> - Required Item\t\t[] - Optional Item**'; // Process message against every controller Object.keys(controllers).forEach((key) => { diff --git a/package.json b/package.json index dd5f91b..4430557 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "max", - "version": "1.3.2", + "version": "1.4.0", "description": "", "main": "bot/client.js", "engines": {