diff --git a/HxBot.hx b/HxBot.hx index 096519e..024b8ec 100644 --- a/HxBot.hx +++ b/HxBot.hx @@ -1,5 +1,5 @@ // Haxe RawBot v1.1 -// Attempts to catch connection loss +// Catches connection problems import sys.net.Host; import sys.net.Socket; @@ -7,52 +7,138 @@ using StringTools; class HxBot { public static function main() { - var server = new Host('127.0.0.1'); + // Initialize variables + var server = '127.0.0.1'; var port = 6667; - var channel = '#lobby'; + var autojoin = ['#lobby']; var botnick = 'HxRawBot'; - // Program begins - Sys.println('Connecting to ${Std.string(server)}:${Std.string(port)}...'); + // Connect to server + Sys.println('Connecting to ${server}:${Std.string(port)}...'); Sys.println(""); var irc = new Socket(); - // Need to put a try-catch in here probably - irc.connect(server, port); + irc.connect(new Host(server), port); - irc.write('NICK ${Std.string(botnick)}\n'); - irc.write('USER ${Std.string(botnick)} ${Std.string(botnick)} ${Std.string(botnick)} :Haxe IRC bot\r\n'); + // Define functions + function say(sendtarget, sendmsg) { + irc.write('PRIVMSG ${sendtarget} :${sendmsg}\r\n'); + } + function ctcp(sendtype, sendtarget, sendmsg) { + irc.write('${sendtype} ${sendtarget} :\x01${sendmsg}\x01\r\n'); + } + function join(chan) { + irc.write('JOIN :${chan}\r\n'); + } + function part(chan) { + irc.write('PART '+ chan +'\r\n'); + } - irc.write('JOIN :${Std.string(channel)}\r\n'); + irc.write('NICK ${botnick}\n'); + irc.write('USER ${botnick} ${botnick} ${botnick} :Haxe IRC bot\r\n'); - // Initialize our data vars - var data; - var source; + for (i in autojoin) { + join(i); + } // Main program loop while (true) { - data = irc.input.readLine(); // The good one + try { + var data = irc.input.readLine(); + if (data.charAt(0) == ":") {data = data.substr(1);}; // Strip leading colons - Sys.println(data); + //var timestamp = '[${DateTools.format(Date.now(), "%T")}]'; // For later - // Respond to server pings - if (data.indexOf("PING") == 0) { - irc.write('PONG ${data.substring(data.indexOf(" ")+1)}\r\n'); - } - - // Auto-Rejoin when kicked - // TODO: Make sure this only happens when THIS BOT is kicked! - if (data.indexOf("KICK #") == data.indexOf(" ")+1) { - source = data.substring(data.indexOf("#"), data.indexOf(" ", data.indexOf("#"))); - irc.write('JOIN ${source}\r\n'); - } - - // Shutdown command - if (data.toLowerCase().indexOf("!quit") != -1) { - irc.write('QUIT :Shutdown\r\n'); + // Respond to server pings, but only print the first to avoid console spam + if (data.indexOf("PING") == 0) { + irc.write('PONG${data.substr(data.indexOf(" "))}\r\n'); + if (!Global.ping) {Sys.println('${data} (No further notifications)');Global.ping = true;} + } + + // Print everything that's not a server ping + if (data.indexOf("PING") != 0) {Sys.println(data);} + + // Begin processing data + if (StringTools.contains(data.substring(0, data.indexOf(" ")), "!")) { + var sender = data.substring(0, data.indexOf("!")); + var hostmask = data.substring(data.indexOf("!")+1, data.indexOf(" ")); + // var action = data.substring(data.indexOf(" ")+1, data.indexOf(" ", data.indexOf(" ")+1)); // Works but not very extensible + var action = data.substring(data.indexOf(" ")+1, data.indexOf(" ", data.indexOf(hostmask)+hostmask.length+1)); + var channel = data.substring(data.indexOf(" ", data.indexOf(action))+1, data.indexOf(" ", data.indexOf(action)+action.length+1)); + var msg = data.substring(data.indexOf(" ", data.indexOf(channel))+1); + if (msg.charAt(0) == ":") {msg = msg.substring(1);} // Strip leading colons + + // Auto-Rejoin when kicked + if ((action == "KICK") && (msg.substring(0, msg.indexOf(" ")) == botnick)) { + irc.write('JOIN ${channel}\r\n'); + } + + + // HxBot chat responses begin here + if (action == "PRIVMSG") { + // Command processor + if (msg.charAt(0) == Global.prompt) { + var command = ""; + + if (msg.indexOf(" ") != -1) { + // Separate command from rest of message, if necessary + command = msg.substring(1, msg.indexOf(" ")).toLowerCase(); + msg = msg.substring(msg.indexOf(" ")+1); + } else { + // Otherwise, remove msg + command = msg.substring(1).toLowerCase(); + msg = ""; + } + + switch (command) { + case "quit": + if (Global.authorized.contains(sender)) {irc.write('QUIT :Received shutdown command from ${sender}\r\n');} + case "join": + if ((Global.authorized.contains(sender)) && (msg.length > 0)) {join(msg);} + case "part": + if ((Global.authorized.contains(sender)) && (msg.length > 0)) {part(msg);} + case "chk": + say(channel, '${sender}: ack'); + case "slap": + if (msg.length != 0) {ctcp('PRIVMSG', channel, 'ACTION slaps ${msg} around a bit with a large trout');} + } + } + + // Non-command responses + /*switch (msg.substring(0, msg.indexOf(" ")).toLowerCase()) { + case "hi" | "hi," | "hello" | "hello," | "hey" | "hey," | "yo" | "yo," | "'lo" | "'lo,": // This could be a Global array + if (msg.substring(msg.indexOf(" ")+1, msg.indexOf(" ")+botnick.length+1).toLowerCase() == botnick.toLowerCase()) { + say(channel, 'Hello, ${sender}!'); + } + }*/ + if (Global.greetings.contains(msg.substring(0, msg.indexOf(" ")).toLowerCase())) { + if (msg.substring(msg.indexOf(" ")+1, msg.indexOf(" ")+botnick.length+1).toLowerCase() == botnick.toLowerCase()) { + say(channel, 'Hello, ${sender}!'); + } + } + // if (msg.toString() == 'ACTION slaps ${botnick} around a bit with a large trout') {} // This doesn't work apparently + if (msg.contains('slaps ${botnick} around a bit with a large trout')) { + say(channel, "This is the Trout Protection Agency. Please put the Trout down and walk away with your hands in the air."); + } + if ((msg.charAt(0) == ".") && (msg.length == 1)) {say(channel, "omg a meteor");} + } + } + } catch (e:haxe.io.Eof) { + Sys.println(""); + Sys.println('[Eof] Connection terminated.'); + return; + } catch (e) { + trace('Uncaught: ${e}'); // throw e; + return; } } - Sys.println(""); - Sys.println("Connection terminated."); } +} + +// Not strictly necessary but I wanted to test feasibility +class Global { + static public var authorized = ["Izwzyzx", "Ikewise"]; + static public var prompt = "?"; + static public var ping = false; + static public var greetings = ["hi", "hi,", "hello", "hello,", "hey", "hey,", "yo", "yo,", "'lo", "'lo,"]; } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..670d0a2 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# HxBot +**HxBot** is an IRC bot written in Haxe. It is the successor project to [PyBot](https://forge.ds8.zone/Izwzyzx/PyBot) and [GimelBot](https://forge.ds8.zone/Izwzyzx/GimelBot). + +The success of HxBot quickly led to the start of [onequestionmark-chat](https://forge.ds8.zone/Izwzyzx/onequestionmark-chat), which directly uses HxBot's command processor. + +## Changelog +### HxRawBot v1.1 +- ✔️ Cleaned code +- ✔️ Added exception catching +- ✔️ Changed command prefix to `?` +- ✔️ Added command processor +- ✔️ Channels joined on startup are now an array +- ✔️ Implemented functions +- ✔️ Implemented all chat commands from PyBot +- ✔️ Fix: Only accepts `?quit` from authorized users +- ✔️ Fix: No longer attempts rejoin when others are kicked + +### HxRawBot v1.0 +- ✔️ Connects to specified IRC server +- ✔️ Joins #lobby +- ✔️ Prints received messages to console +- ✔️ Responds to server pings +- ✔️ Rejoins channel when kicked +- ✔️ Responds to `!quit` command +- ❌ ~~Does not detect connection loss~~ (Haxe interpreter bug) \ No newline at end of file