feat: sauce api
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/old_apis
|
56
Cargo.lock
generated
56
Cargo.lock
generated
@@ -192,6 +192,21 @@ dependencies = [
|
|||||||
"windows-targets 0.52.0",
|
"windows-targets 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono-wasi"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc966f9982ebc257d603e29e5e2ef7691826fc66b791a237b1e172a45a337143"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mach",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"redox_syscall 0.1.57",
|
||||||
|
"wasi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colored"
|
name = "colored"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@@ -783,6 +798,15 @@ version = "0.4.20"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mach"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
@@ -864,6 +888,15 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.18"
|
version = "0.2.18"
|
||||||
@@ -969,7 +1002,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall 0.4.1",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
@@ -1098,6 +1131,12 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.1.57"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -1267,9 +1306,13 @@ dependencies = [
|
|||||||
name = "sauce_connoisseur"
|
name = "sauce_connoisseur"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono-wasi",
|
||||||
"log",
|
"log",
|
||||||
"poise",
|
"poise",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_repr",
|
||||||
"serenity",
|
"serenity",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -1373,6 +1416,17 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_repr"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.50",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@@ -6,9 +6,13 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono-wasi = "0.4.11"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
poise = "0.6.1"
|
poise = "0.6.1"
|
||||||
reqwest = { version = "0.11.24", features = ["blocking", "json"] }
|
reqwest = { version = "0.11.24", features = ["blocking", "json"] }
|
||||||
|
serde = "1.0.197"
|
||||||
|
serde_json = "1.0.114"
|
||||||
|
serde_repr = "0.1.18"
|
||||||
serenity = "0.12.0"
|
serenity = "0.12.0"
|
||||||
simple_logger = { version = "4.3.3", features = ["threads"] }
|
simple_logger = { version = "4.3.3", features = ["threads"] }
|
||||||
tokio = { version = "1.36.0", features = ["rt-multi-thread"] }
|
tokio = { version = "1.36.0", features = ["rt-multi-thread"] }
|
||||||
|
40
misc_data/api_gelbooru_post.json
Normal file
40
misc_data/api_gelbooru_post.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"@attributes": {
|
||||||
|
"limit": 1,
|
||||||
|
"offset": 0,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"post": [
|
||||||
|
{
|
||||||
|
"id": 9659332,
|
||||||
|
"created_at": "Wed Feb 21 18:51:07 -0600 2024",
|
||||||
|
"score": 238,
|
||||||
|
"width": 720,
|
||||||
|
"height": 924,
|
||||||
|
"md5": "940876caab42e3d12fbc33ea11fa40a6",
|
||||||
|
"directory": "94\/08",
|
||||||
|
"image": "940876caab42e3d12fbc33ea11fa40a6.webm",
|
||||||
|
"rating": "explicit",
|
||||||
|
"source": "https:\/\/twitter.com\/Lx3_LCubed\/status\/1737880617520636134",
|
||||||
|
"change": 1708563067,
|
||||||
|
"owner": "only_kemonomimi",
|
||||||
|
"creator_id": 1198238,
|
||||||
|
"parent_id": 0,
|
||||||
|
"sample": 0,
|
||||||
|
"preview_height": 250,
|
||||||
|
"preview_width": 194,
|
||||||
|
"tags": "1boy 1girl animated arm_strap arms_between_legs artist_name asymmetrical_hair black_choker black_eyeshadow black_gloves black_hair blush bouncing_breasts breasts breath chest_belt choker cleavage closed_eyes closed_mouth covered_erect_nipples cowboy_shot cowgirl_position cum dress ejaculation elbow_gloves english_text evil_grin evil_smile eyebrows eyelashes eyeliner eyeshadow female_orgasm femdom fingerless_gloves frills from_above girl_on_top gloves glowing glowing_eyes goth_fashion grin hair_ornament hetero holding holding_scissors hololive hololive_english large_breasts leaning_forward live2d looking_at_viewer looking_down looking_up losloslos_lx3 makeup multicolored_hair myth1carts open_mouth orange_eyes orgasm pov pov_crotch purple_eyes rape riding runny_makeup scissors sex shiori_novella shiori_novella_(1st_costume) simple_background sitting sitting_on_person smile solo_focus sound spread_legs straddling strap_slip strapless strapless_dress striped_clothes striped_gloves tagme tears tongue tongue_out two-tone_hair vaginal video virtual_youtuber watermark white_background white_hair yandere",
|
||||||
|
"title": "",
|
||||||
|
"has_notes": "false",
|
||||||
|
"has_comments": "false",
|
||||||
|
"file_url": "https:\/\/video-cdn1.gelbooru.com\/images\/94\/08\/940876caab42e3d12fbc33ea11fa40a6.mp4",
|
||||||
|
"preview_url": "https:\/\/img3.gelbooru.com\/thumbnails\/94\/08\/thumbnail_940876caab42e3d12fbc33ea11fa40a6.jpg",
|
||||||
|
"sample_url": "",
|
||||||
|
"sample_height": 0,
|
||||||
|
"sample_width": 0,
|
||||||
|
"status": "active",
|
||||||
|
"post_locked": 0,
|
||||||
|
"has_children": "false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
261
misc_data/api_gelbooru_tags.json
Normal file
261
misc_data/api_gelbooru_tags.json
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
{
|
||||||
|
"@attributes": {
|
||||||
|
"limit": 100,
|
||||||
|
"offset": 0,
|
||||||
|
"count": 36
|
||||||
|
},
|
||||||
|
"tag": [
|
||||||
|
{
|
||||||
|
"id": 262,
|
||||||
|
"name": "highres",
|
||||||
|
"count": 5223646,
|
||||||
|
"type": 5,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 796,
|
||||||
|
"name": "smile",
|
||||||
|
"count": 2697850,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12336,
|
||||||
|
"name": "solo",
|
||||||
|
"count": 4866469,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 930,
|
||||||
|
"name": "thighs",
|
||||||
|
"count": 739228,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 33975,
|
||||||
|
"name": "looking_at_viewer",
|
||||||
|
"count": 3084580,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 152532,
|
||||||
|
"name": "1girl",
|
||||||
|
"count": 6298399,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 432,
|
||||||
|
"name": "nipples",
|
||||||
|
"count": 1282668,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 265,
|
||||||
|
"name": "long_hair",
|
||||||
|
"count": 4155835,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14256,
|
||||||
|
"name": "hair_ribbon",
|
||||||
|
"count": 555093,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13764,
|
||||||
|
"name": "ribbon",
|
||||||
|
"count": 994344,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 337,
|
||||||
|
"name": "brown_hair",
|
||||||
|
"count": 1597712,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 177,
|
||||||
|
"name": "yellow_eyes",
|
||||||
|
"count": 653600,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 25360,
|
||||||
|
"name": "collarbone",
|
||||||
|
"count": 731687,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 43250,
|
||||||
|
"name": "closed_mouth",
|
||||||
|
"count": 844280,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 747581,
|
||||||
|
"name": "feet_out_of_frame",
|
||||||
|
"count": 115123,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 337849,
|
||||||
|
"name": "hair_between_eyes",
|
||||||
|
"count": 963451,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3477,
|
||||||
|
"name": "navel",
|
||||||
|
"count": 1198508,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 656,
|
||||||
|
"name": "toes",
|
||||||
|
"count": 206973,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5498,
|
||||||
|
"name": "indoors",
|
||||||
|
"count": 365503,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 607371,
|
||||||
|
"name": "female_focus",
|
||||||
|
"count": 880105,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 171,
|
||||||
|
"name": "loli",
|
||||||
|
"count": 364398,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 884,
|
||||||
|
"name": "spread_legs",
|
||||||
|
"count": 402322,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 248,
|
||||||
|
"name": "pussy",
|
||||||
|
"count": 742082,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 50,
|
||||||
|
"name": "nude",
|
||||||
|
"count": 875073,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15477,
|
||||||
|
"name": "red_ribbon",
|
||||||
|
"count": 140277,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 708618,
|
||||||
|
"name": "completely_nude",
|
||||||
|
"count": 163434,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1331,
|
||||||
|
"name": "pillow",
|
||||||
|
"count": 155206,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13319,
|
||||||
|
"name": "toenails",
|
||||||
|
"count": 38778,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 523,
|
||||||
|
"name": "flat_chest",
|
||||||
|
"count": 256879,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 151031,
|
||||||
|
"name": "bed_sheet",
|
||||||
|
"count": 99951,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19606,
|
||||||
|
"name": "arm_support",
|
||||||
|
"count": 93891,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7053,
|
||||||
|
"name": "ribs",
|
||||||
|
"count": 10331,
|
||||||
|
"type": 0,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 306143,
|
||||||
|
"name": "to_love-ru",
|
||||||
|
"count": 11675,
|
||||||
|
"type": 3,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 319567,
|
||||||
|
"name": "to_love-ru_darkness",
|
||||||
|
"count": 3709,
|
||||||
|
"type": 3,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1391655,
|
||||||
|
"name": "elementary",
|
||||||
|
"count": 102,
|
||||||
|
"type": 1,
|
||||||
|
"ambiguous": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 402955,
|
||||||
|
"name": "master_nemesis",
|
||||||
|
"count": 304,
|
||||||
|
"type": 4,
|
||||||
|
"ambiguous": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -1,78 +0,0 @@
|
|||||||
use super::gelbooru::GelbooruApi;
|
|
||||||
use poise::serenity_prelude as serenity;
|
|
||||||
use ::serenity::all::Timestamp;
|
|
||||||
|
|
||||||
|
|
||||||
pub trait SauceApi {
|
|
||||||
fn name(&self) -> &str;
|
|
||||||
fn color(&self) -> (u8, u8, u8);
|
|
||||||
fn logo(&self) -> String;
|
|
||||||
fn into_sauce(&self) -> SauceData;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SauceData {
|
|
||||||
pub title: String, // Embed (title)
|
|
||||||
pub url: String, // Embed (Post link)
|
|
||||||
pub image_url: String, // Embed (image)
|
|
||||||
pub source_url: String, // Embed (Author link)
|
|
||||||
pub rating: String, // Embed (footer)
|
|
||||||
pub tags: Option<Vec<String>>, // Embed (Tags formatted in 3 columns)
|
|
||||||
pub characters: Option<Vec<String>>, // Embed
|
|
||||||
pub artists: Option<Vec<String>>, // Embed
|
|
||||||
pub parodies: Option<Vec<String>>, // Embed
|
|
||||||
pub color: (u8, u8, u8), // Embed (color)
|
|
||||||
pub timestamp: Timestamp, // Embed (timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Sauce {
|
|
||||||
pub api: Box<dyn SauceApi>,
|
|
||||||
pub data: SauceData,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sauce {
|
|
||||||
pub fn new(url: &str) -> Self {
|
|
||||||
let api = Box::new(GelbooruApi::new());
|
|
||||||
let data = api.into_sauce();
|
|
||||||
Self { api, data }
|
|
||||||
} // TODO: this is most likely async
|
|
||||||
|
|
||||||
pub fn to_embed(&self) -> serenity::CreateEmbed {
|
|
||||||
let author = serenity::CreateEmbedAuthor::new("Author")
|
|
||||||
.name(self.data.artists.clone().unwrap().join(" & "))
|
|
||||||
.url(self.data.source_url.clone());
|
|
||||||
|
|
||||||
// divide tags into 3 columns
|
|
||||||
let tags = self.data.tags.clone().unwrap(); // TODO: filter out non-image descriptive tags
|
|
||||||
let column_1 = Self::tag_column(tags.clone(), 0);
|
|
||||||
let column_2 = Self::tag_column(tags.clone(), 1);
|
|
||||||
let column_3 = Self::tag_column(tags.clone(), 2);
|
|
||||||
|
|
||||||
let characters = self.data.characters.clone().unwrap().join("\n");
|
|
||||||
|
|
||||||
let footer = serenity::CreateEmbedFooter::new(self.data.rating.clone());
|
|
||||||
|
|
||||||
serenity::CreateEmbed::new()
|
|
||||||
.color(self.data.color.clone())
|
|
||||||
.author(author)
|
|
||||||
.title(self.data.title.clone()) // TODO: For posts without a title, use the first character tag in a short sentence that describes the image depending on the rating (e.g. "Link, Zelda and Riju" for safe posts, "Link and Zelda (explicit)" for explicit posts)
|
|
||||||
.url(self.data.url.clone())
|
|
||||||
.image(self.data.image_url.clone()) // TODO: We can't use that for videos, we need to make a button to link to the video "View video" and show the thumbnail
|
|
||||||
.fields(vec![
|
|
||||||
("Tags", column_1, true),
|
|
||||||
("", column_2, true),
|
|
||||||
("", column_3, true),
|
|
||||||
("Characters", characters, false)
|
|
||||||
])
|
|
||||||
.footer(footer)
|
|
||||||
.timestamp(self.data.timestamp.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tag_column(tags: Vec<String>, column: i32) -> String {
|
|
||||||
tags.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(i, _)| i % 3 == column as usize)
|
|
||||||
.map(|(_, tag)| tag.to_string())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n")
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,119 +1,244 @@
|
|||||||
use crate::apis::api::SauceApi;
|
use super::sauce::Sauce;
|
||||||
use crate::apis::api::SauceData;
|
use crate::apis::utils::{SauceApi, SauceType, Timestamp};
|
||||||
use serenity::all::Timestamp;
|
use chrono::prelude::*;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
pub struct GelbooruApi;
|
const API_POST_URL: &str = "https://gelbooru.com/index.php?page=dapi&s=post&q=index&json=1&id=";
|
||||||
impl GelbooruApi {
|
const API_TAGS_URL: &str = "https://gelbooru.com/index.php?page=dapi&s=tag&q=index&json=1&names=";
|
||||||
pub fn new() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn boxed(self) -> Box<dyn SauceApi> {
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
Box::new(Self::new())
|
pub struct GelbooruPosts {
|
||||||
}
|
pub post: Vec<GelbooruPost>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SauceApi for GelbooruApi {
|
impl From<GelbooruPosts> for Sauce {
|
||||||
fn name(&self) -> &str {
|
fn from(val: GelbooruPosts) -> Self {
|
||||||
"Gelbooru"
|
let is_video = &val.post[0].tags.tags.iter().any(|tag| tag.name == "video");
|
||||||
}
|
let title_action: String = match &val.post[0].rating {
|
||||||
|
GelbooruRating::Safe => String::from(" lookin' cute"),
|
||||||
fn color(&self) -> (u8, u8, u8) {
|
GelbooruRating::Questionable => String::from(" OWO what's this?"),
|
||||||
(37, 150, 190) // Gelbooru blue
|
GelbooruRating::Explicit => String::from(" gettin' lewd"),
|
||||||
}
|
};
|
||||||
|
let post = &val.post[0];
|
||||||
fn logo(&self) -> String {
|
let title = post
|
||||||
String::from("https://gelbooru.com/favicon.ico") // TODO: find high-res logo
|
.tags
|
||||||
}
|
.tags
|
||||||
|
.iter()
|
||||||
fn into_sauce(&self) -> SauceData {
|
.filter(|tag| tag.type_ == GelbooruTagType::Character)
|
||||||
SauceData {
|
.map(|tag| tag.name.clone())
|
||||||
title: String::from("Gelbooru"),
|
.collect::<Vec<String>>()
|
||||||
url: String::from("https://gelbooru.com/"),
|
.join(", ")
|
||||||
image_url: String::from("https://gelbooru.com/"),
|
+ " "
|
||||||
source_url: String::from("https://gelbooru.com/"),
|
+ &title_action;
|
||||||
rating: String::from("Rating"),
|
let url = post.url.clone();
|
||||||
tags: None,
|
let image_url = match is_video {
|
||||||
characters: None,
|
true => post.preview_url.clone(), // TODO: get video thumbnail/link
|
||||||
artists: None,
|
false => post.file_url.clone(),
|
||||||
parodies: None,
|
};
|
||||||
color: self.color(),
|
let source_url = post.source_url.clone();
|
||||||
timestamp: Timestamp::from_millis(0 as i64).unwrap(),
|
let rating = match post.rating {
|
||||||
|
GelbooruRating::Safe => String::from("Safe"),
|
||||||
|
GelbooruRating::Questionable => String::from("Questionable"),
|
||||||
|
GelbooruRating::Explicit => String::from("Explicit"),
|
||||||
|
};
|
||||||
|
let tags = Some(
|
||||||
|
post.tags
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.map(|tag| tag.name.clone())
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
);
|
||||||
|
let characters = Some(
|
||||||
|
post.tags
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.filter(|tag| tag.type_ == GelbooruTagType::Character)
|
||||||
|
.map(|tag| tag.name.clone())
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
);
|
||||||
|
let artists = Some(
|
||||||
|
post.tags
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.filter(|tag| tag.type_ == GelbooruTagType::Artist)
|
||||||
|
.map(|tag| tag.name.clone())
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
);
|
||||||
|
let parodies = Some(
|
||||||
|
post.tags
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.filter(|tag| tag.type_ == GelbooruTagType::Faults)
|
||||||
|
.map(|tag| tag.name.clone())
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
);
|
||||||
|
let color = (37, 150, 190);
|
||||||
|
let timestamp = post.created_at.clone();
|
||||||
|
Self {
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
image_url,
|
||||||
|
source_url,
|
||||||
|
rating,
|
||||||
|
tags,
|
||||||
|
characters,
|
||||||
|
artists,
|
||||||
|
parodies,
|
||||||
|
color,
|
||||||
|
timestamp,
|
||||||
|
api: SauceApi::Gelbooru,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GelbooruPosts {
|
||||||
|
pub async fn from_saucetype(val: SauceType) -> Self {
|
||||||
|
let url = match val {
|
||||||
|
SauceType::Id(id) => format!("{}{}", API_POST_URL, id.id),
|
||||||
|
SauceType::Url(url) => url.url,
|
||||||
|
};
|
||||||
|
let response = reqwest::get(url)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
let mut posts: GelbooruPosts = serde_json::from_str(response.as_str()).unwrap();
|
||||||
|
posts.post[0].tags = GelbooruTags::from(posts.post[0].raw_tags.clone()).await;
|
||||||
|
posts.post[0].url = format!(
|
||||||
|
"https://gelbooru.com/index.php?page=post&s=view&id={}",
|
||||||
|
posts.post[0].id
|
||||||
|
);
|
||||||
|
posts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct GelbooruPost {
|
||||||
|
pub id: i64,
|
||||||
|
#[serde(deserialize_with = "timestamp_from_js_date")]
|
||||||
|
pub created_at: Timestamp,
|
||||||
|
pub score: i64,
|
||||||
|
pub width: i64,
|
||||||
|
pub height: i64,
|
||||||
|
//pub md5: String,
|
||||||
|
pub image: String, //image field
|
||||||
|
pub rating: GelbooruRating,
|
||||||
|
#[serde(rename = "source")]
|
||||||
|
pub source_url: String,
|
||||||
|
pub owner: String,
|
||||||
|
pub creator_id: i64,
|
||||||
|
//#[serde(deserialize_with = "zero_is_none")]
|
||||||
|
//pub parent_id: Option<i64>,
|
||||||
|
//#[serde(deserialize_with = "zero_is_none")]
|
||||||
|
//pub sample: Option<i64>,
|
||||||
|
#[serde(rename = "tags", deserialize_with = "tags_from_string")]
|
||||||
|
raw_tags: GelbooruTagsRequest,
|
||||||
|
#[serde(skip_serializing, default)]
|
||||||
|
pub tags: GelbooruTags,
|
||||||
|
pub file_url: String,
|
||||||
|
pub preview_url: String,
|
||||||
|
pub status: GelbooruPostStatus,
|
||||||
|
//pub has_children: String,
|
||||||
|
#[serde(skip_serializing, default)]
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum GelbooruRating {
|
pub enum GelbooruRating {
|
||||||
Safe,
|
Safe,
|
||||||
Questionable,
|
Questionable,
|
||||||
|
#[default]
|
||||||
Explicit,
|
Explicit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
|
pub struct GelbooruTagsRequest {
|
||||||
|
pub tags: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
|
pub struct GelbooruTags {
|
||||||
|
#[serde(rename = "tag")]
|
||||||
|
pub tags: Vec<GelbooruTag>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GelbooruTags {
|
||||||
|
pub async fn from(req: GelbooruTagsRequest) -> Self {
|
||||||
|
let url = format!("{}{}", API_TAGS_URL, req.tags.replace(' ', "%20"));
|
||||||
|
let response = reqwest::get(url)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
let tags: GelbooruTags = serde_json::from_str(response.as_str()).unwrap();
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
|
pub struct GelbooruTag {
|
||||||
|
pub id: i64,
|
||||||
|
pub name: String,
|
||||||
|
pub count: i64,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_: GelbooruTagType,
|
||||||
|
#[serde(deserialize_with = "bool_from_int")]
|
||||||
|
pub ambiguous: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Deserialize_repr, Serialize_repr, Default)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum GelbooruTagType {
|
||||||
|
#[default]
|
||||||
|
General = 0,
|
||||||
|
Artist = 1,
|
||||||
|
Faults = 2,
|
||||||
|
Copyright = 3,
|
||||||
|
Character = 4,
|
||||||
|
Meta = 5,
|
||||||
|
Style = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum GelbooruPostStatus {
|
pub enum GelbooruPostStatus {
|
||||||
|
#[default]
|
||||||
Active,
|
Active,
|
||||||
Flagged,
|
Flagged,
|
||||||
Pending,
|
Pending,
|
||||||
Deleted,
|
Deleted,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum GelbooruTagType {
|
fn bool_from_int<'de, D>(deserializer: D) -> Result<bool, D::Error>
|
||||||
General,
|
where
|
||||||
Artist,
|
D: Deserializer<'de>,
|
||||||
Faults,
|
{
|
||||||
Copyright,
|
let s = u8::deserialize(deserializer)?;
|
||||||
Character,
|
if s == 0 {
|
||||||
Meta,
|
Ok(false)
|
||||||
Style,
|
} else {
|
||||||
}
|
Ok(true)
|
||||||
|
|
||||||
pub struct GelbooruTag {
|
|
||||||
pub id: i64,
|
|
||||||
pub name: String,
|
|
||||||
pub count: i64,
|
|
||||||
pub type_: GelbooruTagType,
|
|
||||||
pub ambiguous: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GelbooruTag {
|
|
||||||
pub fn new(id: i64, name: String, count: i64, type_: GelbooruTagType, ambiguous: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
count,
|
|
||||||
type_,
|
|
||||||
ambiguous,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GelbooruPost {
|
fn tags_from_string<'de, D>(deserializer: D) -> Result<GelbooruTagsRequest, D::Error>
|
||||||
pub id: i64,
|
where
|
||||||
pub created_at: String,
|
D: Deserializer<'de>,
|
||||||
pub score: i64,
|
{
|
||||||
pub width: i64,
|
let s = String::deserialize(deserializer)?;
|
||||||
pub height: i64,
|
Ok(GelbooruTagsRequest { tags: s })
|
||||||
pub md5: String,
|
}
|
||||||
//directory
|
|
||||||
pub file_name: String, //image field
|
fn timestamp_from_js_date<'de, D>(deserializer: D) -> Result<Timestamp, D::Error>
|
||||||
pub rating: GelbooruRating,
|
where
|
||||||
pub source_url: String,
|
D: Deserializer<'de>,
|
||||||
pub change: Option<i64>,
|
{
|
||||||
pub owner: String,
|
let s = String::deserialize(deserializer)?;
|
||||||
pub creator_id: i64,
|
let created_at =
|
||||||
//parent_id
|
DateTime::parse_from_str(&s, "%a %b %e %T %z %Y").unwrap_or(DateTime::from(Utc::now()));
|
||||||
//sample
|
Ok(Timestamp::from(created_at))
|
||||||
pub preview_height: i64,
|
|
||||||
pub preview_width: i64,
|
|
||||||
pub tags: Vec<GelbooruTag>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub has_notes: bool,
|
|
||||||
pub has_comments: bool,
|
|
||||||
pub file_url: String,
|
|
||||||
pub preview_url: String,
|
|
||||||
pub sample_url: Option<String>,
|
|
||||||
pub sample_height: Option<i64>,
|
|
||||||
pub sample_width: Option<i64>,
|
|
||||||
pub status: GelbooruPostStatus,
|
|
||||||
pub post_locked: bool,
|
|
||||||
//has_children
|
|
||||||
}
|
}
|
@@ -1,2 +1,3 @@
|
|||||||
pub mod api;
|
pub mod sauce;
|
||||||
pub mod gelbooru;
|
pub mod gelbooru;
|
||||||
|
pub mod utils;
|
51
src/apis/sauce.rs
Normal file
51
src/apis/sauce.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use crate::apis::gelbooru;
|
||||||
|
use crate::apis::utils::{SauceApi, Timestamp};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::vec;
|
||||||
|
|
||||||
|
use super::utils::SauceRequest;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Sauce {
|
||||||
|
pub title: String,
|
||||||
|
pub url: String,
|
||||||
|
pub image_url: String,
|
||||||
|
pub source_url: String,
|
||||||
|
pub rating: String,
|
||||||
|
pub tags: Option<Vec<String>>,
|
||||||
|
pub characters: Option<Vec<String>>,
|
||||||
|
pub artists: Option<Vec<String>>,
|
||||||
|
pub parodies: Option<Vec<String>>,
|
||||||
|
pub color: (u8, u8, u8),
|
||||||
|
pub timestamp: Timestamp,
|
||||||
|
pub api: SauceApi,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Sauce {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
title: String::from("Gayniggers from Outer Space"),
|
||||||
|
url: String::from("https://www.imdb.com/title/tt0274518/"),
|
||||||
|
image_url: String::from("https://m.media-amazon.com/images/M/MV5BZjFkYTU2ODctNTQ4MS00YzRjLWE2M2YtM2U0NzUzMmIwMzRhXkEyXkFqcGdeQXVyNjExODE1MDc@._V1_FMjpg_UY500_.jpg"),
|
||||||
|
source_url: String::from("https://twitter.com/Lx3_LCubed/status/1737880617520636134"),
|
||||||
|
rating: String::from("Explicit"),
|
||||||
|
tags: Some(vec![String::from("Kasumi"), String::from("Dead or Alive"), String::from("Lx3") , String::from("Video")]),
|
||||||
|
characters: Some(vec![String::from("Kasumi")]),
|
||||||
|
artists: Some(vec![String::from("Lx3")]),
|
||||||
|
parodies: None,
|
||||||
|
color: (37, 150, 190),
|
||||||
|
timestamp: Timestamp::default(),
|
||||||
|
api: SauceApi::Gelbooru,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sauce {
|
||||||
|
pub async fn from_request(value: SauceRequest) -> Self {
|
||||||
|
match value.api {
|
||||||
|
SauceApi::Gelbooru => {
|
||||||
|
Sauce::from(gelbooru::GelbooruPosts::from_saucetype(value._ref).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
src/apis/utils.rs
Normal file
86
src/apis/utils.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
use chrono::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub enum SauceApi {
|
||||||
|
Gelbooru,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct Timestamp {
|
||||||
|
pub timestamp: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DateTime<FixedOffset>> for Timestamp {
|
||||||
|
fn from(val: DateTime<FixedOffset>) -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: val.timestamp(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DateTime<Utc>> for Timestamp {
|
||||||
|
fn from(val: DateTime<Utc>) -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: val.timestamp(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DateTime<Local>> for Timestamp {
|
||||||
|
fn from(val: DateTime<Local>) -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: val.timestamp(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Timestamp {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Utc::now().timestamp(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct SauceRequest {
|
||||||
|
pub _ref: SauceType,
|
||||||
|
pub api: SauceApi,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub enum SauceType {
|
||||||
|
Id(SauceId),
|
||||||
|
Url(SauceUrl),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
|
pub struct SauceId {
|
||||||
|
pub id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for SauceId {
|
||||||
|
fn from(val: i64) -> Self {
|
||||||
|
Self { id: val }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct SauceUrl {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for SauceUrl {
|
||||||
|
fn from(val: String) -> Self {
|
||||||
|
Self { url: val }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for SauceUrl {
|
||||||
|
fn from(val: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
url: val.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,10 @@
|
|||||||
use poise::serenity_prelude as serenity;
|
use crate::apis::utils::{SauceRequest, SauceType};
|
||||||
|
use crate::apis::{
|
||||||
|
sauce::Sauce,
|
||||||
|
utils::{SauceApi, SauceId},
|
||||||
|
};
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
|
use poise::serenity_prelude as serenity;
|
||||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
|
|
||||||
pub async fn sc_event_handler(
|
pub async fn sc_event_handler(
|
||||||
@@ -9,20 +14,33 @@ pub async fn sc_event_handler(
|
|||||||
data: &Data,
|
data: &Data,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match event {
|
match event {
|
||||||
serenity::FullEvent::Ready { .. }=> {
|
serenity::FullEvent::Ready { .. } => {
|
||||||
log::info!("Connected to Discord");
|
log::info!("Connected to Discord");
|
||||||
}
|
}
|
||||||
serenity::FullEvent::Message { new_message } => message_handler(ctx, new_message, data)?,
|
serenity::FullEvent::Message { new_message } => {
|
||||||
|
message_handler(ctx, new_message, data).await?
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn message_handler(
|
async fn message_handler(
|
||||||
_ctx: &serenity::Context,
|
_ctx: &serenity::Context,
|
||||||
msg: &serenity::Message,
|
msg: &serenity::Message,
|
||||||
_data: &Data,
|
_data: &Data,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
log::info!("Received message: {:?}", msg.content);
|
log::info!("Received message: {:?}", msg.content);
|
||||||
|
|
||||||
|
if msg.content == "?test" {
|
||||||
|
let req = SauceRequest {
|
||||||
|
_ref: SauceType::Id(SauceId::from(9526475)),
|
||||||
|
api: SauceApi::Gelbooru,
|
||||||
|
};
|
||||||
|
let embed = serenity::CreateEmbed::from(Sauce::from_request(req).await);
|
||||||
|
let res = serenity::CreateMessage::new().embed(embed);
|
||||||
|
msg.channel_id.send_message(&_ctx.http, res).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use poise::serenity_prelude as serenity;
|
use poise::serenity_prelude as serenity;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
pub struct Data {} // User data, which is stored and accessible in all command invocations
|
pub struct Data {} // User data, which is stored and accessible in all command invocations
|
||||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
type Context<'a> = poise::Context<'a, Data, Error>;
|
type Context<'a> = poise::Context<'a, Data, Error>;
|
||||||
|
|
||||||
pub mod event_handlers;
|
|
||||||
pub mod apis;
|
pub mod apis;
|
||||||
|
pub mod event_handlers;
|
||||||
|
|
||||||
/// Ping command with latency measurement
|
/// Ping command with latency measurement
|
||||||
#[poise::command(slash_command, prefix_command)]
|
#[poise::command(slash_command, prefix_command)]
|
||||||
|
22
src/utils.rs
Normal file
22
src/utils.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use crate::apis::sauce::Sauce;
|
||||||
|
use crate::apis::utils::Timestamp;
|
||||||
|
use poise::serenity_prelude as serenity;
|
||||||
|
|
||||||
|
impl From<Sauce> for serenity::CreateEmbed {
|
||||||
|
fn from(val: Sauce) -> Self {
|
||||||
|
serenity::CreateEmbed::default()
|
||||||
|
.title(val.title)
|
||||||
|
.url(val.url)
|
||||||
|
.image(val.image_url)
|
||||||
|
.footer(serenity::CreateEmbedFooter::new("").text(val.rating))
|
||||||
|
.timestamp(serenity::Timestamp::from(val.timestamp))
|
||||||
|
.color(val.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Timestamp> for serenity::Timestamp {
|
||||||
|
fn from(val: Timestamp) -> Self {
|
||||||
|
serenity::Timestamp::from_unix_timestamp(val.timestamp)
|
||||||
|
.unwrap_or(serenity::Timestamp::now())
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user