## page was renamed from zhArticleTemplate ##language:zh #pragma section-numbers on {{{#!python #coding=utf-8 #!/usr/bin/env python # confbot -- a conference bot for google talk. # Copyright (C) 2005 Perry Lorier (aka Isomer) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Modified by limodou 2005/09/05 # * 进行汉化处理 # * 当出现网络中断是不再退出,而是自动重联 # * 增加config.py,将配置分离 # * 增加是否记录日志标志recordflag # * 增加日志保存路径设置logpath # * 可自定义系统信息提示符 system_prompt # * 将欢迎信息独立出来,可以方便修改welcome # * 将用户名可以使用不同的包括字符处理,帮助显示name_quote_begin和name_quote_end # * 完善表情串emotes # * 增加管理员命令reload,用于重新装入配置信息,这样可以动态进行修改,对于logpath无效 # # You will want to change these: import config # You shouldn't have to change anything below this line -------------------- import socket import jabber import xmlstream import sys import time import random import traceback import urllib import os.path version='1.2' commandchrs = '/)' #打开日志文件 logf用于记录聊天日志 xmllogf用于记录xml消息 #每运行一次bot,创建一个新的日志文件 if config.recordflag: logf = open(os.path.join(config.logpath, time.strftime("%Y%m%d%H%M%S.log")),"w") xmllogf = open("xmllog","w") last_activity=time.time() #xmllogf = sys.stderr #打开管理员列表文件 try: adminfile = open("adminlist.txt","r") admins=[i.strip() for i in adminfile.readlines()] adminfile.close() except: print "Could not open admin file, creating a new one" con=None def getdisplayname(x): "显示一个用户名,去掉/后面的东西和@gmail.com" x=unicode(x) if '/' in x: x=x[:x.find("/")] if '@' in x and x[x.find('@'):]=="@gmail.com": x=x[:x.find("@")] return x def getjid(x): "根据显示名得到一个完整的gmail帐号" if '@' not in x: x=x+"@gmail.com" return x def saveadminlist(): "保存管理员列表到文件中" a=open("adminlist.txt","w") for i in admins: print >>a,i a.close() def sendtoone(who,msg): "向一个用户发送消息" m = jabber.Message(who,msg) m.setType('chat') con.send(m) def sendtoall(msg,butnot=[],including=[]): "向所有用户发送消息, 列在butnot中的用户不发送消息,including为必发消息名单列表" "如果用户的状态为'available', 'chat', None则可以发送消息,如果为busy则不会收到消息" r = con.getRoster() if config.recordflag: print >>logf,time.strftime("%Y-%m-%d %H:%M:%S"),msg.encode("utf-8") logf.flush() #print time.strftime("%Y-%m-%d %H:%M:%S"),msg.encode("utf-8") for i in r.getJIDs(): if getdisplayname(i) in butnot: continue state=r.getShow(unicode(i)) if state in ['available','chat',None] or getdisplayname(i) in including: sendtoone(i,msg) time.sleep(.1) statuses={} suppressing=1 def sendstatus(who,txt,msg): "群发某人的状态" who = getdisplayname(who) if statuses.has_key(who) and statuses[who]==txt: return if txt == 'busy': txt = u'忙碌' statuses[who]=txt if not statuses.has_key(who): # Suppress initial status return if suppressing: return if msg: sendtoall(config.system_prompt + u'%s %s (%s)' % (who,txt,msg),including=[who]) else: sendtoall(config.system_prompt + u'%s %s' % (who,txt),including=[who]) def boot(jid): "将某人从聊天室中去掉" con.send(jabber.Presence(to=jid, type='unsubscribe')) con.send(jabber.Presence(to=jid, type='unsubscribed')) if statuses.has_key(getdisplayname(jid)): del statuses[getdisplayname(jid)] def cmd(who,msg): "聊天指令处理" if " " in msg: cmd,msg=msg.split(" ",1) else: cmd,msg=msg.strip(),"" if cmd[:1] in commandchrs: cmd=cmd[1:] if cmd in ["me"]: if msg.strip()=="": action=random.choice(config.emotes.keys()) sendtoone(who, config.system_prompt + u'用法: /me <表情串>\n表现你的一种表情。\n' u'例如 "/me %(action)s" 将表示为 "* %(nick)s %(emote)s" <消息>' % { "nick" : getdisplayname(who), "action" : action, "emote" : config.emotes[action] }) else: if " " in msg: action, msg = msg.split(" ", 1) else: action, msg = "", msg sendtoall('%s%s%s _%s_ %s' % (config.name_quote_begin, getdisplayname(who), config.name_quote_end, config.emotes.get(action, ""), msg),butnot=[getdisplayname(who)]) elif cmd in ["help"]: sendtoone(who, config.system_prompt + u"""命令列表: /help 显示本帮助信息 /me <emote> <msg> 设置表情串 /names 显示聊天室人名 /quit <msg> 退出聊天室,一旦退出需要重新加入 /msg <nick> <msg> 私聊""") if who.getStripped() in admins: sendtoone(who, config.system_prompt + u'''管理员命令列表: /die 关闭聊天室 /addadmin <nick> 增加管理员 /deladmin <nick> 删除管理员 /kick <nick> 踢除某个人 /reload 重新装入配置信息''') sendtoone(who, config.system_prompt + u'请访问 http://coders.meta.net.nz/~perry/jabber/confbot.php 了解更多内容\n' u'还可以访问 http://www.donews.net/limodou 的Blog了解本汉化修正版') elif cmd in ["names"]: r = con.getRoster() names=[] for i in r.getJIDs(): state=r.getShow(unicode(i)) name=getdisplayname(i) if i.getStripped() in admins: name="@%s" % name if state in ['available','chat',None]: names.insert(0,name) else: names.append('(%s)' % name) sendtoone(who, config.system_prompt + u'名单:\n%s\n有@符的为管理员' % " ".join(names)) elif cmd in ["quit","leave","exit"]: if msg: msg = u' (%s)' % msg sendtoall(config.system_prompt + u'%s 已经退出%s' % (getdisplayname(who), msg)) boot(who.getStripped()) elif cmd in ['msg']: if not ' ' in msg: sendtoone(who, config.system_prompt + u'用法: /msg <对方名称> <消息>') else: target,msg = msg.split(' ',1) sendtoone(getjid(target),'%s%s%s 对你悄悄说: %s' % (config.name_quote_begin, getdisplayname(who),config.name_quote_end, msg)) sendtoone(who,'你对 %s%s%s 悄悄说: %s' % (config.name_quote_begin, getdisplayname(target), config.name_quote_end, msg)) elif cmd in ['kick','boot'] and who.getStripped() in admins: boot(getjid(msg)) sendtoall(config.system_prompt + u'%s 被管理员踢掉了' % msg.strip()) elif cmd in ['addadmin'] and who.getStripped() in admins: admins.append(getjid(msg.strip())) sendtoone(who, config.system_prompt + u'把 %s 加为管理员' % getjid(msg.strip())) sendtoone(getjid(msg.strip()), config.system_prompt + u'%s 已经把你加为管理员' % getdisplayname(who)) saveadminlist() elif cmd in ['deladmin'] and who.getStripped() in admins: if getjid(msg.strip()) in admins: admins.remove(getjid(msg.strip())) sendtoone(who, config.system_prompt + u'把 %s 从管理员中删除' % getjid(msg.strip())) sendtoone(getjid(msg.strip()), config.system_prompt + u'%s 把你从管理员中删除了' % getdisplayname(who)) saveadminlist() else: sendtoone(who, config.system_prompt + u'%s 不是一个管理员' % getjid(msg.strip())) elif cmd in ['die'] and who.getStripped() in admins: sendtoall(config.system_prompt + u'聊天室被 %s 关闭' % who.getStripped()) sys.exit(1) elif cmd in ['reload'] and who.getStripped() in admins: reload(config) sendtoone(who, config.system_prompt + u'重载配置成功') print 'reload config' else: sendtoone(who, config.system_prompt + u'不可识别的命令 %s' % cmd) def messageCB(con,msg): if msg.getError()!=None: if statuses.has_key(getdisplayname(msg.getFrom())): sendstatus(unicode(msg.getFrom()), u'离开',"Blocked") boot(msg.getFrom().getStripped()) elif msg.getBody(): if len(msg.getBody())>1024: sendtoall(config.system_prompt + u"%s 正在刷屏" % (getdisplayname(msg.getFrom()))) elif msg.getBody()[:1] in commandchrs: cmd(msg.getFrom(),msg.getBody()) else: global suppressing,last_activity suppressing=0 last_activity=time.time() sendtoall('%s%s%s %s' % (config.name_quote_begin, getdisplayname(msg.getFrom()), config.name_quote_end, msg.getBody()), butnot=[getdisplayname(msg.getFrom())], ) print 'status:',con.getRoster().getShow(msg.getFrom().getStripped()),msg.getFrom().getStripped() if con.getRoster().getShow(msg.getFrom()) not in ['available','chat',None]: sendtoone(msg.getFrom(), u'config.system_prompt 警告: 你已经在客户端标记为"忙(busy)",\n' u'你将不会收到其他人的谈话,在客户端将你自已\n' u'设为"在线(available)"才可以看到别人的回复') xmllogf.flush() # just so flushes happen regularly def presenceCB(con,prs): who = unicode(prs.getFrom()) type = prs.getType() # TODO: Try only acking their subscription when they ack ours. if type == 'subscribe': con.send(jabber.Presence(to=who, type='subscribed')) con.send(jabber.Presence(to=who, type='subscribe')) print "Subscribe from",who elif type == 'unsubscribe': boot(prs.getFrom().getStripped()) print "Unsubscribe from",who elif type == 'subscribed': sendtoone(who, config.welcome) sendstatus(who,u'在线', u'加入') elif type == 'unsubscribed': sendtoall(config.system_prompt + u'%s 已经退出' % getdisplayname(who)) elif type == 'available' or type == None: show = prs.getShow() if show in [None,'chat','available']: sendstatus(who, u'在线', prs.getStatus()) elif show in ['xa']: sendstatus(who, u'离开',prs.getStatus()) elif show in ['away']: sendstatus(who,u'离开',prs.getStatus()) elif show in ['dnd']: sendstatus(who,u'离开',prs.getStatus()) else: sendstatus(who,u'离开',show+" [[%s]]" % prs.getStatus()) elif type == 'unavailable': status = prs.getShow() sendstatus(who, u'离开', status) else: print "Unknown presence:",who,type def iqCB(con,iq): # reply to all IQ's with an error reply=None try: # Google are bad bad people # they don't put their query inside a <query> in <iq> reply=jabber.Iq(to=iq.getFrom(),type='error') stuff=iq._node.getChildren() for i in stuff: reply._node.insertNode(i) reply.setError('501','Feature not implemented') con.send(reply) except: traceback.print_exc() def disconnectedCB(con): sys.exit(1) reload(sys) sys.setdefaultencoding('utf-8') def connect(): con = jabber.Client(host=config.server,debug=False ,log=xmllogf, port=5223, connection=xmlstream.TCP_SSL) con.connect() con.setMessageHandler(messageCB) con.setPresenceHandler(presenceCB) con.setIqHandler(iqCB) con.setDisconnectHandler(disconnectedCB) con.auth(config.account,config.password,config.resource) con.requestRoster() con.sendInitPresence() _roster = con.getRoster() for jid in _roster.getJIDs(): print jid,_roster.getOnline(jid),_roster.getStatus(jid),_roster.getShow(jid) return con con = connect() JID="%s@%s/%s" % (config.account,config.server,config.resource) last_update=0 saveadminlist() while 1: # We announce ourselves to a url, this url then keeps track of all # the conference bots that are running, and provides a directory # for people to browse. if time.time()-last_update>4*60*60: # every 4 hours args={ 'action':'register', 'account':"%s@%s" % (config.account,config.server), 'users':len(con.getRoster().getJIDs()), 'last_activity':time.time()-last_activity, 'version':version, 'topic':config.topic, } try: urllib.urlretrieve('http://coders.meta.net.nz/~perry/jabber/confbot.php?'+urllib.urlencode(args)) print "Updated directory site" except: print "Can't reach the directory site" traceback.print_exc() last_update = time.time() try: con.process(1) except KeyboardInterrupt: break except SystemExit: break except: traceback.print_exc() try: time.sleep(1) con = connect() except: traceback.print_exc() }}}