Update README, naming

This commit is contained in:
Max Isom 2022-01-18 22:09:07 -06:00
parent 0f0c3eb681
commit 86e9936578
No known key found for this signature in database
GPG key ID: 25C9B1A7F6798880
5 changed files with 3416 additions and 3514 deletions

View file

@ -4,7 +4,7 @@
Muse is a **highly-opinionated midwestern self-hosted** Discord music bot **that doesn't suck**. It's made for small to medium-sized Discord servers/guilds (think about a group the size of you, your friends, and your friend's friends). Muse is a **highly-opinionated midwestern self-hosted** Discord music bot **that doesn't suck**. It's made for small to medium-sized Discord servers/guilds (think about a group the size of you, your friends, and your friend's friends).
### Features ## Features
- 🎥 Livestreams - 🎥 Livestreams
- ⏩ Seeking within a song/video - ⏩ Seeking within a song/video
@ -16,11 +16,11 @@ Muse is a **highly-opinionated midwestern self-hosted** Discord music bot **that
- ✍️ Written in TypeScript, easily extendable - ✍️ Written in TypeScript, easily extendable
- ❤️ Loyal Packers fan - ❤️ Loyal Packers fan
### Design Philosophy ## Design Philosophy
I believe it makes much more sense to let Discord handle user permissions (whenever possible) rather than building them into a bot and adding additional complexity. Instead of only allowing users with a certain role to control Muse, Muse allows anyone who has access to its bound channel to control it. Instead of specifying the owner as a user ID in the config, Muse simply looks at the guild owner. I believe it makes much more sense to let Discord handle user permissions (whenever possible) rather than building them into a bot and adding additional complexity. Instead of only allowing users with a certain role to control Muse, Muse allows anyone who has access to its bound channel to control it. Instead of specifying the owner as a user ID in the config, Muse simply looks at the guild owner.
### Running ## Running
Muse is written in TypeScript. You can either run Muse with Docker (recommended) or directly with Node.js. Both methods require API keys passed in as environment variables: Muse is written in TypeScript. You can either run Muse with Docker (recommended) or directly with Node.js. Both methods require API keys passed in as environment variables:
@ -30,14 +30,14 @@ Muse is written in TypeScript. You can either run Muse with Docker (recommended)
Muse will log a URL when run. Open this URL in a browser to invite Muse to your server. Muse will DM the server owner after it's added with setup instructions. Muse will log a URL when run. Open this URL in a browser to invite Muse to your server. Muse will DM the server owner after it's added with setup instructions.
#### Versioning ### Versioning
The `master` branch acts as the developing / bleeding edge branch and is not guaranteed to be stable. The `master` branch acts as the developing / bleeding edge branch and is not guaranteed to be stable.
When running a production instance, I recommend that you use the [latest release](https://github.com/codetheweb/muse/releases/). When running a production instance, I recommend that you use the [latest release](https://github.com/codetheweb/muse/releases/).
#### Docker ### 🐳 Docker
There are a variety of image tags available: There are a variety of image tags available:
- `:2`: versions >= 2.0.0 - `:2`: versions >= 2.0.0
@ -48,7 +48,7 @@ There are a variety of image tags available:
(Replace empty config strings with correct values.) (Replace empty config strings with correct values.)
```bash ```bash
docker run -it -v "$(pwd)/data":/data -e DISCORD_TOKEN='' -e SPOTIFY_CLIENT_ID='' -e SPOTIFY_CLIENT_SECRET='' -e YOUTUBE_API_KEY='' -e NODE_ENV='development' codetheweb/muse:latest docker run -it -v "$(pwd)/data":/data -e DISCORD_TOKEN='' -e SPOTIFY_CLIENT_ID='' -e SPOTIFY_CLIENT_SECRET='' -e YOUTUBE_API_KEY='' codetheweb/muse:latest
``` ```
This starts Muse and creates a data directory in your current directory. This starts Muse and creates a data directory in your current directory.
@ -69,10 +69,9 @@ services:
- YOUTUBE_API_KEY= - YOUTUBE_API_KEY=
- SPOTIFY_CLIENT_ID= - SPOTIFY_CLIENT_ID=
- SPOTIFY_CLIENT_SECRET= - SPOTIFY_CLIENT_SECRET=
# - NODE_ENV=production
``` ```
#### Node.js ### Node.js
**Prerequisites**: Node.js, ffmpeg **Prerequisites**: Node.js, ffmpeg
@ -85,6 +84,12 @@ services:
**Note**: if you're on Windows, you may need to manually set the ffmpeg path. See [#345](https://github.com/codetheweb/muse/issues/345) for details. **Note**: if you're on Windows, you may need to manually set the ffmpeg path. See [#345](https://github.com/codetheweb/muse/issues/345) for details.
#### Advanced ## ⚙️ Additional configuration (advanced)
### Cache
By default, Muse limits the total cache size to around 2 GB. If you want to change this, set the environment variable `CACHE_LIMIT`. For example, `CACHE_LIMIT=512MB` or `CACHE_LIMIT=10GB`. By default, Muse limits the total cache size to around 2 GB. If you want to change this, set the environment variable `CACHE_LIMIT`. For example, `CACHE_LIMIT=512MB` or `CACHE_LIMIT=10GB`.
### Bot-wide commands
If you have Muse running in a lot of guilds (10+) you may want to switch to registering commands bot-wide rather than for each guild. (The downside to this is that command updates can take up to an hour to propogate.) To do this, set the environment variable `REGISTER_COMMANDS_ON_BOT` to `true`.

View file

@ -13,32 +13,24 @@ import Config from './services/config.js';
import {generateDependencyReport} from '@discordjs/voice'; import {generateDependencyReport} from '@discordjs/voice';
import {REST} from '@discordjs/rest'; import {REST} from '@discordjs/rest';
import {Routes} from 'discord-api-types/v9'; import {Routes} from 'discord-api-types/v9';
import {Promise} from 'bluebird';
@injectable() @injectable()
export default class { export default class {
private readonly client: Client; private readonly client: Client;
private readonly token: string; private readonly token: string;
private readonly isProduction: boolean; private readonly shouldRegisterCommandsOnBot: boolean;
private readonly commandsByName!: Collection<string, Command>; private readonly commandsByName!: Collection<string, Command>;
private readonly commandsByButtonId!: Collection<string, Command>; private readonly commandsByButtonId!: Collection<string, Command>;
constructor(@inject(TYPES.Client) client: Client, @inject(TYPES.Config) config: Config) { constructor(@inject(TYPES.Client) client: Client, @inject(TYPES.Config) config: Config) {
this.client = client; this.client = client;
this.token = config.DISCORD_TOKEN; this.token = config.DISCORD_TOKEN;
this.isProduction = config.IS_PRODUCTION; this.shouldRegisterCommandsOnBot = config.REGISTER_COMMANDS_ON_BOT;
this.commandsByName = new Collection(); this.commandsByName = new Collection();
this.commandsByButtonId = new Collection(); this.commandsByButtonId = new Collection();
} }
public async listen(): Promise<void> { public async listen(): Promise<void> {
// Log environment
if (this.isProduction) {
console.log('Production environment\n');
} else {
console.log('Development environment\n');
}
// Load in commands // Load in commands
container.getAll<Command>(TYPES.Command).forEach(command => { container.getAll<Command>(TYPES.Command).forEach(command => {
// TODO: remove ! // TODO: remove !
@ -122,18 +114,19 @@ export default class {
this.client.once('ready', async () => { this.client.once('ready', async () => {
debug(generateDependencyReport()); debug(generateDependencyReport());
spinner.text = '📡 Updating commands in all guilds...';
// Update commands // Update commands
const rest = new REST({version: '9'}).setToken(this.token); const rest = new REST({version: '9'}).setToken(this.token);
if (this.isProduction) { if (this.shouldRegisterCommandsOnBot) {
spinner.text = '📡 updating commands on bot...';
await rest.put( await rest.put(
Routes.applicationCommands(this.client.user!.id), Routes.applicationCommands(this.client.user!.id),
{body: this.commandsByName.map(command => command.slashCommand ? command.slashCommand.toJSON() : null)}, {body: this.commandsByName.map(command => command.slashCommand ? command.slashCommand.toJSON() : null)},
); );
} else { } else {
// If development, set commands guild-wide spinner.text = '📡 updating commands in all guilds...';
await Promise.all( await Promise.all(
this.client.guilds.cache.map(async guild => { this.client.guilds.cache.map(async guild => {
await rest.put( await rest.put(

View file

@ -17,7 +17,7 @@ export default async (guild: Guild): Promise<void> => {
const config = container.get<Config>(TYPES.Config); const config = container.get<Config>(TYPES.Config);
// Setup slash commands // Setup slash commands
if (!config.IS_PRODUCTION) { if (!config.REGISTER_COMMANDS_ON_BOT) {
const commands: ApplicationCommandData[] = container.getAll<Command>(TYPES.Command) const commands: ApplicationCommandData[] = container.getAll<Command>(TYPES.Command)
.filter(command => command.slashCommand?.name) .filter(command => command.slashCommand?.name)
.map(command => command.slashCommand as ApplicationCommandData); .map(command => command.slashCommand as ApplicationCommandData);

View file

@ -12,7 +12,7 @@ const CONFIG_MAP = {
YOUTUBE_API_KEY: process.env.YOUTUBE_API_KEY, YOUTUBE_API_KEY: process.env.YOUTUBE_API_KEY,
SPOTIFY_CLIENT_ID: process.env.SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_ID: process.env.SPOTIFY_CLIENT_ID,
SPOTIFY_CLIENT_SECRET: process.env.SPOTIFY_CLIENT_SECRET, SPOTIFY_CLIENT_SECRET: process.env.SPOTIFY_CLIENT_SECRET,
IS_PRODUCTION: process.env.NODE_ENV === 'production', REGISTER_COMMANDS_ON_BOT: process.env.REGISTER_COMMANDS_ON_BOT === 'true',
DATA_DIR, DATA_DIR,
CACHE_DIR: path.join(DATA_DIR, 'cache'), CACHE_DIR: path.join(DATA_DIR, 'cache'),
CACHE_LIMIT_IN_BYTES: xbytes.parseSize(process.env.CACHE_LIMIT ?? '2GB'), CACHE_LIMIT_IN_BYTES: xbytes.parseSize(process.env.CACHE_LIMIT ?? '2GB'),
@ -24,7 +24,7 @@ export default class Config {
readonly YOUTUBE_API_KEY!: string; readonly YOUTUBE_API_KEY!: string;
readonly SPOTIFY_CLIENT_ID!: string; readonly SPOTIFY_CLIENT_ID!: string;
readonly SPOTIFY_CLIENT_SECRET!: string; readonly SPOTIFY_CLIENT_SECRET!: string;
readonly IS_PRODUCTION!: boolean; readonly REGISTER_COMMANDS_ON_BOT!: boolean;
readonly DATA_DIR!: string; readonly DATA_DIR!: string;
readonly CACHE_DIR!: string; readonly CACHE_DIR!: string;
readonly CACHE_LIMIT_IN_BYTES!: number; readonly CACHE_LIMIT_IN_BYTES!: number;
@ -40,6 +40,8 @@ export default class Config {
this[key as ConditionalKeys<typeof CONFIG_MAP, number>] = value; this[key as ConditionalKeys<typeof CONFIG_MAP, number>] = value;
} else if (typeof value === 'string') { } else if (typeof value === 'string') {
this[key as ConditionalKeys<typeof CONFIG_MAP, string>] = value; this[key as ConditionalKeys<typeof CONFIG_MAP, string>] = value;
} else if (typeof value === 'boolean') {
this[key as ConditionalKeys<typeof CONFIG_MAP, boolean>] = value;
} else { } else {
throw new Error(`Unsupported type for ${key}`); throw new Error(`Unsupported type for ${key}`);
} }

6878
yarn.lock

File diff suppressed because it is too large Load diff