diff --git a/ctrl/ircbot.ini b/ctrl/ircbot.ini new file mode 100644 index 0000000000000000000000000000000000000000..c8ec943b692e76a5047f0ae5e54b51bfe0d33e8c --- /dev/null +++ b/ctrl/ircbot.ini @@ -0,0 +1,23 @@ +;$Id$ +command_prefix=bot +real_name=Synchronet IRC Bot +help_filename=ircbot_help.txt +config_write_delay=300 +squelch_list= + +[server_Synchronet] +;addresses=127.0.0.1 +;nick=myIRCbot +;services_password=nothing +;channels=#synchronet,#bbs +;port=6667 + +[quotes] +1=<DigitalMan> I'm not sure why JS really needs all this fancy math shit. +2=<Cyan> "The box said 'Windows 95 or better required', therefore Linux was clearly a supported platform." +3=<pcm> I have a very hard time understanding you. Is english your native language? <kernel2> no <pcm> where are you from? <kernel2> Virginia +4=<Cyan> what bothers me more about TV is the high-pitched whine coming from the flyback transformer :P <Deuce> Uhhh... <Deuce> You sure that's not the power steering on your Ford? +5=ircd.txt: "This document is not a replacement for your brain." +6=<kernel2> For example mircosoft can not write software worth shit without releasing bugs in there programs, yahoo can not control there animals, hotmail.com is a joke because they are the second spammers in the us +7=<Vagabond> heh. Deuce only Codes. He does not make things Pretty. + diff --git a/exec/ircbot.js b/exec/ircbot.js new file mode 100644 index 0000000000000000000000000000000000000000..817e824789727afb1b447a206e76d36e1b3dcea7 --- /dev/null +++ b/exec/ircbot.js @@ -0,0 +1,235 @@ +// $Id$ +/* + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details: + http://www.gnu.org/licenses/gpl.txt + + An IRC bot written in JS that interfaces with the local BBS. + + Copyright 2010 Randolph E. Sommerfeld <sysop@rrx.ca> + +*/ + +load("sockdefs.js"); +load("sbbsdefs.js"); +load("irclib.js"); + +load("load/ircbot_functions.js"); + +js.branch_limit=0; /* we're not an infinite loop. */ + +Module_Save_Data = new Object(); +Bot_Commands = new Object(); + +Config_Last_Write = time(); /* Store when the config was last written. */ + +/* Global Arrays */ +bot_servers = new Array(); +masks = new Object(); +quotes = new Array(); +dcc_chats = new Array(); +squelch_list = new Array(); + +var config_filename = "ircbot.ini"; +for (cmdarg=0;cmdarg<argc;cmdarg++) { + switch(argv[cmdarg].toLowerCase()) { + case "-f": + config_filename = argv[++cmdarg]; + break; + default: + break; + } +} + +var config = new File(system.ctrl_dir + config_filename); +if (config.open("r")) { + /* Global Variables */ + command_prefix = config.iniGetValue(null, "command_prefix"); + real_name = config.iniGetValue(null, "real_name"); + help_filename = config.iniGetValue(null, "help_filename"); + help_file = new File(help_filename); + config_write_delay=parseInt(config.iniGetValue(null, "config_write_delay")); +// squelch_list = config.iniGetValue(null, "squelch_list").split(","); + + /* Servers */ + var ini_server_secs = config.iniGetSections("server_"); + for (s in ini_server_secs) { + var mysec = ini_server_secs[s]; + bot_servers.push(new Bot_IRC_Server( + 0, /* Socket */ + config.iniGetValue(mysec, "addresses"), + config.iniGetValue(mysec, "nick"), + config.iniGetValue(mysec, "services_password"), + config.iniGetValue(mysec, "channels"), + parseInt(config.iniGetValue(mysec, "port")), + mysec.slice(7) /* Network Name */ + )); + } + + /* Quotes */ + var ini_quotes = config.iniGetKeys("quotes"); + for (q in ini_quotes) { + quotes.push(config.iniGetValue("quotes", ini_quotes[q])); + } + + config.close(); +} else { + exit("Couldn't open config file!"); +} + +var user_settings_files = directory("/home/bbs/data/user/*.ircbot.ini"); +for (f in user_settings_files) { + var us_file = new File(user_settings_files[f]); + if (us_file.open("r")) { + var tokenized_path = user_settings_files[f].split("/"); + var us_filename = tokenized_path[tokenized_path.length-1]; + var uid_str = us_filename.split(".")[0]; + while (uid_str[0] == "0") { + uid_str = uid_str.slice(1); + } + printf("***Reading: " + us_file.name + "\r\n"); + var read_masks = us_file.iniGetValue(null, "masks"); + if (read_masks) + masks[parseInt(uid_str)] = read_masks.split(","); + } +} + +log("*** Entering Main Loop. ***"); + +function main() { + while (!js.terminated) { + for (my_srv in bot_servers) { + var cmdline; + var srv = bot_servers[my_srv]; + if (!srv.sock &&(srv.lastcon <time())) { //we're not connected. + var consock = IRC_client_connect(srv.host, srv.nick, + command_prefix, real_name, srv.port); + if (consock) { + srv.sock = consock; + log("--- Connected to " + srv.host); + /* If we just connected, then clear all our joined channels. */ + for (c in srv.channel) { + srv.channel[c].is_joined = false; + } + } else { + log("--- Connect to " + srv.host + " failed, " + + "retry in 60 seconds."); + srv.lastcon = time() + 60; + } + } else if (srv.sock && srv.sock.data_waiting && + (cmdline=srv.sock.recvline(4096,0))) { + var onick; + var ouh; + var outline; + var sorigin = cmdline.split(" ")[0].slice(1); + if ((cmdline[0] == ":") && sorigin.match(/[@]/)) { + onick = sorigin.split("!")[0]; + ouh = sorigin.split("!")[1]; + outline = "["+onick+"("+ouh+")] " + cmdline; + } else { + onick = ""; + ouh = ""; + outline = cmdline; + } + log("<-- " + srv.host + ": " + outline); + srv.server_command(IRC_parsecommand(cmdline),onick,ouh); + } + + // Run through some commands. + if (srv.sock && srv.is_registered) { + for (c in srv.channel) { + if (!srv.channel[c].is_joined && + (srv.channel[c].lastjoin < time())) { + srv.writeout("JOIN " + srv.channel[c].name); + srv.channel[c].lastjoin = time() + 60; + } + } + } + mswait(10); /* Don't peg the CPU */ + } + if ( (time() - Config_Last_Write) > config_write_delay ) + save_everything(); + + } +} + +//////////////////// Objects and Functions //////////////////// +function Bot_IRC_Server(sock,host,nick,svspass,channels,port,name) { + // Static variables (never change) + this.sock = sock; + this.host = host; + this.nick = nick; + this.svspass = svspass; + this.port = port; + this.name = name; + // Channels + this.channel = new Array(); + var my_channels = channels.split(","); + for (c in my_channels) { + log ("--- Adding Channel: " + my_channels[c]); + this.channel[my_channels[c].toUpperCase()] = new Bot_IRC_Channel( + my_channels[c]); + } + // Dynamic variables (used for the bot's state. + this.curnick = nick; + this.lastcon = 0; // When it's OK to reconnect. + this.is_registered = false; + this.juped = false; + this.users = new Object(); // Store local nicks & uh info. + // Functions + this.ctcp = Server_CTCP; + this.ctcp_reply = Server_CTCP_Reply; + this.o = Server_target_out; + this.writeout = Server_writeout; + this.server_command = Server_command; + this.check_bot_command = Server_check_bot_command; + this.bot_access = Server_Bot_Access; +} + +function Bot_IRC_Channel(name) { + // Statics. + this.name = name; + // Dynamics. + this.is_joined = false; + this.lastjoin = 0; + // Functions. +} + +function Server_User(uh) { + this.uh = uh; + this.ident = false; +} + +function Bot_Command(min_security,args_needed,ident_needed) { + this.min_security = min_security; + this.args_needed = args_needed; + this.ident_needed = ident_needed; + this.command = false; +} + +function DCC_Chat(sock,id) { + this.sock = sock; + this.id = id; + /* State info */ + this.waiting_for_password = true; + /* Functions */ + this.o = DCC_Out; +} + +function DCC_Out(target,str) { + this.sock.write(str + "\r\n"); +} + +/* This must be at the very bottom. */ +load("load/ircbot_commands.js"); +//load("load/ircbot_functions.js"); +//load("ircbot/fieldday.js"); +main(); diff --git a/exec/load/ircbot_commands.js b/exec/load/ircbot_commands.js new file mode 100644 index 0000000000000000000000000000000000000000..1e84494a9d2516a2bc23f6d7642691390b5a9b75 --- /dev/null +++ b/exec/load/ircbot_commands.js @@ -0,0 +1,595 @@ +// $Id$ +/* + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details: + http://www.gnu.org/licenses/gpl.txt + + Synchronet IRC Daemon as per RFC 1459, link compatible with Bahamut + + Copyright 2010 Randolph Erwin Sommerfeld <sysop@rrx.ca> + + An IRC bot written in JS that interfaces with the local BBS. + +*/ + +Bot_Commands["RELOAD"] = new Bot_Command(50,false,false); +Bot_Commands["RELOAD"].usage = + "RELOAD"; +Bot_Commands["RELOAD"].help = + "Reloads the internal bot command and function structure. No arguments."; +Bot_Commands["RELOAD"].command = function (target,onick,ouh,srv,lvl,cmd) { + load("load/ircbot_commands.js"); + load("load/ircbot_functions.js"); + srv.o(target,"Reloaded."); + return; +} + +Bot_Commands["WHOIS"] = new Bot_Command(0,false,false); +Bot_Commands["WHOIS"].usage = + "WHOIS <nick>"; +Bot_Commands["WHOIS"].help = + "Brings up information about a user. If the <nick> argument is omitted, " + + "then it will display information about you."; +Bot_Commands["WHOIS"].command = function (target,onick,ouh,srv,lvl,cmd) { + if (!cmd[1]) { + srv.o(target,"You are recognized as access level " + lvl); + cmd[1] = onick; + } + var usr = new User(system.matchuser(cmd[1])); + if (usr.number > 0) { + srv.o(target,usr.alias+" has an access level of " + +usr.security.level+"."); + if (masks[usr.number]) + srv.o(target,"Masks: " + masks[usr.number].join(" ")); + else + srv.o(target,usr.alias + " has no IRC masks defined."); + srv.o(target,usr.alias + " last signed on " + usr.laston_date + " via " + + usr.connection + "."); + } else { + srv.o(target,"I have no such user in my database."); + } + return; +} + +Bot_Commands["ADDMASK"] = new Bot_Command(50,1,false); +Bot_Commands["ADDMASK"].command = function (target,onick,ouh,srv,lvl,cmd) { + var addmask = false; + var delmask = false; + if (cmd[0] == "ADDMASK") { + addmask = true; + } else if (cmd[0] == "DELMASK") { + delmask = true; + } + if (!addmask && !delmask) { + srv.o(target,"Huh? I'm confused :("); + return; + } + if (!cmd[2]) { + cmd[2] = cmd[1]; + cmd[1] = onick; + } + if (!cmd[2].match(/[@]/)) { + srv.o(target,"Typically, hostmasks need a '@' in them."); + return; + } + var usr = new User(system.matchuser(cmd[1])); + if (usr.number == 0) { + srv.o(target,"That user doesn't exist!"); + return; + } + var self_change = (onick.toUpperCase() == cmd[1].toUpperCase()); + if ( (lvl < 80) && !self_change) { + srv.o(target,"You do not have permission to change IRC masks " + + "for other users."); + return; + } + if ((usr.security.level >= lvl) && !self_change) { + srv.o(target,"You cannot add or delete masks for a user whose " + + "access level is equal to or greater than yours."); + return; + } + if (addmask) { + for (m in masks[usr.number]) { + if (wildmatch(cmd[2],masks[usr.number][m])) { + srv.o(target,"This user already has a mask matching that. " + + "Try deleting it with 'DELMASK' first."); + return; + } + } + } + if (delmask) { + for (m in masks[usr.number]) { + if (masks[usr.number][m].toUpperCase() == cmd[2].toUpperCase()) { + masks[usr.number].splice(m,1); + srv.o(target,"Mask deleted for user " + usr.alias + ": " + + cmd[2]); + return; + } + } + srv.o(target,"Couldn't find the mask you were looking for."); + } else if (addmask) { + if (!masks[usr.number]) + masks[usr.number] = new Array(); + masks[usr.number].push(cmd[2]); + srv.o(target,"Mask added for user " + usr.alias + ": " + cmd[2]); + } + return; +} +Bot_Commands["DELMASK"] = Bot_Commands["ADDMASK"]; + +Bot_Commands["ADDUSER"] = new Bot_Command(80,2,false); +Bot_Commands["ADDUSER"].command = function (target,onick,ouh,srv,lvl,cmd) { + if (IRC_check_nick(cmd[1],40)) { + srv.o(target,cmd[1] + " isn't a valid nickname."); + return; + } + var usr = cmd[1].toUpperCase(); + var syncusr = new User(system.matchuser(cmd[1])); + if (!srv.users[usr] && !cmd[2]) { + srv.o(target,cmd[1] + " is not on any channels that I'm currently on. " + + "To force an add for this user, please specify a mask."); + return; + } else if (syncusr.number > 0) { + srv.o(target,cmd[1] + " already exists in my database!"); + return; + } + var mask; + var level = 50; + if (cmd[2] && cmd[2].match(/[.]/)) {//2nd arg is a mask + mask = cmd[2]; + } else if (cmd[2]) { // must be a level. + if (!srv.users[usr] && !cmd[3]) { + srv.o(target,cmd[1] + " is not on any channels that I'm " + + "currently on. To force an add for this user, please " + + "specify a mask."); + return; + } + level = parseInt(cmd[2]); + if (level >= lvl) { + srv.o(target,"You may only add users with a lower access level " + + "than your own."); + return; + } + } + if (cmd[3] && !mask) + mask = cmd[3]; + // create a mask for this user. + if (!mask) + mask = IRC_create_default_mask(srv.users[usr].uh); + if (!mask && !level) { + srv.o(target,"Uh oh, something bogus happened. " + + "Alert the bot owner. (!mask && !level)"); + return; + } + var mask_array = mask.split(","); + var inval_mask = false; + for (my_mask in mask_array) { + if (IRC_check_host(mask_array[my_mask],true,true,false)) { + srv.o(target,"The mask (" + mask_array[my_mask] + ") is " + + "invalid. No user added."); + inval_mask = true; + return; + } + } + if (inval_mask) + return; + srv.o(target,"Added " + cmd[1] + " as level " + level + + " with mask(s): " + mask); + srv.o(target,"This user should now set a password with /MSG " + srv.nick + + " PASS"); + var newuser = system.new_user(cmd[1]); + masks[newuser.number] = mask_array; + login_user(newuser); + newuser.settings |= USER_INACTIVE; + newuser.security.level = level; + return; +} + +Bot_Commands["CHANGE"] = new Bot_Command(80,2,false); +Bot_Commands["CHANGE"].command = function (target,onick,ouh,srv,lvl,cmd) { + var usr = new User(system.matchuser(cmd[1])); + if (usr.number == 0) { + srv.o(target,"The user " + cmd[1] + " doesn't exist in my database."); + return; + } + if (lvl <= usr.security.level) { + srv.o(target,"You can only use the change command on users with a " + + "lower level than yours. (" + lvl + ") " + + "This error message Copyright 2006 Deuce. ;)"); + return; + } + if (parseInt(cmd[2]) >= lvl) { + srv.o(target,"You cannot change an access level to be higher or equal " + + "to your own. (" + lvl + ")"); + return; + } + srv.o(target,"Access level for " + usr.alias + " changed to " + + parseInt(cmd[2])); + usr.security.level = parseInt(cmd[2]); + return; +} + +Bot_Commands["RESETPASS"] = new Bot_Command(90,true,false); +Bot_Commands["RESETPASS"].command = function (target,onick,ouh,srv,lvl,cmd) { + var usr = new User(system.matchuser(cmd[1])); + if (usr.number > 0) { + srv.o(target,usr.alias + "'s password has been reset. " + + "They should now set a new one with /MSG " + srv.nick + " " + + "PASS <newpass>"); + usr.security.password = ""; + usr.settings |= USER_INACTIVE; + } else { + srv.o(target,cmd[1] + " doesn't exist in my database!"); + } + return; +} + +Bot_Commands["PASS"] = new Bot_Command(50,true,false); +Bot_Commands["PASS"].command = function (target,onick,ouh,srv,lvl,cmd) { + if ((target[0] == "#") || (target[0] == "&")) { + srv.o(target,"Fool! I'm not setting your password to something " + + "you broadcast in a public channel. Pick a new password and " + + "then /MSG " + srv.nick + " PASS <newpass>"); + return; + } + var usr = new User(system.matchuser(onick)); + if (usr.number == 0) { + srv.o(target,"Huh? You don't exist. Inform the bot owner (!usr)"); + return; + } + if (usr.security.password != "") { + if (!cmd[2]) { + srv.o(target,"I need your old password too, bud. " + + "/MSG " + srv.nick + " PASS <newpass> <oldpass>"); + return; + } + if (cmd[2].toUpperCase() != usr.security.password) { + srv.o(target,"Password mismatch. /MSG " + srv.nick + " PASS " + + "<newpass> <oldpass>"); + return; + } + } + srv.o(target,"Your password has now been set to '" + cmd[1] + "', " + +"don't forget it!"); + usr.security.password = cmd[1]; + if (usr.settings&USER_INACTIVE) + usr.settings &= ~USER_INACTIVE; + return; +} + +Bot_Commands["IDENT"] = new Bot_Command(0,true,false); +Bot_Commands["IDENT"].command = function (target,onick,ouh,srv,lvl,cmd) { + var usr = new User(system.matchuser(onick)); + if (cmd[2]) { /* Username passed */ + usr = new User(system.matchuser(cmd[1])); + cmd[1] = cmd[2]; + } + if (!usr.number) { + srv.o(target,"No such user."); + return; + } + if ((target[0] == "#") || (target[0] == "&")) { + if (lvl >= 50) { + srv.o(target,"Fool! You've just broadcasted your password to " + + "a public channel! Because of this, I've reset your " + + "password. Pick a new password, then /MSG " + srv.nick + " " + + "PASS <newpass>"); + usr.security.password = ""; + } else { + srv.o(target,"Is broadcasting a password to a public channel " + + "really a smart idea?"); + } + return; + } + if (usr.security.password == "") { + srv.o(target,"Your password is blank. Please set one with /MSG " + + srv.nick + " PASS <newpass>, and then use IDENT."); + return; + } + if (cmd[1].toUpperCase() == usr.security.password) { + srv.o(target,"You are now recognized as user '" + usr.alias + "'"); + srv.users[onick.toUpperCase()].ident = usr.number; + login_user(usr); + return; + } + srv.o(target,"Incorrect password."); + return; +} + +Bot_Commands["EVAL"] = new Bot_Command(0,true,false); +Bot_Commands["EVAL"].command = function (target,onick,ouh,srv,lvl,cmd) { + cmd.shift(); + var query = cmd.join(" "); + js.branch_limit=1000; // protection + js.branch_counter=0; // reset + try { + srv.o(target, strip_ctrl(js.eval(query))); + } catch(e) { + srv.o(target,"ERROR: "+e); + } + js.branch_limit=0; // protection off + return; +} + +Bot_Commands["SEVAL"] = new Bot_Command(99,true,true); +Bot_Commands["SEVAL"].command = function (target,onick,ouh,srv,lvl,cmd) { + cmd.shift(); + var query = cmd.join(" "); + try { + srv.o(target,eval(query)); + } catch(e) { + srv.o(target,"ERROR: "+e); + } + return; +} + +Bot_Commands["DIE"] = new Bot_Command(90,false,false); +Bot_Commands["DIE"].command = function (target,onick,ouh,srv,lvl,cmd) { + for (s in bot_servers) { + bot_servers[s].writeout("QUIT :" + onick + " told me to die."); + } + js.terminated=true; + return; +} + +Bot_Commands["RESTART"] = new Bot_Command(90,false,false); +Bot_Commands["RESTART"].command = function (target,onick,ouh,srv,lvl,cmd) { + for (s in bot_servers) { + bot_servers[s].writeout("QUIT :Restarting as per " + onick); + } + exit(); + return; +} + +Bot_Commands["GROUPS"] = new Bot_Command(50,true,false); +Bot_Commands["GROUPS"].command = function (target,onick,ouh,srv,lvl,cmd) { + for (g in msg_area.grp_list) { + srv.o(target,"[" + msg_area.grp_list[g].number + "] " + + msg_area.grp_list[g].description); + } + return; +} + +/* HELP needs to be rewritten. +case "HELP": + if (!cmd[1]) + cmd[1] = "HELP"; + var search = "!" + cmd[1].toUpperCase(); + if (help_file.open("r")) { + while (!help_file.eof) { + var hf_line = help_file.readln(); + if (hf_line && (hf_line == search)) { + while (!hf_line[0] != "@") { + hf_line = help_file.readln(); + if (hf_line[0] == "@") { + break; + } else if (hf_line[0] == "^") { + hf_line = parseInt(hf_line.slice(1)); + if (bot_access(onick,ouh) < hf_line) + break; + } else if (hf_line[0] == ":") { + + var str = hf_line.slice(1); + if (!str) + str = " "; + this.writeout("NOTICE "+onick+" :" + str); + } + } + if ((hf_line[0] == "@") && hf_line[1]) { + hf_line = hf_line.slice(1); + this.writeout("NOTICE "+onick+" :Restricted to access level " + hf_line); + } + help_file.close(); + break; + } + } + } + break; +*/ + +Bot_Commands["SUBS"] = new Bot_Command(50,true,false); +Bot_Commands["SUBS"].command = function (target,onick,ouh,srv,lvl,cmd) { + var groupnum = parseInt(cmd[1]); + if (!msg_area.grp_list[groupnum]) { + srv.o(target,"Group number " + cmd[1] + " doesn't exist!"); + return; + } + var sg = msg_area.grp_list[groupnum].sub_list; + for (g in msg_area.grp_list[groupnum].sub_list) { + srv.o(target,"[" + sg[g].number + "] " + sg[g].description + + " (" + sg[g].code + ")"); + } + return; +} +Bot_Commands["SUBGROUPS"] = Bot_Commands["SUBS"]; + +Bot_Commands["READ"] = new Bot_Command(50,true,false); +Bot_Commands["READ"].command = function (target,onick,ouh,srv,lvl,cmd) { + if (!cmd[2]) { // user wants to list msgs? + var msgs = new MsgBase(cmd[1]); + msgs.open(); + srv.o(target,"There are " + msgs.total_msgs + " messages to read from " + + msgs.first_msg + " to " + msgs.last_msg); + msgs.close(); + return; + } else if (cmd[2]) { // reading a msg + var msgs = new MsgBase(cmd[1]); + var mn = parseInt(cmd[2]); + msgs.open(); + var mh = msgs.get_msg_header(mn); + srv.o(target," To: " + mh.to); + srv.o(target,"From: " + mh.from); + srv.o(target,"Subj: " + mh.subject); + var my_msg = msgs.get_msg_body(mn).split("\r\n"); + for (line in my_msg) { + if (!my_msg[line]) + my_msg[line] = " "; + srv.o(target, my_msg[line]); + } + msgs.close(); + return; + } + return; +} + +Bot_Commands["FINGER"] = new Bot_Command(50,true,false); +Bot_Commands["FINGER"].command = function (target,onick,ouh,srv,lvl,cmd) { + var udpfinger = false; + if (cmd[0] == "UDPFINGER") + udpfinger = true; + var f_host; + var f_user; + if (cmd[1].match(/[@]/)) { // user@host + f_host = cmd[1].split("@")[1]; + f_user = cmd[1].split("@")[0]; + } else { // assume just host + f_host = cmd[1]; + f_user = ""; + } + var f_sock; + if (udpfinger) { + f_sock = new Socket(SOCK_DGRAM); + f_sock.nonblocking = true; + } else { + f_sock = new Socket(); + } + if (!f_sock.connect(f_host,79)) { + srv.o(target,"Couldn't connect to "+ f_host +": " + f_sock.last_error); + return; + } else { + f_sock.send(f_user + "\r\n"); + var f_line_count = 0; + var timeout = time()+10; + while(f_sock.is_connected) { + if (udpfinger) { + var tmp = f_sock.read(); + if (tmp) { + var udp_lines = tmp.split("\r\n"); + for (ul in udp_lines) { + srv.o(target, strip_ctrl(udp_lines[ul])); + f_line_count++; + if ((f_line_count > 10) && (lvl < 75) && + ((target[0] == "#") || (target[0] == "&")) ) { + srv.o(target,"*** Connection Terminated " + + "(output squelched after 10 lines)"); + return; + } + } + return; + } + } else { + srv.o(target, strip_ctrl(f_sock.readline())); + f_line_count++; + if ((f_line_count > 10) && + (lvl < 75) && + ((target[0] == "#") || + (target[0] == "&")) ) { + srv.o(target,"*** Connection Terminated " + +"(output squelched after 10 lines)"); + return; + } + } + if (time() >= timeout) { + srv.o(target,"*** Your query timed out after 10 seconds."); + return; + } + } + f_sock.close(); + } + return; +} +Bot_Commands["UDPFINGER"] = Bot_Commands["FINGER"]; + +Bot_Commands["ADDQUOTE"] = new Bot_Command(80,true,false); +Bot_Commands["ADDQUOTE"].command = function (target,onick,ouh,srv,lvl,cmd) { + cmd.shift(); + var the_quote = cmd.join(" "); + quotes.push(the_quote); + srv.o(target,"Thanks for the quote!"); + return; +} + +Bot_Commands["GREET"] = new Bot_Command(50,false,false); +Bot_Commands["GREET"].command = function (target,onick,ouh,srv,lvl,cmd) { + var usr = new User(system.matchuser(onick)); + if (!usr.number) { + srv.o(target,"You don't exist."); + return; + } + if (cmd[1]) { + if (cmd[1].toUpperCase() == "NULL") + cmd[1] = ""; + cmd.shift(); + var the_greet = cmd.join(" "); + srv.o(target,"Your greet has been changed."); + usr.comment = the_greet; + return; + } else { + srv.o(target,"[" + onick + "] " + usr.comment); + return; + } + return; +} + +Bot_Commands["QUOTE"] = new Bot_Command(0,false,false); +Bot_Commands["QUOTE"].command = function (target,onick,ouh,srv,lvl,cmd) { + if (cmd[1]) { + cmd.shift(); + var searched_quotes = new Array(); + var search_params = cmd.join(" "); + var lucky_number; + var found_a_quote = false; + while (searched_quotes.length < quotes.length) { + lucky_number = random(quotes.length); + if (!searched_quotes[lucky_number]) { + if (quotes[lucky_number].toUpperCase().match(search_params.toUpperCase())) { + srv.o(target, quotes[lucky_number]); + found_a_quote = true; + break; + } + searched_quotes[lucky_number] = true; + } + } + if (!found_a_quote) + srv.o(target,"Couldn't find a quote that matches your criteria."); + return; + } + srv.o(target, quotes[random(quotes.length)]); + return; +} + +Bot_Commands["SAVE"] = new Bot_Command(80,false,false); +Bot_Commands["SAVE"].command = function (target,onick,ouh,srv,lvl,cmd) { + if (save_everything()) { + srv.o(target,"Data successfully written. Congratulations."); + } else { + srv.o(target,"Oops, couldn't write to disk. Sorry, bud."); + } + return; +} + +Bot_Commands["EXEC"] = new Bot_Command(99,true,true); +Bot_Commands["EXEC"].command = function (target,onick,ouh,srv,lvl,cmd) { + cmd.shift(); + var query = cmd.join(" "); + var this_poutput = system.popen(query); + if (!this_poutput) { + srv.o(target,"Command failed. :("); + return; + } + for (line in this_poutput) { + if (!this_poutput[line]) + this_poutput[line] = " "; + srv.o(target, this_poutput[line]); + } + return; +} + diff --git a/exec/load/ircbot_functions.js b/exec/load/ircbot_functions.js new file mode 100644 index 0000000000000000000000000000000000000000..0f508a5ada6256428cf765ba8e0e47394bfbc368 --- /dev/null +++ b/exec/load/ircbot_functions.js @@ -0,0 +1,256 @@ +// $Id$ +/* + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details: + http://www.gnu.org/licenses/gpl.txt + + Copyright 2010 Randolph E. Sommerfeld <sysop@rrx.ca> + +*/ + +/********** Command Processors. **********/ +function Server_command(cmd,onick,ouh) { + var cmdline = cmd.join(" "); + switch (cmd[0]) { + case "001": // "Welcome." + this.is_registered = true; + break; + case "352": // WHO reply. Process into local cache. + var nick = cmd[6].toUpperCase(); + this.users[nick] = new Server_User(cmd[3] + "@" + cmd[4]); + break; + case "433": // Nick already in use. + this.juped = true; + var newnick = this.nick+"-"+random(50000).toString(36); + this.writeout("NICK " + newnick); + this.curnick = newnick; + log("*** Trying alternative nick, my nick is jupitered. " + + "(Temp: " + newnick + ")"); + break; + case "JOIN": + if (cmd[1][0] == ":") + cmd[1] = cmd[1].slice(1); + var chan = this.channel[cmd[1].toUpperCase()]; + if ((onick == this.curnick) && chan && !chan.is_joined) { + chan.is_joined = true; + this.writeout("WHO " + cmd[1]); + break; + } + // Someone else joining. + this.users[onick.toUpperCase()] = new Server_User(ouh); + var lvl = this.bot_access(onick,ouh); + if (lvl >= 50) { + var usr = new User(system.matchuser(onick)); + if (lvl >= 60) + this.writeout("MODE " + cmd[1] + " +o " + onick); + if (usr.number > 0) { + if (usr.comment) + this.o(cmd[1],"[" + onick + "] " + usr.comment); + login_user(usr); + } + } + break; + case "PRIVMSG": + if ((cmd[1][0] == "#") || (cmd[1][0] == "&")) { + var chan = this.channel[cmd[1].toUpperCase()]; + if (!chan) + break; + if (!chan.is_joined) + break; + cmd[2] = cmd[2].slice(1); + if ( (cmd[2].toUpperCase() == command_prefix.toUpperCase()) + && cmd[3]) { + cmd[3] = cmd[3].toUpperCase(); + cmd.shift(); + cmd.shift(); + cmd.shift(); + this.check_bot_command(chan.name,onick,ouh,cmd); + break; + } + } else if (cmd[1].toUpperCase() == + this.curnick.toUpperCase()) { // MSG? + cmd[2] = cmd[2].slice(1).toUpperCase(); + cmd.shift(); + cmd.shift(); + if (cmd[0][0] == "\1") { + cmd[0] = cmd[0].slice(1).toUpperCase(); + cmd[cmd.length-1] = cmd[cmd.length-1].slice(0,-1); + this.ctcp(onick,ouh,cmd); + break; + } + this.check_bot_command(onick,onick,ouh,cmd); + } + break; + case "PING": + this.writeout("PONG :" + IRC_string(cmdline)); + break; + case "ERROR": + this.sock.close(); + this.sock = 0; + break; + default: + break; + } +} + +function Server_CTCP(onick,ouh,cmd) { + switch (cmd[0]) { + case "DCC": + if (cmd[4]) { + log("cmd1:" + cmd[1] + ":"); + log("cmd2:" + cmd[2] + ":"); + log("cmd3:" + cmd[3] + ":"); + log("cmd4:" + cmd[4] + ":"); + if ((cmd[1].toUpperCase() == "CHAT") + && (cmd[2].toUpperCase() == "CHAT") + && (parseInt(cmd[3]) == cmd[3]) + && (parseInt(cmd[4]) == cmd[4])) { + var ip = int_to_ip(cmd[3]); + var port = parseInt(cmd[4]); + var sock = new Socket(); + sock.connect(ip, port, 3 /* Timeout */); + if (sock.is_connected) { + sock.write("Enter your password.\r\n"); + dcc_chats.push(new DCC_Chat(sock,onick)); + } + } + } + break; + case "PING": + var reply = "PING "; + if (parseInt(cmd[1]) == cmd[1]) { + reply += cmd[1]; + if (cmd[2] && (parseInt(cmd[2]) == cmd[2])) + reply += " " + cmd[2]; + this.ctcp_reply(onick, reply); + } + break; + case "VERSION": + this.ctcp_reply(onick, "VERSION " + + "Synchronet IRC Bot by Randy E. Sommerfeld <cyan@rrx.ca>"); + break; + case "FINGER": + this.ctcp_reply(onick, "FINGER " + + "Finger message goes here."); + break; + default: + break; + } + return; +} + +function Server_CTCP_Reply(nick,str) { + this.writeout("NOTICE " + nick + " :\1" + str + "\1"); +} + +function Server_check_bot_command(target,onick,ouh,cmd) { + var access_level = this.bot_access(onick,ouh); + var botcmd = Bot_Commands[cmd[0]]; + if (botcmd) { + if (botcmd.ident_needed && !this.users[onick.toUpperCase()].ident) { + this.o(target,"You must be identified to use this command."); + return 0; + } + if (access_level < botcmd.min_security) { + this.o(target,"You do not have sufficient access to this command."); + return 0; + } + if ((botcmd.args_needed == true) && !cmd[1]) { + this.o(target,"Hey buddy, I need some arguments for this command."); + return 0; + } else if ((parseInt(botcmd.args_needed) == botcmd.args_needed) + && !cmd[botcmd.args_needed]) { + this.o(target,"Hey buddy, incorrect number of arguments provided."); + return 0; + } + /* If we made it this far, we're good. */ + botcmd.command(target,onick,ouh,this,access_level,cmd); + return 1; + } + return 0; /* No such command */ +} + +//////////////////// Non-object Functions //////////////////// + +/* Save everything */ +function save_everything() { + if (!config.open("r+")) + return false; + + config.iniSetValue(null, "command_prefix", command_prefix); + config.iniSetValue(null, "real_name", real_name); + config.iniSetValue(null, "help_filename", help_filename); + config.iniSetValue(null, "config_write_delay", config_write_delay); + config.iniSetValue(null, "squelch_list", squelch_list.join(",")); + + for (m in masks) { + var uid_str = format("%04u", m); + var us_file = new File("/home/bbs/data/user/" +uid_str+ ".ircbot.ini"); + if (us_file.open("r+")) { + us_file.iniSetValue(null, "masks", masks[m].join(",")); + us_file.close(); + } + } + + for (q in quotes) { + config.iniSetValue("quotes", q, quotes[q]); + } + + config.close(); + + for (s in Module_Save_Data) { + Module_Save_Data[s](); + } + + Config_Last_Write = time(); + + return true; +} + +function login_user(usr) { + usr.connection = "IRC"; + usr.logontime = time(); +} + +// return the access level of this user. +function Server_Bot_Access(nick,uh) { + var ucnick = nick.toUpperCase(); + if (this.users[ucnick].ident) { + var usrnum = this.users[ucnick].ident; + var thisuser = new User(usrnum); + return thisuser.security.level; + } + var usrnum = system.matchuser(nick); + if (!usrnum) + return 0; + var thisuser = new User(usrnum); + for (m in masks[usrnum]) { + if (wildmatch(uh,masks[usrnum][m])) + return thisuser.security.level; + } + return 0; // assume failure +} + +function Server_writeout(str) { + log("--> " + this.host + ": " + str); + this.sock.write(str + "\r\n"); +} + +function Server_target_out(target,str) { + for (c in squelch_list) { + if (target.toUpperCase() == squelch_list[c].toUpperCase()) + return; + } + var outstr = "PRIVMSG " + target + " :" + str; + log("--> " + this.host + ": " + outstr); + this.sock.write(outstr + "\r\n"); +} +