From 5a5c135c4a5603e31fc049af3f46f2f7bf6c2b9a Mon Sep 17 00:00:00 2001 From: Ethan Beloff Date: Tue, 3 Feb 2026 16:21:55 -0500 Subject: [PATCH 1/5] Add privacy cog --- capy_discord/exts/tools/privacy.py | 100 +++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 capy_discord/exts/tools/privacy.py diff --git a/capy_discord/exts/tools/privacy.py b/capy_discord/exts/tools/privacy.py new file mode 100644 index 0000000..3fd6727 --- /dev/null +++ b/capy_discord/exts/tools/privacy.py @@ -0,0 +1,100 @@ +"""Privacy policy cog for displaying data handling information. + +This module handles the display of privacy policy information to users. +""" +import logging +import discord +from discord import app_commands +from discord.ext import commands + +from capy_discord.config import settings + + +class Privacy(commands.Cog): + """Privacy policy and data handling information cog.""" + def __init__(self, bot: commands.Bot) -> None: + """Initialize the Privacy cog. + + Args: + bot: The Discord bot instance + """ + self.bot = bot + self.log = logging.getLogger(__name__) + self.log.info("Privacy cog initialized") + + @app_commands.command( + name="privacy", + description="View our privacy policy and data handling practices", + ) + async def privacy(self, interaction: discord.Interaction) -> None: + """Display privacy policy and data handling information. + + Args: + interaction: The Discord interaction initiating the command + """ + embed = discord.Embed( + title="Privacy Policy & Data Handling", + color=discord.Color.blue(), + description="Here's how we handle your information:", + ) + + embed.add_field( + name="📝 Data We Collect", + value=( + "**Basic Discord Data:**\n" + "• Discord User ID\n" + "• Server (Guild) ID\n" + "• Channel configurations\n" + "• Role assignments\n\n" + "**Academic Profile Data:**\n" + "• Full name (first, middle, last)\n" + "• School email address\n" + "• Student ID number\n" + "• Major(s)\n" + "• Expected graduation year\n" + "• Phone number (optional)\n" + ), + inline=False, + ) + + embed.add_field( + name="🔒 Data Storage", + value=("• Data is stored in a secure MongoDB database\n• Regular backups are maintained\n"), + inline=False, + ) + + embed.add_field( + name="👥 Data Access", + value=( + "**Who can access your data:**\n" + "• Club/Organization officers for member management\n" + "• Server administrators for server settings\n" + "• Bot developers for maintenance only\n\n" + "**How your data is used:**\n" + "• Member verification and tracking\n" + "• Event participation management\n" + "• Academic program coordination\n" + "• Communication within organizations\n\n" + "Your information is never shared with third parties" + "or used for marketing purposes." + ), + inline=False, + ) + + embed.add_field( + name="❌ Data Deletion", + value=( + "You can request data deletion through:\n" + "• Contacting the bot administrators\n" + "• Calling /profile delete\n\n" + "Note: Some basic data may be retained for academic records as required." + ), + inline=False, + ) + + embed.set_footer(text="Last updated: February 2024") + await interaction.response.send_message(embed=embed, ephemeral=True) + + +async def setup(bot: commands.Bot) -> None: + await bot.add_cog(Privacy(bot)) \ No newline at end of file From ad7ed62f24a8b61e56f79638ef7cbd7255ae5057 Mon Sep 17 00:00:00 2001 From: Ethan Beloff Date: Tue, 3 Feb 2026 16:30:57 -0500 Subject: [PATCH 2/5] Add privacy command --- capy_discord/exts/tools/privacy.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/capy_discord/exts/tools/privacy.py b/capy_discord/exts/tools/privacy.py index 3fd6727..e346af7 100644 --- a/capy_discord/exts/tools/privacy.py +++ b/capy_discord/exts/tools/privacy.py @@ -2,26 +2,27 @@ This module handles the display of privacy policy information to users. """ + import logging + import discord from discord import app_commands from discord.ext import commands -from capy_discord.config import settings - class Privacy(commands.Cog): """Privacy policy and data handling information cog.""" + def __init__(self, bot: commands.Bot) -> None: """Initialize the Privacy cog. - + Args: bot: The Discord bot instance """ self.bot = bot self.log = logging.getLogger(__name__) self.log.info("Privacy cog initialized") - + @app_commands.command( name="privacy", description="View our privacy policy and data handling practices", @@ -97,4 +98,5 @@ async def privacy(self, interaction: discord.Interaction) -> None: async def setup(bot: commands.Bot) -> None: - await bot.add_cog(Privacy(bot)) \ No newline at end of file + """Set up the Privacy cog.""" + await bot.add_cog(Privacy(bot)) From 1634d7eebcb56345e19cdd01188f0d22fa22ee06 Mon Sep 17 00:00:00 2001 From: Ethan Beloff Date: Thu, 5 Feb 2026 10:55:33 -0500 Subject: [PATCH 3/5] Improve privacy formatting --- capy_discord/exts/tools/privacy.py | 40 +++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/capy_discord/exts/tools/privacy.py b/capy_discord/exts/tools/privacy.py index e346af7..0f09976 100644 --- a/capy_discord/exts/tools/privacy.py +++ b/capy_discord/exts/tools/privacy.py @@ -36,58 +36,64 @@ async def privacy(self, interaction: discord.Interaction) -> None: embed = discord.Embed( title="Privacy Policy & Data Handling", color=discord.Color.blue(), - description="Here's how we handle your information:", + description="**Here's how we collect and handle your information:**", ) embed.add_field( - name="📝 Data We Collect", + name="Basic Discord Data", + value=("• Discord User ID\n• Server (Guild) ID\n• Channel configurations\n• Role assignments\n\n"), + inline=False, + ) + embed.add_field( + name="Academic Profile Data", value=( - "**Basic Discord Data:**\n" - "• Discord User ID\n" - "• Server (Guild) ID\n" - "• Channel configurations\n" - "• Role assignments\n\n" - "**Academic Profile Data:**\n" "• Full name (first, middle, last)\n" "• School email address\n" "• Student ID number\n" "• Major(s)\n" "• Expected graduation year\n" - "• Phone number (optional)\n" + "• Phone number (optional)\n\n" ), inline=False, ) embed.add_field( - name="🔒 Data Storage", - value=("• Data is stored in a secure MongoDB database\n• Regular backups are maintained\n"), + name="How We Store Your Data", + value=("• Data is stored in a secure MongoDB database\n• Regular backups are maintained\n\n"), inline=False, ) embed.add_field( - name="👥 Data Access", + name="\n", value=( "**Who can access your data:**\n" "• Club/Organization officers for member management\n" "• Server administrators for server settings\n" - "• Bot developers for maintenance only\n\n" - "**How your data is used:**\n" + "• Bot developers for maintenance only\n" + ), + inline=False, + ) + embed.add_field( + name="How your data is used", + value=( "• Member verification and tracking\n" "• Event participation management\n" "• Academic program coordination\n" "• Communication within organizations\n\n" - "Your information is never shared with third parties" - "or used for marketing purposes." ), inline=False, ) + embed.add_field( + name="Your information is never shared or used for marketing purposes.\n", value=(""), inline=False + ) embed.add_field( - name="❌ Data Deletion", + name="Data Deletion", value=( "You can request data deletion through:\n" "• Contacting the bot administrators\n" "• Calling /profile delete\n\n" + "\n" "Note: Some basic data may be retained for academic records as required." ), inline=False, From 0556457664515bb1f1eeb8f3b403fe2d946c026c Mon Sep 17 00:00:00 2001 From: Ethan Beloff Date: Thu, 5 Feb 2026 20:50:32 -0500 Subject: [PATCH 4/5] Update privacy embed layout --- capy_discord/exts/tools/privacy.py | 81 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/capy_discord/exts/tools/privacy.py b/capy_discord/exts/tools/privacy.py index 0f09976..01538b2 100644 --- a/capy_discord/exts/tools/privacy.py +++ b/capy_discord/exts/tools/privacy.py @@ -9,6 +9,39 @@ from discord import app_commands from discord.ext import commands +EMBED_TITLE = "Privacy Policy & Data Handling" +EMBED_DESCRIPTION = "**Here's how we collect and handle your information:**" +BASIC_DISCORD_DATA = "• Discord User ID\n• Server (Guild) ID\n• Channel configurations\n• Role assignments" +ACADEMIC_PROFILE_DATA = ( + "• Full name (first, middle, last)\n" + "• School email address\n" + "• Student ID number\n" + "• Major(s)\n" + "• Expected graduation year\n" + "• Phone number (optional)" +) +DATA_STORAGE = "• Data is stored in a secure MongoDB database\n• Regular backups are maintained" +DATA_ACCESS = ( + "• Club/Organization officers for member management\n" + "• Server administrators for server settings\n" + "• Bot developers for maintenance only" +) +DATA_USAGE = ( + "• Member verification and tracking\n" + "• Event participation management\n" + "• Academic program coordination\n" + "• Communication within organizations" +) +DATA_SHARING = "**Your information is never shared with third parties or used for marketing purposes.**" +DATA_DELETION = ( + "You can request data deletion through:\n" + "• Contacting the bot administrators\n" + "• Calling /profile delete\n\n" + f"{DATA_SHARING}\n\n" + "Note: Some basic data may be retained for academic records as required." +) +FOOTER_TEXT = "Last updated: February 2026" + class Privacy(commands.Cog): """Privacy policy and data handling information cog.""" @@ -34,72 +67,46 @@ async def privacy(self, interaction: discord.Interaction) -> None: interaction: The Discord interaction initiating the command """ embed = discord.Embed( - title="Privacy Policy & Data Handling", + title=EMBED_TITLE, color=discord.Color.blue(), - description="**Here's how we collect and handle your information:**", + description=EMBED_DESCRIPTION, ) embed.add_field( name="Basic Discord Data", - value=("• Discord User ID\n• Server (Guild) ID\n• Channel configurations\n• Role assignments\n\n"), + value=BASIC_DISCORD_DATA, inline=False, ) embed.add_field( name="Academic Profile Data", - value=( - "• Full name (first, middle, last)\n" - "• School email address\n" - "• Student ID number\n" - "• Major(s)\n" - "• Expected graduation year\n" - "• Phone number (optional)\n\n" - ), + value=ACADEMIC_PROFILE_DATA, inline=False, ) embed.add_field( name="How We Store Your Data", - value=("• Data is stored in a secure MongoDB database\n• Regular backups are maintained\n\n"), + value=DATA_STORAGE, inline=False, ) embed.add_field( - name="\n", - value=( - "**Who can access your data:**\n" - "• Club/Organization officers for member management\n" - "• Server administrators for server settings\n" - "• Bot developers for maintenance only\n" - ), + name="Who Can Access Your Data", + value=DATA_ACCESS, inline=False, ) embed.add_field( - name="How your data is used", - value=( - "• Member verification and tracking\n" - "• Event participation management\n" - "• Academic program coordination\n" - "• Communication within organizations\n\n" - ), + name="How Your Data Is Used", + value=DATA_USAGE, inline=False, ) - embed.add_field( - name="Your information is never shared or used for marketing purposes.\n", value=(""), inline=False - ) embed.add_field( name="Data Deletion", - value=( - "You can request data deletion through:\n" - "• Contacting the bot administrators\n" - "• Calling /profile delete\n\n" - "\n" - "Note: Some basic data may be retained for academic records as required." - ), + value=DATA_DELETION, inline=False, ) - embed.set_footer(text="Last updated: February 2024") + embed.set_footer(text=FOOTER_TEXT) await interaction.response.send_message(embed=embed, ephemeral=True) From 9ea7b1bd5294a7038d5635a7eb44f9cc78633a95 Mon Sep 17 00:00:00 2001 From: Shamik Karkhanis Date: Thu, 5 Feb 2026 21:41:51 -0500 Subject: [PATCH 5/5] feat(privacy): updated logging format and agents --- AGENTS.md | 19 ++++++++++++++++--- capy_discord/exts/tools/privacy.py | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1e3b58c..d0b6b78 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -100,12 +100,25 @@ We use a global `on_tree_error` handler in `bot.py`. * Exceptions are logged with the specific module name. * Do not wrap every command in `try/except` blocks unless handling specific business logic errors. -## 5. Time and Timezones +## 5. Logging +All logs follow a standardized format for consistency across the console and log files. + +* **Format**: `[{asctime}] [{levelname:<8}] {name}: {message}` +* **Date Format**: `%Y-%m-%d %H:%M:%S` +* **Usage**: Always use `logging.getLogger(__name__)` to ensure logs are attributed to the correct module. + +```python +import logging +self.log = logging.getLogger(__name__) +self.log.info("Starting feature X") +``` + +## 6. Time and Timezones **Always use `zoneinfo.ZoneInfo`**. * **Storage**: `UTC`. * **Usage**: `datetime.now(ZoneInfo("UTC"))`. -## 6. Development Workflow +## 7. Development Workflow ### Linear & Branching * **Issue Tracking**: Every task must have a Linear issue. @@ -133,7 +146,7 @@ Format: `(): ` 2. **Reviewers**: Must include `Shamik` and `Jason`. 3. **Checks**: All CI checks (Lint, Test, Build) must pass. -## 7. Cog Standards +## 8. Cog Standards ### Initialization All Cogs **MUST** accept the `bot` instance in `__init__`. The use of the global `capy_discord.instance` is **deprecated** and should not be used in new code. diff --git a/capy_discord/exts/tools/privacy.py b/capy_discord/exts/tools/privacy.py index 01538b2..459bdb5 100644 --- a/capy_discord/exts/tools/privacy.py +++ b/capy_discord/exts/tools/privacy.py @@ -107,6 +107,7 @@ async def privacy(self, interaction: discord.Interaction) -> None: ) embed.set_footer(text=FOOTER_TEXT) + self.log.info("/privacy invoked user: %s guild: %s", interaction.user.id, interaction.guild_id) await interaction.response.send_message(embed=embed, ephemeral=True)