Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config #287

Merged
merged 16 commits into from
Dec 10, 2023
6 changes: 0 additions & 6 deletions bot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ class ErrorHandling(BaseModel):
webhook_url: str


class CustomRoles(BaseModel):
log_channel_id: int
divider_role_id: int


class YouTube(BaseModel):
channel_id: str
text_channel_id: int
Expand All @@ -109,7 +104,6 @@ class Settings(BaseSettings):
tags: Tags
hastebin: Hastebin
errors: ErrorHandling
custom_roles: CustomRoles
youtube: YouTube

class Config:
Expand Down
20 changes: 20 additions & 0 deletions bot/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from discord.ext import commands, tasks

from bot.config import settings
from bot.models import GuildConfig
from bot.services import http, paste
from bot.services.paste import Document
from utils.errors import IgnorableException
Expand Down Expand Up @@ -79,6 +80,25 @@ async def sync_commands(self) -> None:
log.info("Commands synced.")
log.info(f"Successfully logged in as {self.user}. In {len(self.guilds)} guilds")

async def on_ready(self):
query = """SELECT * FROM guild_configs"""
data = await GuildConfig.fetch(query)
for index, guild_id in enumerate(self.guilds):
if guild_id.id != data[index].guild_id:
query = """
INSERT INTO guild_configs (guild_id)
VALUES ($1)
"""
await GuildConfig.execute(query, guild_id.id)
log.info(f"Inserted new config for guild: {guild_id.name}, id: {guild_id.id}")
SylteA marked this conversation as resolved.
Show resolved Hide resolved

async def on_guild_join(self, guild):
query = """INSERT INTO guild_configs (guild_id)
VALUES ($1)
ON CONFLICT (guild_id)
DO NOTHING"""
await GuildConfig.execute(query, guild.id)

async def on_message(self, message):
await self.wait_until_ready()

Expand Down
79 changes: 75 additions & 4 deletions bot/extensions/custom_roles/commands.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
from __future__ import annotations

import datetime

import discord
from discord import app_commands, utils
from discord.ext import commands

from bot import core
from bot.config import settings
from bot.models import CustomRole
from bot.models import CustomRole, GuildConfig


class CustomRoles(commands.Cog):
custom_roles = app_commands.Group(
name="custom_roles",
description="Custom Role commands",
default_permissions=discord.Permissions(administrator=True),
)

config = app_commands.Group(
parent=custom_roles,
name="config",
description="Set configuration for custom role",
default_permissions=discord.Permissions(administrator=True),
)

def __init__(self, bot):
self.bot = bot

Expand Down Expand Up @@ -50,8 +64,15 @@ async def myrole(
# Create and assign the role to user
role = await interaction.guild.create_role(name=name, colour=color or discord.Color.random())

divider_role = interaction.guild.get_role(settings.custom_roles.divider_role_id)
await role.edit(position=divider_role.position + 1)
divider_role_query = """
SELECT divider_role_id
FROM guild_configs
WHERE guild_id = $1"""
divider_role_id = await GuildConfig.fetchval(divider_role_query, interaction.guild.id)

if divider_role_id is not None:
divider_role = interaction.guild.get_role(divider_role_id)
await role.edit(position=divider_role.position + 1)

record = await CustomRole.ensure_exists(
guild_id=interaction.guild.id,
Expand Down Expand Up @@ -105,6 +126,56 @@ async def myrole(
ephemeral=True,
)

@config.command(name="log-channel")
@app_commands.describe(channel="New channel")
async def log_channel(self, interaction: discord.Interaction, channel: discord.TextChannel = None):
"""Set custom role log channel"""
query = """UPDATE guild_configs
SET custom_role_log_channel_id = $1
WHERE guild_id = $2"""
if channel is None:
await GuildConfig.execute(query, None, interaction.guild.id)
return await interaction.response.send_message("No Role Log Channel Set")

await GuildConfig.execute(query, channel.id, interaction.guild.id)
return await interaction.response.send_message(f"Log Channel set to {channel.mention}")
SylteA marked this conversation as resolved.
Show resolved Hide resolved

@config.command(name="divider-role-id")
@app_commands.describe(role="divider role")
async def divider_role(self, interaction: discord.Interaction, role: discord.Role = None):
"""Set Divider role"""
query = """UPDATE guild_configs
SET divider_role_id = $1
WHERE guild_id = $2"""
if role is None:
await GuildConfig.execute(query, None, interaction.guild.id)
return await interaction.response.send_message("No Divider Role Set")

await GuildConfig.execute(query, role.id, interaction.guild.id)
return await interaction.response.send_message(f"Divider role has been configured to {role.mention}")

@config.command(name="show")
async def show(self, interaction: discord.Interaction):
"""Return the current custom role configuration"""
query = """
SELECT * FROM guild_configs
WHERE guild_id = $1
"""
data = await GuildConfig.fetchrow(query, interaction.guild.id)
embed = discord.Embed(
title=f"Server Information - {interaction.guild.name}",
description="Custom Role Configuration",
color=discord.Color.blue(),
timestamp=datetime.datetime.utcnow(),
)

embed.set_thumbnail(url=interaction.guild.icon.url)
embed.add_field(
name="Custom Role Log Channel", value=interaction.guild.get_channel(data.custom_role_log_channel_id)
SylteA marked this conversation as resolved.
Show resolved Hide resolved
)

return await interaction.response.send_message(embed=embed)


async def setup(bot: commands.Bot):
await bot.add_cog(CustomRoles(bot=bot))
25 changes: 18 additions & 7 deletions bot/extensions/custom_roles/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
from discord.ext import commands

from bot import core
from bot.config import settings
from bot.models.custom_roles import CustomRole
from bot.models import CustomRole, GuildConfig


class CustomRoleEvents(commands.Cog):
Expand All @@ -16,9 +15,11 @@ def __init__(self, bot: core.DiscordBot):
self.bot = bot
self.updated_at: dict[int, datetime] = {}

@property
def custom_roles_logs_channel(self) -> discord.TextChannel | None:
return self.bot.guild.get_channel(settings.custom_roles.log_channel_id)
async def get_custom_roles_logs_channel(self, guild_id: int):
query = """SELECT custom_role_log_channel_id FROM guild_configs
WHERE guild_id = $1"""
data = await GuildConfig.fetchval(query, guild_id)
return self.bot.guild.get_channel(data)

@commands.Cog.listener()
async def on_guild_role_update(self, before: discord.Role, after: discord.Role):
Expand Down Expand Up @@ -54,7 +55,12 @@ async def on_custom_role_create(self, custom_role: CustomRole):
embed.set_thumbnail(url=user.avatar)
embed.set_footer(text=f"user_id: {custom_role.user_id}")

return await self.custom_roles_logs_channel.send(embed=embed)
channel = await self.get_custom_roles_logs_channel(custom_role.guild_id)
# Return if no log channel has been set
if channel is None:
return

return await channel.send(embed=embed)

@commands.Cog.listener()
async def on_custom_role_update(self, before: CustomRole, after: CustomRole):
Expand Down Expand Up @@ -85,4 +91,9 @@ async def on_custom_role_update(self, before: CustomRole, after: CustomRole):
embed.set_author(name=user.name, icon_url=user.avatar)
embed.set_footer(text=f"user_id: {after.user_id}")

return await self.custom_roles_logs_channel.send(embed=embed)
channel = await self.get_custom_roles_logs_channel(before.guild_id)
# Return if no log channel has been set
if channel is None:
return

return await channel.send(embed=embed)
111 changes: 91 additions & 20 deletions bot/extensions/levelling/commands.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
import datetime
import logging
import random
from io import BytesIO

import discord
Expand All @@ -10,7 +10,7 @@

from bot import core
from bot.extensions.levelling import utils
from bot.models import IgnoredChannel, LevellingRole, LevellingUser
from bot.models import GuildConfig, IgnoredChannel, LevellingRole, LevellingUser
from bot.models.custom_roles import CustomRole
from cli import ROOT_DIR

Expand Down Expand Up @@ -45,12 +45,18 @@ class Levelling(commands.Cog):
default_permissions=discord.Permissions(administrator=True),
)

config = app_commands.Group(
parent=admin_commands,
name="config",
description="Manage the levelling configuration(XP Boost, min/max xp awarded)",
default_permissions=discord.Permissions(administrator=True),
)

def __init__(self, bot: core.DiscordBot):
self.bot = bot

self.ignored_channels: dict[int, list[int]] = {}
self.required_xp = [0]
self.xp_boost = 1

# Initializing fonts
font = f"{ROOT_DIR.as_posix()}/assets/ABeeZee-Regular.otf"
Expand Down Expand Up @@ -89,28 +95,27 @@ async def on_message(self, message):
return

query = """
INSERT INTO levelling_users (guild_id, user_id, total_xp)
VALUES ($1, $2, $3)
ON CONFLICT (guild_id, user_id)
DO UPDATE SET
total_xp = levelling_users.total_xp + $3,
last_msg = create_snowflake()
WHERE levelling_users.guild_id = $1
AND levelling_users.user_id = $2
AND snowflake_to_timestamp(levelling_users.last_msg) < NOW() - INTERVAL '1 min'
RETURNING *;
WITH generate_xp
AS (SELECT floor( random() * (gc.max_xp - gc.min_xp) + gc.min_xp ) * gc.xp_boost as random_xp
FROM guild_configs gc
WHERE gc.guild_id = $1)
INSERT INTO levelling_users (guild_id, user_id, total_xp)
VALUES ($1, $2, (SELECT random_xp FROM generate_xp))
ON CONFLICT (guild_id, user_id)
DO UPDATE SET
total_xp = levelling_users.total_xp + (SELECT random_xp FROM generate_xp),
last_msg = create_snowflake()
WHERE levelling_users.guild_id = $1
AND levelling_users.user_id = $2
AND snowflake_to_timestamp(levelling_users.last_msg) < NOW() - INTERVAL '1 min'
RETURNING *, (select random_xp from generate_xp) AS random_xp;
"""

# TODO: Allow each guild to set custom xp range and boost.
xp = random.randint(15, 25) * self.xp_boost
after = await LevellingUser.fetchrow(query, message.guild.id, message.author.id, xp)

after = await LevellingUser.fetchrow(query, message.guild.id, message.author.id, convert=False)
if after is None:
return # Last message was less than a minute ago.

before = after.copy(update={"total_xp": after.total_xp - xp})

self.bot.dispatch("xp_update", before=before, after=after)
self.bot.dispatch("xp_update", after=after)

def generate_rank_image(self, username: str, avatar_bytes: bytes, rank: int, level: int, xp: int, required_xp: int):
img = Image.new("RGBA", (1000, 240))
Expand Down Expand Up @@ -472,6 +477,72 @@ async def list_roles(self, interaction: core.InteractionType):

return await interaction.response.send_message(f"```\n{response}\n```\n")

@config.command(name="xp_boost")
async def xp_boost(self, interaction: discord.Interaction, multiplier: int):
"""Change the xp multiplier"""
if multiplier < 0 or multiplier > 10:
return await interaction.response.send_message("XP multiply range should be between 1 and 10.")

query = """
UPDATE guild_configs
SET xp_boost = $1
WHERE guild_id = $2
"""
await GuildConfig.execute(query, multiplier, interaction.guild.id)
return await interaction.response.send_message(f"XP multiplied by {multiplier}x.")

@config.command(name="min_xp")
async def min_xp(self, interaction: discord.Interaction, xp: int):
"""Set Min XP gained per message"""
if xp < 0 or xp > 100:
return await interaction.response.send_message("Min XP range should be between 1 and 100")
query = """
UPDATE guild_configs
SET min_xp = $1
WHERE guild_id = $2 AND max_xp >= $1
"""
data = await GuildConfig.execute(query, xp, interaction.guild.id)
if data == "UPDATE 1":
return await interaction.response.send_message(f"Min XP gained each message has been updated to {xp}.")
return await interaction.response.send_message("Min XP can not be greater than max XP.")

@config.command(name="max_xp")
async def max_xp(self, interaction: discord.Interaction, xp: int):
"""Set Max XP gained per message"""
if xp < 0 or xp > 100:
return await interaction.response.send_message("Max XP range should be between 1 and 100")
query = """
UPDATE guild_configs
SET max_xp = $1
WHERE guild_id = $2 AND min_xp < $1
"""
data = await GuildConfig.execute(query, xp, interaction.guild.id)
if data == "UPDATE 1":
return await interaction.response.send_message(f"Max XP gained each message has been updated to {xp}.")
return await interaction.response.send_message("Max XP can not be less than min XP.")

@config.command(name="show")
async def show(self, interaction: discord.Interaction):
"""Return the current levelling configuration"""
query = """
SELECT * FROM guild_configs
WHERE guild_id = $1
"""
data = await GuildConfig.fetchrow(query, interaction.guild.id)
embed = discord.Embed(
title=f"Server Information - {interaction.guild.name}",
description="Levelling Configuration",
color=discord.Color.blue(),
timestamp=datetime.datetime.utcnow(),
)

embed.set_thumbnail(url=interaction.guild.icon.url)
embed.add_field(name="Minimum XP", value=data.min_xp)
embed.add_field(name="Maximum XP", value=data.max_xp)
embed.add_field(name="XP Boost", value=data.xp_boost)

return await interaction.response.send_message(embed=embed)


async def setup(bot: core.DiscordBot):
await bot.add_cog(Levelling(bot=bot))
6 changes: 3 additions & 3 deletions bot/extensions/levelling/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ def __init__(self, bot: core.DiscordBot):
self.bot = bot

@commands.Cog.listener()
async def on_xp_update(self, before: LevellingUser, after: LevellingUser):
if after.total_xp == before.total_xp:
async def on_xp_update(self, after: LevellingUser):
if after.total_xp == after.total_xp - after.random_xp:
return

elif after.total_xp > before.total_xp:
elif after.total_xp > after.total_xp - after.random_xp:
query = """
SELECT COALESCE(array_agg(role_id), '{}')
FROM levelling_roles lr
Expand Down
2 changes: 2 additions & 0 deletions bot/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .custom_roles import CustomRole
from .gconfig import FilterConfig
from .guild_configs import GuildConfig
from .levelling_ignored_channels import IgnoredChannel
from .levelling_roles import LevellingRole
from .levelling_users import LevellingUser
Expand All @@ -22,4 +23,5 @@
IgnoredChannel,
LevellingRole,
CustomRole,
GuildConfig,
) # Fixes F401
Loading
Loading