<\!DOCTYPE html> Contributing to Mail-Rulez

Contributing to Mail-Rulez

Thank you for your interest in contributing to Mail-Rulez! This guide will help you get started with development.

Getting Started

Prerequisites

  • Python 3.9+
  • Docker and Docker Compose
  • Git
  • A GitHub account
  • Basic knowledge of Flask and Python

Development Setup

  1. Fork and Clone

    # Fork the repository on GitHub, then:
    git clone https://github.com/YOUR-USERNAME/mail-rulez-public.git
    cd mail-rulez-public
    
  2. Create Virtual Environment

    python -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
    pip install -r requirements.txt
    pip install -r requirements-dev.txt  # Development dependencies
    
  3. Set Up Pre-commit Hooks

    pre-commit install
    
  4. Configure Development Environment

    cp env-template .env
    # Edit .env with your test email accounts
    
  5. Run Tests

    pytest
    pytest --cov=.  # With coverage
    

Development Workflow

1. Create a Feature Branch

git checkout -b feature/your-feature-name
# or
git checkout -b fix/issue-description

2. Make Your Changes

Follow the coding standards and ensure:

  • All tests pass
  • New features have tests
  • Documentation is updated
  • Code follows PEP 8

3. Test Your Changes

# Run unit tests
pytest tests/

# Run specific test
pytest tests/test_rules.py::test_rule_creation

# Run with coverage
pytest --cov=. --cov-report=html

# Run linting
flake8 .
black . --check

4. Test in Docker

# Build local image
docker build -f docker/Dockerfile.local -t mail-rulez:dev .

# Run container
docker-compose -f docker/docker-compose.test.yml up

# Access at http://localhost:5001

5. Commit Your Changes

git add .
git commit -m "feat: add support for regex in email rules

- Add regex validation
- Update rule builder UI
- Add regex examples in documentation"

Follow conventional commits:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes
  • refactor: Code refactoring
  • test: Test changes
  • chore: Build/tooling changes

6. Push and Create PR

git push origin feature/your-feature-name

Then create a Pull Request on GitHub.

Code Architecture

Project Structure

mail-rulez/
├── web/                    # Flask web application
│   ├── routes/            # Route handlers
│   ├── templates/         # Jinja2 templates
│   └── static/           # CSS, JS, images
├── services/              # Background services
│   ├── email_processor.py # Email processing engine
│   ├── scheduler_manager.py # Task scheduling
│   └── task_manager.py    # Task coordination
├── retention/             # Retention system
│   ├── manager.py        # Retention logic
│   ├── scheduler.py      # Retention scheduling
│   └── models.py         # Data models
├── tests/                # Test suite
├── docker/               # Docker configurations
└── docs/                 # Documentation

Key Components

Email Processing Engine

# services/email_processor.py
class EmailProcessor:
    """Core email processing logic"""
    
    def process_account(self, account_id: int) -> ProcessingResult:
        """Process emails for a single account"""
        
    def apply_rules(self, email: Email, rules: List[Rule]) -> List[Action]:
        """Apply rules to an email"""

Rule System

# rules.py
class RuleEngine:
    """Rule matching and execution"""
    
    def match(self, email: Email, rule: Rule) -> bool:
        """Check if email matches rule conditions"""
        
    def execute_actions(self, email: Email, actions: List[Action]) -> None:
        """Execute rule actions on email"""

Web Routes

# web/routes/rules.py
@bp.route('/rules/add', methods=['GET', 'POST'])
@login_required
def add_rule():
    """Add new rule endpoint"""

Database Schema

-- Core tables
CREATE TABLE accounts (
    id INTEGER PRIMARY KEY,
    email TEXT NOT NULL,
    server TEXT NOT NULL,
    encrypted_password TEXT NOT NULL,
    active BOOLEAN DEFAULT 1
);

CREATE TABLE rules (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    priority INTEGER DEFAULT 10,
    conditions TEXT NOT NULL,  -- JSON
    actions TEXT NOT NULL,      -- JSON
    active BOOLEAN DEFAULT 1
);

CREATE TABLE processing_log (
    id INTEGER PRIMARY KEY,
    account_id INTEGER,
    timestamp DATETIME,
    emails_processed INTEGER,
    duration REAL
);

Testing Guidelines

Unit Tests

# tests/test_rules.py
def test_rule_matching():
    """Test rule matching logic"""
    rule = Rule(
        conditions={"from_contains": ["[email protected]"]},
        actions={"move_to": "Test"}
    )
    email = Email(from_addr="[email protected]")
    
    assert rule_engine.match(email, rule) is True

Integration Tests

# tests/test_integration.py
def test_email_processing_flow(test_account):
    """Test complete email processing flow"""
    processor = EmailProcessor()
    result = processor.process_account(test_account.id)
    
    assert result.processed_count > 0
    assert result.errors == []

Testing Best Practices

  1. Use Fixtures

    @pytest.fixture
    def test_account():
        return Account(email="[email protected]", server="imap.test.com")
    
  2. Mock External Services

    @patch('imaplib.IMAP4_SSL')
    def test_imap_connection(mock_imap):
        mock_imap.return_value.login.return_value = ('OK', [])
    
  3. Test Edge Cases

    • Empty inputs
    • Invalid data
    • Network failures
    • Concurrent access

Code Style Guide

Python Style

Follow PEP 8 with these additions:

  1. Imports

    # Standard library
    import os
    import sys
    
    # Third-party
    from flask import Flask, request
    import pytest
    
    # Local
    from web.routes import auth
    from services.email_processor import EmailProcessor
    
  2. Type Hints

    def process_emails(
        account_id: int,
        batch_size: int = 100,
        dry_run: bool = False
    ) -> ProcessingResult:
        """Process emails with type hints"""
    
  3. Docstrings

    def calculate_retention_date(
        policy: RetentionPolicy,
        email_date: datetime
    ) -> datetime:
        """
        Calculate when an email should be deleted.
        
        Args:
            policy: The retention policy to apply
            email_date: Original email date
            
        Returns:
            datetime: Date when email should be deleted
            
        Raises:
            ValueError: If policy is invalid
        """
    

JavaScript Style

  1. Modern ES6+

    // Use const/let, arrow functions, template literals
    const processEmail = async (emailId) => {
        const response = await fetch(`/api/emails/${emailId}`);
        return response.json();
    };
    
  2. Consistent Naming

    // camelCase for variables and functions
    const emailCount = 0;
    function processNextBatch() {}
    
    // PascalCase for classes
    class EmailProcessor {}
    

HTML/CSS Style

  1. Semantic HTML

    <article class="email-rule">
        <header>
            <h3>{{ rule.name }}</h3>
        </header>
        <section class="conditions">
            <!-- Rule conditions -->
        </section>
    </article>
    
  2. BEM CSS Naming

    .email-rule {}
    .email-rule__header {}
    .email-rule__condition {}
    .email-rule--active {}
    

Security Considerations

Input Validation

Always validate user input:

from werkzeug.security import check_password_hash
from email_validator import validate_email

def validate_account_form(form_data):
    """Validate account creation form"""
    errors = []
    
    # Validate email
    try:
        validate_email(form_data['email'])
    except EmailNotValidError:
        errors.append("Invalid email address")
    
    # Validate server
    if not form_data.get('server'):
        errors.append("Server is required")
    
    return errors

SQL Injection Prevention

Use parameterized queries:

# Good
cursor.execute(
    "SELECT * FROM accounts WHERE email = ?",
    (email,)
)

# Bad - Never do this!
cursor.execute(
    f"SELECT * FROM accounts WHERE email = '{email}'"
)

XSS Prevention

Escape output in templates:

<!-- Jinja2 auto-escapes by default -->
<p>{{ user_input }}</p>

<!-- For raw HTML, be explicit -->
{{ trusted_html|safe }}

Performance Guidelines

Database Optimization

  1. Use Indexes

    CREATE INDEX idx_rules_priority ON rules(priority);
    CREATE INDEX idx_emails_account_date ON emails(account_id, date);
    
  2. Batch Operations

    # Good - Single query
    cursor.executemany(
        "INSERT INTO emails (subject, from_addr) VALUES (?, ?)",
        email_data
    )
    
    # Bad - Multiple queries
    for email in emails:
        cursor.execute("INSERT INTO emails ...")
    

Caching

from functools import lru_cache

@lru_cache(maxsize=128)
def get_account_folders(account_id: int) -> List[str]:
    """Cache folder list for performance"""
    return fetch_folders_from_imap(account_id)

Documentation

Code Documentation

  1. Module Docstrings

    """
    Email Processing Module
    
    This module handles the core email processing logic including:
    - Fetching emails from IMAP servers
    - Applying rules and filters
    - Moving emails to appropriate folders
    """
    
  2. README Files Add README.md files in major directories explaining:

    • Purpose of the directory
    • Key files and their roles
    • Dependencies

User Documentation

When adding features, update:

  • User-facing documentation in /docs
  • In-app help text
  • Configuration examples

Submitting Pull Requests

PR Checklist

  • [ ] Code follows project style guide
  • [ ] All tests pass (pytest)
  • [ ] New features have tests
  • [ ] Documentation is updated
  • [ ] Commit messages follow conventions
  • [ ] No sensitive data in commits
  • [ ] PR description explains changes

PR Template

## Description
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed

## Screenshots (if applicable)
Add screenshots for UI changes

## Related Issues
Fixes #123

Getting Help

Resources

Communication

  • Bug Reports: Use GitHub Issues
  • Feature Requests: Start a Discussion
  • Security Issues: Email [email protected]
  • General Questions: Use Discussions

License

By contributing, you agree that your contributions will be licensed under the project's dual license (AGPL v3 / Commercial).

Thank You!

Your contributions make Mail-Rulez better for everyone. We appreciate your time and effort!