っていうか作ったよ。っていう話。それから日本語の情報が全くなくて苦労したから、ここにメモっておくよっていう話。
xmpppy とは、 XMPP のプロトコルを Python から使うためのパッケージのこと。http://xmpppy.sourceforge.net/ に詳しく書いてある。
XMPP とは、 Jabber っていう会社が作った仕様がオープンなプロトコルのこと。チャットとかメールとか、メッセージを交換するために使うためのプロトコル。 SMTP が何かに置き換わるとしたら、たぶんこれに置き換わる。っていうか置き換わってほしい。 XMPP は、有名なところでは Google Talk とか Pidgin がサポートしている。つまり XMPP のプロトコルでしゃべる bot を作れば、その bot と Google Talk を使ってお話できる!!そのボットのアドレスを twitter に登録しておけば、 bot が twitter を使ってしゃべってくれる。まぁ twitter は API があるから、それを定期的に動かせば済むけどね。
XMPP の詳しい仕様はXMPP RFCsに全部書いてある。でも、チャットする bot を作るだけだったら、RFC3921を見れば、だいたい大丈夫。その bot を xmpppy を使って書くんだったら、このエントリを見るだけでだいたい大丈夫。
まず、このプロトコルは C/S になってるから、サーバを決める必要がある。Google にアカウントを作ってもいいし、www.jabber.jpで作っても OK。ちなみに jabber.jp を管理してくれているのは、あの有名な KLab 株式会社 さんです。ありがとう!
アカウントを作ったら、次は xmpppy をインストールしよう。xmpppy を使うには pydns も必要だから、先に pydns をインストールする。インストールはいつもの python setup.py install でやってくれる。
インストールしたら、さっそく bot のスクリプトを書く。
import xmpp, sys
user = 'ユーザ名' # @gmail.com, @jabber.jp
pwd = 'パスワード'
cnx = xmpp.Client('ドメイン名', debug=[])
cnx.connect( server=('サーバ名',5222) )
def presenceCB(con, prs):
"""Called when a presence is recieved"""
who = str(prs.getFrom())
type = prs.getType()
if type == None: type = 'available'
# subscription request:
# - accept their subscription
# - send request for subscription to their presence
if type == 'subscribe':
con.send(xmpp.protocol.Presence(to=who, typ='subscribed'))
con.send(xmpp.protocol.Presence(to=who, typ='subscribe'))
# unsubscription request:
# - accept their unsubscription
# - send request for unsubscription to their presence
elif type == 'unsubscribe':
con.send(xmpp.protocol.Presence(to=who, typ='unsubscribed'))
con.send(xmpp.protocol.Presence(to=who, typ='unsubscribe'))
def messageCB(con, mess):
txt = mess.getBody()
reply = mess.buildReply(txt)
con.send(reply)
cnx.RegisterHandler("presence", presenceCB)
cnx.RegisterHandler("message", messageCB)
cnx.sendInitPresence()
while 1:
try:
cnx.Process(1)
except:
cnx.send(xmpp.protocol.Presence(typ="unavailable"))
cnx.disconnect()
sys.exit()
これがひな形。XMPP の RFC で MUST になっている部分は、これで満たしている(はず)。このスクリプトは、起動するとサーバに接続する。他のアカウントから話しかけられると、オウム返しする。スクリプトを止めるときは C-c する。
bot として使うためには、messageCB を適当な関数に置き換える。たとえば、 messageCB に
from urllib import urlopen, quote_plus
from lxml import etree
import xmpp, re
rename = {"euc":"eucjp"}
def urlread(url):
import re, codecs
page = urlopen(url)
charset = re.search("charset=(\w+)", page.info().getheader("Content-Type"))
if charset:
charset = charset.group(1)
return codecs.getdecoder(charset)(urlopen(url).read())[0]
else:
parser = etree.HTMLParser(remove_comments=True, remove_blank_text=True)
html_page = etree.parse(urlopen(url), parser)
root = html_page.getroot()
meta = root.xpath('.//meta[@http-equiv="Content-Type"]')
charset = re.search("charset=(\w+)", meta[0].attrib["content"])
if charset:
charset = charset.group(1)
if charset in rename.keys():
charset = rename[charset]
return codecs.getdecoder(charset)(urlopen(url).read())[0]
else:
return urlopen(url).read()
def extractDefinition(data):
from StringIO import StringIO
from lxml import etree
parser = etree.HTMLParser()
root = etree.parse(StringIO(data), parser).getroot()
resultList = root.xpath('//table[@id="resultList"]/tr/td/ul/li')
if len(resultList) < 10:
resultList = resultList[:-1]
else:
resultList = resultList[:10]
rtag = re.compile(u"<.*?>")
rspace = re.compile(u"(\s)+")
def rep(s):
return rspace.sub(r"\1", rtag.sub(" ", s))
res = []
for e in resultList:
res.append( rep( etree.tostring(e, encoding="utf-8")))
res = " ".join(res)
return res
def messageCB(conn, msg):
try:
txt = msg.getBody()
user = msg.getFrom()
if txt:
txt = quote_plus(txt.encode("utf-8"))
reply_msg = extractDefinition(urlread("http://eow.alc.co.jp/"+txt+"/UTF-8/"))
if len(reply_msg) > 0:
reply = msg.buildReply(reply_msg)
else:
reply = msg.buildReply('"'+txt+'"'+u'っていう単語は辞書にないよ。「もしかして機能」はまだ無いんだ。ごめんね。')
conn.send(reply)
except:
pass
こんなスクリプトを書いたら、話しかけられた単語を英辞郎で検索して、ヒットした単語を上から 10 個を答える bot になる。
私はこのスクリプトにログ機能を持たせた bot を動かしている。これを使って自分専用の「最近調べた単語リスト」を作って、で、あとでログをcat logfile | sort | uniq して眺めてみて、忘れてしまった単語があったらもう1度調べる。ってことをしばらくやった後で単語の出現頻度を調べると、「よく出てくるんだけど覚えられない単語」が浮かび上がってくる、という仕組み。これをウェブサービスにしようと思ってたけれど、今は忙しくてそれどころじゃないのでメモだけ書いて公開しておく。2月になって落ち着いたら作る予定。 Enjoy!