Last active
December 11, 2015 18:49
-
-
Save boxmein/4644761 to your computer and use it in GitHub Desktop.
An IRC bot in Javascript which uses node.js. Incomplete.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@echo off | |
:a | |
cls | |
node irc-bot.js | |
echo Fix me! | |
pause | |
goto a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @language ECMASCRIPT5 | |
// When everything falls apart | |
// Just don't back down | |
// Just keep running | |
// Just look ahead | |
// You might be losing but you're not dead | |
var net = require("net"); | |
var http = require("http"); | |
var sys = require("sys"); | |
// IRC bot core | |
var irc = { | |
client: new net.Socket(), | |
PORT: 6667, | |
HOST: "irc.freenode.net", | |
textlogging: true, // If we log regular messages like <nick/#chan> Message | |
onConnected: function() { // we are connected! | |
output.log("irc.onConnected", "Connection successful"); | |
output.announce("Press q + enter to exit program."); | |
setTimeout(function() { | |
irc.raw("NICK " + config.nickname); | |
irc.raw("USER " + config.nickname + " 8 * :" + config.realname); | |
irc.raw("JOIN " + config.channels); | |
}, 7000); | |
}, | |
handleCommands: function(ircdata) { | |
output.inn(ircdata.sender + "(in " + ircdata.channel +") called for: " + ircdata.message); | |
// IRC message's first word without the # and the \r\n, if existent | |
// Also performs some changes | |
// 1. args ["#command", "arg0", "arg1" ...] -> args ["arg0", "arg1" ...] | |
// 2. .command = "command" | |
// 3. remove foreigns from commands | |
ircdata.command = ircdata.args.shift().substring(1).trim().replace(/[^A-Za-z]+?/gi, ""); | |
// commands.cmdlist is now chief in executive for testing whether a command exists | |
if (typeof(commands.cmdlist[ircdata.command]) != "undefined") { | |
commands[ircdata.command](ircdata); | |
} | |
else | |
output.err("irc.onData", ircdata.command+" is not a command."); | |
}, | |
onData: function(data) { | |
if (data.indexOf("PRIVMSG ") != -1) { // Channel message | |
var arr = data.split(' '); // Creates an array with | |
var result = arr.splice(0,3);// ["hostmask", "command", "channel", "mess age"] | |
result.push(arr.join(' ')); | |
var ircdata = { | |
hostmask : result[0].substring(1), // Sender's hostmask | |
channel : result[2], // Sender's channel | |
sender : result[0].substring(1, result[0].indexOf("!")), // Sender's handle | |
message : result[3].substring(1).trim(), // Full message | |
}; | |
if (irc.textlogging) | |
output.chanmsg(ircdata); | |
ircdata.args = ircdata.message.split(" "); | |
if (ircdata.message.indexOf(commands.prefix) == 0) { | |
irc.handleCommands(ircdata); | |
} | |
} | |
else if(data.indexOf("PING") != -1) { // Ping-pong handling | |
irc.raw("PONG", true); // Disables its logging. | |
} | |
}, | |
onConnectionClose: function() { // If the server disconnects us | |
output.err("irc.onConnectionClose", "Disconnected ()"); | |
process.exit(0); | |
}, | |
// // // // // // | |
// IRC wrappers | |
// // // // // // | |
ctcp: function(channel, data) { // to send CTCP data which must end in \x01 | |
irc.raw("PRIVMSG " + channel + " :\x01" + data + "\x01"); | |
}, | |
action: function(channel, action) { // to send a /me action | |
irc.ctcp(channel, "ACTION "+ action); | |
}, | |
privmsg: function(channel, message) { // To send a regular message to #chan or user | |
irc.raw("PRIVMSG " + channel + " :" + message); | |
}, | |
notice: function(channel, message) { | |
irc.raw("NOTICE " + channel + " :" + message); | |
}, | |
join: function(channel) { // To join a channel | |
irc.raw("JOIN " + channel); | |
}, | |
part: function(channel) { // To leave a channel | |
irc.raw("PART " + channel + " :" + config.partmsg); | |
}, | |
quit: function() { | |
irc.raw("QUIT :"+config.quitmsg); | |
process.exit(0); | |
}, | |
raw: function(data, hide) { // To send raw IRC data (hide hides the sending box, optionally) | |
irc.client.write(data + "\n", "ascii", function() { | |
if(!hide) | |
console.log("[>>] " + data); | |
}); | |
} | |
}; | |
var commands = { | |
prefix: "#", | |
crypt: function(ircdata) { | |
if (ircdata.args.length < 3) | |
{ | |
// Send the help text | |
ircdata.args[0] = "crypt"; | |
commands.help(ircdata); | |
return; | |
} | |
var subcommand = ircdata.args[0]; | |
if (typeof crypto[subcommand] == "undefined") { | |
output.err("commands.crypt", "No such crypt subcommand: " + subcommand); | |
commands._respond("No such crypto method: " + subcommand); | |
return; | |
} | |
output.log("commands.crypt", "found crypto subcommand: " + subcommand); | |
output.log("commands.crypt", "calling with key: " + ircdata.args[1] + " & plain: " + ircdata.args[2]); | |
commands._respond(ircdata, crypto[subcommand](ircdata.args[1], ircdata.args[2])); | |
}, | |
eat: function(ircdata) { // eat <user> | eat - eats the user or the sender | |
output.log("commands.eat", "Eating an user."); | |
irc.action(ircdata.channel, "eats " + ( | |
ircdata.args[0] != "" | |
? ircdata.args[0].trim() | |
: ircdata.sender) ); | |
}, | |
time: function(ircdata) { // time - shows time in my zone | |
var d = new Date(Date.now()); | |
output.log("commands.time", "Doing commands.time"); | |
commands._respond(ircdata, d.getHours() + ":" + d.getMinutes()); | |
}, | |
list: function(ircdata) { // list - lists all commands | |
var keys = []; | |
for (var k in commands.cmdlist) | |
keys.push(k); | |
commands._respond(ircdata, "" + keys.join(", ").trim()); | |
}, | |
ping: function(ircdata) { // ping - responds with pong | |
commands._respond(ircdata, "pong!") | |
}, | |
help: function(ircdata) { // help - helps with the functionality | |
var requested = (ircdata.args[0] || "help").trim(); | |
if(typeof(commands.cmdlist[requested]) != "undefined" | |
&& requested.indexOf("_") == -1) { | |
output.log("commands.help", "command was found"); | |
commands._respond(ircdata, commands.cmdlist[requested]); | |
} | |
else | |
output.log("commands.help", "command was either not found or empty string"); | |
}, | |
version: function(ircdata) { // version - id string | |
commands._respond(ircdata, "boxmein's bot in node.js : https://gist.github.com/4644761"); | |
}, | |
isowner: function(ircdata) { // isowner - if the sender is the owner of the bot. | |
commands._respond(ircdata, commands._isowner(ircdata)); | |
}, | |
js: function(ircdata) { // js <code> - makes javascript | |
output.log("commands.js", "JS METHOD CALL STARTED"); | |
if (commands._isowner(ircdata)) { | |
output.log("commands.js", "Sender is the owner! Executing..."); | |
output.log("commands.js", "JS: " + ircdata.args.join(" ")); | |
var response = ""; | |
try { | |
response = "[return]: " + eval("(function() {" + ircdata.args.join(" ") + "})();"); | |
} | |
catch (error) { | |
response = "[Broken syntax]"; | |
} | |
output.log("commands.js", response); | |
commands._respond(ircdata, response); | |
output.log("commands.js", "JS METHOD CALL ENDED"); | |
} | |
}, | |
stm: function(ircdata) { // sendtomniip <nick> <msg> - makes a HTTP request as mniip's | |
output.log("commands.sendtomniip", "Starting HTTP request"); | |
if (ircdata.args[0] != "") { | |
var message = ""; | |
if (ircdata.args.length > 2) { | |
message = ircdata.args.splice(1, ircdata.args.length -1).join(" "); | |
output.log("commands.sendtomniip", "(0) message: " + message); | |
} | |
else if(ircdata.args[1] != "" && ircdata.args[1] != undefined) { | |
message = ircdata.args[1]; | |
output.log("commands.sendtomniip", "(1) message: " + message); | |
} | |
else { | |
message = "disregard that, I suck cocks"; // http://bash.org/?5775 | |
} | |
var req = http.request( | |
// Object with all the details | |
{ | |
hostname: "mniip.com", | |
path: "/bot.lua?nick=" + ircdata.args[0] + "&msg=" + encodeURIComponent(message), | |
method: "GET", | |
headers: { | |
"User-Agent": "boxnode@" + ircdata.channel, | |
"X-Powered-By": "lolcats" | |
} | |
}, | |
// When the response comes by | |
function(response) { | |
output.log("commands.sendtomniip", "Status: " + response.statusCode); | |
output.log("commands.sendtomniip", "Headers: " + JSON.stringify(response.headers, null, "\t")); | |
// Might need to be in here | |
req.on("error", function(evt) { | |
output.err("commands.sendtomniip", evt.message); | |
}); | |
}); | |
req.end(); | |
} | |
output.log("commands.sendtomniip", "Ending function body"); | |
}, | |
// _functions are meant to be used not as commands, but as utilities | |
_respond: function(ircdata, message) { | |
if (ircdata.channel.indexOf("#") == -1) { // If recipient is an user, send notices | |
irc.notice(ircdata.channel, message); | |
} | |
else | |
irc.privmsg(ircdata.channel, ircdata.sender + ": " + message); | |
}, | |
_isowner: function(ircdata) { | |
var isowner = (ircdata.hostmask.indexOf(config.owner) != -1); | |
output.log("commands._isowner", "user "+ircdata.sender+ (isowner ? " is owner" : " isn't owner")); | |
return isowner; | |
}, | |
_add: function(commandname, helptext, callback) { | |
commands.cmdlist[commandname] = helptext; // Add its help text | |
commands[commandname] = callback; // Add function body | |
}, | |
cmdlist: { | |
// Using this list is mandatory now! | |
"help": "help <cmd> - shows you help on a command", | |
"eat" : "eat <user> | eat - eats another user or you", | |
"time": "time - gives my current time", | |
"list": "list - lists all commands", | |
"ping": "ping - Responds with pong ASAP", | |
"help": "help <command> - help text on a topic", | |
"js": "js <javascript> - run javascript [owner only]", | |
"version": "version - gives you version and my gist link", | |
"isowner": "isowner - check whether or not you're the owner", | |
"stm": "stm <nick> <message> - send a message to make xsbot say it out", | |
"crypt": "crypt <method=otp,caesar,rot13> <key/shift> <plaintext> - Encrypt text" | |
}, | |
}; | |
var output = { | |
log: function(sender, message) { | |
if(!config.silent) console.log("[LL] " + sender + ": " + message); | |
}, | |
err: function(sender, message) { | |
console.log("[EE] " + sender + ": " + message); | |
}, | |
out: function(sender, message) { | |
if(!config.silent) console.log("[>>] " + sender + ": " + message); | |
}, | |
inn: function(message) { // typo is on purpose | |
if(!config.silent) console.log("[<<] " + message); | |
}, | |
announce: function(message) { | |
if (!config.silent) console.log("[--] --- " + message + " ---") | |
}, | |
chanmsg: function(ircdata) { | |
if (!config.silent) console.log("[##]<{0}/{1}> {2}".format(ircdata.sender, ircdata.channel, ircdata.message)); | |
} | |
} | |
var config = { | |
// For prefix, see commands | |
silent: false, // Hides log and channel messages, leaves errors | |
partmsg: "", | |
quitmsg: "", | |
nickname: "", | |
realname: "", | |
channels: "", | |
owner: "" // Use a part of the hostmask | |
}; | |
var crypto = { | |
otp: function(pad, message) { | |
var newstr = ""; | |
if (pad.length >= message.length) { | |
message = message.toUpperCase(); | |
pad = pad.toUpperCase(); | |
for (var i = 0; i < message.length; i++) { | |
newstr += String.fromCharCode( ( (message.charCodeAt(i) - 65) + (pad.charCodeAt(i) - 65) ) % 26 + 65 ); | |
} | |
output.log("crypto.otp", "OTP: " + newstr); | |
output.log("crypto.otp", "Pad: " + pad); | |
return newstr; | |
} | |
else | |
output.err("crypto.otp", "Pad is shorter than the message"); | |
}, | |
caesar: function(offset, message) { | |
message = message.toUpperCase(); | |
var newstr = ""; | |
try { | |
offset = parseInt(offset); | |
} | |
catch (error) { | |
output.err("crypto.caesar", "couldn't parse integer: " + offset); | |
output.log("crypto.caesar", "error: " + error); | |
offset = 5 | |
} | |
for (var i = 0; i < message.length; i++) { | |
newstr += String.fromCharCode((message.charCodeAt(i) - 65 + offset) % 26 + 65); | |
} | |
output.log("crypto.caesar", "Shift complete: " + newstr); | |
return newstr; | |
}, | |
rot13: function(notused, message) { | |
return crypto.caesar(13, message); | |
} | |
}; | |
String.prototype.trim = function() { // woo stackoverflow | |
return this.replace(/^\s+|\s+$/g, ""); | |
}; | |
//first, checks if it isn't implemented yet | |
if (!String.prototype.format) { | |
String.prototype.format = function() { | |
var args = arguments; | |
return this.replace(/{(\d+)}/g, function(match, number) { | |
return typeof args[number] != 'undefined' | |
? args[number] | |
: match | |
; | |
}); | |
}; | |
} | |
irc.client.setEncoding("ascii"); | |
irc.client.setNoDelay(); | |
irc.client.connect(irc.PORT, irc.HOST, irc.onConnected); | |
var stdin = process.openStdin(); | |
irc.client.on("data", irc.onData); | |
irc.client.on("close", irc.onConnectionClose); | |
stdin.on('data', function(data) { | |
// feel free to add commands here. | |
data = data.toString().trim(); | |
if (data == "") | |
return; | |
var args = data.split(" "); | |
var cmd = args.shift(); | |
// output.log("stdin.onData", "received input: " + data); | |
if(cmd == "say") | |
irc.privmsg(args.shift(), args.join(" ")); | |
else if(cmd == "raw") | |
irc.raw(args.join(" ")); | |
else if(cmd == "join") | |
irc.join(args.shift()); | |
else if(cmd == "part") | |
irc.part(args.shift()); | |
else if(cmd == "quit") | |
irc.quit(); | |
else if(cmd == "tlogging") | |
irc.textlogging = !irc.textlogging; | |
else if(cmd == "help") | |
{ | |
console.log( | |
"[--] Help:\n" + | |
"[--] say <chan> <msg> - sends message to channel\n" + | |
"[--] raw <RAW> - sends raw IRC\n[--] join <chan> - joins a channel\n" + | |
"[--] part <chan> - parts a channel\n[--] quit - quits the server\n" + | |
"[--] tlogging - toggles text logging"); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment