Arman MB
A free Discord music bot built for my friends, with Spotify imports, YouTube search, lyrics, and shared music controls. Runs on Oracle Cloud even when my laptop is off.
- 19 slash commands
- Independent server queues
- Cloud-hosted playback
Free hosted bot. Add it to a server you can manage, join voice, and use /play. Source download available in the case study.

What it’s for
I knew friends who were willing to pay for a music-bot service just to get all the features they wanted. I built Arman MB so we could listen together and use those controls for free. It started as a private-server tool and became one of my most-used projects among friends. That made the everyday details matter: picking the right recording, keeping the queue intact, and making the controls easy to find.
How I built it
The TypeScript application separates Discord commands, Spotify metadata, source resolution, deterministic song matching, playback, and per-server queue state. Spotify identifies the song; it does not supply the audio or take over my personal Spotify playback. Lavalink and maintained source tools handle streaming into Discord. I kept custom code focused on recording selection, queue behavior, and the interface rather than building a YouTube extractor.
What it does
Friends can submit Spotify tracks, albums, and accessible playlists, YouTube links and playlists, or a plain-text search. A shared player menu supports pause, resume, skip, stop, queue navigation, and looping. Each Discord server has its own player and queue. The bot now runs alongside my Jev 2048 service on Oracle Cloud, with audible playback confirmed after migration and automatic startup enabled. Usage is based on my experience with friends, not a published user-count or uptime benchmark.
The interesting part
A search result, a resolved stream URL, and a TrackStart event are different milestones from audible music. Cloud deployment exposed that distinction: the same source that worked locally hit YouTube login checks on Oracle. Account cookies cleared authentication, but Lavalink still received HTTP 403. Comparing a small yt-dlp audio download with a direct audio request isolated a playback-context difference. The fix used documented upstream settings and a maintained token provider, followed by a real Discord listening test.
Getting the music and controls right
Built with TypeScript, Node.js, discord.js, lavalink-client, Spotify Web API, and LRCLIB. Lavalink on Java streams audio through LavaSrc, yt-dlp, and Deno, with Opus preferred. The tricky parts were choosing the right recording, preferring explicit versions when appropriate, and keeping play-next and skip from disrupting a playlist. Deterministic scoring checks identity, duration, uploader, and version keywords; concurrent searches and queue pre-resolution reduce waiting. Compact menus replace giant previews and repeated popups. The 88-test suite covers matching, authorization, imports, queue transitions, controls, and failures; timing logs help locate delays.
Making local playback work in the cloud
I moved the bot from Windows to an Oracle Cloud ARM64 Ubuntu server already running Jev 2048. YouTube rejected anonymous cloud requests; cookies cleared login checks, but streaming still returned HTTP 403. A maintained bgutil PO-token provider plus yt-dlp’s documented mweb playback-context setting resolved the tested stream. I verified audio bytes, Lavalink preparation, and finally sound in Discord. Separate systemd services, private credentials, localhost-only audio endpoints, and memory limits keep both projects running, with automatic startup after reboots. Cookies can expire, queues reset on restart, and the shared server has not been load-tested for a large audience.
VibeSafe
HAVE A QUESTION OR AN IDEA?