diff --git a/src/backend/bot_brain.js b/src/backend/bot_brain.js index dce0e03..36a059a 100644 --- a/src/backend/bot_brain.js +++ b/src/backend/bot_brain.js @@ -1,9 +1,18 @@ "use strict"; const obs = require("./obs_helper.js"); +const consts = require("./consts.js"); +const commands = require("./commands.js"); +const { add } = require("winston"); let matanbot_mention_count = 0; let mlk_quote_num = 0; +const added_commands = commands.loadCommands(); + +//every 5 minutes, save commands to persist usage counts +setInterval(() => commands.saveCommands(added_commands), 5 * 60 * 1000); + +//logic for when matanbot is mentioned function respondToMatanbotMention(user_info) { //random number 1 to 10 const random_num = Math.floor(Math.random() * 10 + 1); @@ -28,21 +37,11 @@ function respondToMatanbotMention(user_info) { } } -//make not case sensitive +//TODO: make not case sensitive +//logic for !bg / !background command +//if requested background exists, switch the background in OBS const changeGreenScreenBackground = async (image_request) => { - let images = [ - { obs_name: "pool", command_name: "pool" }, - { obs_name: "tile-gradient", command_name: "tile" }, - { obs_name: "earth stock", command_name: "earth" }, - { obs_name: "duck", command_name: "duck" }, - { obs_name: "universe 1", command_name: "universe" }, - { obs_name: "invisible", command_name: "invisible" }, - { obs_name: "tornado", command_name: "fire" }, - { obs_name: "rainbow", command_name: "rainbow" }, - { obs_name: "static", command_name: "static" } - ]; - - for (let image of images) { + for (let image of consts.background_images) { if (image_request === image.command_name) { try { if (await obs.switchGreenScreenBG(image.obs_name)) { @@ -57,53 +56,112 @@ const changeGreenScreenBackground = async (image_request) => { } //cannot find matching image, tell user what possible image names are - return `Possible backgrounds are, pool, tile, earth, duck, universe, invisible, fire, rainbow, static. E.g. !background duck`; + return consts.backgrounds_help_message; +}; + +//logic for !mlk command +//return new quote everytime, looping when all quotes have been called +function get_mlk_quote() { + mlk_quote_num = (mlk_quote_num + 1) % consts.mlk_quotes.length; + return `${consts.mlk_quotes[mlk_quote_num]} - Martin Luther King Jr.`; +} + +//logic for mod !add / !addBackground command +function addCommand(user_info, user_parameters) { + if (user_parameters.length < 2) { + return `Mods can add a command with !add {command} {response} {+m for mod only}`; + } + + if (commands.findCommand(user_parameters[0], added_commands)) { + return `!${user_parameters[0]} already exists. Use !edit to change it.`; + } + + const new_command = commands.newCommand(user_info["display-name"], user_parameters); + added_commands.push(new_command); + commands.saveCommands(added_commands); + return `Added !${new_command.command_word}`; +} + +//logic for mod !edit / !editCommand command +function editCommand(user_parameters) { + if (user_parameters.length < 2) { + return `Mods can edit an existing command with !edit {command} {response} {+m for mod only}. Must add +m for command to stay mod only.`; + } + + const edit_command_index = added_commands.indexOf(commands.findCommand(user_parameters[0], added_commands)); + + if (edit_command_index === -1) { + return `!${command_word} is not an existing command.`; + } + + const edit_command = commands.editCommand(added_commands[edit_command_index], user_parameters); + added_commands[edit_command_index] = edit_command; + commands.saveCommands(added_commands); + + return `Edited !${edit_command.command_word}`; } // Called every time a message comes in const message_main = async (user_info, user_msg) => { // Remove whitespace from chat message - const user_command = user_msg.trim().toLowerCase(); + const user_text = user_msg.trim().toLowerCase(); - if (user_command.search("clap") != -1) { + if (user_text.search("clap") != -1) { obs.showHeartEyes(); } - if (user_command.search("discord") != -1) { + if (user_text.search("discord") != -1) { return "Did someone say discord? Join Matan's discord to get updates on stream schedule, juggling advice, hangout and all around have a good time! https://discord.gg/bNUaFRE"; } - if (user_command.search("matanbot") != -1) { + if (user_text.search("matanbot") != -1) { return respondToMatanbotMention(user_info); } + const user_parameters = user_msg.split(" "); + const user_command = user_parameters.shift().toLowerCase(); + const mod_privileges = + user_info.mod || user_info.badges?.broadcaster === `1`; - const parameters = user_msg.split(" "); - const command = parameters.shift().toLowerCase(); + if (user_command === "!background" || user_command === "!bg") { + return await changeGreenScreenBackground(user_parameters[0]); + } - if (command === "!background" || command === "!bg") { - return await changeGreenScreenBackground(parameters[0]); + if (user_command === "!mlk") { + return get_mlk_quote(); } - if (command === '!mlk') { - const quotes = [ - "I have a dream that one day this nation will rise up and live out the true meaning of its creed: We hold these truths to be self-evident that all men are created equal. I have a dream that my four little children will one day live in a nation where they will not be judged by the color of their skin but by the content of their character. I have a dream today.", - "The ultimate measure of a man is not where he stands in moments of comfort and convenience, but where he stands at times of challenge and controversy.", - "Injustice anywhere is a threat to justice everywhere.", - "The time is always right to do what is right.", - "I have decided to stick with love. Hate is too great a burden to bear.", - "Darkness can not drive out darkness; only light can do that. Hate cannot drive out hate; only love can do that.", - "Nothing in the world is more dangerous than sincere ignorance and conscientious stupidity.", - "There can be no deep disappointment where there is not deep love.", - "I have a dream that one day this nation will rise up and live out the true meaning of its creed; We hold these truths to be self-evident: that all men are created equal", - "Morality cannot be legislated, but behavior can be regulated. Judicial decrees may not change the heart, but they can restrain the heartless.", - "Everybody can be great... because anybody can serve. You don't have to have a college degree to serve. You don't have to make your subject and verb agree to serve. you only need a heart full of grace. a soul generated by love.", - "He who passively accepts evil is as much involved in it as he who helps to perpetrate it.", - "Riots are the voices of the unheard.", - "I must confess that over the past few years I have been gravely disappointed with the white moderate. I have almost reached the regrettable conclusion that the Negro's great stumbling block in his stride toward freedom is not the White Citizen's Counciler or the Ku Klux Klanner, but the white moderate, who is more devoted to 'order' than to justice; who prefers a negative peace which is the absence of tension to a positive peace which is the presence of justice [...]" - ]; - - mlk_quote_num = (mlk_quote_num + 1) % quotes.length; - return `${quotes[mlk_quote_num]} - Martin Luther King Jr.`; + if ( + mod_privileges && + (user_command === `!add` || user_command === `!addCommand`) + ) { + return addCommand(user_info, user_parameters); } -} + + if ( + mod_privileges && + (user_command === `!edit` || user_command === `!editCommand`) + ) { + return editCommand(user_parameters); + } + + //loop through commands + for (let added_command of added_commands) { + if (user_command === `!${added_command.command_word}`) { + //user doesn't have privileges to call this command + if(!mod_privileges && added_command.mod_only) { + continue; + } + + if (user_parameters[0] === `count`) { + return `${user_command} has been used ${added_command.usage_count} times.`; + } else if (user_parameters[0] === `age`) { + return `${user_command} was added on ${added_command.added_date} by ${added_command.added_by}.`; + } else { + added_command.usage_count = added_command.usage_count + 1; + + return commands.commandResponse(added_command); + } + } + } +}; module.exports = { message_main: message_main }; diff --git a/src/backend/commands.js b/src/backend/commands.js new file mode 100644 index 0000000..131d8e4 --- /dev/null +++ b/src/backend/commands.js @@ -0,0 +1,65 @@ +const fs = require("fs"); + +//load commands from json file +module.exports.loadCommands = () => { + const commands_json = fs.readFileSync("commands.json"); + return JSON.parse(commands_json).commands; +} + +//persist commands to store state when bot is off +module.exports.saveCommands = (commands) => { + fs.writeFileSync("commands.json", JSON.stringify({commands: commands})); +} + +module.exports.findCommand = (command_word, commands) => { + return commands.find( + (command) => command.command_word === command_word + ) +} + +//creates a new command initialized on today's date +//added by added_by_user, set usage_count to 0 +module.exports.newCommand = (added_by_user, user_parameters) => { + const now = new Date(Date.now()); + const mod_only = user_parameters[user_parameters.length - 1] === `+m`; + if (mod_only) { + user_parameters.pop(); //remove +m from response message + } + + const new_command = { + command_word: user_parameters.shift(), + response_array: user_parameters, + mod_only: mod_only, + added_by: added_by_user, + added_timestamp: `${ + now.getMonth() + 1 + }-${now.getDate()}, ${now.getFullYear()}`, + usage_count: 0, + }; + + return new_command; +} + +//create new command with the new parameters +//but use original commands added_by, added_timestamp and usage_count +module.exports.editCommand = (command, user_parameters) => { + const edited_command = this.newCommand(command.added_by, user_parameters); + edited_command.added_timestamp = command.added_timestamp; + edited_command.usage_count = command.usage_count; + return edited_command; +} + +//replace instances of {count} in the response_array w/command's usage count +//reduce array into a string and return +module.exports.commandResponse = (command) => { + return command.response_array.reduce( + (cur_value, add_value) => { + if (add_value === `{count}`) { + return `${cur_value} ${command.usage_count}`; + } else { + return `${cur_value} ${add_value}`; + } + }, + "" + ); +} diff --git a/src/backend/commands.json b/src/backend/commands.json new file mode 100644 index 0000000..d8122ef --- /dev/null +++ b/src/backend/commands.json @@ -0,0 +1 @@ +{"commands":[{"command_word":"hello","response_array":["world"],"mod_only":false,"added_by":"matanjuggles","added_date":"1-25, 2021","usage_count":6},{"command_word":"a","response_array":["this","has","been","used","{count}","times"],"mod_only":false,"added_by":"matanjuggles","added_timestamp":"1-28, 2021","usage_count":10},{"command_word":"fam","response_array":["Jugg","LEFam"],"mod_only":false,"added_by":"matanjuggles","added_timestamp":"1-30, 2021","usage_count":13},{"command_word":"siteswap","response_array":["Siteswap,","also","called","quantum","juggling","or","the","Cambridge","notation,","is","a","numeric","juggling","notation","used","to","describe","or","represent","juggling","patterns.","...","Siteswap","assumes","that","\"throws","happen","on","beats","that","are","equally","spaced","in","time.\""],"mod_only":false,"added_by":"matanjuggles","added_timestamp":"2-4, 2021","usage_count":1}]} \ No newline at end of file diff --git a/src/backend/consts.js b/src/backend/consts.js new file mode 100644 index 0000000..24ff7af --- /dev/null +++ b/src/backend/consts.js @@ -0,0 +1,31 @@ +module.exports.mlk_quotes = [ + "I have a dream that one day this nation will rise up and live out the true meaning of its creed: We hold these truths to be self-evident that all men are created equal. I have a dream that my four little children will one day live in a nation where they will not be judged by the color of their skin but by the content of their character. I have a dream today.", + "The ultimate measure of a man is not where he stands in moments of comfort and convenience, but where he stands at times of challenge and controversy.", + "Injustice anywhere is a threat to justice everywhere.", + "The time is always right to do what is right.", + "I have decided to stick with love. Hate is too great a burden to bear.", + "Darkness can not drive out darkness; only light can do that. Hate cannot drive out hate; only love can do that.", + "Nothing in the world is more dangerous than sincere ignorance and conscientious stupidity.", + "There can be no deep disappointment where there is not deep love.", + "I have a dream that one day this nation will rise up and live out the true meaning of its creed; We hold these truths to be self-evident: that all men are created equal", + "Morality cannot be legislated, but behavior can be regulated. Judicial decrees may not change the heart, but they can restrain the heartless.", + "Everybody can be great... because anybody can serve. You don't have to have a college degree to serve. You don't have to make your subject and verb agree to serve. you only need a heart full of grace. a soul generated by love.", + "He who passively accepts evil is as much involved in it as he who helps to perpetrate it.", + "Riots are the voices of the unheard.", + "I must confess that over the past few years I have been gravely disappointed with the white moderate. I have almost reached the regrettable conclusion that the Negro's great stumbling block in his stride toward freedom is not the White Citizen's Counciler or the Ku Klux Klanner, but the white moderate, who is more devoted to 'order' than to justice; who prefers a negative peace which is the absence of tension to a positive peace which is the presence of justice [...]", +]; + +module.exports.background_images = [ + { obs_name: "pool", command_name: "pool" }, + { obs_name: "tile-gradient", command_name: "tile" }, + { obs_name: "earth stock", command_name: "earth" }, + { obs_name: "duck", command_name: "duck" }, + { obs_name: "universe 1", command_name: "universe" }, + { obs_name: "invisible", command_name: "invisible" }, + { obs_name: "tornado", command_name: "fire" }, + { obs_name: "rainbow", command_name: "rainbow" }, + { obs_name: "static", command_name: "static" } +]; + +module.exports.backgrounds_help_message = `Possible backgrounds are, pool, tile, earth, duck, universe, invisible, fire, rainbow, static. E.g. !background duck`; +