3889 lines
156 KiB
Python
3889 lines
156 KiB
Python
# -*- coding: utf-8 -*-
|
|
###
|
|
# Copyright (c) 2013, tann <tann@trivialand.org>
|
|
# All rights reserved.
|
|
#
|
|
#
|
|
###
|
|
|
|
import supybot.utils as utils
|
|
from supybot.commands import *
|
|
import supybot.plugins as plugins
|
|
import supybot.ircutils as ircutils
|
|
import supybot.callbacks as callbacks
|
|
import supybot.ircdb as ircdb
|
|
import supybot.ircmsgs as ircmsgs
|
|
import supybot.schedule as schedule
|
|
import supybot.log as log
|
|
import supybot.conf as conf
|
|
import os
|
|
import re
|
|
import sqlite3
|
|
import random
|
|
import time
|
|
import datetime
|
|
import unicodedata
|
|
import hashlib
|
|
|
|
#A list with items that are removed when timeout is reached, values must be unique
|
|
class TimeoutList:
|
|
def __init__(self, timeout):
|
|
self.timeout = timeout
|
|
self.dict = {}
|
|
|
|
def setTimeout(self, timeout):
|
|
self.timeout = timeout
|
|
|
|
def clearTimeout(self):
|
|
for k, t in self.dict.items():
|
|
if t < (time.time() - self.timeout):
|
|
del self.dict[k]
|
|
|
|
def append(self, value):
|
|
self.clearTimeout()
|
|
self.dict[value] = time.time()
|
|
|
|
def has(self, value):
|
|
self.clearTimeout()
|
|
if value in self.dict:
|
|
return True
|
|
return False
|
|
|
|
class TriviaTime(callbacks.Plugin):
|
|
"""
|
|
TriviaTime - A trivia word game, guess the word and score points.
|
|
Play KAOS rounds and work together to solve clues to find groups of words.
|
|
"""
|
|
threaded = True # enables threading for supybot plugin
|
|
currentDBVersion = 1.2
|
|
|
|
def __init__(self, irc):
|
|
log.info('*** Loaded TriviaTime!!! ***')
|
|
self.__parent = super(TriviaTime, self)
|
|
self.__parent.__init__(irc)
|
|
|
|
# games info
|
|
self.games = {} # separate game for each channel
|
|
self.voiceTimeouts = TimeoutList(self.registryValue('voice.timeoutVoice'))
|
|
|
|
#Database amend statements for outdated versions
|
|
self.dbamends = {} #Formatted like this: <DBVersion>: "<ALTERSTATEMENT>; <ALTERSTATEMENT>;" (This IS valid SQL as long as we include the semicolons)
|
|
|
|
#logger
|
|
self.logger = self.Logger(self)
|
|
|
|
# connections
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
# tuple head, tail ('example/example/', 'filename.txt')
|
|
dbFolder = os.path.split(dbLocation)
|
|
# take folder from split
|
|
dbFolder = dbFolder[0]
|
|
# create the folder
|
|
if not os.path.exists(dbFolder):
|
|
log.info('The database location did not exist, creating folder structure')
|
|
os.makedirs(dbFolder)
|
|
self.storage = self.Storage(dbLocation)
|
|
#self.storage.dropActivityTable()
|
|
self.storage.makeActivityTable()
|
|
#self.storage.dropUserLogTable()
|
|
self.storage.makeUserLogTable()
|
|
#self.storage.dropGameTable()
|
|
self.storage.makeGameTable()
|
|
#self.storage.dropGameLogTable()
|
|
self.storage.makeGameLogTable()
|
|
#self.storage.dropUserTable()
|
|
self.storage.makeUserTable()
|
|
#self.storage.dropLoginTable()
|
|
self.storage.makeLoginTable()
|
|
#self.storage.dropReportTable()
|
|
self.storage.makeReportTable()
|
|
#self.storage.dropQuestionTable()
|
|
self.storage.makeQuestionTable()
|
|
#self.storage.dropTemporaryQuestionTable()
|
|
self.storage.makeTemporaryQuestionTable()
|
|
#self.storage.dropEditTable()
|
|
self.storage.makeEditTable()
|
|
#self.storage.dropDeleteTable()
|
|
self.storage.makeDeleteTable()
|
|
self.storage.makeInfoTable()
|
|
#triviainfo table check
|
|
#if self.storage.isTriviaVersionSet():
|
|
if self.storage.getVersion() != None and self.storage.getVersion() != self.currentDBVersion:
|
|
return
|
|
|
|
def _games(self):
|
|
for (network, games) in self.games.items():
|
|
for (channel, game) in games.items():
|
|
yield game
|
|
|
|
def die(self):
|
|
for game in self._games():
|
|
game.stop()
|
|
|
|
def reset(self):
|
|
for game in self._games():
|
|
game.stop()
|
|
|
|
def doPrivmsg(self, irc, msg):
|
|
"""
|
|
Catches all PRIVMSG, including channels communication
|
|
"""
|
|
username = msg.nick
|
|
try:
|
|
# rootcoma!~rootcomaa@unaffiliated/rootcoma
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
username = user.name
|
|
except KeyError:
|
|
pass
|
|
channel = msg.args[0]
|
|
# Make sure that it is starting inside of a channel, not in pm
|
|
if not irc.isChannel(channel):
|
|
return
|
|
if callbacks.addressed(irc.nick, msg):
|
|
return
|
|
channelCanonical = ircutils.toLower(channel)
|
|
|
|
otherHintCommand = self.registryValue('commands.showHintCommandKAOS', channel)
|
|
kaosRemainingCommand = self.registryValue('commands.extraHint', channel)
|
|
|
|
game = self.getGame(irc, channel)
|
|
|
|
if game is not None:
|
|
# Look for command to list remaining KAOS
|
|
if msg.args[1] == otherHintCommand:
|
|
game.getRemainingKAOS()
|
|
elif msg.args[1] == kaosRemainingCommand:
|
|
game.getOtherHint()
|
|
else:
|
|
# check the answer
|
|
game.checkAnswer(msg)
|
|
|
|
def doJoin(self,irc,msg):
|
|
username = msg.nick
|
|
# is it a user?
|
|
try:
|
|
# rootcoma!~rootcomaa@unaffiliated/rootcoma
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
username = user.name
|
|
except KeyError:
|
|
pass
|
|
channel = msg.args[0]
|
|
self.handleVoice(irc, username, channel)
|
|
|
|
def doNotice(self,irc,msg):
|
|
username = msg.nick
|
|
if msg.args[1][1:5] == "PING":
|
|
pingMsg = msg.args[1][6:]
|
|
pingMsg = pingMsg[:-1]
|
|
pingMsg = pingMsg.split('*', 1)
|
|
if len(pingMsg) == 2:
|
|
pingTime = time.time()-float(pingMsg[0])-1300000000
|
|
channelHash = pingMsg[1]
|
|
channel = ''
|
|
for name in irc.state.channels:
|
|
if channelHash == self.shortHash(ircutils.toLower(name)):
|
|
if username in irc.state.channels[name].users:
|
|
channel = name
|
|
break
|
|
if channel == '':
|
|
irc.sendMsg(ircmsgs.notice(username, '%s: Ping reply: %0.2f seconds' % (username, pingTime)))
|
|
else:
|
|
irc.sendMsg(ircmsgs.privmsg(channel, '%s: Ping reply: %0.2f seconds' % (username, pingTime)))
|
|
|
|
def voiceUser(self, irc, username, channel):
|
|
irc.queueMsg(ircmsgs.voice(channel, username))
|
|
usernameCanonical = ircutils.toLower(username)
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if self.registryValue('general.globalStats'):
|
|
user = threadStorage.getUser(username, None)
|
|
else:
|
|
user = threadStorage.getUser(username, channel)
|
|
if not self.voiceTimeouts.has(usernameCanonical):
|
|
self.voiceTimeouts.append(usernameCanonical)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Giving MVP to %s for being top #%d this WEEK' % (username, user[15])))
|
|
|
|
def handleVoice(self, irc, username, channel):
|
|
if not self.registryValue('voice.enableVoice'):
|
|
return
|
|
|
|
timeoutVoice = self.registryValue('voice.timeoutVoice')
|
|
self.voiceTimeouts.setTimeout(timeoutVoice)
|
|
usernameCanonical = ircutils.toLower(username)
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if self.registryValue('general.globalStats'):
|
|
user = threadStorage.getUser(username, None)
|
|
else:
|
|
user = threadStorage.getUser(username, channel)
|
|
numTopToVoice = self.registryValue('voice.numTopToVoice')
|
|
minPointsVoiceYear = self.registryValue('voice.minPointsVoiceYear')
|
|
minPointsVoiceMonth = self.registryValue('voice.minPointsVoiceMonth')
|
|
minPointsVoiceWeek = self.registryValue('voice.minPointsVoiceWeek')
|
|
if len(user) >= 1:
|
|
if user[13] <= numTopToVoice and user[4] >= minPointsVoiceYear:
|
|
self.voiceUser(irc, username, channel)
|
|
elif user[14] <= numTopToVoice and user[6] >= minPointsVoiceMonth:
|
|
self.voiceUser(irc, username, channel)
|
|
elif user[15] <= numTopToVoice and user[8] >= minPointsVoiceWeek:
|
|
self.voiceUser(irc, username, channel)
|
|
|
|
def addZeroWidthSpace(self, text):
|
|
if len(text) <= 1:
|
|
return text
|
|
s = u'%s\u200b%s' % (text[:1], text[1:])
|
|
return s.encode('utf-8')
|
|
|
|
def shortHash(self, text):
|
|
hashText = hashlib.sha1(text).hexdigest()
|
|
hashText = self.numToBase94(int(hashText, 16), 8)
|
|
return hashText
|
|
|
|
def numToBase94(self, n, maxChars):
|
|
chars = '!"#$%&\'() +,-./0123456789:;<=>?@ABCDEFGHUJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
|
|
L = []
|
|
for i in range(maxChars):
|
|
L.append(chars[n % len(chars)])
|
|
n = int(n / len(chars))
|
|
return ''.join(L)
|
|
|
|
def addActivity(self, activityType, activityText, channel, irc, storage=None):
|
|
if storage is None:
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
else:
|
|
threadStorage = storage
|
|
threadStorage.removeOldActivity()
|
|
threadStorage.insertActivity(activityType, activityText, channel, irc.network)
|
|
|
|
def deleteGame(self, irc, channel):
|
|
if irc.network in self.games:
|
|
if channelCanonical in self.games[irc.network]:
|
|
del self.games[irc.network][channelCanonical]
|
|
if len(self.games[irc.network]) < 1:
|
|
del self.games[irc.network]
|
|
|
|
def createGame(self, irc, channel):
|
|
if irc.network not in self.games:
|
|
self.games[irc.network] = {}
|
|
channelCanonical = ircutils.toLower(channel)
|
|
self.games[irc.network][channelCanonical] = self.Game(irc, channel, self)
|
|
|
|
def getGame(self, irc, channel):
|
|
channelCanonical = ircutils.toLower(channel)
|
|
if irc.network in self.games:
|
|
if channelCanonical in self.games[irc.network]:
|
|
return self.games[irc.network][channelCanonical]
|
|
return None
|
|
|
|
def acceptdelete(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <num>
|
|
Accept a question deletion
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
delete = self.storage.getDeleteById(num)
|
|
if len(delete) < 1:
|
|
irc.error('Could not find that delete')
|
|
else:
|
|
delete = delete[0]
|
|
questionNumber = delete[3]
|
|
irc.reply('Question #%d deleted!' % delete[3])
|
|
threadStorage.removeReportByQuestionNumber(delete[3])
|
|
threadStorage.removeEditByQuestionNumber(delete[3])
|
|
threadStorage.deleteQuestion(questionNumber)
|
|
threadStorage.removeDelete(num)
|
|
threadStorage.removeDeleteByQuestionNumber(questionNumber)
|
|
self.logger.doLog(irc, channel, "%s accepted delete# %i, question# %i deleted" % (msg.nick, num, questionNumber))
|
|
activityText = '%s deleted a question, approved by %s' % (delete[1], msg.nick)
|
|
self.addActivity('delete', activityText, channel, irc, threadStorage)
|
|
acceptdelete = wrap(acceptdelete, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def acceptedit(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <num>
|
|
Accept a question edit, and remove edit.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
edit = self.storage.getEditById(num)
|
|
if len(edit) < 1:
|
|
irc.error('Could not find that edit')
|
|
else:
|
|
edit = edit[0]
|
|
question = threadStorage.getQuestion(edit[1])
|
|
threadStorage.updateQuestion(edit[1], edit[2])
|
|
threadStorage.updateUser(edit[4], 0, 1)
|
|
threadStorage.removeEdit(edit[0])
|
|
threadStorage.removeReportByQuestionNumber(edit[1])
|
|
irc.reply('Question #%d updated!' % edit[1])
|
|
questionOld = ''
|
|
if len(question) > 0:
|
|
question = question[0]
|
|
questionOld = question[2]
|
|
self.logger.doLog(irc, channel, "%s accepted edit# %i, question# %i edited NEW: '%s' OLD '%s'" % (msg.nick, num, edit[1], edit[2], questionOld))
|
|
activityText = '%s edited a question, approved by %s' % (edit[4], msg.nick)
|
|
self.addActivity('edit', activityText, channel, irc, threadStorage)
|
|
irc.sendMsg(ircmsgs.notice(msg.nick, 'NEW: %s' % (edit[2])))
|
|
if questionOld != '':
|
|
irc.sendMsg(ircmsgs.notice(msg.nick, 'OLD: %s' % (questionOld)))
|
|
else:
|
|
irc.error('Question could not be found for this edit')
|
|
acceptedit = wrap(acceptedit, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def acceptnew(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <num>
|
|
Accept a new question, and add it to the database.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
q = threadStorage.getTemporaryQuestionById(num)
|
|
if len(q) < 1:
|
|
irc.error('Could not find that temp question')
|
|
else:
|
|
q = q[0]
|
|
threadStorage.updateUser(q[1], 0, 0, 0, 0, 1)
|
|
threadStorage.insertQuestionsBulk([(q[3], q[3])])
|
|
threadStorage.removeTemporaryQuestion(q[0])
|
|
irc.reply('Question accepted!')
|
|
self.logger.doLog(irc, channel, "%s accepted new question# %i, '%s'" % (msg.nick, num, q[3]))
|
|
activityText = '%s added a new question, approved by %s' % (q[1], msg.nick)
|
|
self.addActivity('new', activityText, channel, irc, threadStorage)
|
|
acceptnew = wrap(acceptnew, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def add(self, irc, msg, arg, question):
|
|
"""<question text>
|
|
Adds a question to the database
|
|
"""
|
|
username = msg.nick
|
|
channel = msg.args[0]
|
|
charMask = self.registryValue('hints.charMask', channel)
|
|
if charMask not in question:
|
|
irc.error(' The question must include the separating character %s ' % (charMask))
|
|
return
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
threadStorage.updateUser(username, 0, 0, 0, 1)
|
|
threadStorage.insertTemporaryQuestion(username, channel, question)
|
|
irc.reply(' Thank you for adding your question to the question database, it is awaiting approval. ')
|
|
self.logger.doLog(irc, channel, "%s added new question: '%s'" % (username, question))
|
|
add = wrap(add, ['text'])
|
|
|
|
def addfile(self, irc, msg, arg, filename):
|
|
"""[<filename>]
|
|
Add a file of questions to the question database,
|
|
filename defaults to configured quesiton file.
|
|
"""
|
|
if filename is None:
|
|
filename = self.registryValue('admin.quizfile')
|
|
try:
|
|
filesLines = open(filename).readlines()
|
|
except:
|
|
irc.error('Could not open file to add to database. Make sure it exists on the server.')
|
|
return
|
|
irc.reply('Adding questions from %s to database.. This may take a few minutes' % filename)
|
|
insertList = []
|
|
channel = msg.args[0]
|
|
for line in filesLines:
|
|
insertList.append((str(line).strip(),str(line).strip()))
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
info = threadStorage.insertQuestionsBulk(insertList)
|
|
irc.reply('Successfully added %d questions, skipped %d' % (info[0], info[1]))
|
|
self.logger.doLog(irc, channel, "%s added question file: '%s', added: %i, skipped: %i" % (msg.nick, filename, info[0], info[1]))
|
|
addfile = wrap(addfile, ['admin', optional('text')])
|
|
|
|
def authweb(self, irc, msg, arg):
|
|
"""
|
|
Triviamods only. Register for web access
|
|
"""
|
|
try:
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
except KeyError:
|
|
irc.error('Sorry, you must be at least a triviamod to register for web login')
|
|
return
|
|
|
|
try:
|
|
if user.capabilities.check('triviamod'):
|
|
capability = 'triviamod'
|
|
else:
|
|
irc.error('Sorry, you must be at least a triviamod to register for web login')
|
|
return
|
|
except KeyError:
|
|
irc.error('Sorry, you must be at least a triviamod to register for web login')
|
|
return
|
|
|
|
try:
|
|
if user.capabilities.check('owner'):
|
|
capability = 'owner'
|
|
except KeyError:
|
|
pass
|
|
|
|
username = user.name
|
|
channel = msg.args[0]
|
|
salt = ''
|
|
password = ''
|
|
isHashed = user.hashed
|
|
if isHashed:
|
|
(salt, password) = user.password.split('|')
|
|
else:
|
|
password = user.password
|
|
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
info = threadStorage.insertLogin(username, salt, isHashed, password, capability)
|
|
irc.reply('Success, updated your web access login.')
|
|
self.logger.doLog(irc, channel, "%s authed for web access" % (username))
|
|
authweb = wrap(authweb)
|
|
|
|
def clearpoints(self, irc, msg, arg, username):
|
|
"""<username>
|
|
Deletes all of a users points, and removes all their records
|
|
"""
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
threadStorage.removeUserLogs(username)
|
|
irc.reply('Removed all points from %s' % (username))
|
|
self.logger.doLog(irc, channel, "%s cleared points for %s" % (msg.nick, username))
|
|
clearpoints = wrap(clearpoints, ['admin','nick'])
|
|
|
|
def day(self, irc, msg, arg, num):
|
|
"""[<number>]
|
|
Displays the top ten scores of the day.
|
|
Parameter is optional, display up to that number.
|
|
eg 20 - display 11-20
|
|
"""
|
|
if num is None or num < 10:
|
|
num=10
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
tops = []
|
|
if self.registryValue('general.globalStats'):
|
|
tops = threadStorage.viewDayTop10(None, num)
|
|
else:
|
|
tops = threadStorage.viewDayTop10(channel, num)
|
|
offset = num-9
|
|
topsList = ['Today\'s Top 10 Players: ']
|
|
for i in range(len(tops)):
|
|
topsList.append('\x02 #%d:\x02 %s %d ' % ((i+offset) , self.addZeroWidthSpace(tops[i][1]), tops[i][2]))
|
|
topsText = ''.join(topsList)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, topsText))
|
|
irc.noReply()
|
|
day = wrap(day, [optional('int')])
|
|
|
|
def delete(self, irc, msg, arg, channel, t, id, reason):
|
|
"""[<channel>] [<type "R" or "Q">] <question id> [<reason>]
|
|
Deletes a question from the database. Type decides whether to delete
|
|
by round number (r), or question number (q) (default round).
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
username = msg.nick
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if t is None or str.lower(t) == "round":
|
|
q = threadStorage.getQuestionByRound(id, channel)
|
|
if len(q) < 1:
|
|
irc.error('Could not find that round.')
|
|
return
|
|
id = q[0][0]
|
|
if not threadStorage.questionIdExists(id):
|
|
irc.error('That question does not exist.')
|
|
return
|
|
if threadStorage.isQuestionDeleted(id):
|
|
irc.error('That question is already deleted.')
|
|
return
|
|
if threadStorage.isQuestionPendingDeletion(id):
|
|
irc.error('That question is already pending deletion.')
|
|
return
|
|
threadStorage.insertDelete(username, channel, id, reason)
|
|
irc.reply('Question %d marked for deletion and pending review.' % id)
|
|
self.logger.doLog(irc, channel, "%s marked question# %i for deletion" % (username, id))
|
|
delete = wrap(delete, ["channel", optional(('literal',("question", "QUESTION", "ROUND", "round"))),'int', optional('text')])
|
|
|
|
def edit(self, irc, msg, arg, user, channel, num, question):
|
|
"""[<channel>] <question number> <corrected text>
|
|
Correct a question by providing the question number and the corrected text.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
username = msg.nick
|
|
try:
|
|
#rootcoma!~rootcomaa@unaffiliated/rootcoma
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
username = user.name
|
|
except KeyError:
|
|
pass
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
q = threadStorage.getQuestion(num)
|
|
if len(q) > 0:
|
|
q = q[0]
|
|
questionParts = question.split('*')
|
|
if len(questionParts) < 2:
|
|
oldQuestionParts = q[2].split('*')
|
|
questionParts.extend(oldQuestionParts[1:])
|
|
question = questionParts[0]
|
|
for part in questionParts[1:]:
|
|
question += '*'
|
|
question += part
|
|
threadStorage.insertEdit(num, question, username, msg.args[0])
|
|
threadStorage.updateUser(username, 1, 0)
|
|
irc.reply('Success! Submitted edit for further review.')
|
|
irc.sendMsg(ircmsgs.notice(msg.nick, 'NEW: %s' % (question)))
|
|
irc.sendMsg(ircmsgs.notice(msg.nick, 'OLD: %s' % (q[2])))
|
|
self.logger.doLog(irc, channel, "%s edited question# %i: OLD: '%s' NEW: '%s'" % (username, num, q[2], question))
|
|
else:
|
|
irc.error('Question does not exist')
|
|
edit = wrap(edit, ['user', ('checkChannelCapability', 'triviamod'), 'int', 'text'])
|
|
|
|
def givepoints(self, irc, msg, arg, username, points, days):
|
|
"""<username> <points> [<daysAgo>]
|
|
|
|
Give a user points, last argument is optional amount of days in past to add records
|
|
"""
|
|
if points < 1:
|
|
irc.error("You cannot give less than 1 point.")
|
|
return
|
|
try:
|
|
user = ircdb.users.getUser(username)
|
|
username = user.name
|
|
except KeyError:
|
|
pass
|
|
day=None
|
|
month=None
|
|
year=None
|
|
if days is not None:
|
|
d = datetime.date.today()
|
|
d -= datetime.timedelta(days)
|
|
day = d.day
|
|
month = d.month
|
|
year = d.year
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
threadStorage.updateUserLog(username, channel, points, 0, 0, day, month, year)
|
|
irc.reply('Added %d points to %s' % (points, username))
|
|
self.logger.doLog(irc, channel, "%s gave %i points to %s" % (msg.nick, points, username))
|
|
givepoints = wrap(givepoints, ['admin','nick', 'int', optional('int')])
|
|
|
|
def listdeletes(self, irc, msg, arg, user, channel, page):
|
|
"""[<channel>] [<page>]
|
|
List deletes.
|
|
Channel is only required when using the command outside of a channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
count = threadStorage.countDeletes()
|
|
if page is None or page < 1:
|
|
page=1
|
|
pages = int(count/3)
|
|
deletes = threadStorage.getDeleteTop3(page)
|
|
if count % 3 > 0:
|
|
pages += 1
|
|
if count < 1:
|
|
irc.reply('No deletes found')
|
|
else:
|
|
irc.reply('Showing page %i of %i' % (page, pages))
|
|
for delete in deletes:
|
|
question = threadStorage.getQuestion(delete[3])
|
|
questionText = 'Question not found'
|
|
if len(question) > 0:
|
|
question = question[0]
|
|
questionText = question[2]
|
|
irc.reply('Delete #%d, by %s Question#%d: %s, Reason:%s'%(delete[0], delete[1], delete[3], questionText, delete[6]))
|
|
irc.reply('Use the showdelete command to see more information')
|
|
listdeletes = wrap(listdeletes, ['user', ('checkChannelCapability', 'triviamod'), optional('int')])
|
|
|
|
def listedits(self, irc, msg, arg, user, channel, page):
|
|
"""[<channel>] [<page>]
|
|
List edits.
|
|
Channel is only required when using the command outside of a channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
count = threadStorage.countEdits()
|
|
if page is None or page < 1:
|
|
page=1
|
|
pages = int(count/3)
|
|
edits = threadStorage.getEditTop3(page)
|
|
if count % 3 > 0:
|
|
pages += 1
|
|
if count < 1:
|
|
irc.reply('No edits found')
|
|
else:
|
|
irc.reply('Showing page %i of %i' % (page, pages))
|
|
for edit in edits:
|
|
irc.reply('Edit #%d, Question#%d, NEW:%s'%(edit[0], edit[1], edit[2]))
|
|
irc.reply('Use the showedit command to see more information')
|
|
listedits = wrap(listedits, ['user', ('checkChannelCapability', 'triviamod'), optional('int')])
|
|
|
|
def listreports(self, irc, msg, arg, user, channel, page):
|
|
"""[<channel>] [<page>]
|
|
List reports.
|
|
Channel is only required when using the command outside of a channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
count = threadStorage.countReports()
|
|
if page is None or page < 1:
|
|
page=1
|
|
pages = int(count/3)
|
|
reports = threadStorage.getReportTop3(page)
|
|
if count % 3 > 0:
|
|
pages += 1
|
|
if count < 1:
|
|
irc.reply('No reports found')
|
|
else:
|
|
irc.reply('Showing page %i of %i' % (page, pages))
|
|
for report in reports:
|
|
irc.reply('Report #%d `%s` by %s on %s Q#%d '%(report[0], report[3], report[2], report[1], report[7]))
|
|
irc.reply('Use the showreport command to see more information')
|
|
listreports = wrap(listreports, ['user', ('checkChannelCapability', 'triviamod'), optional('int')])
|
|
|
|
def listnew(self, irc, msg, arg, user, channel, page):
|
|
"""[<channel>] [<page>]
|
|
List questions awaiting approval.
|
|
Channel is only required when using the command outside of a channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
count = threadStorage.countTemporaryQuestions()
|
|
if page is None or page < 1:
|
|
page=1
|
|
pages = int(count/3)
|
|
if count % 3 > 0:
|
|
pages += 1
|
|
q = threadStorage.getTemporaryQuestionTop3(page)
|
|
if count < 1:
|
|
irc.reply('No new questions found')
|
|
else:
|
|
irc.reply('Showing page %i of %i' % (page, pages))
|
|
for ques in q:
|
|
irc.reply('Temp Q #%d: %s'%(ques[0], ques[3]))
|
|
irc.reply('Use the shownew to see more information')
|
|
listnew = wrap(listnew, ['user', ('checkChannelCapability', 'triviamod'), optional('int')])
|
|
|
|
def info(self, irc, msg, arg):
|
|
"""
|
|
Get TriviaTime information, how many questions/users in database, time, etc
|
|
"""
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
totalUsersEver = threadStorage.getNumUser(channel)
|
|
numActiveThisWeek = threadStorage.getNumActiveThisWeek(channel)
|
|
infoText = ' TriviaTime v1.01 by Trivialand on Freenode https://github.com/tannn/TriviaTime '
|
|
irc.sendMsg(ircmsgs.privmsg(msg.args[0], infoText))
|
|
infoText = ' Time is %s ' % (time.asctime(time.localtime(),))
|
|
irc.sendMsg(ircmsgs.privmsg(msg.args[0], infoText))
|
|
infoText = '\x02 %d Users\x02 on scoreboard with \x02%d Active This Week\x02' % (totalUsersEver, numActiveThisWeek)
|
|
irc.sendMsg(ircmsgs.privmsg(msg.args[0], infoText))
|
|
numKaos = threadStorage.getNumKAOS()
|
|
numQuestionTotal = threadStorage.getNumQuestions()
|
|
infoText = '\x02 %d Questions\x02 and \x02%d KAOS\x02 (\x02%d Total\x02) in the database ' % ((numQuestionTotal-numKaos), numKaos, numQuestionTotal)
|
|
irc.sendMsg(ircmsgs.privmsg(msg.args[0], infoText))
|
|
info = wrap(info)
|
|
|
|
def ping(self, irc, msg, arg):
|
|
"""
|
|
Check your ping time to the bot. The client must respond correctly to pings.
|
|
"""
|
|
channel = msg.args[0]
|
|
channelHash = self.shortHash(ircutils.toLower(channel))
|
|
username = msg.nick
|
|
irc.sendMsg(ircmsgs.privmsg(username, '\x01PING %0.2f*%s\x01' % (time.time()-1300000000, channelHash)))
|
|
ping = wrap(ping)
|
|
|
|
def me(self, irc, msg, arg):
|
|
"""
|
|
Get your rank, score & questions asked for day, month, year
|
|
"""
|
|
username = msg.nick
|
|
identified = False
|
|
try:
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
username = user.name
|
|
identified = True
|
|
except KeyError:
|
|
pass
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if self.registryValue('general.globalStats'):
|
|
info = threadStorage.getUser(username, None)
|
|
else:
|
|
info = threadStorage.getUser(username, channel)
|
|
if len(info) < 3:
|
|
errorMessage = 'You do not have any points.'
|
|
identifyMessage = ''
|
|
if not identified:
|
|
identifyMessage = ' You should identify to keep track of your score more accurately.'
|
|
irc.reply('%s%s' % (errorMessage, identifyMessage))
|
|
else:
|
|
hasPoints = False
|
|
infoList = ['%s\'s Stats: Points (answers)' % (self.addZeroWidthSpace(info[1]))]
|
|
if info[10] > 0 or info[16] > 0 or info[11] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02Today:\x02 #%d %d (%d)' % (info[16], info[10], info[11]))
|
|
if info[15] > 0 or info[8] > 0 or info[9] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02This Week:\x02 #%d %d (%d)' % (info[15], info[8], info[9]))
|
|
if info[14] > 0 or info[6] > 0 or info[7] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02This Month:\x02 #%d %d (%d)' % (info[14], info[6], info[7]))
|
|
if info[13] > 0 or info[4] > 0 or info[5] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02This Year:\x02 #%d %d (%d)' % (info[13], info[4], info[5]))
|
|
if not hasPoints:
|
|
infoList = ['%s: You do not have any points.' % (username)]
|
|
if not identified:
|
|
infoList.append(' You should identify to keep track of your score more accurately.')
|
|
infoText = ''.join(infoList)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, infoText))
|
|
irc.noReply()
|
|
me = wrap(me)
|
|
|
|
def month(self, irc, msg, arg, num):
|
|
"""[<number>]
|
|
Displays the top ten scores of the month.
|
|
Parameter is optional, display up to that number.
|
|
eg 20 - display 11-20
|
|
"""
|
|
if num is None or num < 10:
|
|
num=10
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if self.registryValue('general.globalStats'):
|
|
tops = threadStorage.viewMonthTop10(None, num)
|
|
else:
|
|
tops = threadStorage.viewMonthTop10(channel, num)
|
|
topsList = ['This Month\'s Top 10 Players: ']
|
|
offset = num-9
|
|
for i in range(len(tops)):
|
|
topsList.append('\x02 #%d:\x02 %s %d ' % ((i+offset) , self.addZeroWidthSpace(tops[i][1]), tops[i][2]))
|
|
topsText = ''.join(topsList)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, topsText))
|
|
irc.noReply()
|
|
month = wrap(month, [optional('int')])
|
|
|
|
def next(self, irc, msg, arg):
|
|
"""
|
|
Skip to the next question immediately. This can only be used by a user with a certain streak, set in the config.
|
|
"""
|
|
username = msg.nick
|
|
channel = msg.args[0]
|
|
try:
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
username = user.name
|
|
except KeyError:
|
|
pass
|
|
|
|
if not irc.isChannel(channel):
|
|
irc.error('This command can only be used in a channel.')
|
|
return
|
|
|
|
minStreak = self.registryValue('general.nextMinStreak', channel)
|
|
channelCanonical = ircutils.toLower(channel)
|
|
|
|
game = self.getGame(irc, channel)
|
|
|
|
# Trivia isn't running
|
|
if game is None or game.active != True:
|
|
irc.sendMsg(ircmsgs.privmsg(channel, '%s: Trivia is not currently running.' % (username)))
|
|
irc.noReply()
|
|
return
|
|
# Question is still being asked, not over
|
|
if game.questionOver == False:
|
|
irc.sendMsg(ircmsgs.privmsg(channel, '%s: You must wait until the current question is over.' % (username)))
|
|
irc.noReply()
|
|
return
|
|
# Username isnt the streak holder
|
|
if game.lastWinner != ircutils.toLower(username):
|
|
irc.sendMsg(ircmsgs.privmsg(channel, '%s: You are not currently the streak holder.' % (username)))
|
|
irc.noReply()
|
|
return
|
|
# Streak isnt high enough
|
|
if game.streak < minStreak:
|
|
irc.sendMsg(ircmsgs.privmsg(channel, '%s: You do not have a large enough streak yet (%i of %i).' % (username, game.streak, minStreak)))
|
|
irc.noReply()
|
|
return
|
|
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'No more waiting; starting the next question.'))
|
|
game.removeEvent()
|
|
game.nextQuestion()
|
|
irc.noReply()
|
|
next = wrap(next)
|
|
|
|
def rmedit(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <int>
|
|
Remove an edit without accepting it.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
edit = threadStorage.getEditById(num)
|
|
if len(edit) < 1:
|
|
irc.error('Could not find that edit')
|
|
else:
|
|
edit = edit[0]
|
|
threadStorage.removeEdit(edit[0])
|
|
irc.reply('Edit %d removed!' % edit[0])
|
|
self.logger.doLog(irc, channel, "%s removed edit# %i, for question# %i, text: %s" % (msg.nick, edit[0], edit[1], edit[2]))
|
|
rmedit = wrap(rmedit, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def rmdelete(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <int>
|
|
Remove a deletion request without accepting it.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
delete = threadStorage.getDeleteById(num)
|
|
if len(delete) < 1:
|
|
irc.error('Could not find that delete')
|
|
else:
|
|
delete = delete[0]
|
|
threadStorage.removeDelete(num)
|
|
irc.reply('Delete %d removed!' % num)
|
|
self.logger.doLog(irc, channel, "%s removed delete# %i, for question# %i, reason was '%s'" % (msg.nick, num, delete[3], delete[6]))
|
|
rmdelete = wrap(rmdelete, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def rmreport(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <report num>
|
|
Delete a report by report number.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
report = threadStorage.getReportById(num)
|
|
if len(report) < 1:
|
|
irc.error('Could not find that report')
|
|
else:
|
|
report = report[0]
|
|
threadStorage.removeReport(report[0])
|
|
irc.reply('Report %d removed!' % report[0])
|
|
self.logger.doLog(irc, channel, "%s removed report# %i, for question# %i text was %s" % (msg.nick, report[0], report[7], report[3]))
|
|
rmreport = wrap(rmreport, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def rmnew(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <int>
|
|
Remove a temp question without accepting it. Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
q = threadStorage.getTemporaryQuestionById(num)
|
|
if len(q) < 1:
|
|
irc.error('Could not find that temp question')
|
|
else:
|
|
q = q[0]
|
|
threadStorage.removeTemporaryQuestion(q[0])
|
|
irc.reply('Temp question #%d removed!' % q[0])
|
|
self.logger.doLog(irc, channel, "%s removed new question# %i, '%s'" % (msg.nick, q[0], q[3]))
|
|
rmnew = wrap(rmnew, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def repeat(self, irc, msg, arg):
|
|
"""
|
|
Repeat the current question.
|
|
"""
|
|
channel = msg.args[0]
|
|
channelCanonical = ircutils.toLower(channel)
|
|
|
|
game = self.getGame(irc, channel)
|
|
|
|
if game is not None:
|
|
game.repeatQuestion()
|
|
repeat = wrap(repeat)
|
|
|
|
def report(self, irc, msg, arg, channel, roundNum, text):
|
|
"""[channel] <round number> <report text>
|
|
Provide a report for a bad question. Be sure to include the round number and any problems.
|
|
Channel is a optional parameter which is only needed when reporting outside of the channel
|
|
"""
|
|
username = msg.nick
|
|
inp = text.strip()
|
|
try:
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
username = user.name
|
|
except KeyError:
|
|
pass
|
|
channelCanonical = ircutils.toLower(channel)
|
|
game = self.getGame(irc, channel)
|
|
if game is not None:
|
|
numAsked = game.numAsked
|
|
questionOver = game.questionOver
|
|
if inp[:2] == 's/':
|
|
if numAsked == roundNum and questionOver == False:
|
|
irc.reply('Sorry you must wait until the current question is over to report it.')
|
|
return
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
question = threadStorage.getQuestionByRound(roundNum, channel)
|
|
if len(question) > 0:
|
|
question = question[0]
|
|
if inp[:2] == 's/':
|
|
regex = inp[2:].split('/')
|
|
if len(regex) > 1:
|
|
threadStorage.updateUser(username, 1, 0)
|
|
newOne = regex[1]
|
|
oldOne = regex[0]
|
|
newQuestionText = question[2].replace(oldOne, newOne)
|
|
threadStorage.insertEdit(question[0], newQuestionText, username, channel)
|
|
irc.reply('** Regex detected ** Your report has been submitted!')
|
|
irc.sendMsg(ircmsgs.notice(username, 'NEW: %s' % (newQuestionText)))
|
|
irc.sendMsg(ircmsgs.notice(username, 'OLD: %s' % (question[2])))
|
|
self.logger.doLog(irc, channel, "%s edited question# %i, NEW: '%s', OLD: '%s'" % (msg.nick, question[0], newQuestionText, question[2]))
|
|
return
|
|
elif str.lower(utils.str.normalizeWhitespace(inp))[:6] == 'delete':
|
|
if not threadStorage.questionIdExists(question[0]):
|
|
irc.error('That question does not exist.')
|
|
return
|
|
if threadStorage.isQuestionDeleted(question[0]):
|
|
irc.error('That question is already deleted.')
|
|
return
|
|
if threadStorage.isQuestionPendingDeletion(question[0]):
|
|
irc.error('That question is already pending deletion.')
|
|
return
|
|
reason = utils.str.normalizeWhitespace(inp)[6:]
|
|
reason = utils.str.normalizeWhitespace(reason)
|
|
irc.reply('Marked question for deletion.')
|
|
self.logger.doLog(irc, channel, "%s marked question# %i for deletion" % (msg.nick, question[0]))
|
|
threadStorage.insertDelete(username, channel, question[0], reason)
|
|
return
|
|
threadStorage.updateUser(username, 0, 0, 1)
|
|
threadStorage.insertReport(channel, username, text, question[0])
|
|
self.logger.doLog(irc, channel, "%s reported question# %i, Text: '%s'" % (msg.nick, question[0], text))
|
|
irc.reply('Your report has been submitted!')
|
|
else:
|
|
irc.error('Sorry, round %d could not be found in the database' % (roundNum))
|
|
report = wrap(report, ['channel', 'int', 'text'])
|
|
|
|
def restorequestion(self, irc, msg, arg, channel, questionNum):
|
|
"""[<channel>] <Question num>
|
|
Restore a question from being deleted.
|
|
"""
|
|
username = msg.nick
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if not threadStorage.questionIdExists(questionNum):
|
|
irc.error('That question does not exist.')
|
|
return
|
|
if not threadStorage.isQuestionDeleted(questionNum):
|
|
irc.error('That question is not deleted.')
|
|
return
|
|
threadStorage.restoreQuestion(questionNum)
|
|
irc.reply('Question %d restored.' % questionNum)
|
|
self.logger.doLog(irc, channel, "%s restored question# %i" % (username, questionNum))
|
|
restorequestion = wrap(restorequestion, [('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def skip(self, irc, msg, arg):
|
|
"""
|
|
Skip the current question and start the next. Rate-limited.
|
|
"""
|
|
username = msg.nick
|
|
usernameCanonical = ircutils.toLower(username)
|
|
channel = msg.args[0]
|
|
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
timeSeconds = self.registryValue('skip.skipActiveTime', channel)
|
|
totalActive = threadStorage.getNumUserActiveIn(channel, timeSeconds)
|
|
channelCanonical = ircutils.toLower(channel)
|
|
game = self.getGame(irc, channel)
|
|
if game is None:
|
|
irc.error('No questions are currently being asked.')
|
|
return
|
|
|
|
if game.questionOver == True:
|
|
irc.error('No question is currently being asked.')
|
|
return
|
|
|
|
if not threadStorage.wasUserActiveIn(username, channel, timeSeconds):
|
|
irc.error('Only users who have answered a question in the last 10 minutes can skip.')
|
|
return
|
|
|
|
if usernameCanonical in game.skipVoteCount:
|
|
irc.error('You can only vote to skip once.')
|
|
return
|
|
|
|
skipSeconds = self.registryValue('skip.skipTime', channel)
|
|
|
|
game.skips.setTimeout(skipSeconds)
|
|
if game.skips.has(usernameCanonical):
|
|
irc.error('You must wait to be able to skip again.')
|
|
return
|
|
|
|
game.skipVoteCount[usernameCanonical] = 1
|
|
|
|
game.skips.append(usernameCanonical)
|
|
|
|
irc.sendMsg(ircmsgs.privmsg(channel, '%s voted to skip this question.' % username))
|
|
if totalActive < 1:
|
|
return
|
|
|
|
percentAnswered = ((1.0*len(game.skipVoteCount))/(totalActive*1.0))
|
|
|
|
# not all have skipped yet, we need to get out of here
|
|
if percentAnswered < self.registryValue('skip.skipThreshold', channel):
|
|
irc.noReply()
|
|
return
|
|
|
|
if game is None:
|
|
irc.error('Trivia is not running.')
|
|
return
|
|
if game.active == False:
|
|
irc.error('Trivia is not running.')
|
|
return
|
|
try:
|
|
schedule.removeEvent('%s.trivia' % channel)
|
|
except KeyError:
|
|
pass
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Skipped question! (%d of %d voted)' % (len(game.skipVoteCount), totalActive)))
|
|
|
|
game.nextQuestion()
|
|
irc.noReply()
|
|
skip = wrap(skip)
|
|
|
|
def stats(self, irc, msg, arg, username):
|
|
""" <username>
|
|
Show a player's rank, score & questions asked for day, month, and year
|
|
"""
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if self.registryValue('general.globalStats'):
|
|
info = threadStorage.getUser(username, None)
|
|
else:
|
|
info = threadStorage.getUser(username, channel)
|
|
if len(info) < 3:
|
|
irc.error("I couldn't find that user in the database.")
|
|
else:
|
|
hasPoints = False
|
|
infoList = ['%s\'s Stats: Points (answers)' % (self.addZeroWidthSpace(info[1]))]
|
|
if info[10] > 0 or info[16] > 0 or info[11] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02Today:\x02 #%d %d (%d)' % (info[16], info[10], info[11]))
|
|
if info[15] > 0 or info[8] > 0 or info[9] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02This Week:\x02 #%d %d (%d)' % (info[15], info[8], info[9]))
|
|
if info[14] > 0 or info[6] > 0 or info[7] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02This Month:\x02 #%d %d (%d)' % (info[14], info[6], info[7]))
|
|
if info[13] > 0 or info[4] > 0 or info[5] > 0:
|
|
hasPoints = True
|
|
infoList.append(' \x02This Year:\x02 #%d %d (%d)' % (info[13], info[4], info[5]))
|
|
if not hasPoints:
|
|
infoList = ['%s: %s does not have any points.' % (msg.nick, username)]
|
|
infoText = ''.join(infoList)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, infoText))
|
|
irc.noReply()
|
|
stats = wrap(stats,['nick'])
|
|
|
|
def showdelete(self, irc, msg, arg, user, channel, num):
|
|
"""[<temp question #>]
|
|
Show deletes awaiting approval
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if num is not None:
|
|
q = threadStorage.getDeleteById(num)
|
|
if len(q) > 0:
|
|
q = q[0]
|
|
question = threadStorage.getQuestion(q[3])
|
|
questionText = 'Question not found'
|
|
if len(question) > 0:
|
|
question = question[0]
|
|
questionText = question[2]
|
|
irc.reply('Delete #%d, by %s Question#%d: %s, Reason: %s'%(q[0], q[1], q[3], questionText, q[6]))
|
|
else:
|
|
irc.error('Delete #%d not found' % num)
|
|
else:
|
|
q = threadStorage.getDeleteTop3()
|
|
if len(q) < 1:
|
|
irc.reply('No deletes found')
|
|
for ques in q:
|
|
question = threadStorage.getQuestion(ques[3])
|
|
questionText = 'Delete not found'
|
|
if len(question) > 0:
|
|
question = question[0]
|
|
questionText = question[2]
|
|
irc.reply('Delete #%d, by %s Question#%d: %s'%(ques[0], ques[1], ques[3], questionText))
|
|
irc.reply('Use the showdelete to see more information')
|
|
showdelete = wrap(showdelete, ['user', ('checkChannelCapability', 'triviamod'),optional('int')])
|
|
|
|
def showquestion(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <num>
|
|
Search question database for question at line num.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
question = threadStorage.getQuestion(num)
|
|
if len(question) < 1:
|
|
irc.error('Question not found')
|
|
else:
|
|
question = question[0]
|
|
if question[3] == 1:
|
|
irc.reply('Info: This question is currently deleted.')
|
|
irc.reply('Question#%d: %s' % (num, question[2]))
|
|
showquestion = wrap(showquestion, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def showround(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] <round num>
|
|
Show what question was asked during the round.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
question = threadStorage.getQuestionByRound(num, channel)
|
|
if len(question) < 1:
|
|
irc.error('Round not found')
|
|
else:
|
|
question = question[0]
|
|
irc.reply('Round %d: Question#%d, Text:%s' % (num, question[0], question[2]))
|
|
showround = wrap(showround, ['user', ('checkChannelCapability', 'triviamod'), 'int'])
|
|
|
|
def showreport(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] [<report num>]
|
|
Shows report information, if num is provided one record is shown, otherwise the last 3 are.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if num is not None:
|
|
report = threadStorage.getReportById(num)
|
|
if len(report) < 1:
|
|
irc.reply('No reports found')
|
|
else:
|
|
report = report[0]
|
|
irc.reply('Report #%d `%s` by %s on %s Q#%d '%(report[0], report[3], report[2], report[1], report[7]))
|
|
|
|
question = threadStorage.getQuestion(report[7])
|
|
if len(question) < 1:
|
|
irc.reply('Error: Tried to find question but couldn\'t')
|
|
else:
|
|
question = question[0]
|
|
irc.reply('Question#%d: %s' % (question[0], question[2]))
|
|
else:
|
|
reports = threadStorage.getReportTop3()
|
|
if len(reports) < 1:
|
|
irc.reply('No reports found')
|
|
for report in reports:
|
|
irc.reply('Report #%d `%s` by %s on %s Q#%d '%(report[0], report[3], report[2], report[1], report[7]))
|
|
showreport = wrap(showreport, ['user', ('checkChannelCapability', 'triviamod'), optional('int')])
|
|
|
|
def showedit(self, irc, msg, arg, user, channel, num):
|
|
"""[<channel>] [<edit num>]
|
|
Show top 3 edits, or provide edit num to view one.
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if num is not None:
|
|
edit = threadStorage.getEditById(num)
|
|
if len(edit) > 0:
|
|
edit = edit[0]
|
|
question = threadStorage.getQuestion(edit[1])
|
|
irc.reply('Edit #%d, Question#%d'%(edit[0], edit[1]))
|
|
irc.reply('NEW:%s' %(edit[2]))
|
|
if len(question) > 0:
|
|
question = question[0]
|
|
irc.reply('OLD:%s' % (question[2]))
|
|
else:
|
|
irc.error('Question could not be found for this edit')
|
|
else:
|
|
irc.error('Edit #%d not found' % num)
|
|
else:
|
|
edits = threadStorage.getEditTop3()
|
|
if len(edits) < 1:
|
|
irc.reply('No edits found')
|
|
for edit in edits:
|
|
question = threadStorage.getQuestion(edit[1])
|
|
question = question[0]
|
|
irc.reply('Edit #%d, Question#%d, NEW:%s'%(edit[0], edit[1], edit[2]))
|
|
irc.reply('Use the showedit command to see more information')
|
|
showedit = wrap(showedit, ['user', ('checkChannelCapability', 'triviamod'), optional('int')])
|
|
|
|
def shownew(self, irc, msg, arg, user, channel, num):
|
|
"""[<temp question #>]
|
|
Show questions awaiting approval
|
|
Channel is only necessary when editing from outside of the channel
|
|
"""
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if num is not None:
|
|
q = threadStorage.getTemporaryQuestionById(num)
|
|
if len(q) > 0:
|
|
q = q[0]
|
|
irc.reply('Temp Q #%d: %s'%(q[0], q[3]))
|
|
else:
|
|
irc.error('Temp Q #%d not found' % num)
|
|
else:
|
|
q = threadStorage.getTemporaryQuestionTop3()
|
|
if len(q) < 1:
|
|
irc.reply('No temp questions found')
|
|
for ques in q:
|
|
irc.reply('Temp Q #%d: %s'%(ques[0], ques[3]))
|
|
irc.reply('Use the shownew to see more information')
|
|
shownew = wrap(shownew, ['user', ('checkChannelCapability', 'triviamod'),optional('int')])
|
|
|
|
def start(self, irc, msg, args):
|
|
"""
|
|
Begins a round of Trivia inside the current channel.
|
|
"""
|
|
channel = msg.args[0]
|
|
channelCanonical = ircutils.toLower(channel)
|
|
if not irc.isChannel(channel):
|
|
irc.error('This command can only be used in a channel.')
|
|
return
|
|
game = self.getGame(irc, channel)
|
|
if game is not None:
|
|
if game.stopPending == True:
|
|
game.stopPending = False
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Pending stop aborted'))
|
|
elif not game.active:
|
|
self.deleteGame(irc, channel)
|
|
try:
|
|
schedule.removeEvent('%s.trivia' % channel)
|
|
except KeyError:
|
|
pass
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Another epic round of trivia is about to begin.'))
|
|
self.createGame(irc, channel)
|
|
else:
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Trivia has already been started.'))
|
|
else:
|
|
# create a new game
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Another epic round of trivia is about to begin.'))
|
|
self.createGame(irc, channel)
|
|
irc.noReply()
|
|
start = wrap(start)
|
|
|
|
def stop(self, irc, msg, args, channel):
|
|
"""[<channel>]
|
|
Stops the current Trivia round.
|
|
Channel is only necessary when using from outside of the channel
|
|
"""
|
|
channelCanonical = ircutils.toLower(channel)
|
|
game = self.getGame(irc, channel)
|
|
if game is not None:
|
|
if game.questionOver == True:
|
|
game.stop()
|
|
return
|
|
if game.stopPending:
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Trivia is already pending stop'))
|
|
return
|
|
if game.active:
|
|
game.stopPending = True
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Trivia will now stop after this question.'))
|
|
else:
|
|
self.deleteGame(irc, channel)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Trivia stopped. :\'('))
|
|
else:
|
|
irc.sendMsg(ircmsgs.privmsg(channel, 'Game is already stopped'))
|
|
irc.noReply()
|
|
stop = wrap(stop, [('checkChannelCapability', 'triviamod')])
|
|
|
|
def time(self, irc, msg, arg):
|
|
"""
|
|
Figure out what time/day it is on the server.
|
|
"""
|
|
channel = msg.args[0]
|
|
timeObject = time.asctime(time.localtime())
|
|
timeString = 'The current server time appears to be %s' % timeObject
|
|
irc.sendMsg(ircmsgs.privmsg(channel, timeString))
|
|
irc.noReply()
|
|
time = wrap(time)
|
|
|
|
def transferpoints(self, irc, msg, arg, userfrom, userto):
|
|
"""<userfrom> <userto>
|
|
Transfers all points and records from one user to another
|
|
"""
|
|
userfrom = userfrom
|
|
userto = userto
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
threadStorage.transferUserLogs(userfrom, userto)
|
|
irc.reply('Done! Transfered records from %s to %s' % (userfrom, userto))
|
|
self.logger.doLog(irc, channel, "%s transfered points from %s to %s" % (msg.nick, userfrom, userto))
|
|
transferpoints = wrap(transferpoints, ['admin', 'nick', 'nick'])
|
|
|
|
def week(self, irc, msg, arg, num):
|
|
"""[<number>]
|
|
Displays the top ten scores of the week.
|
|
Parameter is optional, display up to that number.
|
|
eg 20 - display 11-20
|
|
"""
|
|
if num is None or num < 10:
|
|
num=10
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if self.registryValue('general.globalStats'):
|
|
tops = threadStorage.viewWeekTop10(None, num)
|
|
else:
|
|
tops = threadStorage.viewWeekTop10(channel, num)
|
|
topsList = ['This Week\'s Top 10 Players: ']
|
|
offset = num-9
|
|
for i in range(len(tops)):
|
|
topsList.append('\x02 #%d:\x02 %s %d ' % ((i+offset) , self.addZeroWidthSpace(tops[i][1]), tops[i][2]))
|
|
topsText = ''.join(topsList)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, topsText))
|
|
irc.noReply()
|
|
week = wrap(week, [optional('int')])
|
|
|
|
def year(self, irc, msg, arg, num):
|
|
"""[<number>]
|
|
Displays the top ten scores of the year. Parameter is optional, display up to that number. eg 20 - display 11-20
|
|
"""
|
|
if num is None or num < 10:
|
|
num=10
|
|
channel = msg.args[0]
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
if self.registryValue('general.globalStats'):
|
|
tops = threadStorage.viewYearTop10(None, num)
|
|
else:
|
|
tops = threadStorage.viewYearTop10(channel, num)
|
|
topsList = ['This Year\'s Top 10 Players: ']
|
|
offset = num-9
|
|
for i in range(len(tops)):
|
|
topsList.append('\x02 #%d:\x02 %s %d ' % ((i+offset) , self.addZeroWidthSpace(tops[i][1]), tops[i][2]))
|
|
topsText = ''.join(topsList)
|
|
irc.sendMsg(ircmsgs.privmsg(channel, topsText))
|
|
irc.noReply()
|
|
year = wrap(year, [optional('int')])
|
|
|
|
#Game instance
|
|
class Game:
|
|
"""
|
|
Main game logic, single game instance for each channel.
|
|
"""
|
|
def __init__(self, irc, channel, base):
|
|
# constants
|
|
self.unmaskedChars = " -'\"_=+&%$#@!~`[]{}?.,<>|\\/:;"
|
|
|
|
# get utilities from base plugin
|
|
self.base = base
|
|
self.games = base.games
|
|
self.storage = base.storage
|
|
self.Storage = base.Storage
|
|
self.registryValue = base.registryValue
|
|
self.channel = channel
|
|
self.irc = irc
|
|
self.network = irc.network
|
|
|
|
# reset stats
|
|
self.skips = TimeoutList(self.registryValue('skip.skipTime', channel))
|
|
self.stopPending = False
|
|
self.shownHint = False
|
|
self.questionRepeated = False
|
|
self.skipVoteCount = {}
|
|
self.streak = 0
|
|
self.lastWinner = ''
|
|
self.hintsCounter = 0
|
|
self.numAsked = 0
|
|
self.lastAnswer = time.time()
|
|
self.roundStartedAt = time.mktime(time.localtime())
|
|
|
|
self.loadGameState()
|
|
|
|
# activate
|
|
self.questionOver = True
|
|
self.active = True
|
|
|
|
# stop any old game and start a new one
|
|
self.removeEvent()
|
|
self.nextQuestion()
|
|
|
|
def checkAnswer(self, msg):
|
|
"""
|
|
Check users input to see if answer was given.
|
|
"""
|
|
# Already done? get out of here
|
|
if self.questionOver:
|
|
return
|
|
|
|
username = msg.nick
|
|
channel = msg.args[0]
|
|
# is it a user?
|
|
try:
|
|
user = ircdb.users.getUser(msg.prefix)
|
|
username = user.name
|
|
except KeyError:
|
|
pass
|
|
correctAnswerFound = False
|
|
correctAnswer = ''
|
|
|
|
attempt = str.lower(msg.args[1])
|
|
attempt = self.removeAccents(attempt)
|
|
attempt = self.removeExtraSpaces(attempt)
|
|
|
|
# was a correct answer guessed?
|
|
for ans in self.alternativeAnswers:
|
|
normalizedAns = self.removeAccents(ans)
|
|
normalizedAns = self.removeExtraSpaces(normalizedAns)
|
|
normalizedAns = str.lower(normalizedAns)
|
|
if normalizedAns == attempt and normalizedAns not in self.guessedAnswers:
|
|
correctAnswerFound = True
|
|
correctAnswer = ans
|
|
for ans in self.answers:
|
|
normalizedAns = self.removeAccents(ans)
|
|
normalizedAns = self.removeExtraSpaces(normalizedAns)
|
|
normalizedAns = str.lower(normalizedAns)
|
|
if normalizedAns == attempt and normalizedAns not in self.guessedAnswers:
|
|
correctAnswerFound = True
|
|
correctAnswer = ans
|
|
|
|
if correctAnswerFound:
|
|
dbLocation = self.registryValue('admin.sqlitedb')
|
|
threadStorage = self.Storage(dbLocation)
|
|
# time stats
|
|
timeElapsed = float(time.time() - self.askedAt)
|
|
pointsAdded = self.points
|
|
|
|
# Past first hint? deduct points
|
|
if self.hintsCounter > 1:
|
|
pointsAdded /= 2 * (self.hintsCounter - 1)
|
|
|
|
if len(self.answers) > 1:
|
|
if ircutils.toLower(username) not in self.correctPlayers:
|
|
self.correctPlayers[ircutils.toLower(username)] = 1
|
|
self.correctPlayers[ircutils.toLower(username)] += 1
|
|
# KAOS? divide points
|
|
pointsAdded /= (len(self.answers) + 1)
|
|
|
|
# Convert score to int
|
|
pointsAdded = int(pointsAdded)
|
|
|
|
self.totalAmountWon += pointsAdded
|
|
# report the correct guess for kaos item
|
|
threadStorage.updateUserLog(username, self.channel, pointsAdded,0, 0)
|
|
self.lastAnswer = time.time()
|
|
self.sendMessage('\x02%s\x02 gets \x02%d\x02 points for: \x02%s\x02' % (username, pointsAdded, correctAnswer))
|
|
else:
|
|
# Normal question solved
|
|
streakBonus = 0
|
|
# update streak info
|
|
if ircutils.toLower(self.lastWinner) != ircutils.toLower(username):
|
|
self.lastWinner = ircutils.toLower(username)
|
|
self.streak = 1
|
|
else:
|
|
self.streak += 1
|
|
streakBonus = pointsAdded * .01 * (self.streak-1)
|
|
if streakBonus > pointsAdded:
|
|
streakBonus = pointsAdded
|
|
threadStorage.updateGameStreak(self.channel, self.lastWinner, self.streak)
|
|
threadStorage.updateUserHighestStreak(self.lastWinner, self.streak)
|
|
threadStorage.updateGameLongestStreak(self.channel, username, self.streak)
|
|
# Convert score to int
|
|
pointsAdded = int(pointsAdded)
|
|
|
|
# report correct guess, and show players streak
|
|
threadStorage.updateUserLog(username, self.channel, pointsAdded,1, timeElapsed)
|
|
self.lastAnswer = time.time()
|
|
self.sendMessage('DING DING DING, \x02%s\x02 got the correct answer, \x02%s\x02, in \x02%0.4f\x02 seconds for \x02%d(+%d)\x02 points!' % (username, correctAnswer, timeElapsed, pointsAdded, streakBonus))
|
|
|
|
if self.registryValue('general.showStats', self.channel):
|
|
if self.registryValue('general.globalStats'):
|
|
userInfo = threadStorage.getUser(username, None)
|
|
else:
|
|
userInfo = threadStorage.getUser(username, self.channel)
|
|
if len(userInfo) >= 3:
|
|
todaysScore = userInfo[10]
|
|
weekScore = userInfo[8]
|
|
monthScore = userInfo[6]
|
|
recapMessageList = ['\x02%s\x02 has won \x02%d\x02 in a row!' % (username, self.streak)]
|
|
if todaysScore > pointsAdded or weekScore > pointsAdded or monthScore > pointsAdded:
|
|
recapMessageList.append(' Total Points')
|
|
if todaysScore > pointsAdded:
|
|
recapMessageList.append(' TODAY: \x02%d\x02' % (todaysScore))
|
|
if weekScore > pointsAdded:
|
|
recapMessageList.append(' this WEEK \x02%d\x02' % (weekScore))
|
|
if weekScore > pointsAdded or todaysScore > pointsAdded:
|
|
if monthScore > pointsAdded:
|
|
recapMessageList.append(' &')
|
|
if monthScore > pointsAdded:
|
|
recapMessageList.append(' this MONTH: \x02%d\x02' % (monthScore))
|
|
recapMessage = ''.join(recapMessageList)
|
|
self.sendMessage(recapMessage)
|
|
|
|
# add guessed word to list so we can cross it out
|
|
if self.guessedAnswers.count(attempt) == 0:
|
|
self.guessedAnswers.append(attempt)
|
|
# can show more hints now
|
|
self.shownHint = False
|
|
|
|
# Have all of the answers been found?
|
|
if len(self.guessedAnswers) == len(self.answers):
|
|
# question is over
|
|
self.questionOver = True
|
|
if len(self.guessedAnswers) > 1:
|
|
bonusPoints = 0
|
|
if len(self.correctPlayers) >= 2:
|
|
if len(self.answers) >= 9:
|
|
bonusPoints = self.registryValue('kaos.payoutKAOS', self.channel)
|
|
|
|
bonusPointsText = ''
|
|
if bonusPoints > 0:
|
|
for nick in self.correctPlayers:
|
|
threadStorage.updateUserLog(nick, self.channel, bonusPoints, 0, 0)
|
|
bonusPointsText = 'Everyone gets a %d Point Bonus!!' % int(bonusPoints)
|
|
|
|
# give a special message if it was KAOS
|
|
self.sendMessage('All KAOS answered! %s' % bonusPointsText)
|
|
self.sendMessage('Total Awarded: \x02%d Points to %d Players\x02' % (int(self.totalAmountWon), len(self.correctPlayers)))
|
|
|
|
self.removeEvent()
|
|
|
|
threadStorage.updateQuestionStats(self.lineNumber, (4-self.hintsCounter), 0)
|
|
|
|
if self.stopPending == True:
|
|
self.stop()
|
|
return
|
|
|
|
waitTime = self.registryValue('general.waitTime',self.channel)
|
|
if waitTime < 2:
|
|
waitTime = 2
|
|
log.error('waitTime was set too low (<2 seconds). Setting to 2 seconds')
|
|
waitTime = time.time() + waitTime
|
|
self.queueEvent(waitTime, self.nextQuestion)
|
|
self.base.handleVoice(self.irc, username, channel)
|
|
|
|
def getHintString(self, hintNum=None):
|
|
if hintNum == None:
|
|
hintNum = self.hintsCounter
|
|
hintRatio = self.registryValue('hints.hintRatio') # % to show each hint
|
|
hints = ''
|
|
ratio = float(hintRatio * .01)
|
|
charMask = self.registryValue('hints.charMask', self.channel)
|
|
|
|
# create a string with hints for all of the answers
|
|
for ans in self.answers:
|
|
if ircutils.toLower(ans) in self.guessedAnswers:
|
|
continue
|
|
ans = unicode(ans.decode('utf-8'))
|
|
if hints != '':
|
|
hints += ' '
|
|
if len(self.answers) > 1:
|
|
hints += '['
|
|
if hintNum == 0:
|
|
masked = ans
|
|
for i in range(len(masked)):
|
|
if masked[i] in self.unmaskedChars:
|
|
hints+= masked[i]
|
|
else:
|
|
hints += charMask
|
|
elif hintNum == 1:
|
|
divider = int(len(ans) * ratio)
|
|
if divider > 3:
|
|
divider = 3
|
|
if divider >= len(ans):
|
|
divider = len(ans)-1
|
|
hints += ans[:divider]
|
|
masked = ans[divider:]
|
|
for i in range(len(masked)):
|
|
if masked[i] in self.unmaskedChars:
|
|
hints+= masked[i]
|
|
else:
|
|
hints += charMask
|
|
elif hintNum == 2:
|
|
divider = int(len(ans) * ratio)
|
|
if divider > 3:
|
|
divider = 3
|
|
if divider >= len(ans):
|
|
divider = len(ans)-1
|
|
lettersInARow=divider-1
|
|
maskedInARow=0
|
|
hints += ans[:divider]
|
|
ansend = ans[divider:]
|
|
hintsend = ''
|
|
unmasked = 0
|
|
if self.registryValue('hints.vowelsHint', self.channel):
|
|
hints+= self.getMaskedVowels(ansend, divider-1)
|
|
else:
|
|
hints+= self.getMaskedRandom(ansend, divider-1)
|
|
if len(self.answers) > 1:
|
|
hints += ']'
|
|
return hints.encode('utf-8')
|
|
|
|
def getMaskedVowels(self, letters, sizeOfUnmasked):
|
|
charMask = self.registryValue('hints.charMask', self.channel)
|
|
hintsList = ['']
|
|
unmasked = 0
|
|
lettersInARow = sizeOfUnmasked
|
|
for i in range(len(letters)):
|
|
masked = letters[i]
|
|
if masked in self.unmaskedChars:
|
|
hintsList.append(masked)
|
|
elif str.lower(self.removeAccents(masked.encode('utf-8'))) in 'aeiou' and unmasked < (len(letters)-1) and lettersInARow < 3:
|
|
hintsList.append(masked)
|
|
lettersInARow += 1
|
|
unmasked += 1
|
|
else:
|
|
hintsList.append(charMask)
|
|
lettersInARow = 0
|
|
hints = ''.join(hintsList)
|
|
return hints
|
|
|
|
def getMaskedRandom(self, letters, sizeOfUnmasked):
|
|
charMask = self.registryValue('hints.charMask', self.channel)
|
|
hintRatio = self.registryValue('hints.hintRatio') # % to show each hint
|
|
hints = ''
|
|
unmasked = 0
|
|
maskedInARow=0
|
|
lettersInARow=sizeOfUnmasked
|
|
for i in range(len(letters)):
|
|
masked = letters[i]
|
|
if masked in self.unmaskedChars:
|
|
hints += masked
|
|
unmasked += 1
|
|
elif maskedInARow > 2 and unmasked < (len(letters)-1):
|
|
lettersInARow += 1
|
|
unmasked += 1
|
|
maskedInARow = 0
|
|
hints += letters[i]
|
|
elif lettersInARow < 3 and unmasked < (len(letters)-1) and random.randint(0,100) < hintRatio:
|
|
lettersInARow += 1
|
|
unmasked += 1
|
|
maskedInARow = 0
|
|
hints += letters[i]
|
|
else:
|
|
maskedInARow += 1
|
|
lettersInARow=0
|
|
hints += charMask
|
|
return hints
|
|
|
|
def getOtherHintString(self):
|
|
charMask = self.registryValue('hints.charMask', self.channel)
|
|
if len(self.answers) > 1 or len(self.answers) < 1:
|
|
return
|
|
ans = self.answers[0]
|
|
|
|
hints = 'Hint: \x02\x0312'
|
|
|
|
divider = 0
|
|
|
|
if len(ans) < 2:
|
|
divider = 0
|
|
elif self.hintsCounter == 1:
|
|
divider = 1
|
|
elif self.hintsCounter == 2:
|
|
divider = int((len(ans) * .25) + 1)
|
|
if divider > 4:
|
|
divider = 4
|
|
elif self.hintsCounter == 3:
|
|
divider = int((len(ans) * .5) + 1)
|
|
if divider > 6:
|
|
divider = 6
|
|
if divider == len(ans):
|
|
divider -= 1
|
|
|
|
if divider > 0:
|
|
hints += ans[:divider]
|
|
|
|
return hints
|
|
|
|
def getOtherHint(self):
|
|
if self.questionOver:
|
|
return
|
|
if self.shownHint == False:
|
|
self.shownHint = True
|
|
if len(self.answers) == 1:
|
|
self.sendMessage(self.getOtherHintString())
|
|
|
|
def getRemainingKAOS(self):
|
|
if len(self.answers) > 1:
|
|
if self.shownHint == False:
|
|
self.shownHint = True
|
|
self.sendMessage('\x02\x0312%s' % (self.getHintString(self.hintsCounter-1)))
|
|
|
|
def loadGameState(self):
|
|
gameInfo = self.storage.getGame(self.channel)
|
|
if gameInfo is not None:
|
|
self.numAsked = gameInfo[2]
|
|
self.roundStartedAt = gameInfo[3]
|
|
self.lastWinner = gameInfo[4]
|
|
self.streak = int(gameInfo[5])
|
|
|
|
def loopEvent(self):
|
|
"""
|
|
Main game/question/hint loop called by event. Decides whether question or hint is needed.
|
|
"""
|
|
# out of hints to give?
|
|
if self.hintsCounter >= 3:
|
|
answer = ''
|
|
# create a string to show answers missed
|
|
for ans in self.answers:
|
|
# dont show guessed values at loss
|
|
if ircutils.toLower(ans) in self.guessedAnswers:
|
|
continue
|
|
if answer != '':
|
|
answer += ' '
|
|
if len(self.answers) > 1:
|
|
answer += '['
|
|
answer += ans
|
|
if len(self.answers) > 1:
|
|
answer += ']'
|
|
# Give failure message
|
|
if len(self.answers) > 1:
|
|
self.sendMessage("""Time's up! No one got \x02%s\x02""" % answer)
|
|
|
|
self.sendMessage("""Correctly Answered: \x02%d of %d\x02 Total Awarded: \x02%d Points to %d Players\x02"""
|
|
% (len(self.guessedAnswers), len(self.answers), int(self.totalAmountWon), len(self.correctPlayers))
|
|
)
|
|
else:
|
|
self.sendMessage("""Time's up! The answer was \x02%s\x02.""" % answer)
|
|
|
|
self.storage.updateQuestionStats(self.lineNumber, 0, 1)
|
|
|
|
#reset stuff
|
|
self.answers = []
|
|
self.alternativeAnswers = []
|
|
self.question = ''
|
|
self.questionOver = True
|
|
|
|
if self.stopPending == True:
|
|
self.stop()
|
|
return
|
|
|
|
# provide next question
|
|
waitTime = self.registryValue('general.waitTime',self.channel)
|
|
if waitTime < 2:
|
|
waitTime = 2
|
|
log.error('waitTime was set too low (<2 seconds). Setting to 2 seconds')
|
|
waitTime = time.time() + waitTime
|
|
self.queueEvent(waitTime, self.nextQuestion)
|
|
else:
|
|
# give out more hints
|
|
self.nextHint()
|
|
|
|
def nextHint(self):
|
|
"""
|
|
Max hints have not been reached, and no answer is found, need more hints
|
|
"""
|
|
hints = self.getHintString(self.hintsCounter)
|
|
#increment hints counter
|
|
self.hintsCounter += 1
|
|
self.sendMessage('Hint %s: \x02\x0312%s' % (self.hintsCounter, hints), 1, 9)
|
|
#reset hint shown
|
|
self.shownHint = False
|
|
|
|
hintTime = 2
|
|
if len(self.answers) > 1:
|
|
hintTime = self.registryValue('kaos.hintKAOS', self.channel)
|
|
else:
|
|
hintTime = self.registryValue('questions.hintTime', self.channel)
|
|
|
|
if hintTime < 2:
|
|
timout = 2
|
|
log.error('hintTime was set too low(<2 seconds). setting to 2 seconds')
|
|
|
|
hintTime += time.time()
|
|
self.queueEvent(hintTime, self.loopEvent)
|
|
|
|
def nextQuestion(self):
|
|
"""
|
|
Time for a new question
|
|
"""
|
|
inactivityTime = self.registryValue('general.timeout')
|
|
if self.lastAnswer < time.time() - inactivityTime:
|
|
self.stop()
|
|
self.sendMessage('Stopping due to inactivity')
|
|
return
|
|
|
|
|
|
if self.stopPending == True:
|
|
self.stop()
|
|
return
|
|
|
|
|
|
# reset and increment
|
|
self.questionOver = False
|
|
self.questionRepeated = False
|
|
self.shownHint = False
|
|
self.skipVoteCount = {}
|
|
self.question = ''
|
|
self.answers = []
|
|
self.alternativeAnswers = []
|
|
self.guessedAnswers = []
|
|
self.totalAmountWon = 0
|
|
self.lineNumber = -1
|
|
self.correctPlayers = {}
|
|
self.hintsCounter = 0
|
|
self.numAsked += 1
|
|
|
|
# grab the next q
|
|
numQuestion = self.storage.getNumQuestions()
|
|
if numQuestion == 0:
|
|
self.stop()
|
|
self.sendMessage('There are no questions. Stopping. If you are an admin use the addfile to add questions to the database')
|
|
return
|
|
|
|
numQuestionsLeftInRound = self.storage.getNumQuestionsNotAsked(self.channel, self.roundStartedAt)
|
|
if numQuestionsLeftInRound == 0:
|
|
self.numAsked = 1
|
|
self.roundStartedAt = time.mktime(time.localtime())
|
|
self.storage.updateGameRoundStarted(self.channel, self.roundStartedAt)
|
|
self.sendMessage('All of the questions have been asked, shuffling and starting over')
|
|
|
|
self.storage.updateGame(self.channel, self.numAsked) #increment q's asked
|
|
retrievedQuestion = self.retrieveQuestion()
|
|
|
|
self.points = self.registryValue('questions.defaultPoints', self.channel)
|
|
for x in retrievedQuestion:
|
|
if 'q' == x:
|
|
self.question = retrievedQuestion['q']
|
|
if 'a' == x:
|
|
self.answers = retrievedQuestion['a']
|
|
if 'aa' == x:
|
|
self.alternativeAnswers = retrievedQuestion['aa']
|
|
if 'p' == x:
|
|
self.points = retrievedQuestion['p']
|
|
if '#' == x:
|
|
self.lineNumber = retrievedQuestion['#']
|
|
|
|
# store the question number so it can be reported
|
|
self.storage.insertGameLog(self.channel, self.numAsked,
|
|
self.lineNumber, self.question)
|
|
|
|
tempQuestion = self.question.rstrip()
|
|
if tempQuestion[-1:] != '?':
|
|
tempQuestion = '%s?' % (tempQuestion)
|
|
|
|
# bold the q, add color
|
|
questionText = '\x02\x0303%s' % (tempQuestion)
|
|
|
|
# KAOS? report # of answers
|
|
if len(self.answers) > 1:
|
|
questionText += ' %d possible answers' % (len(self.answers))
|
|
|
|
questionMessageString = '%s: %s' % (self.numAsked, questionText)
|
|
|
|
maxLength = 400
|
|
|
|
questionMesagePieces = [questionMessageString[i:i+maxLength] for i in range(0, len(questionMessageString), maxLength)]
|
|
|
|
multipleMessages=False
|
|
|
|
for msgPiece in questionMesagePieces:
|
|
if multipleMessages:
|
|
msgPiece = '\x02\x0303%s' % (msgPiece)
|
|
multipleMessages = True
|
|
self.sendMessage(msgPiece, 1, 9)
|
|
self.queueEvent(0, self.loopEvent)
|
|
self.askedAt = time.time()
|
|
|
|
def queueEvent(self, hintTime, func):
|
|
"""
|
|
Create a new timer event for loopEvent call
|
|
"""
|
|
# create a new thread for event next step to happen for [hintTime] seconds
|
|
def event():
|
|
func()
|
|
if self.active:
|
|
schedule.addEvent(event, hintTime, '%s.trivia' % self.channel)
|
|
|
|
def removeAccents(self, text):
|
|
text = unicode(text.decode('utf-8'))
|
|
normalized = unicodedata.normalize('NFKD', text)
|
|
normalized = u''.join([c for c in normalized if not unicodedata.combining(c)])
|
|
return normalized.encode('utf-8')
|
|
|
|
def removeExtraSpaces(self, text):
|
|
return utils.str.normalizeWhitespace(text)
|
|
|
|
def repeatQuestion(self):
|
|
if self.questionRepeated == True:
|
|
return
|
|
if self.questionOver == True:
|
|
return
|
|
self.questionRepeated = True
|
|
try:
|
|
tempQuestion = self.question.rstrip()
|
|
if tempQuestion[-1:] != '?':
|
|
tempQuestion += ' ?'
|
|
|
|
# bold the q, add color
|
|
questionText = '\x02\x0303%s' % (tempQuestion)
|
|
|
|
# KAOS? report # of answers
|
|
if len(self.answers) > 1:
|
|
questionText += ' %d possible answers' % (len(self.answers))
|
|
|
|
questionMessageString = '%s: %s' % (self.numAsked, questionText)
|
|
|
|
maxLength = 400
|
|
|
|
questionMesagePieces = [questionMessageString[i:i+maxLength] for i in range(0, len(questionMessageString), maxLength)]
|
|
|
|
multipleMessages=False
|
|
|
|
for msgPiece in questionMesagePieces:
|
|
if multipleMessages:
|
|
msgPiece = '\x02\x0303%s' % (msgPiece)
|
|
multipleMessages = True
|
|
self.sendMessage(msgPiece, 1, 9)
|
|
except AttributeError:
|
|
pass
|
|
|
|
def removeEvent(self):
|
|
"""
|
|
Remove/cancel timer event
|
|
"""
|
|
# try and remove the current timer and thread, if we fail don't just carry on
|
|
try:
|
|
schedule.removeEvent('%s.trivia' % self.channel)
|
|
except KeyError:
|
|
pass
|
|
|
|
def retrieveQuestion(self):
|
|
# temporary function to get data
|
|
lineNumber, question, timesAnswered, timesMissed = self.retrieveQuestionFromSql()
|
|
answer = question.split('*', 1)
|
|
if len(answer) > 1:
|
|
question = answer[0].strip()
|
|
answers = answer[1].split('*')
|
|
answer = []
|
|
alternativeAnswers = []
|
|
if ircutils.toLower(question[:4]) == 'kaos':
|
|
for ans in answers:
|
|
if answer.count(ans) == 0:
|
|
answer.append(str(ans).strip())
|
|
elif ircutils.toLower(question[:5]) == 'uword':
|
|
for ans in answers:
|
|
answer.append(str(ans).strip())
|
|
question = 'Unscramble the letters: '
|
|
shuffledLetters = list(unicode(ans.decode('utf-8')))
|
|
random.shuffle(shuffledLetters)
|
|
for letter in shuffledLetters:
|
|
question += letter
|
|
question += ' '
|
|
question = question.encode('utf-8')
|
|
break
|
|
else:
|
|
for ans in answers:
|
|
if answer == []:
|
|
answer.append(str(ans).strip())
|
|
else:
|
|
alternativeAnswers.append(str(ans).strip())
|
|
|
|
points = self.registryValue('questions.defaultPoints', self.channel)
|
|
if len(answer) > 1:
|
|
points = self.registryValue('kaos.defaultKAOS', self.channel) * len(answers)
|
|
|
|
additionalPoints = 0
|
|
additionalPoints += timesAnswered * -5
|
|
additionalPoints += timesMissed * 5
|
|
if additionalPoints > 200:
|
|
additionalPoints = 200
|
|
if additionalPoints < -200:
|
|
additionalPoints = -200
|
|
points += additionalPoints
|
|
return {'p':points,
|
|
'q':question,
|
|
'a':answer,
|
|
'aa':alternativeAnswers,
|
|
'#':lineNumber
|
|
}
|
|
else:
|
|
log.info('Bad question found on line#%d' % lineNumber)
|
|
# TODO report bad question
|
|
|
|
# default question, everything went wrong with grabbing question
|
|
return {'#':lineNumber,
|
|
'p':10050,
|
|
'q':'KAOS: The 10 Worst U.S. Presidents (Last Name Only)? (This is a panic question, if you see this report this question. it is malformed.)',
|
|
'a':['Bush', 'Nixon', 'Hoover', 'Grant', 'Johnson',
|
|
'Ford', 'Reagan', 'Coolidge', 'Pierce'],
|
|
'aa':['Obama']
|
|
}
|
|
|
|
def retrieveQuestionFromSql(self):
|
|
question = self.storage.getRandomQuestionNotAsked(self.channel, self.roundStartedAt)
|
|
question = question[0]
|
|
return (question[0], question[2], question[4], question[5])
|
|
|
|
def sendMessage(self, msg, color=None, bgcolor=None):
|
|
""" <msg>, [<color>], [<bgcolor>]
|
|
helper for game instance to send messages to channel
|
|
"""
|
|
# no color
|
|
self.irc.sendMsg(ircmsgs.privmsg(self.channel, ' %s ' % msg))
|
|
|
|
def stop(self):
|
|
"""
|
|
Stop a game in progress
|
|
"""
|
|
# responsible for stopping a timer/thread after being told to stop
|
|
self.active = False
|
|
self.stopPending = False
|
|
self.removeEvent()
|
|
self.sendMessage('Trivia stopped. :\'(')
|
|
channelCanonical = ircutils.toLower(self.channel)
|
|
if self.network in self.games:
|
|
if channelCanonical in self.games[self.network]:
|
|
del self.games[self.network][channelCanonical]
|
|
|
|
#Storage for users and points using sqlite3
|
|
class Storage:
|
|
"""
|
|
Storage class
|
|
"""
|
|
def __init__(self,loc):
|
|
self.loc = loc
|
|
self.conn = sqlite3.connect(loc, check_same_thread=False) # dont check threads
|
|
# otherwise errors
|
|
self.conn.text_factory = str
|
|
|
|
def chunk(self, qs, rows=10000):
|
|
""" Divides the data into 10000 rows each """
|
|
for i in xrange(0, len(qs), rows):
|
|
yield qs[i:i+rows]
|
|
|
|
def countTemporaryQuestions(self):
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviatemporaryquestion''')
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
return rows
|
|
|
|
def countDeletes(self):
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviadelete''')
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
return rows
|
|
|
|
def countEdits(self):
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviaedit''')
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
return rows
|
|
|
|
def countReports(self):
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviareport''')
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
return rows
|
|
|
|
def deleteQuestion(self, questionId):
|
|
c = self.conn.cursor()
|
|
test = c.execute('''UPDATE triviaquestion set
|
|
deleted=1
|
|
WHERE id=?''', (questionId,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def dropActivityTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviaactivity''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropDeleteTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviadelete''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropUserTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviausers''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropLoginTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE trivialogin''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropUserLogTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviauserlog''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropGameTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP table triviagames''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropGameLogTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviagameslog''')
|
|
c.execute('''DROP INDEX gamelograndomindex''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropReportTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviareport''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropQuestionTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviaquestion''')
|
|
c.execute('''DROP INDEX questionrandomindex''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropTemporaryQuestionTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviatemporaryquestion''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def dropEditTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''DROP TABLE triviaedit''')
|
|
except:
|
|
pass
|
|
c.close()
|
|
|
|
def getRandomQuestionNotAsked(self, channel, roundStart):
|
|
c = self.conn.cursor()
|
|
c.execute('''SELECT * FROM triviaquestion
|
|
WHERE deleted=0 AND id NOT IN (SELECT tl.line_num FROM triviagameslog tl WHERE tl.channel_canonical=? AND tl.asked_at>=?)
|
|
ORDER BY random() LIMIT 1
|
|
''', (ircutils.toLower(channel),roundStart))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getQuestionByRound(self, roundNumber, channel):
|
|
channel=ircutils.toLower(channel)
|
|
c = self.conn.cursor()
|
|
c.execute('''SELECT * FROM triviaquestion WHERE id=(SELECT tgl.line_num
|
|
FROM triviagameslog tgl
|
|
WHERE tgl.round_num=?
|
|
AND tgl.channel_canonical=?
|
|
ORDER BY id DESC
|
|
LIMIT 1)''', (roundNumber,channel))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getNumQuestionsNotAsked(self, channel, roundStart):
|
|
c = self.conn.cursor()
|
|
result = c.execute('''SELECT count(id) FROM triviaquestion
|
|
WHERE deleted=0 AND id NOT IN (SELECT tl.line_num FROM triviagameslog tl WHERE tl.channel=? AND tl.asked_at>=?)''',
|
|
(channel,roundStart))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
return rows
|
|
|
|
def getUserRanks(self, username, channel):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
channelCanonical = None
|
|
if channel is not None:
|
|
channelCanonical = ircutils.toLower(channel)
|
|
dateObject = datetime.date.today()
|
|
day = dateObject.day
|
|
month = dateObject.month
|
|
year = dateObject.year
|
|
|
|
query = '''select tr.rank
|
|
from (
|
|
select count(tu2.id)+1 as rank
|
|
from (
|
|
select id, username, sum(points_made) as totalscore
|
|
from triviauserlog'''
|
|
arguments = []
|
|
|
|
if channel is not None:
|
|
query = '''%s where channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
) as tu2
|
|
where tu2.totalscore > (
|
|
select sum(points_made)
|
|
from triviauserlog
|
|
where username_canonical=?''' % (query)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s )
|
|
) as tr
|
|
where
|
|
exists(
|
|
select *
|
|
from triviauserlog
|
|
where username_canonical=?''' % (query)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s ) limit 1''' % (query)
|
|
|
|
c = self.conn.cursor()
|
|
c.execute(query, tuple(arguments))
|
|
data = []
|
|
|
|
rank = 0
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
rank = d
|
|
break
|
|
data.append(rank)
|
|
|
|
query = '''select tr.rank
|
|
from (
|
|
select count(tu2.id)+1 as rank
|
|
from (
|
|
select id, username, sum(points_made) as totalscore
|
|
from triviauserlog
|
|
where year=?'''
|
|
arguments = [year]
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
) as tu2
|
|
where tu2.totalscore > (
|
|
select sum(points_made)
|
|
from triviauserlog
|
|
where year=?
|
|
and username_canonical=?''' % (query)
|
|
arguments.append(year)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s )
|
|
) as tr
|
|
where
|
|
exists(
|
|
select *
|
|
from triviauserlog
|
|
where year=?
|
|
and username_canonical=?''' % (query)
|
|
arguments.append(year)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s ) limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
rank = 0
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
rank = d
|
|
break
|
|
data.append(rank)
|
|
|
|
query = '''select tr.rank
|
|
from (
|
|
select count(tu2.id)+1 as rank
|
|
from (
|
|
select id, username, sum(points_made) as totalscore
|
|
from triviauserlog
|
|
where month=?
|
|
and year=?'''
|
|
arguments = [month, year]
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
) as tu2
|
|
where tu2.totalscore > (
|
|
select sum(points_made)
|
|
from triviauserlog
|
|
where month=?
|
|
and year=?
|
|
and username_canonical=?''' % (query)
|
|
arguments.append(month)
|
|
arguments.append(year)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s )
|
|
) as tr
|
|
where
|
|
exists(
|
|
select *
|
|
from triviauserlog
|
|
where month=?
|
|
and year=?
|
|
and username=?''' % (query)
|
|
arguments.append(month)
|
|
arguments.append(year)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s ) limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
rank = 0
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
rank = d
|
|
break
|
|
data.append(rank)
|
|
|
|
weekSqlClause = ''
|
|
d = datetime.date.today()
|
|
weekday=d.weekday()
|
|
d -= datetime.timedelta(weekday)
|
|
for i in range(7):
|
|
if i > 0:
|
|
weekSqlClause += ' or '
|
|
weekSqlClause += '''(
|
|
year=%d
|
|
and month=%d
|
|
and day=%d)''' % (d.year, d.month, d.day)
|
|
d += datetime.timedelta(1)
|
|
|
|
weekSql = '''select tr.rank
|
|
from (
|
|
select count(tu2.id)+1 as rank
|
|
from (
|
|
select id, username, sum(points_made) as totalscore
|
|
from triviauserlog
|
|
where ('''
|
|
arguments = []
|
|
|
|
weekSql += weekSqlClause
|
|
weekSql +='''
|
|
)'''
|
|
|
|
if channel is not None:
|
|
weekSql = '''%s and channel_canonical=?''' % (weekSql)
|
|
arguments.append(channelCanonical)
|
|
|
|
weekSql += '''group by username_canonical
|
|
) as tu2
|
|
where tu2.totalscore > (
|
|
select sum(points_made)
|
|
from triviauserlog
|
|
where username_canonical=?'''
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
weekSql = '''%s and channel_canonical=?''' % (weekSql)
|
|
arguments.append(channelCanonical)
|
|
|
|
weekSql += ''' and ('''
|
|
weekSql += weekSqlClause
|
|
weekSql += '''
|
|
)
|
|
)
|
|
) as tr
|
|
where
|
|
exists(
|
|
select *
|
|
from triviauserlog
|
|
where username_canonical=?'''
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
weekSql = '''%s and channel_canonical=?''' % (weekSql)
|
|
arguments.append(channelCanonical)
|
|
|
|
weekSql += ''' and ('''
|
|
weekSql += weekSqlClause
|
|
weekSql += '''
|
|
)
|
|
) limit 1'''
|
|
c.execute(weekSql, tuple(arguments))
|
|
|
|
rank = 0
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
rank = d
|
|
break
|
|
data.append(rank)
|
|
|
|
query = '''select tr.rank
|
|
from (
|
|
select count(tu2.id)+1 as rank
|
|
from (
|
|
select id, username, sum(points_made) as totalscore
|
|
from triviauserlog
|
|
where day=?
|
|
and month=?
|
|
and year=?'''
|
|
arguments = [day, month, year]
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
) as tu2
|
|
where tu2.totalscore > (
|
|
select sum(points_made)
|
|
from triviauserlog
|
|
where day=?
|
|
and month=?
|
|
and year=?
|
|
and username_canonical=?''' % (query)
|
|
arguments.append(day)
|
|
arguments.append(month)
|
|
arguments.append(year)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s )
|
|
) as tr
|
|
where
|
|
exists(
|
|
select *
|
|
from triviauserlog
|
|
where day=?
|
|
and month=?
|
|
and year=?
|
|
and username_canonical=?''' % (query)
|
|
arguments.append(day)
|
|
arguments.append(month)
|
|
arguments.append(year)
|
|
arguments.append(usernameCanonical)
|
|
|
|
if channel is not None:
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s ) limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
rank = 0
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
rank = d
|
|
break
|
|
data.append(rank)
|
|
|
|
c.close()
|
|
return data
|
|
|
|
def getUser(self, username, channel):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
channelCanonical = None
|
|
if channel is not None:
|
|
channelCanonical = ircutils.toLower(channel)
|
|
dateObject = datetime.date.today()
|
|
day = dateObject.day
|
|
month = dateObject.month
|
|
year = dateObject.year
|
|
|
|
c = self.conn.cursor()
|
|
|
|
data = []
|
|
data.append(username)
|
|
data.append(username)
|
|
|
|
query = '''select
|
|
sum(tl.points_made) as points,
|
|
sum(tl.num_answered) as answered
|
|
from triviauserlog tl
|
|
where tl.username_canonical=?'''
|
|
arguments = [usernameCanonical]
|
|
|
|
if channel is not None:
|
|
query = '''%s and tl.channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
data.append(d)
|
|
break
|
|
|
|
query = '''select
|
|
sum(tl.points_made) as yearPoints,
|
|
sum(tl.num_answered) as yearAnswered
|
|
from triviauserlog tl
|
|
where
|
|
tl.username_canonical=?
|
|
and tl.year=?'''
|
|
arguments = [usernameCanonical, year]
|
|
|
|
if channel is not None:
|
|
query = '''%s and tl.channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
data.append(d)
|
|
break
|
|
|
|
query = '''select
|
|
sum(tl.points_made) as yearPoints,
|
|
sum(tl.num_answered) as yearAnswered
|
|
from triviauserlog tl
|
|
where
|
|
tl.username_canonical=?
|
|
and tl.year=?
|
|
and tl.month=?'''
|
|
arguments = [usernameCanonical, year, month]
|
|
|
|
if channel is not None:
|
|
query = '''%s and tl.channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
data.append(d)
|
|
break
|
|
|
|
query = '''select
|
|
sum(tl.points_made) as yearPoints,
|
|
sum(tl.num_answered) as yearAnswered
|
|
from triviauserlog tl
|
|
where
|
|
tl.username_canonical=?
|
|
and ('''
|
|
|
|
d = datetime.date.today()
|
|
weekday=d.weekday()
|
|
d -= datetime.timedelta(weekday)
|
|
for i in range(7):
|
|
if i > 0:
|
|
query += ' or '
|
|
query += '''
|
|
(tl.year=%d
|
|
and tl.month=%d
|
|
and tl.day=%d)''' % (d.year, d.month, d.day)
|
|
d += datetime.timedelta(1)
|
|
|
|
query += ')'
|
|
arguments = [usernameCanonical]
|
|
|
|
if channel is not None:
|
|
query = '''%s and tl.channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
data.append(d)
|
|
break
|
|
|
|
query = '''select
|
|
sum(tl.points_made) as yearPoints,
|
|
sum(tl.num_answered) as yearAnswered
|
|
from triviauserlog tl
|
|
where
|
|
tl.username_canonical=?
|
|
and tl.year=?
|
|
and tl.month=?
|
|
and tl.day=?'''
|
|
arguments = [usernameCanonical, year, month, day]
|
|
|
|
if channel is not None:
|
|
query = '''%s and tl.channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s limit 1''' % (query)
|
|
|
|
c.execute(query, tuple(arguments))
|
|
|
|
for row in c:
|
|
for d in row:
|
|
if d is None:
|
|
d=0
|
|
data.append(d)
|
|
break
|
|
for d in self.getUserRanks(username, channel):
|
|
data.append(d)
|
|
|
|
c.close()
|
|
return data
|
|
|
|
def getGame(self, channel):
|
|
channel = ircutils.toLower(channel)
|
|
c = self.conn.cursor()
|
|
c.execute('''SELECT * FROM triviagames
|
|
where channel_canonical=?
|
|
limit 1''', (channel,))
|
|
data = None
|
|
for row in c:
|
|
data = row
|
|
break
|
|
c.close()
|
|
return data
|
|
|
|
def getNumUser(self, channel):
|
|
channelCanonical = ircutils.toLower(channel)
|
|
c = self.conn.cursor()
|
|
result = c.execute('select count(distinct(username_canonical)) from triviauserlog where channel_canonical=?', (channelCanonical,))
|
|
result = result.fetchone()[0]
|
|
c.close()
|
|
return result
|
|
|
|
def getNumQuestions(self):
|
|
c = self.conn.cursor()
|
|
result = c.execute('select count(*) from triviaquestion where deleted=0')
|
|
result = result.fetchone()[0]
|
|
c.close()
|
|
return result
|
|
|
|
def getNumKAOS(self):
|
|
c = self.conn.cursor()
|
|
result = c.execute('select count(*) from triviaquestion where lower(substr(question,1,4))=?',('kaos',))
|
|
result = result.fetchone()[0]
|
|
c.close()
|
|
return result
|
|
|
|
def getQuestion(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviaquestion where id=? limit 1', (id,))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getNumActiveThisWeek(self, channel):
|
|
channelCanonical = ircutils.toLower(channel)
|
|
d = datetime.date.today()
|
|
weekday=d.weekday()
|
|
d -= datetime.timedelta(weekday)
|
|
weekSqlString = ''
|
|
for i in range(7):
|
|
if i > 0:
|
|
weekSqlString += ' or '
|
|
weekSqlString += '''
|
|
(tl.year=%d
|
|
and tl.month=%d
|
|
and tl.day=%d)''' % (d.year, d.month, d.day)
|
|
d += datetime.timedelta(1)
|
|
c = self.conn.cursor()
|
|
weekSql = '''select count(distinct(tl.username_canonical))
|
|
from triviauserlog tl
|
|
where channel_canonical=? and ('''
|
|
weekSql += weekSqlString
|
|
weekSql += ''')'''
|
|
result = c.execute(weekSql, (channelCanonical,))
|
|
rows = result.fetchone()[0]
|
|
return rows
|
|
|
|
def getDeleteById(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviadelete where id=? limit 1', (id,))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getDeleteTop3(self, page=1, amount=3):
|
|
if page < 1:
|
|
page=1
|
|
if amount < 1:
|
|
amount=3
|
|
page -= 1
|
|
start = page * amount
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviadelete order by id desc limit ?, ?', (start, amount))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getReportById(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviareport where id=? limit 1', (id,))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getReportTop3(self, page=1, amount=3):
|
|
if page < 1:
|
|
page=1
|
|
if amount < 1:
|
|
amount=3
|
|
page -= 1
|
|
start = page * amount
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviareport order by id desc limit ?, ?', (start, amount))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getTemporaryQuestionTop3(self, page=1, amount=3):
|
|
if page < 1:
|
|
page=1
|
|
if amount < 1:
|
|
amount=3
|
|
page -= 1
|
|
start = page * amount
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviatemporaryquestion order by id desc limit ?, ?', (start, amount))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getTemporaryQuestionById(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviatemporaryquestion where id=? limit 1', (id,))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getEditById(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviaedit where id=? limit 1', (id,))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def getNumUserActiveIn(self, channel, timeSeconds):
|
|
channelCanonical = ircutils.toLower(channel)
|
|
epoch = int(time.mktime(time.localtime()))
|
|
dateObject = datetime.date.today()
|
|
day = dateObject.day
|
|
month = dateObject.month
|
|
year = dateObject.year
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviauserlog
|
|
where day=? and month=? and year=?
|
|
and channel_canonical=?
|
|
and last_updated>?''', (day, month, year, channelCanonical, (epoch-timeSeconds)))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
return rows
|
|
|
|
def gameExists(self, channel):
|
|
channel = ircutils.toLower(channel)
|
|
c = self.conn.cursor()
|
|
result = c.execute('select count(id) from triviagames where channel_canonical=?', (channel,))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def getEditTop3(self, page=1, amount=3):
|
|
if page < 1:
|
|
page=1
|
|
if amount < 1:
|
|
amount=3
|
|
page -= 1
|
|
start = page * amount
|
|
c = self.conn.cursor()
|
|
c.execute('SELECT * FROM triviaedit order by id desc limit ?, ?', (start, amount))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def loginExists(self, username):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
c = self.conn.cursor()
|
|
result = c.execute('select count(id) from trivialogin where username_canonical=?', (usernameCanonical,))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def insertActivity(self, aType, activity, channel, network, timestamp=None):
|
|
if timestamp is None:
|
|
timestamp = int(time.mktime(time.localtime()))
|
|
channelCanonical = ircutils.toLower(channel)
|
|
c = self.conn.cursor()
|
|
c.execute('insert into triviaactivity values (NULL, ?, ?, ?, ?, ?, ?)',
|
|
(aType, activity, channel, channelCanonical, network, timestamp))
|
|
self.conn.commit()
|
|
|
|
def insertDelete(self, username, channel, lineNumber, reason):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
channelCanonical = ircutils.toLower(channel)
|
|
c = self.conn.cursor()
|
|
c.execute('insert into triviadelete values (NULL, ?, ?, ?, ?, ?, ?)',
|
|
(username, usernameCanonical, lineNumber, channel, channelCanonical, reason))
|
|
self.conn.commit()
|
|
|
|
def insertLogin(self, username, salt, isHashed, password, capability):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
if self.loginExists(username):
|
|
return self.updateLogin(username, salt, isHashed, password, capability)
|
|
if not isHashed:
|
|
isHashed = 0
|
|
else:
|
|
isHashed = 1
|
|
c = self.conn.cursor()
|
|
c.execute('insert into trivialogin values (NULL, ?, ?, ?, ?, ?, ?)',
|
|
(username, usernameCanonical, salt, isHashed, password, capability))
|
|
self.conn.commit()
|
|
|
|
def insertUserLog(self, username, channel, score, numAnswered, timeTaken, day=None, month=None, year=None, epoch=None):
|
|
if day == None and month == None and year == None:
|
|
dateObject = datetime.date.today()
|
|
day = dateObject.day
|
|
month = dateObject.month
|
|
year = dateObject.year
|
|
score = int(score)
|
|
if epoch is None:
|
|
epoch = int(time.mktime(time.localtime()))
|
|
if self.userLogExists(username, channel, day, month, year):
|
|
return self.updateUserLog(username, channel, score, numAnswered, timeTaken, day, month, year, epoch)
|
|
c = self.conn.cursor()
|
|
usernameCanonical = ircutils.toLower(username)
|
|
channelCanonical = ircutils.toLower(channel)
|
|
scoreAvg = 'NULL'
|
|
if numAnswered >= 1:
|
|
scoreAvg = score / numAnswered
|
|
c.execute('insert into triviauserlog values (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
|
(username, score, numAnswered, day, month, year, epoch, timeTaken, scoreAvg, usernameCanonical, channel, channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def insertUser(self, username, numEditted=0, numEdittedAccepted=0, numReported=0, numQuestionsAdded=0, numQuestionsAccepted=0):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
if self.userExists(username):
|
|
return self.updateUser(username, numEditted, numEdittedAccepted, numReported, numQuestionsAdded, numQuestionsAccepted)
|
|
c = self.conn.cursor()
|
|
c.execute('insert into triviausers values (NULL, ?, ?, ?, ?, ?, ?, ?, 0)',
|
|
(
|
|
username,
|
|
numEditted,
|
|
numEdittedAccepted,
|
|
usernameCanonical,
|
|
numReported,
|
|
numQuestionsAdded,
|
|
numQuestionsAccepted
|
|
)
|
|
)
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def insertGame(self, channel, numAsked=0, epoch=None):
|
|
channelCanonical = ircutils.toLower(channel)
|
|
if self.gameExists(channel):
|
|
return self.updateGame(channel, numAsked)
|
|
if epoch is None:
|
|
epoch = int(time.mktime(time.localtime()))
|
|
c = self.conn.cursor()
|
|
c.execute('insert into triviagames values (NULL, ?, ?, ?, 0, 0, ?, 0, "", "")', (channel,numAsked,epoch,channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def insertGameLog(self, channel, roundNumber, lineNumber, questionText, askedAt=None):
|
|
channelCanonical = ircutils.toLower(channel)
|
|
if askedAt is None:
|
|
askedAt = int(time.mktime(time.localtime()))
|
|
c = self.conn.cursor()
|
|
c.execute('insert into triviagameslog values (NULL, ?, ?, ?, ?, ?, ?)', (channel,roundNumber,lineNumber,questionText,askedAt,channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def insertReport(self, channel, username, reportText, questionNum, reportedAt=None):
|
|
channelCanonical = ircutils.toLower(channel)
|
|
usernameCanonical = ircutils.toLower(username)
|
|
if reportedAt is None:
|
|
reportedAt = int(time.mktime(time.localtime()))
|
|
c = self.conn.cursor()
|
|
c.execute('insert into triviareport values (NULL, ?, ?, ?, ?, NULL, NULL, ?, ?, ?)',
|
|
(channel,username,reportText,reportedAt,questionNum,usernameCanonical,channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def insertQuestionsBulk(self, questions):
|
|
c = self.conn.cursor()
|
|
#skipped=0
|
|
divData = self.chunk(questions) # divide into 10000 rows each
|
|
for chunk in divData:
|
|
c.executemany('''insert into triviaquestion values (NULL, ?, ?, 0, 0, 0)''',
|
|
chunk)
|
|
self.conn.commit()
|
|
skipped = self.removeDuplicateQuestions()
|
|
c.close()
|
|
return ((len(questions) - skipped), skipped)
|
|
|
|
def insertEdit(self, questionId, questionText, username, channel, createdAt=None):
|
|
c = self.conn.cursor()
|
|
channelCanonical = ircutils.toLower(channel)
|
|
usernameCanonical = ircutils.toLower(username)
|
|
if createdAt is None:
|
|
createdAt = int(time.mktime(time.localtime()))
|
|
c.execute('insert into triviaedit values (NULL, ?, ?, NULL, ?, ?, ?, ?, ?)',
|
|
(questionId,questionText,username,channel,createdAt, usernameCanonical, channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def insertTemporaryQuestion(self, username, channel, question):
|
|
c = self.conn.cursor()
|
|
channelCanonical = ircutils.toLower(channel)
|
|
usernameCanonical = ircutils.toLower(username)
|
|
c.execute('insert into triviatemporaryquestion values (NULL, ?, ?, ?, ?, ?)',
|
|
(username,channel,question,usernameCanonical,channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def isQuestionDeleted(self, id):
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviaquestion
|
|
where deleted=1 and id=?''', (id,))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def isQuestionPendingDeletion(self, id):
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviadelete
|
|
where line_num=?''', (id,))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def makeActivityTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviaactivity (
|
|
id integer primary key autoincrement,
|
|
type text,
|
|
activity text,
|
|
channel text,
|
|
channel_canonical text,
|
|
network text,
|
|
timestamp integer
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeDeleteTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviadelete (
|
|
id integer primary key autoincrement,
|
|
username text,
|
|
username_canonical text,
|
|
line_num integer,
|
|
channel text,
|
|
channel_canonical text,
|
|
reason text
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeLoginTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table trivialogin (
|
|
id integer primary key autoincrement,
|
|
username text,
|
|
username_canonical text not null unique,
|
|
salt text,
|
|
is_hashed integer not null default 1,
|
|
password text,
|
|
capability text
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeUserTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviausers (
|
|
id integer primary key autoincrement,
|
|
username text,
|
|
num_editted integer,
|
|
num_editted_accepted integer,
|
|
username_canonical text not null unique,
|
|
num_reported integer,
|
|
num_questions_added integer,
|
|
num_questions_accepted integer,
|
|
highest_streak integer
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeUserLogTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviauserlog (
|
|
id integer primary key autoincrement,
|
|
username text,
|
|
points_made integer,
|
|
num_answered integer,
|
|
day integer,
|
|
month integer,
|
|
year integer,
|
|
last_updated integer,
|
|
average_time integer,
|
|
average_score integer,
|
|
username_canonical text,
|
|
channel text,
|
|
channel_canonical text,
|
|
unique(username_canonical,channel_canonical, day, month, year) on conflict replace
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeGameTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviagames (
|
|
id integer primary key autoincrement,
|
|
channel text,
|
|
num_asked integer,
|
|
round_started integer,
|
|
last_winner text,
|
|
streak integer,
|
|
channel_canonical text not null unique,
|
|
longest_streak integer,
|
|
longest_streak_holder text,
|
|
longest_streak_holder_canonical text
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeGameLogTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviagameslog (
|
|
id integer primary key autoincrement,
|
|
channel text,
|
|
round_num integer,
|
|
line_num integer,
|
|
question text,
|
|
asked_at integer,
|
|
channel_canonical text
|
|
)''')
|
|
c.execute('''create index gamelograndomindex
|
|
on triviagameslog (channel, line_num, asked_at)
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeReportTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviareport (
|
|
id integer primary key autoincrement,
|
|
channel text,
|
|
username text,
|
|
report_text text,
|
|
reported_at integer,
|
|
fixed_at integer,
|
|
fixed_by text,
|
|
question_num integer,
|
|
username_canonical text,
|
|
channel_canonical text
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeTemporaryQuestionTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviatemporaryquestion (
|
|
id integer primary key autoincrement,
|
|
username text,
|
|
channel text,
|
|
question text,
|
|
username_canonical text,
|
|
channel_canonical text
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeQuestionTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviaquestion (
|
|
id integer primary key autoincrement,
|
|
question_canonical text,
|
|
question text,
|
|
deleted integer not null default 0,
|
|
num_answered integer,
|
|
num_missed integer
|
|
)''')
|
|
c.execute('''create index questionrandomindex
|
|
on triviagameslog (id, deleted)
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def makeEditTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviaedit (
|
|
id integer primary key autoincrement,
|
|
question_id integer,
|
|
question text,
|
|
status text,
|
|
username text,
|
|
channel text,
|
|
created_at text,
|
|
username_canonical text,
|
|
channel_canonical
|
|
)''')
|
|
except:
|
|
pass
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def questionExists(self, question):
|
|
c = self.conn.cursor()
|
|
result = c.execute('select count(id) from triviaquestion where question=? or question_canonical=?', (question,question))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def questionIdExists(self, id):
|
|
c = self.conn.cursor()
|
|
result = c.execute('select count(id) from triviaquestion where id=?', (id,))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def removeOldActivity(self,count=100):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviaactivity
|
|
where id not in (
|
|
select id
|
|
from triviaactivity
|
|
order by id desc
|
|
limit ?
|
|
)''', (count,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeDelete(self, deleteId):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviadelete
|
|
where id=?''', (deleteId,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeDuplicateQuestions(self):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviaquestion where id not in (select min(id) from triviaquestion GROUP BY question_canonical)''')
|
|
num = c.rowcount
|
|
self.conn.commit()
|
|
c.close()
|
|
return num
|
|
|
|
def removeEdit(self, editId):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviaedit
|
|
where id=?''', (editId,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeLogin(self, username):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from trivialogin
|
|
where username_canonical=?''', (usernameCanonical,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeReport(self, repId):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviareport
|
|
where id=?''', (repId,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeReportByQuestionNumber(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviareport
|
|
where question_num=?''', (id,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeEditByQuestionNumber(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviaedit
|
|
where question_id=?''', (id,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeDeleteByQuestionNumber(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviadelete
|
|
where line_num=?''', (id,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeTemporaryQuestion(self, id):
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviatemporaryquestion
|
|
where id=?''', (id,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def removeUserLogs(self, username):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
c = self.conn.cursor()
|
|
c.execute('''delete from triviauserlog
|
|
where username_canonical=?''', (usernameCanonical,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def restoreQuestion(self, id):
|
|
c = self.conn.cursor()
|
|
test = c.execute('''update triviaquestion set
|
|
deleted=0
|
|
where id=?''', (id,))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def transferUserLogs(self, userFrom, userTo):
|
|
userFromCanonical = ircutils.toLower(userFrom)
|
|
userToCanonical = ircutils.toLower(userTo)
|
|
c = self.conn.cursor()
|
|
c.execute('''
|
|
update triviauserlog
|
|
set num_answered=num_answered
|
|
+ifnull(
|
|
(
|
|
select t3.num_answered
|
|
from triviauserlog t3
|
|
where t3.day=triviauserlog.day
|
|
and t3.month=triviauserlog.month
|
|
and t3.year=triviauserlog.year
|
|
and t3.channel_canonical=triviauserlog.channel_canonical
|
|
and t3.username_canonical=?
|
|
)
|
|
,0),
|
|
points_made=points_made
|
|
+ifnull(
|
|
(
|
|
select t2.points_made
|
|
from triviauserlog t2
|
|
where t2.day=triviauserlog.day
|
|
and t2.month=triviauserlog.month
|
|
and t2.year=triviauserlog.year
|
|
and t2.channel_canonical=triviauserlog.channel_canonical
|
|
and t2.username_canonical=?
|
|
)
|
|
,0)
|
|
where id in (
|
|
select id
|
|
from triviauserlog tl
|
|
where username_canonical=?
|
|
and exists (
|
|
select id
|
|
from triviauserlog tl2
|
|
where tl2.day=tl.day
|
|
and tl2.month=tl.month
|
|
and tl2.year=tl.year
|
|
and tl2.channel_canonical=tl.channel_canonical
|
|
and username_canonical=?
|
|
)
|
|
)
|
|
''', (userFromCanonical,userFromCanonical,userToCanonical,userFromCanonical))
|
|
|
|
c.execute('''
|
|
update triviauserlog
|
|
set username=?,
|
|
username_canonical=?
|
|
where username_canonical=?
|
|
and not exists (
|
|
select 1
|
|
from triviauserlog tl
|
|
where tl.day=triviauserlog.day
|
|
and tl.month=triviauserlog.month
|
|
and tl.year=triviauserlog.year
|
|
and tl.channel_canonical=triviauserlog.channel_canonical
|
|
and tl.username_canonical=?
|
|
)
|
|
''',(userTo, userToCanonical, userFromCanonical, userToCanonical))
|
|
self.conn.commit()
|
|
|
|
self.removeUserLogs(userFrom)
|
|
|
|
def userLogExists(self, username, channel, day, month, year):
|
|
c = self.conn.cursor()
|
|
args = (ircutils.toLower(username),ircutils.toLower(channel),day,month,year)
|
|
result = c.execute('select count(id) from triviauserlog where username_canonical=? and channel_canonical=? and day=? and month=? and year=?', args)
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def userExists(self, username):
|
|
c = self.conn.cursor()
|
|
usr = (ircutils.toLower(username),)
|
|
result = c.execute('select count(id) from triviausers where username_canonical=?', usr)
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def updateLogin(self, username, salt, isHashed, password, capability):
|
|
if not self.loginExists(username):
|
|
return self.insertLogin(username, salt, isHashed, password, capability)
|
|
usernameCanonical = ircutils.toLower(username)
|
|
if not isHashed:
|
|
isHashed = 0
|
|
else:
|
|
isHashed = 1
|
|
c = self.conn.cursor()
|
|
c.execute('''update trivialogin set
|
|
username=?,
|
|
salt=?,
|
|
is_hashed=?,
|
|
password=?,
|
|
capability=?
|
|
where username_canonical=?''', (username, salt, isHashed, password, capability, usernameCanonical)
|
|
)
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateUserLog(self, username, channel, score, numAnswered, timeTaken, day=None, month=None, year=None, epoch=None):
|
|
if not self.userExists(username):
|
|
self.insertUser(username)
|
|
if day == None and month == None and year == None:
|
|
dateObject = datetime.date.today()
|
|
day = dateObject.day
|
|
month = dateObject.month
|
|
year = dateObject.year
|
|
if epoch is None:
|
|
epoch = int(time.mktime(time.localtime()))
|
|
if not self.userLogExists(username, channel, day, month, year):
|
|
return self.insertUserLog(username, channel, score, numAnswered, timeTaken, day, month, year, epoch)
|
|
c = self.conn.cursor()
|
|
usernameCanonical = ircutils.toLower(username)
|
|
channelCanonical = ircutils.toLower(channel)
|
|
test = c.execute('''update triviauserlog set
|
|
username=?,
|
|
points_made = points_made+?,
|
|
average_time=( average_time * (1.0*num_answered/(num_answered+?)) + ? * (1.0*?/(num_answered+?)) ),
|
|
average_score=( average_score * (1.0*num_answered/(num_answered+?)) + ? * (1.0*?/(num_answered+?)) ),
|
|
num_answered = num_answered+?,
|
|
last_updated = ?
|
|
where username_canonical=?
|
|
and channel_canonical=?
|
|
and day=?
|
|
and month=?
|
|
and year=?''', (username,score,numAnswered,timeTaken,numAnswered,numAnswered,numAnswered,score,numAnswered,numAnswered,numAnswered,epoch,usernameCanonical,channelCanonical,day,month,year))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateUser(self, username, numEditted=0, numEdittedAccepted=0, numReported=0, numQuestionsAdded=0, numQuestionsAccepted=0):
|
|
if not self.userExists(username):
|
|
return self.insertUser(username, numEditted, numEdittedAccepted, numReported, numQuestionsAdded, numQuestionsAccepted)
|
|
usernameCanonical = ircutils.toLower(username)
|
|
c = self.conn.cursor()
|
|
c.execute('''update triviausers set
|
|
username=?,
|
|
num_editted=num_editted+?,
|
|
num_editted_accepted=num_editted_accepted+?,
|
|
num_reported=num_reported+?,
|
|
num_questions_added=num_questions_added+?,
|
|
num_questions_accepted=num_questions_accepted+?
|
|
where username_canonical=?''',
|
|
(
|
|
username,
|
|
numEditted,
|
|
numEdittedAccepted,
|
|
numReported,
|
|
numQuestionsAdded,
|
|
numQuestionsAccepted,
|
|
usernameCanonical
|
|
)
|
|
)
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateUserHighestStreak(self, username, streak):
|
|
if not self.userExists(username):
|
|
return self.insertUser(username)
|
|
usernameCanonical = ircutils.toLower(username)
|
|
c = self.conn.cursor()
|
|
c.execute('''update triviausers set
|
|
highest_streak=?
|
|
where highest_streak < ?
|
|
and username_canonical=?''', (streak, streak, usernameCanonical)
|
|
)
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateGame(self, channel, numAsked):
|
|
if not self.gameExists(channel):
|
|
return self.insertGame(channel, numAsked)
|
|
c = self.conn.cursor()
|
|
channelCanonical = ircutils.toLower(channel)
|
|
test = c.execute('''update triviagames set
|
|
channel=?,
|
|
num_asked=?
|
|
where channel_canonical=?''', (channel, numAsked, channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateGameLongestStreak(self, channel, lastWinner, streak):
|
|
c = self.conn.cursor()
|
|
channelCanonical = ircutils.toLower(channel)
|
|
lastWinnerCanonical = ircutils.toLower(lastWinner)
|
|
test = c.execute('''update triviagames set
|
|
longest_streak=?,
|
|
longest_streak_holder=?,
|
|
longest_streak_holder_canonical=?
|
|
where channel_canonical=?
|
|
and longest_streak<?''', (streak, lastWinner, lastWinnerCanonical, channelCanonical, streak))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateGameStreak(self, channel, lastWinner, streak):
|
|
if not self.gameExists(channel):
|
|
return self.insertGame(channel, 0, None)
|
|
c = self.conn.cursor()
|
|
channelCanonical = ircutils.toLower(channel)
|
|
lastWinner = ircutils.toLower(lastWinner)
|
|
test = c.execute('''update triviagames set
|
|
last_winner=?,
|
|
streak=?
|
|
where channel_canonical=?''', (lastWinner, streak, channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateGameRoundStarted(self, channel, lastRoundStarted):
|
|
if not self.gameExists(channel):
|
|
return self.insertGame(channel, numAsked)
|
|
channelCanonical = ircutils.toLower(channel)
|
|
c = self.conn.cursor()
|
|
test = c.execute('''update triviagames set
|
|
round_started=?
|
|
where channel_canonical=?''', (lastRoundStarted, channelCanonical))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateQuestion(self, id, newQuestion):
|
|
c = self.conn.cursor()
|
|
test = c.execute('''update triviaquestion set
|
|
question=?
|
|
where id=?''', (newQuestion, id))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def updateQuestionStats(self, id, timesAnswered, timesMissed):
|
|
c = self.conn.cursor()
|
|
test = c.execute('''update triviaquestion set
|
|
num_answered=num_answered+?,
|
|
num_missed=num_missed+?
|
|
where id=?''', (timesAnswered, timesMissed, id))
|
|
self.conn.commit()
|
|
c.close()
|
|
|
|
def viewDayTop10(self, channel, numUpTo=10):
|
|
numUpTo -= 10
|
|
dateObject = datetime.date.today()
|
|
day = dateObject.day
|
|
month = dateObject.month
|
|
year = dateObject.year
|
|
|
|
query = '''select id,
|
|
username,
|
|
sum(points_made) as points,
|
|
sum(num_answered)
|
|
from triviauserlog
|
|
where day=?
|
|
and month=?
|
|
and year=?'''
|
|
arguments = [day, month, year]
|
|
|
|
if channel is not None:
|
|
channelCanonical = ircutils.toLower(channel)
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
order by points desc limit ?, 10''' % (query)
|
|
arguments.append(numUpTo)
|
|
|
|
c = self.conn.cursor()
|
|
c.execute(query, tuple(arguments))
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def viewAllTimeTop10(self, channel, numUpTo=10):
|
|
numUpTo -= 10
|
|
|
|
query = '''select id,
|
|
username,
|
|
sum(points_made) as points,
|
|
sum(num_answered)
|
|
from triviauserlog'''
|
|
arguments = []
|
|
|
|
if channel is not None:
|
|
channelCanonical = ircutils.toLower(channel)
|
|
query = '''%s where channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
order by points desc
|
|
limit ?, 10''' % (query)
|
|
arguments.append(numUpTo)
|
|
|
|
c = self.conn.cursor()
|
|
c.execute(query, tuple(arguments))
|
|
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def viewMonthTop10(self, channel, numUpTo=10, year=None, month=None):
|
|
numUpTo -= 10
|
|
d = datetime.date.today()
|
|
if year is None or month is None:
|
|
year = d.year
|
|
month = d.month
|
|
|
|
query = '''select id,
|
|
username,
|
|
sum(points_made) as points,
|
|
sum(num_answered)
|
|
from triviauserlog
|
|
where month=?
|
|
and year=?'''
|
|
arguments = [month, year]
|
|
|
|
if channel is not None:
|
|
channelCanonical = ircutils.toLower(channel)
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
order by points desc limit ?, 10''' % (query)
|
|
arguments.append(numUpTo)
|
|
|
|
c = self.conn.cursor()
|
|
c.execute(query, tuple(arguments))
|
|
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def viewYearTop10(self, channel, numUpTo=10, year=None):
|
|
numUpTo -= 10
|
|
d = datetime.date.today()
|
|
if year is None:
|
|
year = d.year
|
|
|
|
query = '''select id,
|
|
username,
|
|
sum(points_made) as points,
|
|
sum(num_answered)
|
|
from triviauserlog
|
|
where year=?'''
|
|
arguments = [year]
|
|
|
|
if channel is not None:
|
|
channelCanonical = ircutils.toLower(channel)
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
order by points desc limit ?, 10''' % (query)
|
|
arguments.append(numUpTo)
|
|
|
|
c = self.conn.cursor()
|
|
c.execute(query, tuple(arguments))
|
|
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def viewWeekTop10(self, channel, numUpTo=10):
|
|
numUpTo -= 10
|
|
d = datetime.date.today()
|
|
weekday=d.weekday()
|
|
d -= datetime.timedelta(weekday)
|
|
weekSqlString = ''
|
|
for i in range(7):
|
|
if i > 0:
|
|
weekSqlString += ' or '
|
|
weekSqlString += '''
|
|
(year=%d
|
|
and month=%d
|
|
and day=%d)''' % (d.year, d.month, d.day)
|
|
d += datetime.timedelta(1)
|
|
|
|
query = '''select id,
|
|
username,
|
|
sum(points_made) as points,
|
|
sum(num_answered)
|
|
from triviauserlog
|
|
where (%s)''' % weekSqlString
|
|
arguments = []
|
|
|
|
if channel is not None:
|
|
channelCanonical = ircutils.toLower(channel)
|
|
query = '''%s and channel_canonical=?''' % (query)
|
|
arguments.append(channelCanonical)
|
|
|
|
query = '''%s group by username_canonical
|
|
order by points desc limit ?, 10''' % (query)
|
|
arguments.append(numUpTo)
|
|
|
|
c = self.conn.cursor()
|
|
c.execute(query, tuple(arguments))
|
|
|
|
data = []
|
|
for row in c:
|
|
data.append(row)
|
|
c.close()
|
|
return data
|
|
|
|
def wasUserActiveIn(self, username, channel, timeSeconds):
|
|
usernameCanonical = ircutils.toLower(username)
|
|
channelCanonical = ircutils.toLower(channel)
|
|
epoch = int(time.mktime(time.localtime()))
|
|
dateObject = datetime.date.today()
|
|
day = dateObject.day
|
|
month = dateObject.month
|
|
year = dateObject.year
|
|
c = self.conn.cursor()
|
|
result = c.execute('''select count(*) from triviauserlog
|
|
where day=? and month=? and year=?
|
|
and username_canonical=?
|
|
and channel_canonical=?
|
|
and last_updated>?''', (day, month, year, usernameCanonical, channelCanonical, (epoch-timeSeconds)))
|
|
rows = result.fetchone()[0]
|
|
c.close()
|
|
if rows > 0:
|
|
return True
|
|
return False
|
|
|
|
def getVersion(self):
|
|
c = self.conn.cursor();
|
|
try:
|
|
c.execute('''select version from triviainfo''')
|
|
return c.fetchone()
|
|
except:
|
|
pass
|
|
|
|
def makeInfoTable(self):
|
|
c = self.conn.cursor()
|
|
try:
|
|
c.execute('''create table triviainfo (
|
|
version integer
|
|
)''')
|
|
except:
|
|
pass
|
|
|
|
#A log wrapper, ripoff of ChannelLogger
|
|
class Logger:
|
|
def __init__(self, base):
|
|
self.logs = {}
|
|
self.registryValue = base.registryValue
|
|
|
|
def logNameTimestamp(self, channel):
|
|
return time.strftime('%Y-%m-%d')
|
|
|
|
def getLogName(self, channel):
|
|
return '%s.%s.log' % (channel, self.logNameTimestamp(channel))
|
|
|
|
def getLogDir(self, irc, channel):
|
|
logDir = conf.supybot.directories.log.dirize('TriviaTime')
|
|
logDir = os.path.join(logDir, irc.network)
|
|
logDir = os.path.join(logDir, channel)
|
|
timeDir = time.strftime('%B')
|
|
logDir = os.path.join(logDir, timeDir)
|
|
if not os.path.exists(logDir):
|
|
os.makedirs(logDir)
|
|
return logDir
|
|
|
|
def timestamp(self, log):
|
|
format = conf.supybot.log.timestampFormat()
|
|
if format:
|
|
log.write(time.strftime(format))
|
|
log.write(' ')
|
|
|
|
def checkLogNames(self):
|
|
for (irc, logs) in self.logs.items():
|
|
for (channel, log) in logs.items():
|
|
name = self.getLogName(channel)
|
|
if name != log.name:
|
|
log.close()
|
|
del logs[channel]
|
|
|
|
def getLog(self, irc, channel):
|
|
self.checkLogNames()
|
|
try:
|
|
logs = self.logs[irc]
|
|
except KeyError:
|
|
logs = ircutils.IrcDict()
|
|
self.logs[irc] = logs
|
|
if channel in logs:
|
|
return logs[channel]
|
|
else:
|
|
try:
|
|
name = self.getLogName(channel)
|
|
logDir = self.getLogDir(irc, channel)
|
|
log = file(os.path.join(logDir, name), 'a')
|
|
logs[channel] = log
|
|
return log
|
|
except IOError:
|
|
self.log.exception('Error opening log:')
|
|
return self.FakeLog()
|
|
|
|
def doLog(self, irc, channel, s, *args):
|
|
if not self.registryValue('general.logGames'):
|
|
return
|
|
s = format(s, *args)
|
|
channel = self.normalizeChannel(irc, channel)
|
|
log = self.getLog(irc, channel)
|
|
self.timestamp(log)
|
|
s = ircutils.stripFormatting(s)
|
|
log.write(s)
|
|
log.write('\n')
|
|
log.flush()
|
|
|
|
def normalizeChannel(self, irc, channel):
|
|
return ircutils.toLower(channel)
|
|
|
|
class FakeLog(object):
|
|
def flush(self):
|
|
return
|
|
def close(self):
|
|
return
|
|
def write(self, s):
|
|
return
|
|
|
|
Class = TriviaTime
|
|
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|