Document guild settings architecture, service layer, admin commands, API endpoints, and migration strategy from file-based config.
5.8 KiB
Guild Settings System
The guild settings system enables per-guild configuration stored in the database, eliminating environment-specific config files and enabling runtime updates without redeployment.
Overview
Guild settings allow you to:
- Store per-guild configuration in the database
- Update settings at runtime without code changes
- Support multiple guilds with different configurations
- Maintain backward compatibility with file-based config
Architecture
Database Schema
guild_settings table:
| Column | Type | Description |
|---|---|---|
guild_id |
bigint | Primary key (Discord guild ID) |
student_role_id |
bigint | Student role ID |
visitor_role_id |
bigint | Visitor role ID |
color_role_ids |
jsonb | Array of color role IDs |
welcome_channel_id |
bigint | Welcome message channel |
welcome_message |
text | Custom welcome message |
feedback_channel_id |
bigint | Feedback channel |
terminal_channel_id |
bigint | Terminal channel |
terminal_message_id |
bigint | Terminal message ID |
moderation_log_channel_id |
bigint | Moderation log channel |
moderation_dm_on_warn |
jsonb | DM user on warn |
moderation_auto_timeout_threshold |
jsonb | Auto timeout after N warnings |
feature_overrides |
jsonb | Feature flag overrides |
created_at |
timestamp | Creation time |
updated_at |
timestamp | Last update time |
Service Layer
The guildSettingsService (shared/modules/guild-settings/guild-settings.service.ts) provides:
// Get settings for a guild (returns null if not configured)
await guildSettingsService.getSettings(guildId);
// Create or update settings
await guildSettingsService.upsertSettings({
guildId: "123456789",
studentRoleId: "987654321",
visitorRoleId: "111222333",
});
// Update a single setting
await guildSettingsService.updateSetting(guildId, "welcomeChannel", "456789123");
// Delete all settings for a guild
await guildSettingsService.deleteSettings(guildId);
// Color role helpers
await guildSettingsService.addColorRole(guildId, roleId);
await guildSettingsService.removeColorRole(guildId, roleId);
Usage
Getting Guild Configuration
Use getGuildConfig() instead of direct config imports for guild-specific settings:
import { getGuildConfig } from "@shared/lib/config";
// In a command or interaction
const guildConfig = await getGuildConfig(interaction.guildId);
// Access settings
const studentRole = guildConfig.studentRole;
const welcomeChannel = guildConfig.welcomeChannelId;
Fallback Behavior
getGuildConfig() returns settings in this order:
- Database settings (if guild is configured in DB)
- File config fallback (during migration period)
This ensures backward compatibility while migrating from file-based config.
Cache Invalidation
Settings are cached for 60 seconds. After updating settings, invalidate the cache:
import { invalidateGuildConfigCache } from "@shared/lib/config";
await guildSettingsService.upsertSettings({ guildId, ...settings });
invalidateGuildConfigCache(guildId);
Admin Commands
The /settings command (Administrator only) provides full management:
Subcommands
| Command | Description |
|---|---|
/settings show |
Display current guild settings |
/settings set <key> [value] |
Update a setting |
/settings reset <key> |
Reset a setting to default |
/settings colors <action> [role] |
Manage color roles |
Settable Keys
| Key | Type | Description |
|---|---|---|
studentRole |
Role | Role for enrolled students |
visitorRole |
Role | Role for visitors |
welcomeChannel |
Channel | Channel for welcome messages |
welcomeMessage |
Text | Custom welcome message |
feedbackChannel |
Channel | Channel for feedback |
terminalChannel |
Channel | Terminal channel |
terminalMessage |
Text | Terminal message ID |
moderationLogChannel |
Channel | Moderation log channel |
moderationDmOnWarn |
Boolean | DM users on warn |
moderationAutoTimeoutThreshold |
Number | Auto timeout threshold |
API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/guilds/:guildId/settings |
Get guild settings |
| PUT | /api/guilds/:guildId/settings |
Create/replace settings |
| PATCH | /api/guilds/:guildId/settings |
Partial update |
| DELETE | /api/guilds/:guildId/settings |
Delete settings |
Migration
To migrate existing config.json settings to the database:
bun run db:migrate-config
This will:
- Read values from
config.json - Create a database record for
DISCORD_GUILD_ID - Store all guild-specific settings
Migration Strategy for Code
Update code references incrementally:
// Before
import { config } from "@shared/lib/config";
const role = config.studentRole;
// After
import { getGuildConfig } from "@shared/lib/config";
const guildConfig = await getGuildConfig(guildId);
const role = guildConfig.studentRole;
Files to Update
Files using guild-specific config that should be updated:
bot/events/guildMemberAdd.tsbot/modules/user/enrollment.interaction.tsbot/modules/feedback/feedback.interaction.tsbot/commands/feedback/feedback.tsbot/commands/inventory/use.tsbot/commands/admin/create_color.tsshared/modules/moderation/moderation.service.tsshared/modules/terminal/terminal.service.ts
Implementation Files
| File | Purpose |
|---|---|
shared/db/schema/guild-settings.ts |
Database schema |
shared/modules/guild-settings/guild-settings.service.ts |
Service layer |
shared/lib/config.ts |
Config loader with getGuildConfig() |
bot/commands/admin/settings.ts |
Admin command |
web/src/routes/guild-settings.routes.ts |
API routes |
shared/scripts/migrate-config-to-db.ts |
Migration script |