A feature-rich Discord bot for gaming communities. Provides information about server statuses, game recipes, moderation tools, and community features.
Current Version: 1.7.0 | Framework: .NET 10.0 | Discord.Net: v3.18.0
- Ashes of Creation - Craft recipes, items, profession levels, and item search
- Star Citizen - Server status and component health (🚀 enabled)
- Path of Exile - Server status and component health (⚔️ enabled)
- Return of Reckoning - Server status and player counts (enabled)
Each module can be independently enabled/disabled via configuration.
/about- Bot information (version, modules, commands, uptime)/help- Command reference/ping- Latency check/serverinfo- Server information/userinfo- User profile information
/warn- Issue warnings/warnings- Check user warnings/ban- Ban users/kick- Kick users/mute//unmute- Mute/unmute users/clear- Clear messages/purgeuser- Remove all messages from a user/lock//unlock- Lock/unlock channels/slowmode- Enable channel slowmode/singlemessage enable/disable/reset-user/list- Manage single-message-per-user channel enforcement (requires Manage Channels)
All moderation actions are logged as rich embeds to a Discord forum channel. Each embed shows the action type, the target user, the moderator, and the reason. Configure via the ModerationLog root-level config section.
Detects users who send identical messages across multiple channels within a configurable time window. When triggered, a spam alert is posted to the moderation log forum channel with Ban and Dismiss buttons for moderators. Configure via the CrossChannelSpam root-level config section.
Detects messages that are mostly uppercase, deletes them, warns the author via a self-deleting channel message and a DM, records a strike toward the same 3-strike /warn counter, and logs the incident to the user's moderation log thread. Configure via the AllCapsModeration root-level config section.
Note: Cross-Channel Spam Detection and All-Caps Auto-Moderation both require the Message Content privileged intent to be enabled in the Discord Developer Portal. Restart the bot after enabling it — no token refresh is required.
See DEVELOPMENT.md for detailed development guidelines, including:
- Setup instructions
- Version bumping workflow
- Changelog maintenance
- Pre-commit hook validation
- Adding new commands
For AI Agents (GitHub Copilot, etc.):
- Quick Reference: .github/copilot-instructions.md - Essential guidelines (auto-loaded by GitHub Copilot)
- Comprehensive Guide: AI_INSTRUCTIONS.md - Complete development patterns, error handling, and workflows
Quick Setup:
bash setup-hooks.shConfiguration is managed through a hierarchy of sources, with later sources overriding earlier ones:
appsettings.json(base configuration)appsettings.{Environment}.json(environment-specific overrides)- Environment variables (with
PANDABOT_prefix) - User secrets (development only)
Set environment variables with the PANDABOT_ prefix using double underscores (__) for nested properties:
# Linux/Mac
export PANDABOT_Discord__Token="your_discord_bot_token_here"
export PANDABOT_Discord__Prefix="!"
export PANDABOT_Discord__GuildId=1044558967545806940
export PANDABOT_Discord__AllowPrefixCommands=false
# Windows PowerShell
$env:PANDABOT_Discord__Token="your_discord_bot_token_here"
$env:PANDABOT_Discord__Prefix="!"
$env:PANDABOT_Discord__GuildId=1044558967545806940
$env:PANDABOT_Discord__AllowPrefixCommands=false
# Windows CMD
set PANDABOT_Discord__Token=your_discord_bot_token_here
set PANDABOT_Discord__Prefix=!
set PANDABOT_Discord__GuildId=1044558967545806940
set PANDABOT_Discord__AllowPrefixCommands=false# Linux/Mac
export PANDABOT_Discord__Heartbeat__Enabled=true
export PANDABOT_Discord__Heartbeat__PushUrl="https://kuma.example.com/api/push/xxxxx"
export PANDABOT_Discord__Heartbeat__IntervalSeconds=60
export PANDABOT_Discord__Heartbeat__StartupDelaySeconds=15
export PANDABOT_Discord__Heartbeat__TimeoutSeconds=10
# Windows PowerShell
$env:PANDABOT_Discord__Heartbeat__Enabled="true"
$env:PANDABOT_Discord__Heartbeat__PushUrl="https://kuma.example.com/api/push/xxxxx"
$env:PANDABOT_Discord__Heartbeat__IntervalSeconds="60"
$env:PANDABOT_Discord__Heartbeat__StartupDelaySeconds="15"
$env:PANDABOT_Discord__Heartbeat__TimeoutSeconds="10"
# Windows CMD
set PANDABOT_Discord__Heartbeat__Enabled=true
set PANDABOT_Discord__Heartbeat__PushUrl=https://kuma.example.com/api/push/xxxxx
set PANDABOT_Discord__Heartbeat__IntervalSeconds=60
set PANDABOT_Discord__Heartbeat__StartupDelaySeconds=15
set PANDABOT_Discord__Heartbeat__TimeoutSeconds=10# Linux/Mac
export PANDABOT_ConnectionStrings__DefaultConnection="Data Source=pandabot.db"
# Windows PowerShell
$env:PANDABOT_ConnectionStrings__DefaultConnection="Data Source=pandabot.db"
# Windows CMD
set PANDABOT_ConnectionStrings__DefaultConnection=Data Source=pandabot.db# Linux/Mac
export PANDABOT_AshesForge__CacheExpirationHours=24
export PANDABOT_AshesForge__EnableImageCaching=true
# Windows PowerShell
$env:PANDABOT_AshesForge__CacheExpirationHours=24
$env:PANDABOT_AshesForge__EnableImageCaching=true
# Windows CMD
set PANDABOT_AshesForge__CacheExpirationHours=24
set PANDABOT_AshesForge__EnableImageCaching=true# Linux/Mac
export PANDABOT_ModerationLog__ForumChannelId=1234567890
export PANDABOT_ModerationLog__ModeratorRoleId=1234567890
# Windows PowerShell
$env:PANDABOT_ModerationLog__ForumChannelId="1234567890"
$env:PANDABOT_ModerationLog__ModeratorRoleId="1234567890"
# Windows CMD
set PANDABOT_ModerationLog__ForumChannelId=1234567890
set PANDABOT_ModerationLog__ModeratorRoleId=1234567890# Linux/Mac
export PANDABOT_CrossChannelSpam__Enabled=false
export PANDABOT_CrossChannelSpam__TimeWindowSeconds=30
export PANDABOT_CrossChannelSpam__MinimumChannelCount=3
export PANDABOT_CrossChannelSpam__DeleteMessages=true
export PANDABOT_CrossChannelSpam__TimeoutOnDetection=true
# Windows PowerShell
$env:PANDABOT_CrossChannelSpam__Enabled="false"
$env:PANDABOT_CrossChannelSpam__TimeWindowSeconds="30"
$env:PANDABOT_CrossChannelSpam__MinimumChannelCount="3"
$env:PANDABOT_CrossChannelSpam__DeleteMessages="true"
$env:PANDABOT_CrossChannelSpam__TimeoutOnDetection="true"
# Windows CMD
set PANDABOT_CrossChannelSpam__Enabled=false
set PANDABOT_CrossChannelSpam__TimeWindowSeconds=30
set PANDABOT_CrossChannelSpam__MinimumChannelCount=3
set PANDABOT_CrossChannelSpam__DeleteMessages=true
set PANDABOT_CrossChannelSpam__TimeoutOnDetection=true# Linux/Mac
export PANDABOT_AllCapsModeration__Enabled=true
export PANDABOT_AllCapsModeration__DeleteMessage=true
export PANDABOT_AllCapsModeration__MinLetters=8
export PANDABOT_AllCapsModeration__MinUppercaseRatio=0.8
export PANDABOT_AllCapsModeration__EnableLengthScaling=true
export PANDABOT_AllCapsModeration__UppercaseRatioDropPerLetter=0.01
export PANDABOT_AllCapsModeration__MinScaledUppercaseRatio=0.4
export PANDABOT_AllCapsModeration__WarningDurationSeconds=10
# Windows PowerShell
$env:PANDABOT_AllCapsModeration__Enabled="true"
$env:PANDABOT_AllCapsModeration__DeleteMessage="true"
$env:PANDABOT_AllCapsModeration__MinLetters="8"
$env:PANDABOT_AllCapsModeration__MinUppercaseRatio="0.8"
$env:PANDABOT_AllCapsModeration__EnableLengthScaling="true"
$env:PANDABOT_AllCapsModeration__UppercaseRatioDropPerLetter="0.01"
$env:PANDABOT_AllCapsModeration__MinScaledUppercaseRatio="0.4"
$env:PANDABOT_AllCapsModeration__WarningDurationSeconds="10"
# Windows CMD
set PANDABOT_AllCapsModeration__Enabled=true
set PANDABOT_AllCapsModeration__DeleteMessage=true
set PANDABOT_AllCapsModeration__MinLetters=8
set PANDABOT_AllCapsModeration__MinUppercaseRatio=0.8
set PANDABOT_AllCapsModeration__EnableLengthScaling=true
set PANDABOT_AllCapsModeration__UppercaseRatioDropPerLetter=0.01
set PANDABOT_AllCapsModeration__MinScaledUppercaseRatio=0.4
set PANDABOT_AllCapsModeration__WarningDurationSeconds=10# Linux/Mac
export PANDABOT_ModerationExemptions__ExemptUserIds__0=1234567890
export PANDABOT_ModerationExemptions__ExemptRoleIds__0=1234567890
# Windows PowerShell
$env:PANDABOT_ModerationExemptions__ExemptUserIds__0="1234567890"
$env:PANDABOT_ModerationExemptions__ExemptRoleIds__0="1234567890"
# Windows CMD
set PANDABOT_ModerationExemptions__ExemptUserIds__0=1234567890
set PANDABOT_ModerationExemptions__ExemptRoleIds__0=1234567890# Disable all fun commands (meme, 8ball, roll, joke, say)
PANDABOT_CommandAccess__DisableAllFunCommands=true
# Disable specific commands globally
PANDABOT_CommandAccess__DisabledCommands__0=about
PANDABOT_CommandAccess__DisabledCommands__1=avatar
PANDABOT_CommandAccess__DisabledCommands__2=help
PANDABOT_CommandAccess__DisabledCommands__3=ping
PANDABOT_CommandAccess__DisabledCommands__4=remind
PANDABOT_CommandAccess__DisabledCommands__5=serverinfo
PANDABOT_CommandAccess__DisabledCommands__6=userinfo
# Restrict commands to allowed channel IDs
PANDABOT_CommandAccess__RestrictedChannels__about__0=1234567890
PANDABOT_CommandAccess__RestrictedChannels__help__0=1234567890
PANDABOT_CommandAccess__RestrictedChannels__help__1=2345678901# Linux/Mac
export PANDABOT_GameModules__EnableAshesOfCreation=false
export PANDABOT_GameModules__EnableStarCitizen=true
export PANDABOT_GameModules__EnablePathOfExile=true
export PANDABOT_GameModules__EnableReturnOfReckoning=true
# Windows PowerShell
$env:PANDABOT_GameModules__EnableAshesOfCreation="false"
$env:PANDABOT_GameModules__EnableStarCitizen="true"
$env:PANDABOT_GameModules__EnablePathOfExile="true"
$env:PANDABOT_GameModules__EnableReturnOfReckoning="true"
# Windows CMD
set PANDABOT_GameModules__EnableAshesOfCreation=false
set PANDABOT_GameModules__EnableStarCitizen=true
set PANDABOT_GameModules__EnablePathOfExile=true
set PANDABOT_GameModules__EnableReturnOfReckoning=trueFor development, use .NET user secrets to securely store sensitive configuration:
dotnet user-secrets set "Discord:Token" "your_token_here"
dotnet user-secrets set "Discord:Prefix" "!"User secrets are stored securely and not committed to version control.
Edit appsettings.json directly for development defaults. Never commit sensitive tokens to this file.
When the bot starts, it loads configuration in this order:
appsettings.jsonloads default settingsappsettings.Development.json(if running in Development environment) overrides defaults- Environment variables with
PANDABOT_prefix override everything above - User secrets (development only) override everything
Example: If you set both appsettings.json and an environment variable, the environment variable wins:
// appsettings.json
{
"Discord": {
"Prefix": "!"
}
}# Environment variable overrides the ! prefix
export PANDABOT_Discord__Prefix=">"Result: The bot will use > as the prefix, not !.
# Development mode
dotnet run
# Production mode (set environment first)
# Linux/Mac
export DOTNET_ENVIRONMENT=Production
dotnet run
# Windows PowerShell
$env:DOTNET_ENVIRONMENT="Production"
dotnet runToken(string, required): Discord bot tokenPrefix(string): Command prefix for text commands (default:!)GuildId(ulong): Guild/Server ID for testing (optional)AllowPrefixCommands(bool): Enable legacy prefix commands (default:false)AllowedFunChannels(array): Channel IDs where fun commands are allowedHeartbeat.Enabled(bool): Enable Uptime Kuma push heartbeats (default:false)Heartbeat.PushUrl(string): Uptime Kuma push URLHeartbeat.IntervalSeconds(int): Heartbeat interval (minimum enforced:15)Heartbeat.StartupDelaySeconds(int): Delay before first heartbeat pushHeartbeat.TimeoutSeconds(int): HTTP timeout for heartbeat push
This repository uses .github/workflows/dotnet.yml for CI and .github/workflows/deploy.yml for deployment. The deploy workflow writes a runtime .env using these env vars:
| GitHub Secret | Runtime Environment Variable |
|---|---|
DISCORD_TOKEN |
PANDABOT_Discord__Token |
GUILD_ID |
PANDABOT_Discord__GuildId |
HEARTBEAT_ENABLED |
PANDABOT_Discord__Heartbeat__Enabled |
HEARTBEAT_PUSH_URL |
PANDABOT_Discord__Heartbeat__PushUrl |
HEARTBEAT_INTERVAL_SECONDS |
PANDABOT_Discord__Heartbeat__IntervalSeconds |
HEARTBEAT_STARTUP_DELAY_SECONDS |
PANDABOT_Discord__Heartbeat__StartupDelaySeconds |
HEARTBEAT_TIMEOUT_SECONDS |
PANDABOT_Discord__Heartbeat__TimeoutSeconds |
MODERATION_LOG_FORUM_CHANNEL_ID |
PANDABOT_ModerationLog__ForumChannelId |
MODERATION_LOG_MODERATOR_ROLE_ID |
PANDABOT_ModerationLog__ModeratorRoleId |
MODERATION_LOG_EVENT_AUDIT_ENABLED |
PANDABOT_ModerationLog__EventAuditEnabled |
MODERATION_LOG_EVENT_AUDIT_CHANNEL_ID |
PANDABOT_ModerationLog__EventAuditChannelId |
MODERATION_LOG_EVENT_AUDIT_LOG_MESSAGE_DELETES |
PANDABOT_ModerationLog__LogMessageDeletes |
MODERATION_LOG_EVENT_AUDIT_LOG_MEMBER_LEAVES |
PANDABOT_ModerationLog__LogMemberLeaves |
MODERATION_LOG_EVENT_AUDIT_LOG_MEMBER_JOINS |
PANDABOT_ModerationLog__LogMemberJoins |
MODERATION_LOG_IGNORED_USER_ID_0 |
PANDABOT_ModerationLog__IgnoredUserIds__0 |
MODERATION_LOG_IGNORED_USER_ID_1 |
PANDABOT_ModerationLog__IgnoredUserIds__1 |
MODERATION_LOG_AUDIT_LOG_LOOKBACK_SECONDS |
PANDABOT_ModerationLog__AuditLogLookbackSeconds |
CROSS_CHANNEL_SPAM_ENABLED |
PANDABOT_CrossChannelSpam__Enabled |
CROSS_CHANNEL_SPAM_TIME_WINDOW_SECONDS |
PANDABOT_CrossChannelSpam__TimeWindowSeconds |
CROSS_CHANNEL_SPAM_MINIMUM_CHANNEL_COUNT |
PANDABOT_CrossChannelSpam__MinimumChannelCount |
CROSS_CHANNEL_SPAM_DELETE_MESSAGES |
PANDABOT_CrossChannelSpam__DeleteMessages |
CROSS_CHANNEL_SPAM_TIMEOUT_ON_DETECTION |
PANDABOT_CrossChannelSpam__TimeoutOnDetection |
ALL_CAPS_MODERATION_ENABLED |
PANDABOT_AllCapsModeration__Enabled |
ALL_CAPS_MODERATION_DELETE_MESSAGE |
PANDABOT_AllCapsModeration__DeleteMessage |
ALL_CAPS_MODERATION_MIN_LETTERS |
PANDABOT_AllCapsModeration__MinLetters |
ALL_CAPS_MODERATION_MIN_UPPERCASE_RATIO |
PANDABOT_AllCapsModeration__MinUppercaseRatio |
ALL_CAPS_MODERATION_ENABLE_LENGTH_SCALING |
PANDABOT_AllCapsModeration__EnableLengthScaling |
ALL_CAPS_MODERATION_UPPERCASE_RATIO_DROP_PER_LETTER |
PANDABOT_AllCapsModeration__UppercaseRatioDropPerLetter |
ALL_CAPS_MODERATION_MIN_SCALED_UPPERCASE_RATIO |
PANDABOT_AllCapsModeration__MinScaledUppercaseRatio |
ALL_CAPS_MODERATION_WARNING_DURATION_SECONDS |
PANDABOT_AllCapsModeration__WarningDurationSeconds |
The bot now uses a DB-backed acronym allowlist to exempt known acronyms instead of a blanket short-message exemption. Manage entries with /acronyms add, /acronyms remove, and /acronyms list.
The default seed includes common English chat acronyms, Welsh/Wales community acronyms, and common IT/technical acronyms.
| MODERATION_EXEMPT_USER_ID_0 | PANDABOT_ModerationExemptions__ExemptUserIds__0 |
| MODERATION_EXEMPT_ROLE_ID_0 | PANDABOT_ModerationExemptions__ExemptRoleIds__0 |
| COMMAND_ACCESS_DISABLE_ALL_FUN_COMMANDS | PANDABOT_CommandAccess__DisableAllFunCommands |
| COMMAND_ACCESS_DISABLED_COMMAND_0 | PANDABOT_CommandAccess__DisabledCommands__0 |
| COMMAND_ACCESS_DISABLED_COMMAND_1 | PANDABOT_CommandAccess__DisabledCommands__1 |
| COMMAND_ACCESS_DISABLED_COMMAND_2 | PANDABOT_CommandAccess__DisabledCommands__2 |
| COMMAND_ACCESS_DISABLED_COMMAND_3 | PANDABOT_CommandAccess__DisabledCommands__3 |
| COMMAND_ACCESS_DISABLED_COMMAND_4 | PANDABOT_CommandAccess__DisabledCommands__4 |
| COMMAND_ACCESS_DISABLED_COMMAND_5 | PANDABOT_CommandAccess__DisabledCommands__5 |
| COMMAND_ACCESS_DISABLED_COMMAND_6 | PANDABOT_CommandAccess__DisabledCommands__6 |
DefaultConnection(string): SQLite database connection string (default:Data Source=pandabot.db)
CacheExpirationHours(int): How long to cache AshesForge data (default:24)EnableImageCaching(bool): Enable image caching for faster responses (default:true)
Controls which game modules are loaded and available:
EnableAshesOfCreation(bool): Enable Ashes of Creation module (default:false)EnableStarCitizen(bool): Enable Star Citizen status module (default:true)EnablePathOfExile(bool): Enable Path of Exile status module (default:true)EnableReturnOfReckoning(bool): Enable Return of Reckoning status module (default:true)
Note: Modules that are disabled will not load services or consume resources. Slash commands from disabled modules will not be available to users.
All moderation actions are logged as rich embeds to a Discord forum channel. Each embed shows the action type, the target user, the moderator, and the reason.
Note:
ModerationLogis a root-level config section, not nested underDiscord.
The bot reuses one forum thread per user instead of creating a new thread for every action: it first checks for a previously linked thread, then falls back to scanning existing thread titles for an exact [userId] tag or a fuzzy username match (auto-renaming and posting a confirmation note if linked this way) before creating a new thread as a last resort.
ForumChannelId(ulong): Forum channel ID where moderation log threads are created (0= disabled)ModeratorRoleId(ulong): Optional role to mention in log posts (0= no mention)
Detects users who send identical messages across multiple channels within a short time window. When triggered, a spam alert is posted to the moderation log forum channel with Ban and Dismiss buttons for moderators.
Note:
CrossChannelSpamis a root-level config section, not nested underDiscord. Requires the Message Content privileged intent enabled in the Discord Developer Portal.
Enabled(bool): Enable cross-channel spam detection (default:false)TimeWindowSeconds(int): Sliding window duration in seconds (default:30)MinimumChannelCount(int): Minimum number of distinct channels before a detection fires (default:3)DeleteMessages(bool): Delete detected spam messages — requires Manage Messages (default:true)TimeoutOnDetection(bool): Apply a 28-day timeout to the spammer — requires Moderate Members (default:true)
Detects messages that are mostly uppercase, deletes them, warns the author via a self-deleting channel message and a DM, records a strike toward the same 3-strike /warn counter, and logs the incident to the user's moderation log thread.
Note:
AllCapsModerationis a root-level config section, not nested underDiscord.
Enabled(bool): Enable all-caps detection (default:true)DeleteMessage(bool): Delete the offending message — requires Manage Messages (default:true)MinLetters(int): Minimum letter count (non-letters excluded) before a message is considered (default:8)MinUppercaseRatio(double): Fraction of letters that must be uppercase to trigger (default:0.7)WarningDurationSeconds(int): How long the self-deleting channel warning stays visible (default:10)
Register channels that should allow only one message per user. Channels must be listed here before /singlemessage enable will accept them.
{
"SingleMessage": {
"Channels": [
{ "ChannelId": 1234567890123456789, "ScanHistoryOnEnable": false }
]
}
}Channels[].ChannelId(ulong): Discord channel ID to register for single-message enforcementChannels[].ScanHistoryOnEnable(bool): Whentrue, scans the last 100 messages on enable to pre-populate existing posters (default:false)
SingleMessage:Channels is an array and is best managed in appsettings.json rather than environment variables.