diff --git a/cogs/logging.py b/cogs/logging.py index 1d9aefd..4c110af 100644 --- a/cogs/logging.py +++ b/cogs/logging.py @@ -5,39 +5,132 @@ # Do Not Remove This Header # ############################################### -# imports and stuff +# imports and stuff +import os +import sqlite3 +from datetime import datetime, timedelta import disnake from disnake.ext import commands -import os -from datetime import datetime -import random import config -from helpers import errors -class logging(commands.Cog): +class Logging(commands.Cog): def __init__(self, bot): - self.bot = bot - + self.bot = bot + self.db_file = 'data/logging.db' + self.channel_id = config.logs + self.max_age_days = 7 + self.init_db() + + def init_db(self): + conn = sqlite3.connect(self.db_file) + c = conn.cursor() + c.execute('''CREATE TABLE IF NOT EXISTS message_logs + (message_id INTEGER PRIMARY KEY, author_id INTEGER, content TEXT, attachments TEXT, timestamp REAL)''') + conn.commit() + conn.close() + + async def save_message_data(self, message): + if message.author.bot: + return + + now = datetime.utcnow() + content = message.content + attachment_urls = [attachment.url for attachment in message.attachments] + conn = sqlite3.connect(self.db_file) + conn.execute("PRAGMA journal_mode=WAL") # Enable WAL + c = conn.cursor() + c.execute('''INSERT OR REPLACE INTO message_logs (message_id, author_id, content, attachments, timestamp) VALUES (?, ?, ?, ?, ?)''', + (message.id, message.author.id, content, ",".join(attachment_urls), now.timestamp())) + conn.commit() + conn.close() + + async def delete_old_messages(self): + cutoff_time = datetime.utcnow() - timedelta(days=self.max_age_days) + + conn = sqlite3.connect(self.db_file) + c = conn.cursor() + c.execute('''DELETE FROM message_logs WHERE timestamp < ?''', (cutoff_time.timestamp(),)) + conn.commit() + conn.close() + + async def get_message_data(self, message_id): + conn = sqlite3.connect(self.db_file) + c = conn.cursor() + c.execute('''SELECT author_id, content, attachments FROM message_logs WHERE message_id = ?''', (message_id,)) + row = c.fetchone() + conn.close() + + if row: + author_id, content, attachments = row + return { + 'author_id': author_id, + 'content': content, + 'attachments': attachments.split(',') if attachments else [] + } + return None + @commands.Cog.listener() - async def on_ready(self): - print(f'Loaded Cog Logging') - - # Message deleted, logs messages and pulls + async def on_message(self, message): +# print("New message:", message.content) + await self.save_message_data(message) + await self.delete_old_messages() + + @commands.Cog.listener() + async def on_message_edit(self, before, after): + if before.content == after.content: + return + +# print("Message edited:", before.content, "=>", after.content) + await self.save_message_data(after) + await self.delete_old_messages() + + channel = self.bot.get_channel(self.channel_id) + embed = disnake.Embed(title="Message edited", description=f"Message edited by {before.author.mention} in {before.channel.mention}:\nBefore: `{before.content}`\nAfter: `{after.content}`", color=0x00ff00) + embed.set_footer(text=f"Message ID: {before.id}") + await channel.send(embed=embed) + @commands.Cog.listener() async def on_message_delete(self, message): - try: - channel = self.bot.get_channel(1054579107666591804) - embed = disnake.Embed( - title="Message Deleted", - description=f"Message sent by **{message.author.mention}** in **{message.channel.mention}** was deleted.", - color=disnake.Color.red() - ) - embed.add_field(name="Message:", value=f'```{message.content}```') + if message.author.bot: + return + + message_data = await self.get_message_data(message.id) + + if message_data: + author_id = message_data['author_id'] + content = message_data['content'] + attachments = message_data['attachments'] + channel = self.bot.get_channel(self.channel_id) + embed = disnake.Embed(title="Message deleted", description=f"Message deleted by <@{author_id}> in {message.channel.mention}:\n`{content}`", color=0xff0000) + if attachments: + embed.add_field(name="Attachments", value="\n".join(attachments)) embed.set_footer(text=f"Message ID: {message.id}") await channel.send(embed=embed) - except Exception as e: - print(f'Error sending logging message: {e}') - - + else: + channel = self.bot.get_channel(self.channel_id) + embed = disnake.Embed(title="Message deleted", description=f"Message deleted by {message.author.mention} in {message.channel.mention}:\n`{message.content}`", color=0xff0000) + embed.set_footer(text=f"Message ID: {message.id}") + await channel.send(embed=embed) + + @commands.Cog.listener() + async def on_raw_message_delete(self, payload): + if payload.cached_message: + return + + channel = self.bot.get_channel(self.channel_id) + embed = disnake.Embed(title="Message deleted", description=f"Message deleted in <#{payload.channel_id}>:\nMessage ID: {payload.message_id}", color=0xff0000) + await channel.send(embed=embed) + + @commands.Cog.listener() + async def on_raw_message_edit(self, payload): + if payload.cached_message: + return + if payload.data.get('author', {}).get('bot', False): + return + + channel = self.bot.get_channel(self.channel_id) + embed = disnake.Embed(title="Message edited", description=f"Message edited in <#{payload.channel_id}>:\nMessage ID: {payload.message_id}", color=0x00ff00) + await channel.send(embed=embed) + def setup(bot): - bot.add_cog(logging(bot)) \ No newline at end of file + bot.add_cog(Logging(bot)) \ No newline at end of file diff --git a/cogs/moderation.py b/cogs/moderation.py index 8b93942..3512a22 100644 --- a/cogs/moderation.py +++ b/cogs/moderation.py @@ -264,15 +264,24 @@ async def warn(self, inter: disnake.ApplicationCommandInteraction, member: disna reason = "No reason provided" with open('data/warns.json', 'r') as f: warns = json.load(f) - if str(member.id) not in warns: - warns[str(member.id)] = [] - warns[str(member.id)].append(reason) + + guild_id = str(inter.guild.id) + if guild_id not in warns: + warns[guild_id] = {} + + member_id = str(member.id) + if member_id not in warns[guild_id]: + warns[guild_id][member_id] = [] + + warns[guild_id][member_id].append(reason) + with open('data/warns.json', 'w') as f: json.dump(warns, f, indent=4) + embed = disnake.Embed(title=f"Successfully Warned ``{member}`` for ``{reason}``", color=config.Success()) embed.set_footer(text=f'Warned by {inter.author}', icon_url=inter.author.avatar.url) await inter.response.send_message(embed=embed) - if len(warns[str(member.id)]) == 3: + if len(warns[guild_id][member_id]) == 3: embed = disnake.Embed(title=f"You Have Been Warned 3 Times in **{member.guild.name}**!", color=config.Success()) embed.add_field(name="Reason:", value=f"``{reason}``", inline=False) embed.set_footer(text=f'Warned by {inter.author}', icon_url=inter.author.avatar.url) @@ -281,7 +290,8 @@ async def warn(self, inter: disnake.ApplicationCommandInteraction, member: disna embed = disnake.Embed(title=f"Successfully Kicked ``{member}`` for ``{reason}``", color=config.Success()) embed.set_footer(text=f'Kicked by {inter.author}', icon_url=inter.author.avatar.url) await inter.response.send_message(embed=embed) - if len(warns[str(member.id)]) == 5: + + if len(warns[guild_id][member_id]) == 5: embed = disnake.Embed(title=f"You Have Been Warned 5 Times in **{member.guild.name}**!", color=config.Success()) embed.add_field(name="Reason:", value=f"``{reason}``", inline=False) embed.set_footer(text=f'Warned by {inter.author}', icon_url=inter.author.avatar.url) @@ -291,7 +301,7 @@ async def warn(self, inter: disnake.ApplicationCommandInteraction, member: disna embed.set_footer(text=f'Banned by {inter.author}', icon_url=inter.author.avatar.url) await inter.response.send_message(embed=embed) except Exception as e: - print(f'Error sending warn command: {e}') + print(f"An error occurred while warning the member: {e}") await inter.send(embed=errors.create_error_embed(f"Error sending warn command: {e}")) # warns command that shows the amount of warns a user has @@ -309,11 +319,17 @@ async def warns(self, inter: disnake.ApplicationCommandInteraction, member: disn return await inter.response.send_message(delete_after=15, embed=embed) with open('data/warns.json', 'r') as f: warns = json.load(f) - if str(member.id) not in warns: + guild_id = str(inter.guild.id) + member_id = str(member.id) + if guild_id not in warns: + embed = disnake.Embed(title=f"``{member}`` has no warns!", color=config.Success()) + embed.set_footer(text=f'Warns checked by {inter.author}', icon_url=inter.author.avatar.url) + return await inter.response.send_message(embed=embed) + if member_id not in warns[guild_id]: embed = disnake.Embed(title=f"``{member}`` has no warns!", color=config.Success()) embed.set_footer(text=f'Warns checked by {inter.author}', icon_url=inter.author.avatar.url) return await inter.response.send_message(embed=embed) - embed = disnake.Embed(title=f"``{member}`` has ``{len(warns[str(member.id)])}`` warns!", color=config.Success()) + embed = disnake.Embed(title=f"``{member}`` has ``{len(warns[guild_id][member_id])}`` warns!", color=config.Success()) embed.set_footer(text=f'Warns checked by {inter.author}', icon_url=inter.author.avatar.url) await inter.response.send_message(embed=embed) except Exception as e: @@ -343,16 +359,18 @@ async def clearwarns(self, inter: disnake.ApplicationCommandInteraction, member: return await inter.response.send_message(delete_after=15, embed=embed) with open('data/warns.json', 'r') as f: warns = json.load(f) - if str(member.id) not in warns: + guild_id = str(inter.guild.id) + member_id = str(member.id) + if guild_id not in warns or member_id not in warns[guild_id]: embed = disnake.Embed(title=f"``{member}`` has no warns!", color=config.Success()) embed.set_footer(text=f'Checked by {inter.author}', icon_url=inter.author.avatar.url) return await inter.response.send_message(embed=embed) - if len(warns[str(member.id)]) < amount: + if len(warns[guild_id][member_id]) < amount: embed = disnake.Embed(title=f"``{member}`` does not have ``{amount}`` warns!", color=config.Success()) embed.set_footer(text=f'Checked by {inter.author}', icon_url=inter.author.avatar.url) return await inter.response.send_message(embed=embed) for i in range(amount): - warns[str(member.id)].pop() + warns[guild_id][member_id].pop() with open('data/warns.json', 'w') as f: json.dump(warns, f, indent=4) embed = disnake.Embed(title=f"Successfully cleared ``{amount}`` warns of ``{member}``!", color=config.Success()) @@ -381,16 +399,19 @@ async def clearallwarns(self, inter: disnake.ApplicationCommandInteraction, memb return await inter.response.send_message(delete_after=15, embed=embed) with open('data/warns.json', 'r') as f: warns = json.load(f) - if str(member.id) not in warns: + guild_id = str(inter.guild.id) + member_id = str(member.id) + if guild_id not in warns or member_id not in warns[guild_id]: embed = disnake.Embed(title=f"``{member}`` has no warns!", color=config.Success()) embed.set_footer(text=f'Checked by {inter.author}', icon_url=inter.author.avatar.url) return await inter.response.send_message(embed=embed) - warns.pop(str(member.id)) - with open('data/warns.json', 'w') as f: - json.dump(warns, f, indent=4) - embed = disnake.Embed(title=f"Successfully cleared all warns of ``{member}``!", color=config.Success()) - embed.set_footer(text=f'Checked by {inter.author}', icon_url=inter.author.avatar.url) - await inter.response.send_message(embed=embed) + else: + warns[guild_id].pop(member_id) + with open('data/warns.json', 'w') as f: + json.dump(warns, f, indent=4) + embed = disnake.Embed(title=f"Successfully cleared all warns of ``{member}``!", color=config.Success()) + embed.set_footer(text=f'Checked by {inter.author}', icon_url=inter.author.avatar.url) + await inter.response.send_message(embed=embed) except Exception as e: print(f'Error sending clearallwarns command: {e}') await inter.send(embed=errors.create_error_embed(f"Error sending clearallwarns command: {e}")) diff --git a/data/logging.db b/data/logging.db new file mode 100644 index 0000000..e69de29 diff --git a/example.config.py b/example.config.py index 7d3f79a..fdd76a6 100644 --- a/example.config.py +++ b/example.config.py @@ -12,7 +12,7 @@ token = 'TOKEN' # Version -version = '1.5.7' +version = '2.0.0' # Your Discord Server ID Will Go Here guild = 'GUILD ID' diff --git a/main.py b/main.py index 2d80057..b65494d 100644 --- a/main.py +++ b/main.py @@ -27,6 +27,28 @@ owner_ids=config.owner_ids ) + +# Create Missing Files +db_f = "logging.db" +file_names = ["levels.json", "tags.json", "warns.json"] +dir_path = "data" + +for file_name in file_names: + file_path = os.path.join(dir_path, file_name) + + if not os.path.exists(file_path): + os.makedirs(os.path.dirname(file_path), exist_ok=True) + with open(file_path, "w") as f: + f.write("{}") + +for db_f in db_f: + file_path = os.path.join(dir_path, db_f) + + if not os.path.exists(file_path): + os.makedirs(os.path.dirname(file_path), exist_ok=True) + with open(file_path, "w") as f: + f.write("") + @bot.command() async def update(ctx): try: @@ -67,7 +89,7 @@ async def update(ctx): # On Ready @bot.event async def on_ready(): - if config.version != "1.5.7": + if config.version != "2.0.0": print('===============================================') print('WARNING! You are not using the latest version!') print('===============================================')