Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
169 commits
Select commit Hold shift + click to select a range
e459d8a
feat(event): Initial event cog commit
simtiaz5 Jan 30, 2026
df82f0e
feat: port feedback_cog from deprecated repo
GreenJonathan Feb 3, 2026
9bffd10
fix lint issues
GreenJonathan Feb 3, 2026
10c0b86
docs: update agents
shamikkarkhanis Feb 3, 2026
a77bae9
Merge pull request #57 from CAPY-RPI/feature/capr-29-update-agentsmd
shamikkarkhanis Feb 3, 2026
2a9d3cd
Merge branch 'main' of https://github.com/CApy-RPI/discord-bot into f…
simtiaz5 Feb 3, 2026
a906b04
feature(event): create __init__.py and _schemas.py files for event cog
simtiaz5 Feb 3, 2026
a559294
feature(event): created event class
simtiaz5 Feb 3, 2026
543239a
implement event schemas
simtiaz5 Feb 3, 2026
d1aa416
feature(event): add additonal actions and action handling.
simtiaz5 Feb 3, 2026
03f6eef
feature(event): created placeholder action handling functions
simtiaz5 Feb 3, 2026
e1c4e9d
refactor(event): renamed action functions
simtiaz5 Feb 3, 2026
5a5c135
Add privacy cog
Feb 3, 2026
ad7ed62
Add privacy command
Feb 3, 2026
4879b57
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 3, 2026
9935702
Merge branch 'main' into beloff_branch
belofe Feb 3, 2026
b5a9db5
feature(event): implemented logic for handling event creation
simtiaz5 Feb 3, 2026
137ac13
fix: ui, embeds, error handling
GreenJonathan Feb 3, 2026
7dff595
fix: color from embeds to tickets init
GreenJonathan Feb 3, 2026
2c43a58
fix(event): Seperated event and date inputs. Implemented timestamps. …
simtiaz5 Feb 3, 2026
6c47b85
eliminated feedback button to immediate modal popup
GreenJonathan Feb 3, 2026
1634d7e
Improve privacy formatting
Feb 5, 2026
3edc5c0
fix: TicketSchema defined, emojis and labels moved, used asyncio, min…
GreenJonathan Feb 5, 2026
3c8287e
feat(feedback): new embeds, perf changes
shamikkarkhanis Feb 5, 2026
241e7e4
feat(error): global error handling applied everywhere
shamikkarkhanis Feb 5, 2026
0649127
Merge branch 'main' into feature/capr-30-create-global-error-handler
shamikkarkhanis Feb 5, 2026
ac62d64
fix(ty): type errors with bot.py
shamikkarkhanis Feb 5, 2026
6de5bad
fix(test): error log exception messages fix
shamikkarkhanis Feb 5, 2026
0556457
Update privacy embed layout
Feb 6, 2026
6bf241b
fix(views): type fixes
shamikkarkhanis Feb 6, 2026
c2962ed
Merge branch 'develop' into feature/capr-26-port-privacy-policy-command
shamikkarkhanis Feb 6, 2026
895c336
Merge pull request #67 from CAPY-RPI/feature/capr-30-create-global-er…
shamikkarkhanis Feb 6, 2026
1f8b9e5
Merge branch 'develop' into feature/capr-26-port-privacy-policy-command
shamikkarkhanis Feb 6, 2026
4c1a762
Updated purge to include single line command for amount and duration
Feb 6, 2026
9ea7b1b
feat(privacy): updated logging format and agents
shamikkarkhanis Feb 6, 2026
7093854
Merge pull request #60 from CAPY-RPI/feature/capr-26-port-privacy-pol…
shamikkarkhanis Feb 6, 2026
9dd71c4
build(deps-dev): bump uv from 0.9.30 to 0.10.0
dependabot[bot] Feb 6, 2026
7f00bb5
Merge branch 'develop' into feature/purge-branch-update
shamikkarkhanis Feb 6, 2026
18def31
Merge pull request #69 from CAPY-RPI/dependabot/uv/uv-0.10.0
shamikkarkhanis Feb 6, 2026
74671bd
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 6, 2026
85e7528
feature(event): add pattern check to event schemas
simtiaz5 Feb 6, 2026
6103697
Merge branch 'develop' into feature/capr-22-port-feedback_cog-from-de…
GreenJonathan Feb 6, 2026
09e2d38
feature(event): Implemented edit event logic
simtiaz5 Feb 6, 2026
a636702
fix(embeds): fixed merge issues added loading_embed
GreenJonathan Feb 6, 2026
b7bf291
feature(event): Implemented show event action
simtiaz5 Feb 6, 2026
b76722b
Merge pull request #56 from CAPY-RPI/feature/capr-22-port-feedback_co…
shamikkarkhanis Feb 6, 2026
a53b431
feature(event): Implemented delete event logic
simtiaz5 Feb 6, 2026
6a07b8f
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 6, 2026
0bbf85b
feature(event): handle event listing
simtiaz5 Feb 6, 2026
0488eb0
event(feature): Implemetned event announcing.
simtiaz5 Feb 6, 2026
d2d2b6e
feature(event): implemented myevents handling
simtiaz5 Feb 6, 2026
dcae07e
build(deps-dev): bump uv from 0.9.30 to 0.10.0
dependabot[bot] Feb 9, 2026
bfc84cc
build(deps-dev): bump coverage from 7.13.3 to 7.13.4
dependabot[bot] Feb 9, 2026
9860422
refactor(event): Import annoucement channel name from config file. Co…
simtiaz5 Feb 9, 2026
b6b66fb
feat: phase 1 telemetry -- event capture and logging
GreenJonathan Feb 9, 2026
ecb0842
fix: minor fix in telemetry.py
GreenJonathan Feb 9, 2026
9a4fbcc
Apply suggestion from @sourcery-ai
GreenJonathan Feb 10, 2026
e6226fa
fix: fixed lint errors
GreenJonathan Feb 10, 2026
e2dfc30
feat(purge): simplified purge cog and improved overall standard
shamikkarkhanis Feb 10, 2026
2e130fe
Refactor(event): Moved redundant code into helper methods. Simplified…
simtiaz5 Feb 10, 2026
9c50347
fix(purge): regex update and unused attribute fix
shamikkarkhanis Feb 10, 2026
28034a2
Merge pull request #72 from CAPY-RPI/dependabot/uv/coverage-7.13.4
shamikkarkhanis Feb 10, 2026
1e59269
Merge pull request #71 from CAPY-RPI/dependabot/uv/uv-0.10.0
shamikkarkhanis Feb 10, 2026
e144173
Merge pull request #74 from CAPY-RPI/feature/capr-21-purge-branch-update
shamikkarkhanis Feb 10, 2026
f4b2120
Merge branch 'develop' into feature/capr-23-scaffold-event_cog-from-d…
shamikkarkhanis Feb 10, 2026
d740fe5
Merge pull request #70 from CAPY-RPI/feature/capr-23-scaffold-event_c…
shamikkarkhanis Feb 10, 2026
47da7de
Revert "Feature/capr 23 scaffold event cog from deprecated repo"
shamikkarkhanis Feb 10, 2026
69f73b7
Merge pull request #77 from CAPY-RPI/revert-70-feature/capr-23-scaffo…
shamikkarkhanis Feb 10, 2026
8d11645
Revert "Revert "Feature/capr 23 scaffold event cog from deprecated re…
simtiaz5 Feb 10, 2026
4f39783
Scaffold guild settings
Feb 10, 2026
e048f59
refactor(event): Standarization of embeds instead of two seperate em…
simtiaz5 Feb 10, 2026
defed8c
Update guild settings scaffolding
Feb 10, 2026
c7f0e8f
fix(event): Fix annoucement embed and handled 00:00 time input as 12 AM.
simtiaz5 Feb 10, 2026
739e2ca
fix(event): changed annoucement embed emotes.
simtiaz5 Feb 10, 2026
76ab6f3
Update guild settings subcommands
Feb 10, 2026
81a7625
feat: phase 1 complete
GreenJonathan Feb 10, 2026
3b77128
Merge branch 'develop' into feature/capr-33-implement-interaction-log…
GreenJonathan Feb 10, 2026
ed2c42a
fix: phase 1 fixed with tests
GreenJonathan Feb 11, 2026
229890f
Merge pull request #73 from CAPY-RPI/feature/capr-33-implement-intera…
shamikkarkhanis Feb 11, 2026
69f78a3
fix(guild): small file renames and fixes
shamikkarkhanis Feb 11, 2026
4a4f90e
Merge branch 'develop' into feature/capr-43-port-guild-settings
shamikkarkhanis Feb 11, 2026
4f37619
Merge pull request #78 from CAPY-RPI/feature/capr-43-port-guild-settings
shamikkarkhanis Feb 11, 2026
2f92607
build(deps-dev): bump uv from 0.10.1 to 0.10.2
dependabot[bot] Feb 11, 2026
53b1ed7
fix: implemented a stats script and updated telemetry tests
GreenJonathan Feb 11, 2026
9ebc7b4
Merge branch 'develop' into dependabot/uv/uv-0.10.2
shamikkarkhanis Feb 11, 2026
989a980
Merge pull request #81 from CAPY-RPI/dependabot/uv/uv-0.10.2
shamikkarkhanis Feb 11, 2026
3fecf53
fix: get_metrics returns deep copy and added tests
GreenJonathan Feb 11, 2026
fa7df28
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 11, 2026
6cabce9
build(deps): bump actions/checkout from 4 to 6
dependabot[bot] Feb 12, 2026
80692d2
build(deps): bump docker/build-push-action from 5 to 6
dependabot[bot] Feb 12, 2026
a74471c
build(deps-dev): bump uv from 0.10.1 to 0.10.2
dependabot[bot] Feb 12, 2026
6b0b3ba
build(deps-dev): bump ty from 0.0.15 to 0.0.16
dependabot[bot] Feb 12, 2026
ff3c7cb
feat(db): openapi routes in our db, needs customization
shamikkarkhanis Feb 12, 2026
4315fae
fix(event): Error ephermal is replaced by success message when errors…
simtiaz5 Feb 13, 2026
e356426
fix(sync): Uncomment sync permissions check
simtiaz5 Feb 13, 2026
bd91807
Merge pull request #88 from CAPY-RPI/feature/capr-23-scaffold-event_c…
shamikkarkhanis Feb 13, 2026
e7c9f0b
Enhanced /set_role to support multiple member roles. Added set_channe…
Feb 13, 2026
ea6bc88
Merge branch 'develop' into dependabot/uv/ty-0.0.16
shamikkarkhanis Feb 13, 2026
2a1a239
Merge pull request #87 from CAPY-RPI/dependabot/uv/ty-0.0.16
shamikkarkhanis Feb 13, 2026
d378021
Merge branch 'develop' into dependabot/uv/uv-0.10.2
shamikkarkhanis Feb 13, 2026
9da5d8e
Merge pull request #86 from CAPY-RPI/dependabot/uv/uv-0.10.2
shamikkarkhanis Feb 13, 2026
e3af32f
Merge branch 'develop' into dependabot/github_actions/docker/build-pu…
shamikkarkhanis Feb 13, 2026
c831879
Merge pull request #85 from CAPY-RPI/dependabot/github_actions/docker…
shamikkarkhanis Feb 13, 2026
7c6d5c2
Merge branch 'develop' into dependabot/github_actions/actions/checkout-6
shamikkarkhanis Feb 13, 2026
b7b533d
Merge pull request #84 from CAPY-RPI/dependabot/github_actions/action…
shamikkarkhanis Feb 13, 2026
e3fa10d
fix: fixed warnings in test cases
GreenJonathan Feb 19, 2026
3febb0c
fix(event): Fix event creation success not embedding
simtiaz5 Feb 20, 2026
347139b
Feature(event): Add cancel button for dropdowns.
simtiaz5 Feb 20, 2026
78d32f1
Merge pull request #82 from CAPY-RPI/feature/capr-33-implement-intera…
shamikkarkhanis Feb 20, 2026
e2dad6f
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 20, 2026
f1f32f9
Added feedback for timeouts.
simtiaz5 Feb 20, 2026
6020804
feature(event): added confirm button for dropdowns
simtiaz5 Feb 20, 2026
d650fb0
Changed guild to work with single line subcommands for summary, onboa…
Feb 20, 2026
1ff6b85
changed warning message text
simtiaz5 Feb 20, 2026
a20ae35
added method to clear channels and roles
Feb 20, 2026
0fc298f
fix(event): defer announce interaction and harden bot permission checks
simtiaz5 Feb 24, 2026
a0cd3bb
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 24, 2026
8cf5d75
Merge pull request #99 from CAPY-RPI/feature/capr-48-event-cog-better…
shamikkarkhanis Feb 24, 2026
036225b
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 24, 2026
f0c346a
guild folder
Feb 24, 2026
00640a5
Merge branch 'develop' into feature/capr-43-port-guild-settings
shamikkarkhanis Feb 24, 2026
0619772
Merge pull request #101 from CAPY-RPI/feature/capr-43-port-guild-sett…
shamikkarkhanis Feb 24, 2026
629f286
feat(database): implement Swagger-aligned backend API wrapper with ty…
simtiaz5 Feb 24, 2026
6316143
test(database): Expand database test coverage
simtiaz5 Feb 24, 2026
40aa8d8
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Feb 28, 2026
93d7e2a
feat(database): add microsoft and google auth callback
simtiaz5 Mar 10, 2026
db91ec1
tests(database): Further expand database test coverage
simtiaz5 Mar 10, 2026
9ecbbbd
test(database): expand database tests for auth, bot, events, organiza…
simtiaz5 Mar 10, 2026
cc330f9
feat(dm): new dm module
shamikkarkhanis Mar 13, 2026
e5cb24b
fix: updated spec doc with new schema
GreenJonathan Mar 13, 2026
80ce19e
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Mar 17, 2026
5fea149
feat(dm): changed policy reqs
shamikkarkhanis Mar 17, 2026
8ccf359
Merge branch 'develop' into feature/capr-52-dm-module
shamikkarkhanis Mar 17, 2026
daeda0a
Merge pull request #126 from CAPY-RPI/feature/capr-52-dm-module
shamikkarkhanis Mar 17, 2026
4e941c4
Revert "Feature/capr 52 dm module"
shamikkarkhanis Mar 17, 2026
d520f96
Merge pull request #127 from CAPY-RPI/revert-126-feature/capr-52-dm-m…
shamikkarkhanis Mar 17, 2026
29d1e8c
feat(db): preview to debug
shamikkarkhanis Mar 17, 2026
59ea530
Merge branch 'feature/capr-52-dm-module' of https://github.com/CApy-R…
shamikkarkhanis Mar 17, 2026
76e2fb3
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Mar 17, 2026
5bd046a
feat(event): Implement event db calls
simtiaz5 Mar 17, 2026
6d1bdbf
gMerge branch 'develop' into feature/capr-52-dm-module
shamikkarkhanis Mar 17, 2026
ba6777e
Merge pull request #128 from CAPY-RPI/feature/capr-52-dm-module
shamikkarkhanis Mar 17, 2026
09f65b6
fix(database): improved error reporting for typed_dict and typed_list
simtiaz5 Mar 17, 2026
2a13959
fix(event): Improve base url handling
simtiaz5 Mar 17, 2026
5c5b66c
feat(onboarding): add in-memory setup and member verification flow
shamikkarkhanis Feb 28, 2026
aa13ca2
init(onboarding)
shamikkarkhanis Feb 28, 2026
b7b5046
Changed onboarding to setup and added new features to change in /setu…
Mar 13, 2026
6db8f85
Changed onboarding to setup
Mar 13, 2026
f6feeb3
Fix
Mar 13, 2026
ac2b1e2
Extended profile form, added whois cog to learn about other users
Mar 17, 2026
d3494a3
Merge branch 'main' into feature/capr-38-who-is
YC-5002 Mar 17, 2026
2373e1e
fix(database): Improve base url handling
simtiaz5 Mar 17, 2026
97a9cf7
Merge branch 'feature/capr-47-implement-db' of https://github.com/CAp…
simtiaz5 Mar 17, 2026
4505884
fix(database): Check and recreated stopped clients/
simtiaz5 Mar 17, 2026
7d5461f
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Mar 18, 2026
0314271
fix(database): route API calls through /api/v1/bot/events.
simtiaz5 Mar 18, 2026
5edf381
refactor(database): Removed out of scope database interactions and co…
simtiaz5 Mar 19, 2026
82c2250
Change file name
Mar 20, 2026
23717c8
test(database): add exhaustive coverage for pool concurrency/race beh…
simtiaz5 Mar 23, 2026
8c01b90
Merge pull request #119 from CAPY-RPI/feature/capr-47-implement-db
shamikkarkhanis Mar 24, 2026
16e2d88
feat(tests): db integration tests
shamikkarkhanis Mar 24, 2026
ff884e6
updated example env
shamikkarkhanis Mar 24, 2026
0baced5
Merge branch 'develop' of https://github.com/CApy-RPI/discord-bot int…
simtiaz5 Mar 24, 2026
650b93b
commented inactive db routes
shamikkarkhanis Mar 24, 2026
a1d3b75
Merge branch 'feature/capr-53-db-integration-tests' of https://github…
shamikkarkhanis Mar 24, 2026
89df99f
Merge pull request #139 from CAPY-RPI/feature/capr-53-db-integration-…
shamikkarkhanis Mar 27, 2026
d6950cf
Merge pull request #130 from CAPY-RPI/feature/capr-38-who-is
shamikkarkhanis Mar 27, 2026
abe9801
fix(setup): alignes with standards
shamikkarkhanis Mar 27, 2026
d793c47
Merge pull request #149 from CAPY-RPI/feature/capr-56-setup-fixes
shamikkarkhanis Mar 28, 2026
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
# .idea/
.idea/

# Abstra
# Abstra is an AI-powered process automation framework.
Expand Down
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .idea/discord-bot.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ repos:
hooks:
- id: ty
name: ty
entry: ty check
entry: uv run ty check
language: system
types: [python]
pass_filenames: false
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.analysis.extraPaths": ["${workspaceFolder}"]
}
261 changes: 158 additions & 103 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,168 +1,223 @@
# Scalable Cog & Interaction Patterns
# Capy Discord Agent & Contributor Guide

This document outlines the architectural patterns used in the `capy-discord` project to ensure scalability, clean code, and a consistent user experience. All agents and contributors should adhere to these patterns when creating new features.
This document outlines the architectural patterns, workflows, and standards for the `capy-discord` project. All agents and contributors must adhere to these guidelines to ensure scalability and code consistency.

## 1. Directory Structure

We follow a hybrid "Feature Folder" structure. Directories are created only as needed for complexity.
We follow a flexible modular structure within `capy_discord/exts/`.

```
### Guidelines
1. **Feature Folders**: Complex features get their own directory (e.g., `exts/profile/`).
2. **Internal Helpers**: Helper files in a feature folder (schemas, views) **must be prefixed with an underscore** (e.g., `_schemas.py`) to prevent the extension loader from treating them as cogs.
3. **Grouping**: Use directories like `exts/tools/` to group simple, related cogs.
4. **Single File Cogs**: Simple cogs can live directly in `exts/` or a grouping directory.

```text
capy_discord/
├── exts/
│ ├── profile/ # Complex Feature (Directory)
│ │ ├── __init__.py # Cog entry point
│ │ ├── schemas.py # Feature-specific models
│ │ └── views.py # Feature-specific UI
│ ├── ping.py # Simple Feature (Standalone file)
│ ├── guild.py # Simple Cog
│ ├── tools/ # Grouping directory
│ │ ├── ping.py
│ │ └── sync.py
│ ├── profile/ # Complex Feature (Directory)
│ │ ├── profile.py # Main Cog file (shares directory name)
│ │ ├── _schemas.py # Helper (ignored by loader)
│ │ └── _views.py # Helper (ignored by loader)
│ └── __init__.py
├── ui/
│ ├── modal.py # Shared UI components
│ ├── views.py # BaseView and shared UI
│ └── ...
│ ├── forms.py # ModelModal (Standard Forms)
│ ├── views.py # BaseView (Standard Interactions)
│ └── modal.py # Low-level base classes
└── bot.py
```

## 2. The `CallbackModal` Pattern (Decoupled UI)
## 2. UI Patterns

We use high-level abstractions to eliminate boilerplate.

### Standard Forms (`ModelModal`)
**Use for:** Data collection and user input.
Do not subclass `BaseModal` manually for standard forms. Use `ModelModal` to auto-generate forms from Pydantic models.

To prevent business logic from leaking into UI classes, we use the `CallbackModal` pattern. This keeps Modal classes "dumb" (pure UI/Validation) and moves logic into the Controller (Cog/Service).
* **Auto-Generation**: Converts Pydantic fields to TextInputs.
* **Validation**: Validates input against schema on submit.
* **Retry**: Auto-handles validation errors with a "Fix Errors" flow.

### Usage
```python
from capy_discord.ui.forms import ModelModal

class UserProfile(BaseModel):
name: str = Field(title="Display Name", max_length=20)

# In your command:
modal = ModelModal(UserProfile, callback=self.save_profile, title="Edit Profile")
await interaction.response.send_modal(modal)
```

1. **Inherit from `CallbackModal`**: located in `capy_discord.ui.modal`.
2. **Field Limit**: **Discord modals can only have up to 5 fields.** If you need more data, consider using multiple steps or splitting the form.
3. **Dynamic Initialization**: Use `__init__` to accept `default_values` for "Edit" flows.
3. **Inject Logic**: Pass a `callback` function from your Cog that handles the submission.
### Interactive Views (`BaseView`)
**Use for:** Buttons, Selects, and custom interactions.
Always inherit from `BaseView` instead of `discord.ui.View`.

**Example:**
* **Safety**: Handles timeouts and errors automatically.
* **Tracking**: Use `view.reply(interaction, ...)` to link view to message.

```python
# In your Cog file
class MyModal(CallbackModal):
def __init__(self, callback, default_text=None):
super().__init__(callback=callback, title="My Modal")
self.text_input = ui.TextInput(default=default_text, ...)
self.add_item(self.text_input)
from capy_discord.ui.views import BaseView

class MyCog(commands.Cog):
...
async def my_command(self, interaction):
modal = MyModal(callback=self.handle_submit)
await interaction.response.send_modal(modal)

async def handle_submit(self, interaction, modal):
# Business logic here!
value = modal.text_input.value
await interaction.response.send_message(f"You said: {value}")
class ConfirmView(BaseView):
@discord.ui.button(label="Confirm")
async def confirm(self, interaction, button):
...
```

## 3. Command Structure (Single Entry Point)
### Simple Inputs (`CallbackModal`)
**Use for:** Simple one-off inputs where a full Pydantic model is overkill.

To avoid cluttering the Discord command list, prefer a **Single Command with Choices** or **Subcommands** over multiple top-level commands.
```python
from capy_discord.ui.modal import CallbackModal
modal = CallbackModal(callback=my_handler, title="Quick Input")
```

### Pattern: Action Choices
## 3. Command Patterns

Use `app_commands.choices` to route actions within a single command. This is preferred for CRUD operations on a single resource (e.g., `/profile`).
### Action Choices (CRUD)
For managing a single resource, use one command with `app_commands.choices`.

```python
@app_commands.command(name="resource", description="Manage resource")
@app_commands.describe(action="The action to perform")
@app_commands.choices(
action=[
app_commands.Choice(name="create", value="create"),
app_commands.Choice(name="view", value="view"),
]
)
async def resource(self, interaction: discord.Interaction, action: str):
if action == "create":
await self.create_handler(interaction)
elif action == "view":
await self.view_handler(interaction)
@app_commands.choices(action=[
Choice(name="create", value="create"),
Choice(name="view", value="view"),
])
async def resource(self, interaction, action: str):
...
```

## 4. Extension Loading
### Group Cogs
For complex features with multiple distinct sub-functions, use `commands.GroupCog`.

Extensions should be robustly discoverable. Our `extensions.py` utility supports deeply nested subdirectories.
## 4. Internal DM Service

- **Packages (`__init__.py` with `setup`)**: Loaded as a single extension.
- **Modules (`file.py`)**: Loaded individually.
- **Naming**: Avoid starting files/folders with `_` unless they are internal helpers.
Direct messaging is an internal service, **not** a user-facing cog. Do not add `/dm`-style command surfaces for bulk messaging.

## 5. Deployment & Syncing
### Location
Use:

- **Global Sync**: Done automatically on startup for consistent deployments.
- **Dev Guild**: A specific Dev Guild ID can be targeted for rapid testing and clearing "ghost" commands.
- **Manual Sync**: A `!sync` (text) command is available for emergency re-syncing without restarting.
* `capy_discord.services.dm`
* `capy_discord.services.policies`

## 6. Time and Timezones
### Safety Model
* DM sends are **deny-all by default** via `policies.DENY_ALL`.
* Developers must opt into explicit allowlists with helpers like `policies.allow_users(...)`, `policies.allow_roles(...)`, or `policies.allow_targets(...)`.
* The service rejects `@everyone`, rejects targets outside the allowed policy, and enforces `max_recipients`.

To prevent bugs related to naive datetimes, **always use `zoneinfo.ZoneInfo`** for timezone-aware datetimes.
### Usage Pattern
Developers should think in terms of:

- **Default Timezone**: Use `UTC` for database storage and internal logic.
- **Library**: Use the built-in `zoneinfo` module (available in Python 3.9+).
1. The exact user or role to DM.
2. The predefined policy that permits that target.

**Example:**
Prefer explicit entrypoints over generic audience bags:

```python
from datetime import datetime
from zoneinfo import ZoneInfo
from capy_discord.services import dm, policies

EVENT_POLICY = policies.allow_roles(EVENT_ROLE_ID, max_recipients=20)

# Always specify tzinfo
now = datetime.now(ZoneInfo("UTC"))
draft = await dm.compose_to_role(
guild,
EVENT_ROLE_ID,
"Reminder: event starts at 7 PM.",
policy=EVENT_POLICY,
)
result = await dm.send(guild, draft)
```

## 7. Development Workflow
For self-test or single-user flows, use `dm.compose_to_user(...)` with `policies.allow_users(...)`.

### Cog Usage
If you need an operator-facing entrypoint for DM functionality, keep it narrow and task-specific rather than exposing a generic DM surface.

Use a small cog command only for explicit, safe flows such as self-test notifications:

```python
from capy_discord.services import dm, policies

We use `uv` for dependency management and task execution. This ensures all commands run within the project's virtual environment.
policy = policies.allow_users(interaction.user.id, max_recipients=1)

### Running Tasks
draft = await dm.compose_to_user(
guild,
interaction.user.id,
message,
policy=policy,
)
self.log.info("Notify preview\n%s", dm.render_preview(draft))

Use `uv run task <task_name>` to execute common development tasks defined in `pyproject.toml`.
result = await dm.send(guild, draft)
```

- **Start App**: `uv run task start`
- **Lint & Format**: `uv run task lint`
- **Run Tests**: `uv run task test`
- **Build Docker**: `uv run task build`
This pattern is implemented in `capy_discord/exts/tools/notify.py`. Do not add broad `/dm` or bulk-message commands; use explicit commands tied to a specific feature or operational workflow.

**IMPORTANT: After every change, run `uv run task lint` to perform a Ruff and Type check.**
## 5. Error Handling
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.

### Running Scripts
## 6. Logging
All logs follow a standardized format for consistency across the console and log files.

To run arbitrary scripts or commands within the environment:
* **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.

```bash
uv run python path/to/script.py
```python
import logging
self.log = logging.getLogger(__name__)
self.log.info("Starting feature X")
```

## 8. Git Commit Guidelines
## 7. Time and Timezones
**Always use `zoneinfo.ZoneInfo`**.
* **Storage**: `UTC`.
* **Usage**: `datetime.now(ZoneInfo("UTC"))`.

### Pre-Commit Hooks
## 8. Development Workflow

This project uses pre-commit hooks for linting. If a hook fails during commit:
### Linear & Branching
* **Issue Tracking**: Every task must have a Linear issue.
* **Branching**:
* `feature/CAPY-123-description`
* `fix/CAPY-123-description`
* `refactor/` | `docs/` | `test/`

1. **DO NOT** use `git commit --no-verify` to bypass hooks.
2. **DO** run `uv run task lint` manually to verify and fix issues.
3. If `uv run task lint` passes but the hook still fails (e.g., executable not found), there is likely an environment issue with the pre-commit config that needs to be fixed.
### Dependency Management (`uv`)
Always run commands via `uv` to use the virtual environment.

### Cog Initialization Pattern
* **Start**: `uv run task start`
* **Lint**: `uv run task lint` (Run this before every commit!)
* **Test**: `uv run task test`

All Cogs **MUST** accept the `bot` instance as an argument in their `__init__` method:
### Commit Guidelines (Conventional Commits)
Format: `<type>(<scope>): <subject>`

* `feat(auth): add login flow`
* `fix(ui): resolve timeout issue`
* Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`.

### Pull Requests
1. **Base Branch**: Merge into `develop`.
2. **Reviewers**: Must include `Shamik` and `Jason`.
3. **Checks**: All CI checks (Lint, Test, Build) must pass.

## 9. 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.

```python
# CORRECT
class MyCog(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot

async def setup(bot: commands.Bot) -> None:
await bot.add_cog(MyCog(bot))

# INCORRECT - Do not use global instance or omit bot argument
class MyCog(commands.Cog):
def __init__(self) -> None: # Missing bot!
pass
```

This ensures:
- Proper dependency injection
- Testability (can pass mock bot)
- No reliance on global state
Loading
Loading