Backend Overview

Backend Overview

The backend is a TypeScript application that extends the Discord.js Client class into a central Manager orchestrator. This page describes the architecture, lifecycle, and major subsystems.

Entry Point (index.ts)

The process starts in src/index.ts:

  1. Reads config.yml via ConfigDataService.
  2. Creates a Manager instance with the parsed configuration.
  3. Calls manager.start() to boot all services.
  4. Attaches global anti-crash listeners for unhandledRejection and uncaughtException.

Manager Class (manager.ts)

Manager extends discord.js Client and is the root object. It holds references to every major service:

PropertyTypePurpose
configConfigParsed config.yml
loggerLoggerServiceWinston logger with daily rotation
dbDatabaseServiceDatabase abstraction (all table references)
mewslinkMewslinkCustom Lavalink v4 client
commandsCollection<Command>Loaded slash commands
dashboardEmitFunctionEmits events to Socket.IO rooms

Boot Sequence (Manager.start())

The start() method initializes services in this order:

  1. Handlers - loadCommand, loadEvents, loadPlayer (registers commands, events, player events).
  2. Database - Connects the configured driver, registers all tables, runs setup/migrations.
  3. Discord Login - Calls client.login(TOKEN).
  4. REST API - Starts Fastify on the configured port (default 2555). Registers the DashboardPlugin.
  5. WebServer - Starts Express on the configured port (default 2444). Registers webhook routes.
  6. Premium Services - Initializes PatreonService and KofiService if enabled.
  7. Notification Services - Initializes TopggService if enabled.
  8. Deploy - Registers slash commands globally via DeployService.

Discord Gateway Intents

The Manager requests these privileged intents:

IntentRequired For
GuildsGuild cache, channel access
GuildMembersMember events, statistics, voice state
GuildMessagesPrefix commands, message content
GuildVoiceStatesVoice channel join/leave detection
MessageContentReading message text for prefix commands
GuildPresencesPresence/activity tracking

All three privileged intents (Presence, Server Members, Message Content) must be enabled in the Discord Developer Portal.

Mewslink (Custom Lavalink Client)

Mewslink is a custom Lavalink v4 client library built specifically for this project. It lives in src/mewslink/ and provides:

ComponentFilePurpose
MewslinkMewslink.tsMain class - manages nodes and players
MewslinkPlayermain.tsExtended player with custom data store
Manager/DirectoryNode registry and player allocation
Node/DirectoryIndividual node connection and communication
Player/DirectoryPlayer state, queue, filters, voice handling
Drivers/DirectoryProtocol drivers (Lavalink v4 REST + WebSocket)
Library/DirectoryDiscord.js adapter for voice state forwarding
Plugin/DirectoryPlugin system for Lavalink plugins

Player Data Store

Each MewslinkPlayer has a key-value data map used to store runtime flags:

KeyTypePurpose
autoplaybooleanWhether autoplay is active
isMusicTriviabooleanFlags trivia players to bypass normal events
sudoDestroybooleanPrevents reconnect on intentional destroy

Database Layer (database/)

Driver Selection

DatabaseService reads features.DATABASE.driver from config and instantiates the matching driver:

ValueDriver FileLibrary
jsondriver/json.tsmewwme.quick.db/JSONDriver
mongodbdriver/mongodb.tsmewwme.quick.db/MongoDriver + mongoose
mysqldriver/mysql.tsmewwme.quick.db/MySQLDriver + mysql2
postgresdriver/postgres.tsmewwme.quick.db/PostgresDriver + pg

Table Registration

setup/table.ts registers all database tables. Each table is a QuickDatabasePlus instance with typed schema from schema/. The tables cover: guild settings, playlists, premium codes, user profiles, liked songs, trivia stats, session logins, AI chat history, and more.

Cache Layer

QuickDatabasePlus wraps mewwme.quick.db with an in-memory cache layer. Cache clearing is scheduled via the ClearCache cron expression in config (default: every 60 seconds).

Handlers (handlers/)

HandlerFilePurpose
Command LoaderloadCommand.tsScans commands/ and buttons/, registers them in the commands collection
Event LoaderloadEvents.tsScans events/, attaches listeners to the Discord client
Player LoaderloadPlayer.tsScans events/player/ and events/track/, attaches listeners to Mewslink
Autofix CheckloadCheck.tsStarts Lavalink auto-recovery if AUTOFIX_LAVALINK is enabled

Command Handler (CommandHandler.ts)

Receives interactionCreate events and routes them:

  1. Resolves the command from the commands collection.
  2. Checks access level against COMMANDS_ACCESS config.
  3. Checks premium requirements if applicable.
  4. Checks DJ role restrictions.
  5. Executes the command handler.

Services (services/)

ServiceFilePurpose
ConfigDataServiceConfigDataService.tsParses config.yml with environment variable interpolation (${TOKEN} etc.)
DeployServiceDeployService.tsRegisters slash commands globally on Discord
LoggerServiceLoggerService.tsWinston logger with console + daily rotating file transport
ManifestServiceManifestService.tsReads package.json for version and name metadata
PatreonServicePatreonService.tsManages Patreon pledges, webhook events, and guild redemption
KofiServiceKofiService.tsManages Ko-fi subscriptions and webhook processing
TopggServiceTopggService.tsTracks Top.gg votes and voter status
MysqlBackupMysqlBackup.tsScheduled MySQL dump to a Discord channel (cron-based)
TempVoiceServiceTempVoiceService.tsManages auto-created temporary voice channels
FilterMenuServiceFilterMenuService.tsBuilds select menus for audio filter selection
MusicTriviaServiceMusicTriviaService.tsMusic trivia game logic, scoring, badges, stats
SpotifyTopChartServiceSpotifyTopChartService.tsFetches global top chart data
RecommendationServiceRecommendationService.tsFetches song recommendations via Last.fm API

Web Layer (web/)

The web layer runs two separate servers:

RestAPI (Fastify - port 2555)

  • Internal /v1/* API routes for bot control (player state, settings, search, moderation).
  • Requires Authorization header matching config.features.RestAPI.auth.
  • Enforces IP/domain whitelist from config.features.RestAPI.whitelist.
  • Hosts the DashboardPlugin which provides:
    • Discord OAuth2 login flow (/auth/discord, /auth/discord/callback).
    • Session management (Fastify session with configurable store).
    • /api/* proxy routes for the dashboard frontend.
    • Socket.IO server for real-time player state.

WebServer (Express - port 2444)

  • /vote - Top.gg vote webhook with signature verification.
  • /kofi - Ko-fi payment webhook with token verification.
  • /lastfm - Last.fm OAuth callback for account linking.
  • Serves as the public-facing webhook receiver.

Lavalink Auto-Recovery

The autofix/ module provides automatic Lavalink failover:

  1. CheckLavalinkServer - periodically pings configured Lavalink nodes.
  2. GetLavalinkServer - queries fallback node lists if primary nodes are down.
  3. AutoFixLavalink - reconnects or replaces failed nodes automatically.

Enabled via features.AUTOFIX_LAVALINK in config.

Logging

LoggerService uses Winston with two transports:

TransportOutputRotation
Consolestdout-
Daily Rotate Filelogs/ directoryDaily, with configurable retention

Log levels: error, warn, info, debug. Debug mode is toggled via bot.DEBUG_MODE in config.

Track Start & Now Playing

When a track begins playing, the trackStart.ts event handler runs a complete pipeline covering statistics, history, embed rendering, and interactive controls. This section documents the two embed modes and the recommendation system.

Event Flow

Track starts playing
      |
      v
1. Skip if isMusicTrivia player
2. Update music setup channel (if configured)
3. Update music status channel
4. Schedule Last.fm scrobble
5. Emit playerState to Socket.IO
6. Save auto-reconnect data (if enabled)
7. Increment global + guild play counters
8. Save listening history (per-user + per-guild, last 100 entries)
9. Start top stats session (60s listen rule)
10. Build and send now-playing message (V1 or V2)
11. Attach button collector for playback controls

If the playback was triggered from the dashboard (dashboardPlayback flag), the event stops after step 9 and does not send a Discord message.

Embed Mode V1 - Music Card (Default)

When musicCard is enabled (default), the bot generates a visual music card image using the mewcard (opens in a new tab) library and sends it as an embed attachment.

The music card includes:

  • Track title (truncated to 40 characters)
  • Artist name (truncated to 15 characters)
  • Album artwork thumbnail
  • Requester display name
  • Dynamic color extraction from artwork
  • Configurable theme
EmbedBuilder
  Author: localized "Now Playing" text
  Description: Track title with hyperlink
  Image: mewwme.png (generated card)
  Fields:
    - Author name + duration
    - Volume + source/autoplay indicator
  Components:
    - Recommendation select menu (if available)
    - Filter select menu
    - Playback button row 1 (Shuffle, Previous, Pause, Skip, Loop)
    - Playback button row 2 (Autoplay, Vol-, Stop, Vol+, Queue)

Mewcard Themes

The music card theme is configurable per guild. The default theme is set via features.DEFAULT_MUSIC_CARD_THEME.Themes in config.yml (defaults to "Dynamic").

Users can customize the card by visiting the mewcard repository (opens in a new tab) and creating their own themes.

Source Detection

The embed displays a source indicator based on where the track was resolved from:

SourceDisplay
youtubeYouTube icon
spotifySpotify icon
soundcloudSoundCloud icon
deezerDeezer icon
tidalTidal icon
twitchTwitch icon
apple / applemusicApple Music icon
youtube_musicYouTube Music icon
httpHTTP icon
AutoPlay activeAutoPlay indicator

Embed Mode V2 - Container (No Music Card)

When musicCard is disabled, the bot uses Discord's Components V2 ContainerBuilder for a lightweight text-based now-playing message.

ContainerBuilder (accent color: primary embed color)
  Section:
    - "Now Playing" header (localized)
    - Track title + artist with search link
    - Duration + requested by
    Thumbnail: album artwork
  Separator
  Components:
    - Recommendation select menu
    - Filter select menu
    - Playback button rows

V2 mode sends with MessageFlags.IsComponentsV2 and MessageFlags.SuppressNotifications. It does not generate an image, making it faster and lighter on resources.

Guild Player Embed Configuration

Each guild can customize the now-playing message via the dashboard. The configuration is resolved by GuildPlayerEmbedConfig.ts:

SettingDefaultDescription
musicCardtrue (if enabled in config)Use V1 music card image or V2 text container
filterMenutrueShow the audio filter select menu
buttonLabeltrueShow text labels on playback buttons
recommendationMenutrueShow the song recommendation select menu

These settings are stored per-guild in the database and can be toggled individually.

Recommendation Select Menu

The now-playing message includes a "Recommended For You" select menu that suggests similar tracks based on the currently playing song.

How It Works

Current track (title + artist)
      |
      v
RecommendationService.ts
      |
      +---> Last.fm API: track.getSimilar (up to 15 similar tracks)
      |
      +---> Last.fm API: artist.getSimilar (up to 8 similar artists)
      |         |
      |         v
      |    Last.fm API: artist.getTopTracks (top tracks from similar artists)
      |
      v
Merge + deduplicate results (max 10)
      |
      v
Cache for 10 minutes
      |
      v
StringSelectMenu with track options

Recommendation Logic

  1. Similar Tracks - Calls track.getSimilar on the Last.fm API with the current track name and artist. Returns up to 15 similar tracks.

  2. Similar Artists Fallback - If fewer than 10 recommendations are found from similar tracks, calls artist.getSimilar to find up to 8 similar artists. Then fetches top tracks from the top 3 similar artists to fill the remaining slots.

  3. Deduplication - All recommendations are deduplicated by trackName::artistName (lowercase) to avoid showing the same song twice.

  4. Caching - Results are cached in-memory by track+artist key for 10 minutes to avoid repeated API calls when the same song plays again.

  5. Graceful Fallback - If no Last.fm API key is configured or the API call fails, the select menu is silently omitted from the message.

Requirements

  • The Last.fm API Key must be configured in config.yml under features.WebServer.LAST_FM_SCROBBLED.ApiKey.
  • The recommendationMenu must be enabled in the guild's player embed config (enabled by default).

When a user selects a recommendation from the menu, the selected track's searchQuery (formatted as "Artist - Title") is used to search and add the track to the queue.

Playback Control Buttons

The now-playing message includes two rows of interactive buttons:

Row 1:

ButtonCustom IDAction
ShuffleshuffleShuffle the queue
PreviousreplayReplay the current or previous track
Pause/PlaypauseToggle pause state
SkipskipSkip to the next track
LooploopCycle loop mode (off, track, queue)

Row 2:

ButtonCustom IDAction
AutoplayautoplayToggle autoplay mode
Volume -voldownDecrease volume
StopstopStop playback and disconnect
Volume +volupIncrease volume
QueuequeueDisplay the current queue

All button interactions require the user to be in the same voice channel as the bot. If not, an ephemeral error message is returned.

Control Button Toggle

Playback control buttons can be completely disabled per guild via the ControlButton database setting (configurable through the dashboard). When set to ControlButtonEnum.Disable, the trackStart event skips sending the now-playing message entirely.