diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2b3e671..3c1ed9d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -23,5 +23,4 @@ jobs: no-test: "true" secrets: | DISCORD_TOKEN = '${{ secrets.DISCORD_TOKEN }}' - HOST = '${{ secrets.HOST}}' GUILD_ID = '${{ secrets.GUILD_ID }}' diff --git a/Cargo.lock b/Cargo.lock index f406c67..d91ab03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,12 +94,6 @@ dependencies = [ "serde", ] -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - [[package]] name = "async-stream" version = "0.3.5" @@ -166,7 +160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.3.4", "bitflags 1.3.2", "bytes", "futures-util", @@ -181,10 +175,44 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 0.1.2", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.2.0", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.0", + "tokio", "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -204,6 +232,27 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -302,6 +351,7 @@ name = "cangrebot" version = "0.1.0" dependencies = [ "anyhow", + "axum 0.7.5", "color-eyre", "image", "parking_lot", @@ -312,10 +362,8 @@ dependencies = [ "serde_json", "serenity", "shuttle-runtime", - "shuttle-serenity", "songbird", "symphonia", - "tiny_http", "tokio", "tracing", ] @@ -367,12 +415,6 @@ dependencies = [ "windows-targets 0.52.4", ] -[[package]] -name = "chunked_transfer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" - [[package]] name = "cipher" version = "0.4.4" @@ -1248,6 +1290,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2489,7 +2532,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -2535,7 +2578,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -2941,6 +2984,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.18" @@ -3119,16 +3172,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "shuttle-serenity" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a951e067cf3f497c88671ff8145f22d0da7db3252960a20c9f1aa7b23eae75" -dependencies = [ - "serenity", - "shuttle-runtime", -] - [[package]] name = "shuttle-service" version = "0.42.0" @@ -3518,6 +3561,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3640,18 +3689,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny_http" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" -dependencies = [ - "ascii", - "chunked_transfer", - "httpdate", - "log", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -3829,7 +3866,7 @@ checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.6.20", "base64 0.21.7", "bytes", "h2 0.3.25", diff --git a/Cargo.toml b/Cargo.toml index 7f72d88..536a1eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ readme = "README.md" [dependencies] anyhow = "1.0.66" -shuttle-serenity = "0.42.0" shuttle-runtime = "0.42.0" serenity = { version = "=0.12.0", default-features = true, features = [ "cache", @@ -44,7 +43,7 @@ symphonia = { version = "0.5.3", default-features = false, features = [ "ogg", "wav", ] } -tiny_http = "0.12.0" +axum = "0.7.5" [dependencies.parking_lot] version = "0.12" diff --git a/src/config/setup.rs b/src/config/setup.rs index 903ee31..df1efd2 100644 --- a/src/config/setup.rs +++ b/src/config/setup.rs @@ -53,11 +53,7 @@ pub async fn setup( return Err(anyhow!("'GUILD_ID' was not found")); }; - let Some(host) = secret_store.get("HOST") else { - return Err(anyhow!("'HOST' was not found")); - }; - - let handler = Handler::new(guild_id.parse().unwrap(), host); + let handler = Handler::new(guild_id.parse().unwrap()); let client = Client::builder(token, intents) .event_handler(handler) diff --git a/src/config/slash_command_loader.rs b/src/config/slash_command_loader.rs index 1dcd1ba..ea27116 100644 --- a/src/config/slash_command_loader.rs +++ b/src/config/slash_command_loader.rs @@ -10,7 +10,6 @@ use serenity::{ model::prelude::{GuildId, Interaction, Member, Ready}, prelude::{Context, EventHandler}, }; -use tiny_http::Method; use tracing::{error, log::info}; use crate::slash_commands::ping; @@ -23,17 +22,11 @@ use slash_commands::sugerencia; pub struct Handler { guild_id: u64, - host: String, - http_running: AtomicBool, } impl Handler { - pub fn new(guild_id: u64, host: String) -> Self { - Self { - host, - guild_id, - http_running: AtomicBool::new(false), - } + pub fn new(guild_id: u64) -> Self { + Self { guild_id } } } @@ -104,53 +97,6 @@ impl EventHandler for Handler { ) .await; - let ctx = Arc::new(ctx); - - if !self.http_running.load(Ordering::Relaxed) { - let ctx1 = Arc::clone(&ctx); - let host = self.host.clone(); - - // start http server - tokio::spawn(async move { - let server = tiny_http::Server::http(host).unwrap(); - - tracing::debug!("Listening on {:?}", server.server_addr()); - - while let Ok(mut req) = server.recv() { - println!("Request: {req:?}"); - // Ejecuta el servidor - match (req.method(), req.url()) { - (Method::Post, "/daily_challenge") => { - let reader = req.as_reader(); - let Ok(data) = serde_json::from_reader(reader) else { - tracing::error!("Failed load json from reader"); - continue; - }; - match run_daily_challenge(&ctx1, &data).await { - Ok(()) => { - tracing::debug!("Success send daily"); - let r = req.respond(tiny_http::Response::empty(200)); - tracing::debug!("Response sended: {r:?}"); - } - Err(e) => { - tracing::error!("Cannot send daily: {e:?}"); - let r = req.respond( - tiny_http::Response::from_string(e.to_string()) - .with_status_code(400), - ); - tracing::debug!("Response sended: {r:?}"); - } - } - } - _ => {} - } - } - }); - - // Now that the loop is running, we set the bool to true - self.http_running.swap(true, Ordering::Relaxed); - } - // info!("I created the following global slash command: {:#?}", guild_command); } } diff --git a/src/events/daily_challenge.rs b/src/events/daily_challenge.rs index 6941401..84ac2a5 100644 --- a/src/events/daily_challenge.rs +++ b/src/events/daily_challenge.rs @@ -1,9 +1,15 @@ +use std::sync::Arc; + +use axum::extract::State; +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::Json; use serde::{Deserialize, Serialize}; use serenity::all::{ForumTagId, MessageFlags}; use serenity::builder::{CreateAllowedMentions, CreateForumPost, CreateForumTag, CreateMessage}; +use serenity::http::Http; use serenity::model::prelude::ChannelId; use serenity::prelude::Context; -use serenity::Result; use tracing::info; const PARTICIPANT_ROLE: u64 = 1224238464958992495; @@ -16,26 +22,30 @@ pub struct DailyChallengeRequest { } pub async fn run_daily_challenge( - ctx: &Context, - DailyChallengeRequest { + State(ctx): State>, + Json(DailyChallengeRequest { title, message, tag_name, - }: &DailyChallengeRequest, -) -> Result<()> { + }): Json, +) -> impl IntoResponse { info!("Running create suggestion"); - let msg_channel = ChannelId::new(824695624665923594_u64.into()); + let msg_channel = ChannelId::new(1219703076944871616_u64.into()); - let forum = msg_channel - .to_channel(ctx) - .await? - .guild() - .ok_or(serenity::Error::Other("GuildId not found"))?; - let Some(tag) = forum.available_tags.iter().find(|t| &t.name == tag_name) else { - return Err(serenity::Error::Other("Tag not found")); + let Ok(forum) = msg_channel.to_channel(&ctx).await else { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + "Cannot convert to channel", + ); + }; + let Some(forum) = forum.guild() else { + return (StatusCode::NOT_FOUND, "GuildId not found"); + }; + let Some(tag) = forum.available_tags.iter().find(|t| t.name == tag_name) else { + return (StatusCode::NOT_FOUND, "Tag not found"); }; - let _ = msg_channel + match msg_channel .create_forum_post( &ctx, CreateForumPost::new( @@ -46,7 +56,12 @@ pub async fn run_daily_challenge( ) .add_applied_tag(tag.id), ) - .await?; - - Ok(()) + .await + { + Ok(_) => (StatusCode::OK, "Ok"), + Err(_) => ( + StatusCode::INTERNAL_SERVER_ERROR, + "Cannot Create Forum Post", + ), + } } diff --git a/src/main.rs b/src/main.rs index a76faff..124f2c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,12 @@ -use std::path::PathBuf; +use axum::{Router, ServiceExt}; +use events::daily_challenge::run_daily_challenge; +use serenity::http::Http; +use serenity::prelude::Context; +use serenity::Client; use shuttle_runtime::SecretStore; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::sync::Arc; pub mod config; pub mod events; @@ -7,16 +14,52 @@ pub mod general_commands; pub mod slash_commands; use config::setup::setup; +pub struct CustomService { + discord_bot: Client, + router: Router, +} + +#[shuttle_runtime::async_trait] +impl shuttle_runtime::Service for CustomService { + async fn bind(mut self, addr: SocketAddr) -> Result<(), shuttle_runtime::Error> { + let router = self.router.into_service(); + + let listener = tokio::net::TcpListener::bind(&addr).await?; + let serve_router = async move { + axum::serve(listener, router.into_make_service()) + .await + .unwrap(); + }; + + tokio::select! { + _ = self.discord_bot.start_autosharded() => {}, + _ = serve_router => {}, + }; + + Ok(()) + } +} + +fn build_router(ctx: Arc) -> Router { + Router::new() + .route("/daily_challenge", axum::routing::post(run_daily_challenge)) + .with_state(ctx) +} + #[shuttle_runtime::main] -async fn serenity( +async fn init( #[shuttle_runtime::Secrets] secret_store: SecretStore, -) -> shuttle_serenity::ShuttleSerenity { +) -> Result { let Ok(_) = color_eyre::install() else { panic!("Failed to install color_eyre"); }; let public_folder = PathBuf::from("static"); - let client = setup(secret_store, public_folder).await?; + let discord_bot = setup(secret_store, public_folder).await?; + let router = build_router(discord_bot.http.clone()); - Ok(client.into()) + Ok(CustomService { + discord_bot, + router, + }) }