Exemple #1
0
    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}>")
Exemple #2
0
    async def announce(self,
                       context: commands.Context,
                       poll_id: int,
                       announce: str,
                       *,
                       options=""):
        message, channel, _, _, _, _, organizer = await self.get_message_env(
            poll_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 = utils.is_option_enabled(options, 'do-announce')
        do_pin = utils.is_option_enabled(options, 'pin')
        if do_announce or do_pin:
            checker.has_any_mod_role(context, print_error=True)

        prefixed_announce = utils.make_announce(
            context.guild, announce, do_announce and self.ANNOUNCE_ROLE_NAME)
        if do_pin and not message.pinned:
            await message.pin()
        elif not do_pin and message.pinned:
            await message.unpin()
        await message.edit(content=prefixed_announce)
        await context.send(
            f"Annonce du sondage d'identifiant `{poll_id}` remplacée par "
            f"\"`{message.clean_content}`\" : <{message.jump_url}>")
Exemple #3
0
    async def organizer(
            self, context: commands.Context,
            poll_id: int,
            organizer: discord.User
    ):
        message, channel, emoji_list, is_exclusive, required_role_name, time, previous_organizer = \
            await self.get_message_env(poll_id, raise_if_not_found=True)

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

        embed = self.build_announce_embed(
            message.embeds[0].description, is_exclusive, required_role_name, organizer, time,
            self.guild.roles
        )
        await message.edit(embed=embed)

        job_id = self.pending_polls[message.id]['_id']
        poll_data = {'organizer_id': organizer.id}
        zbot.db.update_job_data(self.JOBSTORE, job_id, poll_data)
        self.pending_polls[message.id].update(poll_data)
        await context.send(
            f"Organisateur du sondage d'identifiant `{poll_id}` remplacé par "
            f"`@{organizer.display_name}` : <{message.jump_url}>"
        )
Exemple #4
0
    async def time(
            self, context: commands.Context,
            poll_id: int,
            time: converter.to_datetime
    ):
        message, channel, emoji_list, is_exclusive, required_role_name, _, organizer = \
            await self.get_message_env(poll_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)
        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(
            message.embeds[0].description, is_exclusive, required_role_name, organizer, time,
            self.guild.roles
        )
        await message.edit(embed=embed)

        job_id = self.pending_polls[message.id]['_id']
        scheduler.reschedule_stored_job(job_id, time)  # Also updates the next_run_time in db
        poll_data = {'next_run_time': converter.to_timestamp(time)}
        self.pending_polls[message.id].update(poll_data)
        await context.send(
            f"Date et heure du sondage d'identifiant `{poll_id}` changées pour le "
            f"`{converter.humanize_datetime(time)}` : <{message.jump_url}>"
        )
Exemple #5
0
    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}>")
Exemple #6
0
    async def emojis(self,
                     context: commands.Context,
                     poll_id: int,
                     emoji_list: converter.to_emoji_list,
                     *,
                     options=""):
        message, channel, previous_emoji_list, is_exclusive, required_role_name, time, organizer = \
            await self.get_message_env(poll_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)
        if not emoji_list:
            raise commands.MissingRequiredArgument(
                context.command.params['emoji_list'])
        if len(emoji_list) > 20:
            raise exceptions.OversizedArgument(f"{len(emoji_list)} emojis",
                                               "20 emojis")
        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)

        is_exclusive = utils.is_option_enabled(options, 'exclusive')
        previous_reactions = [
            utils.try_get(message.reactions,
                          error=exceptions.MissingEmoji(previous_emoji),
                          emoji=previous_emoji)
            for previous_emoji in previous_emoji_list
        ]
        for previous_reaction in previous_reactions:
            await previous_reaction.remove(zbot.bot.user)
        for emoji in emoji_list:
            await message.add_reaction(emoji)
        embed = self.build_announce_embed(message.embeds[0].description,
                                          is_exclusive, required_role_name,
                                          organizer, time, self.guild.roles)
        await message.edit(embed=embed)

        job_id = self.pending_polls[message.id]['_id']
        poll_data = {
            'emoji_codes':
            list(map(lambda e: e if isinstance(e, str) else e.id, emoji_list)),
            'is_exclusive':
            is_exclusive,
            'required_role_name':
            required_role_name
        }
        zbot.db.update_poll_data(job_id, poll_data)
        self.pending_polls[message.id].update(poll_data)
        await context.send(
            f"Émojis du sondage d'identifiant `{poll_id}` mis à jour : <{message.jump_url}>"
        )
Exemple #7
0
    async def assess(self, context: commands.Context, poll_id: int):
        message, _, _, _, _, _, organizer = await self.get_message_env(
            poll_id, raise_if_not_found=True
        )

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

        await Poll.close_poll(message.id, manual_run=True)
        await context.send(f"Sondage d'identifiant `{poll_id}` clôturé : <{message.jump_url}>")
Exemple #8
0
    async def help(self, context, *, args: str = ""):
        max_nest_level = utils.get_option_value(args, 'nest')
        if max_nest_level:
            try:
                max_nest_level = int(max_nest_level)
            except ValueError:
                raise exceptions.MisformattedArgument(max_nest_level,
                                                      "valeur entière")
        else:
            max_nest_level = self.DEFAULT_HELP_NEST_LEVEL
        full_command_name = utils.remove_option(args, 'nest')

        if not full_command_name:  # No command specified
            if max_nest_level < 1:
                raise exceptions.UndersizedArgument(max_nest_level, 1)
            await self.display_generic_help(context, max_nest_level)
        else:  # Request help for commands matching the given pattern
            command_name = full_command_name.split(' ')[-1]
            command_chain = full_command_name.split(' ')[:-1]
            matching_commands = utils.get_commands(context, command_chain,
                                                   command_name)
            if not matching_commands:
                raise exceptions.UnknownCommand(command_name)
            else:
                # Don't show the helper of all matching commands if one matches exactly
                if exactly_matching_commands := set(
                        filter(lambda c: c.qualified_name == full_command_name,
                               matching_commands)):
                    matching_commands = exactly_matching_commands

                # Don't show an error for missing permissions if there is at least one public command
                public_commands = list(
                    filter(lambda c: not c.hidden, matching_commands))
                if len(public_commands) < len(
                        matching_commands):  # At least one command is hidden
                    try:  # Don't print the error right away
                        checker.has_any_mod_role(context)
                    except exceptions.MissingRoles as error:
                        if not public_commands:  # All commands requires permissions
                            raise error  # Print the error
                        else:  # At least one command is public
                            matching_commands = public_commands  # Filter out hidden commands

                # Show the helper of matching commands
                sorted_matching_commands = sorted(
                    matching_commands, key=lambda c: c.qualified_name)
                for command in sorted_matching_commands:
                    if isinstance(command, commands.Group):
                        await self.display_group_help(context, command,
                                                      max_nest_level)
                    else:
                        await self.display_command_help(context, command)
Exemple #9
0
    async def switch(self,
                     context,
                     dest_channel: discord.TextChannel,
                     *,
                     options=""):
        if context.channel == dest_channel \
                or not context.author.permissions_in(dest_channel).send_messages:
            raise exceptions.ForbiddenChannel(dest_channel)
        number_option = utils.get_option_value(options, 'number')
        if number_option is not None:  # Value assigned
            try:
                messages_number = int(number_option)
            except ValueError:
                raise exceptions.MisformattedArgument(number_option,
                                                      "valeur entière")
            if messages_number < 0:
                raise exceptions.UndersizedArgument(messages_number, 0)
            elif messages_number > 10 and not checker.has_any_mod_role(
                    context, print_error=False):
                raise exceptions.OversizedArgument(messages_number, 10)
        elif utils.is_option_enabled(options, 'number',
                                     has_value=True):  # No value assigned
            raise exceptions.MisformattedArgument(number_option,
                                                  "valeur entière")
        else:  # Option not used
            messages_number = 3
        do_ping = utils.is_option_enabled(options, 'ping')
        do_delete = utils.is_option_enabled(options, 'delete')
        if do_ping or do_delete:
            checker.has_any_mod_role(context, print_error=True)

        messages = await context.channel.history(limit=messages_number + 1
                                                 ).flatten()
        messages.reverse()  # Order by oldest first
        messages.pop()  # Remove message used for the command

        await context.message.delete()
        if not do_delete:
            await context.send(
                f"**Veuillez basculer cette discussion dans le canal {dest_channel.mention} qui "
                f"serait plus approprié !** 🧹")
        else:
            await context.send(
                f"**La discussion a été déplacée dans le canal {dest_channel.mention} !** "
                f"({len(messages)} messages supprimés) 🧹")
        if messages_number != 0:
            await dest_channel.send(
                f"**Suite de la discussion de {context.channel.mention}** 💨"
            )
        await self.move_messages(messages, dest_channel, do_ping, do_delete)
Exemple #10
0
    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}>"
        )
Exemple #11
0
    async def description(self, context: commands.Context, poll_id: int, description: str):
        message, channel, _, is_exclusive, required_role_name, time, organizer = \
            await self.get_message_env(poll_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)

        embed = self.build_announce_embed(
            description, is_exclusive, required_role_name, organizer, time, self.guild.roles)
        await message.edit(embed=embed)
        await context.send(
            f"Description du sondage d'identifiant `{poll_id}` remplacée par "
            f"\"`{description}`\" : <{message.jump_url}>"
        )
Exemple #12
0
    async def display_group_help(context,
                                 group,
                                 max_nest_level=DEFAULT_HELP_NEST_LEVEL):
        # Fetch visible subcommands
        command_list = Bot.get_command_list(group, max_nest_level)
        authorized_command_list = list(
            filter(
                lambda c: not c.hidden or checker.has_any_mod_role(
                    context, print_error=False), command_list))
        sorted_group_commands = sorted(authorized_command_list,
                                       key=lambda c: c.qualified_name)

        # Compute generic command header
        parent = group.full_parent_name
        embed_description = f"**Description** : {group.brief}" if group.brief else ""
        embed_description += ("\n**Alias** : " + ", ".join([
            f"`+{(parent + ' ') if parent else ''}{alias}`"
            for alias in group.aliases
        ])) if group.aliases else ""

        # Append group helper
        embed_description += "\n\n" + "\n".join([
            f"• `+{command}` : {command.brief}"
            for command in sorted_group_commands
        ])
        embed = discord.Embed(title=f"Commande `+{group}`",
                              description=embed_description,
                              color=Bot.EMBED_COLOR)
        await context.send(embed=embed)
Exemple #13
0
    async def display_group_help(context, group, max_nest_level):
        if group.hidden:
            checker.has_any_mod_role(context, print_error=True)

        command_list = Bot.get_command_list(group, max_nest_level)
        authorized_command_list = list(
            filter(
                lambda c: not c.hidden or checker.has_any_mod_role(
                    context, print_error=False), command_list))
        sorted_group_commands = sorted(authorized_command_list,
                                       key=lambda c: c.name)
        embed = discord.Embed(title=group.cog.DISPLAY_NAME,
                              description="\n".join([
                                  f"• `+{command}` : {command.brief}"
                                  for command in sorted_group_commands
                              ]),
                              color=Bot.EMBED_COLOR)
        await context.send(embed=embed)
Exemple #14
0
    async def display_command_help(context, command):
        if command.hidden:
            checker.has_any_mod_role(context)

        parent = command.full_parent_name
        embed_description = f"**Description** : {command.brief}" if command.brief else ""
        embed_description += ("\n**Alias** : " + ", ".join([
            f"`+{(parent + ' ') if parent else ''}{alias}`"
            for alias in command.aliases
        ])) if command.aliases else ""
        if command.usage:
            embed_description += f"\n**Arguments** : `{command.usage}`"
            embed_description += "\n**Légende** : `<arg>` = obligatoire ; `[arg]` = facultatif ; " \
                                 "`\"arg\"` = argument devant être entouré de guillemets"
        embed_description += f"\n\n{command.help}" if command.help else ""
        embed = discord.Embed(title=f"Commande `+{command}`",
                              description=embed_description,
                              color=Bot.EMBED_COLOR)
        await context.send(embed=embed)
Exemple #15
0
    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}>")
Exemple #16
0
 async def cancel(self, context: commands.Context, poll_id: int):
     message, _, emoji_list, _, _, _, organizer = await self.get_message_env(
         poll_id, raise_if_not_found=False)
     if context.author != organizer:
         checker.has_any_mod_role(context, print_error=True)
     if message:
         for emoji in emoji_list:
             reaction = utils.try_get(
                 message.reactions, error=exceptions.MissingEmoji(emoji), emoji=emoji
             )
             await reaction.remove(zbot.bot.user)
         embed = discord.Embed(
             title=f"Sondage __annulé__ par {context.author.display_name}",
             description=message.embeds[0].description if message.embeds[0].description else "",
             color=Poll.EMBED_COLOR
         )
         embed.set_author(name=f"Organisateur : @{organizer.display_name}", icon_url=organizer.avatar_url)
         await message.edit(embed=embed)
         await message.unpin()
     self.remove_pending_poll(message.id, cancel_job=True)
     await context.send(f"Sondage d'identifiant `{poll_id}` annulé : <{message.jump_url}>")
Exemple #17
0
    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}>")
Exemple #18
0
    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}>"
        )
Exemple #19
0
 async def display_generic_help(context, max_nest_level):
     bot_display_name = await Bot.get_bot_display_name(
         context.bot.user, context.guild)
     embed = discord.Embed(title=f"Commandes de @{bot_display_name}",
                           color=Bot.EMBED_COLOR)
     commands_by_cog = {}
     for command in Bot.get_command_list(context.bot, max_nest_level):
         if not command.hidden or checker.has_any_mod_role(
                 context, print_error=False):
             commands_by_cog.setdefault(command.cog, []).append(command)
     for cog in sorted(commands_by_cog, key=lambda c: c.DISPLAY_SEQUENCE):
         sorted_cog_commands = sorted(commands_by_cog[cog],
                                      key=lambda c: c.name)
         embed.add_field(name=cog.DISPLAY_NAME,
                         value="\n".join([
                             f"• `+{command}` : {command.brief}"
                             for command in sorted_cog_commands
                         ]),
                         inline=False)
     embed.set_footer(
         text="Utilisez +help <commande> pour plus d'informations")
     await context.send(embed=embed)
Exemple #20
0
    async def check_recruitments(
            self, context,
            after: converter.to_datetime = converter.to_datetime('1970-01-01'),
            limit: int = 100,
            add_reaction=True):
        if limit < 1:
            raise exceptions.UndersizedArgument(limit, 1)
        if (utils.get_current_time() - after).total_seconds() <= 0:
            argument_size = converter.humanize_datetime(after)
            max_argument_size = converter.humanize_datetime(utils.get_current_time())
            raise exceptions.OversizedArgument(argument_size, max_argument_size)

        add_reaction and await context.message.add_reaction(self.WORK_IN_PROGRESS_EMOJI)

        recruitment_channel = self.guild.get_channel(self.RECRUITMENT_CHANNEL_ID)
        recruitment_announces = await recruitment_channel.history(
            after=after.replace(tzinfo=None),
            limit=limit,
            oldest_first=False  # Search in reverse in case the filters limit the results
        ).flatten()
        recruitment_announces.reverse()  # Reverse again to have oldest match in first place
        recruitment_announces = list(filter(
            lambda a: not checker.has_any_mod_role(context, a.author, print_error=False)  # Ignore moderation messages
            and not a.pinned  # Ignore pinned messages
            and not a.type.name == 'pins_add',  # Ignore pin notifications
            recruitment_announces
        ))

        await self.check_authors_clan_contact_role(context, recruitment_announces)
        await self.check_recruitment_announces_uniqueness(context, recruitment_announces)
        await self.check_recruitment_announces_length(context, recruitment_announces)
        await self.check_recruitment_announces_embeds(context, recruitment_announces)
        await self.check_recruitment_announces_timespan(context, recruitment_channel, recruitment_announces)

        add_reaction and await context.message.remove_reaction(self.WORK_IN_PROGRESS_EMOJI, self.user)
        add_reaction and await context.message.add_reaction(self.WORK_DONE_EMOJI)
Exemple #21
0
    async def start(self,
                    context: commands.Context,
                    announce: str,
                    description: str,
                    dest_channel: discord.TextChannel,
                    emoji_list: converter.to_emoji_list,
                    time: converter.to_future_datetime,
                    *,
                    options=""):
        # Check arguments
        if not context.author.permissions_in(dest_channel).send_messages:
            raise exceptions.ForbiddenChannel(dest_channel)
        if not emoji_list:
            raise commands.MissingRequiredArgument(
                context.command.params['emoji_list'])
        if len(emoji_list) > 20:
            raise exceptions.OversizedArgument(f"{len(emoji_list)} emojis",
                                               "20 emojis")
        do_announce = utils.is_option_enabled(options, 'do-announce')
        do_pin = utils.is_option_enabled(options, 'pin')
        if do_announce or do_pin:
            checker.has_any_mod_role(context, print_error=True)
        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)

        # Run command
        is_exclusive = utils.is_option_enabled(options, 'exclusive')
        organizer = context.author
        prefixed_announce = utils.make_announce(
            context.guild, announce, do_announce and self.ANNOUNCE_ROLE_NAME)
        embed = self.build_announce_embed(description, is_exclusive,
                                          required_role_name, organizer, time,
                                          self.guild.roles)
        message = await dest_channel.send(prefixed_announce, embed=embed)
        for emoji in emoji_list:
            await message.add_reaction(emoji)
        if do_pin:
            await message.pin()

        # Register data
        job_id = scheduler.schedule_stored_job(
            zbot.db.PENDING_POLLS_COLLECTION, time, self.close_poll,
            message.id).id
        poll_data = {
            'poll_id':
            self.get_next_poll_id(),
            'message_id':
            message.id,
            'channel_id':
            dest_channel.id,
            'emoji_codes':
            list(map(lambda e: e if isinstance(e, str) else e.id, emoji_list)),
            'organizer_id':
            organizer.id,
            'is_exclusive':
            is_exclusive,
            'required_role_name':
            required_role_name,
        }
        zbot.db.update_poll_data(job_id, poll_data)
        # Add data managed by scheduler later to avoid updating the database with them
        poll_data.update({
            '_id': job_id,
            'next_run_time': converter.to_timestamp(time)
        })
        self.pending_polls[message.id] = poll_data

        # Confirm command
        await context.send(
            f"Sondage d'identifiant `{poll_data['poll_id']}` programmé : <{message.jump_url}>."
        )
Exemple #22
0
    async def inspect_recruitment(self,
                                  context,
                                  member: typing.Union[discord.Member,
                                                       str] = None,
                                  *,
                                  options=''):
        """Post the status of the recruitment announces monitoring."""
        if isinstance(member,
                      str):  # Option mistakenly captured as member name
            options += f" {member}"
            member = None
        require_contact_role = not utils.is_option_enabled(options, 'all')
        recruitment_channel = self.guild.get_channel(
            self.RECRUITMENT_CHANNEL_ID)
        zbot.db.update_recruitment_announces(
            await recruitment_channel.history().flatten())

        # Get the record of each author's last announce (deleted or not)
        author_last_announce_data = {}
        for announce_data in zbot.db.load_recruitment_announces_data(
                query={'author': member.id} if member else {},
                order=[('time', -1)]):
            # Associate each author with his/her last announce data
            if announce_data['author'] not in author_last_announce_data:
                author_last_announce_data[announce_data['author']] = {
                    'last_announce_time': announce_data['time'],
                    'message_id': announce_data['_id']
                }

        # Enhance announces data with additional information
        min_timespan = datetime.timedelta(
            # Apply a tolerance of 2 days for players interpreting the 30 days range as "one month".
            # This is a subtraction because the resulting value is the number of days to wait before posting again.
            days=self.MIN_RECRUITMENT_ANNOUNCE_TIMESPAN -
            self.RECRUITMENT_ANNOUNCE_TIMESPAN_TOLERANCE)
        today = utils.bot_tz_now()
        for author_id, announce_data in author_last_announce_data.items():
            last_announce_time_localized = converter.to_utc(
                announce_data['last_announce_time'])
            next_announce_time_localized = last_announce_time_localized + min_timespan
            author_last_announce_data[author_id] = {
                'last_announce_time': last_announce_time_localized,
                'next_announce_time': next_announce_time_localized,
                'is_time_elapsed': next_announce_time_localized <= today
            }

        # Bind the member to the announce data, filter, and order by date asc
        member_announce_data = {
            self.guild.get_member(author_id): _
            for author_id, _ in author_last_announce_data.items()
        }
        filtered_member_announce_data = {
            author: _
            for author, _ in member_announce_data.items()
            if author is not None  # Still member of the server
            and
            not checker.has_any_mod_role(context, author, print_error=False
                                         )  # Ignore moderation messages
            and (not require_contact_role
                 or checker.has_role(author, Stats.CLAN_CONTACT_ROLE_NAME))
        }
        ordered_member_announce_data = sorted(
            filtered_member_announce_data.items(),
            key=lambda elem: elem[1]['last_announce_time'])

        # Post the status of announces data
        if ordered_member_announce_data:
            await context.send("Statut du suivi des annonces de recrutement :")
            for block in utils.make_message_blocks([
                    f"• {author.mention} : {converter.to_human_format(announce_data['last_announce_time'])} "
                    +
                ("✅" if announce_data['is_time_elapsed'] else
                 f"⏳ (→ {converter.to_human_format(announce_data['next_announce_time'])})"
                 ) for author, announce_data in ordered_member_announce_data
            ]):
                await context.send(block)
        else:
            await context.send("Aucun suivi enregistré.")