A Discord bot built with Bun + TypeScript that provides AI-powered chat completions with optional web search capabilities via SearXNG.
- AI Chat Completions: Powered by OpenAI's GPT models
- Web Search Integration: Uses SearXNG for real-time web search results
- Message Triggers: Responds to mentions (@bot) and keywords ("Bad Kitty", "Lumia")
- Slash Commands: Modern Discord slash command interface
- Streaming Support: Real-time response streaming (optional)
- TypeScript: Fully typed for better development experience
- Bun Runtime: Fast, modern JavaScript runtime
- Bun installed (v1.3.5 or higher)
- A Discord Application with Bot token
- OpenAI API key
- SearXNG instance (public or self-hosted)
- Clone the repository (or create from scratch):
cd LumiaBot- Install dependencies:
bun install- Configure environment variables:
cp .env.example .env
# Edit .env with your actual credentials- Set up bot definitions (IMPORTANT):
# Create the prompt_storage directory structure
mkdir -p prompt_storage/persona prompt_storage/instructions prompt_storage/config
# Copy example templates from prompt_storage.example/
cp -r prompt_storage.example/* prompt_storage/
# Edit the files to create your own bot personality
# See BOT_SETUP.md for detailed instructionsCreate a .env file with the following variables:
# Discord Bot Configuration
DISCORD_TOKEN=your_discord_bot_token_here
DISCORD_CLIENT_ID=your_discord_application_id_here
DISCORD_CLIENT_SECRET=your_discord_client_secret_here
DISCORD_REDIRECT_URI=http://localhost:3000/auth/callback
# Bot Identity Configuration (Optional)
# These values are used as template variables in prompt_storage files
# BOT_NAME=Bad Kitty # Bot's display name
# BOT_OWNER_NAME=Prolix # Owner's name
# BOT_OWNER_ID=944783522059673691 # Owner's Discord ID
# BOT_OWNER_USERNAME=prolix_oc # Owner's Discord username
# OpenAI Configuration
OPENAI_API_KEY=your_openai_api_key_here
# OPENAI_BASE_URL=https://api.openai.com/v1 # Optional: Custom API base URL (e.g., for OpenRouter, Together AI)
OPENAI_MODEL=gpt-4o-mini
# OPENAI_MODEL_ALIAS=gpt-4o-mini # Optional: Map to a different model name for the provider
OPENAI_MAX_TOKENS=2000
OPENAI_TEMPERATURE=0.7
OPENAI_FILTER_REASONING=true # Filter out reasoning content from responses (e.g., o1/o3 models)
# SearXNG Configuration
SEARXNG_URL=https://search.example.com
SEARXNG_MAX_RESULTS=5
SEARXNG_SAFE_SEARCH=1
# Server Configuration
PORT=3000You can customize your bot's identity and owner information through environment variables. These values are used as template variables (e.g., {botName}, {ownerName}) in your prompt_storage files:
# Optional - Bot identity (defaults shown)
BOT_NAME=Bad Kitty # Bot's display name
BOT_OWNER_NAME=Prolix # Owner's name
BOT_OWNER_ID=944783522059673691 # Owner's Discord ID
BOT_OWNER_USERNAME=prolix_oc # Owner's Discord usernameThese variables allow you to:
- Change the bot's name without editing prompt files
- Set the bot's owner for special recognition in prompts
- Maintain consistent identity across all prompt templates
See BOT_SETUP.md for more details on using template variables in your bot's personality files.
The bot supports custom OpenAI-compatible API providers such as:
- OpenRouter (access to multiple models)
- Together AI
- Groq
- Google GenAI (supports video/audio modality)
- Local models (via llama.cpp, etc.)
Example: OpenRouter Configuration
OPENAI_BASE_URL=https://openrouter.ai/api/v1
OPENAI_API_KEY=your_openrouter_key_here
OPENAI_MODEL=anthropic/claude-sonnet-4-5
OPENAI_MODEL_ALIAS=claude-sonnet-4-5 # Optional: alias for the modelExample: Local Model via llama.cpp
OPENAI_BASE_URL=http://localhost:8080/v1
OPENAI_API_KEY=optional-for-local
OPENAI_MODEL=local-modelFor Gemini 3 models, you can use Google's native GenAI SDK instead of going through an OpenAI-compatible proxy. This provides better native support for Gemini-specific features.
When to use this:
- When using Gemini 3 Flash/Pro models
- When you have direct access to Google's Gemini API
- When you want to avoid proxy layers
Configuration:
# Set the model to a Gemini 3 variant
OPENAI_MODEL=gemini-3-flash
# Configure Google GenAI (required to activate the native SDK)
GEMINI_API_KEY=your_gemini_api_key_here # Get from https://ai.google.dev/
# GEMINI_BASE_URL=https://generativelanguage.googleapis.com # Optional: for custom endpointsHow it works:
- If
GEMINI_API_KEYis set AND the model name contains "gemini-3", the bot automatically uses the Google GenAI SDK - Otherwise, it falls back to the OpenAI SDK (for OpenRouter, Together AI, etc.)
- The bot logs which service it's using on startup:
[AI Service] Using Google GenAI for gemini-3-flash
- Go to Discord Developer Portal
- Create a new application
- Go to "Bot" section and create a bot
- Copy the bot token (DISCORD_TOKEN)
- Go to "OAuth2" → "General" and copy the Client ID (DISCORD_CLIENT_ID) and Client Secret (DISCORD_CLIENT_SECRET)
- In "OAuth2" → "URL Generator", select
botandapplications.commandsscopes, then copy the generated URL to invite your bot
- Go to OpenAI Platform
- Create an account or sign in
- Go to API keys section and create a new secret key
You have two options:
Option 1: Use a public instance
- Find public instances at searx.space
- Note: Public instances may have rate limits
Option 2: Self-host SearXNG
docker run -d --name searxng -p 8080:8080 -e "BASE_URL=http://localhost:8080" searxng/searxngBefore using the bot, register the slash commands with Discord:
bun run register-commandsDevelopment mode (with hot reload):
bun run devProduction mode:
bun run startOnce the bot is running and invited to your server, use these slash commands:
-
/chat <message> [search]- Chat with the AImessage: Your question or messagesearch: (Optional) Enable web search for better answers
-
/search <query> [category] [timerange]- Search the webquery: What to search forcategory: (Optional) Filter by category (general, images, news, science, files)timerange: (Optional) Filter by time (day, month, year)
LumiaBot/
├── src/
│ ├── bot/
│ │ └── client.ts # Discord client setup
│ ├── commands/
│ │ ├── chat.ts # /chat command
│ │ └── search.ts # /search command
│ ├── scripts/
│ │ └── register-commands.ts # Command registration script
│ ├── services/
│ │ ├── openai.ts # OpenAI integration
│ │ └── searxng.ts # SearXNG search integration
│ ├── utils/
│ │ └── config.ts # Configuration management
│ └── index.ts # Entry point
├── .env # Environment variables
├── .env.example # Example environment file
├── package.json
├── tsconfig.json
└── README.md
- Create a new file in
src/commands/:
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
import type { Command } from '../bot/client';
const command: Command = {
data: new SlashCommandBuilder()
.setName('mycommand')
.setDescription('Description of my command'),
async execute(interaction: ChatInputCommandInteraction) {
await interaction.reply('Hello from my command!');
},
};
export default command;- Register the command:
bun run register-commandsAll configuration is managed through environment variables. See .env.example for all available options.
The bot's personality is defined in the prompt_storage/ directory, which is excluded from git for privacy. You must create these files yourself using the provided templates.
- Create the directory structure:
mkdir -p prompt_storage/persona prompt_storage/instructions prompt_storage/config- Copy example templates:
cp -r prompt_storage.example/* prompt_storage/-
Customize the files to create your own bot personality (see
BOT_SETUP.mdfor detailed instructions) -
Restart the bot to load your custom personality
The prompt_storage/ directory is gitignored to keep your bot's unique personality private. The example templates in prompt_storage.example/ show you the required file structure and format without exposing any proprietary content.
prompt_storage/
├── persona/
│ ├── identity.txt # Main bot personality/system prompt
│ ├── boredom_pings.json # Random messages when bot is "bored"
│ ├── error_templates.json # Error/fallback responses
│ └── command_responses.json # Command-specific responses
├── instructions/
│ ├── video_reaction.txt # How to react to videos
│ ├── reply_context.json # Reply conversation templates
│ ├── memory_system.txt # Memory formation instructions
│ └── boredom_updates.txt # Boredom opt-in/opt-out responses
└── config/
├── triggers.json # Bot trigger keywords
└── tool_descriptions.json # Tool descriptions with personality
See BOT_SETUP.md for complete documentation on creating and customizing these files.
The bot automatically responds to messages when:
- Mentioned directly -
@BadKittyBot hello! - Trigger keywords detected - Messages containing:
Bad Kitty(case insensitive)Lumia(case insensitive)
- Replied to — replies to their messages
@BadKittyBot What's the weather today?
→ Bot responds with AI-generated answer
Hey Bad Kitty, can you help me with something?
→ Bot responds with AI-generated answer
Lumia, what do you think about this?
→ Bot responds with AI-generated answer
- The bot monitors all messages in channels it has access to
- When triggered, it extracts the actual message content (removing the trigger)
- Generates an AI response using the bot's personality from
prompt_storage/persona/identity.txt - Replies directly to the user's message
Note: Message triggers do not use web search by default (unlike the /chat command with search: true).
The bot automatically filters out reasoning content from AI models that include it (such as o1, o3, DeepSeek-R1, etc.). This prevents internal "thinking" from being shown to users.
Filtered patterns include:
<think>...</think>tags<reasoning>...</reasoning>tags[REASONING]...[/REASONING]tags- Lines starting with "reasoning:", "thinking:", etc.
To disable filtering, set:
OPENAI_FILTER_REASONING=false- Ensure you've registered commands with
bun run register-commands - Check that the bot has proper permissions in the Discord server
- Verify the bot token is correct
- Check your API key is valid and has available credits
- Verify the model name is correct
- Check rate limits haven't been exceeded
- Ensure the SearXNG URL is accessible
- Check if the instance requires authentication
- Verify the instance supports JSON output format
MIT
Contributions are welcome! Please feel free to submit a Pull Request.