Compare commits

...

3 commits

Author SHA1 Message Date
9f86c20df4
feat(moderation): updated prisma_log (mysql_log) and get_next_case_number to use prisma, added get_next_global_case_number
Some checks failed
Pylint / Pylint (3.10) (push) Failing after 1m0s
BREAKING: changed database schema
2023-10-27 10:40:18 -04:00
9ec42f5d60
feat(moderation): adding prisma schema
Some checks failed
Pylint / Pylint (3.10) (push) Failing after 58s
2023-10-26 21:05:01 -04:00
b8c3efdedc
feat(moderation): added required config values for prisma connections
Some checks failed
Pylint / Pylint (3.10) (push) Failing after 58s
2023-10-26 20:45:05 -04:00
3 changed files with 107 additions and 82 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
galaxy/slashtag arguments.txt galaxy/slashtag arguments.txt
.venv .venv
*.db

View file

@ -4,7 +4,7 @@ from datetime import datetime, timedelta, timezone
from typing import Union from typing import Union
import discord import discord
import humanize import humanize
import mysql.connector from prisma import Prisma
from discord.ext import tasks from discord.ext import tasks
from pytimeparse2 import disable_dateutil, parse from pytimeparse2 import disable_dateutil, parse
from redbot.core import app_commands, checks, Config, commands from redbot.core import app_commands, checks, Config, commands
@ -19,10 +19,12 @@ class Moderation(commands.Cog):
self.bot = bot self.bot = bot
self.config = Config.get_conf(self, identifier=481923957134912) self.config = Config.get_conf(self, identifier=481923957134912)
self.config.register_global( self.config.register_global(
mysql_address= " ", database_provider = "sqlite",
mysql_database = " ", database_address= "file:moderation.db",
mysql_username = " ", database_port = " ",
mysql_password = " " database_name = " ",
database_username = " ",
database_password = " "
) )
self.config.register_guild( self.config.register_guild(
ignore_other_bots = True, ignore_other_bots = True,
@ -108,67 +110,20 @@ class Moderation(commands.Cog):
await self.mysql_log(entry.guild.id, entry.user.id, moderation_type, entry.target.id, duration, reason) await self.mysql_log(entry.guild.id, entry.user.id, moderation_type, entry.target.id, duration, reason)
async def connect(self): async def connect(self):
"""Connects to the MySQL database, and returns a connection object.""" """Connects to the database, and returns a connection object."""
conf = await self.check_conf([ provider = await self.config.database_provider()
'mysql_address', address = await self.config.database_address()
'mysql_database', if provider == "sqlite" and address == "file:moderation.db":
'mysql_username', return await Prisma().connect()
'mysql_password' else:
]) return await Prisma(
if conf: provider=provider,
raise LookupError("MySQL connection details not set properly!") address=address,
try: port=await self.config.database_port(),
connection = mysql.connector.connect( database=await self.config.database_name(),
host=await self.config.mysql_address(), username=await self.config.database_username(),
user=await self.config.mysql_username(), password=await self.config.database_password()
password=await self.config.mysql_password(), ).connect()
database=await self.config.mysql_database()
)
return connection
except mysql.connector.ProgrammingError as e:
self.logger.fatal("Unable to access the MySQL database!\nError:\n%s", e.msg)
raise ConnectionRefusedError(f"Unable to access the MySQL Database!\n{e.msg}") from e
async def create_guild_table(self, guild: discord.Guild):
database = await self.connect()
cursor = database.cursor()
try:
cursor.execute(f"SELECT * FROM `moderation_{guild.id}`")
self.logger.info("MySQL Table exists for server %s (%s)", guild.name, guild.id)
except mysql.connector.errors.ProgrammingError:
query = f"""
CREATE TABLE `moderation_{guild.id}` (
moderation_id INT UNIQUE PRIMARY KEY NOT NULL,
timestamp INT NOT NULL,
moderation_type LONGTEXT NOT NULL,
target_id LONGTEXT NOT NULL,
moderator_id LONGTEXT NOT NULL,
duration LONGTEXT,
end_timestamp INT,
reason LONGTEXT,
resolved BOOL NOT NULL,
resolved_by LONGTEXT,
resolve_reason LONGTEXT,
expired BOOL NOT NULL
)
"""
cursor.execute(query)
index_query_1 = "CREATE INDEX idx_target_id ON moderation_%s(target_id(25));"
cursor.execute(index_query_1, (guild.id,))
index_query_2 = "CREATE INDEX idx_moderator_id ON moderation_%s(moderator_id(25));"
cursor.execute(index_query_2, (guild.id,))
index_query_3 = "CREATE INDEX idx_moderation_id ON moderation_%s(moderation_id);"
cursor.execute(index_query_3, (guild.id,))
insert_query = f"""
INSERT INTO `moderation_{guild.id}`
(moderation_id, timestamp, moderation_type, target_id, moderator_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
insert_values = (0, 0, "NULL", 0, 0, "NULL", 0, "NULL", 0, "NULL", "NULL", 0)
cursor.execute(insert_query, insert_values)
database.commit()
self.logger.info("MySQL Table (moderation_%s) created for %s (%s)", guild.id, guild.name, guild.id)
database.close()
async def check_conf(self, config: list): async def check_conf(self, config: list):
"""Checks if any required config options are not set.""" """Checks if any required config options are not set."""
@ -193,31 +148,70 @@ class Moderation(commands.Cog):
return permission return permission
return False return False
async def mysql_log(self, guild_id: str, author_id: str, moderation_type: str, target_id: int, duration, reason: str): async def prisma_log(self, guild_id: str, author_id: str, moderation_type: str, target_id: int, duration, reason: str, role_id = None):
timestamp = int(time.time()) timestamp = int(time.time())
if duration != "NULL": if duration != "NULL":
end_timedelta = datetime.fromtimestamp(timestamp) + duration end_timedelta = datetime.fromtimestamp(timestamp) + duration
end_timestamp = int(end_timedelta.timestamp()) end_timestamp = int(end_timedelta.timestamp())
else: else:
end_timestamp = 0 end_timestamp = 0
database = await self.connect() if not role_id:
cursor = database.cursor() role_id = "NULL"
moderation_id = await self.get_next_case_number(guild_id=guild_id, cursor=cursor) db = await self.connect()
sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_id, moderator_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" global_id = await self.get_next_global_case_number(database=db)
val = (moderation_id, timestamp, moderation_type, target_id, author_id, duration, end_timestamp, f"{reason}", 0, "NULL", "NULL", 0) moderation_id = await self.get_next_case_number(guild_id=guild_id, database=db)
cursor.execute(sql, val) await db.Case.create(
database.commit() data={
database.close() 'globalId': global_id,
self.logger.debug("MySQL row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, 0, NULL, NULL, 0", guild_id, moderation_id, timestamp, moderation_type, target_id, author_id, duration, end_timestamp, reason) 'guildId': guild_id,
'moderationId': moderation_id,
'timestamp': timestamp,
'moderationType': moderation_type,
'targetId': target_id,
'moderatorId': author_id,
'roleId': role_id,
'duration': duration,
'endTimestamp': end_timestamp,
'reason': reason,
'resolved': False,
'resolvedBy': None,
'resolveReason': None,
'expired': False
}
)
await db.disconnect()
return moderation_id return moderation_id
async def get_next_case_number(self, guild_id: str, cursor = None): async def get_next_case_number(self, guild_id: str, database = None):
"""This method returns the next case number from the MySQL table for a specific guild.""" """This method returns the next case number from the database table for a specific guild."""
if not cursor: if not database:
database = await self.connect() database = await self.connect()
cursor = database.cursor() db_not_provided = True
cursor.execute(f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1") else:
return cursor.fetchone()[0] + 1 db_not_provided = False
result = await database.Case.find_first(
select={"moderationId": True},
where={"guildId": guild_id},
order=[{"moderationId": "desc"}],
)
if db_not_provided:
await database.disconnect()
return result.moderationId + 1 if result else 1
async def get_next_global_case_number(self, database = None):
"""This method returns the next case number from the database table."""
if not database:
database = await self.connect()
db_not_provided = True
else:
db_not_provided = False
result = await database.Case.find_first(
select={"globalId": True},
order=[{"globalId": "desc"}],
)
if db_not_provided:
await database.disconnect()
return result.globalId + 1 if result else 1
def generate_dict(self, result): def generate_dict(self, result):
case: dict = { case: dict = {

View file

@ -0,0 +1,30 @@
datasource db {
provider = "sqlite"
url = "file:moderation.db"
}
generator client {
provider = "prisma-client-py"
interface = "asyncio"
recursive_type_depth = 5
}
model Case {
globalId Int @id @unique
guildId BigInt
moderationId Int
timestamp DateTime
moderationType String
targetId BigInt
moderatorId BigInt
roleId BigInt?
duration String?
endTimestamp DateTime?
reason String?
resolved Boolean
resolvedBy BigInt?
resolveReason String?
expired Boolean
@@index([guildId, moderationId, targetId, moderatorId])
}