XMPPボット-グループチャットボットの作成

Last-modified: 2014-08-16 (土) 12:41:50 (2116d)

[ 前のページ : XMPPボット-辞書ボットの作成 | ]

メーリングリストをIMで再現します。

あるアカウントにIMを送信すると、そのメッセージは登録メンバー全員に送信されます。要するに、グループチャットです。

Windows Live メッセンジャーでは標準で使える機能なのですが、ejabberd + Pidgin ではこの機能は無いようなので(ただの調査不足か?)、ボットで実現します。

  • 相互にメンバーリストの登録が必要ない (ボットのみメンバーリストに登録してあればよい)
  • 相手側全員がログイン状態になっている必要がない
  • 一元管理されたログが作成できる

などのメリット(?)があります。

ついでに、他のボットへの連携、ログ検索機能も考えてみます。

xmpp-groupmessage.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

#
# XMPP (例:GoogleTalk)用グループチャットデーモン
# ytyng.com
#
# 要は、IM版メーリングリスト、のようなもの。
#


import xmpp
import time
import config as CONFIG

latestMessage=""; #前回メッセージを一時保存する。メッセージのループを回避するため。

#
# XMPPメッセージパーサ
#
def parseMessage(conn,mess):
	
	global latestMessage
	
	text=mess.getBody()
	
	if text is None : return
	if latestMessage == text :
		#print "#### Message loop occurred" #無限ループ回避
		return
	
	senderFull = unicode(mess.getFrom())
	if senderFull.find('/')+1: sender,resource=senderFull.split('/',1)
	else: sender,resource=senderFull,''
	
	#print "#### sender =",sender
	#print "#### senderFull =",senderFull
	#print "#### text =",text
	
	command=""
	args=""
	if text.find(' ')+1: command,args=text.split(' ',1)
	
	#ログサーチ
	if command == "SEARCH" and args and not args.isspace():
		result = seachLog(args.encode('utf-8','ignore'))
		conn.send(xmpp.Message(senderFull,result))
		return
	
	#ボット向けメッセージの場合
	for key in CONFIG.bots.keys() :
		if command == key:
			if sender != CONFIG.bots[key]:
				print "#### BOTsend =",CONFIG.bots[key]
				conn.send(xmpp.Message(CONFIG.bots[key],args))
	
	replyMessage = "["+senderFull + "]\n"+text
	
	for member in CONFIG.members:
		if sender != member:
			print "#### sendto =",member
			conn.send(xmpp.Message(member,replyMessage))
			
	logText = text
	logText = logText.replace("\r\n","  ")
	logText = logText.replace("\r","  ")
	logText = logText.replace("\n","  ")
	logText = logText.replace("\t","  ")
	writeLog(sender+"\t"+resource+"\t"+logText)
	
	latestMessage = replyMessage

#
# ログファイルに書き出し
#
def writeLog(message):
	fp = open(CONFIG.chatLogFile,'a') #file()でも同じ(?)
	fp.write(time.strftime("%Y-%m-%d %H:%M:%S")+"\t")
	message = message.encode('utf-8')
	fp.write(message)
	fp.write("\n")
	fp.close()

#
# ログ内を検索
# grep型全走査
#
def seachLog(word):
	word = word.lower()
	wordList = word.split(' ')
	buffer = ""
	fp = open(CONFIG.chatLogFile,'r')
	try:
		for line in fp:
			lLine = line.lower()
			isHit = False
			for word in wordList:
				if word.isspace() : continue
				#検索文字が無ければブレーク
				if lLine.find(word) +1 :
					isHit = True
				else :
					isHit = False
					break
			if isHit :
				buffer += line
	finally:
		fp.close()
	return buffer


class ConnectionError: pass
class AuthorizationError: pass

#
# XMPPボット基本クラス
#
class Bot:
	def __init__(self, JID, Password,Server,Port):
		jid = xmpp.JID(JID)
		self.connection = xmpp.Client(jid.getDomain(), debug=[])
		
		result = self.connection.connect(server=(Server,Port))
		
		if result is None:
			raise ConnectionError
		result = self.connection.auth(jid.getNode(), Password)
		
		if result is None:
			raise AuthorizationError
		
		self.connection.RegisterHandler('message',parseMessage)
		self.connection.sendInitPresence() # login
		
	def loop(self):
		try:
			while self.connection.Process(1):
				pass
		except KeyboardInterrupt:
			pass

bot = Bot(**CONFIG.account)
bot.loop()

config.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

#デーモンアカウント
#このアカウントにIMを送ると、メンバーアカウント全てに転送される。
account = {
	'JID'     : 'group01@example.com',
	'Password': 'PASSWORD',
	'Server'  : '127.0.0.1',
	'Port'    : 5222,
}

#メンバーアカウント
#あらかじめ、デーモンアカウントと承認関係を作っておくこと。
#承認関係に無いと、最悪メッセージがループしてしまう可能性があるので注意。
members = (
	"user01@example.com",
	"user02@example.com",
	"user03@example.com",
)

#ボットアカウント
#通常メッセージはこのアカウントには送信しない。
#メッセージがこのキーで始まる場合、このアカウントにも送信する。
#例: DICT foo →辞書ボットでfooを検索
bots = {
	'DICT' : "dictionary-bot@example.com",
	'STAT' : "status-bot@example.com",
}

#その他設定
chatLogFile = "chat.log"

実行結果

groupmessage.png

メッセージを送信すると、メンバー全員にそのメッセージが送信されます。

STAT なんとか と送信すると、先回作成したステータス表示ボットに処理を渡し、結果を応答します。

DICT なんとか と送信すると、先回作成した辞書ボットに処理を渡し、検索させます。

SEARCH なんとか と送信すると、ログファイル中からマッチする行を探して返答します。ログ検索は、他メンバーには見えません。