async def help(ctx): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member): # First: Authorize an admin is running this command embed = discord.Embed(title="Command Help", colour=0xFF7D00) # Create an embed, set it's title and color if utils.authorize_admin(ctx.guild, ctx.author): embed.description = "\n`help:` Get sent this list\n\n" \ "`addrole \"Category\" \"Role\" Description:` Adds an assignable role to the role list\n\n" \ "`adddisprole \"Category\" \"Role\" Description:` Adds a non-assignable role to the role list\n\n" \ "`editrole \"Category\" \"Role\" Description:` Removes and re-adds a role to the role list\n\n" \ "`removerole \"Category\" \"Role\":` Removes a role from the role list\n\n" \ "`rolelist:` Sends/updates the role list messages to the current channel\n\n" \ "`newrolelist:` Deletes the old role list and sends a new one to the current channel\n\n" \ "`setadminrole \"Role\":` Sets a role as this bot's \"admin\" role\n\n" \ "`sortcategory \"Category\":` Sorts the roles in a category (alphabetical order)\n\n" \ "`setcategorydescription \"Category\" Description:` Sets a category's description (optional)\n\n\n" \ "`scnew \"Fileame\" Description:` Creates a new scoreboard\n\n" \ "`scload \"Filename\":` Loads a saved scoreboard from file\n\n" \ "`scunload:` Unloads the current scoreboard (disables score submissions)\n\n" \ "`scoreboard:` Sends a new scoreboard message\n\n" \ "`scdisplayname Name:` Sets a scoreboard's display name\n\n" \ "`scdescription Desc:` Sets a scoreboard's description\n\n" \ "`scfield \"name\" type:` Adds/updates a scoreboard field\n\n"\ "`scremovefield \"name\":` Removes a scoreboard field\n\n" \ "`submit score:` Submits a score to the scoreboard (adding a score is optional)\n\n" \ "`verify user score:` Verifies user's score on the scoreboard (score is optional if the user provided a score)\n\n" \ "Note: If an admin role is set, you'll need that role to run most commands!" else: embed.description = "\n`!help:` Get sent this list\n\n" \ "`!submit <score>:` Submits a score to the leaderboard (specifying score is optional, must be verified by an admin)\n\n\n" \ "...yeah there's not much else non-admins can do :/\n\n" \ "I guess you can ping me if you get super bored lol" await ctx.author.send(embed=embed) await ctx.send(embed=utils.format_embed("DM'd ya fam 😉", False))
async def removerole(self, ctx, category, role: discord.Role): if not isinstance(ctx.channel, discord.DMChannel) and isinstance( ctx.author, discord.Member ) and utils.authorize_admin( ctx.guild, ctx.author ): # First: Confirm that it's a member (of a guild) we're being given, and then authorize them as an admin #global self.removerole_message, self.removerole_category, self.removerole_role role_list = config_man.get_roles(category) if role_list is False: # Sanity check we're being given a valid category await ctx.send(embed=utils.format_embed( "Error: Category " + category + " not found!", True)) return embed = discord.Embed(title="Will attempt to remove a role", colour=0xDB2323) msg_text = '\n**Category:** ' + category msg_text += '\n**Role: **' + role.name if len( role_list ) <= 1: # If this is the only role in the category, the category will be removed as well msg_text += '\n**This category will be empty (and thus removed) if this role is removed. It\'s rolelist message will be deleted if it exists, too.**' msg_text += '\n\nTo confirm removal: React with ❌' self.removerole_category = category self.removerole_role = str(role.id) embed.description = msg_text msg = await ctx.send(embed=embed) self.removerole_message[0] = msg.channel.id self.removerole_message[1] = msg.id await msg.add_reaction('❌') print("Sent removerole message: ", msg.id) return
async def setcategorydescription(self, ctx, category, *, description=""): if not isinstance(ctx.channel, discord.DMChannel) and isinstance( ctx.author, discord.Member) and utils.authorize_admin( ctx.guild, ctx.author ): # First: Authorize an admin is running this command msg = config_man.set_category_description(category, description) await ctx.send(embed=utils.format_embed(msg, False))
async def altrolemsg(self, ctx, category, role: discord.Role, msgid): if not isinstance(ctx.channel, discord.DMChannel) and isinstance( ctx.author, discord.Member) and utils.authorize_admin( ctx.guild, ctx.author ): # First: Authorize an admin is running this command # Check to make sure the specified message exists msg = str(msgid).split("-") if len(msg) != 2: await ctx.send(embed=utils.format_embed( "Invalid Message ID specified!\nMake sure you use `Shift + Hover Message -> Copy ID` to get the ID.", True)) return print("Specified channel: " + msg[0] + ", message: " + msg[1]) try: message = await self.bot.get_channel(int( msg[0])).fetch_message(int(msg[1])) assert message is not None except discord.errors.NotFound: print("Received 404 trying to find message with ID " + str(msgid)) await ctx.send(embed=utils.format_embed( "Error getting the specified message ID!\nMake sure you use `Shift + Hover Message -> Copy ID` to get the ID.", True)) return # The message should exist at this point, add a reaction to it and set it as the category's alt message duck_emote = str(get(ctx.guild.emojis, name="discoduck")) if duck_emote == "None": duck_emote = "🦆" await message.add_reaction(duck_emote) ret = config_man.set_category_alt_message(category, msg[0], msg[1], role) await ctx.send(embed=utils.format_embed(ret, False))
async def on_command_error(ctx, error): if isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Invalid commands run by non-admins can still display error messages - Prevent that here if isinstance(error, commands.MissingRequiredArgument): await ctx.send(embed=utils.format_embed('Missing arguments!', True)) return if isinstance(error, commands.BadArgument): await ctx.send(embed=utils.format_embed('Bad argument given!', True)) return raise error
async def editrole( self, ctx, category, role: discord.Role, *, desc="" ): # argument: discord.XYZ converts this argument to this data type (i.e. turn a string into a role) if not isinstance(ctx.channel, discord.DMChannel) and isinstance( ctx.author, discord.Member ) and utils.authorize_admin( ctx.guild, ctx.author ): # First: Confirm that it's a member (of a guild) we're being given, and then authorize them as an admin roles = config_man.get_roles( category) # First, let's do some sanity checks: if roles is False: # Confirm that our role list is valid (if it's false, the category is invalid) await ctx.send(embed=utils.format_embed( "Error getting roles for category \"" + category + "\" - does this category exist?", True)) return if str(role.id) not in roles.keys( ): # If our role list is correct, make sure the role we want to edit is valid, too await ctx.send(embed=utils.format_embed( "Error: Role \"" + role.name + "\" not found in category \"" + category + "\"!", True)) return #global self.addrole_message, self.addrole_role, self.addrole_description, self.addrole_category, self.addrole_dispname, self.addrole_editing, self.addrole_assignable self.addrole_assignable = config_man.is_role_assignable( category, role.id) embed = discord.Embed(title="Will attempt to edit a role", colour=0x4EDB23) msg_text = '\n**Role: **' + role.name msg_text += '\n**Category: **' + category msg_text += '\n**Description: **' + desc msg_text += '\n**Assignable: **' + str(self.addrole_assignable) msg_text += '\n\nRole will be removed and re-added with these values.' msg_text += '\nTo confirm: React to this message with ' + ( 'the emote to listen for!' if self.addrole_assignable else '🛡!') self.addrole_role = str(role.id) self.addrole_dispname = role.name self.addrole_category = category self.addrole_description = desc self.addrole_editing = True embed.description = msg_text msg = await ctx.send(embed=embed) self.addrole_message[0] = msg.channel.id self.addrole_message[1] = msg.id if not self.addrole_assignable: await msg.add_reaction('🛡') print("Sent editrole message: ", msg.id) return
async def adddisprole( self, ctx, category, role: discord.Role, *, desc="" ): # argument: discord.XYZ converts this argument to this data type (i.e. turn a string into a role) if not isinstance(ctx.channel, discord.DMChannel) and isinstance( ctx.author, discord.Member ) and utils.authorize_admin( ctx.guild, ctx.author ): # First: Confirm that it's a member (of a guild) we're being given, and then authorize them as an admin roles = config_man.get_roles( category) # First, let's do some sanity checks: if roles is not False: # A valid dict of roles was returned, run some checks only if this is the case if len( roles ) >= 20: # Discord limits reactions to 20 per message (afaik), so prevent adding a 21st role to a category await ctx.send(embed=utils.format_embed( "Error: A category can't have more than 20 roles!\nThis is a discord reaction limitation, sorry :(", True)) return if str(role.id) in roles.keys( ): # Prevent having the same role pop up multiple times in the category (probably doesn't break anything tbh, it just sounds dumb) await ctx.send(embed=utils.format_embed( "Error: This role is already in this category!", True)) return #global self.addrole_message, self.addrole_role, self.addrole_description, self.addrole_category, self.addrole_dispname, self.addrole_editing, self.addrole_assignable embed = discord.Embed(title="Will attempt to add a display role", colour=0x4EDB23) msg_text = '\n**Role: **' + role.name msg_text += '\n**Category: **' + category msg_text += '\n**Description: **' + desc msg_text += '\n**Assignable: False**' msg_text += '\n\nTo confirm add: React with 🛡 to confirm!' self.addrole_role = str(role.id) self.addrole_dispname = role.name self.addrole_category = category self.addrole_description = desc self.addrole_editing = False self.addrole_assignable = False embed.description = msg_text msg = await ctx.send(embed=embed) self.addrole_message[0] = msg.channel.id self.addrole_message[1] = msg.id await msg.add_reaction('🛡') print("Sent adddisprole message: ", msg.id) return
async def scunload(self, ctx): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: if not self.sc_config.is_scoreboard_loaded(): await ctx.send(embed=utils.format_embed("No scoreboard is currently loaded, nothing to unload!", Flase)) return self.sc_config.save_sc_config() #Don't nuke ur data kids, triple-check that you've saved to disk before loading a new config self.sc_config.unload_sc_config() config_man.set_default_scoreboard("") await ctx.send(embed=utils.format_embed("Unloaded scoreboard - no scoreboard is active now", False))
async def scload(self, ctx, name): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: self.sc_config.save_sc_config() #Don't nuke ur data kids, triple-check that you've saved to disk before loading a new config loaded = self.sc_config.load_sc_config(name, False) if loaded == 0: await ctx.send(embed=utils.format_embed("Loaded scoreboard " + name, False)) config_man.set_default_scoreboard(name) else: await ctx.send(embed=utils.format_embed("Error: Scoreboard config not found: " + name, True))
async def scoreboard(self, ctx): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: if not self.sc_config.is_scoreboard_loaded(): await ctx.send(embed=utils.format_embed("Error: No scoreboard is currently loaded! Load one with !scload", True)) return await self.generate_scoreboard_message(ctx, True)
async def scremoveentry(self, ctx, member: discord.Member): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: if not self.sc_config.is_scoreboard_loaded(): await ctx.send(embed=utils.format_embed("Error: No scoreboard is currently loaded! Load one with !scload", True)) return fields = self.sc_config.get_fields_emoji() if len(fields) == 1: rm_field = list(fields.values())[0] else: rm_field = None # Field prompt part 3rd def rm_reaction_check(reaction, user): if user == ctx.author and reaction.message.id == msg.id and str(reaction.emoji) in fields.keys(): self.scrm_field[user.id] = fields[str(reaction.emoji)] return True return False try: if rm_field is None: embed = discord.Embed(title="Removing a score:", color=0x4EDB23) msg_text = "\n" + ctx.author.display_name msg_text += "\n\n**React with the field to remove this user's score from!**\n" for emote, field in fields.items(): msg_text += emote + " - " + field + "\n" embed.description = msg_text msg = await ctx.send(embed=embed) for emote in fields.keys(): await msg.add_reaction(emote) reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=rm_reaction_check) except asyncio.TimeoutError: msg_text += "\n\n**Waiting for a reaction timed out - run this command again**" embed.description = msg_text embed.color = 0xDB2323 await msg.edit(embed=embed) else: if rm_field is None: rm_field = self.scrm_field[ctx.author.id] print("Reaction-based field set: " + rm_field) print("Attempting entry removal from field " + rm_field) result = self.sc_config.remove_entry(rm_field, member.id) if result: await self.generate_scoreboard_message(ctx, False) # Update the scoreboard await ctx.send(embed=utils.format_embed("Removed " + member.display_name + "'s entry from " + rm_field, False)) else: await ctx.send(embed=utils.format_embed("Unable to remove " + member.display_name + "'s entry from " + rm_field, True))
async def verify(self, ctx, member: discord.Member, score=-1): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: if not self.sc_config.is_scoreboard_loaded(): await ctx.send(embed=utils.format_embed("Error: No scoreboard is currently loaded! Load one with !scload", True)) return fields = self.sc_config.get_fields_emoji() if len(fields) == 1: # Only one field, just submit to that one by default ver_field = list(fields.values())[0] else: # Multiple fields - figure out which later:tm: ver_field = None # Validation method when prompting the user to pick a field to verify scores from def ver_reaction_check(reaction, user): if user == ctx.author and reaction.message.id == msg.id and str(reaction.emoji) in fields.keys(): self.scverify_field[user.id] = fields[str(reaction.emoji)] return True return False try: if ver_field is None: # Still need to prompt the user to choose a field to submit to, do it embed = discord.Embed(title="Verifying score:", color=0x4EDB23) msg_text = "\n" + ctx.author.display_name if int(score) == -1: msg_text += "'s unverified score" else: msg_text += ": " + str(score) msg_text += "\n\n**React with the field to verify this score from!**" embed.description = msg_text msg = await ctx.send(embed=embed) for emote in fields.keys(): # React to this message with the emotes for all the fields we can submit to await msg.add_reaction(emote) reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=ver_reaction_check) except asyncio.TimeoutError: msg_text += "\n\n**Waiting for a reaction timed out - run this command again**" embed.description = msg_text embed.color = 0xDB2323 await msg.edit(embed=embed) else: # On valid reaction/we cheated and already know the field to verify: if ver_field is None: ver_field = self.scverify_field[ctx.author.id] print("Reaction-based field set: " + ver_field) print("Attempting verification of score from field " + ver_field) #try: if int(score) == -1: # Score = -1 means score wasn't specified as an argument, so verify the user's unverified score existing_scores = self.sc_config.get_entry(ver_field, member.id) print("Attempting verify of user " + member.display_name + "'s unverified score") print(existing_scores) if existing_scores is False or existing_scores[0] == -1: # Plot twist: The user doesn't have an unverified score to verify await ctx.send(embed=utils.format_embed("Error: This user doesn't have an unverified score in field " + ver_field + "! Specify their score after their username!", True)) return else: # They have an unverified score, set their new verified score to it score = existing_scores[0] #except TypeError as e: # print("TypeError in score verification: " + str(e) + "\nWas the specified score an int?") # await ctx.send(embed=utils.format_embed("Error: Specified score \"" + str(score) + "\" is not an int!", True)) # return self.sc_config.update_entry(ver_field, member.id, score, True) await self.generate_scoreboard_message(ctx, False) # Update the scoreboard embedd = discord.Embed(title="Set user " + member.display_name + "'s verified score", description=ver_field + ": " + str(score), colour=0x16E200) await ctx.send(embed=embedd)
async def scremovefield(self, ctx, name): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: if not self.sc_config.is_scoreboard_loaded(): await ctx.send(embed=utils.format_embed("Error: No scoreboard is currently loaded! Load one with !scload", True)) return fields = self.sc_config.get_fields() if name in fields.keys(): embed = discord.Embed(title="Removing scoreboard field:", color=0x4EDB23) else: ctx.send(embed=utils.format_embed("Error: Field " + name + " doesn't exist!", True)) return msg_text = '\n**Name:** ' + name msg_text += '\n**Warning: This will permanently delete this field and its scores!**' msg_text += '\n\nTo confirm deletion: React with "❌"' embed.description = msg_text msg = await ctx.send(embed=embed) await msg.add_reaction("❌") def reaction_check(reaction, user): # Checks if the emoji reaction to scremovefield is valid or not return user == ctx.author and reaction.message.id == msg.id and str(reaction.emoji) == '❌' # Only accept 'X' reactions from the command sender on the message we sent try: reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=reaction_check) except asyncio.TimeoutError: msg_text += '\n**Waiting for a reaction timed out - run this command again**' embed.description = msg_text embed.color = 0xDB2323 await msg.edit(embed=embed) else: self.sc_config.remove_field(name) #await msg.channel.send(embed=utils.format_embed("Deleted field " + name, False)) await msg.add_reaction("👍") await self.generate_scoreboard_message(ctx, False) # Update the scoreboard, BOI
async def scfield(self, ctx, name, type): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: if not self.sc_config.is_scoreboard_loaded(): await ctx.send(embed=utils.format_embed("Error: No scoreboard is currently loaded! Load one with !scload", True)) return type = 0 # Todo: We only support one field type for now, so enforce it here ftype = self.sc_config.parse_field_type(type) if ftype is None: await ctx.send(embed=utils.format_embed("Error: Invalid field type \"" + type + "\"!", True)) return fields = self.sc_config.get_fields() if name in fields.keys(): print("Field " + name + " exists") embed = discord.Embed(title="Editing existing scoreboard field:", color=0x4EDB23) else: print("Field " + name + " doesn't exist") embed = discord.Embed(title="Creating new scoreboard field:", color=0x4EDB23) msg_text = '\n**Name:** ' + name msg_text += '\n**Type:** ' + str(type) msg_text += '\n\nTo confirm: React with an emote to associate with this field!' embed.description = msg_text msg = await ctx.send(embed=embed) def reaction_check(reaction, user): # Checks if the emoji reaction to sc_field is valid or not if user != ctx.author or reaction.message.id != msg.id: # First: Only accept reactions from the command sender on the message we just sent return False if str(reaction.emoji) in self.sc_config.get_fields_emoji().keys(): # Make sure the emoji isn't in use in another field print("Reaction check failed: Duplicate emoji") return False if reaction.custom_emoji: # Finally: If this is a custom emote, make sure the bot can actually use it emoji = get(ctx.guild.emojis, id=reaction.emoji.id) if emoji is None or emoji.available is False: return False self.scfield_emote = str(reaction.emoji) return True try: reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=reaction_check) except asyncio.TimeoutError: msg_text += '\n**Waiting for an emote timed out - run this command again**' embed.description = msg_text embed.color = 0xDB2323 await msg.edit(embed=embed) else: print(self.scfield_emote) self.sc_config.update_field(name, str(type), self.scfield_emote) await msg.add_reaction("👍") #await msg.channel.send(embed=utils.format_embed("Updated field " + name + "!", False)) await self.generate_scoreboard_message(ctx, False) # Update the scoreboard, BOI
async def addrole( self, ctx, category, role: discord.Role, *, desc="" ): # argument: discord.XYZ converts this argument to this data type (i.e. turn a string into a role) if not isinstance(ctx.channel, discord.DMChannel) and isinstance( ctx.author, discord.Member ) and utils.authorize_admin( ctx.guild, ctx.author ): # First: Confirm that it's a member (of a guild) we're being given, and then authorize them as an admin roles = config_man.get_roles( category) # First, let's do some sanity checks: if roles is not False: # A valid dict of roles was returned, run some checks only if this is the case if len( roles ) >= 20: # Discord limits reactions to 20 per message (afaik), so prevent adding a 21st role to a category await ctx.send(embed=utils.format_embed( "Error: A category can't have more than 20 roles!\nThis is a discord reaction limitation, sorry :(", True)) return if str(role.id) in roles.keys( ): # Prevent having the same role pop up multiple times in the category (probably doesn't break anything tbh, it just sounds dumb) await ctx.send(embed=utils.format_embed( "Error: This role is already in this category!", True)) return if role.id == config_man.get_admin_role( ): # Prevent adding the admin role to a category (giving random users your admin role doesn't seem smart) await ctx.send(embed=utils.format_embed( "Error: " + role.name + " is this bot's config admin role - You cannot add it to the role list!", True)) return permissions = role.permissions if permissions.administrator: # Prevent adding any admin roles for the guild await ctx.send(embed=utils.format_embed( "Error: " + role.name + " is an admin role in this server - You cannot add it to the role list!", True)) return #global self.addrole_message, self.addrole_role, self.addrole_description, self.addrole_category, self.addrole_dispname, self.addrole_editing, self.addrole_assignable embed = discord.Embed(title="Will attempt to add a role", colour=0x4EDB23) msg_text = '\n**Role: **' + role.name msg_text += '\n**Category: **' + category msg_text += '\n**Description: **' + desc msg_text += '\n**Assignable: True** - Any user can obtain this role!' msg_text += '\n\nTo confirm add: React to this message with the emote to listen for!' self.addrole_role = str(role.id) self.addrole_dispname = role.name self.addrole_category = category self.addrole_description = desc self.addrole_editing = False self.addrole_assignable = True # Finally, warn against adding roles with potentially "dangerous" permissions at the bottom of the message if permissions.kick_members or permissions.ban_members or permissions.manage_channels or permissions.manage_guild or permissions.manage_messages or permissions.mute_members or permissions.deafen_members: msg_text += "\n\n⚠ **Warning: Role " + role.name + " has potentially dangerous permissions.**\nRoles and permissions added to the role list can be obtained by *ANY* user." embed.description = msg_text msg = await ctx.send(embed=embed) self.addrole_message[0] = msg.channel.id self.addrole_message[1] = msg.id print("Sent addrole message: ", msg.id) return
async def setadminrole(ctx, role: discord.Role): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # First: Authorize an admin is running this command if role is not None: embed = discord.Embed(title="Will set the admin role", colour=0xFF7D00) # Create an embed, set it's title and color embed.description = "**Role:** " + role.name + "\n\nUsers without this role can't edit or send the role list, and can only react to it for roles.\n\n**Ensure you have this role and react with 🔒 to confirm!**" msg = await ctx.send(embed=embed) rolelist.setadmin_message[0] = msg.channel.id rolelist.setadmin_message[1] = msg.id rolelist.setadmin_role = str(role.id) await msg.add_reaction('🔒') print("Sent setadminrole message: ", msg.id) else: await ctx.send(embed=utils.format_embed("Invalid role given!", True)) # This might not even get reached, on_command_error() might intercept things first
async def scnew(self, ctx, name, *, description=""): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: self.sc_config.save_sc_config() # Don't nuke ur data kids, triple-check that you've saved to disk before loading a new config #sc_config.sc_config_exists(name) # Not yet loaded = self.sc_config.load_sc_config(name, True, description) if loaded == 0: await ctx.send(embed=utils.format_embed("Loaded scoreboard " + name + " - this really shouldn't happen :sweat_smile:", False)) config_man.set_default_scoreboard(name) elif loaded == 1: await ctx.send(embed=utils.format_embed("Created new scoreboard " + name, False)) config_man.set_default_scoreboard(name) else: await ctx.send(embed=utils.format_embed("Error: Scoreboard config not found: " + name, True))
async def introduction(ctx): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # First: Authorize an admin is running this command await ctx.send("*Musical quack*, da-don 🦆")
async def generateRoleList(self, ctx, sendNew): if not isinstance(ctx.channel, discord.DMChannel) and isinstance( ctx.author, discord.Member ) and utils.authorize_admin( ctx.guild, ctx.author ): # First: Confirm that it's a member (of a guild) we're being given, and then authorize them as an admin if not config_man.categories: # Are no categories loaded? await ctx.send(embed=utils.format_embed( "No role categories to display found! Add some roles to the rolelist with !addrole or !adddisprole.", True)) return #global self.rolelist_messages if sendNew: # Generating a new role list? Delete the old messages we're tracking first # First, delete all the old rolelist messages we're tracking (if any) and clear the list of messages to track for message in self.rolelist_messages.keys(): # print(message) try: msg = await self.bot.get_channel( message[0]).fetch_message(message[1]) await msg.delete() except discord.errors.NotFound: print( "Received 404 trying to delete message with ID " + str(message[1]) + ", was it already deleted?") self.rolelist_messages.clear() categorydescriptions = config_man.get_category_descriptions() for category, ids in config_man.categories.items( ): # Send a rolelist message for each category embed = discord.Embed( title=category, colour=0xFF7D00 ) # Create an embed, set it's title and color msg_text = categorydescriptions[category] if categorydescriptions[ category] is not None else "React with these emotes to get roles!" # Grab this category's description from the config (if there is one), otherwise use some placeholder text msg_text += "\n" emojis = config_man.get_roles_emoji(category) for role_id, desc in config_man.get_roles(category).items( ): # Grab the roles from this category, and add their name/descriptions to the message to send role = get(ctx.guild.roles, id=int(role_id)) msg_text += "\n " if config_man.is_role_assignable( category, role_id ): # Is this role assignable? If so, add its emote! if emojis[role_id][ 1] == 'True': # Custom emoji - Find emoji in guild and then insert it msg_text += str( get(ctx.guild.emojis, name=emojis[role_id][0])) else: # Unicode emoji - Print emoji normally (since it's just stored as a unicode string) msg_text += emojis[role_id][0] if desc == '': msg_text += " **" + role.name + "**" else: msg_text += " **" + role.name + "** - " + desc embed.description = msg_text # Set the embed's description to our role list text msg = None updated_msg = False if not sendNew: # Hol up, if we want to update the existing lists, check if this category already has a message id_list = ids.split(';') if id_list[0] != '-1' and id_list[ 1] != '-1': # There's *probably* a valid message to update? msg = await self.bot.get_channel(int( id_list[0])).fetch_message(int(id_list[1])) await msg.edit(embed=embed) print( "Edited existing role list message for \"" + category + "\": ", msg.id) updated_msg = True if not updated_msg: # Wasn't able to edit an existing category message? That's cool just send a new one msg = await ctx.send(embed=embed) print( "Sent role new list message for \"" + category + "\": ", msg.id) self.rolelist_messages[( msg.channel.id, msg.id )] = category # Store this message's channel/message IDs for later - We'll use them to track these messages for reactions config_man.set_category_message( category, str(msg.channel.id), str(msg.id) ) # Also save these values to the config as well cur_emoji_list = [] if updated_msg: # If we're updated an existing message, check if we should remove any (of our) reactions for roles that no longer exist: new_emoji_list = [] for item in emojis.values( ): # First, grab a list of all the emojis associated with roles in this category new_emoji_list.append(item[0]) for reaction in msg.reactions: # Now go through all of this message's reactions if isinstance( reaction.emoji, discord.Emoji) or isinstance( reaction.emoji, discord.PartialEmoji): emote = reaction.emoji.name else: # We're probably being given a unicode string as an emoji??? emote = reaction.emoji if reaction.me: if emote not in new_emoji_list: # If we've used this reaction and that reaction no longer belongs to a role, remove our reaction to it! await reaction.remove(self.bot.user) await asyncio.sleep(0.5) else: # We've used this reaction and it does belong to a role, add it to the list of reactions we currently have cur_emoji_list.append(emote) for role, emoji in emojis.items( ): # React with the emotes for all assignable roles in this category # Add this emote as a reaction if this role is assignable AND EITHER: # - We're sending a new role list OR # - We're updating a role list and we haven't reacted with this emote yet (it isn't in cur_emoji)list) if config_man.is_role_assignable(category, role) and ( not updated_msg or (updated_msg and emoji[0] not in cur_emoji_list)): await asyncio.sleep( 0.5 ) # Wait a bit to appease rate limits (discordpy apparently does some stuff internally too, this prolly can't hurt tho if emoji[ 1] == 'True': # This is a custom emoji, grab an emoji instance before use await msg.add_reaction( get(ctx.guild.emojis, name=emoji[0])) else: # No fancy emoji conversion needed otherwise, unicode emojis can be passed into add_reaction as-is await msg.add_reaction(emoji[0]) await asyncio.sleep(1) # *bows down to discord api* config_man.save_config( ) # We've updated some entries in the config for message IDs to listen for reactions on, don't forget to save these! return
async def scdisplayname(self, ctx, *, name): if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member) and utils.authorize_admin(ctx.guild, ctx.author): # Prevent this from running outside of a guild or by non-admins: if not self.sc_config.is_scoreboard_loaded(): await ctx.send(embed=utils.format_embed("Error: No scoreboard is currently loaded! Load one with !scload", True)) return self.sc_config.set_disp_name(name) await self.generate_scoreboard_message(ctx, False) # Update the scoreboard, BOI await ctx.send(embed=utils.format_embed("Set scoreboard display name to " + name, False))
async def handle_reaction(self, payload, reaction_added): if payload.user_id == self.bot.user.id or payload.guild_id is None: # Don't process reactions we've added ourselves or aren't in a guild (probably in DMs?) return #global self.addrole_message, self.addrole_role, self.addrole_description, self.addrole_category, self.addrole_dispname, self.addrole_editing, self.addrole_assignable, self.removerole_message, self.removerole_category, self.removerole_role, self.setadmin_message, self.setadmin_role guild = self.bot.get_guild( payload.guild_id ) # Grab the guild we're in and the member that reacted for later member = guild.get_member(payload.user_id) # Reaction processing 1: ROLELIST MESSAGES (available to all server users) for tracked_message in self.rolelist_messages: if payload.channel_id == tracked_message[ 0] and payload.message_id == tracked_message[ 1]: # Only process messages that we are listening for reactions on. Here, we'll process reactions on the role list messages # print("\nRolelist reaction!\nUser: "******"\nChannel: ", payload.channel_id, "\nMessage: ", payload.message_id, "\nEmoji: ", payload.emoji.name, "\nEvent: ", payload.event_type) for category, category_message in config_man.categories.items( ): # Next, let's find the category of the message we just reacted to: category_ids = category_message.split(';') if payload.channel_id == int( category_ids[0]) and payload.message_id == int( category_ids[1]): # Did we find the category? roles = config_man.get_roles_emoji( category ) # Grab the roles in the category message the member reacted on if await RoleList.process_rolelist_reaction( self, payload, guild, category, roles, reaction_added, member ): # Try checking if the reacted emote belongs to a role, if it does update the user and return return print("lol") for category, category_message in config_man.categoriesAlt.items( ): # Finally, see if the message we reacted to was in our list of ALT category messages print(category + ", " + category_message) category_ids = category_message.split(';') if payload.channel_id == int( category_ids[0]) and payload.message_id == int( category_ids[1]): # Did we find the category? print("Found alt message") roles = config_man.get_alt_role_emoji( category ) # Grab the roles in the category message the member reacted on print(roles) if await RoleList.process_rolelist_reaction( self, payload, guild, category, roles, reaction_added, member ): # Try checking if the reacted emote belongs to a role, if it does update the user and return return # Reaction processing 2: Add/remove role reactions - Only available to admins! if utils.authorize_admin(guild, member): if payload.channel_id == self.addrole_message[ 0] and payload.message_id == self.addrole_message[ 1]: # Check for reactions on the latest "addrole" message print("\nAdd/edit role reaction!\nID: ", payload.message_id, "Emoji: ", payload.emoji.name) if self.addrole_assignable: # Run some sanity checks depending on whether we're adding an assignable role or not if payload.emoji.is_custom_emoji( ): # Using a custom emoji? Make sure the bot can actually use it, too emoji = get(guild.emojis, id=payload.emoji.id) if emoji is None or emoji.available is False: await self.bot.get_channel( payload.channel_id ).send(embed=utils.format_embed( "Error: I can't use that custom emoji, try reacting with a different emote.", True)) return else: # Not an assignable role, only process if the added reaction is the confirmation shield if payload.emoji.name != '🛡' and payload.emoji.name != '🛡️': # wth why are there two shields??? return if self.addrole_editing: # If we're editing a role, we want to remove the existing role before we add a new one with the new parameters remove_result = config_man.remove_role( self.addrole_category, self.addrole_role) if remove_result is False: await self.bot.get_channel( payload.channel_id ).send(embed=utils.format_embed( "Role edit macro failed!\n" + remove_result, True)) self.addrole_message = [0, 0] return add_result = config_man.add_role( self.addrole_category, self.addrole_role, self.addrole_dispname, payload.emoji.name, self.addrole_description, payload.emoji.is_custom_emoji(), self.addrole_assignable) if add_result is True: msg = await self.bot.get_channel( payload.channel_id).fetch_message(payload.message_id) await msg.add_reaction('\N{THUMBS UP SIGN}') else: await self.bot.get_channel( payload.channel_id ).send(embed=utils.format_embed(add_result, True)) self.addrole_message = [ 0, 0 ] # Don't forget to blank out the addrole message so we stop listening for reactions on the message we just reacted on! if payload.channel_id == self.removerole_message[ 0] and payload.message_id == self.removerole_message[ 1] and payload.emoji.name == '❌': # Check for reactions on the latest "removerole" message remove_result = config_man.remove_role( self.removerole_category, self.removerole_role) if remove_result is True or isinstance(remove_result, list): msg = await self.bot.get_channel( payload.channel_id).fetch_message(payload.message_id) await msg.add_reaction('\N{THUMBS UP SIGN}') if isinstance(remove_result, list) and remove_result[ 0] != -1: # If we got returned a list, it means this category is now empty. If list[0] isn't -1, it means we should delete the old rolelist message for that category, too msg = await self.bot.get_channel( remove_result[0]).fetch_message(remove_result[1]) await msg.delete() await self.bot.get_channel( payload.channel_id ).send(embed=utils.format_embed( "This category is now empty! Removed category and deleted its role list message", False)) else: await self.bot.get_channel( payload.channel_id ).send(embed=utils.format_embed(remove_result, True)) self.removerole_message = [ 0, 0 ] # Don't forget to blank out the removerole message so we stop listening for reactions on the message we just reacted on! if payload.channel_id == self.setadmin_message[ 0] and payload.message_id == self.setadmin_message[ 1] and payload.emoji.name == '🔒': # Check for reactions on the latest "setadminrole" message print("Reaction: setadmin message match") set_result = config_man.set_admin_role(self.setadmin_role) if set_result is True: msg = await self.bot.get_channel( payload.channel_id).fetch_message(payload.message_id) await msg.add_reaction('\N{THUMBS UP SIGN}') else: await self.bot.get_channel( payload.channel_id ).send(embed=utils.format_embed(set_result, True)) self.setadmin_message = [ 0, 0 ] # Don't forget to blank out the addrole message so we stop listening for reactions on the message we just reacted on!