package org.symcomp.wupsi.handlers; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Collections; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.schwering.irc.lib.IRCConnection; import org.schwering.irc.lib.IRCEventListener; import org.schwering.irc.lib.IRCModeParser; import org.schwering.irc.lib.IRCUser; import org.symcomp.openmath.OpenMathBase; import org.symcomp.scscp.SCSCPClient; import org.symcomp.wupsi.Wupsifer; /** * * @author hornp */ public class IrcHandler extends WupsiHandler { private Wupsifer w; private List<WupsiIrcConnect> wic; public IrcHandler(Wupsifer w) { this.w = w; this.wic = Collections.synchronizedList(new LinkedList<WupsiIrcConnect>()); } public String command() { return "irc "; } public void handle(String in) { Pattern p = Pattern.compile("irc( )+" + "to( )+(([0-9A-Za-z\\_\\-\\.]+)(\\:([0-9]+))?)( )+" + "channel( )+([#0-9A-Za-z\\_\\-\\.]+)( )*" + "(nick ([A-Za-z][0-9A-Za-z\\_]*))?( )*" + "(system ([A-Za-z][0-9A-Za-z\\_]*))?"); Matcher m = p.matcher(in); if (!m.matches()) { w.error("# syntax: " + help()); return; } // The parsing result is: // Group 0: irc to irc.freenode.net:6667 channel #muff nick hornp system maple // Group 1: // Group 2: // Group 3: irc.freenode.net:6667 // Group 4: irc.freenode.net // Group 5: :6667 // Group 6: 6667 // Group 7: // Group 8: // Group 9: #muff // Group 10: // Group 11: nick hornp // Group 12: hornp // Group 13: // Group 14: system maple // Group 15: maple // assemble the parameters String host = m.group(4); int port = m.group(6) == null ? 6667 : Integer.parseInt(m.group(6)); String channel = m.group(9); String system = m.group(15) == null ? w.getActiveClient() : m.group(15); String nick = m.group(12) == null ? "wupsi" + system : m.group(12); //Get the client SCSCPClient client = w.getClient(system); if (client == null) { w.error("# Client with id '" + system + "' does not exist."); return; } wic.add(new WupsiIrcConnect(w, host, port, channel, nick, client)); } public String help() { return "Attaches to an irc server:\nirc to <host>:<port> channel #<channel> nick <nick> system <systemid>"; } @Override public void shutdownHook() { for (WupsiIrcConnect c : wic) { c.shutdown(); } } @Override public void systemDiedHook(SCSCPClient client) { for (int i = wic.size() - 1; i >= 0; --i) { WupsiIrcConnect c = wic.get(i); if (c.client != null && c.client == client) { w.print("# Client died; shutting down IRC connection to " + c.desc()); c.systemdied(); wic.remove(i); } } } /** * Treats IRC events. The most of them are just printed. */ public class WupsiIrcConnect implements IRCEventListener { Wupsifer w; String host; int port; String channel; String nick; SCSCPClient client; IRCConnection conn; Random rng; String[] answers = new String[]{ "I think that's %s.", "Isn't that %s?", "That's %s, isn't it?", "hmmm... %s?", "That's roughly %s, I guess." }; String[] errors = new String[]{ "I'm sorry, I don't know!", "I have no idea what you are talking about :(", "Keine Ahnung!", "Ermmmm... I don't really know... Have you tried Google?" }; String[] leavemsgs = new String[] { "I'm leaving now, see you.", "I'm sorry, I must be off now!", "kthxbye", "That's all, folks!" }; String[] diedmsgs = new String[] { "You know when you're falling asleep, and you imagine yourself walking or something, and suddenly you misstep, stumble, and jolt awake? Well, that's what a segfault feels like." }; String[] parseerrors = new String[]{ "I'm sorry -- that question, I could not parse. Please learn to speak the Popcorn language!" }; public String desc() { return host+":"+port+", channel "+channel+", nick "+nick; } public WupsiIrcConnect(Wupsifer w, String host, int port, String channel, String nick, SCSCPClient client) { this.rng = new Random(); this.w = w; this.host = host; this.port = port; this.channel = channel; this.nick = nick; this.client = client; w.print("# Connecting to IRC server "+desc()); // initialize the irc client //IRCConnection(String host, int portMin, int portMax, String pass, String nick, String username, String realname) conn = new IRCConnection(host, new int[] { port }, null, nick, nick, "SCIEnce Wupsi"); conn.addIRCEventListener(this); conn.setEncoding("UTF-8"); conn.setPong(true); conn.setDaemon(false); conn.setColors(false); // connect try { conn.connect(); } catch (IOException ex) { Logger.getLogger(IrcHandler.class.getName()).log(Level.SEVERE, null, ex); } } public void shutdown() { conn.doPrivmsg(channel, leavemsgs[rng.nextInt(leavemsgs.length)]); conn.doQuit(); } public void systemdied() { conn.doPrivmsg(channel, diedmsgs[rng.nextInt(diedmsgs.length)]); conn.doQuit(); } public void onRegistered() { w.print("# Successfully connected to server, joining "+channel); try { Thread.sleep(1000); } catch (InterruptedException ignore) { } conn.doJoin(channel); } public void onDisconnected() { } public void onError(String msg) { } public void onError(int num, String msg) { } public void onInvite(String chan, IRCUser u, String nickPass) { } public void onJoin(String chan, IRCUser u) { } public void onKick(String chan, IRCUser u, String nickPass, String msg) { } public void onMode(IRCUser u, String nickPass, String mode) { } public void onMode(String chan, IRCUser u, IRCModeParser mp) { } public void onNick(IRCUser u, String nickNew) { } public void onNotice(String target, IRCUser u, String msg) { } public void onPart(String chan, IRCUser u, String msg) { } public void onPrivmsg(String chan, IRCUser u, String msg) { //If chan is equal to my own nick, it was a private message. boolean justme = chan.equals(nick); //Try to parse, OpenMathBase omb; try { omb = OpenMathBase.parse(msg); } catch (Exception ex) { //Apparently, no correct question. if (justme) { conn.doPrivmsg(u.getNick(), parseerrors[rng.nextInt(parseerrors.length)]); } return; } //Try to compute, if (null == client) return; OpenMathBase result = w.compute(client, omb); String answer; if (result.isError()) { answer = errors[rng.nextInt(errors.length)]; } else { String template = answers[rng.nextInt(answers.length)]; answer = String.format(template, result.toPopcorn()); } //and respond. if (justme) { //It was a private message, so we simply respond to the user. conn.doPrivmsg(u.getNick(), answer); } else { //Otherwise, we respond to the channel. conn.doPrivmsg(channel, answer); } } public void onQuit(IRCUser u, String msg) { } public void onReply(int num, String value, String msg) { } public void onTopic(String chan, IRCUser u, String topic) { } public void onPing(String p) { } public void unknown(String a, String b, String c, String d) { } } }