-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
See also: API-Reference | Module-Development | Services-Documentation
- Design Principles
- Component Overview
- Data Flow
- Module System
- Service System
- Database Architecture
- Lifecycle Management
The framework is built around a modular architecture where functionality is organized into independent, reusable modules. Each module can be enabled or disabled per guild, allowing for flexible bot configurations.
Optional dependencies (like database or Redis) are handled gracefully. The framework functions even if these services are unavailable, with appropriate fallbacks and warnings.
Services are managed through a service container, providing dependency injection and making components testable and loosely coupled.
Full type hints throughout the codebase enable better IDE support, static analysis, and self-documenting code.
Built-in support for production concerns:
- Health checks
- Graceful shutdown
- Connection pooling
- Error handling
- Audit logging
┌─────────────────────────────────────────────────────────┐
│ Application Layer │
│ (User's Bot Code, Custom Modules, Runner Bot) │
└────────────────────┬────────────────────────────────────┘
│
┌────────────────────▼────────────────────────────────────┐
│ WispBot │
│ - Command handling │
│ - Event processing │
│ - Module lifecycle │
└────────────────────┬────────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
┌───────▼────────┐ ┌─────────▼──────────┐
│ ModuleRegistry │ │ ServiceContainer │
│ - Registration│ │ - Service mgmt │
│ - Discovery │ │ - DI │
│ - Loading │ │ - Lifecycle │
└────────────────┘ └─────────┬──────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌───────▼───┐ ┌──────▼────┐ ┌─────▼─────┐
│ Database │ │ Cache │ │ Scheduler │
│ Service │ │ Service │ │ Service │
└───────────┘ └───────────┘ └───────────┘
User Command
│
▼
WispBot.on_app_command_completion()
│
▼
Module Command Handler
│
▼
BotContext (services, config, guild_data)
│
▼
Service Layer (if needed)
│
▼
Response to User
Bot Startup
│
▼
LifecycleManager.startup()
│
▼
ServiceContainer.startup_all()
│
▼
ModuleRegistry.load_enabled_modules()
│
▼
Module.setup() for each enabled module
│
▼
Bot Ready
Each module is a Python class that extends Module:
class MyModule(Module):
@property
def name(self) -> str:
return "my_module"
async def setup(self, bot, ctx):
# Register commands, event handlers, etc.
pass-
Registration: Module is registered with
ModuleRegistry - Discovery: Modules can be auto-discovered from packages
- Dependency Resolution: Dependencies are resolved before loading
-
Loading:
setup()is called for enabled modules - Execution: Module handles commands/events
-
Unloading:
teardown()is called when module is disabled
Modules can be enabled or disabled per guild using feature flags:
- Stored in database (if available) or memory
- Checked before module loading
- Can be toggled via
/modules enable/disablecommands
Services are managed through a ServiceContainer that provides:
- Registration: Services register themselves with a name
- Retrieval: Services can be retrieved by name or type
-
Lifecycle: Services have
startup()andshutdown()methods - Dependency Management: Services can depend on other services
- HealthService: Health checks for all services
- CacheService: In-memory caching
- MetricsService: Counter and timing metrics
- SchedulerService: Periodic task scheduling
- AuditService: Audit logging
- WebhookLoggerService: Rate-limited webhook logging
-
DatabaseService: SQLAlchemy async database (requires
[db]extra) -
RedisCacheService: Redis-backed caching (requires
[redis]extra)
class MyService(BaseService):
async def startup(self):
# Initialize service
self._mark_initialized()
async def shutdown(self):
# Cleanup resources
passThe framework provides three base models:
Stores per-guild configuration:
-
guild_id: Discord guild ID (primary key) -
welcome_channel_id: Welcome channel ID - Custom configuration fields
Tracks module enable/disable state per guild:
-
guild_id: Discord guild ID -
module_name: Module name -
enabled: Whether module is enabled
Generic key-value storage for per-guild data:
-
guild_id: Discord guild ID -
key: Data key -
value: JSON-serialized value -
module_name: Optional module namespace
The DatabaseService provides:
- Async SQLAlchemy engine and sessions
- Connection pooling
- Migration support via Alembic
- Graceful degradation (stub if unavailable)
GuildDataService provides a high-level interface for per-guild data:
- Automatic serialization/deserialization
- Module namespacing
- Database-backed with in-memory fallback
- Load configuration from environment
- Set up logging
- Create service container
- Register and start services
- Create feature flags
- Create module registry
- Register/discover modules
- Create bot context
- Initialize bot
- Load modules for each guild
- Start bot
- Stop accepting new commands
- Unload modules (call
teardown()) - Shutdown services (in reverse order)
- Close database connections
- Clean up resources
- Services that fail to start prevent bot startup
- Module loading errors are logged but don't stop bot
- Command errors are caught and displayed to users
- Global error handlers provide user-friendly messages
from wisp_framework.services.base import BaseService
class MyService(BaseService):
async def startup(self):
# Initialize
pass
async def shutdown(self):
# Cleanup
passSee Module-Development.
- Override
WispBotmethods - Add custom event handlers
- Extend
BotContextwith custom services - Create utility functions in
utils/
- Use Type Hints: Always type hint function parameters and return values
- Handle Errors: Use try/except blocks and provide meaningful error messages
- Log Appropriately: Use appropriate log levels (DEBUG, INFO, WARNING, ERROR)
- Test Services: Check if services are available before using them
-
Namespace Data: Use
module_nameparameter inguild_datacalls -
Clean Up: Implement
teardown()if your module needs cleanup - Document Code: Add docstrings to classes and functions
- Follow Patterns: Use existing modules as examples
- Connection Pooling: Database connections are pooled
- Lazy Loading: Modules are loaded only when needed
- Caching: Use cache service for frequently accessed data
- Async Operations: All I/O operations are async
- Resource Limits: Docker deployments include resource limits
- Environment Variables: Sensitive data in environment variables
- Input Validation: Validate user input in commands
- Permission Checks: Use decorators for permission checks
- Audit Logging: All actions are logged via audit service
- Database Passwords: Must be changed from defaults in production