silly goober bot

feat: git instance compatable with github source tree thing ig

+73 -65
+73 -65
src/event_handler/code_expantion.rs
··· 10 10 if let FullEvent::Message { new_message } = event { 11 11 let mut embeds: Vec<serenity::CreateEmbed> = Vec::new(); 12 12 13 - embeds.extend(github_compatable_embeds(new_message.content.to_owned()).await?); 13 + embeds.extend(github_compatable_embeds(new_message.content.clone()).await?); 14 14 15 15 if !embeds.is_empty() { 16 16 new_message ··· 25 25 26 26 async fn github_compatable_embeds(msg: String) -> Result<Vec<serenity::CreateEmbed>> { 27 27 let re = Regex::new( 28 - r"https?://github\.com/(?P<repo>[\w-]+/[\w.-]+)/blob/(?P<reference>\S+?)/(?P<file>\S+)#L(?P<start>\d+)(?:[~-]L?(?P<end>\d+)?)?" 28 + r"https?://(?P<host>(git.*|codeberg\.org))/(?P<repo>[\w-]+/[\w.-]+)/(blob|(src/(commit|branch)))?/(?P<reference>\S+?)/(?P<file>\S+)#L(?P<start>\d+)(?:[~-]L?(?P<end>\d+)?)?" 29 29 ).unwrap(); 30 30 31 31 let mut embeds: Vec<serenity::CreateEmbed> = Vec::new(); 32 32 33 - if re.captures(msg.as_str()).is_some() { 34 - let mut code_blocks = Vec::new(); 33 + let mut code_blocks = Vec::new(); 34 + for caps in re.captures_iter(&msg) { 35 + let host = &caps["host"]; 36 + let repo = &caps["repo"]; 37 + let reference = &caps["reference"]; 38 + let file = &caps["file"]; 39 + let language = file.split('.').last().unwrap_or("").to_string(); 35 40 36 - for caps in re.captures_iter(&msg) { 37 - let full_url = caps.get(0).unwrap().as_str(); 41 + let start = caps["start"].parse::<usize>().unwrap_or(1); 42 + let end = caps 43 + .name("end") 44 + .and_then(|end| end.as_str().parse::<usize>().ok()) 45 + .unwrap_or(start); 38 46 39 - let repo = caps["repo"].to_owned(); 40 - let reference = caps["reference"].to_owned(); 41 - let file = caps["file"].to_owned(); 42 - let language = file.split('.').last().unwrap_or("").to_string(); 47 + let raw_url; 48 + if host == "github.com" { 49 + raw_url = format!("https://raw.githubusercontent.com/{repo}/{reference}/{file}"); 50 + } else { 51 + let refer; 52 + if reference.len() == 40 { 53 + refer = format!("commit/{reference}"); 54 + } else { 55 + refer = format!("branch/{reference}"); 56 + } 57 + // we assume its gitea/forgejo 58 + raw_url = format!("https://{host}/{repo}/raw/{refer}/{file}"); 59 + } 60 + let response = Client::new().get(&raw_url).send().await; 43 61 44 - let start = caps["start"].parse::<usize>().unwrap_or(1); 45 - let end = caps["end"].parse::<usize>().unwrap_or(start); 46 - 47 - let raw_url = format!( 48 - "https://raw.githubusercontent.com/{}/{}/{}", 49 - repo, reference, file 50 - ); 51 - let response = Client::new().get(&raw_url).send().await; 52 - 53 - if let Ok(response) = response { 54 - if !response.status().is_success() { 55 - println!("Failed to fetch {} contents.", full_url); 56 - continue; 57 - } 62 + if let Ok(response) = response { 63 + if !response.status().is_success() { 64 + println!("Failed to fetch content."); 65 + continue; 66 + } 58 67 59 - let text = response.text().await?; 60 - let mut content = text 61 - .lines() 62 - .skip(start - 1) 63 - .take(end - start + 1) 64 - .collect::<Vec<&str>>() 65 - .join("\n"); 68 + let text = response.text().await?; 69 + let mut content = text 70 + .lines() 71 + .skip(start - 1) 72 + .take(end - start + 1) 73 + .collect::<Vec<&str>>() 74 + .join("\n"); 66 75 67 - let mut lines_sliced = 0; 76 + let mut lines_sliced = 0; 68 77 69 - while content.len() > 1950 { 70 - let lines = content.lines().collect::<Vec<&str>>(); 71 - if lines.len() == 1 { 72 - content = content.chars().take(1950).collect(); 73 - break; 74 - } 75 - content = lines[..lines.len() - 1].join("\n"); 76 - lines_sliced += 1; 78 + while content.len() > 1950 { 79 + let lines = content.lines().collect::<Vec<&str>>(); 80 + if lines.len() == 1 { 81 + content = content.chars().take(1950).collect(); 82 + break; 77 83 } 78 - let end = end - lines_sliced; 84 + content = lines[..lines.len() - 1].join("\n"); 85 + lines_sliced += 1; 86 + } 87 + let end = end - lines_sliced; 79 88 80 - let name = format!( 81 - "{repo}@{} {file} L{start}{}", 82 - if reference.len() == 40 { 83 - &reference[..8] 84 - } else { 85 - &reference 86 - }, 87 - if end > start { 88 - format!("-{}", end) 89 - } else { 90 - "".to_string() 91 - } 92 - ); 89 + let name = format!( 90 + "{repo}@{} {file} L{start}{}", 91 + if reference.len() == 40 { 92 + &reference[..8] 93 + } else { 94 + &reference 95 + }, 96 + if end > start { 97 + format!("-{end}") 98 + } else { 99 + String::new() 100 + } 101 + ); 93 102 94 - let body = if lines_sliced > 0 { 95 - format!("... ({} lines not displayed)", lines_sliced) 96 - } else { 97 - "".to_string() 98 - }; 103 + let body = if lines_sliced > 0 { 104 + format!("... ({lines_sliced} lines not displayed)") 105 + } else { 106 + String::new() 107 + }; 99 108 100 - code_blocks.push((name, language, content, body)); 101 - } 109 + code_blocks.push((name, language, content, body)); 102 110 } 103 111 104 112 code_blocks.retain(|(_, _, content, _)| !content.trim().is_empty()); 105 113 106 114 if !code_blocks.is_empty() { 107 - embeds.extend(generic_codeblocks_to_embed(code_blocks)); 115 + embeds.extend(generic_codeblocks_to_embed(&code_blocks)); 108 116 } 109 117 } 110 118 ··· 113 121 114 122 type CodeBlock = (String, String, String, String); 115 123 116 - fn generic_codeblocks_to_embed(codeblocks: Vec<CodeBlock>) -> Vec<serenity::CreateEmbed> { 124 + fn generic_codeblocks_to_embed(codeblocks: &[CodeBlock]) -> Vec<serenity::CreateEmbed> { 117 125 codeblocks 118 - .into_iter() 126 + .iter() 119 127 .map(|(name, language, content, body)| { 120 128 serenity::CreateEmbed::default() 121 129 .title(name) 122 - .description(format!("```{}\n{}\n```", language, content).to_owned()) 130 + .description(format!("```{language}\n{content}\n```").to_owned()) 123 131 .footer(serenity::CreateEmbedFooter::new(body)) 124 132 }) 125 133 .collect::<Vec<_>>()