Uhm so yeah here's some c o d e
This commit is contained in:
@@ -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")
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
pub mod help;
|
||||
pub mod link;
|
||||
pub mod rename;
|
||||
pub mod unlink;
|
||||
pub mod version;
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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,2 +1,3 @@
|
||||
pub mod join;
|
||||
pub mod leave;
|
||||
pub mod playing;
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
Reference in New Issue
Block a user