Overview
Jill Stingray uses PostgreSQL as its primary database for persistent storage and Supabase for file storage (primarily role icons). All database operations use connection pooling for performance.Database Connection
The PostgreSQL connection is established inutils/db.js:3-6:
Environment Variables
Schema Initialization
Tables are created automatically on bot startup by theinit() function in utils/db.js:8-133:
Database Schema
Guild Settings
Stores per-guild configuration.guild_id- Discord guild (server) IDadmin_role_id- Role that can manage bot settingslog_channel_id- Channel for audit logscommand_rules- JSON object with command-specific permissions
Activity Tracking
Tracks user message activity across guilds.guild_id+user_id- Composite primary keychars- Total characters sentmsg_count- Total message count
events/activityTracker.js:18-28
Used by: /regulars, /pulse, activity statistics
Analytics (Daily)
Stores hourly activity heatmaps and channel statistics./pulse dashboard
Emoji Tracking
Tracks custom emoji usage statistics.emoji_id- Discord emoji IDname- Emoji name (updates if renamed)is_animated- Whether emoji is animatedcount- Total usage countlast_used- Timestamp of last use
events/emojiTracker.js:3-21
Used by: /emojistats
Custom Roles
Tracks user-created custom roles.- One custom role per user per guild
- Role is deleted from Discord when user leaves
/custom create, /custom edit, /custom prune
Pending Custom Actions
Temporary storage for confirmation dialogs.Drinks Tracking
Tracks VA-11 HALL-A themed drink orders./mix, /menu, /tab
Active Mutes
Tracks temporary self-mutes.events/muteLoop.js (checks every 60 seconds)
Used by: /selfmute
Dangeru Posts
Anonymous imageboard system./dangeru (disabled by default)
Alias Logging
Tracks username and nickname changes.events/aliasLog.js
Used by: /identity
Trigger System
Custom auto-response keywords.- One keyword per guild (case-insensitive by default)
- Supports both text and image URLs
/trigger add, /trigger remove, events/triggerHandler.js
Query Patterns
Insert or Update (UPSERT)
Most tables useON CONFLICT ... DO UPDATE for atomic upserts:
Querying with Exported Function
Thedb.js module exports a query function:
Accessing the Pool Directly
Supabase Integration
Supabase is used for file storage, primarily for custom role icons.Client Setup
utils/supabase.js:1-14:
Environment Variables
SUPABASE_SERVICE_KEY for server-side operations to bypass Row Level Security (RLS).
Usage Example
Database Exports
utils/db.js:137-141:
Best Practices
Always Use Parameterized Queries
Handle Errors Gracefully
Use Transactions for Multi-Step Operations
Migration Strategy
The bot uses “run-on-startup” migrations viaIF NOT EXISTS. For schema changes:
- Add new
CREATE TABLE IF NOT EXISTSorALTER TABLEtoinit() - Changes apply automatically on next restart
- No manual migration files needed
Performance Considerations
- Connection pooling is enabled by default
- Guild settings are cached for 60 seconds to reduce queries
- Activity tracking uses upserts to avoid race conditions
- JSONB columns store complex data without joins
- Composite primary keys optimize multi-guild queries
Next Steps
- Architecture - Understand the bot structure
- Deployment - Set up production database