Compare commits
1 commit
main
...
moderation
Author | SHA1 | Date | |
---|---|---|---|
144aafccdf |
4 changed files with 365 additions and 211 deletions
|
@ -5,7 +5,7 @@
|
||||||
"short" : "Custom cog intended for use on the Galaxy discord server.",
|
"short" : "Custom cog intended for use on the Galaxy discord server.",
|
||||||
"description" : "Custom cog intended for use on the Galaxy discord server.",
|
"description" : "Custom cog intended for use on the Galaxy discord server.",
|
||||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||||
"requirements": ["mysql-connector-python", "humanize", "pytimeparse2"],
|
"requirements": ["sqlalchemy", "humanize", "pytimeparse2"],
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"disabled": false
|
"disabled": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,11 @@ 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 sqlalchemy import MetaData, Table, Column, Integer, String, Boolean, DateTime, Text, BigInteger, func, or_, and_
|
||||||
|
from sqlalchemy.exc import OperationalError, NoSuchTableError
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
|
||||||
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 +23,8 @@ 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= " ",
|
db_use_sqlite = True,
|
||||||
mysql_database = " ",
|
db_connection_string = " "
|
||||||
mysql_username = " ",
|
|
||||||
mysql_password = " "
|
|
||||||
)
|
)
|
||||||
self.config.register_guild(
|
self.config.register_guild(
|
||||||
ignore_other_bots = True,
|
ignore_other_bots = True,
|
||||||
|
@ -105,78 +107,69 @@ class Moderation(commands.Cog):
|
||||||
moderation_type = 'UNMUTE'
|
moderation_type = 'UNMUTE'
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
await self.mysql_log(entry.guild.id, entry.user.id, moderation_type, entry.target.id, duration, reason)
|
await self.sql_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 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:
|
try:
|
||||||
connection = mysql.connector.connect(
|
if await self.config.db_use_sqlite() is False and await self.config.db_connection_string() != " ":
|
||||||
host=await self.config.mysql_address(),
|
engine = create_async_engine(await self.config.db_connection_string())
|
||||||
user=await self.config.mysql_username(),
|
else:
|
||||||
password=await self.config.mysql_password(),
|
engine = create_async_engine('sqlite:///moderation.db')
|
||||||
database=await self.config.mysql_database()
|
except OperationalError as e:
|
||||||
)
|
self.logger.fatal("Unable to access the database!\nError:\n%s", e.msg)
|
||||||
return connection
|
raise ConnectionRefusedError(f"Unable to access the Database!\n{e.msg}") from e
|
||||||
except mysql.connector.ProgrammingError as e:
|
return engine
|
||||||
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 fetch_table(self, engine, guild_id):
|
||||||
|
table = Table(f'moderation_{guild_id}', MetaData(), autoload_with=engine)
|
||||||
|
return table
|
||||||
|
|
||||||
async def create_guild_table(self, guild: discord.Guild):
|
async def create_guild_table(self, guild: discord.Guild):
|
||||||
database = await self.connect()
|
engine = await self.connect()
|
||||||
cursor = database.cursor()
|
metadata = MetaData()
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"SELECT * FROM `moderation_{guild.id}`")
|
await self.fetch_table(engine, guild.id)
|
||||||
self.logger.info("MySQL Table exists for server %s (%s)", guild.name, guild.id)
|
self.logger.info("MySQL Table exists for server %s (%s)", guild.name, guild.id)
|
||||||
except mysql.connector.errors.ProgrammingError:
|
except NoSuchTableError:
|
||||||
query = f"""
|
moderation = Table(
|
||||||
CREATE TABLE `moderation_{guild.id}` (
|
f'moderation_{guild.id}', metadata,
|
||||||
moderation_id INT UNIQUE PRIMARY KEY NOT NULL,
|
Column('moderationId', Integer, primary_key=True, autoincrement=True, index=True),
|
||||||
timestamp INT NOT NULL,
|
Column('timestamp', Integer, nullable=False),
|
||||||
moderation_type LONGTEXT NOT NULL,
|
Column('moderationType', String(20), nullable=False),
|
||||||
target_id LONGTEXT NOT NULL,
|
Column('targetId', BigInteger, nullable=False, index=True),
|
||||||
moderator_id LONGTEXT NOT NULL,
|
Column('moderatorId', BigInteger, nullable=False, index=True),
|
||||||
duration LONGTEXT,
|
Column('roleId', BigInteger),
|
||||||
end_timestamp INT,
|
Column('duration', String(40)),
|
||||||
reason LONGTEXT,
|
Column('endTimestamp', Integer),
|
||||||
resolved BOOL NOT NULL,
|
Column('reason', Text),
|
||||||
resolved_by LONGTEXT,
|
Column('resolved', Boolean, nullable=False),
|
||||||
resolve_reason LONGTEXT,
|
Column('resolvedBy', BigInteger),
|
||||||
expired BOOL NOT NULL
|
Column('resolveReason', Text),
|
||||||
)
|
Column('expired', Boolean, nullable=False)
|
||||||
"""
|
)
|
||||||
cursor.execute(query)
|
ins = moderation.insert()
|
||||||
index_query_1 = "CREATE INDEX idx_target_id ON moderation_%s(target_id(25));"
|
ins = moderation.insert().values(
|
||||||
cursor.execute(index_query_1, (guild.id,))
|
moderationId=0,
|
||||||
index_query_2 = "CREATE INDEX idx_moderator_id ON moderation_%s(moderator_id(25));"
|
timestamp=0,
|
||||||
cursor.execute(index_query_2, (guild.id,))
|
moderationType="NULL",
|
||||||
index_query_3 = "CREATE INDEX idx_moderation_id ON moderation_%s(moderation_id);"
|
targetId=0,
|
||||||
cursor.execute(index_query_3, (guild.id,))
|
moderatorId=0,
|
||||||
insert_query = f"""
|
roleId=0,
|
||||||
INSERT INTO `moderation_{guild.id}`
|
duration="NULL",
|
||||||
(moderation_id, timestamp, moderation_type, target_id, moderator_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired)
|
endTimestamp=0,
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
reason="NULL",
|
||||||
"""
|
resolved=0,
|
||||||
insert_values = (0, 0, "NULL", 0, 0, "NULL", 0, "NULL", 0, "NULL", "NULL", 0)
|
resolvedBy=0,
|
||||||
cursor.execute(insert_query, insert_values)
|
resolveReason="NULL",
|
||||||
database.commit()
|
expired=0
|
||||||
self.logger.info("MySQL Table (moderation_%s) created for %s (%s)", guild.id, guild.name, guild.id)
|
)
|
||||||
database.close()
|
async with engine.begin() as conn:
|
||||||
|
await conn.run_sync(metadata.create_all)
|
||||||
async def check_conf(self, config: list):
|
await conn.execute(ins)
|
||||||
"""Checks if any required config options are not set."""
|
conn.commit()
|
||||||
not_found_list = []
|
conn.close()
|
||||||
for item in config:
|
self.logger.info("Database Table (moderation_%s) created for %s (%s)", guild.id, guild.name, guild.id)
|
||||||
if await self.config.item() == " ":
|
|
||||||
not_found_list.append(item)
|
|
||||||
return not_found_list
|
|
||||||
|
|
||||||
def check_permissions(self, user: discord.User, permissions: list, ctx: Union[commands.Context, discord.Interaction] = None, guild: discord.Guild = None):
|
def check_permissions(self, user: discord.User, permissions: list, ctx: Union[commands.Context, discord.Interaction] = None, guild: discord.Guild = None):
|
||||||
"""Checks if a user has a specific permission (or a list of permissions) in a channel."""
|
"""Checks if a user has a specific permission (or a list of permissions) in a channel."""
|
||||||
|
@ -193,31 +186,45 @@ 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 sql_log(self, guild_id: str, author_id: str, moderation_type: str, target_id: int, duration, reason: str):
|
||||||
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()
|
engine = await self.connect()
|
||||||
cursor = database.cursor()
|
table = await self.fetch_table(engine, guild_id)
|
||||||
moderation_id = await self.get_next_case_number(guild_id=guild_id, cursor=cursor)
|
moderation_id = await self.get_next_case_number(guild_id=guild_id, engine=engine)
|
||||||
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)"
|
sql = table.insert().values(
|
||||||
val = (moderation_id, timestamp, moderation_type, target_id, author_id, duration, end_timestamp, f"{reason}", 0, "NULL", "NULL", 0)
|
moderation_id=moderation_id,
|
||||||
cursor.execute(sql, val)
|
timestamp=timestamp,
|
||||||
database.commit()
|
moderation_type=moderation_type,
|
||||||
database.close()
|
target_id=target_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)
|
moderator_id=author_id,
|
||||||
|
duration=duration,
|
||||||
|
end_timestamp=end_timestamp,
|
||||||
|
reason=reason,
|
||||||
|
resolved=0,
|
||||||
|
resolved_by="NULL",
|
||||||
|
resolve_reason="NULL",
|
||||||
|
expired=0
|
||||||
|
)
|
||||||
|
async with engine.connect() as conn:
|
||||||
|
conn.execute(sql)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
self.logger.debug("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)
|
||||||
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, engine: AsyncEngine):
|
||||||
"""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:
|
table = await self.fetch_table(engine, guild_id)
|
||||||
database = await self.connect()
|
async with engine.connect() as conn:
|
||||||
cursor = database.cursor()
|
result = await conn.execute(table.select().order_by(table.c.moderationId.desc()).limit(1))
|
||||||
cursor.execute(f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1")
|
result = result.fetchone()
|
||||||
return cursor.fetchone()[0] + 1
|
conn.close()
|
||||||
|
return result[0] + 1 if result else 1
|
||||||
|
|
||||||
def generate_dict(self, result):
|
def generate_dict(self, result):
|
||||||
case: dict = {
|
case: dict = {
|
||||||
|
@ -226,13 +233,14 @@ class Moderation(commands.Cog):
|
||||||
"moderation_type": result[2],
|
"moderation_type": result[2],
|
||||||
"target_id": result[3],
|
"target_id": result[3],
|
||||||
"moderator_id": result[4],
|
"moderator_id": result[4],
|
||||||
"duration": result[5],
|
"role_id": result[5],
|
||||||
"end_timestamp": result[6],
|
"duration": result[6],
|
||||||
"reason": result[7],
|
"end_timestamp": result[7],
|
||||||
"resolved": result[8],
|
"reason": result[8],
|
||||||
"resolved_by": result[9],
|
"resolved": result[9],
|
||||||
"resolve_reason": result[10],
|
"resolved_by": result[10],
|
||||||
"expired": result[11]
|
"resolve_reason": result[11],
|
||||||
|
"expired": result[12]
|
||||||
}
|
}
|
||||||
return case
|
return case
|
||||||
|
|
||||||
|
@ -346,13 +354,12 @@ class Moderation(commands.Cog):
|
||||||
|
|
||||||
async def fetch_case(self, moderation_id: int, guild_id: str):
|
async def fetch_case(self, moderation_id: int, guild_id: str):
|
||||||
"""This method fetches a case from the database and returns the case's dictionary."""
|
"""This method fetches a case from the database and returns the case's dictionary."""
|
||||||
database = await self.connect()
|
engine = await self.connect()
|
||||||
cursor = database.cursor()
|
table = await self.fetch_table(engine, guild_id)
|
||||||
query = "SELECT * FROM moderation_%s WHERE moderation_id = %s;"
|
async with engine.connect() as conn:
|
||||||
cursor.execute(query, (guild_id, moderation_id))
|
result = await conn.execute(table.select().where(table.c.moderation_id == moderation_id))
|
||||||
result = cursor.fetchone()
|
result = result.fetchone()
|
||||||
cursor.close()
|
conn.close()
|
||||||
database.close()
|
|
||||||
return self.generate_dict(result)
|
return self.generate_dict(result)
|
||||||
|
|
||||||
async def log(self, interaction: discord.Interaction, moderation_id: int, resolved: bool = False):
|
async def log(self, interaction: discord.Interaction, moderation_id: int, resolved: bool = False):
|
||||||
|
@ -397,7 +404,7 @@ class Moderation(commands.Cog):
|
||||||
await target.send(embed=embed)
|
await target.send(embed=embed)
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'NOTE', target.id, 'NULL', reason)
|
moderation_id = await self.sql_log(interaction.guild.id, interaction.user.id, 'NOTE', target.id, 'NULL', reason)
|
||||||
await self.log(interaction, moderation_id)
|
await self.log(interaction, moderation_id)
|
||||||
|
|
||||||
@app_commands.command(name="warn")
|
@app_commands.command(name="warn")
|
||||||
|
@ -429,7 +436,7 @@ class Moderation(commands.Cog):
|
||||||
await target.send(embed=embed)
|
await target.send(embed=embed)
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'WARN', target.id, 'NULL', reason)
|
moderation_id = await self.sql_log(interaction.guild.id, interaction.user.id, 'WARN', target.id, 'NULL', reason)
|
||||||
await self.log(interaction, moderation_id)
|
await self.log(interaction, moderation_id)
|
||||||
|
|
||||||
@app_commands.command(name="mute")
|
@app_commands.command(name="mute")
|
||||||
|
@ -479,7 +486,7 @@ class Moderation(commands.Cog):
|
||||||
await target.send(embed=embed)
|
await target.send(embed=embed)
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'MUTE', target.id, parsed_time, reason)
|
moderation_id = await self.sql_log(interaction.guild.id, interaction.user.id, 'MUTE', target.id, parsed_time, reason)
|
||||||
await self.log(interaction, moderation_id)
|
await self.log(interaction, moderation_id)
|
||||||
|
|
||||||
@app_commands.command(name="unmute")
|
@app_commands.command(name="unmute")
|
||||||
|
@ -523,7 +530,7 @@ class Moderation(commands.Cog):
|
||||||
await target.send(embed=embed)
|
await target.send(embed=embed)
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'UNMUTE', target.id, 'NULL', reason)
|
moderation_id = await self.sql_log(interaction.guild.id, interaction.user.id, 'UNMUTE', target.id, 'NULL', reason)
|
||||||
await self.log(interaction, moderation_id)
|
await self.log(interaction, moderation_id)
|
||||||
|
|
||||||
@app_commands.command(name="kick")
|
@app_commands.command(name="kick")
|
||||||
|
@ -560,7 +567,7 @@ class Moderation(commands.Cog):
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
await target.kick(f"Kicked by {interaction.user.id} for: {reason}")
|
await target.kick(f"Kicked by {interaction.user.id} for: {reason}")
|
||||||
moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'KICK', target.id, 'NULL', reason)
|
moderation_id = await self.sql_log(interaction.guild.id, interaction.user.id, 'KICK', target.id, 'NULL', reason)
|
||||||
await self.log(interaction, moderation_id)
|
await self.log(interaction, moderation_id)
|
||||||
|
|
||||||
@app_commands.command(name="ban")
|
@app_commands.command(name="ban")
|
||||||
|
@ -618,7 +625,7 @@ class Moderation(commands.Cog):
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
await interaction.guild.ban(target, reason=f"Tempbanned by {interaction.user.id} for: {reason} (Duration: {parsed_time})", delete_message_seconds=delete_messages)
|
await interaction.guild.ban(target, reason=f"Tempbanned by {interaction.user.id} for: {reason} (Duration: {parsed_time})", delete_message_seconds=delete_messages)
|
||||||
await self.mysql_log(interaction.guild.id, interaction.user.id, 'TEMPBAN', target.id, parsed_time, reason)
|
await self.sql_log(interaction.guild.id, interaction.user.id, 'TEMPBAN', target.id, parsed_time, reason)
|
||||||
else:
|
else:
|
||||||
await interaction.response.send_message(content=f"{target.mention} has been banned!\n**Reason** - `{reason}`")
|
await interaction.response.send_message(content=f"{target.mention} has been banned!\n**Reason** - `{reason}`")
|
||||||
if silent is None:
|
if silent is None:
|
||||||
|
@ -630,7 +637,7 @@ class Moderation(commands.Cog):
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
await interaction.guild.ban(target, reason=f"Banned by {interaction.user.id} for: {reason}", delete_message_seconds=delete_messages)
|
await interaction.guild.ban(target, reason=f"Banned by {interaction.user.id} for: {reason}", delete_message_seconds=delete_messages)
|
||||||
moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'BAN', target.id, 'NULL', reason)
|
moderation_id = await self.sql_log(interaction.guild.id, interaction.user.id, 'BAN', target.id, 'NULL', reason)
|
||||||
await self.log(interaction, moderation_id)
|
await self.log(interaction, moderation_id)
|
||||||
|
|
||||||
@app_commands.command(name="unban")
|
@app_commands.command(name="unban")
|
||||||
|
@ -676,7 +683,7 @@ class Moderation(commands.Cog):
|
||||||
await target.send(embed=embed)
|
await target.send(embed=embed)
|
||||||
except discord.errors.HTTPException:
|
except discord.errors.HTTPException:
|
||||||
pass
|
pass
|
||||||
moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'UNBAN', target.id, 'NULL', reason)
|
moderation_id = await self.sql_log(interaction.guild.id, interaction.user.id, 'UNBAN', target.id, 'NULL', reason)
|
||||||
await self.log(interaction, moderation_id)
|
await self.log(interaction, moderation_id)
|
||||||
|
|
||||||
@app_commands.command(name="history")
|
@app_commands.command(name="history")
|
||||||
|
@ -699,37 +706,26 @@ class Moderation(commands.Cog):
|
||||||
if permissions:
|
if permissions:
|
||||||
await interaction.response.send_message(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True)
|
await interaction.response.send_message(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True)
|
||||||
return
|
return
|
||||||
database = await self.connect()
|
start_index = (page - 1) * pagesize
|
||||||
cursor = database.cursor()
|
end_index = page * pagesize
|
||||||
if target:
|
engine = await self.connect()
|
||||||
query = """SELECT *
|
table = await self.fetch_table(engine, interaction.guild.id)
|
||||||
FROM moderation_%s
|
async with engine.connect() as conn:
|
||||||
WHERE target_id = %s
|
if target:
|
||||||
ORDER BY moderation_id DESC;"""
|
results = conn.execute(table.select().where(table.c.targetId == target.id).order_by(table.c.moderationId.desc()).offset(start_index).limit(end_index - start_index + 1))
|
||||||
cursor.execute(query, (interaction.guild.id, target.id))
|
case_quantity = conn.execute(table.select(func.count()).where(table.c.targetId == target.id))
|
||||||
elif moderator:
|
elif moderator:
|
||||||
query = """SELECT *
|
results = conn.execute(table.select().where(table.c.moderatorId == moderator.id).order_by(table.c.moderationId.desc()).offset(start_index).limit(end_index - start_index + 1))
|
||||||
FROM moderation_%s
|
case_quantity = conn.execute(table.select(func.count()).where(table.c.moderatorId == moderator.id))
|
||||||
WHERE moderator_id = %s
|
else:
|
||||||
ORDER BY moderation_id DESC;"""
|
results = conn.execute(table.select().order_by(table.c.moderationId.desc()).offset(start_index).limit(pagesize))
|
||||||
cursor.execute(query, (interaction.guild.id, moderator.id))
|
case_quantity = conn.execute(table.select(func.count())) - 1 # account for case 0 techincally existing
|
||||||
else:
|
conn.close()
|
||||||
query = """SELECT *
|
|
||||||
FROM moderation_%s
|
|
||||||
ORDER BY moderation_id DESC;"""
|
|
||||||
cursor.execute(query, (interaction.guild.id,))
|
|
||||||
results = cursor.fetchall()
|
|
||||||
result_dict_list = []
|
result_dict_list = []
|
||||||
for result in results:
|
for result in results:
|
||||||
case_dict = self.generate_dict(result)
|
case_dict = self.generate_dict(result)
|
||||||
result_dict_list.append(case_dict)
|
result_dict_list.append(case_dict)
|
||||||
if target or moderator:
|
|
||||||
case_quantity = len(result_dict_list)
|
|
||||||
else:
|
|
||||||
case_quantity = len(result_dict_list) - 1 # account for case 0 technically existing
|
|
||||||
page_quantity = round(case_quantity / pagesize)
|
page_quantity = round(case_quantity / pagesize)
|
||||||
start_index = (page - 1) * pagesize
|
|
||||||
end_index = page * pagesize
|
|
||||||
embed = discord.Embed(color=await self.bot.get_embed_color(None))
|
embed = discord.Embed(color=await self.bot.get_embed_color(None))
|
||||||
embed.set_author(icon_url=interaction.guild.icon.url, name='Infraction History')
|
embed.set_author(icon_url=interaction.guild.icon.url, name='Infraction History')
|
||||||
embed.set_footer(text=f"Page {page}/{page_quantity} | {case_quantity} Results")
|
embed.set_footer(text=f"Page {page}/{page_quantity} | {case_quantity} Results")
|
||||||
|
@ -765,56 +761,49 @@ class Moderation(commands.Cog):
|
||||||
if permissions:
|
if permissions:
|
||||||
await interaction.response.send_message(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True)
|
await interaction.response.send_message(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True)
|
||||||
return
|
return
|
||||||
conf = await self.check_conf(['mysql_database'])
|
engine = await self.connect()
|
||||||
if conf:
|
table = await self.fetch_table(engine, interaction.guild.id)
|
||||||
raise(LookupError)
|
async with engine.connect() as conn:
|
||||||
database = await self.connect()
|
result_1 = conn.execute(table.select().where(table.c.moderationId == case_number))[0]
|
||||||
cursor = database.cursor()
|
if result_1 is None or case_number == 0:
|
||||||
db = await self.config.mysql_database()
|
await interaction.response.send_message(content=f"There is no moderation with a case number of {case_number}.", ephemeral=True)
|
||||||
query_1 = "SELECT * FROM moderation_%s WHERE moderation_id = %s;"
|
conn.close()
|
||||||
cursor.execute(query_1, (interaction.guild.id, case_number))
|
return
|
||||||
result_1 = cursor.fetchone()
|
result_2 = conn.execute(table.select().where(table.c.moderationId == case_number).where(table.c.resolved == 0))[0]
|
||||||
if result_1 is None or case_number == 0:
|
if result_2 is None:
|
||||||
await interaction.response.send_message(content=f"There is no moderation with a case number of {case_number}.", ephemeral=True)
|
await interaction.response.send_message(content=f"This moderation has already been resolved!\nUse `/case {case_number}` for more information.", ephemeral=True)
|
||||||
return
|
conn.close()
|
||||||
query_2 = "SELECT * FROM moderation_%s WHERE moderation_id = %s AND resolved = 0;"
|
return
|
||||||
cursor.execute(query_2, (interaction.guild.id, case_number))
|
case = self.generate_dict(result_2)
|
||||||
result_2 = cursor.fetchone()
|
if reason is None:
|
||||||
if result_2 is None:
|
reason = "No reason given."
|
||||||
await interaction.response.send_message(content=f"This moderation has already been resolved!\nUse `/case {case_number}` for more information.", ephemeral=True)
|
if case['moderation_type'] in ['UNMUTE', 'UNBAN']:
|
||||||
return
|
await interaction.response.send_message(content="You cannot resolve this type of moderation!", ephemeral=True)
|
||||||
case = self.generate_dict(result_2)
|
conn.close()
|
||||||
if reason is None:
|
return
|
||||||
reason = "No reason given."
|
if case['moderation_type'] in ['MUTE', 'TEMPBAN', 'BAN']:
|
||||||
if case['moderation_type'] in ['UNMUTE', 'UNBAN']:
|
if case['moderation_type'] == 'MUTE':
|
||||||
await interaction.response.send_message(content="You cannot resolve this type of moderation!", ephemeral=True)
|
try:
|
||||||
if case['moderation_type'] in ['MUTE', 'TEMPBAN', 'BAN']:
|
member = await interaction.guild.fetch_member(case['target_id'])
|
||||||
if case['moderation_type'] == 'MUTE':
|
await member.timeout(None, reason=f"Case #{case_number} resolved by {interaction.user.id}")
|
||||||
try:
|
except discord.NotFound:
|
||||||
member = await interaction.guild.fetch_member(case['target_id'])
|
pass
|
||||||
await member.timeout(None, reason=f"Case #{case_number} resolved by {interaction.user.id}")
|
if case['moderation_type'] in ['TEMPBAN', 'BAN']:
|
||||||
except discord.NotFound:
|
try:
|
||||||
pass
|
user = await interaction.client.fetch_user(case['target_id'])
|
||||||
if case['moderation_type'] in ['TEMPBAN', 'BAN']:
|
await interaction.guild.unban(user, reason=f"Case #{case_number} resolved by {interaction.user.id}")
|
||||||
try:
|
except discord.NotFound:
|
||||||
user = await interaction.client.fetch_user(case['target_id'])
|
pass
|
||||||
await interaction.guild.unban(user, reason=f"Case #{case_number} resolved by {interaction.user.id}")
|
conn.execute(table.update().where(table.c.moderationId == case_number).values(resolved=1, expired=1, resolvedBy=interaction.user.id, resolveReason=reason))
|
||||||
except discord.NotFound:
|
else:
|
||||||
pass
|
conn.execute(table.update().where(table.c.moderationId == case_number).values(resolved=1, resolvedBy=interaction.user.id, resolveReason=reason))
|
||||||
resolve_query = f"UPDATE `{db}`.`moderation_{interaction.guild.id}` SET resolved = 1, expired = 1, resolved_by = %s, resolve_reason = %s WHERE moderation_id = %s"
|
conn.commit()
|
||||||
else:
|
result = conn.execute(table.select().where(table.c.moderationId == case_number))[0]
|
||||||
resolve_query = f"UPDATE `{db}`.`moderation_{interaction.guild.id}` SET resolved = 1, resolved_by = %s, resolve_reason = %s WHERE moderation_id = %s"
|
conn.close()
|
||||||
cursor.execute(resolve_query, (interaction.user.id, reason, case_number))
|
|
||||||
database.commit()
|
|
||||||
response_query = "SELECT * FROM moderation_%s WHERE moderation_id = %s;"
|
|
||||||
cursor.execute(response_query, (interaction.guild.id, case_number))
|
|
||||||
result = cursor.fetchone()
|
|
||||||
case_dict = self.generate_dict(result)
|
case_dict = self.generate_dict(result)
|
||||||
embed = await self.embed_factory('case', interaction=interaction, case_dict=case_dict)
|
embed = await self.embed_factory('case', interaction=interaction, case_dict=case_dict)
|
||||||
await interaction.response.send_message(content=f"✅ Moderation #{case_number} resolved!", embed=embed)
|
await interaction.response.send_message(content=f"✅ Moderation #{case_number} resolved!", embed=embed)
|
||||||
await self.log(interaction, case_number, True)
|
await self.log(interaction, case_number, True)
|
||||||
cursor.close()
|
|
||||||
database.close()
|
|
||||||
|
|
||||||
@app_commands.command(name="case")
|
@app_commands.command(name="case")
|
||||||
async def case(self, interaction: discord.Interaction, case_number: int, ephemeral: bool = False):
|
async def case(self, interaction: discord.Interaction, case_number: int, ephemeral: bool = False):
|
||||||
|
@ -840,36 +829,44 @@ class Moderation(commands.Cog):
|
||||||
|
|
||||||
@tasks.loop(minutes=1)
|
@tasks.loop(minutes=1)
|
||||||
async def handle_expiry(self):
|
async def handle_expiry(self):
|
||||||
conf = await self.check_conf(['mysql_database'])
|
engine = await self.connect()
|
||||||
if conf:
|
|
||||||
raise(LookupError)
|
|
||||||
database = await self.connect()
|
|
||||||
cursor = database.cursor()
|
|
||||||
db = await self.config.mysql_database()
|
db = await self.config.mysql_database()
|
||||||
guilds: list[discord.Guild] = self.bot.guilds
|
guilds: list[discord.Guild] = self.bot.guilds
|
||||||
for guild in guilds:
|
async with engine.connect() as conn:
|
||||||
if not await self.bot.cog_disabled_in_guild(self, guild):
|
for guild in guilds:
|
||||||
tempban_query = f"SELECT target_id, moderation_id FROM moderation_{guild.id} WHERE end_timestamp != 0 AND end_timestamp <= %s AND moderation_type = 'TEMPBAN' AND expired = 0"
|
if not await self.bot.cog_disabled_in_guild(self, guild):
|
||||||
try:
|
table = await self.fetch_table(engine, guild.id)
|
||||||
cursor.execute(tempban_query, (time.time(),))
|
result = conn.execute(
|
||||||
result = cursor.fetchall()
|
table.select().where(table.c.moderationType == 'TEMPBAN').where(table.c.expired == 0).where(table.c.endTimestamp != 0).where(table.c.endTimestamp <= time.time()))
|
||||||
except mysql.connector.errors.ProgrammingError:
|
target_ids = [row[0] for row in result]
|
||||||
continue
|
moderation_ids = [row[1] for row in result]
|
||||||
target_ids = [row[0] for row in result]
|
for target_id, moderation_id in zip(target_ids, moderation_ids):
|
||||||
moderation_ids = [row[1] for row in result]
|
user: discord.User = await self.bot.fetch_user(target_id)
|
||||||
for target_id, moderation_id in zip(target_ids, moderation_ids):
|
await guild.unban(user, reason=f"Automatic unban from case #{moderation_id}")
|
||||||
user: discord.User = await self.bot.fetch_user(target_id)
|
embed = await self.embed_factory('message', guild, f'Automatic unban from case #{moderation_id}', 'unbanned')
|
||||||
await guild.unban(user, reason=f"Automatic unban from case #{moderation_id}")
|
try:
|
||||||
embed = await self.embed_factory('message', guild, f'Automatic unban from case #{moderation_id}', 'unbanned')
|
await user.send(embed=embed)
|
||||||
try:
|
except discord.errors.HTTPException:
|
||||||
await user.send(embed=embed)
|
pass
|
||||||
except discord.errors.HTTPException:
|
conn.execute(
|
||||||
pass
|
table.update().
|
||||||
expiry_query = f"UPDATE `{db}`.`moderation_{guild.id}` SET expired = 1 WHERE (end_timestamp != 0 AND end_timestamp <= %s AND expired = 0) OR (expired = 0 AND resolved = 1)"
|
where(
|
||||||
cursor.execute(expiry_query, (time.time(),))
|
or_(
|
||||||
database.commit()
|
and_(
|
||||||
cursor.close()
|
table.c.end_timestamp != 0,
|
||||||
database.close()
|
table.c.end_timestamp <= time.time(),
|
||||||
|
table.c.expired == 0
|
||||||
|
),
|
||||||
|
and_(
|
||||||
|
table.c.expired == 0,
|
||||||
|
table.c.resolved == 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).
|
||||||
|
values(expired=1)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
@commands.group(autohelp=True)
|
@commands.group(autohelp=True)
|
||||||
@checks.admin()
|
@checks.admin()
|
||||||
|
|
160
poetry.lock
generated
160
poetry.lock
generated
|
@ -1,4 +1,4 @@
|
||||||
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiodns"
|
name = "aiodns"
|
||||||
|
@ -730,6 +730,76 @@ files = [
|
||||||
{file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"},
|
{file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "greenlet"
|
||||||
|
version = "3.0.1"
|
||||||
|
description = "Lightweight in-process concurrent programming"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"},
|
||||||
|
{file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"},
|
||||||
|
{file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"},
|
||||||
|
{file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"},
|
||||||
|
{file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"},
|
||||||
|
{file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"},
|
||||||
|
{file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"},
|
||||||
|
{file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["Sphinx"]
|
||||||
|
test = ["objgraph", "psutil"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h11"
|
name = "h11"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
|
@ -2015,6 +2085,92 @@ files = [
|
||||||
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlalchemy"
|
||||||
|
version = "2.0.22"
|
||||||
|
description = "Database Abstraction Library"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f146c61ae128ab43ea3a0955de1af7e1633942c2b2b4985ac51cc292daf33222"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:875de9414393e778b655a3d97d60465eb3fae7c919e88b70cc10b40b9f56042d"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13790cb42f917c45c9c850b39b9941539ca8ee7917dacf099cc0b569f3d40da7"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e04ab55cf49daf1aeb8c622c54d23fa4bec91cb051a43cc24351ba97e1dd09f5"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a42c9fa3abcda0dcfad053e49c4f752eef71ecd8c155221e18b99d4224621176"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:14cd3bcbb853379fef2cd01e7c64a5d6f1d005406d877ed9509afb7a05ff40a5"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-win32.whl", hash = "sha256:d143c5a9dada696bcfdb96ba2de4a47d5a89168e71d05a076e88a01386872f97"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp310-cp310-win_amd64.whl", hash = "sha256:ccd87c25e4c8559e1b918d46b4fa90b37f459c9b4566f1dfbce0eb8122571547"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f6ff392b27a743c1ad346d215655503cec64405d3b694228b3454878bf21590"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f776c2c30f0e5f4db45c3ee11a5f2a8d9de68e81eb73ec4237de1e32e04ae81c"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8f1792d20d2f4e875ce7a113f43c3561ad12b34ff796b84002a256f37ce9437"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80eeb5189d7d4b1af519fc3f148fe7521b9dfce8f4d6a0820e8f5769b005051"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:69fd9e41cf9368afa034e1c81f3570afb96f30fcd2eb1ef29cb4d9371c6eece2"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54bcceaf4eebef07dadfde424f5c26b491e4a64e61761dea9459103ecd6ccc95"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-win32.whl", hash = "sha256:7ee7ccf47aa503033b6afd57efbac6b9e05180f492aeed9fcf70752556f95624"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp311-cp311-win_amd64.whl", hash = "sha256:b560f075c151900587ade06706b0c51d04b3277c111151997ea0813455378ae0"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2c9bac865ee06d27a1533471405ad240a6f5d83195eca481f9fc4a71d8b87df8"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:625b72d77ac8ac23da3b1622e2da88c4aedaee14df47c8432bf8f6495e655de2"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b39a6e21110204a8c08d40ff56a73ba542ec60bab701c36ce721e7990df49fb9"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a766cb0b468223cafdf63e2d37f14a4757476157927b09300c8c5832d88560"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0e1ce8ebd2e040357dde01a3fb7d30d9b5736b3e54a94002641dfd0aa12ae6ce"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:505f503763a767556fa4deae5194b2be056b64ecca72ac65224381a0acab7ebe"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-win32.whl", hash = "sha256:154a32f3c7b00de3d090bc60ec8006a78149e221f1182e3edcf0376016be9396"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp312-cp312-win_amd64.whl", hash = "sha256:129415f89744b05741c6f0b04a84525f37fbabe5dc3774f7edf100e7458c48cd"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3940677d341f2b685a999bffe7078697b5848a40b5f6952794ffcf3af150c301"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55914d45a631b81a8a2cb1a54f03eea265cf1783241ac55396ec6d735be14883"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2096d6b018d242a2bcc9e451618166f860bb0304f590d205173d317b69986c95"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:19c6986cf2fb4bc8e0e846f97f4135a8e753b57d2aaaa87c50f9acbe606bd1db"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6ac28bd6888fe3c81fbe97584eb0b96804bd7032d6100b9701255d9441373ec1"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp37-cp37m-win32.whl", hash = "sha256:cb9a758ad973e795267da334a92dd82bb7555cb36a0960dcabcf724d26299db8"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp37-cp37m-win_amd64.whl", hash = "sha256:40b1206a0d923e73aa54f0a6bd61419a96b914f1cd19900b6c8226899d9742ad"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3aa1472bf44f61dd27987cd051f1c893b7d3b17238bff8c23fceaef4f1133868"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:56a7e2bb639df9263bf6418231bc2a92a773f57886d371ddb7a869a24919face"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccca778c0737a773a1ad86b68bda52a71ad5950b25e120b6eb1330f0df54c3d0"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6c3e9350f9fb16de5b5e5fbf17b578811a52d71bb784cc5ff71acb7de2a7f9"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:564e9f9e4e6466273dbfab0e0a2e5fe819eec480c57b53a2cdee8e4fdae3ad5f"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:af66001d7b76a3fab0d5e4c1ec9339ac45748bc4a399cbc2baa48c1980d3c1f4"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-win32.whl", hash = "sha256:9e55dff5ec115316dd7a083cdc1a52de63693695aecf72bc53a8e1468ce429e5"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp38-cp38-win_amd64.whl", hash = "sha256:4e869a8ff7ee7a833b74868a0887e8462445ec462432d8cbeff5e85f475186da"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9886a72c8e6371280cb247c5d32c9c8fa141dc560124348762db8a8b236f8692"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a571bc8ac092a3175a1d994794a8e7a1f2f651e7c744de24a19b4f740fe95034"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8db5ba8b7da759b727faebc4289a9e6a51edadc7fc32207a30f7c6203a181592"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b0b3f2686c3f162123adba3cb8b626ed7e9b8433ab528e36ed270b4f70d1cdb"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c1fea8c0abcb070ffe15311853abfda4e55bf7dc1d4889497b3403629f3bf00"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4bb062784f37b2d75fd9b074c8ec360ad5df71f933f927e9e95c50eb8e05323c"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-win32.whl", hash = "sha256:58a3aba1bfb32ae7af68da3f277ed91d9f57620cf7ce651db96636790a78b736"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-cp39-cp39-win_amd64.whl", hash = "sha256:92e512a6af769e4725fa5b25981ba790335d42c5977e94ded07db7d641490a85"},
|
||||||
|
{file = "SQLAlchemy-2.0.22-py3-none-any.whl", hash = "sha256:3076740335e4aaadd7deb3fe6dcb96b3015f1613bd190a4e1634e1b99b02ec86"},
|
||||||
|
{file = "SQLAlchemy-2.0.22.tar.gz", hash = "sha256:5434cc601aa17570d79e5377f5fd45ff92f9379e2abed0be5e8c2fba8d353d2b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
greenlet = {version = "!=0.4.17", optional = true, markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or extra == \"asyncio\""}
|
||||||
|
typing-extensions = ">=4.2.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
|
||||||
|
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
|
||||||
|
asyncio = ["greenlet (!=0.4.17)"]
|
||||||
|
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
|
||||||
|
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
|
||||||
|
mssql = ["pyodbc"]
|
||||||
|
mssql-pymssql = ["pymssql"]
|
||||||
|
mssql-pyodbc = ["pyodbc"]
|
||||||
|
mypy = ["mypy (>=0.910)"]
|
||||||
|
mysql = ["mysqlclient (>=1.4.0)"]
|
||||||
|
mysql-connector = ["mysql-connector-python"]
|
||||||
|
oracle = ["cx-oracle (>=7)"]
|
||||||
|
oracle-oracledb = ["oracledb (>=1.0.1)"]
|
||||||
|
postgresql = ["psycopg2 (>=2.7)"]
|
||||||
|
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||||
|
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
|
||||||
|
postgresql-psycopg = ["psycopg (>=3.0.7)"]
|
||||||
|
postgresql-psycopg2binary = ["psycopg2-binary"]
|
||||||
|
postgresql-psycopg2cffi = ["psycopg2cffi"]
|
||||||
|
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
|
||||||
|
pymysql = ["pymysql"]
|
||||||
|
sqlcipher = ["sqlcipher3-binary"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
|
@ -2281,4 +2437,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.9,<3.12"
|
python-versions = ">=3.9,<3.12"
|
||||||
content-hash = "84ec60771c14ef544d86eb389954a47c2767491c95ee63a0632aa5be68205468"
|
content-hash = "353a8b2f960633927176885381eefa3eee9441798d53026556152a635f75cd10"
|
||||||
|
|
|
@ -14,6 +14,7 @@ prisma = "^0.10.0"
|
||||||
mysql-connector-python = "^8.1.0"
|
mysql-connector-python = "^8.1.0"
|
||||||
humanize = "^4.8.0"
|
humanize = "^4.8.0"
|
||||||
pytube = "^15.0.0"
|
pytube = "^15.0.0"
|
||||||
|
sqlalchemy = {extras = ["asyncio"], version = "^2.0.22"}
|
||||||
|
|
||||||
[tool.poetry.group.dev]
|
[tool.poetry.group.dev]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
Loading…
Reference in a new issue