Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -133,7 +146,7 @@ Format: `<type>(<scope>): <subject>`
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.
Expand Down
116 changes: 116 additions & 0 deletions capy_discord/exts/tools/privacy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""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

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."""

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=EMBED_TITLE,
color=discord.Color.blue(),
description=EMBED_DESCRIPTION,
)

embed.add_field(
name="Basic Discord Data",
value=BASIC_DISCORD_DATA,
inline=False,
)
embed.add_field(
name="Academic Profile Data",
value=ACADEMIC_PROFILE_DATA,
inline=False,
)

embed.add_field(
name="How We Store Your Data",
value=DATA_STORAGE,
inline=False,
)

embed.add_field(
name="Who Can Access Your Data",
value=DATA_ACCESS,
inline=False,
)
embed.add_field(
name="How Your Data Is Used",
value=DATA_USAGE,
inline=False,
)

embed.add_field(
name="Data Deletion",
value=DATA_DELETION,
inline=False,
)

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)


async def setup(bot: commands.Bot) -> None:
"""Set up the Privacy cog."""
await bot.add_cog(Privacy(bot))