From 601a9667bfccd410ef0810c371a4fb5572f97788 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Wed, 18 Oct 2023 00:16:46 +0800 Subject: [PATCH] feat: add GitHub link expansion --- Cargo.lock | 13 ++-- Cargo.toml | 1 + src/handlers/github_expansion.rs | 105 +++++++++++++++++++++++++++++++ src/handlers/mod.rs | 10 +++ src/main.rs | 15 ++++- 5 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 src/handlers/github_expansion.rs create mode 100644 src/handlers/mod.rs diff --git a/Cargo.lock b/Cargo.lock index e818ad1..9bed83d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,9 +1006,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.0" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -1018,9 +1018,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -1029,9 +1029,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" @@ -1627,6 +1627,7 @@ dependencies = [ "once_cell", "owo-colors", "poise", + "regex", "reqwest", "serde", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 76c3066..6574a9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ num = "0.4.1" once_cell = "1.18.0" owo-colors = { version = "3.5.0", features = ["supports-colors"] } poise = { git = "https://github.com/serenity-rs/poise.git", branch = "serenity-next", version = "0.5.5" } +regex = "1.10.2" reqwest = { version = "0.11.22", default-features = false, features = [ "rustls-tls", "json", diff --git a/src/handlers/github_expansion.rs b/src/handlers/github_expansion.rs new file mode 100644 index 0000000..1383a6e --- /dev/null +++ b/src/handlers/github_expansion.rs @@ -0,0 +1,105 @@ +use poise::serenity_prelude as serenity; + +use anyhow::{anyhow, Result}; +use once_cell::sync::Lazy; +use regex::Regex; + +use crate::reqwest_client; + +static GITHUB: Lazy = Lazy::new(|| { + Regex::new(r"https?://github\.com/(?P[\w-]+/[\w.-]+)/blob/(?P.+?)/(?P.*\.(?:(?P\w+))?)#L(?P\d+)(?:[~-]L?(?P\d+)?)?").unwrap() +}); + +pub async fn handle(message: &serenity::Message, ctx: &serenity::Context) -> Result<()> { + let message = message.clone(); + let mut embeds: Vec = vec![]; + + for captures in GITHUB.captures_iter(&message.content) { + let repo = captures + .name("repo") + .ok_or_else(|| anyhow!("Could not obtain `repo`"))? + .as_str(); + let ref_ = captures + .name("ref") + .ok_or_else(|| anyhow!("Could not obtain `ref`"))? + .as_str(); + let file = captures + .name("file") + .ok_or_else(|| anyhow!("Could not obtain `file`"))? + .as_str(); + + let language = if let Some(m) = captures.name("language") { + m.as_str().to_owned() + } else { + "".to_owned() + }; + + let start = captures + .name("start") + .ok_or_else(|| anyhow!("Could not obtain `start`"))? + .as_str() + .parse::()?; + let end = captures + .name("end") + .and_then(|end| end.as_str().parse::().ok()); + + let resp = reqwest_client::HTTP + .get(format!( + "https://raw.githubusercontent.com/{repo}/{ref_}/{file}" + )) + .send() + .await?; + + let lines: Vec = resp + .text() + .await? + .split("\n") + .map(|s| s.to_owned()) + .collect(); + + let idx_start = start - 1; + let idx_end = if let Some(end) = end { + end + } else { + lines.len() + }; + let selected_lines = &lines[idx_start..idx_end]; + + let embed = serenity::CreateEmbed::new() + .title(repo) + .field( + file, + "```".to_owned() + &language + "\n" + &selected_lines.join("\n") + "\n```", + true, + ) + .footer(serenity::CreateEmbedFooter::new(ref_)); + + embeds.push(embed); + } + + // { + // let msg_id = message.id; + // let mut message_updates = serenity::collector::collect(&ctx.shard, move |ev| match ev { + // serenity::Event::MessageUpdate(x) if x.id == msg_id => Some(()), + // _ => None, + // }); + // let _ = tokio::time::timeout(Duration::from_millis(2000), message_updates.next()).await; + // message + // .edit(&ctx, serenity::EditMessage::new().suppress_embeds(true)) + // .await?; + // } + + if !embeds.is_empty() { + message + .channel_id + .send_message( + &ctx, + serenity::CreateMessage::new() + .embeds(embeds) + .reference_message(&message), + ) + .await?; + }; + + Ok(()) +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs new file mode 100644 index 0000000..b54f8e1 --- /dev/null +++ b/src/handlers/mod.rs @@ -0,0 +1,10 @@ +use anyhow::Result; +use poise::serenity_prelude as serenity; + +mod github_expansion; + +pub async fn handle(message: &serenity::Message, ctx: &serenity::Context) -> Result<()> { + github_expansion::handle(message, ctx).await?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index f5b6af1..d1a9c32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use anyhow::{Error, Result}; use owo_colors::OwoColorize; use poise::{ - serenity_prelude::{Client, GatewayIntents}, + serenity_prelude::{Client, FullEvent, GatewayIntents}, Framework, FrameworkOptions, }; @@ -11,6 +11,7 @@ pub struct Data {} pub type Context<'a> = poise::Context<'a, Data, Error>; mod commands; +mod handlers; mod reqwest_client; mod utils; @@ -26,6 +27,18 @@ async fn main() -> Result<()> { .framework(Framework::new( FrameworkOptions { commands: commands::vec(), + event_handler: |ev, _, _| { + Box::pin(async move { + match ev { + FullEvent::Message { new_message, ctx } => { + handlers::handle(&new_message, &ctx).await?; + } + &_ => {} + } + + Ok(()) + }) + }, ..Default::default() }, |ctx, ready, framework| {