Achievements is old and unused. deleted
This commit is contained in:
parent
16fa94cb24
commit
c6cc9c4c62
@ -1,470 +0,0 @@
|
|||||||
"""
|
|
||||||
Achievements - gamifying IRC
|
|
||||||
Copyright (C) 2011 Brian S. Stephan
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import thread
|
|
||||||
import time
|
|
||||||
|
|
||||||
import MySQLdb as mdb
|
|
||||||
|
|
||||||
from Module import Module
|
|
||||||
|
|
||||||
from extlib import irclib
|
|
||||||
|
|
||||||
__author__ = "Brian S. Stephan"
|
|
||||||
__copyright__ = "Copyright 2011, Brian S. Stephan"
|
|
||||||
__credits__ = ["Brian S. Stephan", "#lh"]
|
|
||||||
__license__ = "GPL"
|
|
||||||
__version__ = "0.1"
|
|
||||||
__maintainer__ = "Brian S. Stephan"
|
|
||||||
__email__ = "bss@incorporeal.org"
|
|
||||||
__status__ = "Development"
|
|
||||||
|
|
||||||
class Achievements(Module):
|
|
||||||
|
|
||||||
"""Give out achievements for doing stuff on IRC, because why not."""
|
|
||||||
|
|
||||||
class AchievementsSettings():
|
|
||||||
|
|
||||||
"""Track system settings."""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __init__(self, irc, config):
|
|
||||||
"""Set up trigger regexes."""
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
joinpattern = '^!achievements\s+join$'
|
|
||||||
leavepattern = '^!achievements\s+leave$'
|
|
||||||
infopattern = '^!achievements\s+info\s+(.*)$'
|
|
||||||
rankpattern = '^!achievements\s+rank\s+(.*)$'
|
|
||||||
|
|
||||||
self.joinre = re.compile(joinpattern)
|
|
||||||
self.leavere = re.compile(leavepattern)
|
|
||||||
self.infore = re.compile(infopattern)
|
|
||||||
self.rankre = re.compile(rankpattern)
|
|
||||||
|
|
||||||
Module.__init__(self, irc, config)
|
|
||||||
|
|
||||||
self.next_achievements_scan = 0
|
|
||||||
thread.start_new_thread(self.thread_do, ())
|
|
||||||
|
|
||||||
def db_init(self):
|
|
||||||
"""Set up the database tables, if they don't exist."""
|
|
||||||
|
|
||||||
version = self.db_module_registered(self.__class__.__name__)
|
|
||||||
if (version == None):
|
|
||||||
# have to create the database tables
|
|
||||||
db = self.get_db()
|
|
||||||
try:
|
|
||||||
version = 1
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE achievements_player (
|
|
||||||
id SERIAL,
|
|
||||||
nick VARCHAR(64) NOT NULL UNIQUE,
|
|
||||||
userhost VARCHAR(256) NOT NULL DEFAULT '',
|
|
||||||
is_playing INTEGER NOT NULL DEFAULT 0,
|
|
||||||
last_seen_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin
|
|
||||||
''')
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE achievements_event (
|
|
||||||
id SERIAL,
|
|
||||||
player_id BIGINT(20) UNSIGNED NOT NULL,
|
|
||||||
event VARCHAR(64) NOT NULL,
|
|
||||||
target VARCHAR(64),
|
|
||||||
msg_len INTEGER,
|
|
||||||
event_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY(player_id) REFERENCES achievements_player(id)
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin
|
|
||||||
''')
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE achievements_achievement (
|
|
||||||
id SERIAL,
|
|
||||||
name VARCHAR(256) NOT NULL,
|
|
||||||
description VARCHAR(256) NOT NULL,
|
|
||||||
query VARCHAR(1024) NOT NULL
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin
|
|
||||||
''')
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE achievements_log (
|
|
||||||
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)
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin
|
|
||||||
''')
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE achievements_config (
|
|
||||||
channel TEXT NOT NULL
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin
|
|
||||||
''')
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE achievements_filter (
|
|
||||||
id SERIAL,
|
|
||||||
filter VARCHAR(256) NOT NULL
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin
|
|
||||||
''')
|
|
||||||
cur.execute('''
|
|
||||||
CREATE TABLE achievements_filter_log (
|
|
||||||
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 CHARACTER SET utf8 COLLATE utf8_bin
|
|
||||||
''')
|
|
||||||
db.commit()
|
|
||||||
self.db_register_module_version(self.__class__.__name__, version)
|
|
||||||
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 register_handlers(self):
|
|
||||||
"""Handle all sorts of things to track."""
|
|
||||||
|
|
||||||
self.irc.server.add_global_handler('pubmsg', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('pubnotice', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('privmsg', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('privnotice', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('join', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('kick', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('mode', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('part', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('quit', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('invite', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('action', self.track_irc_event)
|
|
||||||
self.irc.server.add_global_handler('topic', self.track_irc_event)
|
|
||||||
|
|
||||||
self.irc.server.add_global_handler('pubmsg', self.on_pub_or_privmsg)
|
|
||||||
self.irc.server.add_global_handler('privmsg', self.on_pub_or_privmsg)
|
|
||||||
|
|
||||||
def unregister_handlers(self):
|
|
||||||
self.irc.server.remove_global_handler('pubmsg', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('pubnotice', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('privmsg', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('privnotice', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('join', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('kick', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('mode', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('part', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('quit', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('invite', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('action', self.track_irc_event)
|
|
||||||
self.irc.server.remove_global_handler('topic', self.track_irc_event)
|
|
||||||
|
|
||||||
self.irc.server.remove_global_handler('pubmsg', self.on_pub_or_privmsg)
|
|
||||||
self.irc.server.remove_global_handler('privmsg', self.on_pub_or_privmsg)
|
|
||||||
|
|
||||||
def track_irc_event(self, connection, event):
|
|
||||||
"""Put events in the database."""
|
|
||||||
|
|
||||||
if event.source():
|
|
||||||
if event.source().find('!') >= 0:
|
|
||||||
nick = irclib.nm_to_n(event.source())
|
|
||||||
userhost = irclib.nm_to_uh(event.source())
|
|
||||||
self.log.debug('good: ' + nick + ' ' + userhost + ' ' + event.eventtype() + ' ' + str(event.target()))
|
|
||||||
if event.arguments():
|
|
||||||
msg = event.arguments()[0]
|
|
||||||
msg_len = len(event.arguments()[0])
|
|
||||||
else:
|
|
||||||
msg = ''
|
|
||||||
msg_len = 0
|
|
||||||
|
|
||||||
player_id = self._get_or_add_player(nick, userhost)
|
|
||||||
self._add_event(player_id, event.eventtype(), event.target(), msg, msg_len)
|
|
||||||
else:
|
|
||||||
self.log.debug('bad: ' + event.source() + ' ' + event.eventtype())
|
|
||||||
else:
|
|
||||||
self.log.debug('really bad: ' + event.eventtype())
|
|
||||||
|
|
||||||
def do(self, connection, event, nick, userhost, what, admin_unlocked):
|
|
||||||
"""Do stuff when commanded."""
|
|
||||||
|
|
||||||
if self.joinre.search(what):
|
|
||||||
return self.irc.reply(event, self._join_system(nick))
|
|
||||||
elif self.leavere.search(what):
|
|
||||||
return self.irc.reply(event, self._leave_system(nick))
|
|
||||||
elif self.infore.search(what):
|
|
||||||
match = self.infore.search(what)
|
|
||||||
achievement = match.group(1)
|
|
||||||
desc = self._get_achievement_info(achievement)
|
|
||||||
if desc:
|
|
||||||
return self.irc.reply(event, achievement + ': ' + desc)
|
|
||||||
elif self.rankre.search(what):
|
|
||||||
match = self.rankre.search(what)
|
|
||||||
player = match.group(1)
|
|
||||||
achievements = self._get_player_achievements(player)
|
|
||||||
if len(achievements):
|
|
||||||
return self.irc.reply(event, player + ' has obtained ' + ', '.join(achievements))
|
|
||||||
|
|
||||||
def thread_do(self):
|
|
||||||
"""Do the scan for achievements and other miscellaneous tasks."""
|
|
||||||
|
|
||||||
while not self.is_shutdown:
|
|
||||||
self._do_achievement_scan()
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
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:
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
statement = '''
|
|
||||||
INSERT IGNORE INTO achievements_player (nick) VALUES (%s)'''
|
|
||||||
cur.execute(statement, (nick,))
|
|
||||||
statement = '''
|
|
||||||
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 = %s'''
|
|
||||||
cur.execute(statement, (nick,))
|
|
||||||
result = cur.fetchone()
|
|
||||||
return result['id']
|
|
||||||
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:
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
statement = '''
|
|
||||||
INSERT INTO achievements_event (
|
|
||||||
player_id, event, target, msg_len
|
|
||||||
) VALUES (%s, %s, %s, %s)
|
|
||||||
'''
|
|
||||||
cur.execute(statement, (player_id, event, target, msg_len))
|
|
||||||
db.commit()
|
|
||||||
event_id = cur.lastrowid
|
|
||||||
|
|
||||||
# now see if the event matched any filters
|
|
||||||
query = '''
|
|
||||||
SELECT id FROM achievements_filter WHERE %s REGEXP filter
|
|
||||||
'''
|
|
||||||
cur.execute(query, (msg,))
|
|
||||||
results = cur.fetchall()
|
|
||||||
for result in results:
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
statement = '''
|
|
||||||
INSERT INTO achievements_filter_log (filter_id, event_id)
|
|
||||||
VALUES (%s, %s)
|
|
||||||
'''
|
|
||||||
cur.execute(statement, (result['id'], event_id))
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
return event_id
|
|
||||||
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
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
query = 'SELECT channel FROM achievements_config'
|
|
||||||
cur.execute(query)
|
|
||||||
result = cur.fetchone()
|
|
||||||
if result:
|
|
||||||
settings = self.AchievementsSettings()
|
|
||||||
settings.channel = result['channel']
|
|
||||||
|
|
||||||
return settings
|
|
||||||
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 system."""
|
|
||||||
|
|
||||||
db = self.get_db()
|
|
||||||
try:
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
statement = 'UPDATE achievements_player SET is_playing = 1 WHERE nick = %s'
|
|
||||||
cur.execute(statement, (nick,))
|
|
||||||
db.commit()
|
|
||||||
return nick + ' joined.'
|
|
||||||
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 system."""
|
|
||||||
|
|
||||||
db = self.get_db()
|
|
||||||
try:
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
statement = 'UPDATE achievements_player SET is_playing = 0 WHERE nick = %s'
|
|
||||||
cur.execute(statement, (nick,))
|
|
||||||
db.commit()
|
|
||||||
return nick + ' left.'
|
|
||||||
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:
|
|
||||||
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()
|
|
||||||
return
|
|
||||||
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:
|
|
||||||
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['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:
|
|
||||||
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 = %s
|
|
||||||
'''
|
|
||||||
cur.execute(query, (nick,))
|
|
||||||
results = cur.fetchall()
|
|
||||||
for result in results:
|
|
||||||
achievements.append(result['name'])
|
|
||||||
|
|
||||||
return achievements
|
|
||||||
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."""
|
|
||||||
|
|
||||||
# don't do anything the first time
|
|
||||||
if self.next_achievements_scan == 0:
|
|
||||||
self.next_achievements_scan = time.time() + 300
|
|
||||||
|
|
||||||
if self.next_achievements_scan < time.time():
|
|
||||||
self.next_achievements_scan = time.time() + 300
|
|
||||||
|
|
||||||
self.log.debug('in achievement scan')
|
|
||||||
settings = self._get_achievements_settings()
|
|
||||||
if settings is not None:
|
|
||||||
channel = settings.channel
|
|
||||||
achievers = self._query_for_new_achievers()
|
|
||||||
for achiever in achievers:
|
|
||||||
self.sendmsg(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:
|
|
||||||
cur = db.cursor(mdb.cursors.DictCursor)
|
|
||||||
query = 'SELECT id, name, query FROM achievements_achievement'
|
|
||||||
cur.execute(query)
|
|
||||||
achievements = cur.fetchall()
|
|
||||||
|
|
||||||
for achievement in achievements:
|
|
||||||
self.log.debug('checking achievement:[' + achievement['name'] + ']')
|
|
||||||
query = '''
|
|
||||||
SELECT p.id, p.nick FROM achievements_player p WHERE
|
|
||||||
p.nick IN (
|
|
||||||
'''
|
|
||||||
query = query + achievement['query']
|
|
||||||
query = query + '''
|
|
||||||
) AND p.is_playing = 1
|
|
||||||
AND p.id NOT IN (
|
|
||||||
SELECT player_id FROM achievements_log l
|
|
||||||
INNER JOIN achievements_achievement a
|
|
||||||
ON a.id = l.achievement_id
|
|
||||||
WHERE a.name = %s
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
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']))
|
|
||||||
|
|
||||||
return achievers
|
|
||||||
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
|
|
Loading…
Reference in New Issue
Block a user