Add comprehensive Support cog that automatically disables and replaces Red's built-in contact and dm commands with enhanced, feature-rich versions. Features: - Command override mechanism (disables core commands on load, restores on unload) - Multi-category support system with dedicated channels - Custom emojis, greetings, and descriptions per category - User blocking/unblocking system - Comprehensive logging to dedicated log channel - Anonymous mode for staff replies - Enhanced embed formatting - Category selection dropdown UI - Staff control buttons (Close Thread, Block User) - Reply modal for staff responses - Background thread cleanup task Commands: - [p]contact - Enhanced contact command (DM only) - [p]dm - Enhanced DM command with logging - [p]supportset - 15 admin configuration commands Documentation: - README.md - Full documentation (7.9K) - QUICKSTART.md - Quick setup guide (2.5K) - EXAMPLES.md - Configuration examples (8.1K) - TECHNICAL.md - Technical docs (8.6K) - CHANGELOG.md - Version history - test_override.py - Override mechanism reference Technical: - 625 lines of code - 33 async functions - Full type hints - Config-based data storage - Red 3.5.0+ compatible - Follows NexusCore patterns Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
8.5 KiB
Support Cog - Technical Documentation
Command Override Mechanism
How It Works
This cog replaces Red-DiscordBot's built-in contact and dm commands by disabling them when the cog loads and re-enabling them when it unloads.
Implementation Details
On Load (cog_load)
async def _disable_core_commands(self):
"""Disable Red's built-in contact and dm commands."""
core_cog = self.bot.get_cog("Core")
if not core_cog:
return
commands_to_disable = ["contact", "dm"]
for cmd_name in commands_to_disable:
cmd = self.bot.get_command(cmd_name)
if cmd and cmd.cog_name == "Core":
cmd.enabled = False
self._disabled_core_commands.append(cmd_name)
What happens:
- Gets the Core cog from Red
- Finds the
contactanddmcommands - Verifies they belong to Core (not another cog)
- Sets
enabled = Falseon each command - Tracks which commands were disabled
On Unload (cog_unload)
async def _enable_core_commands(self):
"""Re-enable Red's core commands when this cog unloads."""
for cmd_name in self._disabled_core_commands:
cmd = self.bot.get_command(cmd_name)
if cmd:
cmd.enabled = True
self._disabled_core_commands.clear()
What happens:
- Iterates through disabled commands
- Finds each command
- Sets
enabled = Trueto restore them - Clears the tracking list
Command Registration
The cog registers its own versions of these commands with the same names:
@commands.command(name="contact")
@commands.dm_only()
async def contact(self, ctx: commands.Context, *, message: str):
"""Enhanced contact command"""
# ... implementation
@commands.command(name="dm")
@commands.guild_only()
@commands.admin_or_permissions(manage_guild=True)
async def dm_command(self, ctx: commands.Context, user: discord.User, *, message: str):
"""Enhanced dm command"""
# ... implementation
Why This Approach?
Pros:
- ✅ Clean override without modifying Red's core
- ✅ Automatic restoration when unloaded
- ✅ No conflicts with other cogs
- ✅ Preserves command names users are familiar with
- ✅ Works with Red's permission system
Alternatives considered:
- ❌ Command name collision (Red would error)
- ❌ Monkey-patching (fragile and unsafe)
- ❌ Different command names (breaks user familiarity)
Architecture
Data Storage (Config)
Guild Settings
DEFAULT_GUILD = {
"enabled": False,
"categories": {}, # name -> {channel_id, description, emoji, roles, greeting}
"default_category": None,
"log_channel": None,
"mod_roles": [],
"reply_with_embed": True,
"show_author_info": True,
"anonymous_mode": False,
"thread_mode": False,
"dm_on_reply": True,
"active_threads": {}, # channel_id -> {user_id, category, started_at, message_count}
"thread_timeout": 3600,
"custom_greeting": "",
"require_category": False,
}
User Settings
DEFAULT_USER = {
"blocked": False,
"preference_category": None,
"last_contact": 0,
}
Message Flow
User → Staff
User (DM) → [p]contact message
↓
Check blocked status
↓
Select category (if multiple)
↓
Get category channel
↓
Send to staff channel (embed with user info)
↓
Log to log channel (if configured)
↓
Send confirmation to user
Staff → User
Staff (Guild) → [p]dm @user message
↓
Check permissions
↓
Format message (embed/plain, anonymous/named)
↓
Send DM to user
↓
Log to log channel (if configured)
↓
Confirm to staff
UI Components
CategorySelectView
- Discord UI Select dropdown
- Shows up to 25 categories
- Includes emoji, name, and description
- 180 second timeout
- Returns selected category
SupportControlView
- Persistent view (timeout=None)
- Custom IDs for persistence across restarts
- Buttons: Close Thread, Block User
- Staff-only controls
ReplyModal
- Text input for staff replies
- Paragraph style (multi-line)
- 1900 character limit
- Sends reply on submit
Background Tasks
Thread Cleanup Loop
async def _thread_cleanup_loop(self):
"""Background task to clean up inactive threads."""
# Runs every 5 minutes
# Checks thread_timeout setting
# Removes stale threads from active_threads
Purpose: Clean up inactive support threads based on guild settings
Interval: 5 minutes
Action: Removes threads older than thread_timeout seconds
Security Considerations
Permission Checks
-
contact command:
- DM-only (
@commands.dm_only()) - Checks user blocked status
- Validates guild membership
- DM-only (
-
dm command:
- Guild-only (
@commands.guild_only()) - Requires Manage Guild permission
- Validates target user exists
- Guild-only (
-
supportset commands:
- Guild-only
- Requires Manage Guild permission
- Validates input data
Data Privacy
- User IDs stored (not usernames - they can change)
- Message content not stored (only transmitted)
- Last contact timestamp for rate limiting
- Blocked status for abuse prevention
Error Handling
discord.Forbidden- User has DMs disableddiscord.HTTPException- Network/API errors- Missing channels - Graceful degradation
- Invalid config - Falls back to defaults
Performance
Optimizations
-
Config Caching:
- Red's Config system caches values
- Only writes on changes
-
Async Operations:
- All I/O is async
- Background tasks don't block
-
View Persistence:
- Views registered once at load
- Reused across all interactions
-
Minimal Database Writes:
- Bulk operations where possible
- Async context managers for safety
Scalability
- Small servers (<100 members): No issues
- Medium servers (100-1000 members): Optimized for this
- Large servers (1000+ members): Thread cleanup may need tuning
Recommendations for large servers:
- Increase
thread_timeoutto reduce cleanup frequency - Use separate log channel (not same as support channel)
- Consider rate limiting (future feature)
Compatibility
Red-DiscordBot Versions
- Minimum: 3.5.0
- Tested on: 3.5.0+
- Max: Latest
Python Versions
- Minimum: 3.8
- Recommended: 3.9+
Discord.py Version
- Matches Red's discord.py version
- Uses discord.py 2.x features (UI components)
Debugging
Enable Debug Logging
import logging
logging.getLogger("red.evecogs.support").setLevel(logging.DEBUG)
Common Issues
"Commands not found after loading"
- Check if Core cog is loaded
- Verify
[p]load supportsucceeded - Try
[p]reload support
"Core commands still work"
- Check cog load order
- Ensure no errors in console during load
- Try
[p]unload corethen[p]load coreand[p]load support
"Messages not showing up"
- Verify channel exists:
[p]supportset list - Check bot permissions in target channel
- Ensure category is properly configured
"Can't send DMs"
- User likely has DMs disabled
- Check bot can DM users (not always possible)
- Verify user hasn't blocked the bot
Testing
Manual Testing Checklist
- Load cog successfully
- Core commands disabled
[p]contactworks in DMs- Category selection appears (if multiple categories)
- Messages reach staff channel
- User info displays correctly
[p]dmsends to users- Logging works (if configured)
- Blocking works
- Unload restores core commands
Test Cases
-
Single Category:
- Create one category
- Contact should go directly there
- No category selection shown
-
Multiple Categories:
- Create 2+ categories
- Contact should show selection dropdown
- Messages route to correct channels
-
Blocked User:
- Block a user
- User cannot contact
- Error message shown
-
Anonymous Mode:
- Enable anonymous mode
- Staff replies hide names
- Shows "Server Name Staff" instead
Future Enhancements
Potential features for future versions:
- Rate limiting (X messages per hour)
- Priority queues
- Auto-responses
- Ticket numbers/IDs
- Thread persistence
- Multi-server support (user chooses)
- Attachment support
- Transcript generation
- Staff assignments
- SLA tracking
- Analytics dashboard
Contributing
When contributing to this cog:
- Maintain async/await patterns
- Use type hints
- Follow existing code style
- Test with multiple guilds
- Ensure commands still override properly
- Update documentation
License
Follows EveCogs repository license.