jarvis-bot/jarvis/cogs/settings.py

287 lines
12 KiB
Python

"""JARVIS Settings Management Cog."""
import asyncio
import logging
from typing import Any
from jarvis_core.db import q
from jarvis_core.db.models import Setting
from naff import Client, Cog, InteractionContext
from naff.models.discord.channel import GuildText
from naff.models.discord.components import ActionRow, Button, ButtonStyles
from naff.models.discord.embed import EmbedField
from naff.models.discord.enums import Permissions
from naff.models.discord.role import Role
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 SettingsCog(Cog):
"""JARVIS Settings Management Cog."""
def __init__(self, bot: Client):
self.bot = bot
self.logger = logging.getLogger(__name__)
async def update_settings(self, setting: str, value: Any, guild: int) -> bool:
"""Update a guild setting."""
existing = await Setting.find_one(q(setting=setting, guild=guild))
if not existing:
existing = Setting(setting=setting, guild=guild, value=value)
existing.value = value
updated = await existing.commit()
return updated is not None
async def delete_settings(self, setting: str, guild: int) -> bool:
"""Delete a guild setting."""
existing = await Setting.find_one(q(setting=setting, guild=guild))
if existing:
return await existing.delete()
return False
settings = SlashCommand(name="settings", description="Control guild settings")
set_ = settings.group(name="set", description="Set a setting")
unset = settings.group(name="unset", description="Unset a setting")
@set_.subcommand(
sub_cmd_name="modlog",
sub_cmd_description="Set Moglod channel",
)
@slash_option(
name="channel", description="ModLog Channel", opt_type=OptionTypes.CHANNEL, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _set_modlog(self, ctx: InteractionContext, channel: GuildText) -> None:
if not isinstance(channel, GuildText):
await ctx.send("Channel must be a GuildText", ephemeral=True)
return
await self.update_settings("modlog", channel.id, ctx.guild.id)
await ctx.send(f"Settings applied. New modlog channel is {channel.mention}")
@set_.subcommand(
sub_cmd_name="activitylog",
sub_cmd_description="Set Activitylog channel",
)
@slash_option(
name="channel",
description="Activitylog Channel",
opt_type=OptionTypes.CHANNEL,
required=True,
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _set_activitylog(self, ctx: InteractionContext, channel: GuildText) -> None:
if not isinstance(channel, GuildText):
await ctx.send("Channel must be a GuildText", ephemeral=True)
return
await self.update_settings("activitylog", channel.id, ctx.guild.id)
await ctx.send(f"Settings applied. New activitylog channel is {channel.mention}")
@set_.subcommand(sub_cmd_name="massmention", sub_cmd_description="Set massmention output")
@slash_option(
name="amount",
description="Amount of mentions (0 to disable)",
opt_type=OptionTypes.INTEGER,
required=True,
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _set_massmention(self, ctx: InteractionContext, amount: int) -> None:
await ctx.defer()
await self.update_settings("massmention", amount, ctx.guild.id)
await ctx.send(f"Settings applied. New massmention limit is {amount}")
@set_.subcommand(sub_cmd_name="verified", sub_cmd_description="Set verified role")
@slash_option(
name="role", description="Verified role", opt_type=OptionTypes.ROLE, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _set_verified(self, ctx: InteractionContext, role: Role) -> None:
if role.id == ctx.guild.id:
await ctx.send("Cannot set verified to `@everyone`", ephemeral=True)
return
if role.bot_managed or not role.is_assignable:
await ctx.send(
"Cannot assign this role, try lowering it below my role or using a different role",
ephemeral=True,
)
return
await ctx.defer()
await self.update_settings("verified", role.id, ctx.guild.id)
await ctx.send(f"Settings applied. New verified role is `{role.name}`")
@set_.subcommand(sub_cmd_name="unverified", sub_cmd_description="Set unverified role")
@slash_option(
name="role", description="Unverified role", opt_type=OptionTypes.ROLE, required=True
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _set_unverified(self, ctx: InteractionContext, role: Role) -> None:
if role.id == ctx.guild.id:
await ctx.send("Cannot set unverified to `@everyone`", ephemeral=True)
return
if role.bot_managed or not role.is_assignable:
await ctx.send(
"Cannot assign this role, try lowering it below my role or using a different role",
ephemeral=True,
)
return
await ctx.defer()
await self.update_settings("unverified", role.id, ctx.guild.id)
await ctx.send(f"Settings applied. New unverified role is `{role.name}`")
@set_.subcommand(
sub_cmd_name="noinvite", sub_cmd_description="Set if invite deletion should happen"
)
@slash_option(name="active", description="Active?", opt_type=OptionTypes.BOOLEAN, required=True)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _set_invitedel(self, ctx: InteractionContext, active: bool) -> None:
await ctx.defer()
await self.update_settings("noinvite", active, ctx.guild.id)
await ctx.send(f"Settings applied. Automatic invite active: {active}")
@set_.subcommand(sub_cmd_name="notify", sub_cmd_description="Notify users of admin action?")
@slash_option(name="active", description="Notify?", opt_type=OptionTypes.BOOLEAN, required=True)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _set_notify(self, ctx: InteractionContext, active: bool) -> None:
await ctx.defer()
await self.update_settings("notify", active, ctx.guild.id)
await ctx.send(f"Settings applied. Notifications active: {active}")
# Unset
@unset.subcommand(
sub_cmd_name="modlog",
sub_cmd_description="Unset Modlog channel",
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _unset_modlog(self, ctx: InteractionContext) -> None:
await ctx.defer()
await self.delete_settings("modlog", ctx.guild.id)
await ctx.send("Setting `modlog` unset")
@unset.subcommand(
sub_cmd_name="activitylog",
sub_cmd_description="Unset Activitylog channel",
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _unset_activitylog(self, ctx: InteractionContext) -> None:
await ctx.defer()
await self.delete_settings("activitylog", ctx.guild.id)
await ctx.send("Setting `activitylog` unset")
@unset.subcommand(sub_cmd_name="massmention", sub_cmd_description="Unset massmention output")
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _unset_massmention(self, ctx: InteractionContext) -> None:
await ctx.defer()
await self.delete_settings("massmention", ctx.guild.id)
await ctx.send("Setting `massmention` unset")
@unset.subcommand(sub_cmd_name="verified", sub_cmd_description="Unset verified role")
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _unset_verified(self, ctx: InteractionContext) -> None:
await ctx.defer()
await self.delete_settings("verified", ctx.guild.id)
await ctx.send("Setting `verified` unset")
@unset.subcommand(sub_cmd_name="unverified", sub_cmd_description="Unset unverified role")
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _unset_unverified(self, ctx: InteractionContext) -> None:
await ctx.defer()
await self.delete_settings("unverified", ctx.guild.id)
await ctx.send("Setting `unverified` unset")
@unset.subcommand(
sub_cmd_name="noinvite", sub_cmd_description="Unset if invite deletion should happen"
)
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _unset_invitedel(self, ctx: InteractionContext, active: bool) -> None:
await ctx.defer()
await self.delete_settings("noinvite", ctx.guild.id)
await ctx.send(f"Setting `{active}` unset")
@unset.subcommand(sub_cmd_name="notify", sub_cmd_description="Unset admin action notifications")
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _unset_notify(self, ctx: InteractionContext) -> None:
await ctx.defer()
await self.delete_settings("notify", ctx.guild.id)
await ctx.send("Setting `notify` unset")
@settings.subcommand(sub_cmd_name="view", sub_cmd_description="View settings")
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _view(self, ctx: InteractionContext) -> None:
settings = Setting.find(q(guild=ctx.guild.id))
fields = []
async for setting in settings:
value = setting.value
if setting.setting in ["unverified", "verified", "mute"]:
try:
value = await ctx.guild.fetch_role(value)
except KeyError:
await setting.delete()
value = None
if value:
value = value.mention
else:
value = "||`[redacted]`||"
elif setting.setting in ["activitylog", "modlog"]:
value = await ctx.guild.fetch_channel(value)
if value:
value = value.mention
else:
value = "||`[redacted]`||"
elif setting.setting == "rolegiver":
value = ""
for _role in setting.value:
nvalue = await ctx.guild.fetch_role(_role)
if nvalue:
value += "\n" + nvalue.mention
else:
value += "\n||`[redacted]`||"
fields.append(EmbedField(name=setting.setting, value=str(value) or "N/A", inline=False))
embed = build_embed(title="Current Settings", description="", fields=fields)
await ctx.send(embed=embed)
@settings.subcommand(sub_cmd_name="clear", sub_cmd_description="Clear all settings")
@check(admin_or_permissions(Permissions.MANAGE_GUILD))
async def _clear(self, ctx: InteractionContext) -> None:
components = [
ActionRow(
Button(style=ButtonStyles.RED, emoji="✖️", custom_id="no"),
Button(style=ButtonStyles.GREEN, emoji="✔️", custom_id="yes"),
)
]
message = await ctx.send("***Are you sure?***", components=components)
try:
context = await self.bot.wait_for_component(
check=lambda x: ctx.author.id == x.context.author.id,
messages=message,
timeout=60 * 5,
)
content = "***Are you sure?***"
if context.context.custom_id == "yes":
async for setting in Setting.find(q(guild=ctx.guild.id)):
await setting.delete()
content = "Guild settings cleared"
else:
content = "Guild settings not cleared"
for row in components:
for component in row.components:
component.disabled = True
await context.context.edit_origin(content=content, components=components)
except asyncio.TimeoutError:
for row in components:
for component in row.components:
component.disabled = True
await message.edit(content="Guild settings not cleared", components=components)
def setup(bot: Client) -> None:
"""Add SettingsCog to JARVIS"""
SettingsCog(bot)