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!")
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
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 = ""
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()
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] = []
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()
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
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()
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()
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
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
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()
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
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