Merge pull request 'convert Moderation to sqlite3 (and rename it to Aurora)' (#12) from sqlite3 into main
Reviewed-on: https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs/pulls/12
This commit is contained in:
commit
3d05a8e68e
16 changed files with 139 additions and 268 deletions
5
aurora/__init__.py
Normal file
5
aurora/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from .aurora import Aurora
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
await bot.add_cog(Aurora(bot))
|
|
@ -8,27 +8,27 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import sqlite3
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import humanize
|
import humanize
|
||||||
import mysql.connector
|
|
||||||
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, commands, data_manager
|
from redbot.core import app_commands, checks, commands, data_manager
|
||||||
from redbot.core.app_commands import Choice
|
from redbot.core.app_commands import Choice
|
||||||
|
|
||||||
from .importers.galacticbot import ImportGalacticBotView
|
from .importers.galacticbot import ImportGalacticBotView
|
||||||
from .importers.moderation import ImportModerationView
|
from .importers.aurora import ImportAuroraView
|
||||||
from .utilities.config import config, register_config
|
from .utilities.config import config, register_config
|
||||||
from .utilities.database import connect, create_guild_table, fetch_case, mysql_log
|
from .utilities.database import connect, create_guild_table, fetch_case, mysql_log
|
||||||
from .utilities.embed_factory import embed_factory
|
from .utilities.embed_factory import embed_factory
|
||||||
from .utilities.logger import logger
|
from .utilities.logger import logger
|
||||||
from .utilities.utils import check_conf, check_moddable, check_permissions, fetch_channel_dict, fetch_user_dict, generate_dict, log, send_evidenceformat
|
from .utilities.utils import check_moddable, check_permissions, fetch_channel_dict, fetch_user_dict, generate_dict, log, send_evidenceformat
|
||||||
|
|
||||||
|
|
||||||
class Moderation(commands.Cog):
|
class Aurora(commands.Cog):
|
||||||
"""Custom moderation cog.
|
"""Custom moderation cog.
|
||||||
Developed by SeaswimmerTheFsh."""
|
Developed by SeaswimmerTheFsh."""
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class Moderation(commands.Cog):
|
||||||
if requester == "discord_deleted_user":
|
if requester == "discord_deleted_user":
|
||||||
await config.user_from_id(user_id).clear()
|
await config.user_from_id(user_id).clear()
|
||||||
|
|
||||||
database = await connect()
|
database = connect()
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
|
|
||||||
cursor.execute("SHOW TABLES;")
|
cursor.execute("SHOW TABLES;")
|
||||||
|
@ -68,17 +68,6 @@ class Moderation(commands.Cog):
|
||||||
|
|
||||||
async def cog_load(self):
|
async def cog_load(self):
|
||||||
"""This method prepares the database schema for all of the guilds the bot is currently in."""
|
"""This method prepares the database schema for all of the guilds the bot is currently in."""
|
||||||
conf = await check_conf([
|
|
||||||
'mysql_address',
|
|
||||||
'mysql_database',
|
|
||||||
'mysql_username',
|
|
||||||
'mysql_password'
|
|
||||||
])
|
|
||||||
|
|
||||||
if conf:
|
|
||||||
logger.error("Failed to create tables, due to MySQL connection configuration being unset.")
|
|
||||||
return
|
|
||||||
|
|
||||||
guilds: list[discord.Guild] = self.bot.guilds
|
guilds: list[discord.Guild] = self.bot.guilds
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -96,20 +85,8 @@ class Moderation(commands.Cog):
|
||||||
async def db_generate_guild_join(self, guild: discord.Guild):
|
async def db_generate_guild_join(self, guild: discord.Guild):
|
||||||
"""This method prepares the database schema whenever the bot joins a guild."""
|
"""This method prepares the database schema whenever the bot joins a guild."""
|
||||||
if not await self.bot.cog_disabled_in_guild(self, guild):
|
if not await self.bot.cog_disabled_in_guild(self, guild):
|
||||||
conf = await check_conf([
|
|
||||||
'mysql_address',
|
|
||||||
'mysql_database',
|
|
||||||
'mysql_username',
|
|
||||||
'mysql_password'
|
|
||||||
|
|
||||||
])
|
|
||||||
if conf:
|
|
||||||
logger.error("Failed to create a table for %s, due to MySQL connection configuration being unset.", guild.id)
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await create_guild_table(guild)
|
await create_guild_table(guild)
|
||||||
|
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -592,15 +569,16 @@ class Moderation(commands.Cog):
|
||||||
await interaction.followup.send(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True)
|
await interaction.followup.send(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
database = await connect()
|
database = connect()
|
||||||
|
|
||||||
if export:
|
if export:
|
||||||
cursor = database.cursor(dictionary=True)
|
database.row_factory = sqlite3.Row
|
||||||
|
cursor = database.cursor()
|
||||||
|
|
||||||
query = """SELECT *
|
query = f"""SELECT *
|
||||||
FROM moderation_%s
|
FROM moderation_{interaction.guild.id}
|
||||||
ORDER BY moderation_id DESC;"""
|
ORDER BY moderation_id DESC;"""
|
||||||
cursor.execute(query, (interaction.guild.id,))
|
cursor.execute(query)
|
||||||
|
|
||||||
results = cursor.fetchall()
|
results = cursor.fetchall()
|
||||||
|
|
||||||
|
@ -622,22 +600,22 @@ class Moderation(commands.Cog):
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
|
|
||||||
if target:
|
if target:
|
||||||
query = """SELECT *
|
query = f"""SELECT *
|
||||||
FROM moderation_%s
|
FROM moderation_{interaction.guild.id}
|
||||||
WHERE target_id = %s
|
WHERE target_id = ?
|
||||||
ORDER BY moderation_id DESC;"""
|
ORDER BY moderation_id DESC;"""
|
||||||
cursor.execute(query, (interaction.guild.id, target.id))
|
cursor.execute(query, (target.id,))
|
||||||
elif moderator:
|
elif moderator:
|
||||||
query = """SELECT *
|
query = f"""SELECT *
|
||||||
FROM moderation_%s
|
FROM moderation_{interaction.guild.id}
|
||||||
WHERE moderator_id = %s
|
WHERE moderator_id = ?
|
||||||
ORDER BY moderation_id DESC;"""
|
ORDER BY moderation_id DESC;"""
|
||||||
cursor.execute(query, (interaction.guild.id, moderator.id))
|
cursor.execute(query, (moderator.id,))
|
||||||
else:
|
else:
|
||||||
query = """SELECT *
|
query = f"""SELECT *
|
||||||
FROM moderation_%s
|
FROM moderation_{interaction.guild.id}
|
||||||
ORDER BY moderation_id DESC;"""
|
ORDER BY moderation_id DESC;"""
|
||||||
cursor.execute(query, (interaction.guild.id,))
|
cursor.execute(query)
|
||||||
|
|
||||||
results = cursor.fetchall()
|
results = cursor.fetchall()
|
||||||
result_dict_list = []
|
result_dict_list = []
|
||||||
|
@ -714,23 +692,18 @@ class Moderation(commands.Cog):
|
||||||
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 check_conf(['mysql_database'])
|
database = connect()
|
||||||
if conf:
|
|
||||||
raise(LookupError)
|
|
||||||
|
|
||||||
database = await connect()
|
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
db = await config.mysql_database()
|
|
||||||
|
|
||||||
query_1 = "SELECT * FROM moderation_%s WHERE moderation_id = %s;"
|
query_1 = f"SELECT * FROM moderation_{interaction.guild.id} WHERE moderation_id = ?;"
|
||||||
cursor.execute(query_1, (interaction.guild.id, case))
|
cursor.execute(query_1, (case,))
|
||||||
result_1 = cursor.fetchone()
|
result_1 = cursor.fetchone()
|
||||||
if result_1 is None or case == 0:
|
if result_1 is None or case == 0:
|
||||||
await interaction.response.send_message(content=f"There is no moderation with a case number of {case}.", ephemeral=True)
|
await interaction.response.send_message(content=f"There is no moderation with a case number of {case}.", ephemeral=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
query_2 = "SELECT * FROM moderation_%s WHERE moderation_id = %s AND resolved = 0;"
|
query_2 = f"SELECT * FROM moderation_{interaction.guild.id} WHERE moderation_id = ? AND resolved = 0;"
|
||||||
cursor.execute(query_2, (interaction.guild.id, case))
|
cursor.execute(query_2, (case,))
|
||||||
result_2 = cursor.fetchone()
|
result_2 = cursor.fetchone()
|
||||||
if result_2 is None:
|
if result_2 is None:
|
||||||
await interaction.response.send_message(content=f"This moderation has already been resolved!\nUse `/case {case}` for more information.", ephemeral=True)
|
await interaction.response.send_message(content=f"This moderation has already been resolved!\nUse `/case {case}` for more information.", ephemeral=True)
|
||||||
|
@ -782,9 +755,9 @@ class Moderation(commands.Cog):
|
||||||
except discord.NotFound:
|
except discord.NotFound:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
resolve_query = f"UPDATE `{db}`.`moderation_{interaction.guild.id}` SET resolved = 1, changes = %s, resolved_by = %s, resolve_reason = %s WHERE moderation_id = %s"
|
resolve_query = f"UPDATE `moderation_{interaction.guild.id}` SET resolved = 1, changes = ?, resolved_by = ?, resolve_reason = ? WHERE moderation_id = ?"
|
||||||
else:
|
else:
|
||||||
resolve_query = f"UPDATE `{db}`.`moderation_{interaction.guild.id}` SET resolved = 1, changes = %s, resolved_by = %s, resolve_reason = %s WHERE moderation_id = %s"
|
resolve_query = f"UPDATE `moderation_{interaction.guild.id}` SET resolved = 1, changes = ?, resolved_by = ?, resolve_reason = ? WHERE moderation_id = ?"
|
||||||
|
|
||||||
cursor.execute(resolve_query, (json.dumps(changes), interaction.user.id, reason, case_dict['moderation_id']))
|
cursor.execute(resolve_query, (json.dumps(changes), interaction.user.id, reason, case_dict['moderation_id']))
|
||||||
database.commit()
|
database.commit()
|
||||||
|
@ -878,10 +851,6 @@ class Moderation(commands.Cog):
|
||||||
parsed_time = None
|
parsed_time = None
|
||||||
case_dict = await fetch_case(case, interaction.guild.id)
|
case_dict = await fetch_case(case, interaction.guild.id)
|
||||||
if case_dict:
|
if case_dict:
|
||||||
conf = await check_conf(['mysql_database'])
|
|
||||||
if conf:
|
|
||||||
raise(LookupError)
|
|
||||||
|
|
||||||
if duration:
|
if duration:
|
||||||
try:
|
try:
|
||||||
parsed_time = parse(sval=duration, as_timedelta=True, raise_exception=True)
|
parsed_time = parse(sval=duration, as_timedelta=True, raise_exception=True)
|
||||||
|
@ -941,15 +910,14 @@ class Moderation(commands.Cog):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
database = await connect()
|
database = connect()
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
db = await config.mysql_database()
|
|
||||||
|
|
||||||
if parsed_time:
|
if parsed_time:
|
||||||
update_query = f"UPDATE `{db}`.`moderation_{interaction.guild.id}` SET changes = %s, reason = %s, duration = %s, end_timestamp = %s WHERE moderation_id = %s"
|
update_query = f"UPDATE `moderation_{interaction.guild.id}` SET changes = %s, reason = %s, duration = %s, end_timestamp = %s WHERE moderation_id = %s"
|
||||||
cursor.execute(update_query, (json.dumps(changes), reason, parsed_time, end_timestamp, case))
|
cursor.execute(update_query, (json.dumps(changes), reason, parsed_time, end_timestamp, case))
|
||||||
else:
|
else:
|
||||||
update_query = f"UPDATE `{db}`.`moderation_{interaction.guild.id}` SET changes = %s, reason = %s WHERE moderation_id = %s"
|
update_query = f"UPDATE `moderation_{interaction.guild.id}` SET changes = %s, reason = %s WHERE moderation_id = %s"
|
||||||
cursor.execute(update_query, (json.dumps(changes), reason, case))
|
cursor.execute(update_query, (json.dumps(changes), reason, case))
|
||||||
database.commit()
|
database.commit()
|
||||||
|
|
||||||
|
@ -966,24 +934,19 @@ class Moderation(commands.Cog):
|
||||||
|
|
||||||
@tasks.loop(minutes=1)
|
@tasks.loop(minutes=1)
|
||||||
async def handle_expiry(self):
|
async def handle_expiry(self):
|
||||||
conf = await check_conf(['mysql_database'])
|
database = connect()
|
||||||
if conf:
|
|
||||||
raise(LookupError)
|
|
||||||
|
|
||||||
database = await connect()
|
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
db = await config.mysql_database()
|
|
||||||
|
|
||||||
guilds: list[discord.Guild] = self.bot.guilds
|
guilds: list[discord.Guild] = self.bot.guilds
|
||||||
for guild in guilds:
|
for guild in guilds:
|
||||||
if not await self.bot.cog_disabled_in_guild(self, guild):
|
if not await self.bot.cog_disabled_in_guild(self, guild):
|
||||||
|
|
||||||
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"
|
tempban_query = f"SELECT target_id, moderation_id FROM moderation_{guild.id} WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = 'TEMPBAN' AND expired = 0"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(tempban_query, (time.time(),))
|
cursor.execute(tempban_query, (time.time(),))
|
||||||
result = cursor.fetchall()
|
result = cursor.fetchall()
|
||||||
except mysql.connector.errors.ProgrammingError:
|
except sqlite3.OperationalError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
target_ids = [row[0] for row in result]
|
target_ids = [row[0] for row in result]
|
||||||
|
@ -1003,15 +966,14 @@ class Moderation(commands.Cog):
|
||||||
except [discord.errors.NotFound, discord.errors.Forbidden, discord.errors.HTTPException] as e:
|
except [discord.errors.NotFound, discord.errors.Forbidden, discord.errors.HTTPException] as e:
|
||||||
print(f"Failed to unban {user.name}#{user.discriminator} ({user.id}) from {guild.name} ({guild.id})\n{e}")
|
print(f"Failed to unban {user.name}#{user.discriminator} ({user.id}) from {guild.name} ({guild.id})\n{e}")
|
||||||
|
|
||||||
expiry_query = f"UPDATE `{db}`.`moderation_{guild.id}` SET expired = 1 WHERE (end_timestamp != 0 AND end_timestamp <= %s AND expired = 0 AND moderation_type != 'BLACKLIST') OR (expired = 0 AND resolved = 1 AND moderation_type != 'BLACKLIST')"
|
expiry_query = f"UPDATE `moderation_{guild.id}` SET expired = 1 WHERE (end_timestamp != 0 AND end_timestamp <= ? AND expired = 0 AND moderation_type != 'BLACKLIST') OR (expired = 0 AND resolved = 1 AND moderation_type != 'BLACKLIST')"
|
||||||
cursor.execute(expiry_query, (time.time(),))
|
cursor.execute(expiry_query, (time.time(),))
|
||||||
|
|
||||||
blacklist_query = f"SELECT target_id, moderation_id, role_id FROM moderation_{guild.id} WHERE end_timestamp != 0 AND end_timestamp <= %s AND moderation_type = 'BLACKLIST' AND expired = 0"
|
blacklist_query = f"SELECT target_id, moderation_id, role_id FROM moderation_{guild.id} WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = 'BLACKLIST' AND expired = 0"
|
||||||
try:
|
try:
|
||||||
cursor.execute(blacklist_query, (time.time(),))
|
cursor.execute(blacklist_query, (time.time(),))
|
||||||
result = cursor.fetchall()
|
result = cursor.fetchall()
|
||||||
except mysql.connector.errors.ProgrammingError:
|
except sqlite3.OperationalError:
|
||||||
|
|
||||||
continue
|
continue
|
||||||
target_ids = [row[0] for row in result]
|
target_ids = [row[0] for row in result]
|
||||||
moderation_ids = [row[1] for row in result]
|
moderation_ids = [row[1] for row in result]
|
||||||
|
@ -1047,7 +1009,7 @@ class Moderation(commands.Cog):
|
||||||
|
|
||||||
guild_settings_string = ""
|
guild_settings_string = ""
|
||||||
for setting in guild_settings:
|
for setting in guild_settings:
|
||||||
if 'mysql' in setting or 'roles' in setting:
|
if 'roles' in setting:
|
||||||
continue
|
continue
|
||||||
if setting == 'log_channel':
|
if setting == 'log_channel':
|
||||||
channel = ctx.guild.get_channel(guild_settings[setting])
|
channel = ctx.guild.get_channel(guild_settings[setting])
|
||||||
|
@ -1323,96 +1285,20 @@ class Moderation(commands.Cog):
|
||||||
await config.guild(ctx.guild).log_channel.set(" ")
|
await config.guild(ctx.guild).log_channel.set(" ")
|
||||||
await ctx.send("Logging channel disabled.")
|
await ctx.send("Logging channel disabled.")
|
||||||
|
|
||||||
@moderationset.command(name="mysql")
|
|
||||||
@checks.is_owner()
|
|
||||||
async def moderationset_mysql(self, ctx: commands.Context):
|
|
||||||
"""Configure MySQL connection details."""
|
|
||||||
await ctx.message.add_reaction("✅")
|
|
||||||
await ctx.author.send(content="Click the button below to configure your MySQL connection details.", view=self.ConfigButtons(60))
|
|
||||||
|
|
||||||
class ConfigButtons(discord.ui.View):
|
|
||||||
def __init__(self, timeout):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
@discord.ui.button(label="Edit", style=discord.ButtonStyle.success)
|
|
||||||
async def config_button(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
|
||||||
await interaction.response.send_modal(Moderation.MySQLConfigModal)
|
|
||||||
|
|
||||||
class MySQLConfigModal(discord.ui.Modal, title="MySQL Database Configuration"):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
address = discord.ui.TextInput(
|
|
||||||
label="Address",
|
|
||||||
placeholder="Input your MySQL address here.",
|
|
||||||
style=discord.TextStyle.short,
|
|
||||||
required=False,
|
|
||||||
max_length=300
|
|
||||||
)
|
|
||||||
database = discord.ui.TextInput(
|
|
||||||
label="Database",
|
|
||||||
placeholder="Input the name of your database here.",
|
|
||||||
style=discord.TextStyle.short,
|
|
||||||
required=False,
|
|
||||||
max_length=300
|
|
||||||
)
|
|
||||||
username = discord.ui.TextInput(
|
|
||||||
label="Username",
|
|
||||||
placeholder="Input your MySQL username here.",
|
|
||||||
style=discord.TextStyle.short,
|
|
||||||
required=False,
|
|
||||||
max_length=300
|
|
||||||
)
|
|
||||||
password = discord.ui.TextInput(
|
|
||||||
label="Password",
|
|
||||||
placeholder="Input your MySQL password here.",
|
|
||||||
style=discord.TextStyle.short,
|
|
||||||
required=False,
|
|
||||||
max_length=300
|
|
||||||
)
|
|
||||||
|
|
||||||
async def on_submit(self, interaction: discord.Interaction):
|
|
||||||
message = ""
|
|
||||||
|
|
||||||
if self.address.value != "":
|
|
||||||
await config.mysql_address.set(self.address.value)
|
|
||||||
message += f"- Address set to\n - `{self.address.value}`\n"
|
|
||||||
|
|
||||||
if self.database.value != "":
|
|
||||||
await config.mysql_database.set(self.database.value)
|
|
||||||
message += f"- Database set to\n - `{self.database.value}`\n"
|
|
||||||
|
|
||||||
if self.username.value != "":
|
|
||||||
await config.mysql_username.set(self.username.value)
|
|
||||||
message += f"- Username set to\n - `{self.username.value}`\n"
|
|
||||||
|
|
||||||
if self.password.value != "":
|
|
||||||
await config.mysql_password.set(self.password.value)
|
|
||||||
trimmed_password = self.password.value[:8]
|
|
||||||
message += f"- Password set to\n - `{trimmed_password}` - Trimmed for security\n"
|
|
||||||
|
|
||||||
if message == "":
|
|
||||||
trimmed_password = str(await config.mysql_password())[:8]
|
|
||||||
send = f"No changes were made.\nCurrent configuration:\n- Address:\n - `{await config.mysql_address()}`\n- Database:\n - `{await config.mysql_database()}`\n- Username:\n - `{await config.mysql_username()}`\n- Password:\n - `{trimmed_password}` - Trimmed for security"
|
|
||||||
|
|
||||||
else:
|
|
||||||
send = f"Configuration changed:\n{message}"
|
|
||||||
|
|
||||||
await interaction.response.send_message(send, ephemeral=True)
|
|
||||||
|
|
||||||
@moderationset.group(autohelp=True, name='import')
|
@moderationset.group(autohelp=True, name='import')
|
||||||
@checks.admin()
|
@checks.admin()
|
||||||
async def moderationset_import(self, ctx: commands.Context):
|
async def moderationset_import(self, ctx: commands.Context):
|
||||||
"""Import moderations from other bots."""
|
"""Import moderations from other bots."""
|
||||||
|
|
||||||
@moderationset_import.command(name="moderation")
|
@moderationset_import.command(name="aurora")
|
||||||
@checks.admin()
|
@checks.admin()
|
||||||
async def moderationset_import_moderation(self, ctx: commands.Context):
|
async def moderationset_import_aurora(self, ctx: commands.Context):
|
||||||
"""Import moderations from another bot using this cog."""
|
"""Import moderations from another bot using Aurora."""
|
||||||
if ctx.message.attachments and ctx.message.attachments[0].content_type == 'application/json; charset=utf-8':
|
if ctx.message.attachments and ctx.message.attachments[0].content_type == 'application/json; charset=utf-8':
|
||||||
message = await ctx.send("Are you sure you want to import moderations from another bot?\n**This will overwrite any moderations that already exist in this guild's moderation table.**\n*The import process will block the rest of your bot until it is complete.*")
|
message = await ctx.send("Are you sure you want to import moderations from another bot?\n**This will overwrite any moderations that already exist in this guild's moderation table.**\n*The import process will block the rest of your bot until it is complete.*")
|
||||||
await message.edit(view=ImportModerationView(60, ctx, message))
|
await message.edit(view=ImportAuroraView(60, ctx, message))
|
||||||
else:
|
else:
|
||||||
await ctx.send("Please provide a valid moderation export file.")
|
await ctx.send("Please provide a valid Aurora export file.")
|
||||||
|
|
||||||
@moderationset_import.command(name="galacticbot")
|
@moderationset_import.command(name="galacticbot")
|
||||||
@checks.admin()
|
@checks.admin()
|
|
@ -8,7 +8,7 @@ from redbot.core import commands
|
||||||
from ..utilities.database import connect, create_guild_table, mysql_log
|
from ..utilities.database import connect, create_guild_table, mysql_log
|
||||||
|
|
||||||
|
|
||||||
class ImportModerationView(ui.View):
|
class ImportAuroraView(ui.View):
|
||||||
def __init__(self, timeout, ctx, message):
|
def __init__(self, timeout, ctx, message):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ctx: commands.Context = ctx
|
self.ctx: commands.Context = ctx
|
||||||
|
@ -23,7 +23,7 @@ class ImportModerationView(ui.View):
|
||||||
"Deleting original table...", ephemeral=True
|
"Deleting original table...", ephemeral=True
|
||||||
)
|
)
|
||||||
|
|
||||||
database = await connect()
|
database = connect()
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
|
|
||||||
query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};"
|
query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};"
|
||||||
|
@ -66,9 +66,10 @@ class ImportModerationView(ui.View):
|
||||||
if "metadata" not in case:
|
if "metadata" not in case:
|
||||||
metadata = {}
|
metadata = {}
|
||||||
else:
|
else:
|
||||||
metadata: Dict[str, any] = case["metadata"]
|
metadata: Dict[str, any] = json.loads(case["metadata"])
|
||||||
|
if not metadata['imported_from']:
|
||||||
metadata.update({
|
metadata.update({
|
||||||
'imported_from': 'SeaCogs/Moderation'
|
'imported_from': 'Aurora'
|
||||||
})
|
})
|
||||||
|
|
||||||
if case["duration"] != "NULL":
|
if case["duration"] != "NULL":
|
||||||
|
@ -106,6 +107,6 @@ class ImportModerationView(ui.View):
|
||||||
async def import_button_n(
|
async def import_button_n(
|
||||||
self, interaction: Interaction, button: ui.Button
|
self, interaction: Interaction, button: ui.Button
|
||||||
): # pylint: disable=unused-argument
|
): # pylint: disable=unused-argument
|
||||||
await self.message.edit("Import cancelled.", view=None)
|
await self.message.edit(content="Import cancelled.", view=None)
|
||||||
await self.message.delete(10)
|
await self.message.delete(10)
|
||||||
await self.ctx.message.delete(10)
|
await self.ctx.message.delete(10)
|
|
@ -18,7 +18,7 @@ class ImportGalacticBotView(ui.View):
|
||||||
await self.message.delete()
|
await self.message.delete()
|
||||||
await interaction.response.send_message("Deleting original table...", ephemeral=True)
|
await interaction.response.send_message("Deleting original table...", ephemeral=True)
|
||||||
|
|
||||||
database = await connect()
|
database = connect()
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
|
|
||||||
query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};"
|
query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};"
|
||||||
|
@ -140,6 +140,6 @@ class ImportGalacticBotView(ui.View):
|
||||||
|
|
||||||
@ui.button(label="No", style=ButtonStyle.danger)
|
@ui.button(label="No", style=ButtonStyle.danger)
|
||||||
async def import_button_n(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
|
async def import_button_n(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
|
||||||
await self.message.edit("Import cancelled.", view=None)
|
await self.message.edit(content="Import cancelled.", view=None)
|
||||||
await self.message.delete(10)
|
await self.message.delete(10)
|
||||||
await self.ctx.message.delete(10)
|
await self.ctx.message.delete(10)
|
11
aurora/info.json
Normal file
11
aurora/info.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"author" : ["SeaswimmerTheFsh"],
|
||||||
|
"install_msg" : "Thank you for installing Aurora!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
|
||||||
|
"name" : "Aurora",
|
||||||
|
"short" : "A full replacement for Red's core Mod cogs.",
|
||||||
|
"description" : "Aurora is designed to be a full replacement for Red's core Mod cogs. It is heavily inspired by GalacticBot, and is designed to be a more user-friendly alternative to Red's core Mod cogs. This cog stores all of its data in an SQLite database.",
|
||||||
|
"end_user_data_statement" : "This cog stores the following information:\n- User IDs of accounts who moderate users or are moderated\n- Guild IDs of guilds with the cog enabled\n- Timestamps of moderations\n- Other information relating to moderations",
|
||||||
|
"requirements": ["humanize", "pytimeparse2"],
|
||||||
|
"hidden": false,
|
||||||
|
"disabled": false
|
||||||
|
}
|
|
@ -3,12 +3,6 @@ from redbot.core import Config
|
||||||
config: Config = Config.get_conf(None, identifier=481923957134912, cog_name="Moderation")
|
config: Config = Config.get_conf(None, identifier=481923957134912, cog_name="Moderation")
|
||||||
|
|
||||||
def register_config(config_obj: Config):
|
def register_config(config_obj: Config):
|
||||||
config_obj.register_global(
|
|
||||||
mysql_address= " ",
|
|
||||||
mysql_database = " ",
|
|
||||||
mysql_username = " ",
|
|
||||||
mysql_password = " "
|
|
||||||
)
|
|
||||||
config_obj.register_guild(
|
config_obj.register_guild(
|
||||||
use_discord_permissions = True,
|
use_discord_permissions = True,
|
||||||
ignore_other_bots = True,
|
ignore_other_bots = True,
|
|
@ -2,88 +2,73 @@
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
import sqlite3
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import mysql.connector
|
|
||||||
from discord import Guild
|
from discord import Guild
|
||||||
|
from redbot.core import data_manager
|
||||||
|
|
||||||
from .config import config
|
|
||||||
from .logger import logger
|
from .logger import logger
|
||||||
from .utils import check_conf, generate_dict, get_next_case_number
|
from .utils import generate_dict, get_next_case_number
|
||||||
|
|
||||||
|
|
||||||
async def connect():
|
def connect() -> sqlite3.Connection:
|
||||||
"""Connects to the MySQL database, and returns a connection object."""
|
"""Connects to the SQLite database, and returns a connection object."""
|
||||||
conf = await 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(
|
connection = sqlite3.connect(database=data_manager.cog_data_path(raw_name='Aurora') / 'aurora.db')
|
||||||
host=await config.mysql_address(),
|
|
||||||
user=await config.mysql_username(),
|
|
||||||
password=await config.mysql_password(),
|
|
||||||
database=await config.mysql_database(),
|
|
||||||
)
|
|
||||||
|
|
||||||
return connection
|
return connection
|
||||||
|
|
||||||
except mysql.connector.ProgrammingError as e:
|
except sqlite3.OperationalError as e:
|
||||||
logger.error("Unable to access the MySQL database!\nError:\n%s", e.msg)
|
logger.error("Unable to access the SQLite database!\nError:\n%s", e.msg)
|
||||||
raise ConnectionRefusedError(
|
raise ConnectionRefusedError(
|
||||||
f"Unable to access the MySQL Database!\n{e.msg}"
|
f"Unable to access the SQLite Database!\n{e.msg}"
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
||||||
async def create_guild_table(guild: Guild):
|
async def create_guild_table(guild: Guild):
|
||||||
database = await connect()
|
database = connect()
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"SELECT * FROM `moderation_{guild.id}`")
|
cursor.execute(f"SELECT * FROM `moderation_{guild.id}`")
|
||||||
logger.debug("MySQL Table exists for server %s (%s)", guild.name, guild.id)
|
logger.debug("SQLite Table exists for server %s (%s)", guild.name, guild.id)
|
||||||
|
|
||||||
except mysql.connector.errors.ProgrammingError:
|
except sqlite3.OperationalError:
|
||||||
query = f"""
|
query = f"""
|
||||||
CREATE TABLE `moderation_{guild.id}` (
|
CREATE TABLE `moderation_{guild.id}` (
|
||||||
moderation_id INT UNIQUE PRIMARY KEY NOT NULL,
|
moderation_id INTEGER PRIMARY KEY,
|
||||||
timestamp INT NOT NULL,
|
timestamp INTEGER NOT NULL,
|
||||||
moderation_type LONGTEXT NOT NULL,
|
moderation_type TEXT NOT NULL,
|
||||||
target_type LONGTEXT NOT NULL,
|
target_type TEXT NOT NULL,
|
||||||
target_id LONGTEXT NOT NULL,
|
target_id TEXT NOT NULL,
|
||||||
moderator_id LONGTEXT NOT NULL,
|
moderator_id TEXT NOT NULL,
|
||||||
role_id LONGTEXT,
|
role_id TEXT,
|
||||||
duration LONGTEXT,
|
duration TEXT,
|
||||||
end_timestamp INT,
|
end_timestamp INTEGER,
|
||||||
reason LONGTEXT,
|
reason TEXT,
|
||||||
resolved BOOL NOT NULL,
|
resolved INTEGER NOT NULL,
|
||||||
resolved_by LONGTEXT,
|
resolved_by TEXT,
|
||||||
resolve_reason LONGTEXT,
|
resolve_reason TEXT,
|
||||||
expired BOOL NOT NULL,
|
expired INTEGER NOT NULL,
|
||||||
changes JSON NOT NULL,
|
changes TEXT NOT NULL,
|
||||||
metadata JSON NOT NULL
|
metadata TEXT NOT NULL
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
cursor.execute(query)
|
cursor.execute(query)
|
||||||
|
|
||||||
index_query_1 = "CREATE INDEX idx_target_id ON moderation_%s(target_id(25));"
|
index_query_1 = "CREATE INDEX idx_target_id ON moderation_{}(target_id);"
|
||||||
cursor.execute(index_query_1, (guild.id,))
|
cursor.execute(index_query_1.format(guild.id))
|
||||||
index_query_2 = (
|
|
||||||
"CREATE INDEX idx_moderator_id ON moderation_%s(moderator_id(25));"
|
index_query_2 = "CREATE INDEX idx_moderator_id ON moderation_{}(moderator_id);"
|
||||||
)
|
cursor.execute(index_query_2.format(guild.id))
|
||||||
cursor.execute(index_query_2, (guild.id,))
|
|
||||||
index_query_3 = (
|
index_query_3 = "CREATE INDEX idx_moderation_id ON moderation_{}(moderation_id);"
|
||||||
"CREATE INDEX idx_moderation_id ON moderation_%s(moderation_id);"
|
cursor.execute(index_query_3.format(guild.id))
|
||||||
)
|
|
||||||
cursor.execute(index_query_3, (guild.id,))
|
|
||||||
|
|
||||||
insert_query = f"""
|
insert_query = f"""
|
||||||
INSERT INTO `moderation_{guild.id}`
|
INSERT INTO `moderation_{guild.id}`
|
||||||
(moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata)
|
(moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
"""
|
"""
|
||||||
insert_values = (
|
insert_values = (
|
||||||
0,
|
0,
|
||||||
|
@ -108,7 +93,7 @@ async def create_guild_table(guild: Guild):
|
||||||
database.commit()
|
database.commit()
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"MySQL Table (moderation_%s) created for %s (%s)",
|
"SQLite Table (moderation_%s) created for %s (%s)",
|
||||||
guild.id,
|
guild.id,
|
||||||
guild.name,
|
guild.name,
|
||||||
guild.id,
|
guild.id,
|
||||||
|
@ -117,6 +102,7 @@ async def create_guild_table(guild: Guild):
|
||||||
database.close()
|
database.close()
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=dangerous-default-value
|
||||||
async def mysql_log(
|
async def mysql_log(
|
||||||
guild_id: str,
|
guild_id: str,
|
||||||
author_id: str,
|
author_id: str,
|
||||||
|
@ -124,9 +110,9 @@ async def mysql_log(
|
||||||
target_type: str,
|
target_type: str,
|
||||||
target_id: int,
|
target_id: int,
|
||||||
role_id: int,
|
role_id: int,
|
||||||
duration,
|
duration: timedelta,
|
||||||
reason: str,
|
reason: str,
|
||||||
database: mysql.connector.MySQLConnection = None,
|
database: sqlite3.Connection = None,
|
||||||
timestamp: int = None,
|
timestamp: int = None,
|
||||||
resolved: bool = False,
|
resolved: bool = False,
|
||||||
resolved_by: str = None,
|
resolved_by: str = None,
|
||||||
|
@ -134,13 +120,19 @@ async def mysql_log(
|
||||||
expired: bool = None,
|
expired: bool = None,
|
||||||
changes: list = [],
|
changes: list = [],
|
||||||
metadata: dict = {},
|
metadata: dict = {},
|
||||||
): # pylint: disable=dangerous-default-value
|
) -> int:
|
||||||
if not timestamp:
|
if not timestamp:
|
||||||
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())
|
||||||
|
|
||||||
|
total_seconds = int(duration.total_seconds())
|
||||||
|
hours = total_seconds // 3600
|
||||||
|
minutes = (total_seconds % 3600) // 60
|
||||||
|
seconds = total_seconds % 60
|
||||||
|
duration = f"{hours}:{minutes}:{seconds}"
|
||||||
else:
|
else:
|
||||||
end_timestamp = 0
|
end_timestamp = 0
|
||||||
|
|
||||||
|
@ -157,7 +149,7 @@ async def mysql_log(
|
||||||
resolved_reason = "NULL"
|
resolved_reason = "NULL"
|
||||||
|
|
||||||
if not database:
|
if not database:
|
||||||
database = await connect()
|
database = connect()
|
||||||
close_db = True
|
close_db = True
|
||||||
else:
|
else:
|
||||||
close_db = False
|
close_db = False
|
||||||
|
@ -165,7 +157,7 @@ async def mysql_log(
|
||||||
|
|
||||||
moderation_id = await get_next_case_number(guild_id=guild_id, cursor=cursor)
|
moderation_id = await get_next_case_number(guild_id=guild_id, cursor=cursor)
|
||||||
|
|
||||||
sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
|
sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
val = (
|
val = (
|
||||||
moderation_id,
|
moderation_id,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
@ -192,7 +184,7 @@ async def mysql_log(
|
||||||
database.close()
|
database.close()
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"MySQL row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s",
|
"Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s",
|
||||||
guild_id,
|
guild_id,
|
||||||
moderation_id,
|
moderation_id,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
@ -215,13 +207,13 @@ async def mysql_log(
|
||||||
return moderation_id
|
return moderation_id
|
||||||
|
|
||||||
|
|
||||||
async def fetch_case(moderation_id: int, guild_id: str):
|
async def fetch_case(moderation_id: int, guild_id: str) -> dict:
|
||||||
"""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 connect()
|
database = connect()
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
|
|
||||||
query = "SELECT * FROM moderation_%s WHERE moderation_id = %s;"
|
query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;"
|
||||||
cursor.execute(query, (guild_id, moderation_id))
|
cursor.execute(query, (moderation_id,))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
3
aurora/utilities/logger.py
Normal file
3
aurora/utilities/logger.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger("red.sea.aurora")
|
|
@ -10,17 +10,6 @@ from redbot.core import commands
|
||||||
from .config import config
|
from .config import config
|
||||||
|
|
||||||
|
|
||||||
async def check_conf(config_list: list):
|
|
||||||
"""Checks if any required config options are not set."""
|
|
||||||
not_found_list = []
|
|
||||||
|
|
||||||
for item in config_list:
|
|
||||||
if await config.item() == " ":
|
|
||||||
not_found_list.append(item)
|
|
||||||
|
|
||||||
return not_found_list
|
|
||||||
|
|
||||||
|
|
||||||
def check_permissions(
|
def check_permissions(
|
||||||
user: User,
|
user: User,
|
||||||
permissions: list,
|
permissions: list,
|
||||||
|
@ -116,7 +105,7 @@ async def get_next_case_number(guild_id: str, cursor=None):
|
||||||
from .database import connect
|
from .database import connect
|
||||||
|
|
||||||
if not cursor:
|
if not cursor:
|
||||||
database = await connect()
|
database = connect()
|
||||||
cursor = database.cursor()
|
cursor = database.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1"
|
f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1"
|
9
info.json
Normal file
9
info.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"author": [
|
||||||
|
"SeaswimmerTheFsh (seasw.)"
|
||||||
|
],
|
||||||
|
"install_msg": "Thanks for installing my repo!\n\nIf you have any issues with any of the cogs, please create an issue [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs/issues) or join my [Discord Server](https://discord.gg/eMUMe77Yb8 ).",
|
||||||
|
"name": "SeaCogs",
|
||||||
|
"short": "Various cogs for Red, by SeaswimmerTheFsh (seasw.)",
|
||||||
|
"description": "Various cogs for Red, by SeaswimmerTheFsh (seasw.)"
|
||||||
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
from .moderation import Moderation
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
|
||||||
await bot.add_cog(Moderation(bot))
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"author" : ["SeaswimmerTheFsh"],
|
|
||||||
"install_msg" : "Thank you for installing Moderation!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).\nThis cog currently requires a MySQL database to function, instructions on how to set this up can be found [here]()",
|
|
||||||
"name" : "Moderation",
|
|
||||||
"short" : "Implements a variety of moderation commands",
|
|
||||||
"description" : "Implements a variety of moderation commands, including a warning system, a mute system, and a ban system.",
|
|
||||||
"end_user_data_statement" : "This cog stores the following information:\n- User IDs of accounts who moderate users or are moderated\n- Guild IDs of guilds with the cog enabled\n- Timestamps of moderations\n- Other information relating to moderations",
|
|
||||||
"requirements": ["mysql-connector-python", "humanize", "pytimeparse2"],
|
|
||||||
"hidden": false,
|
|
||||||
"disabled": false
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger("red.sea.moderation")
|
|
Loading…
Reference in a new issue