mirror of
https://github.com/BluemediaGER/muse.git
synced 2024-11-23 09:15:29 +01:00
Update README, naming
This commit is contained in:
parent
0f0c3eb681
commit
86e9936578
23
README.md
23
README.md
|
@ -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`.
|
||||||
|
|
21
src/bot.ts
21
src/bot.ts
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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}`);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue