Пример #1
0
    async def report_recruitment(self, context, announce_id: int):
        recruitment_channel = self.guild.get_channel(self.RECRUITMENT_CHANNEL_ID)
        recruitment_announce = await utils.try_get_message(
            recruitment_channel, announce_id, error=exceptions.MissingMessage(announce_id)
        )
        author = recruitment_announce.author

        # Run checks

        patched_context = copy(context)
        patched_context.send = Admin.mock_send
        Admin.send_buffer.clear()
        await self.check_authors_clan_contact_role(patched_context, [recruitment_announce]) \
            or Admin.send_buffer.pop()
        await self.check_recruitment_announces_uniqueness(patched_context, [recruitment_announce]) \
            or Admin.send_buffer.pop()
        await self.check_recruitment_announces_length(patched_context, [recruitment_announce]) \
            or Admin.send_buffer.pop()
        await self.check_recruitment_announces_embeds(patched_context, [recruitment_announce]) \
            or Admin.send_buffer.pop()
        await self.check_recruitment_announces_timespan(patched_context, recruitment_channel, [recruitment_announce]) \
            or Admin.send_buffer.pop()

        if not Admin.send_buffer:
            await context.send(f"L'annonce ne présente aucun problème. :ok_hand: ")
        else:
            # DM author
            await utils.try_dm(
                author,
                f"Bonjour. Il a été détecté que ton annonce de recrutement ne respectait pas le "
                f"règlement du serveur. Voici un rapport de l'analyse effectuée: \n _ _"
            )
            await utils.try_dms(author, Admin.send_buffer, group_in_blocks=True)
            await utils.try_dm(
                author,
                f"_ _ \n"
                f"En attendant que le problème soit réglé, ton annonce as été supprimée.\n"
                f"En cas de besoin, tu peux contacter {context.author.mention} qui a reçu une copie du "
                f"rapport d'analyse.\n _ _"
            )
            await utils.try_dm(
                author,
                f"Copie du contenu de l'annonce:\n _ _ \n"
                f">>> {recruitment_announce.content}"
            )

            # DM moderator
            await utils.try_dm(context.author, f"Rapport d'analyse envoyé à {author.mention}: \n _ _")
            await utils.try_dms(context.author, Admin.send_buffer, group_in_blocks=True)
            await utils.try_dm(
                context.author,
                f"_ _ \n"
                f"Copie du contenu de l'annonce:\n _ _ \n"
                f">>> {recruitment_announce.content}"
            )

            # Delete announce
            await recruitment_announce.delete()
            await context.send(f"L'annonce a été supprimée et un rapport envoyé par MP. :ok_hand: ")
Пример #2
0
    async def simulate(self,
                       context: commands.Context,
                       src_channel: discord.TextChannel,
                       message_id: int,
                       emoji_list: converter.to_emoji_list = (),
                       dest_channel: discord.TextChannel = None,
                       *,
                       options=""):
        if dest_channel and not context.author.permissions_in(
                dest_channel).send_messages:
            raise exceptions.ForbiddenChannel(dest_channel)
        is_exclusive = utils.is_option_enabled(options, 'exclusive')
        required_role_name = utils.get_option_value(options, 'role')
        if required_role_name:
            utils.try_get(  # Raise if role does not exist
                self.guild.roles,
                error=exceptions.UnknownRole(required_role_name),
                name=required_role_name)

        message = await utils.try_get_message(
            src_channel,
            message_id,
            error=exceptions.MissingMessage(message_id))
        if not emoji_list:
            if len(message.reactions) == 0:
                raise exceptions.MissingConditionalArgument(
                    "Une liste d'émojis doit être fournie si le message ciblé n'a pas de réaction."
                )
            else:
                emoji_list = [reaction.emoji for reaction in message.reactions]
        elif len(emoji_list) > 20:
            raise exceptions.OversizedArgument(f"{len(emoji_list)} emojis",
                                               "20 emojis")

        reactions, results = await Poll.count_votes(message, emoji_list,
                                                    is_exclusive,
                                                    required_role_name)
        announcement = await (
            dest_channel
            or context).send("Évaluation des votes sur base des réactions.")
        await Poll.announce_results(results,
                                    message,
                                    announcement.channel,
                                    is_exclusive,
                                    required_role_name,
                                    dest_message=announcement)
Пример #3
0
    async def simulate(self,
                       context: commands.Context,
                       src_channel: discord.TextChannel,
                       message_id: int,
                       emoji: typing.Union[discord.Emoji, str] = None,
                       nb_winners: int = 1,
                       dest_channel: discord.TextChannel = None,
                       organizer: discord.User = None,
                       seed: int = None):
        if emoji and isinstance(emoji,
                                str) and emojis.emojis.count(emoji) != 1:
            raise exceptions.ForbiddenEmoji(emoji)
        if nb_winners < 1:
            raise exceptions.UndersizedArgument(nb_winners, 1)
        if dest_channel and not context.author.permissions_in(
                dest_channel).send_messages:
            raise exceptions.ForbiddenChannel(dest_channel)

        message = await utils.try_get_message(
            src_channel,
            message_id,
            error=exceptions.MissingMessage(message_id))
        if not emoji:
            if len(message.reactions) != 1:
                raise exceptions.MissingConditionalArgument(
                    "Un émoji doit être fourni si le message ciblé n'a pas exactement une réaction."
                )
            else:
                emoji = message.reactions[0].emoji
        players, reaction, winners = await Lottery.draw(message,
                                                        emoji,
                                                        nb_winners,
                                                        seed=seed)
        announce = f"Tirage au sort sur base de la réaction {emoji}" \
                   f"{f' et du seed `{seed}`' if seed else ''} au message {message.jump_url}"
        announcement = await (dest_channel or context).send(announce)
        await Lottery.announce_winners(winners,
                                       players,
                                       announcement,
                                       organizer=organizer)
Пример #4
0
class Lottery(_command.Command):
    """Commands for management of lotteries."""

    DISPLAY_NAME = "Loteries & Tirages au sort"
    DISPLAY_SEQUENCE = 5
    MOD_ROLE_NAMES = ['Administrateur', 'Modérateur', 'Annonceur']
    USER_ROLE_NAMES = ['Joueur']

    ANNOUNCE_ROLE_NAME = 'Abonné Annonces'
    EMBED_COLOR = 0x77B255  # Four leaf clover green
    JOBSTORE = database.MongoDBConnector.PENDING_LOTTERIES_COLLECTION

    pending_lotteries = {}  # Local cache of lottery data for reactions check

    def __init__(self, bot):
        super().__init__(bot)
        # Use class attribute to be available from static methods
        Lottery.pending_lotteries = zbot.db.load_pending_jobs_data(
            self.JOBSTORE,
            data_keys=('_id', 'lottery_id', 'message_id', 'channel_id',
                       'emoji_code', 'nb_winners', 'next_run_time',
                       'organizer_id'))

    @commands.group(name='lottery',
                    brief="Gère les tirages au sort",
                    invoke_without_command=True)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def lottery(self, context):
        if context.invoked_subcommand is None:
            raise exceptions.MissingSubCommand(context.command.name)

    @lottery.command(
        name='setup',
        aliases=['s', 'set', 'plan'],
        usage=
        "<\"announce\"> <#dest_channel> <:emoji:> <nb_winners> <\"time\"> [--no-announce]",
        brief="Programme un tirage au sort",
        help=
        "Le bot publie une **annonce** dans le **canal de destination**. Les joueurs "
        "participent en cliquant sur l'**émoji** de réaction. À la **date et heure** "
        "indiquées (au format `\"YYYY-MM-DD HH:MM:SS\"`), un **nombre de gagnants** sont "
        "tirés au sort et contactés par MP par le bot. L'organisateur reçoit par MP une copie "
        "du résultat et de la liste des participants injoignables. Par défaut, l'annonce "
        "mentionne automatiquement le rôle `@Abonné Annonces`. Pour éviter cela, il faut "
        "ajouter l'argument `--no-announce`.",
        ignore_extra=False)
    @commands.check(checker.has_any_mod_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def setup(self,
                    context: commands.Context,
                    announce: str,
                    dest_channel: discord.TextChannel,
                    emoji: typing.Union[discord.Emoji, str],
                    nb_winners: int,
                    time: converter.to_datetime,
                    *,
                    options=""):
        # Check arguments
        if not context.author.permissions_in(dest_channel).send_messages:
            raise exceptions.ForbiddenChannel(dest_channel)
        if isinstance(emoji, str) and emojis.emojis.count(emoji) != 1:
            raise exceptions.ForbiddenEmoji(emoji)
        if nb_winners < 1:
            raise exceptions.UndersizedArgument(nb_winners, 1)
        if (time - utils.get_current_time()).total_seconds() <= 0:
            argument_size = converter.humanize_datetime(time)
            min_argument_size = converter.humanize_datetime(
                utils.get_current_time())
            raise exceptions.UndersizedArgument(argument_size,
                                                min_argument_size)

        # Run command
        organizer = context.author
        do_announce = not utils.is_option_enabled(options, 'no-announce')
        prefixed_announce = utils.make_announce(
            context.guild, announce, do_announce and self.ANNOUNCE_ROLE_NAME)
        embed = self.build_announce_embed(emoji, nb_winners, organizer, time,
                                          self.guild.roles)
        message = await dest_channel.send(prefixed_announce, embed=embed)
        await message.add_reaction(emoji)

        # Register data
        job_id = scheduler.schedule_stored_job(self.JOBSTORE, time,
                                               self.run_lottery, message.id).id
        lottery_data = {
            'lottery_id': self.get_next_lottery_id(),
            'message_id': message.id,
            'channel_id': dest_channel.id,
            'emoji_code': emoji if isinstance(emoji, str) else emoji.id,
            'nb_winners': nb_winners,
            'organizer_id': organizer.id,
        }
        zbot.db.update_job_data(self.JOBSTORE, job_id, lottery_data)
        # Add data managed by scheduler later to avoid updating the database with them
        lottery_data.update({
            '_id': job_id,
            'next_run_time': converter.to_timestamp(time)
        })
        self.pending_lotteries[message.id] = lottery_data

        # Confirm command
        await context.send(
            f"Tirage au sort d'identifiant `{lottery_data['lottery_id']}` programmé : <{message.jump_url}>."
        )

    @staticmethod
    def build_announce_embed(emoji, nb_winners, organizer, time, guild_roles):
        embed = discord.Embed(
            title=
            f"Tirage au sort le {converter.humanize_datetime(time)} :alarm_clock:",
            color=Lottery.EMBED_COLOR)
        embed.add_field(
            name="Nombre de gagnants",
            value=f"**{nb_winners}** joueur{('s' if nb_winners > 1 else '')}")
        embed.add_field(name="Inscription", value=f"Réagissez avec {emoji}")
        embed.add_field(name="Rôle requis",
                        value=utils.try_get(
                            guild_roles,
                            error=exceptions.UnknownRole(
                                Lottery.USER_ROLE_NAMES[0]),
                            name=Lottery.USER_ROLE_NAMES[0]).mention)
        embed.set_author(name=f"Organisateur : @{organizer.display_name}",
                         icon_url=organizer.avatar_url)
        return embed

    def get_next_lottery_id(self) -> int:
        pending_lottery_ids = [
            lottery_data['lottery_id']
            for lottery_data in self.pending_lotteries.values()
        ]
        return max(pending_lottery_ids) + 1 if pending_lottery_ids else 1

    @commands.Cog.listener()
    async def on_reaction_add(self, reaction, user):
        message = reaction.message
        message_id = message.id
        emoji = reaction.emoji
        if message_id in Lottery.pending_lotteries:
            lottery_emoji_code = Lottery.pending_lotteries[message_id][
                'emoji_code']
            same_emoji = emoji == lottery_emoji_code if isinstance(
                emoji, str) else emoji.id == lottery_emoji_code
            if same_emoji and \
                    not user.bot and \
                    not checker.has_any_role(self.guild, user, Lottery.USER_ROLE_NAMES):
                try:
                    await utils.try_dm(
                        user,
                        f"Vous devez avoir le rôle @{Lottery.USER_ROLE_NAMES[0]} pour "
                        f"participer à cette loterie.")
                    await message.remove_reaction(emoji, user)
                except (discord.errors.HTTPException, discord.errors.NotFound,
                        discord.errors.Forbidden):
                    pass

    @lottery.command(name='list',
                     aliases=['l', 'ls'],
                     brief="Affiche la liste des tirages au sort en cours",
                     ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def list(self, context: commands.Context):
        lottery_descriptions, guild_id = {}, self.guild.id
        for message_id, lottery_data in self.pending_lotteries.items():
            lottery_id = lottery_data['lottery_id']
            channel_id = lottery_data['channel_id']
            organizer = self.guild.get_member(lottery_data['organizer_id'])
            time = scheduler.get_job_run_date(lottery_data['_id'])
            message_link = f"https://discordapp.com/channels/{guild_id}/{channel_id}/{message_id}"
            lottery_descriptions[lottery_id] = f" • `[{lottery_id}]` - Programmé par {organizer.mention} " \
                                               f"pour le [__{converter.humanize_datetime(time)}__]({message_link})"
        embed_description = "Aucun" if not lottery_descriptions \
            else "\n".join([lottery_descriptions[lottery_id] for lottery_id in sorted(lottery_descriptions.keys())])
        embed = discord.Embed(title="Tirage(s) au sort en cours",
                              description=embed_description,
                              color=self.EMBED_COLOR)
        await context.send(embed=embed)

    @lottery.command(
        name='pick',
        aliases=['p', 'run', 'r'],
        usage="<lottery_id> [seed]",
        brief="Effectue un tirage au sort en cours",
        help=
        "Force un tirage au sort à se dérouler à l'avance. Si un seed est fourni, le tirage au"
        "sort se base dessus pour le choix des gagnants.",
        ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def pick(self,
                   context: commands.Context,
                   lottery_id: int,
                   seed: int = None):
        message, _, _, _, _, organizer = await self.get_message_env(
            lottery_id, raise_if_not_found=True)

        if context.author != organizer:
            checker.has_any_mod_role(context, print_error=True)

        await Lottery.run_lottery(message.id, seed=seed, manual_run=True)
        await context.send(
            f"Tirage au sort d'identifiant `{lottery_id}` exécuté : <{message.jump_url}>"
        )

    @staticmethod
    async def run_lottery(message_id, seed=None, manual_run=False):
        lottery_id = Lottery.pending_lotteries[message_id]['lottery_id']
        message, channel, emoji, nb_winners, time, organizer = await Lottery.get_message_env(
            lottery_id)
        try:
            players, reaction, winners = await Lottery.draw(
                message, emoji, nb_winners, seed)
            await reaction.remove(zbot.bot.user)
            await Lottery.announce_winners(winners, players, message,
                                           organizer)
            Lottery.remove_pending_lottery(message_id, cancel_job=manual_run)
        except commands.CommandError as error:
            context = commands.Context(
                bot=zbot.bot,
                cog=Lottery,
                prefix=zbot.bot.command_prefix,
                channel=channel,
                message=message,
            )
            await error_handler.handle(context, error)

    @lottery.command(
        name='cancel',
        aliases=['c'],
        usage="<lottery_id>",
        brief="Annule le tirage au sort",
        help=
        "Le numéro de loterie est affiché entre crochets par la commande `+lottery list`.",
        ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def cancel(self, context: commands.Context, lottery_id: int):
        message, _, emoji, _, _, organizer = await self.get_message_env(
            lottery_id, raise_if_not_found=False)

        if context.author != organizer:
            checker.has_any_mod_role(context, print_error=True)

        if message:
            reaction = utils.try_get(message.reactions,
                                     error=exceptions.MissingEmoji(emoji),
                                     emoji=emoji)
            await reaction.remove(zbot.bot.user)
            embed = discord.Embed(
                title=
                f"Tirage au sort __annulé__ par {context.author.display_name}",
                color=Lottery.EMBED_COLOR)
            embed.set_author(name=f"Organisateur : @{organizer.display_name}",
                             icon_url=organizer.avatar_url)
            await message.edit(embed=embed)
        self.remove_pending_lottery(message.id, cancel_job=True)
        await context.send(
            f"Tirage au sort d'identifiant `{lottery_id}` annulé : <{message.jump_url}>"
        )

    @lottery.group(name='edit',
                   brief="Modifie un tirage au sort",
                   invoke_without_command=True)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def edit(self, context):
        if context.invoked_subcommand is None:
            raise exceptions.MissingSubCommand(
                f'lottery {context.command.name}')

    @edit.command(
        name='announce',
        aliases=['annonce', 'a', 'description', 'desc', 'd', 'message', 'm'],
        usage="<lottery_id> <\"announce\"> [--no-announce]",
        brief="Modifie l'annonce du tirage au sort",
        help=
        "La précédente annonce associée au tirage au sort est remplacée par le message fourni. "
        "Par défaut, la nouvelle annonce mentionne automatiquement le rôle `@Abonné Annonces`. "
        "Pour éviter cela, il faut ajouter l'argument `--no-announce`.\n"
        "Dans tous les cas, les membres du serveur ne seront pas notifiés.",
        ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def announce(self,
                       context: commands.Context,
                       lottery_id: int,
                       announce: str,
                       *,
                       options=""):
        message, channel, _, _, _, organizer = await self.get_message_env(
            lottery_id, raise_if_not_found=True)

        if context.author != organizer:
            checker.has_any_mod_role(context, print_error=True)
        if not context.author.permissions_in(channel).send_messages:
            raise exceptions.ForbiddenChannel(channel)

        do_announce = not utils.is_option_enabled(options, 'no-announce')
        prefixed_announce = utils.make_announce(
            context.guild, announce, do_announce and self.ANNOUNCE_ROLE_NAME)
        await message.edit(content=prefixed_announce)
        await context.send(
            f"Annonce du tirage au sort d'identifiant `{lottery_id}` remplacée par "
            f"\"`{message.clean_content}`\" : <{message.jump_url}>")

    @edit.command(
        name='emoji',
        aliases=['émoji', 'e'],
        usage="<lottery_id> <:emoji:>",
        brief="Modifie l'émoji du tirage au sort",
        help=
        "Le précédent émoji associé au tirage au sort est remplacé par l'émoji fourni. "
        "Les réactions à l'ancien émoji ne sont pas prises en compte.",
        ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def emoji(self, context: commands.Context, lottery_id: int,
                    emoji: typing.Union[discord.Emoji, str]):
        message, channel, previous_emoji, nb_winners, time, organizer = \
            await self.get_message_env(lottery_id, raise_if_not_found=True)

        if context.author != organizer:
            checker.has_any_mod_role(context, print_error=True)
        if isinstance(emoji, str) and emojis.emojis.count(emoji) != 1:
            raise exceptions.ForbiddenEmoji(emoji)

        previous_reaction = utils.try_get(
            message.reactions,
            error=exceptions.MissingEmoji(previous_emoji),
            emoji=previous_emoji)
        await previous_reaction.remove(zbot.bot.user)
        await message.add_reaction(emoji)
        embed = self.build_announce_embed(emoji, nb_winners, organizer, time,
                                          self.guild.roles)
        await message.edit(embed=embed)

        job_id = self.pending_lotteries[message.id]['_id']
        lottery_data = {
            'emoji_code': emoji if isinstance(emoji, str) else emoji.id
        }
        zbot.db.update_job_data(self.JOBSTORE, job_id, lottery_data)
        self.pending_lotteries[message.id].update(lottery_data)
        await context.send(
            f"Émoji du tirage au sort d'identifiant `{lottery_id}` remplacé par \"{emoji}\" : "
            f"<{message.jump_url}>")

    @edit.command(
        name='organizer',
        aliases=['organisateur', 'org', 'o'],
        usage="<lottery_id> <@organizer>",
        brief="Modifie l'organisateur du tirage au sort",
        help=
        "Le précédent organisateur du tirage au sort est remplacé par l'organisateur fourni.",
        ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def organizer(self, context: commands.Context, lottery_id: int,
                        organizer: discord.User):
        message, channel, emoji, nb_winners, time, previous_organizer = \
            await self.get_message_env(lottery_id, raise_if_not_found=True)

        if context.author != previous_organizer:
            checker.has_any_mod_role(context, print_error=True)

        embed = self.build_announce_embed(emoji, nb_winners, organizer, time,
                                          self.guild.roles)
        await message.edit(embed=embed)

        job_id = self.pending_lotteries[message.id]['_id']
        lottery_data = {'organizer_id': organizer.id}
        zbot.db.update_job_data(self.JOBSTORE, job_id, lottery_data)
        self.pending_lotteries[message.id].update(lottery_data)
        await context.send(
            f"Organisateur du tirage au sort d'identifiant `{lottery_id}` remplacé par "
            f"`@{organizer.display_name}` : <{message.jump_url}>")

    @edit.command(
        name='time',
        aliases=['date', 'heure', 't'],
        usage="<lottery_id> <\"time\">",
        brief="Modifie la date et l'heure du tirage au sort",
        help=
        "Le précédente date et heure du tirage au sort sont changées pour celles fournies (au "
        "format `\"YYYY-MM-DD HH:MM:SS\"`). ",
        ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def time(self, context: commands.Context, lottery_id: int,
                   time: converter.to_datetime):
        message, channel, emoji, nb_winners, _, organizer = \
            await self.get_message_env(lottery_id, raise_if_not_found=True)

        if context.author != organizer:
            checker.has_any_mod_role(context, print_error=True)
        if (time - utils.get_current_time()).total_seconds() <= 0:
            argument_size = converter.humanize_datetime(time)
            min_argument_size = converter.humanize_datetime(
                utils.get_current_time())
            raise exceptions.UndersizedArgument(argument_size,
                                                min_argument_size)

        embed = self.build_announce_embed(emoji, nb_winners, organizer, time,
                                          self.guild.roles)
        await message.edit(embed=embed)

        job_id = self.pending_lotteries[message.id]['_id']
        scheduler.reschedule_stored_job(
            job_id, time)  # Also updates the next_run_time in db
        lottery_data = {'next_run_time': converter.to_timestamp(time)}
        self.pending_lotteries[message.id].update(lottery_data)
        await context.send(
            f"Date et heure du tirage au sort d'identifiant `{lottery_id}` changées pour le "
            f"`{converter.humanize_datetime(time)}` : <{message.jump_url}>")

    @edit.command(
        name='winners',
        aliases=['gagnants', 'nb_winners', 'n'],
        usage="<lottery_id> <nb_winners>",
        brief="Modifie le nombre de gagnants du tirage au sort",
        help=
        "Le précédent nombre de gagnants du tirage au sort est remplacé par le nombre de "
        "gagnants fourni.",
        ignore_extra=False)
    @commands.check(checker.has_any_user_role)
    @commands.check(checker.is_allowed_in_current_guild_channel)
    async def winners(self, context: commands.Context, lottery_id: int,
                      nb_winners: int):
        message, channel, emoji, _, time, organizer = \
            await self.get_message_env(lottery_id, raise_if_not_found=True)

        if context.author != organizer:
            checker.has_any_mod_role(context, print_error=True)
        if nb_winners < 1:
            raise exceptions.UndersizedArgument(nb_winners, 1)

        embed = self.build_announce_embed(emoji, nb_winners, organizer, time,
                                          self.guild.roles)
        await message.edit(embed=embed)

        job_id = self.pending_lotteries[message.id]['_id']
        lottery_data = {'nb_winners': nb_winners}
        zbot.db.update_job_data(self.JOBSTORE, job_id, lottery_data)
        self.pending_lotteries[message.id].update(lottery_data)
        await context.send(
            f"Nombre de gagnants du tirage au sort d'identifiant `{lottery_id}` changé à "
            f"`{nb_winners}` : <{message.jump_url}>")

    @staticmethod
    async def get_message_env(lottery_id: int, raise_if_not_found=True) -> \
            (discord.Message, discord.TextChannel, typing.Union[str, discord.Emoji],
             datetime.datetime, str, discord.Member):
        if not (lottery_data := discord.utils.find(
                lambda data: data['lottery_id'] == lottery_id,
                Lottery.pending_lotteries.values())):
            raise exceptions.UnknownLottery(lottery_id)

        channel = zbot.bot.get_channel(lottery_data['channel_id'])
        message = await utils.try_get_message(
            channel,
            lottery_data['message_id'],
            error=exceptions.MissingMessage(lottery_data['message_id'])
            if raise_if_not_found else None)
        emoji = utils.try_get_emoji(
            lottery_data['emoji_code'], zbot.bot.emojis,
            error=None)  # TODO cancel lottery if not found
        nb_winners = lottery_data['nb_winners']
        time = converter.from_timestamp(lottery_data['next_run_time'])
        organizer = zbot.bot.get_user(lottery_data['organizer_id'])

        return message, channel, emoji, nb_winners, time, organizer
Пример #5
0
    async def report_recruitment(self,
                                 context,
                                 target: typing.Union[discord.Member, int],
                                 *,
                                 options=""):
        recruitment_channel = self.guild.get_channel(
            self.RECRUITMENT_CHANNEL_ID)
        if isinstance(target, discord.Member):
            author = target
            all_recruitment_announces = await recruitment_channel.history(
                oldest_first=False).flatten()
            recruitment_announces = list(
                filter(lambda a: a.author == author,
                       all_recruitment_announces))
            last_recruitment_announce = recruitment_announces and recruitment_announces[
                0]
        else:
            announce_id = target
            last_recruitment_announce = await utils.try_get_message(
                recruitment_channel,
                announce_id,
                error=exceptions.MissingMessage(announce_id))
            recruitment_announces = [last_recruitment_announce]
            author = last_recruitment_announce.author
        clear = utils.is_option_enabled(options, 'clear')

        if not last_recruitment_announce:
            raise exceptions.MissingMessage(missing_message_id=None)

        # Run checks
        patched_context = copy(context)
        patched_context.send = self.mock_send
        self.send_buffer.clear()
        await self.check_authors_clan_contact_role(patched_context, [last_recruitment_announce]) \
            or self.send_buffer.pop()
        await self.check_recruitment_announces_uniqueness(patched_context, recruitment_announces) \
            or self.send_buffer.pop()
        await self.check_recruitment_announces_length(patched_context, [last_recruitment_announce]) \
            or self.send_buffer.pop()
        await self.check_recruitment_announces_embeds(patched_context, [last_recruitment_announce]) \
            or self.send_buffer.pop()
        await self.check_recruitment_announces_timespan(
            patched_context, recruitment_channel,
            [last_recruitment_announce]) or self.send_buffer.pop()

        if not self.send_buffer:
            await context.send(
                f"L'annonce ne présente aucun problème. :ok_hand: ")
        else:
            # DM author
            await utils.try_dm(
                author,
                f"Bonjour. Il a été détecté que ton annonce de recrutement ne respectait pas le "
                f"règlement du serveur. Voici un rapport de l'analyse effectuée: \n _ _"
            )
            await utils.try_dms(author, self.send_buffer, group_in_blocks=True)
            await utils.try_dm(
                author, f"_ _ \n"
                f"En attendant que le problème soit réglé, ton annonce a été supprimée.\n"
                f"En cas de besoin, tu peux contacter {context.author.mention} qui a reçu une copie du "
                f"rapport d'analyse.\n"
                f"Pour éviter ce genre de désagrément, vérifie que ton annonce respecte le règlement en utilisant la "
                f"commande `+valider annonce` dans le canal <#557870289292230666>.\n _ _"
            )
            await utils.try_dm(
                author, f"Copie du contenu de l'annonce:\n _ _ \n"
                f">>> {last_recruitment_announce.content}")

            # DM moderator
            await utils.try_dm(
                context.author,
                f"Rapport d'analyse envoyé à {author.mention}: \n _ _")
            await utils.try_dms(context.author,
                                self.send_buffer,
                                group_in_blocks=True)
            await utils.try_dm(
                context.author, f"_ _ \n"
                f"Copie du contenu de l'annonce:\n _ _ \n"
                f">>> {last_recruitment_announce.content}")

            # Delete announce
            await last_recruitment_announce.delete()
            await context.send(
                f"L'annonce a été supprimée et un rapport envoyé par MP. :ok_hand: "
            )

            # Clear announce tracking records
            if clear:
                await self.clear_recruitment(context, author)
Пример #6
0
            f"Date et heure du sondage d'identifiant `{poll_id}` changées pour le "
            f"`{converter.to_human_format(time)}` : <{message.jump_url}>")

    @staticmethod
    async def get_message_env(poll_id: int, raise_if_not_found=True) -> \
            (discord.Message, discord.TextChannel, typing.List[typing.Union[str, discord.Emoji]],
             bool, bool, datetime.datetime, discord.Member):
        if not (poll_data := discord.utils.find(
                lambda data: data['poll_id'] == poll_id,
                Poll.pending_polls.values())):
            raise exceptions.UnknownPoll(poll_id)
        channel = zbot.bot.get_channel(poll_data['channel_id'])
        message = await utils.try_get_message(
            channel,
            poll_data['message_id'],
            error=exceptions.MissingMessage(poll_data['message_id'])
            if raise_if_not_found else None)
        emoji_list = []
        for emoji_code in poll_data['emoji_codes']:
            emoji = utils.try_get_emoji(zbot.bot.emojis,
                                        emoji_code,
                                        error=None)
            if emoji:
                emoji_list.append(emoji)
            else:
                logger.warning(
                    f"Custom emoji with id `{emoji_code}` not found.")
        is_exclusive = poll_data['is_exclusive']
        required_role_name = poll_data['required_role_name']
        time = converter.from_timestamp(poll_data['next_run_time'])
        organizer = zbot.bot.get_user(poll_data['organizer_id'])
Пример #7
0
            f"`{converter.humanize_datetime(time)}` : <{message.jump_url}>"
        )

    @staticmethod
    async def get_message_env(poll_id: int, raise_if_not_found=True) -> \
            (discord.Message, discord.TextChannel, typing.List[typing.Union[str, discord.Emoji]],
             bool, bool, datetime.datetime, discord.Member):
        if not (poll_data := discord.utils.find(
                lambda data: data['poll_id'] == poll_id,
                Poll.pending_polls.values()
        )):
            raise exceptions.UnknownPoll(poll_id)
        channel = zbot.bot.get_channel(poll_data['channel_id'])
        message = await utils.try_get_message(
            channel, poll_data['message_id'],
            error=exceptions.MissingMessage(poll_data['message_id']) if raise_if_not_found else None
        )
        emoji_list = []
        for emoji_code in poll_data['emoji_codes']:
            emoji = utils.try_get_emoji(zbot.bot.emojis, emoji_code, error=None)
            if emoji:
                emoji_list.append(emoji)
            else:
                logger.warning(f"Custom emoji with id `{emoji_code}` not found.")
        is_exclusive = poll_data['is_exclusive']
        required_role_name = poll_data['required_role_name']
        time = converter.from_timestamp(poll_data['next_run_time'])
        organizer = zbot.bot.get_user(poll_data['organizer_id'])
        return message, channel, emoji_list, is_exclusive, required_role_name, time, organizer

    @staticmethod