#!/usr/bin/env python2.6 from ConfigParser import ConfigParser, NoSectionError, NoOptionError from datetime import datetime import os import re import sys from dateutil.parser import * from dateutil.relativedelta import * from dateutil.tz import * import irclib ##### # sub_join_channel # join a channel when told to by an admin ##### def sub_join_channel(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'join' and admin_unlocked and len(whats) >= 2: channel = whats[1] if irclib.is_channel(channel): connection.join(channel) connection.privmsg(replypath, 'joined ' + channel) ##### # sub_part_channel # leave a channel when told to by an admin. optionally provide a message ##### def sub_part_channel(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'part' and admin_unlocked and len(whats) >= 2: channel = whats[1] if irclib.is_channel(channel): connection.part(channel, ' '.join(whats[2:])) connection.privmsg(replypath, 'parted ' + channel) ##### # sub_quit_channel # quit irc server. optionally provide a message ##### def sub_quit_channel(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'quit' and admin_unlocked: connection.privmsg(replypath, 'quitting') connection.quit(' '.join(whats[1:])) with open('dr.botzo.cfg', 'w') as cfg: config.write(cfg) ##### # sub_handle_autojoin # add/remove on channel autojoin list. ##### def sub_autojoin_manipulate(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'autojoin' and admin_unlocked and len(whats) >= 3: if whats[1] == 'add': try: # get existing list channel = whats[2] if irclib.is_channel(channel): channelset = set(config.get('channels', 'autojoin').split(',')) channelset.add(channel) config.set('channels', 'autojoin', ','.join(channelset)) connection.privmsg(replypath, 'added ' + channel + ' to autojoin') except NoOptionError: pass elif whats[1] == 'remove': try: # get existing list channel = whats[2] if irclib.is_channel(channel): channelset = set(config.get('channels', 'autojoin').split(',')) channelset.discard(channel) config.set('channels', 'autojoin', ','.join(channelset)) connection.privmsg(replypath, 'removed ' + channel + ' from autojoin') except NoOptionError: pass ##### # sub_add_to_seen # when someone says a pubmsg, keep it in the config ##### def sub_add_to_seen(connection, event, nick, userhost, what): if not config.has_section('seen'): config.add_section('seen') config.set('seen', nick, userhost + '|:|' + datetime.utcnow().isoformat() + '|:|' + what) ##### # sub_report_seen # report when a person has been seen, based on config ##### def sub_report_seen(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'seen' and len(whats) >= 2: query = whats[1] try: seendata = config.get('seen', query).split('|:|') converted = datetime.strptime(seendata[1], "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=tzutc()) connection.privmsg(replypath, 'last saw ' + query + ' at ' + converted.astimezone(tzlocal()).strftime("%Y/%m/%d %H:%M:%S %Z") + ' saying \'' + seendata[2] + '\'') except NoOptionError: pass ##### # sub_save_config # save the config file ##### def sub_save_config(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'save' and admin_unlocked: with open('dr.botzo.cfg', 'w') as cfg: config.write(cfg) connection.privmsg(replypath, 'saved config file') ##### # sub_change_nick # change the bot's nickname ##### def sub_change_nick(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'nick' and admin_unlocked and len(whats) >= 2: newnick = whats[1] connection.nick(newnick) config.set('IRC', 'nick', newnick) connection.privmsg(replypath, 'changed nickname') ##### # sub_add_countdown # add a countdown item to the bot ##### def sub_add_countdown(connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'countdown' and len(whats) >= 4: if whats[1] == 'add': item = whats[2] target = parse(' '.join(whats[3:]), default=datetime.now().replace(tzinfo=tzlocal())) if not config.has_section('countdown'): config.add_section('countdown') config.set('countdown', item, target.astimezone(tzutc()).isoformat()) ##### # on_connect # handler for when the bot has connected to IRC ##### def on_connect(connection, event): # user modes try: usermode = config.get('IRC', 'usermode') connection.mode(botnick, usermode) except NoOptionError: pass # join the specified channels try: autojoins = config.get('channels', 'autojoin').split(',') for channel in autojoins: if irclib.is_channel(channel): connection.join(channel) except NoOptionError: pass ##### # on_privmsg # private messages to the bot ##### def on_privmsg(connection, event): nick = irclib.nm_to_n(event.source()) userhost = irclib.nm_to_uh(event.source()) replypath = nick what = event.arguments()[0] admin_unlocked = False try: if userhost == config.get('admin', 'userhost'): admin_unlocked = True except NoOptionError: pass # admin commands sub_join_channel(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_part_channel(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_quit_channel(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_autojoin_manipulate(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_save_config(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_change_nick(connection, event, nick, userhost, replypath, what, admin_unlocked) # standard commands sub_report_seen(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_add_countdown(connection, event, nick, userhost, replypath, what, admin_unlocked) ##### # on_pubmsg # public messages in a channel where the bot is ##### def on_pubmsg(connection, event): nick = irclib.nm_to_n(event.source()) userhost = irclib.nm_to_uh(event.source()) replypath = event.target() what = event.arguments()[0] admin_unlocked = False try: if userhost == config.get('admin', 'userhost'): admin_unlocked = True except NoOptionError: pass sub_add_to_seen(connection, event, nick, userhost, what) # only do commands if the bot has been addressed directly addressed_pattern = '^' + connection.get_nickname() + '[:,]?\s+' addressed_re = re.compile(addressed_pattern) if not addressed_re.match(what): return else: what = addressed_re.sub('', what) # admin commands sub_join_channel(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_part_channel(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_quit_channel(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_autojoin_manipulate(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_save_config(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_change_nick(connection, event, nick, userhost, replypath, what, admin_unlocked) # standard commands sub_report_seen(connection, event, nick, userhost, replypath, what, admin_unlocked) sub_add_countdown(connection, event, nick, userhost, replypath, what, admin_unlocked) ##### # init ##### # read config file config = ConfigParser({'debug': 'false'}) config.read([os.path.expanduser('~/.dr.botzo.cfg'), 'dr.botzo.cfg']) # load necessary options try: # load connection info botserver = config.get('IRC', 'server') botport = config.getint('IRC', 'port') botnick = config.get('IRC', 'nick') botircname = config.get('IRC', 'name') except NoSectionError as e: sys.exit("Aborted due to error with necessary configuration: " + str(e)) except NoOptionError as e: sys.exit("Aborted due to error with necessary configuration: " + str(e)) # load additional options irclib.DEBUG = config.getboolean('IRC', 'debug') # start up the IRC bot # create IRC and server objects and connect irc = irclib.IRC() server = irc.server().connect(botserver, botport, botnick, botircname) # install handlers server.add_global_handler("welcome", on_connect) server.add_global_handler('privmsg', on_privmsg) server.add_global_handler('pubmsg', on_pubmsg) # loop forever irc.process_forever() # vi:tabstop=4:expandtab:autoindent