Make clippy happy, add tokio monitoring
parent
4897e943dd
commit
4253c9f1bb
|
@ -1,2 +1,5 @@
|
||||||
[target.x86_64-pc-windows-gnu]
|
[target.x86_64-pc-windows-gnu]
|
||||||
rustflags = "-C link-args=-lssp" # Does not compile without this line
|
rustflags = "-C link-args=-lssp" # Does not compile without this line
|
||||||
|
|
||||||
|
[build]
|
||||||
|
rustflags = ["--cfg", "tokio_unstable"]
|
|
@ -88,12 +88,39 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.68"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream-impl",
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream-impl"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.60"
|
version = "0.1.60"
|
||||||
|
@ -158,6 +185,52 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"axum-core",
|
||||||
|
"bitflags",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"itoa",
|
||||||
|
"matchit",
|
||||||
|
"memchr",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustversion",
|
||||||
|
"serde",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-core"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"mime",
|
||||||
|
"rustversion",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.67"
|
version = "0.3.67"
|
||||||
|
@ -327,6 +400,42 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console-api"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e57ff02e8ad8e06ab9731d5dc72dc23bef9200778eae1a89d555d8c42e5d4a86"
|
||||||
|
dependencies = [
|
||||||
|
"prost",
|
||||||
|
"prost-types",
|
||||||
|
"tonic",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console-subscriber"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22a3a81dfaf6b66bce5d159eddae701e3a002f194d378cbf7be5f053c281d9be"
|
||||||
|
dependencies = [
|
||||||
|
"console-api",
|
||||||
|
"crossbeam-channel 0.5.6",
|
||||||
|
"crossbeam-utils 0.8.14",
|
||||||
|
"futures",
|
||||||
|
"hdrhistogram",
|
||||||
|
"humantime",
|
||||||
|
"prost-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thread_local",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tonic",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
@ -371,6 +480,16 @@ dependencies = [
|
||||||
"maybe-uninit",
|
"maybe-uninit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"crossbeam-utils 0.8.14",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
|
@ -524,6 +643,12 @@ version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.31"
|
version = "0.8.31"
|
||||||
|
@ -838,6 +963,19 @@ version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hdrhistogram"
|
||||||
|
version = "7.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"byteorder",
|
||||||
|
"flate2",
|
||||||
|
"nom",
|
||||||
|
"num-traits 0.2.15",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "headers"
|
name = "headers"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -930,6 +1068,12 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-range-header"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -1000,6 +1144,18 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-timeout"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
|
||||||
|
dependencies = [
|
||||||
|
"hyper",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-io-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-tls"
|
name = "hyper-tls"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -1092,7 +1248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cb1d9211085f0ea6f1379d944b93c4d07e8207aa3bcf49f37eda12b85081887"
|
checksum = "7cb1d9211085f0ea6f1379d944b93c4d07e8207aa3bcf49f37eda12b85081887"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel 0.4.4",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-test",
|
"futures-test",
|
||||||
|
@ -1112,6 +1268,15 @@ version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e"
|
checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
@ -1435,6 +1600,12 @@ dependencies = [
|
||||||
"regex-automata",
|
"regex-automata",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchit"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maybe-uninit"
|
name = "maybe-uninit"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -1472,6 +1643,12 @@ dependencies = [
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
@ -1584,6 +1761,16 @@ dependencies = [
|
||||||
"memoffset",
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
|
@ -1888,6 +2075,39 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost"
|
||||||
|
version = "0.11.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"prost-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-derive"
|
||||||
|
version = "0.11.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"itertools",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-types"
|
||||||
|
version = "0.11.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"prost",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "protobuf"
|
name = "protobuf"
|
||||||
version = "2.28.0"
|
version = "2.28.0"
|
||||||
|
@ -2528,6 +2748,7 @@ dependencies = [
|
||||||
name = "spoticord"
|
name = "spoticord"
|
||||||
version = "2.0.0-beta"
|
version = "2.0.0-beta"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"console-subscriber",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
|
@ -2593,6 +2814,12 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sync_wrapper"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.3.0"
|
version = "3.3.0"
|
||||||
|
@ -2726,9 +2953,20 @@ dependencies = [
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
|
"tracing",
|
||||||
"windows-sys 0.42.0",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-io-timeout"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "1.8.2"
|
version = "1.8.2"
|
||||||
|
@ -2786,6 +3024,83 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tonic"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream",
|
||||||
|
"async-trait",
|
||||||
|
"axum",
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"h2",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"hyper-timeout",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project",
|
||||||
|
"prost",
|
||||||
|
"prost-derive",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tokio-util",
|
||||||
|
"tower",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
"tracing-futures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"indexmap",
|
||||||
|
"pin-project",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-http"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-range-header",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-layer"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
|
|
@ -8,10 +8,11 @@ name = "spoticord"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
console-subscriber = "0.1.8"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
env_logger = "0.9.3"
|
env_logger = "0.9.3"
|
||||||
ipc-channel = { version = "0.16.0", features = ["async"] }
|
ipc-channel = { version = "0.16.0", features = ["async"] }
|
||||||
librespot = { version = "0.4.2", default-features = false }
|
librespot = { version = "0.4.2", default-features = false }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
redis = "0.22.1"
|
redis = "0.22.1"
|
||||||
reqwest = "0.11.12"
|
reqwest = "0.11.12"
|
||||||
|
|
|
@ -20,4 +20,6 @@ RUN apt-get update && apt-get install -y openssl ca-certificates && rm -rf /var/
|
||||||
# Copy spoticord binary from builder
|
# Copy spoticord binary from builder
|
||||||
COPY --from=builder /usr/local/cargo/bin/spoticord ./spoticord
|
COPY --from=builder /usr/local/cargo/bin/spoticord ./spoticord
|
||||||
|
|
||||||
|
ENV TOKIO_CONSOLE_BIND=0.0.0.0:4567
|
||||||
|
|
||||||
CMD ["./spoticord"]
|
CMD ["./spoticord"]
|
|
@ -42,9 +42,7 @@ impl Sink for StdoutSink {
|
||||||
self
|
self
|
||||||
.output
|
.output
|
||||||
.take()
|
.take()
|
||||||
.ok_or(SinkError::NotConnected(
|
.ok_or_else(|| SinkError::NotConnected("StdoutSink is not connected".to_string()))?
|
||||||
"StdoutSink is not connected".to_string(),
|
|
||||||
))?
|
|
||||||
.flush()
|
.flush()
|
||||||
.map_err(|why| SinkError::OnWrite(why.to_string()))?;
|
.map_err(|why| SinkError::OnWrite(why.to_string()))?;
|
||||||
|
|
||||||
|
@ -62,9 +60,9 @@ impl Sink for StdoutSink {
|
||||||
48000,
|
48000,
|
||||||
2,
|
2,
|
||||||
samplerate::ConverterType::Linear,
|
samplerate::ConverterType::Linear,
|
||||||
&samples_f32,
|
samples_f32,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.expect("to succeed");
|
||||||
|
|
||||||
let samples_i16 =
|
let samples_i16 =
|
||||||
&converter.f64_to_s16(&resampled.iter().map(|v| *v as f64).collect::<Vec<f64>>());
|
&converter.f64_to_s16(&resampled.iter().map(|v| *v as f64).collect::<Vec<f64>>());
|
||||||
|
@ -81,9 +79,7 @@ impl SinkAsBytes for StdoutSink {
|
||||||
self
|
self
|
||||||
.output
|
.output
|
||||||
.as_deref_mut()
|
.as_deref_mut()
|
||||||
.ok_or(SinkError::NotConnected(
|
.ok_or_else(|| SinkError::NotConnected("StdoutSink is not connected".to_string()))?
|
||||||
"StdoutSink is not connected".to_string(),
|
|
||||||
))?
|
|
||||||
.write_all(data)
|
.write_all(data)
|
||||||
.map_err(|why| SinkError::OnWrite(why.to_string()))?;
|
.map_err(|why| SinkError::OnWrite(why.to_string()))?;
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,14 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
EmbedBuilder::new()
|
EmbedBuilder::new()
|
||||||
.title("Spoticord Help")
|
.title("Spoticord Help")
|
||||||
.icon_url("https://spoticord.com/logo-standard.webp")
|
.icon_url("https://spoticord.com/logo-standard.webp")
|
||||||
.description(format!("**Welcome to Spoticord**
|
.description("**Welcome to Spoticord**
|
||||||
It seems you have requested some help. Not to worry, we can help you out.\n
|
It seems you have requested some help. Not to worry, we can help you out.\n
|
||||||
**Not sure how the bot works?**
|
**Not sure how the bot works?**
|
||||||
**[Click here](https://spoticord.com/#how-to)** for a quick overview about how to set up Spoticord and how to use it.\n
|
**[Click here](https://spoticord.com/#how-to)** for a quick overview about how to set up Spoticord and how to use it.\n
|
||||||
**Which commands are there?**
|
**Which commands are there?**
|
||||||
You can find all **[the commands](https://spoticord.com/#commands)** on the website. You may also just type `/` in Discord and see which commands are available there.\n
|
You can find all **[the commands](https://spoticord.com/#commands)** on the website. You may also just type `/` in Discord and see which commands are available there.\n
|
||||||
**Need more help?**
|
**Need more help?**
|
||||||
If you still need some help, whether you are having issues with the bot or you just want to give us some feedback, you can join our **[Discord server](https://discord.gg/wRCyhVqBZ5)**."))
|
If you still need some help, whether you are having issues with the bot or you just want to give us some feedback, you can join our **[Discord server](https://discord.gg/wRCyhVqBZ5)**.".to_string())
|
||||||
.status(Status::Info)
|
.status(Status::Info)
|
||||||
.build(),
|
.build(),
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -17,9 +17,13 @@ pub const NAME: &str = "link";
|
||||||
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let database = data.get::<Database>().unwrap();
|
let database = data.get::<Database>().expect("to contain a value");
|
||||||
|
|
||||||
if let Ok(_) = database.get_user_account(command.user.id.to_string()).await {
|
if database
|
||||||
|
.get_user_account(command.user.id.to_string())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
respond_message(
|
respond_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
&command,
|
&command,
|
||||||
|
@ -35,7 +39,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(request) = database.get_user_request(command.user.id.to_string()).await {
|
if let Ok(request) = database.get_user_request(command.user.id.to_string()).await {
|
||||||
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").unwrap();
|
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").expect("to be present");
|
||||||
let link = format!("{}/spotify/{}", base, request.token);
|
let link = format!("{}/spotify/{}", base, request.token);
|
||||||
|
|
||||||
respond_message(
|
respond_message(
|
||||||
|
@ -102,7 +106,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(request) => {
|
Ok(request) => {
|
||||||
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").unwrap();
|
let base = std::env::var("SPOTICORD_ACCOUNTS_URL").expect("to be present");
|
||||||
let link = format!("{}/spotify/{}", base, request.token);
|
let link = format!("{}/spotify/{}", base, request.token);
|
||||||
|
|
||||||
respond_message(
|
respond_message(
|
||||||
|
@ -121,9 +125,8 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
error!("Error creating user request: {:?}", why);
|
error!("Error creating user request: {:?}", why);
|
||||||
|
|
||||||
|
@ -137,8 +140,6 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub const NAME: &str = "rename";
|
||||||
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let database = data.get::<Database>().unwrap();
|
let database = data.get::<Database>().expect("to contain a value");
|
||||||
|
|
||||||
// Check if user exists, if not, create them
|
// Check if user exists, if not, create them
|
||||||
if let Err(why) = database.get_user(command.user.id.to_string()).await {
|
if let Err(why) = database.get_user(command.user.id.to_string()).await {
|
||||||
|
@ -65,7 +65,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
|
|
||||||
let device_name = match command.data.options.get(0) {
|
let device_name = match command.data.options.get(0) {
|
||||||
Some(option) => match option.value {
|
Some(option) => match option.value {
|
||||||
Some(ref value) => value.as_str().unwrap().to_string(),
|
Some(ref value) => value.as_str().expect("to be a string").to_string(),
|
||||||
None => {
|
None => {
|
||||||
respond_message(
|
respond_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
|
|
|
@ -17,8 +17,8 @@ pub const NAME: &str = "unlink";
|
||||||
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let database = data.get::<Database>().unwrap();
|
let database = data.get::<Database>().expect("to contain a value");
|
||||||
let session_manager = data.get::<SessionManager>().unwrap();
|
let session_manager = data.get::<SessionManager>().expect("to contain a value");
|
||||||
|
|
||||||
// Disconnect session if user has any
|
// Disconnect session if user has any
|
||||||
if let Some(session) = session_manager.find(command.user.id).await {
|
if let Some(session) = session_manager.find(command.user.id).await {
|
||||||
|
|
|
@ -170,7 +170,7 @@ impl CommandManager {
|
||||||
cmds: &HashMap<String, CommandInfo>,
|
cmds: &HashMap<String, CommandInfo>,
|
||||||
mut commands: &'a mut CreateApplicationCommands,
|
mut commands: &'a mut CreateApplicationCommands,
|
||||||
) -> &'a mut CreateApplicationCommands {
|
) -> &'a mut CreateApplicationCommands {
|
||||||
for (_, command_info) in cmds {
|
for command_info in cmds.values() {
|
||||||
commands = commands.create_application_command(|command| (command_info.register)(command));
|
commands = commands.create_application_command(|command| (command_info.register)(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,10 @@ pub const NAME: &str = "join";
|
||||||
|
|
||||||
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let guild = ctx.cache.guild(command.guild_id.unwrap()).unwrap();
|
let guild = ctx
|
||||||
|
.cache
|
||||||
|
.guild(command.guild_id.expect("to contain a value"))
|
||||||
|
.expect("to be present");
|
||||||
|
|
||||||
// Get the voice channel id of the calling user
|
// Get the voice channel id of the calling user
|
||||||
let channel_id = match guild
|
let channel_id = match guild
|
||||||
|
@ -81,8 +84,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(permissions) =
|
if let Ok(permissions) = channel.permissions_for_user(&ctx.cache, ctx.cache.current_user_id())
|
||||||
channel.permissions_for_user(&ctx.cache, &ctx.cache.current_user_id())
|
|
||||||
{
|
{
|
||||||
if !permissions.view_channel() || !permissions.connect() || !permissions.speak() {
|
if !permissions.view_channel() || !permissions.connect() || !permissions.speak() {
|
||||||
respond_message(
|
respond_message(
|
||||||
|
@ -142,8 +144,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(permissions) =
|
if let Ok(permissions) = channel.permissions_for_user(&ctx.cache, ctx.cache.current_user_id())
|
||||||
channel.permissions_for_user(&ctx.cache, &ctx.cache.current_user_id())
|
|
||||||
{
|
{
|
||||||
if !permissions.view_channel() || !permissions.send_messages() || !permissions.embed_links()
|
if !permissions.view_channel() || !permissions.send_messages() || !permissions.embed_links()
|
||||||
{
|
{
|
||||||
|
@ -167,7 +168,10 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let session_manager = data.get::<SessionManager>().unwrap().clone();
|
let session_manager = data
|
||||||
|
.get::<SessionManager>()
|
||||||
|
.expect("to contain a value")
|
||||||
|
.clone();
|
||||||
|
|
||||||
// Check if another session is already active in this server
|
// Check if another session is already active in this server
|
||||||
let mut session_opt = session_manager.get_session(guild.id).await;
|
let mut session_opt = session_manager.get_session(guild.id).await;
|
||||||
|
@ -206,7 +210,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.description(
|
.description(
|
||||||
format!(
|
format!(
|
||||||
"You are already playing music in another server ({}).\nStop playing in that server first before joining this one.",
|
"You are already playing music in another server ({}).\nStop playing in that server first before joining this one.",
|
||||||
ctx.cache.guild(session.guild_id().await).unwrap().name
|
ctx.cache.guild(session.guild_id().await).expect("to be present").name
|
||||||
)).status(Status::Error).build(),
|
)).status(Status::Error).build(),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
@ -230,7 +234,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
if let Some(session) = session_opt.as_mut() {
|
if let Some(session) = session_opt.as_mut() {
|
||||||
if let Err(why) = session.update_owner(&ctx, command.user.id).await {
|
if let Err(why) = session.update_owner(&ctx, command.user.id).await {
|
||||||
// Need to link first
|
// Need to link first
|
||||||
if let SessionCreateError::NoSpotifyError = why {
|
if let SessionCreateError::NoSpotify = why {
|
||||||
update_message(
|
update_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
&command,
|
&command,
|
||||||
|
@ -243,7 +247,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if let SessionCreateError::NoLongerSpotifyError = why {
|
} else if let SessionCreateError::SpotifyExpired = why {
|
||||||
update_message(
|
update_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
&command,
|
&command,
|
||||||
|
@ -284,7 +288,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
// Need to link first
|
// Need to link first
|
||||||
if let SessionCreateError::NoSpotifyError = why {
|
if let SessionCreateError::NoSpotify = why {
|
||||||
update_message(
|
update_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
&command,
|
&command,
|
||||||
|
@ -297,7 +301,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if let SessionCreateError::NoLongerSpotifyError = why {
|
} else if let SessionCreateError::SpotifyExpired = why {
|
||||||
update_message(
|
update_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
&command,
|
&command,
|
||||||
|
|
|
@ -15,9 +15,15 @@ pub const NAME: &str = "leave";
|
||||||
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let session_manager = data.get::<SessionManager>().unwrap().clone();
|
let session_manager = data
|
||||||
|
.get::<SessionManager>()
|
||||||
|
.expect("to contain a value")
|
||||||
|
.clone();
|
||||||
|
|
||||||
let session = match session_manager.get_session(command.guild_id.unwrap()).await {
|
let session = match session_manager
|
||||||
|
.get_session(command.guild_id.expect("to contain a value"))
|
||||||
|
.await
|
||||||
|
{
|
||||||
Some(session) => session,
|
Some(session) => session,
|
||||||
None => {
|
None => {
|
||||||
respond_message(
|
respond_message(
|
||||||
|
|
|
@ -37,9 +37,15 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let session_manager = data.get::<SessionManager>().unwrap().clone();
|
let session_manager = data
|
||||||
|
.get::<SessionManager>()
|
||||||
|
.expect("to contain a value")
|
||||||
|
.clone();
|
||||||
|
|
||||||
let session = match session_manager.get_session(command.guild_id.unwrap()).await {
|
let session = match session_manager
|
||||||
|
.get_session(command.guild_id.expect("to contain a value"))
|
||||||
|
.await
|
||||||
|
{
|
||||||
Some(session) => session,
|
Some(session) => session,
|
||||||
None => {
|
None => {
|
||||||
not_playing.await;
|
not_playing.await;
|
||||||
|
@ -86,8 +92,8 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
// Create title
|
// Create title
|
||||||
let title = format!(
|
let title = format!(
|
||||||
"{} - {}",
|
"{} - {}",
|
||||||
pbi.get_artists().unwrap(),
|
pbi.get_artists().expect("to contain a value"),
|
||||||
pbi.get_name().unwrap()
|
pbi.get_name().expect("to contain a value")
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create description
|
// Create description
|
||||||
|
@ -100,9 +106,9 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
|
|
||||||
for i in 0..20 {
|
for i in 0..20 {
|
||||||
if i == spot {
|
if i == spot {
|
||||||
description.push_str("🔵");
|
description.push('🔵');
|
||||||
} else {
|
} else {
|
||||||
description.push_str("▬");
|
description.push('▬');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +147,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the thumbnail image
|
// Get the thumbnail image
|
||||||
let thumbnail = pbi.get_thumbnail_url().unwrap();
|
let thumbnail = pbi.get_thumbnail_url().expect("to contain a value");
|
||||||
|
|
||||||
if let Err(why) = command
|
if let Err(why) = command
|
||||||
.create_interaction_response(&ctx.http, |response| {
|
.create_interaction_response(&ctx.http, |response| {
|
||||||
|
@ -159,7 +165,9 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.url(format!(
|
.url(format!(
|
||||||
"https://open.spotify.com/{}/{}",
|
"https://open.spotify.com/{}/{}",
|
||||||
audio_type,
|
audio_type,
|
||||||
spotify_id.to_base62().unwrap()
|
spotify_id
|
||||||
|
.to_base62()
|
||||||
|
.expect("to be able to convert to base62")
|
||||||
))
|
))
|
||||||
.description(description)
|
.description(description)
|
||||||
.footer(|footer| footer.text(&owner.name).icon_url(owner.face()))
|
.footer(|footer| footer.text(&owner.name).icon_url(owner.face()))
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.interaction_response_data(|message| message.content("Pong!"))
|
.interaction_response_data(|message| message.content("Pong!"))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub const NAME: &str = "token";
|
||||||
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let db = data.get::<Database>().unwrap();
|
let db = data.get::<Database>().expect("to contain a value");
|
||||||
|
|
||||||
let token = db.get_access_token(command.user.id.to_string()).await;
|
let token = db.get_access_token(command.user.id.to_string()).await;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ pub fn run(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutpu
|
||||||
.interaction_response_data(|message| message.content(content).ephemeral(true))
|
.interaction_response_data(|message| message.content(content).ephemeral(true))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ impl EventHandler for Handler {
|
||||||
// READY event, emitted when the bot/shard starts up
|
// READY event, emitted when the bot/shard starts up
|
||||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let command_manager = data.get::<CommandManager>().unwrap();
|
let command_manager = data.get::<CommandManager>().expect("to contain a value");
|
||||||
|
|
||||||
debug!("Ready received, logged in as {}", ready.user.name);
|
debug!("Ready received, logged in as {}", ready.user.name);
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ impl EventHandler for Handler {
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let command_manager = data.get::<CommandManager>().unwrap();
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,7 +320,7 @@ impl Database {
|
||||||
) -> Result<(), DatabaseError> {
|
) -> Result<(), DatabaseError> {
|
||||||
let device_name: String = name.into();
|
let device_name: String = name.into();
|
||||||
|
|
||||||
if device_name.len() > 16 || device_name.len() < 1 {
|
if device_name.len() > 16 || device_name.is_empty() {
|
||||||
return Err(DatabaseError::InvalidInputBody(
|
return Err(DatabaseError::InvalidInputBody(
|
||||||
"Invalid device name length".into(),
|
"Invalid device name length".into(),
|
||||||
));
|
));
|
||||||
|
@ -345,7 +345,7 @@ impl Database {
|
||||||
StatusCode::OK | StatusCode::CREATED | StatusCode::ACCEPTED | StatusCode::NO_CONTENT => {
|
StatusCode::OK | StatusCode::CREATED | StatusCode::ACCEPTED | StatusCode::NO_CONTENT => {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
status => return Err(DatabaseError::InvalidStatusCode(status)),
|
status => Err(DatabaseError::InvalidStatusCode(status)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,12 +58,12 @@ impl Client {
|
||||||
self
|
self
|
||||||
.tx
|
.tx
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect("to be able to lock")
|
||||||
.send(packet)
|
.send(packet)
|
||||||
.map_err(IpcError::Bincode)
|
.map_err(IpcError::Bincode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_recv(&self) -> Result<IpcPacket, TryRecvError> {
|
pub fn try_recv(&self) -> Result<IpcPacket, TryRecvError> {
|
||||||
self.rx.lock().unwrap().try_recv()
|
self.rx.lock().expect("to be able to lock").try_recv()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -26,6 +26,8 @@ mod utils;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
console_subscriber::init();
|
||||||
|
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
|
@ -42,18 +44,16 @@ async fn main() {
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
if args.len() > 2 {
|
if args.len() > 2 && &args[1] == "--player" {
|
||||||
if &args[1] == "--player" {
|
// Woah! We're running in player mode!
|
||||||
// Woah! We're running in player mode!
|
|
||||||
|
|
||||||
debug!("Starting Spoticord player");
|
debug!("Starting Spoticord player");
|
||||||
|
|
||||||
player::main().await;
|
player::main().await;
|
||||||
|
|
||||||
debug!("Player exited, shutting down");
|
debug!("Player exited, shutting down");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("It's a good day");
|
info!("It's a good day");
|
||||||
|
@ -62,7 +62,10 @@ async fn main() {
|
||||||
let result = dotenv();
|
let result = dotenv();
|
||||||
|
|
||||||
if let Ok(path) = result {
|
if let Ok(path) = result {
|
||||||
debug!("Loaded environment file: {}", path.to_str().unwrap());
|
debug!(
|
||||||
|
"Loaded environment file: {}",
|
||||||
|
path.to_str().expect("to get the string")
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
warn!("No .env file found, expecting all necessary environment variables");
|
warn!("No .env file found, expecting all necessary environment variables");
|
||||||
}
|
}
|
||||||
|
@ -83,7 +86,7 @@ async fn main() {
|
||||||
.framework(StandardFramework::new())
|
.framework(StandardFramework::new())
|
||||||
.register_songbird()
|
.register_songbird()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.expect("to create a client");
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut data = client.data.write().await;
|
let mut data = client.data.write().await;
|
||||||
|
@ -98,7 +101,8 @@ async fn main() {
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let mut term: Option<Box<dyn Any + Send>> = Some(Box::new(
|
let mut term: Option<Box<dyn Any + Send>> = Some(Box::new(
|
||||||
tokio::signal::unix::signal(SignalKind::terminate()).unwrap(),
|
tokio::signal::unix::signal(SignalKind::terminate())
|
||||||
|
.expect("to be able to create the signal stream"),
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
|
@ -145,7 +149,7 @@ async fn main() {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
match term {
|
match term {
|
||||||
Some(ref mut term) => {
|
Some(ref mut term) => {
|
||||||
let term = term.downcast_mut::<tokio::signal::unix::Signal>().unwrap();
|
let term = term.downcast_mut::<tokio::signal::unix::Signal>().expect("to be able to downcast");
|
||||||
|
|
||||||
term.recv().await
|
term.recv().await
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,9 @@ impl SpoticordPlayer {
|
||||||
let token = token.into();
|
let token = token.into();
|
||||||
|
|
||||||
// Get the username (required for librespot)
|
// Get the username (required for librespot)
|
||||||
let username = utils::spotify::get_username(&token).await.unwrap();
|
let username = utils::spotify::get_username(&token)
|
||||||
|
.await
|
||||||
|
.expect("to get the username");
|
||||||
|
|
||||||
let session_config = SessionConfig::default();
|
let session_config = SessionConfig::default();
|
||||||
let player_config = PlayerConfig {
|
let player_config = PlayerConfig {
|
||||||
|
@ -68,7 +70,7 @@ impl SpoticordPlayer {
|
||||||
self
|
self
|
||||||
.client
|
.client
|
||||||
.send(IpcPacket::ConnectError(why.to_string()))
|
.send(IpcPacket::ConnectError(why.to_string()))
|
||||||
.unwrap();
|
.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -77,7 +79,7 @@ impl SpoticordPlayer {
|
||||||
self.session = Some(session.clone());
|
self.session = Some(session.clone());
|
||||||
|
|
||||||
// Volume mixer
|
// Volume mixer
|
||||||
let mixer = (mixer::find(Some("softvol")).unwrap())(MixerConfig {
|
let mixer = (mixer::find(Some("softvol")).expect("to exist"))(MixerConfig {
|
||||||
volume_ctrl: librespot::playback::config::VolumeCtrl::Linear,
|
volume_ctrl: librespot::playback::config::VolumeCtrl::Linear,
|
||||||
..MixerConfig::default()
|
..MixerConfig::default()
|
||||||
});
|
});
|
||||||
|
@ -138,13 +140,13 @@ impl SpoticordPlayer {
|
||||||
.send(IpcPacket::ConnectError(
|
.send(IpcPacket::ConnectError(
|
||||||
"Switch to Spoticord device timed out".to_string(),
|
"Switch to Spoticord device timed out".to_string(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.ok();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
error!("Failed to set device: {}", why);
|
error!("Failed to set device: {}", why);
|
||||||
ipc.send(IpcPacket::ConnectError(why.to_string())).unwrap();
|
ipc.send(IpcPacket::ConnectError(why.to_string())).ok();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +169,7 @@ impl SpoticordPlayer {
|
||||||
duration_ms,
|
duration_ms,
|
||||||
} => {
|
} => {
|
||||||
if let Err(why) = ipc.send(IpcPacket::Playing(
|
if let Err(why) = ipc.send(IpcPacket::Playing(
|
||||||
track_id.to_uri().unwrap(),
|
track_id.to_uri().expect("to not fail"),
|
||||||
position_ms,
|
position_ms,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
)) {
|
)) {
|
||||||
|
@ -182,7 +184,7 @@ impl SpoticordPlayer {
|
||||||
duration_ms,
|
duration_ms,
|
||||||
} => {
|
} => {
|
||||||
if let Err(why) = ipc.send(IpcPacket::Paused(
|
if let Err(why) = ipc.send(IpcPacket::Paused(
|
||||||
track_id.to_uri().unwrap(),
|
track_id.to_uri().expect("to not fail"),
|
||||||
position_ms,
|
position_ms,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
)) {
|
)) {
|
||||||
|
@ -194,7 +196,9 @@ impl SpoticordPlayer {
|
||||||
old_track_id: _,
|
old_track_id: _,
|
||||||
new_track_id,
|
new_track_id,
|
||||||
} => {
|
} => {
|
||||||
if let Err(why) = ipc.send(IpcPacket::TrackChange(new_track_id.to_uri().unwrap())) {
|
if let Err(why) = ipc.send(IpcPacket::TrackChange(
|
||||||
|
new_track_id.to_uri().expect("to not fail"),
|
||||||
|
)) {
|
||||||
error!("Failed to send track change packet: {}", why);
|
error!("Failed to send track change packet: {}", why);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,11 +251,9 @@ pub async fn main() {
|
||||||
tokio::time::sleep(Duration::from_millis(25)).await;
|
tokio::time::sleep(Duration::from_millis(25)).await;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else if let TryRecvError::IpcError(why) = &why {
|
} else if let TryRecvError::IpcError(IpcError::Disconnected) = &why {
|
||||||
if let IpcError::Disconnected = why {
|
debug!("IPC connection closed, goodbye");
|
||||||
debug!("IPC connection closed, goodbye");
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error!("Failed to receive message: {}", why);
|
error!("Failed to receive message: {}", why);
|
||||||
|
|
|
@ -11,13 +11,13 @@ use super::SpoticordSession;
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum SessionCreateError {
|
pub enum SessionCreateError {
|
||||||
#[error("This session has no owner assigned")]
|
#[error("This session has no owner assigned")]
|
||||||
NoOwnerError,
|
NoOwner,
|
||||||
|
|
||||||
#[error("The user has not linked their Spotify account")]
|
#[error("The user has not linked their Spotify account")]
|
||||||
NoSpotifyError,
|
NoSpotify,
|
||||||
|
|
||||||
#[error("The application no longer has access to the user's Spotify account")]
|
#[error("The application no longer has access to the user's Spotify account")]
|
||||||
NoLongerSpotifyError,
|
SpotifyExpired,
|
||||||
|
|
||||||
#[error("An error has occured while communicating with the database")]
|
#[error("An error has occured while communicating with the database")]
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
|
@ -99,7 +99,7 @@ impl InnerSessionManager {
|
||||||
pub fn find(&self, owner_id: UserId) -> Option<SpoticordSession> {
|
pub fn find(&self, owner_id: UserId) -> Option<SpoticordSession> {
|
||||||
let guild_id = self.owner_map.get(&owner_id)?;
|
let guild_id = self.owner_map.get(&owner_id)?;
|
||||||
|
|
||||||
self.sessions.get(&guild_id).cloned()
|
self.sessions.get(guild_id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the amount of sessions
|
/// Get the amount of sessions
|
||||||
|
|
|
@ -70,10 +70,13 @@ impl SpoticordSession {
|
||||||
) -> Result<SpoticordSession, SessionCreateError> {
|
) -> Result<SpoticordSession, SessionCreateError> {
|
||||||
// Get the Spotify token of the owner
|
// Get the Spotify token of the owner
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let session_manager = data.get::<SessionManager>().unwrap().clone();
|
let session_manager = data
|
||||||
|
.get::<SessionManager>()
|
||||||
|
.expect("to contain a value")
|
||||||
|
.clone();
|
||||||
|
|
||||||
// Join the voice channel
|
// Join the voice channel
|
||||||
let songbird = songbird::get(ctx).await.unwrap().clone();
|
let songbird = songbird::get(ctx).await.expect("to be present").clone();
|
||||||
|
|
||||||
let (call, result) = songbird.join(guild_id, channel_id).await;
|
let (call, result) = songbird.join(guild_id, channel_id).await;
|
||||||
|
|
||||||
|
@ -83,7 +86,7 @@ impl SpoticordSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner = InnerSpoticordSession {
|
let inner = InnerSpoticordSession {
|
||||||
owner: Some(owner_id.clone()),
|
owner: Some(owner_id),
|
||||||
guild_id,
|
guild_id,
|
||||||
channel_id,
|
channel_id,
|
||||||
text_channel_id,
|
text_channel_id,
|
||||||
|
@ -124,7 +127,10 @@ impl SpoticordSession {
|
||||||
) -> Result<(), SessionCreateError> {
|
) -> Result<(), SessionCreateError> {
|
||||||
// Get the Spotify token of the owner
|
// Get the Spotify token of the owner
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let session_manager = data.get::<SessionManager>().unwrap().clone();
|
let session_manager = data
|
||||||
|
.get::<SessionManager>()
|
||||||
|
.expect("to contain a value")
|
||||||
|
.clone();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut inner = self.0.write().await;
|
let mut inner = self.0.write().await;
|
||||||
|
@ -144,22 +150,22 @@ impl SpoticordSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_player(&mut self, ctx: &Context) -> Result<(), SessionCreateError> {
|
async fn create_player(&mut self, ctx: &Context) -> Result<(), SessionCreateError> {
|
||||||
let owner_id = match self.owner().await.clone() {
|
let owner_id = match self.owner().await {
|
||||||
Some(owner_id) => owner_id,
|
Some(owner_id) => owner_id,
|
||||||
None => return Err(SessionCreateError::NoOwnerError),
|
None => return Err(SessionCreateError::NoOwner),
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let database = data.get::<Database>().unwrap();
|
let database = data.get::<Database>().expect("to contain a value");
|
||||||
|
|
||||||
let token = match database.get_access_token(owner_id.to_string()).await {
|
let token = match database.get_access_token(owner_id.to_string()).await {
|
||||||
Ok(token) => token,
|
Ok(token) => token,
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
if let DatabaseError::InvalidStatusCode(code) = why {
|
if let DatabaseError::InvalidStatusCode(code) = why {
|
||||||
if code == 404 {
|
if code == 404 {
|
||||||
return Err(SessionCreateError::NoSpotifyError);
|
return Err(SessionCreateError::NoSpotify);
|
||||||
} else if code == 400 {
|
} else if code == 400 {
|
||||||
return Err(SessionCreateError::NoLongerSpotifyError);
|
return Err(SessionCreateError::SpotifyExpired);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,24 +191,25 @@ impl SpoticordSession {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Spawn player process
|
// Spawn player process
|
||||||
let child = match Command::new(std::env::current_exe().unwrap())
|
let child =
|
||||||
.args([
|
match Command::new(std::env::current_exe().expect("to know the current executable path"))
|
||||||
"--player",
|
.args([
|
||||||
&tx_name,
|
"--player",
|
||||||
&rx_name,
|
&tx_name,
|
||||||
"--debug-guild-id",
|
&rx_name,
|
||||||
&self.guild_id().await.to_string(),
|
"--debug-guild-id",
|
||||||
])
|
&self.guild_id().await.to_string(),
|
||||||
.stdout(Stdio::piped())
|
])
|
||||||
.stderr(Stdio::inherit())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.stderr(Stdio::inherit())
|
||||||
{
|
.spawn()
|
||||||
Ok(child) => child,
|
{
|
||||||
Err(why) => {
|
Ok(child) => child,
|
||||||
error!("Failed to start player process: {:?}", why);
|
Err(why) => {
|
||||||
return Err(SessionCreateError::ForkError);
|
error!("Failed to start player process: {:?}", why);
|
||||||
}
|
return Err(SessionCreateError::ForkError);
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Establish bi-directional IPC channel
|
// Establish bi-directional IPC channel
|
||||||
let client = match server.accept() {
|
let client = match server.accept() {
|
||||||
|
@ -256,11 +263,9 @@ impl SpoticordSession {
|
||||||
tokio::time::sleep(Duration::from_millis(25)).await;
|
tokio::time::sleep(Duration::from_millis(25)).await;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else if let TryRecvError::IpcError(why) = &why {
|
} else if let TryRecvError::IpcError(IpcError::Disconnected) = &why {
|
||||||
if let IpcError::Disconnected = why {
|
trace!("IPC connection closed, exiting IPC handler");
|
||||||
trace!("IPC connection closed, exiting IPC handler");
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error!("Failed to receive IPC message: {:?}", why);
|
error!("Failed to receive IPC message: {:?}", why);
|
||||||
|
@ -310,7 +315,7 @@ impl SpoticordSession {
|
||||||
// A new track has been set by the player
|
// A new track has been set by the player
|
||||||
IpcPacket::TrackChange(track) => {
|
IpcPacket::TrackChange(track) => {
|
||||||
// Convert to SpotifyId
|
// Convert to SpotifyId
|
||||||
let track_id = SpotifyId::from_uri(&track).unwrap();
|
let track_id = SpotifyId::from_uri(&track).expect("to be a valid uri");
|
||||||
|
|
||||||
let instance = instance.clone();
|
let instance = instance.clone();
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
|
@ -329,7 +334,7 @@ impl SpoticordSession {
|
||||||
// The player has started playing a track
|
// The player has started playing a track
|
||||||
IpcPacket::Playing(track, position_ms, duration_ms) => {
|
IpcPacket::Playing(track, position_ms, duration_ms) => {
|
||||||
// Convert to SpotifyId
|
// Convert to SpotifyId
|
||||||
let track_id = SpotifyId::from_uri(&track).unwrap();
|
let track_id = SpotifyId::from_uri(&track).expect("to be a valid uri");
|
||||||
|
|
||||||
let was_none = instance
|
let was_none = instance
|
||||||
.update_playback(duration_ms, position_ms, true)
|
.update_playback(duration_ms, position_ms, true)
|
||||||
|
@ -350,7 +355,7 @@ impl SpoticordSession {
|
||||||
instance.start_disconnect_timer().await;
|
instance.start_disconnect_timer().await;
|
||||||
|
|
||||||
// Convert to SpotifyId
|
// Convert to SpotifyId
|
||||||
let track_id = SpotifyId::from_uri(&track).unwrap();
|
let track_id = SpotifyId::from_uri(&track).expect("to be a valid uri");
|
||||||
|
|
||||||
let was_none = instance
|
let was_none = instance
|
||||||
.update_playback(duration_ms, position_ms, false)
|
.update_playback(duration_ms, position_ms, false)
|
||||||
|
@ -415,7 +420,7 @@ impl SpoticordSession {
|
||||||
let pbi = self.playback_info().await;
|
let pbi = self.playback_info().await;
|
||||||
|
|
||||||
if let Some(pbi) = pbi {
|
if let Some(pbi) = pbi {
|
||||||
pbi.spotify_id.is_none() || pbi.spotify_id.unwrap() != spotify_id
|
pbi.spotify_id.is_none() || pbi.spotify_id != Some(spotify_id)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -426,7 +431,7 @@ impl SpoticordSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let database = data.get::<Database>().unwrap();
|
let database = data.get::<Database>().expect("to contain a value");
|
||||||
|
|
||||||
let token = match database.get_access_token(&owner_id.to_string()).await {
|
let token = match database.get_access_token(&owner_id.to_string()).await {
|
||||||
Ok(token) => token,
|
Ok(token) => token,
|
||||||
|
@ -549,7 +554,7 @@ impl SpoticordSession {
|
||||||
inner
|
inner
|
||||||
.playback_info
|
.playback_info
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.expect("to contain a value")
|
||||||
.update_pos_dur(position_ms, duration_ms, playing);
|
.update_pos_dur(position_ms, duration_ms, playing);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -66,10 +66,8 @@ impl PlaybackInfo {
|
||||||
pub fn get_name(&self) -> Option<String> {
|
pub fn get_name(&self) -> Option<String> {
|
||||||
if let Some(track) = &self.track {
|
if let Some(track) = &self.track {
|
||||||
Some(track.name.clone())
|
Some(track.name.clone())
|
||||||
} else if let Some(episode) = &self.episode {
|
|
||||||
Some(episode.name.clone())
|
|
||||||
} else {
|
} else {
|
||||||
None
|
self.episode.as_ref().map(|episode| episode.name.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,10 +82,11 @@ impl PlaybackInfo {
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", "),
|
.join(", "),
|
||||||
)
|
)
|
||||||
} else if let Some(episode) = &self.episode {
|
|
||||||
Some(episode.show.name.clone())
|
|
||||||
} else {
|
} else {
|
||||||
None
|
self
|
||||||
|
.episode
|
||||||
|
.as_ref()
|
||||||
|
.map(|episode| episode.show.name.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,12 +96,12 @@ impl PlaybackInfo {
|
||||||
let mut images = track.album.images.clone();
|
let mut images = track.album.images.clone();
|
||||||
images.sort_by(|a, b| b.width.cmp(&a.width));
|
images.sort_by(|a, b| b.width.cmp(&a.width));
|
||||||
|
|
||||||
Some(images.get(0).unwrap().url.clone())
|
images.get(0).as_ref().map(|image| image.url.clone())
|
||||||
} else if let Some(episode) = &self.episode {
|
} else if let Some(episode) = &self.episode {
|
||||||
let mut images = episode.show.images.clone();
|
let mut images = episode.show.images.clone();
|
||||||
images.sort_by(|a, b| b.width.cmp(&a.width));
|
images.sort_by(|a, b| b.width.cmp(&a.width));
|
||||||
|
|
||||||
Some(images.get(0).unwrap().url.clone())
|
images.get(0).as_ref().map(|image| image.url.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@ pub fn escape(text: impl Into<String>) -> String {
|
||||||
let text: String = text.into();
|
let text: String = text.into();
|
||||||
|
|
||||||
text
|
text
|
||||||
.replace("\\", "\\\\")
|
.replace('\\', "\\\\")
|
||||||
.replace("*", "\\*")
|
.replace('*', "\\*")
|
||||||
.replace("_", "\\_")
|
.replace('_', "\\_")
|
||||||
.replace("~", "\\~")
|
.replace('~', "\\~")
|
||||||
.replace("`", "\\`")
|
.replace('`', "\\`")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_user(ctx: &Context, id: UserId) -> Option<User> {
|
pub async fn get_user(ctx: &Context, id: UserId) -> Option<User> {
|
||||||
|
|
|
@ -65,10 +65,10 @@ impl EmbedBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_embed_message<'a>(
|
pub fn make_embed_message(
|
||||||
embed: &'a mut CreateEmbed,
|
embed: &'_ mut CreateEmbed,
|
||||||
options: EmbedMessageOptions,
|
options: EmbedMessageOptions,
|
||||||
) -> &'a mut CreateEmbed {
|
) -> &'_ mut CreateEmbed {
|
||||||
let status = options.status.unwrap_or(Status::None);
|
let status = options.status.unwrap_or(Status::None);
|
||||||
|
|
||||||
embed.author(|author| {
|
embed.author(|author| {
|
||||||
|
|
|
@ -23,15 +23,15 @@ pub fn time_to_str(time: u32) -> String {
|
||||||
let min = 60;
|
let min = 60;
|
||||||
|
|
||||||
if time / hour >= 1 {
|
if time / hour >= 1 {
|
||||||
return format!(
|
format!(
|
||||||
"{}h{}m{}s",
|
"{}h{}m{}s",
|
||||||
time / hour,
|
time / hour,
|
||||||
(time % hour) / min,
|
(time % hour) / min,
|
||||||
(time % hour) % min
|
(time % hour) % min
|
||||||
);
|
)
|
||||||
} else if time / min >= 1 {
|
} else if time / min >= 1 {
|
||||||
return format!("{}m{}s", time / min, time % min);
|
format!("{}m{}s", time / min, time % min)
|
||||||
} else {
|
} else {
|
||||||
return format!("{}s", time);
|
format!("{}s", time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,13 +69,10 @@ pub async fn get_username(token: impl Into<String>) -> Result<String, String> {
|
||||||
|
|
||||||
if response.status() != 200 {
|
if response.status() != 200 {
|
||||||
error!("Failed to get username: {}", response.status());
|
error!("Failed to get username: {}", response.status());
|
||||||
return Err(
|
return Err(format!(
|
||||||
format!(
|
"Failed to get track info: Invalid status code: {}",
|
||||||
"Failed to get track info: Invalid status code: {}",
|
response.status()
|
||||||
response.status()
|
));
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let body: Value = match response.json().await {
|
let body: Value = match response.json().await {
|
||||||
|
|
Loading…
Reference in New Issue