Uhm so yeah here's some c o d e

This commit is contained in:
DaXcess
2022-10-31 22:46:34 +01:00
parent 6a77189343
commit d530b182b3
33 changed files with 1934 additions and 298 deletions
+34
View File
@@ -0,0 +1,34 @@
use serenity::{
builder::CreateApplicationCommand,
model::prelude::interaction::application_command::ApplicationCommandInteraction,
prelude::Context,
};
use crate::{
bot::commands::{respond_message, CommandOutput},
utils::embed::{EmbedBuilder, Status},
};
pub const NAME: &str = "help";
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Spoticord Help")
.icon_url("https://spoticord.com/img/logo-standard.webp")
.description(format!("Click **[here](https://spoticord.com/commands)** for a list of commands.\n{}",
"If you need help setting Spoticord up you can check out the **[Documentation](https://spoticord.com/documentation)** page on the Spoticord website.\n\n"))
.status(Status::Info)
.build(),
false,
)
.await;
})
}
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command.name(NAME).description("Shows the help message")
}
+58 -62
View File
@@ -1,53 +1,34 @@
use log::error;
use serenity::{
builder::CreateApplicationCommand,
model::prelude::interaction::{
application_command::ApplicationCommandInteraction, InteractionResponseType,
},
model::prelude::interaction::application_command::ApplicationCommandInteraction,
prelude::Context,
Result as SerenityResult,
};
use crate::{bot::commands::CommandOutput, database::Database};
use crate::{
bot::commands::{respond_message, CommandOutput},
database::Database,
utils::embed::{EmbedBuilder, Status},
};
pub const NAME: &str = "link";
async fn respond_message(
ctx: &Context,
command: &ApplicationCommandInteraction,
msg: impl Into<String>,
ephemeral: bool,
) -> SerenityResult<()> {
command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| message.content(msg.into()).ephemeral(ephemeral))
})
.await
}
fn check_msg(result: SerenityResult<()>) {
if let Err(why) = result {
error!("Error sending message: {:?}", why);
}
}
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
let data = ctx.data.read().await;
let database = data.get::<Database>().unwrap();
if let Ok(_) = database.get_user_account(command.user.id.to_string()).await {
check_msg(
respond_message(
&ctx,
&command,
"You have already linked your Spotify account.",
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("You have already linked your Spotify account.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
@@ -56,15 +37,22 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").unwrap();
let link = format!("{}/spotify/{}", base, request.token);
check_msg(
respond_message(
&ctx,
&command,
format!("Go to the following URL to link your account:\n{}", link),
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Link your Spotify account")
.title_url(&link)
.icon_url("https://spoticord.com/img/spotify-logo.png")
.description(format!(
"Go to [this link]({}) to connect your Spotify account.",
link
))
.status(Status::Info)
.build(),
true,
)
.await;
return;
}
@@ -77,30 +65,38 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").unwrap();
let link = format!("{}/spotify/{}", base, request.token);
check_msg(
respond_message(
&ctx,
&command,
format!("Go to the following URL to link your account:\n{}", link),
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Link your Spotify account")
.title_url(&link)
.icon_url("https://spoticord.com/img/spotify-logo.png")
.description(format!(
"Go to [this link]({}) to connect your Spotify account.",
link
))
.status(Status::Info)
.build(),
true,
)
.await;
return;
}
Err(why) => {
error!("Error creating user request: {:?}", why);
check_msg(
respond_message(
&ctx,
&command,
"An error occurred while serving your request. Please try again later.",
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("An error occurred while serving your request. Please try again later.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
+3
View File
@@ -1,2 +1,5 @@
pub mod help;
pub mod link;
pub mod rename;
pub mod unlink;
pub mod version;
+165
View File
@@ -0,0 +1,165 @@
use log::error;
use reqwest::StatusCode;
use serenity::{
builder::CreateApplicationCommand,
model::prelude::{
command::CommandOptionType, interaction::application_command::ApplicationCommandInteraction,
},
prelude::Context,
};
use crate::{
bot::commands::{respond_message, CommandOutput},
database::{Database, DatabaseError},
utils::{
self,
embed::{EmbedBuilder, Status},
},
};
pub const NAME: &str = "rename";
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
let data = ctx.data.read().await;
let database = data.get::<Database>().unwrap();
// Check if user exists, if not, create them
if let Err(why) = database.get_user(command.user.id.to_string()).await {
match why {
DatabaseError::InvalidStatusCode(StatusCode::NOT_FOUND) => {
if let Err(why) = database.create_user(command.user.id.to_string()).await {
error!("Error creating user: {:?}", why);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("Something went wrong while trying to rename your Spoticord device.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
}
_ => {
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("Something went wrong while trying to rename your Spoticord device.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
}
}
let device_name = match command.data.options.get(0) {
Some(option) => match option.value {
Some(ref value) => value.as_str().unwrap().to_string(),
None => {
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("You need to provide a name for your Spoticord device.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
},
None => {
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("You need to provide a name for your Spoticord device.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
};
if let Err(why) = database
.update_user_device_name(command.user.id.to_string(), &device_name)
.await
{
if let DatabaseError::InvalidInputBody(_) = why {
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description(
"Your device name must not exceed 16 characters and be at least 1 character long.",
)
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
error!("Error updating user device name: {:?}", why);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("Something went wrong while trying to rename your Spoticord device.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description(format!(
"Successfully changed the Spotify device name to **{}**",
utils::discord::escape(device_name)
))
.status(Status::Success)
.build(),
true,
)
.await;
})
}
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command
.name(NAME)
.description("Set a new device name that is displayed in Spotify")
.create_option(|option| {
option
.name("name")
.description("The new device name")
.kind(CommandOptionType::String)
.max_length(16)
.required(true)
})
}
+30 -52
View File
@@ -1,42 +1,19 @@
use log::error;
use serenity::{
builder::CreateApplicationCommand,
model::prelude::interaction::{
application_command::ApplicationCommandInteraction, InteractionResponseType,
},
model::prelude::interaction::application_command::ApplicationCommandInteraction,
prelude::Context,
Result as SerenityResult,
};
use crate::{
bot::commands::CommandOutput,
bot::commands::{respond_message, CommandOutput},
database::{Database, DatabaseError},
session::manager::SessionManager,
utils::embed::{EmbedBuilder, Status},
};
pub const NAME: &str = "unlink";
async fn respond_message(
ctx: &Context,
command: &ApplicationCommandInteraction,
msg: impl Into<String>,
ephemeral: bool,
) -> SerenityResult<()> {
command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| message.content(msg.into()).ephemeral(ephemeral))
})
.await
}
fn check_msg(result: SerenityResult<()>) {
if let Err(why) = result {
error!("Error sending message: {:?}", why);
}
}
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
let data = ctx.data.read().await;
@@ -45,9 +22,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
// Disconnect session if user has any
if let Some(session) = session_manager.find(command.user.id).await {
if let Err(why) = session.disconnect().await {
error!("Error disconnecting session: {:?}", why);
}
session.disconnect().await;
}
// Check if user exists in the first place
@@ -57,15 +32,16 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
{
if let DatabaseError::InvalidStatusCode(status) = why {
if status == 404 {
check_msg(
respond_message(
&ctx,
&command,
"You cannot unlink your Spotify account if you currently don't have a linked Spotify account.",
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("You cannot unlink your Spotify account if you haven't linked one.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
@@ -73,28 +49,30 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
error!("Error deleting user account: {:?}", why);
check_msg(
respond_message(
respond_message(
&ctx,
&command,
"An unexpected error has occured while trying to unlink your account. Please try again later.",
EmbedBuilder::new()
.description("An unexpected error has occured while trying to unlink your account. Please try again later.")
.status(Status::Error)
.build(),
true,
)
.await,
);
.await;
return;
}
check_msg(
respond_message(
&ctx,
&command,
"Successfully unlinked your Spotify account from Spoticord",
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("Successfully unlinked your Spotify account from Spoticord")
.status(Status::Success)
.build(),
true,
)
.await;
})
}
+49
View File
@@ -0,0 +1,49 @@
use log::error;
use serenity::{
builder::CreateApplicationCommand,
model::prelude::interaction::{
application_command::ApplicationCommandInteraction, InteractionResponseType,
},
prelude::Context,
};
use crate::{
bot::commands::CommandOutput,
utils::{consts::VERSION, embed::Status},
};
pub const NAME: &str = "version";
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
if let Err(why) = command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| {
message.embed(|embed| {
embed
.title("Spoticord Version")
.author(|author| {
author
.name("Maintained by: RoDaBaFilms")
.url("https://rodabafilms.com/")
.icon_url("https://rodabafilms.com/logo_2021_nobg.png")
})
.description(format!("Current version: {}\n\nSpoticord is open source, check out [our GitHub](https://github.com/SpoticordMusic)", VERSION))
.color(Status::Info as u64)
})
})
})
.await
{
error!("Error sending message: {:?}", why);
}
})
}
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command
.name(NAME)
.description("Shows the current running version of Spoticord")
}
+45 -2
View File
@@ -11,12 +11,39 @@ use serenity::{
prelude::{Context, TypeMapKey},
};
use crate::utils::embed::{make_embed_message, EmbedMessageOptions};
mod core;
mod music;
#[cfg(debug_assertions)]
mod ping;
#[cfg(debug_assertions)]
mod token;
pub async fn respond_message(
ctx: &Context,
command: &ApplicationCommandInteraction,
options: EmbedMessageOptions,
ephemeral: bool,
) {
if let Err(why) = command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| {
message
.embed(|embed| make_embed_message(embed, options))
.ephemeral(ephemeral)
})
})
.await
{
error!("Error sending message: {:?}", why);
}
}
pub type CommandOutput = Pin<Box<dyn Future<Output = ()> + Send>>;
pub type CommandExecutor = fn(Context, ApplicationCommandInteraction) -> CommandOutput;
@@ -44,12 +71,23 @@ impl CommandManager {
}
// Core commands
instance.insert_command(core::help::NAME, core::help::register, core::help::run);
instance.insert_command(
core::version::NAME,
core::version::register,
core::version::run,
);
instance.insert_command(core::link::NAME, core::link::register, core::link::run);
instance.insert_command(
core::unlink::NAME,
core::unlink::register,
core::unlink::run,
);
instance.insert_command(
core::rename::NAME,
core::rename::register,
core::rename::run,
);
// Music commands
instance.insert_command(music::join::NAME, music::join::register, music::join::run);
@@ -58,6 +96,11 @@ impl CommandManager {
music::leave::register,
music::leave::run,
);
instance.insert_command(
music::playing::NAME,
music::playing::register,
music::playing::run,
);
instance
}
@@ -93,8 +136,8 @@ impl CommandManager {
cmds: &HashMap<String, CommandInfo>,
mut commands: &'a mut CreateApplicationCommands,
) -> &'a mut CreateApplicationCommands {
for cmd in cmds {
commands = commands.create_application_command(|command| (cmd.1.register)(command));
for (_, command_info) in cmds {
commands = commands.create_application_command(|command| (command_info.register)(command));
}
commands
+121 -70
View File
@@ -1,41 +1,17 @@
use log::error;
use serenity::{
builder::CreateApplicationCommand,
model::prelude::interaction::{
application_command::ApplicationCommandInteraction, InteractionResponseType,
},
model::prelude::interaction::application_command::ApplicationCommandInteraction,
prelude::Context,
Result as SerenityResult,
};
use crate::{
bot::commands::CommandOutput,
bot::commands::{respond_message, CommandOutput},
session::manager::{SessionCreateError, SessionManager},
utils::embed::{EmbedBuilder, Status},
};
pub const NAME: &str = "join";
async fn respond_message(
ctx: &Context,
command: &ApplicationCommandInteraction,
msg: impl Into<String>,
ephemeral: bool,
) -> SerenityResult<()> {
command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| message.content(msg.into()).ephemeral(ephemeral))
})
.await
}
fn check_msg(result: SerenityResult<()>) {
if let Err(why) = result {
error!("Error sending message: {:?}", why);
}
}
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
let guild = ctx.cache.guild(command.guild_id.unwrap()).unwrap();
@@ -48,15 +24,18 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
{
Some(channel_id) => channel_id,
None => {
check_msg(
respond_message(
&ctx,
&command,
"You need to connect to a voice channel",
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Cannot join voice channel")
.icon_url("https://spoticord.com/static/image/prohibited.png")
.description("You need to connect to a voice channel")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
@@ -66,71 +45,143 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
let mut session_manager = data.get::<SessionManager>().unwrap().clone();
// Check if another session is already active in this server
if let Some(session) = session_manager.get_session(guild.id).await {
let msg = if session.get_owner() == command.user.id {
"You are already playing music in this server"
} else {
"Someone else is already playing music in this server"
};
let session_opt = session_manager.get_session(guild.id).await;
if let Some(session) = &session_opt {
if let Some(owner) = session.get_owner().await {
let msg = if owner == command.user.id {
"You are already controlling the bot"
} else {
"The bot is currently being controlled by someone else"
};
check_msg(respond_message(&ctx, &command, msg, true).await);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Cannot join voice channel")
.icon_url("https://spoticord.com/static/image/prohibited.png")
.description(msg)
.status(Status::Error)
.build(),
true,
)
.await;
return;
return;
}
};
// Prevent duplicate Spotify sessions
if let Some(session) = session_manager.find(command.user.id).await {
check_msg(
respond_message(
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Cannot join voice channel")
.icon_url("https://spoticord.com/static/image/prohibited.png")
.description(
format!(
"You are already playing music in another server ({}).\nStop playing in that server first before joining this one.",
ctx.cache.guild(session.get_guild_id()).unwrap().name
),
)).status(Status::Error).build(),
true,
)
.await,
);
.await;
return;
}
// Create the session, and handle potential errors
if let Err(why) = session_manager
.create_session(&ctx, guild.id, channel_id, command.user.id)
.await
{
// Need to link first
if let SessionCreateError::NoSpotifyError = why {
check_msg(
if let Some(session) = &session_opt {
if let Err(why) = session.update_owner(&ctx, command.user.id).await {
// Need to link first
if let SessionCreateError::NoSpotifyError = why {
respond_message(
&ctx,
&command,
"You need to link your Spotify account. Use `/link` or go to https://account.spoticord.com/ to get started.",
EmbedBuilder::new()
.title("Cannot join voice channel")
.icon_url("https://spoticord.com/static/image/prohibited.png")
.description("You need to link your Spotify account. Use </link:1036714850367320136> or go to https://account.spoticord.com/ to get started.")
.status(Status::Error)
.build(),
true,
)
.await,
);
.await;
return;
}
return;
}
// Any other error
check_msg(
// Any other error
respond_message(
&ctx,
&command,
"An error occurred while joining the channel. Please try again later.",
EmbedBuilder::new()
.title("Cannot join voice channel")
.icon_url("https://spoticord.com/static/image/prohibited.png")
.description("An error occured while joining the channel. Please try again later.")
.status(Status::Error)
.build(),
true,
)
.await,
);
.await;
return;
};
return;
}
} else {
// Create the session, and handle potential errors
if let Err(why) = session_manager
.create_session(&ctx, guild.id, channel_id, command.user.id)
.await
{
// Need to link first
if let SessionCreateError::NoSpotifyError = why {
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Cannot join voice channel")
.icon_url("https://spoticord.com/static/image/prohibited.png")
.description("You need to link your Spotify account. Use </link:1036714850367320136> or go to https://account.spoticord.com/ to get started.")
.status(Status::Error)
.build(),
true,
)
.await;
check_msg(respond_message(&ctx, &command, "Joined the voice channel.", false).await);
return;
}
// Any other error
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Cannot join voice channel")
.icon_url("https://spoticord.com/static/image/prohibited.png")
.description("An error occured while joining the channel. Please try again later.")
.status(Status::Error)
.build(),
true,
)
.await;
return;
};
}
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Connected to voice channel")
.icon_url("https://spoticord.com/static/image/speaker.png")
.description(format!("Come listen along in <#{}>", channel_id))
.footer("Spotify will automatically start playing on Spoticord")
.status(Status::Success)
.build(),
false,
)
.await;
})
}
+42 -51
View File
@@ -1,38 +1,17 @@
use log::error;
use serenity::{
builder::CreateApplicationCommand,
model::prelude::interaction::{
application_command::ApplicationCommandInteraction, InteractionResponseType,
},
model::prelude::interaction::application_command::ApplicationCommandInteraction,
prelude::Context,
Result as SerenityResult,
};
use crate::{bot::commands::CommandOutput, session::manager::SessionManager};
use crate::{
bot::commands::{respond_message, CommandOutput},
session::manager::SessionManager,
utils::embed::{EmbedBuilder, Status},
};
pub const NAME: &str = "leave";
async fn respond_message(
ctx: &Context,
command: &ApplicationCommandInteraction,
msg: &str,
ephemeral: bool,
) -> SerenityResult<()> {
command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| message.content(msg).ephemeral(ephemeral))
})
.await
}
fn check_msg(result: SerenityResult<()>) {
if let Err(why) = result {
error!("Error sending message: {:?}", why);
}
}
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
let data = ctx.data.read().await;
@@ -41,41 +20,53 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
let session = match session_manager.get_session(command.guild_id.unwrap()).await {
Some(session) => session,
None => {
check_msg(
respond_message(
&ctx,
&command,
"I'm currently not connected to any voice channel",
true,
)
.await,
);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Cannot disconnect bot")
.icon_url("https://tabler-icons.io/static/tabler-icons/icons/ban.svg")
.description("I'm currently not connected to any voice channel")
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
};
if session.get_owner() != command.user.id {
// This message was generated by AI, and I love it.
check_msg(respond_message(&ctx, &command, "You are not the one who summoned me", true).await);
return;
};
if let Err(why) = session.disconnect().await {
error!("Error disconnecting from voice channel: {:?}", why);
check_msg(
if let Some(owner) = session.get_owner().await {
if owner != command.user.id {
// This message was generated by AI, and I love it.
respond_message(
&ctx,
&command,
"An error occurred while disconnecting from the voice channel",
EmbedBuilder::new()
.description("You are not the one who summoned me")
.status(Status::Error)
.build(),
true,
)
.await,
);
return;
.await;
return;
};
}
check_msg(respond_message(&ctx, &command, "Successfully left the voice channel", false).await);
session.disconnect().await;
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.description("I have left the voice channel, goodbye for now")
.status(Status::Info)
.build(),
false,
)
.await;
})
}
+1
View File
@@ -1,2 +1,3 @@
pub mod join;
pub mod leave;
pub mod playing;
+170
View File
@@ -0,0 +1,170 @@
use librespot::core::spotify_id::SpotifyAudioType;
use log::error;
use serenity::{
builder::CreateApplicationCommand,
model::prelude::interaction::{
application_command::ApplicationCommandInteraction, InteractionResponseType,
},
prelude::Context,
};
use crate::{
bot::commands::{respond_message, CommandOutput},
session::manager::SessionManager,
utils::{embed::{EmbedBuilder, Status}, self},
};
pub const NAME: &str = "playing";
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
Box::pin(async move {
let not_playing = async {
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("Cannot get track info")
.icon_url("https://tabler-icons.io/static/tabler-icons/icons/ban.svg")
.description("I'm currently not playing any music in this server")
.status(Status::Error)
.build(),
true,
)
.await;
};
let data = ctx.data.read().await;
let session_manager = data.get::<SessionManager>().unwrap().clone();
let session = match session_manager.get_session(command.guild_id.unwrap()).await {
Some(session) => session,
None => {
not_playing.await;
return;
}
};
let owner = match session.get_owner().await {
Some(owner) => owner,
None => {
not_playing.await;
return;
}
};
// Get Playback Info from session
let pbi = match session.get_playback_info().await {
Some(pbi) => pbi,
None => {
not_playing.await;
return;
}
};
let spotify_id = match pbi.spotify_id {
Some(spotify_id) => spotify_id,
None => {
not_playing.await;
return;
}
};
// Get audio type
let audio_type = if spotify_id.audio_type == SpotifyAudioType::Track {
"track"
} else {
"episode"
};
// Create title
let title = format!("{} - {}", pbi.get_artists().unwrap(), pbi.get_name().unwrap());
// Create description
let mut description = String::new();
let position = pbi.get_position();
let spot = position * 20 / pbi.duration_ms;
description.push_str(if pbi.is_playing { "▶️ " } else { "⏸️ " });
for i in 0..20 {
if i == spot {
description.push_str("🔵");
} else {
description.push_str("▬");
}
}
description.push_str("\n:alarm_clock: ");
description.push_str(&format!("{} / {}", utils::time_to_str(position / 1000), utils::time_to_str(pbi.duration_ms / 1000)));
// Get owner of session
let owner = match ctx.cache.user(owner) {
Some(user) => user,
None => {
// This shouldn't happen
// TODO: This can happen, idk when
error!("Could not find user with id {}", owner);
respond_message(
&ctx,
&command,
EmbedBuilder::new()
.title("[INTERNAL ERROR] Cannot get track info")
.description(format!("Could not find user with id {}\nThis is an issue with the bot!", owner))
.status(Status::Error)
.build(),
true,
)
.await;
return;
}
};
// Get the thumbnail image
let thumbnail = pbi.get_thumbnail_url().unwrap();
if let Err(why) = command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| {
message
.embed(|embed|
embed
.author(|author|
author
.name("Currently Playing")
.icon_url("https://www.freepnglogos.com/uploads/spotify-logo-png/file-spotify-logo-png-4.png")
)
.title(title)
.url(format!("https://open.spotify.com/{}/{}", audio_type, spotify_id.to_base62().unwrap()))
.description(description)
.footer(|footer|
footer
.text(&owner.name)
.icon_url(owner.face())
)
.thumbnail(&thumbnail)
.color(Status::Success as u64)
)
})
})
.await
{
error!("Error sending message: {:?}", why);
}
})
}
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command
.name(NAME)
.description("Display which song is currently being played")
}