async def call_herald_event(ci, destination: str, event_name: str, **kwargs) -> Dict: """Send a :class:`rh.Request` to a specific destination, and wait for a :class:`rh.Response`.""" if self.herald is None: raise rc.UnsupportedError("`royalherald` is not enabled on this Constellation.") request: rh.Request = rh.Request(handler=event_name, data=kwargs) response: rh.Response = await self.herald.request(destination=destination, request=request) if isinstance(response, rh.ResponseFailure): if response.name == "no_event": raise rc.CommandError(f"There is no event named {event_name} in {destination}.") elif response.name == "exception_in_event": # TODO: pretty sure there's a better way to do this if response.extra_info["type"] == "CommandError": raise rc.CommandError(response.extra_info["message"]) elif response.extra_info["type"] == "UserError": raise rc.UserError(response.extra_info["message"]) elif response.extra_info["type"] == "InvalidInputError": raise rc.InvalidInputError(response.extra_info["message"]) elif response.extra_info["type"] == "UnsupportedError": raise rc.UnsupportedError(response.extra_info["message"]) elif response.extra_info["type"] == "ConfigurationError": raise rc.ConfigurationError(response.extra_info["message"]) elif response.extra_info["type"] == "ExternalError": raise rc.ExternalError(response.extra_info["message"]) else: raise TypeError(f"Herald action call returned invalid error:\n" f"[p]{response}[/p]") elif isinstance(response, rh.ResponseSuccess): return response.data else: raise TypeError(f"Other Herald Link returned unknown response:\n" f"[p]{response}[/p]")
async def correct(data: rc.CommandData): answerer_ = await data.find_author(session=session, required=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 run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: async with data.session_acm() as session: author = await data.find_author(session=session, required=True) if "banker" not in author.roles: raise rc.UserError( "Non hai permessi sufficienti per eseguire questo comando." ) user_arg = args[0] qty_arg = args[1] reason_arg = " ".join(args[2:]) if user_arg is None: raise rc.InvalidInputError( "Non hai specificato un destinatario!") user = await rbt.User.find(self.alchemy, session, user_arg) if user is None: raise rc.InvalidInputError("L'utente specificato non esiste!") if qty_arg is None: raise rc.InvalidInputError("Non hai specificato una quantità!") try: qty = int(qty_arg) except ValueError: raise rc.InvalidInputError( "La quantità specificata non è un numero!") if reason_arg == "": raise rc.InvalidInputError("Non hai specificato un motivo!") await FiorygiTransaction.spawn_fiorygi(user, qty, reason_arg, data=data, session=session)
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: async with data.session_acm() as session: author = await data.find_author(session=session, required=True) if len(args) == 0: message = [] for obj in await self.get_updatables_of_user(session=session, user=author): async def change(attribute: str, value: Any): """A shortcut for self.__change.""" await self._change(session=session, obj=obj, attribute=attribute, new=value) await self.update(session=session, obj=obj, change=change) message.append(self.describe(obj)) if len(message) == 0: raise rc.UserError("Nessun account connesso.") await ru.asyncify(session.commit) await data.reply("\n".join(message)) else: created = await self.create(session=session, user=author, args=args, data=data) await ru.asyncify(session.commit) if created is not None: message = [ "🔗 Account collegato!", "", self.describe(created) ] await data.reply("\n".join(message))
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: TreasureT = self.alchemy.get(Treasure) async with data.session_acm() as session: author = await data.find_author(session=session, required=True) code = args[0].lower() treasure = await ru.asyncify(session.query(TreasureT).get, code) if treasure is None: raise rc.UserError( "Non esiste nessun Treasure con quel codice.") if treasure.redeemed_by is not None: raise rc.UserError( f"Quel tesoro è già stato riscattato da {treasure.redeemed_by}." ) treasure.redeemed_by = author await ru.asyncify(session.commit) await FiorygiTransaction.spawn_fiorygi( data, author, treasure.value, f'aver trovato il tesoro "{treasure.code}"') await data.reply("🤑 Tesoro riscattato!")
async def call_herald_event(self, destination: str, event_name: str, **kwargs) -> Dict: """Send a :class:`royalherald.Request` to a specific destination, and wait for a :class:`royalherald.Response`.""" if self.herald is None: raise rc.UnsupportedError( "`royalherald` is not enabled on this serf.") request: rh.Request = rh.Request(handler=event_name, data=kwargs) response: rh.Response = await self.herald.request( destination=destination, request=request) if isinstance(response, rh.ResponseFailure): if response.name == "no_event": raise rc.ProgramError( f"There is no event named {event_name} in {destination}.") elif response.name == "error_in_event": if response.extra_info["type"] == "CommandError": raise rc.CommandError(response.extra_info["message"]) elif response.extra_info["type"] == "UserError": raise rc.UserError(response.extra_info["message"]) elif response.extra_info["type"] == "InvalidInputError": raise rc.InvalidInputError(response.extra_info["message"]) elif response.extra_info["type"] == "UnsupportedError": raise rc.UnsupportedError(response.extra_info["message"]) elif response.extra_info["type"] == "ConfigurationError": raise rc.ConfigurationError(response.extra_info["message"]) elif response.extra_info["type"] == "ExternalError": raise rc.ExternalError(response.extra_info["message"]) else: raise rc.ProgramError( f"Invalid error in Herald event '{event_name}':\n" f"[b]{response.extra_info['type']}[/b]\n" f"{response.extra_info['message']}") elif response.name == "unhandled_exception_in_event": raise rc.ProgramError( f"Unhandled exception in Herald event '{event_name}':\n" f"[b]{response.extra_info['type']}[/b]\n" f"{response.extra_info['message']}") else: raise rc.ProgramError( f"Unknown response in Herald event '{event_name}':\n" f"[b]{response.name}[/b]" f"[p]{response}[/p]") elif isinstance(response, rh.ResponseSuccess): return response.data else: raise rc.ProgramError( f"Other Herald Link returned unknown response:\n" f"[p]{response}[/p]")
async def _create_treasure(self, author, code, value, data, session): TreasureT = self.alchemy.get(Treasure) treasure = await ru.asyncify(session.query(TreasureT).get, code) if treasure is not None: raise rc.UserError("Esiste già un Treasure con quel codice.") treasure = TreasureT( code=code, value=value, redeemed_by=None ) await FiorygiTransaction.spawn_fiorygi(author, -value, "aver creato un tesoro", data=data, session=session) return treasure
async def callback(data: rc.CommandData): async with data.session_acm() as session: # Find the user who clicked on the button user = await data.find_author(session=session, required=True) # Get the related MMEvent mmevent: MMEvent = await ru.asyncify( session.query(self._EventT).get, self.mmid) # Check if the user had already responded mmresponse: MMResponse = await ru.asyncify( session.query(self._ResponseT).filter_by( user=user, mmevent=mmevent).one_or_none) if mmresponse is None: # If they didn't respond, create a new MMResponse # noinspection PyArgumentList mmresponse = self._ResponseT(user=user, mmevent=mmevent, choice=choice) session.add(mmresponse) # Drop fiorygi if random.randrange(100) < self.command.config[ "matchmaking"]["fiorygi_award_chance"]: await FiorygiTransaction.spawn_fiorygi( user, 1, "aver risposto a un matchmaking", data=data, session=session) else: # Change their response mmresponse.choice = choice try: await ru.asyncify(session.commit) except psycopg2.Error: raise rc.UserError( "Hai già risposto nello stesso modo a questo matchmaking." ) await self.telegram_channel_message_update() await data.reply(f"{choice.value} Hai risposto al matchmaking!" )
async def callback(data: rc.CommandData): async with data.session_acm() as session: # Find the user who clicked on the button user = await data.find_author(session=session, required=True) # Get the related MMEvent mmevent: MMEvent = await ru.asyncify( session.query(self._EventT).get, self.mmid) # Ensure the user has the required roles to start the matchmaking if user != mmevent.creator and "admin" not in user.roles: raise rc.UserError( "Non hai i permessi per eliminare questo matchmaking!") # Interrupt the matchmaking with the MANUAL_DELETE reason await self.queue.put(Interrupts.MANUAL_START) await data.reply(f"🚩 Evento avviato!")
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: async with data.session_acm() as session: author = await data.find_author(session=session, required=False) if author is not None: raise rc.UserError(f"This account is already connected to {author}!") username = args[0] password = "******".join(args[1:]) user = await data.find_user(session=session, identifier=username) if user is None: raise rc.UserError("No such user.") try: successful = user.test_password(password) except ValueError: raise rc.UserError(f"User {user} has no password set!") if not successful: raise rc.InvalidInputError(f"Invalid password!") if rst is not None and isinstance(self.serf, rst.TelegramSerf): import telegram message: telegram.Message = data.message from_user: telegram.User = message.from_user TelegramT = self.alchemy.get(Telegram) tg_user: Telegram = await ru.asyncify( session.query(TelegramT).filter_by(tg_id=from_user.id).one_or_none ) if tg_user is None: # Create tg_user = TelegramT( user=user, tg_id=from_user.id, first_name=from_user.first_name, last_name=from_user.last_name, username=from_user.username ) session.add(tg_user) else: # Edit tg_user.first_name = from_user.first_name tg_user.last_name = from_user.last_name tg_user.username = from_user.username await ru.asyncify(session.commit) await data.reply(f"↔️ Account {tg_user} synced to {user}!") elif rsd is not None and isinstance(self.serf, rsd.DiscordSerf): import discord message: discord.Message = data.message ds_author: discord.User = message.author DiscordT = self.alchemy.get(Discord) ds_user: Discord = await ru.asyncify( session.query(DiscordT).filter_by(discord_id=ds_author.id).one_or_none ) if ds_user is None: # Create # noinspection PyProtectedMember ds_user = DiscordT( user=user, discord_id=ds_author.id, username=ds_author.name, discriminator=ds_author.discriminator, avatar_url=ds_author.avatar_url._url ) session.add(ds_user) else: # Edit ds_user.username = ds_author.name ds_user.discriminator = ds_author.discriminator ds_user.avatar_url = ds_author.avatar_url await ru.asyncify(session.commit) await data.reply(f"↔️ Account {ds_user} synced to {ds_author}!") else: raise rc.UnsupportedError(f"Unknown interface: {self.serf.__class__.__qualname__}")
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: async with data.session_acm() as session: if isinstance(self.serf, rst.TelegramSerf): message: telegram.Message = data.message reply: telegram.Message = message.reply_to_message creator = await data.find_author(session=session, required=True) # noinspection PyUnusedLocal quoted: Optional[str] # noinspection PyUnusedLocal text: Optional[str] # noinspection PyUnusedLocal context: Optional[str] # noinspection PyUnusedLocal timestamp: datetime.datetime # noinspection PyUnusedLocal media_url: Optional[str] # noinspection PyUnusedLocal spoiler: bool if creator is None: await data.reply( "⚠️ Devi essere registrato a Royalnet per usare questo comando!" ) return if reply is not None: # Get the message text text = reply.text # Check if there's an image associated with the reply photosizes: Optional[List[ telegram.PhotoSize]] = reply.photo if photosizes: # Text is a caption text = reply.caption media_url = await to_imgur( self.config["Imgur"]["token"], photosizes, text if text is not None else "") else: media_url = None # Ensure there is a text or an image if not (text or media_url): raise rc.InvalidInputError( "Il messaggio a cui hai risposto non contiene testo o immagini." ) # Find the Royalnet account associated with the sender quoted_tg = await ru.asyncify( session.query(self.alchemy.get( rbt.Telegram)).filter_by( tg_id=reply.from_user.id).one_or_none) quoted_account = quoted_tg.user if quoted_tg is not None else None # Find the quoted name to assign quoted_user: telegram.User = reply.from_user quoted = quoted_user.full_name # Get the timestamp timestamp = reply.date # Set the other properties spoiler = False context = None else: # Get the current timestamp timestamp = datetime.datetime.now() # Get the message text raw_text = " ".join(args) # Check if there's an image associated with the reply photosizes: Optional[List[ telegram.PhotoSize]] = message.photo if photosizes: media_url = await to_imgur( self.config["Imgur"]["token"], photosizes, raw_text if raw_text is not None else "") else: media_url = None # Parse the text, if it exists if raw_text: # Pass the sentence through the diario regex match = re.match( r'(!)? *["«‘“‛‟❛❝〝"`]([^"]+)["»’”❜❞〞"`] *(?:(?:-{1,2}|—) *([^,]+))?(?:, *([^ ].*))?', raw_text) # Find the corresponding matches if match is not None: spoiler = bool(match.group(1)) text = match.group(2) quoted = match.group(3) context = match.group(4) # Otherwise, consider everything part of the text else: spoiler = False text = raw_text quoted = None context = None # Ensure there's a quoted if not quoted: quoted = None if not context: context = None # Find if there's a Royalnet account associated with the quoted name if quoted is not None: quoted_alias = await ru.asyncify( session.query(self.alchemy.get( rbt.Alias)).filter_by( alias=quoted.lower()).one_or_none) else: quoted_alias = None quoted_account = quoted_alias.user if quoted_alias is not None else None else: text = None quoted = None quoted_account = None spoiler = False context = None # Ensure there is a text or an image if not (text or media_url): raise rc.InvalidInputError( "Manca il testo o l'immagine da inserire nel diario." ) # Create the diario quote diario = self.alchemy.get(Diario)( creator=creator, quoted_account=quoted_account, quoted=quoted, text=text, context=context, timestamp=timestamp, media_url=media_url, spoiler=spoiler) session.add(diario) await ru.asyncify(session.commit) await data.reply(f"✅ {str(diario)}") else: # Find the creator of the quotes creator = await data.find_author(session=session, required=True) # Recreate the full sentence raw_text = " ".join(args) # Pass the sentence through the diario regex match = re.match( r'(!)? *["«‘“‛‟❛❝〝"`]([^"]+)["»’”❜❞〞"`] *(?:(?:-{1,2}|—) *([^,]+))?(?:, *([^ ].*))?', raw_text) # Find the corresponding matches if match is not None: spoiler = bool(match.group(1)) text = match.group(2) quoted = match.group(3) context = match.group(4) # Otherwise, consider everything part of the text else: spoiler = False text = raw_text quoted = None context = None timestamp = datetime.datetime.now() # Ensure there is some text if not text: raise rc.InvalidInputError( "Manca il testo o l'immagine da inserire nel diario.") # Or a quoted if not quoted: quoted = None if not context: context = None # Find if there's a Royalnet account associated with the quoted name if quoted is not None: quoted_account = await rbt.User.find( self.alchemy, session, quoted) else: quoted_account = None if quoted_account is None: raise rc.UserError( "Il nome dell'autore è ambiguo, quindi la riga non è stata aggiunta.\n" "Per piacere, ripeti il comando con un nome più specifico!" ) # Create the diario quote DiarioT = self.alchemy.get(Diario) diario = DiarioT(creator=creator, quoted_account=quoted_account, quoted=quoted, text=text, context=context, timestamp=timestamp, media_url=None, spoiler=spoiler) session.add(diario) await ru.asyncify(session.commit) await data.reply(f"✅ {str(diario)}")
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: username = args[0] password = "******".join(args[1:]) author = await data.get_author(error_if_none=True) user = await data.find_user(username) try: successful = user.test_password(password) except ValueError: raise rc.UserError(f"User {user} has no password set!") if not successful: raise rc.InvalidInputError(f"Invalid password!") if self.interface.name == "telegram": import telegram message: telegram.Message = data.message from_user: telegram.User = message.from_user TelegramT = self.alchemy.get(Telegram) tg_user: Telegram = await ru.asyncify( data.session.query(TelegramT).filter_by( tg_id=from_user.id).one_or_none) if tg_user is None: # Create tg_user = TelegramT(user=author, tg_id=from_user.id, first_name=from_user.first_name, last_name=from_user.last_name, username=from_user.username) data.session.add(tg_user) else: # Edit tg_user.first_name = from_user.first_name tg_user.last_name = from_user.last_name tg_user.username = from_user.username await data.session_commit() await data.reply(f"↔️ Account {tg_user} synced to {author}!") elif self.interface.name == "discord": import discord message: discord.Message = data.message author: discord.User = message.author DiscordT = self.alchemy.get(Discord) ds_user: Discord = await ru.asyncify( data.session.query(DiscordT).filter_by( discord_id=author.id).one_or_none) if ds_user is None: # Create ds_user = DiscordT(user=author, discord_id=author.id, username=author.name, discriminator=author.discriminator, avatar_url=author.avatar_url) data.session.add(ds_user) else: # Edit ds_user.username = author.name ds_user.discriminator = author.discriminator ds_user.avatar_url = author.avatar_url await data.session_commit() await data.reply(f"↔️ Account {ds_user} synced to {author}!") elif self.interface.name == "matrix": raise rc.UnsupportedError( f"{self} hasn't been implemented for Matrix yet") else: raise rc.UnsupportedError( f"Unknown interface: {self.interface.name}")
async def _permission_check(self, author, code, value, data, session): if author.fiorygi.fiorygi < value: raise rc.UserError("Non hai abbastanza fiorygi per creare questo Treasure.")
async def run(self, guild_id: Optional[int] = None, **kwargs) -> dict: if not isinstance(self.parent, DiscordSerf): raise rc.UnsupportedError() # noinspection PyTypeChecker serf: DiscordSerf = self.parent client: discord.Client = serf.client if guild_id is None: guilds: List[discord.Guild] = client.guilds if len(guilds) == 0: raise rc.ConfigurationError("Il bot non è in nessun Server.") elif len(guilds) > 1: raise rc.UserError( "Non hai specificato di quale Server vuoi vedere le informazioni!" ) else: guild = guilds[0] else: guild: discord.Guild = client.get_guild(guild_id) members: List[Union[discord.User, discord.Member]] = guild.members results = [] for member in members: data = { "id": member.id, "name": member.name, "discriminator": member.discriminator, "nick": member.nick, "bot": member.bot, "voice": { "channel": { "id": member.voice.channel.id, "name": member.voice.channel.name, "position": member.voice.channel.position, "category": { "id": member.voice.channel.category_id, "name": member.voice.channel.category.name, "position": member.voice.channel.category.position, } if member.voice.channel.category is not None else { "id": None, "name": None, "position": -1, }, "bitrate": member.voice.channel.bitrate, "user_limit": member.voice.channel.user_limit, }, "server_mute": member.voice.mute, "server_deaf": member.voice.deaf, "self_mute": member.voice.self_mute, "self_deaf": member.voice.self_deaf, "video": member.voice.self_video, "golive": member.voice.self_stream, "afk": member.voice.afk, } if member.voice is not None else None, "status": { "main": member.status.value, "desktop": member.desktop_status.value, "mobile": member.mobile_status.value, "web": member.web_status.value, }, "activities": [ activity.to_dict() for activity in member.activities if activity is not None ], "roles": [{ "id": role.id, "name": role.name, "hoist": role.hoist, "position": role.position, "managed": role.managed, "mentionable": role.mentionable, } for role in member.roles] } results.append(data) return { "guild": { "id": guild.id, "name": guild.name, "members": results, } }