this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add httpservver

authored by

aprlfm and committed by
isabel
3d707f93 ac3d07c7

+146 -1
+1
Cargo.lock
··· 209 209 "regex", 210 210 "reqwest 0.12.15", 211 211 "serde", 212 + "serde_json", 212 213 "serenity", 213 214 "tokio", 214 215 ]
+1
Cargo.toml
··· 20 20 poise = "0.6.1" 21 21 rand = "0.9.0" 22 22 regex = "1.11.1" 23 + serde_json = "1.0.140" 23 24 serenity = "0.12.4" 24 25 25 26 [dependencies.serde]
+133
src/http_server.rs
··· 1 + use crate::types::Data; 2 + use poise::serenity_prelude::{Context, GuildId}; 3 + use serde::Serialize; 4 + use std::collections::HashMap; 5 + use std::sync::Arc; 6 + use tokio::io::{AsyncReadExt, AsyncWriteExt}; 7 + use tokio::net::{TcpListener, TcpStream}; 8 + 9 + #[derive(Serialize)] 10 + struct MemberInfo { 11 + id: String, 12 + username: String, 13 + avatar_url: Option<String>, 14 + } 15 + 16 + #[derive(Serialize)] 17 + struct MembersResponse { 18 + guild_id: String, 19 + members: Vec<MemberInfo>, 20 + } 21 + 22 + pub async fn start_http_server( 23 + ctx: Arc<Context>, 24 + data: Arc<Data>, 25 + port: u16, 26 + ) -> color_eyre::eyre::Result<()> { 27 + let addr = format!("127.0.0.1:{}", port); 28 + let listener = TcpListener::bind(&addr).await?; 29 + 30 + println!("HTTP server listening on http://{}", addr); 31 + 32 + loop { 33 + let (stream, _) = listener.accept().await?; 34 + let ctx = Arc::clone(&ctx); 35 + let data = Arc::clone(&data); 36 + 37 + tokio::spawn(async move { 38 + if let Err(e) = handle_connection(stream, ctx, data).await { 39 + eprintln!("Error handling connection: {}", e); 40 + } 41 + }); 42 + } 43 + } 44 + 45 + async fn handle_connection( 46 + mut stream: TcpStream, 47 + ctx: Arc<Context>, 48 + _data: Arc<Data>, 49 + ) -> color_eyre::eyre::Result<()> { 50 + let mut buffer = [0; 1024]; 51 + stream.read(&mut buffer).await?; 52 + 53 + let request = String::from_utf8_lossy(&buffer[..]); 54 + let request_line = request.lines().next().unwrap_or(""); 55 + 56 + if request_line.starts_with("GET /members") { 57 + let guild_id = extract_guild_id_from_request(request_line) 58 + .unwrap_or(GuildId::new(1095080242219073606)); 59 + 60 + match get_guild_members(&ctx, guild_id).await { 61 + Ok(response_body) => { 62 + let response = format!( 63 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}", 64 + response_body.len(), 65 + response_body 66 + ); 67 + stream.write_all(response.as_bytes()).await?; 68 + } 69 + Err(e) => { 70 + let error_body = format!(r#"{{"error": "Failed to fetch members: {}"}}"#, e); 71 + let response = format!( 72 + "HTTP/1.1 500 Internal Server Error\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}", 73 + error_body.len(), 74 + error_body 75 + ); 76 + stream.write_all(response.as_bytes()).await?; 77 + } 78 + } 79 + } else { 80 + let not_found = r#"{"error": "Not found. Use /members?guild_id=YOUR_GUILD_ID"}"#; 81 + let response = format!( 82 + "HTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}", 83 + not_found.len(), 84 + not_found 85 + ); 86 + stream.write_all(response.as_bytes()).await?; 87 + } 88 + 89 + Ok(()) 90 + } 91 + 92 + fn extract_guild_id_from_request(request_line: &str) -> Option<GuildId> { 93 + if let Some(query_start) = request_line.find('?') { 94 + let query = &request_line[query_start + 1..]; 95 + let params: HashMap<&str, &str> = query 96 + .split('&') 97 + .filter_map(|param| { 98 + let mut parts = param.split('='); 99 + Some((parts.next()?, parts.next()?)) 100 + }) 101 + .collect(); 102 + 103 + if let Some(guild_id_str) = params.get("guild_id") { 104 + if let Ok(guild_id) = guild_id_str.parse::<u64>() { 105 + return Some(GuildId::new(guild_id)); 106 + } 107 + } 108 + } 109 + None 110 + } 111 + 112 + async fn get_guild_members( 113 + ctx: &Context, 114 + guild_id: GuildId, 115 + ) -> color_eyre::eyre::Result<String> { 116 + let members = guild_id.members(&ctx.http, None, None).await?; 117 + 118 + let member_infos: Vec<MemberInfo> = members 119 + .iter() 120 + .map(|member| MemberInfo { 121 + id: member.user.id.to_string(), 122 + username: member.user.name.clone(), 123 + avatar_url: member.user.avatar_url(), 124 + }) 125 + .collect(); 126 + 127 + let response = MembersResponse { 128 + guild_id: guild_id.to_string(), 129 + members: member_infos, 130 + }; 131 + 132 + Ok(serde_json::to_string_pretty(&response)?) 133 + }
+11 -1
src/main.rs
··· 1 1 mod commands; 2 2 mod event_handler; 3 + mod http_server; 3 4 mod types; 4 5 5 6 use dotenv::dotenv; 6 - use std::env; 7 + use std::{env, sync::Arc}; 7 8 8 9 use color_eyre::eyre::Result; 9 10 use poise::serenity_prelude::{ActivityData, ClientBuilder, GatewayIntents}; ··· 60 61 ctx.set_activity(Some(ActivityData::custom("new bot, who dis?"))); 61 62 62 63 poise::builtins::register_globally(ctx, &framework.options().commands).await?; 64 + 65 + // h tee tee pee 66 + let ctx_clone = Arc::new(ctx.clone()); 67 + let data_clone = Arc::new(types::Data::new()); 68 + tokio::spawn(async move { 69 + if let Err(e) = http_server::start_http_server(ctx_clone, data_clone, 3000).await { 70 + eprintln!("HTTP server error: {}", e); 71 + } 72 + }); 63 73 64 74 Ok(types::Data::new()) 65 75 })