-
Notifications
You must be signed in to change notification settings - Fork 2
Developer Guide
pancakes-proxy edited this page Jul 14, 2025
·
1 revision
This comprehensive guide covers everything developers need to know to contribute to AIMod, including setup, architecture, coding standards, and contribution workflows.
- Python 3.11+ with pip and venv
- Node.js 18+ with npm
- PostgreSQL 13+
- Redis 6.0+
- Git for version control
- IDE (VS Code, PyCharm, or similar)
# Clone the repository
git clone https://github.com/discordaimod/aimod.git
cd aimod
# Create and activate virtual environment
python3.11 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install development dependencies
pip install -r requirements.txt
pip install -r requirements-dev.txt
# Set up pre-commit hooks
pre-commit install
# Copy environment template
cp .env.example .env.dev# requirements-dev.txt
pytest>=7.4.0
pytest-asyncio>=0.21.0
pytest-cov>=4.1.0
black>=23.7.0
pylint>=2.17.0
mypy>=1.5.0
pre-commit>=3.3.0
bandit>=1.7.0
safety>=2.3.0
isort>=5.12.0
flake8>=6.0.0// .vscode/settings.json
{
"python.defaultInterpreterPath": "./venv/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.mypyEnabled": true,
"python.formatting.provider": "black",
"python.formatting.blackArgs": ["--line-length", "88"],
"python.sortImports.args": ["--profile", "black"],
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"files.exclude": {
"**/__pycache__": true,
"**/*.pyc": true,
"**/node_modules": true,
"**/.pytest_cache": true
}
}// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Bot",
"type": "python",
"request": "launch",
"program": "bot.py",
"console": "integratedTerminal",
"envFile": "${workspaceFolder}/.env.dev",
"cwd": "${workspaceFolder}"
},
{
"name": "Python: Tests",
"type": "python",
"request": "launch",
"module": "pytest",
"args": ["-v"],
"console": "integratedTerminal",
"envFile": "${workspaceFolder}/.env.dev"
}
]
}-
Interpreter: Configure Python interpreter to use
./venv/bin/python - Code Style: Import Black formatter configuration
- Database: Configure PostgreSQL connection in Database tool
- Run Configurations: Create configurations for bot.py and tests
# Discord Configuration
DISCORD_TOKEN=your_dev_bot_token
DISCORD_CLIENT_ID=your_dev_client_id
DISCORD_CLIENT_SECRET=your_dev_client_secret
# Database Configuration
DATABASE_URL=postgresql://aimod_user:password@localhost:5432/aimod_bot_dev
REDIS_URL=redis://localhost:6379/1
# AI Provider Configuration
OPENROUTER_API_KEY=your_dev_api_key
# Development Settings
ENVIRONMENT=development
DEBUG=true
LOG_LEVEL=DEBUG
# Testing
TEST_GUILD_ID=your_test_guild_id
TEST_USER_ID=your_test_user_idaimod/
βββ bot.py # Main bot entry point
βββ cogs/ # Bot functionality modules
β βββ core_ai_cog.py # AI moderation core
β βββ config_cog.py # Configuration management
β βββ human_moderation_cog.py # Manual moderation tools
β βββ logging_cog.py # Event logging
β βββ statistics.py # Analytics and stats
β βββ botdetect.py # Bot detection
β βββ raiddefence.py # Raid protection
β βββ appeal_cog.py # Appeal system
β βββ aimod_helpers/ # Helper modules
β βββ config_manager.py # Configuration utilities
β βββ litellm_config.py # AI client configuration
β βββ media_processor.py # Image/media processing
β βββ system_prompt.py # AI prompt templates
β βββ utils.py # General utilities
β βββ ui.py # Discord UI components
βββ database/ # Database layer
β βββ __init__.py
β βββ connection.py # Database connections
β βββ models.py # Data models
β βββ operations.py # Database operations
β βββ cache.py # Redis caching
β βββ migrations/ # Database migrations
βββ dashboard/ # Web dashboard
β βββ backend/ # FastAPI backend
β β βββ main.py # FastAPI application
β β βββ app/ # Application modules
β β βββ api.py # API endpoints
β β βββ schemas.py # Pydantic models
β β βββ crud.py # Database operations
β β βββ db.py # Database connection
β βββ frontend/ # React frontend
β βββ src/ # Source code
β βββ public/ # Static assets
β βββ package.json # Dependencies
βββ tests/ # Test suite
β βββ test_core_ai.py # AI moderation tests
β βββ test_database.py # Database tests
β βββ test_config.py # Configuration tests
β βββ fixtures/ # Test fixtures
βββ scripts/ # Utility scripts
β βββ deploy_backend.sh # Backend deployment
β βββ deploy_frontend.sh # Frontend deployment
β βββ setup_dev.sh # Development setup
βββ docs/ # Documentation
βββ requirements.txt # Production dependencies
βββ requirements-dev.txt # Development dependencies
βββ pyproject.toml # Project configuration
βββ .pre-commit-config.yaml # Pre-commit hooks
βββ README.md # Project overview
- Cogs: Discrete functionality modules
- Database: Data persistence layer
- Helpers: Utility functions and classes
- Dashboard: Web interface (separate from bot)
- Core Dependencies: Essential for bot operation
- Optional Dependencies: Feature-specific (e.g., image processing)
- Development Dependencies: Testing and code quality tools
# Use Black formatter with 88-character line length
# Example of properly formatted code
from typing import Dict, List, Optional, Union
import asyncio
import discord
from discord.ext import commands
class ExampleCog(commands.Cog, name="Example"):
"""Example cog demonstrating coding standards."""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.cache: Dict[int, str] = {}
async def process_data(
self,
guild_id: int,
data: List[Dict[str, Union[str, int]]]
) -> Optional[str]:
"""Process data with proper type hints and documentation.
Args:
guild_id: The Discord guild ID
data: List of data dictionaries to process
Returns:
Processed result string or None if processing failed
Raises:
ValueError: If guild_id is invalid
ProcessingError: If data processing fails
"""
if guild_id <= 0:
raise ValueError("Guild ID must be positive")
try:
# Process data here
result = await self._internal_processing(data)
return result
except Exception as e:
logger.error(f"Processing failed for guild {guild_id}: {e}")
return None
async def _internal_processing(self, data: List[Dict]) -> str:
"""Internal processing method (private)."""
# Implementation details
pass# Variables and functions: snake_case
user_id = 123456789
guild_config = {}
async def get_user_data(user_id: int) -> dict:
pass
# Classes: PascalCase
class UserManager:
pass
class AIConfigurationError(Exception):
pass
# Constants: UPPER_SNAKE_CASE
DEFAULT_TIMEOUT_DURATION = 3600
MAX_MESSAGE_LENGTH = 2000
# Private methods: leading underscore
def _internal_helper(self) -> None:
pass
# Discord.py conventions
@commands.command(name="example")
async def example_command(self, ctx: commands.Context) -> None:
pass
@app_commands.command(name="example")
async def example_slash(self, interaction: discord.Interaction) -> None:
passfrom typing import Dict, List, Optional, Union, Any, Callable, Awaitable
from discord.ext import commands
import discord
# Function signatures
async def get_guild_config(
guild_id: int,
key: str,
default: Optional[Any] = None
) -> Any:
pass
# Class attributes
class ConfigManager:
cache: Dict[str, Any]
bot: commands.Bot
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.cache = {}
# Complex types
UserData = Dict[str, Union[str, int, bool]]
ConfigCallback = Callable[[int, str, Any], Awaitable[None]]
async def process_users(
users: List[UserData],
callback: Optional[ConfigCallback] = None
) -> List[str]:
passdef complex_function(
param1: str,
param2: int,
param3: Optional[bool] = None
) -> Dict[str, Any]:
"""Brief description of the function.
Longer description explaining the function's purpose,
behavior, and any important implementation details.
Args:
param1: Description of the first parameter
param2: Description of the second parameter
param3: Optional parameter with default value
Returns:
Dictionary containing the processed results with keys:
- 'status': Processing status string
- 'data': Processed data
- 'errors': List of any errors encountered
Raises:
ValueError: If param1 is empty or param2 is negative
ProcessingError: If data processing fails
Example:
>>> result = complex_function("test", 42, True)
>>> print(result['status'])
'success'
"""
pass# Good comments explain WHY, not WHAT
class MessageProcessor:
def __init__(self):
# Use deque for efficient FIFO operations on recent decisions
self.recent_decisions = collections.deque(maxlen=5)
# Cache AI responses to avoid redundant API calls for identical content
self.response_cache = TTLCache(maxsize=1000, ttl=300)
async def process_message(self, message: discord.Message) -> None:
# Skip processing if user is whitelisted to avoid false positives
if await self.is_whitelisted(message.author):
return
# TODO: Implement rate limiting for AI API calls
# See issue #123 for implementation details
result = await self.analyze_with_ai(message.content)import logging
from typing import Optional
logger = logging.getLogger(__name__)
# Specific exception handling
async def get_user_data(user_id: int) -> Optional[dict]:
"""Get user data with proper error handling."""
try:
async with get_connection() as conn:
result = await conn.fetchrow(
"SELECT * FROM user_data WHERE user_id = $1",
user_id
)
return dict(result) if result else None
except asyncpg.PostgresError as e:
logger.error(f"Database error getting user {user_id}: {e}")
return None
except Exception as e:
logger.error(f"Unexpected error getting user {user_id}: {e}")
return None
# Discord.py error handling
@commands.command()
async def example_command(self, ctx: commands.Context) -> None:
"""Example command with error handling."""
try:
# Command implementation
await ctx.send("Success!")
except discord.Forbidden:
await ctx.send("β I don't have permission to do that.")
except discord.HTTPException as e:
logger.error(f"Discord API error in command: {e}")
await ctx.send("β Something went wrong. Please try again.")
except Exception as e:
logger.error(f"Unexpected error in command: {e}")
await ctx.send("β An unexpected error occurred.")
# Custom exceptions
class AIModError(Exception):
"""Base exception for AIMod-specific errors."""
pass
class ConfigurationError(AIModError):
"""Raised when configuration is invalid."""
pass
class AIProviderError(AIModError):
"""Raised when AI provider API fails."""
passtests/
βββ conftest.py # Pytest configuration and fixtures
βββ test_core_ai.py # AI moderation tests
βββ test_database.py # Database operation tests
βββ test_config.py # Configuration tests
βββ test_security.py # Security feature tests
βββ test_dashboard.py # Dashboard API tests
βββ integration/ # Integration tests
β βββ test_bot_integration.py
β βββ test_api_integration.py
βββ fixtures/ # Test data
β βββ sample_messages.json
β βββ test_config.json
β βββ mock_responses.json
βββ utils/ # Test utilities
βββ mock_discord.py # Discord.py mocks
βββ test_helpers.py # Test helper functions
import pytest
import asyncio
import asyncpg
from unittest.mock import AsyncMock, MagicMock
import discord
from discord.ext import commands
@pytest.fixture(scope="session")
def event_loop():
"""Create an instance of the default event loop for the test session."""
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest.fixture
async def test_db():
"""Create a test database connection."""
conn = await asyncpg.connect("postgresql://test_user:password@localhost/test_db")
yield conn
await conn.close()
@pytest.fixture
def mock_bot():
"""Create a mock Discord bot."""
bot = AsyncMock(spec=commands.Bot)
bot.user = MagicMock()
bot.user.id = 123456789
return bot
@pytest.fixture
def mock_guild():
"""Create a mock Discord guild."""
guild = MagicMock(spec=discord.Guild)
guild.id = 987654321
guild.name = "Test Guild"
return guild
@pytest.fixture
def mock_message(mock_guild):
"""Create a mock Discord message."""
message = MagicMock(spec=discord.Message)
message.id = 111222333
message.content = "Test message content"
message.guild = mock_guild
message.author = MagicMock()
message.author.id = 444555666
message.author.bot = False
return message# test_core_ai.py
import pytest
from unittest.mock import AsyncMock, patch
from cogs.core_ai_cog import CoreAICog
class TestCoreAICog:
"""Test suite for Core AI Cog."""
@pytest.fixture
async def ai_cog(self, mock_bot):
"""Create AI cog instance for testing."""
cog = CoreAICog(mock_bot)
await cog.cog_load()
return cog
async def test_message_analysis(self, ai_cog, mock_message):
"""Test AI message analysis functionality."""
# Mock AI response
mock_response = {
"action": "WARN",
"rule_violated": "Rule 1",
"reasoning": "Test violation",
"confidence": 85
}
with patch.object(ai_cog, 'analyze_message_with_ai', return_value=mock_response):
result = await ai_cog.analyze_message_with_ai(
mock_message,
"Test rules",
"test-model"
)
assert result["action"] == "WARN"
assert result["confidence"] == 85
async def test_global_ban_enforcement(self, ai_cog, mock_message):
"""Test global ban enforcement."""
# Add user to global ban list
with patch('cogs.core_ai_cog.GLOBAL_BANS', {mock_message.author.id}):
# Mock guild ban method
mock_message.guild.ban = AsyncMock()
await ai_cog.message_listener(mock_message)
# Verify ban was called
mock_message.guild.ban.assert_called_once()
# test_database.py
import pytest
from database.operations import get_guild_config, set_guild_config
class TestDatabaseOperations:
"""Test suite for database operations."""
async def test_guild_config_operations(self, test_db):
"""Test guild configuration CRUD operations."""
guild_id = 123456789
key = "TEST_KEY"
value = "test_value"
# Test setting configuration
await set_guild_config(guild_id, key, value)
# Test getting configuration
result = await get_guild_config(guild_id, key)
assert result == value
# Test default value
result = await get_guild_config(guild_id, "NONEXISTENT", "default")
assert result == "default"# Run all tests
pytest
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/test_core_ai.py
# Run specific test method
pytest tests/test_core_ai.py::TestCoreAICog::test_message_analysis
# Run with coverage
pytest --cov=. --cov-report=html
# Run only fast tests (skip integration tests)
pytest -m "not integration"[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--disable-warnings",
]
markers = [
"integration: marks tests as integration tests",
"slow: marks tests as slow",
"requires_db: marks tests that require database",
"requires_redis: marks tests that require Redis",
]# Main branches
main # Production-ready code
develop # Integration branch for features
# Feature branches
feature/ai-improvements
feature/dashboard-redesign
feature/new-security-features
# Release branches
release/v2.1.0
# Hotfix branches
hotfix/critical-bug-fixtype(scope): brief description
Longer description explaining the change in detail.
Include motivation for the change and contrast with
previous behavior.
Fixes #123
Closes #456
Commit Types:
-
feat: New feature -
fix: Bug fix -
docs: Documentation changes -
style: Code style changes (formatting, etc.) -
refactor: Code refactoring -
test: Adding or updating tests -
chore: Maintenance tasks
Examples:
feat(ai): add support for image analysis in moderation
- Implement OCR text extraction from images
- Add content classification for NSFW detection
- Update system prompt to include image context
Closes #234
fix(database): resolve connection pool exhaustion
The connection pool was not properly releasing connections
after failed queries, leading to pool exhaustion under load.
- Add proper connection cleanup in error handlers
- Increase default pool size from 10 to 20
- Add connection pool monitoring
Fixes #456repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
language_version: python3.11
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black"]
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
args: ["--max-line-length=88", "--extend-ignore=E203,W503"]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.0
hooks:
- id: mypy
additional_dependencies: [types-all]
- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
hooks:
- id: bandit
args: ["-r", ".", "-x", "tests/"]- Fork the repository and create a feature branch
- Make your changes following coding standards
- Add tests for new functionality
- Update documentation as needed
- Run the test suite and ensure all tests pass
- Submit a pull request with detailed description
## Description
Brief description of the changes made.
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## Testing
- [ ] Tests pass locally
- [ ] New tests added for new functionality
- [ ] Manual testing completed
## Checklist
- [ ] Code follows the project's coding standards
- [ ] Self-review of code completed
- [ ] Documentation updated
- [ ] No new warnings introducedFor Reviewers:
- Check code quality and adherence to standards
- Verify test coverage for new features
- Ensure documentation is updated
- Test functionality manually if needed
- Provide constructive feedback
For Contributors:
- Respond to feedback promptly
- Make requested changes
- Keep PR scope focused and manageable
- Rebase on latest main before merging
- Discord Server: Join our community for real-time help
- GitHub Issues: Report bugs and request features
- Documentation: Check existing docs before asking questions
- Code Review: Learn from feedback on your PRs
- Core Bot Features: AI moderation, security, commands
- Dashboard: Frontend and backend improvements
- Documentation: Guides, API docs, tutorials
- Testing: Unit tests, integration tests, performance tests
- Infrastructure: Deployment, monitoring, optimization
Contributors are recognized in:
- CONTRIBUTORS.md file
- Release notes for significant contributions
- Discord server contributor roles
- Annual contributor highlights
This completes the comprehensive AIMod documentation wiki with detailed coverage of all major systems, components, and development processes.