From 4b8e04a66b9801aab04c2d47e532e35864b797b1 Mon Sep 17 00:00:00 2001 From: Mike Zrimsek Date: Sun, 1 Nov 2020 19:40:08 -0500 Subject: [PATCH] refactor commands into groups and handler functions add logging --- constants/commands.js | 30 ++++++++ constants/obs.js | 9 +++ index.js | 161 ++++++++++++++++++++++++------------------ package-lock.json | 102 +++++++++++++++++++++----- package.json | 5 +- utils/index.js | 7 ++ 6 files changed, 225 insertions(+), 89 deletions(-) create mode 100644 constants/commands.js create mode 100644 constants/obs.js create mode 100644 utils/index.js diff --git a/constants/commands.js b/constants/commands.js new file mode 100644 index 0000000..72e49ed --- /dev/null +++ b/constants/commands.js @@ -0,0 +1,30 @@ +const COMMAND_PREFACE = '!'; + +const ADMIN_USER = 'bastulos'; + +const ADMIN_COMMANDS = { + TOGGLE_COMMANDS_ACTIVE: 'active', + RECONNECT_OBS: 'obs' +}; + +const OBS_COMMANDS = { + RESET: 'reset', + TOGGLE_CAM: 'cam', + TOGGLE_MUTE_MIC: 'mic', + CHANGE_OVERLAY_COLOR: 'color', + TOGGLE_AQUA: 'aqua' +}; + +const USER_COMMANDS = { + COMMAND_LIST: 'help', + HELLO: 'hello', + HEART: 'heart' +}; + +module.exports = { + COMMAND_PREFACE, + ADMIN_USER, + ADMIN_COMMANDS, + OBS_COMMANDS, + USER_COMMANDS +}; \ No newline at end of file diff --git a/constants/obs.js b/constants/obs.js new file mode 100644 index 0000000..ff544f1 --- /dev/null +++ b/constants/obs.js @@ -0,0 +1,9 @@ +const SOURCES = { + WEBCAM: 'Webcam', + MIC: 'Desktop Mic', + AQUA: 'Aqua' +}; + +module.exports = { + SOURCES +}; \ No newline at end of file diff --git a/index.js b/index.js index dc5ef38..f745996 100644 --- a/index.js +++ b/index.js @@ -2,76 +2,90 @@ require('dotenv').config(); const tmi = require('tmi.js'); const OBSWebSocket = require('obs-websocket-js'); +const logger = require('winston'); + +logger.remove(logger.transports.Console); +logger.add(logger.transports.Console, { colorize: true }); +logger.level = 'debug'; const tmiConfig = require('./config/tmi'); const obsConfig = require('./config/obs'); -const client = new tmi.client(tmiConfig); -const obs = new OBSWebSocket(); +const { COMMAND_PREFACE, ADMIN_USER, ADMIN_COMMANDS, OBS_COMMANDS, USER_COMMANDS } = require('./constants/commands'); +const { SOURCES } = require('./constants/obs'); + +const { getRandomColor } = require('./utils'); +const client = new tmi.client(tmiConfig); client.connect(); -obs.connect(obsConfig); -const COMMAND_PREFACE = '!'; -const ADMIN_USER = 'bastulos'; -const ADMIN_COMMANDS = { - TOGGLE_COMMANDS_ACTIVE: 'active', - RECONNECT_OBS: 'obs' -}; -const COMMANDS = { - COMMAND_LIST: 'help', - RESET: 'reset', - TOGGLE_CAM: 'cam', - TOGGLE_MUTE_MIC: 'mic', - CHANGE_OVERLAY_COLOR: 'color', - TOGGLE_AQUA: 'aqua', - HELLO: 'hello', - HEART: 'heart' -}; -const SOURCES = { - WEBCAM: 'Webcam', - MIC: 'Desktop Mic', - AQUA: 'Aqua' -}; +const obs = new OBSWebSocket(); let active = true; -client.on('chat', async (channel, userInfo, message, self) => { - if (self) return; // ignore messages from the bot +function handleAdminCommand(channel, messageParts) { + const command = messageParts[0]; - if (message[0] !== COMMAND_PREFACE) return; // ignore non command messages + switch (command) { + case `${COMMAND_PREFACE}${ADMIN_COMMANDS.TOGGLE_COMMANDS_ACTIVE}`: { + if (active) { + const message = 'Bot commands are disabled!'; - const normalizedMessage = message.toLowerCase(); - const messageParts = normalizedMessage.split(' '); - const command = messageParts[0]; + client.say(channel, message); + logger.info(message); - if (userInfo.username === ADMIN_USER) { - switch (command) { - case `${COMMAND_PREFACE}${ADMIN_COMMANDS.TOGGLE_COMMANDS_ACTIVE}`: { - if (active) { - client.say(channel, `Bot commands are disabled!`); - active = false; - } - else { - client.say(channel, `Bot commands are enabled!`); - active = true; - } - break; - } - case `${COMMAND_PREFACE}${ADMIN_COMMANDS.RECONNECT_OBS}`: { - obs.connect(obsConfig); - break; + active = false; } - default: { - break; + else { + const message = 'Bot commands are enabled!'; + + client.say(channel, message); + logger.info(message); + + active = true; } + break; + } + case `${COMMAND_PREFACE}${ADMIN_COMMANDS.RECONNECT_OBS}`: { + obs.connect(obsConfig).then(() => logger.info('Connected to OBSWebSocket')); + break; + } + default: { + break; } } +}; - if (!active) return; +function handleUserCommand(channel, userInfo, messageParts) { + const command = messageParts[0]; switch (command) { - case `${COMMAND_PREFACE}${COMMANDS.RESET}`: { + case `${COMMAND_PREFACE}${USER_COMMANDS.HEART}`: + case `${COMMAND_PREFACE}${USER_COMMANDS.HELLO}`: { + client.say(channel, `@${userInfo.username}, may your heart be your guiding key`); + break; + } + case `${COMMAND_PREFACE}${USER_COMMANDS.COMMAND_LIST}`: { + const allCommands = { + ...OBS_COMMANDS, + ...USER_COMMANDS + }; + const commands = Object.keys(allCommands); + const commandList = commands.map(commandKey => `${COMMAND_PREFACE}${allCommands[commandKey]}`).join(', '); + client.say(channel, `Here are the available commands: \n${commandList}`); + break; + } + default: { + break; + } + } +} + +async function handleOBSCommand(messageParts) { + const command = messageParts[0]; + + switch (command) { + case `${COMMAND_PREFACE}${OBS_COMMANDS.RESET}`: { obs.send('SetSourceFilterVisibility', { sourceName: SOURCES.WEBCAM, filterName: 'Color Correction', @@ -81,28 +95,17 @@ client.on('chat', async (channel, userInfo, message, self) => { obs.send('SetMute', { source: SOURCES.MIC, mute: false }); break; } - case `${COMMAND_PREFACE}${COMMANDS.HEART}`: - case `${COMMAND_PREFACE}${COMMANDS.HELLO}`: { - client.say(channel, `@${userInfo.username}, may your heart be your guiding key`); - break; - } - case `${COMMAND_PREFACE}${COMMANDS.COMMAND_LIST}`: { - const commands = Object.keys(COMMANDS); - const commandList = commands.map(commandKey => `${COMMAND_PREFACE}${COMMANDS[commandKey]}`).join(', '); - client.say(channel, `Here are the available commands: \n${commandList}`); - break; - } - case `${COMMAND_PREFACE}${COMMANDS.TOGGLE_CAM}`: { + case `${COMMAND_PREFACE}${OBS_COMMANDS.TOGGLE_CAM}`: { const properties = await obs.send('GetSceneItemProperties', { item: { name: SOURCES.WEBCAM } }); const { visible } = properties; obs.send('SetSceneItemRender', { source: SOURCES.WEBCAM, render: !visible }); break; } - case `${COMMAND_PREFACE}${COMMANDS.TOGGLE_MUTE_MIC}`: { + case `${COMMAND_PREFACE}${OBS_COMMANDS.TOGGLE_MUTE_MIC}`: { obs.send('ToggleMute', { source: SOURCES.MIC }); break; } - case `${COMMAND_PREFACE}${COMMANDS.CHANGE_OVERLAY_COLOR}`: { + case `${COMMAND_PREFACE}${OBS_COMMANDS.CHANGE_OVERLAY_COLOR}`: { let numTimes = messageParts[1] ? parseInt(messageParts[1]) : 1; if (numTimes < 0) { @@ -119,7 +122,7 @@ client.on('chat', async (channel, userInfo, message, self) => { filterEnabled: true }); - const setColorCorrectionToRandomColor = () => { + function setColorCorrectionToRandomColor() { const randomColor = getRandomColor(); obs.send('SetSourceFilterSettings', { sourceName: SOURCES.WEBCAM, @@ -137,7 +140,7 @@ client.on('chat', async (channel, userInfo, message, self) => { break; } - case `${COMMAND_PREFACE}${COMMANDS.TOGGLE_AQUA}`: { + case `${COMMAND_PREFACE}${OBS_COMMANDS.TOGGLE_AQUA}`: { const properties = await obs.send('GetSceneItemProperties', { item: { name: SOURCES.AQUA } }); const { visible } = properties; obs.send('SetSceneItemRender', { source: SOURCES.AQUA, render: !visible }); @@ -147,8 +150,26 @@ client.on('chat', async (channel, userInfo, message, self) => { break; } } -}); +} + +client.on('chat', async (channel, userInfo, message, self) => { + if (self) return; // ignore messages from the bot + + if (message[0] !== COMMAND_PREFACE) return; // ignore non command messages -function getRandomColor() { - return (Math.random() * 4294967296) >>> 0; -} \ No newline at end of file + const normalizedMessage = message.toLowerCase(); + const messageParts = normalizedMessage.split(' '); + + try { + if (userInfo.username === ADMIN_USER) { + handleAdminCommand(channel, messageParts); + } + + if (!active) return; + + handleUserCommand(channel, userInfo, messageParts); + handleOBSCommand(messageParts); + } catch (error) { + logger.error(error); + } +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 12cd546..643024f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -518,11 +518,40 @@ "winston": "0.8.x" }, "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, "colors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true + }, + "winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", + "dev": true, + "requires": { + "async": "0.2.x", + "colors": "0.6.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", + "dev": true + } + } } } }, @@ -680,8 +709,7 @@ "cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", - "dev": true + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" }, "dashdash": { "version": "1.14.1", @@ -1070,8 +1098,7 @@ "eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", - "dev": true + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" }, "fast-deep-equal": { "version": "3.1.3", @@ -1132,6 +1159,31 @@ "utile": "~0.3.0", "winston": "~0.8.1", "yargs": "^3.32.0" + }, + "dependencies": { + "winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", + "dev": true, + "requires": { + "async": "0.2.x", + "colors": "0.6.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + } + } + } } }, "forever-agent": { @@ -2548,6 +2600,21 @@ "ncp": "0.4.x", "rimraf": "2.x.x" } + }, + "winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", + "dev": true, + "requires": { + "async": "0.2.x", + "colors": "0.6.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + } } } }, @@ -3069,8 +3136,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "static-extend": { "version": "0.1.2", @@ -3560,25 +3626,27 @@ "dev": true }, "winston": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", - "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", - "dev": true, + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", + "integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==", "requires": { - "async": "0.2.x", - "colors": "0.6.x", + "async": "~1.0.0", + "colors": "1.0.x", "cycle": "1.0.x", "eyes": "0.1.x", "isstream": "0.1.x", - "pkginfo": "0.3.x", "stack-trace": "0.0.x" }, "dependencies": { "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" } } }, diff --git a/package.json b/package.json index 87a539b..771cfd3 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,9 @@ "license": "ISC", "dependencies": { "dotenv": "^8.2.0", - "obs-websocket-js": "^4.0.2", - "tmi.js": "^1.5.0" + "obs-websocket-js": "~4.0.2", + "tmi.js": "~1.5.0", + "winston": "~2.4.5" }, "devDependencies": { "forever": "^3.0.2", diff --git a/utils/index.js b/utils/index.js new file mode 100644 index 0000000..364d9dd --- /dev/null +++ b/utils/index.js @@ -0,0 +1,7 @@ +function getRandomColor() { + return (Math.random() * 4294967296) >>> 0; +} + +module.exports = { + getRandomColor +}; \ No newline at end of file