Project Overview
PFP Checker is a Discord bot built with Rust that tracks profile pictures, usernames, and server icons. The codebase is designed for clarity, maintainability, and performance using modern Rust patterns and async/await.Technology Stack
- Serenity 0.12: Discord API library with async support
- SQLx 0.8: Async SQL with compile-time query verification
- Tokio 1.48: Async runtime for concurrent operations
- SQLite: Embedded database for data persistence
- Reqwest 0.12: HTTP client for image downloads and API calls
The bot is built with Rust 1.86+ and uses the 2021 edition language features.
Project Structure
The source code is organized into clear, focused modules:Core Components
main.rs - Application Entry Point
Themain.rs file (src/main.rs:368-387) contains:
- Application initialization: Config loading and database setup
- Discord client creation: Serenity client with event handlers
- Event handling: Command routing and interaction responses
Handler Structure
TheHandler struct implements Serenity’s EventHandler trait:
- Route incoming slash commands to appropriate handlers
- Handle button interactions for pagination
- Manage the automatic update scheduler
Event Handling Flow
interaction_create event
The
Handler::interaction_create method (src/main.rs:24-246) is called with the interaction data.Command execution
The command handler performs the requested operation (database query, API call, etc.).
Command Registration System
Commands are registered globally when the bot starts (src/main.rs:248-284):
register() function that returns a CreateCommand builder defining:
- Command name
- Description
- Parameters and their types
- Required permissions
Global commands can take up to 1 hour to propagate across all Discord servers. For faster development, consider using guild-specific commands.
Module Breakdown
Commands Module
Location:src/commands/
Each command is implemented as a separate module with:
register()function: Returns command definition for Discordrun()function: Async handler that executes the command logic- Helper functions: Pagination, formatting, data fetching
Command Categories
User Tracking Commands:monitor.rs: Add users to tracking databaseremovemonitor.rs: Remove users from trackingpfphistory.rs: Display paginated profile picture historyusernamehistory.rs: Display paginated username historystats.rs: Calculate and display statistics
monitorserver.rs: Start tracking server iconsremovemonitorserver.rs: Stop tracking server iconsserverpfphistory.rs: Display server icon history with paginationserverstats.rs: Server icon change statistics
ping.rs: Simple health check command
Database Module
Location:src/db/
Connection Management
File:src/db/connection.rs
The database module provides:
- Creates a SQLite connection pool (max 5 connections)
- Creates the database file if it doesn’t exist
- Runs all pending migrations from
migrations/ - Returns an
Arc-wrapped pool for sharing across threads
SQLx’s compile-time query verification ensures all database queries are valid at build time.
Utility Module
Location:src/util/
Automatic Update Scheduler
File:src/util/chron_update.rs
The scheduler runs on a 30-minute interval (src/main.rs:273-281):
Generic Update Logic:
The update system uses a generic helper function (
src/util/chron_update.rs:14-236) that handles both users and servers:
- Parameterized over entity type (user vs server)
- Reusable checksum and upload logic
- Efficient batching of database operations
Configuration Management
File:src/util/config.rs
The Config struct loads environment variables:
.env file using the dotenvy crate.
Data Structures
File:src/util/objects.rs
Shared structs used across modules:
External Integrations
Directory:src/util/external/
ImgBB Integration (imgbb.rs):
- Uploads images to ImgBB hosting service
- Returns permanent URLs for historical records
- Handles multipart form data and API authentication
Design Patterns
Async/Await Architecture
The entire application is built on async Rust:- Tokio runtime: Multi-threaded async executor
- Async database: Non-blocking SQLx queries
- Async Discord API: Serenity uses async/await
- Concurrent updates: Multiple entities updated in parallel
Shared State with Arc
The database pool is wrapped inArc<T> for safe sharing across async tasks:
- Multiple concurrent command handlers accessing the database
- Background update task sharing the same connection pool
- Zero-cost reference counting for thread-safe sharing
Error Handling
The codebase uses Rust’sResult<T, E> type for error handling:
- Functions return
Resultfor operations that can fail - SQLx operations return
Result<T, sqlx::Error> - Serenity operations return
Result<T, serenity::Error> - Errors are propagated with
?operator or handled locally
Compile-Time SQL Verification
SQLx verifies queries at compile time:- Catches SQL errors before runtime
- Provides type-safe query results
- Prevents SQL injection
- Auto-generates result types from schema
Component Interaction Pattern
Paginated commands use Discord’s component system (src/main.rs:125-243):
- Command sends initial embed with navigation buttons
- User clicks “Next” or “Back” button
- Component interaction received with custom ID
- Custom ID parsed to determine page and direction
- New embed generated and message updated
- Process repeats for subsequent pages
{command}_{direction}_{current_page}_{entity_id}
Example: pfphistory_next_2_123456789
Key Dependencies
FromCargo.toml:
Core Dependencies
-
serenity 0.12: Discord API library
- Features:
client,gateway,rustls_backend,model,collector - Provides async Discord bot framework
- Features:
-
tokio 1.48: Async runtime
- Features:
macros,rt-multi-thread - Enables async/await and concurrent execution
- Features:
-
sqlx 0.8: Database toolkit
- Features:
runtime-tokio-rustls,sqlite,macros,migrate - Compile-time verified queries
- Features:
Utility Dependencies
- chrono 0.4: Date and time handling
- reqwest 0.12: HTTP client with multipart support
- sha1 0.10: SHA-1 hashing for checksums
- dotenvy 0.15: .env file parsing
- base64 0.22: Base64 encoding/decoding
- serde 1.0: Serialization framework
- serde_json 1.0: JSON parsing
Development Dependencies
- tempfile 3.23: Temporary file creation for tests
Performance Considerations
Database Connection Pooling
The application uses a connection pool (src/db/connection.rs:6-14):
- Reuses connections instead of creating new ones
- Limits concurrent database operations
- Prevents resource exhaustion
Batch Operations
The update scheduler processes entities in batches:- Fetches all monitored IDs in single query
- Updates multiple entities in parallel
- Reduces database round-trips
Efficient Image Handling
Images are handled efficiently:- Downloaded once and checksummed
- Only uploaded to ImgBB if new
- Reuses existing URLs for duplicate images
- SHA-1 checksums prevent re-uploading identical images
Testing Strategy
The project uses Rust’s built-in testing framework:- Unit tests in the same file as the code they test
- Integration tests in
tests/directory (if present) - Database tests use temporary SQLite files
Next Steps
Database Schema
Learn about the database design and migrations
Contributing Guide
Start contributing to the project
Setup Guide
Set up your development environment
Commands Reference
Explore available bot commands