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 run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: try: entry_id = int(args[0].lstrip("#")) except ValueError: raise rc.CommandError("L'id che hai specificato non è valido.") async with data.session_acm() as session: entry: Diario = await ru.asyncify( session.query(self.alchemy.get(Diario)).get, entry_id) if entry is None: raise rc.CommandError("Nessuna riga con quell'id trovata.") await data.reply(f"ℹ️ {entry}")
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: async with data.session_acm() as session: user: rbt.User = await data.find_author(session=session, required=True) if "admin" not in user.roles: raise rc.CommandError( "Non sei autorizzato a eseguire codice arbitrario!\n" "(Sarebbe un po' pericoloso se te lo lasciassi eseguire, non trovi?)" ) try: result = eval(args.joined(require_at_least=1)) except Exception as e: raise rc.CommandError(f"Eval fallito: {e}") await data.reply(repr(result))
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: faction = Faction[args[0].upper()] initiative_mod = int(args.optional(1, default="0")) async with data.session_acm() as session: DndBattleUnitT = self.alchemy.get(DndBattleUnit) active_battle = await get_active_battle(data=data, session=session) if active_battle is None: raise rc.CommandError("No battle is active in this chat.") active_character = await get_active_character(data=data, session=session) if active_character is None: raise rc.CommandError("You don't have an active character.") char: DndCharacter = active_character.character units_with_same_name = await ru.asyncify(session.query(DndBattleUnitT).filter_by( name=char.name, battle=active_battle.battle ).all) if len(units_with_same_name) != 0: raise rc.InvalidInputError("A unit with the same name already exists.") roll = random.randrange(1, 21) modifier = char.initiative + initiative_mod modifier_str = f"{modifier:+d}" if modifier != 0 else "" initiative = roll + modifier dbu = DndBattleUnitT( linked_character=char, initiative=initiative, faction=faction, name=char.name, health_string=f"{char.current_hp}/{char.max_hp}", armor_class=char.armor_class, battle=active_battle.battle ) session.add(dbu) await ru.asyncify(session.commit) await data.reply(f"{dbu}\n" f"joins the battle!\n" f"\n" f"🎲 1d20{modifier_str} = {roll}{modifier_str} = {initiative}")
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: async with data.session_acm() as session: DiarioT = self.alchemy.get(Diario) entry: List[Diario] = await ru.asyncify( session.query(DiarioT).order_by( func.random()).limit(1).one_or_none) if entry is None: raise rc.CommandError("Nessuna riga del diario trovata.") await data.reply(f"ℹ️ {entry}")
async def get_author(data, error_if_none=False): user: str = data.event.sender query = data.session.query(self.master_table) for link in self.identity_chain: query = query.join(link.mapper.class_) query = query.filter(self.identity_column == user) result = await ru.asyncify(query.one_or_none) if result is None and error_if_none: raise rc.CommandError( "You must be registered to use this command.") return result
async def find_author(data, *, session, required: bool = False) -> Optional[rbt.User]: user: "******" = data.cbq.from_user TelegramT = data.alchemy.get(rbt.Telegram) result = await ru.asyncify( session.query(TelegramT).filter(TelegramT.tg_id == user.id).one_or_none ) if result is None and required: raise rc.CommandError("You must be registered to use this command.") return result.user
async def find_author(data, *, session, required: bool = False) -> Optional[rbt.User]: user: Union["discord.User", "discord.Member"] = data.message.author DiscordT = data.alchemy.get(rbt.Discord) result = await asyncify( session.query(DiscordT).filter(DiscordT.discord_id == user.id).one_or_none ) if result is None and required: raise rc.CommandError("You must be registered to use this command.") return result
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 run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: faction = Faction[args[0].upper()] name = args[1] initiative = int(args[2]) health = args[3] armor_class = int(args[4]) DndBattleUnitT = self.alchemy.get(DndBattleUnit) async with data.session_acm() as session: active_battle = await get_active_battle(session=session, data=data) if active_battle is None: raise rc.CommandError("No battle is active in this chat.") units_with_same_name = await ru.asyncify( session.query(DndBattleUnitT).filter_by( name=name, battle=active_battle.battle).all) if len(units_with_same_name) != 0: raise rc.InvalidInputError( "A unit with the same name already exists.") try: health = Health.from_text(health) except ValueError: raise rc.InvalidInputError("Invalid health string.") dbu = DndBattleUnitT(linked_character_id=None, initiative=initiative, faction=faction, name=name, health_string=health, armor_class=armor_class, battle=active_battle.battle) session.add(dbu) await ru.asyncify(session.commit) await data.reply(f"{dbu}\n" f"joins the battle!") if dbu.health.hidden: await data.delete_invoking()
async def get_targets(target: Optional[str], *, data: rc.CommandData, session) -> List[DndBattleUnit]: DndBattleUnitT = data.alchemy.get(DndBattleUnit) active_battle = await get_active_battle(data) if active_battle is None: raise rc.CommandError("No battle is active in this chat.") battle = active_battle.battle # Get the active character if not target or target.upper() == "SELF": active_character = await get_active_character(data) if active_character is None: return [] char = active_character.character return await ru.asyncify( session.query(DndBattleUnitT).filter_by(linked_character=char, battle=battle).all) # Get all if target.upper() == "ALL": return await ru.asyncify( session.query(DndBattleUnitT).filter_by(battle=battle).all) # Get by faction try: faction = Faction.get(target) except ValueError: pass else: return await ru.asyncify( session.query(DndBattleUnitT).filter_by(faction=faction, battle=battle).all) # Get by ilike return await ru.asyncify( session.query(DndBattleUnitT).filter( and_(DndBattleUnitT.name.ilike(target), DndBattleUnitT.battle == battle)).all)
async def to_imgur(imgur_api_key, photosizes: List[telegram.PhotoSize], caption="") -> str: # Select the largest photo largest_photo = sorted(photosizes, key=lambda p: p.width * p.height)[-1] # Get the photo url photo_file: telegram.File = await ru.asyncify(largest_photo.get_file) # Forward the url to imgur, as an upload async with aiohttp.request( "post", "https://api.imgur.com/3/upload", data={ "image": photo_file.file_path, "type": "URL", "title": "Diario image", "description": caption }, headers={"Authorization": f"Client-ID {imgur_api_key}"}) as request: response = await request.json() if not response["success"]: raise rc.CommandError( f"Imgur returned an error in the image upload: {response}") return response["data"]["link"]
async def run(self, args: rc.CommandArgs, data: rc.CommandData) -> None: arg = args.optional(0) async with data.session_acm() as session: if arg == "credits": await data.reply(f"ℹ️ [c]{self.serf.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(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.score)): if index > 3: index = 3 strings.append(f"{self._medal_emojis[index]} {ts.user.username}: [b]{ts.score:.0f}p[/b]" 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 ws: async with ws.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"]}[/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.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 wrong(data: rc.CommandData): answerer_ = await data.find_author(session=session, required=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(short=self._letter_emojis[index], text=answers[index], callback=correct)) else: keyboard.append(rc.KeyboardKey(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 = session.query(self.alchemy.get(rbt.users.User)).get(answerer_id) if answerer.trivia_score is None: ts = self.alchemy.get(TriviaScore)(user=answerer) session.add(ts) await ru.asyncify(session.commit) previous_score = answerer.trivia_score.score 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 current_score = answerer.trivia_score.score score_difference = current_score - previous_score results += f" {answerer}: [b]{current_score:.0f}p[/b] ({score_difference:+.0f}p)\n" await data.reply(results) del self._answerers[question_id] await ru.asyncify(session.commit)
async def create( self, session, user: rbt.User, args: rc.CommandArgs, data: Optional[rc.CommandData] = None ) -> Optional[LeagueOfLegends]: name = args.joined() # Connect a new League of Legends account to Royalnet log.debug(f"Searching for: {name}") summoner = self._lolwatcher.summoner.by_name(region=self.region(), summoner_name=name) # Ensure the account isn't already connected to something else leagueoflegends = await ru.asyncify( session.query(self.alchemy.get(LeagueOfLegends)).filter_by( summoner_id=summoner["id"]).one_or_none) if leagueoflegends: raise rc.CommandError( f"L'account {leagueoflegends} è già registrato su Royalnet.") # Get rank information log.debug(f"Getting leagues data: {name}") leagues = self._lolwatcher.league.by_summoner( region=self.region(), encrypted_summoner_id=summoner["id"]) soloq = LeagueLeague() flexq = LeagueLeague() twtrq = LeagueLeague() tftq = LeagueLeague() for league in leagues: if league["queueType"] == "RANKED_SOLO_5x5": soloq = LeagueLeague.from_dict(league) if league["queueType"] == "RANKED_FLEX_SR": flexq = LeagueLeague.from_dict(league) if league["queueType"] == "RANKED_FLEX_TT": twtrq = LeagueLeague.from_dict(league) if league["queueType"] == "RANKED_TFT": tftq = LeagueLeague.from_dict(league) # Get mastery score log.debug(f"Getting mastery data: {name}") mastery = self._lolwatcher.champion_mastery.scores_by_summoner( region=self.region(), encrypted_summoner_id=summoner["id"]) # Create database row leagueoflegends = self.alchemy.get(LeagueOfLegends)( region=self.region(), user=user, profile_icon_id=summoner["profileIconId"], summoner_name=summoner["name"], puuid=summoner["puuid"], summoner_level=summoner["summonerLevel"], summoner_id=summoner["id"], account_id=summoner["accountId"], rank_soloq=soloq, rank_flexq=flexq, rank_twtrq=twtrq, rank_tftq=tftq, mastery_score=mastery) await FiorygiTransaction.spawn_fiorygi( data=data, session=session, user=user, qty=1, reason= "aver collegato a Royalnet il proprio account di League of Legends" ) session.add(leagueoflegends) return leagueoflegends