From b4031b43f9ef71a12873b9ffd23294ab94280aa0 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 29 Aug 2021 04:00:13 +0200 Subject: [PATCH] Init --- .gitignore | 4 ++ bot.js | 167 ++++++++++++++++++++++++++++++++++++++++++++++ buttsbot.config | 1 + package-lock.json | 92 +++++++++++++++++++++++++ package.json | 15 +++++ 5 files changed, 279 insertions(+) create mode 100644 .gitignore create mode 100644 bot.js create mode 100644 buttsbot.config create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd7c5b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +twitch.config + +node_modules/ diff --git a/bot.js b/bot.js new file mode 100644 index 0000000..1b735a4 --- /dev/null +++ b/bot.js @@ -0,0 +1,167 @@ +const fs = require('fs'); +const tmi = require('tmi.js'); +const moment = require('moment'); + +// Twitch bot options +const optsFile = fs.readFileSync('twitch.config'); +const opts = JSON.parse(optsFile); + +const client = tmi.client(opts); + +// Buttsbot options +const syllableRegex = /[^aeiouy]*[aeiouy]+(?:[^aeiouy]*$|[^aeiouy](?=[^aeiouy]))?/gi; + +let configFile = fs.readFileSync('buttsbot.config') +let config = JSON.parse(configFile); + +console.log(`* Startup: Loaded config: ${config}`); + +let lastMessageButtified = 0; + +// Client +client.on('message', onMessageHandler); +client.on('connected', onConnectedHandler); + +client.connect(); + +// Helper methods +function syllabify(words) { + return words.match(syllableRegex); +} + +function randomIntFromInterval(min, max) { // min and max included + return Math.floor(Math.random() * (max - min + 1) + min) +} + +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} + +// Event handlers +function onMessageHandler (channel, userstate, message, self) { + if (self) { return; } + + var trimmedMessage = message.trim(); + + if (trimmedMessage.startsWith("!buttsbot")) { + if (userstate['room-id'] === userstate['user-id'] || userstate['mod']) { + + var parts = trimmedMessage.split(' '); + var action = parts[1]; + var value = parts[2]; + + switch (action) { + case "config": + client.say(channel, "Timer: " + config.timer + " - Limit: " + config.limit + " - Ignored Users: " + config.ignoredUsers.join(', ')); + break; + case "timer": + if (isNumeric(value)) { + config.timer = parseInt(value) + fs.writeFileSync('buttsbot.config', JSON.stringify(config)); + client.say(channel, "Set new timer to " + value + " seconds.") + } else { + client.say(channel, "Expected a number, got " + value); + } + break; + case "ignore": + if (value && value !== "") { + console.log(`* ignore: value ok`); + value = value.replace("@", ""); + if (!config.ignoredUsers.includes(value)) { + console.log(`* ignore: value not already ignored`); + config.ignoredUsers.push(value); + fs.writeFileSync('buttsbot.config', JSON.stringify(config)); + } + client.say(channel, "User " + value + " will be ignored from now on."); + } else { + client.say(channel, "Excepted username, got empty value"); + } + break; + case "rmignore": + if (value && value !== "") { + console.log(`* rmignore: value ok`); + value = value.replace("@", ""); + if (config.ignoredUsers.includes(value)) { + console.log(`* rmignore: value ignored`); + + let index = config.ignoredUsers.indexOf(value); + config.ignoredUsers.splice(index, 1); + + fs.writeFileSync('buttsbot.config', JSON.stringify(config)); + } + client.say(channel, "User " + value + " will no longer be ignored."); + } else { + client.say(channel, "Expected username, got empty value"); + } + break; + case "limit": + if (isNumeric(value)) { + config.limit = parseInt(value); + fs.writeFileSync('buttsbot.config', JSON.stringify(config)); + client.say(channel, "Set new limit: 1 butt per " + value + " words"); + } else { + client.say(channel, "Expected a number, got " + value); + } + break; + default: + client.say(channel, "Action '" + action + "' not recognized."); + } + } else { + console.log(`* buttsbot: User not authorized`); + } + } else { + if (config.ignoredUsers.includes(userstate['display-name']) || config.ignoredUsers.includes(userstate['username'])) { + console.log(`* User ${userstate['username']} is on the ignore list.`); + return; + } + + var checkDate = moment().subtract(config.timer, 'seconds'); + + if (checkDate < lastMessageButtified) + return; + + if (trimmedMessage.startsWith("!")) + return; + + var words = trimmedMessage.split(' '); + var syllables = words.map(syllabify); + + var buttCount = Math.ceil(words.length / config.limit); + + var randomNumbersUsed = []; + + for (var i = 0; i < buttCount; i++) { + + var random = randomIntFromInterval(1, words.length); + + while (randomNumbersUsed.includes(random)) random = randomIntFromInterval(1, words.length); + + randomNumbersUsed.push(random); + + var word = syllables[random - 1]; + + if (word) { + var randomSyllable = randomIntFromInterval(1, word.length); + + var firstLetterOfSyllable = word[randomSyllable - 1][0]; + + word[randomSyllable - 1] = firstLetterOfSyllable == firstLetterOfSyllable.toUpperCase() ? "BUTT" : "butt"; + } + } + + var newMessage = ""; + + syllables.forEach((s) => { + if (s != null) + newMessage += s.join('') + ' '; + }); + + client.say(channel, newMessage.trim()); + + lastMessageButtified = moment(); + } +} + +function onConnectedHandler (addr, port) { + console.log(`* Connected to ${addr}:${port}`); +} diff --git a/buttsbot.config b/buttsbot.config new file mode 100644 index 0000000..bc95180 --- /dev/null +++ b/buttsbot.config @@ -0,0 +1 @@ +{"timer":120,"limit":10,"ignoredUsers":["StreamElements","Nightbot"]} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c2aa416 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,92 @@ +{ + "name": "subathonbot", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "subathonbot", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "moment": "^2.29.1", + "tmi.js": "^1.8.3" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/tmi.js": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/tmi.js/-/tmi.js-1.8.3.tgz", + "integrity": "sha512-1YAO3hwnkpAbUfVlpxcqOHP4Nun/+TqxWE/e/ZKMB6p40ZKczhI03oZW2Qx3pppqWjXz5kSjHdT/vTTfi2gCGQ==", + "dependencies": { + "node-fetch": "^2.6.1", + "ws": "^7.4.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "tmi.js": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/tmi.js/-/tmi.js-1.8.3.tgz", + "integrity": "sha512-1YAO3hwnkpAbUfVlpxcqOHP4Nun/+TqxWE/e/ZKMB6p40ZKczhI03oZW2Qx3pppqWjXz5kSjHdT/vTTfi2gCGQ==", + "requires": { + "node-fetch": "^2.6.1", + "ws": "^7.4.3" + } + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "requires": {} + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ce3a801 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "subathonbot", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "moment": "^2.29.1", + "tmi.js": "^1.8.3" + } +}