예제 #1
0
    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))
예제 #2
0
 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))
예제 #3
0
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
예제 #4
0
 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))
예제 #5
0
 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))
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
    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
예제 #9
0
    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))
예제 #10
0
 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))
예제 #11
0
    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
예제 #12
0
    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)
예제 #13
0
 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))
예제 #14
0
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))
예제 #15
0
    async def submit(self, ctx, score=-1):
        if not isinstance(ctx.channel, discord.DMChannel) and isinstance(ctx.author, discord.Member):  # Prevent this from running outside of a guild:
            if not self.sc_config.is_scoreboard_loaded():
                await ctx.send(embed=utils.format_embed("Error: No scoreboard is currently loaded!", True))
                return

            # Our logic here changes if there's only one field on the scoreboard
            # If there's one field, select it by default and just submit scores to that
            # For multiple fields, we need to prompt the user to select a field
            fields = self.sc_config.get_fields_emoji()
            if len(fields) == 1:
                sub_field = list(fields.values())[0]
            else:
                sub_field = None

            # Our reaction-based field prompt - Validate input if the submitting user reacted to it, and their reaction is a field emoji
            def sub_reaction_check(reaction, user):
                if user == ctx.author and reaction.message.id == msg.id and str(reaction.emoji) in fields.keys():
                    self.scsubmit_field[user.id] = fields[str(reaction.emoji)]
                    return True
                return False

            try:
                if sub_field is None:  # If we haven't set a default field to submit to (above), send the reaction prompt and wait for a reaction
                    embed = discord.Embed(title="Submitting a score:", color=0x4EDB23)
                    msg_text = "\n" + ctx.author.display_name + ": " + str(score)
                    msg_text += "\n\n**React with the field to submit this score to!**\n"
                    for emote, field in fields.items():  # Display the emotes for all the fields we can submit to
                        msg_text += emote + " - " + field + "\n"
                    embed.description = msg_text
                    msg = await ctx.send(embed=embed)

                    for emote in fields.keys():  # And react to the message with all the fields we can submit to (two for loops yeehaw)
                        await msg.add_reaction(emote)

                    reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=sub_reaction_check)  # Now wait for the user to react
            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 reaction (or if we have a field to submit to and never prompted for a reaction), submit the score!
                if sub_field is None:
                    sub_field = self.scsubmit_field[ctx.author.id]
                    print("Reaction-based field set: " + sub_field)
                print("Attempting submission to field " + sub_field)
                self.sc_config.update_entry(sub_field, ctx.author.id, score, False)

                if randrange(0, 100) == 0:
                    scmsg = "You showed us... your ULTIMATE dance... Thank you very much... I can't stop CRYING, BUCKETS of ***TEARS.....***"
                else:
                    scmsg = Scoreboard.score_messages[randrange(len(Scoreboard.score_messages))]

                await self.generate_scoreboard_message(ctx, False)  # Update the scoreboard
                embedd = discord.Embed(title="Score submitted for verification - " + scmsg, description=sub_field + ": " + str(score), colour=0x16E200)
                await ctx.send(embed=embedd)
예제 #16
0
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
예제 #17
0
    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
예제 #18
0
async def on_message(message):
    text = message.content
    dev_msg = message.author.id == dev_id
    bot_msg = message.author == client.user
    chid = ChannelIdentifier(message, CH_NAMES)

    if text == QUIT_MSG and chid.is_dm and dev_msg:
        await client.close()

    # auto-reactions
    if bot_msg and chid.allow_ar:
        if chid.calendar:
            await add_calendar_reactions(message, DATE_FMT)
        elif chid.wanted:
            await add_wanted_reactions(message)
    elif bot_msg:
        return

    # help
    #TODO move to bot_messages
    bot_call = '!lastseen'
    help_str = (f'LastSeen commands:\n'
                f'`{bot_call} help` - show available commands.\n'
                f'`{bot_call} #1111` - show when username#1111 '
                'was online.')

    # commands
    if text.startswith(bot_call):
        c_start = len(bot_call) + 1
        command = text[c_start:]

        if command.startswith('heartbeat'):
            resp = 'staying alive'

        elif command.startswith('help'):
            resp = help_str

        elif command.startswith('#'):
            guild = d_get(client.guilds, id=guild_id)
            d = command[1:5]

            req_ms = [
                member for member in guild.members if member.discriminator == d
            ]
            if len(req_ms) == 1:
                req_m = req_ms[0]
                if req_m.status == on:
                    resp = f'{req_m.name} is online now'
                else:
                    q_get = '''
                    SELECT timestamp FROM id_timestamp
                    WHERE id = ?'''
                    res = c.execute(q_get, (req_m.id, ))
                    ls_ts = res.fetchone()[0]

                    if ls_ts == -1:
                        resp = f'Bot has never seen {req_m.name} online'
                    else:
                        # TODO user timezone
                        msc_tz = pytz.timezone('Europe/Moscow')
                        ls_dt = datetime.fromtimestamp(ls_ts, msc_tz)

                        resp = (f'{req_m.name} was online '
                                f'{ls_dt:%Y-%m-%d %H:%M} MSC')

            elif len(req_ms) > 1:
                m_names = (f'{i + 1} : {m.name}' for i, m in enumerate(req_ms))
                m_names_str = '\n'.join(m_names)
                resp = f'''Several users found:
                {m_names_str}'''
                # TODO choice for same discriminator
            else:
                resp = f'User with #{d} not found'

        # TODO
        #elif command.startswith('MAU'):
        # TODO
        #def last_period_count(timestamps, per)
        #per = 30
        #per_start = datetime.now() - timedelta(days=per)
        #per_count = sum(ts > per_start for ts in timestamps)
        #elif command.startswith('DAU'):

        else:
            resp = ('Unknown command.\n'
                    f'`{bot_call} help` for awailable commands.')

        await message.channel.send(resp)

    # recon
    atts = message.attachments

    if atts and chid.wanted:
        img = await atts[0].read()
        img = Image.open(BytesIO(img)).convert('RGB')
        img = ProfileImg(img)
        if img.name_box_found:
            player = img.recon_player()
            player.name = format_embed(player.name)
            player.crew_tag = format_embed(player.crew_tag)
            emb = discord.Embed(title=player.name)
            emb.add_field(name='Crew tag', value=player.crew_tag, inline=False)
            emb.add_field(name='Image ID', value=str(message.id), inline=False)
            await message.channel.send(embed=emb)
        else:
            await message.channel.send('Image was not recognized')

    # edit last recon message
    if chid.wanted:
        is_edit_command = any(text.startswith(c) for c in edit_commands)
        if is_edit_command:
            msgs = message.channel.history(limit=20)
            check_editor = partial(check_reaction,
                                   emoji=emoji_utils.edit,
                                   user=message.author)
            msg = await msgs.find(check_editor)
            if msg is not None:
                await edit_wanted_embed(msg, text)
            else:
                ch = message.channel
                await ch.send('Message to edit was not selected')
예제 #19
0
    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
예제 #20
0
    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)
예제 #21
0
    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
예제 #22
0
    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!