Browse Source

v2.0 (#60)

* use airbnb js

* fix plugins

* fix bookclub

* fix rb cmt and dublin bus

* fix countdown and uno

* use airbnb linting?

* rename dublin bus code

* rename folders

* bug fixes

* remove duplicate code

* update docker

* fix errors from moving to classes

* clean up

* use async

* replace plugger with dynamic glob

* use plugins for cah and dbus

* update package.json
tags/2.0.0^0
Cian Butler 3 years ago
committed by GitHub
parent
commit
625aeb789f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 315 additions and 42192 deletions
  1. +18
    -0
      .codeclimate.yml
  2. +4
    -0
      .dockerignore
  3. +2
    -0
      .eslintignore
  4. +8
    -66
      .eslintrc.js
  5. +1
    -1
      .gitignore
  6. +0
    -1
      .nvmrc
  7. +5
    -5
      Dockerfile
  8. +0
    -20
      app.js
  9. +0
    -194
      app/bot.js
  10. +177
    -0
      bot/Bot.js
  11. +28
    -0
      bot/index.js
  12. +22
    -23
      config/config.json
  13. +2
    -2
      docker-compose.yml
  14. +9
    -0
      index.js
  15. +39
    -25
      package.json
  16. +0
    -53
      plugin_code/announce/README.md
  17. +0
    -50
      plugin_code/announce/app/controllers/announce.js
  18. +0
    -13
      plugin_code/announce/config/config.json
  19. +0
    -10
      plugin_code/announce/setup.js
  20. +0
    -57
      plugin_code/bookclub/README.md
  21. +0
    -306
      plugin_code/bookclub/app/controllers/Bookclub.js
  22. +0
    -58
      plugin_code/bookclub/config/booksRead.json
  23. +0
    -234
      plugin_code/bookclub/config/booksToRead.json
  24. +0
    -21
      plugin_code/bookclub/config/config.json
  25. +0
    -8
      plugin_code/bookclub/config/nextMonthBook.json
  26. +0
    -8
      plugin_code/bookclub/config/thisMonthBook.json
  27. +0
    -52
      plugin_code/bookclub/setup.js
  28. +0
    -36
      plugin_code/cards_against_humanity/README.md
  29. +0
    -125
      plugin_code/cards_against_humanity/app/controllers/cards.js
  30. +0
    -361
      plugin_code/cards_against_humanity/app/controllers/cards_against_humanity_controller.js
  31. +0
    -1312
      plugin_code/cards_against_humanity/app/controllers/game.js
  32. +0
    -18
      plugin_code/cards_against_humanity/app/models/card.js
  33. +0
    -25
      plugin_code/cards_against_humanity/app/models/player.js
  34. +0
    -2
      plugin_code/cards_against_humanity/config/cards/.gitignore
  35. +0
    -186
      plugin_code/cards_against_humanity/config/cards/90sNostalgiaExpansion_answers.json
  36. +0
    -58
      plugin_code/cards_against_humanity/config/cards/90sNostalgiaExpansion_questions.json
  37. +0
    -218
      plugin_code/cards_against_humanity/config/cards/AntisocialInjusticeExpansion_answers.json
  38. +0
    -74
      plugin_code/cards_against_humanity/config/cards/AntisocialInjusticeExpansion_questions.json
  39. +0
    -58
      plugin_code/cards_against_humanity/config/cards/BGGAmerica_q.json
  40. +0
    -1
      plugin_code/cards_against_humanity/config/cards/BGG_a.json
  41. +0
    -1658
      plugin_code/cards_against_humanity/config/cards/BGG_q.json
  42. +0
    -1834
      plugin_code/cards_against_humanity/config/cards/CardsAgainstMatrimonyExpansion_answers.json
  43. +0
    -442
      plugin_code/cards_against_humanity/config/cards/CardsAgainstMatrimonyExpansion_questions.json
  44. +0
    -6218
      plugin_code/cards_against_humanity/config/cards/DevOpsAgainstHumanityExpansion_answers.json
  45. +0
    -4475
      plugin_code/cards_against_humanity/config/cards/DevOpsAgainstHumanityExpansion_questions.json
  46. +0
    -642
      plugin_code/cards_against_humanity/config/cards/DisneyExpansion_answers.json
  47. +0
    -162
      plugin_code/cards_against_humanity/config/cards/DisneyExpansion_questions.json
  48. +0
    -570
      plugin_code/cards_against_humanity/config/cards/DoctorWhoExpansion_answers.json
  49. +0
    -210
      plugin_code/cards_against_humanity/config/cards/DoctorWhoExpansion_questions.json
  50. +0
    -642
      plugin_code/cards_against_humanity/config/cards/GameOfThronesExpansion_answers.json
  51. +0
    -162
      plugin_code/cards_against_humanity/config/cards/GameOfThronesExpansion_questions.json
  52. +0
    -2234
      plugin_code/cards_against_humanity/config/cards/HackersAgainstHumanityExpansion_answers.json
  53. +0
    -626
      plugin_code/cards_against_humanity/config/cards/HackersAgainstHumanityExpansion_questions.json
  54. +0
    -122
      plugin_code/cards_against_humanity/config/cards/HouseOfCardsAgainstHumanityExpansion_answers.json
  55. +0
    -74
      plugin_code/cards_against_humanity/config/cards/HouseOfCardsAgainstHumanityExpansion_questions.json
  56. +0
    -722
      plugin_code/cards_against_humanity/config/cards/KhaosWolfKat_answers.json
  57. +0
    -610
      plugin_code/cards_against_humanity/config/cards/KhaosWolfKat_questions.json
  58. +0
    -706
      plugin_code/cards_against_humanity/config/cards/LadiesAgainstHumanityExpansion_answers.json
  59. +0
    -202
      plugin_code/cards_against_humanity/config/cards/LadiesAgainstHumanityExpansion_questions.json
  60. +0
    -794
      plugin_code/cards_against_humanity/config/cards/MrManCollection_answers.json
  61. +0
    -226
      plugin_code/cards_against_humanity/config/cards/MrManCollection_questions.json
  62. +0
    -194
      plugin_code/cards_against_humanity/config/cards/NobilisReedExpansion_answers.json
  63. +0
    -82
      plugin_code/cards_against_humanity/config/cards/NobilisReedExpansion_questions.json
  64. +0
    -1370
      plugin_code/cards_against_humanity/config/cards/NorthernlionExpansion_answers.json
  65. +0
    -810
      plugin_code/cards_against_humanity/config/cards/NorthernlionExpansion_questions.json
  66. +0
    -778
      plugin_code/cards_against_humanity/config/cards/NotSafeForHumanityExpansion_answers.json
  67. +0
    -258
      plugin_code/cards_against_humanity/config/cards/NotSafeForHumanityExpansion_questions.json
  68. +0
    -634
      plugin_code/cards_against_humanity/config/cards/Official1stExpansion_answers.json
  69. +0
    -162
      plugin_code/cards_against_humanity/config/cards/Official1stExpansion_questions.json
  70. +0
    -186
      plugin_code/cards_against_humanity/config/cards/Official2012HolidayExpansion_answers.json
  71. +0
    -58
      plugin_code/cards_against_humanity/config/cards/Official2012HolidayExpansion_questions.json
  72. +0
    -169
      plugin_code/cards_against_humanity/config/cards/Official2013HolidayExpansion_answers.json
  73. +0
    -74
      plugin_code/cards_against_humanity/config/cards/Official2013HolidayExpansion_questions.json
  74. +0
    -194
      plugin_code/cards_against_humanity/config/cards/Official2014HolidayExpansion_answers.json
  75. +0
    -50
      plugin_code/cards_against_humanity/config/cards/Official2014HolidayExpansion_questions.json
  76. +0
    -601
      plugin_code/cards_against_humanity/config/cards/Official2ndExpansion_answers.json
  77. +0
    -202
      plugin_code/cards_against_humanity/config/cards/Official2ndExpansion_questions.json
  78. +0
    -594
      plugin_code/cards_against_humanity/config/cards/Official3rdExpansion_answers.json
  79. +0
    -202
      plugin_code/cards_against_humanity/config/cards/Official3rdExpansion_questions.json
  80. +0
    -562
      plugin_code/cards_against_humanity/config/cards/Official4thExpansion_answers.json
  81. +0
    -242
      plugin_code/cards_against_humanity/config/cards/Official4thExpansion_questions.json
  82. +0
    -602
      plugin_code/cards_against_humanity/config/cards/Official5thExpansion_answers.json
  83. +0
    -202
      plugin_code/cards_against_humanity/config/cards/Official5thExpansion_questions.json
  84. +0
    -602
      plugin_code/cards_against_humanity/config/cards/Official6thExpansion_answers.json
  85. +0
    -202
      plugin_code/cards_against_humanity/config/cards/Official6thExpansion_questions.json
  86. +0
    -3674
      plugin_code/cards_against_humanity/config/cards/OfficialBaseSet_answers.json
  87. +0
    -722
      plugin_code/cards_against_humanity/config/cards/OfficialBaseSet_questions.json
  88. +0
    -146
      plugin_code/cards_against_humanity/config/cards/OfficialBoxExpansion_answers.json
  89. +0
    -194
      plugin_code/cards_against_humanity/config/cards/PAXEast2013Expansion_answers.json
  90. +0
    -50
      plugin_code/cards_against_humanity/config/cards/PAXEast2013Expansion_questions.json
  91. +0
    -178
      plugin_code/cards_against_humanity/config/cards/PAXEast2014Expansion_answers.json
  92. +0
    -42
      plugin_code/cards_against_humanity/config/cards/PAXEast2014Expansion_questions.json
  93. +0
    -58
      plugin_code/cards_against_humanity/config/cards/PAXEastPanel2014Expansion_answers.json
  94. +0
    -18
      plugin_code/cards_against_humanity/config/cards/PAXEastPanel2014Expansion_questions.json
  95. +0
    -298
      plugin_code/cards_against_humanity/config/cards/PAXPrime2013Expansion_answers.json
  96. +0
    -58
      plugin_code/cards_against_humanity/config/cards/PAXPrime2013Expansion_questions.json
  97. +0
    -50
      plugin_code/cards_against_humanity/config/cards/PAXPrimePanel2014Expansion_answers.json
  98. +0
    -34
      plugin_code/cards_against_humanity/config/cards/PAXPrimePanel2014Expansion_questions.json
  99. +0
    -498
      plugin_code/cards_against_humanity/config/cards/RagingPsyfagsPackofShenanigans_answers.json
  100. +0
    -130
      plugin_code/cards_against_humanity/config/cards/RagingPsyfagsPackofShenanigans_questions.json

+ 18
- 0
.codeclimate.yml View File

@@ -0,0 +1,18 @@
---
engines:
duplication:
enabled: true
config:
languages:
- javascript
eslint:
enabled: true
fixme:
enabled: true
ratings:
paths:
- "**.js"
exclude_paths:
- config/
- node_modules/
- pluginsEnabled/

+ 4
- 0
.dockerignore View File

@@ -0,0 +1,4 @@
node_modules
.esm-cache
.DS_Store
challenge.json

+ 2
- 0
.eslintignore View File

@@ -1 +1,3 @@
node_modules/
.esm-cache
bot/index.js

+ 8
- 66
.eslintrc.js View File

@@ -1,70 +1,12 @@
module.exports = {
'env': {
'es6' : true,
'node' : true,
parserOptions: {
sourceType: 'module',
},
'extends': ['eslint:recommended'],
'rules' : {
'strict': [
2,
'global'
],
'indent': [
'error',
2,
],
'linebreak-style': [
'error',
'unix',
],
'quotes': [
'error',
'single',
],
'no-console': [
"error",
{
allow: [
"log",
"warning",
"warn",
"error"
]
}
],
'semi': [
'error',
'always',
],
'comma-dangle': [
'error',
'always-multiline',
],
'key-spacing': [
'error',
{
'multiLine': {
'beforeColon': false,
'afterColon' : true,
},
'align': {
'beforeColon': false,
'afterColon' : true,
'on' : 'colon',
'mode' : 'strict',
},
},
],
'no-multi-spaces': 0,
'no-var': [
'error',
],
'prefer-const': [
'error',
{
'destructuring' : 'any',
'ignoreReadBeforeAssign': false,
},
],
root: true,
extends: ['coderdojo'],
rules: {
'no-console': ['error', { allow: ['warn', 'trace', 'log', 'error'] }],
'class-methods-use-this': 0,
'consistent-return': 0,
},
};

+ 1
- 1
.gitignore View File

@@ -25,6 +25,6 @@ build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
.esm-cache
.DS_Store
challenge.json

+ 0
- 1
.nvmrc View File

@@ -1 +0,0 @@
lts/boron

+ 5
- 5
Dockerfile View File

@@ -1,8 +1,8 @@
FROM node:6-alpine
FROM node:alpine
MAINTAINER butlerx@notthe.cloud
ENV NODE_ENV=production
WORKDIR /usr/src/app
ADD . /usr/src/app/
RUN apk add --no-cache git build-base file nasm autoconf libpng-dev openssl &&\
yarn
VOLUME /usr/src/app/plugin_code/bookclub/config /usr/src/app/plugin_code/poping/config
COPY . /usr/src/app/
RUN apk add --no-cache git build-base file nasm autoconf libpng-dev openssl && yarn
VOLUME /usr/src/app/pluginCode/bookclub/config /usr/src/app/pluginCode/countdown/config
CMD yarn start

+ 0
- 20
app.js View File

@@ -1,20 +0,0 @@
#!/usr/bin/env node
/**
* Creabot
* main application script
* @author creadak <creadak@gmail.com>
* @version 1.1.0
*/
'use strict';
console.log('butlerbot');

// Set node env
process.env.NODE_ENV = process.env.NODE_ENV || 'production';

// dependencies
const bot = require('./app/bot');

// init the bot
bot.init();
// load plugins
require('./plugins.js')(bot);

+ 0
- 194
app/bot.js View File

@@ -1,194 +0,0 @@
'use strict';

const _ = require('lodash');
const irc = require('irc');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config.json')[env];
let client;
const commands = [];
const msgs = [];

function checkUserMode () {
return true;
}

/**
* Initialize the bot
*/
exports.init = function () {
const self = this;

// Don't join channels until registered on the server
self.registered = false;
self.delayedChannels = [];

console.log('Initializing...');
// init irc client
config.server = process.env.SERVER || config.server;
config.nick = process.env.NICK || config.nick;
config.clientOptions.port = process.env.PORT || config.clientOptions.port;
config.clientOptions.userName = process.env.USER || config.clientOptions.userName;

if (process.env.TARGET && process.env.MESSAGE) {
const target = process.env.TARGET;
const message = process.env.MESSAGE;
config.connectCommands.push({ target, message });
}

console.log(`Connecting to ${config.server} as ${config.nick}...`);
client = new irc.Client(config.server, config.nick, config.clientOptions);

self.joinChannels = channels => {
if (!_.isUndefined(channels)) {
if (self.registered) {
_.forEach(channels, channel => {
client.join(channel);
});
} else {
self.delayedChannels = self.delayedChannels.concat(channels);
}
}
};

self.setTopic = (channel, topic) => {
// ignore if not configured to set topic
if (_.isUndefined(config.setTopic) || !config.setTopic) {
return false;
}

// construct new topic
let newTopic = topic;
if (!_.isUndefined(config.topicBase)) {
newTopic = `${topic} ${config.topicBase}`;
}

// set it
client.send('TOPIC', channel, newTopic);
};

// handle connection to server for logging
client.addListener('registered', ({ server }) => {
console.log(`Connected to server ${server}`);
self.registered = true;

// Send connect commands after joining a server
if (!_.isUndefined(config.connectCommands) && config.connectCommands.length > 0) {
_.forEach(config.connectCommands, ({ target, message }) => {
if (target && message) {
client.say(target, message);
}
});
}

// Join delayed channels
if (self.delayedChannels.length > 0) {
self.joinChannels(self.delayedChannels);
}
});

// handle joins to channels for logging
client.addListener('join', (channel, nick) => {
console.log(`Joined ${channel} as ${nick}`);
// Send join command after joining a channel
if (!_.isUndefined(config.joinCommands) && config.joinCommands.hasOwnProperty(channel) && config.joinCommands[channel].length > 0) {
_.forEach(config.joinCommands[channel], cmd => {
if (cmd.target && cmd.message) {
client.say(cmd.target, cmd.message);
}
});
}
});

// output errors
client.addListener('error', message => {
console.warn('IRC client error: ', message);
});

client.addListener('message', function (from, to, text, message) {
console.log(`message from ${from} to ${to}: ${text}`);
// parse command
const cmdArr = text.trim().match(/^[.|!](\w+)\s?(.*)$/i);
if (!cmdArr || cmdArr.length <= 1) {
// command not found
return false;
}
const cmd = cmdArr[1].toLowerCase();
// parse arguments
const cmdArgs = cmdArr[2];
console.log(cmdArr);

// build callback options
if (config.nick === to) {
// private message commands
_.forEach(
msgs,
_.bind(
c => {
if (cmd === c.cmd) {
console.log(`command: ${c.cmd}`);
// check user mode
if (checkUserMode(message, c.mode)) {
c.callback(client, message, cmdArgs);
}
}
},
this
)
);
} else {
// public commands
_.forEach(
commands,
_.bind(
c => {
// If the command matches
if (cmd === c.cmd) {
// If the channel matches the command channels or is set to respond on all channels and is not in the
// commands excluded channels
if (_.includes(c.channel, to) || c.channel === 'all') {
if (_.isUndefined(c.exclude) || !_.includes(c.exclude, to)) {
console.log(`command: ${c.cmd}`);
// check user mode
if (checkUserMode(message, c.mode)) {
c.callback(client, message, cmdArgs);
}
}
}
}
},
this
)
);
}
});
};

/**
* Add a public command to the bot
* @param cmd Command keyword
* @param mode User mode that is allowed
* @param cb Callback function
*/
exports.cmd = (cmd, mode, channel, excludes, cb) => {
commands.push({
cmd,
mode,
channel,
exclude : excludes,
callback: cb,
});
};

/**
* Add a msg command to the bot
* @param cmd Command keyword
* @param mode User mode that is allowed
* @param cb Callback function
*/
exports.msg = (cmd, mode, cb) => {
msgs.push({
cmd,
mode,
callback: cb,
});
};

+ 177
- 0
bot/Bot.js View File

@@ -0,0 +1,177 @@
import _ from 'lodash';
import irc from 'irc';
import config from '../config/config.json';

const env = process.env.NODE_ENV || 'development';
const checkUserMode = () => true;

/** Class for IRC Bot */
export default class Bot {
/**
* Initialize the bot
*/
constructor() {
// Don't join channels until registered on the server
this.registered = false;
this.delayedChannels = [];
this.commands = [];
this.msgs = [];
this.config = config[env];
console.log('Initializing...');
// init irc client
this.config.server = process.env.SERVER || this.config.server;
this.config.nick = process.env.NICK || this.config.nick;
this.config.clientOptions.port = process.env.PORT || this.config.clientOptions.port;
this.config.clientOptions.userName = process.env.USER || this.config.clientOptions.userName;

if (process.env.TARGET && process.env.MESSAGE) {
const target = process.env.TARGET;
const message = process.env.MESSAGE;
config.connectCommands.push({ target, message });
}
}

/**
* Join channels on irc server
* @param {string[]} channels List of channels to join
*/
joinChannels(channels) {
if (!_.isUndefined(channels)) {
if (this.registered) {
_.forEach(channels, (channel) => {
this.client.join(channel);
});
} else {
this.delayedChannels = this.delayedChannels.concat(channels);
}
}
}

setTopic(channel, topic) {
// ignore if not configured to set topic
if (_.isUndefined(this.config.setTopic) || !this.config.setTopic) return false;
// construct new topic
let newTopic = topic;
if (!_.isUndefined(this.config.topicBase)) newTopic = `${topic} ${this.config.topicBase}`;
// set it
this.client.send('TOPIC', channel, newTopic);
}

/**
* Initialize the bot and connect to irc server
*/
init() {
console.log(`Connecting to ${this.config.server} as ${this.config.nick}...`);
this.client = new irc.Client(this.config.server, this.config.nick, this.config.clientOptions);
// handle connection to server for logging
this.client.addListener('registered', ({ server }) => {
console.log(`Connected to server ${server}`);
this.registered = true;

// Send connect commands after joining a server
if (!_.isUndefined(this.config.connectCommands) && this.config.connectCommands.length > 0) {
_.forEach(this.config.connectCommands, ({ target, message }) => {
if (target && message) this.client.say(target, message);
});
}

// Join delayed channels
if (this.delayedChannels.length > 0) this.joinChannels(this.delayedChannels);
});

// handle joins to channels for logging
this.client.addListener('join', (channel, nick) => {
console.log(`Joined ${channel} as ${nick}`);
// Send join command after joining a channel
if (
!_.isUndefined(this.config.joinCommands) &&
Object.prototype.hasOwnProperty.call(this.config.joinCommands, channel) &&
this.config.joinCommands[channel].length > 0
) {
_.forEach(this.config.joinCommands[channel], ({ target, message }) => {
if (target && message) this.client.say(target, message);
});
}
});

// output errors
this.client.addListener('error', message => console.warn('IRC client error: ', message));

this.client.addListener('message', function messageListener(from, to, text, message) {
console.log(`message from ${from} to ${to}: ${text}`);
// parse command
const cmdArr = text.trim().match(/^[.|!](\w+)\s?(.*)$/i);
if (!cmdArr || cmdArr.length <= 1) return false;
const command = cmdArr[1].toLowerCase();
// parse arguments
const cmdArgs = cmdArr[2];
console.log(cmdArr);

// build callback options
if (this.config.nick === to) {
// private message commands
_.forEach(
this.msgs,
_.bind((c) => {
if (command === c.cmd) {
console.log(`command: ${c.cmd}`);
// check user mode
if (checkUserMode(message, c.mode)) c.callback(this.client, message, cmdArgs);
}
}, this),
);
} else {
// public commands
_.forEach(
this.commands,
_.bind((c) => {
// If the command matches
if (command === c.cmd) {
// If the channel matches the command channels or is set to respond on all
// channels and is not in the commands excluded channels
if (_.includes(c.channel, to) || c.channel === 'all') {
if (_.isUndefined(c.exclude) || !_.includes(c.exclude, to)) {
console.log(`command: ${c.cmd}`);
// check user mode
if (checkUserMode(message, c.mode)) c.callback(this.client, message, cmdArgs);
}
}
}
}, this),
);
}
});
}

/**
* Add a public command to the bot
* @param {string} cmd Command keyword
* @param {string} mode User mode that is allowed
* @param {string[]} channel list of channels to listen too
* @param {string[]} exclude list of channels to ignore
* @param {requestCallback} cb Callback function
*/
cmd(cmd, mode, channel, exclude, callback) {
this.commands.push({
cmd,
mode,
channel,
exclude,
callback,
});
}

/**
* Add a msg command to the bot
* @param {string} cmd Command keyword
* @param {string} mode User mode that is allowed
* @param {requestCallback} cb Callback function
*/
msg(cmd, mode, callback) {
this.msgs.push({
cmd,
mode,
callback,
});
}
}

+ 28
- 0
bot/index.js View File

@@ -0,0 +1,28 @@
import Bot from './Bot';
import pkg from '../package.json';
import glob from 'glob';
import { promisify } from 'util';

const globP = promisify(glob);
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
console.log(pkg.name, pkg.version, process.env.NODE_ENV);
const bot = new Bot();
bot.init();
globP(`${__dirname}/../plugins/*.js`)
.then(files => {
files.forEach(pluginName => {
import(pluginName)
.then(plugin => {
plugin.default(bot);
console.log(`Loaded ${pluginName} plugin.`);
})
.catch(err => {
console.error(err);
process.exit(1);
});
});
})
.catch(err => {
console.error(err);
process.exit(1);
});

+ 22
- 23
config/config.json View File

@@ -1,34 +1,33 @@
{
"production": {
"server": "localhost",
"nick": "butlerbot",
"connectCommands": [{
"target": "nickserv",
"message": "identify"
}],
"server": "localhost",
"nick": "butlerbot",
"connectCommands": [
{
"target": "nickserv",
"message": "identify"
}
],
"clientOptions": {
"userName": "butlerx",
"port": "6697",
"floodProtection": true,
"userName": "butlerx",
"port": "6697",
"floodProtection": true,
"floodProtectionDelay": 175
}
},

"development": {
"server": "localhost",
"nick": "butlerDev",
"connectCommands": [{
"target": "",
"message": ""
}],
"server": "localhost",
"nick": "butlerDev",
"connectCommands": [
{
"target": "",
"message": ""
}
],
"clientOptions": {
"userName": "butlerx",
"port": "6697",
"floodProtection": true,
"userName": "butlerx",
"port": "6697",
"floodProtection": true,
"floodProtectionDelay": 175
}
}


+ 2
- 2
docker-compose.yml View File

@@ -4,8 +4,8 @@ services:
image: 'butlerx/butlerbot:latest'
restart: 'always'
volumes:
- ./bookclub:/usr/src/app/plugin_code/bookclub/config
- ./pop:/usr/src/app/plugin_code/poping/config
- ./bookclub:/usr/src/app/pluginCode/bookclub/config
- ./countdown:/usr/src/app/pluginCode/countdown/config
environment:
SERVER: localhost
PORT: 6697


+ 9
- 0
index.js View File

@@ -0,0 +1,9 @@
#!/usr/bin/env node
/**
* butlerbot
* IRC Bot framework
* @author butlerx <butlerx@notthe.cloud>
* @version 2.0
*/
require('@std/esm');
require('./bot');

+ 39
- 25
package.json View File

@@ -1,8 +1,8 @@
{
"name": "butlerbot",
"version": "1.2.0",
"description": "Butlerbot",
"main": "app.js",
"version": "2.0.0",
"description": "IRC Game Bot",
"main": "index.js",
"author": "butlerx <butlerx@redbrick.dcu.ie>",
"repository": {
"type": "git",
@@ -12,36 +12,50 @@
"url": "http://github.com/butlerx/butlerbot/issues"
},
"scripts": {
"start": "node app",
"start": "node index.js",
"test": "yarn lint",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
"fix": "eslint . --fix",
"format": "prettier-eslint --write \"**/*.js\" \"**/*.json\"",
"precommit": "lint-staged"
},
"lint-staged": {
"*.js*": ["prettier-eslint --write", "git add"]
},
"@std/esm": {
"esm": "js",
"cjs": "true"
},
"license": "CC-BY-NC-SA-2.0",
"dependencies": {
"amazon-product-api": "^0.4.3",
"cheerio": "^0.22.0",
"dublin-bus.js": "^1.3.2",
"es6-promise": "^4.0.5",
"inflection": "^1.12.0",
"@std/esm": "^0.2.2",
"butlerbot-announce": "^1.0.0",
"butlerbot-bookclub": "^1.0.0",
"butlerbot-cards-against-humanity": "^1.1.1",
"butlerbot-countdown": "^1.0.0",
"butlerbot-dublin-bus": "^1.0.1",
"butlerbot-helpdesk": "^1.0.0",
"butlerbot-popping": "^1.0.0",
"butlerbot-redbrick-committee": "^1.0.2",
"butlerbot-uno": "^1.0.0",
"glob": "^7.1.2",
"irc": "^0.5.2",
"irc-colors": "^1.3.3",
"jayschema": "^0.3.1",
"lodash": "^4.17.4",
"mathjs": "^3.10.1",
"mysql": "^2.13.0",
"node-schedule": "^1.2.1",
"plug": "https://github.com/butlerx/plug.git",
"request": "^2.81.0",
"request-promise-native": "^1.0.4",
"sequelize": "^3.30.4",
"snoowrap": "^1.14.0"
"request": "2.34"
},
"devDependencies": {
"eslint": "3.16.1",
"eslint-config-standard": "6.2.1",
"eslint-plugin-lodash": "^2.3.5",
"eslint-plugin-promise": "3.5.0",
"eslint-plugin-standard": "2.1.0"
"eslint": "^4.6.0",
"eslint-config-coderdojo": "^1.0.6",
"eslint-plugin-chai-friendly": "^0.4.0",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-lodash": "^2.4.4",
"eslint-plugin-node": "^5.1.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.3.0",
"eslint-plugin-standard": "^3.0.1",
"husky": "^0.14.3",
"lint-staged": "^4.0.3",
"prettier-eslint-cli": "^4.1.1"
}
}

+ 0
- 53
plugin_code/announce/README.md View File

@@ -1,53 +0,0 @@
# Redbrick Committee
This plugin allows users to look up who is in on the redbrick committee and who is currently in what position

## Commands
- **!cmt** - lists full Committee
- **!chair** - print the chairs name and their username
- **!secretary** - print the secretary name and their username
- **!treasurer** - print the treasurer name and their username
- **!pro** - print the Public Relations Officer name and their username
- **!events** - print the events name and their username
- **!helpdesk** - print the current helpdesk members and their usernames
- **!admins** - print the current team of admins and their usernames
- **!webmaster** - print the webmaster name and their username
- **!fyr** - print the first year rep name and their username

## Usage
If you want to print off a list of the full Committee you can simply use the command:

```
!cmt
```

But if you want to print an individual position you just use that position as a command, such as:

```
!helpdesk
```

If you wanted to just print the helpdesk.

Each command is rate limited to once a minute so the user will have to wait a minute between printing the position.

## Configuration
### Committee
Inside `plugin_code/redbrick_committee/config/committee.json` are all the current committee members in the format:

```
{ "name": "Cian Butler", "nick": "butlerx", "role": "Helpdesk" },
```

Where Cian is the persons name, butlerx the username and role their current position on committee. These can be easily updated but only recognised positions will be printed

### waitTime
Inside `plugin_code/redbrick_committee/config/config.json` is the variable waitTime, this number, by default 1, is the amount of minutes the wait timer will be set for.

### channels
This option contains a list of channels that the plugin should respond to commands it defines.

### channelsToExclude
This is an array of strings that the command should ignore commands in. Only used if the channels option is set to 'all'

### channelsToJoin
This is an array of channels that the bot should join. The cards against humanity plugin doesn't support playing the games in multiple channels, so this should only contain one entry.

+ 0
- 50
plugin_code/announce/app/controllers/announce.js View File

@@ -1,50 +0,0 @@
'use strict';

const schedule = require('node-schedule');
const request = require('request-promise-native');
const moment = require('moment');

const env = process.env.NODE_ENV || 'development';
const config = require('../../config/config.json')[env];

function Announce () {
const self = this;

self.config = config;
self.post = null;

self.update = schedule.scheduleJob(' */5 * * * *', () => {
if (self.client !== null) {
console.log('Scheduled update');
self.getLatestPost().then(post => {
if(!moment(post.date).isSameOrBefore(self.post.date)) {
self.post = post;
self.config.forEach(channel => {
self.setTopic(
channel,
`${self.post.title} - ${self.post.permalink}`
);
});
}
})
.catch(reason => console.log(reason));
} else {
console.log('update failed');
}
});

self.getLatestPost= () => {
return new Promise((resolve, reject) => {
request({
uri : `${config.url}/posts`,
headers: {
'User-Agent': 'butlerbot',
},
json: true,
}).then((posts => resolve(posts[0])))
.catch((error => reject(error)));
});
};
}

module.exports = Announce;

+ 0
- 13
plugin_code/announce/config/config.json View File

@@ -1,13 +0,0 @@
{
"production": {
"waitTime": 1,
"channelsToJoin": [],
"baseUrl": "https://www.redbrick.dcu.ie/api/"
},

"development": {
"waitTime": 1,
"channelsToJoin": [ "#botdev" ],
"baseUrl": "https://www.redbrick.dcu.ie/api/"
}
}

+ 0
- 10
plugin_code/announce/setup.js View File

@@ -1,10 +0,0 @@
'use strict';

const Announce = require('./app/controllers/announce.js');

module.exports = app => {
const redbrickCommittee = new Announce();

// Join Channels
app.joinChannels(redbrickCommittee.announce.channelsToJoin);
};

+ 0
- 57
plugin_code/bookclub/README.md View File

@@ -1,57 +0,0 @@
# BookClub
This plugin manages a bookclub keeping track of what books have been read and to read and choosing which book will be read.

## Commands
### Channel Commands
- **!current** - prints this months book
- **!next** - prints next months book
- **!suggest [title];[author];[pages]** - adds a book to the list of suggestions
### PM Commands
- **!listbook** - pms a list of suggestions (to be implemented)

## Usage
### current
To get this months book:
```
<@butlerx> !current
<butlerbot> This months book is The Sphere by Michael Crichton by creadak
```
### next
To get next months book:
```
<@butlerx> !next
<butlerbot> Next months book is The Trial by Franz Kafka suggested by fgsfds_lo
```
### suggest
To suggest a book to be read:
```
<butlerx> !suggest Do Androids Dream of Electric Sheep?; Philip K. Dick; 210
<butlerbot> Book added!
```
It parses to the ; to seperate in put input. It doesn't require all the arguments to add a book
```
<butlerx> !suggest Do Androids Dream of Electric Sheep?; Philip K. Dick
<butlerbot> Book added!
```
will add it without a page number
```
<butlerx> !suggest Do Androids Dream of Electric Sheep?
<butlerbot> Book added!
```
will add it as author unknown

## Configuration
### channels
This option contains a list of channels that the plugin should respond to commands it defines.

### channelsToExclude
This is an array of strings that the command should ignore commands in. Only used if the channels option is set to 'all'

### channelsToJoin
This is an array of channels that the bot should join. The cards against humanity plugin doesn't support playing the games in multiple channels, so this should only contain one entry.

### topicBase
This is the base for the topic for the channel to be based off.

### setTopic
Tells the bot if it should set the topic for the channel or not.

+ 0
- 306
plugin_code/bookclub/app/controllers/Bookclub.js View File

@@ -1,306 +0,0 @@
'use strict';

const _ = require('lodash');
const fs = require('fs');
const schedule = require('node-schedule');
const amazon = require('amazon-product-api');
const env = process.env.NODE_ENV || 'development';
const config = require('../../config/config.json')[env];
const booksToRead = require('../../config/booksToRead.json');
const booksRead = require('../../config/booksRead.json');
const thisMonthBook = require('../../config/thisMonthBook.json');
const nextMonthBook = require('../../config/nextMonthBook.json');

const Bookclub = function Bookclub () {
const self = this;
self.config = config;
self.booksToRead = booksToRead;
self.thisMonthBook = thisMonthBook;
self.nextMonthBook = nextMonthBook;
self.booksRead = booksRead;
self.date = new Date();
self.client = null;
self.new = 0;
self.keep = 0;
self.voted = [];

self.amazon = amazon.createClient({
awsId : process.env.AWSID || self.config.awsId,
awsSecret: process.env.AWSSECRET || self.config.awsSecret,
awsTag : 'BookClub',
});

self.update = schedule.scheduleJob('0 0 1 * *', () => {
if (self.client !== null) {
console.log('Scheduled update');
const month = self.date.getMonth();
self.changeBook(self.client, month, self.config.channels[0]);
} else {
console.log('update failed');
}
});

self.thisMonth = (client, { args }) => {
console.log('in thisMonth');
const month = self.date.getMonth();
self.client = client;
if (month === self.thisMonthBook.month) {
client.say(
args[0],
`This months book is ${self.thisMonthBook.title} by ${self.thisMonthBook.author}, ${self.thisMonthBook.link}`
);
} else {
self.changeBook(client, month, args[0]);
}
};

self.nextMonth = (client, { args }) => {
console.log('in nextMonth');
const month = self.date.getMonth();
self.client = client;
if (month === self.thisMonthBook.month) {
client.say(
args[0],
`Next months book is ${self.nextMonthBook.title} by ${self.nextMonthBook.author}, ${self.nextMonthBook.link}`
);
} else {
self.changeBook(client, month, args[0]);
}
};

self.suggest = (client, { args, nick }, cmdArgs) => {
console.log('in suggest');
self.client = client;
const input = cmdArgs.split('; ');

if (input[0] === '') {
client.say(args[0], 'You must provide a title');
return false;
}
if (input.length !== 3) {
if (input.length !== 2) {
input.push('unknown');
input.push(null);
} else if (input.length === 2) {
input.push(null);
}
}

const books = _.filter(
self.booksToRead,
book => book.title.toLowerCase() === input[0].toLowerCase()
);
const titles = _.map(books, book => book.title.toLowerCase());
const read = _.filter(
self.booksRead,
book => book.title.toLowerCase() === input[0].toLowerCase()
);
const titlesRead = _.map(read, book => book.title.toLowerCase());

const title = input[0].toString();
const author = input[1].toString();
let pages = input[2];

if (!_.isNumber(pages)) {
pages = null;
}
if (
_.includes(titlesRead, title.toLowerCase()) ||
title.toLowerCase() === self.thisMonthBook.title.toLowerCase() ||
title.toLowerCase() === self.nextMonthBook.title.toLowerCase()
) {
client.say(args[0], 'That book has already been read');
} else if (_.includes(titles, title.toLowerCase())) {
client.say(args[0], 'That book has already been suggested');
} else {
self.amazon.itemSearch(
{
title,
author,
searchIndex: 'Books',
},
(err, results) => {
let link;
if (err) {
console.log(err);
link = 'No link found';
} else {
const result = results[0].DetailPageURL[0].split('%');
link = result[0];
}
self.booksToRead.push({ title, author, pages, suggested: nick, month: 0, link });
self.write('booksToRead', self.booksToRead);
client.say(args[0], 'Book added!');
}
);
}
};

self.changeBook = (client, month, channel) => {
console.log('changing book');
// add book to read list
self.setTopic(
client,
channel,
`This months book is ${self.nextMonthBook.title} by ${self.nextMonthBook.author} || This months discussion: ${self.thisMonthBook.title}`
);
self.booksRead.push(thisMonthBook);
self.write('booksRead', self.booksRead);
// choose random book from booksToRead
self.thisMonthBook = self.nextMonthBook;
const newbook = Math.floor(Math.random() * self.booksToRead.length);
self.nextMonthBook = self.booksToRead[newbook];
self.booksToRead.splice(newbook, 1);
self.nextMonthBook.month = month + 1 % 12;
// write out booksToRead and thisMonthBook
self.write('booksToRead', self.booksToRead);
self.write('thisMonthBook', self.thisMonthBook);
self.write('nextMonthBook', self.nextMonthBook);
// say book and cvhange TOPIC
client.say(
channel,
`This months book is ${self.thisMonthBook.title} by ${self.thisMonthBook.author} suggested by ${self.thisMonthBook.suggested}, ${self.thisMonthBook.link}`
);
client.say(
channel,
`Next months book is ${self.nextMonthBook.title} by ${self.nextMonthBook.author} suggested by ${self.nextMonthBook.suggested}, ${self.nextMonthBook.link}`
);
};

self.setTopic = (client, channel, topic) => {
// ignore if not configured to set topic
if (_.isUndefined(config.setTopic) || !config.setTopic) {
return false;
}
// construct new topic
let newTopic = topic;
if (!_.isUndefined(config.topicBase)) {
newTopic = `${topic} ${config.topicBase}`;
}
// set it
client.send('TOPIC', channel, newTopic);
};

self.write = (fileName, file) => {
fileName = `plugin_code/bookclub/config/${fileName}.json`;
fs.writeFile(fileName, JSON.stringify(file, null, 2), err => {
if (err) return console.log(err);
console.log(`writing to ${fileName}`);
});
};

self.showBooks = (client, { nick }) => {
self.client = client;
for (let i = 0; i < self.booksToRead.length; i++) {
client.say(
nick,
` [${i}] ${self.booksToRead[i].title} by ${self.booksToRead[i].author} suggested by ${self.booksToRead[i].suggested}, ${self.booksToRead[i].link}`
);
}
};

self.showRead = (client, { nick }) => {
self.client = client;
for (let i = 0; i < self.booksRead.length; i++) {
let month = 'No Month';
switch (self.booksRead[i].month) {
case 0:
month = 'January';
break;
case 1:
month = 'Febuary';
break;
case 2:
month = 'March';
break;
case 3:
month = 'April';
break;
case 4:
month = 'May';
break;
case 5:
month = 'June';
break;
case 6:
month = 'July';
break;
case 7:
month = 'August';
break;
case 8:
month = 'September';
break;
case 9:
month = 'October';
break;
case 10:
month = 'November';
break;
case 11:
month = 'December';
break;
}
client.say(
nick,
`${month}: ${self.booksRead[i].title} by ${self.booksRead[i].author} suggested by ${self.booksRead[i].suggested}, ${self.booksRead[i].link}`
);
}
};

self.vote = (client, message, cmdArgs) => {
const args = cmdArgs.split(' ', 1);
self.client = client;
if (args[0] === '') {
client.say(message.args[0], `Keep: ${self.keep} Against: ${self.new}`);
} else {
if (_.includes(self.voted, message.nick.toLowerCase())) {
client.say(message.args[0], `${message.nick} you've arlready voted`);
return false;
} else {
if (args[0].toLowerCase() === 'keep') {
self.keep++;
self.voted.push(message.nick.toLowerCase());
client.say(message.args[0], `Keep: ${self.keep} Against: ${self.new}`);
} else {
if (args[0].toLowerCase() === 'new') {
if (self.new === 2) {
self.startTimeout = setTimeout(self.startTimeoutFunction, 10 * 60 * 1000);
}
self.new++;
self.voted.push(message.nick.toLowerCase());
if (self.new === 6 && self.keep === 0) {
const month = self.date.getMonth();
self.changeBook(self.client, month, message.args[0]); // NOTE: need to fix change book
self.keep = 0;
self.new = 0;
self.voted = [];
clearTimeout(self.startTimeout);
return true;
}
client.say(message.args[0], `Keep: ${self.keep} Against: ${self.new}`);
} else {
client.say(message.args[0], `${args[0]} is not a valid input`);
}
}
}
}
};

self.startTimeoutFunction = () => {
clearTimeout(self.startTimeout);
if (self.client !== null) {
if (self.new > self.keep) {
const month = self.date.getMonth() - 1;
self.changeBook(self.client, month, self.config.channels[0]);
} else {
self.client.say(self.config.channels[0], 'You\'ve voted to keep this months book');
}
self.keep = 0;
self.new = 0;
self.voted = [];
}
};
};

exports = module.exports = Bookclub;

+ 0
- 58
plugin_code/bookclub/config/booksRead.json View File

@@ -1,58 +0,0 @@
[
{
"title": "The Martian",
"author": "Andy Weir",
"pages": "369",
"suggested": "butlerx",
"month": 7,
"link": "http://www.amazon.co.uk/Martian-Andy-Weir/dp/1785031139"
},
{
"title": "Ready Player One",
"author": "Ernest Cline",
"pages": null,
"suggested": "gamma",
"month": 8,
"link": "http://www.amazon.co.uk/Ready-Player-One-Ernest-Cline/dp/0099560437"
},
{
"title": "The Hitchhikers Guide to the Galaxy",
"author": "Douglas Adams",
"pages": null,
"suggested": "gamma",
"month": 9,
"link": "http://www.amazon.co.uk/Hitchhikers-Guide-Galaxy-Douglas-Adams/dp/0330508539"
},
{
"title": "World War Z",
"author": "Max Brooks",
"pages": null,
"suggested": "creadak",
"month": 10,
"link": "http://www.amazon.co.uk/World-War-Export-Brooks-Max/dp/0715645110"
},
{
"title": "The Sphere",
"author": "Michael Crichton",
"pages": 385,
"suggested": "creadak",
"month": 11,
"link": "http://www.amazon.co.uk/Sphere-Michael-Crichton-ebook/dp/B007UH4G9C"
},
{
"title": "The Three-Body Problem",
"author": "Liu Cixin",
"pages": 302,
"suggested": "butlerx",
"month": 0,
"link": "http://www.amazon.co.uk/Three-Body-Problem-Cixin-Liu/dp/178497157X/"
},
{
"title": "The Sandman, Vol. 1: Preludes and Nocturnes",
"author": "Neil Gaiman",
"pages": null,
"suggested": "butlerx",
"month": 1,
"link": "http://www.amazon.co.uk/gp/product/1563892278/"
}
]

+ 0
- 234
plugin_code/bookclub/config/booksToRead.json View File

@@ -1,234 +0,0 @@
[
{
"title": "Brave New World",
"author": "Aldous Huxley",
"pages": 259,
"suggested": "fgsfds_lo",
"month": 0,
"link": "http://www.amazon.co.uk/Brave-New-World-Aldous-Huxley/dp/0099518473"
},
{
"title": "Catch-22",
"author": "Joseph Heller",
"pages": 435,
"suggested": "gamma",
"month": 0,
"link": "http://www.amazon.co.uk/Catch-22-Joseph-Heller/dp/0099477319"
},
{
"title": "Cryptonomicon",
"author": "Neal Stephenson",
"pages": 1168,
"suggested": "cooker3",
"month": 0,
"link": "http://www.amazon.co.uk/Cryptonomicon-Neal-Stephenson/dp/0099410672"
},
{
"title": "Fahrenheit 451",
"author": "Ray Bradbury",
"pages": 159,
"suggested": "fgsfds_lo",
"month": 0,
"link": "http://www.amazon.co.uk/Fahrenheit-451-Flamingo-Modern-Classics/dp/0006546064"
},
{
"title": "Neuromancer",
"author": "William Gibson",
"pages": 271,
"suggested": "moju",
"month": 0,
"link": "http://www.amazon.co.uk/Neuromancer-William-Gibson/dp/0006480411"
},
{
"title": "Night In Gaza",
"author": "Dr. Mads Gilbert",
"pages": 190,
"suggested": "cooker3",
"month": 0,
"link": "http://www.amazon.co.uk/Night-Gaza-Mads-Gilbert/dp/0993153364"
},
{
"title": "Shiprocked: Life on the Waves with Radio Caroline",
"author": "Steve Conway",
"pages": 211,
"suggested": "mrs_girl",
"month": 0,
"link": "http://www.amazon.co.uk/Shiprocked-Life-Waves-Radio-Caroline/dp/1909718521"
},
{
"title": "The Cuckoo's Calling",
"author": "Robert Galbraith",
"pages": 464,
"suggested": "creadak",
"month": 0,
"link": "http://www.amazon.co.uk/Cuckoos-Calling-Cormoran-Strike/dp/0751549258"
},
{
"title": "The Girl on the Train",
"author": "Paula Hawkins",
"pages": 338,
"suggested": "mrs_girl",
"month": 0,
"link": "http://www.amazon.co.uk/Girl-Train-Paula-Hawkins/dp/0857522310"
},
{
"title": "Bright's Passage",
"author": "Josh Ritter ",
"pages": 208,
"suggested": "gamma",
"month": 0,
"link": "No link found"
},
{
"title": "The Shepherd's Crown",
"author": "Terry Pratchett",
"pages": 304,
"suggested": "Chimaera",
"month": 0,
"link": "No link found"
},
{
"title": "Childhood's End",
"author": "Arthur C. Clarke",
"pages": 214,
"suggested": "butlerx",
"month": 0,
"link": "http://www.amazon.co.uk/Childhoods-End-Del-Rey-Impact/dp/0345444051"
},
{
"title": "Jurassic Park",
"author": "Michael Chricton",
"pages": 448,
"suggested": "creadak",
"month": 0,
"link": "No link found"
},
{
"title": "2001: A Space Oddyssey",
"author": "Arthur C Clarke",
"pages": 224,
"suggested": "creadak",
"month": 0,
"link": "No link found"
},
{
"title": "Yurope: The American Invasion",
"author": "Michael D. Wood",
"pages": 118,
"suggested": "fgsfds_lo",
"month": 0,
"link": "No link found"
},
{
"title": "The Nightmare",
"author": "Lars Kepler ",
"pages": 609,
"suggested": "mrs_girl",
"month": 0,
"link": "http://www.amazon.co.uk/The-Nightmare-Novel-Detective-Inspector/dp/1250024102"
},
{
"title": "Gun Control in the Third Reich",
"author": "Stephen P. Halbrook",
"pages": 364,
"suggested": "fgsfds_lo",
"month": 0,
"link": "http://www.amazon.co.uk/Gun-Control-Third-Reich-Disarming/dp/1598131621"
},
{
"title": "Dune",
"author": "Frank Herbert",
"pages": 418,
"suggested": "fgsfds_lo",
"month": 0,
"link": "http://www.amazon.co.uk/Dune-Frank-Herbert/dp/0441172717"
},
{
"title": "Seeds Of Earth",
"author": "Michael Cobley",
"pages": 400,
"suggested": "creadak",
"month": 0,
"link": "http://www.amazon.co.uk/Seeds-Earth-Humanitys-Michael-Cobley/dp/0316213985"
},
{
"title": "Leviathan Wakes",
"author": "James S. A. Corey",
"pages": 582,
"suggested": "butlerx",
"month": 0,
"link": "http://www.amazon.co.uk/Leviathan-Wakes-James-S-A-Corey/dp/0316129089"
},
{
"title": "The Trial",
"author": "Franz Kafka",
"pages": 213,
"suggested": "fgsfds_lo",
"month": 0,
"link": "http://www.amazon.co.uk/The-Trial-Translation-Restored-Schocken/dp/0805209999"
},
{
"title": "The Restaurant at the End of the Universe ",
"author": "Douglas Adams",
"pages": 208,
"suggested": "fgsfds_lo",
"month": 0,
"link": "http://www.amazon.co.uk/The-Restaurant-at-End-Universe/dp/0345391810"
},
{
"title": "The Casual Vacancy",
"author": "J. K. Rowling",
"pages": 503,
"suggested": "creadak",
"month": 0,
"link": "http://www.amazon.co.uk/The-Casual-Vacancy-J-Rowling/dp/0316228583"
},
{
"title": "Elantris",
"author": "Brandon Sanderson",
"pages": 496,
"suggested": "gamma",
"month": 0,
"link": "http://www.amazon.co.uk/Elantris-Brandon-Sanderson/dp/0765350378"
},
{
"title": "The Plot Against America",
"author": "Philip Roth",
"pages": 400,
"suggested": "gamma",
"month": 0,
"link": "http://www.amazon.co.uk/Plot-Against-America-Philip-Roth/dp/1400079497"
},
{
"title": "Alan Turing: The Enigma",
"author": "Andrew Hodges",
"pages": 768,
"suggested": "butlerx",
"month": 0,
"link": "http://www.amazon.co.uk/Alan-Turing-Enigma-Inspired-Imitation/dp/069116472X"
},
{
"title": "The Sandman: Endless Nights",
"author": "Neil Gaiman",
"pages": null,
"suggested": "butlerx",
"month": 0,
"link": "http://www.amazon.co.uk/Sandman-Endless-Nights-Graphic-Novels/dp/1401242332"
},
{
"title": "Nineteen Eighty-Four",
"author": "George Orwell",
"pages": 267,
"suggested": "creadak",
"month": 0,
"link": "http://www.amazon.co.uk/Nineteen-Eighty-Four-George-Orwell/dp/0679417397"
},
{
"title": "Pride and Prejudice and Zombies",
"author": "Seth Grahame-Smith",
"pages": 319,
"suggested": "butlerx",
"month": 0,
"link": "http://www.amazon.co.uk/Pride-Prejudice-Zombies-Classic-Ultraviolent/dp/1594743347"
}
]

+ 0
- 21
plugin_code/bookclub/config/config.json View File

@@ -1,21 +0,0 @@
{
"development": {
"setTopic": true,
"topicBase": "|| Dev Bot || Expect spam || Expect breakings",
"awsId": "",
"awsSecret": "",
"channels": [ "#botdev" ],
"channelsToExclude": [],
"channelsToJoin": [ "#botdev" ]
},

"production": {
"setTopic": true,
"topicBase": " || Welcome to Bookclub check out https://github.com/butlerx/butlerbot/wiki/bookclub for commands",
"awsId": "",
"awsSecret ": "",
"channels": ["#BookClub"],
"channelsToExclude": ["#Uno", "#CardsAgainstHumanity", "#Countdown", "bots", "helpdesk"],
"channelsToJoin": ["#BookClub"]
}
}

+ 0
- 8
plugin_code/bookclub/config/nextMonthBook.json View File

@@ -1,8 +0,0 @@
{
"title": "The Man in the High Castle",
"author": "Philip K. Dick",
"pages": 239,
"suggested": "butlerx",
"month": 3,
"link": "http://www.amazon.co.uk/The-High-Castle-Philip-Dick/dp/0547572484"
}

+ 0
- 8
plugin_code/bookclub/config/thisMonthBook.json View File

@@ -1,8 +0,0 @@
{
"title": "The Stranger",
"author": "Albert Camus",
"pages": 123,
"suggested": "fgsfds_lo",
"month": 2,
"link": "http://www.amazon.co.uk/The-Stranger-Albert-Camus/dp/0679720200"
}

+ 0
- 52
plugin_code/bookclub/setup.js View File

@@ -1,52 +0,0 @@
'use strict';

const Bookclub = require('./app/controllers/Bookclub.js');

module.exports = app => {
const bookclub = new Bookclub();

// Join Channels
app.joinChannels(bookclub.config.channelsToJoin);

// Add commands
app.cmd(
'suggest',
'',
bookclub.config.channels,
bookclub.config.channelsToExclude,
bookclub.suggest
);
app.cmd(
'current',
'',
bookclub.config.channels,
bookclub.config.channelsToExclude,
bookclub.thisMonth
);
app.cmd(
'next',
'',
bookclub.config.channels,
bookclub.config.channelsToExclude,
bookclub.nextMonth
);
app.cmd(
'listbooks',
'',
bookclub.config.channels,
bookclub.config.channelsToExclude,
bookclub.showBooks
);
app.cmd(
'listread',
'',
bookclub.config.channels,
bookclub.config.channelsToExclude,
bookclub.showRead
);
app.cmd('v', '', bookclub.config.channels, bookclub.config.channelsToExclude, bookclub.vote);

// Private commands
app.msg('listbooks', '', bookclub.showBooks);
app.msg('listread', '', bookclub.showRead);
};

+ 0
- 36
plugin_code/cards_against_humanity/README.md View File

@@ -1,36 +0,0 @@
# Cards Against Humanity

This plugin allows you to play Cards Against Humanity over irc

## Commands

### Public Commands
* **!start #** - Start a new game. Optional parameter can by used to set a point limit for the game (e.g. `!start 10` to play until one player has 10 points.)
* **!stop** - Stop the currently running game.
* **!pause** - Pause the currently running game.
* **!resume** - Resume a paused game.
* **!join** - Join to the currently running game.
* **!j** - Alias for join command.
* **!quit** - Quit from the game.
* **!q** - alias for quit.
* **!cards** - Show the cards you have in your hand.
* **!cah # (#)** - Depending on the context this command will either play cards from your hand, or if you are the czar, will pick the winner at the end of a round
* **!points** - Show players' *awesome points* in the current game.
* **!list** - List players in the current game.
* **!players** - Alias for !list command
* **!status** - Show current status of the game. Output depends on the state of the game (e.g. when waiting for players to play, you can check who hasn't played yet)
* **!discard (#)** - Discard cards once per round, at the cost of one awesome point. You can either provide no arugments to get an entirely new hand, or you can provide indices to only discard those cards. You must have at least one awesome point to discard cards.

### Private Commands
* **!cah # (#)** - Play a card from your hand

All of these commands are case insensitive and are trimmed for whitespace so "!start" and " !StaRt" will work the same


## Configuration

In config/config.json there are three settings.

* **channels** - Lists the channels this plugin can be used in. If you wish it to be usable in all channels the bot is in, set this to all.
* **channelsToExclude** - Lists the channels the plugin cannot be used in if you have chosen that the plugin can be used in all channels but want to exclude one in particular
* **channelsToJoin** - This is an array of channels the bot should join when this plugin is loaded.

+ 0
- 125
plugin_code/cards_against_humanity/app/controllers/cards.js View File

@@ -1,125 +0,0 @@
'use strict';

const _ = require('lodash');
const Card = require('../models/card');

const Cards = function Cards (cards) {
const self = this;

self.cards = [];

// add all cards in init array
_.forEach(cards, c => {
let card;
if (c instanceof Card) {
card = c;
} else if (c.hasOwnProperty('value')) {
card = new Card(c);
} else {
console.warning('Invalid card', c);
}
self.cards.push(card);
});

/**
* Reset the collection
* @param cards Optional replacement list of cards
* @returns {Array} Array of the old, replaced cards
*/
self.reset = cards => {
if (_.isUndefined(cards)) {
cards = [];
}
const oldCards = self.cards;
self.cards = cards;
return oldCards;
};

/**
* Shuffle the cards
* @returns {Cards} The shuffled collection
*/
self.shuffle = () => {
self.cards = _.shuffle(self.cards);
return self;
};

/**
* Add card to collection
* @param card
* @returns {*}
*/
self.addCard = card => {
self.cards.push(card);
return card;
};

/**
* Remove a card from the collection
* @param card
* @returns {*}
*/
self.removeCard = card => {
if (!_.isUndefined(card)) {
self.cards = _.without(self.cards, card);
}
return card;
};

/**
* Pick cards from the collection
* @param index (int|Array) Index of a single card, of Array of multiple indexes to remove and return
* @returns {Card|Cards} Instance of a single card, or instance of Cards if multiple indexes picked
*/
self.pickCards = function (index) {
if (_.isUndefined(index)) index = 0;
if (_.isArray(index)) {
// get multiple cards
const pickedCards = new Cards();
// first get all cards
_.forEach(index, _.bind(i => {
const c = self.cards[i];
if (_.isUndefined(c)) {
throw new Error('Invalid card index');
}
// cards.push();
pickedCards.addCard(c);
}, this));
// then remove them
self.cards = _.without.apply(this, _.union([self.cards], pickedCards.cards));
// _.forEach(pickedCards, function(card) {
// self.cards.removeCard(card);
// }, this);
console.log('picked cards:');
console.log(_.map(pickedCards.cards, 'id'));
console.log(_.map(pickedCards.cards, 'value'));
console.log('remaining cards:');
console.log(_.map(self.cards, 'id'));
console.log(_.map(self.cards, 'value'));
return pickedCards;
} else {
const card = self.cards[index];
self.removeCard(card);
return card;
}
};

/**
* Get all cards in collection
* @returns {Array}
*/
self.getCards = () => self.cards;

/**
* Get amount of cards in collection
* @returns {Number}
*/
self.numCards = function () {
return this.cards.length;
};
};

/**
* Expose `Cards()`
*/
exports = module.exports = Cards;

+ 0
- 361
plugin_code/cards_against_humanity/app/controllers/cards_against_humanity_controller.js View File

@@ -1,361 +0,0 @@
'use strict';

const _ = require('lodash');
const Game = require('./game');
const Player = require('../models/player');
const config = require('../../config/config');
const dbModels = require('../../models');

const CardsAgainstHumanity = function CardsAgainstHumanity () {
const self = this;
self.game;
self.config = config;

/**
* Start a game
* @param client
* @param message
* @param cmdArgs
*/
self.start = (client, message, cmdArgs) => {
// check if game running on the channel
const channel = message.args[0];

const nick = message.nick;
const user = message.user;
const hostname = message.host;

if (cmdArgs !== '') {
cmdArgs = _.invokeMap(cmdArgs.match(/(\w+)\s?/gi), str => str.trim());
}

if (!_.isUndefined(self.game) && self.game.state !== Game.STATES.STOPPED) {
// game exists
client.say(channel, 'A game is already running. Type !join to join the game.');
} else {
// init game
const player = new Player(nick, user, hostname);
const newGame = new Game(channel, client, self.config, cmdArgs, dbModels);
self.game = newGame;
self.game.addPlayer(player);
}
};

/**
* Stop a game
* @param client
* @param message
* @param cmdArgs
*/
self.stop = (client, message, cmdArgs) => {
const channel = message.args[0];
const nick = message.nick;
const hostname = message.host;

if (cmdArgs !== '') {
cmdArgs = _.invokeMap(cmdArgs.match(/(\w+)\s?/gi), str => str.trim());
}

if (_.isUndefined(self.game) || self.game.state === Game.STATES.STOPPED) {
client.say(channel, 'No game running. Start the game by typing !start.');
} else {
const player = self.game.getPlayer({ nick, hostname });
if (!_.isUndefined(player)) {
self.game.stop(self.game.getPlayer({ nick, hostname }));
self.game = undefined;
}
}
};

/**
* Pause a game
* @param client
* @param message
* @param cmdArgs
*/
self.pause = (client, message, cmdArgs) => {
const channel = message.args[0];
const nick = message.nick;
const hostname = message.host;

if (cmdArgs !== '') {
cmdArgs = _.invokeMap(cmdArgs.match(/(\w+)\s?/gi), str => str.trim());
}

if (_.isUndefined(self.game) || self.game.state === Game.STATES.STOPPED) {
client.say(channel, 'No game running. Start the game by typing !start.');
} else {
const player = self.game.getPlayer({ nick, hostname });
if (!_.isUndefined(player)) {
self.game.pause();
}
}
};

/**
* Resume a game
* @param client
* @param message
* @param cmdArgs
*/
self.resume = (client, message, cmdArgs) => {
const channel = message.args[0];
const nick = message.nick;
const hostname = message.host;

if (cmdArgs !== '') {
cmdArgs = _.invokeMap(cmdArgs.match(/(\w+)\s?/gi), str => str.trim());
}

if (_.isUndefined(self.game) || self.game.state === Game.STATES.STOPPED) {
client.say(channel, 'No game running. Start the game by typing !start.');
} else {
const player = self.game.getPlayer({ nick, hostname });
if (!_.isUndefined(player)) {
self.game.resume();
}
}
};

/**
* Add player to game
* @param client
* @param message
* @param cmdArgs
*/
self.join = (client, message, cmdArgs) => {
const nick = message.nick;
const user = message.user;
const hostname = message.host;

if (cmdArgs !== '') {
cmdArgs = _.invokeMap(cmdArgs.match(/(\w+)\s?/gi), str => str.trim());
}

if (_.isUndefined(self.game) || self.game.state === Game.STATES.STOPPED) {
self.start(client, message, cmdArgs);
} else {
const player = new Player(nick, user, hostname);
self.game.addPlayer(player);
}
};

/**
* Remove player from game
* @param client
* @param message
* @param cmdArgs
*/
self.quit = (client, message, cmdArgs) => {
const channel = message.args[0];
const nick = message.nick;
const hostname = message.host;

if (cmdArgs !== '') {
cmdArgs = _.invokeMap(cmdArgs.match(/(\w+)\s?/gi), str => str.trim());
}

if (_.isUndefined(self.game) || self.game.state === Game.STATES.STOPPED) {
client.say(channel, 'No game running. Start the game by typing !start.');
} else {
self.game.removePlayer(self.game.getPlayer({ nick, hostname }));
}
};

/**
* Get players cards
* @param client
* @param message
* @param cmdArgs
*/
self.cards = (client, message, cmdArgs) => {
const channel = message.args[0];
const nick = message.nick;
const hostname = message.host;

if (cmdArgs !== '') {
cmdArgs = _.invokeMap(cmdArgs.match(/(\w+)\s?/gi), str => str.trim());
}

if (_.isUndefined(self.game) || self.game.state === Game.STATES.STOPPED) {
client.say(channel, 'No game running. Start the game by typing !start.');
} else {
const player = self.game.getPlayer({ nick, hostname });
self.game.showCards(player);
}
};

/**
* Play cards