diff --git a/Cargo.lock b/Cargo.lock index efe68c0..a2f29be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -636,6 +636,7 @@ dependencies = [ "cripto_api", "gen_image", "lingua", + "meme_generator", "num-format", "octorust", "regex", @@ -2228,6 +2229,19 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "meme_generator" +version = "0.1.0" +dependencies = [ + "async-trait", + "reqwest 0.12.5", + "serde", + "serde_json", + "strum 0.26.3", + "strum_macros 0.26.4", + "tokio", +] + [[package]] name = "mime" version = "0.3.17" diff --git a/Cargo.toml b/Cargo.toml index 2fac30b..14069db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/cripto_api", "crates/gen_image"] +members = ["crates/cripto_api", "crates/gen_image", "crates/meme_generator"] [workspace.dependencies] @@ -59,6 +59,7 @@ num-format = { version = "0.4.4" } gen_image = { version = "0.1.0", path = "crates/gen_image" } cripto_api = { version = "0.1.0", path = "crates/cripto_api" } +meme_generator ={ version = "0.1.0", path = "crates/meme_generator" } reqwest = { workspace = true } serde = { workspace = true } diff --git a/README.md b/README.md index f2bccd2..d7b04a2 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ Se debe de instalar `shuttle` para ello usar `cargo-binstall`. #### 馃崕 Mac / 馃惂Linux: ```bash -curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash +curl -sSfL https://www.shuttle.dev/install | bash ``` #### Para Windows: ```powershell -Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr "https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1").Content +iwr https://www.shuttle.dev/install-win | iex ``` Una vez instalado instalamos shuttle: @@ -33,7 +33,7 @@ cargo binstall cargo-shuttle Luego ejecuta el siguiente comando para ejecutar de modo local el bot: ```bash -cargo shuttle run +shuttle run ``` ## Producci贸n @@ -41,9 +41,9 @@ cargo shuttle run Para ejecutar el bot en modo producci贸n debemos ejecutar el siguiente comando: ```bash -cargo shuttle deploy +shuttle deploy ``` Esto deployara en Shuttle el bot. -> Documentaci贸n de [Shuttle](https://docs.shuttle.rs/getting-started/installation) para m谩s informaci贸n. \ No newline at end of file +> Documentaci贸n de [Shuttle](https://docs.shuttle.rs/getting-started/installation) para m谩s informaci贸n. diff --git a/Shuttle.toml b/Shuttle.toml index ddc293c..fc9d4ef 100644 --- a/Shuttle.toml +++ b/Shuttle.toml @@ -1,4 +1,8 @@ id = "proj_01JK2AC6TTKNA7J84A9J35A81X" +[build] +assets = [ + "assets/*", +] [deploy] include = [ "assets/*", diff --git a/crates/meme_generator/Cargo.toml b/crates/meme_generator/Cargo.toml new file mode 100644 index 0000000..0bc542f --- /dev/null +++ b/crates/meme_generator/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "meme_generator" +version = "0.1.0" +edition = "2021" + +[dependencies] +async-trait = { workspace = true } +serde = { workspace = true, features = ["derive"] } +reqwest = { workspace = true, features = ["blocking", "json"] } +serde_json = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/meme_generator/src/api.rs b/crates/meme_generator/src/api.rs new file mode 100644 index 0000000..363dcb5 --- /dev/null +++ b/crates/meme_generator/src/api.rs @@ -0,0 +1,37 @@ +use crate::meme::{InfoMemeResponse, MemeResponse}; +use reqwest::Error; + +const RANDOM_MEME_API: &str = "https://meme-api.com/gimme"; + +pub async fn get_random_meme_information() -> Result { + let client = reqwest::Client::new(); + let response = client + .get(RANDOM_MEME_API) + .header("accept", "application/json") + .send() + .await?; + + let meme_data = response.json::().await?; + + Ok(meme_data) +} + +pub async fn download_random_meme() -> Result> { + let meme_info = get_random_meme_information().await?; + let meme_url = meme_info.preview.last().unwrap(); + + let client = reqwest::Client::new(); + + let response = client.get(meme_url).send().await?; + + if !response.status().is_success() { + return Err(format!("Error on download: {}", response.status()).into()); + } + + let bytes = response.bytes().await?; + + Ok(MemeResponse { + title: meme_info.title, + content: bytes.to_vec(), + }) +} diff --git a/crates/meme_generator/src/lib.rs b/crates/meme_generator/src/lib.rs new file mode 100644 index 0000000..7c5d706 --- /dev/null +++ b/crates/meme_generator/src/lib.rs @@ -0,0 +1,2 @@ +pub mod api; +pub mod meme; diff --git a/crates/meme_generator/src/main.rs b/crates/meme_generator/src/main.rs new file mode 100644 index 0000000..fb394e1 --- /dev/null +++ b/crates/meme_generator/src/main.rs @@ -0,0 +1,8 @@ +use meme_generator::api::get_random_meme_information; + +#[tokio::main] +async fn main() { + let meme = get_random_meme_information().await; + + println!("Meme {:?}", meme); +} diff --git a/crates/meme_generator/src/meme.rs b/crates/meme_generator/src/meme.rs new file mode 100644 index 0000000..0fd4c9c --- /dev/null +++ b/crates/meme_generator/src/meme.rs @@ -0,0 +1,21 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct InfoMemeResponse { + pub post_link: String, + pub subreddit: String, + pub title: String, + pub url: String, + pub nsfw: bool, + pub spoiler: bool, + pub author: String, + pub ups: i64, + pub preview: Vec, +} + +#[derive(Debug)] +pub struct MemeResponse { + pub title: String, + pub content: Vec, +} diff --git a/src/bot.rs b/src/bot.rs index 38d8181..f28bd97 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -209,6 +209,21 @@ impl EventHandler for Handler { } } } + "meme" => { + if let Err(why) = command.defer(&ctx.http).await { + log_error!("Error deferring interaction: {:?}", why); + + return; + } + match commands::meme::run(&ctx, &command).await { + Ok(embed) => Some(EmbedPayload::new(vec![embed]).defer(true)), + Err(err) => { + log_error!("Error on interaction: {:?}", err); + + None + } + } + } _ => None, }; @@ -329,6 +344,7 @@ impl EventHandler for Handler { commands::wallet_leaderboard::register(), commands::courses::register(), commands::crypto_prices::register(), + commands::meme::register(), ], ) .await diff --git a/src/commands/bans_info.rs b/src/commands/bans_info.rs index d9ffad4..e4aef37 100644 --- a/src/commands/bans_info.rs +++ b/src/commands/bans_info.rs @@ -1,4 +1,4 @@ -use serenity::all::{Ban, CommandInteraction, Context, CreateCommand}; +use serenity::all::{Ban, CommandInteraction, Context, CreateCommand, Permissions}; fn ban_message(ban: Ban) -> String { let user = ban.user.name; @@ -31,4 +31,5 @@ pub async fn run(ctx: &Context, command: &CommandInteraction) -> String { pub fn register() -> CreateCommand { CreateCommand::new("bans_info") .description("Devuelve un detalle de los miembros baneados del servidor 馃實") + .default_member_permissions(Permissions::ADMINISTRATOR) } diff --git a/src/commands/meme.rs b/src/commands/meme.rs new file mode 100644 index 0000000..2c2af77 --- /dev/null +++ b/src/commands/meme.rs @@ -0,0 +1,11 @@ +use serenity::all::{CommandInteraction, Context, CreateCommand, CreateEmbed}; + +use crate::{errors::CustomError, meme}; + +pub async fn run(_: &Context, _: &CommandInteraction) -> Result { + meme::create_meme_embeb().await +} + +pub fn register() -> CreateCommand { + CreateCommand::new("meme").description("Obtener un meme random") +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 8ab5a18..83a3617 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -6,6 +6,7 @@ pub mod donate_coins; pub mod fox; pub mod list_projects; pub mod members_count; +pub mod meme; pub mod propose_project; pub mod register_wallet; pub mod say_hello; diff --git a/src/commands/warn.rs b/src/commands/warn.rs index 0ebf4f1..625db45 100644 --- a/src/commands/warn.rs +++ b/src/commands/warn.rs @@ -1,6 +1,6 @@ use serenity::all::{ CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, - ResolvedOption, ResolvedValue, + Permissions, ResolvedOption, ResolvedValue, }; pub async fn run(ctx: &Context, interaction: &CommandInteraction) -> String { @@ -62,4 +62,5 @@ pub fn register() -> CreateCommand { CreateCommandOption::new(CommandOptionType::String, "reason", "The reason of warn") .required(true), ) + .default_member_permissions(Permissions::ADMINISTRATOR) } diff --git a/src/consts.rs b/src/consts.rs index 06692d8..5a9ecfd 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -13,8 +13,6 @@ pub const BOTS_IDS: [i64; 4] = [ 276060004262477825, 1265501760013729923, ]; -pub const CAT_CHAD_STICKER: u64 = 1265339053356613663; -pub const BASED_CAT_STICKER: u64 = 1267682074341408891; pub const ENGLISH_DAYS: [chrono::Weekday; 2] = [chrono::Weekday::Wed, chrono::Weekday::Fri]; pub const ENGLISH_DAY_WHITELIST: [u64; 1] = [1072587560116817930]; pub const DUDE_EMOJI: (u64, &str) = (1257619715430420540, "tonohmm"); diff --git a/src/main.rs b/src/main.rs index f40f425..b1c2909 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod errors; mod events; mod github; mod helpers; +mod meme; mod projects; mod router; mod state; diff --git a/src/meme.rs b/src/meme.rs new file mode 100644 index 0000000..ab7b752 --- /dev/null +++ b/src/meme.rs @@ -0,0 +1,25 @@ +use meme_generator::{self}; + +use serenity::all::CreateEmbed; +use tracing::error; + +use crate::errors::CustomError; + +pub async fn create_meme_embeb() -> Result { + match meme_generator::api::get_random_meme_information().await { + Ok(meme_response) => { + let embed = CreateEmbed::new() + .title(meme_response.title) + .image(meme_response.preview.last().unwrap()); + + Ok(embed) + } + Err(err) => { + error!("{:?}", err); + + Err(CustomError::FetchError( + "Error on download meme image".to_string(), + )) + } + } +} diff --git a/src/router/reminders.rs b/src/router/reminders.rs index f2e5843..825a877 100644 --- a/src/router/reminders.rs +++ b/src/router/reminders.rs @@ -1,6 +1,6 @@ use super::setup::RouterState; -use crate::consts; use crate::utils; +use crate::{consts, meme}; use axum::routing::post; use axum::Router; use reqwest::StatusCode; @@ -9,11 +9,17 @@ use axum::{extract::State, response::IntoResponse}; use tracing::{error, info}; async fn reminder_good_night(State(ctx): State) -> impl IntoResponse { - let response = utils::send_message_to_channel( + let embeb_meme_result = meme::create_meme_embeb().await; + + if embeb_meme_result.is_err() { + return (StatusCode::BAD_GATEWAY, "Error on generate meme").into_response(); + } + + let response = utils::send_embeds_to_channel( &ctx.0, consts::GENERAL_CHANNEL_ID, - "Buenas noches gente".to_string(), - Some(consts::CAT_CHAD_STICKER), + vec![embeb_meme_result.unwrap()], + Some("Buenas noches gente".to_string()), ) .await; @@ -29,11 +35,17 @@ async fn reminder_good_night(State(ctx): State) -> impl IntoRespons } async fn reminder_good_morning(State(ctx): State) -> impl IntoResponse { - let response = utils::send_message_to_channel( + let embeb_meme_result = meme::create_meme_embeb().await; + + if embeb_meme_result.is_err() { + return (StatusCode::BAD_GATEWAY, "Error on generate meme").into_response(); + } + + let response = utils::send_embeds_to_channel( &ctx.0, consts::GENERAL_CHANNEL_ID, - "Buenos d铆as gente".to_string(), - Some(consts::BASED_CAT_STICKER), + vec![embeb_meme_result.unwrap()], + Some("Buenos d铆as gente".to_string()), ) .await; diff --git a/src/welcome/info.rs b/src/welcome/info.rs index ad7f0a8..e566dbb 100644 --- a/src/welcome/info.rs +++ b/src/welcome/info.rs @@ -34,8 +34,10 @@ fn generate_welcome_information(user_id: UserId) -> String { Recuerda registrarte primero en la wallet y cada semana se te acreditar谩n **chad-coins** (solo v谩lido dentro del servidor) 隆Estamos emocionados de que formes parte!" ); + let website = "Visita nuestro website: https://chads-programming.dev"; + format!( - "馃帀 **Bienvenido/a: ** <@{}> 馃帀\nTe dejamos presente la siguiente informaci贸n: \n\n{github_information}\n\n{wallet_information}", + "馃帀 **Bienvenido/a: ** <@{}> 馃帀\nTe dejamos presente la siguiente informaci贸n: \n\n{github_information}\n\n{wallet_information}\n\n{website}", user_id ) }