commit
d1c01e9274
|
@ -4,3 +4,5 @@ protocol = "sparse"
|
|||
[target.x86_64-pc-windows-gnu]
|
||||
rustflags = "-C link-args=-lssp" # Does not compile without this line
|
||||
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
rustflags = "-C linker=aarch64-linux-gnu-gcc"
|
|
@ -1,34 +1,66 @@
|
|||
name: Build and push to repository
|
||||
name: Build and push to registry
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: [ "main", "dev" ]
|
||||
tags: [ "v*.*.*" ]
|
||||
pull_request:
|
||||
branches: [ "main", "dev" ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build Docker image and push to repository
|
||||
name: Build Docker image and push to registry
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Set up Docker buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to repository
|
||||
|
||||
- name: Login to GitHub's container registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ secrets.REGISTRY_URL }}
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: Entepotenz/change-string-case-action-min-dependencies@v1 # https://github.com/orgs/community/discussions/10553
|
||||
id: repo-uri-string
|
||||
with:
|
||||
string: ghcr.io/${{ github.repository }}
|
||||
|
||||
- name: Generate image metadata
|
||||
id: docker-meta # used in next step
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: ${{ steps.repo-uri-string.outputs.lowercase }}
|
||||
# Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
tags: |
|
||||
${{ secrets.REGISTRY_URL }}/spoticord/spoticord:latest
|
||||
push: ${{ github.ref == 'refs/heads/main' }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.docker-meta.outputs.tags }}
|
||||
labels: ${{ steps.docker-meta.outputs.labels }}
|
||||
# Some basic caching of the layers...
|
||||
cache-from: ${{ steps.repo-uri-string.outputs.lowercase }}:latest-cache
|
||||
cache-to: ${{ steps.repo-uri-string.outputs.lowercase }}:latest-cache
|
||||
|
|
46
CHANGELOG.md
46
CHANGELOG.md
|
@ -1,3 +1,47 @@
|
|||
# Changelog
|
||||
|
||||
## 2.1.2 | September 28th 2023
|
||||
|
||||
### Changes
|
||||
* Removed OpenSSL dependency
|
||||
* Added aarch64 support
|
||||
* Added cross compilation to Github Actions
|
||||
* Added `dev` branch to Github Actions
|
||||
* Removed hardcoded URL in the /join command
|
||||
* Fixed an issue in /playing where the bot showed it was playing even though it was paused
|
||||
|
||||
**Full Changelog**: https://github.com/SpoticordMusic/spoticord/compare/v2.1.1...v2.1.2
|
||||
|
||||
## 2.1.1 | September 23rd 2023
|
||||
Reduced the amount of CPU that the bot uses from ~15%-25% per user to 1%-2% per user (percentage per core, benched on an AMD Ryzen 9 5950X).
|
||||
|
||||
### Changes
|
||||
* Fixed issue #20
|
||||
|
||||
**Full Changelog**: https://github.com/SpoticordMusic/spoticord/compare/v2.1.0...v2.1.1
|
||||
|
||||
## 2.1.0 | September 20th 2023
|
||||
So, it's been a while since I worked on this project, and some bugs have since been discovered.
|
||||
The main focus for this version is to stop using multiple processes for every player, and instead do everything in threads.
|
||||
|
||||
### Changes
|
||||
|
||||
- Remove metrics, as I wasn't using this feature anyways
|
||||
- Bring back KV for storing total/active sessions, as prometheus is no longer being used
|
||||
- Allocate new players in-memory, instead of using subprocesses
|
||||
- Fix issue #17
|
||||
- Fix some issues with the auto-disconnect
|
||||
- Removed the automatic device switching on bot join, which was causing some people to not be able to use the bot
|
||||
- Force communication through the closest Spotify AP, reducing latency
|
||||
- Potential jitter reduction
|
||||
- Enable autoplay
|
||||
- After skipping a song, you will no longer hear a tiny bit of the previous song after the silence
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/SpoticordMusic/spoticord/compare/v2.0.0...v2.1.0
|
||||
|
||||
### Issues
|
||||
- Currently, the CPU usage is much higher than it used to be. I really wanted to push this update out before taking the time to do some optimizations, as the bot and server are still easily able to hold up the limited amount of Spoticord users (and v2.0.0 was just falling apart). Issue is being tracked in #20
|
||||
|
||||
## 2.0.0 | June 8th 2023
|
||||
- Initial Release
|
||||
- Initial Release
|
|
@ -27,17 +27,17 @@ sudo pacman -S base-devel
|
|||
sudo dnf install gcc
|
||||
```
|
||||
|
||||
Additionally, you will need to install CMake and OpenSSL (Linux only). On Windows, you can download CMake [here](https://cmake.org/download/). On Linux, you can use your package manager to install them:
|
||||
Additionally, you will need to install CMake. On Windows, you can download CMake [here](https://cmake.org/download/). On Linux, you can use your package manager to install it:
|
||||
|
||||
```sh
|
||||
# Debian/Ubuntu
|
||||
sudo apt install cmake libssl-dev
|
||||
sudo apt install cmake
|
||||
|
||||
# Arch
|
||||
sudo pacman -S cmake openssl
|
||||
sudo pacman -S cmake
|
||||
|
||||
# Fedora
|
||||
sudo dnf install cmake openssl-devel
|
||||
sudo dnf install cmake
|
||||
```
|
||||
|
||||
## Compiling
|
||||
|
|
|
@ -328,16 +328,6 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
|
@ -541,9 +531,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
|
@ -580,21 +570,6 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.0"
|
||||
|
@ -940,19 +915,6 @@ dependencies = [
|
|||
"tokio-rustls 0.24.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.57"
|
||||
|
@ -1409,24 +1371,6 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.2"
|
||||
|
@ -1531,50 +1475,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.10.0"
|
||||
|
@ -1827,8 +1727,6 @@ dependencies = [
|
|||
"itoa",
|
||||
"percent-encoding",
|
||||
"ryu",
|
||||
"sha1_smol",
|
||||
"socket2 0.4.9",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
@ -1910,13 +1808,11 @@ dependencies = [
|
|||
"http-body",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
|
@ -1926,7 +1822,6 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls 0.24.1",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
|
@ -2068,15 +1963,6 @@ dependencies = [
|
|||
"libsamplerate-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
|
@ -2099,29 +1985,6 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.19"
|
||||
|
@ -2274,12 +2137,6 @@ dependencies = [
|
|||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||
|
||||
[[package]]
|
||||
name = "shannon"
|
||||
version = "0.2.0"
|
||||
|
@ -2398,12 +2255,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "spoticord"
|
||||
version = "2.1.1"
|
||||
version = "2.1.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dotenv",
|
||||
"env_logger 0.10.0",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"librespot",
|
||||
"log",
|
||||
"protobuf",
|
||||
|
@ -2415,7 +2273,6 @@ dependencies = [
|
|||
"serenity",
|
||||
"songbird",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"zerocopy 0.7.5",
|
||||
]
|
||||
|
@ -2543,9 +2400,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.28"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48"
|
||||
checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
|
@ -2556,15 +2413,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572"
|
||||
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
@ -2614,16 +2471,6 @@ dependencies = [
|
|||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.23.4"
|
||||
|
@ -2888,12 +2735,6 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "3.2.0"
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "spoticord"
|
||||
version = "2.1.1"
|
||||
version = "2.1.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
|
@ -16,18 +16,18 @@ anyhow = "1.0.75"
|
|||
dotenv = "0.15.0"
|
||||
env_logger = "0.10.0"
|
||||
hex = "0.4.3"
|
||||
lazy_static = "1.4.0"
|
||||
librespot = { version = "0.4.2", default-features = false }
|
||||
log = "0.4.20"
|
||||
protobuf = "2.28.0"
|
||||
redis = { version = "0.23.3", optional = true }
|
||||
reqwest = "0.11.20"
|
||||
redis = { version = "0.23.3", optional = true, default-features = false }
|
||||
reqwest = { version = "0.11.20", default-features = false }
|
||||
samplerate = "0.2.4"
|
||||
serde = "1.0.188"
|
||||
serde_json = "1.0.107"
|
||||
serenity = { version = "0.11.6", features = ["framework", "cache", "standard_framework"], default-features = false }
|
||||
songbird = "0.3.2"
|
||||
serenity = { version = "0.11.6", features = ["framework", "cache", "standard_framework", "rustls_backend", "gateway"], default-features = false }
|
||||
songbird = { version = "0.3.2", features = ["driver", "serenity-rustls"], default-features = false }
|
||||
thiserror = "1.0.48"
|
||||
time = "0.3.28"
|
||||
tokio = { version = "1.32.0", features = ["rt", "full"] }
|
||||
zerocopy = "0.7.5"
|
||||
|
||||
|
|
37
Dockerfile
37
Dockerfile
|
@ -1,25 +1,44 @@
|
|||
# Builder
|
||||
FROM rust:1.72.1-buster as builder
|
||||
FROM --platform=linux/amd64 rust:1.72.1-buster as builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Add extra build dependencies here
|
||||
RUN apt-get update && apt-get install -y cmake
|
||||
RUN apt-get update && apt-get install -yqq \
|
||||
cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
|
||||
|
||||
COPY . .
|
||||
|
||||
# Remove `--features stats` if you want to deploy without stats collection
|
||||
RUN cargo install --path . --features stats
|
||||
RUN rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu
|
||||
|
||||
# Remove `--features=stats` if you want to deploy without stats collection
|
||||
RUN cargo build --features=stats --release \
|
||||
--target=x86_64-unknown-linux-gnu --target=aarch64-unknown-linux-gnu
|
||||
|
||||
# Runtime
|
||||
FROM debian:buster-slim
|
||||
|
||||
WORKDIR /app
|
||||
ARG TARGETPLATFORM
|
||||
ENV TARGETPLATFORM=$TARGETPLATFORM
|
||||
|
||||
# Add extra runtime dependencies here
|
||||
RUN apt-get update && apt-get install -y openssl ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
# RUN apt-get update && apt-get install -yqq --no-install-recommends \
|
||||
# openssl ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy spoticord binary from builder
|
||||
COPY --from=builder /usr/local/cargo/bin/spoticord ./spoticord
|
||||
# Copy spoticord binaries from builder to /tmp
|
||||
COPY --from=builder \
|
||||
/app/target/x86_64-unknown-linux-gnu/release/spoticord /tmp/x86_64
|
||||
COPY --from=builder \
|
||||
/app/target/aarch64-unknown-linux-gnu/release/spoticord /tmp/aarch64
|
||||
|
||||
CMD ["./spoticord"]
|
||||
# Copy appropiate binary for target arch from /tmp
|
||||
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
|
||||
cp /tmp/x86_64 /usr/local/bin/spoticord; \
|
||||
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
|
||||
cp /tmp/aarch64 /usr/local/bin/spoticord; \
|
||||
fi
|
||||
|
||||
# Delete unused binaries
|
||||
RUN rm -rvf /tmp/x86_64 /tmp/aarch64
|
||||
|
||||
ENTRYPOINT [ "/usr/local/bin/spoticord" ]
|
||||
|
|
|
@ -8,6 +8,7 @@ use serenity::{
|
|||
|
||||
use crate::{
|
||||
bot::commands::{respond_message, CommandOutput},
|
||||
consts::SPOTICORD_ACCOUNTS_URL,
|
||||
database::{Database, DatabaseError},
|
||||
utils::embed::{EmbedBuilder, Status},
|
||||
};
|
||||
|
@ -39,8 +40,11 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO
|
|||
}
|
||||
|
||||
if let Ok(request) = database.get_user_request(command.user.id.to_string()).await {
|
||||
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").expect("to be present");
|
||||
let link = format!("{}/spotify/{}", base, request.token);
|
||||
let link = format!(
|
||||
"{}/spotify/{}",
|
||||
SPOTICORD_ACCOUNTS_URL.as_str(),
|
||||
request.token
|
||||
);
|
||||
|
||||
respond_message(
|
||||
&ctx,
|
||||
|
@ -106,8 +110,11 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO
|
|||
.await
|
||||
{
|
||||
Ok(request) => {
|
||||
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").expect("to be present");
|
||||
let link = format!("{}/spotify/{}", base, request.token);
|
||||
let link = format!(
|
||||
"{}/spotify/{}",
|
||||
SPOTICORD_ACCOUNTS_URL.as_str(),
|
||||
request.token
|
||||
);
|
||||
|
||||
respond_message(
|
||||
&ctx,
|
||||
|
|
|
@ -7,6 +7,7 @@ use serenity::{
|
|||
|
||||
use crate::{
|
||||
bot::commands::{defer_message, respond_message, update_message, CommandOutput},
|
||||
consts::SPOTICORD_ACCOUNTS_URL,
|
||||
session::manager::{SessionCreateError, SessionManager},
|
||||
utils::embed::{EmbedBuilder, Status},
|
||||
};
|
||||
|
@ -241,7 +242,7 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO
|
|||
&command,
|
||||
EmbedBuilder::new()
|
||||
.title("Cannot join voice channel")
|
||||
.description("You need to link your Spotify account. Use </link:1036714850367320136> or go to [the accounts website](https://account.spoticord.com/) to get started.")
|
||||
.description(format!("You need to link your Spotify account. Use </link:1036714850367320136> or go to [the accounts website]({}) to get started.", SPOTICORD_ACCOUNTS_URL.as_str()))
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
)
|
||||
|
@ -255,7 +256,7 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO
|
|||
&command,
|
||||
EmbedBuilder::new()
|
||||
.title("Cannot join voice channel")
|
||||
.description("Spoticord no longer has access to your Spotify account. Use </link:1036714850367320136> or go to [the accounts website](https://account.spoticord.com/) to relink your Spotify account.")
|
||||
.description(format!("Spoticord no longer has access to your Spotify account. Use </link:1036714850367320136> or go to [the accounts website]({}) to relink your Spotify account.", SPOTICORD_ACCOUNTS_URL.as_str()))
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
).await;
|
||||
|
|
|
@ -30,20 +30,22 @@ pub const NAME: &str = "playing";
|
|||
|
||||
pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
||||
Box::pin(async move {
|
||||
let not_playing = async {
|
||||
respond_message(
|
||||
&ctx,
|
||||
&command,
|
||||
EmbedBuilder::new()
|
||||
.title("Cannot get track info")
|
||||
.icon_url("https://spoticord.com/forbidden.png")
|
||||
.description("I'm currently not playing any music in this server")
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
};
|
||||
macro_rules! not_playing {
|
||||
() => {
|
||||
respond_message(
|
||||
&ctx,
|
||||
&command,
|
||||
EmbedBuilder::new()
|
||||
.title("Cannot get track info")
|
||||
.icon_url("https://spoticord.com/forbidden.png")
|
||||
.description("I'm currently not playing any music in this server")
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
};
|
||||
}
|
||||
|
||||
let data = ctx.data.read().await;
|
||||
let session_manager = data
|
||||
|
@ -51,62 +53,50 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO
|
|||
.expect("to contain a value")
|
||||
.clone();
|
||||
|
||||
let session = match session_manager
|
||||
let Some(session) = session_manager
|
||||
.get_session(command.guild_id.expect("to contain a value"))
|
||||
.await
|
||||
{
|
||||
Some(session) => session,
|
||||
None => {
|
||||
not_playing.await;
|
||||
else {
|
||||
not_playing!();
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
let owner = match session.owner().await {
|
||||
Some(owner) => owner,
|
||||
None => {
|
||||
not_playing.await;
|
||||
let Some(owner) = session.owner().await else {
|
||||
not_playing!();
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Get Playback Info from session
|
||||
let pbi = match session.playback_info().await {
|
||||
Some(pbi) => pbi,
|
||||
None => {
|
||||
not_playing.await;
|
||||
let Some(pbi) = session.playback_info().await else {
|
||||
not_playing!();
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Get owner of session
|
||||
let owner = match utils::discord::get_user(&ctx, owner).await {
|
||||
Some(user) => user,
|
||||
None => {
|
||||
// This shouldn't happen
|
||||
let Some(owner) = utils::discord::get_user(&ctx, owner).await else {
|
||||
// This shouldn't happen
|
||||
|
||||
error!("Could not find user with ID: {owner}");
|
||||
error!("Could not find user with ID: {owner}");
|
||||
|
||||
respond_message(
|
||||
&ctx,
|
||||
&command,
|
||||
EmbedBuilder::new()
|
||||
.title("[INTERNAL ERROR] Cannot get track info")
|
||||
.description(format!(
|
||||
"Could not find user with ID `{}`\nThis is an issue with the bot!",
|
||||
owner
|
||||
))
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
respond_message(
|
||||
&ctx,
|
||||
&command,
|
||||
EmbedBuilder::new()
|
||||
.title("[INTERNAL ERROR] Cannot get track info")
|
||||
.description(format!(
|
||||
"Could not find user with ID `{}`\nThis is an issue with the bot!",
|
||||
owner
|
||||
))
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Get metadata
|
||||
|
@ -188,48 +178,39 @@ pub fn component(ctx: Context, mut interaction: MessageComponentInteraction) ->
|
|||
.clone();
|
||||
|
||||
// Check if session still exists
|
||||
let mut session = match session_manager
|
||||
let Some(mut session) = session_manager
|
||||
.get_session(interaction.guild_id.expect("to contain a value"))
|
||||
.await
|
||||
{
|
||||
Some(session) => session,
|
||||
None => {
|
||||
error_edit(
|
||||
"Cannot perform action",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
else {
|
||||
error_edit(
|
||||
"Cannot perform action",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if the session contains an owner
|
||||
let owner = match session.owner().await {
|
||||
Some(owner) => owner,
|
||||
None => {
|
||||
error_edit(
|
||||
"Cannot change playback state",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
let Some(owner) = session.owner().await else {
|
||||
error_edit(
|
||||
"Cannot change playback state",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Get Playback Info from session
|
||||
let pbi = match session.playback_info().await {
|
||||
Some(pbi) => pbi,
|
||||
None => {
|
||||
error_edit(
|
||||
"Cannot change playback state",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
let Some(pbi) = session.playback_info().await else {
|
||||
error_edit(
|
||||
"Cannot change playback state",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if the user is the owner of the session
|
||||
|
@ -244,30 +225,27 @@ pub fn component(ctx: Context, mut interaction: MessageComponentInteraction) ->
|
|||
}
|
||||
|
||||
// Get owner of session
|
||||
let owner = match utils::discord::get_user(&ctx, owner).await {
|
||||
Some(user) => user,
|
||||
None => {
|
||||
// This shouldn't happen
|
||||
let Some(owner) = utils::discord::get_user(&ctx, owner).await else {
|
||||
// This shouldn't happen
|
||||
|
||||
error!("Could not find user with ID: {owner}");
|
||||
error!("Could not find user with ID: {owner}");
|
||||
|
||||
respond_component_message(
|
||||
&ctx,
|
||||
&interaction,
|
||||
EmbedBuilder::new()
|
||||
.title("[INTERNAL ERROR] Cannot get track info")
|
||||
.description(format!(
|
||||
"Could not find user with ID `{}`\nThis is an issue with the bot!",
|
||||
owner
|
||||
))
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
respond_component_message(
|
||||
&ctx,
|
||||
&interaction,
|
||||
EmbedBuilder::new()
|
||||
.title("[INTERNAL ERROR] Cannot get track info")
|
||||
.description(format!(
|
||||
"Could not find user with ID `{}`\nThis is an issue with the bot!",
|
||||
owner
|
||||
))
|
||||
.status(Status::Error)
|
||||
.build(),
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Send the desired command to the session
|
||||
|
@ -370,34 +348,28 @@ async fn update_embed(interaction: &mut MessageComponentInteraction, ctx: &Conte
|
|||
.clone();
|
||||
|
||||
// Check if session still exists
|
||||
let session = match session_manager
|
||||
let Some(session) = session_manager
|
||||
.get_session(interaction.guild_id.expect("to contain a value"))
|
||||
.await
|
||||
{
|
||||
Some(session) => session,
|
||||
None => {
|
||||
error_edit(
|
||||
"Cannot perform action",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
else {
|
||||
error_edit(
|
||||
"Cannot perform action",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
// Get Playback Info from session
|
||||
let pbi = match session.playback_info().await {
|
||||
Some(pbi) => pbi,
|
||||
None => {
|
||||
error_edit(
|
||||
"Cannot change playback state",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
let Some(pbi) = session.playback_info().await else {
|
||||
error_edit(
|
||||
"Cannot change playback state",
|
||||
"I'm currently not playing any music in this server",
|
||||
)
|
||||
.await;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
let (title, description, thumbnail) = get_metadata(&pbi);
|
||||
|
|
|
@ -54,23 +54,22 @@ 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) {
|
||||
match interaction {
|
||||
Interaction::ApplicationCommand(command) => self.handle_command(ctx, command).await,
|
||||
Interaction::MessageComponent(component) => self.handle_component(ctx, component).await,
|
||||
Interaction::ApplicationCommand(command) => handle_command(ctx, command).await,
|
||||
Interaction::MessageComponent(component) => handle_component(ctx, component).await,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
async fn handle_command(&self, ctx: Context, command: ApplicationCommandInteraction) {
|
||||
enforce_guild!(command);
|
||||
async fn handle_command(ctx: Context, command: ApplicationCommandInteraction) {
|
||||
enforce_guild!(command);
|
||||
|
||||
// Commands must only be executed inside of guilds
|
||||
// Commands must only be executed inside of guilds
|
||||
|
||||
let guild_id = match command.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
if let Err(why) = 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)
|
||||
|
@ -82,32 +81,32 @@ impl Handler {
|
|||
error!("Failed to send run-in-guild-only error message: {}", why);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
trace!(
|
||||
"Received command interaction: command={} user={} guild={}",
|
||||
command.data.name,
|
||||
command.user.id,
|
||||
guild_id
|
||||
);
|
||||
trace!(
|
||||
"Received command interaction: command={} user={} guild={}",
|
||||
command.data.name,
|
||||
command.user.id,
|
||||
guild_id
|
||||
);
|
||||
|
||||
let data = ctx.data.read().await;
|
||||
let command_manager = data.get::<CommandManager>().expect("to contain a value");
|
||||
let data = ctx.data.read().await;
|
||||
let command_manager = data.get::<CommandManager>().expect("to contain a value");
|
||||
|
||||
command_manager.execute_command(&ctx, command).await;
|
||||
}
|
||||
command_manager.execute_command(&ctx, command).await;
|
||||
}
|
||||
|
||||
async fn handle_component(&self, ctx: Context, component: MessageComponentInteraction) {
|
||||
enforce_guild!(component);
|
||||
async fn handle_component(ctx: Context, component: MessageComponentInteraction) {
|
||||
enforce_guild!(component);
|
||||
|
||||
// Components can only be interacted with inside of guilds
|
||||
// Components can only be interacted with inside of guilds
|
||||
|
||||
let guild_id = match component.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
if let Err(why) = component
|
||||
let guild_id = match component.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
if let Err(why) = component
|
||||
.create_interaction_response(&ctx.http, |response| {
|
||||
response
|
||||
.kind(serenity::model::prelude::interaction::InteractionResponseType::ChannelMessageWithSource)
|
||||
|
@ -119,20 +118,19 @@ impl Handler {
|
|||
error!("Failed to send run-in-guild-only error message: {}", why);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
trace!(
|
||||
"Received component interaction: command={} user={} guild={}",
|
||||
component.data.custom_id,
|
||||
component.user.id,
|
||||
guild_id
|
||||
);
|
||||
trace!(
|
||||
"Received component interaction: command={} user={} guild={}",
|
||||
component.data.custom_id,
|
||||
component.user.id,
|
||||
guild_id
|
||||
);
|
||||
|
||||
let data = ctx.data.read().await;
|
||||
let command_manager = data.get::<CommandManager>().expect("to contain a value");
|
||||
let data = ctx.data.read().await;
|
||||
let command_manager = data.get::<CommandManager>().expect("to contain a value");
|
||||
|
||||
command_manager.execute_component(&ctx, component).await;
|
||||
}
|
||||
command_manager.execute_component(&ctx, component).await;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use lazy_static::lazy_static;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
|
@ -8,3 +10,18 @@ pub const MOTD: &str = "some good 'ol music";
|
|||
|
||||
/// The time it takes for Spoticord to disconnect when no music is being played
|
||||
pub const DISCONNECT_TIME: u64 = 5 * 60;
|
||||
|
||||
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 SPOTICORD_ACCOUNTS_URL: String = std::env::var("SPOTICORD_ACCOUNTS_URL")
|
||||
.expect("missing SPOTICORD_ACCOUNTS_URL environment variable");
|
||||
}
|
||||
|
||||
#[cfg(feature = "stats")]
|
||||
lazy_static! {
|
||||
pub static ref KV_URL: String =
|
||||
std::env::var("KV_URL").expect("missing KV_URL environment variable");
|
||||
}
|
||||
|
|
33
src/main.rs
33
src/main.rs
|
@ -1,10 +1,18 @@
|
|||
use dotenv::dotenv;
|
||||
|
||||
use crate::{bot::commands::CommandManager, database::Database, session::manager::SessionManager};
|
||||
#[cfg(feature = "stats")]
|
||||
use crate::consts::KV_URL;
|
||||
|
||||
use crate::{
|
||||
bot::commands::CommandManager,
|
||||
consts::{DATABASE_URL, DISCORD_TOKEN, MOTD},
|
||||
database::Database,
|
||||
session::manager::SessionManager,
|
||||
};
|
||||
use log::*;
|
||||
use serenity::{framework::StandardFramework, prelude::GatewayIntents, Client};
|
||||
use songbird::SerenityInit;
|
||||
use std::{any::Any, env, process::exit};
|
||||
use std::{any::Any, process::exit};
|
||||
|
||||
#[cfg(unix)]
|
||||
use tokio::signal::unix::SignalKind;
|
||||
|
@ -41,7 +49,7 @@ async fn main() {
|
|||
env_logger::init();
|
||||
|
||||
info!("It's a good day");
|
||||
info!(" - Spoticord {}", time::OffsetDateTime::now_utc().year());
|
||||
info!(" - Spoticord, {}", MOTD);
|
||||
|
||||
let result = dotenv();
|
||||
|
||||
|
@ -54,19 +62,14 @@ async fn main() {
|
|||
warn!("No .env file found, expecting all necessary environment variables");
|
||||
}
|
||||
|
||||
let token = env::var("DISCORD_TOKEN").expect("a token in the environment");
|
||||
let db_url = env::var("DATABASE_URL").expect("a database URL in the environment");
|
||||
|
||||
#[cfg(feature = "stats")]
|
||||
let stats_manager =
|
||||
StatsManager::new(env::var("KV_URL").expect("a redis URL in the environment"))
|
||||
.expect("Failed to connect to redis");
|
||||
let stats_manager = StatsManager::new(KV_URL.as_str()).expect("Failed to connect to redis");
|
||||
|
||||
let session_manager = SessionManager::new();
|
||||
|
||||
// Create client
|
||||
let mut client = Client::builder(
|
||||
token,
|
||||
DISCORD_TOKEN.as_str(),
|
||||
GatewayIntents::GUILDS | GatewayIntents::GUILD_VOICE_STATES,
|
||||
)
|
||||
.event_handler(crate::bot::events::Handler)
|
||||
|
@ -78,16 +81,13 @@ async fn main() {
|
|||
{
|
||||
let mut data = client.data.write().await;
|
||||
|
||||
data.insert::<Database>(Database::new(db_url, None));
|
||||
data.insert::<Database>(Database::new(DATABASE_URL.as_str(), None));
|
||||
data.insert::<CommandManager>(CommandManager::new());
|
||||
data.insert::<SessionManager>(session_manager.clone());
|
||||
}
|
||||
|
||||
let shard_manager = client.shard_manager.clone();
|
||||
|
||||
#[cfg(feature = "stats")]
|
||||
let cache = client.cache_and_http.cache.clone();
|
||||
|
||||
#[cfg(unix)]
|
||||
let mut term: Option<Box<dyn Any + Send>> = Some(Box::new(
|
||||
tokio::signal::unix::signal(SignalKind::terminate())
|
||||
|
@ -104,13 +104,8 @@ async fn main() {
|
|||
_ = tokio::time::sleep(std::time::Duration::from_secs(60)) => {
|
||||
#[cfg(feature = "stats")]
|
||||
{
|
||||
let guild_count = cache.guilds().len();
|
||||
let active_count = session_manager.get_active_session_count().await;
|
||||
|
||||
if let Err(why) = stats_manager.set_server_count(guild_count) {
|
||||
error!("Failed to update server count: {why}");
|
||||
}
|
||||
|
||||
if let Err(why) = stats_manager.set_active_count(active_count) {
|
||||
error!("Failed to update active count: {why}");
|
||||
}
|
||||
|
|
|
@ -318,13 +318,13 @@ impl PlayerTask {
|
|||
match pbi.as_mut() {
|
||||
Some(pbi) => {
|
||||
pbi.update_track(spotify_id, current);
|
||||
pbi.update_pos_dur(position_ms, duration_ms, true);
|
||||
pbi.update_pos_dur(position_ms, duration_ms, playing);
|
||||
}
|
||||
None => {
|
||||
*pbi = Some(PlaybackInfo::new(
|
||||
duration_ms,
|
||||
position_ms,
|
||||
true,
|
||||
playing,
|
||||
current,
|
||||
spotify_id,
|
||||
));
|
||||
|
|
|
@ -56,11 +56,9 @@ impl InnerSessionManager {
|
|||
session: SpoticordSession,
|
||||
guild_id: GuildId,
|
||||
owner_id: UserId,
|
||||
) -> Result<(), SessionCreateError> {
|
||||
) {
|
||||
self.sessions.insert(guild_id, session);
|
||||
self.owner_map.insert(owner_id, guild_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a session
|
||||
|
@ -147,7 +145,9 @@ impl SessionManager {
|
|||
.write()
|
||||
.await
|
||||
.create_session(session, guild_id, owner_id)
|
||||
.await
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a session
|
||||
|
|
|
@ -36,6 +36,12 @@ use tokio::sync::{Mutex, RwLockReadGuard, RwLockWriteGuard};
|
|||
#[derive(Clone)]
|
||||
pub struct SpoticordSession(Arc<RwLock<InnerSpoticordSession>>);
|
||||
|
||||
impl Drop for SpoticordSession {
|
||||
fn drop(&mut self) {
|
||||
log::trace!("drop SpoticordSession");
|
||||
}
|
||||
}
|
||||
|
||||
struct InnerSpoticordSession {
|
||||
owner: Option<UserId>,
|
||||
guild_id: GuildId,
|
||||
|
@ -97,7 +103,11 @@ impl SpoticordSession {
|
|||
};
|
||||
|
||||
let mut instance = Self(Arc::new(RwLock::new(inner)));
|
||||
instance.create_player(ctx).await?;
|
||||
if let Err(why) = instance.create_player(ctx).await {
|
||||
songbird.remove(guild_id).await.ok();
|
||||
|
||||
return Err(why);
|
||||
}
|
||||
|
||||
let mut call = call.lock().await;
|
||||
|
||||
|
@ -336,6 +346,8 @@ impl SpoticordSession {
|
|||
timer.tick().await;
|
||||
timer.tick().await;
|
||||
|
||||
trace!("Ring ring, time to check :)");
|
||||
|
||||
// Make sure this task has not been aborted, if it has this will automatically stop execution.
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
|
@ -345,6 +357,8 @@ impl SpoticordSession {
|
|||
.map(|pbi| pbi.is_playing)
|
||||
.unwrap_or(false);
|
||||
|
||||
trace!("is_playing = {is_playing}");
|
||||
|
||||
if !is_playing {
|
||||
info!("Player is not playing, disconnecting");
|
||||
session
|
||||
|
@ -499,19 +513,19 @@ impl InnerSpoticordSession {
|
|||
.remove_session(self.guild_id, self.owner)
|
||||
.await;
|
||||
|
||||
let mut call = self.call.lock().await;
|
||||
|
||||
if let Some(track) = self.track.take() {
|
||||
if let Err(why) = track.stop() {
|
||||
error!("Failed to stop track: {:?}", why);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
call.remove_all_global_events();
|
||||
let mut call = self.call.lock().await;
|
||||
|
||||
if let Err(why) = call.leave().await {
|
||||
error!("Failed to leave voice channel: {:?}", why);
|
||||
}
|
||||
|
||||
call.remove_all_global_events();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -521,10 +535,12 @@ impl EventHandler for SpoticordSession {
|
|||
match ctx {
|
||||
EventContext::DriverDisconnect(_) => {
|
||||
debug!("Driver disconnected, leaving voice channel");
|
||||
trace!("Arc strong count: {}", Arc::strong_count(&self.0));
|
||||
self.disconnect().await;
|
||||
}
|
||||
EventContext::ClientDisconnect(who) => {
|
||||
trace!("Client disconnected, {}", who.user_id.to_string());
|
||||
trace!("Arc strong count: {}", Arc::strong_count(&self.0));
|
||||
|
||||
if let Some(session) = self
|
||||
.session_manager()
|
||||
|
@ -545,3 +561,9 @@ impl EventHandler for SpoticordSession {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InnerSpoticordSession {
|
||||
fn drop(&mut self) {
|
||||
log::trace!("drop InnerSpoticordSession");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,6 @@ impl StatsManager {
|
|||
Ok(StatsManager { redis })
|
||||
}
|
||||
|
||||
pub fn set_server_count(&self, count: usize) -> Result<()> {
|
||||
let mut con = self.redis.get_connection()?;
|
||||
|
||||
con.set("sc-bot-total-servers", count.to_string())
|
||||
}
|
||||
|
||||
pub fn set_active_count(&self, count: usize) -> Result<()> {
|
||||
let mut con = self.redis.get_connection()?;
|
||||
|
||||
|
|
Loading…
Reference in New Issue