Thank you for your interest in contributing to KoolBot! This document provides guidelines and instructions for contributing to the project.
- Getting Started
- Development Setup
- Development Workflow
- Coding Standards
- Testing Guidelines
- Commit Guidelines
- Pull Request Process
- Issue Reporting
- Community
- Developer Resources
For detailed technical guidance on extending KoolBot:
- DEVELOPER_GUIDE.md - Comprehensive developer documentation
- Architecture overview and patterns
- Bot-controlled channel header posts pattern (reusable)
- Service implementation examples
- Configuration management
- Code style and best practices
- Node.js 20 or higher
- npm (comes with Node.js)
- Docker and Docker Compose (for containerized development)
- Git for version control
- Discord Bot Token (for testing)
- MongoDB (local installation or Docker)
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/koolbot.git cd koolbot -
Add upstream remote:
git remote add upstream https://github.com/lonix/koolbot.git
-
Install dependencies:
npm install
-
Create your
.envfile:cp .env.example .env
Edit
.envand add your Discord bot credentials and MongoDB URI. -
Build the project:
npm run build
-
Run tests to ensure everything works:
npm test
-
Start MongoDB (if not using Docker):
# Using Docker for MongoDB only docker run -d -p 27017:27017 --name mongodb mongo:latest # Or use your local MongoDB installation
-
Run in development mode (with hot reload):
npm run dev
Use the development Docker Compose configuration:
# Start in development mode with hot reloading
docker-compose -f docker-compose.dev.yml up
# Run in detached mode
docker-compose -f docker-compose.dev.yml up -d
# View logs
docker-compose -f docker-compose.dev.yml logs -f bot
# Stop containers
docker-compose -f docker-compose.dev.yml down# Development
npm run dev # Run with hot reload
npm run watch # Watch mode for TypeScript compilation
# Building
npm run build # Compile TypeScript
# Testing
npm test # Run all tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
npm run test:ci # Run tests in CI mode
# Code Quality
npm run lint # Check code quality with ESLint
npm run lint:fix # Auto-fix linting issues
npm run format # Format code with Prettier
npm run format:check # Check code formatting
npm run check # Run build + lint + format check
npm run check:all # Run all checks including tests
# Utilities
npm run validate-config # Validate configuration schema
npm run migrate-config # Migrate old configuration
npm run cleanup-global-commands # Clean up Discord commands-
Create a feature branch from
main:git checkout main git pull upstream main git checkout -b feature/your-feature-name
-
Make your changes in small, focused commits
-
Keep your branch updated:
git fetch upstream git rebase upstream/main
-
Push to your fork:
git push origin feature/your-feature-name
-
Open a Pull Request on GitHub
- π Bug Fixes - Fix issues or incorrect behavior
- β¨ Features - Add new functionality
- π Documentation - Improve or add documentation
- π§ͺ Tests - Add or improve tests
- β»οΈ Refactoring - Code improvements without changing functionality
- π¨ Style - Code formatting and style improvements
- β‘ Performance - Performance improvements
- π§ Tooling - Build tools, CI/CD, development environment
- Use explicit types - Avoid
anywhere possible - Follow existing patterns - Maintain consistency with the codebase
- Use meaningful names - Variables, functions, and classes should be descriptive
- Keep functions focused - Each function should do one thing well
- Add JSDoc comments for complex logic or public APIs
We use ESLint and Prettier to enforce code style:
# Check for linting issues
npm run lint
# Auto-fix linting issues
npm run lint:fix
# Check formatting
npm run format:check
# Auto-format code
npm run formatKey conventions:
- Indentation: 2 spaces
- Quotes: Single quotes for strings
- Semicolons: Required
- Line length: Max 120 characters (enforced by Prettier)
- Trailing commas: Required in multi-line objects/arrays
Follow the established architecture:
-
Services (
src/services/) - Business logic and core functionality- Use singleton pattern with
getInstance() - Handle initialization and cleanup
- Emit events for important state changes
- Use singleton pattern with
-
Commands (
src/commands/) - Discord slash commands- Export
data(SlashCommandBuilder) andexecutefunction - Handle errors gracefully with user-friendly messages
- Use
interaction.deferReply()for long-running operations
- Export
-
Models (
src/models/) - MongoDB schemas- Define clear TypeScript interfaces
- Use Mongoose schemas for validation
- Export both interface and model
-
Utils (
src/utils/) - Helper functions- Pure functions when possible
- Well-documented and tested
- Reusable across the codebase
CRITICAL: When adding a new command, you must update TWO places in src/services/command-manager.ts:
-
loadCommandsDynamically()method - Registers command with Discord API- Add
{ name, configKey, file }to thecommandConfigsarray
- Add
-
populateClientCommands()method - Loads execute handler into client- Add the same
{ name, configKey, file }to thecommandConfigsarray
- Add the same
Why both? These methods serve different purposes:
loadCommandsDynamically()tells Discord about your command (makes it appear in the UI)populateClientCommands()gives the bot the code to execute when the command is used
Failure Mode: If you only update loadCommandsDynamically(), the command will appear in Discord but
will fail with "The application did not respond" because the bot has no handler for it.
Example:
// In BOTH loadCommandsDynamically() and populateClientCommands()
{ name: "mycmd", configKey: "mycmd.enabled", file: "mycmd" }Checklist for adding a command:
- β
Create
src/commands/mycmd.tswithdataandexecuteexports - β
Add to
commandConfigsinloadCommandsDynamically() - β
Add to
commandConfigsinpopulateClientCommands() - β
Add
mycmd.enabledtoconfig-schema.ts - β
Document in
COMMANDS.md
- All new features must be toggleable via configuration
- Add configuration schema in
src/services/config-schema.ts - Use dot notation for keys (e.g.,
feature.subfeature.enabled) - Document new config keys in
SETTINGS.md - Use
ConfigServicefor all configuration access
- Never crash the bot - Catch and log errors appropriately
- Use try-catch blocks for async operations
- Log errors with context using the logger utility
- Return user-friendly error messages in Discord interactions
- Recover gracefully when possible
- Write tests for all new features and bug fixes
- Follow the AAA pattern: Arrange, Act, Assert
- Use descriptive test names:
it('should do something specific when condition') - Mock external dependencies: Discord.js, MongoDB, file system, network calls
- Test edge cases and error conditions
import { describe, it, expect } from '@jest/globals';
import { myFunction } from '../../src/utils/myFunction.js';
describe('MyService', () => {
describe('myMethod', () => {
it('should return expected value when given valid input', () => {
// Arrange
const input = 'test';
const service = new MyService();
// Act
const result = service.myMethod(input);
// Assert
expect(result).toBe('expected');
});
it('should throw error when given invalid input', () => {
// Arrange
const invalidInput = null;
const service = new MyService();
// Act & Assert
expect(() => service.myMethod(invalidInput)).toThrow('Invalid input');
});
});
});# Run all tests
npm test
# Run tests in watch mode (recommended during development)
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Run specific test file
npm test -- path/to/test.test.ts- Minimum coverage: 2% (current baseline)
- Target coverage: 70-80% for critical modules
- Focus on: Commands, services, utilities
- Coverage reports are generated in
coverage/directory
We use conventional commits format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, no logic change)refactor: Code refactoringtest: Adding or updating testschore: Build process, tooling, dependenciesperf: Performance improvementsci: CI/CD changes
Examples:
feat(commands): add quote search functionality
Add ability to search quotes by keyword using /quote search command.
Includes fuzzy matching and pagination for results.
Closes #123
---
fix(voice): prevent memory leak in voice channel cleanup
Voice channels were not being properly cleaned up due to missing
event listener removal. Added cleanup in the disconnect handler.
Fixes #456
---
docs: update CONTRIBUTING.md with testing guidelines
Add detailed section on writing tests and test coverage expectations.- Make atomic commits - Each commit should represent one logical change
- Write clear messages - Explain what and why, not how
- Reference issues - Use
Closes #123,Fixes #456, orRefs #789 - Keep commits focused - Don't mix refactoring with bug fixes
-
Run all quality checks:
npm run check:all
-
Ensure tests pass:
npm test -
Verify markdown (if documentation changed):
npx markdownlint "**/*.md" --ignore node_modules --ignore dist -
Update documentation if needed:
COMMANDS.mdfor new commandsSETTINGS.mdfor new configuration optionsREADME.mdfor user-facing features- Add comments for complex logic
-
Update Dockerfiles if dependencies changed
When you open a PR, the template will guide you through the required information:
- Description: What does this PR do?
- Type of Change: Bug fix, feature, documentation, etc.
- Testing: How was this tested?
- Checklist: Code quality, tests, documentation
- Related Issues: Link to relevant issues
-
Automated checks will run:
- TypeScript compilation
- ESLint checks
- Prettier formatting
- Jest tests with coverage
- Markdown linting (for doc changes)
- Docker build validation
-
Code review by maintainers:
- Code quality and adherence to standards
- Test coverage and quality
- Documentation completeness
- Security considerations
-
Address feedback:
- Make requested changes
- Push updates to your branch
- Respond to review comments
-
Merge:
- Maintainers will merge approved PRs
- Your contribution will be included in the next release
- PR will be automatically labeled for release notes
Your PR will be automatically labeled based on file changes:
documentation- Markdown file changesdependencies- package.json changesgithub-actions- Workflow changestest- Test file changesdocker- Dockerfile changes
You can also add manual labels:
feature/enhancement- New featuresbug/fix- Bug fixesbreaking- Breaking changespatch- Patch release changes
- Search existing issues - Your issue may already be reported
- Check documentation - The answer might be in the docs
- Try latest version - The issue may already be fixed
Use the appropriate issue template:
- π Bug Report - Report a bug or unexpected behavior
- β¨ Feature Request - Suggest a new feature or enhancement
- π Documentation - Request documentation improvements
- β Question - Ask questions (or use Discussions)
- Clear title - Summarize the issue in one line
- Description - Detailed explanation of the issue
- Steps to reproduce - For bugs, exact steps to reproduce
- Expected behavior - What you expected to happen
- Actual behavior - What actually happened
- Environment - OS, Node version, Docker version
- Logs - Relevant error messages or logs
- Screenshots - For UI issues
- GitHub Issues - Bug reports and feature requests
- GitHub Discussions - Questions, ideas, and general discussion
- Pull Requests - Code contributions
- Be respectful - Treat everyone with respect and empathy
- Be constructive - Provide helpful feedback and suggestions
- Be patient - Maintainers are volunteers with limited time
- Be clear - Provide sufficient context and information
- Stay on topic - Keep discussions focused and relevant
Contributors are recognized in:
- Release notes - Automatic attribution in release changelogs
- GitHub insights - Contributor graphs and statistics
- Pull request comments - Public acknowledgment and thanks
If you have questions about contributing, please:
- Check this guide thoroughly
- Search existing issues and discussions
- Open a new discussion on GitHub
- Tag your question appropriately
Thank you for contributing to KoolBot! π