Added spotify link functionality
This commit is contained in:
120
Cargo.lock
generated
120
Cargo.lock
generated
@ -713,6 +713,18 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum_dispatch"
|
||||||
|
version = "0.3.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@ -979,8 +991,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1766,6 +1780,17 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maybe-async"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maybe-rayon"
|
name = "maybe-rayon"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -2574,6 +2599,7 @@ dependencies = [
|
|||||||
"system-configuration 0.6.1",
|
"system-configuration 0.6.1",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
|
"tokio-socks",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
@ -2638,6 +2664,64 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rspotify"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77beedc33ecff4c39e8ef0e6f7ebc8d849f3ffebbeb786f9997d96f0d9cf4017"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream",
|
||||||
|
"async-trait",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"chrono",
|
||||||
|
"futures",
|
||||||
|
"getrandom 0.2.16",
|
||||||
|
"log",
|
||||||
|
"maybe-async",
|
||||||
|
"rspotify-http",
|
||||||
|
"rspotify-macros",
|
||||||
|
"rspotify-model",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rspotify-http"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fde1ea9e2a49698cffbc994a83f5f909b37736c31cccb202f9577e8a32df3a63"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"log",
|
||||||
|
"maybe-async",
|
||||||
|
"reqwest 0.12.15",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rspotify-macros"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee3dfb51ee54bd754ad76e96ad60a3b64bc70ae33a89261d9dbabc4c148a496f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rspotify-model"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "018f29a6a8c47cfe7923c48140ed546a395f660c7af05b73e6001d4505f89c8d"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"enum_dispatch",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"strum",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
@ -3347,6 +3431,27 @@ dependencies = [
|
|||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@ -3684,6 +3789,18 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-socks"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"futures-util",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.17"
|
version = "0.1.17"
|
||||||
@ -4153,7 +4270,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "watcat"
|
name = "watcat"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arabic_reshaper",
|
"arabic_reshaper",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
@ -4165,6 +4282,7 @@ dependencies = [
|
|||||||
"palette",
|
"palette",
|
||||||
"reqwest 0.12.15",
|
"reqwest 0.12.15",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
|
"rspotify",
|
||||||
"serenity",
|
"serenity",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "watcat"
|
name = "watcat"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -11,6 +11,7 @@ reqwest = "0.12"
|
|||||||
reqwest-middleware = "0.4.2"
|
reqwest-middleware = "0.4.2"
|
||||||
http-cache-reqwest = "0.15.1"
|
http-cache-reqwest = "0.15.1"
|
||||||
palette = "0.7.6"
|
palette = "0.7.6"
|
||||||
|
rspotify = "0.15.0"
|
||||||
# text rendering dependencies
|
# text rendering dependencies
|
||||||
arabic_reshaper = "0.4.2"
|
arabic_reshaper = "0.4.2"
|
||||||
unicode-bidi = "0.3.18"
|
unicode-bidi = "0.3.18"
|
||||||
|
|||||||
@ -30,7 +30,9 @@ TODO
|
|||||||
|
|
||||||
+ ~~add art and lastfm url fetching~~
|
+ ~~add art and lastfm url fetching~~
|
||||||
+ add help command
|
+ add help command
|
||||||
+ refactor to combine repeated parts of art, url, and fmi. also use macro for commands, and use Option instead of empty string sentinel value
|
+ ~~refactor to combine repeated parts of art, url, and fmi.~~
|
||||||
+ add spotify link fetching
|
+ use macro for commands
|
||||||
|
+ use Option instead of empty string sentinel value
|
||||||
|
+ ~~add spotify link fetching~~
|
||||||
+ make various parameters user-configurable
|
+ make various parameters user-configurable
|
||||||
+ use tiny-skia instead of magick-rust?
|
+ use tiny-skia instead of magick-rust?
|
||||||
|
|||||||
108
src/main.rs
108
src/main.rs
@ -14,7 +14,8 @@ extern crate magick_rust;
|
|||||||
use magick_rust::{ColorspaceType, CompositeOperator, MagickWand, PixelWand, magick_wand_genesis, FilterType};
|
use magick_rust::{ColorspaceType, CompositeOperator, MagickWand, PixelWand, magick_wand_genesis, FilterType};
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::sync::Once;
|
use std::sync::{Once, Arc};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use palette::color_difference::ImprovedCiede2000;
|
use palette::color_difference::ImprovedCiede2000;
|
||||||
use palette::lab::Lab;
|
use palette::lab::Lab;
|
||||||
@ -23,6 +24,9 @@ use palette::white_point::D65;
|
|||||||
extern crate sqlx;
|
extern crate sqlx;
|
||||||
use sqlx::sqlite::SqlitePool;
|
use sqlx::sqlite::SqlitePool;
|
||||||
|
|
||||||
|
use rspotify::{ClientCredsSpotify, Credentials, clients::BaseClient,};
|
||||||
|
use rspotify::model::{enums::types::SearchType, search::SearchResult};
|
||||||
|
|
||||||
use imagetext::fontdb::FontDB;
|
use imagetext::fontdb::FontDB;
|
||||||
use imagetext::superfont::SuperFont;
|
use imagetext::superfont::SuperFont;
|
||||||
|
|
||||||
@ -114,23 +118,31 @@ fn validate_color(col: &PixelWand) -> Option<Lab> {
|
|||||||
Some(Lab::<D65>::from_components(color_raw))
|
Some(Lab::<D65>::from_components(color_raw))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn art(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
async fn get_track(ctx: &Context, arg: &str, id: UserId) -> Result<lastfm::track::NowPlayingTrack, Reply> {
|
||||||
let lastfm_user = match arg {
|
let lastfm_user = match arg {
|
||||||
"" => get_lastfm_username(ctx, id).await,
|
"" => get_lastfm_username(ctx, id).await,
|
||||||
_ => Some(arg.to_string()),
|
_ => Some(arg.to_string()),
|
||||||
};
|
};
|
||||||
let lastfm_client = match lastfm_user {
|
let lastfm_client = match lastfm_user {
|
||||||
Some(s) => lastfm::Client::<String, String>::from_env(s),
|
Some(s) => lastfm::Client::<String, String>::from_env(s),
|
||||||
None => return Reply::Text("No last.fm username set.".to_string()),
|
None => return Err(Reply::Text("No last.fm username set.".to_string())),
|
||||||
};
|
};
|
||||||
let now_playing = match lastfm_client.now_playing().await {
|
let now_playing = match lastfm_client.now_playing().await {
|
||||||
Ok(np) => np,
|
Ok(np) => np,
|
||||||
Err(e) => return Reply::Text(format!("Error: grabbing last.fm user data failed {e}")),
|
Err(e) => return Err(Reply::Text(format!("Error: grabbing last.fm user data failed {e}"))),
|
||||||
};
|
};
|
||||||
let track = match now_playing {
|
let track = match now_playing {
|
||||||
Some(track) => track,
|
Some(track) => track,
|
||||||
None => return Reply::Text("Nothing playing.".to_string()),
|
None => return Err(Reply::Text("Nothing playing.".to_string())),
|
||||||
};
|
};
|
||||||
|
Ok(track)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn art(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
||||||
|
let track = match get_track(ctx, arg, id).await {
|
||||||
|
Ok(track) => track,
|
||||||
|
Err(e) => return e,
|
||||||
|
};
|
||||||
let track_art = match track.image.extralarge {
|
let track_art = match track.image.extralarge {
|
||||||
Some(track_art) => track_art,
|
Some(track_art) => track_art,
|
||||||
None => return Reply::Text("Error: getting image uri failed".to_string()),
|
None => return Reply::Text("Error: getting image uri failed".to_string()),
|
||||||
@ -139,42 +151,44 @@ async fn art(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn url(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
async fn url(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
||||||
let lastfm_user = match arg {
|
let track = match get_track(ctx, arg, id).await {
|
||||||
"" => get_lastfm_username(ctx, id).await,
|
Ok(track) => track,
|
||||||
_ => Some(arg.to_string()),
|
Err(e) => return e,
|
||||||
};
|
};
|
||||||
let lastfm_client = match lastfm_user {
|
|
||||||
Some(s) => lastfm::Client::<String, String>::from_env(s),
|
|
||||||
None => return Reply::Text("No last.fm username set.".to_string()),
|
|
||||||
};
|
|
||||||
let now_playing = match lastfm_client.now_playing().await {
|
|
||||||
Ok(np) => np,
|
|
||||||
Err(e) => return Reply::Text(format!("Error: grabbing last.fm user data failed {e}")),
|
|
||||||
};
|
|
||||||
let track = match now_playing {
|
|
||||||
Some(track) => track,
|
|
||||||
None => return Reply::Text("Nothing playing.".to_string()),
|
|
||||||
};
|
|
||||||
Reply::Text(track.url)
|
Reply::Text(track.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn spot(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
||||||
|
let track = match get_track(ctx, arg, id).await {
|
||||||
|
Ok(track) => track,
|
||||||
|
Err(e) => return e,
|
||||||
|
};
|
||||||
|
let mut data = ctx.data.write().await;
|
||||||
|
let spotify_option = data.get_mut::<SpotifyHaver>().expect("Failed to have spotify option");
|
||||||
|
if spotify_option.is_none() {
|
||||||
|
return Reply::Text("Unable to use Spotify command: Contact bot Administrator.".to_owned())
|
||||||
|
}
|
||||||
|
let spotify_arc_mutex = spotify_option.as_mut().unwrap();
|
||||||
|
//let spotify_arc_mutex_ref = Arc::clone(&spotify_arc_mutex);
|
||||||
|
let spotify_client = spotify_arc_mutex.lock().await;
|
||||||
|
spotify_client.request_token().await.unwrap();
|
||||||
|
let search = spotify_client.search(format!("{} {}", track.name, track.artist.name).as_str(), SearchType::Track, None, None, None, None).await;
|
||||||
|
let tracks = match search {
|
||||||
|
Ok(SearchResult::Tracks(track_page)) => track_page,
|
||||||
|
Err(e) => return Reply::Text(format!("Failed to get track {e}")),
|
||||||
|
_ => return Reply::Text("Spotify search failed".to_owned()),
|
||||||
|
};
|
||||||
|
match &tracks.items[0].external_urls.get("spotify") {
|
||||||
|
Some(url) => Reply::Text(url.to_owned().to_owned()),
|
||||||
|
None => Reply::Text("Unable to get spotify url".to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option<String>) -> Reply {
|
async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option<String>) -> Reply {
|
||||||
let lastfm_user = match arg {
|
let track = match get_track(ctx, arg, id).await {
|
||||||
"" => get_lastfm_username(ctx, id).await,
|
Ok(track) => track,
|
||||||
_ => Some(arg.to_string()),
|
Err(e) => return e,
|
||||||
};
|
};
|
||||||
let lastfm_client = match lastfm_user {
|
|
||||||
Some(s) => lastfm::Client::<String, String>::from_env(s),
|
|
||||||
None => return Reply::Text("No last.fm username set.".to_string()),
|
|
||||||
};
|
|
||||||
let now_playing = match lastfm_client.now_playing().await {
|
|
||||||
Ok(np) => np,
|
|
||||||
Err(e) => return Reply::Text(format!("Error: grabbing last.fm user data failed {e}")),
|
|
||||||
};
|
|
||||||
let track = match now_playing {
|
|
||||||
Some(track) => track,
|
|
||||||
None => return Reply::Text("Nothing playing.".to_string()),
|
|
||||||
};
|
|
||||||
let track_info = text::TrackInfo {
|
let track_info = text::TrackInfo {
|
||||||
title: track.name,
|
title: track.name,
|
||||||
artist: track.artist.name,
|
artist: track.artist.name,
|
||||||
@ -257,7 +271,6 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option<String>) -> Re
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let use_dark_color = match validate_color(&colors[0]) {
|
let use_dark_color = match validate_color(&colors[0]) {
|
||||||
Some(color) => {
|
Some(color) => {
|
||||||
let black_delta_e = Lab::<D65>::new(0.0, 0.0, 0.0).improved_difference(color);
|
let black_delta_e = Lab::<D65>::new(0.0, 0.0, 0.0).improved_difference(color);
|
||||||
@ -382,6 +395,12 @@ async fn set(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
|||||||
Reply::Text(format!("set user {arg}"))
|
Reply::Text(format!("set user {arg}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//macro_rules! cmd {
|
||||||
|
// (c:lit, abbr:lit, a:ident) => {
|
||||||
|
// (".k", $c, $a) | (".k", $a, $c) | (concat!(".k", $abbr), $ident, "")
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
async fn message(&self, ctx: Context, msg: Message) {
|
async fn message(&self, ctx: Context, msg: Message) {
|
||||||
@ -402,6 +421,10 @@ impl EventHandler for Handler {
|
|||||||
log!("{} received", msg.content);
|
log!("{} received", msg.content);
|
||||||
Some(url(&ctx, arg, msg.author.id).await)
|
Some(url(&ctx, arg, msg.author.id).await)
|
||||||
},
|
},
|
||||||
|
(".k", "spot" | "spotify", arg) | (".k", arg, "spot" | "spotify") | (".ks" | ".kspot", arg, "") => {
|
||||||
|
log!("{} received", msg.content);
|
||||||
|
Some(spot(&ctx, arg, msg.author.id).await)
|
||||||
|
},
|
||||||
(".fmi" | ".k" | ".kf" | ".ki" | ".kfmi", arg, "") | (".k", "fm" | "fmi" | "get" | "img", arg) => {
|
(".fmi" | ".k" | ".kf" | ".ki" | ".kfmi", arg, "") | (".k", "fm" | "fmi" | "get" | "img", arg) => {
|
||||||
log!("{} received", msg.content);
|
log!("{} received", msg.content);
|
||||||
Some(fmi(&ctx, arg, msg.author.id, msg.author.avatar_url()).await)
|
Some(fmi(&ctx, arg, msg.author.id, msg.author.avatar_url()).await)
|
||||||
@ -440,6 +463,11 @@ impl TypeMapKey for FontsHaver {
|
|||||||
type Value = (SuperFont<'static>, SuperFont<'static>);
|
type Value = (SuperFont<'static>, SuperFont<'static>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SpotifyHaver;
|
||||||
|
impl TypeMapKey for SpotifyHaver {
|
||||||
|
type Value = Option<Arc<Mutex<ClientCredsSpotify>>>;
|
||||||
|
}
|
||||||
|
|
||||||
struct DBResponse {
|
struct DBResponse {
|
||||||
lastfm_username: String,
|
lastfm_username: String,
|
||||||
}
|
}
|
||||||
@ -563,6 +591,9 @@ async fn main() {
|
|||||||
Unifont
|
Unifont
|
||||||
").expect("Failed to load bold fonts");
|
").expect("Failed to load bold fonts");
|
||||||
|
|
||||||
|
let spotify_creds = Credentials::from_env();
|
||||||
|
let spotify = spotify_creds.map(ClientCredsSpotify::new).map(Mutex::new).map(Arc::new);
|
||||||
|
|
||||||
let mut discord_client = Client::builder(&token, intents)
|
let mut discord_client = Client::builder(&token, intents)
|
||||||
.event_handler(Handler)
|
.event_handler(Handler)
|
||||||
.await
|
.await
|
||||||
@ -573,6 +604,7 @@ async fn main() {
|
|||||||
data.insert::<PoolHaver>(pool);
|
data.insert::<PoolHaver>(pool);
|
||||||
data.insert::<HttpHaver>(http);
|
data.insert::<HttpHaver>(http);
|
||||||
data.insert::<FontsHaver>((regular_fonts, bold_fonts));
|
data.insert::<FontsHaver>((regular_fonts, bold_fonts));
|
||||||
|
data.insert::<SpotifyHaver>(spotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(why) = discord_client.start().await {
|
if let Err(why) = discord_client.start().await {
|
||||||
|
|||||||
Reference in New Issue
Block a user