IRCボットの実装で見るJava言語
ネットワーク対戦ゲームの募集文をNOTICE発言するボットをJavaで実装してみた。IRCライブラリは自作のIRCKitを使用しました。
ボットのソースコードは次のとおりです。
/* * Copyright (c) 2009 tarchan. All rights reserved. */ package com.mac.tarchan.ircbox; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import com.mac.tarchan.net.irc.client.IRCClient; import com.mac.tarchan.net.irc.client.IRCMessage; import com.mac.tarchan.net.irc.client.Reply; /** * GameOffer * * @author tarchan */ public class GameOffer { /** * @param args 設定ファイル名 */ public static void main(String[] args) { try { new GameOffer(args[0]); } catch (IOException x) { x.printStackTrace(); } } /** IRCクライアント */ private IRCClient irc; /** * GameOffer を構築します。 * * @param name 設定ファイル名 * @throws IOException 入出力エラーが発生した場合 */ public GameOffer(String name) throws IOException { irc = new IRCClient().load(name).addAllHandlers(this); irc.open(); } /** * メッセージを解析します。 * * @param msg IRCメッセージ */ @Reply("PRIVMSG") public void onMessage(IRCMessage msg) { String input = msg.getTrail(); if (input.matches("^[1-9]\\d{3,4}$")) { postGameOffer(msg); } else if (input.matches("^good night, jewel.$")) { postQuit(msg); } } /** 募集中メッセージを送信します。 */ private void postGameOffer(IRCMessage msg) { String channel = msg.getParam(0); // msg.channel String nick = msg.getPrefix().getNick(); // msg.prefix.nick String host = msg.getPrefix().getHost(); // msg.prefix.host String port = msg.getTrail(); // msg.trail host = dnsLookup(host); irc.postMessage(String.format("NOTICE %s :%s さんが %s:%s で募集中だよ。", channel, nick, host, port)); } /** QUITコマンドを送信します。 */ private void postQuit(IRCMessage msg) { irc.quit("good bye."); } /** ホスト名に対応するIPアドレスを取得します。 */ private String dnsLookup(String host) { try { InetAddress address = InetAddress.getByName(host); return address.getHostAddress(); } catch (UnknownHostException x) { x.printStackTrace(); return host; } } }
スクリプト言語と比べて思ったこと
Javaの正規表現はString#matches()メソッドで提供されているんだけど、「\\d」とかエスケープシーケンスが重複すると混乱する。
- フォーマット文字列に直接format()メソッドが書きたい
PHPみたいに変数をフォーマット文字列に含ませるのはやりすぎかと思うけど、せめてformatメソッドが書きたい。
普通は次のように書くところを...
postMessage(String.format("NOTICE %s :%s さんが %s:%s で募集中だよ。", channel, nick, host, port));
次のように書きたいと思った。
postMessage("NOTICE %s :%s さんが %s:%s で募集中だよ。".format(channel, nick, host, port));
ちなみに、PHPだとこうなる...
postMessage("NOTICE $channel :$nick さんが $host:$port で募集中だよ。");
引数を整理するだけなら、オーバーロードで可変引数付きのメソッドを宣言しておけば、
public void postMessage(String format, Object... args);
すっきりは書けるけど、これだとコンパイル時にフォーマット文字列のエラーが検出されないので却下。FindBugs++
postMessage("NOTICE %s :%s さんが %s で募集中だよ。", channel, nick, host, port);
- リスト代入が欲しい
IRCプロトコルを扱うプログラムは、文字列を刻んで複数の変数に代入することが多いので、リスト代入が欲しいと思った。
String[] tmp = "nick!user@host".split("[!@]"); nick = tmp[0]; user = tmp[1]; host = tmp[2];
リスト代入を使って書くと次のようになる。
list(nick, user, host) = "nick!user@host".split("[!@]");