Addes self-role groups.

This commit is contained in:
Vegard Berg 2022-08-03 19:48:01 +02:00
parent 09b87df3bc
commit 7e2f113c52
3 changed files with 519 additions and 17 deletions

View File

@ -69,4 +69,42 @@ By default it is accessible to those with the *Kick User* permission.
## User-infractions
`user-infractions <user>`
Displays the infractions of a user.
Displays the infractions of a user.
## Adm
Server administrative commands.
# Join/leave messages
Join and leave messages allow the server and the member to be referenced.
## Member
Can be accessed with `{member}`.
- `{member.id}`
The member's unique Snowflake ID.
- `{member.bot}`
Whether or not this member is a bot.
- `{member.display_name}`
The member's nickname, if any, or their username.
- `{member.joined_at}`
The time at which the member joined, as a timestamp.
- `{member.username}`
The member's username.
- `{member.discriminator}`
The member's discriminator, i.e. the four digits following their username and the `#` symbol.
- `{member.mention}`
A mention of the member.
## Guild (server)
Can be accessed with `{guild}`.
- `{guild.id}`
The unique Snowflake ID of the guild.
- `{guild.member_count}`
The total number of members in the guild.
- `{guild.name}`
The name of the guild.
- `{guild.description}`
The Discovery description of the guild.
- `{guild.created_at}`
A timestamp of when the guild was created.

View File

@ -1,5 +1,5 @@
from re import S
from typing import List, Optional
from typing import Dict, List, Optional, Tuple
import logging
from naff import (
Extension,
@ -13,8 +13,18 @@ from naff import (
Embed,
EmbedField,
AutocompleteContext,
Select,
SelectOption,
listen,
component_callback,
)
from naff.api import events
from naff import components
from database import (
SelfRoles as SelfRolesModel,
SelfRoleGroups as SelfRoleGroupsModel,
SelfRoleGroupRelations as SelfRoleGroupRelationsModel,
)
from database import SelfRoles as SelfRolesModel
class SelfRoles(Extension):
@ -94,6 +104,397 @@ class SelfRoles(Extension):
f"Removed self-role {role.mention} from the database.", ephemeral=True
)
@slash_command(
name="role-group",
description="Manage self-role groups",
sub_cmd_name="add-group",
sub_cmd_description="Add a self-role group",
dm_permission=False,
default_member_permissions=Permissions.MANAGE_ROLES,
)
@slash_option(
name="group",
description="Group to add",
required=True,
opt_type=OptionTypes.STRING,
)
@slash_option(
name="description",
description="Description of the group",
required=False,
opt_type=OptionTypes.STRING,
)
async def role_group_add_group(
self, ctx: InteractionContext, group: str, description: str = None
):
await ctx.defer(ephemeral=True)
SelfRoleGroupsModel.insert(
guild_id=int(ctx.guild_id),
group_name=group,
group_description=description,
).on_conflict_replace().execute()
await ctx.send(
f"Added self-role group {group} to the database."
f"\n{'Description: ' + description if description is not None else ''}",
ephemeral=True,
)
@slash_command(
name="role-group",
description="Manage self-role groups",
sub_cmd_name="remove-group",
sub_cmd_description="Remove a self-role group",
dm_permission=False,
default_member_permissions=Permissions.MANAGE_ROLES,
)
@slash_option(
name="group",
description="Group to remove",
required=True,
opt_type=OptionTypes.STRING,
)
async def role_group_remove_group(self, ctx: InteractionContext, group: str):
await ctx.defer(ephemeral=True)
SelfRoleGroupRelationsModel.delete().where(
SelfRoleGroupRelationsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupRelationsModel.group_name == group,
).execute()
SelfRoleGroupsModel.delete().where(
SelfRoleGroupsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupsModel.group_name == group,
).execute()
await ctx.send(
f"Removed self-role group {group} from the database.", ephemeral=True
)
@slash_command(
name="role-group",
description="Manage self-role groups",
sub_cmd_name="add-role",
sub_cmd_description="Add a role to a self-role group",
dm_permission=False,
default_member_permissions=Permissions.MANAGE_ROLES,
)
@slash_option(
name="group",
description="Group to add the role to",
required=True,
opt_type=OptionTypes.STRING,
autocomplete=True,
)
@slash_option(
name="role",
description="Role to add to the group",
required=True,
opt_type=OptionTypes.STRING,
autocomplete=True,
)
async def role_group_add_role(self, ctx: InteractionContext, group: str, role: str):
await ctx.defer(ephemeral=True)
r: Role = await ctx.guild.fetch_role(role)
if r is None:
await ctx.send(f"Role {role} not found in this server.", ephemeral=True)
return
SelfRoleGroupRelationsModel.insert(
guild_id=int(ctx.guild_id),
group_name=group,
role_id=int(role),
).on_conflict_replace().execute()
await ctx.send(f"Added role {r.mention} to the group {group}.", ephemeral=True)
@role_group_add_role.autocomplete("group")
async def role_group_add_role_group_autocomplete(
self, ctx: AutocompleteContext, group: str
):
groups_q: List[SelfRoleGroupsModel] = (
SelfRoleGroupsModel.select()
.where(
SelfRoleGroupsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupsModel.group_name.startswith(group),
)
.execute()
)
await ctx.send(choices=[g.group_name for g in groups_q])
@role_group_add_role.autocomplete("role")
async def role_group_add_role_role_autocomplete(
self, ctx: AutocompleteContext, role: str, **kwargs
):
roles_q: List[SelfRolesModel] = (
SelfRolesModel.select()
.where(
SelfRolesModel.guild_id == int(ctx.guild_id),
SelfRolesModel.role_name.startswith(role),
)
.execute()
)
await ctx.send(
choices=[{"name": r.role_name, "value": str(r.role_id)} for r in roles_q]
)
@slash_command(
name="role-group",
description="Manage self-role groups",
sub_cmd_name="remove-role",
sub_cmd_description="Remove a role from a self-role group",
dm_permission=False,
default_member_permissions=Permissions.MANAGE_ROLES,
)
@slash_option(
name="group",
description="Group to remove the role from",
required=True,
opt_type=OptionTypes.STRING,
autocomplete=True,
)
@slash_option(
name="role",
description="Role to remove from the group",
required=True,
opt_type=OptionTypes.STRING,
autocomplete=True,
)
async def role_group_remove_role(
self, ctx: InteractionContext, group: str, role: str
):
await ctx.defer(ephemeral=True)
r: Role = await ctx.guild.fetch_role(role)
if r is None:
await ctx.send(f"Role {role} not found in this server.", ephemeral=True)
return
SelfRoleGroupRelationsModel.delete().where(
SelfRoleGroupRelationsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupRelationsModel.group_name == group,
SelfRoleGroupRelationsModel.role_id == int(role),
).execute()
await ctx.send(
f"Removed role {r.mention} from the group {group}.", ephemeral=True
)
@role_group_remove_role.autocomplete("group")
async def role_group_remove_role_group_autocomplete(
self, ctx: AutocompleteContext, group: str
):
groups_q: List[SelfRoleGroupsModel] = (
SelfRoleGroupsModel.select()
.where(
SelfRoleGroupsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupsModel.group_name.startswith(group),
)
.execute()
)
await ctx.send(choices=[g.group_name for g in groups_q])
@role_group_remove_role.autocomplete("role")
async def role_group_remove_role_role_autocomplete(
self, ctx: AutocompleteContext, role: str, **kwargs
):
roles_q = (
SelfRolesModel.select()
.join(
SelfRoleGroupRelationsModel,
on=(SelfRolesModel.role_id == SelfRoleGroupRelationsModel.role_id),
)
.where(
SelfRolesModel.guild_id == int(ctx.guild_id),
SelfRoleGroupRelationsModel.group_name == kwargs["group"],
SelfRolesModel.role_name.startswith(role),
)
.execute()
)
await ctx.send(
choices=[{"name": r.role_name, "value": str(r.role_id)} for r in roles_q]
)
@slash_command(
name="role-group",
description="Manage self-role groups",
sub_cmd_name="list-groups",
sub_cmd_description="List all self-role groups",
dm_permission=False,
default_member_permissions=Permissions.MANAGE_ROLES,
)
async def role_group_list_groups(self, ctx: InteractionContext):
await ctx.defer(ephemeral=True)
embeds: List[Embed] = []
groups_q = (
SelfRolesModel.select(
SelfRolesModel.role_name,
SelfRolesModel.role_description,
SelfRoleGroupsModel.group_name,
SelfRoleGroupsModel.group_description,
)
.join(
SelfRoleGroupRelationsModel,
on=(SelfRoleGroupRelationsModel.role_id == SelfRolesModel.role_id),
)
.join(
SelfRoleGroupsModel,
on=(
SelfRoleGroupRelationsModel.group_name
== SelfRoleGroupsModel.group_name
),
)
.where(SelfRoleGroupsModel.guild_id == int(ctx.guild_id))
.objects()
)
groups: Dict[str, List[str, List[Tuple[str, str]]]] = {}
for row in groups_q:
if row.group_name not in groups:
groups[row.group_name] = [
row.group_description,
[(row.role_name, row.role_description)],
]
else:
groups[row.group_name][1].append((row.role_name, row.role_description))
for group, data in groups.items():
embed = Embed(title=group, description=data[0])
for role in data[1]:
embed.add_field(name=role[0], value=role[1], inline=False)
embeds.append(embed)
await ctx.send(
embeds=embeds,
ephemeral=True,
)
@slash_command(
name="role-group",
description="Manage self-role groups",
sub_cmd_name="generate",
sub_cmd_description="Generate a message with a selection of roles from a group",
dm_permission=False,
default_member_permissions=Permissions.MANAGE_ROLES,
)
@slash_option(
name="group",
description="Group to generate the message from",
required=True,
opt_type=OptionTypes.STRING,
autocomplete=True,
)
async def role_group_generate(self, ctx: InteractionContext, group: str):
await ctx.defer(ephemeral=False)
roles_q: List[SelfRolesModel] = (
SelfRolesModel.select(SelfRolesModel.role_name, SelfRolesModel.role_id, SelfRolesModel.role_description)
.join(SelfRoleGroupRelationsModel, on=(SelfRolesModel.role_id == SelfRoleGroupRelationsModel.role_id))
.where(
SelfRoleGroupRelationsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupRelationsModel.group_name == group,
).objects()
)
group_q = SelfRoleGroupsModel.get(SelfRoleGroupsModel.group_name == group)
options = []
for role in roles_q:
opt = SelectOption(
label=role.role_name,
value=str(role.role_id),
description=role.role_description,
)
options.append(opt)
select = Select(
options=options,
placeholder=f"Select roles from the group {group}",
custom_id=f"role-group-assign:{group}",
min_values=0,
max_values=len(options),
)
await ctx.send(
embed=Embed(
title=f"{group}",
description=group_q.group_description,
),
components=select,
)
@role_group_generate.autocomplete("group")
async def role_group_generate_group_autocomplete(
self, ctx: AutocompleteContext, group: str
):
groups_q: List[SelfRoleGroupsModel] = (
SelfRoleGroupsModel.select()
.where(
SelfRoleGroupsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupsModel.group_name.startswith(group),
)
.execute()
)
await ctx.send(choices=[g.group_name for g in groups_q])
@listen(events.Select)
async def on_role_selected(self, event: events.Select):
ctx = event.context
await ctx.defer(ephemeral=True)
if not ctx.custom_id.startswith("role-group-assign"):
return
group = ctx.custom_id.split(":")[1]
role_ids = [int(r) for r in ctx.values]
roles_q: List[SelfRolesModel] = (
SelfRolesModel.select(SelfRolesModel.role_name, SelfRolesModel.role_id, SelfRolesModel.requires, SelfRoleGroupRelationsModel.group_name)
.join(SelfRoleGroupRelationsModel, on=(SelfRolesModel.role_id == SelfRoleGroupRelationsModel.role_id))
.where(
SelfRoleGroupRelationsModel.guild_id == int(ctx.guild_id),
SelfRoleGroupRelationsModel.group_name == group,
).objects()
)
actions = []
for role in roles_q:
if role.role_id in role_ids:
if not ctx.author.has_role(role.role_id):
if not role.requires is None:
if not ctx.author.has_role(role.requires):
actions.append(f"{role.role_name} requires {role.requires} and was not added.")
continue
await ctx.author.add_role(role.role_id)
actions.append(f"Added role {role.role_name}")
else:
if ctx.author.has_role(role.role_id):
if not role.requires is None:
if not ctx.author.has_role(role.requires):
actions.append(f"{role.role_name} requires {role.requires} to be managed, and was therefore not removed.")
continue
await ctx.author.remove_role(role.role_id)
actions.append(f"Removed role {role.role_name}")
await ctx.send(
content="\n".join(actions),
ephemeral=True,
)
@slash_command(
name="role",
description="Manage your roles",
@ -146,7 +547,7 @@ class SelfRoles(Extension):
@role_add.autocomplete("role")
async def role_add_autocomplete_role(
self, ctx: AutocompleteContext, role: str = None
self, ctx: AutocompleteContext, role: str = None, **kwargs
):
choices = []
@ -226,7 +627,7 @@ class SelfRoles(Extension):
@role_remove.autocomplete("role")
async def role_remove_autocomplete_role(
self, ctx: AutocompleteContext, role: str = None
self, ctx: AutocompleteContext, role: str = None, **kwargs
):
choices = []
@ -300,4 +701,6 @@ class SelfRoles(Extension):
def setup(client: Client):
SelfRoles(client)
SelfRolesModel.create_table()
SelfRoleGroupsModel.create_table()
SelfRoleGroupRelationsModel.create_table()
logging.info("SelfRoles extension loaded.")

View File

@ -19,6 +19,9 @@ from peewee import (
db = None
"""The database connection."""
load_dotenv()
if (postgres_url := os.getenv("HEIMDALLR_POSTGRES_URL")) is not None:
db = PostgresqlDatabase(postgres_url)
else:
@ -26,14 +29,24 @@ else:
class Infractions(Model):
"""A model for infractions."""
id = AutoField()
"""The ID of the infraction."""
guild_id = BigIntegerField()
"""The guild ID of the infraction."""
user_id = BigIntegerField()
"""The user ID of the user receiving the infraction."""
at_time = DateTimeField(default=datetime.datetime.now)
"""The time at which the infraction was received."""
weight = FloatField(default=1.0)
"""The weight/severity of the infraction."""
reason = TextField(null=True)
"""The reason for the infraction."""
given_by = BigIntegerField()
"""The user ID of the user giving the infraction."""
silent = BooleanField(default=False)
"""Whether the infraction should be silent."""
class Meta:
table_name = "Infractions"
@ -41,11 +54,18 @@ class Infractions(Model):
class GuildSettings(Model):
"""A model for guild settings."""
guild_id = BigIntegerField(primary_key=True)
"""The guild ID of the guild settings."""
admin_channel = BigIntegerField(null=True)
"""The channel ID of the admin channel."""
use_name_filter = BooleanField(default=False)
"""Whether the bot should use a name filter."""
use_gatekeep = BooleanField(default=False)
"""Whether the bot should use gatekeep."""
use_logging = BooleanField(default=False)
"""Whether the bot should use logging."""
class Meta:
table_name = "GuildSettings"
@ -53,12 +73,20 @@ class GuildSettings(Model):
class JoinLeave(Model):
"""A model for join/leave messages."""
guild_id = BigIntegerField(primary_key=True)
"""The guild ID of the guild settings."""
join_message = TextField(null=True)
"""The join message."""
leave_message = TextField(null=True)
"""The leave message."""
message_channel = BigIntegerField(null=True)
"""The channel ID of the channel to send the message in."""
join_message_enabled = BooleanField(default=True)
"""Whether the join message is enabled."""
leave_message_enabled = BooleanField(default=False)
"""Whether the leave message is enabled."""
class Meta:
table_name = "JoinLeave"
@ -66,11 +94,18 @@ class JoinLeave(Model):
class SelfRoles(Model):
"""A model for self-assignable roles."""
guild_id = BigIntegerField()
"""The guild ID of the guild for the self-role."""
role_id = BigIntegerField()
"""The role ID of the self-role."""
role_name = TextField()
"""The name of the self-role."""
role_description = TextField(null=True)
"""The description of the self-role."""
requires = BigIntegerField(null=True)
"""The role ID of the role required to assign the self-role, if any."""
class Meta:
primary_key = CompositeKey("guild_id", "role_id")
@ -78,11 +113,48 @@ class SelfRoles(Model):
database = db
class ConditionalRoles(Model):
class SelfRoleGroups(Model):
"""A model for self-role groups."""
guild_id = BigIntegerField()
"""The guild ID of the guild for the self-role group."""
group_name = TextField()
"""The name of the self-role group."""
group_description = TextField(null=True)
"""The description of the self-role group."""
class Meta:
primary_key = CompositeKey("guild_id", "group_name")
table_name = "SelfRoleGroups"
database = db
class SelfRoleGroupRelations(Model):
"""A model for self-role group relations."""
guild_id = ForeignKeyField(SelfRoleGroups, field="guild_id", backref="GuildRelation")
"""The guild ID of the guild for the self-role group."""
group_name = ForeignKeyField(SelfRoleGroups, field="group_name", backref="GroupRelation")
"""The name of the self-role group."""
role_id = BigIntegerField()
"""The role ID of the self-role."""
class Meta:
primary_key = CompositeKey("guild_id", "group_name", "role_id")
table_name = "SelfRoleGroupRelations"
database = db
class ConditionalRoles(Model):
"""A model for conditional roles."""
guild_id = BigIntegerField()
"""The guild ID of the guild for the conditional role."""
role_id = BigIntegerField()
"""The role ID of the conditional role."""
condition = CharField(max_length=16)
"""The condition for the conditional role."""
condition_data = TextField()
"""The data for the condition."""
class Meta:
primary_key = CompositeKey("guild_id", "role_id")
@ -131,14 +203,3 @@ class BadNames(Model):
class Meta:
table_name = "BadNames"
database = db
class NotifyWords(Model):
id = AutoField()
guild_id = BigIntegerField()
word = TextField()
user_to_notify = BigIntegerField()
class Meta:
table_name = "NotifyWords"
database = db