From 982dd7df48f75783d49d85e39e42de5b70bcbb1b Mon Sep 17 00:00:00 2001 From: Ladislav Hano <524934@mail.muni.cz> Date: Sun, 18 Feb 2024 12:14:02 +0100 Subject: [PATCH] basic gifs implementation --- Cargo.toml | 1 + src/main.rs | 4 +-- src/other/notice.rs | 57 +++++++++++++++++++++++++++----------- src/other/tenor.rs | 17 ++++++++++++ src/other/tenor_builder.rs | 47 +++++++++++++++++++------------ src/other/tenor_types.rs | 1 - 6 files changed, 89 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd0b109..0fb6186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ chrono = "0.4.31" sqlx = {version="0.7.3", features=["runtime-tokio", "sqlite"]} json = "0.12.4" reqwest = "0.11.24" +form_urlencoded = "1.2.1" [features] DEBUG = [] diff --git a/src/main.rs b/src/main.rs index b2df9f0..6a2c39d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,8 +14,6 @@ use message_handler::handle; use std::future::Future; use std::pin::Pin; -// use chrono::Utc; - mod commands; mod util; @@ -37,7 +35,7 @@ impl EventHandler for Handler { use util::debug::hello; hello(ctx.http.clone()).await; } - + let sched = JobScheduler::new().await.unwrap(); let job_closure = move |_, _| -> Pin + Send>> { diff --git a/src/other/notice.rs b/src/other/notice.rs index 512438c..09a94a5 100644 --- a/src/other/notice.rs +++ b/src/other/notice.rs @@ -2,7 +2,7 @@ use chrono::{Datelike, Local}; use serenity::{all::{GuildId, UserId}, builder::{CreateEmbed, CreateMessage}, client::Context, http::Http, model::Colour}; -use anyhow::Ok; +// use anyhow::Ok; use sqlx::{Connection, FromRow, SqliteConnection}; @@ -12,6 +12,8 @@ use crate::util::utilities; use std::sync::Arc; +use super::{tenor, tenor_builder::Tenor, tenor_types::{ContentFilter, MediaFilter}}; + // pub async fn notice_wrapper(http: Arc) { pub async fn notice_wrapper(ctx: Context) { match notice(ctx.http.clone()).await { @@ -24,6 +26,7 @@ pub async fn notice_wrapper(ctx: Context) { } async fn announce_event(guild_id: GuildId, name: &str, special_message: &str, http: Arc) -> anyhow::Result<()> { + let mut event_embed = CreateEmbed::new() .color(Colour::new(rand::random::() % 0xFFFFFF)) .title("Today's event is:"); @@ -45,13 +48,35 @@ async fn announce_event(guild_id: GuildId, name: &str, special_message: &str, ht } async fn celebrate_birthday(guild_id: GuildId, user_id: UserId, nick: &str, http: Arc) -> anyhow::Result<()> { + + const LIMIT: u8 = 20; + + let tenor_response = Tenor::new() + .random(true) + .contentfilter(ContentFilter::low) + .add_media_filter(MediaFilter::gif) + .search("vsetko najlepsie").await?; + + let index = rand::random::() % LIMIT as usize; + let gif_url = match tenor::get_gif_url(MediaFilter::gif, tenor_response) { + Ok(urls) => Some(urls), + Err(e) => { + send_error(http.clone(), e.to_string()).await; + None + } + }; + let system_channel = utilities::get_system_channel(guild_id, http.clone()).await?; - let embed = CreateEmbed::new() + let mut embed = CreateEmbed::new() .color(Colour::new(rand::random::() % 0xFFFFFF)) .title(format!("HAPPY BIRTHDAY {}!", nick)) .description(format!("<@{}>'s birthday is today!!! Yay!", user_id.get())); + if gif_url.is_some() { + embed = embed.image(gif_url.unwrap()[index].as_str()); + } + system_channel.send_message(http.clone(), CreateMessage::new().add_embed(embed.clone())).await?; Ok(()) @@ -90,19 +115,19 @@ async fn notice(http: Arc) -> anyhow::Result<()> { "SELECT id, nick FROM birthdays WHERE day=? AND month=?;" ) - .bind(day) - .bind(month) - .fetch_all(&mut db) - .await?; + .bind(day) + .bind(month) + .fetch_all(&mut db) + .await?; let global_events = sqlx::query_as::<_, EventRow>( "SELECT id, guild, name, year, special_message from events WHERE day=? AND month=? AND guild=0;" ) - .bind(day) - .bind(month) - .fetch_all(&mut db) - .await?; + .bind(day) + .bind(month) + .fetch_all(&mut db) + .await?; let guilds = http.get_guilds(None, None).await?; @@ -127,18 +152,18 @@ async fn notice(http: Arc) -> anyhow::Result<()> { } } - let global_events = sqlx::query_as::<_, EventRow>( + let guild_events = sqlx::query_as::<_, EventRow>( "SELECT id, guild, name, year, special_message from events WHERE day=? AND month=? AND guild!=0;" ) - .bind(day) - .bind(month) - .fetch_all(&mut db) - .await?; + .bind(day) + .bind(month) + .fetch_all(&mut db) + .await?; // TODO if has year delete it from announce and delete - for e in &global_events { + for e in &guild_events { announce_event(GuildId::new(e.guild), e.name.as_str(), e.special_message.as_str(), http.clone()).await?; } diff --git a/src/other/tenor.rs b/src/other/tenor.rs index e69de29..6ac9049 100644 --- a/src/other/tenor.rs +++ b/src/other/tenor.rs @@ -0,0 +1,17 @@ +use super::tenor_types::MediaFilter; + +use json::JsonValue; + +pub fn get_gif_url(filter: MediaFilter, tenor_response: JsonValue) -> anyhow::Result> { + use anyhow::Context; + + let mut urls: Vec = Vec::new(); + let results = tenor_response["results"].members(); + + for result in results { + let url = result["media_formats"][filter.to_string()]["url"].as_str().context("Value not found in Json")?; + urls.push(url.to_string()); + } + + Ok(urls) +} \ No newline at end of file diff --git a/src/other/tenor_builder.rs b/src/other/tenor_builder.rs index 047aa40..01a94ef 100644 --- a/src/other/tenor_builder.rs +++ b/src/other/tenor_builder.rs @@ -1,5 +1,3 @@ -use std::{collections::hash_map::RandomState, fmt}; - use json::JsonValue; use std::collections::HashSet; @@ -40,7 +38,7 @@ pub struct Tenor { } impl Tenor { - fn new(q: String) -> Self { + pub fn new() -> Self { Tenor { country: None, locale: None, @@ -54,31 +52,31 @@ impl Tenor { } /// Replaces current country with the passed one - fn country(mut self, country: String) -> Self { + pub fn country(mut self, country: String) -> Self { self.country = Some(country); self } /// Replaces current locale with the passed one - fn locale(mut self, locale: String) -> Self { + pub fn locale(mut self, locale: String) -> Self { self.locale = Some(locale); self } /// Replaces current media_filter with the passed one - fn media_filter(mut self, filter: HashSet) -> Self { + pub fn media_filter(mut self, filter: HashSet) -> Self { self.media_filter = Some(filter); self } /// Replaces current contentfilter with the passed one - fn contentfilter(mut self, filter: ContentFilter) -> Self { + pub fn contentfilter(mut self, filter: ContentFilter) -> Self { self.contentfilter = Some(filter); self } /// Replaces current media_filter with the passed one - fn add_media_filter(mut self, filter: MediaFilter) -> Self { + pub fn add_media_filter(mut self, filter: MediaFilter) -> Self { if self.media_filter.is_none() { let mut set = HashSet::new(); set.insert(filter); @@ -90,17 +88,17 @@ impl Tenor { self } - fn ar_range(mut self, range: ArRange) -> Self { + pub fn ar_range(mut self, range: ArRange) -> Self { self.ar_range = Some(range); self } - fn random(mut self, random: bool) -> Self { + pub fn random(mut self, random: bool) -> Self { self.random = Some(random); self } - fn limit(mut self, mut limit: u8) -> Self { + pub fn limit(mut self, mut limit: u8) -> Self { if limit < 20 { limit = 20; } @@ -112,19 +110,21 @@ impl Tenor { self } - fn pos(mut self, pos: String) -> Self { + pub fn pos(mut self, pos: String) -> Self { self.pos = Some(pos); self } - async fn search(self, query: String) -> Result { + pub async fn search(self, query: &str) -> Result { use anyhow::Context; + let q: String = form_urlencoded::byte_serialize(query.as_bytes()).collect(); + // TODO encode query for urls (replace special characters and stuff) let base_url = "https://tenor.googleapis.com/v2/search?"; let api_key = dotenv_var("TENORV2").context("TENORV2 key not found in the .env")?; - let mut request = format!("{base_url}q={query}&key={api_key}"); + let mut request = format!("{base_url}q={q}&key={api_key}"); if self.country.is_some() { request.push_str(&format!("&country={}", self.country.unwrap())); @@ -134,10 +134,21 @@ impl Tenor { request.push_str(&format!("&locale={}", self.locale.unwrap())); } - if self.media_filter.is_some() { - // TODO implement - // self.media_filter_to_string() - // request.push_str(); + if self.contentfilter.is_some() { + request.push_str(&format!("&contentfilter={}", self.contentfilter.unwrap())) + } + + if self.media_filter.is_some() && self.media_filter.as_ref().unwrap().len() > 0 { + request.push_str( + format!("&media_filter={}", + self.media_filter.unwrap() + .iter() + .map(|e| e.to_string()) + .collect::>() + .join(",") + .as_str() + ).as_str() + ); } if self.ar_range.is_some() { diff --git a/src/other/tenor_types.rs b/src/other/tenor_types.rs index f20e29d..91620e5 100644 --- a/src/other/tenor_types.rs +++ b/src/other/tenor_types.rs @@ -2,7 +2,6 @@ use std::fmt; - #[derive(Debug)] pub enum ContentFilter { off,