mercury/bot.js

254 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Mercury RSS Client - git.supernets.org/hgw/mercury
// ____ ___ ___ ____________ _________ __
// / __ `__ \/ _ \/ ___/ ___/ / / / ___/ / / /
// / / / / / / __/ / / /__/ /_/ / / / /_/ /
// /_/ /_/ /_/\___/_/ \___/\__,_/_/ \__, /
// COLD HARD FEEDS /____/
//
var config = require('./config/default.json');
//var uconfig = require('./config/usersettings.json');
var irc = require("irc");
var fs = require("fs");
var readline = require('readline');
const { Worker } = require('worker_threads');
//var randomWords = require('better-random-words');
warningMsg = ''+config.colours.brackets+'['+config.colours.warning+'WARNING'+config.colours.brackets+']'
errorMsg = ''+config.colours.brackets+'['+config.colours.error+'ERROR'+config.colours.brackets+']'
var bot = new irc.Client(config.irc.server, config.irc.nickname, {
channels: config.irc.channels,
secure: config.irc.ssl,
port: config.irc.port,
autoRejoin: config.irc.autorejoin,
userName: config.irc.username,
realName: config.irc.realname,
floodProtection: config.floodprotect.flood_protection,
floodProtectionDelay: config.floodprotect.flood_protection_delay
});
const msgTimeout = new Set();
const msgTimeoutMsg = new Set();
const timer = ms => new Promise(res => setTimeout(res, ms))
const isValidUrl = urlString=> {
var urlPattern = new RegExp('^(https?:\\/\\/)?'+ // validate protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // validate domain name
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // validate OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // validate port and path
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // validate query string
'(\\#[-a-z\\d_]*)?$','i'); // validate fragment locator
return !!urlPattern.test(urlString);
}
function consoleLog(log) {
if (config.misc.logging === "true") {
console.log(log)
} else {
return;
}
}
var hostmask = null
function checkConfigValidity() {
consoleLog(`[bot.checkConfigValidity] Opening cvc worker`)
const worker = new Worker(`./commands/cvc.js`, {});
worker.once('message', (string) => {
if (string == 'kill') {
process.exit()
} else if (string == 'allg') {
consoleLog('[bot.checkConfigValidity] Config validity checked, seems all good, continuing with initialisation')
}
});
}
function checkUserHostmask(user) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
bot.whois(user, function(callback) {
hostmask = callback.user+"@"+callback.host
consoleLog('[main.checkUserHostmask] User hostmask is '+hostmask)
resolve(hostmask)
})
}, 750)
})
}
function openPostWorker(chan, command, d1, d2, d3, d4, d5, d6) {
consoleLog(`[bot.openPostWorker] Opening ${command} worker`)
const worker = new Worker(`./commands/${command}.js`, {
workerData: {
d1, d2, d3, d4, d5, d6
}
});
worker.once('message', (string) => {
consoleLog(`[bot.openPostWorker.finalising] Got output from ${command}, sending to `+chan);
bot.say(chan, string);
});
}
async function help(chan, sub) {
if (sub != undefined ) {
var sub = sub.toLowerCase()
}
openPostWorker(chan, 'help', sub)
}
async function opt(chan, user, setting, setting2, value, value2) {
if (setting == undefined && setting2 == undefined && value == undefined && value2 == undefined) {
openPostWorker(chan, 'help', 'opt')
}
if (setting == 'operset' || setting == "get") {
await checkUserHostmask(user)
}
openPostWorker(chan, 'options', user, setting, setting2, value, value2, hostmask)
}
async function feed(chan, nick, provfeed, n) {
var userconf = fs.readFileSync('./config/usersettings.json')
var uconfig = JSON.parse(userconf)
if (isValidUrl(provfeed) === false) {
consoleLog('[bot.feed] Provided feed is not a URL, transforming to lowercase')
var provfeed = provfeed.toLowerCase()
}
var predefinedFeeds = ['twitter', 'github']
var predefString = provfeed.split("/")
if (provfeed === undefined) {
consoleLog('[bot.feed] No feed provided')
bot.say(chan, errorMsg+" No feed has been provided.")
return;
} else if (provfeed === 'me' ) {
consoleLog('[bot.feed] \"me\" was passed, correcting to '+nick)
var provfeed = nick;
}
if (n === undefined) {
consoleLog('[bot.feed] No post was passed, reverting to '+config.feed.default_amount+', your set default.')
var n = config.feed.default_amount;
}
if (isValidUrl(provfeed) === true) { //URL Lookup
consoleLog('[bot.feed] Valid URL requested')
openPostWorker(chan, 'feed-preset', provfeed, n, nick);
} else if (predefinedFeeds.includes(predefString[0])) { //Predefined Feed lookup
consoleLog('[bot.feed] Detected predefined feed: '+predefString[0])
openPostWorker(chan, "feed-predef", provfeed, n, nick)
} else if (provfeed === nick) { //User Feed Lookup
consoleLog('[bot.feed] User feed requested')
if ( uconfig[nick] !== undefined ) { //If users nickname exists in json file
openPostWorker(chan, 'feed-list', provfeed, n, nick);
} else { //If it does not
bot.say(chan, "You have no saved feeds")
return;
}
} else if (uconfig[nick].alias !== undefined ) { //Alias Lookup
consoleLog('[bot.feed] Alias requested')
var provfeed = uconfig[nick].alias[provfeed.toUpperCase()]
openPostWorker(chan, "feed-preset", provfeed, n, nick);
} else {
consoleLog('[bot.feed] No valid feed entered')
bot.say(chan, errorMsg+" Your chosen feed or alias is not valid")
}
}
async function twitter(chan, provfeed, n) {
if (provfeed === undefined) {
consoleLog('[bot.twitter] No twitter account provided')
bot.say(chan, errorMsg+" No account has been provided.")
return;
}
if (n === undefined) {
var n = config.twitter.default_amount;
}
openPostWorker(chan, "twitter", provfeed, n)
}
bot.addListener('message', function(nick, to, text, from) {
if (text.startsWith(config.irc.prefix)) {
if (msgTimeout.has(to)) {
if (msgTimeoutMsg.has("yes")) {
return;
} else {
bot.say(to, errorMsg+" You are sending commands too quickly")
msgTimeoutMsg.add("yes");
setTimeout(() => {
msgTimeoutMsg.delete("yes");
}, config.floodprotect.command_listen_timeout)
}
} else {
var args = text.split(' ');
var command = args[0].toLowerCase()
if (command === config.irc.prefix+'help') {
help(to, args[1]);
} else if (command === config.irc.prefix+'feed') {
if (args[1] == undefined ) {
help(to, "feed")
} else {
feed(to, nick, args[1], args[2]);
}
} else if (command === config.irc.prefix+'opt') {
if (args[1] == undefined ) {
help(to, "opt")
} else {
opt(to, nick, args[1], args[2], args[3], args[4])
}
}
msgTimeout.add(to);
setTimeout(() => {
msgTimeout.delete(to);
}, config.floodprotect.command_listen_timeout)
}
}
});
bot.addListener('error', function(message) {
consoleLog('[ERROR]' +message)
});
async function init() {
consoleLog('[bot.init] Checking config validity')
checkConfigValidity()
await timer(1500)
consoleLog('[bot.init] Checking if user settings file exists')
fs.open('./config/usersettings.json','r',function(err, fd){
if (err) {
fs.writeFile('./config/usersettings.json', '', function(err) {
if(err) {
consoleLog(err);
consoleLog('[bot.init] [FATAL] User settings file could not be created. Mercury can not start')
process.exit()
}
consoleLog("[bot.init] User settings does not exist, please hold...");
});
try {
fs.writeFileSync('./config/usersettings.json', "\{\n\}")
} catch(e) {
consoleLog(e)
consoleLog('[bot.init] [FATAL] User settings file was created but is not writable, could be a permissions issue. Mercury can not start')
process.exit()
}
consoleLog('[bot.init] User settings file has been created')
} else {
consoleLog("[bot.init] User settings file exists");
}
});
await timer(100)
if (config.irc.ssl == "true") {
consoleLog('[bot.init] Initialisation completed, connecting to '+config.irc.server+'/'+config.irc.port+' (SSL) as '+config.irc.nickname);
} else {
consoleLog('[bot.init] Initialisation completed, connecting to '+config.irc.server+'/'+config.irc.port+' as '+config.irc.nickname);
}
consoleLog('[bot] Welcome to Mercury');
}
init()
process.on('uncaughtException', function (err) {
console.error(err);
if (config.errorhandling.log_errors_to_irc == 'true') {
bot.say(config.errorhandling.error_channel, errorMsg+" "+err.stack.split('\n',1).join(" "))
}
});