Performance upgrades, fixes for 2 issues

main
DaXcess 2022-11-05 15:39:19 +01:00
parent dccf8c2057
commit f3dff49c06
10 changed files with 177 additions and 87 deletions

View File

@ -7,12 +7,6 @@ edition = "2021"
name = "spoticord"
path = "src/main.rs"
[profile.release]
lto = true
codegen-units = 1
strip = true
opt-level = "z"
[dependencies]
chrono = "0.4.22"
dotenv = "0.15.0"

View File

@ -44,13 +44,32 @@ pub async fn respond_message(
}
}
pub async fn defer_message(
ctx: &Context,
command: &ApplicationCommandInteraction,
ephemeral: bool,
) {
if let Err(why) = command
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::DeferredChannelMessageWithSource)
.interaction_response_data(|message| message.ephemeral(ephemeral))
})
.await
{
error!("Error deferring message: {:?}", why);
}
}
pub type CommandOutput = Pin<Box<dyn Future<Output = ()> + Send>>;
pub type CommandExecutor = fn(Context, ApplicationCommandInteraction) -> CommandOutput;
#[derive(Clone)]
pub struct CommandManager {
commands: HashMap<String, CommandInfo>,
}
#[derive(Clone)]
pub struct CommandInfo {
pub name: String,
pub executor: CommandExecutor,

View File

@ -5,7 +5,7 @@ use serenity::{
};
use crate::{
bot::commands::{respond_message, CommandOutput},
bot::commands::{defer_message, respond_message, CommandOutput},
session::manager::{SessionCreateError, SessionManager},
utils::embed::{EmbedBuilder, Status},
};
@ -46,6 +46,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
// Check if another session is already active 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 {
@ -91,6 +92,8 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
return;
}
defer_message(&ctx, &command, true).await;
if let Some(session) = &session_opt {
if let Err(why) = session.update_owner(&ctx, command.user.id).await {
// Need to link first

View File

@ -23,7 +23,10 @@ impl EventHandler for Handler {
debug!("Ready received, logged in as {}", ready.user.name);
// Set this to true only when a command is removed/updated/created
if false {
command_manager.register_commands(&ctx).await;
}
ctx.set_activity(Activity::listening(MOTD)).await;
@ -32,10 +35,15 @@ impl EventHandler for Handler {
// INTERACTION_CREATE event, emitted when the bot receives an interaction (slash command, button, etc.)
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
trace!("interaction_create START");
if let Interaction::ApplicationCommand(command) = interaction {
// Commands must only be executed inside of guilds
if command.guild_id.is_none() {
command
let guild_id = match command.guild_id {
Some(guild_id) => guild_id,
None => {
if let Err(why) = command
.create_interaction_response(&ctx.http, |response| {
response
.kind(serenity::model::prelude::interaction::InteractionResponseType::ChannelMessageWithSource)
@ -43,17 +51,20 @@ impl EventHandler for Handler {
message.content("You can only execute commands inside of a server")
})
})
.await
.unwrap();
.await {
error!("Failed to send run-in-guild-only error message: {}", why);
}
trace!("interaction_create END2");
return;
}
};
trace!(
"Received command interaction: command={} user={} guild={}",
command.data.name,
command.user.id,
command.guild_id.unwrap()
guild_id
);
let data = ctx.data.read().await;
@ -61,5 +72,7 @@ impl EventHandler for Handler {
command_manager.execute_command(&ctx, command).await;
}
trace!("interaction_create END");
}
}

View File

@ -1,3 +1,3 @@
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const MOTD: &str = "OPEN BETA (v2)";
pub const MOTD: &str = "UNSTABLE BETA (v2)";
// pub const MOTD: &str = "some good 'ol music";

View File

@ -1,6 +1,6 @@
use std::sync::{Arc, Mutex};
use ipc_channel::ipc::{self, IpcError, IpcOneShotServer, IpcReceiver, IpcSender};
use ipc_channel::ipc::{self, IpcError, IpcOneShotServer, IpcReceiver, IpcSender, TryRecvError};
use self::packet::IpcPacket;
@ -66,4 +66,8 @@ impl Client {
pub fn recv(&self) -> Result<IpcPacket, IpcError> {
self.rx.lock().unwrap().recv()
}
pub fn try_recv(&self) -> Result<IpcPacket, TryRecvError> {
self.rx.lock().unwrap().try_recv()
}
}

View File

@ -23,7 +23,7 @@ mod session;
mod stats;
mod utils;
#[tokio::main(flavor = "multi_thread")]
#[tokio::main]
async fn main() {
if std::env::var("RUST_LOG").is_err() {
#[cfg(debug_assertions)]
@ -39,6 +39,14 @@ async fn main() {
env_logger::init();
let orig_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
error!("Panic: {}", panic_info);
orig_hook(panic_info);
std::process::exit(1);
}));
let args: Vec<String> = env::args().collect();
if args.len() > 2 {
@ -95,6 +103,7 @@ async fn main() {
let shard_manager = client.shard_manager.clone();
let cache = client.cache_and_http.cache.clone();
#[cfg(unix)]
let mut sigterm = tokio::signal::unix::signal(SignalKind::terminate()).unwrap();
// Background tasks

View File

@ -49,11 +49,12 @@ impl SessionManager {
channel_id: ChannelId,
owner_id: UserId,
) -> Result<(), SessionCreateError> {
// Create session first to make sure locks are kept for as little time as possible
let session = SpoticordSession::new(ctx, guild_id, channel_id, owner_id).await?;
let mut sessions = self.sessions.write().await;
let mut owner_map = self.owner_map.write().await;
let session = SpoticordSession::new(ctx, guild_id, channel_id, owner_id).await?;
sessions.insert(guild_id, Arc::new(session));
owner_map.insert(owner_id, guild_id);

View File

@ -4,7 +4,7 @@ use crate::{
ipc::{self, packet::IpcPacket, Client},
utils::{self, spotify},
};
use ipc_channel::ipc::IpcError;
use ipc_channel::ipc::{IpcError, TryRecvError};
use librespot::core::spotify_id::{SpotifyAudioType, SpotifyId};
use log::*;
use serenity::{
@ -21,6 +21,7 @@ use songbird::{
use std::{
process::{Command, Stdio},
sync::Arc,
time::Duration,
};
use tokio::sync::Mutex;
@ -262,12 +263,19 @@ impl SpoticordSession {
// Required for IpcPacket::TrackChange to work
tokio::task::yield_now().await;
let msg = match ipc_client.recv() {
let msg = match ipc_client.try_recv() {
Ok(msg) => msg,
Err(why) => {
if let TryRecvError::Empty = why {
// No message, wait a bit and try again
tokio::time::sleep(Duration::from_millis(25)).await;
continue;
} else if let TryRecvError::IpcError(why) = &why {
if let IpcError::Disconnected = why {
break;
}
}
error!("Failed to receive IPC message: {:?}", why);
break;
@ -407,8 +415,10 @@ impl SpoticordSession {
}
};
{
let mut owner = self.owner.write().await;
*owner = Some(owner_id);
}
session_manager.set_owner(owner_id, self.guild_id).await;

View File

@ -46,9 +46,12 @@ pub async fn get_username(token: impl Into<String>) -> Result<String, String> {
let token = token.into();
let client = reqwest::Client::new();
let mut retries = 3;
loop {
let response = match client
.get("https://api.spotify.com/v1/me")
.bearer_auth(token)
.bearer_auth(&token)
.send()
.await
{
@ -59,6 +62,21 @@ pub async fn get_username(token: impl Into<String>) -> Result<String, String> {
}
};
if response.status().as_u16() >= 500 && retries > 0 {
retries -= 1;
continue;
}
if response.status() != 200 {
return Err(
format!(
"Failed to get track info: Invalid status code: {}",
response.status()
)
.into(),
);
}
let body: Value = match response.json().await {
Ok(body) => body,
Err(why) => {
@ -72,8 +90,9 @@ pub async fn get_username(token: impl Into<String>) -> Result<String, String> {
return Ok(username.clone());
}
error!("Missing 'id' field in body");
Err("Failed to parse body: Invalid body received".to_string())
error!("Missing 'id' field in body: {:#?}", body);
return Err("Failed to parse body: Invalid body received".to_string());
}
}
pub async fn get_track_info(
@ -83,15 +102,23 @@ pub async fn get_track_info(
let token = token.into();
let client = reqwest::Client::new();
let mut retries = 3;
loop {
let response = client
.get(format!(
"https://api.spotify.com/v1/tracks/{}",
track.to_base62()?
))
.bearer_auth(token)
.bearer_auth(&token)
.send()
.await?;
if response.status().as_u16() >= 500 && retries > 0 {
retries -= 1;
continue;
}
if response.status() != 200 {
return Err(
format!(
@ -102,7 +129,8 @@ pub async fn get_track_info(
);
}
Ok(response.json().await?)
return Ok(response.json().await?);
}
}
pub async fn get_episode_info(
@ -112,15 +140,23 @@ pub async fn get_episode_info(
let token = token.into();
let client = reqwest::Client::new();
let mut retries = 3;
loop {
let response = client
.get(format!(
"https://api.spotify.com/v1/episodes/{}",
episode.to_base62()?
))
.bearer_auth(token)
.bearer_auth(&token)
.send()
.await?;
if response.status().as_u16() >= 500 && retries > 0 {
retries -= 1;
continue;
}
if response.status() != 200 {
return Err(
format!(
@ -131,5 +167,6 @@ pub async fn get_episode_info(
);
}
Ok(response.json().await?)
return Ok(response.json().await?);
}
}