forgot formmating

This commit is contained in:
brandons209 2020-01-30 03:15:53 -05:00
parent 217fe41183
commit 54c72d8369
7 changed files with 98 additions and 279 deletions

View file

@ -28,9 +28,7 @@ class MixinMeta(ABC):
raise NotImplementedError()
@abstractmethod
async def is_self_assign_eligible(
self, who: discord.Member, role: discord.Role
) -> List[discord.Role]:
async def is_self_assign_eligible(self, who: discord.Member, role: discord.Role) -> List[discord.Role]:
raise NotImplementedError()
@abstractmethod

View file

@ -19,9 +19,7 @@ class RoleSyntaxConverter(NamedTuple):
@classmethod
async def convert(cls, ctx: Context, argument: str):
parser = NoExitParser(
description="Role management syntax help", add_help=False, allow_abbrev=True
)
parser = NoExitParser(description="Role management syntax help", add_help=False, allow_abbrev=True)
parser.add_argument("--add", nargs="*", dest="add", default=[])
parser.add_argument("--remove", nargs="*", dest="remove", default=[])
try:
@ -68,9 +66,7 @@ class ComplexActionConverter(NamedTuple):
parser.add_argument("--has-any", nargs="*", dest="any", default=[])
parser.add_argument("--has-all", nargs="*", dest="all", default=[])
parser.add_argument("--has-none", nargs="*", dest="none", default=[])
parser.add_argument(
"--has-no-roles", action="store_true", default=False, dest="noroles"
)
parser.add_argument("--has-no-roles", action="store_true", default=False, dest="noroles")
parser.add_argument("--has-perms", nargs="*", dest="hasperm", default=[])
parser.add_argument("--any-perm", nargs="*", dest="anyperm", default=[])
parser.add_argument("--not-perm", nargs="*", dest="notperm", default=[])
@ -82,15 +78,9 @@ class ComplexActionConverter(NamedTuple):
parser.add_argument("--above", dest="above", type=str, default=None)
parser.add_argument("--below", dest="below", type=str, default=None)
hum_or_bot = parser.add_mutually_exclusive_group()
hum_or_bot.add_argument(
"--only-humans", action="store_true", default=False, dest="humans"
)
hum_or_bot.add_argument(
"--only-bots", action="store_true", default=False, dest="bots"
)
hum_or_bot.add_argument(
"--everyone", action="store_true", default=False, dest="everyone"
)
hum_or_bot.add_argument("--only-humans", action="store_true", default=False, dest="humans")
hum_or_bot.add_argument("--only-bots", action="store_true", default=False, dest="bots")
hum_or_bot.add_argument("--everyone", action="store_true", default=False, dest="everyone")
try:
vals = vars(parser.parse_args(shlex.split(argument)))
@ -131,10 +121,7 @@ class ComplexActionConverter(NamedTuple):
for attr in ("hasperm", "anyperm", "notperm"):
vals[attr] = [
i.replace("_", " ").lower().replace(" ", "_").replace("server", "guild")
for i in vals[attr]
]
vals[attr] = [i.replace("_", " ").lower().replace(" ", "_").replace("server", "guild") for i in vals[attr]]
if any(perm not in dir(discord.Permissions) for perm in vals[attr]):
raise BadArgument("You gave an invalid permission")
@ -169,30 +156,20 @@ class ComplexSearchConverter(NamedTuple):
parser.add_argument("--has-any", nargs="*", dest="any", default=[])
parser.add_argument("--has-all", nargs="*", dest="all", default=[])
parser.add_argument("--has-none", nargs="*", dest="none", default=[])
parser.add_argument(
"--has-no-roles", action="store_true", default=False, dest="noroles"
)
parser.add_argument("--has-no-roles", action="store_true", default=False, dest="noroles")
parser.add_argument("--has-perms", nargs="*", dest="hasperm", default=[])
parser.add_argument("--any-perm", nargs="*", dest="anyperm", default=[])
parser.add_argument("--not-perm", nargs="*", dest="notperm", default=[])
parser.add_argument("--csv", action="store_true", default=False)
parser.add_argument(
"--has-exactly-nroles", dest="quantity", type=int, default=None
)
parser.add_argument("--has-exactly-nroles", dest="quantity", type=int, default=None)
parser.add_argument("--has-more-than-nroles", dest="gt", type=int, default=None)
parser.add_argument("--has-less-than-nroles", dest="lt", type=int, default=None)
parser.add_argument("--above", dest="above", type=str, default=None)
parser.add_argument("--below", dest="below", type=str, default=None)
hum_or_bot = parser.add_mutually_exclusive_group()
hum_or_bot.add_argument(
"--only-humans", action="store_true", default=False, dest="humans"
)
hum_or_bot.add_argument(
"--only-bots", action="store_true", default=False, dest="bots"
)
hum_or_bot.add_argument(
"--everyone", action="store_true", default=False, dest="everyone"
)
hum_or_bot.add_argument("--only-humans", action="store_true", default=False, dest="humans")
hum_or_bot.add_argument("--only-bots", action="store_true", default=False, dest="bots")
hum_or_bot.add_argument("--everyone", action="store_true", default=False, dest="everyone")
try:
vals = vars(parser.parse_args(shlex.split(argument)))
except Exception:
@ -229,10 +206,7 @@ class ComplexSearchConverter(NamedTuple):
for attr in ("hasperm", "anyperm", "notperm"):
vals[attr] = [
i.replace("_", " ").lower().replace(" ", "_").replace("server", "guild")
for i in vals[attr]
]
vals[attr] = [i.replace("_", " ").lower().replace(" ", "_").replace("server", "guild") for i in vals[attr]]
if any(perm not in dir(discord.Permissions) for perm in vals[attr]):
raise BadArgument("You gave an invalid permission")

View file

@ -38,15 +38,13 @@ class CompositeMetaClass(DPYCogMeta, ABCMeta):
pass # MRO is fine on __new__ with super() use
# no need to manually ensure both get handled here.
MIN_SUB_TIME = 3600
SLEEP_TIME = 300
class RoleManagement(
UtilMixin,
MassManagementMixin,
EventMixin,
commands.Cog,
metaclass=CompositeMetaClass,
UtilMixin, MassManagementMixin, EventMixin, commands.Cog, metaclass=CompositeMetaClass,
):
"""
Cog for role management
@ -61,12 +59,8 @@ class RoleManagement(
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(
self, identifier=78631113035100160, force_registration=True
)
self.config.register_global(
handled_variation=False, handled_full_str_emoji=False
)
self.config = Config.get_conf(self, identifier=78631113035100160, force_registration=True)
self.config.register_global(handled_variation=False, handled_full_str_emoji=False)
self.config.register_role(
exclusive_to=[],
requires_any=[],
@ -77,8 +71,8 @@ class RoleManagement(
protected=False,
cost=0,
subscription=0,
subscribed_users={}
)#subscribed_users maps str(user.id)-> end time in unix timestamp
subscribed_users={},
) # subscribed_users maps str(user.id)-> end time in unix timestamp
self.config.register_member(roles=[], forbidden=[])
self.config.init_custom("REACTROLE", 2)
self.config.register_custom(
@ -155,7 +149,7 @@ class RoleManagement(
now_time = time.time()
if end_time <= now_time:
member = guild.get_member(int(user_id))
if not member: # clean absent members
if not member: # clean absent members
del role_data["subscribed_users"][user_id]
continue
# charge user
@ -172,7 +166,7 @@ class RoleManagement(
await bank.withdraw_credits(member, cost)
msg += f"\n\nNo further action is required! You'll be charged again in {parse_seconds(curr_sub)}."
role_data["subscribed_users"][user_id] = now_time + curr_sub
except ValueError: # user is poor
except ValueError: # user is poor
msg += f"\n\nHowever, you do not have enough {currency_name} to cover the subscription. The role will be removed."
await self.update_roles_atomically(who=member, remove=[role])
del role_data["subscribed_users"][user_id]
@ -202,7 +196,7 @@ class RoleManagement(
async with self.config.guild(guild).s_roles() as s_roles:
for role_id in reversed(s_roles):
role = guild.get_role(role_id)
if not role: # clean stale subs if role is deleted
if not role: # clean stale subs if role is deleted
s_roles.remove(role_id)
continue
@ -210,9 +204,7 @@ class RoleManagement(
role_data = await self.sub_helper(guild, role, role_data)
await self.config.role(role).subscribed_users.set(
role_data["subscribed_users"]
)
await self.config.role(role).subscribed_users.set(role_data["subscribed_users"])
if len(role_data["subscribed_users"]) == 0:
s_roles.remove(role_id)
@ -226,9 +218,7 @@ class RoleManagement(
"""
if not await self.all_are_valid_roles(ctx, role):
return await ctx.maybe_send_embed(
"Can't do that. Discord role heirarchy applies here."
)
return await ctx.maybe_send_embed("Can't do that. Discord role heirarchy applies here.")
if not await self.config.role(role).sticky():
return await ctx.send("This only works on sticky roles.")
@ -244,9 +234,7 @@ class RoleManagement(
await ctx.maybe_send_embed("They are in the guild...assigned anyway.")
else:
async with self.config.member_from_ids(
ctx.guild.id, user_id
).roles() as sticky:
async with self.config.member_from_ids(ctx.guild.id, user_id).roles() as sticky:
if role.id not in sticky:
sticky.append(role.id)
@ -292,12 +280,7 @@ class RoleManagement(
@checks.admin_or_permissions(manage_guild=True)
@commands.command(name="rolebind")
async def bind_role_to_reactions(
self,
ctx: GuildContext,
role: discord.Role,
channel: discord.TextChannel,
msgid: int,
emoji: str,
self, ctx: GuildContext, role: discord.Role, channel: discord.TextChannel, msgid: int, emoji: str,
):
"""
Binds a role to a reaction on a message...
@ -307,9 +290,7 @@ class RoleManagement(
"""
if not await self.all_are_valid_roles(ctx, role):
return await ctx.maybe_send_embed(
"Can't do that. Discord role heirarchy applies here."
)
return await ctx.maybe_send_embed("Can't do that. Discord role heirarchy applies here.")
try:
message = await channel.fetch_message(msgid)
@ -334,17 +315,11 @@ class RoleManagement(
try:
await message.add_reaction(_emoji)
except discord.HTTPException:
return await ctx.maybe_send_embed(
"Hmm, that message couldn't be reacted to"
)
return await ctx.maybe_send_embed("Hmm, that message couldn't be reacted to")
cfg = self.config.custom("REACTROLE", str(message.id), eid)
await cfg.set(
{
"roleid": role.id,
"channelid": message.channel.id,
"guildid": role.guild.id,
}
{"roleid": role.id, "channelid": message.channel.id, "guildid": role.guild.id,}
)
await ctx.send(
f"Remember, the reactions only function according to "
@ -356,21 +331,15 @@ class RoleManagement(
@commands.bot_has_permissions(manage_roles=True)
@checks.admin_or_permissions(manage_guild=True)
@commands.command(name="roleunbind")
async def unbind_role_from_reactions(
self, ctx: commands.Context, role: discord.Role, msgid: int, emoji: str
):
async def unbind_role_from_reactions(self, ctx: commands.Context, role: discord.Role, msgid: int, emoji: str):
"""
unbinds a role from a reaction on a message
"""
if not await self.all_are_valid_roles(ctx, role):
return await ctx.maybe_send_embed(
"Can't do that. Discord role heirarchy applies here."
)
return await ctx.maybe_send_embed("Can't do that. Discord role heirarchy applies here.")
await self.config.custom(
"REACTROLE", f"{msgid}", self.strip_variations(emoji)
).clear()
await self.config.custom("REACTROLE", f"{msgid}", self.strip_variations(emoji)).clear()
await ctx.tick()
@commands.guild_only()
@ -392,12 +361,7 @@ class RoleManagement(
use_embeds = await ctx.embed_requested()
react_roles = "\n".join(
[
msg
async for msg in self.build_messages_for_react_roles(
*ctx.guild.roles, use_embeds=use_embeds
)
]
[msg async for msg in self.build_messages_for_react_roles(*ctx.guild.roles, use_embeds=use_embeds)]
)
if not react_roles:
@ -408,9 +372,7 @@ class RoleManagement(
color = await ctx.embed_colour() if use_embeds else None
for page in pagify(
react_roles, escape_mass_mentions=False, page_length=1800, shorten_by=0
):
for page in pagify(react_roles, escape_mass_mentions=False, page_length=1800, shorten_by=0):
# unrolling iterative calling of ctx.maybe_send_embed
if use_embeds:
await ctx.send(embed=discord.Embed(description=page, color=color))
@ -431,22 +393,14 @@ class RoleManagement(
f"\n{'is' if rsets['sticky'] else 'is not'} sticky."
)
if rsets["requires_any"]:
rstring = ", ".join(
r.name for r in ctx.guild.roles if r.id in rsets["requires_any"]
)
rstring = ", ".join(r.name for r in ctx.guild.roles if r.id in rsets["requires_any"])
output += f"\nThis role requires any of the following roles: {rstring}"
if rsets["requires_all"]:
rstring = ", ".join(
r.name for r in ctx.guild.roles if r.id in rsets["requires_all"]
)
rstring = ", ".join(r.name for r in ctx.guild.roles if r.id in rsets["requires_all"])
output += f"\nThis role requires all of the following roles: {rstring}"
if rsets["exclusive_to"]:
rstring = ", ".join(
r.name for r in ctx.guild.roles if r.id in rsets["exclusive_to"]
)
output += (
f"\nThis role is mutually exclusive to the following roles: {rstring}"
)
rstring = ", ".join(r.name for r in ctx.guild.roles if r.id in rsets["exclusive_to"])
output += f"\nThis role is mutually exclusive to the following roles: {rstring}"
if rsets["cost"]:
curr = await bank.get_currency_name(ctx.guild)
cost = rsets["cost"]
@ -462,9 +416,7 @@ class RoleManagement(
await ctx.send(page)
@rgroup.command(name="cost")
async def make_purchasable(
self, ctx: GuildContext, cost: int, *, role: discord.Role
):
async def make_purchasable(self, ctx: GuildContext, cost: int, *, role: discord.Role):
"""
Makes a role purchasable for a specified cost.
Cost must be a number greater than 0.
@ -477,9 +429,7 @@ class RoleManagement(
"""
if not await self.all_are_valid_roles(ctx, role):
return await ctx.maybe_send_embed(
"Can't do that. Discord role heirarchy applies here."
)
return await ctx.maybe_send_embed("Can't do that. Discord role heirarchy applies here.")
if cost < 0:
return await ctx.send_help()
@ -507,9 +457,7 @@ class RoleManagement(
(etc)
"""
if not await self.all_are_valid_roles(ctx, role):
return await ctx.maybe_send_embed(
"Can't do that. Discord role heirarchy applies here."
)
return await ctx.maybe_send_embed("Can't do that. Discord role heirarchy applies here.")
role_cost = await self.config.role(role).cost()
if role_cost == 0:
@ -532,9 +480,7 @@ class RoleManagement(
await ctx.send(f"Subscription set to {parse_seconds(time.total_seconds())}.")
@rgroup.command(name="forbid")
async def forbid_role(
self, ctx: GuildContext, role: discord.Role, *, user: discord.Member
):
async def forbid_role(self, ctx: GuildContext, role: discord.Role, *, user: discord.Member):
"""
Forbids a user from gaining a specific role.
"""
@ -546,9 +492,7 @@ class RoleManagement(
await ctx.tick()
@rgroup.command(name="unforbid")
async def unforbid_role(
self, ctx: GuildContext, role: discord.Role, *, user: discord.Member
):
async def unforbid_role(self, ctx: GuildContext, role: discord.Role, *, user: discord.Member):
"""
Unforbids a user from gaining a specific role.
"""
@ -572,9 +516,7 @@ class RoleManagement(
for role in _roles:
async with self.config.role(role).exclusive_to() as ex_list:
ex_list.extend(
[r.id for r in _roles if r != role and r.id not in ex_list]
)
ex_list.extend([r.id for r in _roles if r != role and r.id not in ex_list])
await ctx.tick()
@rgroup.command(name="unexclusive")
@ -595,20 +537,14 @@ class RoleManagement(
await ctx.tick()
@rgroup.command(name="sticky")
async def setsticky(
self, ctx: GuildContext, role: discord.Role, sticky: bool = None
):
async def setsticky(self, ctx: GuildContext, role: discord.Role, sticky: bool = None):
"""
sets a role as sticky if used without a settings, gets the current ones
"""
if sticky is None:
is_sticky = await self.config.role(role).sticky()
return await ctx.send(
"{role} {verb} sticky".format(
role=role.name, verb=("is" if is_sticky else "is not")
)
)
return await ctx.send("{role} {verb} sticky".format(role=role.name, verb=("is" if is_sticky else "is not")))
await self.config.role(role).sticky.set(sticky)
if sticky:
@ -619,7 +555,7 @@ class RoleManagement(
await ctx.tick()
#TODO set roles who don't need to pay for roles
# TODO set roles who don't need to pay for roles
@rgroup.command(name="requireall")
async def reqall(self, ctx: GuildContext, role: discord.Role, *roles: discord.Role):
"""
@ -645,9 +581,7 @@ class RoleManagement(
await ctx.tick()
@rgroup.command(name="selfrem")
async def selfrem(
self, ctx: GuildContext, role: discord.Role, removable: bool = None
):
async def selfrem(self, ctx: GuildContext, role: discord.Role, removable: bool = None):
"""
Sets if a role is self-removable (default False)
@ -657,18 +591,14 @@ class RoleManagement(
if removable is None:
is_removable = await self.config.role(role).self_removable()
return await ctx.send(
"{role} {verb} self-removable".format(
role=role.name, verb=("is" if is_removable else "is not")
)
"{role} {verb} self-removable".format(role=role.name, verb=("is" if is_removable else "is not"))
)
await self.config.role(role).self_removable.set(removable)
await ctx.tick()
@rgroup.command(name="selfadd")
async def selfadd(
self, ctx: GuildContext, role: discord.Role, assignable: bool = None
):
async def selfadd(self, ctx: GuildContext, role: discord.Role, assignable: bool = None):
"""
Sets if a role is self-assignable via command
@ -680,9 +610,7 @@ class RoleManagement(
if assignable is None:
is_assignable = await self.config.role(role).self_role()
return await ctx.send(
"{role} {verb} self-assignable".format(
role=role.name, verb=("is" if is_assignable else "is not")
)
"{role} {verb} self-assignable".format(role=role.name, verb=("is" if is_assignable else "is not"))
)
await self.config.role(role).self_role.set(assignable)
@ -778,7 +706,8 @@ class RoleManagement(
for role, (cost, sub) in sorted(data.items(), key=lambda kv: kv[1]):
embed.add_field(
name=f"__**{i+1}. {role.name}**__",
value="%s%s" % ((f"Cost: {cost}" if cost else "Free"), (f", every {parse_seconds(sub)}" if sub else ""))
value="%s%s"
% ((f"Cost: {cost}" if cost else "Free"), (f", every {parse_seconds(sub)}" if sub else "")),
)
i += 1
if i % 25 == 0:
@ -802,37 +731,27 @@ class RoleManagement(
except RoleManagementException:
return
except PermissionOrHierarchyException:
await ctx.send(
"I cannot assign roles which I can not manage. (Discord Hierarchy)"
)
await ctx.send("I cannot assign roles which I can not manage. (Discord Hierarchy)")
else:
if not eligible:
return await ctx.send(
f"You aren't allowed to add `{role}` to yourself {ctx.author.mention}!"
)
return await ctx.send(f"You aren't allowed to add `{role}` to yourself {ctx.author.mention}!")
if not cost:
return await ctx.send(
"This role doesn't have a cost. Please try again using `[p]srole add`."
)
return await ctx.send("This role doesn't have a cost. Please try again using `[p]srole add`.")
free_roles = await self.config.guild(ctx.guild).free_roles()
currency_name = await bank.get_currency_name(ctx.guild)
for m_role in ctx.author.roles:
if m_role.id in free_roles:
await ctx.send(f"You're special, no {currency_name} will be deducted from your account.")
await self.update_roles_atomically(
who=ctx.author, give=[role], remove=remove
)
await self.update_roles_atomically(who=ctx.author, give=[role], remove=remove)
await ctx.tick()
return
try:
await bank.withdraw_credits(ctx.author, cost)
except ValueError:
return await ctx.send(
f"You don't have enough {currency_name} (Cost: {cost} {currency_name})"
)
return await ctx.send(f"You don't have enough {currency_name} (Cost: {cost} {currency_name})")
else:
if subscription > 0:
await ctx.send(f"{role.name} will be renewed every {parse_seconds(subscription)}")
@ -842,9 +761,7 @@ class RoleManagement(
if role.id not in s:
s.append(role.id)
await self.update_roles_atomically(
who=ctx.author, give=[role], remove=remove
)
await self.update_roles_atomically(who=ctx.author, give=[role], remove=remove)
await ctx.tick()
@srole.command(name="add")
@ -862,24 +779,15 @@ class RoleManagement(
except RoleManagementException:
return
except PermissionOrHierarchyException:
await ctx.send(
"I cannot assign roles which I can not manage. (Discord Hierarchy)"
)
await ctx.send("I cannot assign roles which I can not manage. (Discord Hierarchy)")
else:
if not eligible:
await ctx.send(
f"You aren't allowed to add `{role}` to yourself {ctx.author.mention}!"
)
await ctx.send(f"You aren't allowed to add `{role}` to yourself {ctx.author.mention}!")
elif cost:
await ctx.send(
"This role is not free. "
"Please use `[p]srole buy` if you would like to purchase it."
)
await ctx.send("This role is not free. " "Please use `[p]srole buy` if you would like to purchase it.")
else:
await self.update_roles_atomically(
who=ctx.author, give=[role], remove=remove
)
await self.update_roles_atomically(who=ctx.author, give=[role], remove=remove)
await ctx.tick()
@srole.command(name="remove")
@ -892,22 +800,18 @@ class RoleManagement(
return
if await self.config.role(role).self_removable():
await self.update_roles_atomically(who=ctx.author, remove=[role])
try: # remove subscription, if any
try: # remove subscription, if any
async with self.config.role(role).subscribed_users() as s:
del s[str(ctx.author.id)]
except:
pass
await ctx.tick()
else:
await ctx.send(
f"You aren't allowed to remove `{role}` from yourself {ctx.author.mention}!`"
)
await ctx.send(f"You aren't allowed to remove `{role}` from yourself {ctx.author.mention}!`")
# Stuff for clean interaction with react role entries
async def build_messages_for_react_roles(
self, *roles: discord.Role, use_embeds=True
) -> AsyncIterator[str]:
async def build_messages_for_react_roles(self, *roles: discord.Role, use_embeds=True) -> AsyncIterator[str]:
"""
Builds info.
@ -926,22 +830,16 @@ class RoleManagement(
channel_id = data.get("channelid", None)
if channel_id:
link = linkfmt.format(
guild_id=role.guild.id,
channel_id=channel_id,
message_id=message_id,
)
link = linkfmt.format(guild_id=role.guild.id, channel_id=channel_id, message_id=message_id,)
else:
link = (
f"unknown message with id {message_id}"
f" (use `roleset fixup` to find missing data for this)"
f"unknown message with id {message_id}" f" (use `roleset fixup` to find missing data for this)"
)
emoji: Union[discord.Emoji, str]
if emoji_info.isdigit():
emoji = (
discord.utils.get(self.bot.emojis, id=int(emoji_info))
or f"A custom enoji with id {emoji_info}"
discord.utils.get(self.bot.emojis, id=int(emoji_info)) or f"A custom enoji with id {emoji_info}"
)
else:
emoji = emoji_info
@ -949,9 +847,7 @@ class RoleManagement(
react_m = f"{role.name} is bound to {emoji} on {link}"
yield react_m
async def get_react_role_entries(
self, role: discord.Role
) -> AsyncIterator[Tuple[str, str, dict]]:
async def get_react_role_entries(self, role: discord.Role) -> AsyncIterator[Tuple[str, str, dict]]:
"""
yields:
str, str, dict

View file

@ -84,9 +84,7 @@ class EventMixin(MixinMeta):
await member.add_roles(*to_add)
@commands.Cog.listener()
async def on_raw_reaction_add(
self, payload: discord.raw_models.RawReactionActionEvent
):
async def on_raw_reaction_add(self, payload: discord.raw_models.RawReactionActionEvent):
await self.wait_for_ready()
if not payload.guild_id:
return
@ -127,9 +125,7 @@ class EventMixin(MixinMeta):
await self.update_roles_atomically(who=member, give=[role], remove=remove)
@commands.Cog.listener()
async def on_raw_reaction_remove(
self, payload: discord.raw_models.RawReactionActionEvent
):
async def on_raw_reaction_remove(self, payload: discord.raw_models.RawReactionActionEvent):
await self.wait_for_ready()
if not payload.guild_id:
return

View file

@ -4,9 +4,11 @@ from __future__ import annotations
class RoleManagementException(Exception):
pass
class PermissionOrHierarchyException(Exception):
pass
class MissingRequirementsException(RoleManagementException):
def __init__(self, *, miss_any=None, miss_all=None):
self.miss_all = miss_all or []

View file

@ -88,14 +88,12 @@ class MassManagementMixin(MixinMeta):
return False
if query["anyperm"] and not any(
bool(value and perm in query["anyperm"])
for perm, value in iter(m.guild_permissions)
bool(value and perm in query["anyperm"]) for perm, value in iter(m.guild_permissions)
):
return False
if query["notperm"] and any(
bool(value and perm in query["notperm"])
for perm, value in iter(m.guild_permissions)
bool(value and perm in query["notperm"]) for perm, value in iter(m.guild_permissions)
):
return False
@ -126,11 +124,7 @@ class MassManagementMixin(MixinMeta):
@mrole.command(name="user")
async def mrole_user(
self,
ctx: GuildContext,
users: commands.Greedy[discord.Member],
*,
_query: RoleSyntaxConverter,
self, ctx: GuildContext, users: commands.Greedy[discord.Member], *, _query: RoleSyntaxConverter,
) -> None:
"""
adds/removes roles to one or more users
@ -150,16 +144,11 @@ class MassManagementMixin(MixinMeta):
query = _query.parsed
apply = query["add"] + query["remove"]
if not await self.all_are_valid_roles(ctx, *apply):
await ctx.send(
"Either you or I don't have the required permissions "
"or position in the hierarchy."
)
await ctx.send("Either you or I don't have the required permissions " "or position in the hierarchy.")
return
for user in users:
await self.update_roles_atomically(
who=user, give=query["add"], remove=query["remove"]
)
await self.update_roles_atomically(who=user, give=query["add"], remove=query["remove"])
await ctx.tick()
@ -213,9 +202,7 @@ class MassManagementMixin(MixinMeta):
embed = discord.Embed(description=description)
if ctx.guild:
embed.color = ctx.guild.me.color
await ctx.send(
embed=embed, content=f"Search results for {ctx.author.mention}"
)
await ctx.send(embed=embed, content=f"Search results for {ctx.author.mention}")
else:
await self.send_maybe_chunked_csv(ctx, list(members))
@ -223,9 +210,7 @@ class MassManagementMixin(MixinMeta):
@staticmethod
async def send_maybe_chunked_csv(ctx: GuildContext, members):
chunk_size = 75000
chunks = [
members[i : (i + chunk_size)] for i in range(0, len(members), chunk_size)
]
chunks = [members[i : (i + chunk_size)] for i in range(0, len(members), chunk_size)]
for part, chunk in enumerate(chunks, 1):
@ -246,9 +231,7 @@ class MassManagementMixin(MixinMeta):
"ID": member.id,
"Display Name": member.display_name,
"Username#Discrim": str(member),
"Joined Server": member.joined_at.strftime(fmt)
if member.joined_at
else None,
"Joined Server": member.joined_at.strftime(fmt) if member.joined_at else None,
"Joined Discord": member.created_at.strftime(fmt),
}
)
@ -262,8 +245,7 @@ class MassManagementMixin(MixinMeta):
filename += f"-part{part}"
filename += ".csv"
await ctx.send(
content=f"Data for {ctx.author.mention}",
files=[discord.File(data, filename=filename)],
content=f"Data for {ctx.author.mention}", files=[discord.File(data, filename=filename)],
)
csvf.close()
data.close()
@ -302,37 +284,26 @@ class MassManagementMixin(MixinMeta):
apply = query["add"] + query["remove"]
if not await self.all_are_valid_roles(ctx, *apply):
return await ctx.send(
"Either you or I don't have the required permissions "
"or position in the hierarchy."
"Either you or I don't have the required permissions " "or position in the hierarchy."
)
members = set(ctx.guild.members)
members = self.search_filter(members, query)
if len(members) > 100:
await ctx.send(
"This may take a while given the number of members to update."
)
await ctx.send("This may take a while given the number of members to update.")
async with ctx.typing():
for member in members:
try:
await self.update_roles_atomically(
who=member, give=query["add"], remove=query["remove"]
)
await self.update_roles_atomically(who=member, give=query["add"], remove=query["remove"])
except RoleManagementException:
log.debug(
"Internal filter failure on member id %d guild id %d query %s",
member.id,
ctx.guild.id,
query,
"Internal filter failure on member id %d guild id %d query %s", member.id, ctx.guild.id, query,
)
except discord.HTTPException:
log.debug(
"Unpredicted failure for member id %d in guild id %d query %s",
member.id,
ctx.guild.id,
query,
"Unpredicted failure for member id %d in guild id %d query %s", member.id, ctx.guild.id, query,
)
await ctx.tick()

View file

@ -26,6 +26,7 @@ TIME_RE_STRING = r"\s?".join(
TIME_RE = re.compile(TIME_RE_STRING, re.I)
def parse_timedelta(argument: str) -> timedelta:
"""
Parses a string that contains a time interval and converts it to a timedelta object.
@ -37,6 +38,7 @@ def parse_timedelta(argument: str) -> timedelta:
return timedelta(**params)
return None
def parse_seconds(seconds) -> str:
"""
Take seconds and converts it to larger units
@ -77,11 +79,7 @@ class UtilMixin(MixinMeta):
return variation_stripper_re.sub("", s)
async def update_roles_atomically(
self,
*,
who: discord.Member,
give: List[discord.Role] = None,
remove: List[discord.Role] = None,
self, *, who: discord.Member, give: List[discord.Role] = None, remove: List[discord.Role] = None,
):
"""
Give and remove roles as a single op with some slight sanity
@ -95,10 +93,7 @@ class UtilMixin(MixinMeta):
roles.extend([r for r in give if r not in roles])
if sorted(roles) == sorted(who.roles):
return
if (
any(r >= me.top_role for r in heirarchy_testing)
or not me.guild_permissions.manage_roles
):
if any(r >= me.top_role for r in heirarchy_testing) or not me.guild_permissions.manage_roles:
raise PermissionOrHierarchyException("Can't do that.")
await who.edit(roles=roles)
@ -120,10 +115,7 @@ class UtilMixin(MixinMeta):
# Bot allowed
if not (
guild.me.guild_permissions.manage_roles
and (
guild.me == guild.owner
or all(guild.me.top_role > role for role in roles)
)
and (guild.me == guild.owner or all(guild.me.top_role > role for role in roles))
):
return False
@ -133,9 +125,7 @@ class UtilMixin(MixinMeta):
return True
async def is_self_assign_eligible(
self, who: discord.Member, role: discord.Role
) -> List[discord.Role]:
async def is_self_assign_eligible(self, who: discord.Member, role: discord.Role) -> List[discord.Role]:
"""
Returns a list of roles to be removed if this one is added, or raises an
exception
@ -167,22 +157,14 @@ class UtilMixin(MixinMeta):
req_any_fail = []
break
req_all_fail = [
idx
for idx in await self.config.role(role).requires_all()
if not who._roles.has(idx)
]
req_all_fail = [idx for idx in await self.config.role(role).requires_all() if not who._roles.has(idx)]
if req_any_fail or req_all_fail:
raise MissingRequirementsException(
miss_all=req_all_fail, miss_any=req_any_fail
)
raise MissingRequirementsException(miss_all=req_all_fail, miss_any=req_any_fail)
return None
async def check_exclusivity(
self, who: discord.Member, role: discord.Role
) -> List[discord.Role]:
async def check_exclusivity(self, who: discord.Member, role: discord.Role) -> List[discord.Role]:
"""
Returns a list of roles to remove, or raises an error
"""