TitanBot is a powerful, feature-rich Discord bot designed to enhance your server experience with comprehensive moderation tools, engaging economy systems, utility features, and much more. Built with modern Discord.js v14 and PostgreSQL for optimal performance and data persistence.
- Features Overview
- Ticket AI Assistant
- Quick Setup
- Manual Installation Steps
- Support Server
- Required Bot Intents
- Contributing
TitanBot offers a complete suite of tools for Discord server management and community engagement:
|
|
/trade is a read-only market research suite. It never connects to wallets or
exchanges and cannot place orders — every number is a hypothetical what-if.
Six subcommands:
/trade coin— a live-updating market card for one coin (ticker, name, EVM or Solana address). Includes a 7-day chart when available, risk flags, optional hypothetical P/L on anamount, and an optional AI read. Auto-refreshes on a timer you control./trade scan— scans up to 250 coins in CoinGecko's memecoin category and separates fast 24-hour movers from more balanced setups, scoring momentum, market depth, trading activity, volatility, and data quality./trade signals— finds coins whose momentum points up, sizes a hypothetical entry against a budget, projects what that stake would be worth under a few labelled scenarios, and pings the configured user with the result. A button turns the signal into a live tracker./trade simulate— the "how much would I get" ladder: shows exactly what a hypothetical stake would be worth at 1.25x through 10x and at an all-time-high retest./trade trending— freshly boosted on-chain tokens from DexScreener (the brand-new names not yet listed anywhere), each with a liquidity floor and a risk label./trade watch add|list|remove— one-shot price alerts that ping you in the channel you set them when a coin crosses your target.
Setup scores and signals explain current statistics only. They are not forecasts or buy recommendations. Users should independently verify contracts, holder concentration, liquidity locks, taxes, and audits.
Examples:
/trade scan
/trade coin coin:BONK amount:100 entry:0.00002
/trade signals budget:200
/trade simulate coin:BONK amount:100
/trade trending
/trade watch add coin:BONK price:0.00003 direction:Above the target
CoinGecko and DexScreener work without API keys. Scan and trending results are
cached for one minute, and per-user rate limits protect public API quotas. The
signal budget defaults to $100 and is set with TRADE_SIGNAL_BUDGET; the user
pinged by signals defaults to the operator ID and is set with TRADE_NOTIFY_USER_ID.
The honeypot posts a warning embed in a channel of your choice. Any message sent there (bot or human) is deleted and the author is softbanned (kicked). Every kick increments the counter in the embed footer.
/honeypot setup [channel] Post the honeypot embed (defaults to the current channel)
/honeypot text Change the heading, description, counter label, icon, or color
/honeypot status Show the current honeypot configuration
/honeypot remove Delete the embed and disable the honeypot
The default embed style:
[icon] DO NOT SEND
MESSAGES IN THIS
CHANNEL
This channel is used to catch spam bots. Any messages sent here will result in a **softban.**
Kicks: 0
Requires the Manage Server permission. The bot needs View Channel, Send Messages, and Embed Links in the honeypot channel, plus Kick Members to softban offenders. Server owners and administrators are never kicked by the honeypot.
TitanBot can answer basic questions inside support tickets automatically.
- Answer-first by design — the assistant replies with text and has no tools. Its only safety-scoped side effects are closing a resolved ticket and warning the ticket creator for clear, deliberate misuse; it can never give/remove roles, ban, timeout, manage channels, change permissions, run commands, generate images, create files, or take any other action. Three AI warnings are required before a kick is considered.
- Always on — when a ticket is created, the assistant greets the user and starts answering. There is no enable command.
- Player reports — if the ticket looks like a player report, the assistant keeps asking until the reporter gives a username and a video (upload or clip link).
- Request Human button — every ticket shows a 🧑💼 Request Human button. Clicking it pings the configured staff user and the AI stops replying in that ticket.
- Honest fallback — if the AI doesn't know an answer or can't help, it says so and points the user to Request Human instead of making things up.
- Spam-safe — burst coalescing, per-user rate limits, a per-ticket reply cap, duplicate reply suppression, and a provider circuit breaker keep it quiet and cheap.
- Replies are sent as embeds with all mention parsing disabled, so the AI can never ping
@everyone, roles, or users.
Add an OpenAI-compatible API key to your environment and restart the bot:
AI_TICKETS_ENABLED=true
AI_API_KEY=your_api_key_here
# Leave the next two blank to auto-detect from the key prefix
# (gsk_ → Groq, sk-or- → OpenRouter, sk-ant- → Anthropic,
# AIza… → Gemini, csk- → Cerebras, sk- → OpenAI).
# AI_API_BASE_URL=
# AI_TICKET_MODEL=
TICKET_HUMAN_NOTIFY_USER_ID=1377402826514235442 # user pinged on "Request Human"AI_API_KEY is the documented variable. The key prefix is detected automatically,
so a Groq key (gsk_...) pasted there is routed to Groq — it is not sent to
OpenAI. The same holds for Anthropic (sk-ant-...), Gemini/Google (AIza...), and
Cerebras (csk-...) keys. A provider-specific key already in your environment is
also picked up (with a matching default endpoint and model):
| Variable | Default endpoint | Default model |
|---|---|---|
AI_API_KEY (gsk_...) |
https://api.groq.com/openai/v1 |
qwen/qwen3.6-27b |
AI_API_KEY (sk-ant-...) |
https://api.anthropic.com/v1 |
claude-3-5-haiku-latest |
AI_API_KEY (AIza...) |
https://generativelanguage.googleapis.com/v1beta/openai |
gemini-2.5-flash |
AI_API_KEY (csk-...) |
https://api.cerebras.ai/v1 |
llama-3.3-70b |
AI_API_KEY (sk-...) |
https://api.openai.com/v1 |
gpt-4o-mini |
OPENAI_API_KEY |
https://api.openai.com/v1 |
gpt-4o-mini |
OPENROUTER_API_KEY |
https://openrouter.ai/api/v1 |
openai/gpt-4o-mini |
GROQ_API_KEY |
https://api.groq.com/openai/v1 |
qwen/qwen3.6-27b |
DEEPSEEK_API_KEY |
https://api.deepseek.com/v1 |
deepseek-chat |
TOGETHER_API_KEY |
https://api.together.xyz/v1 |
Llama-3.1-8B-Instruct-Turbo |
XAI_API_KEY |
https://api.x.ai/v1 |
grok-2-latest |
MISTRAL_API_KEY |
https://api.mistral.ai/v1 |
mistral-small-latest |
ANTHROPIC_API_KEY |
https://api.anthropic.com/v1 |
claude-3-5-haiku-latest |
GEMINI_API_KEY (or GOOGLE_API_KEY) |
https://generativelanguage.googleapis.com/v1beta/openai |
gemini-2.5-flash |
CEREBRAS_API_KEY |
https://api.cerebras.ai/v1 |
llama-3.3-70b |
A custom AI_API_BASE_URL / AI_TICKET_MODEL still overrides the defaults.
The stock .env.example OpenAI values (https://api.openai.com/v1, gpt-4o-mini)
are ignored when the key is clearly from another provider, so copying the example
file next to a Groq key no longer 401s. Keys are read tolerantly — surrounding
quotes, trailing newlines and stray whitespace (the usual result of pasting into
a hosting panel) are stripped automatically.
Groq's old default
llama-3.1-8b-instantis shut down for free/developer tiers on 2026-08-16. TitanBot now usesqwen/qwen3.6-27bon Groq.openai/gpt-oss-20bwas tried first but forces tool calling: plain text requests get rejected with HTTP 400tool_use_failed("Tool choice is none, but model called a tool"). If you explicitly setAI_TICKET_MODELto a gpt-oss model, the bot automatically retries with tool use pinned off and a lower temperature, but the Qwen default is the reliable choice.
Without a key, chat answers stay off. Player-report collection (username + video) and AI logs still work. Clear, deliberate spam/trolling still receives the same guarded AI warning flow when the assistant is enabled, so a provider refusing to emit an action token cannot silently disable warnings.
Pick where the assistant should post report logs:
/ticket ai logs channel:#staff-ai-logs
That channel receives a log when a ticket looks like a player report, when the reporter adds a username or video, and when the report is ready for staff.
The bot prints the assistant's state on startup, e.g.
🤖 Ticket AI: ready | key: AI_API_KEY (sk-a••••••6789) | model: gpt-4o-mini.
If it says the AI is inactive, the message names the actual cause:
| Message | Cause / fix |
|---|---|
No API key found |
No key variable is set in the environment the bot actually runs in. |
still holds the example placeholder value |
.env was copied from .env.example but never edited. |
AI_TICKETS_ENABLED is set to a false value |
The key is fine — the master switch is off. Set it to true. |
401 Unauthorized |
The provider rejected the key (wrong/revoked key, or wrong provider for AI_API_BASE_URL). |
400 Bad Request |
The provider rejected the request. The log now includes its safe error text plus the configured endpoint and model; correct AI_API_BASE_URL / AI_TICKET_MODEL if needed. Unsupported optional parameters are retried automatically with a minimal compatible request. Tool choice is none, but model called a tool (Groq tool_use_failed) is retried automatically with tool use pinned off; if it persists, switch AI_TICKET_MODEL to qwen/qwen3.6-27b. |
404 |
Wrong AI_API_BASE_URL, or the model name doesn't exist on that provider. |
429 |
Out of quota / rate limited — check billing at your provider. |
.env is always read from the folder containing package.json, so the bot picks up
your key even when it is launched from a different working directory (systemd, pm2,
Docker). Real environment variables take priority over .env, except when they are
empty — a blank value never shadows a real one.
/ticket ai logs channel:#staff-ai-logs Where player-report logs are sent
/ticket ai logs channel:#staff-ai-logs notify_user:@User Also override the Request Human ping
The assistant turns on automatically in every new ticket. It only responds to the ticket creator (never to staff, bots, or commands), and only in open tickets where no human has been requested.
For a detailed step-by-step setup guide, watch our comprehensive video tutorial: TitanBot Setup Tutorial
TitanBot is fully containerized for easy deployment.
-
Clone the repository:
git clone https://github.com/codebymitch/TitanBot.git cd TitanBot -
Configure environment variables:
cp .env.example .env
Set at minimum
DISCORD_TOKEN,CLIENT_ID, andGUILD_ID. Docker Compose also readsPOSTGRES_USER,POSTGRES_PASSWORD, andPOSTGRES_DBfrom.env(defaults:titanbot/password/titanbot). -
Build and start the containers:
docker compose up -d --build
-
Check status:
docker compose ps curl http://localhost:3000/health
This starts the bot and PostgreSQL. The compose file sets POSTGRES_SSL=false and AUTO_MIGRATE=true for the bundled database. Music uses public Lavalink v4 nodes from lavalink/nodes.json by default.
Music uses Lavalink v4 via Riffy, similar to Musicify.
- By default, the bot loads multiple public v4 SSL nodes from
lavalink/nodes.json(sourced from lavalink.darrennathanael.com). Edit that file to add or remove nodes. - To self-host Lavalink instead, run
docker compose --profile local-lavalink up -dand set single-node env vars in.env:Remove or renameLAVALINK_HOST=lavalink LAVALINK_PORT=2333 LAVALINK_PASSWORD=youshallnotpass LAVALINK_SECURE=false
lavalink/nodes.jsonso the bot falls back to those env vars. - Override nodes inline with
LAVALINK_NODES(JSON array) or point at another file withLAVALINK_NODES_FILE. - Use
/play <song>from a voice channel, or/jointo connect without playing. Paste any YouTube link (youtube.com/watch,youtu.be,music.youtube.com, Shorts, and playlists all work) and it plays directly. For searches,/playdefaults to YouTube Music; use thesourceoption to search YouTube, Spotify, SoundCloud, or Deezer instead (or type a prefix likeytsearch:/ytmsearch:directly into the query). Prefix shortcuts:join,np,leave,pause,resume,skip,stop,volume <0-100>, ormusic <subcommand>. Use/now-playingand/queuefor status;/musicfor loop, shuffle, seek, and other controls.
The bot is automatically published to GitHub Container Registry on every push to main.
docker pull ghcr.io/codebymitch/titanbot:main- Node.js 20.10.0 or higher
- PostgreSQL server (recommended) or memory storage fallback
- Discord bot application with proper intents
-
Clone the Repository
git clone https://github.com/codebymitch/TitanBot.git cd TitanBot -
Install Dependencies
npm install
-
Configure Environment Variables
cp .env.example .env
Edit
.envwith your configuration (only the following variables require configuration, leave remaining variables as default):# Discord Bot Configuration DISCORD_TOKEN=your_discord_bot_token_here CLIENT_ID=your_discord_client_id_here GUILD_ID=your_discord_guild_id_here # PostgreSQL Configuration (Primary Database) POSTGRES_URL=postgresql://postgres:yourpassword@localhost:5432/titanbot POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DB=titanbot POSTGRES_USER=postgres POSTGRES_PASSWORD=yourpassword
Production note:
NODE_ENV=productionLOG_LEVEL=warnfor a clean production console (critical issues + startup status)LOG_LEVEL=infoif you want more detailed operational logs- If your chosen
PORTis already used, TitanBot automatically tries the next port(s)
Environment options reference:
NODE_ENV:development,production,test(any non-productionvalue is treated as non-production)LOG_LEVEL:error,warn,info,http,verbose,debug,silly- Accepted aliases for
LOG_LEVELin this bot:warns,warning,warnings→warn
Recommended production
.env(easy mode + default mode):NODE_ENV=production LOG_LEVEL=warn WEB_HOST=0.0.0.0 PORT=3000 PORT_RETRY_ATTEMPTS=5
This gives clear startup/online status messages while keeping logs simple for non-technical operators. If port
3000is busy, the bot tries the next available ports automatically (up toPORT_RETRY_ATTEMPTS).
Slash commands are registered globally on startup (via CLIENT_ID), so the bot works in every server it is invited to. GUILD_ID stays in the tutorial .env for setup steps but is not used for command registration.
Notes:
- Global slash commands may take up to about an hour to propagate on first deploy
- Each server has isolated data: config, economy, tickets, leveling, dashboards, warnings, etc. (all keys are scoped as
guild:{guildId}:...) - In the Discord Developer Portal, ensure your bot is not restricted to a single guild if you plan to invite it elsewhere
- Generate an OAuth2 invite URL from the Discord Developer Portal (OAuth2 → URL Generator, scopes:
botandapplications.commands)
-
Setup PostgreSQL Database (Optional but recommended)
# Create database and user createdb titanbot createuser titanbot psql -c "ALTER USER titanbot PASSWORD 'yourpassword';" psql -c "GRANT ALL PRIVILEGES ON DATABASE titanbot TO titanbot;"
-
Verify Database Setup
npm run migrate:check
-
Start the Bot
npm start
Note on database migrations: Schema tables and legacy key migrations run automatically on startup, so
managed hosts like **Railway** need no manual migration step — just deploy/restart. To disable auto-migration setAUTO_MIGRATE=false. You can still run a manual key migration locally withnode scripts/migrate-keys.js --dry-run(preview) ornode scripts/migrate-keys.js`.
TitanBot requires the following Discord intents:
- Guilds
- Guild Messages
- Message Content
- Guild Members
- Guild Message Reactions
- Guild Voice States
- Direct Messages
- Bot
- Applications.commands
- View Channels
- Send Messages
- Embed Links
- Attach Files
- Read Message History
- Manage Messages
- Manage Channels
- Manage Roles
- Kick Members
- Manage Messages
- Ban Members
- Moderate Members
- Connect
TitanBot is released under the MIT License. See LICENSE for details.
Thank you for choosing TitanBot for your Discord server! We're constantly working to improve and add new features based on community feedback.
Last updated: May 2026