refactor, cleanup

This commit is contained in:
2024-10-11 09:29:33 -04:00
parent 07a1e9277e
commit 2688d2304e
35 changed files with 66 additions and 127 deletions

View File

@@ -2,7 +2,7 @@
name = "siren" name = "siren"
version = "0.2.8" version = "0.2.8"
edition = "2021" edition = "2021"
authors = ["Ben Sherriff <hello@bensherriff.com>"] authors = ["Ben Sherriff <ben@bensherriff.com>"]
description = "A Discord bot for playing music" description = "A Discord bot for playing music"
repository = "https://github.com/bensherriff/siren" repository = "https://github.com/bensherriff/siren"
readme = "README.md" readme = "README.md"

View File

@@ -52,23 +52,10 @@ services:
- backend - backend
restart: unless-stopped restart: unless-stopped
redis:
image: redis:latest
container_name: siren-redis
volumes:
- redis:/data
ports:
- ${REDIS_PORT:-6379}:6379
networks:
- backend
profiles:
- backend
restart: unless-stopped
volumes: volumes:
postgres: postgres:
postgres_logs: postgres_logs:
redis:
networks: networks:
frontend: frontend:

View File

@@ -1,26 +0,0 @@
#! /bin/bash
DIR="./keys"
if [ "$#" -eq 1 ]; then
DIR=$1
fi
# Create the keys directory (if it doesn't exist)
echo "Generating public/private keys in: $DIR"
mkdir -p "$DIR"
# Remove any existing keys
rm -f $DIR/*_private_key.pem
rm -f $DIR/*_public_key.pem
# Generate Keys
openssl genrsa -out $DIR/access_private_key.pem 4096
openssl rsa -in $DIR/access_private_key.pem -pubout -outform PEM -out $DIR/access_public_key.pem
chmod 600 $DIR/access_private_key.pem
chmod 644 $DIR/access_public_key.pem
openssl genrsa -out $DIR/refresh_private_key.pem 4096
openssl rsa -in $DIR/refresh_private_key.pem -pubout -outform PEM -out $DIR/refresh_public_key.pem
chmod 600 $DIR/refresh_private_key.pem
chmod 644 $DIR/refresh_public_key.pem

View File

@@ -31,7 +31,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
let handler = handler_lock.lock().await; let handler = handler_lock.lock().await;
match handler.queue().pause() { match handler.queue().pause() {
Ok(_) => { Ok(_) => {
log::debug!("Paused the track"); log::debug!("<{guild_id}> Paused the track");
edit_response(&ctx, &command, format!("Pausing the track")).await; edit_response(&ctx, &command, format!("Pausing the track")).await;
} }
Err(err) => { Err(err) => {

View File

@@ -3,19 +3,17 @@ use std::sync::Arc;
use serenity::all::{CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}; use serenity::all::{CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption};
use serenity::model::prelude::GuildId; use serenity::model::prelude::GuildId;
use serenity::{prelude::*, async_trait}; use serenity::{prelude::*, async_trait};
use songbird::input::{AuxMetadata, Input, YoutubeDl}; use songbird::input::{Input, YoutubeDl};
use songbird::tracks::TrackHandle; use songbird::tracks::TrackHandle;
use songbird::{Call, Event, EventHandler, Songbird, TrackEvent}; use songbird::{Event, EventHandler, Songbird, TrackEvent};
use crate::database::guilds::GuildCache; use crate::bot::commands::audio::leave_voice_channel;
use crate::data::guilds::GuildCache;
use crate::bot::ytdlp::{PlaylistItem, YtDlp}; use crate::bot::ytdlp::{PlaylistItem, YtDlp};
use crate::error::{SirenResult, Error as SirenError}; use crate::error::{SirenResult, Error as SirenError};
use crate::HttpKey; use crate::HttpKey;
use super::{ use super::{create_response, edit_response, get_songbird, is_valid_url, join_voice_channel};
create_response, edit_response, get_songbird, is_valid_url, join_voice_channel,
leave_voice_channel,
};
pub async fn run(ctx: &Context, command: &CommandInteraction) { pub async fn run(ctx: &Context, command: &CommandInteraction) {
// Process the command options // Process the command options
@@ -87,10 +85,7 @@ pub async fn play_track(
) -> 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) {
let is_queue_empty = { let mut handler = handler_lock.lock().await;
let call_handler = handler_lock.lock().await;
call_handler.queue().is_empty()
};
let guild = GuildCache::get_by_id(guild_id.get() as i64).await?.unwrap(); let guild = GuildCache::get_by_id(guild_id.get() as i64).await?.unwrap();
let valid = is_valid_url(&track_url); let valid = is_valid_url(&track_url);
// Check if the URL is valid // Check if the URL is valid
@@ -123,71 +118,49 @@ pub async fn play_track(
} }
// Add each track to the queue // Add each track to the queue
for item in playlist_items { for item in playlist_items {
match add_song( let volume = guild.volume as f32 / 100.0;
ctx, let http_client = {
handler_lock.clone(), let data = ctx.data.read().await;
&item.url, data
is_queue_empty, .get::<HttpKey>()
guild.volume as f32 / 100.0, .cloned()
) .expect("Guaranteed to exist in the typemap.")
.await };
{ let source = YoutubeDl::new(http_client, item.url.to_owned());
Ok(metadata) => { let mut input: Input = source.into();
let track_title = metadata.title.unwrap(); let metadata = match input.aux_metadata().await {
log::debug!("Added track: {}", track_title); Ok(metadata) => metadata,
let mut handler = handler_lock.lock().await;
// handler.remove_all_global_events();
handler.add_global_event(
Event::Track(TrackEvent::End),
TrackEndNotifier {
guild_id,
call: manager.clone(),
},
);
track_count += 1;
}
Err(err) => { Err(err) => {
log::warn!("Failed to add song: {}", err); log::warn!("Failed to get metadata for track: {err}");
if let Err(why) = leave_voice_channel(&manager, &guild_id).await { let _ = leave_voice_channel(&manager, &guild_id).await;
log::error!("Failed to leave voice channel: {}", why);
}
return Err(SirenError::new(422, err.to_string())); return Err(SirenError::new(422, err.to_string()));
} }
};
let track_handle: TrackHandle;
let is_queue_empty = handler.queue().is_empty();
if is_queue_empty {
track_handle = handler.play_input(input);
} else {
track_handle = handler.enqueue_input(input).await;
} }
// Set the volume
let _ = track_handle.set_volume(volume);
let track_title = metadata.title.unwrap();
log::debug!("<{guild_id}> Added track: {}", track_title);
handler.remove_all_global_events();
handler.add_global_event(
Event::Track(TrackEvent::End),
TrackEndNotifier {
guild_id,
call: manager.clone(),
},
);
track_count += 1;
} }
} }
Ok(track_count) Ok(track_count)
} }
async fn add_song(
ctx: &Context,
call: Arc<Mutex<Call>>,
url: &str,
lazy: bool,
volume: f32,
) -> SirenResult<AuxMetadata> {
let http_client = {
let data = ctx.data.read().await;
data
.get::<HttpKey>()
.cloned()
.expect("Guaranteed to exist in the typemap.")
};
let source = YoutubeDl::new(http_client, url.to_owned());
let mut handler = call.lock().await;
let mut input: Input = source.into();
let metadata = input.aux_metadata().await.unwrap();
let track_handle: TrackHandle;
if lazy {
track_handle = handler.play_input(input);
} else {
track_handle = handler.enqueue_input(input).await;
}
// Set the volume
let _ = track_handle.set_volume(volume);
Ok(metadata)
}
pub fn get_playlist_urls(url: &str) -> SirenResult<Vec<PlaylistItem>> { pub fn get_playlist_urls(url: &str) -> SirenResult<Vec<PlaylistItem>> {
let output = YtDlp::new() let output = YtDlp::new()
.arg("--flat-playlist") .arg("--flat-playlist")

View File

@@ -31,7 +31,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
let handler = handler_lock.lock().await; let handler = handler_lock.lock().await;
match handler.queue().resume() { match handler.queue().resume() {
Ok(_) => { Ok(_) => {
log::debug!("Resumed the track"); log::debug!("<{guild_id}> Resumed the track");
edit_response(&ctx, &command, format!("Resuming the track")).await; edit_response(&ctx, &command, format!("Resuming the track")).await;
} }
Err(err) => { Err(err) => {

View File

@@ -31,7 +31,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
let handler = handler_lock.lock().await; let handler = handler_lock.lock().await;
match handler.queue().skip() { match handler.queue().skip() {
Ok(_) => { Ok(_) => {
log::debug!("Skipped the track"); log::debug!("<{guild_id}> Skipped the track");
edit_response(&ctx, &command, format!("Skipping the track")).await; edit_response(&ctx, &command, format!("Skipping the track")).await;
} }
Err(err) => { Err(err) => {

View File

@@ -30,7 +30,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
if let Some(handler_lock) = manager.get(guild_id) { if let Some(handler_lock) = manager.get(guild_id) {
let handler = handler_lock.lock().await; let handler = handler_lock.lock().await;
handler.queue().stop(); handler.queue().stop();
log::debug!("Stopped the track"); log::debug!("<{guild_id}> Stopped the track");
edit_response(&ctx, &command, format!("Stopping the tracks")).await; edit_response(&ctx, &command, format!("Stopping the tracks")).await;
} }
} }

View File

@@ -7,7 +7,7 @@ use serenity::{
}; };
use songbird::Songbird; use songbird::Songbird;
use crate::database::guilds::GuildCache; use crate::data::guilds::GuildCache;
use super::{get_songbird, create_response, edit_response}; use super::{get_songbird, create_response, edit_response};
@@ -47,7 +47,12 @@ 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;
edit_response(&ctx, &command, format!("Setting the volume to {}", volume)).await; edit_response(
&ctx,
&command,
format!("<{guild_id}> 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

@@ -4,7 +4,7 @@ use serenity::model::channel::Message;
use serenity::model::prelude::{ChannelType, PermissionOverwrite, PermissionOverwriteType}; use serenity::model::prelude::{ChannelType, PermissionOverwrite, PermissionOverwriteType};
use serenity::prelude::*; use serenity::prelude::*;
use crate::database::messages::MessageCache; use crate::data::messages::MessageCache;
use crate::bot::oai::{ChatCompletionMessage, ChatCompletionRequest, GPTRole, OAI}; use crate::bot::oai::{ChatCompletionMessage, ChatCompletionRequest, GPTRole, OAI};
pub async fn generate_response(ctx: &Context, msg: &Message, oai: &OAI) { pub async fn generate_response(ctx: &Context, msg: &Message, oai: &OAI) {

View File

@@ -2,10 +2,10 @@ use chrono::{DateTime, NaiveDate, TimeZone, Utc};
use regex::Regex; use regex::Regex;
use serenity::all::{ use serenity::all::{
Color, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, Color, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption,
CreateEmbed, CreateEmbedFooter, CreateScheduledEvent, EditInteractionResponse, Timestamp, CreateEmbed, CreateEmbedFooter, EditInteractionResponse, Timestamp,
}; };
use crate::{bot::commands::audio::create_response, database::events::Event}; use crate::{bot::commands::audio::create_response, data::events::Event};
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

@@ -5,7 +5,7 @@ use serenity::model::gateway::Ready;
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::prelude::*; use serenity::prelude::*;
use crate::database::guilds::GuildCache; use crate::data::guilds::GuildCache;
use super::{commands, oai}; use super::{commands, oai};
use super::commands::audio::create_response; use super::commands::audio::create_response;

View File

@@ -19,7 +19,7 @@ pub struct Event {
impl Event { impl Event {
pub async fn insert(&self) -> SirenResult<()> { pub async fn insert(&self) -> SirenResult<()> {
let pool = crate::database::pool(); let pool = crate::data::pool();
sqlx::query(&format!( sqlx::query(&format!(
"INSERT INTO {} ( "INSERT INTO {} (
id, id,
@@ -47,7 +47,7 @@ impl Event {
} }
pub async fn get_by_id(id: i64) -> SirenResult<Option<Self>> { pub async fn get_by_id(id: i64) -> SirenResult<Option<Self>> {
let pool = crate::database::pool(); let pool = crate::data::pool();
let item = sqlx::query_as::<_, Self>(&format!("SELECT * FROM {} WHERE id = $1", TABLE_NAME)) let item = sqlx::query_as::<_, Self>(&format!("SELECT * FROM {} WHERE id = $1", TABLE_NAME))
.bind(id) .bind(id)
.fetch_optional(pool) .fetch_optional(pool)

View File

@@ -12,7 +12,7 @@ pub struct GuildCache {
impl GuildCache { impl GuildCache {
pub async fn insert(&self) -> SirenResult<()> { pub async fn insert(&self) -> SirenResult<()> {
let pool = crate::database::pool(); let pool = crate::data::pool();
sqlx::query(&format!( sqlx::query(&format!(
"INSERT INTO {} ( "INSERT INTO {} (
id, id,
@@ -32,7 +32,7 @@ impl GuildCache {
} }
pub async fn get_by_id(id: i64) -> SirenResult<Option<Self>> { pub async fn get_by_id(id: i64) -> SirenResult<Option<Self>> {
let pool = crate::database::pool(); let pool = crate::data::pool();
let item = sqlx::query_as::<_, Self>(&format!("SELECT * FROM {} WHERE id = $1", TABLE_NAME)) let item = sqlx::query_as::<_, Self>(&format!("SELECT * FROM {} WHERE id = $1", TABLE_NAME))
.bind(id) .bind(id)
.fetch_optional(pool) .fetch_optional(pool)
@@ -42,7 +42,7 @@ impl GuildCache {
} }
pub async fn update(&self) -> SirenResult<()> { pub async fn update(&self) -> SirenResult<()> {
let pool = crate::database::pool(); let pool = crate::data::pool();
sqlx::query(&format!( sqlx::query(&format!(
"UPDATE {} SET "UPDATE {} SET
bot_id = $2, bot_id = $2,

View File

@@ -19,7 +19,7 @@ pub struct MessageCache {
impl MessageCache { impl MessageCache {
pub async fn insert(&self) -> SirenResult<()> { pub async fn insert(&self) -> SirenResult<()> {
let pool = crate::database::pool(); let pool = crate::data::pool();
sqlx::query(&format!( sqlx::query(&format!(
"INSERT INTO {} ( "INSERT INTO {} (
id, id,
@@ -58,9 +58,9 @@ impl MessageCache {
author_id: i64, author_id: i64,
limit: i64, limit: i64,
) -> SirenResult<Vec<MessageCache>> { ) -> SirenResult<Vec<MessageCache>> {
let pool = crate::database::pool(); let pool = crate::data::pool();
let messages = sqlx::query_as::<_, MessageCache>(&format!( let messages = sqlx::query_as::<_, MessageCache>(&format!(
"SELECT * FROM {} WHERE guild_id = $1 AND channel_id = $2 AND author_id = $3 ORDER BY created DESC LIMIT $4", "SELECT * FROM {} WHERE guild_id = $1 AND channel_id = $2 AND author_id = $3 ORDER BY created ASC LIMIT $4",
TABLE_NAME TABLE_NAME
)) ))
.bind(guild_id) .bind(guild_id)

View File

@@ -9,7 +9,7 @@ use reqwest::Client as HttpClient;
use crate::bot::handler::Handler; use crate::bot::handler::Handler;
mod bot; mod bot;
mod database; mod data;
mod dnd; mod dnd;
mod error; mod error;
@@ -23,7 +23,7 @@ impl TypeMapKey for HttpKey {
async fn main() { async fn main() {
dotenv::dotenv().ok(); dotenv::dotenv().ok();
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,siren=info")); env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,siren=info"));
if let Err(err) = database::initialize().await { if let Err(err) = data::initialize().await {
log::error!("Failed to initialize database: {err}"); log::error!("Failed to initialize database: {err}");
return; return;
}; };