Finish upgrading pose and songbird

This commit is contained in:
Alex Page 2024-01-25 17:50:05 -05:00
parent 4244469610
commit ec66822602
6 changed files with 3221 additions and 125 deletions

4
.gitignore vendored
View file

@ -3,10 +3,6 @@
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

3124
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -11,15 +11,18 @@ tracing = "0.1.37"
tracing-futures = "0.2.5"
openai = "1.0.0-alpha.8"
rand = "0.8.5"
reqwest = "0.11.23"
songbird = "0.4.0"
thiserror = "1.0.39"
[dependencies.tracing-subscriber]
version = "0.3.16"
features = ["fmt", "env-filter", "std"]
[dependencies.songbird]
version = "0.4.0"
[dependencies.symphonia]
version = "0.5.2"
features = ["mkv"]
[dependencies.tokio]
version = "1.26.0"
features = ["macros", "rt-multi-thread", "signal"]
[dependencies.tracing-subscriber]
version = "0.3.18"
features = ["fmt", "env-filter", "std"]

View file

@ -1,21 +1,20 @@
use anyhow::{Context, Result};
use poise::serenity_prelude::{CreateEmbed, EmbedMessageBuilding, MessageBuilder};
use poise::{
serenity_prelude::{CreateEmbed, EmbedMessageBuilding, MessageBuilder},
CreateReply,
};
use songbird::input::{Input, YoutubeDl};
use tracing::{debug, log::warn};
use crate::{personality, CommandContext, Error};
#[poise::command(slash_command)]
pub async fn join(ctx: CommandContext<'_>) -> Result<(), Error> {
let Some(guild) = ctx.guild() else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
let Some(Some(channel_id)) = guild
.voice_states
.get(&ctx.author().id)
.map(|vs| vs.channel_id)
else {
let Some((guild_id, Some(channel_id))) = ctx.guild().and_then(|g| {
g.voice_states
.get(&ctx.author().id)
.map(|vs| (g.id, vs.channel_id))
}) else {
ctx.say("You're not in a voice channel, silly.").await?;
return Ok(());
};
@ -24,7 +23,7 @@ pub async fn join(ctx: CommandContext<'_>) -> Result<(), Error> {
.await
.context("Expected a songbird manager")?
.clone();
let _handler = manager.join(guild.id, channel_id).await;
let _handler = manager.join(guild_id, channel_id).await;
ctx.say("Joining your channel!").await?;
@ -33,7 +32,7 @@ pub async fn join(ctx: CommandContext<'_>) -> Result<(), Error> {
#[poise::command(slash_command)]
pub async fn leave(ctx: CommandContext<'_>) -> Result<(), Error> {
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -43,12 +42,12 @@ pub async fn leave(ctx: CommandContext<'_>) -> Result<(), Error> {
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_none() {
if manager.get(guild_id).is_none() {
ctx.say("I'm not even in a voice channel!").await?;
return Ok(());
}
let _handler = manager.remove(guild.id).await;
let _handler = manager.remove(guild_id).await;
ctx.say("Okay bye!").await?;
@ -65,7 +64,7 @@ pub async fn play(
return Ok(());
};
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -75,20 +74,19 @@ pub async fn play(
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_none() {
let Some(Some(channel_id)) = guild
.voice_states
.get(&ctx.author().id)
.map(|vs| vs.channel_id)
else {
ctx.say("Neither of us are in a voice channel, silly.")
.await?;
if manager.get(guild_id).is_none() {
let Some((guild_id, Some(channel_id))) = ctx.guild().and_then(|g| {
g.voice_states
.get(&ctx.author().id)
.map(|vs| (g.id, vs.channel_id))
}) else {
ctx.say("You're not in a voice channel, silly.").await?;
return Ok(());
};
let _handler = manager.join(guild.id, channel_id).await;
let _handler = manager.join(guild_id, channel_id).await;
}
if let Some(handler_lock) = manager.get(guild.id) {
if let Some(handler_lock) = manager.get(guild_id) {
let reply = poise::CreateReply::default()
.content(personality::get_random_loading_message())
.embed(
@ -100,33 +98,11 @@ pub async fn play(
let mut handler = handler_lock.lock().await;
debug!("Trying to play: {}", url);
let source = songbird::ytdl(&url).await;
let mut source: Input = YoutubeDl::new(ctx.data().http_client.clone(), url.clone()).into();
let metadata = source.aux_metadata().await?;
let source = match source {
Ok(source) => source,
Err(e) => {
match e {
songbird::input::error::Error::Json {
ref error,
ref parsed_text,
} => {
debug!("Failed to play: {}", error);
debug!("Parsed text: {}", parsed_text);
}
_ => {
debug!("Failed to play: {}", e);
}
}
return Err(Box::new(e));
}
};
debug!("Playing: {:?}", source.metadata);
let title = source
.metadata
.title
.clone()
.unwrap_or(String::from("This video"));
debug!("Playing: {:?}", metadata);
let title = metadata.title.clone().unwrap_or(String::from("This video"));
let mut msg = MessageBuilder::new();
@ -142,13 +118,15 @@ pub async fn play(
msg.push_bold("Now playing: ").push_named_link(title, url);
response.edit(ctx, |r| r.content(msg.build())).await?;
response
.edit(ctx, CreateReply::default().content(msg.build()))
.await?;
let mut queue = ctx.data().queue.lock();
if !queue.is_empty() {
let _ = queue.stop();
}
queue.add_next(source, &mut handler);
queue.add_next(source, &mut handler)?;
queue.resume()?;
} else {
ctx.say("Neither of us are in a voice channel, silly.")
@ -168,7 +146,7 @@ pub async fn queue(
return Ok(());
};
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -178,20 +156,19 @@ pub async fn queue(
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_none() {
let Some(Some(channel_id)) = guild
.voice_states
.get(&ctx.author().id)
.map(|vs| vs.channel_id)
else {
ctx.say("Neither of us are in a voice channel, silly.")
.await?;
if manager.get(guild_id).is_none() {
let Some((guild_id, Some(channel_id))) = ctx.guild().and_then(|g| {
g.voice_states
.get(&ctx.author().id)
.map(|vs| (g.id, vs.channel_id))
}) else {
ctx.say("You're not in a voice channel, silly.").await?;
return Ok(());
};
let _handler = manager.join(guild.id, channel_id).await;
let _handler = manager.join(guild_id, channel_id).await;
}
if let Some(handler_lock) = manager.get(guild.id) {
if let Some(handler_lock) = manager.get(guild_id) {
let reply = poise::CreateReply::default()
.content(personality::get_random_loading_message())
.embed(
@ -203,13 +180,11 @@ pub async fn queue(
let mut handler = handler_lock.lock().await;
debug!("Trying to play: {}", url);
let source = songbird::ytdl(&url).await?;
debug!("Playing: {:?}", source.metadata);
let title = source
.metadata
.title
.clone()
.unwrap_or(String::from("This video"));
let mut source: Input = YoutubeDl::new(ctx.data().http_client.clone(), url.clone()).into();
let metadata = source.aux_metadata().await?;
debug!("Playing: {:?}", metadata);
let title = metadata.title.clone().unwrap_or(String::from("This video"));
let mut msg = MessageBuilder::new();
@ -225,10 +200,12 @@ pub async fn queue(
msg.push_bold("Queued: ").push_named_link(title, url);
response.edit(ctx, |r| r.content(msg.build())).await?;
response
.edit(ctx, CreateReply::default().content(msg.build()))
.await?;
let mut queue = ctx.data().queue.lock();
queue.add_to_end(source, &mut handler);
queue.add_to_end(source, &mut handler)?;
} else {
ctx.say("Neither of us are in a voice channel, silly.")
.await?;
@ -239,7 +216,7 @@ pub async fn queue(
#[poise::command(slash_command)]
pub async fn stop(ctx: CommandContext<'_>) -> Result<(), Error> {
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -249,7 +226,7 @@ pub async fn stop(ctx: CommandContext<'_>) -> Result<(), Error> {
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_some() {
if manager.get(guild_id).is_some() {
{
let mut queue = ctx.data().queue.lock();
queue.stop()?;
@ -265,7 +242,7 @@ pub async fn stop(ctx: CommandContext<'_>) -> Result<(), Error> {
#[poise::command(slash_command)]
pub async fn skip(ctx: CommandContext<'_>) -> Result<(), Error> {
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -275,7 +252,7 @@ pub async fn skip(ctx: CommandContext<'_>) -> Result<(), Error> {
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_some() {
if manager.get(guild_id).is_some() {
{
let mut queue = ctx.data().queue.lock();
let _ = queue.stop();
@ -292,7 +269,7 @@ pub async fn skip(ctx: CommandContext<'_>) -> Result<(), Error> {
#[poise::command(slash_command)]
pub async fn pause(ctx: CommandContext<'_>) -> Result<(), Error> {
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -302,7 +279,7 @@ pub async fn pause(ctx: CommandContext<'_>) -> Result<(), Error> {
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_some() {
if manager.get(guild_id).is_some() {
{
let mut queue = ctx.data().queue.lock();
queue.pause()?;
@ -318,7 +295,7 @@ pub async fn pause(ctx: CommandContext<'_>) -> Result<(), Error> {
#[poise::command(slash_command)]
pub async fn resume(ctx: CommandContext<'_>) -> Result<(), Error> {
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -328,7 +305,7 @@ pub async fn resume(ctx: CommandContext<'_>) -> Result<(), Error> {
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_some() {
if manager.get(guild_id).is_some() {
{
let mut queue = ctx.data().queue.lock();
queue.resume()?;
@ -344,7 +321,7 @@ pub async fn resume(ctx: CommandContext<'_>) -> Result<(), Error> {
#[poise::command(slash_command)]
pub async fn clear(ctx: CommandContext<'_>) -> Result<(), Error> {
let Some(guild) = ctx.guild() else {
let Some(guild_id) = ctx.guild().map(|g| g.id) else {
ctx.say("You're not in a server, silly.").await?;
return Ok(());
};
@ -354,7 +331,7 @@ pub async fn clear(ctx: CommandContext<'_>) -> Result<(), Error> {
.context("Expected a songbird manager")?
.clone();
if manager.get(guild.id).is_some() {
if manager.get(guild_id).is_some() {
{
let mut queue = ctx.data().queue.lock();
queue.clear();

View file

@ -5,17 +5,19 @@ mod personality;
mod queue;
use commands::*;
use openai::set_key;
use songbird::SerenityInit;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
use std::{env, sync::Arc};
use anyhow::Result;
use openai::set_key;
use parking_lot::Mutex;
use poise::serenity_prelude as serenity;
use reqwest::Client as HttpClient;
use songbird::SerenityInit;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
pub struct Data {
http_client: HttpClient,
queue: Arc<Mutex<queue::Queue>>,
}
pub type Error = Box<dyn std::error::Error + Send + Sync>;
@ -80,13 +82,14 @@ async fn main() -> Result<()> {
.setup(|_ctx, _data, _framework| {
Box::pin(async move {
Ok(Data {
http_client: HttpClient::new(),
queue: Arc::new(Mutex::new(queue::Queue::new())),
})
})
})
.build();
let client = serenity::ClientBuilder::new(token, intents)
serenity::ClientBuilder::new(token, intents)
.framework(framework)
.register_songbird()
.await?

View file

@ -6,9 +6,8 @@ use anyhow::Result;
use parking_lot::Mutex;
use poise::async_trait;
use songbird::{
events::EventData,
input::Input,
tracks::{self, Track, TrackHandle},
tracks::{Track, TrackHandle},
Driver, Event, EventContext, EventHandler, TrackEvent,
};
use thiserror::Error;
@ -31,16 +30,12 @@ struct QueueHandler {
impl QueueHandler {
/// Event to remove the track from the queue when it ends
pub fn register_track_end_event(track: &mut Track, remote_lock: Arc<Mutex<QueueCore>>) {
let position = track.position();
track
.events
.as_mut()
.expect("Why is this even an Option?")
.add_event(
EventData::new(Event::Track(TrackEvent::End), QueueHandler { remote_lock }),
position,
);
pub fn register_track_end_event(
track: &mut TrackHandle,
remote_lock: Arc<Mutex<QueueCore>>,
) -> Result<()> {
track.add_event(Event::Track(TrackEvent::End), QueueHandler { remote_lock })?;
Ok(())
}
}
@ -127,14 +122,13 @@ impl Queue {
}
/// Adds a track to the end of the queue
pub fn add_to_end(&mut self, source: Input, handler: &mut Driver) -> TrackHandle {
pub fn add_to_end(&mut self, source: Input, handler: &mut Driver) -> Result<TrackHandle> {
let mut inner = self.inner.lock();
let (mut track, handle) = tracks::create_player(source);
track.pause();
QueueHandler::register_track_end_event(&mut track, self.inner.clone());
let track = Track::from(source).pause();
let mut handle = handler.play(track);
QueueHandler::register_track_end_event(&mut handle, self.inner.clone())?;
inner.tracks.push_back(handle.clone());
handler.play(track);
handle
Ok(handle)
}
/// Adds multiple tracks to the end of the queue
@ -142,27 +136,26 @@ impl Queue {
&mut self,
sources: Vec<Input>,
handler: &mut Driver,
) -> Vec<TrackHandle> {
) -> Result<Vec<TrackHandle>> {
let mut handles = Vec::new();
for source in sources {
handles.push(self.add_to_end(source, handler));
handles.push(self.add_to_end(source, handler)?);
}
handles
Ok(handles)
}
/// Adds a track to play next
pub fn add_next(&mut self, source: Input, handler: &mut Driver) -> TrackHandle {
pub fn add_next(&mut self, source: Input, handler: &mut Driver) -> Result<TrackHandle> {
let mut inner = self.inner.lock();
let (mut track, handle) = tracks::create_player(source);
track.pause();
QueueHandler::register_track_end_event(&mut track, self.inner.clone());
let track = Track::from(source).pause();
let mut handle = handler.play(track);
QueueHandler::register_track_end_event(&mut handle, self.inner.clone())?;
if inner.tracks.is_empty() {
inner.tracks.push_back(handle.clone());
} else {
inner.tracks.insert(1, handle.clone());
}
handler.play(track);
handle
Ok(handle)
}
/// Clears the queue