buttsbot/bot.js

252 lines
7.5 KiB
JavaScript

const fs = require('fs');
const tmi = require('tmi.js');
// 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 configurations = JSON.parse(configFile);
let defaultConfig = {
word: "butt",
chance: 20,
limit: 10,
syllableCount: 2,
ignoredUsers: []
};
// 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) {
var message = message.trim();
var messageChannel = "#" + userstate['username'];
if (channel === "#butt5b0t") {
if (message === "!subscribe") {
client.join(messageChannel);
client.say(channel, "Joined channel " + messageChannel);
if (!opts.channels.includes(messageChannel)) {
opts.channels.push(messageChannel);
fs.writeFileSync('twitch.config', JSON.stringify(opts));
}
if (!(messageChannel in configurations)) {
configurations[messageChannel] = defaultConfig;
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
}
}
if (message === "!unsubscribe") {
client.part(messageChannel);
client.say(channel, "Left channel " + messageChannel);
if (opts.channels.includes(messageChannel)) {
var indexChannel = opts.channels.indexOf(messageChannel);
opts.channels.splice(indexChannel, 1);
fs.writeFileSync('twitch.config', JSON.stringify(opts));
}
if (messageChannel in configurations) {
delete configurations[messageChannel];
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
}
}
} else {
if (self) return;
let config = (channel in configurations) ? configurations[channel] : defaultConfig;
if (message.startsWith("!buttsbot")) {
if (userstate['room-id'] === userstate['user-id'] || userstate['mod']) {
var parts = message.split(' ');
var action = parts[1];
var value = parts[2];
switch (action) {
case "config":
client.say(channel, "Chance: " + config.chance + "% - Limit: " + config.limit + " - Word: " + config.word + " - Ignored Users: " + config.ignoredUsers.join(', '));
break;
case "word":
config.word = value;
configurations[channel] = config;
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
client.say(channel, "Word set to " + value);
break;
case "chance":
if (isNumeric(value)) {
if (value > 100)
value = 100;
if (value < 0)
value = 0;
config.chance = parseInt(value);
configurations[channel] = config;
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
client.say(channel, "Chance set to " + value + "%");
} 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);
configurations[channel] = config;
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
}
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);
configurations[channel] = config;
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
}
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);
configurations[channel] = config;
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
client.say(channel, "Set new limit: 1 butt per " + value + " words");
} else {
client.say(channel, "Expected a number, got " + value);
}
break;
case "syllables":
if (isNumeric(value)) {
config.syllableCount = parseInt(value);
configurations[channel] = config;
fs.writeFileSync('buttsbot.config', JSON.stringify(configurations));
client.say(channel, "Set required syllables to " + value);
} else {
client.say(channel, "Expected a number, got " + value);
}
default:
client.say(channel, "Action '" + action + "' not recognized.");
}
} else {
console.log(`* buttsbot: User not authorized`);
}
} else {
// ignore commands
if (message.startsWith("!"))
return;
// ignore ignored users
if (config.ignoredUsers.includes(userstate['display-name']) || config.ignoredUsers.includes(userstate['username'])) {
console.log(`* User ${userstate['username']} is on the ignore list.`);
return;
}
// ignore messages containing URLs
let regex = /((http(s)?(\:\/\/))*(www\.)?([\w\-\.\/])*(\.[a-zA-Z]{2,3}\/?))[^\s\b\n|]*[^.,;:\?\!\@\^\$ -]/g;
let matches = message.match(regex);
if (matches && matches.length > 0) {
console.log(`* Message contained URL - skip`);
return;
}
// split messages into word array and try to determine syllabes
var words = message.split(' ');
var syllables = words.map(syllabify);
// calculate syllabe count
var syllableCount = 0;
syllables.forEach((s) => {
syllableCount += s.length;
});
// ignore message if it doesn't contain enough syllabes
if (syllableCount < config.syllableCount) {
console.log(`* Message had too few syllables`);
return;
}
// random chance
// TODO: find a better alternative
var number = Math.random() * 100;
if (number <= config.chance) {
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() ? config.word.toUpperCase() : config.word;
}
}
var newMessage = "";
syllables.forEach((s) => {
if (s != null)
newMessage += s.join('') + ' ';
});
client.say(channel, newMessage.trim());
}
}
}
}
function onConnectedHandler (addr, port) {
console.log(`* Connected to ${addr}:${port}`);
}