forked from publiclab/plotsbot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request publiclab#42 from publiclab/modularity
Shift to behavior based architecture
- Loading branch information
Showing
10 changed files
with
174 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
const Behavior = require('../models/behavior'); | ||
|
||
const greetAction = (channel, username, botNick) => { | ||
return `Welcome to Publiclab, ${username}! For a quick walkthrough, send the message: \`${botNick} help\``; | ||
}; | ||
|
||
module.exports = new Behavior('join', greetAction); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const Behavior = require('../models/behavior'); | ||
|
||
function helpMessage (name, service) { | ||
let out = `# ${service}\n`; | ||
|
||
switch (service) { | ||
case 'chatbot': | ||
out += `\`${name} help [<module>...]\`: Prints out this descriptive help message for each mentioned module. If no modules are specified, print the help message for ALL modules.`; | ||
break; | ||
default: | ||
out += `\`${service}\` is not the name of a valid module. Try looking up the \`chatbot\` module instead.`; | ||
} | ||
|
||
return out; | ||
} | ||
|
||
function printGeneralHelp (botNick) { | ||
return helpMessage(botNick, 'chatbot'); | ||
} | ||
|
||
function printSpecificHelp (botNick, options) { | ||
return options.map((service) => helpMessage(botNick, service)).join('\n\n'); | ||
} | ||
|
||
let helpAction = (botNick, options) => { | ||
if(options.length == 0) { | ||
return printGeneralHelp(botNick); | ||
} else { | ||
return printSpecificHelp(botNick, options); | ||
} | ||
}; | ||
|
||
module.exports = { | ||
helpBehavior: new Behavior('message', helpAction, 'help'), | ||
helpMessage | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
const utils = require('../utils'); | ||
|
||
const greetBehavior = require('./greet'); | ||
const helpBehavior = require('./help').helpBehavior; | ||
|
||
function parseMessage (message) { | ||
return message.split(/[\s,.;:!?]/g).filter(String); | ||
} | ||
|
||
function messageResponse(botNick, parsed, behaviors) { | ||
// This function takes the parsed version of the message and the array of | ||
// behaviors with `trigger` equal to "message" (i.e. the behaviors supposed to | ||
// be triggered on message), executes the action of a behavior if it was | ||
// mentioned by `keyword` and returns the result | ||
|
||
// This snippet looks if the parsed message contains the keyword of any of the | ||
// behavior inside the behaviors array. | ||
const behavior = behaviors.find(behavior => | ||
utils.contains(parsed, behavior.keyword) | ||
); | ||
|
||
// If there was a match, remove the behavior's keyword from the parsed message | ||
// call the behavior's action with the remaining message and the bot's nick | ||
if (behavior) { | ||
utils.remove(parsed, behavior.keyword); | ||
return behavior.action(botNick, parsed); | ||
} | ||
} | ||
|
||
class Behaviors { | ||
constructor(botNick, client) { | ||
this.botNick = botNick; | ||
this.client = client; | ||
this.joinBehaviors = [ greetBehavior ]; | ||
this.messageBehaviors = [ helpBehavior ]; | ||
} | ||
|
||
addJoinHandler() { | ||
this.client.addJoinHandler((channel, username) => { | ||
this.joinBehaviors.forEach(behavior => { | ||
this.client.sendMessage(channel, behavior.action(channel, username, this.botNick)); | ||
}); | ||
}); | ||
} | ||
|
||
addMessageHandler() { | ||
this.client.addMessageHandler((from, to, message) => { | ||
const response = this.getResponse(to, message); | ||
if(response) { | ||
if(to === this.botNick) { | ||
// Message was recieved in a DM | ||
this.client.sendMessage(from, response); | ||
} else { | ||
// Message was recieved in a normal channel | ||
this.client.sendMessage(to, response); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
getResponse(to, message) { | ||
let parsed = parseMessage(message); | ||
if(to === this.botNick) { | ||
// If the message was sent directly to the bot (eg: in a DM) | ||
return messageResponse(this.botNick, parsed, this.messageBehaviors); | ||
} else if (utils.contains(parsed, this.botNick)) { | ||
// If bot was mentioned | ||
utils.remove(parsed, this.botNick); | ||
return messageResponse(this.botNick, parsed, this.messageBehaviors); | ||
} else if (utils.contains(parsed, '@' + this.botNick)) { | ||
// If bot was mentioned, Gitter style | ||
utils.remove(parsed, '@' + this.botNick); | ||
return messageResponse(this.botNick, parsed, this.messageBehaviors); | ||
} else { | ||
// If the message was not meant for the bot | ||
return undefined; | ||
} | ||
} | ||
|
||
bootstrap() { | ||
this.addJoinHandler(); | ||
this.addMessageHandler(); | ||
} | ||
} | ||
|
||
module.exports = Behaviors; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
class Behavior { | ||
constructor(trigger, action, keyword) { | ||
this.trigger = trigger; | ||
this.action = action; | ||
this.keyword = keyword; | ||
} | ||
} | ||
|
||
module.exports = Behavior; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "plotsbot", | ||
"version": "0.3.11", | ||
"version": "1.0.0", | ||
"description": "A bot for Public Lab", | ||
"main": "bot.js", | ||
"repository": "[email protected]:publiclab/plotsbot.git", | ||
|
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const Behaviors = require('../behaviors'); | ||
const helpMessage = require('../behaviors/help').helpMessage; | ||
|
||
describe('Help Behavior', () => { | ||
const botNick = 'testbot'; | ||
const behaviors = new Behaviors(botNick, undefined); | ||
|
||
const chatbotHelp = helpMessage(botNick, 'chatbot'); | ||
const invalidHelp = helpMessage(botNick, 'invalid'); | ||
|
||
it('should print general help', () => { | ||
expect(behaviors.getResponse(botNick, 'help')).toBe(chatbotHelp); | ||
}); | ||
|
||
it('should print specific help for existing modules', () => { | ||
expect(behaviors.getResponse(botNick, 'help chatbot')).toBe(chatbotHelp); | ||
}); | ||
|
||
it('should print specific help for nonexisting modules', () => { | ||
expect(behaviors.getResponse(botNick, 'help invalid')).toBe(invalidHelp); | ||
}); | ||
|
||
it('should print specific help for existing and nonexisting modules combined', () => { | ||
expect(behaviors.getResponse(botNick, 'help chatbot invalid')).toBe(chatbotHelp + '\n\n' + invalidHelp); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,12 @@ | ||
function helpMessage (name, service) { | ||
let out = `# ${service}\n`; | ||
|
||
switch (service) { | ||
case 'chatbot': | ||
out += `\`${name} help [<module>...]\`: Prints out this descriptive help \ | ||
message for each mentioned module. If no modules are specified, print the help \ | ||
message for ALL modules.`; | ||
break; | ||
default: | ||
out += `\`${service}\` is not the name of a valid module. Try looking up \ | ||
the \`chatbot\` module instead.`; | ||
} | ||
|
||
return out; | ||
function contains(array, element) { | ||
return array.indexOf(element) == -1 ? false : true; | ||
} | ||
|
||
function printGeneralHelp (name) { | ||
return helpMessage(name, 'chatbot'); | ||
} | ||
|
||
function printSpecificHelp (name, message) { | ||
return message.map((service) => helpMessage(name, service)).join('\n\n'); | ||
} | ||
|
||
function parseMessage (message) { | ||
return message.split(/[\s,.;:!?]/g).filter(String); | ||
} | ||
|
||
function messageResponse (name, message) { | ||
switch (message[0]) { | ||
case 'help': | ||
message.splice(0, 1); | ||
|
||
if (message.length === 0) { | ||
return printGeneralHelp(name); | ||
} else { | ||
return printSpecificHelp(name, message); | ||
} | ||
} | ||
function remove(array, element) { | ||
array.splice(array.indexOf(element), 1); | ||
} | ||
|
||
module.exports = { | ||
parseMessage, | ||
messageResponse | ||
contains, | ||
remove | ||
}; |