async def my_lists(self, ctx: commands.Context): """ Show the lists the current user is subscribed to. :param ctx: The current context. (discord.ext.commands.Context) """ guild_data = await get_guild_data(ctx.message.guild.id) subbed_lists = [] # Error if no lists exist yet if not guild_data.notification_lists: msg = translate("no_existing_lists", await culture(ctx)) return await ctx.send(msg) # Fetch the lists the author is subscribed to for list_name, list_data in guild_data.notification_lists.items(): if ctx.author.id in list_data["users"]: subbed_lists.append(list_name) # Error if the author is not subscribed to any lists if len(subbed_lists) < 1: msg = translate("no_subscriptions_error", await culture(ctx)) return await ctx.send(msg) # Show the user his lists msg = translate("your_lists_title", await culture(ctx)) + "\n - " + "\n - ".join( sorted(subbed_lists)) await ctx.send(msg)
async def command_help(ctx: commands.Context, command_name: str): """ Builds and sends the help info on the given command to the current context. :param ctx: The current context. (discord.ext.commands.Context) :param command_name: The command to request help on. (str) """ # TODO print aliases embed = discord.Embed() command = ctx.bot.get_command(str(command_name)) title = ctx.prefix + command.name help_text = translate(command.help, await culture(ctx)) if command.usage is None: title += '\n' embed.add_field(name=title, value=help_text, inline=False) return await send_embed(ctx, embed) title += '\u2000' + translate(command.usage, await culture(ctx)) + '\n' embed.add_field(name=title, value=help_text, inline=False) extra_info = translate("help_info_arguments", await culture(ctx)) embed.add_field(name=empty, value=extra_info, inline=False) await send_embed(ctx, embed)
async def remove_youtube_channel(self, ctx: commands.Context, youtube_channel_id: str): """ Remove a Youtube channel that was being notified :param ctx: The current context. (discord.ext.commands.Context) :param youtube_channel_id: The Youtube channel to be notified of (str) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) remove_response = await guild_data.remove_youtube_channel( youtube_channel_id) msg = "" if remove_response: msg = translate("youtube_removed", await culture(ctx)).format(youtube_channel_id) else: msg = translate("youtube_no_exists", await culture(ctx)).format(youtube_channel_id) info(msg) await ctx.send(msg)
async def act_unsubscribe(self, ctx: commands.Context, list_name: str, user_id: int): """ Unsubscribes the user from the provided list :param ctx: The current context. (discord.ext.commands.Context) :param list_name: The list to unsubscribe from. (str) :param user_id: the user to unsubscribe (int) """ # make sure list is lowercase list_name = list_name.lower() guild_data = await get_guild_data(ctx.message.guild.id) # Error if list does not exist if not guild_data.does_list_exist(list_name): msg = translate("list_err_does_not_exit", await culture(ctx)) return await ctx.send(msg) # Unsubscribe user and error if failed if not await guild_data.unsub_user(list_name, user_id): msg = translate("list_err_not_subscribed", await culture(ctx)).format(str(user_id), list_name) return await ctx.send(msg) # Unsubscribe successful, show result to user msg = translate("list_unsubscribed", await culture(ctx)).format(str(user_id), list_name) await ctx.send(msg)
async def list_purger_channels(self, ctx: commands.Context): """ List all purger channels that are being monitored """ guild_data = await get_guild_data(ctx.message.guild.id) msg = translate("purger_list_title", await culture(ctx)) for text_channel, max_age in guild_data.purgers.items(): msg = (msg + "\n" + translate("purger_list_item", await culture(ctx)).format( text_channel, max_age)) await ctx.send(msg)
async def add_youtube_channel(self, ctx: commands.Context, youtube_channel_id: str, text_channel: str): """ Add a Youtube channel to be notified :param ctx: The current context. (discord.ext.commands.Context) :param youtube_channel_id: The Youtube channel to be notified of (str) :param text_channel: The text channel that will receive the notification (str) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) # TODO: throw specific error with message when channel ID is wrong latest_video = await get_latest_video(youtube_channel_id) # Get the channel channel = get_channel(ctx, text_channel) # TODO: Give information to the user when the text channel does not exist if not channel: await ctx.channel.send( translate("membercount_channel_nonexistant", await culture(ctx))) raise Exception("Invalid text channel provided") if isinstance(channel, discord.VoiceChannel): await ctx.channel.send( translate("channel_is_voice", await culture(ctx))) return add_response = await guild_data.add_youtube_channel( youtube_channel_id, channel, latest_video["video_id"]) msg = "" if add_response: msg = translate("youtube_added", await culture(ctx)).format(youtube_channel_id, channel) else: msg = translate("youtube_exists", await culture(ctx)).format(youtube_channel_id) info(msg) await ctx.send(msg)
async def on_command_error(self, ctx: commands.Context, error: Any): """ Handles any errors thrown by the commands. :param ctx: The current context. (discord.ext.commands.Context) :param error: The current error. (Any) """ # Log the error log_error(error) # Notify user for MissingRequiredArgument errors if isinstance(error, commands.MissingRequiredArgument): command_name = ctx.message.content.split(" ")[0] msg = translate("err_missing_parameter", await culture(ctx)).format(command_name, error.param.name) return await ctx.send(msg) # Notify user with general error msg = translate("err_unrecognized_command", await culture(ctx)) await ctx.send(msg)
async def add_admin(self, ctx: commands.Context): """ Add a new bot admin. :param ctx: The current context. (discord.ext.commands.Context) """ guild_data = await get_guild_data(ctx.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) # Error if the command had no mention if not ctx.message.mentions: err = translate("mention_required", await culture(ctx)) return await ctx.send(err) # Fetch user data user_id_to_add = ctx.message.mentions[0].id user_to_add = ctx.guild.get_member(int(user_id_to_add)) user_name_to_add = user_to_add.display_name # Error if the user is a server admin (no point in giving him bot admin rights) if ctx.guild.get_member( int(user_id_to_add)).guild_permissions.administrator: err = translate("bot_admin_error_discord_admin", await culture(ctx)).format(user_name_to_add) return await ctx.send(err) # Error if the user already is a bot admin if ctx.message.mentions[0].id in guild_data.bot_admins: err = translate("bot_admin_error_already_admin", await culture(ctx)).format(user_name_to_add) return await ctx.send(err) # Actually add the user to the admins await guild_data.add_admin(user_id_to_add) # Display success msg = translate("bot_admin_add_success", await culture(ctx)).format(user_name_to_add) await ctx.send(msg)
async def set_language(self, ctx: commands.Context): """ Show the current language, and allow for updates. :param ctx: The current context. (discord.ext.commands.Context) """ # Get guild data guild_data = await get_guild_data(ctx.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) # Get current language current_culture = await culture(ctx) # Show current language msg = translate("current_language", current_culture).format( translate(current_culture, current_culture)) await ctx.send(msg) # Request new language confirmation_message = translate("pick_new_language", current_culture) confirmation_ref = await ctx.send(confirmation_message) for emoji in flags.values(): await confirmation_ref.add_reaction(emoji) # Handle user reaction try: reaction, user = await ctx.bot.wait_for( "reaction_add", check=lambda new_reaction, author: new_reaction.message.id == confirmation_ref.id and author == ctx.message.author, timeout=INTERACT_TIMEOUT, ) # Parse reaction new_language = None for lan in flags.keys(): if flags[lan] == reaction.emoji: new_language = lan break # Update language if found if new_language: await guild_data.update_language(new_language) await confirmation_ref.delete() msg = translate("picked_new_language", new_language).format( translate(new_language, new_language)) return await ctx.send(msg) # Handle timeout except asyncio.TimeoutError: await confirmation_ref.delete() msg = translate("snooze_lose", await culture(ctx)) return await ctx.send(msg)
async def select_random_user(self, ctx: commands.Context, *, channel_name: str = None): """ Selects a random user from the server, channel, or online members. :param ctx: The current context. (discord.ext.commands.Context) :param channel_name: The channel to pick from, 'online' in order to pick an online member from the server. (str) """ # Error for missing parameter if channel_name is None: msg = translate("random_user_no_channel", await culture(ctx)) return await ctx.send(msg) # Choose from online members if requested if channel_name == "online": member = pick_random_online_member(ctx) msg = translate("random_user_chosen", await culture(ctx)).format(member.id) return await ctx.send(msg) # Sanitize channel name channel = get_channel(ctx, channel_name) # Error if channel does not exist if channel is None: msg = translate("membercount_channel_nonexistant", await culture(ctx)) return await ctx.send(msg) # Error if channel empty if len(channel.members) < 1: msg = translate("membercount_empty_channel", await culture(ctx)).format(channel.id) return await ctx.send(msg) # Pick a random user from channel, and report back to user member = random.choice(channel.members) msg = translate("random_user_chosen", await culture(ctx)).format(member.id) await ctx.send(msg)
async def cmd_wombat_pic(self, ctx): wombat_list = [ os.path.join(WOMBATS_DIR_NAME, w) for w in os.listdir(WOMBATS_DIR_NAME) ] if not wombat_list: msg = translate("empty_wombat_list", await culture(ctx)) return await ctx.send(msg) await ctx.send(file=discord.File(random.choice(wombat_list)))
async def admins_bot(self, ctx: commands.Context): """ Show a list of all bot admins. :param ctx: The current context. (discord.ext.commands.Context) """ guild_data = await get_guild_data(ctx.guild.id) # Error if no admins found if not guild_data.bot_admins: msg = translate("bot_admin_err_no_admins", await culture(ctx)) return await ctx.send(msg) # Build list message = translate("bot_admin_list_prefix", await culture(ctx)) for user_id in guild_data.bot_admins: message += f"\n- {ctx.guild.get_member(int(user_id)).display_name}" # Show list await ctx.send(message)
async def general_help(ctx: commands.Context): """ Builds and sends the general help info to the current context. :param ctx: The current context. (discord.ext.commands.Context) """ embed = discord.Embed() for cog_name in ctx.bot.cogs: content = await build_commands_message(ctx.bot.get_cog(cog_name), await culture(ctx)) if len(content) == 0: continue title = build_title(cog_name) embed.add_field(name=title, value=content, inline=False) extra_info_command = translate("help_info_command", await culture(ctx)).format(ctx.prefix) extra_info_category = translate("help_info_category", await culture(ctx)).format(ctx.prefix) extra_info = f'{extra_info_command}\n{extra_info_category}' embed.add_field(name=empty, value=extra_info, inline=False) await send_embed(ctx, embed)
async def remove_purger(self, ctx: commands.Context, text_channel: str): """ Remove a purger channel that was being notified :param ctx: The current context. (discord.ext.commands.Context) :param text_channel: The channel where the purger is attached to (str) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) # Get channel channel = get_channel(ctx, text_channel) # TODO: Give information to the user when the text channel does not exist if not channel: await ctx.channel.send( translate("membercount_channel_nonexistant", await culture(ctx))) raise Exception("Invalid text channel provided") #Give error if the channel is a voice channel if isinstance(channel, discord.VoiceChannel): await ctx.channel.send( translate("channel_is_voice", await culture(ctx))) return remove_response = await guild_data.remove_purger(channel) msg = "" if remove_response: msg = translate("purger_removed", await culture(ctx)).format(str(channel.id)) else: msg = translate("purger_no_exists", await culture(ctx)).format(str(channel.id)) info(msg) await ctx.send(msg)
async def send_embed(ctx: commands.Context, embed: discord.Embed): """ Send embed to the current context :param ctx: The current context. (discord.ext.commands.Context) :param embed: the embed to send back to the current context. (discord.Embed) """ if len(embed) > 0: return await ctx.channel.send(embed=embed) # if we reach here, the embed is empty and we show an error name = empty value = translate("help_err_not_recognized", await culture(ctx)).format(ctx.prefix) embed.add_field(name=name, value=value, inline=False) await ctx.channel.send(ctx, embed)
async def build_commands_message(cog: commands.Cog, current_culture: str) -> str: """ Builds the help info for a certain cog :param cog: The requested cog. (discord.ext.commands.Cog) :param current_culture: The culture in which the help should be generated (str) :return: The help info for the given cog (str) """ message = {} for command in cog.get_commands(): if command.hidden: continue if command.brief is None: message[command.name] = translate(command.help, current_culture) else: message[command.name] = translate(command.brief, current_culture) strings = [] for name in sorted(message): strings.append("*{0}*\n \u2003 {1}\n".format(name, message[name])) return ' '.join(strings)
async def list_youtube_channels(self, ctx: commands.Context): """ List all Youtube channels that are being monitored """ guild_data = await get_guild_data(ctx.message.guild.id) msg = translate("youtube_list_title", await culture(ctx)) for channel_id, channel_data in guild_data.youtube_channels.items(): msg = ( msg + f"\n - Channel `{channel_id}` posts in <#{channel_data['text_channel_id']}>, last video ID: `{channel_data['latest_video_id']}`" ) await ctx.send(msg)
async def remove_list(self, ctx: commands.Context, list_name: str): """ Removes the given list. :param ctx: The current contest. (discord.ext.commands.Context) :param list_name: The list to be removed. (str) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) # Make sure the list name is lowercase list_name = list_name.lower() # Error if list does not exist if not guild_data.does_list_exist(list_name): msg = translate("list_err_does_not_exit", await culture(ctx)) return await ctx.send(msg) # Ask user confirmation msg = translate("confirmation_question", await culture(ctx)) confirmation_ref = await ctx.send(msg) await confirmation_ref.add_reaction(thumbs_up) await confirmation_ref.add_reaction(thumbs_down) # Handle user reaction try: reaction, user = await ctx.bot.wait_for( "reaction_add", check=lambda emoji, author: emoji.message.id == confirmation_ref.id and author == ctx.message.author, timeout=INTERACT_TIMEOUT, ) # Process emoji if reaction.emoji == thumbs_up: await guild_data.remove_notification_list(list_name) msg = translate("remove_list_success", await culture(ctx)).format(list_name) await ctx.send(msg) elif reaction.emoji == thumbs_down: msg = translate("remove_list_cancel", await culture(ctx)).format(list_name) await ctx.send(msg) # Delete message await confirmation_ref.delete() # Handle Timeout except asyncio.TimeoutError: await confirmation_ref.delete() msg = translate("snooze_lose", await culture(ctx)) return await ctx.send(msg)
async def subject_help(ctx: commands.Context, cog_name: str): """ Builds and sends the help info for the given cog to the current context. :param ctx: The current context. (discord.ext.commands.Context) :param cog_name: The subject to request help on. (str) """ embed = discord.Embed() cog = ctx.bot.get_cog(cog_name) content = await build_commands_message(cog, await culture(ctx)) # add subject title title = f'**{cog_name}**\n' embed.add_field(name=title, value=content, inline=False) # add extra info extra_info = translate("help_info_command", await culture(ctx)).format(ctx.prefix) embed.add_field(name=empty, value=extra_info, inline=False) await send_embed(ctx, embed)
async def add_purger(self, ctx: commands.Context, text_channel: str, max_age: int): """ Add a channel to be regularly purged. :param ctx: The current context. (discord.ext.commands.Context) :param text_channel: The text channel that will be purged (str) :param max_age: The max age of messages in days (int) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) text_channel = text_channel.lower() # Get channel channel = get_channel(ctx, text_channel) # TODO: Give information to the user when the text channel does not exist if not channel: await ctx.channel.send( translate("membercount_channel_nonexistant", await culture(ctx))) raise Exception("Invalid text channel provided") #Give error if the channel is a voice channel if isinstance(channel, discord.VoiceChannel): await ctx.channel.send( translate("channel_is_voice", await culture(ctx))) return # member = ctx.get_member(ctx.user.id) channel_permissions = channel.permissions_for(ctx.me) if not (channel_permissions.manage_messages and channel_permissions.read_message_history): return await ctx.send( translate("purger_permissions", await culture(ctx))) add_response = await guild_data.add_purger(channel, max_age) msg = "" if add_response: msg = translate("purger_added", await culture(ctx)).format(str(channel.id), max_age) else: msg = translate("purger_exists", await culture(ctx)).format(str(channel.id)) info(msg) await ctx.send(msg)
async def on_member_join(self, member: discord.Member): """ This function is executed on every member_join event, and logs a message if a certain threshold is passed. :param member: The member that just joined. (discord.Member) """ # Fetch server guild = member.guild # Get member count member_count = guild.member_count # Return if count is no multiple of threshold if member_count % member_notification_trigger != 0: return # Send message to dedicated channel channel = discord.utils.get(guild.channels, name=notification_channel_name) culture = (await get_guild_data(member.guild.id)).culture msg = translate("member_join_count", culture).format(member.guild, member_count) await channel.send(msg)
async def count(self, ctx, *, channel_name=None): """ Count the members in a given channel, the members in the current server, or the online members in the current server. :param ctx: The current context. (discord.ext.commands.Context) :param channel_name: The name of the channel to count, 'online' to count online members, or nothing to count the entire server. (optional - str - default = None) """ if channel_name == "online": online_member_count = count_online_members(ctx) msg = translate("membercount_online_result", await culture(ctx)).format(online_member_count) return await ctx.send(msg) if not channel_name: msg = translate("membercount_server_result", await culture(ctx)).format(len(ctx.guild.members), ctx.guild.name) return await ctx.send(msg) # Sanitize channel name channel = get_channel(ctx, channel_name) if channel is None: msg = translate("membercount_channel_nonexistant", await culture(ctx)) return await ctx.send(msg) if len(channel.members) < 1: msg = translate("membercount_empty_channel", await culture(ctx)).format(channel.id) return await ctx.send(msg) if len(channel.members) == 1: msg = translate("membercount_single_person", await culture(ctx)).format(channel.id) return await ctx.send(msg) msg = translate("membercount_channel_result", await culture(ctx)).format(len(channel.members), channel.id) await ctx.send(msg)
async def remove_admin(self, ctx: commands.Context): """ Remove a bot admin. :param ctx: The current context. (discord.ext.commands.Context) """ guild_data = await get_guild_data(ctx.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) # Error if the command had no mention if not ctx.message.mentions: err = translate("mention_required", await culture(ctx)) return await ctx.send(err) # Fetch user data user_id_to_remove = ctx.message.mentions[0].id user_to_remove = ctx.guild.get_member(int(user_id_to_remove)) user_name_to_remove = user_to_remove.display_name # Error if the user is not a bot admin if user_id_to_remove not in guild_data.bot_admins: err = translate("bot_admin_error_not_admin", await culture(ctx)).format(user_name_to_remove) return await ctx.send(err) # User is trying to revoke his own rights if ctx.author.id == user_id_to_remove: # Ask confirmation confirmation_text = translate("bot_admin_confirm_remove_self", await culture(ctx)) confirmation_ref = await ctx.send(confirmation_text) await confirmation_ref.add_reaction(thumbs_up) await confirmation_ref.add_reaction(thumbs_down) # Handle user reaction try: reaction, user = await ctx.bot.wait_for( "reaction_add", check=lambda new_reaction, author: new_reaction.message.id == confirmation_ref.id and author == ctx.message.author, timeout=INTERACT_TIMEOUT, ) # Process thumbs up if reaction.emoji == thumbs_up: await guild_data.remove_admin(user_id_to_remove) msg = translate("bot_admin_remove_success", await culture(ctx)).format(user_name_to_remove) return await ctx.send(msg) # Process thumbs down if reaction.emoji == thumbs_down: msg = translate("bot_admin_remove_cancel", await culture(ctx)).format(user_name_to_remove) return await ctx.send(msg) # If we reach here, an invalid emoji was used await confirmation_ref.delete() return # Handle timeout except asyncio.TimeoutError: await confirmation_ref.delete() msg = translate("snooze_lose", await culture(ctx)) return await ctx.send(msg) # Authorized user wants to remove another bot admin # Ask confirmation confirmation_text = translate("bot_admin_confirm_remove", await culture(ctx)).format(user_name_to_remove) confirmation_ref = await ctx.send(confirmation_text) await confirmation_ref.add_reaction(thumbs_up) await confirmation_ref.add_reaction(thumbs_down) # Handle user reaction try: reaction, user = await ctx.bot.wait_for( "reaction_add", check=lambda new_reaction, author: new_reaction.message.id == confirmation_ref.id and author == ctx.message.author, timeout=INTERACT_TIMEOUT, ) # Process thumbs up if reaction.emoji == thumbs_up: await guild_data.remove_admin(user_id_to_remove) msg = translate("bot_admin_remove_success", await culture(ctx)).format(user_name_to_remove) return await ctx.send(msg) # Process thumbs down elif reaction.emoji == thumbs_down: msg = translate("bot_admin_remove_cancel", await culture(ctx)).format(user_name_to_remove) return await ctx.send(msg) # If we reach here, an invalid emoji was used await confirmation_ref.delete() return # Handle timeout except asyncio.TimeoutError: await confirmation_ref.delete() msg = translate("snooze_lose", await culture(ctx)) return await ctx.send(msg)
async def notify(self, ctx: commands.Context, list_name: str, *, message: typing.Optional[str] = None): """ Notify all subscribers for the given list with the given message. :param ctx:The current context. (discord.ext.commands.Context) :param list_name: The name of the list to notify. (str) :param message: The message to send with the notification. (optional - str - default= None) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if list does not exist list_name = list_name.lower() if not guild_data.does_list_exist(list_name): msg = translate("list_err_does_not_exit", await culture(ctx)) return await ctx.send(msg) # Fetch users to notify users = guild_data.get_users_list(list_name) emoji, is_custom_emoji = guild_data.get_emoji(list_name) if is_custom_emoji: emoji = get_custom_emoji(ctx, int(emoji)) # Error if no users were found if len(users) < 1: msg = translate("list_err_empty", await culture(ctx)) return await ctx.send(msg) # Setup the announcement with the subject and caller message_text = translate("notifying", await culture(ctx)).format( list_name.capitalize(), ctx.message.author.id, ctx.guild.get_member(ctx.bot.user.id).display_name) # build users mentioning strings user_tags = f'<@{str(users[0])}>' user_messages = [] for user_id in users[1:]: if len(user_tags) + len(str(user_id)) + 5 < DISCORD_MAX_MSG_LENGTH: user_tags += ', ' + (f'<@{str(user_id)}>') else: user_messages.append(user_tags) user_tags = (f'<@{str(user_id)}>') user_messages.append(user_tags) embed = discord.Embed( title=emoji + "\t" + list_name.capitalize() + "\t" + emoji, description=message_text, color=NOTIFY_EMBED_COLOR, ) # append the message if provided if message: # If message too long, tell user to write shorter message excess = (-1 * NOTIFY_MAX_MSG_LENGTH) + len(message) if excess > 0: msg = translate("notif_too_long", await culture(ctx)).format(excess) return await ctx.send(msg) embed.add_field(name=translate("message", await culture(ctx)), value=message) await ctx.channel.send(embed=embed) for users_str in user_messages: await ctx.send(users_str)
async def add_list(self, ctx: commands.Context, list_name: str): """ Adds a new notification list with the given name. :param ctx: The current context. (discord.ext.commands.Context) :param list_name: The name to be used for the list. (str) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if not admin if not guild_data.user_is_admin(ctx.author): gif = translate("not_admin_gif", await culture(ctx)) return await ctx.send(gif) # Make sure the list name is lowercase list_name = list_name.lower() # Error if list already exists if guild_data.does_list_exist(list_name): msg = translate("list_already_exists", await culture(ctx)).format(list_name) return await ctx.send(msg) # Request emoji from user msg = await ctx.send("What emoji do you want to use for " + list_name + " ?") # Handle user reaction try: reaction, user = await ctx.bot.wait_for( "reaction_add", check=lambda emoji, author: emoji.message.id == msg.id and author == ctx.message.author, timeout=INTERACT_TIMEOUT, ) # Process emoji if reaction.custom_emoji: try: reaction_emoji = str(reaction.emoji.id) emoji_to_print = get_custom_emoji(ctx, int(reaction_emoji)) custom_emoji = True except AttributeError: msg = translate("unknown_emoji", await culture(ctx)) return await ctx.send(msg) else: reaction_emoji = reaction.emoji emoji_to_print = str(reaction_emoji) custom_emoji = False # Error if emoji is being used already on this server for data in guild_data.notification_lists.values(): if reaction_emoji == data["emoji"]: msg = translate("emoji_already_in_use", await culture(ctx)) return await ctx.send(msg) # Add list to GuildData await guild_data.add_notification_list(list_name, reaction_emoji, custom_emoji) # Show success message to user await ctx.send("The list `" + list_name + "` is saved with the emoji " + emoji_to_print) # Handle timeout except asyncio.TimeoutError: await msg.delete() msg = translate("snooze_lose", await culture(ctx)) return await ctx.send(msg)
async def poll(self, ctx: commands.Context, *, input_str: str): """ Create a poll with either yes or no as an answer or self submitted options. Poll will be open for an amount of time determined by the user. Syntax: question (multiple words) timeout (numerals) <options> (words split by ;) Example: This is a quention? 10 option 1; option 2; option 3 :param input: input to be parsed according to above syntax """ poller_id = ctx.message.author.id #check if there is a question if '?' not in input_str: await ctx.send( translate("poll_no_questionmark", await culture(ctx))) return input_split = input_str.split('?', 1) question = input_split[0] numbers_and_options = input_split[1].strip() # parse timeout and options if len(numbers_and_options) > 0: first_word = numbers_and_options.split()[0] if not first_word.isdigit(): await ctx.send( translate("poll_no_timeout", await culture(ctx)).format(POLL_DEFAULT_TIMEOUT)) timeout_s = POLL_DEFAULT_TIMEOUT * 60 else: timeout_s = int(first_word) * 60 if timeout_s > POLL_MAX_TIMEOUT * 60: await ctx.send( translate("poll_max_timeout", await culture(ctx)).format(POLL_MAX_TIMEOUT)) timeout_s = POLL_MAX_TIMEOUT * 60 # parse options options = numbers_and_options.split(first_word, 1)[1].strip() if len(options) > 0: options_list = options.split(';') is_yes_no = False else: is_yes_no = True else: is_yes_no = True await ctx.send( translate("poll_no_timeout", await culture(ctx)).format(POLL_DEFAULT_TIMEOUT)) timeout_s = POLL_DEFAULT_TIMEOUT * 60 # create message to send to channel txt = translate("poll_start", await culture(ctx)).format(poller_id, question) # add options to message options_dict = dict() if is_yes_no: txt += translate("poll_yes_no", await culture(ctx)).format(yes, no) options_dict[yes] = translate("yes", await culture(ctx)) options_dict[no] = translate("no", await culture(ctx)) else: i = 1 for option in options_list: txt += "{} - {}\n".format(number_emojis[i], option) options_dict[number_emojis[i]] = option i += 1 msg = await ctx.send(txt) # add reactions to message if is_yes_no: await msg.add_reaction(yes) await msg.add_reaction(no) else: i = 1 for option in options_list: await msg.add_reaction(number_emojis[i]) i += 1 # wait until timeout await asyncio.sleep(timeout_s) # refresh message msg = await ctx.fetch_message(msg.id) # get the reactions reactions = msg.reactions reactions_dict = dict() for reaction in reactions: reactions_dict[reaction.emoji] = reaction.count reactions_sorted = sorted(reactions_dict.items(), key=lambda x: x[1], reverse=True) # send poll results txt = translate("poll_results", await culture(ctx)).format(drum, poller_id, question) for reaction in reactions_sorted: try: option_str = options_dict[reaction[0]].strip() count = reaction[1] - 1 txt += translate("poll_votes", await culture(ctx)).format(option_str, count) except KeyError: pass # send message with results await ctx.send(txt)
async def show_lists(self, ctx: commands.Context): """ Show all currently existing lists for this server :param ctx: The current context. (discord.ext.commands.Context) """ guild_data = await get_guild_data(ctx.message.guild.id) # Error if no lists exist yet if not guild_data.notification_lists: msg = translate("no_existing_lists", await culture(ctx)) return await ctx.send(msg) # Check list count max_per_page = NOTIFY_MAX_PER_PAGE page_count = math.ceil( len(guild_data.notification_lists) / max_per_page) sorted_lists = sorted(guild_data.notification_lists.items()) messages = [] for page in range(1, page_count + 1): # Init text with title text = translate("lists", await culture(ctx)) if page_count > 1: text += " " + translate("lists_page_count", await culture(ctx)).format(page, page_count) text += ":\n" # Loop and append all lists first_index = (page - 1) * max_per_page last_index = (page * max_per_page) for list_name, list_data in sorted_lists[first_index:last_index]: if list_data["is_custom_emoji"]: text += get_custom_emoji(ctx, int(list_data["emoji"])) else: text += list_data["emoji"] text += " - " + list_name + "\n" # Send lists to context msg = await ctx.send(text) messages.append(msg) # Add reactions for _, list_data in sorted_lists[first_index:last_index]: await msg.add_reaction( list_data["emoji"] if not list_data["is_custom_emoji"] else ctx.bot.get_emoji(int(list_data["emoji"]))) reaction_tasks = [] for message in messages: reaction_added_task = asyncio.create_task( self.wait_for_added_reactions(ctx, message.id, guild_data)) reaction_tasks.append(reaction_added_task) reaction_removed_task = asyncio.create_task( self.wait_for_removed_reactions(ctx, message.id, guild_data)) reaction_tasks.append(reaction_removed_task) # Listen for reactions await asyncio.gather(*reaction_tasks, return_exceptions=True) # Delete messages for message in messages: await message.delete()