Add roleping expansion, closes #38

This commit is contained in:
Zeva Rose 2021-07-26 19:10:49 -06:00
parent feda3036ab
commit e48c987bcd
3 changed files with 364 additions and 46 deletions

View file

@ -10,6 +10,7 @@ from psutil import Process
from jarvis import logo, tasks, utils from jarvis import logo, tasks, utils
from jarvis.config import get_config from jarvis.config import get_config
from jarvis.db import DBManager from jarvis.db import DBManager
from jarvis.events import guild, member, message
if asyncio.get_event_loop().is_closed(): if asyncio.get_event_loop().is_closed():
asyncio.set_event_loop(asyncio.new_event_loop()) asyncio.set_event_loop(asyncio.new_event_loop())
@ -70,6 +71,14 @@ def run(ctx=None):
jarvis.max_messages = config.max_messages jarvis.max_messages = config.max_messages
tasks.init() tasks.init()
# Add event listeners
listeners = [
guild.GuildEventHandler(jarvis),
member.MemberEventHandler(jarvis),
message.MessageEventHandler(jarvis),
]
jarvis.run(config.token, bot=True, reconnect=True) jarvis.run(config.token, bot=True, reconnect=True)
for cog in jarvis.cogs: for cog in jarvis.cogs:
session = getattr(cog, "_session", None) session = getattr(cog, "_session", None)

View file

@ -1,71 +1,79 @@
from discord import Role from datetime import datetime, timedelta
from ButtonPaginator import Paginator
from discord import Member, Role
from discord.ext import commands from discord.ext import commands
from discord_slash import SlashContext, cog_ext from discord_slash import SlashContext, cog_ext
from discord_slash.model import ButtonStyle
from discord_slash.utils.manage_commands import create_option from discord_slash.utils.manage_commands import create_option
from jarvis.db.types import Setting from jarvis.db.types import Roleping
from jarvis.utils import build_embed
from jarvis.utils.cachecog import CacheCog
from jarvis.utils.field import Field
from jarvis.utils.permissions import admin_or_permissions
class RolepingCog(commands.Cog): class RolepingCog(CacheCog):
def __init__(self, bot): def __init__(self, bot):
self.bot = bot super().__init__(bot)
@cog_ext.cog_subcommand( @cog_ext.cog_subcommand(
base="roleping", base="roleping",
name="block", name="add",
description="Add a role to the roleping blocklist", description="Add a role to roleping",
options=[ options=[
create_option( create_option(
name="role", name="role",
description="Role to add to blocklist", description="Role to add to roleping",
option_type=8, option_type=8,
required=True, required=True,
) )
], ],
) )
@commands.has_permissions(administrator=True) @admin_or_permissions(manage_server=True)
async def _roleping_block(self, ctx: SlashContext, role: Role): async def _roleping_add(self, ctx: SlashContext, role: Role):
roles = Setting.get(guild=ctx.guild.id, setting="roleping") roleping = Roleping.get(guild=ctx.guild.id, role=role.id)
if not roles: if not roleping:
roles = Setting(guild=ctx.guild.id, setting="roleping", value=[]) roleping = Roleping(
role=role.id,
guild=ctx.guild.id,
admin=ctx.author.id,
active=True,
bypass={"roles": [], "users": []},
)
if role.id in roles.value: else:
await ctx.send( await ctx.send(
f"Role `{role.name}` already in blocklist.", hidden=True f"Role `{role.name}` already in roleping.", hidden=True
) )
return return
roles.value.append(role.id)
roles.update() roleping.insert()
await ctx.send(f"Role `{role.name}` added to blocklist.") await ctx.send(f"Role `{role.name}` added to roleping.")
@cog_ext.cog_subcommand( @cog_ext.cog_subcommand(
base="roleping", base="roleping",
name="allow", name="remove",
description="Remove a role from the roleping blocklist", description="Remove a role from the roleping",
options=[ options=[
create_option( create_option(
name="role", name="role",
description="Role to remove from blocklist", description="Role to remove from roleping",
option_type=8, option_type=8,
required=True, required=True,
) )
], ],
) )
@commands.has_permissions(administrator=True) @admin_or_permissions(manage_server=True)
async def _roleping_allow(self, ctx: SlashContext, role: Role): async def _roleping_remove(self, ctx: SlashContext, role: Role):
roles = Setting.get(guild=ctx.guild.id, setting="roleping") roleping = Roleping.get(guild=ctx.guild.id, role=role.id)
if not roles: if not roleping:
await ctx.send("No blocklist configured.", hidden=True) await ctx.send("Roleping does not exist", hidden=True)
return return
if role.id not in roles.value: roleping.delete()
await ctx.send( await ctx.send(f"Role `{role.name}` removed from roleping.")
f"Role `{role.name}` not in blocklist.", hidden=True
)
return
roles.value.remove(role.id)
roles.update()
await ctx.send(f"Role `{role.name}` removed blocklist.")
@cog_ext.cog_subcommand( @cog_ext.cog_subcommand(
base="roleping", base="roleping",
@ -73,19 +81,298 @@ class RolepingCog(commands.Cog):
description="List all blocklisted roles", description="List all blocklisted roles",
) )
async def _roleping_list(self, ctx: SlashContext): async def _roleping_list(self, ctx: SlashContext):
roles = Setting.get(guild=ctx.guild.id, setting="roleping") exists = self.check_cache(ctx)
if not roles: if exists:
await ctx.send("No blocklist configured.", hidden=True) await ctx.defer(hidden=True)
await ctx.send(
"Please use existing interaction: "
+ f"{exists['paginator']._message.jump_url}",
hidden=True,
)
return return
message = "Blocklisted Roles:\n```\n" rolepings = Roleping.get_many(guild=ctx.guild.id)
if not roles.value: if not rolepings:
await ctx.send("No roles blocklisted.", hidden=True) await ctx.send("No rolepings configured", hidden=True)
return return
for role in roles.value:
role = ctx.guild.get_role(role) embeds = []
if not role: for roleping in rolepings:
continue role = ctx.guild.get_role(roleping.role)
message += role.name + "\n" bypass_roles = list(
message += "```" filter(
await ctx.send(message) lambda x: x.id in roleping.bypass["roles"], ctx.guild.roles
)
)
bypass_roles = [
r.mention or "||`[redacted]`||" for r in bypass_roles
]
bypass_users = [
ctx.guild.get_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=[
Field(
name="Created At",
value=roleping.created_at.strftime(
"%a, %b %d, %Y %I:%M %p"
),
inline=False,
),
Field(name="Active", value=str(roleping.active)),
Field(
name="Bypass Users",
value="\n".join(bypass_users),
),
Field(
name="Bypass Roles",
value="\n".join(bypass_roles),
),
],
)
admin = ctx.guild.get_member(roleping.admin)
if not admin:
admin = self.bot.user
embed.set_author(
name=admin.nick or admin.name, icon_url=admin.avatar_url
)
embed.set_footer(
text=f"{admin.name}#{admin.discriminator} | {admin.id}"
)
embeds.append(embed)
paginator = Paginator(
bot=self.bot,
ctx=ctx,
embeds=embeds,
only=ctx.author,
timeout=60 * 5, # 5 minute timeout
disable_after_timeout=True,
use_extend=len(embeds) > 2,
left_button_style=ButtonStyle.grey,
right_button_style=ButtonStyle.grey,
basic_buttons=["", ""],
)
self.cache[hash(paginator)] = {
"user": ctx.author.id,
"guild": ctx.guild.id,
"timeout": datetime.utcnow() + timedelta(minutes=5),
"command": ctx.subcommand_name,
"paginator": paginator,
}
await paginator.start()
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="bypass",
name="user",
description="Add a user as a bypass to a roleping",
base_desc="Block roles from being pinged",
sub_group_desc="Allow specific users/roles to ping rolepings",
options=[
create_option(
name="user",
description="User to add",
option_type=6,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_server=True)
async def _roleping_bypass_user(
self, ctx: SlashContext, user: Member, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if user.id in roleping.bypass["users"]:
await ctx.send(f"{user.mention} already in bypass", hidden=True)
return
if len(roleping.bypass["users"]) == 10:
await ctx.send(
"Already have 10 users in bypass. "
"Please consider using roles for roleping bypass",
hidden=True,
)
return
matching_role = list(
filter(lambda x: x.id in roleping.bypass["roles"], user.roles)
)
if matching_role:
await ctx.send(
f"{user.mention} already has bypass "
f"via {matching_role[0].mention}",
hidden=True,
)
return
roleping.bypass["users"].append(user.id)
roleping.update()
await ctx.send(
f"{user.nick or user.name} user bypass added for `{rping.name}`"
)
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="bypass",
name="role",
description="Add a role as a bypass to a roleping",
base_desc="Block roles from being pinged",
sub_group_desc="Allow specific users/roles to ping rolepings",
options=[
create_option(
name="role",
description="Role to add",
option_type=8,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_server=True)
async def _roleping_bypass_role(
self, ctx: SlashContext, role: Role, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if role.id in roleping.bypass["roles"]:
await ctx.send(f"{role.mention} already in bypass", hidden=True)
return
if len(roleping.bypass["roles"]) == 10:
await ctx.send(
"Already have 10 roles in bypass. "
"Please consider consolidating roles for roleping bypass",
hidden=True,
)
return
roleping.bypass["roles"].append(role.id)
roleping.update()
await ctx.send(f"{role.name} role bypass added for `{rping.name}`")
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="restore",
name="user",
description="Remove a role bypass",
base_desc="Block roles from being pinged",
sub_group_desc="Remove a bypass from a roleping (restoring it)",
options=[
create_option(
name="user",
description="User to add",
option_type=6,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_server=True)
async def _roleping_restore_user(
self, ctx: SlashContext, user: Member, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if user.id not in roleping.bypass["users"]:
await ctx.send(f"{user.mention} not in bypass", hidden=True)
return
roleping.bypass["users"].delete(user.id)
roleping.update()
await ctx.send(
f"{user.nick or user.name} user bypass removed for `{rping.name}`"
)
@cog_ext.cog_subcommand(
base="roleping",
subcommand_group="restore",
name="role",
description="Remove a role bypass",
base_desc="Block roles from being pinged",
sub_group_desc="Remove a bypass from a roleping (restoring it)",
options=[
create_option(
name="role",
description="Role to add",
option_type=8,
required=True,
),
create_option(
name="rping",
description="Rolepinged role",
option_type=8,
required=True,
),
],
)
@admin_or_permissions(manage_server=True)
async def _roleping_restore_role(
self, ctx: SlashContext, role: Role, rping: Role
):
roleping = Roleping.get(guild=ctx.guild.id, role=rping.id)
if not roleping:
await ctx.send(
f"Roleping not configured for {rping.mention}", hidden=True
)
return
if role.id in roleping.bypass["roles"]:
await ctx.send(f"{role.mention} already in bypass", hidden=True)
return
if len(roleping.bypass["roles"]) == 10:
await ctx.send(
"Already have 10 roles in bypass. "
"Please consider consolidating roles for roleping bypass",
hidden=True,
)
return
roleping.bypass["roles"].append(role.id)
roleping.update()
await ctx.send(f"{role.name} role bypass added for `{rping.name}`")

View file

@ -28,6 +28,7 @@ coll_lookup = {
"Mute": "mutes", "Mute": "mutes",
"Purge": "purges", "Purge": "purges",
"Reminder": "reminders", "Reminder": "reminders",
"Roleping": "rolepings",
"Setting": "settings", "Setting": "settings",
"Starboard": "starboard", "Starboard": "starboard",
"Star": "stars", "Star": "stars",
@ -460,6 +461,27 @@ class Reminder(MongoObject, ActiveObject):
created_at: datetime = field(default_factory=datetime.utcnow) created_at: datetime = field(default_factory=datetime.utcnow)
@dataclass
class Roleping(MongoObject, ActiveObject):
"""
Guild Roleping object
:param _id: MongoDB ID
:param role: Blocked role
:param guild: ID of origin guild
:param admin: Admin who added roleping
:param bypass: Roles/users who may bypass this roleping
:param active: If the roleping is disabled
:param created_at: Time the roleping was created
"""
role: int
guild: int
admin: int
bypass: dict
created_at: datetime = field(default_factory=datetime.utcnow)
@dataclass @dataclass
class Setting(MongoObject): class Setting(MongoObject):
""" """