Merge pull request #36 from SpoticordMusic/dev

PR: Update Spoticord to v2.2.1
main
Daniel 2024-08-22 10:25:21 +02:00 committed by GitHub
commit e383b4ab41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 274 additions and 94 deletions

View File

@ -18,15 +18,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Docker buildx
id: buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to GitHub's container registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}

View File

@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up Rust
uses: actions-rs/toolchain@v1

View File

@ -1,6 +1,17 @@
# Changelog
## 2.2.0 | August 8th 2024
## 2.2.1 | August 22nd 2024
- Added new option: `/playing` can now receive an updating behavior parameter
- Added album name to `/playing` embed
- Fixed a bug where uncached guilds would panic the bot
- Fixed small issue with embed styling
- Updated to Rust 1.80.1 (from 1.79.0)
- Updated `diesel` and addons to latest versions
- Removed `lazy_static` in favor of `LazyLock` (Rust 1.80.0+ feature)
- Bumped MSRV to 1.80.0 due to the introduction of `LazyLock`
## 2.2.0 | August 13th 2024
### Changes

View File

@ -86,4 +86,4 @@ cargo build [--release] --features stats
# MSRV
The current minimum supported rust version is `1.75.0` _(Checked with `cargo-msrv`)_.
The current minimum supported rust version is `1.80.0` _(Checked with `cargo-msrv`)_.

70
Cargo.lock generated
View File

@ -592,14 +592,12 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
[[package]]
name = "deadpool"
version = "0.9.5"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e"
checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed"
dependencies = [
"async-trait",
"deadpool-runtime",
"num_cpus",
"retain_mut",
"tokio",
]
@ -681,22 +679,23 @@ dependencies = [
[[package]]
name = "diesel"
version = "2.1.6"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2"
checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94"
dependencies = [
"bitflags 2.6.0",
"byteorder",
"chrono",
"diesel_derives",
"itoa",
"pq-sys",
]
[[package]]
name = "diesel-async"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be"
checksum = "fcb799bb6f8ca6a794462125d7b8983b0c86e6c93a33a9c55934a4a5de4409d3"
dependencies = [
"async-trait",
"deadpool",
@ -709,9 +708,9 @@ dependencies = [
[[package]]
name = "diesel_async_migrations"
version = "0.12.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a700d6b83a17973b94d3065970fd2b36f1036c3fe08adcbdce1c9beb8fb25553"
checksum = "377dd8e9d0fdab3dbb66236f8f71206b98e121991449f3b363a19d7948333160"
dependencies = [
"diesel",
"diesel-async",
@ -732,11 +731,12 @@ dependencies = [
[[package]]
name = "diesel_derives"
version = "2.1.4"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c"
checksum = "d6ff2be1e7312c858b2ef974f5c7089833ae57b5311b334b30923af58e5718d8"
dependencies = [
"diesel_table_macro_syntax",
"dsl_auto_type",
"proc-macro2",
"quote",
"syn 2.0.72",
@ -744,9 +744,9 @@ dependencies = [
[[package]]
name = "diesel_table_macro_syntax"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
dependencies = [
"syn 2.0.72",
]
@ -779,6 +779,20 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "dsl_auto_type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607"
dependencies = [
"darling 0.20.10",
"either",
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]]
name = "either"
version = "1.13.0"
@ -2515,6 +2529,15 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "pq-sys"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a24ff9e4cf6945c988f0db7005d87747bf72864965c3529d259ad155ac41d584"
dependencies = [
"vcpkg",
]
[[package]]
name = "primal-check"
version = "0.3.4"
@ -2904,12 +2927,6 @@ dependencies = [
"winreg 0.52.0",
]
[[package]]
name = "retain_mut"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0"
[[package]]
name = "ring"
version = "0.16.20"
@ -3721,7 +3738,7 @@ dependencies = [
[[package]]
name = "spoticord"
version = "2.2.0"
version = "2.2.1"
dependencies = [
"anyhow",
"dotenvy",
@ -3753,7 +3770,6 @@ dependencies = [
name = "spoticord_config"
version = "2.2.0"
dependencies = [
"lazy_static",
"rspotify",
"serenity",
]
@ -4217,9 +4233,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.39.2"
version = "1.39.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
dependencies = [
"backtrace",
"bytes",
@ -4792,6 +4808,12 @@ dependencies = [
"ryu",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vergen"
version = "8.3.2"

View File

@ -1,8 +1,8 @@
[package]
name = "spoticord"
version = "2.2.0"
version = "2.2.1"
edition = "2021"
rust-version = "1.75.0"
rust-version = "1.80.0"
[[bin]]
name = "spoticord"
@ -38,7 +38,7 @@ log = "0.4.22"
poise = "0.6.1"
serenity = "0.12.2"
songbird = { version = "0.4.3", features = ["simd-json"] }
tokio = { version = "1.39.2", features = ["full"] }
tokio = { version = "1.39.3", features = ["full"] }
[profile.release]
opt-level = 3

View File

@ -1,11 +1,29 @@
# This Dockerfile has been specifically crafted to be run on an AMD64 build host, where
# the build should compile for both amd64 and arm64 targets
#
# Building on any other platform, or building for only a single target will be significantly
# slower compared to a platform agnostic Dockerfile, or might not work at all
#
# This has been done to make this file be optimized for use within GitHub Actions,
# as using QEMU to compile takes way too long (multiple hours)
# Builder
FROM --platform=linux/amd64 rust:1.79.0-buster AS builder
FROM --platform=linux/amd64 rust:1.80.1-slim AS builder
WORKDIR /app
# Add extra build dependencies here
RUN apt-get update && apt-get install -yqq \
cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
RUN apt-get update && apt install -yqq \
cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu libpq-dev curl bzip2
# Manually compile an arm64 build of libpq
ENV PGVER=16.4
RUN curl -o postgresql.tar.bz2 https://ftp.postgresql.org/pub/source/v${PGVER}/postgresql-${PGVER}.tar.bz2 && \
tar xjf postgresql.tar.bz2 && \
cd postgresql-${PGVER} && \
./configure --host=aarch64-linux-gnu --enable-shared --disable-static --without-readline --without-zlib --without-icu && \
cd src/interfaces/libpq && \
make
COPY . .
@ -14,13 +32,14 @@ RUN rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu
# Add `--no-default-features` if you don't want stats collection
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release --target=x86_64-unknown-linux-gnu --target=aarch64-unknown-linux-gnu && \
cargo build --release --target=x86_64-unknown-linux-gnu && \
RUSTFLAGS="-L /app/postgresql-${PGVER}/src/interfaces/libpq -C linker=aarch64-linux-gnu-gcc" cargo build --release --target=aarch64-unknown-linux-gnu && \
# Copy the executables outside of /target as it'll get unmounted after this RUN command
cp /app/target/x86_64-unknown-linux-gnu/release/spoticord /app/x86_64 && \
cp /app/target/aarch64-unknown-linux-gnu/release/spoticord /app/aarch64
# Runtime
FROM debian:buster-slim
FROM debian:bookworm-slim
ARG TARGETPLATFORM
ENV TARGETPLATFORM=${TARGETPLATFORM}

View File

@ -8,5 +8,5 @@ edition = "2021"
[dependencies]
librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false }
songbird = { version = "0.4.3", features = ["simd-json"] }
tokio = { version = "1.39.2", features = ["sync"], default-features = false }
tokio = { version = "1.39.3", features = ["sync"], default-features = false }
zerocopy = "0.7.35"

View File

@ -4,7 +4,6 @@ version = "2.2.0"
edition = "2021"
[dependencies]
lazy_static = "1.5.0"
rspotify = { version = "0.13.2", default-features = false, features = [
"client-reqwest",
"reqwest-rustls-tls",

View File

@ -1,14 +1,17 @@
use lazy_static::lazy_static;
use std::sync::LazyLock;
lazy_static! {
pub static ref DISCORD_TOKEN: String =
std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN environment variable");
pub static ref DATABASE_URL: String =
std::env::var("DATABASE_URL").expect("missing DATABASE_URL environment variable");
pub static ref LINK_URL: String =
std::env::var("LINK_URL").expect("missing LINK_URL environment variable");
pub static ref SPOTIFY_CLIENT_ID: String =
std::env::var("SPOTIFY_CLIENT_ID").expect("missing SPOTIFY_CLIENT_ID environment variable");
pub static ref SPOTIFY_CLIENT_SECRET: String = std::env::var("SPOTIFY_CLIENT_SECRET")
.expect("missing SPOTIFY_CLIENT_SECRET environment variable");
}
pub static DISCORD_TOKEN: LazyLock<String> = LazyLock::new(|| {
std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN environment variable")
});
pub static DATABASE_URL: LazyLock<String> = LazyLock::new(|| {
std::env::var("DATABASE_URL").expect("missing DATABASE_URL environment variable")
});
pub static LINK_URL: LazyLock<String> =
LazyLock::new(|| std::env::var("LINK_URL").expect("missing LINK_URL environment variable"));
pub static SPOTIFY_CLIENT_ID: LazyLock<String> = LazyLock::new(|| {
std::env::var("SPOTIFY_CLIENT_ID").expect("missing SPOTIFY_CLIENT_ID environment variable")
});
pub static SPOTIFY_CLIENT_SECRET: LazyLock<String> = LazyLock::new(|| {
std::env::var("SPOTIFY_CLIENT_SECRET")
.expect("missing SPOTIFY_CLIENT_SECRET environment variable")
});

View File

@ -6,8 +6,8 @@ edition = "2021"
[dependencies]
spoticord_config = { path = "../spoticord_config" }
diesel = { version = "2.1.6", features = ["chrono"] }
diesel-async = { version = "0.4.1", features = ["deadpool", "postgres"] }
diesel = { version = "2.2.2", features = ["chrono"] }
diesel-async = { version = "0.5.0", features = ["deadpool", "postgres"] }
rspotify = { version = "0.13.2", default-features = false, features = [
"client-reqwest",
"reqwest-rustls-tls",
@ -15,4 +15,4 @@ rspotify = { version = "0.13.2", default-features = false, features = [
chrono = "0.4.38"
thiserror = "1.0.63"
rand = "0.8.5"
diesel_async_migrations = "0.12.0"
diesel_async_migrations = "0.13.0"

View File

@ -11,7 +11,7 @@ spoticord_utils = { path = "../spoticord_utils" }
librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false }
songbird = { version = "0.4.3", features = ["simd-json"] }
tokio = { version = "1.39.2", features = ["full"] }
tokio = { version = "1.39.3", features = ["full"] }
anyhow = "1.0.86"
log = "0.4.22"
symphonia = { version = "0.5.4", default-features = false, features = ["pcm"] }

View File

@ -65,6 +65,13 @@ impl PlaybackInfo {
}
}
pub fn album_name(&self) -> Option<String> {
match &self.audio_item.unique_fields {
UniqueFields::Episode { .. } => None,
UniqueFields::Track { album, .. } => Some(album.to_string()),
}
}
pub fn thumbnail(&self) -> String {
self.audio_item
.covers

View File

@ -11,7 +11,7 @@ spoticord_database = { path = "../spoticord_database" }
spoticord_player = { path = "../spoticord_player" }
spoticord_utils = { path = "../spoticord_utils" }
tokio = { version = "1.39.2", features = ["full"] }
tokio = { version = "1.39.3", features = ["full"] }
librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false }
serenity = "0.12.2"
songbird = { version = "0.4.3", features = ["simd-json"] }

View File

@ -31,7 +31,11 @@ pub enum SessionCommand {
GetPlayer(oneshot::Sender<PlayerHandle>),
GetActive(oneshot::Sender<bool>),
CreatePlaybackEmbed(SessionHandle, CommandInteraction),
CreatePlaybackEmbed(
SessionHandle,
CommandInteraction,
playback_embed::UpdateBehavior,
),
CreateLyricsEmbed(SessionHandle, CommandInteraction),
Reactivate(UserId, oneshot::Sender<Result<()>>),
@ -207,8 +211,8 @@ impl Session {
SessionCommand::GetPlayer(sender) => _ = sender.send(self.player.clone()),
SessionCommand::GetActive(sender) => _ = sender.send(self.active),
SessionCommand::CreatePlaybackEmbed(handle, interaction) => {
match PlaybackEmbed::create(self, handle, interaction).await {
SessionCommand::CreatePlaybackEmbed(handle, interaction, behavior) => {
match PlaybackEmbed::create(self, handle, interaction, behavior).await {
Ok(Some(playback_embed)) => {
self.playback_embed = Some(playback_embed);
}
@ -274,8 +278,10 @@ impl Session {
PlayerEvent::TrackChanged(_) => {}
}
let force_edit = !matches!(event, PlayerEvent::TrackChanged(_));
if let Some(playback_embed) = &self.playback_embed {
if playback_embed.invoke_update().await.is_err() {
if playback_embed.invoke_update(force_edit).await.is_err() {
self.playback_embed = None;
}
}
@ -457,11 +463,16 @@ impl SessionHandle {
/// Create a playback embed as a response to an interaction
///
/// This playback embed will automatically update when certain events happen
pub async fn create_playback_embed(&self, interaction: CommandInteraction) -> Result<()> {
pub async fn create_playback_embed(
&self,
interaction: CommandInteraction,
behavior: playback_embed::UpdateBehavior,
) -> Result<()> {
self.commands
.send(SessionCommand::CreatePlaybackEmbed(
self.clone(),
interaction,
behavior,
))
.await?;

View File

@ -1,11 +1,12 @@
use anyhow::{anyhow, Result};
use log::{error, trace};
use poise::ChoiceParameter;
use serenity::{
all::{
ButtonStyle, CommandInteraction, ComponentInteraction, ComponentInteractionCollector,
Context, CreateActionRow, CreateButton, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter,
CreateInteractionResponse, CreateInteractionResponseFollowup,
CreateInteractionResponseMessage, EditMessage, Message, User,
CreateInteractionResponseMessage, CreateMessage, EditMessage, Message, User,
},
futures::StreamExt,
};
@ -18,7 +19,30 @@ use crate::{Session, SessionHandle};
#[derive(Debug)]
pub enum Command {
InvokeUpdate,
InvokeUpdate(bool),
}
#[derive(Debug, Default, ChoiceParameter)]
pub enum UpdateBehavior {
#[default]
#[name = "Automatically update the embed"]
Default,
#[name = "Do not update the embed"]
Static,
#[name = "Re-send the embed after track changes"]
Pinned,
}
impl UpdateBehavior {
pub fn is_static(&self) -> bool {
matches!(self, Self::Static)
}
pub fn is_pinned(&self) -> bool {
matches!(self, Self::Pinned)
}
}
pub struct PlaybackEmbed {
@ -29,6 +53,8 @@ pub struct PlaybackEmbed {
last_update: Instant,
update_in: Option<Duration>,
force_edit: bool,
update_behavior: UpdateBehavior,
rx: mpsc::Receiver<Command>,
}
@ -38,6 +64,7 @@ impl PlaybackEmbed {
session: &Session,
handle: SessionHandle,
interaction: CommandInteraction,
update_behavior: UpdateBehavior,
) -> Result<Option<PlaybackEmbedHandle>> {
let ctx = session.context.clone();
@ -69,6 +96,11 @@ impl PlaybackEmbed {
)
.await?;
// If this is a static embed, we don't need to return any handles
if update_behavior.is_static() {
return Ok(None);
}
// Retrieve message instead of editing interaction response, as those tokens are only valid for 15 minutes
let message = interaction.get_response(&ctx).await?;
@ -84,6 +116,8 @@ impl PlaybackEmbed {
message,
last_update: Instant::now(),
update_in: None,
force_edit: false,
update_behavior,
rx,
};
@ -121,7 +155,7 @@ impl PlaybackEmbed {
tokio::time::sleep(update_in).await;
}
}, if self.update_in.is_some() => {
if self.update_embed().await.is_break() {
if self.update_embed(self.force_edit).await.is_break() {
break;
}
}
@ -133,15 +167,16 @@ impl PlaybackEmbed {
trace!("Received command: {command:?}");
match command {
Command::InvokeUpdate => {
Command::InvokeUpdate(force_edit) => {
if self.last_update.elapsed() < Duration::from_secs(2) {
if self.update_in.is_some() {
return ControlFlow::Continue(());
}
self.update_in = Some(Duration::from_secs(2) - self.last_update.elapsed());
self.force_edit = force_edit;
} else {
self.update_embed().await?;
self.update_embed(force_edit).await?;
}
}
}
@ -216,7 +251,7 @@ impl PlaybackEmbed {
Ok((player, playback_info, owner))
}
async fn update_embed(&mut self) -> ControlFlow<(), ()> {
async fn update_embed(&mut self, force_edit: bool) -> ControlFlow<(), ()> {
self.update_in = None;
let Ok(owner) = self.session.owner().await else {
@ -246,7 +281,30 @@ impl PlaybackEmbed {
}
};
if let Err(why) = self
let should_pin = !force_edit && self.update_behavior.is_pinned();
if should_pin {
self.message.delete(&self.ctx).await.ok();
match self
.message
.channel_id
.send_message(
&self.ctx,
CreateMessage::new()
.embed(build_embed(&playback_info, &owner))
.components(vec![build_buttons(self.id, playback_info.playing())]),
)
.await
{
Ok(message) => self.message = message,
Err(why) => {
error!("Failed to update playback embed: {why}");
return ControlFlow::Break(());
}
};
} else if let Err(why) = self
.message
.edit(
&self.ctx,
@ -259,7 +317,7 @@ impl PlaybackEmbed {
error!("Failed to update playback embed: {why}");
return ControlFlow::Break(());
};
}
self.last_update = Instant::now();
@ -267,6 +325,18 @@ impl PlaybackEmbed {
}
async fn update_not_playing(&mut self) -> Result<()> {
// If pinned, try to delete old message and send new one
if self.update_behavior.is_pinned() {
self.message.delete(&self.ctx).await.ok();
self.message = self
.message
.channel_id
.send_message(&self.ctx, CreateMessage::new().embed(not_playing_embed()))
.await?;
return Ok(());
}
self.message
.edit(&self.ctx, EditMessage::new().embed(not_playing_embed()))
.await?;
@ -284,8 +354,8 @@ impl PlaybackEmbedHandle {
!self.tx.is_closed()
}
pub async fn invoke_update(&self) -> Result<()> {
self.tx.send(Command::InvokeUpdate).await?;
pub async fn invoke_update(&self, force_edit: bool) -> Result<()> {
self.tx.send(Command::InvokeUpdate(force_edit)).await?;
Ok(())
}
@ -331,21 +401,27 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed {
.collect::<Vec<_>>()
.join(", ");
description += &format!("By {artists}\n\n");
description += &format!("By {artists}\n");
}
if let Some(album_name) = playback_info.album_name() {
description += &format!("Album: **{album_name}**\n");
}
if let Some(show_name) = playback_info.show_name() {
description += &format!("On {show_name}\n\n");
description += &format!("On {show_name}\n");
}
description += "\n";
let position = playback_info.current_position();
let index = position * 20 / playback_info.duration();
description.push_str(if playback_info.playing() {
description += if playback_info.playing() {
"▶️ "
} else {
"⏸️ "
});
};
for i in 0..20 {
if i == index {
@ -355,12 +431,12 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed {
}
}
description.push_str("\n:alarm_clock: ");
description.push_str(&format!(
description += "\n:alarm_clock: ";
description += &format!(
"{} / {}",
spoticord_utils::time_to_string(position / 1000),
spoticord_utils::time_to_string(playback_info.duration() / 1000)
));
);
CreateEmbed::new()
.author(

View File

@ -9,7 +9,7 @@ use crate::bot::Context;
#[poise::command(slash_command, guild_only)]
pub async fn disconnect(ctx: Context<'_>) -> Result<(), Error> {
let manager = ctx.data();
let guild = ctx.guild().expect("poise lied to me").id;
let guild = ctx.guild_id().expect("poise lied to me");
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
ctx.send(

View File

@ -4,7 +4,7 @@ use anyhow::Result;
use log::error;
use poise::CreateReply;
use serenity::all::{
Channel, ChannelId, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Guild, UserId,
Channel, ChannelId, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, UserId,
};
use spoticord_database::error::DatabaseError;
use spoticord_session::manager::SessionQuery;
@ -15,9 +15,30 @@ use crate::bot::Context;
/// Join the current voice channel
#[poise::command(slash_command, guild_only)]
pub async fn join(ctx: Context<'_>) -> Result<()> {
let guild: Guild = ctx.guild().expect("poise lied to me").clone();
let guild = ctx.guild_id().expect("poise lied to me");
let manager = ctx.data();
let Some(guild) = guild
.to_guild_cached(ctx.serenity_context())
.map(|guild| guild.clone())
else {
error!("Unable to fetch guild from cache, how did we get here?");
ctx.send(
CreateReply::default()
.embed(
CreateEmbed::new()
.title("An error occured")
.description("This server hasn't been cached yet?")
.color(Colors::Error),
)
.ephemeral(true),
)
.await?;
return Ok(());
};
let Some(channel) = guild
.voice_states
.get(&ctx.author().id)
@ -98,8 +119,6 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
return Ok(());
}
ctx.defer().await?;
let mut session_opt = manager.get_session(SessionQuery::Guild(guild.id));
// Check if this server already has a session active
@ -134,7 +153,8 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
"You are already using Spoticord in `{}`\n\n\
Stop playing in that server first before starting a new session.",
spoticord_utils::discord::escape(server_name)
)),
))
.color(Colors::Error),
)
.ephemeral(true),
)
@ -143,6 +163,8 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
return Ok(());
}
ctx.defer().await?;
if let Some(session) = &session_opt {
if session.voice_channel() != channel {
session.disconnect().await;
@ -163,7 +185,7 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
CreateEmbed::new()
.title("Failed to reactivate session")
.description(
"An error occured whilst trying to reactivate the session.",
"An error occured whilst trying to reactivate the session. Please try again.",
)
.color(Colors::Error),
)
@ -190,7 +212,9 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
.embed(
CreateEmbed::new()
.title("Failed to create session")
.description("An error occured whilst trying to create a session.")
.description(
"An error occured whilst trying to create a session. Please try again.",
)
.color(Colors::Error),
)
.ephemeral(true),

View File

@ -10,7 +10,7 @@ use crate::bot::Context;
#[poise::command(slash_command, guild_only)]
pub async fn lyrics(ctx: Context<'_>) -> Result<()> {
let manager = ctx.data();
let guild = ctx.guild().expect("poise lied to me").id;
let guild = ctx.guild_id().expect("poise lied to me");
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
ctx.send(

View File

@ -1,16 +1,21 @@
use anyhow::Result;
use poise::CreateReply;
use serenity::all::CreateEmbed;
use spoticord_session::manager::SessionQuery;
use spoticord_session::{manager::SessionQuery, playback_embed::UpdateBehavior};
use spoticord_utils::discord::Colors;
use crate::bot::Context;
/// Show details of the current song that is being played
#[poise::command(slash_command, guild_only)]
pub async fn playing(ctx: Context<'_>) -> Result<()> {
pub async fn playing(
ctx: Context<'_>,
#[description = "How Spoticord should update this information"] update_behavior: Option<
UpdateBehavior,
>,
) -> Result<()> {
let manager = ctx.data();
let guild = ctx.guild().expect("poise lied to me").id;
let guild = ctx.guild_id().expect("poise lied to me");
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
ctx.send(
@ -33,7 +38,10 @@ pub async fn playing(ctx: Context<'_>) -> Result<()> {
};
session
.create_playback_embed(context.interaction.clone())
.create_playback_embed(
context.interaction.clone(),
update_behavior.unwrap_or_default(),
)
.await?;
Ok(())

View File

@ -9,7 +9,7 @@ use crate::bot::Context;
#[poise::command(slash_command, guild_only)]
pub async fn stop(ctx: Context<'_>) -> Result<(), Error> {
let manager = ctx.data();
let guild = ctx.guild().expect("poise lied to me").id;
let guild = ctx.guild_id().expect("poise lied to me");
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
ctx.send(