A Discord bot built with Bun, TypeScript, Discord.js, and Firebase for server moderation and verification features.
- Slash Commands: Modern Discord slash command support
- Context Menus: Right-click context menu commands
- Button Interactions: Interactive button components
- Firebase Integration: Configuration and data storage
- Verification System: User verification workflow
- Report System: User reporting functionality
- Logging: Comprehensive logging system
Before setting up the project, make sure you have:
- Bun runtime installed (v1.2.20 or later)
- A Discord application with bot token
- Firebase project with Firestore database
- Firebase service account key file
-
Clone the repository
git clone https://github.com/acmutd/peechi-bot.git cd peechi-bot
-
Install dependencies
bun install
Create a .env
file in the root directory with the following variables:
# Discord Configuration
DISCORD_TOKEN=your_discord_bot_token
GUILD_ID=your_discord_guild_id
CLIENT_ID=your_discord_application_client_id
# Firebase Configuration
FIRESTORE_PROJECT_ID=your_firebase_project_id
FIRESTORE_KEY_FILENAME=path/to/your/firebase-service-account-key.json
CALENDAR_API_KEY=your_calendar_api_key
CALENDAR_ID=you_know_what
- Go to the Discord Developer Portal
- Create a new application or use an existing one
- Go to the "Bot" section and create a bot
- Copy the bot token and add it to your
.env
file asDISCORD_TOKEN
- Copy the application ID and add it to your
.env
file asCLIENT_ID
- Get your Discord server (guild) ID and add it to your
.env
file asGUILD_ID
Required Bot Permissions:
- Send Messages
- Use Slash Commands
- Manage Messages
- Read Message History
- Manage Roles (if using verification features)
- Create a Firebase project at Firebase Console
- Enable Firestore Database
- Generate a service account key:
- Go to Project Settings → Service Accounts
- Click "Generate new private key"
- Save the JSON file securely
- Add the file path to your
.env
file asFIRESTORE_KEY_FILENAME
Create a configuration document in your Firestore database:
Collection: config
Document ID: environment
Document Structure:
{
"ROLES": {
"VERIFIED": "role_id_for_verified_users"
},
"CHANNELS": {
"VERIFICATION": "channel_id_for_verification",
"ADMIN": "channel_id_for_admin_notifications",
"ERROR": "channel_id_for_error_logs"
}
}
Replace the IDs with actual Discord role and channel IDs from your server.
Run the bot in development mode with auto-reload:
bun run dev
-
Start the bot
bun start
bun run dev
- Run in development mode with file watchingbun run build
- Build the project todist/
directorybun start
- Run the built production version
src/
├── bot/ # Main bot class and initialization
├── buttons/ # Button interaction handlers
├── commands/ # Slash commands
├── constants/ # Bot configuration constants
├── ctx-menus/ # Context menu commands
├── db/ # Database services (Firebase)
├── events/ # Discord event handlers
├── types/ # TypeScript type definitions
└── utils/ # Utility functions and services
The bot includes several built-in commands:
/ping
- Check bot responsiveness/verify
- User verification command/fail
- Testing/debugging command/recache
- Recache the environment variables/points
- Check your points or view the leaderboard/calendar-sync
- Sync Google Calendar events to Discord guild events
- Create a new file in
src/commands/
- Export a command object with
data
andexecute
properties:
import { SlashCommandBuilder } from 'discord.js'
import type { Command } from '../types'
export const myCommand: Command = {
data: new SlashCommandBuilder().setName('mycommand').setDescription('Description of my command'),
async execute(interaction) {
await interaction.reply('Hello from my command!')
},
}
- Create a new file in
src/buttons/
- Export a button object with
baseId
andexecute
properties:
import type { ButtonCommand } from '../types'
export const myButton: ButtonCommand = {
baseId: 'my_button',
async execute(interaction) {
await interaction.reply('Button clicked!')
},
}
- Create a new file in
src/events/
- Export event properties:
import type { Events } from 'discord.js'
export const name = Events.MessageCreate
export const once = false
export async function execute(message) {
// Handle the event
}
The bot includes comprehensive error handling and logging:
- All errors are logged to the console and error channel
- Critical errors trigger graceful shutdowns
- Failed command/event loading is logged but doesn't crash the bot
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
"Environment initialization failed"
- Check that all required environment variables are set
- Verify Firebase service account key file exists and is readable
- Ensure Firestore database is properly configured
"Commands not deploying"
- Verify
DISCORD_TOKEN
,CLIENT_ID
, andGUILD_ID
are correct - Check bot permissions in Discord server
- Ensure bot is invited to the server with proper scopes
"Firebase connection failed"
- Verify
FIRESTORE_PROJECT_ID
matches your Firebase project - Check service account key file permissions
- Ensure Firestore is enabled in Firebase console
Check the console output for detailed error messages. The bot uses structured logging with different severity levels (info, warn, error, critical).