Skip to content

Contributing

umutxyp edited this page Oct 17, 2026 · 1 revision

Contributing

Thanks for helping to make Beatra better. Fixes, new translations, tests and well-described bug reports are all welcome.


Getting set up

  1. Fork umutxyp/MusicBot on GitHub and clone your fork.
  2. Install Node.js 22.12 or newer.
  3. Create a test bot in the Discord Developer Portal and a test server, so you never experiment on a live bot.
  4. Make a .env with the token and your test server. GUILD_ID makes command changes appear in that server right away:
    DISCORD_TOKEN=your_test_bot_token
    GUILD_ID=your_test_server_id
    DEBUG=true
  5. Start it with npm start. The startup check installs the packages and downloads yt-dlp and ffmpeg.

Scripts

Command What it does
npm start Startup check, then the bot under the sharding manager
npm test Runs every test in test/*.test.js with Node's built-in test runner (node --test)
npm run check Syntax check of every .js file and a JSON check of every file in languages/, without starting the bot
npm run deploy Forces slash command registration (needs DISCORD_TOKEN and CLIENT_ID in .env)
npm run shard Same as npm start (kept for older start scripts)
postinstall Runs by itself after npm install and downloads yt-dlp and ffmpeg (scripts/install-binaries.js). A failure is only a warning: the startup check tries again

The audio pipeline has an end-to-end test that runs the real yt-dlp and ffmpeg. It is skipped unless you point YTDLP_E2E at a real yt-dlp program:

YTDLP_E2E=$(which yt-dlp) npm test

Please run npm test and npm run check before opening a pull request.


Project structure

index.js               Entry point: startup check, then the sharding manager
src/
  setup/               Startup check (bootstrap needs no npm packages), launcher
  bot.js               One shard: Discord client, events, graceful shutdown
  config.js            Reads .env
  deploy.js            Slash command registration (only when commands changed)
  commands/            Slash commands
  handlers/            Interaction router and permission checks
  music/               DisTube setup, YouTube extractor, yt-dlp runner, audio relay, sessions, lyrics
  ui/views.js          Components V2 layouts
  core/                Logger, i18n, cache, JSON store, formatting, yt-dlp/ffmpeg download
languages/             Translations
test/                  Tests
bin/                   yt-dlp program folder (downloaded and updated automatically, git-ignored)
data/                  Settings and saved queues (created automatically, git-ignored)

Code style

Follow what the existing code does:

  • CommonJS modules with 'use strict'; at the top, and node: prefixes for built-ins (require('node:fs')).
  • 4 spaces for indentation, single quotes, semicolons.
  • Short JSDoc comments that explain why, above functions that need it.
  • No new dependencies unless they are really needed. The startup files in src/setup/ (bootstrap.js, report.js) must keep working with Node built-ins only, because they run before npm install.
  • Texts that users see come from the language files through t('key'), so they can be translated.
  • Add or update tests in test/ for behaviour you change.

Adding a slash command

  1. Create a file in src/commands/, for example src/commands/example.js. Files starting with _ are helpers and are not loaded as commands.
  2. Export a category (playback, queue or settings, which decides the order in /help), data and execute. Use the command() helper from _shared.js, which makes the command server-only and adds translated descriptions:
    'use strict';
    
    const { command, queueAction } = require('./_shared');
    const actions = require('../music/actions');
    
    module.exports = {
        category: 'playback',
        data: command('example', 'What the command does'),
    
        execute(interaction, ctx) {
            return queueAction(interaction, ctx, (queue, t) => actions.pause(queue, t));
        },
    };
    queueAction runs the usual voice checks first (user in the bot's channel, music playing) and replies with the result.
  3. Optional: add commands.example.description to the language files. It is used for the translated description in Discord and in /help; without it the English text from command() is shown.
  4. Restart the bot. It notices the new command and registers it; with GUILD_ID set it appears in your test server at once.

Command names must be unique; the loader stops with Duplicate command name otherwise.


Adding or improving a translation

  1. Copy languages/en.json to languages/<code>.json and translate the values. Keep the keys and the {placeholders} exactly as they are.
  2. Set "language": { "name": "...", "code": "<code>" } at the top. The name is shown in the /language menu.
  3. To follow Discord's language setting automatically, add the Discord locale to DISCORD_LOCALES in src/core/i18n.js.
  4. Run npm run check to make sure the JSON is valid.

Good to know: missing keys fall back to English, and emoji are stripped from translations so the panels look clean. The /language menu can show up to 25 languages.


Reporting bugs

Open an issue on GitHub Issues and include:

  • What you did, what you expected, and what happened instead.
  • The bot version (version in package.json), your operating system and node -v.
  • The startup check output and the console log around the problem, ideally with DEBUG=true.

Warning: Before you paste anything, remove your token, proxy passwords and anything from cookies.txt. Never attach your .env or cookies.txt.


Reporting security issues

Please do not open a public issue for a security problem. Use GitHub's private reporting instead: Report a vulnerability. Describe the problem and how to reproduce it.

If your bot token ever leaks, press Reset Token in the Discord Developer Portal right away, so the old token stops working.


License

Beatra is released under the MIT License. By contributing you agree that your contribution is released under the same license.

Clone this wiki locally