Skip to content

Commit

Permalink
Merge pull request #109 from reactivepixel/dev
Browse files Browse the repository at this point in the history
1.4.0 Release: Controller Refactor
  • Loading branch information
reactivepixel authored Feb 20, 2018
2 parents 5de7a73 + 7b2c420 commit 8f7fb8a
Show file tree
Hide file tree
Showing 10 changed files with 494 additions and 408 deletions.
18 changes: 18 additions & 0 deletions bot/baseCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class BaseCommand {
constructor(
command, example, title,
description, action, responseType = 'reply',
adminOnly = false, showWithHelp = true, allowInDM = 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;
}
}
module.exports = BaseCommand;
70 changes: 70 additions & 0 deletions bot/baseController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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 = [];
}

// 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.responseType === 'reply') {
return this.message.reply(commandResponse);
} else if (command.responseType === 'dm') {
return this.message.author.send(commandResponse);
}
}
// Fail safe
return false;
}

onError(errorMessage = 'I Broke... Beep...Boop...Beep') {
return this.message.reply(errorMessage);
}

// Execute the command's functionality
run() {
// 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);

// 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:');
}

// If non-admin enters admin 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
const commandResponse = this.commands[key].action();
// Handle if command responds or breaks
if (commandResponse) {
this.onSuccess(commandResponse, this.commands[key]);
} else {
this.onError();
}
}
return this.commands[key];
});
}
}
module.exports = BaseController;
32 changes: 32 additions & 0 deletions bot/botUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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);
}
max = pow(10, n + add);
min = max / 10;
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', 'Fleet Officer',
'Moderator', 'Tester',
];
if (adminRoles.some(role => member.roles.find('name', role))) {
return true;
}
return false;
};
61 changes: 34 additions & 27 deletions bot/client.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
const Discord = require('discord.js');
const util = require('apex-util');
const { isAdmin } = require('./botUtils.js');

// 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);

const ctrlResponses = [];
// Build basic help string
let helpString = 'v1.4.0 Discovered Commands:\n\n\t**<> - Required Item\t\t[] - Optional Item**';

// Process message against every controller
Object.keys(ctrls).forEach((ctrlKey) => {
if (ctrlKey) {
// initialize the controller
const ctrlInstance = ctrls[ctrlKey]();

// 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));
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();

// 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];
// 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}`;
} else if (commandInstance.adminOnly && !isAdmin(message.member)) {
helpString += '';
} else {
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 [email protected]` \n\t Self verify that you are a Full Sail Student / Alumni via your Full Sail email account.');
message.reply(helpString);
}
}
});
Expand Down
71 changes: 71 additions & 0 deletions bot/controllers/channels.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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 Armada channels.',
this.channelsAction.bind(controller),
'dm',
),
new Command(
'!announce',
'!announce <channel_name>,[channel_name] <message>',
'Announce To Channels',
'Broadcast to multiple channels. Channels are case-sensitive.',
this.announceAction.bind(controller),
'reply',
true,
),
];
}

channelsAction() {
const { message } = this;
const channels = [];
message.guild.channels.map(channel => channels.push(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);
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)';
}

// 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(sender + ' has an announcment: ```' + preparedMessage + '```');
});

return 'Broadcast sent!';
}
}

module.exports = ChannelController;
19 changes: 9 additions & 10 deletions bot/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 "."
Expand All @@ -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;
};
Loading

0 comments on commit 8f7fb8a

Please sign in to comment.