#!/usr/bin/python # -*- coding: UTF-8 -*- # $Id$ import re import time import xmpp import os import sys #import pyDaemon #import daemonize from optparse import OptionParser from jolem_api import JolemAPI class ConnectionError: pass class AuthorizationError: pass """ Jolem is a jabber golem that takes commands from users and performs appropriate actions. It is also responsible for logging presence changes and keeping track of presence states for multiple resources for a single jid. """ __version__ = "$Revision$" class Jolem: def __init__(self, config_file): """ Creates a new bot, connects to the server, and logs in. """ # API for storing information self.api = JolemAPI(config_file) # parse config self.config = {} self.config = self.api.getConfig() self.config['keepalive_timeout'] = 30 # connect to XMPP server self.jid = None self.connection = None self.roster = None self.xmpp_keepalive_time = time.time() self.__connectXMPP() def loop(self): """ Do nothing except handling new xmpp stanzas. """ try: while self.connection.Process(1): self.__xmpp_keepalive() pass except KeyboardInterrupt: pass def handleMessage(self, conn, event): """ Handles incoming messages from other users. """ user = event.getFrom() event_type = event.getType() if user.getStripped() == self.jid.getStripped(): return None if event_type in ['message', 'chat', None]: event_subject = event.getSubject() event_body = event.getBody() response_subject = None response_body = None # build a response subject by removing any preceeding 'Re: 's # and adding our own if event_subject is not None: response_subject = subject p = re.compile('^re:?\s*', re.I) response_subject = p.sub('', response_subject) response_subject = 'Re: ' + response_subject # print "Messge from User: " + user.getStripped() # print " type: %s" % event_type # print " subject: %s" % event_subject # print " body: %s" % event_body # print " rsubject: %s" % response_subject # TODO: determine command and make simple command selection # TODO: log command execution stats if re.match('help', event_body): response_body = self.__handleMessageHelp(event_subject, event_body) else: response_body = self.__handleMessageBlog(user, event_subject, event_body) response = event.buildReply(text=response_body) response.setType(event_type) if response_subject is not None: response.setSubject("Re: " + response_subject) self.connection.send(response) def handlePresence(self, conn, event): """ Handles incoming presence from other users. """ user = event.getFrom() type = event.getType() show = event.getShow() status = event.getStatus() if user.getStripped() == self.jid.getStripped(): return None # print "Presence from User: " + user.getStripped() + "/" + user.getResource() # print "Type %s" % (type) # print "Show %s" % (show) # print "Status %s" % (status) # status = online if type is None and show is None: # status = online # print "Online" self.api.addStatus(user, 'online', status) # status = * (away, xa, unavailable) elif type is None and show is not None: # status = * (away, xa, unavailable) # print "Online & %s" % (show) self.api.addStatus(user, show, status) # status = offline elif type == "unavailable": # status = offline # print "Offline" self.api.addStatus(user, 'offline', status) # subscription request elif type == "subscribe": self.roster.Authorize(user) self.roster.Subscribe(user) # print "Subscribing to %s" % (user.getStripped()) """ stuff """ # subscribed to user elif type == "subscribed": # add to roster # print "Subscribed to %s" % (user.getStripped()) """ stuff """ # unsubscribe request elif type == "unsubscribe": self.roster.Unauthorize(user) self.roster.Unsubscribe(user) # print "Unsubscribing from %s" % (user.getStripped()) """ stuff """ # unsubscribed to user elif type == "unsubscribed": # remove from roster # print "Unsubscribed from %s" % (user.getStripped()) """ stuff """ def __connectXMPP(self): """ Connects to XMPP server and sets up handlers. """ # connect self.jid = xmpp.JID(self.config['jid']) self.connection = xmpp.Client( self.jid.getDomain(), debug=[]) result = self.connection.connect() if result is None: raise ConnectionError # authorize result = self.connection.auth( self.jid.getNode(), self.config['password'], self.jid.getResource()) if result is None: raise AuthorizationError # message self.connection.RegisterHandler('message', self.handleMessage) # presence self.connection.RegisterHandler('presence', self.handlePresence) # presence self.roster = self.connection.sendInitPresence() def __xmpp_keepalive(self): """ Handles keeping the connnection alive """ if time.time() > (self.xmpp_keepalive_time + self.config['keepalive_timeout']): # print "Keeping connection alive" self.connection.send(xmpp.protocol.Message(self.jid.getNode(), ' ')) self.xmpp_keepalive_time = time.time() def __handleMessageHelp(self, subject, body): """ Handles an incoming message and displays help """ return_body = None # print "Got subj: %s" % subject # print "Got Body: %s" % body p = re.compile('help\s*(?P\w+)?', re.I) m = p.match(body) if m is not None: # print "help_cmd: %s" % m.group("help_cmd") if m.group("help_cmd") is None: return_body = "default help" elif re.match("log", m.group("help_cmd"), re.I): return_body = "log help" return return_body def __handleMessageBlog(self, jid, subject, body): """ Handles an incoming message and logs the message """ return_body = "Message Logged" # print "Got subj: %s" % subject # print "Got Body: %s" % body # find if the subject is set in the body p = re.compile('^Subject:\s+(?P.*)', re.I|re.M) m = p.search(body) if m is not None: subject = m.group("subject") # remove tags found from body body = p.sub('', body) # find any tags in the body tags = ['general'] p = re.compile('^Tags?:\s+(?P[\w\s,]+)', re.I|re.M) m = p.search(body) if m is not None: tag_list = m.group("tags") if tag_list is not None: tag_list = tag_list.strip() tags = re.split('\s*,\s*', m.group("tags")) # remove tags found from body body = p.sub('', body) # trim up the blog body = body.strip() # verify contents of blog if body is None \ or subject is None \ or jid is None: return_body = "Log message failed; subject, body, or username not found" return return_body # add the blog to the database self.api.addBlog(jid, subject, body, tags) return return_body def main(): """ Main execution starting point for this file """ parser = OptionParser() parser.add_option("-c", "--config", dest="config_file", default="jolem.xml", help="path to XML config file") parser.add_option("-f", "--fork", action="store_true", dest="fork", help="whether to fork into the background") (options, args) = parser.parse_args() # if options.fork is not None: # daemonize.daemonize() bot = Jolem(options.config_file) bot.loop() if __name__ == '__main__': main()