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
.venv
*.db

View file

@ -4,7 +4,7 @@ from datetime import datetime, timedelta, timezone
from typing import Union
import discord
import humanize
import mysql.connector
from prisma import Prisma
from discord.ext import tasks
from pytimeparse2 import disable_dateutil, parse
from redbot.core import app_commands, checks, Config, commands
@ -19,10 +19,12 @@ class Moderation(commands.Cog):
self.bot = bot
self.config = Config.get_conf(self, identifier=481923957134912)
self.config.register_global(
mysql_address= " ",
mysql_database = " ",
mysql_username = " ",
mysql_password = " "
database_provider = "sqlite",
database_address= "file:moderation.db",
database_port = " ",
database_name = " ",
database_username = " ",
database_password = " "
)
self.config.register_guild(
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)
async def connect(self):
"""Connects to the MySQL database, and returns a connection object."""
conf = await self.check_conf([
'mysql_address',
'mysql_database',
'mysql_username',
'mysql_password'
])
if conf:
raise LookupError("MySQL connection details not set properly!")
try:
connection = mysql.connector.connect(
host=await self.config.mysql_address(),
user=await self.config.mysql_username(),
password=await self.config.mysql_password(),
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()
"""Connects to the database, and returns a connection object."""
provider = await self.config.database_provider()
address = await self.config.database_address()
if provider == "sqlite" and address == "file:moderation.db":
return await Prisma().connect()
else:
return await Prisma(
provider=provider,
address=address,
port=await self.config.database_port(),
database=await self.config.database_name(),
username=await self.config.database_username(),
password=await self.config.database_password()
).connect()
async def check_conf(self, config: list):
"""Checks if any required config options are not set."""
@ -193,31 +148,70 @@ class Moderation(commands.Cog):
return permission
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())
if duration != "NULL":
end_timedelta = datetime.fromtimestamp(timestamp) + duration
end_timestamp = int(end_timedelta.timestamp())
else:
end_timestamp = 0
database = await self.connect()
cursor = database.cursor()
moderation_id = await self.get_next_case_number(guild_id=guild_id, cursor=cursor)
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)"
val = (moderation_id, timestamp, moderation_type, target_id, author_id, duration, end_timestamp, f"{reason}", 0, "NULL", "NULL", 0)
cursor.execute(sql, val)
database.commit()
database.close()
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)
if not role_id:
role_id = "NULL"
db = await self.connect()
global_id = await self.get_next_global_case_number(database=db)
moderation_id = await self.get_next_case_number(guild_id=guild_id, database=db)
await db.Case.create(
data={
'globalId': global_id,
'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
async def get_next_case_number(self, guild_id: str, cursor = None):
"""This method returns the next case number from the MySQL table for a specific guild."""
if not cursor:
async def get_next_case_number(self, guild_id: str, database = None):
"""This method returns the next case number from the database table for a specific guild."""
if not database:
database = await self.connect()
cursor = database.cursor()
cursor.execute(f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1")
return cursor.fetchone()[0] + 1
db_not_provided = True
else:
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):
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])
}