A command-line tool for managing Project Loved operations including news generation, forum thread management, mapper notifications, and voting results processing.
This project is the successor to clayton's Project Loved CLI, rewritten with safety, flexibility, and code readability in mind.
- Node.js >= 18 (developed with 22.19.0)
- pnpm (
npm install -g pnpm)
- Node Version Manager (nvm) or nvm-windows
pnpm installRun the interactive setup to configure the application:
pnpm loved setupThis will prompt you for the following required fields:
- loved.sh API Key - API key from loved.sh
- loved.sh Admin API Key - API key provided to admins directly (ask Hivie)
osu-wikidirectory path - local path to your osu-wiki fork- Loved Round ID - the current Loved round ID (must update manually every round)
- Enable automatic Git update checks - whether to check for script updates automatically
The configuration is stored in config/config.json. You can also edit this file directly.
| Field | Description | Required | Default |
|---|---|---|---|
lovedWebApiKey |
loved.sh API key | ✅ | - |
lovedWebBaseUrl |
loved.sh base URL | ✅ | https://loved.sh |
lovedAdminApiKey |
loved.sh admin API key | ✅ | - |
lovedAdminBaseUrl |
loved.sh admin base URL | ✅ | https://admin.loved.sh |
lovedRoundId |
Current round ID | ✅ | - |
osuBaseUrl |
osu! base URL | ✅ | https://osu.ppy.sh |
osuWikiPath |
Absolute path to osu-wiki repository | ✅ | - |
updates |
Enable automatic Git update checks | ✅ | true |
bannerTitleOverrides |
Custom banner titles by beatmapset ID | ❌ | - |
webhookOverrides |
Custom webhook URLs by mode | ❌ | - |
bannerTitleOverrides is an object of key-value pairs, where the key is the beatmapset ID and the value is the banner title.
{
"524026": "Custom Banner Title",
"396221": "Another Custom Title"
}webhookOverrides is an object of key-value pairs, where the key is the mode's short name and the value is the webhook URL.
{
"osu": "https://discord.com/api/webhooks/.../...",
"taiko": "https://discord.com/api/webhooks/.../...",
"catch": "https://discord.com/api/webhooks/.../...",
"mania": "https://discord.com/api/webhooks/.../..."
}pnpm loved <command> [options]If you want to run commands through a separate development config, use pnpm loved:dev <command> [options], which will use config/config.dev.json instead of config/config.json.
-h, --helpShow help for a command-r, --round <id>Override the round ID from config--skip-updateSkip automatic Git update check
pnpm loved setup
Interactive wizard that writes config/config.json. Prompts for API keys, wiki path, round ID, and whether to enable automatic Git update checks.
Beatmapset commands.
pnpm loved maps download [options]— download beatmapset backgrounds tobackgrounds/{roundId}/.-r, --round <id>Override round ID--skip-updateSkip Git update check
pnpm loved maps open [options]— open all nominated beatmapsets in your browser.-r, --round <id>Override round ID--skip-updateSkip Git update check
pnpm loved messages [options]
Send chat announcements to nominated mappers.
-r, --round <id>Override round ID--poll-start <guess>Provide poll start time and skip the prompt--dry-runPreview without sending (outputs the operation logs inlogs/YYYY-MM-DD/HH-MM-SS-messages.json)--skip-updateSkip Git update check
pnpm loved news [options]
Generate banners, news post, and optionally create forum threads / Discord announcements.
-r, --round <id>Override round ID--threadsCreate forum threads and post Discord announcements--banners-onlyOnly generate banners--discord-onlyOnly post Discord announcements--skip-bannersSkip banner generation--skip-discordSkip Discord announcements--dry-runPreview thread creation (requires--threads) (outputs the operation logs inlogs/YYYY-MM-DD/HH-MM-SS-poll-start.json)--skip-updateSkip Git update check
pnpm loved results [options]
Process voting results (forum, chat, Discord, and loved.sh state).
-r, --round <id>Override round ID--forceAllow concluding polls despite timers (forum/chat only)--skip-discordSkip Discord announcements--skip-threadsSkip forum operations--skip-messagesSkip chat announcements--discord-onlyOnly post Discord results--threads-onlyOnly perform forum operations--messages-onlyOnly send chat announcements--dry-runPreview without making changes (outputs the operation logs inlogs/YYYY-MM-DD/HH-MM-SS-poll-end.json)--skip-updateSkip Git update check