Exemplo n.º 1
0
    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}>."
        )
Exemplo n.º 2
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}>")
Exemplo n.º 3
0
Arquivo: server.py Projeto: Zedd7/ZBot
    async def parse_time_arguments(options, default_days_number=30):
        # Check arguments
        time_option = utils.get_option_value(options, 'time')
        if time_option is not None:  # Value assigned
            try:
                days_number = int(time_option)
            except ValueError:
                raise exceptions.MisformattedArgument(time_option,
                                                      "valeur entière")
            if days_number < 1:
                raise exceptions.UndersizedArgument(days_number, 1)
            elif days_number > 1000:
                raise exceptions.OversizedArgument(days_number, 1000)
        elif utils.is_option_enabled(options, 'time',
                                     has_value=True):  # No value assigned
            raise exceptions.MisformattedArgument(time_option,
                                                  "valeur entière")
        else:  # Option not used
            days_number = default_days_number

        # Determine granularity
        granularity = None
        for granularity_option in ('hour', 'day', 'month', 'year'):
            if utils.is_option_enabled(options, granularity_option):
                granularity = granularity_option
                break
        if not granularity:
            granularity = 'hour' if days_number < Server.HOURS_GRANULARITY_LIMIT \
                else 'day' if days_number < Server.DAYS_GRANULARITY_LIMIT \
                else 'month' if days_number < Server.MONTHS_GRANULARITY_LIMIT \
                else 'year'
        return days_number, granularity
Exemplo n.º 4
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 = Bot.MAX_COMMAND_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 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:
             sorted_matching_commands = sorted(matching_commands,
                                               key=lambda c: c.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)
Exemplo n.º 5
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}>"
        )
Exemplo n.º 6
0
Arquivo: bot.py Projeto: Zedd7/ZBot
    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)
Exemplo n.º 7
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)
Exemplo n.º 8
0
    async def check_recruitments(
            self,
            context,
            after: converter.to_datetime = converter.to_datetime('1970-01-01'),
            limit: int = 100):
        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)

        await context.message.add_reaction(self.WORK_IN_PROGRESS_EMOJI)

        recruitment_channel = context.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()  # Put back 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_announces)

        await context.message.remove_reaction(self.WORK_IN_PROGRESS_EMOJI,
                                              self.user)
        await context.message.add_reaction(self.WORK_DONE_EMOJI)
Exemplo n.º 9
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}>")
Exemplo n.º 10
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)
Exemplo n.º 11
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_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'])
        for emoji in emoji_list:
            if isinstance(emoji, str) and emojis.emojis.count(emoji) != 1:
                raise exceptions.ForbiddenEmoji(emoji)
        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)
        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(self.JOBSTORE, 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_job_data(self.JOBSTORE, 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}>.")
        await context.send(f"Sondage démarré.")