Change now playing to update automatically

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/2/head
trivernis 3 years ago
parent f32228e61f
commit f36cf5a477
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -4,6 +4,8 @@ use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
use crate::commands::music::get_queue_for_guild;
use crate::messages::music::NowPlayingMessage;
use std::mem;
#[command]
#[only_in(guilds)]
@ -15,29 +17,17 @@ async fn current(ctx: &Context, msg: &Message) -> CommandResult {
log::debug!("Displaying current song for queue in {}", guild.id);
let queue = get_queue_for_guild(ctx, &guild.id).await?;
let queue_lock = queue.lock().await;
let mut queue_lock = queue.lock().await;
if let Some(current) = queue_lock.current() {
let metadata = current.metadata().clone();
log::trace!("Metadata is {:?}", metadata);
msg.channel_id
.send_message(ctx, |m| {
m.embed(|mut e| {
e = e.description(format!(
"Now Playing [{}]({}) by {}",
metadata.title.unwrap(),
metadata.source_url.unwrap(),
metadata.artist.unwrap()
));
let np_msg =
NowPlayingMessage::create(ctx.http.clone(), &msg.channel_id, &metadata).await?;
if let Some(thumb) = metadata.thumbnail {
e = e.thumbnail(thumb);
}
e
})
})
.await?;
if let Some(old_np) = mem::replace(&mut queue_lock.now_playing_msg, Some(np_msg)) {
let _ = old_np.inner().delete().await;
}
}
Ok(())

@ -13,12 +13,10 @@ use crate::commands::music::{get_queue_for_guild, get_voice_manager};
#[allowed_roles("DJ")]
async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
log::debug!("Leave request received for guild {}", guild.id);
let manager = get_voice_manager(ctx).await;
let queue = get_queue_for_guild(ctx, &guild.id).await?;
let queue_lock = queue.lock().await;
log::trace!("Queue is {:?}", queue_lock);
let handler = manager.get(guild.id);
if let Some(handler) = handler {

@ -1,5 +1,12 @@
use std::sync::Arc;
use crate::providers::music::queue::{MusicQueue, Song};
use crate::providers::music::{
get_video_information, get_videos_for_playlist, search_video_information,
};
use crate::utils::context_data::{DatabaseContainer, Store};
use crate::utils::error::{BotError, BotResult};
use regex::Regex;
use serenity::async_trait;
use serenity::client::Context;
use serenity::framework::standard::macros::group;
@ -10,31 +17,10 @@ use serenity::model::id::{ChannelId, GuildId, UserId};
use songbird::{
Call, Event, EventContext, EventHandler as VoiceEventHandler, Songbird, TrackEvent,
};
use tokio::sync::Mutex;
use clear_queue::CLEAR_QUEUE_COMMAND;
use current::CURRENT_COMMAND;
use join::JOIN_COMMAND;
use leave::LEAVE_COMMAND;
use lyrics::LYRICS_COMMAND;
use pause::PAUSE_COMMAND;
use play::PLAY_COMMAND;
use play_next::PLAY_NEXT_COMMAND;
use playlists::PLAYLISTS_COMMAND;
use queue::QUEUE_COMMAND;
use save_playlist::SAVE_PLAYLIST_COMMAND;
use shuffle::SHUFFLE_COMMAND;
use skip::SKIP_COMMAND;
use crate::providers::music::queue::{MusicQueue, Song};
use crate::providers::music::{
get_video_information, get_videos_for_playlist, search_video_information,
};
use crate::utils::context_data::{DatabaseContainer, Store};
use crate::utils::error::{BotError, BotResult};
use regex::Regex;
use std::mem;
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use std::time::Duration;
use tokio::sync::Mutex;
mod clear_queue;
mod current;
@ -50,6 +36,20 @@ mod save_playlist;
mod shuffle;
mod skip;
use clear_queue::CLEAR_QUEUE_COMMAND;
use current::CURRENT_COMMAND;
use join::JOIN_COMMAND;
use leave::LEAVE_COMMAND;
use lyrics::LYRICS_COMMAND;
use pause::PAUSE_COMMAND;
use play::PLAY_COMMAND;
use play_next::PLAY_NEXT_COMMAND;
use playlists::PLAYLISTS_COMMAND;
use queue::QUEUE_COMMAND;
use save_playlist::SAVE_PLAYLIST_COMMAND;
use shuffle::SHUFFLE_COMMAND;
use skip::SKIP_COMMAND;
#[group]
#[commands(
join,
@ -248,8 +248,15 @@ async fn play_next_in_queue(
let mut handler_lock = handler.lock().await;
let track = handler_lock.play_only_source(source);
log::trace!("Track is {:?}", track);
if let Some(np) = &queue_lock.now_playing_msg {
let _ = np.refresh(track.metadata()).await;
}
queue_lock.set_current(track);
} else {
if let Some(np) = mem::take(&mut queue_lock.now_playing_msg) {
let _ = np.inner().delete().await;
}
queue_lock.clear_current();
}
true

@ -8,7 +8,7 @@ use crate::commands::music::{
join_channel, play_next_in_queue,
};
use crate::providers::constants::SETTING_AUTOSHUFFLE;
use crate::commands::settings::SETTING_AUTOSHUFFLE;
use crate::utils::context_data::get_database_from_context;
#[command]

@ -3,7 +3,7 @@ use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message;
use crate::providers::constants::GUILD_SETTINGS;
use crate::commands::settings::GUILD_SETTINGS;
use crate::utils::context_data::get_database_from_context;
#[command]

@ -10,3 +10,6 @@ mod set;
#[commands(set, get)]
#[prefix("settings")]
pub struct Settings;
pub const SETTING_AUTOSHUFFLE: &str = "music.autoshuffle";
pub const GUILD_SETTINGS: &[&str] = &[SETTING_AUTOSHUFFLE];

@ -3,7 +3,7 @@ use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message;
use crate::providers::constants::GUILD_SETTINGS;
use crate::commands::settings::GUILD_SETTINGS;
use crate::utils::context_data::get_database_from_context;
#[command]

@ -1,15 +1,25 @@
use crate::commands::music::get_queue_for_guild;
use crate::utils::context_data::EventDrivenMessageContainer;
use serenity::async_trait;
use serenity::client::Context;
use serenity::model::channel::Reaction;
use serenity::model::event::ResumedEvent;
use serenity::model::gateway::{Activity, Ready};
use serenity::model::guild::Member;
use serenity::model::id::{ChannelId, GuildId};
use serenity::model::id::{ChannelId, GuildId, MessageId};
use serenity::model::voice::VoiceState;
use serenity::prelude::*;
pub(crate) struct Handler;
macro_rules! log_msg_fire_error {
($msg:expr) => {
if let Err(e) = $msg {
log::error!("Failed to handle event for message: {:?}", e);
}
};
}
#[async_trait]
impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) {
@ -55,6 +65,67 @@ impl EventHandler for Handler {
queue_lock.leave_flag = count == 0;
}
}
/// Fired when a message was deleted
async fn message_delete(
&self,
ctx: Context,
channel_id: ChannelId,
message_id: MessageId,
_: Option<GuildId>,
) {
let mut data = ctx.data.write().await;
let listeners = data.get_mut::<EventDrivenMessageContainer>().unwrap();
if let Some(msg) = listeners.get(&(channel_id.0, message_id.0)) {
log_msg_fire_error!(msg.on_deleted().await);
listeners.remove(&(channel_id.0, message_id.0));
}
}
/// Fired when multiple messages were deleted
async fn message_delete_bulk(
&self,
ctx: Context,
channel_id: ChannelId,
message_ids: Vec<MessageId>,
_: Option<GuildId>,
) {
let data = ctx.data.read().await;
let listeners = data.get::<EventDrivenMessageContainer>().unwrap();
for message_id in message_ids {
if let Some(msg) = listeners.get(&(channel_id.0, message_id.0)) {
log_msg_fire_error!(msg.on_deleted().await);
}
}
}
/// Fired when a reaction was added to a message
async fn reaction_add(&self, ctx: Context, reaction: Reaction) {
let data = ctx.data.read().await;
let listeners = data.get::<EventDrivenMessageContainer>().unwrap();
let message_id = reaction.message_id;
let channel_id = reaction.channel_id;
if let Some(msg) = listeners.get(&(channel_id.0, message_id.0)) {
log_msg_fire_error!(msg.on_reaction_add(reaction).await);
}
}
/// Fired when a reaction was added to a message
async fn reaction_remove(&self, ctx: Context, reaction: Reaction) {
let data = ctx.data.read().await;
let listeners = data.get::<EventDrivenMessageContainer>().unwrap();
let message_id = reaction.message_id;
let channel_id = reaction.channel_id;
if let Some(msg) = listeners.get(&(channel_id.0, message_id.0)) {
log_msg_fire_error!(msg.on_reaction_remove(reaction).await);
}
}
}
/// Returns the number of members in the channel if it's the bots voice channel

@ -4,6 +4,7 @@ use crate::utils::logging::init_logger;
pub mod client;
mod commands;
pub mod handler;
mod messages;
mod providers;
pub mod utils;

@ -0,0 +1 @@
pub mod music;

@ -0,0 +1,58 @@
use crate::utils::error::BotResult;
use crate::utils::messages::ShareableMessage;
use serenity::builder::CreateEmbed;
use serenity::http::Http;
use serenity::model::prelude::ChannelId;
use songbird::input::Metadata;
use std::sync::Arc;
#[derive(Clone)]
pub struct NowPlayingMessage {
inner: ShareableMessage,
}
impl NowPlayingMessage {
/// Creates a new now playing message
pub async fn create(
ctx: Arc<Http>,
channel_id: &ChannelId,
meta: &Metadata,
) -> BotResult<Self> {
let inner = ShareableMessage::create(ctx, channel_id, |f| {
f.embed(|e| Self::create_embed(meta, e))
})
.await?;
Ok(Self { inner })
}
/// Returns the inner shareable message
pub fn inner(&self) -> &ShareableMessage {
&self.inner
}
/// Refreshes the now playing message
pub async fn refresh(&self, meta: &Metadata) -> BotResult<()> {
self.inner
.edit(|m| m.embed(|e| Self::create_embed(meta, e)))
.await?;
Ok(())
}
/// Creates the embed of the now playing message
fn create_embed<'a>(meta: &Metadata, mut embed: &'a mut CreateEmbed) -> &'a mut CreateEmbed {
embed = embed.description(format!(
"Now Playing [{}]({}) by {}",
meta.title.clone().unwrap(),
meta.source_url.clone().unwrap(),
meta.artist.clone().unwrap()
));
if let Some(thumb) = meta.thumbnail.clone() {
embed = embed.thumbnail(thumb);
}
embed
}
}

@ -1,2 +0,0 @@
pub const SETTING_AUTOSHUFFLE: &str = "music.autoshuffle";
pub const GUILD_SETTINGS: &[&str] = &[SETTING_AUTOSHUFFLE];

@ -1,2 +1 @@
pub(crate) mod constants;
pub(crate) mod music;

@ -2,16 +2,18 @@ use std::collections::VecDeque;
use songbird::tracks::TrackHandle;
use crate::messages::music::NowPlayingMessage;
use crate::providers::music::responses::{PlaylistEntry, VideoInformation};
use crate::providers::music::search_video_information;
use crate::utils::shuffle_vec_deque;
use aspotify::{Track, TrackSimplified};
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct MusicQueue {
inner: VecDeque<Song>,
current: Option<TrackHandle>,
paused: bool,
pub now_playing_msg: Option<NowPlayingMessage>,
pub leave_flag: bool,
}
@ -22,6 +24,7 @@ impl MusicQueue {
current: None,
paused: false,
leave_flag: false,
now_playing_msg: None,
}
}
@ -85,6 +88,7 @@ impl MusicQueue {
}
}
/// Returns if the queue is paused
pub fn paused(&self) -> bool {
self.paused
}

@ -7,6 +7,7 @@ use tokio::sync::Mutex;
use crate::providers::music::queue::MusicQueue;
use crate::providers::music::spotify::SpotifyApi;
use crate::utils::messages::EventDrivenMessage;
use database::Database;
use serenity::client::Context;
@ -49,3 +50,9 @@ pub async fn get_database_from_context(ctx: &Context) -> Database {
database.clone()
}
pub struct EventDrivenMessageContainer;
impl TypeMapKey for EventDrivenMessageContainer {
type Value = HashMap<(u64, u64), Box<dyn EventDrivenMessage>>;
}

@ -0,0 +1,72 @@
use crate::utils::error::BotResult;
use serenity::async_trait;
use serenity::builder::{CreateMessage, EditMessage};
use serenity::http::{CacheHttp, Http};
use serenity::model::channel::{Message, Reaction};
use serenity::model::id::{ChannelId, MessageId};
use std::sync::Arc;
#[async_trait]
pub trait EventDrivenMessage: Send + Sync {
async fn on_deleted(&self) -> BotResult<()>;
async fn on_reaction_add(&self, reaction: Reaction) -> BotResult<()>;
async fn on_reaction_remove(&self, reaction: Reaction) -> BotResult<()>;
}
#[derive(Clone)]
pub struct ShareableMessage {
http: Arc<Http>,
channel_id: u64,
message_id: u64,
}
impl ShareableMessage {
/// Creates a new active message
pub async fn create<'a, F>(http: Arc<Http>, channel_id: &ChannelId, f: F) -> BotResult<Self>
where
for<'b> F: FnOnce(&'b mut CreateMessage<'a>) -> &'b mut CreateMessage<'a>,
{
let msg = channel_id.send_message(http.http(), f).await?;
Ok(Self::new(http, &msg.channel_id, &msg.id))
}
/// Creates a new active message
pub fn new(http: Arc<Http>, channel_id: &ChannelId, message_id: &MessageId) -> Self {
Self {
http,
channel_id: channel_id.0,
message_id: message_id.0,
}
}
/// Deletes the underlying message
pub async fn delete(&self) -> BotResult<()> {
let msg = self.get_message().await?;
msg.delete(&self.http).await?;
Ok(())
}
/// Edits the active message
pub async fn edit<F>(&self, f: F) -> BotResult<()>
where
F: FnOnce(&mut EditMessage) -> &mut EditMessage,
{
let mut message = self.get_message().await?;
message.edit(&self.http, f).await?;
Ok(())
}
/// Returns the underlying message
async fn get_message(&self) -> BotResult<Message> {
let message = self
.http
.http()
.get_message(self.channel_id, self.message_id)
.await?;
Ok(message)
}
}

@ -5,6 +5,7 @@ use rand::Rng;
pub(crate) mod context_data;
pub(crate) mod error;
pub(crate) mod logging;
pub(crate) mod messages;
/// Fisher-Yates shuffle for VecDeque
pub fn shuffle_vec_deque<T>(deque: &mut VecDeque<T>) {

Loading…
Cancel
Save