diff --git a/Module.py b/Module.py index 94cb111..fd3156b 100644 --- a/Module.py +++ b/Module.py @@ -21,9 +21,10 @@ from ConfigParser import NoSectionError, NoOptionError import logging import re import sys -import sqlite3 from threading import Timer +import MySQLdb as mdb + from extlib import irclib class Module(object): @@ -165,7 +166,7 @@ class Module(object): def get_db(self): """ - Get a database connection to sqlite3. Once grabbed, it should be closed + Get a database connection to mdb. Once grabbed, it should be closed when work is done. Modules that need a database connection should test for and create (or, eventually, alter) required table structure in their __init__ IF that structure does not already exist. Well-behaved @@ -175,17 +176,13 @@ class Module(object): See also db_module_registered, below. """ - dbfile = self.config.get('dr.botzo', 'database') - conn = sqlite3.connect(dbfile, isolation_level=None) - conn.row_factory = sqlite3.Row + dbhost = self.config.get('dr.botzo', 'dbhost') + dbuser = self.config.get('dr.botzo', 'dbuser') + dbpass = self.config.get('dr.botzo', 'dbpass') + dbname = self.config.get('dr.botzo', 'dbname') + db = mdb.connect(dbhost, dbuser, dbpass, dbname, use_unicode=True) - # setup regexp function in sqlite - def regexp(expr, item): - reg = re.compile(expr, re.IGNORECASE) - return reg.search(item) is not None - conn.create_function('REGEXP', 2, regexp) - - return conn + return db def db_module_registered(self, modulename): """ @@ -193,19 +190,20 @@ class Module(object): number if the module has registered, or None if not """ - conn = self.get_db() + db = self.get_db() version = None try: - cur = conn.cursor() - cur.execute("SELECT version FROM drbotzo_modules WHERE module = :name", - {'name': modulename}) + cur = db.cursor() + cur.execute("SELECT version FROM drbotzo_modules WHERE module = %s", + (modulename,)) version = cur.fetchone() if (version != None): version = version[0] - conn.close() - except sqlite3.Error as e: - conn.close() - self.log.error("sqlite error:" + str(e)) + except mdb.Error as e: + self.log.error("database error during module registered check") + self.log.exception(e) + raise + finally: cur.close() return version @@ -214,14 +212,16 @@ class Module(object): db = self.get_db() try: - db.execute('INSERT OR REPLACE INTO drbotzo_modules (version, module) VALUES (?, ?)', (version, modulename)) + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute('INSERT IGNORE INTO drbotzo_modules (version, module) VALUES (%s, %s)', + (version, modulename)) db.commit() - db.close() - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error during register module version") + self.log.exception(e) raise + finally: cur.close() def db_init(self): """ diff --git a/dr.botzo.cfg.example b/dr.botzo.cfg.example index efa4417..d9ade37 100644 --- a/dr.botzo.cfg.example +++ b/dr.botzo.cfg.example @@ -7,10 +7,13 @@ usermode = -x debug = true admin_userhost = bss@ayu.incorporeal.org module_list = IrcAdmin -database = dr.botzo.data +dbhost = localhost +dbuser = dr_botzo +dbpass = password +dbname = dr_botzo [IrcAdmin] autojoin = #bss [Karma] -meta.pubmsg_needs_bot_prefix = false \ No newline at end of file +meta.pubmsg_needs_bot_prefix = false diff --git a/dr.botzo.py b/dr.botzo.py index 2560ad9..01572da 100644 --- a/dr.botzo.py +++ b/dr.botzo.py @@ -21,7 +21,8 @@ import logging import logging.config import os import sys -import sqlite3 + +import MySQLdb as mdb import DrBotIRC from extlib import irclib @@ -53,28 +54,31 @@ logging.config.fileConfig('logging.cfg') log = logging.getLogger('drbotzo') try: - # make sure we can initialize the database, if such a thing is - # called for in the config file - dbfile = config.get('dr.botzo', 'database') - conn = sqlite3.connect(dbfile, isolation_level=None) + dbhost = config.get('dr.botzo', 'dbhost') + dbuser = config.get('dr.botzo', 'dbuser') + dbpass = config.get('dr.botzo', 'dbpass') + dbname = config.get('dr.botzo', 'dbname') + db = mdb.connect(dbhost, dbuser, dbpass, dbname, use_unicode=True) try: + cur = db.cursor() + # need to create the drbotzo_modules table if it doesn't exist query = """ - SELECT COUNT(*) - FROM sqlite_master - WHERE type = 'table' AND name = 'drbotzo_modules' + SELECT COUNT(*) FROM information_schema.tables + WHERE table_schema = %s + AND table_name = %s """ - row = conn.execute(query).fetchone() + cur.execute(query, (dbname, 'drbotzo_modules')) + row = cur.fetchone() if row[0] == 0: - # need to create the drbotzo_modules table query = """ - CREATE TABLE drbotzo_modules ( - module TEXT PRIMARY KEY, - version INTEGER - ) + CREATE TABLE IF NOT EXISTS drbotzo_modules ( + module VARCHAR(64) PRIMARY KEY, + version INTEGER + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 """ - conn.execute(query) - conn.commit() - finally: conn.close() + cur.execute(query) + db.commit() + finally: cur.close() except NoOptionError as e: sys.exit("Aborted due to error with necessary configuration: " + str(e)) diff --git a/modules/Achievements.py b/modules/Achievements.py index 8298a54..70fafc8 100644 --- a/modules/Achievements.py +++ b/modules/Achievements.py @@ -17,10 +17,11 @@ along with this program. If not, see . """ import re -import sqlite3 import thread import time +import MySQLdb as mdb + from Module import Module from extlib import irclib @@ -73,89 +74,73 @@ class Achievements(Module): db = self.get_db() try: version = 1 - db.execute(''' + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(''' CREATE TABLE achievements_player ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - nick TEXT NOT NULL UNIQUE, - userhost TEXT NOT NULL DEFAULT '', + id SERIAL, + nick VARCHAR(64) NOT NULL UNIQUE, + userhost VARCHAR(256) NOT NULL DEFAULT '', is_playing INTEGER NOT NULL DEFAULT 0, - last_seen_time TEXT DEFAULT CURRENT_TIMESTAMP - )''') - db.execute(''' + last_seen_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE achievements_event ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - player_id INTEGER NOT NULL, - event TEXT NOT NULL, - target TEXT, + id SERIAL, + player_id BIGINT(20) UNSIGNED NOT NULL, + event VARCHAR(64) NOT NULL, + target VARCHAR(64), msg_len INTEGER, - event_time TEXT DEFAULT CURRENT_TIMESTAMP, + event_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(player_id) REFERENCES achievements_player(id) - )''') - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE achievements_achievement ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - desc TEXT NOT NULL, - query TEXT NOT NULL - )''') - db.execute(''' + id SERIAL, + name VARCHAR(256) NOT NULL, + description VARCHAR(256) NOT NULL, + query VARCHAR(1024) NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE achievements_log ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - player_id INTEGER NOT NULL, - achievement_id INTEGER NOT NULL, - event_time TEXT DEFAULT CURRENT_TIMESTAMP, + id SERIAL, + player_id BIGINT(20) UNSIGNED NOT NULL, + achievement_id BIGINT(20) UNSIGNED NOT NULL, + event_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(player_id) REFERENCES achievements_player(id), FOREIGN KEY(achievement_id) REFERENCES achievements_achievement(id) - )''') - db.commit() - db.close() - self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: - db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) - raise - if (version < 2): - db = self.get_db() - try: - version = 2 - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE achievements_config ( channel TEXT NOT NULL - )''') - db.commit() - db.close() - self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: - db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) - raise - if (version < 3): - db = self.get_db() - try: - version = 3 - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE achievements_filter ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - filter TEXT NOT NULL - )''') - db.execute(''' + id SERIAL, + filter VARCHAR(256) NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE achievements_filter_log ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - filter_id INTEGER NOT NULL, - event_id INTEGER NOT NULL, + id SERIAL, + filter_id BIGINT(20) UNSIGNED NOT NULL, + event_id BIGINT(20) UNSIGNED NOT NULL, FOREIGN KEY(filter_id) REFERENCES achievements_filter(id), FOREIGN KEY(event_id) REFERENCES achievements_event(id) - )''') + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') db.commit() - db.close() self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def register_handlers(self): """Handle all sorts of things to track.""" @@ -247,37 +232,37 @@ class Achievements(Module): def _get_or_add_player(self, nick, userhost): """Add a player to the database, or update the existing one, and return the id.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' - INSERT OR IGNORE INTO achievements_player (nick) VALUES (?)''' + INSERT IGNORE INTO achievements_player (nick) VALUES (%s)''' cur.execute(statement, (nick,)) statement = ''' - UPDATE achievements_player SET userhost = ?, last_seen_time = CURRENT_TIMESTAMP - WHERE nick = ?''' + UPDATE achievements_player SET userhost = %s, last_seen_time = CURRENT_TIMESTAMP + WHERE nick = %s''' cur.execute(statement, (userhost, nick)) db.commit() - statement = '''SELECT id FROM achievements_player WHERE nick = ?''' - cur = db.execute(statement, (nick,)) + statement = '''SELECT id FROM achievements_player WHERE nick = %s''' + cur.execute(statement, (nick,)) result = cur.fetchone() - db.close() return result['id'] - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error getting or adding player") + self.log.exception(e) raise + finally: cur.close() def _add_event(self, player_id, event, target, msg, msg_len): """Add an event to the log.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' INSERT INTO achievements_event ( player_id, event, target, msg_len - ) VALUES (?, ?, ?, ?) + ) VALUES (%s, %s, %s, %s) ''' cur.execute(statement, (player_id, event, target, msg_len)) db.commit() @@ -285,135 +270,143 @@ class Achievements(Module): # now see if the event matched any filters query = ''' - SELECT id FROM achievements_filter WHERE ? REGEXP filter + SELECT id FROM achievements_filter WHERE %s REGEXP filter ''' - cursor = db.execute(query, (msg.decode('utf-8', 'replace'),)) - results = cursor.fetchall() + cur.execute(query, (msg.decode('utf-8', 'replace'),)) + results = cur.fetchall() for result in results: - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' INSERT INTO achievements_filter_log (filter_id, event_id) - VALUES (?, ?) + VALUES (%s, %s) ''' cur.execute(statement, (result['id'], event_id)) db.commit() - db.close() - return event_id - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error adding event") + self.log.exception(e) raise + finally: cur.close() def _get_achievements_settings(self): """Get the report settings.""" + db = self.get_db() try: # get the settings - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = 'SELECT channel FROM achievements_config' - cursor = db.execute(query) - result = cursor.fetchone() - db.close() + cur.execute(query) + result = cur.fetchone() if result: settings = self.AchievementsSettings() settings.channel = result['channel'] return settings - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + else: + return None + except mdb.Error as e: + self.log.error("database error getting settings") + self.log.exception(e) raise + except AttributeError as e: + self.log.error("could not get channel settings, probably unset") + self.log.exception(e) + return None + finally: cur.close() def _join_system(self, nick): - """Add the appropriate nick to the game.""" + """Add the appropriate nick to the system.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() - statement = 'UPDATE achievements_player SET is_playing = 1 WHERE nick = ?' + cur = db.cursor(mdb.cursors.DictCursor) + statement = 'UPDATE achievements_player SET is_playing = 1 WHERE nick = %s' cur.execute(statement, (nick,)) db.commit() - db.close() return nick + ' joined.' - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error joining the system") + self.log.exception(e) raise + finally: cur.close() def _leave_system(self, nick): - """Remove the appropriate nick from the game.""" + """Remove the appropriate nick from the system.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() - statement = 'UPDATE achievements_player SET is_playing = 0 WHERE nick = ?' + cur = db.cursor(mdb.cursors.DictCursor) + statement = 'UPDATE achievements_player SET is_playing = 0 WHERE nick = %s' cur.execute(statement, (nick,)) db.commit() - db.close() return nick + ' left.' - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error leaving the system") + self.log.exception(e) raise + finally: cur.close() def _add_player_to_achievement_log(self, player_id, achievement_id): """Log the success of a player.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() - statement = 'INSERT INTO achievements_log (player_id, achievement_id) VALUES (?, ?)' + cur = db.cursor(mdb.cursors.DictCursor) + statement = 'INSERT INTO achievements_log (player_id, achievement_id) VALUES (%s, %s)' cur.execute(statement, (player_id, achievement_id)) db.commit() - db.close() return - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error adding player to achievement log") + self.log.exception(e) raise + finally: cur.close() def _get_achievement_info(self, achievement): """Return the description of a given achievement.""" + db = self.get_db() try: - db = self.get_db() - query = 'SELECT desc FROM achievements_achievement WHERE name = ?' - cursor = db.execute(query, (achievement,)) - result = cursor.fetchone() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + query = 'SELECT description FROM achievements_achievement WHERE name = %s' + cur.execute(query, (achievement,)) + result = cur.fetchone() if result: - return result['desc'] - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + return result['description'] + except mdb.Error as e: + self.log.error("database error getting achievement info") + self.log.exception(e) raise + finally: cur.close() def _get_player_achievements(self, nick): """Return the achievements the nick has.""" achievements = [] + db = self.get_db() try: - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT a.name FROM achievements_achievement a INNER JOIN achievements_log l ON l.achievement_id = a.id INNER JOIN achievements_player p ON p.id = l.player_id - WHERE p.nick = ? + WHERE p.nick = %s ''' - cursor = db.execute(query, (nick,)) - results = cursor.fetchall() - db.close() + cur.execute(query, (nick,)) + results = cur.fetchall() for result in results: achievements.append(result['name']) return achievements - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error getting player achievements") + self.log.exception(e) raise + finally: cur.close() def _do_achievement_scan(self): """Run the queries in the database, seeing if anyone new has an achievement.""" @@ -428,22 +421,26 @@ class Achievements(Module): if self.connection is None: return + self.log.debug('in achievement scan') settings = self._get_achievements_settings() - channel = settings.channel - achievers = self._query_for_new_achievers() - for achiever in achievers: - self.sendmsg(self.connection, channel, achiever[0] + ' achieved ' + achiever[1] + '!') + if settings is not None: + channel = settings.channel + achievers = self._query_for_new_achievers() + for achiever in achievers: + self.sendmsg(self.connection, channel, achiever[0] + ' achieved ' + achiever[1] + '!') def _query_for_new_achievers(self): """Get new achievement earners for each achievement.""" achievers = [] + self.log.debug('checking achievements') + db = self.get_db() try: - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = 'SELECT id, name, query FROM achievements_achievement' - cursor = db.execute(query) - achievements = cursor.fetchall() + cur.execute(query) + achievements = cur.fetchall() for achievement in achievements: self.log.debug('checking achievement:[' + achievement['name'] + ']') @@ -458,22 +455,22 @@ class Achievements(Module): SELECT player_id FROM achievements_log l INNER JOIN achievements_achievement a ON a.id = l.achievement_id - WHERE a.name = ? + WHERE a.name = %s ) ''' - cursor = db.execute(query, (achievement['name'],)) - ach_achievers = cursor.fetchall() + cur.execute(query, (achievement['name'],)) + ach_achievers = cur.fetchall() for ach_achiever in ach_achievers: self.log.debug('name:[' + ach_achiever['nick'] + '] achievement:[' + achievement['name'] + ']') self._add_player_to_achievement_log(ach_achiever['id'], achievement['id']) achievers.append((ach_achiever['nick'], achievement['name'])) - db.close() return achievers - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error scanning new achievers") + self.log.exception(e) raise + finally: cur.close() # vi:tabstop=4:expandtab:autoindent diff --git a/modules/Acro.py b/modules/Acro.py index 00b21a5..2243095 100644 --- a/modules/Acro.py +++ b/modules/Acro.py @@ -18,7 +18,6 @@ along with this program. If not, see . import random import re -import sqlite3 import thread import time diff --git a/modules/Facts.py b/modules/Facts.py index b16ab8f..f4bb95b 100644 --- a/modules/Facts.py +++ b/modules/Facts.py @@ -18,7 +18,8 @@ along with this program. If not, see . import random import re -import sqlite3 + +import MySQLdb as mdb from extlib import irclib @@ -46,38 +47,40 @@ class Facts(Module): if version == None: db = self.get_db() try: - db.execute(''' + version = 1 + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(''' CREATE TABLE facts_facts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - category TEXT NOT NULL, - fact TEXT NOT NULL, - who TEXT NOT NULL, - userhost TEXT NOT NULL, - time TEXT DEFAULT CURRENT_TIMESTAMP - ) + id SERIAL, + category VARCHAR(64) NOT NULL, + fact LONGTEXT NOT NULL, + who VARCHAR(64) NOT NULL, + userhost VARCHAR(256) NOT NULL, + time TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ''') - db.execute('INSERT INTO drbotzo_modules VALUES (?,?)', (self.__class__.__name__, 1)) db.commit() - db.close() - except sqlite3.Error as e: + self.db_register_module_version(self.__class__.__name__, version) + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def do(self, connection, event, nick, userhost, what, admin_unlocked): """Add or retrieve a fact from the database.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) match = re.search('^!facts\s+add\s+(\S+)\s+(.*)$', what) if match: category = match.group(1) fact = match.group(2) cur.execute('''INSERT INTO facts_facts (category, fact, who, userhost) - VALUES (?, ?, ?, ?)''', (category, fact, nick, userhost)) + VALUES (%s, %s, %s, %s)''', (category, fact, nick, userhost)) db.commit() return self.reply(connection, event, category + ' added.') @@ -91,10 +94,12 @@ class Facts(Module): if match: category = match.group(1) return self.reply(connection, event, self._get_fact(category)) - db.close() - except sqlite3.Error as e: - db.close() - return self.reply(connection, event, "sqlite error: " + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error during add/retrieve") + self.log.exception(e) + return self.reply(connection, event, "database error during add/retrieve fact") + finally: cur.close() def _get_fact(self, category, search=""): """ @@ -105,26 +110,27 @@ class Facts(Module): search - the optional regex to match against within that category """ + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) if search == "": - category_facts = cur.execute("SELECT * FROM facts_facts WHERE category=?", (category,)) - facts = category_facts.fetchall() - db.close() + cur.execute("SELECT * FROM facts_facts WHERE category = %s", + (category,)) + facts = cur.fetchall() else: - category_facts = cur.execute("SELECT * FROM facts_facts WHERE category=? AND fact REGEXP ?", - (category, search)) - facts = category_facts.fetchall() - db.close() + cur.execute("SELECT * FROM facts_facts WHERE category = %s AND fact REGEXP %s", + (category, search)) + facts = cur.fetchall() if len(facts) > 0: fact = facts[random.randint(1,len(facts))-1] return fact['fact'].rstrip().encode('utf-8', 'ignore') - except sqlite3.Error as e: - db.close() - return self.reply(connection, event, "sqlite error in _get_fact: " + str(e)) + except mdb.Error as e: + self.log.error("database error in _get_fact") + self.log.exception(e) + return self.reply(connection, event, "database error in _get_fact") + finally: cur.close() # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on; diff --git a/modules/Help.py b/modules/Help.py index eabbf1c..77c1b62 100644 --- a/modules/Help.py +++ b/modules/Help.py @@ -17,7 +17,6 @@ along with this program. If not, see . """ import re -import sqlite3 from Module import Module diff --git a/modules/Karma.py b/modules/Karma.py index f7e2f9a..467d263 100644 --- a/modules/Karma.py +++ b/modules/Karma.py @@ -16,10 +16,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ +from math import floor import re -import sqlite3 import string +import MySQLdb as mdb + from Module import Module __author__ = "Mike Bloy " @@ -50,67 +52,40 @@ class Karma(Module): version = self.db_module_registered(self.__class__.__name__) if (version == None): # have to create the database tables - conn = self.get_db() + db = self.get_db() try: - conn.execute(''' - CREATE TABLE karma_log ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - key TEXT NOT NULL, - delta INTEGER NOT NULL, - who TEXT NOT NULL, - userhost TEXT NOT NULL, - karmatime TEXT DEFAULT CURRENT_TIMESTAMP - )''') - conn.execute('CREATE INDEX karma_log_key_ix ON karma_log (key)') - conn.execute('CREATE INDEX karma_log_who_ix ON karma_log (who)') - conn.execute(''' - CREATE VIEW karma_values AS - SELECT key, SUM(delta) AS value - FROM karma_log - GROUP BY key''') - - sql = 'INSERT INTO drbotzo_modules VALUES (?,?)' - conn.execute(sql, (self.__class__.__name__, 1)) - conn.commit() - conn.close() version = 1 - except sqlite3.Error as e: - conn.rollback() - conn.close() - self.log.error("sqlite error: " + str(e)) - raise - if (version < 2): - conn = self.get_db() - try: - conn.execute(''' + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(''' + CREATE TABLE karma_log ( + id SERIAL, + karma_key VARCHAR(128) NOT NULL, + delta INTEGER NOT NULL, + who VARCHAR(64) NOT NULL, + userhost VARCHAR(256) NOT NULL, + karmatime TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute('CREATE INDEX karma_log_key_ix ON karma_log (karma_key)') + cur.execute('CREATE INDEX karma_log_who_ix ON karma_log (who)') + cur.execute(''' + CREATE VIEW karma_values AS + SELECT karma_key, SUM(delta) AS value + FROM karma_log + GROUP BY karma_key''') + cur.execute(''' CREATE VIEW karma_users AS SELECT who, COUNT(NULLIF(delta, -1)) AS pos, COUNT(NULLIF(delta, 1)) AS neg FROM karma_log GROUP BY who''') - sql = 'UPDATE drbotzo_modules SET version = ? WHERE module = ?' - conn.execute(sql, (2, self.__class__.__name__)) - conn.commit() - conn.close() - version = 2 - except sqlite3.Error as e: - conn.rollback() - conn.close() - self.log.error("sqlite error: " + str(e)) - raise - if (version < 3): - conn = self.get_db() - try: - version = 3 - conn.execute(''' - UPDATE karma_log SET key = LOWER(key)''') - conn.commit() - conn.close() + db.commit() self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: - conn.rollback() - conn.close() - self.log.error("sqlite error: " + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def do(self, connection, event, nick, userhost, what, admin_unlocked): """look for karma strings at the start of messages""" @@ -147,19 +122,21 @@ class Karma(Module): Go out to the database and update the karma value. """ - conn = self.get_db() + db = self.get_db() try: + cur = db.cursor(mdb.cursors.DictCursor) sql = ''' - INSERT INTO karma_log (key, delta, who, userhost) - VALUES (?, ?, ?, ?) + INSERT INTO karma_log (karma_key, delta, who, userhost) + VALUES (%s, %s, %s, %s) ''' - conn.execute(sql, (key.decode('utf-8', 'ignore').lower(), value, nick, userhost)) - conn.commit() - conn.close() - except sqlite3.Error as e: - conn.rollback() - conn.close() - return "sqlite error: " + str(e) + cur.execute(sql, (key.decode('utf-8', 'ignore').lower(), value, nick, userhost)) + db.commit() + except mdb.Error as e: + db.rollback() + self.log.error("database error modifying karma") + self.log.exception(e) + raise + finally: cur.close() def handle_report_query(self, connection, nick, userhost, what): match = self.reportre.search(what) @@ -169,61 +146,66 @@ class Karma(Module): query = None header = None if (report == 'highest'): - query = 'SELECT key, value FROM karma_values ORDER BY value DESC LIMIT 5' + query = 'SELECT karma_key AS who, value FROM karma_values ORDER BY value DESC LIMIT 5' header = 'Top 5 karma recipients:' elif (report == 'lowest'): - query = 'SELECT key, value FROM karma_values ORDER BY value ASC LIMIT 5' + query = 'SELECT karma_key AS who, value FROM karma_values ORDER BY value ASC LIMIT 5' header = 'Bottom 5 karma recipients:' elif (report == 'positive'): - query = 'SELECT who, pos FROM karma_users ORDER BY pos DESC LIMIT 5' + query = 'SELECT who, pos AS value FROM karma_users ORDER BY pos DESC LIMIT 5' header = 'Top 5 Optimists:' elif (report == 'negative'): - query = 'SELECT who, neg FROM karma_users ORDER BY neg DESC LIMIT 5' + query = 'SELECT who, neg AS value FROM karma_users ORDER BY neg DESC LIMIT 5' header = 'Top 5 Pessimists:' elif (report == 'top'): - query = 'SELECT who, pos+neg AS total FROM karma_users ORDER BY total DESC LIMIT 5' + query = 'SELECT who, pos+neg AS value FROM karma_users ORDER BY value DESC LIMIT 5' header = 'Top 5 Total Karma Givers:' if (query != None): - conn = self.get_db() + db = self.get_db() list = [] try: - cursor = conn.execute(query) - result = cursor.fetchone() + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(query) + result = cur.fetchone() while (result != None): - list.append("{key} ({value})".format(key=result[0], value=result[1])) - result = cursor.fetchone() + list.append("{key} ({value})".format(key=result['who'], value=result['value'])) + result = cur.fetchone() list = ', '.join(list) message = '{header} {list}'.format(header=header, list=list) - conn.close() - except sqlite3.Error as e: - conn.rollback() - conn.close() - return "sqlite error: " + str(e) + except mdb.Error as e: + self.log.error("database error during report query") + self.log.exception(e) + raise + finally: cur.close() + return message def handle_stat_query(self, connection, nick, userhost, what): match = self.statre.search(what) statnick = match.group(1) - conn = self.get_db() + db = self.get_db() reply = '{nick}: {statnick} has never given karma'.format(nick=nick, statnick=statnick) try: + cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT pos, neg FROM karma_users WHERE who = :who ''' - value = conn.execute(query, {'who': statnick}).fetchone() + cur.execute(query, {'who': statnick}) + value = cur.fetchone() if (value != None): pos = value[0] neg = value[1] total = pos+neg; reply = '{nick}: {statnick} has given {pos} postive karma and {neg} negative karma, for a total of {total} karma'.format(nick=nick, statnick=statnick, pos=pos, neg=neg, total=total) - conn.close() - except sqlite3.Error as e: - conn.close() - return "sqlite error: " + str(e) + except mdb.Error as e: + self.log.error("database error during handle stat query") + self.log.exception(e) + raise + finally: cur.close() return reply @@ -231,29 +213,33 @@ class Karma(Module): match = self.queryre.search(what) key = match.group(1) - conn = self.get_db() + db = self.get_db() reply = '{nick}: {key} has no karma'.format(nick=nick, key=key) try: + cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT value FROM karma_values - WHERE key = :key + WHERE karma_key = %s ''' - value = conn.execute(query, {'key': key.decode('utf-8', 'ignore').lower()}).fetchone() + cur.execute(query, (key.decode('utf-8', 'ignore').lower(),)) + value = cur.fetchone() if (value != None): query = ''' - SELECT count(*) FROM karma_values WHERE value > :value + SELECT count(*) FROM karma_values WHERE value > %s ''' - rank = conn.execute(query, {'value': value[0]}).fetchone() - rank = rank[0] + 1; + cur.execute(query, (value['value'],)) + rank = cur.fetchone() + rank = rank['count(*)'] + 1; - reply = '{nick}: {key} has {value[0]!s} points of karma (rank {rank})'.format( - nick=nick, key=key, value=value, rank=rank) - conn.close() - except sqlite3.Error as e: - conn.close() - return "sqlite error: " + str(e) + reply = '{0:s}: {1:s} has {2:d} points of karma (rank {3:d})'.format( + nick, key, int(floor(value['value'])), rank) + except mdb.Error as e: + self.log.error("database error during handle karma query") + self.log.exception(e) + raise + finally: cur.close() return reply diff --git a/modules/Markov.py b/modules/Markov.py index 60e02e6..ffa68f8 100644 --- a/modules/Markov.py +++ b/modules/Markov.py @@ -21,11 +21,12 @@ from datetime import datetime import os import random import re -import sqlite3 import sys import thread import time +import MySQLdb as mdb + from dateutil.parser import * from dateutil.relativedelta import * from extlib import irclib @@ -76,67 +77,58 @@ class Markov(Module): """Create the markov chain table.""" version = self.db_module_registered(self.__class__.__name__) - if (version == None or version < 9): + if version == None: db = self.get_db() try: - version = 9 - - # recreating the tables, since i need to add some foreign key constraints - db.execute('''DROP INDEX IF EXISTS markov_chain_keys_and_context_index''') - db.execute('''DROP INDEX IF EXISTS markov_chain_keys_index''') - db.execute('''DROP INDEX IF EXISTS markov_chain_value_and_context_index''') - db.execute('''DROP TABLE IF EXISTS markov_chain''') - db.execute('''DROP TABLE IF EXISTS markov_target_to_context_map''') - db.execute('''DROP TABLE IF EXISTS markov_chatter_target''') - db.execute('''DROP TABLE IF EXISTS markov_context''') - - db.execute(''' + version = 1 + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(''' CREATE TABLE markov_chatter_target ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - target TEXT NOT NULL, + id SERIAL, + target VARCHAR(256) NOT NULL, chance INTEGER NOT NULL DEFAULT 99999 - )''') - - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE markov_context ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - context TEXT NOT NULL - )''') - - db.execute(''' + id SERIAL, + context VARCHAR(256) NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE markov_target_to_context_map ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - target TEXT NOT NULL, - context_id INTEGER NOT NULL, + id SERIAL, + target VARCHAR(256) NOT NULL, + context_id BIGINT(20) UNSIGNED NOT NULL, FOREIGN KEY(context_id) REFERENCES markov_context(id) - )''') - - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE markov_chain ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - k1 TEXT NOT NULL, - k2 TEXT NOT NULL, - v TEXT NOT NULL, - context_id INTEGER DEFAULT NULL, + id SERIAL, + k1 VARCHAR(128) NOT NULL, + k2 VARCHAR(128) NOT NULL, + v VARCHAR(128) NOT NULL, + context_id BIGINT(20) UNSIGNED NOT NULL, FOREIGN KEY(context_id) REFERENCES markov_context(id) - )''') - - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE INDEX markov_chain_keys_and_context_id_index ON markov_chain (k1, k2, context_id)''') - db.execute(''' + cur.execute(''' CREATE INDEX markov_chain_value_and_context_id_index ON markov_chain (v, context_id)''') db.commit() - db.close() self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error('sqlite error: ' + str(e)) + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def register_handlers(self): """Handle pubmsg/privmsg, to learn and/or reply to IRC events.""" @@ -303,10 +295,10 @@ class Markov(Module): if len(words) <= 0: return line + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() - statement = 'INSERT INTO markov_chain (k1, k2, v, context_id) VALUES (?, ?, ?, ?)' + cur = db.cursor(mdb.cursors.DictCursor) + statement = 'INSERT INTO markov_chain (k1, k2, v, context_id) VALUES (%s, %s, %s, %s)' for word in words: cur.execute(statement, (k1.decode('utf-8', 'replace'), k2.decode('utf-8', 'replace'), word.decode('utf-8', 'replace'), context_id)) @@ -315,12 +307,12 @@ class Markov(Module): k2.decode('utf-8', 'replace'), self.stop, context_id)) db.commit() - db.close() - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error in Markov._learn_line: " + str(e)) + self.log.error("database error learning line") + self.log.exception(e) raise + finally: cur.close() def _generate_line(self, target, line='', min_size=15, max_size=100): """ @@ -412,69 +404,73 @@ class Markov(Module): """Get the value(s) for a given key (a pair of strings).""" values = [] + db = self.get_db() try: - db = self.get_db() query = '' if k1 == self.start1 and k2 == self.start2: # hack. get a quasi-random start from the database, in # a faster fashion than selecting all starts max_id = self._get_max_chain_id() rand_id = random.randint(1,max_id) - query = ('SELECT v FROM markov_chain WHERE k1 = ? AND k2 = ? AND ' - '(context_id = ?) AND id >= {0:d} LIMIT 1'.format(rand_id)) + query = ('SELECT v FROM markov_chain WHERE k1 = %s AND k2 = %s AND ' + '(context_id = %s) AND id >= {0:d} LIMIT 1'.format(rand_id)) else: - query = ('SELECT v FROM markov_chain WHERE k1 = ? AND k2 = ? AND ' - '(context_id = ?)') - cursor = db.execute(query, (k1.decode('utf-8', 'replace'), - k2.decode('utf-8', 'replace'), - context_id)) - results = cursor.fetchall() + query = ('SELECT v FROM markov_chain WHERE k1 = %s AND k2 = %s AND ' + '(context_id = %s)') + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(query, (k1.decode('utf-8', 'replace'), + k2.decode('utf-8', 'replace'), + context_id)) + results = cur.fetchall() for result in results: values.append(result['v'].encode('utf-8', 'replace')) - db.close() return values - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error in Markov._retrieve_chains_for_key: ' + str(e)) + except mdb.Error as e: + self.log.error("database error in _retrieve_chains_for_key") + self.log.exception(e) raise + finally: cur.close() def _retrieve_k2_for_value(self, v, context_id): """Get the value(s) for a given key (a pair of strings).""" values = [] + db = self.get_db() try: - db = self.get_db() - query = 'SELECT k2 FROM markov_chain WHERE v = ? AND (context_id = ?)' - cursor = db.execute(query, (v.decode('utf-8', 'replace'), context_id)) - results = cursor.fetchall() + query = 'SELECT k2 FROM markov_chain WHERE v = %s AND context_id = %s' + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(query, (v.decode('utf-8', 'replace'), context_id)) + results = cur.fetchall() for result in results: values.append(result['k2'].encode('utf-8', 'replace')) - db.close() return values - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error in Markov._retrieve_k2_for_value: ' + str(e)) + except mdb.Error as e: + self.log.error("database error in _retrieve_k2_for_value") + self.log.exception(e) raise + finally: cur.close() def _get_chatter_targets(self): """Get all possible chatter targets.""" values = [] + db = self.get_db() try: # need to create our own db object, since this is likely going to be in a new thread - db = self.get_db() query = 'SELECT target, chance FROM markov_chatter_target' - cursor = db.execute(query) - results = cursor.fetchall() + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(query) + results = cur.fetchall() return results - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error in Markov._get_chatter_targets: ' + str(e)) + except mdb.Error as e: + self.log.error("database error in _get_chatter_targets") + self.log.exception(e) raise + finally: cur.close() def _get_one_chatter_target(self): """Select one random chatter target.""" @@ -486,37 +482,39 @@ class Markov(Module): def _get_max_chain_id(self): """Get the highest id in the chain table.""" + db = self.get_db() try: - db = self.get_db() query = ''' SELECT id FROM markov_chain ORDER BY id DESC LIMIT 1 ''' - cursor = db.execute(query) - result = cursor.fetchone() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(query) + result = cur.fetchone() if result: return result['id'] else: return None - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error in Markov._get_max_chain_id: ' + str(e)) + except mdb.Error as e: + self.log.error("database error in _get_max_chain_id") + self.log.exception(e) raise + finally: cur.close() def _get_context_id_for_target(self, target): """Get the context ID for the desired/input target.""" + db = self.get_db() try: - db = self.get_db() query = ''' SELECT mc.id FROM markov_context mc INNER JOIN markov_target_to_context_map mt ON mt.context_id = mc.id - WHERE mt.target = ? + WHERE mt.target = %s ''' - cursor = db.execute(query, (target,)) - result = cursor.fetchone() + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(query, (target,)) + result = cur.fetchone() db.close() if result: return result['id'] @@ -524,53 +522,55 @@ class Markov(Module): # auto-generate a context to keep things private self._add_context_for_target(target) return self._get_context_id_for_target(target) - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error in Markov._get_context_id_for_target: ' + str(e)) + except mdb.Error as e: + self.log.error("database error in _get_context_id_for_target") + self.log.exception(e) raise + finally: cur.close() def _add_context_for_target(self, target): """Create a new context for the desired/input target.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() - statement = 'INSERT INTO markov_context (context) VALUES (?)' + statement = 'INSERT INTO markov_context (context) VALUES (%s)' + cur = db.cursor(mdb.cursors.DictCursor) cur.execute(statement, (target,)) statement = ''' INSERT INTO markov_target_to_context_map (target, context_id) - VALUES (?, (SELECT id FROM markov_context WHERE context = ?)) + VALUES (%s, (SELECT id FROM markov_context WHERE context = %s)) ''' cur.execute(statement, (target,target)) db.commit() - db.close() - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error in Markov._add_context_for_target: " + str(e)) + self.log.error("database error in _add_context_for_target") + self.log.exception(e) raise + finally: cur.close() + try: - db = self.get_db() query = ''' SELECT mc.id FROM markov_context mc INNER JOIN markov_target_to_context_map mt ON mt.context_id = mc.id - WHERE mt.target = ? + WHERE mt.target = %s ''' - cursor = db.execute(query, (target,)) - result = cursor.fetchone() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(query, (target,)) + result = cur.fetchone() if result: return result['id'] else: # auto-generate a context to keep things private self._add_context_for_target(target) return self._get_context_id_for_target(target) - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error in Markov._get_context_id_for_target: ' + str(e)) + except mdb.Error as e: + self.log.error("database error in _get_context_id_for_target") + self.log.exception(e) raise + finally: cur.close() # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on; diff --git a/modules/Pi.py b/modules/Pi.py index 1f1af2e..04e400e 100644 --- a/modules/Pi.py +++ b/modules/Pi.py @@ -19,7 +19,8 @@ along with this program. If not, see . import math import random import re -import sqlite3 + +import MySQLdb as mdb from extlib import irclib @@ -46,37 +47,39 @@ class Pi(Module): # create tables db = self.get_db() try: - db.execute(''' + version = 1 + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(''' CREATE TABLE pi_log ( - id INTEGER PRIMARY KEY AUTOINCREMENT, + id SERIAL, count_inside INTEGER NOT NULL, count_total INTEGER NOT NULL, - time TEXT DEFAULT CURRENT_TIMESTAMP - ) + time TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ''') - db.execute(''' + cur.execute(''' CREATE VIEW pi_latest_pi AS SELECT count_inside, count_total FROM pi_log ORDER BY id DESC ''') - db.execute('INSERT INTO drbotzo_modules VALUES (?,?)', (self.__class__.__name__, 1)) db.commit() - db.close() - except sqlite3.Error as e: + self.db_register_module_version(self.__class__.__name__, version) + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def do(self, connection, event, nick, userhost, what, admin_unlocked): match = re.search('^!pi$', what) if match: db = self.get_db() try: - cur = db.cursor() - pi_data = cur.execute('SELECT * FROM pi_latest_pi') - datum = pi_data.fetchone() + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute('SELECT * FROM pi_latest_pi') + datum = cur.fetchone() if datum == None: count_inside = 0 @@ -96,13 +99,15 @@ class Pi(Module): count += 1 pi = 4.0 * count_inside / count - cur.execute('INSERT INTO pi_log (count_inside, count_total) VALUES (?,?)', (count_inside, count)) + cur.execute('INSERT INTO pi_log (count_inside, count_total) VALUES (%s,%s)', + (count_inside, count)) db.commit() - db.close() - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - return self.reply(connection, event, "sqlite error: " + str(e)) + self.log.error("database error doing pi stuff") + self.log.exception(e) + return self.reply(connection, event, "database error doing pi stuff") + finally: cur.close() return self.reply(connection, event, "({0:.10f}, {1:.10f}) is {2}within the circle. pi is {5:.10f}. (i:{3:d} p:{4:d})".format(x, y, "" if inside else "not ", count_inside, count, pi)) diff --git a/modules/Seen.py b/modules/Seen.py index 7442a19..ea06c3e 100644 --- a/modules/Seen.py +++ b/modules/Seen.py @@ -17,7 +17,8 @@ along with this program. If not, see . """ import re -import sqlite3 + +import MySQLdb as mdb from datetime import datetime from dateutil.tz import * @@ -36,89 +37,72 @@ class Seen(Module): if version == None: db = self.get_db() try: - db.execute(''' - CREATE TABLE seen_nicks ( - nick TEXT NOT NULL PRIMARY KEY, - host TEXT NOT NULL, - time TEXT DEFAULT CURRENT_TIMESTAMP, - what TEXT NOT NULL - )''') - sql = 'INSERT INTO drbotzo_modules VALUES (?,?)' - db.execute(sql, (self.__class__.__name__, 1)) - db.commit() - db.close() version = 1 - except sqlite3.Error as e: - db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) - raise - if version < 2: - db = self.get_db() - try: - version = 2 - db.execute('''DROP TABLE seen_nicks''') - db.execute(''' + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(''' CREATE TABLE seen_nicks ( - nick TEXT NOT NULL, - location TEXT NOT NULL, - host TEXT NOT NULL, - time TEXT DEFAULT CURRENT_TIMESTAMP, - what TEXT NOT NULL - )''') - db.execute(''' + nick VARCHAR(64) NOT NULL PRIMARY KEY, + location VARCHAR(64) NOT NULL, + host VARCHAR(256) NOT NULL, + time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + what LONGTEXT NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE UNIQUE INDEX seen_nicks_nick_and_location_index - ON seen_nicks (nick, location)''') + ON seen_nicks (nick, location) + ''') db.commit() - db.close() self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def do(self, connection, event, nick, userhost, what, admin_unlocked): """Track pubmsg/privmsg events, and if asked, report on someone.""" where = event.target() + db = self.get_db() # whatever it is, store it try: # if there's no where, this is probably a sub-command. don't learn it if where: - db = self.get_db() - cur = db.cursor() - statement = 'REPLACE INTO seen_nicks (nick, location, host, what) VALUES (?, ?, ?, ?)' + cur = db.cursor(mdb.cursors.DictCursor) + statement = 'REPLACE INTO seen_nicks (nick, location, host, what) VALUES (%s, %s, %s, %s)' cur.execute(statement, (nick, where, userhost, what.decode('utf-8', 'replace'))) db.commit() - db.close() - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error storing seen data") + self.log.exception(e) raise + finally: cur.close() match = re.search('^!seen\s+(\S+)$', what) if match: nick = match.group(1) + db = self.get_db() try: - db = self.get_db() - query = 'SELECT * FROM seen_nicks WHERE nick = ? AND location = ?' - cursor = db.execute(query, (nick,where)) - result = cursor.fetchone() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + query = 'SELECT * FROM seen_nicks WHERE nick = %s AND location = %s' + cur.execute(query, (nick,where)) + result = cur.fetchone() if result: - seentime = datetime.strptime(result['time'], '%Y-%m-%d %H:%M:%S').replace(tzinfo=tzutc()) + seentime = result['time'].replace(tzinfo=tzlocal()) replystr = 'last saw {0:s} in {3:s} at {1:s} saying \'{2:s}\'.'.format(result['nick'], seentime.astimezone(tzlocal()).strftime('%Y/%m/%d %H:%M:%S %Z'), result['what'].encode('utf-8', 'ignore'), result['location'].encode('utf-8', 'ignore')) return self.reply(connection, event, replystr) else: return self.reply(connection, event, 'i have not seen {0:s} in {1:s}.'.format(nick, where)) - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error retrieving seen data") + self.log.exception(e) raise + finally: cur.close() # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on; diff --git a/modules/Storycraft.py b/modules/Storycraft.py index 3e3f171..b7fa362 100644 --- a/modules/Storycraft.py +++ b/modules/Storycraft.py @@ -18,9 +18,10 @@ along with this program. If not, see . import random import re -import sqlite3 import time +import MySQLdb as mdb + from Module import Module __author__ = "Brian S. Stephan" @@ -101,66 +102,70 @@ class Storycraft(Module): # have to create the database tables db = self.get_db() try: - db.execute(''' + version = 1 + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(''' CREATE TABLE storycraft_config ( - master_channel TEXT NOT NULL, + master_channel VARCHAR(64) NOT NULL, concurrent_games INTEGER NOT NULL, default_round_mode INTEGER NOT NULL, default_game_length INTEGER NOT NULL, default_line_length INTEGER NOT NULL, default_random_method INTEGER NOT NULL, default_lines_per_turn INTEGER NOT NULL - )''') - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' INSERT INTO storycraft_config (master_channel, concurrent_games, default_round_mode, default_game_length, default_line_length, default_random_method, default_lines_per_turn) - VALUES ('#drbotzo', 10, 1, 20, 140, 1, 2) + VALUES ('#dr.botzo', 10, 1, 20, 140, 1, 2) ''') - db.execute(''' + cur.execute(''' CREATE TABLE storycraft_game ( - id INTEGER PRIMARY KEY AUTOINCREMENT, + id SERIAL, round_mode INTEGER NOT NULL, game_length INTEGER NOT NULL, line_length INTEGER NOT NULL, random_method INTEGER NOT NULL, lines_per_turn INTEGER NOT NULL, - status TEXT NOT NULL, - owner_nick TEXT NOT NULL, - owner_userhost TEXT NOT NULL, - start_time TEXT DEFAULT CURRENT_TIMESTAMP, - end_time TEXT DEFAULT NULL - )''') - db.execute(''' + status VARCHAR(16) NOT NULL, + owner_nick VARCHAR(64) NOT NULL, + owner_userhost VARCHAR(256) NOT NULL, + start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + end_time TIMESTAMP NULL DEFAULT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE storycraft_player ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - game_id INTEGER NOT NULL, - nick TEXT NOT NULL, - userhost TEXT NOT NULL, + id SERIAL, + game_id BIGINT(20) UNSIGNED NOT NULL, + nick VARCHAR(64) NOT NULL, + userhost VARCHAR(256) NOT NULL, FOREIGN KEY(game_id) REFERENCES storycraft_game(id) - )''') - db.execute(''' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') + cur.execute(''' CREATE TABLE storycraft_line ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - game_id INTEGER NOT NULL, - player_id INTEGER NOT NULL, - line TEXT NOT NULL, - time TEXT DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY(game_id) REFERENCES storycraft_game(id) + id SERIAL, + game_id BIGINT(20) UNSIGNED NOT NULL, + player_id BIGINT(20) UNSIGNED NOT NULL, + line LONGTEXT NOT NULL, + time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY(game_id) REFERENCES storycraft_game(id), FOREIGN KEY(player_id) REFERENCES storycraft_player(id) - )''') + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + ''') - sql = 'INSERT INTO drbotzo_modules VALUES (?,?)' - db.execute(sql, (self.__class__.__name__, 1)) db.commit() - db.close() - version = 1 - except sqlite3.Error as e: + self.db_register_module_version(self.__class__.__name__, version) + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def do(self, connection, event, nick, userhost, what, admin_unlocked): """Pass storycraft control commands to the appropriate method based on input.""" @@ -525,18 +530,18 @@ class Storycraft(Module): def _get_game_details(self, game_id): """Get the details of one game.""" + db = self.get_db() try: + cur = db.cursor(mdb.cursors.DictCursor) # get the specified game and populate a StorycraftGame - db = self.get_db() query = ''' SELECT id, round_mode, game_length, line_length, random_method, lines_per_turn, status, owner_nick, owner_userhost, start_time, end_time - FROM storycraft_game WHERE id = ? + FROM storycraft_game WHERE id = %s ''' - cursor = db.execute(query, (game_id,)) - result = cursor.fetchone() - db.close() + cur.execute(query, (game_id,)) + result = cur.fetchone() if result: game = self.StorycraftGame() @@ -553,10 +558,11 @@ class Storycraft(Module): game.end_time = result['end_time'] return game - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get game details") + self.log.exception(e) raise + finally: cur.close() def _get_game_summary(self, game): """Display game info for a general summary.""" @@ -601,35 +607,37 @@ class Storycraft(Module): def _add_new_game(self, round_mode, game_length, line_length, random_method, lines_per_turn, nick, userhost): """Add a new game to the system.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' INSERT INTO storycraft_game ( round_mode, game_length, line_length, random_method, lines_per_turn, status, owner_nick, owner_userhost - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ''' - cur.execute(statement, (round_mode, game_length, line_length, random_method, lines_per_turn, 'OPEN', nick, userhost)) + cur.execute(statement, (round_mode, game_length, line_length, + random_method, lines_per_turn, 'OPEN', nick, userhost)) db.commit() - db.close() return cur.lastrowid - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error during add new game") + self.log.exception(e) raise + finally: cur.close() def _get_player_list_for_game(self, game_id): """Get the list of players in one game.""" players = [] + db = self.get_db() try: # get the players for specified game and populate a list - db = self.get_db() - query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE game_id = ?' - cursor = db.execute(query, (game_id,)) - results = cursor.fetchall() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE game_id = %s' + cur.execute(query, (game_id,)) + results = cur.fetchall() for result in results: player = self.StorycraftPlayer() @@ -641,10 +649,11 @@ class Storycraft(Module): players.append(player) return players - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during getting player list for game") + self.log.exception(e) raise + finally: cur.close() def _get_game_exists(self, game_id): """Return the existence of a game.""" @@ -654,20 +663,21 @@ class Storycraft(Module): def _start_game(self, game_id): """Start a game, if it's currently open.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' - UPDATE storycraft_game SET status = 'IN PROGRESS' WHERE status = 'OPEN' AND id = ? + UPDATE storycraft_game SET status = 'IN PROGRESS' WHERE status = 'OPEN' AND id = %s ''' cur.execute(statement, (game_id,)) db.commit() - db.close() return game_id - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error during start game") + self.log.exception(e) raise + finally: cur.close() def _pick_next_player(self, game_id): """Based on the game's settings and state, set who will be replying next.""" @@ -717,51 +727,53 @@ class Storycraft(Module): def _assign_game_to_player(self, game_id, player_id): """Assign the game to a player, prompting them for responses.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' INSERT INTO storycraft_line (game_id, player_id, line) - VALUES (?, ?, ?) + VALUES (%s, %s, %s) ''' cur.execute(statement, (game_id, player_id, '')) db.commit() - db.close() return cur.lastrowid - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error during assign game to player") + self.log.exception(e) raise + finally: cur.close() def _end_game(self, game_id): """End the given game, disallowing adding lines.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' UPDATE storycraft_game SET status = 'COMPLETED', end_time = CURRENT_TIMESTAMP - WHERE status = 'IN PROGRESS' AND id = ? + WHERE status = 'IN PROGRESS' AND id = %s ''' cur.execute(statement, (game_id,)) db.commit() - db.close() return game_id - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error during end game") + self.log.exception(e) raise + finally: cur.close() def _get_player_by_id(self, player_id): """Get the player details based on id.""" + db = self.get_db() try: # get the specified player and populate a StorycraftPlayer - db = self.get_db() - query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE id = ?' - cursor = db.execute(query, (player_id,)) - result = cursor.fetchone() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE id = %s' + cur.execute(query, (player_id,)) + result = cur.fetchone() if result: player = self.StorycraftPlayer() @@ -771,21 +783,22 @@ class Storycraft(Module): player.userhost = result['userhost'] return player - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get player by id") + self.log.exception(e) raise + finally: cur.close() def _get_player_by_userhost_in_game(self, game_id, userhost): """Get the player details if they exist in the given game.""" + db = self.get_db() try: # get the specified player if they exist in the game and populate a StorycraftPlayer - db = self.get_db() - query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE game_id = ? AND userhost = ?' - cursor = db.execute(query, (game_id,userhost)) - result = cursor.fetchone() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE game_id = %s AND userhost = %s' + cur.execute(query, (game_id,userhost)) + result = cur.fetchone() if result: player = self.StorycraftPlayer() @@ -795,10 +808,11 @@ class Storycraft(Module): player.userhost = result['userhost'] return player - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get player by userhost") + self.log.exception(e) raise + finally: cur.close() def _get_completed_games(self): """Get the games with COMPLETED status.""" @@ -819,20 +833,20 @@ class Storycraft(Module): """Return the in progress/open games that include the player nick.""" games = [] + db = self.get_db() try: # get the games of specified type and populate a list - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT game.id, game.round_mode, game.game_length, game.line_length, game.random_method, game.lines_per_turn, game.status, game.owner_nick, game.owner_userhost, game.start_time, game.end_time FROM storycraft_game game INNER JOIN storycraft_player player ON player.game_id = game.id - WHERE player.nick = ? AND (game.status = 'OPEN' OR game.status = 'IN PROGRESS') + WHERE player.nick = %s AND (game.status = 'OPEN' OR game.status = 'IN PROGRESS') ''' - cursor = db.execute(query, (player_nick,)) - results = cursor.fetchall() - db.close() + cur.execute(query, (player_nick,)) + results = cur.fetchall() for result in results: game = self.StorycraftGame() @@ -851,10 +865,11 @@ class Storycraft(Module): games.append(game) return games - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get active games with player") + self.log.exception(e) raise + finally: cur.close() def _get_games_waiting_on_player(self, player_nick): """Return the games where the player nick is the owner of a pending line. @@ -864,9 +879,10 @@ class Storycraft(Module): """ games = [] + db = self.get_db() try: # get the games of specified type and populate a list - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT game.id, game.round_mode, game.game_length, game.line_length, game.random_method, game.lines_per_turn, game.status, game.owner_nick, @@ -874,56 +890,16 @@ class Storycraft(Module): FROM storycraft_game game INNER JOIN storycraft_player player ON player.game_id = game.id INNER JOIN storycraft_line line ON line.player_id = player.id - WHERE player.nick = ? AND game.status = 'IN PROGRESS' AND line.line = '' + WHERE player.nick = %s AND game.status = 'IN PROGRESS' AND line.line = '' UNION SELECT game.id, game.round_mode, game.game_length, game.line_length, game.random_method, game.lines_per_turn, game.status, game.owner_nick, game.owner_userhost, game.start_time, game.end_time FROM storycraft_game game - WHERE game.owner_nick = ? AND game.status = 'OPEN' + WHERE game.owner_nick = %s AND game.status = 'OPEN' ''' - cursor = db.execute(query, (player_nick, player_nick)) - results = cursor.fetchall() - db.close() - for result in results: - game = self.StorycraftGame() - - game.id = int(result['game.id']) - game.round_mode = int(result['game.round_mode']) - game.game_length = int(result['game.game_length']) - game.line_length = int(result['game.line_length']) - game.random_method = int(result['game.random_method']) - game.lines_per_turn = int(result['game.lines_per_turn']) - game.status = result['game.status'] - game.owner_nick = result['game.owner_nick'] - game.owner_userhost = result['game.owner_userhost'] - game.start_time = result['game.start_time'] - game.end_time = result['game.end_time'] - - games.append(game) - - return games - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) - raise - - def _get_games_of_type(self, game_type): - """Return the games of the specified type.""" - - games = [] - try: - # get the games of specified type and populate a list - db = self.get_db() - query = ''' - SELECT id, round_mode, game_length, line_length, random_method, - lines_per_turn, status, owner_nick, owner_userhost, start_time, - end_time - FROM storycraft_game WHERE status = ? - ''' - cursor = db.execute(query, (game_type,)) - results = cursor.fetchall() - db.close() + cur.execute(query, (player_nick, player_nick)) + results = cur.fetchall() for result in results: game = self.StorycraftGame() @@ -942,29 +918,71 @@ class Storycraft(Module): games.append(game) return games - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get games waiting on player") + self.log.exception(e) raise + finally: cur.close() + + def _get_games_of_type(self, game_type): + """Return the games of the specified type.""" + + games = [] + db = self.get_db() + try: + # get the games of specified type and populate a list + cur = db.cursor(mdb.cursors.DictCursor) + query = ''' + SELECT id, round_mode, game_length, line_length, random_method, + lines_per_turn, status, owner_nick, owner_userhost, start_time, + end_time + FROM storycraft_game WHERE status = %s + ''' + cur.execute(query, (game_type,)) + results = cur.fetchall() + for result in results: + game = self.StorycraftGame() + + game.id = int(result['id']) + game.round_mode = int(result['round_mode']) + game.game_length = int(result['game_length']) + game.line_length = int(result['line_length']) + game.random_method = int(result['random_method']) + game.lines_per_turn = int(result['lines_per_turn']) + game.status = result['status'] + game.owner_nick = result['owner_nick'] + game.owner_userhost = result['owner_userhost'] + game.start_time = result['start_time'] + game.end_time = result['end_time'] + + games.append(game) + + return games + except mdb.Error as e: + self.log.error("database error during get games of type") + self.log.exception(e) + raise + finally: cur.close() def _add_player_to_game(self, game_id, nick, userhost): """Add a player to a game, so that they may eventually play.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' INSERT INTO storycraft_player (game_id, nick, userhost) - VALUES (?, ?, ?) + VALUES (%s, %s, %s) ''' cur.execute(statement, (game_id, nick, userhost)) db.commit() - db.close() return cur.lastrowid - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error during add player to game") + self.log.exception(e) raise + finally: cur.close() def _get_player_exists_in_game(self, game_id, userhost): """Return the existence of a player in a game.""" @@ -998,19 +1016,20 @@ class Storycraft(Module): """Return the number of games of the specified type.""" count = 0 + db = self.get_db() try: # get count of game_type games - db = self.get_db() - query = 'SELECT COUNT(*) FROM storycraft_game WHERE status = ?' - cursor = db.execute(query, (game_type,)) - result = cursor.fetchone() - db.close() + cur = db.cursor(mdb.cursors.DictCursor) + query = 'SELECT COUNT(*) FROM storycraft_game WHERE status = %s' + cur.execute(query, (game_type,)) + result = cur.fetchone() if result: count = result['COUNT(*)'] - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get game type count") + self.log.exception(e) raise + finally: cur.close() return count @@ -1018,19 +1037,20 @@ class Storycraft(Module): """Return the current game server concurrency.""" concurrency = 0 + db = self.get_db() try: # get the concurrency value from config table - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = 'SELECT concurrent_games FROM storycraft_config' - cursor = db.execute(query) - result = cursor.fetchone() - db.close() + cur.execute(query) + result = cur.fetchone() if result: concurrency = result['concurrent_games'] - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get concurrent game count") + self.log.exception(e) raise + finally: cur.close() return concurrency @@ -1038,17 +1058,17 @@ class Storycraft(Module): """Get the lines for the specified game_id.""" lines = [] + db = self.get_db() try: # get the games of specified type and populate a list - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT id, game_id, player_id, line, time - FROM storycraft_line WHERE game_id = ? + FROM storycraft_line WHERE game_id = %s ORDER BY time DESC, id DESC ''' - cursor = db.execute(query, (game_id,)) - results = cursor.fetchall() - db.close() + cur.execute(query, (game_id,)) + results = cur.fetchall() for result in results: line = self.StorycraftLine() @@ -1061,28 +1081,30 @@ class Storycraft(Module): lines.append(line) return lines - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get lines for game") + self.log.exception(e) raise + finally: cur.close() def _update_line(self, line_id, input_line): """Update the specified line with the given text.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() + cur = db.cursor(mdb.cursors.DictCursor) statement = ''' - UPDATE storycraft_line SET line = ?, time = CURRENT_TIMESTAMP WHERE id = ? + UPDATE storycraft_line SET line = %s, time = CURRENT_TIMESTAMP WHERE id = %s ''' cur.execute(statement, (input_line.decode('utf-8', 'replace'), line_id)) db.commit() - db.close() return line_id - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + db.rollback() + self.log.error("database error during update line") + self.log.exception(e) raise + finally: cur.close() def _export_game_to_disk(self, game, lines): """Write a game to disk.""" @@ -1118,18 +1140,18 @@ class Storycraft(Module): def _get_storycraft_settings(self): """Get the server settings.""" + db = self.get_db() try: # get the settings and return in StorycraftSettings - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT master_channel, concurrent_games, default_round_mode, default_game_length, default_line_length, default_random_method, default_lines_per_turn FROM storycraft_config ''' - cursor = db.execute(query) - result = cursor.fetchone() - db.close() + cur.execute(query) + result = cur.fetchone() if result: settings = self.StorycraftSettings() @@ -1142,9 +1164,10 @@ class Storycraft(Module): settings.default_lines_per_turn = int(result['default_lines_per_turn']) return settings - except sqlite3.Error as e: - db.close() - self.log.error('sqlite error: ' + str(e)) + except mdb.Error as e: + self.log.error("database error during get storycraft settings") + self.log.exception(e) raise + finally: cur.close() # vi:tabstop=4:expandtab:autoindent diff --git a/modules/Twitter.py b/modules/Twitter.py index d26b98f..0686eed 100644 --- a/modules/Twitter.py +++ b/modules/Twitter.py @@ -19,11 +19,12 @@ along with this program. If not, see . from ConfigParser import NoSectionError, NoOptionError import oauth2 as oauth import re -import sqlite3 import thread import time import urlparse +import MySQLdb as mdb + from extlib import irclib from extlib import twitter @@ -96,41 +97,28 @@ class Twitter(Module): # init the table if it doesn't exist version = self.db_module_registered(self.__class__.__name__) if version == None or version < 1: - # create tables db = self.get_db() + # create tables try: version = 1 - - db.execute(""" + cur = db.cursor(mdb.cursors.DictCursor) + cur.execute(""" CREATE TABLE twitter_settings ( - since_id INTEGER NOT NULL, - output_channel TEXT NOT NULL - )""") - db.execute("""INSERT INTO twitter_settings (since_id, output_channel) VALUES (0, '#drbotzo')""") + since_id SERIAL, + output_channel VARCHAR(64) NOT NULL, + oauth_token VARCHAR(256) DEFAULT NULL, + oauth_token_secret VARCHAR(256) DEFAULT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 + """) + cur.execute("""INSERT INTO twitter_settings (since_id, output_channel) VALUES (0, '#drbotzo')""") db.commit() - db.close() self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: + except mdb.Error as e: db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) - raise - if version < 2: - db = self.get_db() - try: - version = 2 - - db.execute("""ALTER TABLE twitter_settings ADD COLUMN oauth_token TEXT DEFAULT NULL""") - db.execute("""ALTER TABLE twitter_settings ADD COLUMN oauth_token_secret TEXT DEFAULT NULL""") - - db.commit() - db.close() - self.db_register_module_version(self.__class__.__name__, version) - except sqlite3.Error as e: - db.rollback() - db.close() - self.log.error("sqlite error: " + str(e)) + self.log.error("database error trying to create tables") + self.log.exception(e) raise + finally: cur.close() def shutdown(self): """Deauth, and make the twitter API item inoperable.""" @@ -395,49 +383,51 @@ class Twitter(Module): def _get_last_since_id(self): """Get the since_id out of the database.""" + db = self.get_db() try: - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = "SELECT since_id FROM twitter_settings" - cursor = db.execute(query) - result = cursor.fetchone() - db.close() + cur.execute(query) + result = cur.fetchone() if result: return result['since_id'] - except sqlite3.Error as e: - db.close() - self.log.error("sqlite error: " + str(e)) + except mdb.Error as e: + self.log.error("database error getting last since ID") + self.log.exception(e) raise + finally: cur.close() def _get_output_channel(self): """Get the output_channel out of the database.""" + db = self.get_db() try: - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = "SELECT output_channel FROM twitter_settings" - cursor = db.execute(query) - result = cursor.fetchone() - db.close() + cur.execute(query) + result = cur.fetchone() if result: return result['output_channel'] - except sqlite3.Error as e: - db.close() - self.log.error("sqlite error: " + str(e)) + except mdb.Error as e: + self.log.error("database error getting output channel") + self.log.exception(e) raise + finally: cur.close() def _set_last_since_id(self, since_id): """Set the since_id.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() - statement = "UPDATE twitter_settings SET since_id = ?" + cur = db.cursor(mdb.cursors.DictCursor) + statement = "UPDATE twitter_settings SET since_id = %s" cur.execute(statement, (since_id,)) db.commit() - db.close() - except sqlite3.Error as e: - db.close() - self.log.error("sqlite error: " + str(e)) + except mdb.Error as e: + self.log.error("database error saving last since ID") + self.log.exception(e) raise + finally: cur.close() def _get_latest_tweet_id(self, tweets, since_id): """Find the latest tweet id in the provided list, or the given since_id.""" @@ -452,34 +442,34 @@ class Twitter(Module): def _persist_auth_tokens(self, oauth_token, oauth_token_secret): """Save the auth tokens to the database, with the intent of reusing them.""" + db = self.get_db() try: - db = self.get_db() - cur = db.cursor() - statement = "UPDATE twitter_settings SET oauth_token = ?, oauth_token_secret = ?" + cur = db.cursor(mdb.cursors.DictCursor) + statement = "UPDATE twitter_settings SET oauth_token = %s, oauth_token_secret = %s" cur.execute(statement, (oauth_token, oauth_token_secret)) db.commit() - db.close() - except sqlite3.Error as e: - db.close() - self.log.error("sqlite error: " + str(e)) + except mdb.Error as e: + self.log.error("database error saving auth tokens") + self.log.exception(e) raise + finally: cur.close() def _retrieve_stored_auth_tokens(self): """Check the database for existing auth tokens, try reusing them.""" + db = self.get_db() try: - db = self.get_db() + cur = db.cursor(mdb.cursors.DictCursor) query = "SELECT oauth_token, oauth_token_secret FROM twitter_settings" - cursor = db.execute(query) - result = cursor.fetchone() - db.close() + cur.execute(query) + result = cur.fetchone() if result: return (result['oauth_token'], result['oauth_token_secret']) - except sqlite3.Error as e: - db.close() - self.log.error("sqlite error: " + str(e)) + except mdb.Error as e: + self.log.error("database error retrieving auth tokens") + self.log.exception(e) raise - + finally: cur.close() # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on; diff --git a/requirements.txt b/requirements.txt index a64a319..f8d2f37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +MySQL-python==1.2.3 httplib2==0.7.4 oauth2==1.5.211 ply==3.4