Added picox support!
This commit is contained in:
parent
4dd45ec681
commit
c08ec0120d
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -761,6 +761,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serenity",
|
"serenity",
|
||||||
"sha3",
|
"sha3",
|
||||||
"songbird",
|
"songbird",
|
||||||
@ -2062,9 +2063,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.23"
|
version = "0.11.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
|
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -2091,6 +2092,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
|
"sync_wrapper",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
@ -2431,9 +2433,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.108"
|
version = "1.0.113"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -3661,9 +3663,9 @@ checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-streams"
|
name = "wasm-streams"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
|
checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fren"
|
name = "fren"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
edition = "2021"
|
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
|
||||||
@ -8,7 +8,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
config = "0.13.4"
|
config = "0.13.4"
|
||||||
structopt = "0.3.26"
|
structopt = "0.3.26"
|
||||||
reqwest = { version = "0.11.23", features = ["json"] }
|
reqwest = { version = "0.11.24", features = ["json"] }
|
||||||
serde = "1.0.195"
|
serde = "1.0.195"
|
||||||
toml = "0.8.8"
|
toml = "0.8.8"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
@ -26,6 +26,7 @@ j_db = { version = "0.1.0", registry = "jojo-dev" }
|
|||||||
chrono = { version = "0.4.31", features = ["serde"] }
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
chrono-tz = "0.8.5"
|
chrono-tz = "0.8.5"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
serde_json = "1.0.113"
|
||||||
|
|
||||||
[dependencies.serenity]
|
[dependencies.serenity]
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
|||||||
@ -1,126 +1,198 @@
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use j_db::database::Database;
|
use chrono::{DateTime, Utc};
|
||||||
use j_db::error::JDbError;
|
use reqwest::multipart::{Form, Part};
|
||||||
use j_db::model::JdbModel;
|
use reqwest::{Client, Url};
|
||||||
use rand::prelude::SliceRandom;
|
|
||||||
use reqwest::Url;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CreateAlbum {
|
||||||
|
pub album_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
pub hash: u64,
|
pub create_date: DateTime<Utc>,
|
||||||
pub db_name: String,
|
pub link: Url,
|
||||||
|
pub created_by: u64,
|
||||||
|
pub album: u64,
|
||||||
|
pub id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub async fn new(data: &[u8], filename: &str, base_dir: PathBuf, tags: Vec<String>) -> Self {
|
pub struct AlbumQuery {
|
||||||
let mut hasher = DefaultHasher::new();
|
pub album_name: Option<String>,
|
||||||
data.hash(&mut hasher);
|
|
||||||
filename.hash(&mut hasher);
|
|
||||||
let hash = hasher.finish();
|
|
||||||
let file = PathBuf::from(filename);
|
|
||||||
let ext = file.extension().unwrap().to_str().unwrap();
|
|
||||||
|
|
||||||
let db_name = format!("{}.{}", hash, ext);
|
|
||||||
let path = base_dir.join(db_name.clone());
|
|
||||||
tokio::fs::write(path, data).await.unwrap();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
filename: filename.to_string(),
|
|
||||||
tags,
|
|
||||||
hash,
|
|
||||||
db_name,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn full_path(&self, base_dir: PathBuf) -> PathBuf {
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
base_dir.join(&self.db_name)
|
pub struct AddImage {
|
||||||
|
pub album: AlbumQuery,
|
||||||
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn link(&self, base_link: &Url) -> Url {
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
base_link.join(&self.db_name).unwrap()
|
pub enum ImageSort {
|
||||||
|
Random,
|
||||||
|
DateAscending,
|
||||||
|
DateDescending,
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ImageQuery {
|
||||||
|
pub album: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub order: ImageSort,
|
||||||
|
#[serde(default)]
|
||||||
|
pub limit: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct Album {
|
pub struct Album {
|
||||||
id: Option<u64>,
|
|
||||||
pub album_name: String,
|
pub album_name: String,
|
||||||
pub images: Vec<Image>,
|
pub images: Vec<u64>,
|
||||||
pub aliases: Vec<String>,
|
pub aliases: Vec<String>,
|
||||||
|
pub created_at: DateTime<Utc>,
|
||||||
|
pub modified_at: DateTime<Utc>,
|
||||||
|
pub owner: u64,
|
||||||
|
pub id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Album {
|
#[derive(Debug, Clone)]
|
||||||
pub fn find_album_by_name_or_alias(
|
pub struct AlbumManager {
|
||||||
db: &Database,
|
api_client: Client,
|
||||||
name: &str,
|
base_url: Url,
|
||||||
) -> Result<Option<Album>, JDbError> {
|
token: String,
|
||||||
Ok(db
|
|
||||||
.filter(|_, album: &Album| {
|
|
||||||
album.album_name.eq_ignore_ascii_case(name)
|
|
||||||
|| album.aliases.contains(&name.to_string())
|
|
||||||
})?
|
|
||||||
.next())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_random_image(
|
impl AlbumManager {
|
||||||
db: &Database,
|
pub fn new(base_url: Url, token: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
api_client: Client::new(),
|
||||||
|
base_url,
|
||||||
|
token: token.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_album(&self, query: AlbumQuery) -> Result<Vec<Album>, Error> {
|
||||||
|
let resp = self
|
||||||
|
.api_client
|
||||||
|
.get(self.base_url.join("api/album/").unwrap())
|
||||||
|
.query(&query)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(resp.json().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_album_by_id(&self, id: u64) -> Result<Album, Error> {
|
||||||
|
Ok(self
|
||||||
|
.api_client
|
||||||
|
.get(
|
||||||
|
self.base_url
|
||||||
|
.join("api/album/")
|
||||||
|
.unwrap()
|
||||||
|
.join(&id.to_string())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_image(&self, query: ImageQuery) -> Result<Vec<Image>, Error> {
|
||||||
|
let url = self.base_url.join("api/image/").unwrap();
|
||||||
|
Ok(self
|
||||||
|
.api_client
|
||||||
|
.get(url)
|
||||||
|
.json(&query)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.json()
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_image_by_id(&self, id: u64) -> Result<Image, Error> {
|
||||||
|
let url = self
|
||||||
|
.base_url
|
||||||
|
.join("api/image/")
|
||||||
|
.unwrap()
|
||||||
|
.join(&id.to_string())
|
||||||
|
.unwrap();
|
||||||
|
Ok(self.api_client.get(url).send().await?.json().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_random_image(
|
||||||
|
&self,
|
||||||
album_name: &str,
|
album_name: &str,
|
||||||
tags: Vec<&str>,
|
tags: Vec<&str>,
|
||||||
) -> Result<Option<Image>, Error> {
|
) -> Result<Option<Image>, Error> {
|
||||||
let mut rng = rand::thread_rng();
|
let query = ImageQuery {
|
||||||
let album = match Self::find_album_by_name_or_alias(db, album_name)? {
|
album: Some(album_name.to_string()),
|
||||||
None => return Err(Error::NoAlbumFound),
|
tags: tags.iter().map(|s| s.to_string()).collect(),
|
||||||
Some(a) => a,
|
order: ImageSort::Random,
|
||||||
|
limit: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let images: Vec<Image> = if tags.is_empty() {
|
Ok(self.query_image(query).await?.first().cloned())
|
||||||
album.images
|
|
||||||
} else {
|
|
||||||
album
|
|
||||||
.images
|
|
||||||
.iter()
|
|
||||||
.filter(|img| {
|
|
||||||
for tag in &tags {
|
|
||||||
if img.tags.contains(&tag.to_lowercase()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
#[allow(dead_code)]
|
||||||
})
|
pub async fn add_album(&self, album_name: &str) -> Result<Album, Error> {
|
||||||
.cloned()
|
let create_album = CreateAlbum {
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(images.choose(&mut rng).cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_album(db: &Database, album_name: &str) -> Result<Self, JDbError> {
|
|
||||||
db.insert(Album {
|
|
||||||
id: None,
|
|
||||||
album_name: album_name.to_string(),
|
album_name: album_name.to_string(),
|
||||||
images: vec![],
|
};
|
||||||
aliases: vec![],
|
|
||||||
})
|
Ok(self
|
||||||
}
|
.api_client
|
||||||
|
.post(self.base_url.join("api/album/create").unwrap())
|
||||||
|
.header("token", &self.token)
|
||||||
|
.json(&create_album)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JdbModel for Album {
|
pub async fn add_image(
|
||||||
fn id(&self) -> Option<u64> {
|
&self,
|
||||||
self.id
|
album_name: &str,
|
||||||
}
|
tags: Vec<String>,
|
||||||
|
file_name: &str,
|
||||||
|
img_data: Vec<u8>,
|
||||||
|
) -> Result<Image, Error> {
|
||||||
|
let add_image = AddImage {
|
||||||
|
album: AlbumQuery {
|
||||||
|
album_name: Some(album_name.to_string()),
|
||||||
|
},
|
||||||
|
tags,
|
||||||
|
};
|
||||||
|
|
||||||
fn set_id(&mut self, id: u64) {
|
let file_name = file_name.to_string();
|
||||||
self.id = Some(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tree() -> String {
|
let form = Form::new()
|
||||||
"img_album".to_string()
|
.part(
|
||||||
|
"metadata",
|
||||||
|
Part::text(serde_json::to_string(&add_image).unwrap()),
|
||||||
|
)
|
||||||
|
.part("img_data", Part::bytes(img_data).file_name(file_name));
|
||||||
|
|
||||||
|
let resp = self
|
||||||
|
.api_client
|
||||||
|
.post(self.base_url.join("api/image/").unwrap())
|
||||||
|
.header("token", &self.token)
|
||||||
|
.multipart(form)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use crate::album_manager::AlbumManager;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::migrations::do_migration;
|
use crate::migrations::do_migration;
|
||||||
use config::{Config, File};
|
use config::{Config, File};
|
||||||
@ -20,6 +21,12 @@ pub struct Args {
|
|||||||
pub cfg_path: PathBuf,
|
pub cfg_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct PicOxConfig {
|
||||||
|
api_base_url: Url,
|
||||||
|
token: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct BotConfig {
|
pub struct BotConfig {
|
||||||
pub bot_token: String,
|
pub bot_token: String,
|
||||||
@ -35,6 +42,7 @@ pub struct BotConfig {
|
|||||||
pub announcement_channel: ChannelId,
|
pub announcement_channel: ChannelId,
|
||||||
pub toys: Vec<String>,
|
pub toys: Vec<String>,
|
||||||
pub effect_role_duration: i64,
|
pub effect_role_duration: i64,
|
||||||
|
pub picox: PicOxConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BotConfig {
|
impl BotConfig {
|
||||||
@ -69,6 +77,7 @@ pub struct GlobalData {
|
|||||||
pub cfg: BotConfig,
|
pub cfg: BotConfig,
|
||||||
pub bot_state: BotState,
|
pub bot_state: BotState,
|
||||||
pub db: Database,
|
pub db: Database,
|
||||||
|
pub picox: AlbumManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalData {
|
impl GlobalData {
|
||||||
@ -81,7 +90,8 @@ impl GlobalData {
|
|||||||
args,
|
args,
|
||||||
bot_state: BotState::new().await?,
|
bot_state: BotState::new().await?,
|
||||||
db,
|
db,
|
||||||
cfg,
|
cfg: cfg.clone(),
|
||||||
|
picox: AlbumManager::new(cfg.picox.api_base_url, &cfg.picox.token),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
use crate::album_manager::{Album, Image};
|
use crate::album_manager::AlbumQuery;
|
||||||
use crate::discord::admin::is_admin;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::{command, group, GlobalData};
|
use crate::{command, group, GlobalData};
|
||||||
use j_db::model::JdbModel;
|
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandResult};
|
use serenity::framework::standard::{Args, CommandResult};
|
||||||
use serenity::model::channel::Message;
|
use serenity::model::channel::Message;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(add_image, remove_album, list_albums, import_from_file)]
|
#[commands(add_image, list_albums)]
|
||||||
pub struct AlbumCmd;
|
pub struct AlbumCmd;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
@ -24,27 +22,14 @@ async fn add_image(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
|
|||||||
|
|
||||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
||||||
|
|
||||||
let album = Album::find_album_by_name_or_alias(&global_data.db, &album_name).unwrap();
|
|
||||||
|
|
||||||
let mut album = if let Some(album) = album {
|
|
||||||
album
|
|
||||||
} else {
|
|
||||||
Album::add_album(&global_data.db, &album_name).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
for attachment in &msg.attachments {
|
for attachment in &msg.attachments {
|
||||||
let data = attachment.download().await.unwrap();
|
let data = attachment.download().await.unwrap();
|
||||||
let img = Image::new(
|
|
||||||
&data,
|
|
||||||
&attachment.filename,
|
|
||||||
global_data.cfg.img_path.clone(),
|
|
||||||
tags.clone(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
album.images.push(img);
|
|
||||||
}
|
|
||||||
|
|
||||||
global_data.db.insert(album).unwrap();
|
global_data
|
||||||
|
.picox
|
||||||
|
.add_image(&album_name, tags.clone(), &attachment.filename, data)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
let plural = if msg.attachments.len() > 1 { "s" } else { "" };
|
let plural = if msg.attachments.len() > 1 { "s" } else { "" };
|
||||||
|
|
||||||
@ -57,97 +42,6 @@ async fn add_image(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
|
||||||
#[min_args(1)]
|
|
||||||
#[description("Bulk add images")]
|
|
||||||
#[usage("<path>")]
|
|
||||||
async fn import_from_file(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|
||||||
let album_name = args.parse::<String>()?;
|
|
||||||
args.advance();
|
|
||||||
let album_path = args.parse::<String>()?;
|
|
||||||
let album_path = album_path.replace('"', "");
|
|
||||||
|
|
||||||
let mut data = ctx.data.write().await;
|
|
||||||
|
|
||||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
||||||
|
|
||||||
if !is_admin(&msg.author.id, &global_data.cfg) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let album = Album::find_album_by_name_or_alias(&global_data.db, &album_name).unwrap();
|
|
||||||
|
|
||||||
let mut album = if let Some(album) = album {
|
|
||||||
album
|
|
||||||
} else {
|
|
||||||
Album::add_album(&global_data.db, &album_name).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut dir = tokio::fs::read_dir(album_path).await.unwrap();
|
|
||||||
|
|
||||||
while let Some(entry) = dir.next_entry().await? {
|
|
||||||
let data = tokio::fs::read(entry.path()).await?;
|
|
||||||
let img = Image::new(
|
|
||||||
&data,
|
|
||||||
entry.path().file_name().unwrap().to_str().unwrap(),
|
|
||||||
global_data.cfg.img_path.clone(),
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
album.images.push(img);
|
|
||||||
}
|
|
||||||
|
|
||||||
global_data.db.insert(album).unwrap();
|
|
||||||
|
|
||||||
let plural = if msg.attachments.len() > 1 { "s" } else { "" };
|
|
||||||
|
|
||||||
msg.reply(
|
|
||||||
&ctx.http,
|
|
||||||
format!("Image{} added to {}!", plural, album_name),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
#[max_args(1)]
|
|
||||||
#[description("Remove an imgur album command.")]
|
|
||||||
#[usage("<name>")]
|
|
||||||
async fn remove_album(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|
||||||
let album_name = args.parse::<String>()?;
|
|
||||||
|
|
||||||
let mut data = ctx.data.write().await;
|
|
||||||
|
|
||||||
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
||||||
|
|
||||||
let album = Album::find_album_by_name_or_alias(&global_data.db, &album_name).unwrap();
|
|
||||||
|
|
||||||
match album {
|
|
||||||
None => {
|
|
||||||
msg.reply(
|
|
||||||
&ctx.http,
|
|
||||||
"I already got rid of that one, or I never had it. Who knows!",
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Some(album) => {
|
|
||||||
for img in &album.images {
|
|
||||||
tokio::fs::remove_file(img.full_path(global_data.cfg.img_path.clone()))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
global_data.db.remove::<Album>(album.id().unwrap())?;
|
|
||||||
|
|
||||||
msg.reply(&ctx.http, format!("{} album removed!", album_name))
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[aliases("albums")]
|
#[aliases("albums")]
|
||||||
#[description("List all album commands.")]
|
#[description("List all album commands.")]
|
||||||
@ -156,14 +50,15 @@ async fn list_albums(ctx: &Context, msg: &Message, _args: Args) -> CommandResult
|
|||||||
|
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get::<GlobalData>().unwrap();
|
||||||
|
|
||||||
let album_names: Vec<String> = global_data
|
let albums = global_data
|
||||||
.db
|
.picox
|
||||||
.filter(|_, _: &Album| true)?
|
.query_album(AlbumQuery { album_name: None })
|
||||||
.map(|album| album.album_name)
|
.await?;
|
||||||
.collect();
|
|
||||||
|
let album_names: Vec<String> = albums.iter().map(|a| a.album_name.clone()).collect();
|
||||||
|
|
||||||
if album_names.is_empty() {
|
if album_names.is_empty() {
|
||||||
msg.reply(&ctx.http, "There are no albums configured!")
|
msg.reply(&ctx.http, "There are no albums in picox!")
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
msg.reply(
|
msg.reply(
|
||||||
@ -185,19 +80,13 @@ pub async fn parse_album(
|
|||||||
let data = ctx.data.read().await;
|
let data = ctx.data.read().await;
|
||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get::<GlobalData>().unwrap();
|
||||||
|
|
||||||
let img = match Album::get_random_image(&global_data.db, album_name, tags) {
|
let img = match global_data.picox.get_random_image(album_name, tags).await {
|
||||||
Ok(img) => img,
|
Ok(img) => img,
|
||||||
Err(err) => {
|
Err(_) => return Ok(false),
|
||||||
return match err {
|
|
||||||
Error::NoAlbumFound => Ok(false),
|
|
||||||
_ => Err(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(img) = img {
|
if let Some(img) = img {
|
||||||
msg.reply(&ctx.http, img.link(&global_data.cfg.base_url))
|
msg.reply(&ctx.http, img.link).await?;
|
||||||
.await?;
|
|
||||||
} else {
|
} else {
|
||||||
msg.reply(&ctx.http, "No image found :(").await?;
|
msg.reply(&ctx.http, "No image found :(").await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
use crate::album_manager::Album;
|
use crate::album_manager::{Album, AlbumQuery, ImageQuery, ImageSort};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType};
|
use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType};
|
||||||
use crate::models::random::RandomConfig;
|
use crate::models::random::RandomConfig;
|
||||||
use crate::{command, group, GlobalData, BAD_APPLE};
|
use crate::{command, group, GlobalData, BAD_APPLE};
|
||||||
use rand::prelude::SliceRandom;
|
|
||||||
use rand::thread_rng;
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serenity::builder::EditMessage;
|
use serenity::builder::EditMessage;
|
||||||
@ -53,18 +51,28 @@ struct RandomCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RandomCtx {
|
impl RandomCtx {
|
||||||
pub fn new(user_name: &str, global_data: &GlobalData) -> Result<Self, Error> {
|
pub async fn new(user_name: &str, global_data: &GlobalData) -> Result<Self, Error> {
|
||||||
let mut random_image: HashMap<String, String> = HashMap::new();
|
let mut random_image: HashMap<String, String> = HashMap::new();
|
||||||
|
|
||||||
let albums: Vec<Album> = global_data.db.filter(|_, _album: &Album| true)?.collect();
|
let albums: Vec<Album> = global_data
|
||||||
|
.picox
|
||||||
|
.query_album(AlbumQuery { album_name: None })
|
||||||
|
.await?;
|
||||||
for album in albums {
|
for album in albums {
|
||||||
let image = album.images.choose(&mut thread_rng());
|
let images = global_data
|
||||||
|
.picox
|
||||||
|
.query_image(ImageQuery {
|
||||||
|
album: Some(album.album_name.clone()),
|
||||||
|
tags: vec![],
|
||||||
|
order: ImageSort::Random,
|
||||||
|
limit: 1,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let image = images.first();
|
||||||
|
|
||||||
if let Some(image) = image {
|
if let Some(image) = image {
|
||||||
random_image.insert(
|
random_image.insert(album.album_name.clone(), image.link.to_string());
|
||||||
album.album_name.clone(),
|
|
||||||
image.link(&global_data.cfg.base_url).to_string(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,12 +83,12 @@ impl RandomCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_random(
|
pub async fn render_random(
|
||||||
author_name: &str,
|
author_name: &str,
|
||||||
global_data: &GlobalData,
|
global_data: &GlobalData,
|
||||||
template: &str,
|
template: &str,
|
||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let random_ctx = RandomCtx::new(author_name, global_data)?;
|
let random_ctx = RandomCtx::new(author_name, global_data).await?;
|
||||||
|
|
||||||
Ok(tera::Tera::one_off(
|
Ok(tera::Tera::one_off(
|
||||||
template,
|
template,
|
||||||
@ -118,7 +126,8 @@ pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandR
|
|||||||
guild_member.display_name(),
|
guild_member.display_name(),
|
||||||
global_data,
|
global_data,
|
||||||
response_template_str,
|
response_template_str,
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
msg.reply(&ctx.http, reply).await?;
|
msg.reply(&ctx.http, reply).await?;
|
||||||
|
|
||||||
@ -144,7 +153,7 @@ pub async fn add_random(ctx: &Context, msg: &Message, mut args: Args) -> Command
|
|||||||
args.advance();
|
args.advance();
|
||||||
let random_response = args.rest();
|
let random_response = args.rest();
|
||||||
|
|
||||||
if let Err(err) = render_random(&msg.author.name, global_data, random_response) {
|
if let Err(err) = render_random(&msg.author.name, global_data, random_response).await {
|
||||||
msg.reply(
|
msg.reply(
|
||||||
&ctx.http,
|
&ctx.http,
|
||||||
format!("Template failed test render, try again: {}", err),
|
format!("Template failed test render, try again: {}", err),
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
use crate::album_manager::AlbumQuery;
|
||||||
use crate::models::motivation::{Motivation, MotivationConfig};
|
use crate::models::motivation::{Motivation, MotivationConfig};
|
||||||
use crate::{command, group, GlobalData};
|
use crate::{command, group, GlobalData};
|
||||||
use magick_rust::{DrawingWand, MagickWand, PixelWand};
|
use magick_rust::{DrawingWand, MagickWand, PixelWand};
|
||||||
|
use reqwest::Client;
|
||||||
use serenity::builder::{CreateAttachment, CreateMessage};
|
use serenity::builder::{CreateAttachment, CreateMessage};
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
||||||
@ -17,7 +19,15 @@ use std::borrow::Cow;
|
|||||||
pub struct Motivate;
|
pub struct Motivate;
|
||||||
|
|
||||||
pub async fn create_motivation_image(motivation: Motivation) -> Result<Vec<u8>, CommandError> {
|
pub async fn create_motivation_image(motivation: Motivation) -> Result<Vec<u8>, CommandError> {
|
||||||
let motivation_image_blob = tokio::fs::read(motivation.image_path).await?;
|
let client = Client::new();
|
||||||
|
|
||||||
|
let motivation_image_blob = client
|
||||||
|
.get(motivation.image.link)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.bytes()
|
||||||
|
.await?
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
let text = format!("{} {}", motivation.action, motivation.goal);
|
let text = format!("{} {}", motivation.action, motivation.goal);
|
||||||
|
|
||||||
@ -72,7 +82,7 @@ async fn motivation(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
|
|
||||||
let motivation = MotivationConfig::generate_motivation(
|
let motivation = MotivationConfig::generate_motivation(
|
||||||
&global_data.db,
|
&global_data.db,
|
||||||
&global_data.cfg.img_path,
|
&global_data.picox,
|
||||||
"white",
|
"white",
|
||||||
album_name,
|
album_name,
|
||||||
)
|
)
|
||||||
@ -102,8 +112,21 @@ async fn motivation_add_album(ctx: &Context, msg: &Message, args: Args) -> Comma
|
|||||||
let global_data = data.get::<GlobalData>().unwrap();
|
let global_data = data.get::<GlobalData>().unwrap();
|
||||||
|
|
||||||
let album = args.parse::<String>()?;
|
let album = args.parse::<String>()?;
|
||||||
|
let albums = global_data
|
||||||
|
.picox
|
||||||
|
.query_album(AlbumQuery {
|
||||||
|
album_name: Some(album),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
MotivationConfig::add_album(&global_data.db, &album)?;
|
let album = if let Some(album) = albums.first().cloned() {
|
||||||
|
album
|
||||||
|
} else {
|
||||||
|
msg.reply(&ctx.http, "Fake album tbh, try again!").await?;
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
MotivationConfig::add_album(&global_data.db, album.id)?;
|
||||||
|
|
||||||
msg.reply(&ctx.http, "Done ;)").await?;
|
msg.reply(&ctx.http, "Done ;)").await?;
|
||||||
|
|
||||||
|
|||||||
@ -466,7 +466,7 @@ pub async fn restock_shop(ctx: &Context, global_data: &GlobalData) -> Result<(),
|
|||||||
|
|
||||||
let nft_motivation = MotivationConfig::generate_motivation(
|
let nft_motivation = MotivationConfig::generate_motivation(
|
||||||
&global_data.db,
|
&global_data.db,
|
||||||
&global_data.cfg.img_path,
|
&global_data.picox,
|
||||||
"gold",
|
"gold",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ pub enum Error {
|
|||||||
NoAlbumFound,
|
NoAlbumFound,
|
||||||
UserError(user::UserError),
|
UserError(user::UserError),
|
||||||
DbError(j_db::error::JDbError),
|
DbError(j_db::error::JDbError),
|
||||||
|
ReqwestError(reqwest::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for Error {}
|
impl StdError for Error {}
|
||||||
@ -46,6 +47,12 @@ impl From<j_db::error::JDbError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::Error> for Error {
|
||||||
|
fn from(err: reqwest::Error) -> Self {
|
||||||
|
Self::ReqwestError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
@ -55,6 +62,7 @@ impl Display for Error {
|
|||||||
Error::NoAlbumFound => write!(f, "No album found"),
|
Error::NoAlbumFound => write!(f, "No album found"),
|
||||||
Error::UserError(e) => write!(f, "User error: {}", e),
|
Error::UserError(e) => write!(f, "User error: {}", e),
|
||||||
Error::DbError(e) => write!(f, "DB error: {}", e),
|
Error::DbError(e) => write!(f, "DB error: {}", e),
|
||||||
|
Error::ReqwestError(e) => write!(f, "Reqwest Error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::album_manager::Album;
|
use crate::album_manager::{AlbumManager, AlbumQuery, Image};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use j_db::database::Database;
|
use j_db::database::Database;
|
||||||
use j_db::error::JDbError;
|
use j_db::error::JDbError;
|
||||||
@ -6,7 +6,7 @@ use j_db::model::JdbModel;
|
|||||||
use rand::prelude::{IteratorRandom, SliceRandom};
|
use rand::prelude::{IteratorRandom, SliceRandom};
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::{Path, PathBuf};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct MotivationConfig {
|
pub struct MotivationConfig {
|
||||||
@ -47,12 +47,9 @@ impl MotivationConfig {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_album(db: &Database, album: &str) -> Result<(), Error> {
|
pub fn add_album(db: &Database, id: u64) -> Result<(), Error> {
|
||||||
let mut motivation = Self::get_motivation_config(db)?;
|
let mut motivation = Self::get_motivation_config(db)?;
|
||||||
let album = Album::find_album_by_name_or_alias(db, album)
|
motivation.album.push(id);
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
motivation.album.push(album.id().unwrap());
|
|
||||||
|
|
||||||
db.insert(motivation)?;
|
db.insert(motivation)?;
|
||||||
|
|
||||||
@ -79,7 +76,7 @@ impl MotivationConfig {
|
|||||||
|
|
||||||
pub async fn generate_motivation(
|
pub async fn generate_motivation(
|
||||||
db: &Database,
|
db: &Database,
|
||||||
base_path: &Path,
|
picox: &AlbumManager,
|
||||||
border_color: &str,
|
border_color: &str,
|
||||||
album_name: Option<String>,
|
album_name: Option<String>,
|
||||||
) -> Result<Motivation, Error> {
|
) -> Result<Motivation, Error> {
|
||||||
@ -88,16 +85,21 @@ impl MotivationConfig {
|
|||||||
let mut images = Vec::new();
|
let mut images = Vec::new();
|
||||||
|
|
||||||
if let Some(album_name) = album_name {
|
if let Some(album_name) = album_name {
|
||||||
let album = Album::find_album_by_name_or_alias(db, &album_name)?;
|
let query = AlbumQuery {
|
||||||
|
album_name: Some(album_name),
|
||||||
|
};
|
||||||
|
let albums = picox.query_album(query).await?;
|
||||||
|
|
||||||
if let Some(mut album) = album {
|
let album = albums.first();
|
||||||
images.append(&mut album.images)
|
|
||||||
|
if let Some(album) = album {
|
||||||
|
images.append(&mut album.images.clone())
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::from(JDbError::NotFound));
|
return Err(Error::from(JDbError::NotFound));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for album in motivation.album {
|
for album in motivation.album {
|
||||||
let album = db.get::<Album>(album);
|
let album = picox.get_album_by_id(album).await;
|
||||||
|
|
||||||
if let Ok(album) = album {
|
if let Ok(album) = album {
|
||||||
let mut album_images = album.images.clone();
|
let mut album_images = album.images.clone();
|
||||||
@ -114,8 +116,10 @@ impl MotivationConfig {
|
|||||||
|
|
||||||
let goal = motivation.goal.iter().choose(&mut thread_rng()).unwrap();
|
let goal = motivation.goal.iter().choose(&mut thread_rng()).unwrap();
|
||||||
|
|
||||||
|
let image = picox.get_image_by_id(*image).await.unwrap();
|
||||||
|
|
||||||
Ok(Motivation {
|
Ok(Motivation {
|
||||||
image_path: image.full_path(base_path.to_path_buf()),
|
image,
|
||||||
action: action.to_string(),
|
action: action.to_string(),
|
||||||
goal: goal.to_string(),
|
goal: goal.to_string(),
|
||||||
border_color: border_color.to_string(),
|
border_color: border_color.to_string(),
|
||||||
@ -125,7 +129,7 @@ impl MotivationConfig {
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct Motivation {
|
pub struct Motivation {
|
||||||
pub image_path: PathBuf,
|
pub image: Image,
|
||||||
pub action: String,
|
pub action: String,
|
||||||
pub goal: String,
|
pub goal: String,
|
||||||
pub border_color: String,
|
pub border_color: String,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user