From 69a3dfe8bae032606f74221ab535f9d7a0ba6b4a Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 18 Jun 2024 23:19:31 -0400 Subject: [PATCH 01/10] Initial code --- commands.py | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 commands.py create mode 100644 main.py diff --git a/commands.py b/commands.py new file mode 100644 index 0000000..7316896 --- /dev/null +++ b/commands.py @@ -0,0 +1,105 @@ +import discord +from discord import app_commands +from discord.ext import commands +import aiosqlite +import os +import sys +import subprocess + + +class ModCommands(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="addnote", description="Add a note to a user") + async def add_note(self, interaction: discord.Interaction, + user: discord.User, note: str): + async with aiosqlite.connect("bot.db") as db: + cursor = await db.execute( + "SELECT notes FROM user_notes WHERE user_id = ?", + (user.id,) + ) + row = await cursor.fetchone() + if row: + notes = row[0] + "\n" + note + await db.execute( + "UPDATE user_notes SET notes = ? WHERE user_id = ?", + (notes, user.id) + ) + else: + await db.execute( + "INSERT INTO user_notes (user_id, notes, strikes) " + "VALUES (?, ?, ?)", + (user.id, note, 0) + ) + await db.commit() + await interaction.response.send_message( + f"Note added for {user.name}: {note}" + ) + + @app_commands.command(name="warn", description="Warn a user") + async def warn_user(self, interaction: discord.Interaction, + user: discord.User, reason: str): + async with aiosqlite.connect("bot.db") as db: + cursor = await db.execute( + "SELECT strikes FROM user_notes WHERE user_id = ?", + (user.id,) + ) + row = await cursor.fetchone() + if row: + strikes = row[0] + 1 + await db.execute( + "UPDATE user_notes SET strikes = ? WHERE user_id = ?", + (strikes, user.id) + ) + else: + strikes = 1 + await db.execute( + "INSERT INTO user_notes (user_id, notes, strikes) " + "VALUES (?, ?, ?)", + (user.id, "", strikes) + ) + await db.commit() + await interaction.response.send_message( + f"User {user.name} warned for: {reason}." + f"They now have {strikes} strikes." + ) + await user.send( + f"You have been warned for: {reason}." + f"You now have {strikes} strikes." + ) + + @app_commands.command(name="checknotes", + description="Check notes and strikes of a user") + async def check_notes(self, interaction: discord.Interaction, + user: discord.User): + async with aiosqlite.connect("bot.db") as db: + cursor = await db.execute( + "SELECT notes, strikes FROM user_notes WHERE user_id = ?", + (user.id,) + ) + row = await cursor.fetchone() + if row: + notes, strikes = row + await interaction.response.send_message( + f"Notes for {user.name}: {notes}\nStrikes: {strikes}" + ) + else: + await interaction.response.send_message( + f"No notes found for {user.name}." + ) + + @app_commands.command(name="update", + description="Update the bot from the repository") + @commands.is_owner() + async def update(self, interaction: discord.Interaction): + await interaction.response.send_message("Updating the bot...") + # Pull latest changes from the repository + subprocess.run(["git", "pull"]) + # Restart the bot + await interaction.followup.send("Restarting the bot...") + os.execv(sys.executable, ['python'] + sys.argv) + + +async def setup(bot): + await bot.add_cog(ModCommands(bot)) diff --git a/main.py b/main.py new file mode 100644 index 0000000..790612f --- /dev/null +++ b/main.py @@ -0,0 +1,64 @@ +import discord +from discord.ext import commands +import aiosqlite +import asyncio +import os +import sys +import subprocess +from dotenv import load_dotenv + +load_dotenv() + +TOKEN = os.getenv('DISCORD_TOKEN') + +intents = discord.Intents.default() +intents.message_content = True +intents.members = True + + +class Ariella(discord.Client): + def __init__(self): + super().__init__(intents=intents) + self.tree = discord.app_commands.CommandTree(self) + + async def setup_hook(self): + await self.tree.sync() + # Load commands + await self.load_extension('commands') + + +bot = Ariella() + + +# Database setup +async def init_db(): + async with aiosqlite.connect("bot.db") as db: + await db.execute(""" + CREATE TABLE IF NOT EXISTS user_notes ( + user_id INTEGER PRIMARY KEY, + notes TEXT, + strikes INTEGER + ) + """) + await db.commit() + + +@bot.event +async def on_ready(): + print(f'Logged in as {bot.user} (ID: {bot.user.id})') + print('------') + await init_db() + + +@bot.event +async def on_guild_join(guild): + await bot.tree.sync(guild=guild) + + +# Load commands +async def load_commands(): + await bot.load_extension('commands') + +asyncio.run(load_commands()) + +bot.run('TOKEN') From 00f2748235fbc3d6d9c6b3d465841a0c3e6bb4ab Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 18 Jun 2024 23:44:13 -0400 Subject: [PATCH 02/10] FIX: Bot starts now. DOC: Need to test commands and such, find problems. --- bot.db | Bin 0 -> 8192 bytes main.py | 19 +++++-------------- 2 files changed, 5 insertions(+), 14 deletions(-) create mode 100644 bot.db diff --git a/bot.db b/bot.db new file mode 100644 index 0000000000000000000000000000000000000000..b5477bb8a6ef6f07caee34a2e667490805c17754 GIT binary patch literal 8192 zcmeI#PYZ%D7zXf7f*^>xb$z`RR1kfEEJjooWrOk*Mhm2Wpz8zFCvOTO<)L%&JUHgN zcRKjpwqCiRR&=~Ymq1gW)fwk(K*Sj1W>{uqN$OHom^rDxg|qHvS~a_P&1L36AOHaf zKmY;|fB*y_009U<00KWR@aXY!!?yXeql5if-D4H)t~agX{LLD9!jXccGjs*z>(s2! zN780^pvgjtvG8c=P3Dg0(@gm7!shfCl42{1y;w)#NyXWZ-o9-mcXM;;M<4(J2tWV= T5P$##AOHafKmY;|_+x<=Au}}* literal 0 HcmV?d00001 diff --git a/main.py b/main.py index 790612f..90e1ce8 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,10 @@ import discord from discord.ext import commands import aiosqlite -import asyncio import os import sys import subprocess +import asyncio from dotenv import load_dotenv load_dotenv() @@ -16,15 +16,13 @@ intents.message_content = True intents.members = True -class Ariella(discord.Client): +class Ariella(commands.Bot): def __init__(self): - super().__init__(intents=intents) - self.tree = discord.app_commands.CommandTree(self) + super().__init__(command_prefix='!', intents=intents) async def setup_hook(self): - await self.tree.sync() - # Load commands await self.load_extension('commands') + await self.tree.sync() bot = Ariella() @@ -54,11 +52,4 @@ async def on_ready(): async def on_guild_join(guild): await bot.tree.sync(guild=guild) - -# Load commands -async def load_commands(): - await bot.load_extension('commands') - -asyncio.run(load_commands()) - -bot.run('TOKEN') +bot.run(TOKEN) From 7430769b06a563d58053b0527ecb40d1e4baab7d Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 18 Jun 2024 23:46:43 -0400 Subject: [PATCH 03/10] REF: Added a .json file for debugging easier --- .vscode/launch.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e147d1e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Ariellia: Main", + "type": "debugpy", + "request": "launch", + "program": "E:\\Development\\AI Development\\Ariellia\\main.py", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file From 150c4a89f0eb4b14d2b095acb6e5e7e6a311fd5d Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Jun 2024 07:59:32 -0400 Subject: [PATCH 04/10] FEAT: Added Help command (can't test yet due to Discord's Sync time) REF: Changed the database name to reflect the bot's name (ariella instead of bot) --- bot.db | Bin 8192 -> 0 bytes commands.py | 16 ++++++++++++---- main.py | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) delete mode 100644 bot.db diff --git a/bot.db b/bot.db deleted file mode 100644 index b5477bb8a6ef6f07caee34a2e667490805c17754..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeI#PYZ%D7zXf7f*^>xb$z`RR1kfEEJjooWrOk*Mhm2Wpz8zFCvOTO<)L%&JUHgN zcRKjpwqCiRR&=~Ymq1gW)fwk(K*Sj1W>{uqN$OHom^rDxg|qHvS~a_P&1L36AOHaf zKmY;|fB*y_009U<00KWR@aXY!!?yXeql5if-D4H)t~agX{LLD9!jXccGjs*z>(s2! zN780^pvgjtvG8c=P3Dg0(@gm7!shfCl42{1y;w)#NyXWZ-o9-mcXM;;M<4(J2tWV= T5P$##AOHafKmY;|_+x<=Au}}* diff --git a/commands.py b/commands.py index 7316896..de12b97 100644 --- a/commands.py +++ b/commands.py @@ -14,7 +14,7 @@ class ModCommands(commands.Cog): @app_commands.command(name="addnote", description="Add a note to a user") async def add_note(self, interaction: discord.Interaction, user: discord.User, note: str): - async with aiosqlite.connect("bot.db") as db: + async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( "SELECT notes FROM user_notes WHERE user_id = ?", (user.id,) @@ -40,7 +40,7 @@ class ModCommands(commands.Cog): @app_commands.command(name="warn", description="Warn a user") async def warn_user(self, interaction: discord.Interaction, user: discord.User, reason: str): - async with aiosqlite.connect("bot.db") as db: + async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( "SELECT strikes FROM user_notes WHERE user_id = ?", (user.id,) @@ -73,7 +73,7 @@ class ModCommands(commands.Cog): description="Check notes and strikes of a user") async def check_notes(self, interaction: discord.Interaction, user: discord.User): - async with aiosqlite.connect("bot.db") as db: + async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( "SELECT notes, strikes FROM user_notes WHERE user_id = ?", (user.id,) @@ -90,7 +90,7 @@ class ModCommands(commands.Cog): ) @app_commands.command(name="update", - description="Update the bot from the repository") + description="Update Ariellia to the latest version") @commands.is_owner() async def update(self, interaction: discord.Interaction): await interaction.response.send_message("Updating the bot...") @@ -100,6 +100,14 @@ class ModCommands(commands.Cog): await interaction.followup.send("Restarting the bot...") os.execv(sys.executable, ['python'] + sys.argv) + @app_commands.command(name="help", description="List all commands") + async def help_command(self, interaction: discord.Interaction): + commands = self.bot.tree.walk_commands() + help_text = "Here are the available commands:\n" + for command in commands: + help_text += f"/{command.name} - {command.description}\n" + await interaction.response.send_message(help_text) + async def setup(bot): await bot.add_cog(ModCommands(bot)) diff --git a/main.py b/main.py index 90e1ce8..e917b0a 100644 --- a/main.py +++ b/main.py @@ -30,7 +30,7 @@ bot = Ariella() # Database setup async def init_db(): - async with aiosqlite.connect("bot.db") as db: + async with aiosqlite.connect("ariella.db") as db: await db.execute(""" CREATE TABLE IF NOT EXISTS user_notes ( user_id INTEGER PRIMARY KEY, From bc4b0a916e7677988fb11b63377b0bfd7f0d6569 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Jun 2024 08:02:31 -0400 Subject: [PATCH 05/10] FIX: Found a small misspelling in the pathname --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e147d1e..3560b35 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,10 +5,10 @@ "version": "0.2.0", "configurations": [ { - "name": "Ariellia: Main", + "name": "Ariella: Main", "type": "debugpy", "request": "launch", - "program": "E:\\Development\\AI Development\\Ariellia\\main.py", + "program": "E:\\Development\\AI Development\\Ariella\\main.py", "console": "integratedTerminal" } ] From 19b48dd67d10f78915bdde8d710d66dfc32dfc18 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Jun 2024 08:18:39 -0400 Subject: [PATCH 06/10] FIX: Added a force sync to at least my testing server for now FEAT: Added better control over the notes and strikes. --- commands.py | 46 ++++++++++++++++++++++++++++++++++++++-------- main.py | 4 +++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/commands.py b/commands.py index de12b97..d10783f 100644 --- a/commands.py +++ b/commands.py @@ -11,30 +11,33 @@ class ModCommands(commands.Cog): def __init__(self, bot): self.bot = bot - @app_commands.command(name="addnote", description="Add a note to a user") + @app_commands.command(name="addnote", + description="Add a note to a user and/or add strikes") # noqa: E501 async def add_note(self, interaction: discord.Interaction, - user: discord.User, note: str): + user: discord.User, note: str, strikes: int = 0): async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( - "SELECT notes FROM user_notes WHERE user_id = ?", + "SELECT notes, strikes FROM user_notes WHERE user_id = ?", (user.id,) ) row = await cursor.fetchone() if row: notes = row[0] + "\n" + note + current_strikes = row[1] + strikes await db.execute( - "UPDATE user_notes SET notes = ? WHERE user_id = ?", - (notes, user.id) + "UPDATE user_notes SET notes = ?," + "strikes = ? WHERE user_id = ?", + (notes, current_strikes, user.id) ) else: await db.execute( "INSERT INTO user_notes (user_id, notes, strikes) " "VALUES (?, ?, ?)", - (user.id, note, 0) + (user.id, note, strikes) ) await db.commit() await interaction.response.send_message( - f"Note added for {user.name}: {note}" + f"Note added for {user.name}: {note}. Strikes: {strikes}" ) @app_commands.command(name="warn", description="Warn a user") @@ -69,6 +72,32 @@ class ModCommands(commands.Cog): f"You now have {strikes} strikes." ) + @app_commands.command(name="removestrikes", + description="Remove strikes from a user") + async def remove_strikes(self, interaction: discord.Interaction, + user: discord.User, strikes: int): + async with aiosqlite.connect("ariella.db") as db: + cursor = await db.execute( + "SELECT strikes FROM user_notes WHERE user_id = ?", + (user.id,) + ) + row = await cursor.fetchone() + if row: + current_strikes = max(row[0] - strikes, 0) + await db.execute( + "UPDATE user_notes SET strikes = ? WHERE user_id = ?", + (current_strikes, user.id) + ) + await db.commit() + await interaction.response.send_message( + f"Removed {strikes} strikes from {user.name}. " + f"They now have {current_strikes} strikes." + ) + else: + await interaction.response.send_message( + f"No strikes found for {user.name}." + ) + @app_commands.command(name="checknotes", description="Check notes and strikes of a user") async def check_notes(self, interaction: discord.Interaction, @@ -95,7 +124,8 @@ class ModCommands(commands.Cog): async def update(self, interaction: discord.Interaction): await interaction.response.send_message("Updating the bot...") # Pull latest changes from the repository - subprocess.run(["git", "pull"]) + repo_dir = os.path.dirname(os.path.abspath(__file__)) + subprocess.run(["git", "-C", repo_dir, "pull"]) # Restart the bot await interaction.followup.send("Restarting the bot...") os.execv(sys.executable, ['python'] + sys.argv) diff --git a/main.py b/main.py index e917b0a..c066b1d 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,7 @@ from dotenv import load_dotenv load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') +GUILD_ID = int(os.getenv('GUILD_ID')) intents = discord.Intents.default() intents.message_content = True @@ -22,7 +23,8 @@ class Ariella(commands.Bot): async def setup_hook(self): await self.load_extension('commands') - await self.tree.sync() + self.tree.copy_global_to(guild=discord.Object(id=GUILD_ID)) + await self.tree.sync(guild=discord.Object(id=GUILD_ID)) bot = Ariella() From 27b051f9eb4297e33462fc8761b2b99cff667ec8 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Jun 2024 09:29:16 -0400 Subject: [PATCH 07/10] DOC: Added *.db to the .gitignore to prevent any data from being shared. FEAT: Updated code to start following GDPR compliance. --- .gitignore | 1 + commands.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++------- gdpr.py | 33 ++++++++++++++++++++++++++++++ main.py | 8 +++++++- 4 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 gdpr.py diff --git a/.gitignore b/.gitignore index 5d381cc..73a0dd9 100644 --- a/.gitignore +++ b/.gitignore @@ -160,3 +160,4 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +*.db \ No newline at end of file diff --git a/commands.py b/commands.py index d10783f..5fede55 100644 --- a/commands.py +++ b/commands.py @@ -5,16 +5,40 @@ import aiosqlite import os import sys import subprocess +from gdpr import check_consent, give_consent, revoke_consent class ModCommands(commands.Cog): def __init__(self, bot): self.bot = bot + @app_commands.command(name="consent", + description="Give consent to store data") + async def consent(self, interaction: discord.Interaction): + await give_consent(interaction.user.id) + await interaction.response.send_message( + "Consent given to store your data." + ) + + @app_commands.command(name="revoke_consent", + description="Revoke consent to store data") + async def revoke_consent(self, interaction: discord.Interaction): + await revoke_consent(interaction.user.id) + await interaction.response.send_message( + "Consent revoked and your data has been deleted." + ) + @app_commands.command(name="addnote", - description="Add a note to a user and/or add strikes") # noqa: E501 + description="Add a note to a user and optionally " + "add strikes") async def add_note(self, interaction: discord.Interaction, user: discord.User, note: str, strikes: int = 0): + if not await check_consent(user.id): + await interaction.response.send_message( + f"{user.name} has not given consent to store data." + ) + return + async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( "SELECT notes, strikes FROM user_notes WHERE user_id = ?", @@ -40,9 +64,16 @@ class ModCommands(commands.Cog): f"Note added for {user.name}: {note}. Strikes: {strikes}" ) - @app_commands.command(name="warn", description="Warn a user") + @app_commands.command(name="warn", + description="Warn a user") async def warn_user(self, interaction: discord.Interaction, user: discord.User, reason: str): + if not await check_consent(user.id): + await interaction.response.send_message( + f"{user.name} has not given consent to store data." + ) + return + async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( "SELECT strikes FROM user_notes WHERE user_id = ?", @@ -64,18 +95,24 @@ class ModCommands(commands.Cog): ) await db.commit() await interaction.response.send_message( - f"User {user.name} warned for: {reason}." - f"They now have {strikes} strikes." + f"User {user.name} warned for: {reason}. They now have {strikes} " + f"strikes." ) await user.send( - f"You have been warned for: {reason}." - f"You now have {strikes} strikes." + f"You have been warned for: {reason}. You now have {strikes} " + f"strikes." ) @app_commands.command(name="removestrikes", description="Remove strikes from a user") async def remove_strikes(self, interaction: discord.Interaction, user: discord.User, strikes: int): + if not await check_consent(user.id): + await interaction.response.send_message( + f"{user.name} has not given consent to store data." + ) + return + async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( "SELECT strikes FROM user_notes WHERE user_id = ?", @@ -102,6 +139,12 @@ class ModCommands(commands.Cog): description="Check notes and strikes of a user") async def check_notes(self, interaction: discord.Interaction, user: discord.User): + if not await check_consent(user.id): + await interaction.response.send_message( + f"{user.name} has not given consent to store data." + ) + return + async with aiosqlite.connect("ariella.db") as db: cursor = await db.execute( "SELECT notes, strikes FROM user_notes WHERE user_id = ?", @@ -130,7 +173,8 @@ class ModCommands(commands.Cog): await interaction.followup.send("Restarting the bot...") os.execv(sys.executable, ['python'] + sys.argv) - @app_commands.command(name="help", description="List all commands") + @app_commands.command(name="help", + description="List all commands") async def help_command(self, interaction: discord.Interaction): commands = self.bot.tree.walk_commands() help_text = "Here are the available commands:\n" diff --git a/gdpr.py b/gdpr.py new file mode 100644 index 0000000..5cb3005 --- /dev/null +++ b/gdpr.py @@ -0,0 +1,33 @@ +import aiosqlite + + +async def check_consent(user_id: int) -> bool: + async with aiosqlite.connect("ariella.db") as db: + cursor = await db.execute( + "SELECT consent FROM user_consent WHERE user_id = ?", + (user_id,) + ) + row = await cursor.fetchone() + return row and row[0] == 1 + + +async def give_consent(user_id: int): + async with aiosqlite.connect("ariella.db") as db: + await db.execute( + "INSERT OR REPLACE INTO user_consent (user_id, consent) VALUES (?, ?)", # noqa: E501 + (user_id, 1) + ) + await db.commit() + + +async def revoke_consent(user_id: int): + async with aiosqlite.connect("ariella.db") as db: + await db.execute( + "DELETE FROM user_consent WHERE user_id = ?", + (user_id,) + ) + await db.execute( + "DELETE FROM user_notes WHERE user_id = ?", + (user_id,) + ) + await db.commit() diff --git a/main.py b/main.py index c066b1d..6605b10 100644 --- a/main.py +++ b/main.py @@ -9,8 +9,8 @@ from dotenv import load_dotenv load_dotenv() -TOKEN = os.getenv('DISCORD_TOKEN') GUILD_ID = int(os.getenv('GUILD_ID')) +TOKEN = os.getenv('DISCORD_TOKEN') intents = discord.Intents.default() intents.message_content = True @@ -40,6 +40,12 @@ async def init_db(): strikes INTEGER ) """) + await db.execute(""" + CREATE TABLE IF NOT EXISTS user_consent ( + user_id INTEGER PRIMARY KEY, + consent INTEGER + ) + """) await db.commit() From 8616a014693e48bccbc80424ec2ade9c4b4e1573 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Jun 2024 09:44:04 -0400 Subject: [PATCH 08/10] DOC: Added sample.env to allow other people to use the bot FEAT: Added seperate set of commands focused strictly on GDPR REF: Updated the other files to reflect the new changes. --- commands.py | 22 +++------------- gdpr_commands.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 5 ++-- sample.env | 2 ++ 4 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 gdpr_commands.py create mode 100644 sample.env diff --git a/commands.py b/commands.py index 5fede55..cc8c5c9 100644 --- a/commands.py +++ b/commands.py @@ -5,29 +5,13 @@ import aiosqlite import os import sys import subprocess -from gdpr import check_consent, give_consent, revoke_consent +from gdpr import check_consent class ModCommands(commands.Cog): def __init__(self, bot): self.bot = bot - @app_commands.command(name="consent", - description="Give consent to store data") - async def consent(self, interaction: discord.Interaction): - await give_consent(interaction.user.id) - await interaction.response.send_message( - "Consent given to store your data." - ) - - @app_commands.command(name="revoke_consent", - description="Revoke consent to store data") - async def revoke_consent(self, interaction: discord.Interaction): - await revoke_consent(interaction.user.id) - await interaction.response.send_message( - "Consent revoked and your data has been deleted." - ) - @app_commands.command(name="addnote", description="Add a note to a user and optionally " "add strikes") @@ -49,8 +33,8 @@ class ModCommands(commands.Cog): notes = row[0] + "\n" + note current_strikes = row[1] + strikes await db.execute( - "UPDATE user_notes SET notes = ?," - "strikes = ? WHERE user_id = ?", + "UPDATE user_notes SET notes = ?, strikes = ? " + "WHERE user_id = ?", (notes, current_strikes, user.id) ) else: diff --git a/gdpr_commands.py b/gdpr_commands.py new file mode 100644 index 0000000..78c60b4 --- /dev/null +++ b/gdpr_commands.py @@ -0,0 +1,67 @@ +import discord +from discord import app_commands +from discord.ext import commands +import aiosqlite +from gdpr import check_consent, give_consent, revoke_consent + + +class GDPRCommands(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="consent", + description="Give consent to store data") + async def consent(self, interaction: discord.Interaction): + await give_consent(interaction.user.id) + await interaction.response.send_message( + "Consent given to store your data." + ) + + @app_commands.command(name="revoke_consent", + description="Revoke consent to store data") + async def revoke_consent(self, interaction: discord.Interaction): + await revoke_consent(interaction.user.id) + await interaction.response.send_message( + "Consent revoked and your data has been deleted." + ) + + @app_commands.command(name="privacy_policy", + description="View the privacy policy") + async def privacy_policy(self, interaction: discord.Interaction): + privacy_text = ( + "Privacy Policy:\n" + "We collect and store data to provide better services. The data " + "includes:\n" + "- User ID\n" + "- Notes and Strikes added by moderators\n" + "Data is stored securely and only accessible by authorized " + "personnel.\n" + "You can revoke consent at any time by using the /revoke_consent " + "command.\n" + "For more details, visit our [website](https://example.com/privacy)." # noqa: E501 + ) + await interaction.response.send_message(privacy_text) + + @app_commands.command(name="get_my_data", + description="Get a copy of your data") + async def get_my_data(self, interaction: discord.Interaction): + user_id = interaction.user.id + async with aiosqlite.connect("ariella.db") as db: + cursor = await db.execute( + "SELECT notes, strikes FROM user_notes WHERE user_id = ?", + (user_id,) + ) + row = await cursor.fetchone() + if row: + notes, strikes = row + data_text = f"Your data:\nNotes: {notes}\nStrikes: {strikes}" + await interaction.user.send(data_text) + await interaction.response.send_message( + "Your data has been sent to you privately." + ) + else: + await interaction.response.send_message("No data found for you.") # noqa: E501 + + +async def setup(bot): + await bot.add_cog(GDPRCommands(bot)) diff --git a/main.py b/main.py index 6605b10..f90f1a1 100644 --- a/main.py +++ b/main.py @@ -9,8 +9,8 @@ from dotenv import load_dotenv load_dotenv() -GUILD_ID = int(os.getenv('GUILD_ID')) TOKEN = os.getenv('DISCORD_TOKEN') +GUILD_ID = int(os.getenv('GUILD_ID')) intents = discord.Intents.default() intents.message_content = True @@ -19,10 +19,11 @@ intents.members = True class Ariella(commands.Bot): def __init__(self): - super().__init__(command_prefix='!', intents=intents) + super().__init__(intents=intents) async def setup_hook(self): await self.load_extension('commands') + await self.load_extension('gdpr_commands') self.tree.copy_global_to(guild=discord.Object(id=GUILD_ID)) await self.tree.sync(guild=discord.Object(id=GUILD_ID)) diff --git a/sample.env b/sample.env new file mode 100644 index 0000000..325ed88 --- /dev/null +++ b/sample.env @@ -0,0 +1,2 @@ +DISCORD_TOKEN = "ADD YOUR TOKEN HERE" +GUILD_ID = "ADD YOUR GUILD ID HERE" \ No newline at end of file From d82d1e0729c9baab5700b1df446ca6e15bb15cf3 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Jun 2024 09:45:57 -0400 Subject: [PATCH 09/10] FIX: Broken Main.py by mistake. --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index f90f1a1..436dfa9 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ intents.members = True class Ariella(commands.Bot): def __init__(self): - super().__init__(intents=intents) + super().__init__(command_prefix='!', intents=intents) async def setup_hook(self): await self.load_extension('commands') From 6009c82ce9b605bdfb115c2b11cfd6d833e5c214 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Jun 2024 13:51:34 -0400 Subject: [PATCH 10/10] REF: Final update to make sure the code is GDPR compliant --- gdpr_commands.py | 17 ++++++++++++----- main.py | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/gdpr_commands.py b/gdpr_commands.py index 78c60b4..6c7ea1f 100644 --- a/gdpr_commands.py +++ b/gdpr_commands.py @@ -2,6 +2,7 @@ import discord from discord import app_commands from discord.ext import commands import aiosqlite +import json from gdpr import check_consent, give_consent, revoke_consent @@ -37,8 +38,7 @@ class GDPRCommands(commands.Cog): "Data is stored securely and only accessible by authorized " "personnel.\n" "You can revoke consent at any time by using the /revoke_consent " - "command.\n" - "For more details, visit our [website](https://example.com/privacy)." # noqa: E501 + "command." ) await interaction.response.send_message(privacy_text) @@ -54,13 +54,20 @@ class GDPRCommands(commands.Cog): row = await cursor.fetchone() if row: notes, strikes = row - data_text = f"Your data:\nNotes: {notes}\nStrikes: {strikes}" - await interaction.user.send(data_text) + data = { + "user_id": user_id, + "notes": notes, + "strikes": strikes + } + data_text = json.dumps(data, indent=4) + await interaction.user.send + (f"Your data:\n```json\n{data_text}\n```") await interaction.response.send_message( "Your data has been sent to you privately." ) else: - await interaction.response.send_message("No data found for you.") # noqa: E501 + await interaction.response.send_message + ("No data found for you.") async def setup(bot): diff --git a/main.py b/main.py index 436dfa9..68d8f0a 100644 --- a/main.py +++ b/main.py @@ -9,8 +9,8 @@ from dotenv import load_dotenv load_dotenv() +GUILD_ID = os.getenv('GUILD_ID') TOKEN = os.getenv('DISCORD_TOKEN') -GUILD_ID = int(os.getenv('GUILD_ID')) intents = discord.Intents.default() intents.message_content = True @@ -25,7 +25,7 @@ class Ariella(commands.Bot): await self.load_extension('commands') await self.load_extension('gdpr_commands') self.tree.copy_global_to(guild=discord.Object(id=GUILD_ID)) - await self.tree.sync(guild=discord.Object(id=GUILD_ID)) + await self.tree.sync() bot = Ariella()