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>
370 lines
8.5 KiB
Markdown
370 lines
8.5 KiB
Markdown
# 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`)
|
|
|
|
```python
|
|
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:**
|
|
1. Gets the Core cog from Red
|
|
2. Finds the `contact` and `dm` commands
|
|
3. Verifies they belong to Core (not another cog)
|
|
4. Sets `enabled = False` on each command
|
|
5. Tracks which commands were disabled
|
|
|
|
#### On Unload (`cog_unload`)
|
|
|
|
```python
|
|
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:**
|
|
1. Iterates through disabled commands
|
|
2. Finds each command
|
|
3. Sets `enabled = True` to restore them
|
|
4. Clears the tracking list
|
|
|
|
### Command Registration
|
|
|
|
The cog registers its own versions of these commands with the same names:
|
|
|
|
```python
|
|
@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
|
|
```python
|
|
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
|
|
```python
|
|
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
|
|
```python
|
|
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
|
|
|
|
1. **contact command:**
|
|
- DM-only (`@commands.dm_only()`)
|
|
- Checks user blocked status
|
|
- Validates guild membership
|
|
|
|
2. **dm command:**
|
|
- Guild-only (`@commands.guild_only()`)
|
|
- Requires Manage Guild permission
|
|
- Validates target user exists
|
|
|
|
3. **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 disabled
|
|
- `discord.HTTPException` - Network/API errors
|
|
- Missing channels - Graceful degradation
|
|
- Invalid config - Falls back to defaults
|
|
|
|
## Performance
|
|
|
|
### Optimizations
|
|
|
|
1. **Config Caching:**
|
|
- Red's Config system caches values
|
|
- Only writes on changes
|
|
|
|
2. **Async Operations:**
|
|
- All I/O is async
|
|
- Background tasks don't block
|
|
|
|
3. **View Persistence:**
|
|
- Views registered once at load
|
|
- Reused across all interactions
|
|
|
|
4. **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_timeout` to 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
|
|
|
|
```python
|
|
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 support` succeeded
|
|
- Try `[p]reload support`
|
|
|
|
**"Core commands still work"**
|
|
- Check cog load order
|
|
- Ensure no errors in console during load
|
|
- Try `[p]unload core` then `[p]load core` and `[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]contact` works in DMs
|
|
- [ ] Category selection appears (if multiple categories)
|
|
- [ ] Messages reach staff channel
|
|
- [ ] User info displays correctly
|
|
- [ ] `[p]dm` sends to users
|
|
- [ ] Logging works (if configured)
|
|
- [ ] Blocking works
|
|
- [ ] Unload restores core commands
|
|
|
|
### Test Cases
|
|
|
|
1. **Single Category:**
|
|
- Create one category
|
|
- Contact should go directly there
|
|
- No category selection shown
|
|
|
|
2. **Multiple Categories:**
|
|
- Create 2+ categories
|
|
- Contact should show selection dropdown
|
|
- Messages route to correct channels
|
|
|
|
3. **Blocked User:**
|
|
- Block a user
|
|
- User cannot contact
|
|
- Error message shown
|
|
|
|
4. **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:
|
|
|
|
1. Maintain async/await patterns
|
|
2. Use type hints
|
|
3. Follow existing code style
|
|
4. Test with multiple guilds
|
|
5. Ensure commands still override properly
|
|
6. Update documentation
|
|
|
|
## License
|
|
|
|
Follows EveCogs repository license.
|