convert to a MySQL backend

WARNING!
there's no going back now. this change is *huge* but it was overdue.
WARNING!

the database backend is now mysql. modules that should use a database
but don't yet were left untouched, they'll come later. scripts haven't
been converted yet, though i'm pretty sure i'll need to soon.

while i was going through everything, connection/cursor idioms were
cleaned up, as were a bunch of log messages and exception handling. this
change is so gross i'm happy things appear to be working, which is
the case --- all modules are lightly tested.
This commit is contained in:
Brian S. Stephan 2012-07-27 02:18:01 -05:00
parent deedf330e3
commit 1a36becead
14 changed files with 760 additions and 763 deletions

View File

@ -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):
"""

View File

@ -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
meta.pubmsg_needs_bot_prefix = false

View File

@ -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))

View File

@ -17,10 +17,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
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

View File

@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import random
import re
import sqlite3
import thread
import time

View File

@ -18,7 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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;

View File

@ -17,7 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import re
import sqlite3
from Module import Module

View File

@ -16,10 +16,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from math import floor
import re
import sqlite3
import string
import MySQLdb as mdb
from Module import Module
__author__ = "Mike Bloy <mike@bloy.org>"
@ -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

View File

@ -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;

View File

@ -19,7 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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))

View File

@ -17,7 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
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;

View File

@ -18,9 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -19,11 +19,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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;

View File

@ -1,3 +1,4 @@
MySQL-python==1.2.3
httplib2==0.7.4
oauth2==1.5.211
ply==3.4