Play api request
This commit is contained in:
38
src/api/audio/mod.rs
Normal file
38
src/api/audio/mod.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use axum::extract::{Path, State};
|
||||||
|
use axum::middleware::from_extractor;
|
||||||
|
use axum::{Extension, Json, Router};
|
||||||
|
use axum::routing::post;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use crate::api::auth::{AuthorizationMiddleware, Session};
|
||||||
|
use crate::AppState;
|
||||||
|
use crate::bot::commands::audio::join_voice_channel;
|
||||||
|
use crate::bot::commands::audio::play::enqueue_track;
|
||||||
|
use crate::bot::handler::get_songbird;
|
||||||
|
use crate::error::SirenResult;
|
||||||
|
|
||||||
|
pub fn get_routes() -> Router<Arc<AppState>> {
|
||||||
|
Router::new()
|
||||||
|
.route("/play", post(play_audio))
|
||||||
|
.route_layer(from_extractor::<AuthorizationMiddleware>())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct TrackRequest {
|
||||||
|
url: String,
|
||||||
|
guild_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn play_audio(
|
||||||
|
Extension(session): Extension<Session>,
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
Json(payload): Json<TrackRequest>,
|
||||||
|
) -> SirenResult<()> {
|
||||||
|
log::debug!("Playing audio in guild: {}", payload.guild_id);
|
||||||
|
let manager = get_songbird();
|
||||||
|
let user_id = state.cache.user(session.user_id).unwrap().id;
|
||||||
|
let guild_id = state.cache.guild(payload.guild_id).unwrap().id;
|
||||||
|
let _channel_id = join_voice_channel(&state.cache, &manager, &guild_id, &user_id).await?;
|
||||||
|
enqueue_track(manager, guild_id.to_owned(), &payload.url).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -2,35 +2,40 @@ use std::sync::Arc;
|
|||||||
use axum::{middleware, Extension, Router};
|
use axum::{middleware, Extension, Router};
|
||||||
use axum::middleware::from_extractor;
|
use axum::middleware::from_extractor;
|
||||||
use axum::routing::post;
|
use axum::routing::post;
|
||||||
use crate::api::auth::{authenticate_middleware, csprng};
|
use crate::api::auth::csprng;
|
||||||
use crate::api::auth::middleware::AuthorizationMiddleware;
|
use crate::api::auth::AuthorizationMiddleware;
|
||||||
use crate::api::auth::session::Session;
|
use crate::api::auth::session::Session;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use crate::error::SirenResult;
|
use crate::error::SirenResult;
|
||||||
|
|
||||||
pub fn get_routes() -> Router<Arc<AppState>> {
|
pub fn get_routes() -> Router<Arc<AppState>> {
|
||||||
Router::new().route("/api-key", post(create_api_key))
|
Router::new()
|
||||||
|
.route("/api-key", post(create_api_key))
|
||||||
.route_layer(from_extractor::<AuthorizationMiddleware>())
|
.route_layer(from_extractor::<AuthorizationMiddleware>())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ApiKey {
|
struct ApiKey {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
pub user_id: String,
|
pub user_id: u64,
|
||||||
pub access_mask: u32,
|
pub access_mask: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApiKey {
|
impl ApiKey {
|
||||||
fn new(user_id: String, access_mask: u32) -> Self {
|
fn new(user_id: u64, access_mask: u32) -> Self {
|
||||||
ApiKey {
|
ApiKey {
|
||||||
key: csprng(64),
|
key: csprng(64),
|
||||||
user_id,
|
user_id,
|
||||||
access_mask
|
access_mask,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_api_key(Extension(session): Extension<Session>) -> SirenResult<String> {
|
async fn create_api_key(Extension(session): Extension<Session>) -> SirenResult<String> {
|
||||||
log::debug!("Generating API key for {} ({})", &session.user_id, &session.user_name);
|
log::debug!(
|
||||||
|
"Generating API key for {} ({})",
|
||||||
|
&session.user_id,
|
||||||
|
&session.user_name
|
||||||
|
);
|
||||||
let api_key = ApiKey::new(session.user_id, 0);
|
let api_key = ApiKey::new(session.user_id, 0);
|
||||||
Ok(api_key.key)
|
Ok(api_key.key)
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct BearerTokenClaims {
|
pub struct BearerTokenClaims {
|
||||||
pub sub: String,
|
pub sub: u64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub iat: i64,
|
pub iat: i64,
|
||||||
pub exp: i64,
|
pub exp: i64,
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ use axum::async_trait;
|
|||||||
use axum::extract::FromRequestParts;
|
use axum::extract::FromRequestParts;
|
||||||
use axum::http::request::Parts;
|
use axum::http::request::Parts;
|
||||||
use axum::http::{Method, StatusCode};
|
use axum::http::{Method, StatusCode};
|
||||||
use axum_extra::{TypedHeader, headers::{Authorization, authorization::Bearer}};
|
use axum::middleware::{from_extractor, FromExtractorLayer};
|
||||||
|
use axum_extra::{
|
||||||
|
TypedHeader,
|
||||||
|
headers::{Authorization, authorization::Bearer},
|
||||||
|
};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use jsonwebtoken::{decode, DecodingKey, Validation};
|
use jsonwebtoken::{decode, DecodingKey, Validation};
|
||||||
use crate::api::auth::bearer_token::BearerTokenClaims;
|
use crate::api::auth::bearer_token::BearerTokenClaims;
|
||||||
@@ -27,7 +31,6 @@ where
|
|||||||
let Ok(TypedHeader(Authorization(bearer))) =
|
let Ok(TypedHeader(Authorization(bearer))) =
|
||||||
TypedHeader::<Authorization<Bearer>>::from_request_parts(parts, state).await
|
TypedHeader::<Authorization<Bearer>>::from_request_parts(parts, state).await
|
||||||
else {
|
else {
|
||||||
log::error!("Could not get Authorization header from the request");
|
|
||||||
return Err(StatusCode::UNAUTHORIZED);
|
return Err(StatusCode::UNAUTHORIZED);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ where
|
|||||||
Ok(session) => {
|
Ok(session) => {
|
||||||
parts.extensions.insert(session);
|
parts.extensions.insert(session);
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("{:?}", err);
|
log::error!("{:?}", err);
|
||||||
Err(StatusCode::UNAUTHORIZED)
|
Err(StatusCode::UNAUTHORIZED)
|
||||||
@@ -49,11 +52,9 @@ async fn check_auth(bearer: Bearer) -> SirenResult<Session> {
|
|||||||
let jwt_secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set in the environment");
|
let jwt_secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set in the environment");
|
||||||
let decoding_key = DecodingKey::from_secret(jwt_secret.as_bytes());
|
let decoding_key = DecodingKey::from_secret(jwt_secret.as_bytes());
|
||||||
|
|
||||||
let token_data = decode::<BearerTokenClaims>(
|
let token_data =
|
||||||
bearer.token(),
|
decode::<BearerTokenClaims>(bearer.token(), &decoding_key, &Validation::default())
|
||||||
&decoding_key,
|
.map_err(|_| StatusCode::UNAUTHORIZED)?;
|
||||||
&Validation::default()
|
|
||||||
).map_err(|_| StatusCode::UNAUTHORIZED)?;
|
|
||||||
|
|
||||||
let claims = token_data.claims;
|
let claims = token_data.claims;
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ use crate::AppState;
|
|||||||
|
|
||||||
mod oauth;
|
mod oauth;
|
||||||
mod session;
|
mod session;
|
||||||
|
pub use session::Session;
|
||||||
mod api_key;
|
mod api_key;
|
||||||
mod bearer_token;
|
mod bearer_token;
|
||||||
mod middleware;
|
mod middleware;
|
||||||
|
pub use middleware::AuthorizationMiddleware;
|
||||||
|
|
||||||
pub fn get_routes() -> Router<Arc<AppState>> {
|
pub fn get_routes() -> Router<Arc<AppState>> {
|
||||||
Router::new()
|
Router::new()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::error::SirenResult;
|
|||||||
pub fn get_routes() -> Router<Arc<AppState>> {
|
pub fn get_routes() -> Router<Arc<AppState>> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/authorize", get(discord_authorize))
|
.route("/authorize", get(discord_authorize))
|
||||||
|
.route("/authorize/redirect", get(discord_authorize_redirect))
|
||||||
.route("/callback", get(oauth_callback))
|
.route("/callback", get(oauth_callback))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,14 +43,14 @@ struct DiscordUser {
|
|||||||
avatar: Option<String>,
|
avatar: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// async fn discord_authorize_redirect(State(state): State<Arc<AppState>>) -> Redirect {
|
async fn discord_authorize_redirect(State(state): State<Arc<AppState>>) -> Redirect {
|
||||||
// // Construct the Discord OAuth URL
|
// Construct the Discord OAuth URL
|
||||||
// let discord_auth_url = format!(
|
let discord_auth_url = format!(
|
||||||
// "https://discord.com/api/oauth2/authorize?client_id={}&redirect_uri={}&response_type=code&scope=identify",
|
"https://discord.com/api/oauth2/authorize?client_id={}&redirect_uri={}&response_type=code&scope=identify",
|
||||||
// state.client_id, state.redirect_uri
|
state.client_id, state.redirect_uri
|
||||||
// );
|
);
|
||||||
// Redirect::temporary(&discord_auth_url)
|
Redirect::temporary(&discord_auth_url)
|
||||||
// }
|
}
|
||||||
|
|
||||||
async fn discord_authorize(State(state): State<Arc<AppState>>) -> SirenResult<String> {
|
async fn discord_authorize(State(state): State<Arc<AppState>>) -> SirenResult<String> {
|
||||||
// Construct the Discord OAuth URL
|
// Construct the Discord OAuth URL
|
||||||
@@ -124,13 +125,16 @@ async fn oauth_callback(
|
|||||||
log::debug!("User authenticated: {:?}", user_data);
|
log::debug!("User authenticated: {:?}", user_data);
|
||||||
|
|
||||||
// Create and insert the session
|
// Create and insert the session
|
||||||
let session = Session::new(user_data.id.clone(), user_data.username.clone());
|
let session = Session::new(
|
||||||
|
user_data.id.parse::<u64>().unwrap(),
|
||||||
|
user_data.username.clone(),
|
||||||
|
);
|
||||||
session.insert().await?;
|
session.insert().await?;
|
||||||
|
|
||||||
let issued_at = chrono::Utc::now();
|
let issued_at = chrono::Utc::now();
|
||||||
|
|
||||||
let claims = BearerTokenClaims {
|
let claims = BearerTokenClaims {
|
||||||
sub: session.user_id.clone(),
|
sub: session.user_id,
|
||||||
name: session.user_name.clone(),
|
name: session.user_name.clone(),
|
||||||
iat: issued_at.timestamp(),
|
iat: issued_at.timestamp(),
|
||||||
exp: session.expires_at.timestamp(),
|
exp: session.expires_at.timestamp(),
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ fn get_session_ttl() -> i64 {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
pub session_id: String,
|
pub session_id: String,
|
||||||
pub user_id: String,
|
pub user_id: u64,
|
||||||
pub user_name: String,
|
pub user_name: String,
|
||||||
pub expires_at: DateTime<Utc>,
|
pub expires_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub fn new(user_id: String, user_name: String) -> Session {
|
pub fn new(user_id: u64, user_name: String) -> Session {
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
let session_ttl = get_session_ttl();
|
let session_ttl = get_session_ttl();
|
||||||
Session {
|
Session {
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ use axum::Router;
|
|||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
|
mod audio;
|
||||||
mod auth;
|
mod auth;
|
||||||
|
|
||||||
pub fn get_routes() -> Router<Arc<AppState>> {
|
pub fn get_routes() -> Router<Arc<AppState>> {
|
||||||
Router::new().merge(auth::get_routes())
|
Router::new()
|
||||||
|
.merge(auth::get_routes())
|
||||||
|
.nest("/audio", audio::get_routes())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::{Arc, OnceLock};
|
||||||
|
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
use serenity::all::UserId;
|
||||||
use serenity::client::Cache;
|
use serenity::client::Cache;
|
||||||
use serenity::model::prelude::{GuildId, ChannelId};
|
use serenity::model::prelude::{GuildId, ChannelId};
|
||||||
use serenity::model::user::User;
|
use serenity::model::user::User;
|
||||||
@@ -17,12 +18,6 @@ pub mod skip;
|
|||||||
pub mod stop;
|
pub mod stop;
|
||||||
pub mod volume;
|
pub mod volume;
|
||||||
|
|
||||||
pub async fn get_songbird(ctx: &Context) -> Arc<Songbird> {
|
|
||||||
songbird::get(ctx)
|
|
||||||
.await
|
|
||||||
.expect("Songbird Voice client placed in at initialization")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a voice channel that the user is currently in, and attempts to join it.
|
* Finds a voice channel that the user is currently in, and attempts to join it.
|
||||||
*/
|
*/
|
||||||
@@ -30,9 +25,9 @@ pub async fn join_voice_channel(
|
|||||||
cache: &Arc<Cache>,
|
cache: &Arc<Cache>,
|
||||||
manager: &Arc<Songbird>,
|
manager: &Arc<Songbird>,
|
||||||
guild_id: &GuildId,
|
guild_id: &GuildId,
|
||||||
user: &User,
|
user_id: &UserId,
|
||||||
) -> SirenResult<ChannelId> {
|
) -> SirenResult<ChannelId> {
|
||||||
let channel_id = find_voice_channel(cache, guild_id, user)?;
|
let channel_id = find_voice_channel(cache, guild_id, user_id)?;
|
||||||
log::debug!("<{}> Joining channel {}", guild_id.get(), channel_id.get());
|
log::debug!("<{}> Joining channel {}", guild_id.get(), channel_id.get());
|
||||||
manager
|
manager
|
||||||
.join(guild_id.to_owned(), channel_id.to_owned())
|
.join(guild_id.to_owned(), channel_id.to_owned())
|
||||||
@@ -66,7 +61,7 @@ fn is_valid_url(url: &str) -> bool {
|
|||||||
fn find_voice_channel(
|
fn find_voice_channel(
|
||||||
cache: &Arc<Cache>,
|
cache: &Arc<Cache>,
|
||||||
guild_id: &GuildId,
|
guild_id: &GuildId,
|
||||||
user: &User,
|
user_id: &UserId,
|
||||||
) -> SirenResult<ChannelId> {
|
) -> SirenResult<ChannelId> {
|
||||||
let guild = match guild_id.to_guild_cached(cache) {
|
let guild = match guild_id.to_guild_cached(cache) {
|
||||||
Some(g) => g,
|
Some(g) => g,
|
||||||
@@ -75,7 +70,7 @@ fn find_voice_channel(
|
|||||||
|
|
||||||
match guild
|
match guild
|
||||||
.voice_states
|
.voice_states
|
||||||
.get(&user.id)
|
.get(&user_id)
|
||||||
.and_then(|voice_state| voice_state.channel_id)
|
.and_then(|voice_state| voice_state.channel_id)
|
||||||
{
|
{
|
||||||
Some(channel) => Ok(channel),
|
Some(channel) => Ok(channel),
|
||||||
|
|||||||
@@ -3,15 +3,14 @@ use serenity::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use crate::bot::chat::{edit_response, process_message};
|
use crate::bot::chat::{edit_response, process_message};
|
||||||
|
use crate::bot::handler::get_songbird;
|
||||||
use super::get_songbird;
|
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
process_message(&ctx, &command, false).await;
|
process_message(&ctx, &command, false).await;
|
||||||
|
|
||||||
// Get the songbird manager
|
// Get the songbird manager
|
||||||
let manager = get_songbird(ctx).await;
|
let manager = get_songbird();
|
||||||
|
|
||||||
// Extract the guild ID
|
// Extract the guild ID
|
||||||
let guild_id = match &command.guild_id {
|
let guild_id = match &command.guild_id {
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ use serenity::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::bot::chat::{edit_response, process_message};
|
use crate::bot::chat::{edit_response, process_message};
|
||||||
|
use crate::bot::handler::get_songbird;
|
||||||
use super::get_songbird;
|
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
process_message(&ctx, &command, false).await;
|
process_message(&ctx, &command, false).await;
|
||||||
|
|
||||||
// Get the songbird manager
|
// Get the songbird manager
|
||||||
let manager = get_songbird(ctx).await;
|
let manager = get_songbird();
|
||||||
|
|
||||||
// Extract the guild ID
|
// Extract the guild ID
|
||||||
let guild_id = match &command.guild_id {
|
let guild_id = match &command.guild_id {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use serenity::all::{CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption};
|
use serenity::all::{
|
||||||
|
Cache, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption, Http,
|
||||||
|
};
|
||||||
use serenity::model::prelude::GuildId;
|
use serenity::model::prelude::GuildId;
|
||||||
use serenity::{prelude::*, async_trait};
|
use serenity::{prelude::*, async_trait};
|
||||||
use songbird::input::{Input, YoutubeDl};
|
use songbird::input::{Input, YoutubeDl};
|
||||||
@@ -12,9 +14,10 @@ use crate::bot::ytdlp::{YtDlp, YtDlpItem};
|
|||||||
use crate::error::{SirenResult, Error as SirenError};
|
use crate::error::{SirenResult, Error as SirenError};
|
||||||
use crate::{signal_shutdown, HttpKey};
|
use crate::{signal_shutdown, HttpKey};
|
||||||
|
|
||||||
use super::{get_songbird, is_valid_url, join_voice_channel};
|
use super::{is_valid_url, join_voice_channel};
|
||||||
|
|
||||||
use crate::bot::chat::{create_message_response, edit_response, process_message};
|
use crate::bot::chat::{create_message_response, edit_response, process_message};
|
||||||
|
use crate::bot::handler::{get_client, get_songbird};
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Process the command options
|
// Process the command options
|
||||||
@@ -34,7 +37,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
|||||||
process_message(&ctx, &command, false).await;
|
process_message(&ctx, &command, false).await;
|
||||||
|
|
||||||
// Get the songbird manager
|
// Get the songbird manager
|
||||||
let manager = get_songbird(ctx).await;
|
let manager = get_songbird();
|
||||||
|
|
||||||
// Extract the guild ID
|
// Extract the guild ID
|
||||||
let guild_id = match &command.guild_id {
|
let guild_id = match &command.guild_id {
|
||||||
@@ -51,13 +54,13 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Join the user's voice channel
|
// Join the user's voice channel
|
||||||
match join_voice_channel(&ctx.cache, &manager, guild_id, &command.user).await {
|
match join_voice_channel(&ctx.cache, &manager, guild_id, &command.user.id).await {
|
||||||
Ok(channel_id) => {
|
Ok(channel_id) => {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"<{guild_id}> Play command executed on channel {channel_id} with track: {track_url:?}"
|
"<{guild_id}> Play command executed on channel {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).await {
|
match enqueue_track(manager, guild_id.to_owned(), track_url).await {
|
||||||
Ok(items) => {
|
Ok(items) => {
|
||||||
let mut message = format!("Added {} tracks", items.len());
|
let mut message = format!("Added {} tracks", items.len());
|
||||||
if items.len() == 0 {
|
if items.len() == 0 {
|
||||||
@@ -81,8 +84,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn enqueue_track(
|
pub async fn enqueue_track(
|
||||||
ctx: &Context,
|
manager: &Arc<Songbird>,
|
||||||
manager: Arc<Songbird>,
|
|
||||||
guild_id: GuildId,
|
guild_id: GuildId,
|
||||||
track_url: &str,
|
track_url: &str,
|
||||||
) -> SirenResult<Vec<YtDlpItem>> {
|
) -> SirenResult<Vec<YtDlpItem>> {
|
||||||
@@ -112,15 +114,9 @@ pub async fn enqueue_track(
|
|||||||
// Add each track to the queue
|
// Add each track to the queue
|
||||||
for item in &playlist_items {
|
for item in &playlist_items {
|
||||||
let volume = guild.volume as f32 / 100.0;
|
let volume = guild.volume as f32 / 100.0;
|
||||||
let http_client = {
|
let http_client = get_client();
|
||||||
let data = ctx.data.read().await;
|
|
||||||
data
|
|
||||||
.get::<HttpKey>()
|
|
||||||
.cloned()
|
|
||||||
.expect("Guaranteed to exist in the typemap.")
|
|
||||||
};
|
|
||||||
|
|
||||||
let source = YoutubeDl::new(http_client, item.get_url().to_owned());
|
let source = YoutubeDl::new(http_client.to_owned(), item.get_url().to_owned());
|
||||||
let input: Input = source.into();
|
let input: Input = source.into();
|
||||||
let track_title = item.get_title().to_owned();
|
let track_title = item.get_title().to_owned();
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ use serenity::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::bot::chat::{edit_response, process_message};
|
use crate::bot::chat::{edit_response, process_message};
|
||||||
|
use crate::bot::handler::get_songbird;
|
||||||
use super::get_songbird;
|
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
process_message(&ctx, &command, false).await;
|
process_message(&ctx, &command, false).await;
|
||||||
|
|
||||||
// Get the songbird manager
|
// Get the songbird manager
|
||||||
let manager = get_songbird(ctx).await;
|
let manager = get_songbird();
|
||||||
|
|
||||||
// Extract the guild ID
|
// Extract the guild ID
|
||||||
let guild_id = match &command.guild_id {
|
let guild_id = match &command.guild_id {
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ use serenity::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::bot::chat::{edit_response, process_message};
|
use crate::bot::chat::{edit_response, process_message};
|
||||||
|
use crate::bot::handler::get_songbird;
|
||||||
use super::get_songbird;
|
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
process_message(&ctx, &command, false).await;
|
process_message(&ctx, &command, false).await;
|
||||||
|
|
||||||
// Get the songbird manager
|
// Get the songbird manager
|
||||||
let manager = get_songbird(ctx).await;
|
let manager = get_songbird();
|
||||||
|
|
||||||
// Extract the guild ID
|
// Extract the guild ID
|
||||||
let guild_id = match &command.guild_id {
|
let guild_id = match &command.guild_id {
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ use serenity::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::bot::chat::{edit_response, process_message};
|
use crate::bot::chat::{edit_response, process_message};
|
||||||
|
use crate::bot::handler::get_songbird;
|
||||||
use super::get_songbird;
|
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Create the initial response
|
// Create the initial response
|
||||||
process_message(&ctx, &command, false).await;
|
process_message(&ctx, &command, false).await;
|
||||||
|
|
||||||
// Get the songbird manager
|
// Get the songbird manager
|
||||||
let manager = get_songbird(ctx).await;
|
let manager = get_songbird();
|
||||||
|
|
||||||
// Extract the guild ID
|
// Extract the guild ID
|
||||||
let guild_id = match command.guild_id {
|
let guild_id = match command.guild_id {
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ use songbird::Songbird;
|
|||||||
use crate::data::guilds::GuildCache;
|
use crate::data::guilds::GuildCache;
|
||||||
|
|
||||||
use crate::bot::chat::{create_message_response, edit_response, process_message};
|
use crate::bot::chat::{create_message_response, edit_response, process_message};
|
||||||
|
use crate::bot::handler::get_songbird;
|
||||||
use super::get_songbird;
|
|
||||||
|
|
||||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||||
// Process the command options
|
// Process the command options
|
||||||
@@ -37,7 +36,7 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
|||||||
process_message(&ctx, &command, false).await;
|
process_message(&ctx, &command, false).await;
|
||||||
|
|
||||||
// Get the songbird manager
|
// Get the songbird manager
|
||||||
let manager = get_songbird(ctx).await;
|
let manager = get_songbird();
|
||||||
|
|
||||||
// Extract the guild ID
|
// Extract the guild ID
|
||||||
let guild_id = match &command.guild_id {
|
let guild_id = match &command.guild_id {
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::sync::{Arc, OnceLock};
|
||||||
use serenity::all::{Interaction, ResumedEvent};
|
use serenity::all::{Interaction, ResumedEvent};
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use serenity::model::gateway::Ready;
|
use serenity::model::gateway::Ready;
|
||||||
use serenity::model::channel::Message;
|
use serenity::model::channel::Message;
|
||||||
use serenity::prelude::*;
|
use serenity::prelude::*;
|
||||||
|
use songbird::Songbird;
|
||||||
use crate::bot::commands::chat::generate_response;
|
use crate::bot::commands::chat::generate_response;
|
||||||
use crate::bot::oai::OAI;
|
use crate::bot::oai::OAI;
|
||||||
use crate::data::guilds::GuildCache;
|
use crate::data::guilds::GuildCache;
|
||||||
|
use crate::HttpKey;
|
||||||
use super::{commands};
|
use super::{commands};
|
||||||
use super::chat::{create_modal_response};
|
use super::chat::{create_modal_response};
|
||||||
|
|
||||||
@@ -14,6 +18,43 @@ pub struct BotHandler {
|
|||||||
pub oai: Option<OAI>,
|
pub oai: Option<OAI>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SONGBIRD: OnceLock<Arc<Songbird>> = OnceLock::new();
|
||||||
|
static CLIENT: OnceLock<reqwest::Client> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_songbird() -> &'static Arc<Songbird> {
|
||||||
|
SONGBIRD.get().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_client() -> &'static reqwest::Client {
|
||||||
|
CLIENT.get().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BotHandler {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
match env::var("OPENAI_TOKEN") {
|
||||||
|
Ok(token) => {
|
||||||
|
log::debug!("OpenAI functionality enabled");
|
||||||
|
let default_model = env::var("OPENAI_MODEL").unwrap_or_else(|_| "gpt-4o-mini".to_string());
|
||||||
|
let base_url = env::var("OPENAI_BASE_URL").unwrap();
|
||||||
|
Self {
|
||||||
|
oai: Some(OAI {
|
||||||
|
client: reqwest::Client::new(),
|
||||||
|
base_url,
|
||||||
|
token,
|
||||||
|
max_conversation_history: 30,
|
||||||
|
max_tokens: 8192,
|
||||||
|
default_model,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
log::warn!("OpenAI functionality disabled");
|
||||||
|
Self { oai: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EventHandler for BotHandler {
|
impl EventHandler for BotHandler {
|
||||||
async fn message(&self, ctx: Context, msg: Message) {
|
async fn message(&self, ctx: Context, msg: Message) {
|
||||||
@@ -40,6 +81,20 @@ impl EventHandler for BotHandler {
|
|||||||
if ready.guilds.is_empty() {
|
if ready.guilds.is_empty() {
|
||||||
log::warn!("No ready guilds found");
|
log::warn!("No ready guilds found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let songbird = songbird::get(&ctx).await.unwrap();
|
||||||
|
SONGBIRD
|
||||||
|
.set(songbird.clone())
|
||||||
|
.expect("Songbird value could not be set");
|
||||||
|
let http_client = {
|
||||||
|
let data = ctx.data.read().await;
|
||||||
|
data
|
||||||
|
.get::<HttpKey>()
|
||||||
|
.cloned()
|
||||||
|
.expect("Guaranteed to exist in the typemap.")
|
||||||
|
};
|
||||||
|
CLIENT.set(http_client).ok();
|
||||||
|
|
||||||
log::trace!("Handling {} guilds", ready.guilds.len());
|
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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
mod chat;
|
pub mod chat;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
pub mod oai;
|
pub mod oai;
|
||||||
|
|||||||
26
src/main.rs
26
src/main.rs
@@ -41,7 +41,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let token: String = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
|
let token: String = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
|
||||||
|
|
||||||
// Set up handler with optional OpenAI integration
|
// Set up handler with optional OpenAI integration
|
||||||
let handler = configure_handler();
|
let handler = BotHandler::new();
|
||||||
|
|
||||||
// Set up Songbird for voice functionality
|
// Set up Songbird for voice functionality
|
||||||
let songbird = Songbird::serenity();
|
let songbird = Songbird::serenity();
|
||||||
@@ -113,30 +113,6 @@ async fn get_bot_info(http: &Http) -> (Option<UserId>, UserId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_handler() -> BotHandler {
|
|
||||||
match env::var("OPENAI_TOKEN") {
|
|
||||||
Ok(token) => {
|
|
||||||
log::debug!("OpenAI functionality enabled");
|
|
||||||
let default_model = env::var("OPENAI_MODEL").unwrap_or_else(|_| "gpt-4o-mini".to_string());
|
|
||||||
let base_url = env::var("OPENAI_BASE_URL").unwrap();
|
|
||||||
BotHandler {
|
|
||||||
oai: Some(OAI {
|
|
||||||
client: reqwest::Client::new(),
|
|
||||||
base_url,
|
|
||||||
token,
|
|
||||||
max_conversation_history: 30,
|
|
||||||
max_tokens: 8192,
|
|
||||||
default_model,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
log::warn!("OpenAI functionality disabled");
|
|
||||||
BotHandler { oai: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn signal_shutdown(shard_manager: Arc<ShardManager>) {
|
async fn signal_shutdown(shard_manager: Arc<ShardManager>) {
|
||||||
tokio::signal::ctrl_c()
|
tokio::signal::ctrl_c()
|
||||||
.await
|
.await
|
||||||
|
|||||||
Reference in New Issue
Block a user