diff --git a/ircbot/modules/Storycraft.py b/ircbot/modules/Storycraft.py deleted file mode 100644 index 174e97d..0000000 --- a/ircbot/modules/Storycraft.py +++ /dev/null @@ -1,1187 +0,0 @@ -""" -Storycraft - collaborative nonsense story writing -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 . -""" - -import random -import re -import time - -from dateutil.tz import * -import MySQLdb as mdb - -from Module import Module - -__author__ = "Brian S. Stephan" -__copyright__ = "Copyright 2011, Brian S. Stephan" -__credits__ = ["Brian S. Stephan", "Mike Bloy", "#lh"] -__license__ = "GPL" -__version__ = "0.1" -__maintainer__ = "Brian S. Stephan" -__email__ = "bss@incorporeal.org" -__status__ = "Development" - -class Storycraft(Module): - - """Play Storycraft, the game of varying authors adding to a story mostly devoid of context. - - This is a game for people who like making up facts, insulting each other, telling - insane stories, making in-jokes, and generally having a good time writing what can - fairly only be described as pure nonsense. It does not assume that all players are - available at any given time, so games may take a while, but the module allows for - concurrency in the number of games running. - """ - - class StorycraftGame(): - - """Track a storycraft game details.""" - - pass - - class StorycraftPlayer(): - - """Track a storycraft player details.""" - - pass - - class StorycraftLine(): - - """Track a storycraft line details.""" - - pass - - class StorycraftSettings(): - - """Track the settings for the server.""" - - pass - - def __init__(self, irc, config): - """Set up trigger regexes.""" - - Module.__init__(self, irc, config) - - rootcommand = '^!storycraft' - statuspattern = rootcommand + '\s+status$' - newgamepattern = rootcommand + '\s+new\s+game(\s+with\s+config\s+(.*)$|$)' - joingamepattern = rootcommand + '\s+game\s+(\d+)\s+join$' - listgamespattern = rootcommand + '\s+list\s+games\s+(open|in progress|completed|my games|waiting for me)$' - startgamepattern = rootcommand + '\s+game\s+(\d+)\s+start$' - showlinepattern = rootcommand + '\s+game\s+(\d+)\s+show\s+line$' - addlinepattern = rootcommand + '\s+game\s+(\d+)\s+add\s+line\s+(.*)$' - gamestatuspattern = rootcommand + '\s+game\s+(\d+)\s+status$' - exportpattern = rootcommand + '\s+game\s+(\d+)\s+export$' - - self.statusre = re.compile(statuspattern) - self.newgamere = re.compile(newgamepattern) - self.joingamere = re.compile(joingamepattern) - self.listgamesre = re.compile(listgamespattern) - self.startgamere = re.compile(startgamepattern) - self.showlinere = re.compile(showlinepattern) - self.addlinere = re.compile(addlinepattern) - self.gamestatusre = re.compile(gamestatuspattern) - self.exportre = re.compile(exportpattern) - - 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 storycraft_config ( - 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 - ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin - ''') - 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 ('#dr.botzo', 10, 1, 20, 140, 1, 2) - ''') - cur.execute(''' - CREATE TABLE storycraft_game ( - 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 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 CHARACTER SET utf8 COLLATE utf8_bin - ''') - cur.execute(''' - CREATE TABLE storycraft_player ( - 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) - ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin - ''') - cur.execute(''' - CREATE TABLE storycraft_line ( - 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 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 do(self, connection, event, nick, userhost, what, admin_unlocked): - """Pass storycraft control commands to the appropriate method based on input.""" - - if self.statusre.search(what): - return self.irc.reply(event, self.storycraft_status(event, nick, userhost, what, admin_unlocked)) - elif self.newgamere.search(what): - return self.irc.reply(event, self.storycraft_newgame(event, nick, userhost, what, admin_unlocked)) - elif self.joingamere.search(what): - return self.irc.reply(event, self.storycraft_joingame(event, nick, userhost, what, admin_unlocked)) - elif self.listgamesre.search(what): - return self.irc.reply(event, self.storycraft_listgames(event, nick, userhost, what, admin_unlocked)) - elif self.startgamere.search(what): - return self.irc.reply(event, self.storycraft_startgame(event, nick, userhost, what, admin_unlocked)) - elif self.showlinere.search(what): - return self.irc.reply(event, self.storycraft_showline(event, nick, userhost, what, admin_unlocked)) - elif self.addlinere.search(what): - return self.irc.reply(event, self.storycraft_addline(event, nick, userhost, what, admin_unlocked)) - elif self.gamestatusre.search(what): - return self.irc.reply(event, self.storycraft_gamestatus(event, nick, userhost, what, admin_unlocked)) - elif self.exportre.search(what): - return self.irc.reply(event, self.storycraft_export(event, nick, userhost, what, admin_unlocked)) - - def storycraft_status(self, event, nick, userhost, what, admin_unlocked): - """Print information about the storycraft games, or one specific game.""" - - match = self.statusre.search(what) - if match: - # do the general status of all games - count_completed = self._get_completed_game_count() - count_in_progress = self._get_in_progress_game_count() - count_open = self._get_open_game_count() - count_free = self._get_free_game_count() - - return 'Storycraft {0:s} - {1:d} games completed, {2:d} in progress, {3:d} open. {4:d} slots free.'.format(__version__, count_completed, count_in_progress, count_open, count_free) - - def storycraft_gamestatus(self, event, nick, userhost, what, admin_unlocked): - """Print information about one specific game.""" - - match = self.gamestatusre.search(what) - if match: - if match.group(1): - game_id = int(match.group(1)) - - # do the status of one game - game = self._get_game_details(game_id) - if game: - # get game string - status_str = self._get_game_summary(game) - - return status_str - else: - return 'Game #{0:d} does not exist.'.format(game_id) - - def storycraft_newgame(self, event, nick, userhost, what, admin_unlocked): - """Add a game to the system, which users can join.""" - - match = self.newgamere.search(what) - if match: - # ensure the system isn't at capacity - count_free = self._get_free_game_count() - if count_free > 0: - # get the default settings - settings = self._get_storycraft_settings() - - master_channel = settings.master_channel - round_mode = settings.default_round_mode - game_length = settings.default_game_length - line_length = settings.default_line_length - random_method = settings.default_random_method - lines_per_turn = settings.default_lines_per_turn - - # check for custom options - if match.group(2): - options = match.group(2) - - # override settings with provided options - tuples = options.split(';') - for option in tuples: - optpair = option.split(',') - - if len(optpair) == 2: - if optpair[0] == 'round_mode': - round_mode = int(optpair[1]) - elif optpair[0] == 'game_length': - game_length = int(optpair[1]) - elif optpair[0] == 'line_length': - line_length = int(optpair[1]) - elif optpair[0] == 'random_method': - random_method = int(optpair[1]) - elif optpair[0] == 'lines_per_turn': - lines_per_turn = int(optpair[1]) - - # add a new game - game_id = self._add_new_game(round_mode, game_length, line_length, random_method, lines_per_turn, nick, userhost) - if game_id: - # add the player to the game, too - self._add_player_to_game(game_id, nick, userhost) - - # tell the control channel - self.sendmsg(master_channel, '{0:s} created a game of storycraft - do \'!storycraft game {1:d} join\' to take part!'.format(nick, game_id)) - - self.log.debug("{0:s} added a new game".format(nick)) - return 'Game #{0:d} has been created. When everyone has joined, do \'!storycraft game {0:d} start\''.format(game_id) - else: - return 'Error creating game.' - else: - return 'All slots are full.' - - def storycraft_joingame(self, event, nick, userhost, what, admin_unlocked): - """Add a player to an open game.""" - - match = self.joingamere.search(what) - if match: - game_id = int(match.group(1)) - - # ensure the game exists - game = self._get_game_details(game_id) - if game: - # check that the game hasn't started yet - if game.status == 'OPEN': - # see if userhost is already in the game - if not self._get_player_exists_in_game(game_id, userhost): - # add the player - if self._add_player_to_game(game_id, nick, userhost): - # output results - - # get the default settings - settings = self._get_storycraft_settings() - master_channel = settings.master_channel - - self.sendmsg(master_channel, '{0:s} joined storycraft #{1:d}!'.format(nick, game_id)) - self.log.debug("{0:s} joined game #{1:d}".format(nick, game_id)) - return '{0:s}, welcome to the game.'.format(nick) - else: - return 'Error joining game.' - else: - return 'You are already in game #{0:d}.'.format(game_id) - else: - return 'Game #{0:d} is not open for new players.'.format(game_id) - else: - return 'Game #{0:d} does not exist.'.format(game_id) - - def storycraft_listgames(self, event, nick, userhost, what, admin_unlocked): - """Get the listing of either open or in progress games.""" - - match = self.listgamesre.search(what) - if match: - category = match.group(1) - - if category == 'open': - games = self._get_open_games() - elif category == 'in progress': - games = self._get_in_progress_games() - elif category == 'completed': - games = self._get_completed_games() - elif category == 'my games': - games = self._get_active_games_with_player(nick) - elif category == 'waiting for me': - games = self._get_games_waiting_on_player(nick) - - if len(games) > 5: - # just list the game ids - gameids = [] - for game in games: - gameids.append(str(game.id)) - - return 'Too many to list! ids: ' + ','.join(gameids) - else: - # show game details, since there's not many - gamestrs = [] - for game in games: - gamestrs.append(self._get_game_summary(game)) - - return '\n'.join(gamestrs) - - def storycraft_startgame(self, event, nick, userhost, what, admin_unlocked): - """Start a game, closing the period to join and starting line trading.""" - - match = self.startgamere.search(what) - if match: - game_id = int(match.group(1)) - - # retrieve the game - game = self._get_game_details(game_id) - if game: - # check that the user is the owner of the game - if userhost == game.owner_userhost: - # check that the game is open - if 'OPEN' == game.status: - # start the game - if self._start_game(game_id): - # determine the first person to send a line - if self._pick_next_player(game_id): - # indicate the status - game = self._get_game_details(game_id) - line = self._get_lines_for_game(game_id)[0] - player = self._get_player_by_id(line.player_id) - - # get the default settings - settings = self._get_storycraft_settings() - master_channel = settings.master_channel - - # tell the control channel - self.sendmsg(master_channel, '{0:s} started storycraft #{1:d}! - first player is {2:s}, do \'!storycraft game {1:d} show line\' when the game is assigned to you.'.format(nick, game_id, player.nick)) - self.log.debug("{0:s} started game #{1:d}".format(nick, game_id)) - - return 'Game #{0:d} started.'.format(game_id) - else: - return 'Failed to assign game to first player.' - else: - return 'Could not start game.' - else: - return 'Game #{0:d} is not open.'.format(game_id) - else: - return 'You are not the owner of #{0:d}.'.format(game_id) - else: - return 'Game #{0:d} does not exist.'.format(game_id) - - def storycraft_showline(self, event, nick, userhost, what, admin_unlocked): - """List the line to continue with, if queryer is the assignee.""" - - match = self.showlinere.search(what) - if match: - game_id = int(match.group(1)) - - # retrieve the game - game = self._get_game_details(game_id) - if game: - # check that the game is in progress - if 'IN PROGRESS' == game.status: - # get the most recent line - lines = self._get_lines_for_game(game_id) - if lines: - line = lines[0] - - # check that the line is a prompt for input - if line.line == '': - player = self._get_player_by_id(line.player_id) - - # check that the caller is the person the line is assigned to - if player.userhost == userhost: - # check previous line - if len(lines) > 1: - # get previous line and display it - repline = lines[1] - return 'The current line is \'{0:s}\'. Continue the story with \'!storycraft game {1:d} add line YOUR TEXT\'.'.format(repline.line, game_id) - else: - # player is starting the story - return 'You are starting the story! Add the first line with \'!storycraft game {0:d} add line YOUR TEXT\'.'.format(game_id) - else: - return 'You are not the assignee of the current game.' - else: - return 'Game is in progress but the most recent line is not available - internal consistency error.' - else: - return 'Game #{0:d} has no lines - internal consistency error.'.format(game_id) - else: - return 'Game #{0:d} has not been started.'.format(game_id) - else: - return 'Game #{0:d} does not exist.'.format(game_id) - - def storycraft_addline(self, event, nick, userhost, what, admin_unlocked): - """Add a line to an in progress game.""" - - match = self.addlinere.search(what) - if match: - game_id = int(match.group(1)) - input_line = match.group(2) - - # retrieve the game - game = self._get_game_details(game_id) - if game: - # check that the game is in progress - if 'IN PROGRESS' == game.status: - # get the most recent line - lines = self._get_lines_for_game(game_id) - if lines: - line = lines[0] - - # check that the line is a prompt for input - if line.line == '': - player = self._get_player_by_id(line.player_id) - if player.userhost == userhost: - # check the input line length - if len(input_line) <= game.line_length: - # add line - if self._update_line(line.id, input_line): - # determine the first person to send a line - if self._pick_next_player(game_id): - # indicate the status - game = self._get_game_details(game_id) - line = self._get_lines_for_game(game_id)[0] - last_line = self._get_lines_for_game(game_id)[1] - player = self._get_player_by_id(line.player_id) - - return_msg = 'Line logged.' - - # get progress, so user knows how much time is left - progress_str = self._get_progress_string_for_game(game) - - # message the new owner - if player.nick == nick: - # simpily notify them they're up again - return_msg = 'Line logged. Please add another. ' + progress_str - else: - # notify the new owner, too - self.sendmsg(player.nick, 'You have a new line in storycraft #{0:d}: \'{1:s}\' {2:s}'.format(game_id, last_line.line, progress_str)) - - # get the default settings - settings = self._get_storycraft_settings() - master_channel = settings.master_channel - - self.log.debug("{0:s} added a line to #{1:d}".format(nick, game_id)) - # log output - if game.status == 'IN PROGRESS': - self.sendmsg(master_channel, '{0:s} added a line to storycraft #{1:d}! - next player is {2:s}'.format(nick, game_id, player.nick)) - return return_msg - else: - self.sendmsg(master_channel, '{0:s} finished storycraft #{1:d}!'.format(nick, game_id)) - return 'Line logged (and game completed).' - else: - return 'Failed to assign game to next player.' - else: - return 'Failed to save your line.' - else: - return 'The maximum line length for this game is {0:d} characters. (You were over by {1:d}.)'.format(game.line_length, len(input_line)-game.line_length) - else: - return 'You are not the assignee of the current game.' - else: - return 'Game is in progress but the most recent line is not available - internal consistency error.' - else: - return 'Game #{0:d} has no lines - internal consistency error.'.format(game_id) - else: - return 'Game #{0:d} has not been started.'.format(game_id) - else: - return 'Game #{0:d} does not exist.'.format(game_id) - - def storycraft_export(self, event, nick, userhost, what, admin_unlocked): - """Provide the story for access outside of the bot.""" - - match = self.exportre.search(what) - if match: - game_id = int(match.group(1)) - - # retrieve the game - game = self._get_game_details(game_id) - if game: - # check that the game is completed - if 'COMPLETED' == game.status: - # get the most recent line - lines = self._get_lines_for_game(game_id) - if lines: - # write the story to disk - fn = self._export_game_to_disk(game, lines) - if fn: - return 'Story written as {0:s}.'.format(fn) - else: - return 'Error writing story to disk.' - else: - return 'Game #{0:d} has no lines - internal consistency error.'.format(game_id) - else: - return 'Game #{0:d} has not been completed.'.format(game_id) - else: - return 'Game #{0:d} does not exist.'.format(game_id) - - 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 - 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 = %s - ''' - cur.execute(query, (game_id,)) - result = cur.fetchone() - if result: - 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'].replace(tzinfo=tzlocal()) - game.end_time = result['end_time'] - if game.end_time is not None: - game.end_time = game.end_time.replace(tzinfo=tzlocal()) - - return game - 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.""" - - status_str = '#{0:d} - created on {1:s} by {2:s}, {3:s}'.format(game.id, - game.start_time.strftime('%Y/%m/%d %H:%M:%S'), - game.owner_nick, game.status) - if game.status == 'COMPLETED' and game.end_time: - status_str = status_str + ' ({0:s})'.format(game.end_time.strftime('%Y/%m/%d %H:%M:%S')) - elif game.status == 'IN PROGRESS': - lines = self._get_lines_for_game(game.id) - player = self._get_player_by_id(lines[0].player_id) - status_str = status_str + ' ({0:d} lines, next is {1:s})'.format(len(lines)-1, player.nick) - - # get players in game - player_names = [] - players = self._get_player_list_for_game(game.id) - if players: - for player in players: - player_names.append(player.nick) - status_str = status_str + ', players: {0:s}'.format(', '.join(player_names)) - - status_str = status_str + '. (o:{0:d}[{1:d}],{2:d},{3:d},{4:d})'.format(game.round_mode, game.game_length, game.line_length, game.random_method, game.lines_per_turn) - - return status_str - - def _get_progress_string_for_game(self, game): - """Get a terse summary of the game's progress.""" - - if game.round_mode == 1: - lines = self._get_lines_for_game(game.id) - num_lines = len(lines)-1 - - progress = '(lines: {0:d}/{1:d}'.format(num_lines, game.game_length) - - if num_lines == game.game_length - 1: - progress = progress + ' LAST LINE' - - progress = progress + ')' - return progress - else: - return '' - - 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: - 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 (%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)) - db.commit() - return cur.lastrowid - 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 - 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() - - player.id = result['id'] - player.game_id = result['game_id'] - player.nick = result['nick'] - player.userhost = result['userhost'] - - players.append(player) - - return players - 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.""" - - return self._get_game_details(game_id) is not None - - def _start_game(self, game_id): - """Start a game, if it's currently open.""" - - db = self.get_db() - try: - cur = db.cursor(mdb.cursors.DictCursor) - statement = ''' - UPDATE storycraft_game SET status = 'IN PROGRESS' WHERE status = 'OPEN' AND id = %s - ''' - cur.execute(statement, (game_id,)) - db.commit() - return game_id - 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.""" - - game = self._get_game_details(game_id) - if game: - # do round_mode stuff - if game.round_mode == 1: - count_by_latest_player = 0 - latest_player_id = 0 - - # get the lines in this game, latest first - lines = self._get_lines_for_game(game_id) - - for line in lines: - if latest_player_id == 0: - latest_player_id = line.player_id - - if latest_player_id == line.player_id: - count_by_latest_player = count_by_latest_player + 1 - else: - break - - # now we know how many times the most recent person has responded, - # figure out what to do - if count_by_latest_player == 0: - # must be a new game, get a random player - players = self._get_player_list_for_game(game.id) - random_player = players[random.randint(1,len(players))-1] - return self._assign_game_to_player(game_id, random_player.id) - elif count_by_latest_player >= game.lines_per_turn and len(lines) < game.game_length: - # player has reached the max, get a random different player to assign - players = self._get_player_list_for_game(game.id) - random_player_id = latest_player_id - if len(players) > 1: - while random_player_id == latest_player_id: - random_player = players[random.randint(1,len(players))-1] - random_player_id = random_player.id - return self._assign_game_to_player(game_id, random_player_id) - elif count_by_latest_player >= game.lines_per_turn and len(lines) >= game.game_length: - # end of game - return self._end_game(game_id) - elif count_by_latest_player < game.lines_per_turn: - # player should continue getting lines - return self._assign_game_to_player(game_id, latest_player_id) - - 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: - cur = db.cursor(mdb.cursors.DictCursor) - statement = ''' - INSERT INTO storycraft_line (game_id, player_id, line) - VALUES (%s, %s, %s) - ''' - cur.execute(statement, (game_id, player_id, '')) - db.commit() - return cur.lastrowid - 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: - cur = db.cursor(mdb.cursors.DictCursor) - statement = ''' - UPDATE storycraft_game SET status = 'COMPLETED', end_time = CURRENT_TIMESTAMP - WHERE status = 'IN PROGRESS' AND id = %s - ''' - cur.execute(statement, (game_id,)) - db.commit() - return game_id - 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 - 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() - - player.id = result['id'] - player.game_id = result['game_id'] - player.nick = result['nick'] - player.userhost = result['userhost'] - - return player - 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 - 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() - - player.id = result['id'] - player.game_id = result['game_id'] - player.nick = result['nick'] - player.userhost = result['userhost'] - - return player - 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.""" - - return self._get_games_of_type('COMPLETED') - - def _get_in_progress_games(self): - """Get the games with IN PROGRESS status.""" - - return self._get_games_of_type('IN PROGRESS') - - def _get_open_games(self): - """Get the games with OPEN status.""" - - return self._get_games_of_type('OPEN') - - def _get_active_games_with_player(self, player_nick): - """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 - 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 = %s AND (game.status = 'OPEN' OR game.status = 'IN PROGRESS') - ''' - cur.execute(query, (player_nick,)) - 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'].replace(tzinfo=tzlocal()) - game.end_time = result['end_time'] - if game.end_time is not None: - game.end_time = game.end_time.replace(tzinfo=tzlocal()) - - games.append(game) - - return games - 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. - - Include the games that the player started and are open, since they're waiting - on the person too. - """ - - games = [] - db = self.get_db() - try: - # get the games of specified type and populate a list - 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 - INNER JOIN storycraft_line line ON line.player_id = player.id - 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 = %s AND game.status = 'OPEN' - ''' - cur.execute(query, (player_nick, player_nick)) - 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'].replace(tzinfo=tzlocal()) - game.end_time = result['end_time'] - if game.end_time is not None: - game.end_time = game.end_time.replace(tzinfo=tzlocal()) - - games.append(game) - - return games - 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'].replace(tzinfo=tzlocal()) - game.end_time = result['end_time'] - if game.end_time is not None: - game.end_time = game.end_time.replace(tzinfo=tzlocal()) - - 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: - cur = db.cursor(mdb.cursors.DictCursor) - statement = ''' - INSERT INTO storycraft_player (game_id, nick, userhost) - VALUES (%s, %s, %s) - ''' - cur.execute(statement, (game_id, nick, userhost)) - db.commit() - return cur.lastrowid - 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.""" - - return self._get_player_by_userhost_in_game(game_id, userhost) is not None - - def _get_completed_game_count(self): - """Get the number of games with COMPLETED status.""" - - return self._get_game_type_count('COMPLETED') - - def _get_in_progress_game_count(self): - """Get the number of games with IN PROGRESS status.""" - - return self._get_game_type_count('IN PROGRESS') - - def _get_open_game_count(self): - """Get the number of games with OPEN status.""" - - return self._get_game_type_count('OPEN') - - def _get_free_game_count(self): - """Get the number of slots available, given current in progess/open games.""" - - all_slots = self._get_concurrent_game_count() - count_in_progress = self._get_in_progress_game_count() - count_open = self._get_open_game_count() - return all_slots - count_in_progress - count_open - - def _get_game_type_count(self, game_type): - """Return the number of games of the specified type.""" - - count = 0 - db = self.get_db() - try: - # get count of game_type games - 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 mdb.Error as e: - self.log.error("database error during get game type count") - self.log.exception(e) - raise - finally: cur.close() - - return count - - def _get_concurrent_game_count(self): - """Return the current game server concurrency.""" - - concurrency = 0 - db = self.get_db() - try: - # get the concurrency value from config table - cur = db.cursor(mdb.cursors.DictCursor) - query = 'SELECT concurrent_games FROM storycraft_config' - cur.execute(query) - result = cur.fetchone() - if result: - concurrency = result['concurrent_games'] - 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 - - def _get_lines_for_game(self, game_id): - """Get the lines for the specified game_id.""" - - lines = [] - db = self.get_db() - try: - # get the games of specified type and populate a list - cur = db.cursor(mdb.cursors.DictCursor) - query = ''' - SELECT id, game_id, player_id, line, time - FROM storycraft_line WHERE game_id = %s - ORDER BY time DESC, id DESC - ''' - cur.execute(query, (game_id,)) - results = cur.fetchall() - for result in results: - line = self.StorycraftLine() - - line.id = result['id'] - line.game_id = result['game_id'] - line.player_id = result['player_id'] - line.line = result['line'] - line.time = result['time'] - - lines.append(line) - - return lines - 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: - cur = db.cursor(mdb.cursors.DictCursor) - statement = ''' - UPDATE storycraft_line SET line = %s, time = CURRENT_TIMESTAMP WHERE id = %s - ''' - cur.execute(statement, (input_line, line_id)) - db.commit() - return line_id - 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.""" - - filename = 'storycraft-{0:0000d}-{1:s}-{2:s}.txt'.format(game.id, game.owner_nick, time.strftime('%Y%m%d%H%M%S')) - f = open(filename, 'w') - - lines_by_player = {} - line_count = 0 - - # the array is in LIFO, so reverse it - lines.reverse() - - for line in lines: - # get player for this line - player = self._get_player_by_id(line.player_id) - - line_count = line_count + 1 - if not player.nick in lines_by_player: - lines_by_player[player.nick] = [] - lines_by_player[player.nick].append(str(line_count)) - - f.write(line.line + '\n') - - f.write('\n') - for player in lines_by_player.keys(): - f.write(player + ':' + ','.join(lines_by_player[player]) + '\n') - - f.close() - return filename - - def _get_storycraft_settings(self): - """Get the server settings.""" - - db = self.get_db() - try: - # get the settings and return in StorycraftSettings - 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 - ''' - cur.execute(query) - result = cur.fetchone() - if result: - settings = self.StorycraftSettings() - - settings.master_channel = result['master_channel'] - settings.concurrent_games = int(result['concurrent_games']) - settings.default_round_mode = int(result['default_round_mode']) - settings.default_game_length = int(result['default_game_length']) - settings.default_line_length = int(result['default_line_length']) - settings.default_random_method = int(result['default_random_method']) - settings.default_lines_per_turn = int(result['default_lines_per_turn']) - - return settings - 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/ircbot/modules/__init__.py b/ircbot/modules/__init__.py deleted file mode 100644 index e69de29..0000000