Codebase: 7 documents (stack, architecture, structure, conventions, testing, integrations, concerns) Research: 5 documents (stack, features, architecture, pitfalls, summary)
274 lines
9.7 KiB
Markdown
274 lines
9.7 KiB
Markdown
# Coding Conventions
|
|
|
|
**Analysis Date:** 2026-02-04
|
|
|
|
## Naming Patterns
|
|
|
|
**Files:**
|
|
- Python files: lowercase with underscores (e.g., `bot.py`, `credentials`)
|
|
- Bash scripts: lowercase with hyphens (e.g., `npm-api`, `uptime-kuma`)
|
|
- Helper scripts in `~/bin/`: all lowercase, no extension (e.g., `pve`, `pbs`, `dns`)
|
|
|
|
**Functions:**
|
|
- Python: snake_case (e.g., `cmd_status()`, `get_authorized_users()`, `run_command()`)
|
|
- Bash: snake_case with `cmd_` prefix for command handlers (e.g., `cmd_status()`, `cmd_tasks()`)
|
|
- Bash: auxiliary functions also use snake_case (e.g., `ssh_pbs()`, `get_token()`)
|
|
|
|
**Variables:**
|
|
- Python: snake_case for local/module vars (e.g., `authorized_users`, `output_lines`)
|
|
- Python: UPPERCASE for constants (e.g., `TOKEN`, `INBOX_FILE`, `AUTHORIZED_FILE`, `NODE`, `PBS_HOST`)
|
|
- Bash: UPPERCASE for environment variables and constants (e.g., `PBS_HOST`, `TOKEN`, `BASE`, `DEFAULT_ZONE`)
|
|
- Bash: lowercase for local variables (e.g., `hours`, `cutoff`, `status_icon`)
|
|
|
|
**Types/Classes:**
|
|
- Python: PascalCase for imported classes (e.g., `ProxmoxAPI`, `Update`, `Application`)
|
|
- Dictionary/config keys: lowercase with hyphens or underscores (e.g., `token_name`, `max-mem`)
|
|
|
|
## Code Style
|
|
|
|
**Formatting:**
|
|
- No automated formatter detected in codebase
|
|
- Python: PEP 8 conventions followed informally
|
|
- 4-space indentation
|
|
- Max line length ~90-100 characters (observed in practice)
|
|
- Blank lines: 2 lines before module-level functions, 1 line before methods
|
|
- Bash: 4-space indentation (observed)
|
|
|
|
**Linting:**
|
|
- No linting configuration detected (no .pylintrc, .flake8, .eslintrc)
|
|
- Code style is manually maintained
|
|
|
|
**Docstrings:**
|
|
- Python: Triple-quoted strings at module level describing purpose
|
|
- Example from `telegram/bot.py`:
|
|
```python
|
|
"""
|
|
Homelab Telegram Bot
|
|
Two-way interactive bot for homelab management and notifications.
|
|
"""
|
|
```
|
|
- Python: Function docstrings used for major functions
|
|
- Single-line format for simple functions
|
|
- Example: `"""Handle /start command - first contact with bot."""`
|
|
- Example: `"""Load authorized user IDs."""`
|
|
|
|
## Import Organization
|
|
|
|
**Order:**
|
|
1. Standard library imports (e.g., `sys`, `os`, `json`, `subprocess`)
|
|
2. Third-party imports (e.g., `ProxmoxAPI`, `telegram`, `pocketbase`)
|
|
3. Local imports (rarely used in this codebase)
|
|
|
|
**Path Aliases:**
|
|
- No aliases detected
|
|
- Absolute imports used throughout
|
|
|
|
**Credential Loading Pattern:**
|
|
All scripts that need credentials follow the same pattern:
|
|
```python
|
|
# Load credentials
|
|
creds_path = Path.home() / ".config" / <service> / "credentials"
|
|
creds = {}
|
|
with open(creds_path) as f:
|
|
for line in f:
|
|
if '=' in line:
|
|
key, value = line.strip().split('=', 1)
|
|
creds[key] = value
|
|
```
|
|
|
|
Or in Bash:
|
|
```bash
|
|
source ~/.config/dns/credentials
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
**Patterns:**
|
|
- Python: Try-except with broad exception catching (bare `except:` used in `pve` script lines 70, 82, 95, 101)
|
|
- Not ideal but pragmatic for CLI tools that need to try multiple approaches
|
|
- Example from `pve`:
|
|
```python
|
|
try:
|
|
status = pve.nodes(NODE).lxc(vmid).status.current.get()
|
|
# ...
|
|
return
|
|
except:
|
|
pass
|
|
```
|
|
|
|
- Python: Explicit exception handling in telegram bot
|
|
- Catches `subprocess.TimeoutExpired` specifically in `run_command()` function
|
|
- Example from `telegram/bot.py`:
|
|
```python
|
|
try:
|
|
result = subprocess.run(...)
|
|
output = result.stdout or result.stderr or "No output"
|
|
if len(output) > 4000:
|
|
output = output[:4000] + "\n... (truncated)"
|
|
return output
|
|
except subprocess.TimeoutExpired:
|
|
return "Command timed out"
|
|
except Exception as e:
|
|
return f"Error: {e}"
|
|
```
|
|
|
|
- Bash: Set strict mode with `set -e` in some scripts (`dns` script line 12)
|
|
- Causes script to exit on first error
|
|
|
|
- Bash: No error handling in most scripts (`pbs`, `beszel`, `kuma`)
|
|
- Relies on exit codes implicitly
|
|
|
|
**Return Value Handling:**
|
|
- Python: Functions return data directly or None on failure
|
|
- Example from `pbs` helper: Returns JSON-parsed data or string output
|
|
- Example from `pve`: Returns nothing (prints output), but uses exceptions for flow control
|
|
|
|
- Python: Command runner returns error strings: `"Command timed out"`, `"Error: {e}"`
|
|
|
|
## Logging
|
|
|
|
**Framework:**
|
|
- Python: Standard `logging` module
|
|
- Configured in `telegram/bot.py` lines 18-22:
|
|
```python
|
|
logging.basicConfig(
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
level=logging.INFO
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
```
|
|
- Log level: INFO
|
|
- Format includes timestamp, logger name, level, message
|
|
|
|
**Patterns:**
|
|
- `logger.info()` for general informational messages
|
|
- Example: `logger.info("Starting Homelab Bot...")`
|
|
- Example: `logger.info(f"Inbox message from {user.first_name}: {message[:50]}...")`
|
|
- Example: `logger.info(f"Photo saved from {user.first_name}: {filepath}")`
|
|
|
|
- Bash: Uses `echo` for output, no structured logging
|
|
- Informational messages for user feedback
|
|
- Error messages sent to stdout (not stderr)
|
|
|
|
## Comments
|
|
|
|
**When to Comment:**
|
|
- Module-level docstrings at top of file (required for all scripts)
|
|
- Usage examples in module docstrings (e.g., `pve`, `pbs`, `kuma`)
|
|
- Inline comments for complex logic (e.g., in `pbs` script parsing hex timestamps)
|
|
- Comments on tricky regex patterns (e.g., `pbs` tasks parsing)
|
|
|
|
**Bash Comments:**
|
|
- Header comment with script name, purpose, and usage (lines 1-10)
|
|
- Inline comments before major sections (e.g., `# Datastore info`, `# Storage stats`)
|
|
- No comments in simple expressions
|
|
|
|
**Python Comments:**
|
|
- Header comment with purpose (module docstring)
|
|
- Sparse inline comments except for complex sections
|
|
- Example from `telegram/bot.py` line 71: `# Telegram has 4096 char limit per message`
|
|
- Example from `pve` line 70: `# Try as container first`
|
|
|
|
## Function Design
|
|
|
|
**Size:**
|
|
- Python: Functions are generally 10-50 lines
|
|
- Smaller functions for simple operations (e.g., `is_authorized()` is 2 lines)
|
|
- Larger functions for command handlers that do setup + API calls (e.g., `status()` is 40 lines)
|
|
|
|
- Bash: Functions are typically 20-80 lines
|
|
- Longer functions acceptable for self-contained operations like `cmd_status()` in `pbs`
|
|
|
|
**Parameters:**
|
|
- Python: Explicit parameters, typically 1-5 parameters per function
|
|
- Optional parameters with defaults (e.g., `timeout: int = 30`, `port=45876`)
|
|
- Type hints not used consistently (some functions have them, many don't)
|
|
|
|
- Bash: Parameters passed as positional arguments
|
|
- Some functions take zero parameters and rely on global variables
|
|
- Example: `ssh_pbs()` in `pbs` uses global `$PBS_HOST`
|
|
|
|
**Return Values:**
|
|
- Python: Functions return data (strings, dicts, lists) or None
|
|
- Command handlers often return nothing (implicitly None)
|
|
- Helper functions return computed values (e.g., `is_authorized()` returns bool)
|
|
|
|
- Bash: Functions print output directly, return exit codes
|
|
- No explicit return values beyond exit codes
|
|
- Output captured by caller with `$()`
|
|
|
|
## Module Design
|
|
|
|
**Exports:**
|
|
- Python: All functions are module-level, no explicit exports
|
|
- `if __name__ == "__main__":` pattern used in all scripts to guard main execution
|
|
- Example from `beszel` lines 101-152
|
|
|
|
- Bash: All functions are script-level, called via case statement
|
|
- Main dispatch logic at bottom of script
|
|
- Example from `dns` lines 29-106: `case "$1" in ... esac`
|
|
|
|
**Async/Await (Telegram Bot Only):**
|
|
- Python telegram bot uses `asyncio` and `async def` for all handlers
|
|
- All command handlers are async (e.g., `async def start()`)
|
|
- Use `await` for async operations (e.g., `await update.message.reply_text()`)
|
|
- Example from `telegram/bot.py` lines 81-94:
|
|
```python
|
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
"""Handle /start command - first contact with bot."""
|
|
user = update.effective_user
|
|
chat_id = update.effective_chat.id
|
|
# ... async operations with await
|
|
```
|
|
|
|
**File Structure:**
|
|
- Single-file modules: Most helpers are single files
|
|
- `telegram/bot.py`: Main bot implementation with all handlers
|
|
- `/bin/` scripts: Each script is self-contained with helper functions + main dispatch
|
|
|
|
## Data Structures
|
|
|
|
**JSON/Config Files:**
|
|
- Credentials files: Simple `KEY=value` format (no JSON)
|
|
- PBS task logging: Uses hex-encoded UPID format, parsed with regex
|
|
- Telegram bot: Saves messages to text files with timestamp prefix
|
|
- JSON output: Parsed with `python3 -c "import sys, json; ..."` in Bash scripts
|
|
|
|
**Error Response Patterns:**
|
|
- API calls: Check for `.get('status') == 'ok'` or similar
|
|
- Command execution: Check `returncode == 0`, capture stdout/stderr
|
|
- API clients: Let exceptions bubble up, caught at command handler level
|
|
|
|
## Conditionals and Flow Control
|
|
|
|
**Python:**
|
|
- if/elif/else chains for command dispatch
|
|
- Simple truthiness checks: `if not user_id:`, `if not alerts:`
|
|
- Example from `telegram/bot.py` line 86-100: Authorization check pattern
|
|
|
|
**Bash:**
|
|
- case/esac for command dispatch (preferred)
|
|
- if [[ ]] with regex matching for parsing
|
|
- Example from `pbs` lines 122-143: Complex regex with BASH_REMATCH array
|
|
|
|
## Security Patterns
|
|
|
|
**Credential Management:**
|
|
- Credentials stored in `~/.config/<service>/credentials` with restricted permissions (not enforced in code)
|
|
- Telegram token loaded from file, not environment
|
|
- Credentials never logged or printed
|
|
|
|
**Input Validation:**
|
|
- Bash: Basic validation with isalnum() check in `ping_host()` function
|
|
- Example: `if not host.replace('.', '').replace('-', '').isalnum():`
|
|
- Bash: Whitelist command names from case statements
|
|
- No SQL injection risk (no databases used directly)
|
|
|
|
**Shell Injection:**
|
|
- Bash scripts use quoted variables appropriately
|
|
- Some inline Python in Bash uses string interpolation (potential risk)
|
|
- Example from `dns` lines 31-37: `curl ... | python3 -c "..."` with variable interpolation
|
|
|
|
---
|
|
|
|
*Convention analysis: 2026-02-04*
|