Skip to content

Instantly share code, notes, and snippets.

@LunaSquee
Last active February 20, 2019 11:34
Show Gist options
  • Save LunaSquee/95e849c9d44a4874501f to your computer and use it in GitHub Desktop.
Save LunaSquee/95e849c9d44a4874501f to your computer and use it in GitHub Desktop.
nBot Plugin of Squeebot.
#!/usr/bin/env node
'use strict';
// IRC bot by LunaSquee (Originally djazz, best poni :3)
// This is a plugin for nBot (https://git.mindcraft.si.eu.org/?p=nBot.git)
// Before using: npm install gamedig googleapis qs
// Have fun!
// Modules
var net = require('net')
var url = require('url')
var util = require('util')
var readline = require('readline')
var gamedig = require('gamedig')
var fs = require('fs')
var events = require("events")
var emitter = new events.EventEmitter()
var exec = require("child_process").exec
var google = require('googleapis')
var qs = require('qs')
var path = require('path')
var squeeDir = __dirname+'/../squeebot/'
var alpaca = require(squeeDir+'alpaca.json')
var responses = 'generic.json'
var responselist = require(squeeDir+'responselist/'+responses)
// useful variables
var NICK; // The bot's nickname
var PREFIX; // The prefix of commands
var relayserver; // Relay server
var relayConnections = {}; // Relay connections
var calSyncInterval = false;
// nBot variables
var botObj;
var pluginId;
var botF;
var botInstanceSettings;
var settings;
var ircChannelUsers;
// Google APIs
var gCalendar = google.calendar('v3');
// Episode countdown (!nextep)
var week = 7*24*60*60*1000;
var airDate;
// Notes:
// Get hostname from sender: data.rawdata[0].split(' ')[0][1];
// rules/infoc: Rules and information for individual channels.
// botops: Bot operators with permission levels
// ophosts: IMPORTANT! Currently, in order for botops to execute commands, their hostnames MUST be listed here!
var squees = {
rules:{},
infoc:{},
botops:{"icydiamond":3},
ophosts:{}
}
// Events
var sEvents = [];
// Squeebot splash
var splash ="\x1b[1;36m____ _ _ \n"+
"/ ___| __ _ _ _ ___ ___| |__ ___ | |_ \n"+
"\\___ \\ / _` | | | |/ _ \\/ _ \\ \'_ \\ / _ \\| __|\n"+
" ___) | (_| | |_| | __/ __/ |_) | (_) | |_ \n"+
"|____/ \\__, |\\__,_|\\___|\\___|_.__/ \\___/ \\__|\n"+
" |_| \x1b[0m"
// This is the list of all your commands.
// "command":{"action":YOUR FUNCTION HERE, description:COMMAND USAGE(IF NOT PRESENT, WONT SHOW UP IN !commands)}
// Action arguments are the following: simplified, nick, chan, message, pretty, target, isMentioned, isPM
var commands = {
"help":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1]) {
if(simplified[1] === "-a") {
listCommands(nick, target, 1);
return;
}
var cmdName = (simplified[1].indexOf(PREFIX) === 0 ? simplified[1].substring(1) : simplified[1]).toLowerCase();
if(cmdName in commands) {
var cmd = commands[cmdName];
if(cmd.description) {
sendPM(target, nick+": \u0002"+PREFIX+cmdName+"\u000f "+cmd.description+("permlevel" in cmd ? " \u0002["+permstring(cmd.permlevel).toUpperCase()+"]" : ""));
} else {
if(cmd.alias) {
var cmd2 = commands[cmd.alias]
if(cmd2 != null && cmd2.description) {
sendPM(target, nick+": \u0002"+PREFIX+cmdName+"\u000f "+cmd2.description+("permlevel" in cmd2 ? " \u0002["+permstring(cmd2.permlevel).toUpperCase()+"]" : "")+" \u00037[ALIAS FOR \u00033"+cmd.alias+"\u00037]");
return;
}
}
sendPM(target, nick+": \u0002"+PREFIX+cmdName+"\u000f - No description :( "+("permlevel" in cmd ? " \u0002["+permstring(cmd.permlevel).toUpperCase()+"]" : ""));
}
} else {
sendPM(target, nick+": That is not a known command!");
}
} else {
listCommands(nick, target);
}
}), description:"[command] - All Commands"},
"cmdsource":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1]) {
var cmdName = (simplified[1].indexOf(PREFIX) === 0 ? simplified[1].substring(1) : simplified[1]).toLowerCase();
if(cmdName in commands) {
var cmd = commands[cmdName];
if("source" in cmd) {
sendPM(target, "Command \u00033"+cmdName+"\u000f is from plugin \u00037"+cmd.source);
} else {
sendPM(target, "Command \u00033"+cmdName+"\u000f is from plugin \u00037"+pluginId);
}
} else {
sendPM(target, nick+": That is not a known command!");
}
} else {
listCommands(nick, target);
}
}), description:"[command] - Source plugin of this command", permlevel: 1},
"squeebot":{"action":(function(simplified, nick, chan, message, pretty, target) {
sendPM(target, "Squeebot is a plugin for nBot (by nnnn20430) written by LunaSquee.");
if(simplified[1] && simplified[1].toLowerCase()==="source")
sendPM(target, nick+", You can see the source here: https://gist.github.com/LunaSquee/95e849c9d44a4874501f");
}), description:"[source] - Squeebot info"},
"info":{"action":(function(simplified, nick, chan, message, pretty, target, mentioned, pm) {
if(pm) {
sendPM(target, "This command can only be executed in a channel.");
} else {
var channel = chan.toLowerCase();
if("infoc" in squees) {
if(channel in squees.infoc) {
sendPM(target, nick+": "+squees.infoc[channel]);
return
}
}
sendPM(target, "No information to display for "+chan);
}
}), description:"- Channel Information"},
"events":{"action":(function(simplified, nick, chan, message, pretty, target, mentioned, pm) {
if(simplified[1] && simplified[1].toLowerCase() == "refresh") {
synccalendar(11);
return;
}
if (sEvents.length === 0) {
sendPM(target, "\u0002Events: \u0002\u00034No events");
return;
}
var eEvents = [];
sEvents.forEach(function(t) {
var isRunning = currentRunCheck(t.eventStartTime, t.eventEndTime || 0);
if(isRunning === 0)
eEvents.push("\u00037"+t.eventName+"\u0003");
else if(isRunning === 1)
eEvents.push("\u00033"+t.eventName+"\u0003");
else
eEvents.push("\u00034"+t.eventName+"\u0003");
});
sendPM(target, "\u0002Events: \u000f"+eEvents.join(", "));
}), description:"- List events"},
"event":{"action":(function(simplified, nick, chan, message, pretty, target, mentioned, pm) {
if(simplified[1] != null) {
var specify = 1;
if(simplified[1] == '-d')
specify = 2;
if(parseInt(simplified[specify])) {
var valid = sEvents[parseInt(simplified[specify]) - 1];
if(valid) {
tellEvent(valid, target, specify === 1);
} else {
sendPM(target, nick+": No event with that id found!");
}
} else if(typeof simplified[specify] == "string") {
var amount = 0;
sEvents.forEach(function(t) {
if(t.eventName.toLowerCase().indexOf(message.split(' ').slice(specify).join(' ').toLowerCase()) === 0) {
if(amount > 0) return;
tellEvent(t, target, specify === 1);
amount += 1;
}
});
if(amount === 0) {
sendPM(target, nick+": No such event found!");
}
}
} else {
sendPM(target, nick+": Not enough arguments! Usage: \u0002!event\u0002 <index/name>");
}
}), description:"[-d] <index/name> - Event information"},
"rules":{"action":(function(simplified, nick, chan, message, pretty, target, mentioned, pm) {
if(pm) {
sendPM(target, "This command can only be executed in a channel.");
} else {
var channel = chan.toLowerCase();
var t = nick;
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
t = simplified[1];
if("rules" in squees) {
if(channel in squees.rules) {
sendPM(channel, t+": Channel Rules of "+chan+": ");
var rls = squees.rules[channel];
if(typeof rls == "object") {
rls.forEach(function(e) {
sendPM(channel, "["+(rls.indexOf(e)+1)+"] "+e);
});
} else {
sendPM(channel, rls);
}
return;
}
}
sendPM(target, "No rules to display for "+chan);
}
}), description:"- Channel Rules"},
"yay":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
sendPM(target, simplified[1]+": http://flutteryay.com");
else
sendPM(target, nick+": http://flutteryay.com");
})},
"date":{"action":(function(simplified, nick, chan, message, pretty, target) {
var date = '';
switch (simplified[1] ? simplified[1].toUpperCase() : null) {
case 'UTC':
date = new Date().toUTCString();
break;
case 'UNIX':
date = Math.round(new Date().getTime() / 1000);
break;
default:
date = new Date();
}
sendPM(target, date);
})},
"squee":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
sendPM(target, simplified[1]+": https://www.youtube.com/watch?v=O1adNgZl_3Q");
else
sendPM(target, nick+": https://www.youtube.com/watch?v=O1adNgZl_3Q");
})},
"timetostop":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
sendPM(target, simplified[1]+": https://www.youtube.com/watch?v=2k0SmqbBIpQ");
else
sendPM(target, nick+": https://www.youtube.com/watch?v=2k0SmqbBIpQ");
})},
"banned":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
sendPM(target, simplified[1]+": https://derpibooru.org/795478");
else
sendPM(target, nick+": https://derpibooru.org/795478");
})},
"request":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
sendPM(target, simplified[1]+": To request a song, simply ask us. Provide a youtube link or just the song's title and artist!");
else
sendPM(target, nick+": To request a song, simply ask us. Provide a youtube link or just the song's title and artist!");
})},
"hug":{"action":(function(simplified, nick, chan, message, pretty, target) {
sendPMact(target, "hugs "+nick);
})},
"episodes":{"action":(function(simplified, nick, chan, message, pretty, target) {
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
sendPM(target, simplified[1]+": List of all MLP:FiM Episodes: http://mlp-episodes.tk/");
else
sendPM(target, nick+": List of all MLP:FiM Episodes: http://mlp-episodes.tk/");
}),description:"- List of pony episodes"},
"mc":{"action":(function(simplified, nick, chan, message, pretty, target) {
var reqplayers = false;
if(simplified[1] === "players") {
reqplayers = true;
}
getGameInfo("minecraft", "minecraft.djazz.se", function(err, msg) {
if(err) {
sendPM(target, err);
return;
}
sendPM(target, msg);
}, reqplayers);
}), alias:"minecraft"},
"mumble":{"action":(function(simplified, nick, chan, message, pretty, target) {
var requsers = false;
if(simplified[1] === "users") {
requsers = true;
}
if(simplified[1] === "download") {
sendPM(target, "\u000310[Mumble] \u00033Download Mumble here: \u000312http://wiki.mumble.info/wiki/Main_Page#Download_Mumble");
return;
}
getGameInfo("mumbleping", "mumble.djazz.se", function(err, msg) {
if(err) {
sendPM(target, err);
return;
}
sendPM(target, msg);
}, requsers);
}), description:"[users/download] - Information about our Mumble Server"},
"episode":{"action":(function(simplified, nick, chan, message, pretty, target) {
var param = simplified[1];
if(param != null) {
var epis = param.match(/^s([0-9]+)e([0-9]+)$/i);
if(epis && epis[2]<=26 && epis[1]<=6){
var link = "http://mlp-episodes.tk/#epi"+epis[2]+"s"+epis[1];
sendPM(target, nick+": Watch the episode you requested here: "+link);
} else {
sendPM(target, nick+": Correct usage !ep s[season number]e[episode number]");
}
} else {
sendPM(target, nick+": Please provide me with episode number and season, for example: !ep s4e4");
}
}),description:"s<Season>e<Episode Number> - Open a pony episode"},
"viewers":{"action":(function(simplified, nick, chan, message, pretty, target) {
livestreamViewerCount((function(r, a) {
if(r === "offline")
r = "\u00034The livestream is offline";
else
r = a;
sendPM(target, r+" \u00033Livestream: \u000312http://radio.djazz.se/#livestream")
}), 0);
}),alias: "livestream"},
"np":{"action":(function(simplified, nick, chan, message, pretty, target) {
getCurrentSong(function(d, e, i) {
if(i) {
sendPM(target, "\u00033Now playing: \u000312"+d+" \u00033Listeners: \u000312"+e+" \u00033Click here to tune in: \u000312http://radio.djazz.se/");
} else {
sendPM(target, d);
}
})
}), alias: "radio"},
"l":{"action":(function(simplified, nick, chan, message, pretty, target) {
getCurrentSong(function(d, e, i) {
if(i) {
sendPM(target, "\u00033Listeners: \u000312"+e+" \u00033Tune in: \u000312http://radio.djazz.se/");
} else {
sendPM(target, d);
}
})
}), alias: "listeners"},
"nextep":{"action":(function(simplified, nick, chan, message, pretty, target) {
var counter = 0;
var now = Date.now();
do {
var timeLeft = Math.max(((airDate+week*(counter++)) - now)/1000, 0);
} while (timeLeft === 0 && counter < settings.nextepisode.countTimes);
if (counter === settings.nextepisode.countTimes) {
sendPM(target, "Season "+settings.nextepisode.inSeason+" is over :(");
} else {
sendPM(target, /*(counter == 1 ? "First" : "Next") + */"Next Season "+settings.nextepisode.inSeason+" episode airs in %s", readableTime(timeLeft, true));
}
//commands["event"].action(["event", "episode"], nick, chan, message, pretty, target);
}),description:"- Time left until next pony episode."},
"nothing":{description:"- Does absolutely nothing."},
"alpaca":{action: function(simplified, nick, chan, message, pretty, target) {
if(simplified[1] && simplified[1] in botObj.publicData.ircChannelUsers[chan])
nick = simplified[1]
var rand = Math.floor(Math.random() * alpaca.length)
sendPM(target, nick+": http://jocketf.se/c/"+alpaca[rand])
}},
"squees":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(nick != "Diamond" && nick != "IcyDiamond" && nick != "LunaSquee")
return sendPM(target, nick+": I don't think so.")
if(simplified[1] === "save") {
squees_save(target);
} else if(simplified[1] === "load") {
squees_load(target);
} else if(simplified[1] === "add") {
if (simplified[2] === "host") {
var hostas = message.split(' ').slice(3).join(' ');
if(hostas != null) {
if(hostas in squees.ophosts)
sendPM(target, "Already there!");
else
squees.ophosts[hostas] = '';
} else {
sendPM(target, "Invalid hostname");
}
}
} else if(simplified[1] === "del") {
if (simplified[2] === "host") {
var hostas = message.split(' ').slice(3).join(' ');
if(hostas != null) {
if(!hostas in squees.ophosts)
sendPM(target, "Hostname not in list!");
else
delete squees.ophosts[hostas];
} else {
sendPM(target, "Invalid hostname");
}
}
} else if(simplified[1] === "promote") {
var nickz = simplified[2];
var level = 0;
if(parseInt(simplified[3])) {
level = parseInt(simplified[3]);
} else {
sendPM(target, "Invalid number '"+simplified[3]+"'");
}
if(nickz != null) {
squees.botops[nickz.toLowerCase()] = level;
sendPM(target, "Changed permlevel of user "+nickz+" to "+permstring(level));
} else {
sendPM(target, "Invalid nickname");
}
} else if(simplified[1] === "demote") {
var nickz = simplified[2];
var level = 0;
if(parseInt(simplified[3])) {
level = parseInt(simplified[3]);
} else {
sendPM(target, "Invalid number '"+simplified[3]+"'");
}
if(nickz != null) {
if (nickz.toLowerCase() in squees.botops) {
delete squees.botops[nickz.toLowerCase()];
sendPM(target, "Removed user "+nickz+" from list");
}
} else {
sendPM(target, "Invalid nickname");
}
}
}), description:"- Permission Management", "permlevel": 3},
"plugin":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(simplified[1] == "load") {
botF.botPluginLoad(simplified[2], botInstanceSettings.pluginDir+'/'+simplified[2]+'.js');
botInstanceSettings.plugins.arrayValueAdd(simplified[2]);
} else if(simplified[1] == "reload") {
if (botObj.pluginData[simplified[2]]) {
pluginReload(simplified[2]);
}
} else if(simplified[1] == "reloadall") {
pluginReload(pluginId);
for (var plugin in botObj.pluginData) {
if (plugin != pluginId && plugin != 'simpleMsg') {
pluginReload(plugin);
}
}
} else if(simplified[1] == "unload" || simplified[1] == "remove") {
botF.botPluginDisable(simplified[2]);
botInstanceSettings.plugins.arrayValueRemove(simplified[2]);
}
}), description:"<load/reload/reloadall/unload> [plugin] - Plugin management", "permlevel":3},
"binary":{action: (function(simplified, nick, chan, message, data, target, isMentioned, isPM) {
var response = '', strArr, i, message = '';
for (i in data.messageARGS) {
if (i > 1) {
message += ' '+data.messageARGS[i];
}
}
message = message.substr(1)
switch (data.messageARGS[1] ? data.messageARGS[1].toUpperCase() : null) {
case 'ENCODE': strArr = message.split('');
for (i in strArr) {
response += ' '+('0000000'+parseInt(new Buffer(strArr[i].toString(), 'utf8').toString('hex'), 16).toString(2)).slice(-8);
}
response=response.substr(1);
break;
case 'DECODE': message=message.split(' ').join('');
i = 0;
while (8*(i+1) <= message.length) {
response += new Buffer(parseInt(message.substr(8*i, 8), 2).toString(16), 'hex').toString('utf8'); i++;
}
response = "Decoded: "+response;
}
sendPM(target, response);
}), description: "<ENCODE/DECODE> <message> - Encode/decode binary (ASCII)"},
"evaljs":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
eval("(function () {"+message.split(" ").slice(1).join(" ")+"})")();
}), description:"<code> - Run javascript code.", "permlevel":3},
"echo":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
sendPM(target, pretty.messageARGS[1].replaceSpecialChars());
}), description:"<msg> - Echo back.", "permlevel":2},
"convertseconds":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(parseInt(simplified[1]))
sendPM(target, readableTime(parseInt(simplified[1]), true));
else
sendPM(target, "Invalid number");
}), description:"<seconds> - Convert seconds to years days hours minutes seconds."},
"converttime":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(simplified[1] != null)
sendPM(target, parseTimeToSeconds(message.substring(PREFIX.length+11-(isPM ? 0 : 1))) + " seconds");
else
sendPM(target, "Invalid string");
}), description:"<years>y <weeks>w <days>d <hours>h <minutes>m <seconds>s - Convert ywdhms to seconds."},
"say":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
var channel = pretty.messageARGS[1];
if(channel != null)
sendPM(channel, message.split(" ").slice(2).join(" ").replaceSpecialChars());
}), description:"<channel> <msg> - Say in channel as bot.", "permlevel":2},
"act":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
var channel = pretty.messageARGS[1];
if(channel != null)
sendPMact(channel, message.split(" ").slice(2).join(" ").replaceSpecialChars());
}), description:"<channel> <msg> - Act in channel as bot.", "permlevel":2},
"permlevel":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(simplified[1] && simplified[1].toLowerCase() in squees.botops) {
sendPM(target, simplified[1]+" is "+permstring(squees.botops[simplified[1].toLowerCase()]));
} else {
sendPM(target, "Not in list.");
}
})},
"skip":{"action":(function(simplified, nick, chan, message, pretty, target, mentioned, isPM) {
if(settings["paraspritekey"] == null) return;
var url = "http://radio.djazz.se/api/skip?apikey="+settings.paraspritekey;
fetchJSON(url, function(err, res) {
if(err) {
sendPM(target, "Skip failed.");
} else {
if(!isPM) {
sendPM(target, "Skipped song");
}
}
});
}),description:"- Skip the current song.", "permlevel":1},
"announce":{"action":(function(simplified, nick, chan, message, pretty, target, mentioned, isPM) {
if(settings["paraspritekey"] == null) return;
var text = "";
if(!simplified[1]) {
sendPM(target, nick+": Not enough parameters!");
return;
}
var text = message.split(" ").slice(1).join(" ");
var url = "http://radio.djazz.se/api/announce?apikey="+settings.paraspritekey+"&message="+encodeURIComponent(text);
fetchJSON(url, function(err, res) {
if(err) {
sendPM(target, "Announce failed.");
} else {
if(!isPM) {
sendPM(target, "Announcement sent!");
}
}
});
}),description:"[say:]<message> - Play an announcement on radio.", "permlevel":1},
"queue":{"action":(function(simplified, nick, chan, message, pretty, target, mentioned, isPM) {
if(settings["paraspritekey"] == null) return;
if(!simplified[1]) {
sendPM(target, nick+": Not enough parameters!");
return;
}
var src = message.split(" ").slice(1).join(" ");
if(src === "") {
sendPM(target, nick+": Error occured!");
return;
}
var url = "http://radio.djazz.se/api/queue?apikey="+settings.paraspritekey+"&add="+encodeURIComponent(src);
fetchJSON(url, function(err, res) {
if(err) {
sendPM(target, "Queue failed.");
} else {
if(!isPM) {
if(src.indexOf("soundcloud.com") !== -1) {
getSoundcloudFromUrl(src, target, true);
} else if(src.indexOf("youtu.be/") !== -1) {
var det = src.match(/youtu.be\/([^\?\&\#]+)/i)[1];
if(det) {
getYoutubeFromVideo(det, target, true);
}
} else if(src.indexOf("youtube.com/") !== -1) {
var det = src.match("[\\?&]v=([^&#]*)");
if(det) {
getYoutubeFromVideo(det[1], target, true);
}
} else {
sendPM(target, "Queued!");
}
}
}
});
}),description:"<file/url> - Queue a song.", "permlevel":1},
"sh":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(settings["allowShell"] == false) {
sendPM(target, "Using shell is disabled.");
return;
}
if(!simplified[1]) return;
var command = message.substring(isPM ? 3 : PREFIX.length+3);
exec(command, {shell: '/bin/bash'}, function(error, stdout, stderr) {
if(stdout) {
sendPM(target, stdout.replace(/\n/g, " ;; "));
} else {
mylog(error);
}
});
}), description:"<command> - icypi shell command.", "permlevel":3},
"icypi":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(settings["allowShell"] == false) {
sendPM(target, "Using shell is disabled.");
return;
}
exec("cat /sys/class/thermal/thermal_zone0/temp", {shell: '/bin/bash'}, function(error1, stdout1, stderr1) {
if(stdout1) {
exec("uptime", {shell: '/bin/bash'}, function(error, stdout, stderr) {
if(stdout) {
var loadavg = stdout.match(/load average: (\d\.\d+), (\d\.\d+), (\d\.\d+)/).slice(1);
var t = stdout.match(/up (.+),? \d+ users?/).slice(1);
var uptime = t[t.length-1].substring(0, t[t.length-1].length-2);
loadavg = loadstat(loadavg);
sendPM(target, "\u000310[icypi]\u000f \u00033uptime:\u000312 "+uptime+" \u000f\u00033load avg:\u000f"+loadavg+" \u00033temp:\u000312 "+(parseInt(stdout1)/1000)+"°C");
} else {
mylog(error);
}
});
} else {
mylog(error1);
}
});
}), description:"- icypi status report."},
"randomsentence":{"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
postJSON("http://watchout4snakes.com/wo4snakes/Random/NewRandomSentence", {},
function(e, c, p) {
sendPM(target, c);
});
}), description:"- Generate a random sentence."},
"aliases": {"action":(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(!simplified[1]) {
sendPM(target, nick+": Please specify command.");
return;
}
if(!commands[simplified[1].toLowerCase()]) {
sendPM(target, nick+": That is not a valid command!");
return;
}
var aliases = [];
for(var c in commands) {
var cmd = commands[c];
if(cmd["alias"] && cmd["alias"] == simplified[1].toLowerCase()) {
aliases.push("\u00033"+c+"\u000f");
}
}
if(aliases.length === 0) {
sendPM(target, nick+": That command has no aliases.");
return;
}
sendPM(target, "Aliases for \u00033"+simplified[1].toLowerCase()+"\u000f: "+aliases.join(", "));
}), description:"<command> - Find aliases for this command."},
"responselist": {action:(function(simplified, nick, chan, message, pretty, target, isMentioned, isPM) {
if(simplified[1] == null)
return sendPM(target, "Currently using response list "+responselist);
if(simplified[1] == 'load')
return response_list_load()
if(simplified[1] == 'save')
return response_list_save()
}), permlevel: 3},
"listeners":{"action":(function() {commands['l'].action.apply(null, arguments)}), description: "- Number of people listening to Parasprite Radio"},
"radio":{"action":(function() {commands['np'].action.apply(null, arguments)}), description: "- Current song on Parasprite Radio"},
"livestream":{"action":(function() {commands['viewers'].action.apply(null, arguments)}), description: "- Number of people watching djazz'es Livestream"},
"minecraft":{"action":(function() {commands['mc'].action.apply(null, arguments)}), description: "[players] - Information about our Minecraft Server"},
"ep":{"action":(function() {commands['episode'].action.apply(null, arguments)}), alias: "episode"}
};
// PRIVMSG functions such as CTCP handling
var privmsgFunc = {
ctcpRespond:function(data) {
var timestamp;
if (new RegExp('\x01VERSION\x01', 'g').exec(data.message) !== null) {
botF.ircSendCommandNOTICE("\x01VERSION I'm a plugin for nBot written by LunaSquee.\x01", data.nick);
} else if (new RegExp('\x01CLIENTINFO\x01', 'g').exec(data.message) !== null) {
botF.ircSendCommandNOTICE("\x01CLIENTINFO VERSION TIME LOCATION PING CLIENTINFO USERINFO SOURCE\x01", data.nick);
} else if (new RegExp('\x01USERINFO\x01', 'g').exec(data.message) !== null) {
botF.ircSendCommandNOTICE("\x01USERINFO Squeebot\x01", data.nick);
} else if (new RegExp('\x01SOURCE\x01', 'g').exec(data.message) !== null) {
botF.ircSendCommandNOTICE("\x01SOURCE http://gist.github.com/LunaSquee/95e849c9d44a4874501f\x01", data.nick);
} else if (new RegExp('\x01TIME\x01', 'g').exec(data.message) !== null) {
botF.ircSendCommandNOTICE("\x01TIME "+new Date()+"\x01", data.nick);
} else if (new RegExp('\x01LOCATION\x01', 'g').exec(data.message) !== null) {
botF.ircSendCommandNOTICE("\x01LOCATION Equestria(imaginative) Diamond's room(reality)\x01", data.nick);
} else if ((timestamp = new RegExp('\x01PING ([^\x01]*)\x01', 'g').exec(data.message)) !== null) {
botF.ircSendCommandNOTICE("\x01PING "+timestamp[1]+"\x01", data.nick);
}
},
};
// Fetch a google calendar
function fetchCalendar(calendar, customDescription, timeFrame) {
if (settings["googleapikey"] == null) return;
if (calendar == null)
return mylog("Calendar not found.");
var now = Date.now();
gCalendar.events.list({calendarId: calendar,
auth: settings.googleapikey,
timeMin: new Date(now-10*60*1000).toISOString(),
timeMax: new Date(now+timeFrame).toISOString(),
singleEvents: true}, function(err, def) {
if(err) {
mylog("Calendar events failed to fetch:")
console.log(err);
return;
}
now = Date.now();
for (var i = 0; i < def.items.length; i++) {
var item = prettifyEvent(def.items[i]);
if(customDescription != null) {
item.description = customDescription;
}
if(now < item.end.getTime()) {
sEvents.push({eventName: item.title, eventStartTime: new Date(item.start)/1000, eventEndTime: new Date(item.end)/1000, description: item.description || ""});
}
}
sEvents.sort(sortStartTime);
});
}
// Sync calendars
function synccalendar(no) {
if(calSyncInterval == false && no == null)
return
sEvents = [];
fetchCalendar(settings.calendars.squeebot, null, 30*24*60*60*1000);
fetchCalendar(settings.calendars.parasprite, "An event on Parasprite Radio.", 14*24*60*60*1000);
if(no == null)
setTimeout(synccalendar, 300000);
}
// Kill all relay connections and close the server
function destroyIrcRelay() {
if(relayserver == null) return;
for(var con in relayConnections) {
var t = relayConnections[con];
t.end();
t.destroy();
}
relayserver.close();
}
/*
===================
NICKNAME UTILITIES!
===================
*/
// Check if nick is op on channel
function isOpOnChannel(user, channel) {
var isUserChanOp = false;
var ircChannelUsers = botObj.publicData.ircChannelUsers;
if (ircChannelUsers[channel] && ircChannelUsers[channel][user] && ircChannelUsers[channel][user].mode) {
if (ircChannelUsers[channel][user].mode.replace(/^(o|q|h|a)$/, "isOp").indexOf("isOp") != -1 ) { isUserChanOp = true; }
}
return isUserChanOp;
}
// Check if nick is a bot operator
function isGlobalOp(hostsa) {
hostsa = hostsa.substring(1).split("!");
if("ophosts" in squees && "botops" in squees) {
if(hostsa[1] in squees.ophosts && hostsa[0].toLowerCase() in squees.botops) {
return true;
}
}
return false;
}
// Return the hostname's permission level (nickname!username@hostname)
function permlevel(hostsa) {
hostsa = (hostsa.indexOf(":") === 0 ? hostsa.substring(1) : hostsa).split("!");
if("ophosts" in squees && "botops" in squees) {
if(hostsa[1] in squees.ophosts && hostsa[0].toLowerCase() in squees.botops) {
return squees.botops[hostsa[0].toLowerCase()];
}
}
return 0;
}
// String representation of a permission level
function permstring(level, color) {
var str = ""
switch(level) {
case 1:
str = "Helper";
break;
case 2:
str = "Moderator";
break;
case 3:
str = "Admin";
break;
default:
str = "User";
break;
}
return str;
}
// Converts prefix to mode
function prefixToMode(prefix) {
var mode = "";
switch (prefix) {
case "~":
mode = "q";
break;
case "&":
mode = "a";
break;
case "@":
mode = "o";
break;
case "%":
mode = "h";
break;
case "+":
mode = "v";
break;
default:
mode = "";
break;
}
return mode;
}
// Converts mode to prefix
function modeToPrefix(mode) {
var prefix = "";
switch (mode) {
case "q":
prefix = "~";
break;
case "a":
prefix = "&";
break;
case "o":
prefix = "@";
break;
case "h":
prefix = "%";
break;
case "v":
prefix = "+";
break;
default:
prefix = "";
break;
}
return prefix;
}
// Converts mode to text
function modeToText(mode) {
var prefix = "";
switch (mode) {
case "q":
prefix = "Owner";
break;
case "a":
prefix = "Admin";
break;
case "o":
prefix = "Op";
break;
case "h":
prefix = "Halfop";
break;
case "v":
prefix = "Voice";
break;
default:
prefix = "Normal";
break;
}
return prefix;
}
/*
End of nick utils.
Misc. Utilities
*/
// Generate random in betweem two ints
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Generate random string of characters
function uid(len, full) {
var buf = []
, chars = (full == null ? 'abcdefghijklmnopqrstuvwxyz' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
, charlen = chars.length;
for (var i = 0; i < len; ++i) {
buf.push(chars[getRandomInt(0, charlen - 1)]);
}
return buf.join('');
}
// Reload a plugin
function pluginReload(plugin) {
botF.botPluginDisable(plugin);
botF.botPluginLoad(plugin, botInstanceSettings.pluginDir+'/'+plugin+'.js');
}
// Sort events by start time
function sortStartTime(a, b) {
return a.eventStartTime - b.eventEndTime
}
// Prettify an event object and keep out the unnecessary data
function prettifyEvent(item) {
var ev = {
id: item.id,
htmlLink: item.htmlLink,
created: new Date(item.created),
updated: new Date(item.updated),
title: item.summary,
location: item.location,
description: item.description,
start: new Date(item.start.dateTime || item.start.date),
end: new Date(item.end.dateTime || item.end.date),
sequence: item.sequence,
id: item.id
}
ev.length = (ev.end.getTime()-ev.start.getTime())/1000
return ev;
}
// Color load averages from uptime shell command
function loadstat(loads) {
var result = [];
for(var i = 0; i<loads.length;i++) {
var load = loads[i];
if(parseFloat(load) < 0.75)
result.push("\u000312 "+load+"\u000f");
else if(parseFloat(load) < 1)
result.push("\u00037 "+load+"\u000f");
else if(parseFloat(load) > 1)
result.push("\u00035 "+load+"\u000f");
}
return result.join(',');
}
function parseForMinecraft(message) {
return message.replace(/\x0310/g, '§3').replace(/\x0311/g, '§b').replace(/\x0312/g, '§9').replace(/\x0313/g, '§d').replace(/\x0314/g, '§8').replace(/\x0315/g, '§7')
.replace(/\x030/g, '§f').replace(/\x031/g, '§0').replace(/\x032/g, '§1').replace(/\x033/g, '§2').replace(/\x034/g, '§c').replace(/\x035/g, '§4').replace(/\x036/g, '§5')
.replace(/\x037/g, '§6').replace(/\x031/g, '§e').replace(/\x039/g, '§a').replace(/\x02/g, '§l').replace(/\x0f/g, '§r').replace(/\x1F/g, '§n');
}
// Strip IRC color codes from string
function stripColors(str) {
return str.replace(/(\x03\d{0,2}(,\d{0,2})?)/g, '');
};
// Strip IRC style codes from string
function stripStyle(str) {
return str.replace(/[\x0F\x02\x16\x1F]/g, '');
};
// Strip IRC formatting from string
function stripColorsAndStyle(str) {
return stripColors(stripStyle(str));
};
// Seconds into HH:MM:SS
function toHHMMSS(numbr) {
var sec_num = parseInt(numbr, 10); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) {hours = "0"+hours;}
if (minutes < 10) {minutes = "0"+minutes;}
if (seconds < 10) {seconds = "0"+seconds;}
var time = '';
if(parseInt(hours) > 0)
time = hours+':'+minutes+':'+seconds;
else
time = minutes+':'+seconds;
return time;
}
// HH:MM:SS from timestamp
function timestamp(unixTimestamp) {
var dt = new Date(unixTimestamp * 1000);
var hours = dt.getHours();
var minutes = dt.getMinutes();
var seconds = dt.getSeconds();
// the above dt.get...() functions return a single digit
// so I prepend the zero here when needed
if (hours < 10)
hours = '0' + hours;
if (minutes < 10)
minutes = '0' + minutes;
if (seconds < 10)
seconds = '0' + seconds;
return hours + ":" + minutes + ":" + seconds;
}
// Add commas to numbers (e.g. 1,234,567; 2,456)
function addCommas(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
// http://stackoverflow.com/a/22149575
function ytDuration(duration) {
var a = duration.match(/\d+/g);
if (duration.indexOf('M') >= 0 && duration.indexOf('H') == -1 && duration.indexOf('S') == -1) {
a = [0, a[0], 0];
}
if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1) {
a = [a[0], 0, a[1]];
}
if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1 && duration.indexOf('S') == -1) {
a = [a[0], 0, 0];
}
duration = 0;
if (a.length == 3) {
duration = duration + parseInt(a[0]) * 3600;
duration = duration + parseInt(a[1]) * 60;
duration = duration + parseInt(a[2]);
}
if (a.length == 2) {
duration = duration + parseInt(a[0]) * 60;
duration = duration + parseInt(a[1]);
}
if (a.length == 1) {
duration = duration + parseInt(a[0]);
}
return toHHMMSS(duration.toString());
}
// Add a zero in front of single-digit numbers
function zf(v) {
if (v > 9) {
return ""+v;
} else {
return "0"+v;
}
}
// Convert seconds into years days hours minutes seconds(.milliseconds)
function readableTime(timems, ignoreMs) {
var time = timems|0;
var ms = ignoreMs?'':"."+zf((timems*100)%100|0);
if (time < 60) return zf(time)+ms+"s";
else if (time < 3600) return zf(time / 60|0)+"m "+zf(time % 60)+ms+"s";
else if (time < 86400) return zf(time / 3600|0)+"h "+zf((time % 3600)/60|0)+"m "+zf((time % 3600)%60)+ms+"s";
else if (time < 31536000) return (time / 86400|0)+"d "+zf((time % 86400)/3600|0)+"h "+zf((time % 3600)/60|0)+"m "+zf((time % 3600)%60)+"s";
else return (time / 31536000|0)+"y "+zf((time % 31536000) / 86400|0)+"d "+zf((time % 86400)/3600|0)+"h "+zf((time % 3600)/60|0)+"m "+zf((time % 3600)%60)+"s";
}
// Convert a time string to seconds (e.g. "1h" -> 3600) written by nnnn20430
function parseTimeToSeconds(string) {
var seconds = 0;
var match;
var secMinute = 1 * 60;
var secHour = secMinute * 60;
var secDay = secHour * 24;
var secWeek = secDay * 7;
var secYear = secDay * 365;
if((match = string.match('([0-9]+)y')) !== null) {
seconds += +match[1]*secYear;
}
if((match = string.match('([0-9]+)w')) !== null) {
seconds += +match[1]*secWeek;
}
if((match = string.match('([0-9]+)d')) !== null) {
seconds += +match[1]*secDay;
}
if((match = string.match('([0-9]+)h')) !== null) {
seconds += +match[1]*secHour;
}
if((match = string.match('([0-9]+)m')) !== null) {
seconds += +match[1]*secMinute;
}
if((match = string.match('([0-9]+)s')) !== null) {
seconds += +match[1];
}
return seconds;
};
/*
End of Misc. Utils.
*/
// List all commands that have a description set
function listCommands(nick, target, all) {
var comms = [];
var listofem = [];
comms.push("All "+NICK+" commands start with a "+PREFIX+" prefix.");
comms.push("Type "+PREFIX+"help <command> for more information on that command.");
for(var command in commands) {
var obj = commands[command];
if(all === 1) {
if("description" in obj) {
listofem.push("\u0002\u0003"+("permlevel" in obj ? "7" : "3")+command+"\u000f");
}
} else {
if("description" in obj && !("permlevel" in obj)) {
listofem.push("\u0002\u00033"+command+"\u000f");
}
}
}
comms.push(listofem.join(", "));
sendWithDelay(comms, target, 1000);
}
// Send an array of messages with a delay
function sendWithDelay(messages, target, time) {
function sendMessageDelayed(c, arri, timeout) {
sendPM(target, arri[c]);
c++;
if(arri[c] != null)
setTimeout(function() {sendMessageDelayed(c, arri, timeout)}, timeout);
}
sendMessageDelayed(0, messages, time || 1000);
}
// Grab JSON from an url
function fetchJSON(link, callback, extendedHeaders) {
var parsed = url.parse(link);
var opts = {
host: parsed.host,
path: parsed.path,
"headers":{
"User-Agent": "Squeebot/nBot"
}
}
if(extendedHeaders != null) {
for(var ext in extendedHeaders) {
var header = extendedHeaders[ext];
opts.headers[ext] = header;
}
}
var httpModule = parsed.protocol === 'https:' ? require('https') : require('http');
httpModule.get(opts, function (res) {
if (res.statusCode === 302 || res.statusCode === 301) {
fetchJSON.call(this, res.headers.location, callback, extendedHeaders);
return;
}
var data = '';
res.on('data', function (chunk) {
data += chunk;
})
res.on('end', function () {
try {
var obj = JSON.parse(data);
}
catch (err) {
callback(err, data);
return;
}
callback(null, obj);
});
}).on('error', function (e) {
callback(e.message);
});
}
var getJSON = fetchJSON; // Just for the sake of making sense
// POST data to an url, expects a JSON response ("http://host:port/data", {heads: null}, function(error, data, res) { }, {\"x-toast\": true})
function postJSON(link, postdata, callback, headers) {
var parsed = url.parse(link);
var post_data = qs.stringify(postdata);
var post_options = {
host: parsed.host,
port: parsed.port,
path: parsed.path,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(post_data),
'User-Agent': 'nBot/Squeebot'
}
}
if(headers != null) {
for(var ext in headers) {
var header = headers[ext];
post_options.headers[ext] = header;
}
}
var httpModule = parsed.protocol === 'https:' ? require('https') : require('http');
var post_req = httpModule.request(post_options, function(res) {
res.setEncoding('utf8');
var data = "";
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function() {
try{
var obj = JSON.parse(data);
} catch (err) {
callback("no-json", data, res);
return;
}
callback(null, obj, res);
});
}).on("error", function(e) {
callback(e.message, null, e);
});
post_req.write(post_data);
post_req.end();
}
// Get current Parasprite Radio song
function getCurrentSong(callback) {
fetchJSON("http://radio.djazz.se/api/status", function(error, content) {
if(error === null) {
if(content.meta != null && content.info != null && content.info.online === true) {
var xt = content.meta;
var theTitle = new Buffer(xt.title, "utf8").toString("utf8");
var artist = xt.artist;
if(artist!=null) {
theTitle=theTitle+" by "+artist;
}
callback(theTitle, content.info.listeners, true);
return;
} else {
callback("\u00037Parasprite Radio\u000f is \u00034offline!", "", false);
}
} else {
callback("\u00037Parasprite Radio\u000f is \u00034offline!", "", false);
}
});
}
// Gameserver info (This function makes me puke)
function getGameInfo(game, host, callback, additional) {
gamedig.query(
{
type: game,
host: host
},
function(state) {
if(state.error) callback("\u00034Server is offline!", null);
else {
switch(game) {
case "tf2":
if(additional) {
callback(null, "\u000310[Team Fortress 2]\u000f " + (typeof(additional) === "object" ? state[additional[0]][additional[1]] : state[additional]));
} else {
callback(null, "\u000310[Team Fortress 2] \u00033IP: \u000312"+host+" \u00033MOTD: \u000312\""+state.name+"\" \u00033Players: \u000312"+state.raw.numplayers+"/"+state.maxplayers);
}
break;
case "minecraft":
if(additional!=null && additional === true) {
if(state.players.length > 0) {
var players = [];
state.players.forEach(function(t) {
players.push(t.name);
});
callback(null, "\u000310[Minecraft] \u00033Players:\u000f "+players.join(", "));
} else {
callback(null, "\u000310[Minecraft] \u00034No players");
}
} else {
callback(null, "\u000310[Minecraft] \u00033IP: \u000312"+host+" \u00033MOTD: \u000312\""+state.name+"\" \u00033Players: \u000312"+state.raw.numplayers+"/"+state.raw.maxplayers);
}
break;
case "mumbleping":
/*if(additional!=null && additional === true) {
if(state.players.length > 0) {
var players = [];
// Sort, show most active first
state.players.sort(function (u1, u2) {
return u1.idlesecs - u2.idlesecs;
});
state.players.forEach(function(t) {
var isMuted = t.mute || t.selfMute;
var isDeaf = t.deaf || t.selfDeaf;
var o = t.name;
if (isMuted && isDeaf) {
o = "\u00035"+o+"\u000f";
} else if (isMuted) {
o = "\u00037"+o+"\u000f";
} else if (isDeaf) {
o = "\u000312"+o+"\u000f";
} else {
o = "\u00039"+o+"\u000f";
}
players.push(o);
});
callback(null, "\u000310[Mumble] Users:\u0003 "+players.join(" "));
} else {
callback(null, "\u000310[Mumble] \u00034No users ");
}
} else {*/
callback(null, "\u000310[Mumble] \u00033Address: \u000312"+host+" \u00033Users online: \u000312"+state.players.length);
//}
break;
};
}
}
);
}
// Dailymotion information from id
function dailymotion(id, target) {
fetchJSON("https://api.dailymotion.com/video/"+id+"?fields=id,title,owner,owner.screenname,duration,views_total", function(error, data) {
if(error === null) {
sendPM(target, "\u0002\u00033Dailymotion\u000f \u000312\""+data.title+"\" \u00033Views: \u000312"+addCommas(data.views_total.toString())+" \u00033Duration: \u000312"+toHHMMSS(data.duration.toString())+" \u00033By \u000312\""+data["owner.screenname"]+"\"");
}
});
}
// Youtube information from id
function getYoutubeFromVideo(id, target, isQueue) {
if(settings["googleapikey"] == null) return;
var g_api_base = "https://www.googleapis.com/youtube/v3/videos?id="+id+"&key="+settings.googleapikey+"&part=snippet,contentDetails,statistics,status&fields=items(id,snippet,statistics,contentDetails)";
fetchJSON(g_api_base, function(error, content) {
if(error!=null) return;
if("items" in content) {
if(content.items.length <= 0) {
sendPM(target, "Video not found.");
return;
}
var tw = content.items[0];
sendPM(target, (isQueue ? "\u000310\u0002[QUEUED] \u0002\u0003" : "")+"\u0002You\u00035Tube\u000f \u000312\""+tw.snippet.title+"\" \u00033Views: \u000312"+addCommas(tw.statistics.viewCount.toString())+" \u00033Duration: \u000312"+ytDuration(tw.contentDetails.duration.toString())+" \u00039▲ "+addCommas(tw.statistics.likeCount.toString())+" \u00034▼ "+addCommas(tw.statistics.dislikeCount.toString())+" \u00033By \u000312\""+tw.snippet.channelTitle+"\"");
}
});
}
function DBTagSort(a, b) {
var indexasfirst = ["suggestive", "questionable", "explicit", "safe", "grimdark", "semi-grimdark", "grotesque"]
var tag = a;
if(indexasfirst.indexOf(tag) !== -1)
return -1;
tag = b;
if(indexasfirst.indexOf(tag) !== -1)
return 1;
}
function DBTagConstruct(taglist) {
taglist = taglist.sort(DBTagSort);
var res = [];
var color = "\u00039";
var t = 0;
for(var tagindex in taglist) {
var tag = taglist[tagindex];
if(tag.indexOf("oc") === 0) {
color = "\u000313";
} else if(tag == "suggestive" || tag == "questionable" || tag == "explicit" || tag == "safe" || tag == "grimdark" || tag == "semi-grimdark" || tag == "grotesque") {
color = "\u000312";
} else if(tag == "edit" || tag.indexOf("artist:") === 0) {
color = "\u00032";
} else if(tag.indexOf("spoiler:") === 0) {
color = "\u00037";
} else {
color = "\u00039";
}
res.push(color+" "+tag+"\u0003");
}
if(res.length > 12) {
t = res.length - 12;
res = res.slice(0, 12);
res.push(" \u0002"+t+" more..");
}
return "\u00036[\u000f"+res.join(",")+" \u00036]\u000f";
}
// Kick on NSFW derpibooru links
function derpibooru_handle(id, target, nick) {
var derpibooRoot = "https://derpibooru.org/";
fetchJSON(derpibooRoot+id+".json", function(error, content) {
if(error == null) {
if("tags" in content) {
var taglist = content.tags.split(", ");
sendPM(target, "\u000312Derpibooru\u00039 >>"+id+" \u00037★ "+addCommas(content.faves.toString())+" \u00039▲ "+addCommas(content.upvotes.toString())+" \u00034▼ "+addCommas(content.downvotes.toString())+" "+DBTagConstruct(taglist));
if(taglist.indexOf("explicit") !== -1) {
//sendPM(target, "\u0002\u00034NSFW CONTENT WARNING!\u000f\u00037 Image "+id+" is tagged \u00034\"explicit\"\u00037!");
if(target.indexOf("#") === 0) {
botF.ircWriteData("KICK "+target+" "+nick+" :Do not post NSFW images in chat.");
}
}
}
}
});
}
// Fetch soundscloud data
function getSoundcloudFromUrl(url, target, isQueue) {
if(settings['soundcloudkey'] == null) return;
var apibase = "https://api.soundcloud.com";
fetchJSON(apibase+"/resolve?url="+url+"&client_id="+settings.soundcloudkey, function(error, response) {
if(error) {
mylog("SoundCloud fetch Failed");
console.log(error);
} else {
if(response.kind === "track") {
sendPM(target, (isQueue ? "\u000310\u0002[QUEUED] \u0002\u0003" : "")+"\u00037SoundCloud\u000f \u000312\""+response.title+"\" \u00033▶ \u000312"+addCommas(response.playback_count.toString())+" \u00037♥ "+addCommas(response.favoritings_count.toString())+" \u00033Duration: \u000312"+toHHMMSS(Math.floor(response.duration/1000).toString())+" \u00033By \u000312\""+response.user.username+"\"");
} else if(response.kind === "playlist") {
sendPM(target, "\u00037SoundCloud\u000f \u000312Playlist \""+response.title+"\" \u00033Tracks: \u000312"+addCommas(response.track_count.toString())+" \u00033Duration: \u000312"+toHHMMSS(Math.floor(response.duration/1000).toString())+" \u00033By \u000312\""+response.user.username+"\"");
}
}
});
}
// Separate array objects "name" by commas
function spotifyArtists(array) {
var d = [];
array.forEach(function(e) {
d.push(e.name);
});
return d.join(", ");
}
// Fetch spotify song data
function getSpotifySongFromID(id, target) {
if(id == null) return;
fetchJSON("https://api.spotify.com/v1/tracks/"+id, function(error, response) {
if(error) {
mylog("Spotify fetch Failed");
console.log(error);
} else {
sendPM(target, "\u00033Spotify\u000f \u000312\""+response.name+"\" \u00033Artists: \u000312"+spotifyArtists(response.artists)+" \u00033Duration: \u000312"+toHHMMSS(Math.floor(response.duration_ms/1000).toString())+(response.album != null ? " \u00033Album: \u000312\""+response.album.name+"\"" : ""));
}
});
}
// See if event is currently running (UTC timestamps)
// 0: no, 1: running, 2: over
function currentRunCheck(startstamp, endstamp) {
var date = new Date();
var currentStamp = Math.floor(new Date(date.toUTCString()).getTime() / 1000);
if(endstamp === 0) {
if(currentStamp >= startstamp) {
return 1;
} else {
return 0;
}
} else {
if(currentStamp >= startstamp && currentStamp < endstamp) {
return 1;
} else if (currentStamp > endstamp) {
return 2;
} else {
return 0;
}
}
}
// eventData: {eventName: "name", eventStartTime: unix seconds (UTC), eventEndTime: unix seconds (UTC) or 0}
function tellEvent(eventData, target, countdown) {
if(eventData != null) {
var isRunning = currentRunCheck(eventData.eventStartTime, eventData.eventEndTime || 0);
var timeLeft = 0;
var date = new Date();
var timeNow = Math.floor(new Date(date.toUTCString()).getTime() / 1000); // short dance to get current UTC timestamp
if(isRunning === 0) {
var timeLeftStamp = countdown ? "in "+readableTime(eventData.eventStartTime - timeNow, true) : new Date(eventData.eventStartTime*1000);
sendPM(target, "\u0002Event: \u000f\u00037"+eventData.eventName+"\u000f\u0002 starts "+timeLeftStamp+". \u000f"+eventData.description || "");
} else if(isRunning === 1) {
var timeLeftStamp = countdown ? "in "+readableTime(eventData.eventEndTime - timeNow, true) : new Date(eventData.eventEndTime*1000);
sendPM(target, "\u0002Event: \u000f\u00033"+eventData.eventName+"\u000f\u0002 ends "+timeLeftStamp+". \u000f"+eventData.description || "");
} else {
sendPM(target, "\u0002Event: \u000f\u00034"+eventData.eventName+"\u000f\u0002 is over :(");
}
}
}
// Finds urls in string
function findUrls(text) {
var source = (text || '').toString();
var urlArray = [];
var url;
var matchArray;
var regexToken = /(((ftp|https?):\/\/)[\-\w@:%_\+.~#?,&\/\/=]+)|((mailto:)?[_.\w-]+@([\w][\w\-]+\.)+[a-zA-Z]{2,3})/g;
while((matchArray = regexToken.exec(source))!== null) {
var token = matchArray[0];
if (token.indexOf("youtube.com/watch") !== -1 || token.indexOf("youtu.be/") !== -1 || token.indexOf("dailymotion.com/video/") !== -1 ||
token.indexOf("soundcloud.com/") !== -1 || token.indexOf("spotify.com/track/") !== -1 || token.indexOf("derpibooru.org") !== -1 ||
token.indexOf("derpiboo.ru") !== -1 || token.indexOf("derpicdn.net") !== -1 || token.indexOf("twitter.com/") !== -1 || token.indexOf("lunasqu.ee/") !== -1) {
urlArray.push(token);
}
}
return urlArray;
}
// Livestream viewers
function livestreamViewerCount(callback, stream, streamer) {
if(stream == 0) {
fetchJSON("http://radio.djazz.se/api/status", function(error, content) {
if(error===null) {
var view = content.livestream;
if(view.online==true) {
callback(null, "\u00033Viewers: \u000311"+view.viewers);
} else {
callback("offline", "\u00034The livestream is offline");
}
} else {
callback(error, "\u00034The livestream is offline");
}
});
}
}
// Handles messages
function handleMessage(nick, chan, message, pretty, simplified, isMentioned, isPM) {
var target = isPM ? nick : chan;
var hirex = new RegExp("(hi|hey|hai|hello|hiya),? "+NICK, 'gim');
var hugrex = new RegExp("\x01ACTION hugs "+NICK, 'gim');
var spotrex = "";
if(simplified[0].indexOf(PREFIX) === 0 && simplified[0].toLowerCase().substring(PREFIX.length) in commands) {
var permission = permlevel(pretty.rawdata[0].split(' ')[0]);
var command = commands[simplified[0].toLowerCase().substring(PREFIX.length)];
if("permlevel" in command) {
if(permission < command.permlevel) {
sendPM(target, nick+": You do not have permission to execute this command!");
return;
}
}
if("action" in command)
command.action(simplified, nick, chan, message, pretty, target, isMentioned, isPM);
} else if(isPM && simplified[0].toLowerCase() in commands) {
var permission = permlevel(pretty.rawdata[0].split(' ')[0]);
var command = commands[simplified[0].toLowerCase()];
if("permlevel" in command) {
if(permission < command.permlevel) {
sendPM(target, nick+": You do not have permission to execute this command!");
return;
}
}
if("action" in command)
command.action(simplified, nick, chan, message, pretty, target, isMentioned, isPM);
} else if(hirex.exec(message) != null) {
sendPMSD(target, "Hey "+nick+"!!");
} else if(hugrex.exec(message) != null) {
sendPMSD(target, "\x01ACTION hugs "+nick+"\x01");
} else if((spotrex = message.match(/spotify:track:([\S]+)/i)) != null) {
getSpotifySongFromID(spotrex[1], target);
} else if(findUrls(message).length > 0) {
var link = findUrls(message)[0];
if(link.indexOf("soundcloud.com") !== -1) {
getSoundcloudFromUrl(link, target);
} else if(link.indexOf("youtu.be/") !== -1) {
var det = link.match(/youtu.be\/([^\?\&\#]+)/i)[1];
if(det) {
getYoutubeFromVideo(det, target);
}
} else if(link.indexOf("youtube.com/") !== -1) {
var det = link.match("[\\?&]v=([^&#]*)");
if(det) {
getYoutubeFromVideo(det[1], target);
}
} else if(link.indexOf("dailymotion.com/video/") !== -1) {
var det = link.match("/video/([^&#]*)")[1];
if(det) {
dailymotion(det, target);
}
} else if(link.indexOf("spotify.com/track/") !== -1) {
var det = link.match("/track/([^&#]*)")[1];
if(det) {
getSpotifySongFromID(det, target);
}
} else if(link.indexOf("derpiboo") !== -1) {
var det = link.match(/derpiboo\.?ru(\.org)?\/(images\/)?(\d+[^#?&])/i);
if(det && det[3] != null) {
var numbere = parseInt(det[3]);
if(numbere) {
derpibooru_handle(numbere, target, nick);
}
}
} else if(link.indexOf("derpicdn") !== -1) {
var det = link.match(/derpicdn\.net\/img\/?(view)?\/\d+\/\d+\/\d+\/(\d[^_\/?#]+)/i);
if(det && det[2] != null) {
var numbere = parseInt(det[2]);
if(numbere) {
derpibooru_handle(numbere, target, nick);
}
}
} else if(link.indexOf("twitter.com/") !== -1) {
var det = link.match(/twitter.com\/\w+\/status\/(\d+[^&#?\s\/])/i);
if(det) {
if("tweety" in botObj.pluginData) {
botObj.pluginData.tweety.plugin.sendTweetResponse(det[1], target, 0);
}
}
}
} else {
let mesgmatcher = message.toLowerCase().replace(/\:|\,|\'|\!|\?|\./g, ' ').trim().split(' ')
for(let i in responselist) {
let resline = responselist[i]
let match = true
for(let ti in resline.tags) {
let tag = resline.tags[ti]
if(mesgmatcher.indexOf(tag) == -1 && match == true) {
match = false
continue
}
}
let response = ''
if(match) {
if(resline.responses.length == 0 && resline.execCommand != null) {
let commandargs = [resline.execCommand.c]
for(let t in resline.execCommand.args)
commandargs.push(resline.execCommand.args[t].replace(/\@sender/g, nick))
commands[resline.execCommand.c].action(commandargs, nick, chan, message, pretty, target, isMentioned, isPM)
break
} else if(resline.responses.length != 0) {
let randnum = getRandomInt(0, resline.responses.length-1)
response = resline.responses[randnum].replace(/\@sender/g, nick)
} else {
continue
}
} else {
continue
}
if(response != '')
sendPMSD(target, response)
break
}
}
}
// Relays irc messages to connected clients
function ircRelayMessageHandle(c) {
emitter.once('newIrcMessage', function (from, to, message, type) {
if (c.writable) {
c.write(type+">"+from+':'+to+':'+parseForMinecraft(message)+'\r\n');
ircRelayMessageHandle(c);
}
});
}
// Creates a new relay server
function ircRelayServer() {
if (!settings.relay.enableRelay) return;
relayserver = net.createServer(function (c) { //'connection' listener
var pingWait = null, pingTimeout = null;
function ping() {
clearTimeout(pingWait);
pingWait = setTimeout(function () {
c.write('ping\r\n');
//info('RELAY: Send ping');
pingTimeout = setTimeout(function () {
c.destroy();
info('RELAY: Connection timed out');
if(c.remoteAddress in relayConnections)
delete relayConnections[c.remoteAddress];
}, 15*1000);
}, 15*1000);
}
function pong() {
//info('RELAY: Got pong');
clearTimeout(pingTimeout);
ping();
}
var addr = c.remoteAddress;
var firstData = true;
info('RELAY: Client %s is connecting...', c.remoteAddress);
c.setEncoding('utf8');
c.once('end', function() {
clearTimeout(timeout);
info('RELAY: Client '+addr+' disconnected');
if(addr in relayConnections)
delete relayConnections[addr];
});
c.once('error', function (err) {
clearTimeout(timeout);
info('RELAY: Client '+addr+' errored: '+err);
c.destroy();
if(addr in relayConnections)
delete relayConnections[addr];
});
c.once('close', function() {
clearTimeout(timeout);
clearTimeout(pingWait);
clearTimeout(pingTimeout);
info('RELAY: Client '+addr+' socket closed');
if(addr in relayConnections)
delete relayConnections[addr];
});
c.on('data', function (data) {
if (firstData) {
firstData = false;
data = data.trim();
clearTimeout(timeout);
if (data === settings.relay.relayPassword) {
info('RELAY: Client '+addr+' logged in');
c.write('Password accepted\r\n');
ircRelayMessageHandle(c);
relayConnections[addr] = c;
ping();
} else {
info('RELAY: Client '+addr+' supplied wrong password: %s', data);
c.end("Wrong password\r\n");
}
} else {
if (data.trim() === 'pong') {
pong();
} else if (data.trim().indexOf("msg:") === 0) {
var da = data.trim();
var dz = da.split(":");
if(dz[0] === "msg" && dz.length > 2) {
sendPM(dz[1], da.substring(dz[0].length + dz[1].length + 2));
} else {
info("Malformed message from %s: ", addr);
mylog(data.trim());
}
}
}
});
var timeout = setTimeout(function () {
c.end("You were too slow :I\r\n");
info('RELAY: Client '+c.remoteAddress+' was too slow (timeout during handshake)');
}, 10*1000);
});
relayserver.listen(settings.relay.relayPort, function () {
info('RELAY: Relay server listening on port %d', settings.relay.relayPort);
});
}
// Save variables of squees
function squees_save(channel) {
var json_en = JSON.stringify(squees);
var savStart = Date.now() / 1000;
if(json_en) {
fs.writeFile(squeeDir+'savedvars.json', json_en, function (err) {
if (err) return mylog(err);
if(channel === false)
mylog('Variables object saved. Took '+readableTime(savStart - Date.now() / 1000));
else
sendPM(channel, "Squees object saved in "+readableTime(savStart -Date.now() / 1000));
});
}
}
// Load variables of squees
function squees_load(channel) {
fs.readFile(squeeDir+'savedvars.json', 'utf8', function (err, data) {
if (err) return;
squees = JSON.parse(data);
if (channel === false)
mylog('Variables object loaded.');
else
sendPM(channel, "Squees object loaded!");
});
}
// Save response list
function response_list_save() {
let json_en = JSON.stringify(responselist, null, '\t');
if(json_en) {
fs.writeFile(squeeDir+'responselist/'+responses, json_en, function (err) {
if (err) return;
});
}
}
// Load response list
function response_list_load(reslist) {
let rtype = reslist || responses
let json_en = JSON.stringify(responselist, null, '\t');
if(json_en) {
fs.readFile(squeeDir+'responselist/'+rtype, 'utf8', function (err, data) {
if (err) return;
responselist = JSON.parse(data);
responses = rtype;
})
}
}
// Log to console
function mylog() {
botF.debugMsg.apply(botF, Array.prototype.slice.call(arguments));
}
// Log to console #2
function info() {
arguments[0] = "\x1b[1;35m --\x1b[0m "+arguments[0];
mylog(util.format.apply(null, arguments));
}
// Send a PM with a human-like delay
function sendPMSD(target) {
var message = util.format.apply(null, Array.prototype.slice.call(arguments, 1));
setTimeout((function() {sendPM(target, message)}), (Math.floor(Math.random() * 3) + 2 ) * 1000);
}
// Send a PRIVMSG
function sendPM(target) {
var message = util.format.apply(null, Array.prototype.slice.call(arguments, 1));
if(settings.stripColors === true)
message = stripColorsAndStyle(message);
if(target.indexOf("#") === 0 && emitter)
emitter.emit('newIrcMessage', NICK, target, message, "PRIVMSG");
botF.ircSendCommandPRIVMSG(message, target);
}
// Send a NOTICE
function sendNOTICE(target) {
var message = util.format.apply(null, Array.prototype.slice.call(arguments, 1));
if(settings.stripColors === true)
message = stripColorsAndStyle(message);
botF.ircSendCommandNOTICE(message, target);
}
// Send a PRIVMSG \x01ACTION
function sendPMact(target) {
var message = '\u0001ACTION '+util.format.apply(null, Array.prototype.slice.call(arguments, 1))+'\u0001';
if(settings.stripColors === true)
message = stripColorsAndStyle(message);
botF.ircSendCommandPRIVMSG(message, target);
}
var SettingsConstructor = function (modified) {
var settings, attrname;
if (this!==SettingsConstructor) {
settings = {
prefix: '!',
googleapikey: null,
soundcloudkey: null,
paraspritekey: null,
stripColors: false,
allowShell: false,
nBotLoggerOverride: true,
nextepisode: {
date: [2016, 2, 26, 16, 0, 0],
countTimes: 26,
inSeason: 6
},
googleauth: {
client_id: null,
client_secret: null,
redirect: ""
},
relay: {
enableRelay: false,
relayPort: 1234,
relayPassword: ""
}
};
for (attrname in modified) {settings[attrname]=modified[attrname];}
return settings;
}
};
// Reserved functions
// Add listeners to simpleMsg plugin
function utilizeSimpleMsg() {
var simpleMsg = botObj.pluginData.simpleMsg.plugin;
/* Unused listeners
simpleMsg.msgListenerAdd(pluginId, 'TOPIC', function (data) {});
simpleMsg.msgListenerAdd(pluginId, 'RPL_NAMREPLY', function (data) {});
simpleMsg.msgListenerAdd(pluginId, 'NOTICE', function (data) {});
simpleMsg.msgListenerAdd(pluginId, '+MODE', function (data) {});
simpleMsg.msgListenerAdd(pluginId, '-MODE', function (data) {});
*/
simpleMsg.msgListenerAdd(pluginId, 'PRIVMSG', function (data) {
for(var bft in privmsgFunc)
privmsgFunc[bft](data);
var simplified = data.message.replace(/\:/g, ' ').replace(/\,/g, ' ').replace(/\./g, ' ').replace(/\?/g, ' ').trim().split(' ');
var isMentioned = simplified.indexOf(NICK) !== -1;
if(data.to.indexOf("#") === 0) {
handleMessage(data.nick, data.to, data.message, data, simplified, isMentioned, false);
if(new RegExp('\x01ACTION ', 'g').exec(data.message) !== null)
emitter.emit('newIrcMessage', data.nick, data.to, data.message.replace('\x01ACTION ', '').replace('\x01', ''), "ACTION");
else
emitter.emit('newIrcMessage', data.nick, data.to, data.message, "PRIVMSG");
} else {
handleMessage(data.nick, "", data.message, data, simplified, isMentioned, true);
}
});
simpleMsg.msgListenerAdd(pluginId, 'NICK', function (data) {
emitter.emit('newIrcMessage', data.nick, "", " is now known as "+data.newnick, "NICK");
botF.debugMsg("\x1b[1;36m["+timestamp(new Date().getTime()/1000)+"]\x1b[1;35m --\x1b[0m "+data.nick+" is now known as "+data.newnick);
});
simpleMsg.msgListenerAdd(pluginId, 'JOIN', function (data) {
emitter.emit('newIrcMessage', data.nick, data.channel, " has joined ", "JOIN");
botF.debugMsg("\x1b[1;36m["+timestamp(new Date().getTime()/1000)+"]\x1b[1;32m -->\x1b[0m "+data.nick+" has joined "+data.channel);
});
simpleMsg.msgListenerAdd(pluginId, 'KICK', function (data) {
emitter.emit('newIrcMessage', data.nick, data.channel, " was kicked by "+data.by+" ("+data.reason+")", "KICK");
botF.debugMsg("\x1b[1;36m["+timestamp(new Date().getTime()/1000)+"]\x1b[1;31m <--\x1b[0m "+data.nick+" was kicked by "+data.by+" from "+data.channel+" ("+data.reason+")");
});
simpleMsg.msgListenerAdd(pluginId, 'PART', function (data) {
emitter.emit('newIrcMessage', data.nick, data.channel, " has left ", "PART");
botF.debugMsg("\x1b[1;36m["+timestamp(new Date().getTime()/1000)+"]\x1b[1;31m <--\x1b[0m "+data.nick+" has left "+data.channel+" "+(data.reason == null ? data.reason : ""));
});
simpleMsg.msgListenerAdd(pluginId, 'QUIT', function (data) {
emitter.emit('newIrcMessage', data.nick, "", " has quit ("+data.reason+")", "QUIT");
botF.debugMsg("\x1b[1;36m["+timestamp(new Date().getTime()/1000)+"]\x1b[1;31m <--\x1b[0m "+data.nick+" has quit ("+data.reason+")");
});
simpleMsg.msgListenerAdd(pluginId, 'RAW', function (data) {
var nick = data[1][0];
var args = data[3];
if (data[2] === 'PRIVMSG' && args[1] && args[1].indexOf("\u0001ACTION ") === 0) {
var action = args[1].substr(8);
action = action.substring(0, action.length-1);
emitter.emit('newIrcMessage', nick, args[0], action, "ACTION");
}
});
//plugin is ready
exports.ready = true;
botF.emitBotEvent('botPluginReadyEvent', pluginId);
}
// Handle "botEvent" from bot (botEvent is used for irc related activity)
module.exports.botEvent = function (event) {
if(event.eventName == "botPluginDisableEvent") {
if(event.eventData == pluginId) {
destroyIrcRelay();
calSyncInterval = false;
} else {
for(var t in commands) {
var command = commands[t];
if("source" in command) {
if(command.source == event.eventData) {
mylog("\x1b[1;35m -!-\x1b[0m "+event.eventData+" Unloaded, command \""+t+"\" removed from "+pluginId+".");
delete commands[t];
}
}
}
}
} else if(event.eventName == "botReceivedNum005") {
botF.ircWriteData("MODE "+botInstanceSettings.botName+" +IB");
} else if(event.eventName == "botPluginReadyEvent") {
if (event.eventData == 'simpleMsg') {
utilizeSimpleMsg();
}
}
};
// Functions available from outside the plugin
module.exports.plugin = {
commandAdd: function(plugin, commandName, action, description, perlevel) {
if(commandName in commands)
return null;
commands[commandName] = {source: plugin, action: action};
if(perlevel != null)
commands[commandName].permlevel = perlevel;
if(description != null)
commands[commandName].description = description;
mylog("\x1b[1;35m -!-\x1b[0m "+plugin+" Added command \""+commandName+"\" to "+pluginId+".");
return commands[commandName];
},
isGlobalOp: isGlobalOp,
isOpOnChannel: isOpOnChannel,
permlevel: permlevel,
sendPM: sendPM,
sendPMSD: sendPMSD,
sendNOTICE: sendNOTICE,
fetchJSON: fetchJSON,
postJSON: postJSON,
readableTime: readableTime,
parseTimeToSeconds: parseTimeToSeconds
};
module.exports.ready = false;
//main function called when plugin is loaded
module.exports.main = function (passedData) {
// Update variables
botObj = passedData.botObj;
pluginId = passedData.id;
botF = botObj.publicData.botFunctions;
botInstanceSettings = botObj.publicData.settings;
settings = botInstanceSettings.pluginsSettings[pluginId];
ircChannelUsers = botObj.publicData.ircChannelUsers;
// If plugin settings are not defined, define them
if (settings === undefined) {
settings = new SettingsConstructor();
botInstanceSettings.pluginsSettings[pluginId] = settings;
botF.botSettingsSave();
}
NICK = botInstanceSettings.botName;
PREFIX = settings.prefix;
// Set up episode countdown
var tr = settings.nextepisode;
airDate = Date.UTC(tr.date[0], tr.date[1], tr.date[2], tr.date[3], tr.date[4], tr.date[5]);
// Load bot variables
squees_load(false);
// Create IRC Relay server
ircRelayServer();
calSyncInterval = true;
synccalendar();
// Plugin is ready
botF.emitBotEvent('botPluginReadyEvent', pluginId);
console.log(splash);
if(settings.nBotLoggerOverride) {
botObj.publicData.externalObjects.instanceBotEventHandleObj['PRIVMSG'] = function(connection, data) {
var nick = data[1][0],
to = data[4][0],
message = data[5]||data[4][1];
if(message.indexOf("\x01ACTION") === 0)
mylog('\x1b[1;36m['+timestamp(new Date().getTime()/1000)+']\x1b[1;34m['+to+']\x1b[0m \x1b[1;92m* \x1b[0m'+nick+'\x1b[0m '+message.substring(8));
else
mylog('\x1b[1;36m['+timestamp(new Date().getTime()/1000)+']\x1b[1;34m['+to+']\x1b[0m \x1b[1;92m<\x1b[0m'+nick+'\x1b[1;92m>\x1b[0m '+message);
}
botObj.publicData.externalObjects.instanceBotEventHandleObj['NOTICE'] = function(connection, data) {
var nick = data[1][0],
to = data[4][0],
message = data[5]||data[4][1];
mylog('\x1b[1;36m['+timestamp(new Date().getTime()/1000)+']\x1b[1;33m[NOTICE('+to+')]\x1b[0m \x1b[1;92m<\x1b[0m'+nick+'\x1b[1;32m>\x1b[0m '+message);
}
}
//check and utilize dependencies
if (botObj.pluginData.simpleMsg && botObj.pluginData.simpleMsg.ready)
utilizeSimpleMsg();
if (require.cache && require.cache[path.resolve(squeeDir+'responselist/'+responses)])
delete require.cache[path.resolve(squeeDir+'responselist/'+responses)];
};
/*jshint node: true*/
/*jshint evil: true*/
// This is a plugin for nBot (https://git.mindcraft.si.eu.org/?p=nBot.git)
// Before using: npm install node-twitter-api html-entities
// Have fun!
// ~ LunaSquee
"use strict";
//reserved nBot variables
var botObj;
var pluginId;
var botF;
var botV;
var settings;
var pluginSettings;
var ircChannelUsers;
//variables
var http = require('http');
var net = require('net');
var fs = require('fs');
var util = require('util');
var events = require('events');
var exec = require('child_process').exec;
var path = require('path');
var Entities = require('html-entities').AllHtmlEntities;
var entities = new Entities();
var twitterAPI = require('node-twitter-api');
var twitter;
var twitterData = {};
var twitClient;
var twitClientSec;
var twitStreams = {}
var pluginDisabled = false;
//settings constructor
var SettingsConstructor = function (modified) {
var settings, attrname;
if (this!==SettingsConstructor) {
settings = {
enableTweetTrack: false,
tweetTrack: {"userid": ["#channel"]},
authinfo: {
app: null,
app_secret: null,
client: null,
client_secret: null
}
};
for (attrname in modified) {settings[attrname]=modified[attrname];}
return settings;
}
};
function addCommas(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
var TwitterTracker = function(userid, channels) {
this.stream = null
this.live = false
this.closeRequested = false
this.reconnTries = 0
this.channels = channels
this.userid = userid
this.user_handle = null
this.recvData = function(error, data) {
this.reconnTries = 0
if(error) {
console.log('\x1b[1;35m --\x1b[0m Twitter stream of ['+userid+'] errored!')
console.log(error)
} else {
if("id_str" in data) {
if(this.user_handle == null)
this.user_handle = data.user.screen_name
if(data.in_reply_to_status_id === null && data.in_reply_to_user_id === null) {
if(data.text.indexOf("RT") === 0 && data.user.screen_name != this.user_handle)
return
channels.forEach(function(u) {
pluginObj.sendTweetResponse(data.id_str, u, 1)
})
}
}
}
}
this.recvFail = function(e) {
var self = this
console.log('\x1b[1;35m --\x1b[0m Twitter stream of ['+userid+'] ended!')
self.stream = null
self.live = false
if (self.closeRequested == false && pluginDisabled == false) {
if(self.reconnTries >= 3) {
console.log("\x1b[1;35m --\x1b[0m Max reconnect tries reached! Gave up.")
self.closeRequested = true
return
}
self.reconnTries += 1
console.log("\x1b[1;35m --\x1b[0m Twitter stream reconnecting in 5s")
setTimeout(function() {
self.init()
}, 5000)
}
}
this.init = function() {
console.log('\x1b[1;35m --\x1b[0m Twitter stream for '+userid+' starting, reporting on '+channels.length+' channels')
var self = this
this.live = true
twitter.users('show', {user_id: self.userid}, twitClient, twitClientSec, function(error, data, response) {
if(error) {
self.user_handle = null
} else {
self.user_handle = data.screen_name
}
})
this.stream = twitter.getStream('filter', {follow: userid}, twitClient, twitClientSec, function(e, d) {
self.recvData(e, d)
}, function(e) {
self.recvFail(e)
})
}
this.kill = function() {
this.closeRequested = true
if(this.stream) {
this.stream.end()
this.stream.destroy()
}
}
}
//main plugin object
var pluginObj = {
sendTweetResponse: function(tweetId, target, showTwo) {
twitter.statuses("show", {id:tweetId}, twitClient, twitClientSec, function(err, data){
if(err) {
botF.ircSendCommandPRIVMSG("Status fetch failed!", target);
} else {
botF.ircSendCommandPRIVMSG("\u000310Twitter\u0003 \u0002@"+data.user.screen_name+"\u0002: "+entities.decode(data.text).replace(/\n/g, ' ').trim(), target);
if(showTwo == 1)
botF.ircSendCommandPRIVMSG("\u0002Link to tweet:\u0002 https://twitter.com/"+data.user.screen_name+"/status/"+data.id_str, target);
}
});
},
twitStreamsInit: function() {
console.log('\x1b[1;35m --\x1b[0m Twitter streams initiating')
for(var trackUID in pluginSettings.tweetTrack) {
var channels = pluginSettings.tweetTrack[trackUID]
if(twitStreams[trackUID] != null)
twitStreams[trackUID].kill()
twitStreams[trackUID] = new TwitterTracker(trackUID, channels)
twitStreams[trackUID].init()
}
},
sendTweet: function(nick, msg, target) {
if('tokens-'+nick.toLowerCase() in twitterData) {
var dats = twitterData['tokens-'+nick.toLowerCase()];
var etr = {status:msg};
var tester;
if((tester = msg.match(/R:(\d+[^\w\s]*)/)) != null) {
etr.in_reply_to_status_id = tester[1];
etr.status = etr.status.replace(tester[0], '');
}
twitter.statuses("update", etr, dats.acc, dats.accsec, function(err, data){
if(err) {
botF.ircSendCommandPRIVMSG("Status update failed!", target);
} else {
botF.ircSendCommandPRIVMSG("Success! https://twitter.com/"+data.user.screen_name+"/status/"+data.id_str, target);
}
});
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
},
displayUser: function(screen_name, target) {
twitter.users('show', {screen_name: screen_name}, twitClient, twitClientSec, function(error, data, response) {
if(error) {
botF.ircSendCommandPRIVMSG("No such user!", target);
} else {
botF.ircSendCommandPRIVMSG("\u0002@"+data["screen_name"]+"\u0002 ("+data["name"]+"): "+entities.decode(data["description"]).replace(/\n/g, ' ').trim(), target);
botF.ircSendCommandPRIVMSG("\u00033Tweets: \u000312"+addCommas(data["statuses_count"])+" \u00033Following: \u000312"+addCommas(data["friends_count"])+" \u00033Followers: \u000312"+addCommas(data["followers_count"])+"\u000f", target);
}
});
},
initCommands: function() {
var manePlugin = botObj.pluginData.squeebot.plugin;
manePlugin.commandAdd(pluginId, "tw", function(simplified, nick, chan, message, pretty, target, mentioned, isPM) {
if(simplified[1] != null) {
var msg = message.split(' ').slice(1).join(' ');
pluginObj.sendTweet(nick, msg, target);
}
});
manePlugin.commandAdd(pluginId, "twitter", function(simplified, nick, chan, message, pretty, target, mentioned, isPM) {
var isOp = manePlugin.permlevel(pretty.rawdata[0].split(' ')[0]) >= 2;
if(simplified[1] === "status" && simplified[2] != null) {
if(!isOp) {
botF.ircSendCommandPRIVMSG(nick+": You do not have permission to execute this command!", target);
return;
}
var msg = message.split(' ').slice(2).join(' ');
twitter.statuses("update", {status:msg}, twitClient, twitClientSec, function(err, data){
if(err) {
botF.ircSendCommandPRIVMSG("Status update failed!", target);
} else {
botF.ircSendCommandPRIVMSG("Success! https://twitter.com/"+data.user.screen_name+"/status/"+data.id_str, target);
}
});
} else if(simplified[1] === "tweet" && simplified[2] != null) {
var msg = message.split(' ').slice(2).join(' ');
pluginObj.sendTweet(nick, msg, target);
} else if(simplified[1] === "display" && simplified[2] != null) {
pluginObj.sendTweetResponse(simplified[2], target, 1);
} else if(simplified[1] === "stream" && simplified[2] != null) {
if(!isOp) {
botF.ircSendCommandPRIVMSG(nick+": You do not have permission to execute this command!", target);
return;
}
if(simplified[2] == "end") {
if(simplified[3] == null) {
botF.ircSendCommandPRIVMSG("Ending all twitter streams..", target);
for(var s in twitStreams) {
var t = twitStreams[s]
t.kill()
delete twitStreams[s]
}
return
}
var stream = twitStreams[simplified[3]]
if(stream) {
botF.ircSendCommandPRIVMSG("Ending twitter stream..", target)
stream.kill()
delete twitStreams[simplified[3]]
}
} else if(simplified[2] == "start") {
if(simplified[3] == null) {
botF.ircSendCommandPRIVMSG("Starting all twitter streams..", target)
pluginObj.twitStreamsInit()
return
}
var str = simplified[3]
if(pluginSettings.tweetTrack[str]) {
twitStreams[str] = new TwitterTracker(str, pluginSettings.tweetTrack[str])
twitStreams[str].init()
}
} else if(simplified[2] == "list") {
var list = []
var i
for(i in twitStreams) {
list.push((twitStreams[i].live == false ? "\u00034 " : "\u00033 ")+i)
}
botF.ircSendCommandPRIVMSG("Streams currently running:"+list.join(','), target)
} else if(simplified[2] == "listall") {
var list = []
var i
for(i in pluginSettings.tweetTrack) {
if(twitStreams[i])
list.push((twitStreams[i].live == false ? "\u00034 " : "\u00033 ")+i)
else
list.push("\u00037 "+i)
}
botF.ircSendCommandPRIVMSG("Streams available:"+list.join(','), target)
} else if(simplified[2] == "status") {
if(simplified[3] == null){
botF.ircSendCommandPRIVMSG("Invalid target", target)
return
}
var str = twitStreams[simplified[3]]
if(str == null) {
botF.ircSendCommandPRIVMSG("Invalid target", target)
return
}
botF.ircSendCommandPRIVMSG("Twitter stream ["+str.userid+"]"+(str.user_handle != null ? " (\u0002@"+str.user_handle+"\u0002)" : "")+" is "+(str.live == false ? "\u00034Offline" : "\u00033Online")+"\u000f broadcasting to: "+str.channels.join(', '), target)
}
} else if(simplified[1] == "admin") {
if(!isOp) {
botF.ircSendCommandPRIVMSG(nick+": You do not have permission to execute this command!", target);
return;
}
if(nick.toLowerCase() in twitterData) {
botF.ircSendCommandPRIVMSG(nick+": Please log out before using admin account!", target);
return;
}
twitter.verifyCredentials(pluginSettings.authinfo.client, pluginSettings.authinfo.client_secret, function(error, data, response) {
if (error) {
botF.ircSendCommandPRIVMSG(nick+": Admin account tokens failed to verify!", target);
} else {
botF.ircSendCommandPRIVMSG("You are now logged in as @"+data["screen_name"]+" ("+data["name"]+")!", target);
twitterData[nick.toLowerCase()] = data;
twitterData['tokens-'+nick.toLowerCase()] = {acc:pluginSettings.authinfo.client, accsec:pluginSettings.authinfo.client_secret};
}
});
} else if(simplified[1] == "auth") {
if(simplified[2] != null) {
if(simplified[2] == "cancel") {
if('reqtokens-'+nick.toLowerCase() in twitterData) {
delete twitterData['reqtokens-'+nick.toLowerCase()];
botF.ircSendCommandPRIVMSG("Authentication cancelled.", target);
} else {
botF.ircSendCommandPRIVMSG("You're not currently in authentication process.", target);
}
return;
}
if('reqtokens-'+nick.toLowerCase() in twitterData) {
var tokens = twitterData['reqtokens-'+nick.toLowerCase()];
twitter.getAccessToken(tokens.req, tokens.reqsec, simplified[2], function(error, accessToken, accessTokenSecret, results) {
if (error) {
console.log(error);
botF.ircSendCommandPRIVMSG("An error occured while verifying.", target);
} else {
twitter.verifyCredentials(accessToken, accessTokenSecret, function(error, data, response) {
if (error) {
botF.ircSendCommandPRIVMSG("An error occured while trying to verify.", target);
} else {
botF.ircSendCommandPRIVMSG("You are now logged in as @"+data["screen_name"]+" ("+data["name"]+")!", target);
botF.ircSendCommandPRIVMSG("Use '!twitter tweet <tweet>' to tweet as yourself.", target);
twitterData['tokens-'+nick.toLowerCase()] = {acc:accessToken, accsec:accessTokenSecret};
twitterData[nick.toLowerCase()] = data;
delete twitterData['reqtokens-'+nick.toLowerCase()];
console.log(data["screen_name"]+" verified for "+nick);
}
});
}
});
}
} else {
twitter.getRequestToken(function(error, requestToken, requestTokenSecret, results){
if (error) {
console.log("Error getting OAuth request token : " + error);
botF.ircSendCommandPRIVMSG("An error occured while trying to get you a token.", target);
} else {
twitterData['reqtokens-'+nick.toLowerCase()] = {req: requestToken, reqsec: requestTokenSecret};
botF.ircSendCommandPRIVMSG("Please authenticate yourself: https://twitter.com/oauth/authenticate?oauth_token="+requestToken, target);
botF.ircSendCommandPRIVMSG("Enter your pin by doing !twitter auth yourPINHere", target);
}
});
}
} else if(simplified[1] == "authed") {
if(nick.toLowerCase() in twitterData) {
var datr = twitterData['tokens-'+nick.toLowerCase()];
twitter.verifyCredentials(datr.acc, datr.accsec, function(error, data, response) {
if (error) {
botF.ircSendCommandPRIVMSG("An error occured while trying to verify.", target);
botF.ircSendCommandPRIVMSG("If this error presists, run !twitter logout and reauthenticate.", target);
} else {
twitterData[nick.toLowerCase()] = data;
botF.ircSendCommandPRIVMSG("You're successfully authenticated as @"+twitterData[nick.toLowerCase()]["screen_name"]+" ("+twitterData[nick.toLowerCase()]["name"]+").", target);
}
});
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
} else if(simplified[1] == "user") {
if(simplified[2] == null) {
if(nick.toLowerCase() in twitterData) {
var datr = twitterData['tokens-'+nick.toLowerCase()];
twitter.verifyCredentials(datr.acc, datr.accsec, function(error, data, response) {
if (error) {
botF.ircSendCommandPRIVMSG("An error occured while trying to verify.", target);
botF.ircSendCommandPRIVMSG("If this error presists, run !twitter logout and reauthenticate.", target);
} else {
twitterData[nick.toLowerCase()] = data;
var xdata = twitterData[nick.toLowerCase()];
botF.ircSendCommandPRIVMSG("\u0002@"+xdata["screen_name"]+"\u0002 ("+xdata["name"]+"): "+entities.decode(xdata["description"]).replace(/\n/g, ' ').trim(), target);
botF.ircSendCommandPRIVMSG("\u00033Tweets: \u000312"+addCommas(xdata["statuses_count"])+" \u00033Following: \u000312"+addCommas(xdata["friends_count"])+" \u00033Followers: \u000312"+addCommas(xdata["followers_count"])+"\u000f", target);
}
});
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
} else {
pluginObj.displayUser(simplified[2].replace(/^\@/, ''), target);
}
} else if(simplified[1] == "retweet") {
if(simplified[2]!=null && simplified[2].match(/\d+/g) != null) {
var id = simplified[2].match(/\d+/g)[0];
if('tokens-'+nick.toLowerCase() in twitterData) {
var datr = twitterData['tokens-'+nick.toLowerCase()];
twitter.statuses("retweet", {id:id}, datr.acc, datr.accsec, function(err, data){
if(err) {
botF.ircSendCommandPRIVMSG("Failed to retweet tweet!", target);
} else {
botF.ircSendCommandPRIVMSG("Success! https://twitter.com/"+data.user.screen_name+"/status/"+data.id_str, target);
}
});
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
} else {
botF.ircSendCommandPRIVMSG("Invalid ID parameter!", target);
}
} else if(simplified[1] == "like") {
if(simplified[2]!=null && simplified[2].match(/\d+/g) != null) {
var id = simplified[2].match(/\d+/g)[0];
if('tokens-'+nick.toLowerCase() in twitterData) {
var datr = twitterData['tokens-'+nick.toLowerCase()];
twitter.favorites("create", {id:id}, datr.acc, datr.accsec, function(err, data){
if(err) {
botF.ircSendCommandPRIVMSG("Failed to like tweet!", target);
} else {
botF.ircSendCommandPRIVMSG("Success! https://twitter.com/"+data.user.screen_name+"/status/"+data.id_str, target);
}
});
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
} else {
botF.ircSendCommandPRIVMSG("Invalid ID parameter!", target);
}
} else if(simplified[1] == "follow") {
if(simplified[2]!=null) {
var iud = simplified[2].replace(/^\@/, '');
if('tokens-'+nick.toLowerCase() in twitterData) {
var datr = twitterData['tokens-'+nick.toLowerCase()];
twitter.friendships("create", {screen_name:iud}, datr.acc, datr.accsec, function(err, data){
if(err) {
botF.ircSendCommandPRIVMSG("Failed to follow user!", target);
console.log(err);
} else {
botF.ircSendCommandPRIVMSG("Successfully followed @"+data.screen_name+" ("+data.name+")!", target);
}
});
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
} else {
botF.ircSendCommandPRIVMSG("Missing screen name!", target);
}
} else if(simplified[1] == "unfollow") {
if(simplified[2]!=null) {
var iud = simplified[2].replace(/^\@/, '');
if('tokens-'+nick.toLowerCase() in twitterData) {
var datr = twitterData['tokens-'+nick.toLowerCase()];
twitter.friendships("destroy", {screen_name:iud}, datr.acc, datr.accsec, function(err, data){
if(err) {
botF.ircSendCommandPRIVMSG("Failed to unfollowed user!", target);
console.log(err);
} else {
botF.ircSendCommandPRIVMSG("Successfully unfollowed @"+data.screen_name+" ("+data.name+")!", target);
}
});
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
} else {
botF.ircSendCommandPRIVMSG("Missing screen name!", target);
}
} else if(simplified[1] == "logout") {
if(nick.toLowerCase() in twitterData) {
botF.ircSendCommandPRIVMSG("Logging you out..", target);
delete twitterData[nick.toLowerCase()];
delete twitterData['tokens-'+nick.toLowerCase()];
} else {
botF.ircSendCommandPRIVMSG("You're not authenticated!", target);
}
} else if(simplified[1] == "help") {
botF.ircSendCommandPRIVMSG("\u00033Commands: \u00037status, stream, admin, \u00033tweet, auth, authed, logout, display, like, retweet, follow, unfollow, user", target);
} else {
botF.ircSendCommandPRIVMSG("Invalid command!", target);
}
}, "<command> [<args>] - twitter integration");
//plugin is ready
exports.ready = true;
botF.emitBotEvent('botPluginReadyEvent', pluginId);
}
};
//exports
module.exports.plugin = pluginObj;
module.exports.ready = false;
//reserved functions
//reserved functions: handle "botEvent" from bot (botEvent is used for irc related activity)
module.exports.botEvent = function (event) {
//event is a object with properties "eventName" and "eventData"
switch (event.eventName) {
case 'botPluginDisableEvent': if (event.eventData == pluginId) {
pluginDisabled = true;
twitterData = {};
for(var str in twitStreams) {
var a = twitStreams[str]
a.kill()
delete twitStreams[str]
}
} break;
case 'botPluginReadyEvent': if (event.eventData == "squeebot") {
pluginObj.initCommands();
} break;
}
};
//reserved functions: main function called when plugin is loaded
module.exports.main = function (passedData) {
//update variables
botObj = passedData.botObj;
pluginId = passedData.id;
botF = botObj.publicData.botFunctions;
botV = botObj.publicData.botVariables;
settings = botObj.publicData.settings;
pluginSettings = settings.pluginsSettings[pluginId];
ircChannelUsers = botObj.publicData.ircChannelUsers;
//if plugin settings are not defined, define them
if (pluginSettings === undefined) {
pluginSettings = new SettingsConstructor();
settings.pluginsSettings[pluginId] = pluginSettings;
botF.botSettingsSave();
}
twitter = new twitterAPI({
consumerKey: pluginSettings.authinfo.app,
consumerSecret: pluginSettings.authinfo.app_secret,
callback: 'oob'
})
twitter.verifyCredentials(pluginSettings.authinfo.client, pluginSettings.authinfo.client_secret, function(error, data, response) {
if (error) {
console.log("\x1b[1;35m --\x1b[0m Twitter credentials are invalid!")
} else {
console.log("\x1b[1;35m --\x1b[0m Twitter credentials are valid! Twitter module ready")
twitClient = pluginSettings.authinfo.client
twitClientSec = pluginSettings.authinfo.client_secret
twitterData[settings.botName] = data
if(pluginSettings.enableTweetTrack) {
pluginObj.twitStreamsInit()
}
}
})
botF.emitBotEvent('botPluginReadyEvent', pluginId);
if (botObj.pluginData.squeebot && botObj.pluginData.squeebot.ready)
pluginObj.initCommands()
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment