From add61701836914102cf0a650fbcdd0d581625d59 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 26 Oct 2019 02:46:56 +0200 Subject: [PATCH] Progress on mm --- royalnet/packs/royal/commands/__init__.py | 2 +- royalnet/packs/royal/commands/cv.py | 2 +- royalnet/packs/royal/commands/diario.py | 2 +- royalnet/packs/royal/commands/dnditem.py | 2 +- royalnet/packs/royal/commands/dndspell.py | 2 +- royalnet/packs/royal/commands/matchmaking.py | 129 ++++++ royalnet/packs/royal/commands/mm.py | 404 ------------------ royalnet/packs/royal/commands/mp3.py | 2 +- royalnet/packs/royal/commands/play.py | 2 +- royalnet/packs/royal/commands/playmode.py | 2 +- royalnet/packs/royal/commands/reminder.py | 6 +- royalnet/packs/royal/commands/ship.py | 2 +- royalnet/packs/royal/commands/soundcloud.py | 2 +- royalnet/packs/royal/commands/youtube.py | 2 +- royalnet/packs/royal/tables/__init__.py | 2 - royalnet/packs/royal/tables/mmdecisions.py | 34 -- royalnet/packs/royal/tables/mmevents.py | 28 +- royalnet/packs/royal/tables/mmresponse.py | 13 +- royalnet/packs/royal/utils/__init__.py | 4 + royalnet/packs/royal/utils/mmchoice.py | 12 + royalnet/packs/royal/utils/mminterfacedata.py | 10 + 21 files changed, 189 insertions(+), 475 deletions(-) create mode 100644 royalnet/packs/royal/commands/matchmaking.py delete mode 100644 royalnet/packs/royal/commands/mm.py delete mode 100644 royalnet/packs/royal/tables/mmdecisions.py create mode 100644 royalnet/packs/royal/utils/__init__.py create mode 100644 royalnet/packs/royal/utils/mmchoice.py create mode 100644 royalnet/packs/royal/utils/mminterfacedata.py diff --git a/royalnet/packs/royal/commands/__init__.py b/royalnet/packs/royal/commands/__init__.py index 5f48eb8a..b85fe503 100644 --- a/royalnet/packs/royal/commands/__init__.py +++ b/royalnet/packs/royal/commands/__init__.py @@ -11,7 +11,7 @@ from .videochannel import VideochannelCommand from .dnditem import DnditemCommand from .dndspell import DndspellCommand from .trivia import TriviaCommand -from .mm import MmCommand +from .matchmaking import MmCommand from .pause import PauseCommand from .play import PlayCommand from .playmode import PlaymodeCommand diff --git a/royalnet/packs/royal/commands/cv.py b/royalnet/packs/royal/commands/cv.py index d15dad37..7dad85fe 100644 --- a/royalnet/packs/royal/commands/cv.py +++ b/royalnet/packs/royal/commands/cv.py @@ -10,7 +10,7 @@ class CvCommand(Command): description: str = "Elenca le persone attualmente connesse alla chat vocale." - syntax: str = "[guildname] ['all']" + syntax: str = "[guildname] [all]" @staticmethod async def _legacy_cv_handler(bot: DiscordBot, guild_name: typing.Optional[str], everyone: bool): diff --git a/royalnet/packs/royal/commands/diario.py b/royalnet/packs/royal/commands/diario.py index d8726553..fecc0b09 100644 --- a/royalnet/packs/royal/commands/diario.py +++ b/royalnet/packs/royal/commands/diario.py @@ -33,7 +33,7 @@ class DiarioCommand(Command): description: str = "Aggiungi una citazione al Diario." - syntax = "[!] \"(testo)\" --[autore], [contesto]" + syntax = "[!] \"{testo}\" --[autore], [contesto]" tables = {User, Diario, Alias} diff --git a/royalnet/packs/royal/commands/dnditem.py b/royalnet/packs/royal/commands/dnditem.py index 510996ac..61cd3601 100644 --- a/royalnet/packs/royal/commands/dnditem.py +++ b/royalnet/packs/royal/commands/dnditem.py @@ -12,7 +12,7 @@ class DnditemCommand(Command): description: str = "Ottieni informazioni su un oggetto di D&D5e." - syntax = "(nomeoggetto)" + syntax = "{nomeoggetto}" _dnddata: sortedcontainers.SortedKeyList = None diff --git a/royalnet/packs/royal/commands/dndspell.py b/royalnet/packs/royal/commands/dndspell.py index 9e0c8c88..3ced2dc4 100644 --- a/royalnet/packs/royal/commands/dndspell.py +++ b/royalnet/packs/royal/commands/dndspell.py @@ -12,7 +12,7 @@ class DndspellCommand(Command): description: str = "Ottieni informazioni su una magia di D&D5e." - syntax = "(nomemagia)" + syntax = "{nomemagia}" _dnddata: sortedcontainers.SortedKeyList = None diff --git a/royalnet/packs/royal/commands/matchmaking.py b/royalnet/packs/royal/commands/matchmaking.py new file mode 100644 index 00000000..7faf28d7 --- /dev/null +++ b/royalnet/packs/royal/commands/matchmaking.py @@ -0,0 +1,129 @@ +import pickle +from telegram import Bot as PTBBot +from telegram import Message as PTBMessage +from telegram.error import BadRequest +from telegram import InlineKeyboardMarkup as IKM +from telegram import InlineKeyboardButton as IKB +from royalnet.commands import * +from royalnet.bots import TelegramBot +from royalnet.utils import telegram_escape, asyncify +from ..tables import MMEvent, MMResponse +from ..utils import MMChoice, MMInterfaceData, MMInterfaceDataTelegram + + +class MatchmakingCommand(Command): + name: str = "matchmaking" + + description: str = "Cerca persone per una partita a qualcosa!" + + syntax: str = "[ {ora} ] {nome}\n[descrizione]" + + aliases = ["mm", "lfg"] + + tables = {MMEvent, MMResponse} + + def __init__(self, interface: CommandInterface): + super().__init__(interface) + # Find all relevant MMEvents and run them + ... + + async def run(self, args: CommandArgs, data: CommandData) -> None: + # Create a new MMEvent and run it + ... + + _mm_chat_id = -1001224004974 + + def _gen_mm_message(self, mmevent: MMEvent) -> str: + text = f"🌐 [{mmevent.datetime.strftime('%Y-%m-%d %H:%M')}] [b]{mmevent.title}[/b]\n" + if mmevent.description: + text += f"{mmevent.description}\n" + text += "\n" + for response in mmevent.responses: + response: MMResponse + text += f"{response.choice.value} {response.royal}\n" + return text + + def _gen_telegram_keyboard(self, mmevent: MMEvent): + return IKM([ + [IKB(f"{MMChoice.YES.value} Ci sarΓ²!", callback_data=f"mm{mmevent.mmid}_YES")], + [IKB(f"{MMChoice.MAYBE.value} (Forse.)", callback_data=f"mm{mmevent.mmid}_MAYBE")], + [IKB(f"{MMChoice.LATE_SHORT.value} Arrivo dopo 5-10 min.", callback_data=f"mm{mmevent.mmid}_LATE_SHORT")], + [IKB(f"{MMChoice.LATE_MEDIUM.value} Arrivo dopo 15-35 min.", callback_data=f"mm{mmevent.mmid}_LATE_MEDIUM")], + [IKB(f"{MMChoice.LATE_LONG.value} Arrivo dopo 40+ min.", callback_data=f"mm{mmevent.mmid}_LATE_LONG")], + [IKB(f"{MMChoice.NO_TIME} Non posso a quell'ora...", callback_data=f"mm{mmevent.mmid}_NO_TIME")], + [IKB(f"{MMChoice.NO_INTEREST} Non mi interessa.", callback_data=f"mm{mmevent.mmid}_NO_INTEREST")], + [IKB(f"{MMChoice.NO_TECH} Ho un problema!", callback_data=f"mm{mmevent.mmid}_NO_TECH")], + ]) + + async def _update_telegram_mm_message(self, client: PTBBot, mmevent: MMEvent): + try: + await self.interface.bot.safe_api_call(client.edit_message_text, + chat_id=self._mm_chat_id, + text=telegram_escape(self._gen_mm_message(mmevent)), + message_id=mmevent.interface_data, + parse_mode="HTML", + disable_web_page_preview=True, + reply_markup=self._gen_telegram_keyboard(mmevent)) + except BadRequest: + pass + + def _gen_mm_telegram_callback(self, client: PTBBot, mmid: int, choice: MMChoice): + async def callback(data: CommandData): + author = await data.get_author(error_if_none=True) + # Find the MMEvent with the current session + mmevent: MMEvent = await asyncify(data.session.query(self.alchemy.MMEvent).get, mmid) + mmresponse: MMResponse = await asyncify(data.session.query(self.alchemy.MMResponse).filter_by(royal=author, mmevent=mmevent).one_or_none) + if mmresponse is None: + mmresponse = self.alchemy.MMResponse(royal=author, mmevent=mmevent, choice=choice) + data.session.add(mmresponse) + else: + mmresponse.choice = choice + await data.session_commit() + await self._update_telegram_mm_message(client, mmevent) + return f"βœ… Messaggio ricevuto!" + return callback + + async def _run_mmevent(self, mmid: int): + """Run a MMEvent.""" + # Open a new Alchemy Session + session = self.alchemy.Session() + # Find the MMEvent with the current session + mmevent: MMEvent = await asyncify(session.query(self.alchemy.MMEvent).get, mmid) + if mmevent is None: + raise ValueError("Invalid mmid.") + # Ensure the MMEvent interface matches the current one + if mmevent.interface != self.interface.name: + raise ValueError("Invalid interface.") + # If the matchmaking message hasn't been sent yet, do so now + if mmevent.interface_data is None: + if self.interface.name == "telegram": + bot: TelegramBot = self.interface.bot + client: PTBBot = bot.client + # Build the Telegram keyboard + # Send the keyboard + message: PTBMessage = await self.interface.bot.safe_api_call(client.send_message, + chat_id=self._mm_chat_id, + text=telegram_escape(self._gen_mm_message(mmevent)), + parse_mode="HTML", + disable_webpage_preview=True, + reply_markup=self._gen_telegram_keyboard(mmevent.mmid)) + # Store message data in the interface data object + mmevent.interface_data = MMInterfaceDataTelegram(chat_id=self._mm_chat_id, message_id=message.message_id) + else: + raise UnsupportedError() + # Register handlers for the keyboard events + if self.interface.name == "telegram": + bot: TelegramBot = self.interface.bot + client: PTBBot = bot.client + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_YES", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.YES)) + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_MAYBE", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.MAYBE)) + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_LATE_SHORT", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.LATE_SHORT)) + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_LATE_MEDIUM", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.LATE_MEDIUM)) + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_LATE_LONG", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.LATE_LONG)) + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_NO_TIME", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.NO_TIME)) + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_NO_INTEREST", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.NO_INTEREST)) + self.interface.register_keyboard_key(f"mm{mmevent.mmid}_NO_TECH", callback=self._gen_mm_telegram_callback(client, mmid, MMChoice.NO_TECH)) + else: + raise UnsupportedError() + # Sleep until the time of the event + ... diff --git a/royalnet/packs/royal/commands/mm.py b/royalnet/packs/royal/commands/mm.py deleted file mode 100644 index a2ea2252..00000000 --- a/royalnet/packs/royal/commands/mm.py +++ /dev/null @@ -1,404 +0,0 @@ -import datetime -import dateparser -import telegram -import asyncio -import re -import logging -import typing -from royalnet.commands import * -from royalnet.utils import asyncify, telegram_escape, sleep_until -from ..tables import MMEvent, MMDecision, MMResponse - -log = logging.getLogger(__name__) - - -class MmCommand(Command): - """Matchmaking command. - - Requires the MM_CHANNEL_ID envvar to be set.""" - name: str = "mm" - - aliases = ["matchmaking", "matchmake", "lfg", "lookingforgroup"] - - description: str = "Trova giocatori per una partita a qualcosa." - - syntax: str = "[ (data) ] (nomegioco)\n[descrizione]" - - tables = {MMEvent, MMDecision, MMResponse} - - _cycle_duration = 10 - - @staticmethod - def _main_keyboard(mmevent: MMEvent) -> typing.Optional[telegram.InlineKeyboardMarkup]: - if mmevent.state == "WAITING": - return telegram.InlineKeyboardMarkup([ - [telegram.InlineKeyboardButton("πŸ”΅ Ci sarΓ²!", callback_data=f"mm_{mmevent.mmid}_d_YES")], - [telegram.InlineKeyboardButton("⚫️ Forse...", callback_data=f"mm_{mmevent.mmid}_d_MAYBE")], - [telegram.InlineKeyboardButton("πŸ”΄ Non mi interessa.", callback_data=f"mm_{mmevent.mmid}_d_NO")] - ]) - elif mmevent.state == "DECISION": - return telegram.InlineKeyboardMarkup([ - [telegram.InlineKeyboardButton("πŸ”΅ Ci sarΓ²!", callback_data=f"mm_{mmevent.mmid}_d_YES"), - telegram.InlineKeyboardButton("πŸ”΄ Non mi interessa...", callback_data=f"mm_{mmevent.mmid}_d_NO")] - ]) - elif mmevent.state == "READY_CHECK": - return telegram.InlineKeyboardMarkup([ - [telegram.InlineKeyboardButton("🚩 Avvia la partita", callback_data=f"mm_{mmevent.mmid}_start")] - ]) - elif mmevent.state == "STARTED": - return None - else: - raise ValueError(f"state is of an unknown value ({mmevent.state})") - - @staticmethod - def _main_text(mmevent: MMEvent): - text = f"🌐 [{mmevent.datetime.strftime('%Y-%m-%d %H:%M')}] [b]{mmevent.title}[/b]\n" - if mmevent.description: - text += f"{mmevent.description}\n" - text += "\n" - if mmevent.state == "WAITING" or mmevent.state == "DECISION": - for mmdecision in sorted(mmevent.decisions, key=lambda mmd: mmd.royal.username): - mmdecision: "MMDecision" - if mmdecision.decision == "YES": - text += "πŸ”΅ " - elif mmdecision.decision == "MAYBE": - text += "⚫️ " - elif mmdecision.decision == "NO": - text += "πŸ”΄ " - else: - raise ValueError(f"decision is of an unknown value ({mmdecision.decision})") - text += f"{mmdecision.royal}\n" - elif mmevent.state == "READY_CHECK": - for mmresponse in sorted(mmevent.responses, key=lambda mmr: mmr.royal.username): - mmresponse: "MMResponse" - if mmresponse.response is None: - text += "❔ " - elif mmresponse.response == "YES": - text += "βœ… " - elif mmresponse.response == "LATER": - text += "πŸ•’ " - elif mmresponse.response == "NO": - text += "❌ " - else: - raise ValueError(f"response is of an unknown value ({mmresponse.response})") - text += f"{mmresponse.royal}\n" - elif mmevent.state == "STARTED": - for mmresponse in sorted(mmevent.responses, key=lambda mmr: mmr.response, reverse=True): - if mmresponse.response == "YES": - text += f"βœ… {mmresponse.royal}\n" - elif mmresponse.response == "NO": - text += f"❌ {mmresponse.royal}\n" - return text - - async def _run_mm(self, mmevent: MMEvent, session) -> None: - client: telegram.Bot = self.interface.bot.client - - async def update_message() -> None: - try: - await self.interface.bot.safe_api_call(client.edit_message_text, - text=telegram_escape(self._main_text(mmevent)), - chat_id=-1001224004974, - message_id=mmevent.message_id, - parse_mode="HTML", - disable_web_page_preview=True, - reply_markup=self._main_keyboard(mmevent)) - except telegram.error.BadRequest: - pass - - decision_string = f"⚫️ Hai detto che forse parteciperai a [b]{mmevent.title}[/b]" \ - f" alle [b]{mmevent.datetime.strftime('%H:%M')}[/b].\n" \ - f"Confermi di volerci essere? (Metti sΓ¬ anche se arrivi un po' in ritardo!)" - - decision_keyboard = telegram.InlineKeyboardMarkup([ - [telegram.InlineKeyboardButton("πŸ”΅ Ci sarΓ²!", callback_data=f"mm_{mmevent.mmid}_d_YES"), - telegram.InlineKeyboardButton("πŸ”΄ Non mi interessa piΓΉ.", callback_data=f"mm_{mmevent.mmid}_d_NO")] - ]) - - async def decision_yes(data: CommandData): - royal = await data.get_author() - mmdecision: MMDecision = await asyncify( - session.query(self.interface.alchemy.MMDecision).filter_by(mmevent=mmevent, - royal=royal).one_or_none) - if mmdecision is None: - mmdecision: MMDecision = self.interface.alchemy.MMDecision(royal=royal, - mmevent=mmevent, - decision="YES") - session.add(mmdecision) - else: - mmdecision.decision = "YES" - await asyncify(session.commit) - await update_message() - return "πŸ”΅ Hai detto che ci sarai!" - - async def decision_maybe(data: CommandData): - royal = await data.get_author() - mmdecision: MMDecision = await asyncify( - session.query(self.interface.alchemy.MMDecision).filter_by(mmevent=mmevent, - royal=royal).one_or_none) - if mmdecision is None: - mmdecision: MMDecision = self.interface.alchemy.MMDecision(royal=royal, - mmevent=mmevent, - decision="MAYBE") - session.add(mmdecision) - else: - mmdecision.decision = "MAYBE" - # Can't asyncify this - session.commit() - await update_message() - return f"⚫️ Hai detto che forse ci sarai." \ - f"Rispondi al messaggio di conferma {self._cycle_duration} minuti prima dell'inizio!" - - async def decision_no(data: CommandData): - royal = await data.get_author() - mmdecision: MMDecision = await asyncify( - session.query(self.interface.alchemy.MMDecision).filter_by(mmevent=mmevent, - royal=royal).one_or_none) - if mmdecision is None: - mmdecision: MMDecision = self.interface.alchemy.MMDecision(royal=royal, - mmevent=mmevent, - decision="NO") - session.add(mmdecision) - else: - mmdecision.decision = "NO" - # Can't asyncify this - session.commit() - await update_message() - return "πŸ”΄ Hai detto che non ti interessa." - - def response_string() -> str: - delay = (datetime.datetime.now() - mmevent.datetime).total_seconds() - if delay < 60: - return f"🚩 E' ora di [b]{mmevent.title}[/b]!\n" \ - f"Sei pronto?" - return f"πŸ•’ Sei in ritardo di [b]{int(delay / 60)} minuti[/b] per [b]{mmevent.title}[/b]...\n" \ - f"Sei pronto?" - - response_keyboard = telegram.InlineKeyboardMarkup([ - [telegram.InlineKeyboardButton("βœ… Ci sono!", - callback_data=f"mm_{mmevent.mmid}_r_YES")], - [telegram.InlineKeyboardButton("πŸ•’ Aspettatemi ancora un po'!", - callback_data=f"mm_{mmevent.mmid}_r_LATER")], - [telegram.InlineKeyboardButton("❌ Non vengo piΓΉ, mi spiace.", - callback_data=f"mm_{mmevent.mmid}_r_NO")] - ]) - - async def response_yes(data: CommandData): - royal = await data.get_author() - mmresponse: MMResponse = await asyncify( - session.query(self.interface.alchemy.MMResponse).filter_by(mmevent=mmevent, - royal=royal).one_or_none) - mmresponse.response = "YES" - # Can't asyncify this - session.commit() - await update_message() - return "βœ… Sei pronto!" - - def later_string(royal) -> str: - return f"πŸ•’ {royal.username} ha chiesto di aspettare {self._cycle_duration} prima di iniziare la" \ - f" partita.\n\n" \ - f"Se vuoi iniziare la partita senza aspettarlo, premi Avvia partita su Royal Matchmaking!" - - async def response_later(data: CommandData): - royal = await data.get_author() - mmresponse: MMResponse = await asyncify( - session.query(self.interface.alchemy.MMResponse).filter_by(mmevent=mmevent, - royal=royal).one_or_none) - mmresponse.response = "LATER" - # Can't asyncify this - session.commit() - await self.interface.bot.safe_api_call(client.send_message, - chat_id=mmevent.creator.telegram[0].tg_id, - text=telegram_escape(later_string(royal)), - parse_mode="HTML", - disable_webpage_preview=True) - await update_message() - return f"πŸ•’ Hai chiesto agli altri di aspettarti {self._cycle_duration} minuti." - - async def response_no(data: CommandData): - royal = await data.get_author() - mmresponse: MMResponse = await asyncify( - session.query(self.interface.alchemy.MMResponse).filter_by(mmevent=mmevent, - royal=royal).one_or_none) - mmresponse.response = "NO" - # Can't asyncify this - session.commit() - await update_message() - return "❌ Hai detto che non ci sarai." - - def started_string(): - text = f"🚩 L'evento [b]{mmevent.title}[/b] Γ¨ iniziato!\n\n" \ - f"Partecipano:\n" - for mmresponse in sorted(mmevent.responses, key=lambda mmr: mmr.response, reverse=True): - if mmresponse.response == "YES": - text += f"βœ… {mmresponse.royal}\n" - elif mmresponse.response == "NO": - text += f"❌ {mmresponse.royal}\n" - return text - - started_without_you_string = f"🚩 Non hai confermato la tua presenza in tempo e [b]{mmevent.title}[/b] Γ¨" \ - f" iniziato senza di te.\n" \ - f"Mi dispiace!" - - async def start_event(): - mmevent.state = "STARTED" - for mmresponse in mmevent.responses: - if mmresponse.response is None: - mmresponse.response = "NO" - if mmresponse.response == "LATER": - mmresponse.response = "NO" - - if mmresponse.response == "YES": - await self.interface.bot.safe_api_call(client.send_message, - chat_id=mmresponse.royal.telegram[0].tg_id, - text=telegram_escape(started_string()), - parse_mode="HTML", - disable_webpage_preview=True) - else: - await self.interface.bot.safe_api_call(client.send_message, - chat_id=mmresponse.royal.telegram[0].tg_id, - text=telegram_escape(started_without_you_string), - parse_mode="HTML", - disable_webpage_preview=True) - await asyncify(session.commit) - await update_message() - - async def start_key(data: CommandData): - royal = await data.get_author() - if royal == mmevent.creator: - await start_event() - - if mmevent.state == "WAITING": - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_d_YES", decision_yes) - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_d_MAYBE", decision_maybe) - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_d_NO", decision_no) - await sleep_until(mmevent.datetime - datetime.timedelta(minutes=10)) - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_d_YES") - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_d_MAYBE") - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_d_NO") - mmevent.state = "DECISION" - for mmdecision in mmevent.decisions: - mmdecision: MMDecision - if mmdecision.decision == "MAYBE": - await self.interface.bot.safe_api_call(client.send_message, - chat_id=mmdecision.royal.telegram[0].tg_id, - text=telegram_escape(decision_string), - parse_mode="HTML", - disable_webpage_preview=True, - reply_markup=decision_keyboard) - await asyncify(session.commit) - await update_message() - - if mmevent.state == "DECISION": - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_d_YES", decision_yes) - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_d_NO", decision_no) - await sleep_until(mmevent.datetime) - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_d_YES") - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_d_NO") - mmevent.state = "READY_CHECK" - for mmdecision in mmevent.decisions: - if mmdecision.decision == "MAYBE": - mmdecision.decision = "NO" - elif mmdecision.decision == "YES": - mmresponse: MMResponse = self.interface.alchemy.MMResponse(royal=mmdecision.royal, mmevent=mmevent) - session.add(mmresponse) - await asyncify(session.commit) - await update_message() - - if mmevent.state == "READY_CHECK": - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_r_YES", response_yes) - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_r_LATER", response_later) - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_r_NO", response_no) - self.interface.register_keyboard_key(f"mm_{mmevent.mmid}_forcestart", start_key) - cycle = 0 - while True: - for mmresponse in mmevent.responses: - # Send messages - if mmresponse.response is None: - await self.interface.bot.safe_api_call(client.send_message, - chat_id=mmresponse.royal.telegram[0].tg_id, - text=telegram_escape(response_string()), - parse_mode="HTML", - disable_webpage_preview=True, - reply_markup=response_keyboard) - # Wait - await asyncio.sleep(60 * self._cycle_duration) - # Advance cycle - for mmresponse in mmevent.responses: - if mmresponse.response is None: - mmresponse.response = "NO" - if mmresponse.response == "LATER": - mmresponse.response = None - # Check if the event can start - for mmresponse in mmevent.responses: - if mmresponse.response is None: - break - else: - break - cycle += 1 - - await start_event() - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_r_YES") - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_r_LATER") - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_r_NO") - self.interface.unregister_keyboard_key(f"mm_{mmevent.mmid}_forcestart") - - def __init__(self, interface): - super().__init__(interface) - # if self.interface.name != "telegram": - # return - # log.debug("Loading pending MMEvents from the database") - # session = interface.alchemy.Session() - # mmevents = session.query(self.interface.alchemy.MMEvent) \ - # .filter(self.interface.alchemy.MMEvent.datetime > datetime.datetime.now()) \ - # .all() - # log.info(f"Found {len(mmevents)} pending MMEvents") - # for mmevent in mmevents: - # session = interface.alchemy.Session() - # new_mmevent = session.query(MMEvent).get(mmevent.mmid) - # interface.loop.create_task(self._run_mm(new_mmevent, session, close_at_end=True)) - # session.close() - - async def run(self, args: CommandArgs, data: CommandData) -> None: - raise UnsupportedError("MmCommand Γ¨ attualmente disabilitato per via di bug introdotti da cambiamenti nella" - " gestione del database del bot.") - # if self.interface.name != "telegram": - # raise UnsupportedError("mm is supported only on Telegram") - # client: telegram.Bot = self.interface.bot.client - # creator = await data.get_author(error_if_none=True) - # try: - # timestring, title, description = args.match(r"\[\s*([^]]+)\s*]\s*([^\n]+)\s*\n?\s*(.+)?\s*", re.DOTALL) - # except InvalidInputError: - # timestring, title, description = args.match(r"\s*(.+?)\s*\n\s*([^\n]+)\s*\n?\s*(.+)?\s*", re.DOTALL) - # try: - # dt: typing.Optional[datetime.datetime] = dateparser.parse(timestring, settings={ - # "PREFER_DATES_FROM": "future" - # }) - # except OverflowError: - # dt = None - # if dt is None: - # await data.reply("⚠️ La data che hai specificato non Γ¨ valida.") - # return - # if dt <= datetime.datetime.now(): - # await data.reply("⚠️ La data che hai specificato Γ¨ nel passato.") - # return - # mmevent: MMEvent = self.interface.alchemy.MMEvent(creator=creator, - # datetime=dt, - # title=title, - # description=description, - # state="WAITING") - # data.session.add(mmevent) - # await asyncify(data.session.commit) - # - # message: telegram.Message = await self.interface.bot.safe_api_call(client.send_message, - # chat_id=-1001287169422, - # text=telegram_escape( - # self._main_text(mmevent)), - # parse_mode="HTML", - # disable_webpage_preview=True, - # reply_markup=self._main_keyboard(mmevent)) - # - # mmevent.message_id = message.message_id - # # Can't asyncify this - # await asyncify(data.session.commit) - # - # await self._run_mm(mmevent, data.session) diff --git a/royalnet/packs/royal/commands/mp3.py b/royalnet/packs/royal/commands/mp3.py index 48e6fdba..45efec4c 100644 --- a/royalnet/packs/royal/commands/mp3.py +++ b/royalnet/packs/royal/commands/mp3.py @@ -13,7 +13,7 @@ class Mp3Command(Command): description: str = "Scarica un video con youtube-dl e invialo in chat." - syntax = "(ytdlstring)" + syntax = "{ytdlstring}" ytdl_args = { "format": "bestaudio", diff --git a/royalnet/packs/royal/commands/play.py b/royalnet/packs/royal/commands/play.py index 5740385f..44e7823d 100644 --- a/royalnet/packs/royal/commands/play.py +++ b/royalnet/packs/royal/commands/play.py @@ -15,7 +15,7 @@ class PlayCommand(Command): description: str = "Aggiunge un url alla coda della chat vocale." - syntax = "[ [guild] ] (url)" + syntax = "[ [guild] ] {url}" @staticmethod async def _legacy_play_handler(bot: "DiscordBot", guild_name: typing.Optional[str], url: str): diff --git a/royalnet/packs/royal/commands/playmode.py b/royalnet/packs/royal/commands/playmode.py index afd07c31..251a353a 100644 --- a/royalnet/packs/royal/commands/playmode.py +++ b/royalnet/packs/royal/commands/playmode.py @@ -12,7 +12,7 @@ class PlaymodeCommand(Command): description: str = "Cambia modalitΓ  di riproduzione per la chat vocale." - syntax = "[ [guild] ] (mode)" + syntax = "[ [guild] ] {mode}" @staticmethod async def _legacy_playmode_handler(bot: "DiscordBot", guild_name: typing.Optional[str], mode_name: str): diff --git a/royalnet/packs/royal/commands/reminder.py b/royalnet/packs/royal/commands/reminder.py index 8626e782..e33b5e80 100644 --- a/royalnet/packs/royal/commands/reminder.py +++ b/royalnet/packs/royal/commands/reminder.py @@ -17,7 +17,7 @@ class ReminderCommand(Command): description: str = "Ti ricorda di fare qualcosa dopo un po' di tempo." - syntax: str = "[ (data) ] (messaggio)" + syntax: str = "[ {data} ] {messaggio}" tables = {Reminder} @@ -37,7 +37,7 @@ class ReminderCommand(Command): async def _remind(self, reminder): await sleep_until(reminder.datetime) if self.interface.name == "telegram": - chat_id: int = pickle.loads(reminder.interface_data) + chat_id: int = pickle.loads(reminder.raw_interface_data) bot: telegram.Bot = self.interface.bot.client await asyncify(bot.send_message, chat_id=chat_id, @@ -45,7 +45,7 @@ class ReminderCommand(Command): parse_mode="HTML", disable_web_page_preview=True) elif self.interface.name == "discord": - channel_id: int = pickle.loads(reminder.interface_data) + channel_id: int = pickle.loads(reminder.raw_interface_data) bot: discord.Client = self.interface.bot.client channel = bot.get_channel(channel_id) await channel.send(discord_escape(f"❗️ {reminder.message}")) diff --git a/royalnet/packs/royal/commands/ship.py b/royalnet/packs/royal/commands/ship.py index 98d38047..83677766 100644 --- a/royalnet/packs/royal/commands/ship.py +++ b/royalnet/packs/royal/commands/ship.py @@ -11,7 +11,7 @@ class ShipCommand(Command): description: str = "Crea una ship tra due nomi." - syntax = "(nomeuno) (nomedue)" + syntax = "{nomeuno} {nomedue}" async def run(self, args: CommandArgs, data: CommandData) -> None: name_one = args[0] diff --git a/royalnet/packs/royal/commands/soundcloud.py b/royalnet/packs/royal/commands/soundcloud.py index 7b648ca2..64ad73d2 100644 --- a/royalnet/packs/royal/commands/soundcloud.py +++ b/royalnet/packs/royal/commands/soundcloud.py @@ -15,7 +15,7 @@ class SoundcloudCommand(Command): description: str = "Cerca una canzone su Soundcloud e la aggiunge alla coda della chat vocale." - syntax = "[ [guild] ] (url)" + syntax = "[ [guild] ] {url}" @staticmethod async def _legacy_soundcloud_handler(bot: "DiscordBot", guild_name: typing.Optional[str], search: str): diff --git a/royalnet/packs/royal/commands/youtube.py b/royalnet/packs/royal/commands/youtube.py index 8d853dca..04f28fec 100644 --- a/royalnet/packs/royal/commands/youtube.py +++ b/royalnet/packs/royal/commands/youtube.py @@ -15,7 +15,7 @@ class YoutubeCommand(Command): description: str = "Cerca un video su YouTube e lo aggiunge alla coda della chat vocale." - syntax = "[ [guild] ] (url)" + syntax = "[ [guild] ] {url}" @classmethod async def _legacy_youtube_handler(cls, bot: "DiscordBot", guild_name: typing.Optional[str], search: str): diff --git a/royalnet/packs/royal/tables/__init__.py b/royalnet/packs/royal/tables/__init__.py index d0201a73..3566b857 100644 --- a/royalnet/packs/royal/tables/__init__.py +++ b/royalnet/packs/royal/tables/__init__.py @@ -10,7 +10,6 @@ from .wikirevisions import WikiRevision from .bios import Bio from .reminders import Reminder from .triviascores import TriviaScore -from .mmdecisions import MMDecision from .mmevents import MMEvent from .mmresponse import MMResponse @@ -26,7 +25,6 @@ available_tables = [ Bio, Reminder, TriviaScore, - MMDecision, MMEvent, MMResponse, ] diff --git a/royalnet/packs/royal/tables/mmdecisions.py b/royalnet/packs/royal/tables/mmdecisions.py deleted file mode 100644 index 08fd7cda..00000000 --- a/royalnet/packs/royal/tables/mmdecisions.py +++ /dev/null @@ -1,34 +0,0 @@ -from sqlalchemy import Column, \ - Integer, \ - String, \ - ForeignKey -from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declared_attr - - -class MMDecision: - __tablename__ = "mmdecisions" - - @declared_attr - def royal_id(self): - return Column(Integer, ForeignKey("users.uid"), primary_key=True) - - @declared_attr - def royal(self): - return relationship("User", backref="mmdecisions_taken") - - @declared_attr - def mmevent_id(self): - return Column(Integer, ForeignKey("mmevents.mmid"), primary_key=True) - - @declared_attr - def mmevent(self): - return relationship("MMEvent", backref="decisions") - - @declared_attr - def decision(self): - # Valid decisions are YES, MAYBE or NO - return Column(String, nullable=False) - - def __repr__(self): - return f"" diff --git a/royalnet/packs/royal/tables/mmevents.py b/royalnet/packs/royal/tables/mmevents.py index a3ce00d5..f633c0f8 100644 --- a/royalnet/packs/royal/tables/mmevents.py +++ b/royalnet/packs/royal/tables/mmevents.py @@ -1,10 +1,5 @@ -from sqlalchemy import Column, \ - Integer, \ - DateTime, \ - String, \ - Text, \ - ForeignKey, \ - BigInteger +import pickle +from sqlalchemy import * from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr @@ -37,14 +32,21 @@ class MMEvent: return Column(Text, nullable=False, default="") @declared_attr - def state(self): - # Valid states are WAITING, DECISION, READY_CHECK, STARTED - return Column(String, nullable=False, default="WAITING") + def interface(self): + return Column(String, nullable=False) @declared_attr - def message_id(self): - return Column(BigInteger) + def raw_interface_data(self): + # The default is a pickled None + return Column(Binary, nullable=False, default=b'\x80\x03N.') + + @property + def interface_data(self): + return pickle.loads(self.raw_interface_data) + + @interface_data.setter + def interface_data(self, value): + self.raw_interface_data = pickle.dumps(value) def __repr__(self): return f"" - diff --git a/royalnet/packs/royal/tables/mmresponse.py b/royalnet/packs/royal/tables/mmresponse.py index afcf71af..37b0b845 100644 --- a/royalnet/packs/royal/tables/mmresponse.py +++ b/royalnet/packs/royal/tables/mmresponse.py @@ -1,9 +1,7 @@ -from sqlalchemy import Column, \ - Integer, \ - String, \ - ForeignKey +from sqlalchemy import * from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr +from ..utils import MMChoice class MMResponse: @@ -26,9 +24,8 @@ class MMResponse: return relationship("MMEvent", backref="responses") @declared_attr - def response(self): - # Valid decisions are YES, LATER or NO - return Column(String) + def choice(self): + return Column(Enum(MMChoice), nullable=False) def __repr__(self): - return f"" + return f"" diff --git a/royalnet/packs/royal/utils/__init__.py b/royalnet/packs/royal/utils/__init__.py new file mode 100644 index 00000000..399ceac2 --- /dev/null +++ b/royalnet/packs/royal/utils/__init__.py @@ -0,0 +1,4 @@ +from .mmchoice import MMChoice +from .mminterfacedata import MMInterfaceData, MMInterfaceDataTelegram + +__all__ = ["MMChoice", "MMInterfaceData", "MMInterfaceDataTelegram"] diff --git a/royalnet/packs/royal/utils/mmchoice.py b/royalnet/packs/royal/utils/mmchoice.py new file mode 100644 index 00000000..5f54a507 --- /dev/null +++ b/royalnet/packs/royal/utils/mmchoice.py @@ -0,0 +1,12 @@ +import enum + + +class MMChoice(enum.Enum): + YES = "πŸ”΅" + MAYBE = "❔" + LATE_SHORT = "πŸ•" + LATE_MEDIUM = "πŸ•’" + LATE_LONG = "πŸ•—" + NO_TIME = "πŸ”΄" + NO_INTEREST = "❌" + NO_TECH = "❗️" diff --git a/royalnet/packs/royal/utils/mminterfacedata.py b/royalnet/packs/royal/utils/mminterfacedata.py new file mode 100644 index 00000000..4cfd6d32 --- /dev/null +++ b/royalnet/packs/royal/utils/mminterfacedata.py @@ -0,0 +1,10 @@ +class MMInterfaceData: + def __init__(self): + pass + + +class MMInterfaceDataTelegram(MMInterfaceData): + def __init__(self, chat_id: int, message_id: int): + super().__init__() + self.chat_id = chat_id + self.message_id = message_id