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}>")
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}>" )
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}>" )
async def automessage_add(self, context, channel: discord.TextChannel, message: str): if not context.author.permissions_in(channel).send_messages: raise exceptions.ForbiddenChannel(channel) automessage_id = self.get_next_automessage_id() zbot.db.insert_automessage(automessage_id, message, channel) await context.send( f"Message automatique d'identifiant `{automessage_id}` créé et lié au canal {channel.mention}." )
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}>." )
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}>" )
async def automessage_edit_channel(self, context, automessage_id: int, channel: discord.TextChannel): automessages_data = zbot.db.load_automessages({'automessage_id': automessage_id}, ['_id']) if not automessages_data: raise exceptions.UnknownAutomessage(automessage_id) if not context.author.permissions_in(channel).send_messages: raise exceptions.ForbiddenChannel(channel) document_id = automessages_data[0]['_id'] zbot.db.update_automessages({document_id: {'channel_id': channel.id}}) await context.send(f"Message automatique d'identifiant {automessage_id} lié au canal {channel.mention}.")
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)
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}>" )
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)
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)
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}>." )