Esempio n. 1
0
    async def delete_category(self,
                              ctx: commands.Context,
                              *,
                              category_name: Optional[str] = None):
        if category_name is None:
            await ctx.send_help(self.delete_category)
            return

        existing_cats = await pDB.get_role_cats(self.pool, ctx.guild.id)

        category = existing_cats.get_cat_by_name(category_name)
        if category is None:
            raise ValueError(
                f"Could not find a category named {category_name}")
        roles = await category.get_roles()
        embed = pn_embed(
            title="Are You Sure?",
            desc=
            f"**{category.cat_name}** contains **{len(roles)}** roles that will have to be readded to PNBot to be user settable if this category is deleted!\n"
            f"Are you sure you want to delete the category **{category.cat_name}**?"
        )

        ui = BoolPage(embed=embed)
        response = await ui.run(ctx)

        if response is not True:
            embed = pn_embed(desc=f"Canceled deletion of {category.cat_name}")
            await ctx.send(embed=embed)
            return

        category_name = category.cat_name
        await category.delete()
        await send_embed(ctx, desc=f"**{category_name}** has been deleted!")
Esempio n. 2
0
    async def prompt_for_new_description(self):
        cat: pDB.RoleCategory = discord.utils.find(
            lambda x: x.cat_name.lower().strip() == self.selected_cat.lower().
            strip(), self.role_cats)

        self.embed = pn_embed(title=f"Change description of *{cat.cat_name}*",
                              desc="Please send the new description now.")

        self.ui = StringReactPage(embed=self.embed, remove_msgs=False)

        response = await self.ui.run(self.ctx)
        if response is None:
            await self.ctx.send(embed=pn_embed(
                title="Change Category Description Canceled"))
            return

        new_description = response.content()

        status_embed = pn_embed(title=f"Description Changed:",
                                desc=new_description)

        # Commit changes to the DB.
        await cat.redescribe(new_description)

        await self.ctx.send(embed=status_embed)
        return
Esempio n. 3
0
    def __init__(self, cats: pDB.RoleCategories):

        self.role_cats = cats.cats
        self.ctx: Optional[commands.Context] = None
        # self.cat_names = [cat.cat_name for cat in role_cats]
        num_of_buttons = len(self.role_cats) if len(
            self.role_cats) <= len(number_emotes) else len(number_emotes)

        buttons = [(number_emotes[i], self.role_cats[i].cat_name)
                   for i in range(num_of_buttons)]
        self.cat_names_w_buttons = []

        for i, cat in enumerate(self.role_cats):
            if i < len(number_emotes):
                self.cat_names_w_buttons.append(
                    f"{number_emotes[i]}  **{cat.cat_name}**\n*Description:*\n{cat.description}"
                )
            else:
                self.cat_names_w_buttons.append(
                    f"    **{cat.cat_name}**\n*Description:*\n{cat.description}"
                )

        cat_desc = "\n".join(self.cat_names_w_buttons)
        self.embed = pn_embed(
            title="Select A Role Category",
            desc=f"Click a react or send the name to select\n\n{cat_desc}")

        self.ui = StringReactPage(
            embed=self.embed,
            buttons=buttons,
            allowable_responses=[cat.cat_name for cat in self.role_cats],
            remove_msgs=False)

        self.selected_cat = ""
Esempio n. 4
0
    async def run(self, ctx: commands.Context):
        """Initializes remaining variables and starts the command."""
        self.ctx = ctx

        if self.max_page_index < 0:
            await self.ctx.send(embed=pn_embed(
                title="No Roles Available To Add Or Remove",
                desc=
                f"There are currently no roles that have been configured as allowable to add or remove."
            ))
            await self.ui.finish()
            return

        embed = await self.prepare_embed()
        self.ui = StringReactPage(
            embed=self.embed,
            buttons=self.buttons,
            remove_msgs=False,
            edit_in_place=True,
            cancel_emoji='🛑')  #, cancel_btn_loc=len(self.navigation_buttons))

        await self.update_current_roles_len(
        )  # Make sure that self.current_roles_len is initialized

        # Start the UI Loop
        await self.prompt_for_toggle_roles(embed)

        # Clean up and remove the reactions
        await self.ui.finish()
Esempio n. 5
0
    def __init__(self, cats: pDB.RoleCategories):
        self.role_cats = cats.cats

        # Page/subpage indexes
        self.page_index: int = 0
        self.max_page_index: int = len(self.role_cats) - 1

        self.sub_page_index: int = 0
        self.showing_categories = False

        # Buttons
        self.buttons = self.navigation_buttons[:]
        num_of_buttons = self.max_per_page if self.max_per_page <= len(
            number_emotes) else len(number_emotes)
        self.buttons.extend([(number_emotes[i + 1], i)
                             for i in range(num_of_buttons)])

        # Vars that get dealt with later
        self.embed = pn_embed()  # discord.Embed()
        self.ui: Optional[StringReactPage] = None
        self.ctx: Optional[commands.Context] = None

        self.conf_msg: Optional[discord.Message] = None
        self.current_roles_len = 0

        self.roles_added: List[int] = []
        self.roles_removed: List[int] = []
Esempio n. 6
0
    async def add_category(self,
                           ctx: commands.Context,
                           *,
                           cat_name: Optional[str] = None):
        if cat_name is None:
            await ctx.send_help(self.add_category)
            return

        existing_cats = await pDB.get_role_cats(self.pool, ctx.guild.id)

        if len(existing_cats.cats) >= MAX_NUMBER_OF_CATS:
            await send_embed(
                ctx,
                title="Can not add new category.",
                desc=
                "You have reached already the maximum number of categories and con not add another.\n"
                "Consider renaming or deleting an existing category")
            return

        embed = pn_embed(
            desc=f"Please enter a description for the new category {cat_name}\n"
            f"Enter `none` for no description.")

        ui = StringReactPage(embed=embed,
                             allow_any_response=True,
                             remove_msgs=False)
        response = await ui.run(ctx)

        if response is None:
            embed = pn_embed(desc=f"Canceled adding {cat_name}")
            await ctx.send(embed=embed)
            await ui.finish()
            return

        if response.content().lower().strip() == 'none':
            cat_desc = None
            cat_desc_msg = "no description."
        else:
            cat_desc = response.content()
            cat_desc_msg = f"the description:\n\n{cat_desc}"

        await existing_cats.add_new_cat(cat_name, cat_desc)
        await send_embed(
            ctx, desc=f"**{cat_name}** has been added with {cat_desc_msg}")
        await ui.finish()
Esempio n. 7
0
    async def prompt_for_new_roles(self, refreshed_embed=None):
        cat: pDB.RoleCategory = discord.utils.find(
            lambda x: x.cat_name.lower().strip() == self.selected_cat.lower().
            strip(), self.role_cats)

        self.embed = pn_embed(
            title=f"Add Roles to *{cat.cat_name}*",
            desc=
            "You may send a single role, or multiple roles separated by a comma."
        )

        self.ui = StringReactPage(embed=self.embed, remove_msgs=False)

        response = await self.ui.run(self.ctx, new_embed=refreshed_embed)
        if response is None:
            await self.ctx.send(embed=pn_embed(title="Add Role Canceled"))
            return

        unparsed_roles = response.content()

        parsed_roles = await parse_csv_roles(self.ctx,
                                             unparsed_roles,
                                             parse_all=True)
        if parsed_roles is None:
            await self.ctx.send(embed=pn_embed(
                title="ERROR!!! Could not parse roles!"))
            return

        # Add the roles to the DB.
        for role in parsed_roles.good_roles:
            await cat.add_new_role(role.id)

        status_embed = add_and_removed_roles_embed(
            parsed_roles,
            disallowed_field_name=
            "The following roles could not be added as they have *Moderator Level* permissions:"
        )

        await self.ctx.send(embed=status_embed)
        return
Esempio n. 8
0
    async def prompt_for_category(self, refreshed_embed=None):
        response = await self.ui.run(self.ctx, new_embed=refreshed_embed)

        if response is None:
            await self.ctx.send(embed=pn_embed(title="Add Role Canceled"))
            return

        self.selected_cat = response.content()

        log.info(f"Selected: {self.selected_cat}")

        # Clean up and remove the reactions from the `Category Prompt` before self.prompt_for_new_roles() creates a new UI
        await self.ui.finish()

        await self.prompt_for_new_roles()
Esempio n. 9
0
    async def prompt_for_category(self):
        response = await self.ui.run(self.ctx)

        if response is None:
            await self.ctx.send(embed=pn_embed(
                title="Change Category Description Canceled"))
            return

        self.selected_cat = response.content()

        log.info(f"Selected: {self.selected_cat}")

        # Clean up and remove the reactions from the `Category Prompt` before self.prompt_for_new_description() creates a new UI
        await self.ui.finish()
        await self.prompt_for_new_description()
Esempio n. 10
0
    async def full_role_list_embed(
            ctx: commands.Context,
            categories: pDB.RoleCategories) -> discord.Embed:
        """
        Constructs and returns an embed containing the full listing of (filled) categories and roles for a guild,
         marked with the roles the user who requested the embed has.
        """
        member: discord.Member = ctx.author

        if len(categories.cats) == 0:
            embed = pn_embed(
                desc=f"{ctx.guild.name} has no user settable roles!")
        else:

            embed = pn_embed(
                desc=f"{ctx.guild.name} has the following roles:\n")
            for cat in categories.cats:

                roles = await cat.get_roles()

                if len(roles) == 0:
                    continue  # Skip Categories that have not yet been set up.

                field_msg = ""  # if len(roles) > 0 else "*No Roles Have Been Added To This Category Yet*\n"
                for role in roles:
                    has_role = discord.utils.get(member.roles, id=role.role_id)
                    # has_role_indicator = " ✅" if has_role is not None else ""  # "❌"
                    # field_msg += f"> <@&{role.role_id}>{has_role_indicator}\n"
                    has_role_indicator = "✅" if has_role is not None else "\N{MEDIUM BLACK CIRCLE}"  # "❌"
                    field_msg += f"{has_role_indicator} <@&{role.role_id}>\n"

                embed.add_field(name=f"__{cat.cat_name}__",
                                value=field_msg,
                                inline=True)

        return embed
Esempio n. 11
0
def add_and_removed_roles_embed(
        roles: 'ParsedRoles',
        disallowed_field_name=None,
        remove_roles_msg: bool = False) -> discord.Embed:

    add_remove_txt = "removed" if remove_roles_msg else "added"

    if disallowed_field_name is None:
        disallowed_field_name = f"The following roles are not allowed to be {add_remove_txt} by PNBot:"

    status_embed = pn_embed(
        title=
        f"{len(roles.good_roles)} out of {len(roles.good_roles) + len(roles.bad_roles) + len(roles.disallowed_roles)} roles {add_remove_txt}"
    )

    if len(roles.good_roles) > 0:
        good_roles_msg = ", ".join(
            [f"<@&{role.id}>" for role in roles.good_roles])
        status_embed.add_field(name=f"Successfully {add_remove_txt}:",
                               value=good_roles_msg,
                               inline=False)

    if len(roles.disallowed_roles) > 0:
        disallowed_roles_msg = ", ".join(
            [f"<@&{role.id}>" for role in roles.disallowed_roles])

        status_embed.add_field(name=disallowed_field_name,
                               value=disallowed_roles_msg,
                               inline=False)

    if len(roles.bad_roles) > 0:
        bad_roles_msg = ", ".join([f"{role}" for role in roles.bad_roles])
        suggestion_strs = [
            f"<@&{role.best_match.id}>" for role in roles.bad_roles
            if role.best_match is not None
        ]
        suggestion_msg = f"\n\nDid you mean? \n{', '.join(set(suggestion_strs))}" if len(
            suggestion_strs) > 0 else ""

        status_embed.add_field(
            name=
            "Could not find the following (check spelling and capitalization):",
            value=f"{bad_roles_msg}{suggestion_msg}\n\N{ZERO WIDTH SPACE}",
            inline=False)

    return status_embed
Esempio n. 12
0
    async def prompt_for_toggle_roles(self, refreshed_embed=None):
        """The main command handler for the Toggle User Roles UI"""

        # discord.utils.find(lambda x: x.cat_name.lower().strip() == self.selected_cat.lower().strip(), self.role_cats)

        # self.embed = refreshed_embed or pn_embed(title=f"Select Roles to Add",
        #                                          desc="Click on the associated react to add roles.")

        await self.show_category_listing()
        while True:
            response = await self.ui.run(self.ctx, new_embed=self.embed)

            # Send out the conf embed right after the first time we send out the UI embed.
            if self.conf_msg is None:
                self.conf_msg = await self.ctx.send(embed=pn_embed(
                    title="Changing Roles"))

            if response is None:
                if self.conf_msg is not None and len(self.conf_msg.embeds) > 0:
                    self.conf_msg.embeds[0].title = "Finished Changing Roles"
                await self.conf_msg.edit(embed=self.conf_msg.embeds[0])
                return

            roles = await self.current_roles()

            if response.content() == 'left_button':
                await self.decrement_page()

            elif response.content() == 'right_button':
                await self.increment_page()

            elif response.content() == "category_list":
                await self.show_category_listing()

            else:
                if self.showing_categories:
                    if response.content() in list(range(len(self.role_cats))):
                        await self.jump_to_page(response.content())
                else:
                    if response.content() in list(range(len(roles))):
                        await self.toggle_role(
                            roles[response.content()].role_id)
                        await self.prepare_embed()
Esempio n. 13
0
    async def remove_role_via_text(self,
                                   ctx: commands.Context,
                                   *,
                                   roles: Optional[str] = None):
        """
        This command can accept either a single role, or many roles at once.
        When removing multiple roles at the same time, each role must be separated by a comma like this:
            `pb;remove_roles She/Her, Pink, Voice, Pats Welcome`

        Quotation marks are not needed for this command.
        """

        if roles is None:
            await ctx.send_help(self.remove_role_via_text)
            return

        member: discord.Member = ctx.author
        cats = await pDB.get_role_cats(self.pool, ctx.guild.id)

        parsed_roles = await parse_csv_roles(ctx, roles, cats.cats)

        if parsed_roles is None:
            await ctx.send(embed=pn_embed(
                title="ERROR!!! Could not parse roles!"))
            return

        if len(parsed_roles.good_roles) > 0:
            # Remove the roles from the User.
            await member.remove_roles(
                *parsed_roles.good_roles,
                reason=
                f"Role removed via PNBot at the command of {member.name}#{member.discriminator}."
            )

        status_embed = add_and_removed_roles_embed(parsed_roles,
                                                   remove_roles_msg=True)
        await ctx.send(embed=status_embed)
        return
Esempio n. 14
0
    async def prompt_for_roles_to_remove(self):

        allowed_roles = await pDB.get_roles_in_guild(self.ctx.bot.db,
                                                     self.ctx.guild.id)
        purged_roles = await purge_deleted_roles(self.ctx, allowed_roles)
        if len(purged_roles) > 0:
            purged_role_msg = f"\n\n(Note, {len(purged_roles)} roles that were previously deleted from discord have been purged.)"
        else:
            purged_role_msg = f""

        embed = pn_embed(
            title=f"Send Roles to Remove",
            desc=
            f"You may send a single role, or multiple roles separated by a comma.{purged_role_msg}"
        )

        self.ui = StringReactPage(embed=embed, remove_msgs=False)

        response = await self.ui.run(self.ctx)
        if response is None:
            await self.ctx.send(embed=pn_embed(title="Remove Roles Canceled"))
            return

        unparsed_roles = response.content()

        parsed_roles = await parse_csv_roles(self.ctx,
                                             unparsed_roles,
                                             parse_all=True,
                                             allow_privileged_roles=True)
        if parsed_roles is None:
            await self.ctx.send(embed=pn_embed(
                title="ERROR!!! Could not parse roles!"))
            return

        status_embed = pn_embed(
            title=
            f"{len(parsed_roles.good_roles)} out of {len(parsed_roles.good_roles) + len(parsed_roles.bad_roles)} roles removed:"
        )

        # Remove the roles from the DB.
        for role in parsed_roles.good_roles:
            # cat: pDB.RoleCategory = discord.utils.find(lambda x: x.role_in(role), self.role_cats)
            await pDB.delete_role(self.ctx.bot.db, self.ctx.guild.id, role.id)

        if len(parsed_roles.good_roles) > 0:
            good_roles_msg = ", ".join(
                [f"<@&{role.id}>" for role in parsed_roles.good_roles])
            status_embed.add_field(name="Successfully removed:",
                                   value=good_roles_msg,
                                   inline=False)

        if len(parsed_roles.bad_roles) > 0:
            bad_roles_msg = ", ".join(
                [f"{role}" for role in parsed_roles.bad_roles])
            suggestion_strs = [
                f"<@&{role.best_match.id}>" for role in parsed_roles.bad_roles
                if role.best_match is not None
            ]
            suggestion_msg = f"\n\nDid you mean? {', '.join(set(suggestion_strs))}" if len(
                suggestion_strs) > 0 else ""

            status_embed.add_field(
                name=
                "Could not find and remove the following (check spelling and capitalization):",
                value=f"{bad_roles_msg}{suggestion_msg}\n\N{ZERO WIDTH SPACE}",
                inline=False)

        await self.ctx.send(embed=status_embed)
        return