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 .envfile asDISCORD_TOKEN
- Copy the application ID and add it to your .envfile asCLIENT_ID
- Get your Discord server (guild) ID and add it to your .envfile 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 .envfile 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 watching
- bun run build- Build the project to- dist/directory
- bun 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 servicesThe 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 dataandexecuteproperties:
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 baseIdandexecuteproperties:
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_IDare correct
- Check bot permissions in Discord server
- Ensure bot is invited to the server with proper scopes
"Firebase connection failed"
- Verify FIRESTORE_PROJECT_IDmatches 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).