Add update behavior settings to /playing
parent
c5ae089930
commit
c7cb26a1f8
|
@ -65,6 +65,13 @@ impl PlaybackInfo {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn album_name(&self) -> Option<String> {
|
||||
match &self.audio_item.unique_fields {
|
||||
UniqueFields::Episode { .. } => None,
|
||||
UniqueFields::Track { album, .. } => Some(album.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thumbnail(&self) -> String {
|
||||
self.audio_item
|
||||
.covers
|
||||
|
|
|
@ -31,7 +31,11 @@ pub enum SessionCommand {
|
|||
GetPlayer(oneshot::Sender<PlayerHandle>),
|
||||
GetActive(oneshot::Sender<bool>),
|
||||
|
||||
CreatePlaybackEmbed(SessionHandle, CommandInteraction),
|
||||
CreatePlaybackEmbed(
|
||||
SessionHandle,
|
||||
CommandInteraction,
|
||||
playback_embed::UpdateBehavior,
|
||||
),
|
||||
CreateLyricsEmbed(SessionHandle, CommandInteraction),
|
||||
|
||||
Reactivate(UserId, oneshot::Sender<Result<()>>),
|
||||
|
@ -207,8 +211,8 @@ impl Session {
|
|||
SessionCommand::GetPlayer(sender) => _ = sender.send(self.player.clone()),
|
||||
SessionCommand::GetActive(sender) => _ = sender.send(self.active),
|
||||
|
||||
SessionCommand::CreatePlaybackEmbed(handle, interaction) => {
|
||||
match PlaybackEmbed::create(self, handle, interaction).await {
|
||||
SessionCommand::CreatePlaybackEmbed(handle, interaction, behavior) => {
|
||||
match PlaybackEmbed::create(self, handle, interaction, behavior).await {
|
||||
Ok(Some(playback_embed)) => {
|
||||
self.playback_embed = Some(playback_embed);
|
||||
}
|
||||
|
@ -274,8 +278,10 @@ impl Session {
|
|||
PlayerEvent::TrackChanged(_) => {}
|
||||
}
|
||||
|
||||
let force_edit = matches!(event, PlayerEvent::TrackChanged(_));
|
||||
|
||||
if let Some(playback_embed) = &self.playback_embed {
|
||||
if playback_embed.invoke_update().await.is_err() {
|
||||
if playback_embed.invoke_update(force_edit).await.is_err() {
|
||||
self.playback_embed = None;
|
||||
}
|
||||
}
|
||||
|
@ -457,11 +463,16 @@ impl SessionHandle {
|
|||
/// Create a playback embed as a response to an interaction
|
||||
///
|
||||
/// This playback embed will automatically update when certain events happen
|
||||
pub async fn create_playback_embed(&self, interaction: CommandInteraction) -> Result<()> {
|
||||
pub async fn create_playback_embed(
|
||||
&self,
|
||||
interaction: CommandInteraction,
|
||||
behavior: playback_embed::UpdateBehavior,
|
||||
) -> Result<()> {
|
||||
self.commands
|
||||
.send(SessionCommand::CreatePlaybackEmbed(
|
||||
self.clone(),
|
||||
interaction,
|
||||
behavior,
|
||||
))
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use log::{error, trace};
|
||||
use poise::ChoiceParameter;
|
||||
use serenity::{
|
||||
all::{
|
||||
ButtonStyle, CommandInteraction, ComponentInteraction, ComponentInteractionCollector,
|
||||
Context, CreateActionRow, CreateButton, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter,
|
||||
CreateInteractionResponse, CreateInteractionResponseFollowup,
|
||||
CreateInteractionResponseMessage, EditMessage, Message, User,
|
||||
CreateInteractionResponseMessage, CreateMessage, EditMessage, Message, User,
|
||||
},
|
||||
futures::StreamExt,
|
||||
};
|
||||
|
@ -18,7 +19,25 @@ use crate::{Session, SessionHandle};
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum Command {
|
||||
InvokeUpdate,
|
||||
InvokeUpdate(bool),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ChoiceParameter)]
|
||||
pub enum UpdateBehavior {
|
||||
#[default]
|
||||
Default,
|
||||
Static,
|
||||
Pinned,
|
||||
}
|
||||
|
||||
impl UpdateBehavior {
|
||||
pub fn is_static(&self) -> bool {
|
||||
matches!(self, Self::Static)
|
||||
}
|
||||
|
||||
pub fn is_pinned(&self) -> bool {
|
||||
matches!(self, Self::Pinned)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PlaybackEmbed {
|
||||
|
@ -29,6 +48,8 @@ pub struct PlaybackEmbed {
|
|||
|
||||
last_update: Instant,
|
||||
update_in: Option<Duration>,
|
||||
force_edit: bool,
|
||||
update_behavior: UpdateBehavior,
|
||||
|
||||
rx: mpsc::Receiver<Command>,
|
||||
}
|
||||
|
@ -38,6 +59,7 @@ impl PlaybackEmbed {
|
|||
session: &Session,
|
||||
handle: SessionHandle,
|
||||
interaction: CommandInteraction,
|
||||
update_behavior: UpdateBehavior,
|
||||
) -> Result<Option<PlaybackEmbedHandle>> {
|
||||
let ctx = session.context.clone();
|
||||
|
||||
|
@ -69,6 +91,11 @@ impl PlaybackEmbed {
|
|||
)
|
||||
.await?;
|
||||
|
||||
// If this is a static embed, we don't need to return any handles
|
||||
if update_behavior.is_static() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Retrieve message instead of editing interaction response, as those tokens are only valid for 15 minutes
|
||||
let message = interaction.get_response(&ctx).await?;
|
||||
|
||||
|
@ -84,6 +111,8 @@ impl PlaybackEmbed {
|
|||
message,
|
||||
last_update: Instant::now(),
|
||||
update_in: None,
|
||||
force_edit: false,
|
||||
update_behavior,
|
||||
rx,
|
||||
};
|
||||
|
||||
|
@ -121,7 +150,7 @@ impl PlaybackEmbed {
|
|||
tokio::time::sleep(update_in).await;
|
||||
}
|
||||
}, if self.update_in.is_some() => {
|
||||
if self.update_embed().await.is_break() {
|
||||
if self.update_embed(self.force_edit).await.is_break() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -133,15 +162,16 @@ impl PlaybackEmbed {
|
|||
trace!("Received command: {command:?}");
|
||||
|
||||
match command {
|
||||
Command::InvokeUpdate => {
|
||||
Command::InvokeUpdate(force_edit) => {
|
||||
if self.last_update.elapsed() < Duration::from_secs(2) {
|
||||
if self.update_in.is_some() {
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
self.update_in = Some(Duration::from_secs(2) - self.last_update.elapsed());
|
||||
self.force_edit = force_edit;
|
||||
} else {
|
||||
self.update_embed().await?;
|
||||
self.update_embed(force_edit).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +246,7 @@ impl PlaybackEmbed {
|
|||
Ok((player, playback_info, owner))
|
||||
}
|
||||
|
||||
async fn update_embed(&mut self) -> ControlFlow<(), ()> {
|
||||
async fn update_embed(&mut self, force_edit: bool) -> ControlFlow<(), ()> {
|
||||
self.update_in = None;
|
||||
|
||||
let Ok(owner) = self.session.owner().await else {
|
||||
|
@ -246,6 +276,30 @@ impl PlaybackEmbed {
|
|||
}
|
||||
};
|
||||
|
||||
let should_pin = !force_edit && self.update_behavior.is_pinned();
|
||||
|
||||
if should_pin {
|
||||
self.message.delete(&self.ctx).await.ok();
|
||||
|
||||
match self
|
||||
.message
|
||||
.channel_id
|
||||
.send_message(
|
||||
&self.ctx,
|
||||
CreateMessage::new()
|
||||
.embed(build_embed(&playback_info, &owner))
|
||||
.components(vec![build_buttons(self.id, playback_info.playing())]),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(message) => self.message = message,
|
||||
Err(why) => {
|
||||
error!("Failed to update playback embed: {why}");
|
||||
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
};
|
||||
} else {
|
||||
if let Err(why) = self
|
||||
.message
|
||||
.edit(
|
||||
|
@ -259,7 +313,8 @@ impl PlaybackEmbed {
|
|||
error!("Failed to update playback embed: {why}");
|
||||
|
||||
return ControlFlow::Break(());
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
self.last_update = Instant::now();
|
||||
|
||||
|
@ -267,6 +322,18 @@ impl PlaybackEmbed {
|
|||
}
|
||||
|
||||
async fn update_not_playing(&mut self) -> Result<()> {
|
||||
// If pinned, try to delete old message and send new one
|
||||
if self.update_behavior.is_pinned() {
|
||||
self.message.delete(&self.ctx).await.ok();
|
||||
self.message = self
|
||||
.message
|
||||
.channel_id
|
||||
.send_message(&self.ctx, CreateMessage::new().embed(not_playing_embed()))
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.message
|
||||
.edit(&self.ctx, EditMessage::new().embed(not_playing_embed()))
|
||||
.await?;
|
||||
|
@ -284,8 +351,8 @@ impl PlaybackEmbedHandle {
|
|||
!self.tx.is_closed()
|
||||
}
|
||||
|
||||
pub async fn invoke_update(&self) -> Result<()> {
|
||||
self.tx.send(Command::InvokeUpdate).await?;
|
||||
pub async fn invoke_update(&self, force_edit: bool) -> Result<()> {
|
||||
self.tx.send(Command::InvokeUpdate(force_edit)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -331,7 +398,13 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed {
|
|||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
description += &format!("By {artists}\n\n");
|
||||
description += &format!("By {artists}\n");
|
||||
}
|
||||
|
||||
if let Some(album_name) = playback_info.album_name() {
|
||||
description += &format!("In album: **{album_name}**\n\n");
|
||||
} else {
|
||||
description += "\n";
|
||||
}
|
||||
|
||||
if let Some(show_name) = playback_info.show_name() {
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
use anyhow::Result;
|
||||
use poise::CreateReply;
|
||||
use serenity::all::CreateEmbed;
|
||||
use spoticord_session::manager::SessionQuery;
|
||||
use spoticord_session::{manager::SessionQuery, playback_embed::UpdateBehavior};
|
||||
use spoticord_utils::discord::Colors;
|
||||
|
||||
use crate::bot::Context;
|
||||
|
||||
/// Show details of the current song that is being played
|
||||
#[poise::command(slash_command, guild_only)]
|
||||
pub async fn playing(ctx: Context<'_>) -> Result<()> {
|
||||
pub async fn playing(
|
||||
ctx: Context<'_>,
|
||||
#[description = "How Spoticord should update this information"] update_behavior: Option<
|
||||
UpdateBehavior,
|
||||
>,
|
||||
) -> Result<()> {
|
||||
let manager = ctx.data();
|
||||
let guild = ctx.guild_id().expect("poise lied to me");
|
||||
|
||||
|
@ -33,7 +38,10 @@ pub async fn playing(ctx: Context<'_>) -> Result<()> {
|
|||
};
|
||||
|
||||
session
|
||||
.create_playback_embed(context.interaction.clone())
|
||||
.create_playback_embed(
|
||||
context.interaction.clone(),
|
||||
update_behavior.unwrap_or_default(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue