import xmpp, os, sys, time, traceback import threading from config import * VERSION = "0.2" MIN_CONNECT_INTERVAL=300 last_connect = 0 commands = dict() commands_private = dict() group_listeners = [] private_listeners = [] status_listeners = [] users = dict() STATUS_JOIN, STATUS_ONLINE, STATUS_OFFLINE, STATUS_RENAME, STATUS_XAWAY, STATUS_AWAY, STATUS_UNKNOWN = range(7) def getUsers(): """Return a dictionary of every user in the group, the values are status codes.""" return users def addCommand(name,help,func): """Add a groupchat command. @param name -- The name of the command, if the command keyword "!name" is used at the start of a message the given handler will be called @param help -- Help string displayed on with the !help command @param func -- Handler of the form func(msg,args) where msg is the xmpppy representation of the message sent and args is a list of arguments""" commands[name]=(help,func) def addPrivateCommand(name,help,func): """Add a private chat command. @param name -- The name of the command, if the command keyword "!name" is used at the start of a message the given handler will be called @param help -- Help string displayed on with the !help command @param func -- Handler of the form func(msg,args) where msg is the xmpppy representation of the message sent and args is a list of arguments""" commands_private[name]=(help,func) def addGroupListener(func): """Add a groupchat listener. @param func -- Handler of the form func(from,msg) where both arguments are unicode strings""" group_listeners.append(func) def addPrivateListener(func): """Adds a private chat listener. @param func -- Handler of the form func(from,msg) where both arguments are unicode strings""" private_listeners.append(func) def addStatusListener(func): """Adds a user status listener. @param func -- Handler of the form func(from,msg,status) where first two arguments are unicode strings and status is one of STATUS_* constants""" status_listeners.append(func) def sendMsg(to,body,mtype=None): """Send a message to the server. @param to -- Recipient of the message @param body -- Message body @param mtype -- Type of the message, (e.g. "groupchat" for group messages)""" mymsg = xmpp.protocol.Message(body=body) mymsg.setTo(to) if mtype!=None: mymsg.setType(mtype) cl.send(mymsg) def sendStyledMsg(to,body,mtype=None,style=""): """Send a CSS-styled message to the server. @param to -- Recipient of the message @param body -- Message body @param mtype -- Type of the message, (e.g. "groupchat" for group messages) @param style -- Inline CSS used to format the message""" mymsg = xmpp.protocol.Message(body=body) mymsg.setTo(to) if mtype!=None: mymsg.setType(mtype) c=mymsg.addChild('html', namespace='http://jabber.org/protocol/xhtml-im') c=c.addChild('body',namespace='http://www.w3.org/1999/xhtml') c=c.addChild('span',payload=body,attrs={'style':style}) cl.send(mymsg) def sendMonoMsg(to,body,mtype=None): """Send a mono-spaced styled message to the server (e.g. ASCII art). @param to -- Recipient of the message @param body -- Message body @param mtype -- Type of the message, (e.g. "groupchat" for group messages)""" mymsg = xmpp.protocol.Message(body=body) mymsg.setTo(to) mymsg.setType(mtype) c=mymsg.addChild('html', namespace='http://jabber.org/protocol/xhtml-im') c=c.addChild('body',namespace='http://www.w3.org/1999/xhtml') c=c.addChild('span', attrs={'style':'font-family: Courier New, monospace;'}) c=c.addChild('pre',payload=body,attrs={'style':'background-color: #ffffff;'}) cl.send(mymsg) def __help(msg,args): sendMsg(ROOM,"Available commands:",'groupchat') for cmd in commands: sendMsg(ROOM," !"+cmd+" - "+commands[cmd][0],'groupchat') sendMsg(ROOM,"Available private chat commands:",'groupchat') for cmd in commands_private: sendMsg(ROOM," !"+cmd+" - "+commands_private[cmd][0],'groupchat') def __messageCB(conn,msg): if msg.getType() == "groupchat": #print unicode(msg.getFrom()) +": "+ unicode(msg.getBody()) +" " +unicode(msg.getChildren()) body = unicode(msg.getBody()) mfrom = unicode(msg.getFrom()) if NAME in body and not room_nick in mfrom: sendMsg(ROOM,NAME+" --(brathuhn bot framework (v"+VERSION+"), type !help)",'groupchat') for cmd in commands: if body.startswith("!"+cmd) and not room_nick in mfrom: args = body.split() commands[cmd][1](msg,args) for listener in group_listeners: listener( unicode(msg.getFrom()), unicode(msg.getBody())) if msg.getType() == "chat": if msg.getBody() != None: body = unicode(msg.getBody()) mfrom = unicode(msg.getFrom()) for cmd in commands_private: if body.startswith("!"+cmd) and not room_nick in mfrom: args = body.split() commands_private[cmd][1](msg,args) for listener in private_listeners: listener( mfrom, body) raise xmpp.NodeProcessed def __presenceCB(conn,msg): #print "Status",msg.getFrom(), msg.getStatus(), msg.getStatusCode(), msg.getType() if msg.getType()=='unavailable': nick=msg.getNick() if nick==None: del users[msg.getFrom()] #Disconnect if not msg.getFrom() in users: for listener in status_listeners: listener(msg.getFrom(),msg,STATUS_OFFLINE) else: del users[msg.getFrom()] #Rename if not msg.getFrom() in users: for listener in status_listeners: listener(msg.getFrom(),msg,STATUS_RENAME) else: show = msg.getShow() if show==None: if not msg.getFrom() in users: for listener in status_listeners: listener(msg.getFrom(),msg,STATUS_JOIN) users[msg.getFrom()] = STATUS_ONLINE #Online else: #print msg.getFrom(),"is now",show if not msg.getFrom() in users: for listener in status_listeners: listener(msg.getFrom(),msg,STATUS_JOIN) if show=="away": users[msg.getFrom()] = STATUS_AWAY elif show=="xa": users[msg.getFrom()] = STATUS_XAWAY else: users[msg.getFrom()] = STATUS_UNKNOWN # status change status = STATUS_OFFLINE if msg.getFrom() in users: status = users[msg.getFrom()] for listener in status_listeners: listener(msg.getFrom(),msg.getStatus(),status) jid=xmpp.protocol.JID(node=NAME,domain=SRV,resource='bot') cl=xmpp.Client(jid.getDomain(),debug=[]) def connect_and_join(): """Connect to the server and join the group chat room from the config.""" global room_nick global room_to global last_connect last_connect=time.time() #connect to server con=cl.connect() if not con: print 'could not connect!' raise Exception('could not connect!') print 'connected' cl.RegisterHandler('message',__messageCB) cl.RegisterHandler('presence',__presenceCB) #authenticate auth=cl.auth(NAME,PWD,resource=jid.getResource()) if not auth: print 'could not authenticate!' raise Exception('could not authenticate!') print 'auth' room_to = "%s/%s" % (ROOM, NAME) room_nick = ROOM+"/"+NAME msg = xmpp.Presence(to=room_to) if PWD_ROOM != "": msg.addChild('x', namespace='http://jabber.org/protocol/muc').addChild('password',payload=PWD_ROOM) #add the help command addCommand("help","Prints this help message",__help) #join the room cl.send(msg) #set status cl.send(xmpp.Presence(to=room_to,status=NAME+" --(brathuhn bot framework (v"+VERSION+"), type !help)")) def loop(): global last_connect """Loop forever and handle the event-queue.""" while True: try: cl.Process(1) except xmpp.protocol.Conflict: print "detected another connection, quitting..." sys.exit(0) except KeyboardInterrupt: print "keyboard interrupt, quitting..." sys.exit(0) except: print "-------ERROR OCCURED-------" exc_type, exc_value, exc_traceback = sys.exc_info() traceback.print_tb(exc_traceback, file=sys.stdout) if not cl.connected: s=MIN_CONNECT_INTERVAL-(time.time()-last_connect) if s>0: time.sleep(s) connect_and_join() print "---------------------------" def callAt(func,_time,*args): s=_time-time.time() def run(): if s>0: time.sleep(s) func(*args) threading.Thread(target=run).start()