jarvis-bot/jarvis/cogs/admin/roleping.py

274 lines
10 KiB
Python

"""JARVIS RolepingCog."""
import logging
from jarvis_core.db import q
from jarvis_core.db.models import Roleping
from naff import Client, Cog, InteractionContext, Permissions
from naff.client.utils.misc_utils import find_all
from naff.ext.paginators import Paginator
from naff.models.discord.embed import EmbedField
from naff.models.discord.role import Role
from naff.models.discord.user import Member
from naff.models.naff.application_commands import (
OptionTypes,
SlashCommand,
slash_option,
)
from naff.models.naff.command import check
from jarvis.utils import build_embed
from jarvis.utils.permissions import admin_or_permissions
class RolepingCog(Cog):
"""JARVIS RolepingCog."""
def __init__(self, bot: Client):
self.bot = bot
self.logger = logging.getLogger(__name__)
roleping = SlashCommand(
name="roleping", description="Set up warnings for pinging specific roles"
)
@roleping.subcommand(
sub_cmd_name="add",
sub_cmd_description="Add a role to roleping",
)
@slash_option(name="role", description="Role to add", opt_type=OptionTypes.ROLE, required=True)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _roleping_add(self, ctx: InteractionContext, role: Role) -> None:
roleping = await Roleping.find_one(q(guild=ctx.guild.id, role=role.id))
if roleping:
await ctx.send(f"Role `{role.name}` already in roleping.", ephemeral=True)
return
if role.id == ctx.guild.id:
await ctx.send("Cannot add `@everyone` to roleping", ephemeral=True)
return
_ = await Roleping(
role=role.id,
guild=ctx.guild.id,
admin=ctx.author.id,
active=True,
bypass={"roles": [], "users": []},
).commit()
await ctx.send(f"Role `{role.name}` added to roleping.")
@roleping.subcommand(sub_cmd_name="remove", sub_cmd_description="Remove a role")
@slash_option(
name="role", description="Role to remove", opt_type=OptionTypes.ROLE, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _roleping_remove(self, ctx: InteractionContext, role: Role) -> None:
roleping = await Roleping.find_one(q(guild=ctx.guild.id, role=role.id))
if not roleping:
await ctx.send("Roleping does not exist", ephemeral=True)
return
try:
await roleping.delete()
except Exception:
self.logger.debug("Ignoring deletion error")
await ctx.send(f"Role `{role.name}` removed from roleping.")
@roleping.subcommand(sub_cmd_name="list", sub_cmd_description="Lick all blocklisted roles")
async def _roleping_list(self, ctx: InteractionContext) -> None:
rolepings = await Roleping.find(q(guild=ctx.guild.id)).to_list(None)
if not rolepings:
await ctx.send("No rolepings configured", ephemeral=True)
return
embeds = []
for roleping in rolepings:
role = await ctx.guild.fetch_role(roleping.role)
if not role:
await roleping.delete()
continue
broles = find_all(lambda x: x.id in roleping.bypass["roles"], ctx.guild.roles)
bypass_roles = [r.mention or "||`[redacted]`||" for r in broles]
bypass_users = [
(await ctx.guild.fetch_member(u)).mention or "||`[redacted]`||"
for u in roleping.bypass["users"]
]
bypass_roles = bypass_roles or ["None"]
bypass_users = bypass_users or ["None"]
embed = build_embed(
title="Roleping",
description=role.mention,
color=str(role.color),
fields=[
EmbedField(
name="Created At",
value=roleping.created_at.strftime("%a, %b %d, %Y %I:%M %p"),
inline=False,
),
# EmbedField(name="Active", value=str(roleping.active), inline=True),
EmbedField(
name="Bypass Users",
value="\n".join(bypass_users),
inline=True,
),
EmbedField(
name="Bypass Roles",
value="\n".join(bypass_roles),
inline=True,
),
],
)
admin = await ctx.guild.fetch_member(roleping.admin)
if not admin:
admin = self.bot.user
embed.set_author(name=admin.display_name, icon_url=admin.display_avatar.url)
embed.set_footer(text=f"{admin.username}#{admin.discriminator} | {admin.id}")
embeds.append(embed)
paginator = Paginator.create_from_embeds(self.bot, *embeds, timeout=300)
await paginator.send(ctx)
bypass = roleping.group(
name="bypass", description="Allow specific users/roles to ping rolepings"
)
@bypass.subcommand(
sub_cmd_name="user",
sub_cmd_description="Add a user as a bypass to a roleping",
)
@slash_option(
name="bypass", description="User to add", opt_type=OptionTypes.USER, required=True
)
@slash_option(
name="role", description="Rolepinged role", opt_type=OptionTypes.ROLE, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _roleping_bypass_user(
self, ctx: InteractionContext, bypass: Member, role: Role
) -> None:
roleping = await Roleping.find_one(q(guild=ctx.guild.id, role=role.id))
if not roleping:
await ctx.send(f"Roleping not configured for {role.mention}", ephemeral=True)
return
if bypass.id in roleping.bypass["users"]:
await ctx.send(f"{bypass.mention} already in bypass", ephemeral=True)
return
if len(roleping.bypass["users"]) == 10:
await ctx.send(
"Already have 10 users in bypass. Please consider using roles for roleping bypass",
ephemeral=True,
)
return
matching_role = list(filter(lambda x: x.id in roleping.bypass["roles"], bypass.roles))
if matching_role:
await ctx.send(
f"{bypass.mention} already has bypass via {matching_role[0].mention}",
ephemeral=True,
)
return
roleping.bypass["users"].append(bypass.id)
await roleping.commit()
await ctx.send(f"{bypass.display_name} user bypass added for `{role.name}`")
@bypass.subcommand(
sub_cmd_name="role",
sub_cmd_description="Add a role as a bypass to roleping",
)
@slash_option(
name="bypass", description="Role to add", opt_type=OptionTypes.ROLE, required=True
)
@slash_option(
name="role", description="Rolepinged role", opt_type=OptionTypes.ROLE, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _roleping_bypass_role(
self, ctx: InteractionContext, bypass: Role, role: Role
) -> None:
if bypass.id == ctx.guild.id:
await ctx.send("Cannot add `@everyone` as a bypass", ephemeral=True)
return
roleping = await Roleping.find_one(q(guild=ctx.guild.id, role=role.id))
if not roleping:
await ctx.send(f"Roleping not configured for {role.mention}", ephemeral=True)
return
if bypass.id in roleping.bypass["roles"]:
await ctx.send(f"{bypass.mention} already in bypass", ephemeral=True)
return
if len(roleping.bypass["roles"]) == 10:
await ctx.send(
"Already have 10 roles in bypass. "
"Please consider consolidating roles for roleping bypass",
ephemeral=True,
)
return
roleping.bypass["roles"].append(bypass.id)
await roleping.commit()
await ctx.send(f"{bypass.name} role bypass added for `{role.name}`")
restore = roleping.group(name="restore", description="Remove a roleping bypass")
@restore.subcommand(
sub_cmd_name="user",
sub_cmd_description="Remove a bypass from a roleping (restoring it)",
)
@slash_option(
name="bypass", description="User to remove", opt_type=OptionTypes.USER, required=True
)
@slash_option(
name="role", description="Rolepinged role", opt_type=OptionTypes.ROLE, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _roleping_restore_user(
self, ctx: InteractionContext, bypass: Member, role: Role
) -> None:
roleping: Roleping = await Roleping.find_one(q(guild=ctx.guild.id, role=role.id))
if not roleping:
await ctx.send(f"Roleping not configured for {role.mention}", ephemeral=True)
return
if bypass.id not in roleping.bypass.users:
await ctx.send(f"{bypass.mention} not in bypass", ephemeral=True)
return
roleping.bypass.users.remove(bypass.id)
await roleping.commit()
await ctx.send(f"{bypass.display_name} user bypass removed for `{role.name}`")
@restore.subcommand(
sub_cmd_name="role",
sub_cmd_description="Remove a bypass from a roleping (restoring it)",
)
@slash_option(
name="bypass", description="Role to remove", opt_type=OptionTypes.ROLE, required=True
)
@slash_option(
name="role", description="Rolepinged role", opt_type=OptionTypes.ROLE, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _roleping_restore_role(
self, ctx: InteractionContext, bypass: Role, role: Role
) -> None:
roleping: Roleping = await Roleping.find_one(q(guild=ctx.guild.id, role=role.id))
if not roleping:
await ctx.send(f"Roleping not configured for {role.mention}", ephemeral=True)
return
if bypass.id not in roleping.bypass.roles:
await ctx.send(f"{bypass.mention} not in bypass", ephemeral=True)
return
roleping.bypass.roles.remove(bypass.id)
await roleping.commit()
await ctx.send(f"{bypass.display_name} user bypass removed for `{role.name}`")