예제 #1
0
 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]")
예제 #2
0
 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!")
예제 #3
0
    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)
예제 #4
0
    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))
예제 #5
0
    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!")
예제 #6
0
 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]")
예제 #7
0
    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
예제 #8
0
        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!"
                                 )
예제 #9
0
        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!")
예제 #10
0
    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__}")
예제 #11
0
 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)}")
예제 #12
0
    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}")
예제 #13
0
 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.")
예제 #14
0
    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,
            }
        }