Skip to content

Latest commit

Β 

History

History
567 lines (399 loc) Β· 14.8 KB

File metadata and controls

567 lines (399 loc) Β· 14.8 KB

Contributing to KoolBot

Thank you for your interest in contributing to KoolBot! This document provides guidelines and instructions for contributing to the project.

Table of Contents

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

Getting Started

Prerequisites

  • 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)

First Time Setup

  1. Fork the repository on GitHub

  2. Clone your fork locally:

    git clone https://github.com/YOUR_USERNAME/koolbot.git
    cd koolbot
  3. Add upstream remote:

    git remote add upstream https://github.com/lonix/koolbot.git
  4. Install dependencies:

    npm install
  5. Create your .env file:

    cp .env.example .env

    Edit .env and add your Discord bot credentials and MongoDB URI.

  6. Build the project:

    npm run build
  7. Run tests to ensure everything works:

    npm test

Development Setup

Local Development (Without Docker)

  1. 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
  2. Run in development mode (with hot reload):

    npm run dev

Docker Development

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

Available Scripts

# 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

Development Workflow

Branch Strategy

  1. Create a feature branch from main:

    git checkout main
    git pull upstream main
    git checkout -b feature/your-feature-name
  2. Make your changes in small, focused commits

  3. Keep your branch updated:

    git fetch upstream
    git rebase upstream/main
  4. Push to your fork:

    git push origin feature/your-feature-name
  5. Open a Pull Request on GitHub

Types of Contributions

  • πŸ› 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

Coding Standards

TypeScript Guidelines

  • Use explicit types - Avoid any where 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

Code Style

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 format

Key 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

Architecture Patterns

Follow the established architecture:

  1. Services (src/services/) - Business logic and core functionality

    • Use singleton pattern with getInstance()
    • Handle initialization and cleanup
    • Emit events for important state changes
  2. Commands (src/commands/) - Discord slash commands

    • Export data (SlashCommandBuilder) and execute function
    • Handle errors gracefully with user-friendly messages
    • Use interaction.deferReply() for long-running operations
  3. Models (src/models/) - MongoDB schemas

    • Define clear TypeScript interfaces
    • Use Mongoose schemas for validation
    • Export both interface and model
  4. Utils (src/utils/) - Helper functions

    • Pure functions when possible
    • Well-documented and tested
    • Reusable across the codebase

Adding New Commands

CRITICAL: When adding a new command, you must update TWO places in src/services/command-manager.ts:

  1. loadCommandsDynamically() method - Registers command with Discord API

    • Add { name, configKey, file } to the commandConfigs array
  2. populateClientCommands() method - Loads execute handler into client

    • Add the same { name, configKey, file } to the commandConfigs array

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:

  1. βœ… Create src/commands/mycmd.ts with data and execute exports
  2. βœ… Add to commandConfigs in loadCommandsDynamically()
  3. βœ… Add to commandConfigs in populateClientCommands()
  4. βœ… Add mycmd.enabled to config-schema.ts
  5. βœ… Document in COMMANDS.md

Configuration

  • 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 ConfigService for all configuration access

Error Handling

  • 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

Testing Guidelines

Writing Tests

  • 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

Test Structure

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');
    });
  });
});

Running Tests

# 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

Test Coverage

  • Minimum coverage: 2% (current baseline)
  • Target coverage: 70-80% for critical modules
  • Focus on: Commands, services, utilities
  • Coverage reports are generated in coverage/ directory

Commit Guidelines

Commit Message Format

We use conventional commits format:

<type>(<scope>): <subject>

<body>

<footer>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting, no logic change)
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: Build process, tooling, dependencies
  • perf: Performance improvements
  • ci: 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.

Commit Best Practices

  • 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, or Refs #789
  • Keep commits focused - Don't mix refactoring with bug fixes

Pull Request Process

Before Submitting

  1. Run all quality checks:

    npm run check:all
  2. Ensure tests pass:

    npm test
  3. Verify markdown (if documentation changed):

    npx markdownlint "**/*.md" --ignore node_modules --ignore dist
  4. Update documentation if needed:

    • COMMANDS.md for new commands
    • SETTINGS.md for new configuration options
    • README.md for user-facing features
    • Add comments for complex logic
  5. Update Dockerfiles if dependencies changed

Pull Request Template

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

Review Process

  1. Automated checks will run:

    • TypeScript compilation
    • ESLint checks
    • Prettier formatting
    • Jest tests with coverage
    • Markdown linting (for doc changes)
    • Docker build validation
  2. Code review by maintainers:

    • Code quality and adherence to standards
    • Test coverage and quality
    • Documentation completeness
    • Security considerations
  3. Address feedback:

    • Make requested changes
    • Push updates to your branch
    • Respond to review comments
  4. Merge:

    • Maintainers will merge approved PRs
    • Your contribution will be included in the next release
    • PR will be automatically labeled for release notes

PR Labels

Your PR will be automatically labeled based on file changes:

  • documentation - Markdown file changes
  • dependencies - package.json changes
  • github-actions - Workflow changes
  • test - Test file changes
  • docker - Dockerfile changes

You can also add manual labels:

  • feature/enhancement - New features
  • bug/fix - Bug fixes
  • breaking - Breaking changes
  • patch - Patch release changes

Issue Reporting

Before Creating an Issue

  1. Search existing issues - Your issue may already be reported
  2. Check documentation - The answer might be in the docs
  3. Try latest version - The issue may already be fixed

Creating an Issue

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)

Good Issue Reports Include

  • 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

Community

Getting Help

  • GitHub Issues - Bug reports and feature requests
  • GitHub Discussions - Questions, ideas, and general discussion
  • Pull Requests - Code contributions

Communication Guidelines

  • 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

Recognition

Contributors are recognized in:

  • Release notes - Automatic attribution in release changelogs
  • GitHub insights - Contributor graphs and statistics
  • Pull request comments - Public acknowledgment and thanks

Questions?

If you have questions about contributing, please:

  1. Check this guide thoroughly
  2. Search existing issues and discussions
  3. Open a new discussion on GitHub
  4. Tag your question appropriately

Thank you for contributing to KoolBot! πŸš€