···1use crate::Context;
2use color_eyre::eyre::Result;
3-use poise::{serenity_prelude as serenity, CreateReply};
4use std::env;
56/// Displays information about the bot
···9 let rev = env::var("BUILD_REV").unwrap_or("unknown".to_string());
1011 let embed = CreateReply::default().embed(
12- serenity::CreateEmbed::default()
13 .title("Bot Info")
14 //.thumbnail(bot.avatar_url().expect("avatar failed"))
15 .color(0x00ff_ffff)
···1use crate::Context;
2use color_eyre::eyre::Result;
3+use poise::{serenity_prelude::CreateEmbed, CreateReply};
4use std::env;
56/// Displays information about the bot
···9 let rev = env::var("BUILD_REV").unwrap_or("unknown".to_string());
1011 let embed = CreateReply::default().embed(
12+ CreateEmbed::default()
13 .title("Bot Info")
14 //.thumbnail(bot.avatar_url().expect("avatar failed"))
15 .color(0x00ff_ffff)
+13-13
src/commands/fun/bottom.rs
···15/// Translate your words for the tops and normies to understand
16#[poise::command(slash_command, guild_only)]
17pub async fn topify(ctx: Context<'_>, #[description = "text"] input: String) -> Result<()> {
18- const MAX_LEN: usize = 1994;
19- const WRAP: &'static str = "```";
20 let out = bottom::decode_string(&input);
2122- if let Ok(out) = out {
23- let mut out = out.as_str();
24- let len = out.len();
25- for _ in 0..(len / MAX_LEN) {
26- let (x, xs) = out.split_at(MAX_LEN);
27- ctx.say(format!("{WRAP}{x}{WRAP}")).await?;
28- out = xs;
29- }
30- if len % MAX_LEN != 0 {
31- ctx.say(format!("{WRAP}{out}{WRAP}")).await?;
32- }
33 } else {
34 ctx.say("I couldn't decode that message.").await?;
35 }
···15/// Translate your words for the tops and normies to understand
16#[poise::command(slash_command, guild_only)]
17pub async fn topify(ctx: Context<'_>, #[description = "text"] input: String) -> Result<()> {
18+ const MAX_LEN: usize = 1994;
19+ const WRAP: &'static str = "```";
20 let out = bottom::decode_string(&input);
2122+ if let Ok(out) = out {
23+ let mut out = out.as_str();
24+ let len = out.len();
25+ for _ in 0..(len / MAX_LEN) {
26+ let (x, xs) = out.split_at(MAX_LEN);
27+ ctx.say(format!("{WRAP}{x}{WRAP}")).await?;
28+ out = xs;
29+ }
30+ if len % MAX_LEN != 0 {
31+ ctx.say(format!("{WRAP}{out}{WRAP}")).await?;
32+ }
33 } else {
34 ctx.say("I couldn't decode that message.").await?;
35 }
+3-3
src/commands/misc/nixpkgs.rs
···1use color_eyre::eyre::Result;
2use git_tracker::Tracker;
3-use poise::{serenity_prelude as serenity, CreateReply};
4use serde::Deserialize;
5use std::env;
6···22 let github_token = env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN not set");
23 let tracker = Tracker::from_path(&nixpkgs_path)?;
2425- let client = &ctx.data().client;
2627 // find out what commit our PR was merged in
28 let Some(commit_sha) = ({
···72 };
7374 let embed = CreateReply::default().embed(
75- serenity::CreateEmbed::new()
76 .title(format!("Nixpkgs PR #{pr} Status"))
77 .url(format!("{NIXPKGS_URL}/pull/{pr}"))
78 .description(embed_description),
···1use color_eyre::eyre::Result;
2use git_tracker::Tracker;
3+use poise::{serenity_prelude::CreateEmbed, CreateReply};
4use serde::Deserialize;
5use std::env;
6···22 let github_token = env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN not set");
23 let tracker = Tracker::from_path(&nixpkgs_path)?;
2425+ let client = &ctx.data().client;
2627 // find out what commit our PR was merged in
28 let Some(commit_sha) = ({
···72 };
7374 let embed = CreateReply::default().embed(
75+ CreateEmbed::new()
76 .title(format!("Nixpkgs PR #{pr} Status"))
77 .url(format!("{NIXPKGS_URL}/pull/{pr}"))
78 .description(embed_description),
+2-2
src/commands/user/avatar.rs
···1use crate::Context;
2use color_eyre::eyre::Result;
3-use poise::serenity_prelude as serenity;
45/// Displays your or another user's avatar
6#[poise::command(slash_command)]
7pub async fn avatar(
8 ctx: Context<'_>,
9- #[description = "Selected user"] user: Option<serenity::User>,
10) -> Result<()> {
11 let user = user.as_ref().unwrap_or_else(|| ctx.author());
12 let avatar = user.avatar_url().expect("Could not get avatar URL");
···1use crate::Context;
2use color_eyre::eyre::Result;
3+use poise::serenity_prelude::User;
45/// Displays your or another user's avatar
6#[poise::command(slash_command)]
7pub async fn avatar(
8 ctx: Context<'_>,
9+ #[description = "Selected user"] user: Option<User>,
10) -> Result<()> {
11 let user = user.as_ref().unwrap_or_else(|| ctx.author());
12 let avatar = user.avatar_url().expect("Could not get avatar URL");
+6-3
src/commands/user/whois.rs
···1use crate::Context;
2use color_eyre::eyre::Result;
3-use poise::{serenity_prelude as serenity, CreateReply};
00045/// Displays your or another user's info
6#[poise::command(slash_command)]
7pub async fn whois(
8 ctx: Context<'_>,
9- #[description = "Selected user"] user: Option<serenity::User>,
10) -> Result<()> {
11 let user = user.as_ref().unwrap_or_else(|| ctx.author());
12 let membership = ctx.guild_id().unwrap().member(ctx.http(), user.id).await?;
···15 let joined_at = membership.joined_at.unwrap().unix_timestamp();
1617 let embed = CreateReply::default().embed(
18- serenity::CreateEmbed::default()
19 .title(&user.name)
20 .thumbnail(user.avatar_url().expect("avatar failed"))
21 .color(0x00ff_ffff)
···1use crate::Context;
2use color_eyre::eyre::Result;
3+use poise::{
4+ serenity_prelude::{CreateEmbed, User},
5+ CreateReply,
6+};
78/// Displays your or another user's info
9#[poise::command(slash_command)]
10pub async fn whois(
11 ctx: Context<'_>,
12+ #[description = "Selected user"] user: Option<User>,
13) -> Result<()> {
14 let user = user.as_ref().unwrap_or_else(|| ctx.author());
15 let membership = ctx.guild_id().unwrap().member(ctx.http(), user.id).await?;
···18 let joined_at = membership.joined_at.unwrap().unix_timestamp();
1920 let embed = CreateReply::default().embed(
21+ CreateEmbed::default()
22 .title(&user.name)
23 .thumbnail(user.avatar_url().expect("avatar failed"))
24 .color(0x00ff_ffff)
+16-7
src/event_handler/code_expantion.rs
···1// the logic here is pretty much ripped from https://github.com/uncenter/discord-forum-bot/blob/main/src/modules/expandGitHubLinks.ts
2// with some modifications so I can make it work on diffrent git hosts
34-use color_eyre::eyre::{self, Result};
5use poise::serenity_prelude::{Context, FullEvent};
6use regex::Regex;
7use reqwest::Client;
89-pub async fn handle(ctx: &Context, event: &FullEvent) -> Result<()> {
10 if let FullEvent::Message { new_message } = event {
11- let code_blocks = extract_code_blocks(new_message.content.clone()).await?;
1213 if !code_blocks.is_empty() {
14 new_message
···21 Ok(())
22}
2324-async fn extract_code_blocks(msg: String) -> Result<Vec<String>> {
25 let re = Regex::new(
26- 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+)?)?",
000000000027 )?;
2829 let mut blocks: Vec<String> = Vec::new();
30- let client = Client::new();
3132 for caps in re.captures_iter(&msg) {
33 let (host, repo, reference, file, start, end) = extract_url_components(&caps)?;
···79) -> Result<String> {
80 let response = client.get(raw_url).send().await?;
81 if !response.status().is_success() {
82- return Err(eyre::eyre!("Failed to fetch content from {}", raw_url));
83 }
8485 let text = response.text().await?;
···1// the logic here is pretty much ripped from https://github.com/uncenter/discord-forum-bot/blob/main/src/modules/expandGitHubLinks.ts
2// with some modifications so I can make it work on diffrent git hosts
34+use color_eyre::eyre::{eyre, Result};
5use poise::serenity_prelude::{Context, FullEvent};
6use regex::Regex;
7use reqwest::Client;
89+pub async fn handle(ctx: &Context, event: &FullEvent, client: &Client) -> Result<()> {
10 if let FullEvent::Message { new_message } = event {
11+ let code_blocks = extract_code_blocks(new_message.content.clone(), client).await?;
1213 if !code_blocks.is_empty() {
14 new_message
···21 Ok(())
22}
2324+async fn extract_code_blocks(msg: String, client: &Client) -> Result<Vec<String>> {
25 let re = Regex::new(
26+ r#"(?x)
27+ https?://
28+ (?P<host>
29+ (git.*|codeberg\.org)) /
30+ (?P<repo> [\w-]+/[\w.-]+) /
31+ (blob|(src/(commit|branch)))? /
32+ (?P<reference> \S+?) /
33+ (?P<file> \S+) #L
34+ (?P<start> \d+)
35+ (?:[~-]L?(?P<end>\d+)?)?
36+ "#,
37 )?;
3839 let mut blocks: Vec<String> = Vec::new();
04041 for caps in re.captures_iter(&msg) {
42 let (host, repo, reference, file, start, end) = extract_url_components(&caps)?;
···88) -> Result<String> {
89 let response = client.get(raw_url).send().await?;
90 if !response.status().is_success() {
91+ return Err(eyre!("Failed to fetch content from {}", raw_url));
92 }
9394 let text = response.text().await?;
+2-2
src/event_handler/mod.rs
···11 println!("Logged in as {}", data_about_bot.user.name);
12 }
1314- code_expantion::handle(ctx, event).await?;
1516- Ok(())
17}
···11 println!("Logged in as {}", data_about_bot.user.name);
12 }
1314+ let client = &_data.client;
1516+ code_expantion::handle(ctx, event, client).await
17}
+12-11
src/main.rs
···6use std::env;
78use color_eyre::eyre::{Report, Result};
9-use poise::serenity_prelude::{self as serenity, ActivityData, GatewayIntents};
1011#[derive(Debug)]
12-pub struct Data { // User data, which is stored and accessible in all command invocations
13- client: Client,
14-}
01516pub type Context<'a> = poise::Context<'a, Data, Report>;
17···48 commands::fun::bottom::topify(),
49 commands::fun::bottom::bottomify(),
50 ],
51- event_handler: |ctx, event, _, data| Box::pin(crate::event_handler::event_handler(ctx, event, data)),
0052 ..Default::default()
53 };
54···57 Box::pin(async move {
58 ctx.set_activity(Some(ActivityData::custom("new bot, who dis?")));
5960- poise::builtins::register_globally(ctx, &framework.options().commands).await?;
6162 Ok(Data {
63- client: Client::builder()
64- .user_agent("blahaj")
65- .build()?,
66- })
67 })
68 })
69 .options(opts)
70 .build();
7172- let client = serenity::ClientBuilder::new(token, intents)
73 .framework(framework)
74 .await;
75
···6use std::env;
78use color_eyre::eyre::{Report, Result};
9+use poise::serenity_prelude::{ActivityData, ClientBuilder, GatewayIntents};
1011#[derive(Debug)]
12+// User data, which is stored and accessible in all command invocations
13+pub struct Data {
14+ client: Client,
15+}
1617pub type Context<'a> = poise::Context<'a, Data, Report>;
18···49 commands::fun::bottom::topify(),
50 commands::fun::bottom::bottomify(),
51 ],
52+ event_handler: |ctx, event, _, data| {
53+ Box::pin(crate::event_handler::event_handler(ctx, event, data))
54+ },
55 ..Default::default()
56 };
57···60 Box::pin(async move {
61 ctx.set_activity(Some(ActivityData::custom("new bot, who dis?")));
6263+ poise::builtins::register_globally(ctx, &framework.options().commands).await?;
6465 Ok(Data {
66+ client: Client::builder().user_agent("blahaj").build()?,
67+ })
0068 })
69 })
70 .options(opts)
71 .build();
7273+ let client = ClientBuilder::new(token, intents)
74 .framework(framework)
75 .await;
76