diff -uNr a/commands/bot_help.py b/commands/bot_help.py --- a/commands/bot_help.py false +++ b/commands/bot_help.py f437341526f6e4af91bc5e137dec97530d071778c4e682063d0abcb51881793aab75b1720b090d00b752f15620e47220952a662e0f10ee67a4054fde9048fb31 @@ -0,0 +1,7 @@ +import postgres_interfacing + +class bot_help(): + + def reply(self, target, source, command_arguments): + my_postgres_interfacing = postgres_interfacing.postgres_interfacing() + my_postgres_interfacing.irc_reply(target, source, "Default help. Known commands: help, ping") diff -uNr a/commands/__init__.py b/commands/__init__.py --- a/commands/__init__.py false +++ b/commands/__init__.py f8aca02e28996a586f535eed5de9f4533b8b2910762f524459f6fae6fb3f8f7540db5f2c809c1c07167a95b33f6f3f85589af99182e2d2bf93f964de169dd4c0 @@ -0,0 +1 @@ + diff -uNr a/commands/ping_pong.py b/commands/ping_pong.py --- a/commands/ping_pong.py false +++ b/commands/ping_pong.py 8c388a0aad581c48387fc3a938fed1aece5d6630d1fd65c98b960e56290e12c31cb5041e5d8a2adf0c1260b72cc0ed3a4b022b605c3dd29e360e251ae0cb86cc @@ -0,0 +1,11 @@ +import postgres_interfacing + +class ping_pong(): + + def reply(self, target, source, command_arguments): + my_postgres_interfacing = postgres_interfacing.postgres_interfacing() + if command_arguments: + reply_string = "pong. Here's your arguments back: %s" % command_arguments + my_postgres_interfacing.irc_reply(target, source, reply_string) + else: + my_postgres_interfacing.irc_reply(target, source, "pong") diff -uNr a/__init__.py b/__init__.py --- a/__init__.py false +++ b/__init__.py 678e7a73f2b948abf48758fb45e9971f58fca12fb4c2bb4e6a3b44d10ce01a13feeb474dae8eb6c67a29aa3799112c11abbe94930ee76e16ad555c860ce355ce @@ -0,0 +1 @@ +from main import main diff -uNr a/INSTALL b/INSTALL --- a/INSTALL false +++ b/INSTALL da9c78cdba2e22483c539bda2f68e8965814d0452b09b74e28c83b85e077aa594efb248e0c0175afc855c7d594049f2c8c82dee1a1ce465db361c8366ff80618 @@ -0,0 +1,15 @@ +Install the following via your preferred method: + +logbot-genesis or logbot-multiple-channels-corrected (http://btcbase.org/patches?patchset=bot&search=) +psycopg2 (a PostgreSQL adapter for Python: http://initd.org/psycopg/) + +Press logbot_command_router_python via your preferred V: + +mkdir -p ~/src/logbot_command_router_python +cd ~/src/logbot_command_router_python + +mkdir .wot +cd .wot && wget http://www.lobbesblog.com/lobbes.asc && cd .. + +v.pl init http://www.lobbesblog.com/src/logbot_command_router_python +v.pl press logbot_command_router_python logbot_command_router_python_genesis.vpatch diff -uNr a/knobs/config.py b/knobs/config.py --- a/knobs/config.py false +++ b/knobs/config.py e5b5be2d2db8e1309b0d34e87700b638915336e6b63413f90c15576a2b60befa4c9fc0f1d837b4e937032a67e95265fbe644756e44843134e65a3b1220cb0579 @@ -0,0 +1,25 @@ +""" +Set your postgres connection info +NOTE: If your Postgres socket is located somewhere other than '/var/run/postgresql/', +you will have to specify your host in the postgres connection string . E.g. 'host=/tmp/' +""" +postgres = {'connection_string': 'dbname=your_postgres_db user=your_postgres_user', + 'channel': 'log_new_message'} +""" +Set your bot name and command prefix +""" +bot = {'bot_name': 'your_bot', + 'bot_command_prefix': '!your_command_prefix'} +""" +Set this to True if you want main.py to pass ALL irc messages to router.py +instead of just messages intended to be commands (i.e. messages starting with bot_command_prefix). +This could be useful if, e.g. you are making an archive bot or a log quoter. +""" +route_all_notifications = False +""" +Set your list of irc nicks to ignore +Irc messages originating from a nick on this list will be 'ignored' by main.py +(i.e. those messages will not be processed nor commands routed) +This could be useful if, e.g. you are running many bots in the same channel and don't want them talking to each other. +""" +ignore_list = ('examplebot','gavinandresen') diff -uNr a/knobs/__init__.py b/knobs/__init__.py --- a/knobs/__init__.py false +++ b/knobs/__init__.py f8aca02e28996a586f535eed5de9f4533b8b2910762f524459f6fae6fb3f8f7540db5f2c809c1c07167a95b33f6f3f85589af99182e2d2bf93f964de169dd4c0 @@ -0,0 +1 @@ + diff -uNr a/knobs/router.py b/knobs/router.py --- a/knobs/router.py false +++ b/knobs/router.py 77c9615f3f5c06b35198259ce46f3c7447ddb952a196da8002aa874124c4e5391b1c46ad0eee62902234996afbf5771f677c1edc70bd5dc0355c11133704acc1 @@ -0,0 +1,45 @@ +""" +Import your custom command modules here +(alternatively, you -could- put all your command handling scripts in the +router() class below, but that could prove messy over time) +""" +from commands import ping_pong, bot_help + +class router: + """ + Each function in this class beginning with 'cmd_' represents a command for logbot. + + Functions intending to be used as callable bot commands MUST use the following format: + 'cmd_[command to be issued from irc](self, target, source, command_arguments)' + source = *irc nick* that issued command + target = *where* the irc nick issued the command + command_arguments = string representing everything *to the right of* the command issued (to be parsed or ignored as you wish) + + Example: + Say you have a knobs.config.bot_command_prefix value of '!G' + Someone issuing '!Gping randomgibberish' in #example-irc-channel-where-logbot-is-sitting + will trigger main.py to call cmd_ping(), passing ' randomgibberish' to command_arguments. + + In the example above, cmd_ping() just ignores the ' randomgibberish' and simply passes the target and source data to ping_pong.pong() function. + However, you could instead choose to parse the 'randomgibberish' and do things with it. + + Hint: You have two easy ways to send command responses back to a target irc channel or nick: + 1) Use postgres_interfacing.send_to_outbox(target, payload) + Directly inserts a record into the 'outbox' table (this is the 'raw' function) + 2) Use postgres_interfacing.irc_reply(self, target, source, payload) + Target and source will be validated before sending data to the 'outbox' table (recommended in most cases) + """ + def cmd_ping(self, target, source, command_arguments): + my_pingpong = ping_pong.ping_pong() + my_pingpong.reply(target, source, command_arguments) + + def cmd_help(self, target, source, command_arguments): + my_bot_help = bot_help.bot_help() + my_bot_help.reply(target, source, command_arguments) + + def passive(self, target, source, message): + """ + If knobs.config.route_all_notifications is set to 'True', then main.py will route ALL 'non-command' messages through this function. + Useful for, e.g. log-quoters and archive bots + """ + pass diff -uNr a/main.py b/main.py --- a/main.py false +++ b/main.py ef0185bd3b618ac6c82eeca7aee17e7dfb5f1863330c9bf80bd68880a69ba1f285f4e51ac9e01438481f52541b8ad01c05f5371809925365baf77ed2fecc5721 @@ -0,0 +1,73 @@ +#!/usr/bin/env python2 +import select +import psycopg2 +import psycopg2.extensions +from knobs import router +import postgres_interfacing + +class main(): + #LISTEN to postgres_channel for postgres-payload and parse for commands intended for bot, routing it to the proper submodule. + #For use with logbot-genesis or logbot-multiple-channels-corrected on a LOCAL postgres server + + def __init__(self): + self.my_router = router.router() + self.my_interface = postgres_interfacing.postgres_interfacing() + + def parse_message(self, message): + #parse irc message for valid command-calling syntax and return extracted command and arguments + bot_command_prefix = self.my_interface.bot_command_prefix + bot_command_prefix_len = len(bot_command_prefix) + if message[:bot_command_prefix_len] in bot_command_prefix: + command_start_pos = bot_command_prefix_len + (len(message[bot_command_prefix_len:]) - len(message[bot_command_prefix_len:].lstrip())) + if message[command_start_pos:].find(" ") > -1: + command_end_pos = command_start_pos + message[command_start_pos:].find(" ") + command = message[command_start_pos:command_end_pos] + command_arguments = message[command_end_pos:] + else: + command = message[command_start_pos:] + command_arguments = "" + return command, command_arguments + + def send_to_command_router(self, target, source, command, command_arguments): + #do stuff with valid commands + command_function = "cmd_%s" % command + try: + route_command = getattr(self.my_router, command_function)(target, source, command_arguments) + except AttributeError: + pass + + def listen_for_notifications(self): + #Main Loop =================== + #Execute a LISTEN on the 'log_new_message' channel on local postgres server. + #If NOTIFY is recieved, feed the payload (which corresponds to an id in the 'log' table) into fetch_irc_message() to return the message. + #Parse message and handle as configured in knobs and commands + conn = psycopg2.connect(self.my_interface.connection_string) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cur = conn.cursor() + cur.execute(self.my_interface.postgres_listen_string) + print "Waiting for notifications on postgres channel '%s'" % self.my_interface.postgres_channel + while 1: + if select.select([conn],[],[],5) == ([],[],[]): + print "Timeout" + else: + conn.poll() + while conn.notifies: + notify = conn.notifies.pop(0) + print "Got NOTIFY:", notify.pid, notify.channel, notify.payload + target, source, message = self.my_interface.fetch_irc_message(notify.payload) + print "Message: %s" % message + if source not in self.my_interface.ignore_list: + if self.parse_message(message): + command, command_arguments = self.parse_message(message) + print "Attempting to route command '%s' with arguments '%s'..." % (command, command_arguments) + self.send_to_command_router(target, source, command, command_arguments) + elif self.my_interface.route_all_notifications == True: + print "Routing non-command message: '%s'" % message + self.my_router.passive(target, source, message) + else: + print "Ignoring nick '%s'" % source + +if __name__ == "__main__": + print "running" + my_connection = main() + my_connection.listen_for_notifications() diff -uNr a/postgres_interfacing.py b/postgres_interfacing.py --- a/postgres_interfacing.py false +++ b/postgres_interfacing.py 13bda7a95edfe0bf6bdbc56629a598b7d1afa21aa96a49396ce49bddb4dc10e5017bb78a098c7873950465044030d75440c476d5c1a14f996c34fb49721de26b @@ -0,0 +1,45 @@ +import select +import psycopg2 +import psycopg2.extensions +from knobs import config + +class postgres_interfacing(): + + def __init__(self): + self.connection_string = config.postgres['connection_string'] + self.postgres_channel = config.postgres['channel'] + self.postgres_listen_string = "LISTEN %s;" % self.postgres_channel + self.bot_command_prefix = config.bot['bot_command_prefix'] + self.bot_name = config.bot['bot_name'] + self.route_all_notifications = config.route_all_notifications + self.ignore_list = config.ignore_list + + def send_to_outbox(self, target, payload): + #Insert a target and message into the 'outbox' table on local postgres server + conn = psycopg2.connect(self.connection_string) + cur = conn.cursor() + cur.execute("INSERT INTO outbox(target, message) VALUES(%s,%s)", (target, payload)) + conn.commit() + cur.close() + conn.close() + + def irc_reply(self, target, source, payload): + if source != self.bot_name: + #make sure bot doesn't talk to itself + if target == self.bot_name: + #is the target this bot + self.send_to_outbox(source, payload) + elif target[:1] == '#': + #is the target a channel + self.send_to_outbox(target, payload) + + def fetch_irc_message(self, log_id): + #Fetch the irc message in the 'log' table based off of the log id + conn = psycopg2.connect(self.connection_string) + cur = conn.cursor() + cur.execute("SELECT target, source, message FROM log WHERE id = %s;", (log_id,)) + row = cur.fetchone() + target, source, message = row[0], row[1], row[2] + return target, source, message + cur.close() + conn.close() diff -uNr a/README b/README --- a/README false +++ b/README 380e59a124dbb4c6b4db0bc8ad7f5518522d1cee432d84781a67142302c1c157b25545421c189be1a63922c94cf15137f6b69b8c28b2cdf691f81e190fc4043a @@ -0,0 +1,15 @@ +HOW TO USE: + +Bits designed to be customized to your needs: +knobs/config.py << Edit you postgres db connection info, bot command prefix, etc. +knobs/router.py << The central command router. Write or pull in your custom commands here. +commands/* << A directory to house command scripts that you can import into router.py + +To run: +Configure your bits mentioned above +Start your local postgres server and your instance of logbot or logbot-multiple-channels-corrected +./main.py + +CAVEATS: + +Designed to communicate with a LOCAL Postgres server only, not remote. (Feel free to extend this to add that capability, however)