diff --git a/Jeopardy/README.md b/Jeopardy/README.md deleted file mode 100644 index 3299125..0000000 --- a/Jeopardy/README.md +++ /dev/null @@ -1,222 +0,0 @@ -A fully-featured and customizable IRC trivia game using an archive of Jeopardy! questions. - -## This... is... Jeopardy! - -Uses [jService](http://jservice.io) - -To configure replies, see the [templates](#templates) section. - -## Commands - -`start [channel] [--num <#>] [--timeout <#>] [--hints <#>] [--no-hints] [--random] [--shuffle] [--restart] [, , etc.]` - -`start` - start a round with random questions - -`start , ` - search for a category by name, separate multiple categories with commas - -`--shuffle` - randomize category searches - -`--num <# of questions>` - set the number of questions for the round - -`--timeout <# of seconds to answer>` - set the time to answer the question, 0 to disable timeout - -`--hint <# of hints>` - specify number of hints, 0 to disable them - -`--no-hints` - set showHints and showBlank to False - -`--random` - select a category at random - -`--restart` - automatically restart a new round when the current round finishes (questions in restarted rounds will be random) - -``` -categories -``` -^ Returns a list of popular Jeopardy! categories. - -``` -stats [--top ] [nick] -``` -^ Returns game scores. Defaults to top 5 players. Use `--top` and a number to receive more top players. Specify a nick to get a score for the selected player. - -``` -question -``` -^ Repeat the currently active question. - -``` -hint -``` -^ Display the next hint. If max hints reached, repeat the latest hint. If max hints is 0, show blanked out answer. - -``` -skip -``` -^ Skip the current question. - -``` -report -``` -^ Report the current question as invalid e.g. audio/video based clues. - - -## Config - -``` -config list plugins.jeopardy -``` -^ List the config variables for the game. - -``` -config help plugins.jeopardy. -``` -^ Get information about what game options the variable controls. - -``` -config [channel #channel] plugins.jeopardy.blankChar * -``` -^ The character used for a blank when displaying hints - -``` -config [channel #channel] plugins.jeopardy.numHints 3 -``` -^ The number of hints to be given for each question - -``` -config [channel #channel] plugins.jeopardy.delay 4 -``` -^ The number of seconds to delay between questions - -``` -config [channel #channel] plugins.jeopardy.timeout 90 -``` -^ The number of seconds to allow for each question - -``` -config [channel #channel] plugins.jeopardy.hintPercentage 0.25 -``` -^ The fraction of the answer that should be revealed with each hint - -``` -config [channel #channel] plugins.jeopardy.hintReduction 0.5 -``` -^ The percentage by which to reduce points with each hint reveal - -``` -config [channel #channel] plugins.jeopardy.flexibility 0.94 -``` -^ Minimum similarity score of the answer checker. Jaro-Winkler distance, 1.0 is identical/disabled. Sane values > 0.9 - -``` -config [channel #channel] plugins.jeopardy.inactiveShutoff 5 -``` -^ The number of questions that can go unanswered before the game stops automatically - -``` -config plugins.jeopardy.jserviceUrl http://jservice.io -``` -^ Alternate URL where jservice can be accessed at, for example a locally run jservice instance: http://127.0.0.1:3000 - -``` -config [channel #channel] plugins.jeopardy.defaultRoundLength 10 -``` -^ The default number of questions to be asked in a round - -``` -config [channel #channel] plugins.jeopardy.randomize True -``` -^ This will determine whether or not the bot will randomize the question order - -``` -config [channel #channel] plugins.jeopardy.requireOps False -``` -^ This will determine whether or not the user must be a channel operator to start/stop the game - -``` -config [channel #channel] plugins.jeopardy.enabled True -``` -^ This will determine whether or not the game is enabled for a given channel - -``` -config [channel #channel] plugins.jeopardy.defaultPointValue 500 -``` -^ The default point value for questions if no point value is given - -``` -config [channel #channel] plugins.jeopardy.autoRestart False -``` -^ Start a new round of random questions after the current round has ended - -``` -config [channel #channel] plugins.jeopardy.keepHistory True -``` -^ Keep a history of previously asked questions and don't repeat them - -``` -config [channel #channel] plugins.jeopardy.useBold True -``` -^ Use bold in replies - -``` -config [channel #channel] plugins.jeopardy.showScores True -``` -^ Show scores at the end of the round - -``` -config [channel #channel] plugins.jeopardy.showBlank True -``` -^ Show first (blank) hint with the question. Overrides showHints only for this reply - -``` -config [channel #channel] plugins.jeopardy.showTime True -``` -^ Show time remaining messages when showHints is False - -``` -config [channel #channel] plugins.jeopardy.showHints True -``` -^ Show hint messages automatically. Overrides showTime - -``` -config [channel #channel] plugins.jeopardy.timeReplies 1 -``` -^ Number of time remaining replies to show when showHints False and showTime True - - -## Templates - -``` -config list plugins.jeopardy.template -``` -^ List the config variables for the templates. - -``` -config help plugins.jeopardy.template. -``` -^ Get information about what template the variable controls. - - -`config [channel #channel] plugins.jeopardy.template.correct "\x0313{{nick}}\x03 got it! The full answer was: {{answer}}. Points: \x0309{{points}}\x03 | Round Score: \x0309{{round}}\x03 | Total: \x0309{{total}}"`
-^ Template for correct answer replies - - -`config [channel #channel] plugins.jeopardy.template.hint "HINT: {{hint}}{% if time %} | ({{time}} seconds remaining){% endif %}{% if points %} | \x0309[${{points}}]{% endif %}"`
-^ Template for hint reply - -`config [channel #channel] plugins.jeopardy.template.question "#{{number}} of {{total}}: \x0313({{airdate}}) \x0309[${{points}}] \x0310\x1f{{category}}\x1f: {{clue}}"`
-^ Template for question reply - -`config [channel #channel] plugins.jeopardy.template.skip "No one got the answer! It was: {{answer}}"`
-^ Template for reply when question unanswered after timeout or the 'skip' command used - -`config [channel #channel] plugins.jeopardy.template.stop "Jeopardy! stopped.{% if answer %} (Answer: {{answer}}){% endif %}"`
-^ Template for reply when using the 'stop' command - -`config [channel #channel] plugins.jeopardy.template.time "{{time}} seconds remaining. [.hint] [.question] [.skip]"`
-^ Template for time remaining reply when showTime = True and showHints = False - - -## Miscellaneous - -Score and history files can be found in /data/jeopardy/ - -Forked and significantly modified version of [this trivia plugin](https://github.com/ProgVal/Supybot-plugins/tree/master/Trivia). diff --git a/Jeopardy/__init__.py b/Jeopardy/__init__.py deleted file mode 100644 index 6176f5c..0000000 --- a/Jeopardy/__init__.py +++ /dev/null @@ -1,81 +0,0 @@ -### -# Copyright (c) 2010, quantumlemur -# Copyright (c) 2011, Valentin Lorentz -# Copyright (c) 2020, oddluck -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -""" -Jeopardy: a trivia game using Jeopardy! questions -""" - -import supybot -import supybot.world as world - -# Use this for the version of this plugin. You may wish to put a CVS keyword -# in here if you're keeping the plugin in CVS or some similar system. -__version__ = "2020.02.24+git" - -# XXX Replace this with an appropriate author or supybot.Author instance. -__author__ = supybot.Author("oddluck", "oddluck", "oddluck@riseup.net") - -# This is a dictionary mapping supybot.Author instances to lists of -# contributions. -if not hasattr(supybot.authors, "progval") or not hasattr( - supybot.authors, "quantumlemur" -): - supybot.authors.progval = supybot.Author( - "Valentin Lorentz", "ProgVal", "progval@gmail.com" - ) - supybot.authors.quantumlemur = supybot.Author( - "quantumlemur", "quantumlemur", "quantumlemur@users.sourceforge.net" - ) -__contributors__ = { - supybot.authors.quantumlemur: ["original plugin base"], - supybot.authors.progval: ["code enhancement"], -} - -# This is a url where the most recent plugin package can be downloaded. -__url__ = "https://github.com/oddluck/limnoria-plugins/" - -from . import config -from . import plugin -from importlib import reload - -reload(plugin) # In case we're being reloaded. -reload(config) -# Add more reloads here if you add third-party modules and want them to be -# reloaded when this plugin is reloaded. Don't forget to import them as well! - -if world.testing: - from . import test - -Class = plugin.Class -configure = config.configure - - -# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/Jeopardy/categories.txt b/Jeopardy/categories.txt deleted file mode 100644 index f85c121..0000000 --- a/Jeopardy/categories.txt +++ /dev/null @@ -1,114 +0,0 @@ -Sports -Potpourri -Animals -American History -Transportation -Science -Business & Industry -Stupid Answers -Television -History -Rhyme Time -U.S. Cities -State Capitals -Food -People -Hodgepodge -The Bible -Food & Drink -Word Origins -Common Bonds -3 Letter Words -U.S. Geography -Weights & Measures -Annual Events -Pop Music -Potent Potables -Holidays & Observances -4 Letter Words -U.S. History -Americana -World History -Museums -Bodies Of Water -Colleges & Universities -Musical Instruments -First Ladies -The Movies -Literature -Religion -Zoology -Mythology -Travel & Tourism -Fruits & Vegetables -Nursery Rhymes -Fashion -Familiar Phrases -Around The World -Nonfiction -U.S. States -Nature -Odds & Ends -Geography -World Capitals -U.S.A. -Languages -Homophones -Organizations -5 Letter Words -Vocabulary -Leftovers -Science & Nature -Proverbs -Anagrams -Toys & Games -Mammals -Magazines -Colors -World Geography -Guinness Records -Books & Authors -The Human Body -Double Talk -Quotations -The Civil War -Cooking -Birds -Medicine -Abbrev. -Letter Perfect -Tv Trivia -Insects -Shakespeare -Foreign Words & Phrases -19th Century America -The Body Human -Mountains -Technology -Awards -Singers -Number, Please -Islands -Games -Furniture -Body Language -Children's Literature -Music -Baseball -Quotes -Actors & Actresses -Before & After -The Old Testament -Etiquette -Fictional Characters -Authors -Art -Biology -Health & Medicine -Brand Names -Word Puzzles -6 Letter Words -In Other Words... -Celebrities -Facts & Figures -Weapons diff --git a/Jeopardy/config.py b/Jeopardy/config.py deleted file mode 100644 index 1e027c1..0000000 --- a/Jeopardy/config.py +++ /dev/null @@ -1,370 +0,0 @@ -### -# Copyright (c) 2010, quantumlemur -# Copyright (c) 2011, Valentin Lorentz -# Copyright (c) 2020, oddluck -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -import supybot.conf as conf -import supybot.registry as registry -from supybot.i18n import PluginInternationalization, internationalizeDocstring - -_ = PluginInternationalization("Jeopardy") - - -def configure(advanced): - # This will be called by supybot to configure this module. advanced is - # a bool that specifies whether the user identified himself as an advanced - # user or not. You should effect your configuration by manipulating the - # registry as appropriate. - from supybot.questions import expect, anything, something, yn - - conf.registerPlugin("Jeopardy", True) - - -Jeopardy = conf.registerPlugin("Jeopardy") -# This is where your configuration variables (if any) should go. For example: -# conf.registerGlobalValue(Jeopardy, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) - -conf.registerChannelValue( - Jeopardy, - "blankChar", - registry.String( - "*", _("""The character used for a blank when displaying hints"""), - ), -) - -conf.registerChannelValue( - Jeopardy, - "numHints", - registry.PositiveInteger( - 3, _("""The number of hints to be given for each question"""), - ), -) - -conf.registerChannelValue( - Jeopardy, - "timeReplies", - registry.PositiveInteger( - 1, - _( - """ - The number of time remaining replies if showHints False and showTime True - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "delay", - registry.Integer( - 4, _("""The number of seconds to increase the delay between questions"""), - ), -) - -conf.registerChannelValue( - Jeopardy, - "timeout", - registry.Integer(90, _("""The number of seconds to allow for each question"""),), -) - -conf.registerChannelValue( - Jeopardy, - "hintPercentage", - registry.Probability( - 0.25, - _("""The fraction of the answer that should be revealed with each hint"""), - ), -) - -conf.registerChannelValue( - Jeopardy, - "hintReduction", - registry.Probability( - 0.5, _("""The percentage by which to reduce points with each hint reveal"""), - ), -) - -conf.registerChannelValue( - Jeopardy, - "flexibility", - registry.Float( - 0.94, - _( - """ - The minimum similarity score of the answer checker. Uses jaro-winkler - distance, 1.0 is identical/disabled. (Sane values > 0.90) - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "inactiveShutoff", - registry.Integer( - 5, - _( - """ - The number of questions that can go - unanswered before the game stops automatically. - """ - ), - ), -) - -conf.registerGlobalValue( - Jeopardy, - "jserviceUrl", - registry.String( - "http://jservice.io", - _( - """ - Set an alternate URL where - jservice can be accessed at, for example a locally run jservice instance. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "defaultRoundLength", - registry.PositiveInteger( - 10, _("""The default number of questions to be asked in a round."""), - ), -) - -conf.registerChannelValue( - Jeopardy, - "randomize", - registry.Boolean( - True, - _( - """ - This will determine whether or not the - bot will randomize the questions. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "requireOps", - registry.Boolean( - False, - _( - """ - This will determine whether or not the - user must be a channel operator to start/stop the game. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "enabled", - registry.Boolean( - True, - _( - """ - This will determine whether or not the - game is enabled for a given channel - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "defaultPointValue", - registry.PositiveInteger( - 500, - _( - """ - The default point value for questions if - no point value is given. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "autoRestart", - registry.Boolean( - False, - _( - """ - Start a new round of random questions after - the current round has ended. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "keepHistory", - registry.Boolean( - True, - _( - """ - Keep a history of previously asked questions per - channel and don't repeat them. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, "useBold", registry.Boolean(True, _("""Use bold in replies.""")) -) - -conf.registerChannelValue( - Jeopardy, - "showScores", - registry.Boolean(True, _("""Show scores at the end of the round.""")), -) - -conf.registerChannelValue( - Jeopardy, - "showBlank", - registry.Boolean( - True, - _( - """ - Show first (blank) hint with the question. Overrides showHints - only for this reply. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy, - "showTime", - registry.Boolean( - True, _("""Show time remaining messages when not showing hints.""") - ), -) - -conf.registerChannelValue( - Jeopardy, - "showHints", - registry.Boolean( - True, _("""Show hint messages automatically. Overrides showTime.""") - ), -) - -conf.registerGroup(Jeopardy, "template") - -conf.registerChannelValue( - Jeopardy.template, - "question", - registry.String( - "#{{number}} of {{total}}: \x0313({{airdate}}) \x0309[${{points}}] " - "\x0310\x1f{{category}}\x1f: {{clue}}", - _("""The template used to render questions."""), - ), -) - -conf.registerGroup(Jeopardy.template, "restart") - -conf.registerChannelValue( - Jeopardy.template.restart, - "question", - registry.String( - "\x0313({{airdate}}) \x0309[${{points}}] \x0310\x1f{{category}}\x1f: {{clue}}", - _("""The template used to render questions when autoRestart is True."""), - ), -) - -conf.registerChannelValue( - Jeopardy.template.restart, - "correct", - registry.String( - "\x0313{{nick}}\x03 got it! The full answer was: {{answer}}. Points: " - "\x0309{{points}}\x03 | Total: \x0309{{total}}", - _( - """ - The template used to render correct answer replies when autoRestart is True. - """ - ), - ), -) - -conf.registerChannelValue( - Jeopardy.template, - "hint", - registry.String( - "HINT: {{hint}}{% if time %} | ({{time}} seconds remaining){% endif %}" - "{% if points %} | \x0309[${{points}}]{% endif %}", - _("""The template used to render hints."""), - ), -) - -conf.registerChannelValue( - Jeopardy.template, - "correct", - registry.String( - "\x0313{{nick}}\x03 got it! The full answer was: {{answer}}. Points: \x0309" - "{{points}}\x03 | Round Score: \x0309{{round}}\x03 | Total: \x0309{{total}}", - _("""The template used to render correct answer replies."""), - ), -) - -conf.registerChannelValue( - Jeopardy.template, - "skip", - registry.String( - "No one got the answer! It was: {{answer}}", - _("""The template used to render unanswered question and skip replies."""), - ), -) - -conf.registerChannelValue( - Jeopardy.template, - "stop", - registry.String( - "Jeopardy! stopped.{% if answer %} (Answer: {{answer}}){% endif %}", - _("""The template used to render stop replies."""), - ), -) - -conf.registerChannelValue( - Jeopardy.template, - "time", - registry.String( - "{{time}} seconds remaining. [.hint] [.question] [.skip]", - _("""The template used to render time remaining replies."""), - ), -) diff --git a/Jeopardy/plugin.py b/Jeopardy/plugin.py deleted file mode 100644 index 4abe787..0000000 --- a/Jeopardy/plugin.py +++ /dev/null @@ -1,1132 +0,0 @@ -### -# Copyright (c) 2010, quantumlemur -# Copyright (c) 2011, Valentin Lorentz -# Copyright (c) 2020, oddluck -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - - -from bs4 import BeautifulSoup -from ftfy import fix_text -from jinja2 import Template -from supybot.commands import * -import math -import os -import random -import re -import requests -from unidecode import unidecode -import json -import string -import supybot.callbacks as callbacks -import supybot.conf as conf -import supybot.ircdb as ircdb -import supybot.ircmsgs as ircmsgs -import supybot.ircutils as ircutils -import supybot.log as log -import supybot.plugins as plugins -import supybot.schedule as schedule -import supybot.utils as utils -import textdistance -import time - - -class Jeopardy(callbacks.Plugin): - """Jeopardy! an IRC Trivia Game""" - - threaded = True - - def __init__(self, irc): - self.__parent = super(Jeopardy, self) - self.__parent.__init__(irc) - self.games = requests.structures.CaseInsensitiveDict() - self.history = requests.structures.CaseInsensitiveDict() - self.jserviceUrl = self.registryValue("jserviceUrl").strip("/") - - def doPrivmsg(self, irc, msg): - channel = msg.channel - if not irc.isChannel(channel): - return - if self.registryValue("enabled", channel) and channel in self.games: - self.games[channel].answer(msg) - - class Game: - def __init__( - self, - irc, - channel, - num, - hints, - timeout, - shuffle, - categories, - restart, - showHints, - showBlank, - showTime, - plugin, - ): - self.registryValue = plugin.registryValue - defaultPoints = self.registryValue("defaultPointValue") - self.active = True - self.answered = 0 - self.blankChar = self.registryValue("blankChar", channel) - self.categories = categories - self.channel = channel - self.correct = True - if restart: - self.correct_template = Template( - self.registryValue("template.restart.correct", channel) - ) - else: - self.correct_template = Template( - self.registryValue("template.correct", channel) - ) - self.currentHint = "" - self.delay = self.registryValue("delay", channel) - self.directory = conf.supybot.directories.data.dirize("jeopardy/") - self.flexibility = self.registryValue("flexibility", channel) - self.games = plugin.games - self.hint_template = Template(self.registryValue("template.hint", channel)) - self.history = plugin.history - self.historyFile = "{0}/history_{1}.txt".format(self.directory, channel) - self.irc = irc - self.jserviceUrl = plugin.jserviceUrl - self.num = num - self.numAsked = 0 - self.numClues = num - self.numHints = hints - self.question = "" - if restart: - self.question_template = Template( - self.registryValue("template.restart.question", channel) - ) - else: - self.question_template = Template( - self.registryValue("template.question", channel) - ) - self.questions = [] - self.points = 0 - self.reduction = self.registryValue("hintReduction", self.channel) - self.restart = restart - self.roundscores = requests.structures.CaseInsensitiveDict() - self.scoreFile = "{0}/scores_{1}.txt".format(self.directory, channel) - self.scores = requests.structures.CaseInsensitiveDict() - self.showBlank = showBlank - self.showHints = showHints - self.showTime = showTime - self.shuffled = shuffle - self.skip_template = Template(self.registryValue("template.skip", channel)) - self.stop_template = Template(self.registryValue("template.stop", channel)) - self.time_template = Template(self.registryValue("template.time", channel)) - self.timeout = timeout - self.timeReplies = self.registryValue("timeReplies", self.channel) - self.total = num - self.unanswered = 0 - if timeout > 0 and showHints: - self.waitTime = timeout / (hints + 1) - elif timeout > 0 and showTime: - self.waitTime = timeout / (self.timeReplies + 1) - if not os.path.exists(self.directory): - os.makedirs(self.directory) - if self.registryValue("keepHistory", channel): - if not os.path.exists(self.historyFile): - f = open(self.historyFile, "w") - f.close() - if not self.history.get(channel): - self.history[channel] = [] - f = open(self.historyFile, "r") - lines = f.readlines() - for line in lines: - self.history[channel].append(int(line)) - f.close() - if not os.path.exists(self.scoreFile): - f = open(self.scoreFile, "w") - f.close() - if not self.scores: - f = open(self.scoreFile, "r") - lines = f.readlines() - for line in lines: - (name, score) = line.split(" ") - self.scores[name] = int(score) - f.close() - cluecount = self.num - asked = [] - if self.categories == "random": - n = 0 - while n <= self.num: - if n == self.num: - break - try: - if self.jserviceUrl == "http://jservice.io": - data = requests.get( - "{0}/api/random".format(self.jserviceUrl), timeout=5 - ) - data = json.loads(data.content.decode()) - else: - data = requests.get( - "{0}/api/random?count={1}".format( - self.jserviceUrl, self.num + 5 - ), - timeout=5, - ) - data = json.loads(data.content.decode()) - if not data: - n += 1 - break - for item in data: - if n == self.num: - break - id = item["id"] - clue = item["question"].strip() - airdate = item["airdate"].split("T")[0] - answer = item["answer"].strip() - category = string.capwords(item["category"]["title"]) - invalid = item["invalid_count"] - points = defaultPoints - if item.get("value"): - try: - points = int(item["value"]) - except: - pass - if self.registryValue("keepHistory", channel): - if ( - clue - and airdate - and answer - and category - and not invalid - and id not in asked - and id not in self.history[channel] - and answer != "=" - ): - q = "{0}|{1}|{2}|{3}|{4}|{5}".format( - id, airdate, points, category, clue, answer - ) - q = self.normalize(q) - self.questions.append(q) - asked.append(id) - n += 1 - else: - if ( - clue - and airdate - and answer - and category - and not invalid - and id not in asked - and answer != "=" - ): - q = "{0}|{1}|{2}|{3}|{4}|{5}".format( - id, airdate, points, category, clue, answer - ) - q = self.normalize(q) - self.questions.append(q) - asked.append(id) - n += 1 - except Exception as e: - log.error("Jeopardy: Error: {0}".format(e)) - break - else: - n = 0 - k = 0 - while n <= self.num: - if n == self.num or k > len(self.categories): - break - for category in self.categories: - if n == self.num or k > len(self.categories): - break - try: - category = int(category) - data = requests.get( - "{0}/api/clues?category={1}".format( - self.jserviceUrl, category - ) - ) - data = json.loads(data.content.decode()) - if not data: - k += 1 - break - cluecount = data[0]["category"]["clues_count"] - if cluecount < self.num and len(self.categories) == 1: - self.num = cluecount - if cluecount > 100: - data.extend( - json.loads( - requests.get( - "{0}/api/clues?&category={1}&offset=100" - .format(self.jserviceUrl, category), - timeout=5, - ).content.decode() - ) - ) - if cluecount > 200: - data.extend( - json.loads( - requests.get( - "{0}/api/clues?&category={1}&offset=200" - .format(self.jserviceUrl, category), - timeout=5, - ).content.decode() - ) - ) - if cluecount > 300: - data.extend( - json.loads( - requests.get( - "{0}/api/clues?&category={1}&offset=300" - .format(self.jserviceUrl, category), - timeout=5, - ).content.decode() - ) - ) - if cluecount > 400: - data.extend( - json.loads( - requests.get( - "{0}/api/clues?&category={1}&offset=400" - .format(self.jserviceUrl, category), - timeout=5, - ).content.decode() - ) - ) - if cluecount > 500: - data.extend( - json.loads( - requests.get( - "{0}/api/clues?&category={1}&offset=500" - .format(self.jserviceUrl, category), - timeout=5, - ).content.decode() - ) - ) - j = 0 - for item in data: - if n == self.num or k > len(self.categories): - break - elif shuffle and k == len(self.categories): - shuffle = False - k = 0 - pass - elif shuffle and j >= self.num * 0.2: - break - id = item["id"] - clue = item["question"].strip() - airdate = item["airdate"].split("T")[0] - answer = item["answer"].strip() - category = string.capwords(item["category"]["title"]) - invalid = item["invalid_count"] - points = defaultPoints - if item.get("value"): - try: - points = int(item["value"]) - except: - pass - if self.registryValue("keepHistory", channel): - if ( - clue - and airdate - and answer - and category - and not invalid - and id not in asked - and id not in self.history[channel] - and answer != "=" - ): - q = "{0}|{1}|{2}|{3}|{4}|{5}".format( - id, airdate, points, category, clue, answer - ) - q = self.normalize(q) - self.questions.append(q) - asked.append(id) - n += 1 - j += 1 - else: - if ( - clue - and airdate - and answer - and category - and not invalid - and id not in asked - and answer != "=" - ): - q = "{0}|{1}|{2}|{3}|{4}|{5}".format( - id, airdate, points, category, clue, answer - ) - q = self.normalize(q) - self.questions.append(q) - asked.append(id) - n += 1 - j += 1 - k += 1 - except Exception as e: - log.error("Jeopardy: Error: {0}".format(e)) - k += 1 - break - if self.shuffled or self.registryValue("randomize", channel): - random.shuffle(self.questions) - else: - self.questions = self.questions[::-1] - self.total = len(self.questions) - self.num = len(self.questions) - if self.num == 0: - self.reply("Sorry, no questions available.") - self.stop() - return - self.newquestion() - - def normalize(self, q): - q = BeautifulSoup(q) - q = fix_text(q.text).replace(r"\'", "'").replace(r"\"", '"') - q = re.sub("([.!?])([A-Z(])(?![.'])", r"\g<1> \g<2>", q) - q = re.sub("([,;:)])([a-zA-Z(])", r"\g<1> \g<2>", q) - q = " ".join(q.split()) - return q - - def clean(self, text): - text = unidecode(text) - if len(text) > 2: - text = re.sub("[^a-zA-Z0-9 ]+", "", text) - text = re.sub("^a |^an |^the |^or ", "", text).replace(" ", "") - else: - text = re.sub("[^a-zA-Z0-9]+", "", text) - return text - - def newquestion(self): - if not self.active: - return - self.clear() - inactiveShutoff = self.registryValue("inactiveShutoff", self.channel) - if ( - self.num == 0 - or self.answered == self.total - or self.numAsked == self.total - ): - self.stop() - return - elif self.unanswered > inactiveShutoff and inactiveShutoff > 0: - self.reply("Seems like no one's playing any more. Jeopardy! stopped.") - self.correct = True - self.active = False - self.stop() - return - elif len(self.questions) == 0: - self.reply("Oops! I ran out of questions!") - self.stop() - return - self.show = {} - self.revealed = {} - self.id = None - self.hints = 0 - self.shown = 0 - self.num -= 1 - self.numAsked += 1 - q = self.questions.pop(len(self.questions) - 1).split("|") - question = {} - self.id = q[0] - question["airdate"] = q[1] - self.p = int(q[2]) - self.points = self.p - question["category"] = q[3] - question["clue"] = q[4] - self.a = q[5] - question["points"], question["number"], question["total"] = ( - self.p, - self.numAsked, - self.total, - ) - self.question = self.question_template.render(question) - self.a = re.sub(r"\(\d of\)", "", self.a) - self.divider = round(len(re.sub("[^a-zA-Z0-9]+", "", self.a))) - self.a = [self.a] - self.blank = re.sub(r"\w", self.blankChar, self.a[0]) - self.currentHint = self.blank - if "(" in self.a[0]: - a1, a2, a3 = re.match(r"(.*)\((.*)\)(.*)", self.a[0]).groups() - self.a.append(a1 + a3) - self.a.append(a2) - if self.numAsked > 1 and self.delay > 0: - delayTime = time.time() + self.delay - - def event(): - self.next_question() - - schedule.addEvent(event, delayTime, "clue_%s" % self.channel) - else: - self.next_question() - - def next_question(self): - self.clear() - self.correct = False - if self.registryValue("keepHistory", self.channel): - self.history[self.channel].append(int(self.id)) - self.reply(self.question) - if self.timeout > 0: - - def endEvent(): - self.end() - - self.endTime = time.time() + self.timeout - schedule.addEvent(endEvent, self.endTime, "end_%s" % self.channel) - if self.showBlank: - self.hint() - elif self.showHints or self.showTime: - - def timedEvent(): - self.timedEvent() - - eventTime = time.time() + self.waitTime - if eventTime < self.endTime: - schedule.addEvent( - timedEvent, eventTime, "event_%s" % self.channel - ) - elif self.showBlank: - self.hint() - - def clear(self): - try: - schedule.removeEvent("event_%s" % self.channel) - except: - pass - try: - schedule.removeEvent("clue_%s" % self.channel) - except: - pass - try: - schedule.removeEvent("end_%s" % self.channel) - except: - pass - - def stop(self): - self.write() - self.clear() - if self.registryValue("showScores", self.channel): - scores = iter(self.roundscores.items()) - sorted = [] - for i in range(0, len(self.roundscores)): - item = next(scores) - sorted.append(item) - sorted.sort(key=lambda item: item[1], reverse=True) - max = 3 - if len(sorted) < max: - max = len(sorted) - s = "Top finishers:" - if max > 0: - for i in range(0, max): - item = sorted[i] - s = "%s (%s: %s)" % (s, item[0], item[1]) - self.reply("{0}".format(s)) - if self.restart and self.active: - - def event(): - self.__init__( - self.irc, - self.channel, - self.numClues, - self.numHints, - self.timeout, - False, - "random", - self.restart, - self.showHints, - self.showBlank, - self.showTime, - self, - ) - - delayTime = time.time() + self.delay - schedule.addEvent(event, delayTime, "clue_%s" % self.channel) - else: - self.correct = True - self.active = False - - def timedEvent(self): - if not self.active or self.timeout == 0 or self.correct: - return - if self.showHints: - self.hint() - elif self.showTime: - reply = self.time_template.render( - time=round(self.endTime - time.time()) - ) - self.reply(reply) - if self.timeout > 0: - - def event(): - self.timedEvent() - - eventTime = time.time() + self.waitTime - if eventTime < self.endTime: - schedule.addEvent(event, eventTime, "event_%s" % self.channel) - - def end(self): - if not self.active or self.correct: - return - self.correct = True - reply = self.skip_template.render(answer=self.a[0]) - self.reply(reply) - self.unanswered += 1 - self.answered += 1 - self.clear() - self.newquestion() - - def hint(self): - if not self.active or self.correct: - return - try: - schedule.removeEvent("event_%s" % self.channel) - except: - pass - self.lastHint = self.currentHint - if self.hints <= self.numHints and self.hints > 0 and self.numHints > 0: - ans = self.a[0] - self.show.setdefault(self.id, None) - self.revealed.setdefault(self.id, None) - hintPercentage = self.registryValue("hintPercentage", self.channel) - divider = round(self.divider * hintPercentage) - self.divider -= divider - if not self.show[self.id]: - self.show[self.id] = list(self.blank) - if not self.revealed[self.id]: - self.revealed[self.id] = list(range(len(self.show[self.id]))) - i = 0 - while i < divider and len(self.revealed[self.id]) > 1: - try: - rand = self.revealed[self.id].pop( - random.randint(0, len(self.revealed[self.id])) - 1 - ) - if self.show[self.id][rand] == self.blankChar: - self.show[self.id][rand] = list(ans)[rand] - i += 1 - except: - break - self.currentHint = "".join(self.show[self.id]) - if self.hints > 0 and self.lastHint != self.currentHint: - self.p -= int(round(self.p * self.reduction, -1)) - if self.points > self.p: - points = self.p - else: - points = None - if self.timeout > 0: - timeLeft = str(round(self.endTime - time.time())).zfill( - len(str(self.timeout)) - ) - reply = self.hint_template.render( - hint=self.currentHint, - time=timeLeft, - points=points, - hintNum=self.hints, - numHints=self.numHints, - ) - if self.showHints or self.showTime: - - def event(): - self.timedEvent() - - eventTime = time.time() + self.waitTime - if eventTime < self.endTime: - schedule.addEvent(event, eventTime, "event_%s" % self.channel) - else: - reply = self.hint_template.render( - hint=self.currentHint, - time=None, - points=points, - hintNum=self.hints, - numHints=self.numHints, - ) - self.reply(reply) - self.hints += 1 - - def answer(self, msg): - if not self.active or self.correct: - return - if not self.correct: - for ans in self.a: - ans = " ".join(ans.split()).strip().lower() - guess = " ".join(msg.args[1].split()).strip().lower() - if guess == ans: - self.correct = True - break - elif not self.correct: - answer = self.clean(ans) - guess = self.clean(guess) - if not self.correct and guess == answer: - self.correct = True - break - elif ( - not self.correct - and self.flexibility < 1 - and self.flexibility > 0.5 - ): - dist = textdistance.jaro_winkler(guess, answer) - log.debug( - "Jeopardy: guess: {0}, answer: {1}, length: {2}, " - "distance: {3}, flexibility: {4}".format( - guess, answer, len(answer), dist, self.flexibility - ) - ) - if dist >= self.flexibility: - self.correct = True - break - elif ( - dist < self.flexibility - and "," in self.a[0] - or "&" in self.a[0] - ): - dist = textdistance.jaccard(guess, answer) - if dist >= self.flexibility: - self.correct = True - break - if self.correct: - if not msg.nick in self.scores: - self.scores[msg.nick] = 0 - self.scores[msg.nick] += self.p - if not msg.nick in self.roundscores: - self.roundscores[msg.nick] = 0 - self.roundscores[msg.nick] += self.p - self.unanswered = 0 - reply = self.correct_template.render( - nick=msg.nick, - answer=self.a[0], - points=self.p, - round=self.roundscores[msg.nick], - total=self.scores[msg.nick], - ) - self.reply(reply) - self.correct = True - self.answered += 1 - self.clear() - self.newquestion() - - def reply(self, s): - if self.registryValue("useBold", self.channel): - self.irc.queueMsg(ircmsgs.privmsg(self.channel, ircutils.bold(s))) - else: - self.irc.queueMsg(ircmsgs.privmsg(self.channel, s)) - - def write(self): - f = open(self.scoreFile, "w") - for score in self.scores.items(): - f.write("%s %s\n" % (score[0], score[1])) - f.close() - if self.registryValue("keepHistory", self.channel): - f = open(self.historyFile, "w") - for id in self.history[self.channel]: - f.write("%s\n" % (id)) - f.close() - - def start(self, irc, msg, args, channel, optlist, categories): - """[--num <#>] [--no-hints] [--shuffle] [, , etc.] - Play Jeopardy! with random questions or search/select categories by name. - --num for number of questions. - --no-hints to disable automatic hint replies (including blanks). - --shuffle to randomize questions from search results. - """ - if not channel: - channel = msg.channel - if ( - self.registryValue("requireOps", channel) - and msg.nick not in irc.state.channels[channel].ops - and not ircdb.checkCapability(msg.prefix, "admin") - ): - return - if not self.registryValue("enabled", channel): - return - if channel in self.games: - if self.games[channel].active: - if self.registryValue("useBold", channel): - irc.reply( - ircutils.bold( - "There is already a Jeopardy! game running in {0}.".format( - channel - ) - ) - ) - else: - irc.reply( - "There is already a Jeopardy! game running in {0}.".format( - channel - ) - ) - return - showBlank = self.registryValue("showBlank", channel) - showHints = self.registryValue("showHints", channel) - showTime = self.registryValue("showTime", channel) - optlist = dict(optlist) - if "num" in optlist: - num = optlist.get("num") - else: - num = self.registryValue("defaultRoundLength", channel) - if "shuffle" in optlist: - shuffle = True - else: - shuffle = False - if "no-hints" in optlist: - showHints = False - showBlank = False - if "hints" in optlist: - hints = optlist.get("hints") - else: - hints = self.registryValue("numHints", channel) - if "timeout" in optlist: - timeout = optlist.get("timeout") - else: - timeout = self.registryValue("timeout", channel) - if timeout == 0 or hints == 0: - showHints = False - showTime = False - if "restart" in optlist: - restart = True - else: - restart = self.registryValue("autoRestart", channel) - if "random-category" in optlist: - if self.jserviceUrl == "http://jservice.io": - seed = random.randint(0, 184) * 100 - else: - seed = random.randint(0, 250) * 100 - data = requests.get( - "{0}/api/categories?count=100&offset={1}".format( - self.jserviceUrl, int(seed) - ), - timeout=5, - ) - data = json.loads(data.content.decode()) - random.shuffle(data) - results = [] - for item in data: - if item["clues_count"] > 9: - results.append(item["id"]) - if not results: - results = "random" - elif categories: - results = [] - categories = categories.strip().split(",") - for category in categories: - category = category.strip() - if category.isdigit(): - results.append(category) - else: - url = "{0}/search?query={1}".format(self.jserviceUrl, category) - data = requests.get(url, timeout=5) - soup = BeautifulSoup(data.content) - searches = soup.find_all("a") - for i in range(len(searches)): - search = searches[i].get("href").split("/")[-1] - if search.isdigit(): - results.append(search) - if not results: - if self.registryValue("useBold", channel): - irc.reply( - ircutils.bold( - "Error. Could not find any results for {0}".format( - categories - ) - ), - prefixNick=False, - ) - else: - irc.reply( - "Error. Could not find any results for {0}".format(categories), - prefixNick=False, - ) - return - elif results and "shuffle" in optlist: - random.shuffle(results) - else: - results = "random" - if self.registryValue("useBold", channel): - irc.reply(ircutils.bold("This... is... Jeopardy!"), prefixNick=False) - else: - irc.reply("This... is... Jeopardy!", prefixNick=False) - self.games[channel] = self.Game( - irc, - channel, - num, - hints, - timeout, - shuffle, - results, - restart, - showHints, - showBlank, - showTime, - self, - ) - irc.noReply() - - start = wrap( - start, - [ - "channel", - getopts( - { - "num": "int", - "hints": "int", - "timeout": "int", - "no-hints": "", - "shuffle": "", - "random": "", - "restart": "", - } - ), - additional("text"), - ], - ) - - def stop(self, irc, msg, args, channel): - """[] - Stops a running game of Jeopardy!. is only necessary if the - message isn't sent in the channel itself.""" - if not channel: - channel = msg.channel - if ( - self.registryValue("requireOps", channel) - and msg.nick not in irc.state.channels[channel].ops - and not ircdb.checkCapability(msg.prefix, "admin") - ): - return - if self.games.get(channel): - if self.games[channel].active: - if self.games[channel].correct: - reply = self.games[channel].stop_template.render() - self.games[channel].reply(reply) - else: - reply = self.games[channel].stop_template.render( - answer=self.games[channel].a[0] - ) - self.games[channel].reply(reply) - try: - self.games[channel].active = False - self.games[channel].correct = True - self.games[channel].stop() - except: - return - else: - return - - stop = wrap(stop, ["channel"]) - - def categories(self, irc, msg, args): - """ - Returns list of popular jeopardy! categories and their category ID # - """ - data = open( - "{0}/categories.txt".format(os.path.dirname(os.path.abspath(__file__))) - ) - text = data.read() - reply = text.splitlines() - if self.registryValue("useBold", msg.channel): - irc.reply( - ircutils.bold( - "Add category name to the start command to select a category by" - " name." - ), - prefixNick=False, - ) - irc.reply(ircutils.bold(", ".join(reply)), prefixNick=False) - else: - irc.reply( - "Add category name to the start command to select a category by name.", - prefixNick=False, - ) - irc.reply(", ".join(reply), prefixNick=False) - - categories = wrap(categories) - - def stats(self, irc, msg, args, channel, optlist, nick): - """[channel] [--top ] [] - Returns Jeopardy! player stats. Supply a nick to get stats for a specific - player. Use --top to set number of players to list. - Defaults to current channel and top 5 players if no options given. - """ - optlist = dict(optlist) - if not channel: - channel = msg.channel - if "top" in optlist: - top = optlist.get("top") - else: - top = 5 - try: - if nick: - try: - total = self.games[channel].scores[nick] - self.games[channel].reply( - "Total score for {0} in {1}: {2}".format(nick, channel, total) - ) - except KeyError: - self.games[channel].reply( - "No scores found for {0} in {1}".format(nick, channel) - ) - else: - sorted_x = [] - sorted_x = sorted( - self.games[channel].scores.items(), - key=lambda kv: kv[1], - reverse=True, - ) - if len(sorted_x) < top: - top = len(sorted_x) - if top > 0: - totals = "" - for i in range(0, top): - item = sorted_x[i] - totals += "#{0} ({1}: {2}), ".format(i + 1, item[0], item[1]) - self.games[channel].reply( - "Top {0} Jeopardy! players for {1}:".format(top, channel) - ) - self.games[channel].reply(totals.strip(", ")) - else: - return - except KeyError: - scores = requests.structures.CaseInsensitiveDict() - scoreFile = "{0}/jeopardy/scores_{1}.txt".format( - conf.supybot.directories.data, channel - ) - if not os.path.exists(scoreFile): - return - if nick: - f = open(scoreFile, "r") - line = f.readline() - while line: - (name, score) = line.split(" ") - scores[name] = int(score.strip("\r\n")) - line = f.readline() - f.close() - try: - total = scores[nick] - irc.reply( - "Total score for {0} in {1}: {2}".format(nick, channel, total), - prefixNick=False, - ) - except KeyError: - irc.reply( - "No scores found for {0} in {1}".format(nick, channel), - prefixNick=False, - ) - else: - f = open(scoreFile, "r") - line = f.readline() - while line: - (name, score) = line.split(" ") - scores[name] = int(score.strip("\r\n")) - line = f.readline() - f.close() - sorted_x = [] - sorted_x = sorted(scores.items(), key=lambda kv: kv[1], reverse=True) - if len(sorted_x) < top: - top = len(sorted_x) - if top > 0: - totals = "" - for i in range(0, top): - item = sorted_x[i] - totals += "#{0} ({1}: {2}), ".format(i + 1, item[0], item[1]) - irc.reply( - "Top {0} Jeopardy! players for {1}:".format(top, channel), - prefixNick=False, - ) - irc.reply(totals.strip(", "), prefixNick=False) - else: - return - - stats = wrap(stats, ["channel", getopts({"top": "int"}), additional("text")]) - - def question(self, irc, msg, args): - """ - Repeat the current question. - """ - channel = msg.channel - if channel in self.games: - if self.games[channel].active and not self.games[channel].correct: - self.games[channel].reply(self.games[channel].question) - else: - return - else: - return - - question = wrap(question) - - def hint(self, irc, msg, args): - """ - Display the next hint. If max hints reached, repeat the latest hint. - If max hints is 0, show blanked out answer. - """ - channel = msg.channel - if channel in self.games: - if not self.games[channel].active or self.games[channel].correct: - return - else: - self.games[channel].hint() - - hint = wrap(hint) - - def report(self, irc, msg, args): - """ - Report the current question as invalid and skip to the next one. - Only use this command if a question is unanswerable (e.g. audio/video clues) - """ - channel = msg.channel - if ( - self.registryValue("requireOps", channel) - and msg.nick not in irc.state.channels[channel].ops - and not ircdb.checkCapability(msg.prefix, "admin") - ): - return - if channel in self.games: - if self.games[channel].active: - r = requests.post( - "{0}/api/invalid".format(self.jserviceUrl), - data={"id": self.games[channel].id}, - ) - if r.status_code == 200: - self.games[channel].reply("Question successfully reported.") - else: - self.games[channel].reply("Error. Question not reported.") - self.games[channel].end() - - report = wrap(report) - - def skip(self, irc, msg, args): - """ - Skip the current question. - """ - channel = msg.channel - if ( - self.registryValue("requireOps", channel) - and msg.nick not in irc.state.channels[channel].ops - and not ircdb.checkCapability(msg.prefix, "admin") - ): - return - if channel in self.games: - if self.games[channel].active: - self.games[channel].end() - self.games[channel].unanswered -= 1 - - skip = wrap(skip) - - -Class = Jeopardy diff --git a/Jeopardy/requirements.txt b/Jeopardy/requirements.txt deleted file mode 100644 index 6d0d713..0000000 --- a/Jeopardy/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -requests -ftfy -beautifulsoup4 -textdistance[extras] -jinja2 -unidecode diff --git a/Jeopardy/setup.py b/Jeopardy/setup.py deleted file mode 100644 index b748de0..0000000 --- a/Jeopardy/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from supybot.setup import plugin_setup - -plugin_setup( - 'Jeopardy', - install_requires=[ - 'beautifulsoup4', - 'ftfy', - 'jinja2', - 'requests', - 'textdistance', - 'unidecode', - ], -)