Fixed docker containers
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# =========
|
# =========
|
||||||
# Builder
|
# Builder
|
||||||
# =========
|
# =========
|
||||||
FROM rust:bookworm as builder
|
FROM rust:bookworm AS builder
|
||||||
WORKDIR /builder
|
WORKDIR /builder
|
||||||
|
|
||||||
COPY migrations ./migrations
|
COPY migrations ./migrations
|
||||||
@@ -14,7 +14,7 @@ RUN cargo build --release
|
|||||||
# ==========
|
# ==========
|
||||||
# Packages
|
# Packages
|
||||||
# ==========
|
# ==========
|
||||||
FROM debian:bookworm-slim as packages
|
FROM debian:bookworm-slim AS packages
|
||||||
WORKDIR /packages
|
WORKDIR /packages
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
@@ -36,7 +36,7 @@ RUN apt-get update && apt-get install -y python3 curl tar xz-utils && \
|
|||||||
# =========
|
# =========
|
||||||
# Runtime
|
# Runtime
|
||||||
# =========
|
# =========
|
||||||
FROM debian:bookworm-slim as runtime
|
FROM debian:bookworm-slim AS runtime
|
||||||
WORKDIR /siren
|
WORKDIR /siren
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
|
|||||||
27
Makefile
27
Makefile
@@ -1,57 +1,56 @@
|
|||||||
#!make
|
#!make
|
||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
|
ENV := ./scripts/apply_env.sh
|
||||||
include .env
|
|
||||||
-include .env.local
|
|
||||||
export
|
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
|
|
||||||
|
export VERSION=$(if $(v),$(v),latest)
|
||||||
|
|
||||||
help: ## Help command
|
help: ## Help command
|
||||||
@echo
|
@echo
|
||||||
@cat Makefile | grep -E '^[a-zA-Z\/_-]+:.*?## .*$$' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
@cat Makefile | grep -E '^[a-zA-Z\/_-]+:.*?## .*$$' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
@echo
|
@echo
|
||||||
|
|
||||||
backend-up: ## Start the backend containers
|
backend-up: ## Start the backend containers
|
||||||
@docker compose --profile backend up -d
|
@$(ENV) docker compose --profile backend up -d
|
||||||
|
|
||||||
up-backend: backend-up
|
up-backend: backend-up
|
||||||
|
|
||||||
backend-down: ## Stop the backend containers
|
backend-down: ## Stop the backend containers
|
||||||
@docker compose --profile backend down
|
@$(ENV) docker compose --profile backend down
|
||||||
|
|
||||||
down-backend: backend-down
|
down-backend: backend-down
|
||||||
|
|
||||||
run: ## Run the project
|
run: ## Run the project
|
||||||
@echo "Running project..."
|
@echo "Running project..."
|
||||||
@cargo run
|
@$(ENV) cargo run
|
||||||
@echo "Run complete"
|
@echo "Run complete"
|
||||||
|
|
||||||
format: ## Format code
|
format: ## Format code
|
||||||
@echo "Formatting code..."
|
@echo "Formatting code..."
|
||||||
@cargo fmt
|
@$(ENV) cargo fmt
|
||||||
@echo "Format complete"
|
@echo "Format complete"
|
||||||
|
|
||||||
clean: ## Clean the project
|
clean: ## Clean the project
|
||||||
@echo "Cleaning project..."
|
@echo "Cleaning project..."
|
||||||
@cargo clean
|
@$(ENV) cargo clean
|
||||||
@echo "Clean complete"
|
@echo "Clean complete"
|
||||||
|
|
||||||
docker-up: ## Start the app
|
docker-up: ## Start the app
|
||||||
@docker compose --profile backend --profile bot up -d
|
@$(ENV) docker compose --profile backend --profile bot up -d
|
||||||
|
|
||||||
docker-down: ## Stop the app
|
docker-down: ## Stop the app
|
||||||
@docker compose --profile backend --profile bot down
|
@$(ENV) docker compose --profile backend --profile bot down
|
||||||
|
|
||||||
docker-build: ## Build the docker image
|
docker-build: ## Build the docker image
|
||||||
@docker compose build
|
@$(ENV) docker build -f Dockerfile -t siren:${VERSION} .
|
||||||
|
|
||||||
docker-clean: ## Stop the docker containers and remove volumes
|
docker-clean: ## Stop the docker containers and remove volumes
|
||||||
@echo "Stopping docker container and removing volumes..."
|
@echo "Stopping docker container and removing volumes..."
|
||||||
@docker compose --profile backend --profile bot down -v
|
@$(ENV) docker compose --profile backend --profile bot down -v
|
||||||
@echo "Docker container stopped and volumes removed"
|
@echo "Docker container stopped and volumes removed"
|
||||||
|
|
||||||
docker-refresh: docker-clean backend-up ## Refresh the docker containers
|
docker-refresh: docker-clean backend-up ## Refresh the docker containers
|
||||||
|
|
||||||
psql: ## Connect to the database
|
psql: ## Connect to the database
|
||||||
@docker exec -it siren-postgres psql -U ${DATABASE_USER} -P pager=off
|
@$(ENV) docker exec -it siren-postgres psql -U ${DATABASE_USER} -P pager=off
|
||||||
53
README.md
53
README.md
@@ -4,8 +4,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
Siren is a D&D Bot built for Discord, written in Rust. Features include:
|
Siren is a D&D Bot built for Discord, written in Rust. Features include:
|
||||||
- Play tracks from Youtube or locally hosted files
|
- Music commands from Youtube and locally hosted files
|
||||||
- Assistant DM tools to be defined later
|
- Database for D&D 5e content
|
||||||
|
- Session scheduling
|
||||||
|
- Backend API
|
||||||
- ChatGPT integration
|
- ChatGPT integration
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
@@ -80,6 +82,7 @@ Siren utilizes Discord slash commands. To view the commands, run `/help` in a se
|
|||||||
| `/resume` | Resume the current track |
|
| `/resume` | Resume the current track |
|
||||||
| `/skip` | Skip the current track |
|
| `/skip` | Skip the current track |
|
||||||
| `/stop` | Stop the current track |
|
| `/stop` | Stop the current track |
|
||||||
|
| `/mute` | Mute the current track |
|
||||||
| `/queue` | ***TODO*** - Display the current queue |
|
| `/queue` | ***TODO*** - Display the current queue |
|
||||||
| `/clear` | ***TODO*** - Clear the current queue |
|
| `/clear` | ***TODO*** - Clear the current queue |
|
||||||
| `/shuffle` | ***TODO*** - Shuffle the current queue |
|
| `/shuffle` | ***TODO*** - Shuffle the current queue |
|
||||||
@@ -111,48 +114,16 @@ Siren utilizes Discord slash commands. To view the commands, run `/help` in a se
|
|||||||
| `/help` | ***TODO*** - Display a list of commands |
|
| `/help` | ***TODO*** - Display a list of commands |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
[Rust](https://www.rust-lang.org/) must be installed to run locally. See [serenity-rs/serenity](https://github.com/serenity-rs/serenity) for more information about Rust Discord API Library.
|
- [Rust](https://www.rust-lang.org/)
|
||||||
|
- [yt-dlp](https://github.com/yt-dlp/yt-dlp)
|
||||||
The following packages must be installed for [serenity-rs/songbird](https://github.com/serenity-rs/songbird). View the repository for additional installation and setup information on other operating systems.
|
- [ffmpeg](https://github.com/yt-dlp/FFmpeg-Builds)
|
||||||
<details>
|
|
||||||
<summary>Unix Installation</summary>
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- [yt-dlp](https://github.com/yt-dlp/yt-dlp/releases) is preferred over youtube-dl.
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo apt install libopus-dev
|
|
||||||
sudo apt install ffmpeg
|
|
||||||
sudo apt apt install youtube-dl # See notes above
|
|
||||||
# PostgreSQL Headers
|
|
||||||
sudo apt install libpq5
|
|
||||||
sudo apt install libpq-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
- Potentially requires [yt-dlp](https://github.com/yt-dlp/yt-dlp#installation) and [yt-dlp FFmpeg Static Auto-Builds](https://github.com/yt-dlp/FFmpeg-Builds).
|
|
||||||
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>Mac Installation</summary>
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- [Homebrew](https://brew.sh/) must be installed to run the following commands.
|
|
||||||
- [youtube-dl](https://formulae.brew.sh/formula/youtube-dl#default) is deprecated, [yt-dlp](https://formulae.brew.sh/formula/yt-dlp) is preferred
|
|
||||||
|
|
||||||
```
|
|
||||||
brew install opus
|
|
||||||
brew install ffmpeg
|
|
||||||
brew install yt-dlp # See notes above
|
|
||||||
brew install postgresql
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Running Locally
|
### Running Locally
|
||||||
1. Start the backend containers with `make docker-refresh`
|
1. Start the backend containers with `make backend-up`
|
||||||
2. Start the application with `make run`
|
2. Run the application locally with `make run`
|
||||||
|
|
||||||
The application can also be tested from within a Docker container:
|
The application can also be tested from within a Docker container:
|
||||||
```
|
```
|
||||||
docker build -t siren:latest .
|
make docker-build
|
||||||
docker run --env-file .env -it --rm --name siren siren:latest
|
make docker-up
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -7,13 +7,8 @@ x-env_file: &env
|
|||||||
name: siren
|
name: siren
|
||||||
services:
|
services:
|
||||||
bot:
|
bot:
|
||||||
image: siren-service:${SIREN_VERSION:-latest}
|
image: siren:${SIREN_VERSION:-latest}
|
||||||
container_name: siren-bot
|
container_name: siren
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
args:
|
|
||||||
- VERSION=${SIREN_VERSION:-latest}
|
|
||||||
env_file: *env
|
env_file: *env
|
||||||
environment:
|
environment:
|
||||||
DATABASE_HOST: postgres
|
DATABASE_HOST: postgres
|
||||||
@@ -34,7 +29,7 @@ services:
|
|||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
container_name: siren-postgres
|
container_name: postgres
|
||||||
env_file: *env
|
env_file: *env
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: ${DATABASE_USER}
|
POSTGRES_USER: ${DATABASE_USER}
|
||||||
|
|||||||
19
scripts/apply_env.sh
Executable file
19
scripts/apply_env.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Enable exporting variables
|
||||||
|
set -a
|
||||||
|
|
||||||
|
# Source the default env variables
|
||||||
|
echo "Sourcing build environment"
|
||||||
|
source .env
|
||||||
|
|
||||||
|
# If there is a .env.local present, source it
|
||||||
|
echo "Sourcing custom environment"
|
||||||
|
if [ -f .env.local ]; then
|
||||||
|
source ./.env.local
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disable exporting variables
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# Run the given command
|
||||||
|
exec "$@"
|
||||||
@@ -3,7 +3,7 @@ use serenity::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{create_response, edit_response, get_songbird, process_message};
|
use super::{edit_response, get_songbird, process_message};
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
|||||||
Ok(channel_id) => {
|
Ok(channel_id) => {
|
||||||
log::debug!("<{guild_id}> Play command executed on {channel_id} with track: {track_url:?}");
|
log::debug!("<{guild_id}> Play command executed on {channel_id} with track: {track_url:?}");
|
||||||
// Handle the track url
|
// Handle the track url
|
||||||
match enqueue_track(ctx, manager, guild_id.to_owned(), track_url, true).await {
|
match enqueue_track(ctx, manager, guild_id.to_owned(), track_url).await {
|
||||||
Ok(count) => {
|
Ok(count) => {
|
||||||
let mut message = format!("Playing {} tracks", count);
|
let mut message = format!("Playing {} tracks", count);
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
@@ -84,7 +84,6 @@ pub async fn enqueue_track(
|
|||||||
manager: Arc<Songbird>,
|
manager: Arc<Songbird>,
|
||||||
guild_id: GuildId,
|
guild_id: GuildId,
|
||||||
track_url: &str,
|
track_url: &str,
|
||||||
play_now: bool,
|
|
||||||
) -> SirenResult<i32> {
|
) -> SirenResult<i32> {
|
||||||
let mut track_count = 0;
|
let mut track_count = 0;
|
||||||
if let Some(handler_lock) = manager.get(guild_id) {
|
if let Some(handler_lock) = manager.get(guild_id) {
|
||||||
@@ -140,7 +139,6 @@ pub async fn enqueue_track(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let track_handle: TrackHandle;
|
let track_handle: TrackHandle;
|
||||||
let is_queue_empty = handler.queue().is_empty();
|
|
||||||
track_handle = handler.enqueue_input(input).await;
|
track_handle = handler.enqueue_input(input).await;
|
||||||
// Set the volume
|
// Set the volume
|
||||||
let _ = track_handle.set_volume(volume);
|
let _ = track_handle.set_volume(volume);
|
||||||
@@ -156,8 +154,8 @@ pub async fn enqueue_track(
|
|||||||
);
|
);
|
||||||
track_count += 1;
|
track_count += 1;
|
||||||
}
|
}
|
||||||
if play_now && !handler.queue().is_empty() {
|
if handler.queue().is_empty() {
|
||||||
handler.queue().resume();
|
let _ = handler.queue().resume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(track_count)
|
Ok(track_count)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use serenity::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{create_response, edit_response, get_songbird, process_message};
|
use super::{edit_response, get_songbird, process_message};
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use serenity::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{get_songbird, create_response, edit_response};
|
use super::{edit_response, get_songbird, process_message};
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use serenity::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{get_songbird, create_response, edit_response};
|
use super::{edit_response, get_songbird, process_message};
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
|
|||||||
@@ -48,12 +48,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
|||||||
// Set the volume
|
// Set the volume
|
||||||
set_volume(&manager, guild_id, volume).await;
|
set_volume(&manager, guild_id, volume).await;
|
||||||
log::debug!("<{guild_id}> Setting the volume to {}", volume);
|
log::debug!("<{guild_id}> Setting the volume to {}", volume);
|
||||||
edit_response(
|
edit_response(&ctx, &command, format!("Setting the volume to {}", volume)).await;
|
||||||
&ctx,
|
|
||||||
&command,
|
|
||||||
format!("Setting the volume to {}", volume),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_volume(manager: &Arc<Songbird>, guild_id: &GuildId, volume: i32) {
|
pub async fn set_volume(manager: &Arc<Songbird>, guild_id: &GuildId, volume: i32) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use log::{warn, info, error};
|
|
||||||
use serenity::all::Interaction;
|
use serenity::all::Interaction;
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use serenity::model::gateway::Ready;
|
use serenity::model::gateway::Ready;
|
||||||
@@ -36,7 +35,7 @@ impl EventHandler for Handler {
|
|||||||
commands::chat::generate_response(&ctx, &msg, oai).await;
|
commands::chat::generate_response(&ctx, &msg, oai).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(why) => warn!("Could not check mentions: {:?}", why),
|
Err(why) => log::warn!("Could not check mentions: {:?}", why),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
@@ -71,8 +70,9 @@ impl EventHandler for Handler {
|
|||||||
|
|
||||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
if ready.guilds.is_empty() {
|
if ready.guilds.is_empty() {
|
||||||
warn!("No ready guilds found");
|
log::warn!("No ready guilds found");
|
||||||
}
|
}
|
||||||
|
log::trace!("Handling {} guilds", ready.guilds.len());
|
||||||
for guild in ready.guilds {
|
for guild in ready.guilds {
|
||||||
// Check if guild exists in database
|
// Check if guild exists in database
|
||||||
let guild_id = guild.id.get() as i64;
|
let guild_id = guild.id.get() as i64;
|
||||||
@@ -103,12 +103,12 @@ impl EventHandler for Handler {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
match commands {
|
match commands {
|
||||||
Ok(c) => info!(
|
Ok(c) => log::info!(
|
||||||
"Registered {} commands for guild {}",
|
"Registered {} commands for guild {}",
|
||||||
c.len(),
|
c.len(),
|
||||||
guild.id.get()
|
guild.id.get()
|
||||||
),
|
),
|
||||||
Err(why) => error!(
|
Err(why) => log::error!(
|
||||||
"Could not register commands for guild {}: {:?}",
|
"Could not register commands for guild {}: {:?}",
|
||||||
guild.id.get(),
|
guild.id.get(),
|
||||||
why
|
why
|
||||||
|
|||||||
Reference in New Issue
Block a user