Skip to content
Snippets Groups Projects
ircbot.js 8.78 KiB
// $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("funclib.js");

js.branch_limit=0; /* we're not an infinite loop. */

Modules=new Array();
Bot_Commands = new Object();
Config_Last_Write = time(); /* Store when the config was last written. */
load("load/ircbot_functions.js");

/* Global Arrays */
bot_servers = new Array();
quotes = new Array();
masks = new Object();
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");
	config_write_delay=parseInt(config.iniGetValue(null, "config_write_delay"));
//	squelch_list = config.iniGetValue(null, "squelch_list").split(",");

	/* Modules */
	var ini_modules = config.iniGetSections("module_");
	for (var m in ini_modules) {
		var mysec = ini_modules[m];
		var module_name=config.iniGetValue(mysec,"name");
		Modules[module_name]=new Object();
		Modules[module_name].Bot_Commands=new Object();
		Modules[module_name].dir=config.iniGetValue(mysec,"dir");
		Modules[module_name].load=directory(Modules[module_name].dir+"*.js");
		Modules[module_name].lib=[];
		var lib_list=config.iniGetValue(mysec,"lib");
		if(lib_list) {
			lib_list=lib_list.split(",");
			for(var l in lib_list) Modules[module_name].lib.push(removeSpaces(lib_list[l]));
		}
	}
	
	/* 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 */
		));
	}

	config.close();
} else {
	exit("Couldn't open config file!");
}

//LOAD MODULE FILES
for(var m in Modules) {
	for(var l in Modules[m].lib) {
		if(Modules[m].lib[l]) load(Modules[m],Modules[m].lib[l]);
	}
	for(var l in Modules[m].load) {
		if(Modules[m].load[l]) load(Modules[m],Modules[m].load[l]);
	}
}

var user_settings_files = directory(system.data_dir + "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(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;
					}
					// Cycle any available module "main" functions
					for(var m in Modules) {
						if(Modules[m].main) Modules[m].main(srv,srv.channel[c].name);
					}
				}
			}
		}

		/* Take care of DCC chat sessions */
		for (c in dcc_chats) {
			if (!dcc_chats[c].sock.is_connected) {
				log("Closing session.");
				dcc_chats[c].sock.close();
				delete dcc_chats[c];
				continue;
			}
			if (dcc_chats[c].waiting_for_password) {
				var dcc_pwd;
				if (dcc_pwd=dcc_chats[c].sock.readln()) {
					var usr = new User(system.matchuser(dcc_chats[c].id));
					if (!usr ||
						(dcc_pwd.toUpperCase() != usr.security.password)) {
						dcc_chats[c].o(null,"Access Denied.");
						dcc_chats[c].sock.close();
						delete dcc_chats[c];
						continue;
					}
					if (dcc_pwd.toUpperCase() == usr.security.password) {
						dcc_chats[c].waiting_for_password = false;
						dcc_chats[c].o(null,"Welcome aboard.");
					}
				}
				continue;
			}
			var line = dcc_chats[c].sock.readln();
			if (!line || line == "")
				continue;
			var usr = new User(system.matchuser(dcc_chats[c].id));
			var cmd = line.split(" ");
			cmd[0] = cmd[0].toUpperCase();
			try {
				dcc_chats[c].check_bot_command(cmd);
			} catch (err) {
				dcc_chats[c].o(null,"ERROR: " + err);
			}
		}

		if ( (time() - Config_Last_Write) > config_write_delay )
			save_everything();

		mswait(10); /* Don't peg the CPU */
	}
}

//////////////////// 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.bot_access = Server_Bot_Access;
	
	this.check_bot_command = function(target,onick,ouh,cmd) {
		Server_check_bot_command(this,Bot_Commands,target,onick,ouh,cmd);
		for(var bot_cmd in Modules) {
			Server_check_bot_command(this,Modules[bot_cmd].Bot_Commands,target,onick,ouh,cmd);
		}
	}

	this.server_command = function(cmdline,onick,ouh) {
		Server_command(this,cmdline,onick,ouh);
		for(var srv_cmd in Modules) {
			if(Modules[srv_cmd].Server_command) Modules[srv_cmd].Server_command(this,cmdline,onick,ouh);
		}
	}
}

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;
	this.last_spoke = false;
	this.channels=[];
}

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;
	this.check_bot_command = function(cmd) {
		Server_check_bot_command(this,Bot_Commands,null,this.id,null,cmd);
		for(var bot_cmd in Modules) {
			Server_check_bot_command(this,Modules[bot_cmd].Bot_Commands,
				null,this.id,null,cmd
			);
		}
	}
}

function DCC_Out(target,str) {
	this.sock.write(str + "\r\n");
}

/* This must be at the very bottom. */
load("load/ircbot_commands.js");
main();