2023-10-02 02:11:27 +00:00
|
|
|
|
// Mercury RSS Client - git.supernets.org/hgw/mercury
|
|
|
|
|
// ____ ___ ___ ____________ _________ __
|
|
|
|
|
// / __ `__ \/ _ \/ ___/ ___/ / / / ___/ / / /
|
|
|
|
|
// / / / / / / __/ / / /__/ /_/ / / / /_/ /
|
|
|
|
|
// /_/ /_/ /_/\___/_/ \___/\__,_/_/ \__, /
|
|
|
|
|
// COLD HARD FEEDS /____/
|
|
|
|
|
//
|
2023-12-04 04:00:56 +00:00
|
|
|
|
//var config = require('./config/default.json');
|
2023-10-02 02:11:25 +00:00
|
|
|
|
//var uconfig = require('./config/usersettings.json');
|
2023-10-02 02:11:23 +00:00
|
|
|
|
var irc = require("irc");
|
|
|
|
|
var fs = require("fs");
|
|
|
|
|
var readline = require('readline');
|
|
|
|
|
const { Worker } = require('worker_threads');
|
2023-12-04 04:00:56 +00:00
|
|
|
|
const { setMaxIdleHTTPParsers } = require("http");
|
2023-10-02 02:11:23 +00:00
|
|
|
|
//var randomWords = require('better-random-words');
|
|
|
|
|
|
2023-12-04 04:00:56 +00:00
|
|
|
|
const timer = ms => new Promise(res => setTimeout(res, ms))
|
|
|
|
|
|
|
|
|
|
console.log('[bot] Checking if config file exists')
|
|
|
|
|
if (fs.existsSync('./config/default.json')) {
|
|
|
|
|
var config = require('./config/default.json');
|
|
|
|
|
console.log('[bot] Config file exists, can proceed with initialisation')
|
|
|
|
|
} else {
|
|
|
|
|
console.log('[bot] The config file, default.json, does not exist in the config folder. Mercury can not start.')
|
|
|
|
|
process.exit()
|
|
|
|
|
}
|
|
|
|
|
timer(100)
|
|
|
|
|
|
2023-10-02 02:11:25 +00:00
|
|
|
|
warningMsg = ''+config.colours.brackets+'['+config.colours.warning+'WARNING'+config.colours.brackets+']'
|
|
|
|
|
errorMsg = ''+config.colours.brackets+'['+config.colours.error+'ERROR'+config.colours.brackets+']'
|
|
|
|
|
|
2023-10-02 02:11:25 +00:00
|
|
|
|
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,
|
2023-10-02 02:11:26 +00:00
|
|
|
|
floodProtection: config.floodprotect.flood_protection,
|
|
|
|
|
floodProtectionDelay: config.floodprotect.flood_protection_delay
|
2023-10-02 02:11:23 +00:00
|
|
|
|
});
|
|
|
|
|
|
2023-10-02 02:11:26 +00:00
|
|
|
|
const msgTimeout = new Set();
|
|
|
|
|
const msgTimeoutMsg = new Set();
|
|
|
|
|
|
2023-10-02 02:11:23 +00:00
|
|
|
|
|
2023-10-02 02:11:25 +00:00
|
|
|
|
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
|
2023-12-01 13:48:30 +00:00
|
|
|
|
consoleLog('[bot.isValidUrl] Testing URL: '+urlString)
|
2023-10-02 02:11:25 +00:00
|
|
|
|
return !!urlPattern.test(urlString);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:26 +00:00
|
|
|
|
function consoleLog(log) {
|
|
|
|
|
if (config.misc.logging === "true") {
|
|
|
|
|
console.log(log)
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:27 +00:00
|
|
|
|
var hostmask = null
|
|
|
|
|
|
2023-10-02 02:11:27 +00:00
|
|
|
|
function checkConfigValidity() {
|
2023-12-04 04:00:56 +00:00
|
|
|
|
consoleLog(`[bot.checkConfigValidity] Opening config validator`)
|
2023-10-02 02:11:27 +00:00
|
|
|
|
const worker = new Worker(`./commands/cvc.js`, {});
|
|
|
|
|
worker.once('message', (string) => {
|
|
|
|
|
if (string == 'kill') {
|
|
|
|
|
process.exit()
|
|
|
|
|
} else if (string == 'allg') {
|
2023-12-04 04:00:56 +00:00
|
|
|
|
consoleLog('[bot.checkConfigValidity] Config seems valid, continuing')
|
2023-10-02 02:11:27 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:27 +00:00
|
|
|
|
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) {
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog(`[bot.openPostWorker] Opening ${command} worker`)
|
2023-10-02 02:11:26 +00:00
|
|
|
|
const worker = new Worker(`./commands/${command}.js`, {
|
|
|
|
|
workerData: {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
d1, d2, d3, d4, d5, d6
|
2023-10-02 02:11:26 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
worker.once('message', (string) => {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
consoleLog(`[bot.openPostWorker.finalising] Got output from ${command}, sending to `+chan);
|
2023-10-02 02:11:26 +00:00
|
|
|
|
bot.say(chan, string);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:23 +00:00
|
|
|
|
async function help(chan, sub) {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
if (sub != undefined ) {
|
|
|
|
|
var sub = sub.toLowerCase()
|
|
|
|
|
}
|
2023-10-02 02:11:26 +00:00
|
|
|
|
openPostWorker(chan, 'help', sub)
|
2023-10-02 02:11:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:25 +00:00
|
|
|
|
async function opt(chan, user, setting, setting2, value, value2) {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
if (setting == undefined && setting2 == undefined && value == undefined && value2 == undefined) {
|
|
|
|
|
openPostWorker(chan, 'help', 'opt')
|
|
|
|
|
}
|
2023-10-02 02:11:27 +00:00
|
|
|
|
if (setting == 'operset' || setting == "get") {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
await checkUserHostmask(user)
|
|
|
|
|
}
|
|
|
|
|
openPostWorker(chan, 'options', user, setting, setting2, value, value2, hostmask)
|
2023-10-02 02:11:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:25 +00:00
|
|
|
|
async function feed(chan, nick, provfeed, n) {
|
2023-10-02 02:11:25 +00:00
|
|
|
|
var userconf = fs.readFileSync('./config/usersettings.json')
|
|
|
|
|
var uconfig = JSON.parse(userconf)
|
2023-10-02 02:11:27 +00:00
|
|
|
|
if (isValidUrl(provfeed) === false) {
|
|
|
|
|
consoleLog('[bot.feed] Provided feed is not a URL, transforming to lowercase')
|
|
|
|
|
var provfeed = provfeed.toLowerCase()
|
|
|
|
|
}
|
2023-10-02 02:11:26 +00:00
|
|
|
|
var predefinedFeeds = ['twitter', 'github']
|
|
|
|
|
var predefString = provfeed.split("/")
|
2023-10-02 02:11:24 +00:00
|
|
|
|
if (provfeed === undefined) {
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[bot.feed] No feed provided')
|
2023-10-02 02:11:25 +00:00
|
|
|
|
bot.say(chan, errorMsg+" No feed has been provided.")
|
|
|
|
|
return;
|
2023-10-02 02:11:25 +00:00
|
|
|
|
} else if (provfeed === 'me' ) {
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[bot.feed] \"me\" was passed, correcting to '+nick)
|
2023-10-02 02:11:25 +00:00
|
|
|
|
var provfeed = nick;
|
2023-10-02 02:11:23 +00:00
|
|
|
|
}
|
2023-10-02 02:11:24 +00:00
|
|
|
|
if (n === undefined) {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
consoleLog('[bot.feed] No post was passed, reverting to '+config.feed.default_amount+', your set default.')
|
2023-10-02 02:11:25 +00:00
|
|
|
|
var n = config.feed.default_amount;
|
2023-10-02 02:11:23 +00:00
|
|
|
|
}
|
2023-10-02 02:11:26 +00:00
|
|
|
|
|
2023-10-02 02:11:26 +00:00
|
|
|
|
if (isValidUrl(provfeed) === true) { //URL Lookup
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[bot.feed] Valid URL requested')
|
2023-10-02 02:11:27 +00:00
|
|
|
|
openPostWorker(chan, 'feed-preset', provfeed, n, nick);
|
2023-10-02 02:11:26 +00:00
|
|
|
|
|
2023-10-02 02:11:27 +00:00
|
|
|
|
} else if (predefinedFeeds.includes(predefString[0])) { //Predefined Feed lookup
|
|
|
|
|
consoleLog('[bot.feed] Detected predefined feed: '+predefString[0])
|
2023-10-02 02:11:27 +00:00
|
|
|
|
openPostWorker(chan, "feed-predef", provfeed, n, nick)
|
2023-10-02 02:11:26 +00:00
|
|
|
|
|
2023-10-02 02:11:26 +00:00
|
|
|
|
} else if (provfeed === nick) { //User Feed Lookup
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[bot.feed] User feed requested')
|
2023-10-02 02:11:26 +00:00
|
|
|
|
if ( uconfig[nick] !== undefined ) { //If users nickname exists in json file
|
|
|
|
|
openPostWorker(chan, 'feed-list', provfeed, n, nick);
|
|
|
|
|
} else { //If it does not
|
2023-10-02 02:11:25 +00:00
|
|
|
|
bot.say(chan, "You have no saved feeds")
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-12-01 13:48:30 +00:00
|
|
|
|
} else if (uconfig[nick].alias[provfeed.toUpperCase()] !== undefined ) { //Alias Lookup
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[bot.feed] Alias requested')
|
2023-10-02 02:11:26 +00:00
|
|
|
|
var provfeed = uconfig[nick].alias[provfeed.toUpperCase()]
|
2023-10-02 02:11:27 +00:00
|
|
|
|
openPostWorker(chan, "feed-preset", provfeed, n, nick);
|
2023-10-02 02:11:26 +00:00
|
|
|
|
} else {
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[bot.feed] No valid feed entered')
|
|
|
|
|
bot.say(chan, errorMsg+" Your chosen feed or alias is not valid")
|
2023-10-02 02:11:25 +00:00
|
|
|
|
}
|
2023-10-02 02:11:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:24 +00:00
|
|
|
|
async function twitter(chan, provfeed, n) {
|
|
|
|
|
if (provfeed === undefined) {
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[bot.twitter] No twitter account provided')
|
2023-10-02 02:11:25 +00:00
|
|
|
|
bot.say(chan, errorMsg+" No account has been provided.")
|
|
|
|
|
return;
|
2023-10-02 02:11:24 +00:00
|
|
|
|
}
|
|
|
|
|
if (n === undefined) {
|
2023-10-02 02:11:25 +00:00
|
|
|
|
var n = config.twitter.default_amount;
|
2023-10-02 02:11:24 +00:00
|
|
|
|
}
|
2023-10-02 02:11:26 +00:00
|
|
|
|
openPostWorker(chan, "twitter", provfeed, n)
|
2023-10-02 02:11:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 02:11:23 +00:00
|
|
|
|
bot.addListener('message', function(nick, to, text, from) {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
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)
|
|
|
|
|
}
|
2023-10-02 02:11:26 +00:00
|
|
|
|
} else {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
var args = text.split(' ');
|
2023-10-02 02:11:27 +00:00
|
|
|
|
var command = args[0].toLowerCase()
|
|
|
|
|
if (command === config.irc.prefix+'help') {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
help(to, args[1]);
|
2023-10-02 02:11:27 +00:00
|
|
|
|
} else if (command === config.irc.prefix+'feed') {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
if (args[1] == undefined ) {
|
|
|
|
|
help(to, "feed")
|
|
|
|
|
} else {
|
|
|
|
|
feed(to, nick, args[1], args[2]);
|
|
|
|
|
}
|
2023-10-02 02:11:27 +00:00
|
|
|
|
} else if (command === config.irc.prefix+'opt') {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
if (args[1] == undefined ) {
|
|
|
|
|
help(to, "opt")
|
|
|
|
|
} else {
|
|
|
|
|
opt(to, nick, args[1], args[2], args[3], args[4])
|
|
|
|
|
}
|
2023-10-02 02:11:27 +00:00
|
|
|
|
}
|
|
|
|
|
msgTimeout.add(to);
|
2023-10-02 02:11:26 +00:00
|
|
|
|
setTimeout(() => {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
msgTimeout.delete(to);
|
|
|
|
|
}, config.floodprotect.command_listen_timeout)
|
2023-10-02 02:11:26 +00:00
|
|
|
|
}
|
2023-10-02 02:11:23 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
bot.addListener('error', function(message) {
|
2023-10-02 02:11:26 +00:00
|
|
|
|
consoleLog('[ERROR]' +message)
|
2023-10-02 02:11:23 +00:00
|
|
|
|
});
|
|
|
|
|
|
2023-10-02 02:11:27 +00:00
|
|
|
|
async function init() {
|
|
|
|
|
consoleLog('[bot.init] Checking if user settings file exists')
|
|
|
|
|
fs.open('./config/usersettings.json','r',function(err, fd){
|
|
|
|
|
if (err) {
|
2023-10-02 02:11:27 +00:00
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
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()
|
|
|
|
|
}
|
2023-12-04 04:00:56 +00:00
|
|
|
|
timer(100)
|
2023-10-02 02:11:27 +00:00
|
|
|
|
consoleLog('[bot.init] User settings file has been created')
|
2023-10-02 02:11:27 +00:00
|
|
|
|
} else {
|
|
|
|
|
consoleLog("[bot.init] User settings file exists");
|
|
|
|
|
}
|
2023-10-02 02:11:27 +00:00
|
|
|
|
});
|
2023-12-04 04:00:56 +00:00
|
|
|
|
await timer(500)
|
2023-12-04 04:20:48 +00:00
|
|
|
|
try {
|
|
|
|
|
if (config.errorhandling.validity_override === "TRUE") {
|
|
|
|
|
consoleLog('[bot.init] Config validity override switch enabled, will not check for validity')
|
|
|
|
|
}
|
|
|
|
|
} catch(e) {
|
|
|
|
|
consoleLog('[bot.init] Checking config validity')
|
|
|
|
|
checkConfigValidity()
|
|
|
|
|
}
|
2023-12-04 04:00:56 +00:00
|
|
|
|
await timer(2000)
|
2023-10-02 02:11:27 +00:00
|
|
|
|
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');
|
2023-10-02 02:11:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init()
|
2023-10-02 02:11:26 +00:00
|
|
|
|
|
|
|
|
|
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(" "))
|
|
|
|
|
}
|
|
|
|
|
});
|