SlopScore
10 crowdincl. 2 critics

PyxeeBot

Discord Musicbot built in Python. Vibe coded with Claude Sonnet 4.6 Free and now Sonnet 5 Free.
Open repo on GitHub Open the demogithub.com/Pylxyr/PyxeeBot
Python · ★ 1 · 0 forks · MIT · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)other
listed 1 hour ago by Pylxyr · last checked 1 hour ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-09-16: Discord Musicbot built in Python. Vibe coded with Claude Sonnet 4.6 Free and now Sonnet 5 Free.; its own README says "Vibe coded with Claude Sonnet 4". 1 stars; MIT license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as Pylxyr. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
Discord Musicbot built in Python. Vibe coded with Claude Sonnet 4.6 Free and now Sonnet 5 Free.
website
https://pylxyr.github.io/PyxeeBot-Page/
created
2026-06-01 · pushed 5 days ago · 204 commits · 1 contributor
languages
Python 85%Shell 15%
paperwork
licensereadme 42% health
dependencies
no dependency graph (no manifest, or disabled) · OSV.dev, checked 1 hour ago

Disclosures, inferred by the Cap'm

slopbucket
vibe-coded
category
other
ai_generated
mostly
human_touch
light
status
works-on-my-machine
language (detected)
pythonshell
license (detected)
mit

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: Discord Musicbot built in Python. Vibe coded with Claude Sonnet 4.6 Free and now Sonnet 5 Free.; its own README says "Vibe coded with Claude Sonnet 4". It carries the MIT license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README — the repo's own words, folded up so the grading fits on one screen
PyxeeBot

PyxeeBot

A self-hosted Discord music bot built for music communities that care about getting the right track.

Stream from YouTube · Last.fm curation · Live controls

Python discord.py yt-dlp License Website

A self-hosted Discord music bot built with discord.py, yt-dlp, and aiosqlite. Designed to run well on a single-core, 1 GB RAM VPS (tested on both Oracle Cloud's Always Free AMD E2.1.Micro and Google Cloud's Always Free e2-micro, running Ubuntu) — or directly on an Android phone via Termux, no VPS required.

Contents


Overview

  • Plays audio from YouTube and YouTube Music
  • !play/!playnext queue yt-dlp's own top search result directly; !search shows a list of candidates to pick from manually when the top hit isn't the one you want
  • Last.fm integration for !vibe similar-track curation and per-server !autoplay
  • Persistent queue snapshots survive restarts; per-server DJ role, prefix, 24/7 mode, and autoplay settings stored in SQLite
  • Designed around the constraints of a 1/8-core shared VPS: single-threaded yt-dlp pool, 64 kbps Opus encoding, debounced panel refreshes, bounded deque-based queue

Features

Playback

  • !play accepts YouTube/YouTube Music URLs, playlist URLs, or plain text search queries — for a text query, the first yt-dlp search result is queued directly
  • !playnext queues a track immediately after the current one
  • !search shows up to 10 interactive results before committing — use this when !play picks the wrong track
  • Vote-skip (!skip): instant if you're the requester or a DJ; otherwise requires ≥50% of listeners to call it
  • !forceskip — immediate skip, DJ-only
  • !skipto <position> — jump to a queue position, dropping everything before it (DJ-only)
  • !prev — requeue the last-played track
  • !pause / !resume
  • !stop — clears the queue and disconnects
  • !loop — cycles through Off → Single track → Entire queue
  • !repeat / !replay — aliases for one-track loop
  • !seek <time> — jump to a position in the current track (1:30, 90, or relative +30/-15); current requester or a DJ
  • !volume [0-200] — show the current volume, or set it (DJ-only); applied via an ffmpeg filter so it stays on the low-CPU FFmpegOpusAudio path rather than switching to PCM
  • !nowplaying — live now-playing embed with queue preview

Vibe Curation (Last.fm)

!vibe <query> discovers similar tracks via Last.fm's track.getSimilar API. Results are sorted by match confidence (0.0–1.0). A curation panel lets you deselect tracks before queuing. When the queue drops to ≤10 tracks during an active vibe session, a refill prompt surfaces automatically offering more similar tracks.

Curation resolutions for a single guild run up to YTDLP_CURATION_CONCURRENCY at a time (own per-guild semaphore, separate from the playback path). Curation also has its own dedicated global semaphore, sized by the same YTDLP_CURATION_CONCURRENCY value — a large !vibe batch resolving in the background can no longer starve !play/!playnext/!search of the single global playback slot (YTDLP_CONCURRENT_EXTRACTS).

Each similar track from Last.fm is resolved to YouTube by taking yt-dlp's top search result for <artist> - <title> — no re-ranking.

Save and reload named curated playlists with !vibe-save / !vibe-load.

If autoplay is enabled for the server (!autoplay), the bot queues one similar track (via the same Last.fm pipeline) whenever the queue fully empties, using the last completed track as the seed — no !vibe required.

URL Pipeline

  • YouTube watch URLs, short URLs (youtu.be), and playlist URLs all resolve correctly
  • Playlist URLs respect MAX_PLAYLIST_SIZE (default 25)
  • yt-dlp selects bestaudio[ext=webm]bestaudio[ext=m4a]bestaudiobest[height<=480]
  • Stream URLs are cached per-track (128 entries, 30-minute TTL by default) and refreshed automatically 30s before the track ends
  • Audio re-encodes through libopus at 64 kbps by default — copy mode is intentionally avoided to prevent pacing irregularities

Performance

  • yt-dlp runs in a ThreadPoolExecutor(max_workers=2) to avoid blocking the event loop
  • A global semaphore (YTDLP_CONCURRENT_EXTRACTS, default 1) limits concurrent playback-path extractions on the constrained vCPU
  • Curation (!vibe) resolves through its own separate global semaphore (sized by YTDLP_CURATION_CONCURRENCY), so a large curated playlist resolving in the background can't block ordinary playback commands
  • Per-guild playback semaphore (Semaphore(1)) isolates guilds from each other
  • Curation resolutions use a separate per-guild semaphore sized by YTDLP_CURATION_CONCURRENCY
  • Thread pool automatically recycles after 3 consecutive extraction timeouts
  • Bounded yt-dlp socket timeout (socket_timeout: 15) prevents stalled connections from permanently consuming a worker slot
  • Now-playing panel refresh is debounced (0.8s) with a state-key check to skip redundant Discord edits
  • Queue duration tracked as a running total (O(1)) rather than summing on every render

Requirements

  • Python 3.11+
  • FFmpeg on PATH
  • Discord bot token
  • Last.fm API key (optional — required for !vibe curation and the per-server !autoplay toggle only)

Installation

Automated VPS setup

Deploying to a fresh Ubuntu VPS? Clone the repo to the server, then run the setup script for your host — it installs everything, walks you through getting a Discord token and (optionally) a Last.fm key with live validation, and starts the bot as a systemd service in one go. All three scripts share the same installer under the hood (deploy/_common.sh); they only differ in a couple of host-specific checks and reminders.

git clone https://github.com/Pylxyr/PyxeeBot.git ~/musicbot
cd ~/musicbot
Host Script What it adds on top of the shared installer
Oracle Cloud bash deploy/setup_oracle.sh Reports whether you're on the AMD (E2.1.Micro) or ARM (Ampere A1) Always Free shape; notes Oracle's ~10 TB/month egress allowance
Google Cloud bash deploy/setup_gcp.sh Checks the VM's region against GCP's Always Free eligibility (us-west1/us-central1/us-east1) via the instance metadata server; warns about the 1 GB/month egress cap and the Network Service Tier / boot disk type gotchas that void the free tier
Anything else (DigitalOcean, Hetzner, AWS, bare metal, etc.) bash deploy/setup.sh Nothing extra — just the shared installer

APP_DIR defaults to wherever you actually cloned the repo (not a hardcoded path), so it doesn't matter what you name the folder or where it lives — cd into it and run the matching script. All three also add a 1 GB swap file automatically on any host with ≤2 GB RAM, since a single yt-dlp/ffmpeg burst can otherwise pressure a 1 GB box hard enough to risk an OOM-killed SSH session.

Automated Termux (Android) setup

Running on your phone instead of a VPS? deploy/setup_termux.sh is a separate, self-contained installer for Termux — Android has no systemd, no apt/sudo, and building the voice stack (PyNaCl, davey) from source needs a couple of Rust-toolchain workarounds a VPS never touches (correct CARGO_BUILD_TARGET for the device's architecture, and an ANDROID_API_LEVEL floor of 34, without which maturin fails outright). It walks through the same interactive wizard as the VPS scripts — Discord token and an optional Last.fm key, both live-validated — then gets the bot running immediately, via termux-services if available, falling back to a detached tmux session otherwise.

pkg install git
git clone https://github.com/Pylxyr/PyxeeBot.git ~/musicbot
cd ~/musicbot
bash deploy/setup_termux.sh

Use Termux from F-Droid, not the Play Store — that build has been unmaintained since 2021. Two optional companion apps (also F-Droid) round it out: Termux:Boot starts Termux — and the bot with it, if you set up the termux-services option — automatically when your phone reboots, and Termux:API enables termux-wake-lock so Android doesn't kill the session in the background.

Local setup

The steps below are for local development or platforms other than the automated script above.

1. Clone

git clone https://github.com/Pylxyr/PyxeeBot.git
cd PyxeeBot

2. Create a virtual environment

python3 -m venv .venv
source .venv/bin/activate

3. Install dependencies

pip install -r requirements.txt

4. Configure

Copy deploy/.env.example to .env in the project root and fill in your token:

cp deploy/.env.example .env
DISCORD_TOKEN=your_discord_bot_token

# Optional
LASTFM_API_KEY=your_lastfm_api_key
DEFAULT_PREFIX=!

5. Run

python bot.py

Running as a systemd service

If you used one of the VPS scripts (setup.sh / setup_oracle.sh / setup_gcp.sh), this is already done — the bot is running as a systemd service. The steps below are for setting it up manually. Termux has no systemd; setup_termux.sh sets up the closest equivalent (termux-services) instead — see Automated Termux (Android) setup.

Create /etc/systemd/system/musicbot.service:

[Unit]
Description=Discord MusicBot
After=network.target
StartLimitIntervalSec=120
StartLimitBurst=5

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/musicbot
Environment="PATH=/home/ubuntu/musicbot/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
Environment="PYTHONMALLOC=malloc"
Environment="MALLOC_TRIM_THRESHOLD_=65536"
EnvironmentFile=/home/ubuntu/musicbot/.env
ExecStart=/home/ubuntu/musicbot/.venv/bin/python bot.py
Nice=-10
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
SyslogIdentifier=musicbot
MemoryHigh=600M
MemoryMax=700M
OOMScoreAdjust=-500
LimitNOFILE=65536
ProtectSystem=full
PrivateTmp=yes
NoNewPrivileges=yes
ProtectHome=read-only
ReadWritePaths=/home/ubuntu/musicbot/data /home/ubuntu/musicbot/logs
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable musicbot
sudo systemctl start musicbot
journalctl -u musicbot -f -o cat

A note on MemoryDenyWriteExecute=yes: you'll notice it's absent from the hardening directives above, even though it's normally a reasonable default. yt-dlp needs an external JS runtime (Deno by default — see YTDLP_JS_RUNTIME_PATH below and the Deno install step in deploy/_common.sh) to fully support YouTube, this bot's primary source. Deno is V8-based, same as Node, and needs writable+executable memory for its JIT compiler — which is exactly what this directive blocks. Tested directly: Deno panics immediately, on a trivial one-line script, under this restriction. Enabling it would break YouTube playback outright, not just as a narrow edge case, so it's deliberately left out.


Configuration

All settings are read from .env. Every value has a default. See deploy/.env.example for the full annotated list.

Variable Default Description
DISCORD_TOKEN required Bot token
LASTFM_API_KEY Enables !vibe curation and the per-server !autoplay toggle
DEFAULT_PREFIX ! Global command prefix (per-server overrides via !setprefix)
BOT_OWNERS Comma-separated owner user IDs (owner-only commands; app owner is always included)
LOG_LEVEL INFO DEBUG / INFO / WARNING / ERROR
LOG_TO_FILE true Write logs to LOG_DIR (rotated weekly by deploy/musicbot-logrotate, not in-process)
LOG_DIR logs Log file directory
MAX_QUEUE_SIZE 100 Maximum queue length per guild
MAX_QUEUE_SIZE_PER_USER 0 Per-user track limit; 0 disables the limit
MAX_PLAYLIST_SIZE 25 Maximum tracks loaded from a single playlist URL
IDLE_TIMEOUT_SECONDS 180 Disconnect after this many seconds idle (no tracks, no listeners)
EMPTY_CHANNEL_TIMEOUT_SECONDS 60 Disconnect after this many seconds alone in a voice channel
YTDLP_CONCURRENT_EXTRACTS 1 Global yt-dlp extraction concurrency limit
YTDLP_PREFETCH_COUNT 1 Tracks to pre-resolve ahead of the current position
YTDLP_CURATION_CONCURRENCY 3 Concurrent per-guild resolutions during !vibe / !vibe-load
YTDLP_SEARCH_RESULTS 5 Raw candidates fetched per search as a safety margin against malformed entries; the first valid one is used
YTDLP_RESOLVE_CACHE_SIZE 128 Maximum cached stream URL entries
YTDLP_RESOLVE_CACHE_TTL_SECONDS 1800 Stream URL cache TTL (30 min)
YTDLP_EXTRACT_TIMEOUT_SECONDS 45 Per-extraction timeout
YTDLP_SOCKET_TIMEOUT 15 yt-dlp socket timeout
NEAR_END_PREFETCH_SECONDS 30 Trigger stream URL refresh this many seconds before track end
YTDLP_COOKIES_FILE Path to Netscape cookies file
YTDLP_JS_RUNTIME_PATH Path to a JS runtime binary, for sites requiring JS signature decryption. If unset, yt-dlp auto-detects a deno binary on PATH — the setup wizards install Deno system-wide for exactly this. Only set this to pin a different runtime/path (e.g. Node) instead
OPUS_BITRATE_KBPS 64 Opus encoding bitrate (64–256)
NP_AUTO_REFRESH false Auto-refresh the now-playing panel on a timer
NP_AUTO_REFRESH_INTERVAL 30 Auto-refresh interval in seconds
ERROR_ANNOUNCE true Post playback errors to the announce channel
RESTORE_QUEUE_ON_RESTART true Restore queue from snapshot after bot restart
BOT_ACTIVITY_URL pylxyr.github.io/PyxeeBot-Page/ Text shown in the bot's Discord status ("Watching …")

Playback

Command Aliases Description
!join summon Join your voice channel
!leave disconnect Leave the voice channel
!play <query> p Queue a URL, playlist, or search query
!playnext <query> pn Queue a track immediately after the current one (DJ-only)
!pause Pause playback
!resume Resume playback
!skip next Vote-skip (instant if you're the requester or a DJ; requires ≥50% of listeners otherwise)
!forceskip fs Immediate skip, DJ-only
!skipto <position> Jump to a queue position, dropping everything before it (DJ-only)
!prev previous, back Requeue the last-played track
!stop Clear the queue and disconnect
!loop Cycle loop mode: Off → Single track → Entire queue (DJ-only)
!repeat rp Toggle single-track loop on/off for the current track
!replay Re-queue the current track to play immediately next (DJ-only)
!seek <time> Jump to a position in the current track — 1:30, 90, or relative +30/-15 (requester or DJ)
!volume [0-200] vol Show the current volume, or set it (DJ-only)
!nowplaying np Show the now-playing embed

Queue

Command Aliases Description
!queue q Show the current queue
!clear Clear the entire queue (DJ-only)
!shuffle Shuffle the queue (DJ-only)
!move <from> <to> Move a track to a different queue position (DJ-only)
!remove <position> Remove a track (requester or DJ)
!history Show recently played tracks (session only)
!toptracks top Show the all-time most-played tracks for this server
!toprequestors topreqs Show the all-time top track requestors for this server

Search

Command Aliases Description
!search <query> find, s Browse up to 10 interactive results before queuing

Playlists

Command Aliases Description
!playlist save <name> Save the current queue as a named server playlist
!playlist load <name> Load a saved playlist into the queue
!playlist list List saved playlists for this server
!playlist show <name> Preview the tracks in a saved playlist
!playlist delete <name> Delete a saved playlist

Curation

Command Aliases Description
!vibe <query> vb Discover similar tracks via Last.fm and queue them interactively. Cooldown: 1 use / 15s per guild
!vibe-save <name> vsave Save the current vibe session's tracks as a named playlist
!vibe-load <name> vload Load and re-queue a saved vibe playlist

Admin & Settings

Command Aliases Description
!setprefix <prefix> Change the command prefix for this server (Manage Server)
!setdj <role> Set the DJ role (Manage Server)
!cleardj Remove the DJ role (Manage Server)
!dj Show the current DJ role
!stay Toggle 24/7 mode — bot stays connected when the queue empties (Manage Server)
!autoplay Toggle per-server autoplay — queues a similar track when the queue empties (Manage Server)
!stats Show bot process stats: versions, guild count, voice connections, RSS, latency (owner only)
!ping Check gateway latency
!commands cmds Open the command help menu

Project Structure

Scan report · 2026-09-16
  • Prohibited terms or links
  • Repository eligibility
  • slopscore.md paperwork
  • Content policy
  • Risk review

From the balcony · 2 of 4 clapped

  1. Princessclapped
    Has MIT license, declared status 'works-on-my-machine', clear installation instructions for multiple platforms, and demonstrates actual functionality with specific features and architecture.
  2. Crusoeclapped
    No vulnerable dependencies, self-hosted with local SQLite storage, no credential requests, and clear data handling story.

Cap'm Slop and Schnitzel read it and passed. Their reasons are on the balcony, with every other verdict.

Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.

0 comments

log in to comment.

report this listinglog in to report