diff --git a/poetry.lock b/poetry.lock index 6b7aeb0e..346ab49a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -253,17 +253,6 @@ sqlalchemy = ["sqlalchemy"] test = ["pytest-cov (>=2.6)", "pytest (>4.0)"] zmq = ["pyzmq"] -[[package]] -category = "main" -description = "A simple, extensible Markov chain generator. Uses include generating random semi-plausible sentences based on an existing text." -name = "markovify" -optional = false -python-versions = "*" -version = "0.8.0" - -[package.dependencies] -unidecode = "*" - [[package]] category = "main" description = "A Python Matrix client library, designed according to sans I/O principles." @@ -451,7 +440,7 @@ description = "A multipurpose bot and web framework" name = "royalnet" optional = false python-versions = ">=3.8,<4.0" -version = "5.2.4" +version = "5.3.1" [package.dependencies] dateparser = ">=0.7.2,<0.8.0" @@ -564,8 +553,8 @@ category = "main" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.13.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.14.0" [[package]] category = "main" @@ -641,14 +630,6 @@ version = "2.0.0" [package.dependencies] pytz = "*" -[[package]] -category = "main" -description = "ASCII transliterations of Unicode text" -name = "unidecode" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.1.1" - [[package]] category = "main" description = "Unpadded Base64" @@ -720,10 +701,10 @@ description = "YouTube video downloader" name = "youtube-dl" optional = false python-versions = "*" -version = "2020.1.1" +version = "2020.1.15" [metadata] -content-hash = "8713e8d5f419fe8df4c610273aed1529ca44d9d3e4af542b89807b76743f146b" +content-hash = "84e93ee35c496ee10d2c2552ad6c38ec257dca2c8f9a91d2b4e4e4940057588a" python-versions = "^3.8" [metadata.files] @@ -890,9 +871,6 @@ logbook = [ {file = "Logbook-1.5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0cf2cdbfb65a03b5987d19109dacad13417809dcf697f66e1a7084fb21744ea9"}, {file = "Logbook-1.5.3.tar.gz", hash = "sha256:66f454ada0f56eae43066f604a222b09893f98c1adc18df169710761b8f32fe8"}, ] -markovify = [ - {file = "markovify-0.8.0.tar.gz", hash = "sha256:c533a2e1aba8148bb98031b7159e8bf1a276c61db53d2e882ecb74fa5603a4f4"}, -] matrix-nio = [ {file = "matrix-nio-0.6.tar.gz", hash = "sha256:25a4ac9d5e1435035f5c5b6e9a6b453ac66ade25cb455ba6bbe9cc3ae1e0ef50"}, ] @@ -1064,8 +1042,8 @@ riotwatcher = [ {file = "riotwatcher-2.7.1.tar.gz", hash = "sha256:5349271c7e00637b7619491a6070e66603705db60558ea2a690e7016f6e6d9a4"}, ] royalnet = [ - {file = "royalnet-5.2.4-py3-none-any.whl", hash = "sha256:074d741ab78b96c98803a0816fd8e2fc61ecae3017f7c41ea8af40fc3675d07b"}, - {file = "royalnet-5.2.4.tar.gz", hash = "sha256:a38f02fa6560a0c05896957aeb504894292eae0a2e3f607d696b242026fed863"}, + {file = "royalnet-5.3.1-py3-none-any.whl", hash = "sha256:5473524e906fefb90f54af5ce62a0685fcea7766e8971792bd0bf740d558e5a5"}, + {file = "royalnet-5.3.1.tar.gz", hash = "sha256:ced549977490bc92864d220ed3c277f8425f4201f181fe2913bcdd85f58a7f36"}, ] royalspells = [ {file = "royalspells-3.2.tar.gz", hash = "sha256:2bd4a9a66514532e35c02c3907425af48c7cb292364c4843c795719a82b25dfe"}, @@ -1075,8 +1053,8 @@ sentry-sdk = [ {file = "sentry_sdk-0.13.5-py2.py3-none-any.whl", hash = "sha256:05285942901d38c7ce2498aba50d8e87b361fc603281a5902dda98f3f8c5e145"}, ] six = [ - {file = "six-1.13.0-py2.py3-none-any.whl", hash = "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd"}, - {file = "six-1.13.0.tar.gz", hash = "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"}, + {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, + {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, ] sqlalchemy = [ {file = "SQLAlchemy-1.3.12.tar.gz", hash = "sha256:bfb8f464a5000b567ac1d350b9090cf081180ec1ab4aa87e7bca12dab25320ec"}, @@ -1106,10 +1084,6 @@ tzlocal = [ {file = "tzlocal-2.0.0-py2.py3-none-any.whl", hash = "sha256:11c9f16e0a633b4b60e1eede97d8a46340d042e67b670b290ca526576e039048"}, {file = "tzlocal-2.0.0.tar.gz", hash = "sha256:949b9dd5ba4be17190a80c0268167d7e6c92c62b30026cf9764caf3e308e5590"}, ] -unidecode = [ - {file = "Unidecode-1.1.1-py2.py3-none-any.whl", hash = "sha256:1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a"}, - {file = "Unidecode-1.1.1.tar.gz", hash = "sha256:2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"}, -] unpaddedbase64 = [ {file = "unpaddedbase64-1.1.0-py2-none-any.whl", hash = "sha256:8917367e4e915b7dce1a72a99db8798c9f3d0d9a74cdd9aafac6d7c65ca495c5"}, {file = "unpaddedbase64-1.1.0-py2.py3-none-any.whl", hash = "sha256:81cb4eaaa28cc6a282dd3f2c3855eaa1fbaafa736b5ee64df69889e20540a339"}, @@ -1177,6 +1151,6 @@ yarl = [ {file = "yarl-1.4.2.tar.gz", hash = "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b"}, ] youtube-dl = [ - {file = "youtube_dl-2020.1.1-py2.py3-none-any.whl", hash = "sha256:3538410130b3adb4690955b2454bfe212d94babefd822aadae4a2b263c421be4"}, - {file = "youtube_dl-2020.1.1.tar.gz", hash = "sha256:0f8e62b5a3a5bb8bdf3d7ae3ec96c233f5110fa0ee1c9df51299b6636ebd4ff4"}, + {file = "youtube_dl-2020.1.15-py2.py3-none-any.whl", hash = "sha256:28630dfa91ba5e74676cbc76cfc91cd820e6dacca6ca175e07e49df73607bf46"}, + {file = "youtube_dl-2020.1.15.tar.gz", hash = "sha256:74e341f05e23e1650f57a01a58346cfabe3c841ccb9852c859ed2a383655db2a"}, ] diff --git a/pyproject.toml b/pyproject.toml index f70378bf..33fa700f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ royalspells = "^3.2" [tool.poetry.dependencies.royalnet] - version = "^5.2.4" + version = "^5.3.1" # Maybe... there is a way to make these selectable? extras = [ "telegram", diff --git a/royalpack/commands/__init__.py b/royalpack/commands/__init__.py index 7bee64cc..56589fd4 100644 --- a/royalpack/commands/__init__.py +++ b/royalpack/commands/__init__.py @@ -30,6 +30,7 @@ from .peertube import PeertubeCommand from .funkwhale import FunkwhaleCommand from .eval import EvalCommand from .exec import ExecCommand +from .trivia import TriviaCommand # Enter the commands of your Pack here! available_commands = [ @@ -64,6 +65,7 @@ available_commands = [ EvalCommand, ExecCommand, FunkwhaleCommand, + TriviaCommand, ] # Don't change this, it should automatically generate __all__ diff --git a/royalpack/commands/peertubeupdates.py b/royalpack/commands/peertubeupdates.py index 03e37078..85423965 100644 --- a/royalpack/commands/peertubeupdates.py +++ b/royalpack/commands/peertubeupdates.py @@ -33,6 +33,8 @@ class PeertubeUpdatesCommand(Command): async with session.get(self.config["Peertube"]["instance_url"] + "/feeds/videos.json?sort=-publishedAt&filter=local") as response: log.debug("Parsing jsonfeed") + if response.status != 200: + raise ExternalError("Peertube is unavailable") j = await response.json() log.debug("Jsonfeed parsed successfully") return j diff --git a/royalpack/commands/trivia.py b/royalpack/commands/trivia.py new file mode 100644 index 00000000..b195e43a --- /dev/null +++ b/royalpack/commands/trivia.py @@ -0,0 +1,144 @@ +from typing import * +import asyncio +import aiohttp +import random +import uuid +import html +import royalnet.commands as rc +import royalnet.utils as ru +from ..tables import TriviaScore +from royalnet.backpack.tables.users import User + + +class TriviaCommand(rc.Command): + name: str = "trivia" + + aliases = ["t"] + + description: str = "Manda una domanda dell'OpenTDB in chat." + + syntax = "[credits|scores]" + + _letter_emojis = ["๐Ÿ‡ฆ", "๐Ÿ‡ง", "๐Ÿ‡จ", "๐Ÿ‡ฉ"] + + _medal_emojis = ["๐Ÿฅ‡", "๐Ÿฅˆ", "๐Ÿฅ‰", "๐Ÿ”น"] + + _correct_emoji = "โœ…" + + _wrong_emoji = "โŒ" + + _answer_time = 17 + + _question_lock: bool = False + + def __init__(self, interface: rc.CommandInterface): + super().__init__(interface) + self._answerers: Dict[uuid.UUID, Dict[str, bool]] = {} + + async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: + arg = args.optional(0) + if arg == "credits": + await data.reply(f"โ„น๏ธ [c]{self.interface.prefix}{self.name}[/c] di [i]Steffo[/i]\n" + f"\n" + f"Tutte le domande vengono dall'[b]Open Trivia Database[/b] di [i]Pixeltail Games[/i]," + f" creatori di Tower Unite, e sono rilasciate sotto la licenza [b]CC BY-SA 4.0[/b].") + return + elif arg == "scores": + trivia_scores = await ru.asyncify(data.session.query(self.alchemy.get(TriviaScore)).all) + strings = ["๐Ÿ† [b]Trivia Leaderboards[/b]\n"] + for index, ts in enumerate(sorted(trivia_scores, key=lambda ts: -ts.correct_rate)): + if index > 3: + index = 3 + strings.append(f"{self._medal_emojis[index]} {ts.royal.username}" + f" ({ts.correct_answers}/{ts.total_answers})") + await data.reply("\n".join(strings)) + return + if self._question_lock: + raise rc.CommandError("C'รจ giร  un'altra domanda attiva!") + self._question_lock = True + # Fetch the question + async with aiohttp.ClientSession() as session: + async with session.get("https://opentdb.com/api.php?amount=1") as response: + j = await response.json() + # Parse the question + if j["response_code"] != 0: + raise rc.CommandError(f"OpenTDB returned an error response_code ({j['response_code']}).") + question = j["results"][0] + text = f'โ“ [b]{question["category"]} - {question["difficulty"].capitalize()}[/b]\n' \ + f'{html.unescape(question["question"])}' + # Prepare answers + correct_answer: str = question["correct_answer"] + wrong_answers: List[str] = question["incorrect_answers"] + answers: List[str] = [correct_answer, *wrong_answers] + if question["type"] == "multiple": + random.shuffle(answers) + elif question["type"] == "boolean": + answers.sort(key=lambda a: a) + answers.reverse() + else: + raise NotImplementedError("Unknown question type") + # Find the correct index + for index, answer in enumerate(answers): + if answer == correct_answer: + correct_index = index + break + else: + raise ValueError("correct_index not found") + # Add emojis + for index, answer in enumerate(answers): + answers[index] = f"{self._letter_emojis[index]} {html.unescape(answers[index])}" + # Create the question id + question_id = uuid.uuid4() + self._answerers[question_id] = {} + + # Create the correct and wrong functions + async def correct(data: rc.CommandData): + answerer_ = await data.get_author(error_if_none=True) + try: + self._answerers[question_id][answerer_.uid] = True + except KeyError: + raise rc.UserError("Tempo scaduto!") + await data.reply("๐Ÿ†— Hai risposto alla domanda. Ora aspetta un attimo per i risultati!") + + async def wrong(data: rc.CommandData): + answerer_ = await data.get_author(error_if_none=True) + try: + self._answerers[question_id][answerer_.uid] = False + except KeyError: + raise rc.UserError("Tempo scaduto!") + await data.reply("๐Ÿ†— Hai risposto alla domanda. Ora aspetta un attimo per i risultati!") + + # Add question + keyboard: List[rc.KeyboardKey] = [] + for index, answer in enumerate(answers): + if index == correct_index: + keyboard.append(rc.KeyboardKey(interface=self.interface, + short=self._letter_emojis[index], + text=answers[index], + callback=correct)) + else: + keyboard.append(rc.KeyboardKey(interface=self.interface, + short=self._letter_emojis[index], + text=answers[index], + callback=wrong)) + async with data.keyboard(text=text, keys=keyboard): + await asyncio.sleep(self._answer_time) + results = f"โ—๏ธ Tempo scaduto!\n" \ + f"La risposta corretta era [b]{answers[correct_index]}[/b]!\n\n" + for answerer_id in self._answerers[question_id]: + answerer = data.session.query(self.alchemy.get(User)).get(answerer_id) + if answerer.trivia_score is None: + ts = self.interface.alchemy.get(TriviaScore)(royal=answerer) + data.session.add(ts) + await ru.asyncify(data.session.commit) + if self._answerers[question_id][answerer_id]: + results += self._correct_emoji + answerer.trivia_score.correct_answers += 1 + else: + results += self._wrong_emoji + answerer.trivia_score.wrong_answers += 1 + results += f" {answerer} ({answerer.trivia_score.correct_answers}/{answerer.trivia_score.total_answers})\n" + await data.reply(results) + del self._answerers[question_id] + await ru.asyncify(data.session.commit) + self._question_lock = False