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("[!@]");