Skip to content
This repository was archived by the owner on Feb 9, 2023. It is now read-only.

Commit 471fa06

Browse files
committed
chore: add docker-compose
Include a docker-compose.yml file for running the bot with Redis storage
1 parent 6f25fd4 commit 471fa06

File tree

5 files changed

+91
-11
lines changed

5 files changed

+91
-11
lines changed

Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ RUN mv src/tsconfig.json tsconfig.json && \
1414
COPY docker/entrypoint.sh /usr/local/bin/
1515
ENTRYPOINT [ "entrypoint.sh" ]
1616

17+
ENV NODE_ENV production
1718
CMD [ "node", "dist/index.js" ]

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,16 @@ The easiest way to run Dwight is via Docker. We host images on
3535
First you need to register the application commands. You can either do this globally,
3636
which can take a couple hours to propegate or for a single guild which is instant.
3737

38-
docker run -e BOT_TOKEN=your-bot-token -e CLIENT_ID=your-client-id ghcr.io/mloberg/dwight-bot [guild]
38+
docker run --rm -e BOT_TOKEN=your-bot-token -e CLIENT_ID=your-client-id ghcr.io/mloberg/dwight-bot install [guild]
3939

4040
Once installed, run the app.
4141

4242
docker run -e BOT_TOKEN=your-bot-token ghcr.io/mloberg/dwight-bot
4343

44+
You can also use Docker Compose. Grab [docker-compose.yml](docker-compose.yml),
45+
create a `.env` from [.env.dist](.env.dist), and run `docker-compose up -d`. To
46+
register the commands, run `docker-compose run --rm bot install`.
47+
4448
### Manually
4549

4650
To run Dwight without Docker, you'll need NodeJS 16.
@@ -62,7 +66,7 @@ can either save it to Redis or to disk as a JSON file. To save in redis, pass
6266
(or set) `DB_URL` to `redis://user:pass@localhost:6379`. To save to disk set
6367
`file:./path/to/file.json`.
6468

65-
docker run -e BOT_TOKEN=your-bot-token -e DB_URL=file:.data/store.json -v $PWD/.data:/app.data ghcr.io/mloberg/dwight-bot
69+
docker run -e BOT_TOKEN=your-bot-token -e DB_URL=file:.data/store.json -v $PWD/.data:/app/.data ghcr.io/mloberg/dwight-bot
6670

6771
## Development
6872

docker-compose.yml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: '3'
2+
services:
3+
bot:
4+
image: ghcr.io/mloberg/dwight-bot
5+
env_file: .env
6+
environment:
7+
DB_URL: redis://redis:6379
8+
redis:
9+
image: redis:alpine
10+
volumes:
11+
- persist:/data
12+
volumes:
13+
persist:

docker/entrypoint.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#!/bin/sh
22
set -e
33

4-
if [ "${1}" = "install" ]; then
5-
GUILD_ID="${2}" node dist/bin/install.js
4+
if [ "$1" = "install" ]; then
5+
GUILD_ID="$2" node dist/bin/install.js
66
exit 0
77
fi
88

src/bin/install.ts

+69-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/* eslint-disable no-process-exit */
22
/* eslint-disable unicorn/no-process-exit */
33

4+
import { SlashCommandBuilder } from '@discordjs/builders';
45
import { REST } from '@discordjs/rest';
5-
import { Routes } from 'discord-api-types/v9';
6+
import { APIApplicationCommand, APIGuild, APIPartialGuild, Routes } from 'discord-api-types/v9';
67

78
import commands from '../commands';
89
import config from '../config';
@@ -14,17 +15,78 @@ if (!clientID) {
1415
process.exit(1);
1516
}
1617

18+
class API {
19+
private rest: REST;
20+
21+
constructor(token: string, private clientID: string, private guildID?: string) {
22+
this.rest = new REST({ version: '9' }).setToken(token);
23+
}
24+
25+
async list(): Promise<APIApplicationCommand[]> {
26+
const route = this.guildID
27+
? Routes.applicationGuildCommands(this.clientID, this.guildID)
28+
: Routes.applicationCommands(this.clientID);
29+
30+
const response = await this.rest.get(route);
31+
return response as APIApplicationCommand[];
32+
}
33+
34+
async install(commands: SlashCommandBuilder[]): Promise<APIApplicationCommand[]> {
35+
const route = this.guildID
36+
? Routes.applicationGuildCommands(this.clientID, this.guildID)
37+
: Routes.applicationCommands(this.clientID);
38+
const body = commands.map((command) => command.toJSON());
39+
40+
const response = await this.rest.put(route, { body });
41+
return response as APIApplicationCommand[];
42+
}
43+
44+
async uninstall(command: APIApplicationCommand): Promise<void> {
45+
const route = command.guild_id
46+
? Routes.applicationGuildCommand(this.clientID, command.guild_id, command.id)
47+
: Routes.applicationCommand(this.clientID, command.id);
48+
49+
await this.rest.delete(route);
50+
}
51+
52+
async permission(command: string, guildID: string, user: string, permission = true): Promise<void> {
53+
const route = Routes.applicationCommandPermissions(this.clientID, guildID, command);
54+
const body = { permissions: [{ id: user, type: 2, permission }] };
55+
56+
await this.rest.put(route, { body });
57+
}
58+
59+
async guilds(): Promise<string[]> {
60+
const response = await this.rest.get(Routes.userGuilds());
61+
const guilds = response as APIPartialGuild[];
62+
return guilds.map((guild) => guild.id);
63+
}
64+
65+
async guild(guildID: string): Promise<APIGuild> {
66+
const response = await this.rest.get(Routes.guild(guildID));
67+
return response as APIGuild;
68+
}
69+
}
70+
1771
(async () => {
18-
const rest = new REST({ version: '9' }).setToken(config.token);
19-
const route = !config.guildID
20-
? Routes.applicationCommands(clientID)
21-
: Routes.applicationGuildCommands(clientID, config.guildID);
72+
const api = new API(config.token, clientID, config.guildID);
2273

2374
try {
75+
if (process.argv.includes('--uninstall')) {
76+
for (const command of await api.list()) {
77+
logger.info(
78+
{ id: command.id, command: command.name, guild: command.guild_id },
79+
'Deleting application (/) command',
80+
);
81+
await api.uninstall(command);
82+
}
83+
return;
84+
}
85+
2486
logger.info({ guild: config.guildID, commands: [...commands.keys()] }, 'Installing application (/) commands');
25-
await rest.put(route, { body: commands.map((command) => command.config.toJSON()) });
87+
await api.install(commands.map((command) => command.config));
2688
} catch (error) {
27-
logger.fatal(error as Error);
89+
logger.fatal(error);
2890
process.exit(1);
2991
}
3092
})();

0 commit comments

Comments
 (0)