Compare commits
3 Commits
maybe-skia
...
158633d077
| Author | SHA1 | Date | |
|---|---|---|---|
| 158633d077 | |||
| 045d19c4c8 | |||
| 4841ab9f06 |
120
Cargo.lock
generated
120
Cargo.lock
generated
@ -713,6 +713,18 @@ dependencies = [
|
||||
"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]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@ -979,8 +991,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1766,6 +1780,17 @@ dependencies = [
|
||||
"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]]
|
||||
name = "maybe-rayon"
|
||||
version = "0.1.1"
|
||||
@ -2574,6 +2599,7 @@ dependencies = [
|
||||
"system-configuration 0.6.1",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-socks",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"url",
|
||||
@ -2638,6 +2664,64 @@ dependencies = [
|
||||
"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]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
@ -3347,6 +3431,27 @@ dependencies = [
|
||||
"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]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@ -3684,6 +3789,18 @@ dependencies = [
|
||||
"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]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.17"
|
||||
@ -4153,7 +4270,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "watcat"
|
||||
version = "0.1.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"arabic_reshaper",
|
||||
"dotenvy",
|
||||
@ -4165,6 +4282,7 @@ dependencies = [
|
||||
"palette",
|
||||
"reqwest 0.12.15",
|
||||
"reqwest-middleware",
|
||||
"rspotify",
|
||||
"serenity",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "watcat"
|
||||
version = "0.2.0"
|
||||
version = "0.4.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
@ -11,6 +11,7 @@ reqwest = "0.12"
|
||||
reqwest-middleware = "0.4.2"
|
||||
http-cache-reqwest = "0.15.1"
|
||||
palette = "0.7.6"
|
||||
rspotify = "0.15.0"
|
||||
# text rendering dependencies
|
||||
arabic_reshaper = "0.4.2"
|
||||
unicode-bidi = "0.3.18"
|
||||
|
||||
@ -28,7 +28,11 @@ init the database ``sqlx migrate run``
|
||||
TODO
|
||||
----
|
||||
|
||||
+ ~~literally just add the text~~
|
||||
+ add easter egg
|
||||
+ ~~add art and lastfm url fetching~~
|
||||
+ add help command
|
||||
+ ~~refactor to combine repeated parts of art, url, and fmi.~~
|
||||
+ use macro for commands
|
||||
+ use Option instead of empty string sentinel value
|
||||
+ ~~add spotify link fetching~~
|
||||
+ make various parameters user-configurable
|
||||
+ use tiny-skia instead of magick-rust?
|
||||
|
||||
119
src/main.rs
119
src/main.rs
@ -14,7 +14,8 @@ extern crate magick_rust;
|
||||
use magick_rust::{ColorspaceType, CompositeOperator, MagickWand, PixelWand, magick_wand_genesis, FilterType};
|
||||
|
||||
use std::env;
|
||||
use std::sync::Once;
|
||||
use std::sync::{Once, Arc};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use palette::color_difference::ImprovedCiede2000;
|
||||
use palette::lab::Lab;
|
||||
@ -23,6 +24,9 @@ use palette::white_point::D65;
|
||||
extern crate sqlx;
|
||||
use sqlx::sqlite::SqlitePool;
|
||||
|
||||
use rspotify::{ClientCredsSpotify, Credentials, clients::BaseClient,};
|
||||
use rspotify::model::{enums::types::SearchType, search::SearchResult};
|
||||
|
||||
use imagetext::fontdb::FontDB;
|
||||
use imagetext::superfont::SuperFont;
|
||||
|
||||
@ -114,23 +118,77 @@ fn validate_color(col: &PixelWand) -> Option<Lab> {
|
||||
Some(Lab::<D65>::from_components(color_raw))
|
||||
}
|
||||
|
||||
async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option<String>) -> Reply {
|
||||
async fn get_track(ctx: &Context, arg: &str, id: UserId) -> Result<lastfm::track::NowPlayingTrack, Reply> {
|
||||
let lastfm_user = match arg {
|
||||
"" => get_lastfm_username(ctx, id).await,
|
||||
_ => Some(arg.to_string()),
|
||||
};
|
||||
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()),
|
||||
None => return Err(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}")),
|
||||
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,
|
||||
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 {
|
||||
Some(track_art) => track_art,
|
||||
None => return Reply::Text("Error: getting image uri failed".to_string()),
|
||||
};
|
||||
Reply::Text(track_art.to_string())
|
||||
}
|
||||
|
||||
async fn url(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
||||
let track = match get_track(ctx, arg, id).await {
|
||||
Ok(track) => track,
|
||||
Err(e) => return e,
|
||||
};
|
||||
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 {
|
||||
let track = match get_track(ctx, arg, id).await {
|
||||
Ok(track) => track,
|
||||
Err(e) => return e,
|
||||
};
|
||||
let track_info = text::TrackInfo {
|
||||
title: track.name,
|
||||
artist: track.artist.name,
|
||||
@ -143,7 +201,7 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option<String>) -> Re
|
||||
|
||||
let image = match get_image(ctx, image_uri.as_str()).await {
|
||||
Ok(i) => i,
|
||||
Err(e) => return Reply::Text(format!("{}", e)),
|
||||
Err(e) => return Reply::Text(format!("{e}")),
|
||||
};
|
||||
let mut base_color = PixelWand::new();
|
||||
let mut white = PixelWand::new();
|
||||
@ -213,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]) {
|
||||
Some(color) => {
|
||||
let black_delta_e = Lab::<D65>::new(0.0, 0.0, 0.0).improved_difference(color);
|
||||
@ -335,24 +392,43 @@ async fn fmi(ctx: &Context, arg: &str, id: UserId, avatar: Option<String>) -> Re
|
||||
|
||||
async fn set(ctx: &Context, arg: &str, id: UserId) -> Reply {
|
||||
set_lastfm_username(ctx, id, arg.to_string()).await;
|
||||
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]
|
||||
impl EventHandler for Handler {
|
||||
async fn message(&self, ctx: Context, msg: Message) {
|
||||
let mut cmd_iter = msg.content.split(" ");
|
||||
let cmd = cmd_iter.next().unwrap_or("");
|
||||
let arg = cmd_iter.next().unwrap_or("");
|
||||
let resp = match cmd {
|
||||
".fmi" => {
|
||||
log!("{} received", msg.content);
|
||||
Some(fmi(&ctx, arg, msg.author.id, msg.author.avatar_url()).await)
|
||||
}
|
||||
".set" => {
|
||||
let arg1 = cmd_iter.next().unwrap_or("");
|
||||
let arg2 = cmd_iter.next().unwrap_or("");
|
||||
let resp = match (cmd, arg1, arg2) {
|
||||
(".set", arg, "") | (".k", "set", arg) | (".kset", arg, "") => {
|
||||
log!("{} received", msg.content);
|
||||
Some(set(&ctx, arg, msg.author.id).await)
|
||||
}
|
||||
},
|
||||
(".k", "art", arg) | (".k", arg, "art") | (".ka" | ".kart", arg, "") => {
|
||||
log!("{} received", msg.content);
|
||||
Some(art(&ctx, arg, msg.author.id).await)
|
||||
},
|
||||
(".k", "url" | "uri" | "link", arg) | (".k", arg, "url" | "uri" | "link") | (".ku" | ".kl" | ".kurl", arg, "") => {
|
||||
log!("{} received", msg.content);
|
||||
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) => {
|
||||
log!("{} received", msg.content);
|
||||
Some(fmi(&ctx, arg, msg.author.id, msg.author.avatar_url()).await)
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let Some(reply) = resp {
|
||||
@ -387,6 +463,11 @@ impl TypeMapKey for FontsHaver {
|
||||
type Value = (SuperFont<'static>, SuperFont<'static>);
|
||||
}
|
||||
|
||||
struct SpotifyHaver;
|
||||
impl TypeMapKey for SpotifyHaver {
|
||||
type Value = Option<Arc<Mutex<ClientCredsSpotify>>>;
|
||||
}
|
||||
|
||||
struct DBResponse {
|
||||
lastfm_username: String,
|
||||
}
|
||||
@ -510,6 +591,9 @@ async fn main() {
|
||||
Unifont
|
||||
").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)
|
||||
.event_handler(Handler)
|
||||
.await
|
||||
@ -520,6 +604,7 @@ async fn main() {
|
||||
data.insert::<PoolHaver>(pool);
|
||||
data.insert::<HttpHaver>(http);
|
||||
data.insert::<FontsHaver>((regular_fonts, bold_fonts));
|
||||
data.insert::<SpotifyHaver>(spotify);
|
||||
}
|
||||
|
||||
if let Err(why) = discord_client.start().await {
|
||||
|
||||
Reference in New Issue
Block a user