use interactions instead of chat commands
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -1,43 +1,84 @@
|
|||||||
use std::{env, error::Error, num::NonZeroU64, time::Duration};
|
use std::{env, error::Error, num::NonZeroU64, time::Duration};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tracing::info;
|
use tracing::{debug, info};
|
||||||
use twilight_model::{channel::Message, id::Id};
|
use twilight_model::{
|
||||||
|
application::interaction::Interaction,
|
||||||
|
channel::message::MessageFlags,
|
||||||
|
http::interaction::{InteractionResponse, InteractionResponseType},
|
||||||
|
id::Id,
|
||||||
|
};
|
||||||
|
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
|
||||||
pub(crate) async fn delete(
|
pub(crate) async fn delete(
|
||||||
msg: Message,
|
interaction: Interaction,
|
||||||
state: State,
|
state: State,
|
||||||
|
count: i64,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
|
debug!(
|
||||||
|
"delete command in guild {:?} in channel {:?} by {:?}",
|
||||||
|
interaction.guild_id,
|
||||||
|
interaction.channel,
|
||||||
|
interaction.author(),
|
||||||
|
);
|
||||||
|
|
||||||
let admin = env::var("ADMIN")?.parse::<u64>()?;
|
let admin = env::var("ADMIN")?.parse::<u64>()?;
|
||||||
if msg.author.id != Id::from(NonZeroU64::new(admin).unwrap()) {
|
if interaction.author_id() != Some(Id::from(NonZeroU64::new(admin).unwrap())) {
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content("You do not have permissions to delete messages.")
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
|
state
|
||||||
|
.http
|
||||||
|
.interaction(interaction.application_id)
|
||||||
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
|
.await?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let n = msg
|
let Some(channel) = interaction.channel else {
|
||||||
.content
|
|
||||||
.split(' ')
|
|
||||||
.last()
|
|
||||||
.unwrap()
|
|
||||||
.parse::<u16>()
|
|
||||||
.unwrap_or(1);
|
|
||||||
if n > 100 {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
};
|
||||||
|
let Some(message_id) = channel.last_message_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let count = count.max(1).min(100) as u16;
|
||||||
|
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content(format!("Deleting {count} messages."))
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
|
state
|
||||||
|
.http
|
||||||
|
.interaction(interaction.application_id)
|
||||||
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let messages = state
|
let messages = state
|
||||||
.http
|
.http
|
||||||
.channel_messages(msg.channel_id)
|
.channel_messages(channel.id)
|
||||||
.before(msg.id)
|
.before(message_id.cast())
|
||||||
.limit(n)?
|
.limit(count)?
|
||||||
.await?
|
.await?
|
||||||
.model()
|
.model()
|
||||||
.await?;
|
.await?;
|
||||||
state.http.delete_message(msg.channel_id, msg.id).await?;
|
|
||||||
for message in messages {
|
for message in messages {
|
||||||
info!("Delete message: {:?}: {:?}", message.author.name, message);
|
debug!("Delete message: {:?}: {:?}", message.author.name, message);
|
||||||
state
|
state.http.delete_message(channel.id, message.id).await?;
|
||||||
.http
|
|
||||||
.delete_message(msg.channel_id, message.id)
|
|
||||||
.await?;
|
|
||||||
sleep(Duration::from_secs(5)).await;
|
sleep(Duration::from_secs(5)).await;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,34 +1,81 @@
|
|||||||
use std::{error::Error, num::NonZeroU64};
|
|
||||||
use twilight_model::channel::Message;
|
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
use std::error::Error;
|
||||||
|
use tracing::debug;
|
||||||
|
use twilight_model::{
|
||||||
|
application::interaction::Interaction,
|
||||||
|
channel::message::MessageFlags,
|
||||||
|
http::interaction::{InteractionResponse, InteractionResponseType},
|
||||||
|
id::{
|
||||||
|
marker::{GuildMarker, UserMarker},
|
||||||
|
Id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||||
|
|
||||||
pub(crate) async fn join(
|
pub(crate) async fn join_channel(
|
||||||
msg: Message,
|
|
||||||
state: State,
|
state: State,
|
||||||
|
guild_id: Id<GuildMarker>,
|
||||||
|
user_id: Id<UserMarker>,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
let guild_id = msg.guild_id.ok_or("No guild id attached to the message.")?;
|
debug!("join user {:?} in guild {:?}", user_id, guild_id);
|
||||||
let user_id = msg.author.id;
|
|
||||||
let channel_id = state
|
let channel_id = state
|
||||||
.cache
|
.cache
|
||||||
.voice_state(user_id, guild_id)
|
.voice_state(user_id, guild_id)
|
||||||
.ok_or("Cannot get voice state for user")?
|
.ok_or("Cannot get voice state for user")?
|
||||||
.channel_id();
|
.channel_id();
|
||||||
let channel_id =
|
|
||||||
NonZeroU64::new(channel_id.into()).ok_or("Joined voice channel must have nonzero ID.")?;
|
|
||||||
|
|
||||||
// join the voice channel
|
// join the voice channel
|
||||||
state
|
state
|
||||||
.songbird
|
.songbird
|
||||||
.join(guild_id, channel_id)
|
.join(guild_id.cast(), channel_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("Could not join voice channel: {:?}", e))?;
|
.map_err(|e| format!("Could not join voice channel: {:?}", e))?;
|
||||||
|
|
||||||
// signal that we are not listening
|
// signal that we are not listening
|
||||||
if let Some(call_lock) = state.songbird.get(guild_id) {
|
if let Some(call_lock) = state.songbird.get(guild_id.cast()) {
|
||||||
let mut call = call_lock.lock().await;
|
let mut call = call_lock.lock().await;
|
||||||
call.deafen(true).await?;
|
call.deafen(true).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn join(
|
||||||
|
interaction: Interaction,
|
||||||
|
state: State,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
|
debug!(
|
||||||
|
"join command in guild {:?} in channel {:?} by {:?}",
|
||||||
|
interaction.guild_id,
|
||||||
|
interaction.channel,
|
||||||
|
interaction.author(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let Some(guild_id) = interaction.guild_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let Some(author_id) = interaction.author_id() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
join_channel(state.clone(), guild_id, author_id).await?;
|
||||||
|
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content("Bin da Brudi!")
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
|
state
|
||||||
|
.http
|
||||||
|
.interaction(interaction.application_id)
|
||||||
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use twilight_model::channel::Message;
|
use twilight_model::application::interaction::Interaction;
|
||||||
|
|
||||||
pub(crate) async fn leave(
|
pub(crate) async fn leave(
|
||||||
msg: Message,
|
interaction: Interaction,
|
||||||
state: State,
|
state: State,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"leave command in channel {} by {}",
|
"leave command n guild {:?} in channel {:?} by {:?}",
|
||||||
msg.channel_id,
|
interaction.guild_id,
|
||||||
msg.author.name
|
interaction.channel,
|
||||||
|
interaction.author(),
|
||||||
);
|
);
|
||||||
let guild_id = msg.guild_id.unwrap();
|
|
||||||
|
let Some(guild_id) = interaction.guild_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
state.songbird.leave(guild_id).await?;
|
state.songbird.leave(guild_id).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ mod delete;
|
|||||||
pub(crate) use delete::delete;
|
pub(crate) use delete::delete;
|
||||||
|
|
||||||
use twilight_model::application::command::CommandType;
|
use twilight_model::application::command::CommandType;
|
||||||
use twilight_util::builder::command::{CommandBuilder, StringBuilder};
|
use twilight_util::builder::command::{CommandBuilder, IntegerBuilder, StringBuilder};
|
||||||
|
|
||||||
pub(crate) fn get_chat_commands() -> Vec<twilight_model::application::command::Command> {
|
pub(crate) fn get_chat_commands() -> Vec<twilight_model::application::command::Command> {
|
||||||
vec![
|
vec![
|
||||||
@@ -36,5 +36,8 @@ pub(crate) fn get_chat_commands() -> Vec<twilight_model::application::command::C
|
|||||||
CommandBuilder::new("queue", "Print track queue", CommandType::ChatInput).build(),
|
CommandBuilder::new("queue", "Print track queue", CommandType::ChatInput).build(),
|
||||||
CommandBuilder::new("resume", "Resume playing", CommandType::ChatInput).build(),
|
CommandBuilder::new("resume", "Resume playing", CommandType::ChatInput).build(),
|
||||||
CommandBuilder::new("stop", "Stop playing", CommandType::ChatInput).build(),
|
CommandBuilder::new("stop", "Stop playing", CommandType::ChatInput).build(),
|
||||||
|
CommandBuilder::new("delete", "Delete messages in chat", CommandType::ChatInput)
|
||||||
|
.option(IntegerBuilder::new("count", "How many messages to delete").required(true))
|
||||||
|
.build(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,46 @@
|
|||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use twilight_model::channel::Message;
|
use twilight_model::{
|
||||||
|
application::interaction::Interaction,
|
||||||
|
channel::message::MessageFlags,
|
||||||
|
http::interaction::{InteractionResponse, InteractionResponseType},
|
||||||
|
};
|
||||||
|
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||||
|
|
||||||
pub(crate) async fn pause(
|
pub(crate) async fn pause(
|
||||||
msg: Message,
|
interaction: Interaction,
|
||||||
state: State,
|
state: State,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"pause command in channel {} by {}",
|
"pause command in guild {:?} in channel {:?} by {:?}",
|
||||||
msg.channel_id,
|
interaction.guild_id,
|
||||||
msg.author.name
|
interaction.channel,
|
||||||
|
interaction.author(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
let Some(guild_id) = interaction.guild_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(call_lock) = state.songbird.get(guild_id) {
|
if let Some(call_lock) = state.songbird.get(guild_id) {
|
||||||
let call = call_lock.lock().await;
|
let call = call_lock.lock().await;
|
||||||
call.queue().pause()?;
|
call.queue().pause()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content("Paused the track")
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
.http
|
.http
|
||||||
.create_message(msg.channel_id)
|
.interaction(interaction.application_id)
|
||||||
.content("Paused the track")?
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::commands::join;
|
use crate::commands::join::join_channel;
|
||||||
use crate::metadata::{Metadata, MetadataMap};
|
use crate::metadata::{Metadata, MetadataMap};
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -7,23 +7,31 @@ use std::io::{BufRead, BufReader};
|
|||||||
use std::{error::Error, ops::Sub, time::Duration};
|
use std::{error::Error, ops::Sub, time::Duration};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use twilight_model::channel::Message;
|
use twilight_model::application::interaction::Interaction;
|
||||||
|
use twilight_model::channel::message::MessageFlags;
|
||||||
|
use twilight_model::http::interaction::{InteractionResponse, InteractionResponseType};
|
||||||
|
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) async fn play(
|
pub(crate) async fn play(
|
||||||
msg: Message,
|
interaction: Interaction,
|
||||||
state: State,
|
state: State,
|
||||||
query: String,
|
query: String,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
tracing::debug!(
|
debug!(
|
||||||
"play command in channel {} by {}",
|
"play command in channel {:?} by {:?}",
|
||||||
msg.channel_id,
|
interaction.channel,
|
||||||
msg.author.name
|
interaction.author(),
|
||||||
);
|
);
|
||||||
|
|
||||||
join(msg.clone(), state.clone()).await?;
|
let Some(user_id) = interaction.author_id() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let Some(guild_id) = interaction.guild_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
join_channel(state.clone(), guild_id, user_id).await?;
|
||||||
|
|
||||||
// handle keyword queries
|
// handle keyword queries
|
||||||
let query = if Url::parse(&query).is_err() {
|
let query = if Url::parse(&query).is_err() {
|
||||||
@@ -32,6 +40,24 @@ pub(crate) async fn play(
|
|||||||
query
|
query
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("query: {:?}", query);
|
||||||
|
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content("Adding tracks to the queue ...")
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
|
state
|
||||||
|
.http
|
||||||
|
.interaction(interaction.application_id)
|
||||||
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// handle playlist links
|
// handle playlist links
|
||||||
let urls = if query.contains("list=") {
|
let urls = if query.contains("list=") {
|
||||||
get_playlist_urls(query).await?
|
get_playlist_urls(query).await?
|
||||||
@@ -62,12 +88,6 @@ pub(crate) async fn play(
|
|||||||
duration: metadata.duration,
|
duration: metadata.duration,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
state
|
|
||||||
.http
|
|
||||||
.create_message(msg.channel_id)
|
|
||||||
.content("Cannot find any results")?
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
|
use twilight_model::http::interaction::InteractionResponseType;
|
||||||
|
use twilight_model::{
|
||||||
|
application::interaction::Interaction, channel::message::MessageFlags,
|
||||||
|
http::interaction::InteractionResponse,
|
||||||
|
};
|
||||||
|
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||||
|
|
||||||
use crate::{metadata::MetadataMap, state::State};
|
use crate::{metadata::MetadataMap, state::State};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use twilight_model::channel::Message;
|
|
||||||
|
|
||||||
pub(crate) async fn queue(
|
pub(crate) async fn queue(
|
||||||
msg: Message,
|
interaction: Interaction,
|
||||||
state: State,
|
state: State,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"queue command in channel {} by {}",
|
"queue command in guild {:?} in channel {:?} by {:?}",
|
||||||
msg.channel_id,
|
interaction.guild_id,
|
||||||
msg.author.name
|
interaction.channel,
|
||||||
|
interaction.author(),
|
||||||
);
|
);
|
||||||
let guild_id = msg.guild_id.unwrap();
|
let Some(guild_id) = interaction.guild_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
if let Some(call_lock) = state.songbird.get(guild_id) {
|
if let Some(call_lock) = state.songbird.get(guild_id) {
|
||||||
let call = call_lock.lock().await;
|
let call = call_lock.lock().await;
|
||||||
let queue = call.queue().current_queue();
|
let queue = call.queue().current_queue();
|
||||||
@@ -48,10 +56,21 @@ pub(crate) async fn queue(
|
|||||||
}
|
}
|
||||||
message.push_str("`\n");
|
message.push_str("`\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content(&message)
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
.http
|
.http
|
||||||
.create_message(msg.channel_id)
|
.interaction(interaction.application_id)
|
||||||
.content(&message)?
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,28 +1,45 @@
|
|||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use twilight_model::channel::Message;
|
use twilight_model::{
|
||||||
|
application::interaction::Interaction,
|
||||||
|
channel::message::MessageFlags,
|
||||||
|
http::interaction::{InteractionResponse, InteractionResponseType},
|
||||||
|
};
|
||||||
|
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||||
|
|
||||||
pub(crate) async fn resume(
|
pub(crate) async fn resume(
|
||||||
msg: Message,
|
interaction: Interaction,
|
||||||
state: State,
|
state: State,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"resume command in channel {} by {}",
|
"resume command in guild {:?} in channel {:?} by {:?}",
|
||||||
msg.channel_id,
|
interaction.guild_id,
|
||||||
msg.author.name
|
interaction.channel,
|
||||||
|
interaction.author(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
let Some(guild_id) = interaction.guild_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
if let Some(call_lock) = state.songbird.get(guild_id) {
|
if let Some(call_lock) = state.songbird.get(guild_id) {
|
||||||
let call = call_lock.lock().await;
|
let call = call_lock.lock().await;
|
||||||
call.queue().resume()?;
|
call.queue().resume()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content("Resumed the track")
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
.http
|
.http
|
||||||
.create_message(msg.channel_id)
|
.interaction(interaction.application_id)
|
||||||
.content("Resumed the track")?
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,28 +1,47 @@
|
|||||||
|
use twilight_model::{
|
||||||
|
application::interaction::Interaction,
|
||||||
|
channel::message::MessageFlags,
|
||||||
|
http::interaction::{InteractionResponse, InteractionResponseType},
|
||||||
|
};
|
||||||
|
use twilight_util::builder::InteractionResponseDataBuilder;
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use twilight_model::channel::Message;
|
|
||||||
|
|
||||||
pub(crate) async fn stop(
|
pub(crate) async fn stop(
|
||||||
msg: Message,
|
interaction: Interaction,
|
||||||
state: State,
|
state: State,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"stop command in channel {} by {}",
|
"stop command in guild {:?} in channel {:?} by {:?}",
|
||||||
msg.channel_id,
|
interaction.guild_id,
|
||||||
msg.author.name
|
interaction.channel,
|
||||||
|
interaction.author(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
let Some(guild_id) = interaction.guild_id else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(call_lock) = state.songbird.get(guild_id) {
|
if let Some(call_lock) = state.songbird.get(guild_id) {
|
||||||
let call = call_lock.lock().await;
|
let call = call_lock.lock().await;
|
||||||
call.queue().stop();
|
call.queue().stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let interaction_response_data = InteractionResponseDataBuilder::new()
|
||||||
|
.content("Stopped the track and cleared the queue")
|
||||||
|
.flags(MessageFlags::EPHEMERAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = InteractionResponse {
|
||||||
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
||||||
|
data: Some(interaction_response_data),
|
||||||
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
.http
|
.http
|
||||||
.create_message(msg.channel_id)
|
.interaction(interaction.application_id)
|
||||||
.content("Stopped the track and cleared the queue")?
|
.create_response(interaction.id, &interaction.token, &response)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
137
src/handler.rs
137
src/handler.rs
@@ -4,47 +4,25 @@ use crate::state::State;
|
|||||||
use futures::Future;
|
use futures::Future;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tracing::debug;
|
||||||
|
use twilight_model::application::interaction::application_command::CommandOptionValue;
|
||||||
|
use twilight_model::application::interaction::{Interaction, InteractionData};
|
||||||
|
|
||||||
use twilight_gateway::Event;
|
use twilight_gateway::Event;
|
||||||
use twilight_model::channel::Message;
|
|
||||||
|
|
||||||
enum ChatCommand {
|
#[derive(Debug)]
|
||||||
Play(Message, String),
|
enum InteractionCommand {
|
||||||
Stop(Message),
|
Play(Interaction, String),
|
||||||
Pause(Message),
|
Stop(Interaction),
|
||||||
Resume(Message),
|
Pause(Interaction),
|
||||||
Leave(Message),
|
Resume(Interaction),
|
||||||
Join(Message),
|
Leave(Interaction),
|
||||||
Queue(Message),
|
Join(Interaction),
|
||||||
Delete(Message),
|
Queue(Interaction),
|
||||||
|
Delete(Interaction, i64),
|
||||||
NotImplemented,
|
NotImplemented,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command(event: Event) -> Option<ChatCommand> {
|
|
||||||
match event {
|
|
||||||
Event::MessageCreate(msg_create) => {
|
|
||||||
if msg_create.guild_id.is_none() || !msg_create.content.starts_with('!') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let split: Vec<&str> = msg_create.content.splitn(2, ' ').collect();
|
|
||||||
match split.as_slice() {
|
|
||||||
["!play", query] => {
|
|
||||||
Some(ChatCommand::Play(msg_create.0.clone(), query.to_string()))
|
|
||||||
}
|
|
||||||
["!stop"] | ["!stop", _] => Some(ChatCommand::Stop(msg_create.0)),
|
|
||||||
["!pause"] | ["!pause", _] => Some(ChatCommand::Pause(msg_create.0)),
|
|
||||||
["!resume"] | ["!resume", _] => Some(ChatCommand::Resume(msg_create.0)),
|
|
||||||
["!leave"] | ["!leave", _] => Some(ChatCommand::Leave(msg_create.0)),
|
|
||||||
["!join"] | ["!join", _] => Some(ChatCommand::Join(msg_create.0)),
|
|
||||||
["!queue"] | ["!queue", _] => Some(ChatCommand::Queue(msg_create.0)),
|
|
||||||
["!delete"] | ["!delete", _] => Some(ChatCommand::Delete(msg_create.0)),
|
|
||||||
_ => Some(ChatCommand::NotImplemented),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn(
|
fn spawn(
|
||||||
fut: impl Future<Output = Result<(), Box<dyn Error + Send + Sync + 'static>>> + Send + 'static,
|
fut: impl Future<Output = Result<(), Box<dyn Error + Send + Sync + 'static>>> + Send + 'static,
|
||||||
) {
|
) {
|
||||||
@@ -63,17 +41,84 @@ impl Handler {
|
|||||||
pub(crate) fn new(state: State) -> Self {
|
pub(crate) fn new(state: State) -> Self {
|
||||||
Self { state }
|
Self { state }
|
||||||
}
|
}
|
||||||
pub(crate) async fn act(&mut self, event: Event) {
|
pub(crate) async fn act(&self, event: Event) {
|
||||||
match parse_command(event) {
|
let interaction_command = match event {
|
||||||
Some(ChatCommand::Play(msg, query)) => spawn(play(msg, Arc::clone(&self.state), query)),
|
Event::InteractionCreate(interaction) => {
|
||||||
Some(ChatCommand::Stop(msg)) => spawn(stop(msg, Arc::clone(&self.state))),
|
debug!("interaction: {:?}", &interaction);
|
||||||
Some(ChatCommand::Pause(msg)) => spawn(pause(msg, Arc::clone(&self.state))),
|
match &interaction.data {
|
||||||
Some(ChatCommand::Resume(msg)) => spawn(resume(msg, Arc::clone(&self.state))),
|
Some(InteractionData::ApplicationCommand(command)) => {
|
||||||
Some(ChatCommand::Leave(msg)) => spawn(leave(msg, Arc::clone(&self.state))),
|
debug!("command: {:?}", command);
|
||||||
Some(ChatCommand::Join(msg)) => spawn(join(msg, Arc::clone(&self.state))),
|
match command.name.as_str() {
|
||||||
Some(ChatCommand::Queue(msg)) => spawn(queue(msg, Arc::clone(&self.state))),
|
"play" => {
|
||||||
Some(ChatCommand::Delete(msg)) => spawn(delete(msg, Arc::clone(&self.state))),
|
if let Some(query_option) =
|
||||||
_ => {}
|
command.options.iter().find(|opt| opt.name == "query")
|
||||||
|
{
|
||||||
|
if let CommandOptionValue::String(query) = &query_option.value {
|
||||||
|
InteractionCommand::Play(
|
||||||
|
interaction.0.clone(),
|
||||||
|
query.clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
InteractionCommand::NotImplemented
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
InteractionCommand::NotImplemented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"stop" => InteractionCommand::Stop(interaction.0.clone()),
|
||||||
|
"pause" => InteractionCommand::Pause(interaction.0.clone()),
|
||||||
|
"resume" => InteractionCommand::Resume(interaction.0.clone()),
|
||||||
|
"leave" => InteractionCommand::Leave(interaction.0.clone()),
|
||||||
|
"join" => InteractionCommand::Join(interaction.0.clone()),
|
||||||
|
"queue" => InteractionCommand::Queue(interaction.0.clone()),
|
||||||
|
"delete" => {
|
||||||
|
if let Some(count_option) =
|
||||||
|
command.options.iter().find(|opt| opt.name == "count")
|
||||||
|
{
|
||||||
|
if let CommandOptionValue::Integer(count) = count_option.value {
|
||||||
|
InteractionCommand::Delete(interaction.0.clone(), count)
|
||||||
|
} else {
|
||||||
|
InteractionCommand::NotImplemented
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
InteractionCommand::NotImplemented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => InteractionCommand::NotImplemented,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => InteractionCommand::NotImplemented,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => InteractionCommand::NotImplemented,
|
||||||
|
};
|
||||||
|
debug!("{:?}", interaction_command);
|
||||||
|
match interaction_command {
|
||||||
|
InteractionCommand::Play(interaction, query) => {
|
||||||
|
spawn(play(interaction, Arc::clone(&self.state), query))
|
||||||
|
}
|
||||||
|
InteractionCommand::Stop(interaction) => {
|
||||||
|
spawn(stop(interaction, Arc::clone(&self.state)))
|
||||||
|
}
|
||||||
|
InteractionCommand::Pause(interaction) => {
|
||||||
|
spawn(pause(interaction, Arc::clone(&self.state)))
|
||||||
|
}
|
||||||
|
InteractionCommand::Resume(interaction) => {
|
||||||
|
spawn(resume(interaction, Arc::clone(&self.state)))
|
||||||
|
}
|
||||||
|
InteractionCommand::Leave(interaction) => {
|
||||||
|
spawn(leave(interaction, Arc::clone(&self.state)))
|
||||||
|
}
|
||||||
|
InteractionCommand::Join(interaction) => {
|
||||||
|
spawn(join(interaction, Arc::clone(&self.state)))
|
||||||
|
}
|
||||||
|
InteractionCommand::Queue(interaction) => {
|
||||||
|
spawn(queue(interaction, Arc::clone(&self.state)))
|
||||||
|
}
|
||||||
|
InteractionCommand::Delete(interaction, count) => {
|
||||||
|
spawn(delete(interaction, Arc::clone(&self.state), count))
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
|||||||
|
|
||||||
info!("Ready to receive events");
|
info!("Ready to receive events");
|
||||||
|
|
||||||
let mut handler = Handler::new(Arc::clone(&state));
|
let handler = Handler::new(Arc::clone(&state));
|
||||||
let mut stop_rx = signal_handler();
|
let mut stop_rx = signal_handler();
|
||||||
let mut stream = ShardEventStream::new(shards.iter_mut());
|
let mut stream = ShardEventStream::new(shards.iter_mut());
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
Reference in New Issue
Block a user