Fixed docker containers

This commit is contained in:
2024-10-13 20:22:34 -04:00
parent 1c6ed2cba4
commit 0bfc320379
12 changed files with 63 additions and 86 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
``` ```

View File

@@ -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
View 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 "$@"

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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