From d3422138ca5b9f08ea2b8f182f727466f94ada58 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 3 Apr 2018 19:28:33 -0400 Subject: [PATCH] Revert "Removed commands: ban, clearWarnings, warn, warnings. Removed data.js in favor of inline fs.readFile. Deleted models. Deleted server.js warnings. Removed warning related functionality from state.js. Changed dep. discord.js function sendMessage to send. Cleanup." This reverts commit b9a9fce06b90cf64aeae867fe870cf503883d3a9. --- src/commands/ban.js | 24 +++++++++++++++++ src/commands/clearWarnings.js | 20 ++++++++++++++ src/commands/grantDeveloper.js | 6 ++--- src/commands/quote.js | 2 +- src/commands/warn.js | 24 +++++++++++++++++ src/commands/warnings.js | 8 ++++++ src/data.js | 49 ++++++++++++++++++++++++++++++++++ src/models/UserBan.js | 12 +++++++++ src/models/UserWarning.js | 12 +++++++++ src/server.js | 49 ++++++++++++++++++++++------------ src/state.js | 5 +++- src/triggers/github.js | 2 +- 12 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 src/commands/ban.js create mode 100644 src/commands/clearWarnings.js create mode 100644 src/commands/warn.js create mode 100644 src/commands/warnings.js create mode 100644 src/data.js create mode 100644 src/models/UserBan.js create mode 100644 src/models/UserWarning.js diff --git a/src/commands/ban.js b/src/commands/ban.js new file mode 100644 index 0000000..21d682c --- /dev/null +++ b/src/commands/ban.js @@ -0,0 +1,24 @@ +const state = require('../state.js'); +const data = require('../data.js'); +const logger = require('../logging.js'); +const UserBan = require('../models/UserBan.js'); + +exports.roles = ['Admins', 'Moderators', 'CitraBot']; +exports.command = function (message) { + message.mentions.users.map((user) => { + var count = state.warnings.filter(x => x.id === user.id && !x.cleared).length || 0; + + message.channel.sendMessage(`${user} ${user.username}, You will now be banned from this channel.`); + logger.info(`${message.author.toString()} has banned ${user.toString()} ${user} ${user.username}.`); + state.logChannel.sendMessage(`${message.author} has banned ${user} ${user.username} [${count}].`); + + state.bans.push(new UserBan(user.id, user.username, message.author.id, message.author.username, count)); + + message.guild.member(user).ban().catch(function (error) { + state.logChannel.sendMessage(`Error banning ${user} ${user.username}`); + logger.error(`Error banning ${user.toString()} ${user} ${user.username}.`, error); + }); + + data.flushBans(); + }); +}; diff --git a/src/commands/clearWarnings.js b/src/commands/clearWarnings.js new file mode 100644 index 0000000..143eb62 --- /dev/null +++ b/src/commands/clearWarnings.js @@ -0,0 +1,20 @@ +const state = require('../state.js'); +const data = require('../data.js'); +const logger = require('../logging.js'); + +exports.roles = ['Admins', 'Moderators']; +exports.command = function (message) { + message.mentions.users.map((user) => { + var count = state.warnings.filter(x => x.id === user.id && !x.cleared); + if (count != null && count.length > 0) { + count.forEach(warning => { warning.cleared = true; }); + data.flushWarnings(); + message.channel.sendMessage(`${user}, your warnings have been cleared.`); + } else { + message.channel.sendMessage(`${user}, you have no warnings to clear.`); + } + + logger.info(`${message.author.toString()} has cleared all warnings for ${user.toString()} [${count}].`); + state.logChannel.sendMessage(`${message.author.toString()} has cleared all warnings for ${user.toString()} [${count}].`); + }); +}; diff --git a/src/commands/grantDeveloper.js b/src/commands/grantDeveloper.js index 525ab85..7f6b66f 100644 --- a/src/commands/grantDeveloper.js +++ b/src/commands/grantDeveloper.js @@ -1,4 +1,4 @@ -exports.roles = ['Admins', 'Moderators']; +exports.roles = ['Admins', 'Moderators', 'CitraBot']; exports.command = function (message) { var role = '345247291843805185'; message.mentions.users.map((user) => { @@ -7,10 +7,10 @@ exports.command = function (message) { if (alreadyJoined) { member.removeRole(role); - message.channel.send(`${user}'s speech has been revoked in the #development channel.`); + message.channel.sendMessage(`${user}'s speech has been revoked in the #development channel.`); } else { member.addRole(role); - message.channel.send(`${user} has been granted speech in the #development channel.`); + message.channel.sendMessage(`${user} has been granted speech in the #development channel.`); } }); } diff --git a/src/commands/quote.js b/src/commands/quote.js index 88d1737..7f1c36b 100644 --- a/src/commands/quote.js +++ b/src/commands/quote.js @@ -7,5 +7,5 @@ exports.command = function (message, reply) { replyMessage = `${message.mentions.users.map(user => `${user}`)} ${reply}`; } - message.channel.send(replyMessage); + message.channel.sendMessage(replyMessage); }; diff --git a/src/commands/warn.js b/src/commands/warn.js new file mode 100644 index 0000000..de6e796 --- /dev/null +++ b/src/commands/warn.js @@ -0,0 +1,24 @@ +const state = require('../state.js'); +const data = require('../data.js'); +const logger = require('../logging.js'); +const UserWarning = require('../models/UserWarning.js'); + +exports.roles = ['Admins', 'Moderators']; +exports.command = function (message) { + message.mentions.users.map((user) => { + var count = state.warnings.filter(x => x.id === user.id && !x.cleared).length || 0; + message.channel.sendMessage(`${user} You have been warned. Additional infractions may result in a ban.`); + + logger.info(`${message.author.username} ${message.author} has warned ${user.username} ${user} [${count} + 1].`); + state.logChannel.sendMessage(`${message.author} has warned ${user} [${count} + 1].`); + + state.warnings.push(new UserWarning(user.id, user.username, message.author.id, message.author.username, count)); + data.flushWarnings(); + + state.stats.warnings += 1; + + if (count + 1 >= 3) { + state.logChannel.sendMessage(`.ban ${user}`); + } + }); +}; diff --git a/src/commands/warnings.js b/src/commands/warnings.js new file mode 100644 index 0000000..3fcbb3a --- /dev/null +++ b/src/commands/warnings.js @@ -0,0 +1,8 @@ +const state = require('../state.js'); + +exports.command = function (message) { + message.mentions.users.map((user) => { + var warnings = state.warnings.filter(x => x.id === user.id && !x.cleared); + message.channel.sendMessage(`${user}, you have ${warnings.length} total warnings.`); + }); +}; diff --git a/src/data.js b/src/data.js new file mode 100644 index 0000000..52fc336 --- /dev/null +++ b/src/data.js @@ -0,0 +1,49 @@ +const fs = require('fs'); +const state = require('./state.js'); +const logger = require('./logging.js'); + +function readWarnings () { + // Load the warnings file into the bans variable. + fs.readFile('/data/discordWarnings.json', 'utf8', function (err, data) { + if (err) { throw err; } + state.warnings = JSON.parse(data); + logger.debug('Loaded warnings file.'); + }); +} + +function readBans () { + // Load the ban file into the bans variable. + fs.readFile('/data/discordBans.json', 'utf8', function (err, data) { + if (err) { throw err; } + state.bans = JSON.parse(data); + logger.debug('Loaded bans file.'); + }); +} + +function readCustomResponses() +{ + // Load the responses file into the responses variable. + fs.readFile('/data/responses.json', 'utf8', function (err, data) { + if (err) { throw err; } + state.responses = JSON.parse(data); + logger.debug('Loaded responses file from external source.'); + }); +} + +function flushWarnings () { + var warningsJson = JSON.stringify(state.warnings, null, 4); + if (!fs.existsSync('./data/')) fs.mkdirSync('data'); + fs.writeFile('/data/discordWarnings.json', warningsJson, 'utf8', function (err) { + if (err) { logger.error(err); } + }); +} + +function flushBans () { + var bansJson = JSON.stringify(state.bans, null, 4); + if (!fs.existsSync('data')) fs.mkdirSync('data'); + fs.writeFile('/data/discordBans.json', bansJson, 'utf8', function (err) { + if (err) { logger.error(err); } + }); +} + +module.exports = { readWarnings: readWarnings, readBans: readBans, readCustomResponses: readCustomResponses, flushWarnings: flushWarnings, flushBans: flushBans }; diff --git a/src/models/UserBan.js b/src/models/UserBan.js new file mode 100644 index 0000000..b16f629 --- /dev/null +++ b/src/models/UserBan.js @@ -0,0 +1,12 @@ +class UserBan { + constructor (id, username, warnedBy, warnedByUsername, priorWarnings) { + this.id = id; + this.username = username; + this.date = new Date(); + this.warnedBy = warnedBy; + this.warnedByUsername = warnedByUsername; + this.priorWarnings = priorWarnings; + } +} + +module.exports = UserBan; diff --git a/src/models/UserWarning.js b/src/models/UserWarning.js new file mode 100644 index 0000000..81675ee --- /dev/null +++ b/src/models/UserWarning.js @@ -0,0 +1,12 @@ +class UserWarning { + constructor (id, username, warnedBy, warnedByUsername, priorWarnings) { + this.id = id; + this.username = username; + this.date = new Date(); + this.warnedBy = warnedBy; + this.warnedByUsername = warnedByUsername; + this.priorWarnings = priorWarnings; + } +} + +module.exports = UserWarning; diff --git a/src/server.js b/src/server.js index 94d8d80..a841ebc 100644 --- a/src/server.js +++ b/src/server.js @@ -8,23 +8,16 @@ const fs = require('fs'); const logger = require('./logging.js'); const state = require('./state.js'); +const data = require('./data.js'); -// Load custom responses -if (process.env.DATA_CUSTOM_RESPONSES) { - fs.readFile('/data/responses.json', 'utf8', function (err, data) { - if (err) { throw err; } - state.responses = JSON.parse(data); - logger.debug('Loaded responses file from external source.'); - }); -} else { - state.responses = require('./responses.json'); - logger.warn(`Loaded resources file from the default, no external source found.`); -} +state.responses = require('./responses.json'); var cachedModules = []; var cachedTriggers = []; var client = new discord.Client(); +logger.info('Application startup. Configuring environment.'); + process.on('unhandledRejection', (error, promise) => { logger.error(`Unhandled promise rejection: ${error.message}.`, error); }); @@ -59,15 +52,14 @@ client.on('guildMemberRemove', (member) => { // Output the stats for state.stats every 24 hours. // Server is in UTC mode, 11:30 EST would be 03:30 UTC. schedule.scheduleJob({ hour: 3, minute: 30 }, function () { - logger.info(`Here are today's stats for ${(new Date()).toLocaleDateString()}! ${state.stats.joins} users have joined, ${state.stats.ruleAccepts} users have accepted the rules, ${state.stats.leaves} users have left.`, { - meta: { stats: state.stats } - }); - state.logChannel.send(`Here are today's stats for ${(new Date()).toLocaleDateString()}! ${state.stats.joins} users have joined, ${state.stats.ruleAccepts} users have accepted the rules, ${state.stats.leaves} users have left.`); + logger.info(`Here are today's stats for ${(new Date()).toLocaleDateString()}! ${state.stats.joins} users have joined, ${state.stats.ruleAccepts} users have accepted the rules, ${state.stats.leaves} users have left, ${state.stats.warnings} warnings have been issued.`); + state.logChannel.sendMessage(`Here are today's stats for ${(new Date()).toLocaleDateString()}! ${state.stats.joins} users have joined, ${state.stats.ruleAccepts} users have accepted the rules, ${state.stats.leaves} users have left, ${state.stats.warnings} warnings have been issued.`); // Clear the stats for the day. state.stats.joins = 0; state.stats.ruleAccepts = 0; state.stats.leaves = 0; + state.stats.warnings = 0; }); client.on('message', message => { @@ -76,7 +68,7 @@ client.on('message', message => { if (message.guild == null && state.responses.pmReply) { // We want to log PM attempts. logger.info(`${message.author.username} ${message.author} [PM]: ${message.content}`); - state.logChannel.send(`${message.author} [PM]: ${message.content}`); + state.logChannel.sendMessage(`${message.author} [PM]: ${message.content}`); message.reply(state.responses.pmReply); return; } @@ -109,7 +101,7 @@ client.on('message', message => { if (cachedModule) { // Check access permissions. if (cachedModule.roles !== undefined && findArray(message.member.roles.map(function (x) { return x.name; }), cachedModule.roles) === false) { - state.logChannel.send(`${message.author} attempted to use admin command: ${message.content}`); + state.logChannel.sendMessage(`${message.author} attempted to use admin command: ${message.content}`); logger.info(`${message.author.username} ${message.author} attempted to use admin command: ${message.content}`); return false; } @@ -124,6 +116,21 @@ client.on('message', message => { cachedModules['quote.js'].command(message, cachedModule.reply); } } catch (err) { logger.error(err); } + + // Warn after running command? + try { + // Check if the command requires a warning. + if (cmd !== 'warn' && cachedModule.warn === true) { + // Access check to see if the user has privilages to warn. + let warnCommand = cachedModules['warn.js']; + if (findArray(message.member.roles.map(function (x) { return x.name; }), warnCommand.roles)) { + // They are allowed to warn because they are in warn's roles. + warnCommand.command(message); + } + } + } catch (err) { logger.error(err); } + } else { + // Not a valid command. } } else if (message.author.bot === false) { // This is a normal channel message. @@ -168,5 +175,13 @@ fs.readdirSync('./src/triggers/').forEach(function (file) { } }); +data.readWarnings(); +data.readBans(); + +// Load custom responses +if (process.env.DATA_CUSTOM_RESPONSES) { + data.readCustomResponses(); +} + client.login(process.env.DISCORD_LOGIN_TOKEN); logger.info('Startup completed. Established connection to Discord.'); diff --git a/src/state.js b/src/state.js index a81a48d..87ca670 100644 --- a/src/state.js +++ b/src/state.js @@ -2,11 +2,14 @@ var State = function () { this.guild = null; this.logChannel = null; + this.warnings = []; this.responses = null; + this.bans = []; this.stats = { joins: 0, ruleAccepts: 0, - leaves: 0 + leaves: 0, + warnings: 0 }; }; diff --git a/src/triggers/github.js b/src/triggers/github.js index 80387d1..c295e5b 100644 --- a/src/triggers/github.js +++ b/src/triggers/github.js @@ -35,7 +35,7 @@ exports.execute = function (message) { // Set path to type of comment (issues/pull) let path = response.request.uri.pathname.split('/')[3]; - message.channel.send(`Github ${map[path]}: ${url}`); + message.channel.sendMessage(`Github ${map[path]}: ${url}`); } });