Skip to content

Internals - Package-by-Package Walkthrough

This document provides a deep dive into every package in the Solstice codebase, explaining the internal APIs, design decisions, and how components interact.

Package Map

musicbot/
├── cmd/bot/          → Application entrypoint and bootstrap
├── internal/
│   ├── config/       → Environment variable loading
│   ├── database/     → MongoDB client and all collection helpers
│   ├── i18n/         → YAML-based internationalization
│   ├── queue/        → Thread-safe per-chat song queue
│   ├── telegram/     → Bot command handlers and VC management
│   └── youtube/      → Search, extraction, caching, and proxies
└── strings/langs/    → Language YAML files

cmd/bot/main.go

The application entrypoint. Orchestrates the startup sequence:

  1. Load .env via godotenv
  2. Call config.LoadConfig() to populate the global config.Config
  3. Load i18n strings from strings/langs/en.yml
  4. Resolve MONGO_URI from environment (tries 3 variable names)
  5. Initialize MongoDB via database.InitDB()
  6. Load YouTube cookies (MongoDB → env var → disk)
  7. Load Pyrogram session string (env var)
  8. Create VoiceChatManager (spawns Python daemon)
  9. Create BotHandler (registers all command routes)
  10. Wire up OnStreamEnd callback for auto-queue advancement
  11. Start bot polling in a goroutine
  12. Block on SIGINT/SIGTERM for graceful shutdown

internal/config/

config.go

A singleton AppConfig struct populated from environment variables. Loaded once via LoadConfig() and accessed globally via config.Config.

Key design decisions:

  • Uses raw os.Getenv() rather than a config framework - keeps dependencies minimal
  • Space-separated lists (START_IMG_URL, EMOJIS) are split via strings.Fields()
  • Provides sensible defaults for all optional values
  • Prints a warning (not fatal) when required tokens are missing - allows partial startup for testing

internal/database/

db.go

The centralized data access layer. Contains a global MongoDB client and exposes functions (not methods) for all collections.

Pattern: All database functions are package-level functions, not methods on a struct. This matches Go's convention for singleton services:

go
// Usage:
database.InitDB(uri)
database.IsSudoer(userID)
database.GetAutoplay(chatID)

Connection management:

  • Single *mongo.Client created at startup
  • All operations use the same client (connection pooling handled by the driver)
  • No explicit connection closing - relies on process termination

See Database Schema for collection documentation.


internal/i18n/

strings.go

Minimal internationalization system using YAML files.

How it works:

  1. LoadStrings(path) reads a YAML file and flattens it into map[string]string
  2. GetString(key, args...) retrieves a string and replaces {0}, {1}, etc. with provided arguments
go
// strings/langs/en.yml:
// start_2: "👋 Hey {0}! I'm {1}"

i18n.GetString("start_2", "Stark", "Solstice")
// → "👋 Hey Stark! I'm Solstice"

Design notes:

  • Custom {N} placeholder format instead of Go's fmt.Sprintf - matches common i18n conventions
  • Graceful fallback: returns the key itself if not found
  • Supports string, int, and generic types via fmt.Sprintf("%v", v)

internal/queue/

queue.go

Thread-safe per-chat song queue. The QueueManager is the shared state between all command handlers and the stream-end callback.

Key struct:

go
type QueueManager struct {
    mu     sync.Mutex
    queues map[int64][]Song  // chatID → ordered song slice
}

Concurrency guarantees:

  • Every operation acquires the mutex before reading or writing
  • The Shuffle() method preserves index 0 (currently playing song) and only shuffles the rest
  • Advance() atomically pops the first element and returns the new first element
  • GetActiveChats() returns a snapshot of chat IDs with non-empty queues

Song struct:

go
type Song struct {
    Title         string
    Link          string
    Duration      string
    Thumbnail     string
    StreamURL     string
    RequesterID   int64
    RequesterName string
    Video         bool    // true = video stream, false = audio only
}

internal/telegram/

The largest package, containing all bot logic split across multiple files.

bot.go - Core Handler (~1230 lines)

Contains:

  • BotHandler struct definition (the central struct holding Bot, VC, Queue, voteSkips)
  • NewBot() - constructor that creates the telebot instance and registers all routes
  • registerRoutes() - calls all Init*() methods to register handlers
  • handlePlay() / handleVPlay() - the main play command logic
  • handleForcePlay() / handleVForcePlay() - force-play variants
  • handlePause(), handleResume(), handleStop(), handleSkip()
  • handleSeek(), handleSeekBack(), handleVolume(), handleSpeed()
  • handleQueue() - paginated queue display
  • handleLoop(), handleShuffle(), handleAutoplay()
  • handleSuggest() - song suggestion with inline buttons
  • handleVoteSkip() - vote skip button callback
  • HandleStreamEnd() - auto-advance callback (called from main.go wire-up)
  • voteSkipStore - in-memory vote tracking

Play command flow (simplified):

go
func (h *BotHandler) handlePlay(c telebot.Context, isVideo, isForce bool) {
    // 1. Auth check
    // 2. Parse query from payload or reply
    // 3. Send "Searching..." message
    // 4. youtube.SearchVideo(query, 1)
    // 5. youtube.GetStreamURL(result.ID, isVideo)
    // 6. Create Song struct
    // 7. h.Queue.Add(chatID, song)
    // 8. If queue length == 1 (first song):
    //    h.VC.Play(chatID, streamURL, isVideo, title)
    // 9. Send "Now Playing" card with inline keyboard
    // 10. h.logPlay(chat, song)
}

vc.go - Voice Chat Manager

See Voice Chat System for full documentation.

admins.go - Group Admin Commands (~858 lines)

All group moderation commands organized into modules:

  • Ban module: /ban, /unban, /tban, /dban
  • Mute module: /mute, /tmute, /unmute
  • Purge module: /purge, /spurge, /del
  • Pin module: /pin, /unpin, /unpinall (with confirmation dialog)
  • Promote module: /promote, /fullpromote, /demote, /setadmintitle
  • Kick module: /kick, /unbanall

Helper functions:

  • getTargetUser() - resolves target from reply → text mention → @username → numeric ID
  • extractReason() - extracts reason text from command payload
  • parseDuration() - parses human-friendly duration (5m, 2h, 3d)
  • mention() - creates HTML mention link

sudo.go - Superuser Commands (~401 lines)

Owner/sudoer-only commands:

  • Sudoer management: /addsudo, /delsudo, /sudolist
  • Global ban: /gban, /ungban (async enforcement across all served chats)
  • Broadcast: /broadcast (iterates all chats/users with 100ms delay)
  • Active info: /activevc, /activevideo, /ac
  • Cookie management: /setcookies, /checkcookies
  • Bot control: /reboot

utils.go - Permission & Logging (~342 lines)

Core utility functions used across all handlers:

Permission hierarchy:

go
func IsAdmin(c, chat, userID) bool {
    // 1. Owner (config.Config.OwnerID)
    // 2. Sudoer (database.IsSudoer)
    // 3. Private chat (always admin)
    // 4. Authorized user (database.IsAuthUser)
    // 5. Telegram group admin/creator
}

CheckAdminSassy() - The central permission middleware:

  • Checks if the command is a "music command" (hardcoded list)
  • For music commands, respects admin_cmd_mode setting (everyone vs admin)
  • Authorized users bypass admin check for music commands
  • Returns sassy denial messages for unauthorized users

Logging functions:

  • logError() - Sends error details to logger channel (with thread ID support)
  • logActivity() - Sends admin action logs
  • logPlay() - Sends play event logs

Permission checks:

  • CheckBotCanRestrict() - Verifies bot has Restrict Members permission
  • CheckBotCanPin() - Verifies bot has Pin Messages permission
  • CheckBotCanDelete() - Verifies bot has Delete Messages permission

fun.go - Reaction Commands (~480 lines)

Interactive reaction system using nekos.best API:

  • Interactive (Accept/Reject): /hug, /kiss, /cuddle, /propose
  • One-way: /slap, /kill, /crush
  • Mood: /cry, /blush, /smile, /happy, /dance, /bully

Callback pattern: Each interactive command creates an inline keyboard with callback data encoding senderID_targetID. Only the target can accept/reject.

tools.go - Utility Commands (~660 lines)

Mixed utility and fun commands:

  • Fun: /truth, /dare, /couples, /wish, /cute
  • Utility: /song, /lyrics (via LrcLib API), /tr (Google Translate)
  • System: /reload, /lang, /afk
  • Couples image: Downloads profile photos, composites them with a background image, crops to circles using pure Go image package (no CGo)

AFK Middleware: AFKMiddleware() is a telebot middleware that checks every message:

  1. If sender was AFK → remove AFK, announce return
  2. If replying to AFK user → show AFK notice
  3. If mentioning AFK user → show AFK notice

whisper.go - Inline Whisper (~178 lines)

Secret message system using Telegram's inline mode:

  • In-memory storage via map[string]string with sync.RWMutex
  • Supports normal and one-time (self-destructing) whispers
  • Unauthorized read attempts trigger notification to the sender

start.go - Start & Welcome (~153 lines)

  • /start in PM → photo + welcome text + navigation keyboard
  • /start in group → group welcome card
  • OnAddedToGroup → automatic welcome message
  • /owner → developer info card with links

help.go - Help Menu System (~227 lines)

Hierarchical help menu using inline keyboard navigation:

Help Menu
├── Admin
│   ├── Music Control
│   └── Group Management
├── Play
├── Sudo (restricted to sudoers)
│   ├── User Management
│   ├── Chat Management
│   └── Bot Control
└── Extra
    ├── Utility
    └── Fun

Each section is rendered by editing the same message with new content and keyboard buttons.


internal/youtube/

See YouTube & Search Pipeline for full documentation.


strings/langs/

en.yml

YAML file containing all English language strings. Format:

yaml
key: "Message text with {0} placeholders for {1} arguments"

Currently contains ~142 string entries covering:

  • Start/welcome messages
  • Help text for all command categories
  • Admin action confirmations
  • Error messages (all with "sassy" personality)
  • UI button labels
  • Settings descriptions

Released under the GPL-3.0 License.