Welcome to Sunny's organization on GitHub.
Sunny is a microservices-based osu! Discord bot ecosystem with integrated web services. The services communicate via shared database, Redis cache/pub-sub, and RESTful APIs.
ℹ️ You may want to join our Discord server.
flowchart TB
subgraph External["External Clients"]
discord(["Discord Users"])
webbrowser(["Web Browser"])
end
subgraph Public["Public-Facing Services"]
bot["bot<br/>(Python/discord.py)"]
website["website<br/>(Vue.js) :3000"]
end
subgraph Internal["Internal Services"]
api["api<br/>(Python/FastAPI) :3001"]
lavalink["lavalink<br/>(Java/Lavalink) :2333"]
end
subgraph Data["Data Stores"]
mongo[("MongoDB<br/>Primary Database")]
redis[("Redis<br/>Cache & Pub/Sub")]
end
subgraph Init["Initialization"]
mongoseed["mongo-seed<br/>(Setup Script)"]
end
subgraph ExternalAPIs["External APIs"]
osuapi["osu! API v1/v2"]
youtube["YouTube/Music Sources"]
soundcloud["SoundCloud"]
bandcamp["Bandcamp"]
end
%% Client connections
discord --> bot
webbrowser --> website
%% Service-to-service HTTP
bot -->|"audio streaming"| lavalink
website -->|"command list"| api
website -->|"bot stats"| api
api -->|"OAuth callbacks"| website
%% External APIs
bot -->|"beatmaps & users"| osuapi
lavalink --> youtube
lavalink --> soundcloud
lavalink --> bandcamp
%% Data store connections
bot --> mongo
api -->|"saves OAuth tokens"| mongo
bot -->|"writes stats/commands"| redis
api -->|"reads stats/commands"| redis
mongoseed -->|"initializes"| mongo
%% Styling
classDef client fill:#e1f5fe,stroke:#01579b,color:#01579b
classDef public fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
classDef internal fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
classDef data fill:#fce4ec,stroke:#c2185b,color:#880e4f
classDef external fill:#eceff1,stroke:#546e7a,color:#37474f
classDef init fill:#fff8e1,stroke:#f57f17,color:#f57f17
class discord,webbrowser client
class bot,website public
class api,lavalink internal
class mongo,redis data
class osuapi,youtube,soundcloud,bandcamp external
class mongoseed init
| Service | Language | Purpose |
|---|---|---|
| bot | Python/discord.py | Discord bot for moderation, osu!, and music |
| api | Python/FastAPI | Statistics API and OAuth2 handler for website |
| website | Vue.js | Public website for commands and stats |
| lavalink | Java/Lavalink | Audio streaming service for music playback |
| mongo | MongoDB | Primary database for persistent storage |
| redis | Redis | Cache for bot stats/commands |
| mongo-seed | Node.js/Shell | Database initialization and seeding |
- MongoDB: Primary database for persistent storage (user data, OAuth tokens)
- Redis Cache: Bot writes stats/commands; API reads for website
- HTTP REST: Website fetches data from API; API handles OAuth callbacks
- Lavalink Protocol: WebSocket connection for audio streaming
- osu! API: Direct integration using aiosu library (stateless client management)
Bot → Redis → API → Website:
- Bot updates command list and statistics in Redis
- API reads from Redis when website requests data
- Website displays information to users
osu! Data Flow:
- User requests osu! information via Discord
- Bot directly queries osu! API v1/v2 using aiosu
- Bot processes and displays data in Discord
OAuth Flow:
- User initiates osu! login from Discord bot
- Bot requests OAuth URL from API (with encrypted Discord user ID as state)
- User authenticates with osu! via browser
- osu! redirects to API callback endpoint with authorization code
- API decrypts state to get Discord user ID
- API exchanges code for token using aiosu's
process_code - API saves token directly to MongoDB with Discord ID mapping
- Bot retrieves tokens from MongoDB via aiosu for stateless API requests
- User sees success message in browser
| Caller | Target | Purpose |
|---|---|---|
| bot | lavalink | Audio track loading and playback |
| website | api | Command list retrieval |
| website | api | Bot statistics display |
| osu! | api | OAuth2 callback redirect |
| Key Pattern | Writer | Reader | TTL | Purpose |
|---|---|---|---|---|
sunny:guild-count |
bot | api | - | Total number of guilds bot is in |
sunny:cog-count |
bot | api | - | Number of loaded cogs/extensions |
sunny:command-count |
bot | api | - | Total number of available commands |
sunny:user-count |
bot | api | - | Total number of users bot can see |
sunny:{channel_id}:beatmap |
bot | bot | - | Last referenced beatmap in Discord channel |
sunny:{osu_id}:{mode_id}:graph |
bot | bot | 24h | Cached user ranking graph (PNG bytes) |
osu! Integration (27 commands):
- Direct osu! API v1/v2 integration (no intermediate service)
- Beatmap information and difficulty calculation
- Player statistics and rankings (all 4 modes: std, taiko, ctb, mania)
- Recent score tracking and comparisons
- Top plays display per gamemode
- PP calculation and simulation (pp, whatif, ppforrank, bonus, recalcpp)
- Replay recording and rendering with customizable skins
- Recording settings (volume, dim, cursor size)
- Ranking graph generation with Redis caching (24h)
- Per-channel beatmap context for convenient commands
Music Playback (19 commands):
- YouTube, SoundCloud, and Bandcamp support
- Queue management and playback controls
- Audio streaming via Lavalink
- Premium audio effects (nightcore, vaporwave, lowpass, vibrato, tremolo)
- Track recommendations (premium)
- DJ role permissions
- Auto-disconnect and autoplay features
- Volume control and seeking
- Loop modes and shuffle
Fun & Games (12 commands):
- osudle guessing game (song and background modes)
- Coin flips, dice rolls, magic 8ball
- User duels and love calculator
- Reaction polls
Moderation (2 commands):
- Message pruning
- Bot message cleanup
Image & Social (5 commands):
- Avatar display
- Animal images
- Reaction GIFs (wink, pat, hug)
Server Management:
- Custom prefix configuration
- Premium server boosting
- Message listener toggles
- User data management (GDPR-compliant forgetme)
Utility:
- Weather forecasts with unit customization
- Bot and server information
- Comprehensive help system
- User initiates OAuth2 flow via Discord bot command
- Bot uses a shared secret with the API and generates an osu! OAuth URL with an encrypted ID as the state
- Bot sends URL to user in Discord
- User clicks link and authenticates with osu! in browser
- osu! redirects to API callback endpoint with authorization code and encrypted state
- API decrypts state to retrieve Discord user ID (session_id)
- API uses aiosu's
process_code()to exchange code for OAuth token - API saves token to MongoDB using aiosu's
add_client()with Discord ID mapping - User sees success message in browser
- Bot retrieves tokens from MongoDB using aiosu for future API requests (stateless)
- User requests beatmap information in Discord
- Bot directly queries osu! API v1/v2 using aiosu
- Bot processes beatmap data and calculates difficulty
- Bot stores beatmap data in Redis per-channel context
- Bot displays formatted information in Discord embed
- Future commands in same channel can reference "recent map"
- No API service involvement - direct bot ↔ osu! communication
- Bot tracks command usage, guild count, cog count, and user count
- Bot writes statistics to Redis keys:
sunny:guild-count- Number of guildssunny:user-count- Number of userssunny:cog-count- Number of loaded cogssunny:command-count- Number of commands
- Website requests data from API
- API reads current values from Redis
- API returns JSON to website
- Website displays real-time bot statistics
Discord channels maintain context of the last referenced beatmap:
- User mentions a beatmap in a Discord channel (via link, ID, or command)
- Bot stores beatmap data in Redis:
sunny:{channel_id}:beatmap - Users can reference "recent map" without specifying ID
- Bot retrieves last beatmap from channel context
- Enables convenient follow-up commands on same map
User ranking graphs are cached to improve performance:
- User requests ranking graph (e.g., PP progression over time)
- Bot generates graph visualization (PNG)
- Bot caches graph in Redis:
sunny:{osu_id}:{mode_id}:graph - Cache expires after 24 hours (86400 seconds)
- Subsequent requests within 24h return cached graph
- Reduces computational load and API calls
Both bot and API use the aiosu library for osu! API interaction:
Bot Usage:
- Retrieves OAuth tokens from MongoDB
- Creates stateless API clients for authenticated requests
- Fetches beatmap metadata, user statistics, recent scores
- Caches results in Redis for performance
API Usage:
- Handles OAuth2 callback from osu!
- Exchanges authorization code for access token
- Saves tokens to MongoDB with Discord ID mapping
- Manages token lifecycle and refresh
| Category | Technologies |
|---|---|
| Languages | Python, TypeScript, Java |
| Web Frameworks | FastAPI, Vue.js |
| Databases | MongoDB, Redis |
| Discord Library | discord.py |
| osu! Library | aiosu (async osu! API wrapper) |
| Audio Streaming | Lavalink |
| Container | Docker, Docker Compose |
| CI/CD | GitHub Actions, Webhook deployment |
| External APIs | osu! API v1/v2, YouTube, SoundCloud, Bandcamp |
Services are containerized and orchestrated via Docker Compose with two compose files:
Main Stack (docker-compose.yml):
Docker Host
├── bot (discord.py) - Main Discord bot
├── api (FastAPI) - REST API service :3001
├── website (Vue.js) - Frontend :3000
├── mongo (MongoDB) - Primary database
├── redis (Redis) - Cache and pub/sub
└── mongo-seed (initialization) - Database seeding
Audio Stack (docker-compose.lavalink.yml):
Docker Host
└── lavalink (Java/Lavalink) - Audio streaming :2333
Main Stack Features:
- Container registry: GitHub Container Registry (ghcr.io/sunnycord)
- Images: bot, api, website (tagged with
:master) - Health checks for MongoDB and Redis
- Dependency management with wait conditions
- Volume mounts for configs and data persistence
- Network:
sunny-network(bridge mode)
Deployment Flow:
GitHub Repository → GitHub Actions → Webhook → Docker Host
- Manual workflow dispatch from GitHub Actions
- Restricted to repository owner (
NiceAesth) - Webhook POST to deployment server with secret authentication
- Server pulls latest
:masterimages from GHCR - Docker Compose performs rolling restart
- Services restart with dependency order
Deployment Trigger:
# Manual deployment via GitHub Actions
workflow_dispatch → webhook → redeploy.shAll images hosted on GitHub Container Registry:
ghcr.io/sunnycord/bot:masterghcr.io/sunnycord/api:masterghcr.io/sunnycord/website:master
Base images:
mongo:latest- Official MongoDBredis:latest- Official Redisghcr.io/lavalink-devs/lavalink:latest- Official Lavalink
| Language | Standards |
|---|---|
| Python | Python 3.9+, type hints, Black formatter, isort |
| TypeScript | ESLint, Prettier, Vue 3 Composition API |
| C# | .NET 6+, async/await, standard C# conventions |
The stack uses health checks and dependency conditions:
mongo-seed → requires mongo (healthy)
bot → requires mongo-seed (completed) + redis (healthy)
api → requires bot (started)
website → requires api (started)
Production Deployment: Trigger the GitHub Actions workflow to redeploy:
- Go to Actions tab in GitHub
- Select "Redeploy Sunny Stack"
- Click "Run workflow"
- Webhook triggers
redeploy.sh
Command Categories:
The bot organizes commands into 10 categories with 70+ total commands:
-
Admin (2 commands) - Server management
/prune- Delete multiple messages/clean- Clean bot messages
-
Fun (8 commands) - Entertainment
/ping- Bot latency/poll- Reaction polls/roll- Random number/flip- Coin flip/8ball- Magic 8ball/duel- User duel/love- Love calculator/rollduel- Roll duel
-
Image (5 commands) - Images and reactions
/avatar- User avatars/animal- Random animals/wink,/pat,/hug- Reaction GIFs
-
Information (3 commands) - Bot and server info
/botinfo- Bot statistics/serverinfo- Server information/help- Command help
-
Music (19 commands) - Audio playback
/play- Play from YouTube/SoundCloud/Bandcamp/playing- Current track/queue- View queue/search- Search tracks/seek- Seek position/pause,/resume,/skip,/stop- Playback control/shuffle- Shuffle queue/volume- Adjust volume/loop- Set loop mode/autoplay- Toggle autoplay/autodisconnect- Auto-disconnect settings/djrole- Set DJ role/clearfilters- Remove filters- Premium effects:
/nightcore,/vaporwave,/lowpass,/vibrato,/tremolo,/recommend
-
osu! (27 commands) - osu! integration
/recent- Recent scores (with list and failed options)/pinned- Pinned scores/compare- Compare scores/scores- Beatmap scores/pp- PP calculation/whatif- PP simulation/ppforrank- PP for rank/bonus- Bonus PP/recalcpp- Recalculate PP/record- Record replay/osu,/mania,/taiko,/ctb- Mode stats (with extended option)/osutop,/maniatop,/taikotop,/ctbtop- Top plays (with recent sort and position options)- Recording settings:
/show,/skins,/toggle,/volume,/dim,/cursorsize
-
osudle (4 commands) - Guessing game
/song- Guess by audio/background- Guess by image/skip- Skip beatmap/stop- End game
-
Premium (4 commands) - Premium features
/info- Premium info/boost- Boost server/unboost- Remove boost/guildinfo- Server premium status
-
Settings (3 commands) - Configuration
/forgetme- Delete user data (GDPR)/prefix- Change prefix/togglelisteners- Toggle listeners
-
Weather (2 commands) - Weather info
/forecast- Get forecast/units- Set units
Command Features:
- Hybrid Commands: Most commands support both slash (/) and prefix modes
- Premium Commands: Music effects and recommendations require premium
- Context-Aware: osu! commands can reference "recent map" in channel
Data Handling:
- Direct integration with osu! API v1/v2 using aiosu library
- Retrieves OAuth tokens from MongoDB for authenticated requests
- Writes statistics to Redis (guild/user/command/cog counts)
- Stores user links and persistent data in MongoDB
- Caches per-channel beatmap context in Redis
- Caches generated ranking graphs in Redis (24h TTL)
- Stateless API client management via aiosu
Endpoints:
GET /api/stats- Bot statistics from Redis (guild/user/command/cog counts)GET /api/commands- Command list with categories, parameters, and metadataGET /api/auth/callback- OAuth2 callback handler (saves to MongoDB)
Command Data Structure: Each command includes:
- Name and description
- Parameters with descriptions
is_hybridflag (slash + text command support)is_premiumflag (requires premium subscription)- Category/cog assignment
Purpose:
- Serve bot statistics and command list to website
- Handle osu! OAuth2 callbacks and token exchange
- Save OAuth tokens directly to MongoDB using aiosu
- Read-only access to Redis for statistics
Technology:
- FastAPI for REST API
- aiosu library for stateless osu! client management
- Encrypted state parameter for Discord ID tracking
- MongoDB for OAuth token persistence
Port: 3001
Pages:
- Command list with search/filtering (10 categories, 70+ commands)
- Bot statistics landing page
- Bot versioning
- Documentation (premium, team, tos, privacy policy)
Features:
- Real-time stats updates from API
- Command categorization by cog
- Premium command badges
- Hybrid command indicators
Port: 3000
Audio Sources:
- YouTube (with search support)
- SoundCloud (with search support)
- Bandcamp
- HTTP streams
Port: 2333
Copyright (c) 2023 NiceAesth, Sunny Bot Developers. Source available, all rights reserved.
See individual repository LICENSE files for complete terms.