commit
e383b4ab41
|
@ -18,15 +18,15 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Docker buildx
|
- name: Set up Docker buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to GitHub's container registry
|
- name: Login to GitHub's container registry
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Rust
|
- name: Set up Rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,6 +1,17 @@
|
||||||
# Changelog
|
# 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
|
### Changes
|
||||||
|
|
||||||
|
|
|
@ -86,4 +86,4 @@ cargo build [--release] --features stats
|
||||||
|
|
||||||
# MSRV
|
# 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`)_.
|
||||||
|
|
|
@ -592,14 +592,12 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deadpool"
|
name = "deadpool"
|
||||||
version = "0.9.5"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e"
|
checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
|
||||||
"deadpool-runtime",
|
"deadpool-runtime",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"retain_mut",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -681,22 +679,23 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel"
|
name = "diesel"
|
||||||
version = "2.1.6"
|
version = "2.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2"
|
checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"chrono",
|
"chrono",
|
||||||
"diesel_derives",
|
"diesel_derives",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"pq-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel-async"
|
name = "diesel-async"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be"
|
checksum = "fcb799bb6f8ca6a794462125d7b8983b0c86e6c93a33a9c55934a4a5de4409d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"deadpool",
|
"deadpool",
|
||||||
|
@ -709,9 +708,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel_async_migrations"
|
name = "diesel_async_migrations"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a700d6b83a17973b94d3065970fd2b36f1036c3fe08adcbdce1c9beb8fb25553"
|
checksum = "377dd8e9d0fdab3dbb66236f8f71206b98e121991449f3b363a19d7948333160"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
|
@ -732,11 +731,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel_derives"
|
name = "diesel_derives"
|
||||||
version = "2.1.4"
|
version = "2.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c"
|
checksum = "d6ff2be1e7312c858b2ef974f5c7089833ae57b5311b334b30923af58e5718d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"diesel_table_macro_syntax",
|
"diesel_table_macro_syntax",
|
||||||
|
"dsl_auto_type",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.72",
|
"syn 2.0.72",
|
||||||
|
@ -744,9 +744,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel_table_macro_syntax"
|
name = "diesel_table_macro_syntax"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
|
checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syn 2.0.72",
|
"syn 2.0.72",
|
||||||
]
|
]
|
||||||
|
@ -779,6 +779,20 @@ version = "0.15.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
|
@ -2515,6 +2529,15 @@ dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pq-sys"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a24ff9e4cf6945c988f0db7005d87747bf72864965c3529d259ad155ac41d584"
|
||||||
|
dependencies = [
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "primal-check"
|
name = "primal-check"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -2904,12 +2927,6 @@ dependencies = [
|
||||||
"winreg 0.52.0",
|
"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]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.16.20"
|
version = "0.16.20"
|
||||||
|
@ -3721,7 +3738,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spoticord"
|
name = "spoticord"
|
||||||
version = "2.2.0"
|
version = "2.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
|
@ -3753,7 +3770,6 @@ dependencies = [
|
||||||
name = "spoticord_config"
|
name = "spoticord_config"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
|
||||||
"rspotify",
|
"rspotify",
|
||||||
"serenity",
|
"serenity",
|
||||||
]
|
]
|
||||||
|
@ -4217,9 +4233,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.39.2"
|
version = "1.39.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
|
checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -4792,6 +4808,12 @@ dependencies = [
|
||||||
"ryu",
|
"ryu",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vergen"
|
name = "vergen"
|
||||||
version = "8.3.2"
|
version = "8.3.2"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "spoticord"
|
name = "spoticord"
|
||||||
version = "2.2.0"
|
version = "2.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.75.0"
|
rust-version = "1.80.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "spoticord"
|
name = "spoticord"
|
||||||
|
@ -38,7 +38,7 @@ log = "0.4.22"
|
||||||
poise = "0.6.1"
|
poise = "0.6.1"
|
||||||
serenity = "0.12.2"
|
serenity = "0.12.2"
|
||||||
songbird = { version = "0.4.3", features = ["simd-json"] }
|
songbird = { version = "0.4.3", features = ["simd-json"] }
|
||||||
tokio = { version = "1.39.2", features = ["full"] }
|
tokio = { version = "1.39.3", features = ["full"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
29
Dockerfile
29
Dockerfile
|
@ -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
|
# 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
|
WORKDIR /app
|
||||||
|
|
||||||
# Add extra build dependencies here
|
# Add extra build dependencies here
|
||||||
RUN apt-get update && apt-get install -yqq \
|
RUN apt-get update && apt install -yqq \
|
||||||
cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
|
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 . .
|
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
|
# Add `--no-default-features` if you don't want stats collection
|
||||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
--mount=type=cache,target=/app/target \
|
--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
|
# 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/x86_64-unknown-linux-gnu/release/spoticord /app/x86_64 && \
|
||||||
cp /app/target/aarch64-unknown-linux-gnu/release/spoticord /app/aarch64
|
cp /app/target/aarch64-unknown-linux-gnu/release/spoticord /app/aarch64
|
||||||
|
|
||||||
# Runtime
|
# Runtime
|
||||||
FROM debian:buster-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ENV TARGETPLATFORM=${TARGETPLATFORM}
|
ENV TARGETPLATFORM=${TARGETPLATFORM}
|
||||||
|
|
|
@ -8,5 +8,5 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false }
|
librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false }
|
||||||
songbird = { version = "0.4.3", features = ["simd-json"] }
|
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"
|
zerocopy = "0.7.35"
|
||||||
|
|
|
@ -4,7 +4,6 @@ version = "2.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "1.5.0"
|
|
||||||
rspotify = { version = "0.13.2", default-features = false, features = [
|
rspotify = { version = "0.13.2", default-features = false, features = [
|
||||||
"client-reqwest",
|
"client-reqwest",
|
||||||
"reqwest-rustls-tls",
|
"reqwest-rustls-tls",
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
use lazy_static::lazy_static;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
lazy_static! {
|
pub static DISCORD_TOKEN: LazyLock<String> = LazyLock::new(|| {
|
||||||
pub static ref DISCORD_TOKEN: String =
|
std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN environment variable")
|
||||||
std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN environment variable");
|
});
|
||||||
pub static ref DATABASE_URL: String =
|
pub static DATABASE_URL: LazyLock<String> = LazyLock::new(|| {
|
||||||
std::env::var("DATABASE_URL").expect("missing DATABASE_URL environment variable");
|
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 LINK_URL: LazyLock<String> =
|
||||||
pub static ref SPOTIFY_CLIENT_ID: String =
|
LazyLock::new(|| std::env::var("LINK_URL").expect("missing LINK_URL environment variable"));
|
||||||
std::env::var("SPOTIFY_CLIENT_ID").expect("missing SPOTIFY_CLIENT_ID environment variable");
|
pub static SPOTIFY_CLIENT_ID: LazyLock<String> = LazyLock::new(|| {
|
||||||
pub static ref SPOTIFY_CLIENT_SECRET: String = std::env::var("SPOTIFY_CLIENT_SECRET")
|
std::env::var("SPOTIFY_CLIENT_ID").expect("missing SPOTIFY_CLIENT_ID environment variable")
|
||||||
.expect("missing SPOTIFY_CLIENT_SECRET environment variable");
|
});
|
||||||
}
|
pub static SPOTIFY_CLIENT_SECRET: LazyLock<String> = LazyLock::new(|| {
|
||||||
|
std::env::var("SPOTIFY_CLIENT_SECRET")
|
||||||
|
.expect("missing SPOTIFY_CLIENT_SECRET environment variable")
|
||||||
|
});
|
||||||
|
|
|
@ -6,8 +6,8 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
spoticord_config = { path = "../spoticord_config" }
|
spoticord_config = { path = "../spoticord_config" }
|
||||||
|
|
||||||
diesel = { version = "2.1.6", features = ["chrono"] }
|
diesel = { version = "2.2.2", features = ["chrono"] }
|
||||||
diesel-async = { version = "0.4.1", features = ["deadpool", "postgres"] }
|
diesel-async = { version = "0.5.0", features = ["deadpool", "postgres"] }
|
||||||
rspotify = { version = "0.13.2", default-features = false, features = [
|
rspotify = { version = "0.13.2", default-features = false, features = [
|
||||||
"client-reqwest",
|
"client-reqwest",
|
||||||
"reqwest-rustls-tls",
|
"reqwest-rustls-tls",
|
||||||
|
@ -15,4 +15,4 @@ rspotify = { version = "0.13.2", default-features = false, features = [
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
thiserror = "1.0.63"
|
thiserror = "1.0.63"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
diesel_async_migrations = "0.12.0"
|
diesel_async_migrations = "0.13.0"
|
||||||
|
|
|
@ -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 }
|
librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false }
|
||||||
songbird = { version = "0.4.3", features = ["simd-json"] }
|
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"
|
anyhow = "1.0.86"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
symphonia = { version = "0.5.4", default-features = false, features = ["pcm"] }
|
symphonia = { version = "0.5.4", default-features = false, features = ["pcm"] }
|
||||||
|
|
|
@ -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 {
|
pub fn thumbnail(&self) -> String {
|
||||||
self.audio_item
|
self.audio_item
|
||||||
.covers
|
.covers
|
||||||
|
|
|
@ -11,7 +11,7 @@ spoticord_database = { path = "../spoticord_database" }
|
||||||
spoticord_player = { path = "../spoticord_player" }
|
spoticord_player = { path = "../spoticord_player" }
|
||||||
spoticord_utils = { path = "../spoticord_utils" }
|
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 }
|
librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false }
|
||||||
serenity = "0.12.2"
|
serenity = "0.12.2"
|
||||||
songbird = { version = "0.4.3", features = ["simd-json"] }
|
songbird = { version = "0.4.3", features = ["simd-json"] }
|
||||||
|
|
|
@ -31,7 +31,11 @@ pub enum SessionCommand {
|
||||||
GetPlayer(oneshot::Sender<PlayerHandle>),
|
GetPlayer(oneshot::Sender<PlayerHandle>),
|
||||||
GetActive(oneshot::Sender<bool>),
|
GetActive(oneshot::Sender<bool>),
|
||||||
|
|
||||||
CreatePlaybackEmbed(SessionHandle, CommandInteraction),
|
CreatePlaybackEmbed(
|
||||||
|
SessionHandle,
|
||||||
|
CommandInteraction,
|
||||||
|
playback_embed::UpdateBehavior,
|
||||||
|
),
|
||||||
CreateLyricsEmbed(SessionHandle, CommandInteraction),
|
CreateLyricsEmbed(SessionHandle, CommandInteraction),
|
||||||
|
|
||||||
Reactivate(UserId, oneshot::Sender<Result<()>>),
|
Reactivate(UserId, oneshot::Sender<Result<()>>),
|
||||||
|
@ -207,8 +211,8 @@ impl Session {
|
||||||
SessionCommand::GetPlayer(sender) => _ = sender.send(self.player.clone()),
|
SessionCommand::GetPlayer(sender) => _ = sender.send(self.player.clone()),
|
||||||
SessionCommand::GetActive(sender) => _ = sender.send(self.active),
|
SessionCommand::GetActive(sender) => _ = sender.send(self.active),
|
||||||
|
|
||||||
SessionCommand::CreatePlaybackEmbed(handle, interaction) => {
|
SessionCommand::CreatePlaybackEmbed(handle, interaction, behavior) => {
|
||||||
match PlaybackEmbed::create(self, handle, interaction).await {
|
match PlaybackEmbed::create(self, handle, interaction, behavior).await {
|
||||||
Ok(Some(playback_embed)) => {
|
Ok(Some(playback_embed)) => {
|
||||||
self.playback_embed = Some(playback_embed);
|
self.playback_embed = Some(playback_embed);
|
||||||
}
|
}
|
||||||
|
@ -274,8 +278,10 @@ impl Session {
|
||||||
PlayerEvent::TrackChanged(_) => {}
|
PlayerEvent::TrackChanged(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let force_edit = !matches!(event, PlayerEvent::TrackChanged(_));
|
||||||
|
|
||||||
if let Some(playback_embed) = &self.playback_embed {
|
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;
|
self.playback_embed = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,11 +463,16 @@ impl SessionHandle {
|
||||||
/// Create a playback embed as a response to an interaction
|
/// Create a playback embed as a response to an interaction
|
||||||
///
|
///
|
||||||
/// This playback embed will automatically update when certain events happen
|
/// 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
|
self.commands
|
||||||
.send(SessionCommand::CreatePlaybackEmbed(
|
.send(SessionCommand::CreatePlaybackEmbed(
|
||||||
self.clone(),
|
self.clone(),
|
||||||
interaction,
|
interaction,
|
||||||
|
behavior,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use log::{error, trace};
|
use log::{error, trace};
|
||||||
|
use poise::ChoiceParameter;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
all::{
|
all::{
|
||||||
ButtonStyle, CommandInteraction, ComponentInteraction, ComponentInteractionCollector,
|
ButtonStyle, CommandInteraction, ComponentInteraction, ComponentInteractionCollector,
|
||||||
Context, CreateActionRow, CreateButton, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter,
|
Context, CreateActionRow, CreateButton, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter,
|
||||||
CreateInteractionResponse, CreateInteractionResponseFollowup,
|
CreateInteractionResponse, CreateInteractionResponseFollowup,
|
||||||
CreateInteractionResponseMessage, EditMessage, Message, User,
|
CreateInteractionResponseMessage, CreateMessage, EditMessage, Message, User,
|
||||||
},
|
},
|
||||||
futures::StreamExt,
|
futures::StreamExt,
|
||||||
};
|
};
|
||||||
|
@ -18,7 +19,30 @@ use crate::{Session, SessionHandle};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Command {
|
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 {
|
pub struct PlaybackEmbed {
|
||||||
|
@ -29,6 +53,8 @@ pub struct PlaybackEmbed {
|
||||||
|
|
||||||
last_update: Instant,
|
last_update: Instant,
|
||||||
update_in: Option<Duration>,
|
update_in: Option<Duration>,
|
||||||
|
force_edit: bool,
|
||||||
|
update_behavior: UpdateBehavior,
|
||||||
|
|
||||||
rx: mpsc::Receiver<Command>,
|
rx: mpsc::Receiver<Command>,
|
||||||
}
|
}
|
||||||
|
@ -38,6 +64,7 @@ impl PlaybackEmbed {
|
||||||
session: &Session,
|
session: &Session,
|
||||||
handle: SessionHandle,
|
handle: SessionHandle,
|
||||||
interaction: CommandInteraction,
|
interaction: CommandInteraction,
|
||||||
|
update_behavior: UpdateBehavior,
|
||||||
) -> Result<Option<PlaybackEmbedHandle>> {
|
) -> Result<Option<PlaybackEmbedHandle>> {
|
||||||
let ctx = session.context.clone();
|
let ctx = session.context.clone();
|
||||||
|
|
||||||
|
@ -69,6 +96,11 @@ impl PlaybackEmbed {
|
||||||
)
|
)
|
||||||
.await?;
|
.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
|
// Retrieve message instead of editing interaction response, as those tokens are only valid for 15 minutes
|
||||||
let message = interaction.get_response(&ctx).await?;
|
let message = interaction.get_response(&ctx).await?;
|
||||||
|
|
||||||
|
@ -84,6 +116,8 @@ impl PlaybackEmbed {
|
||||||
message,
|
message,
|
||||||
last_update: Instant::now(),
|
last_update: Instant::now(),
|
||||||
update_in: None,
|
update_in: None,
|
||||||
|
force_edit: false,
|
||||||
|
update_behavior,
|
||||||
rx,
|
rx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,7 +155,7 @@ impl PlaybackEmbed {
|
||||||
tokio::time::sleep(update_in).await;
|
tokio::time::sleep(update_in).await;
|
||||||
}
|
}
|
||||||
}, if self.update_in.is_some() => {
|
}, if self.update_in.is_some() => {
|
||||||
if self.update_embed().await.is_break() {
|
if self.update_embed(self.force_edit).await.is_break() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,15 +167,16 @@ impl PlaybackEmbed {
|
||||||
trace!("Received command: {command:?}");
|
trace!("Received command: {command:?}");
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
Command::InvokeUpdate => {
|
Command::InvokeUpdate(force_edit) => {
|
||||||
if self.last_update.elapsed() < Duration::from_secs(2) {
|
if self.last_update.elapsed() < Duration::from_secs(2) {
|
||||||
if self.update_in.is_some() {
|
if self.update_in.is_some() {
|
||||||
return ControlFlow::Continue(());
|
return ControlFlow::Continue(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_in = Some(Duration::from_secs(2) - self.last_update.elapsed());
|
self.update_in = Some(Duration::from_secs(2) - self.last_update.elapsed());
|
||||||
|
self.force_edit = force_edit;
|
||||||
} else {
|
} else {
|
||||||
self.update_embed().await?;
|
self.update_embed(force_edit).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +251,7 @@ impl PlaybackEmbed {
|
||||||
Ok((player, playback_info, owner))
|
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;
|
self.update_in = None;
|
||||||
|
|
||||||
let Ok(owner) = self.session.owner().await else {
|
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
|
.message
|
||||||
.edit(
|
.edit(
|
||||||
&self.ctx,
|
&self.ctx,
|
||||||
|
@ -259,7 +317,7 @@ impl PlaybackEmbed {
|
||||||
error!("Failed to update playback embed: {why}");
|
error!("Failed to update playback embed: {why}");
|
||||||
|
|
||||||
return ControlFlow::Break(());
|
return ControlFlow::Break(());
|
||||||
};
|
}
|
||||||
|
|
||||||
self.last_update = Instant::now();
|
self.last_update = Instant::now();
|
||||||
|
|
||||||
|
@ -267,6 +325,18 @@ impl PlaybackEmbed {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_not_playing(&mut self) -> Result<()> {
|
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
|
self.message
|
||||||
.edit(&self.ctx, EditMessage::new().embed(not_playing_embed()))
|
.edit(&self.ctx, EditMessage::new().embed(not_playing_embed()))
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -284,8 +354,8 @@ impl PlaybackEmbedHandle {
|
||||||
!self.tx.is_closed()
|
!self.tx.is_closed()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn invoke_update(&self) -> Result<()> {
|
pub async fn invoke_update(&self, force_edit: bool) -> Result<()> {
|
||||||
self.tx.send(Command::InvokeUpdate).await?;
|
self.tx.send(Command::InvokeUpdate(force_edit)).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -331,21 +401,27 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.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() {
|
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 position = playback_info.current_position();
|
||||||
let index = position * 20 / playback_info.duration();
|
let index = position * 20 / playback_info.duration();
|
||||||
|
|
||||||
description.push_str(if playback_info.playing() {
|
description += if playback_info.playing() {
|
||||||
"▶️ "
|
"▶️ "
|
||||||
} else {
|
} else {
|
||||||
"⏸️ "
|
"⏸️ "
|
||||||
});
|
};
|
||||||
|
|
||||||
for i in 0..20 {
|
for i in 0..20 {
|
||||||
if i == index {
|
if i == index {
|
||||||
|
@ -355,12 +431,12 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
description.push_str("\n:alarm_clock: ");
|
description += "\n:alarm_clock: ";
|
||||||
description.push_str(&format!(
|
description += &format!(
|
||||||
"{} / {}",
|
"{} / {}",
|
||||||
spoticord_utils::time_to_string(position / 1000),
|
spoticord_utils::time_to_string(position / 1000),
|
||||||
spoticord_utils::time_to_string(playback_info.duration() / 1000)
|
spoticord_utils::time_to_string(playback_info.duration() / 1000)
|
||||||
));
|
);
|
||||||
|
|
||||||
CreateEmbed::new()
|
CreateEmbed::new()
|
||||||
.author(
|
.author(
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::bot::Context;
|
||||||
#[poise::command(slash_command, guild_only)]
|
#[poise::command(slash_command, guild_only)]
|
||||||
pub async fn disconnect(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn disconnect(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
let manager = ctx.data();
|
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 {
|
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
|
||||||
ctx.send(
|
ctx.send(
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||||
use log::error;
|
use log::error;
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
use serenity::all::{
|
use serenity::all::{
|
||||||
Channel, ChannelId, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Guild, UserId,
|
Channel, ChannelId, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, UserId,
|
||||||
};
|
};
|
||||||
use spoticord_database::error::DatabaseError;
|
use spoticord_database::error::DatabaseError;
|
||||||
use spoticord_session::manager::SessionQuery;
|
use spoticord_session::manager::SessionQuery;
|
||||||
|
@ -15,9 +15,30 @@ use crate::bot::Context;
|
||||||
/// Join the current voice channel
|
/// Join the current voice channel
|
||||||
#[poise::command(slash_command, guild_only)]
|
#[poise::command(slash_command, guild_only)]
|
||||||
pub async fn join(ctx: Context<'_>) -> Result<()> {
|
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 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
|
let Some(channel) = guild
|
||||||
.voice_states
|
.voice_states
|
||||||
.get(&ctx.author().id)
|
.get(&ctx.author().id)
|
||||||
|
@ -98,8 +119,6 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.defer().await?;
|
|
||||||
|
|
||||||
let mut session_opt = manager.get_session(SessionQuery::Guild(guild.id));
|
let mut session_opt = manager.get_session(SessionQuery::Guild(guild.id));
|
||||||
|
|
||||||
// Check if this server already has a session active
|
// 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\
|
"You are already using Spoticord in `{}`\n\n\
|
||||||
Stop playing in that server first before starting a new session.",
|
Stop playing in that server first before starting a new session.",
|
||||||
spoticord_utils::discord::escape(server_name)
|
spoticord_utils::discord::escape(server_name)
|
||||||
)),
|
))
|
||||||
|
.color(Colors::Error),
|
||||||
)
|
)
|
||||||
.ephemeral(true),
|
.ephemeral(true),
|
||||||
)
|
)
|
||||||
|
@ -143,6 +163,8 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.defer().await?;
|
||||||
|
|
||||||
if let Some(session) = &session_opt {
|
if let Some(session) = &session_opt {
|
||||||
if session.voice_channel() != channel {
|
if session.voice_channel() != channel {
|
||||||
session.disconnect().await;
|
session.disconnect().await;
|
||||||
|
@ -163,7 +185,7 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
|
||||||
CreateEmbed::new()
|
CreateEmbed::new()
|
||||||
.title("Failed to reactivate session")
|
.title("Failed to reactivate session")
|
||||||
.description(
|
.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),
|
.color(Colors::Error),
|
||||||
)
|
)
|
||||||
|
@ -190,7 +212,9 @@ pub async fn join(ctx: Context<'_>) -> Result<()> {
|
||||||
.embed(
|
.embed(
|
||||||
CreateEmbed::new()
|
CreateEmbed::new()
|
||||||
.title("Failed to create session")
|
.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),
|
.color(Colors::Error),
|
||||||
)
|
)
|
||||||
.ephemeral(true),
|
.ephemeral(true),
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::bot::Context;
|
||||||
#[poise::command(slash_command, guild_only)]
|
#[poise::command(slash_command, guild_only)]
|
||||||
pub async fn lyrics(ctx: Context<'_>) -> Result<()> {
|
pub async fn lyrics(ctx: Context<'_>) -> Result<()> {
|
||||||
let manager = ctx.data();
|
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 {
|
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
|
||||||
ctx.send(
|
ctx.send(
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
use serenity::all::CreateEmbed;
|
use serenity::all::CreateEmbed;
|
||||||
use spoticord_session::manager::SessionQuery;
|
use spoticord_session::{manager::SessionQuery, playback_embed::UpdateBehavior};
|
||||||
use spoticord_utils::discord::Colors;
|
use spoticord_utils::discord::Colors;
|
||||||
|
|
||||||
use crate::bot::Context;
|
use crate::bot::Context;
|
||||||
|
|
||||||
/// Show details of the current song that is being played
|
/// Show details of the current song that is being played
|
||||||
#[poise::command(slash_command, guild_only)]
|
#[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 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 {
|
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
|
||||||
ctx.send(
|
ctx.send(
|
||||||
|
@ -33,7 +38,10 @@ pub async fn playing(ctx: Context<'_>) -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
session
|
session
|
||||||
.create_playback_embed(context.interaction.clone())
|
.create_playback_embed(
|
||||||
|
context.interaction.clone(),
|
||||||
|
update_behavior.unwrap_or_default(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::bot::Context;
|
||||||
#[poise::command(slash_command, guild_only)]
|
#[poise::command(slash_command, guild_only)]
|
||||||
pub async fn stop(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn stop(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
let manager = ctx.data();
|
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 {
|
let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else {
|
||||||
ctx.send(
|
ctx.send(
|
||||||
|
|
Loading…
Reference in New Issue