Skip to content

Commit 1be8821

Browse files
Merge branch 'master' into perf
2 parents 32f97bb + 4b06939 commit 1be8821

7 files changed

Lines changed: 174 additions & 38 deletions

File tree

flake.lock

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@
1111

1212
importPkgs = pkgs: import pkgs { inherit system; };
1313

14-
pkgs = import nixpkgs {
15-
inherit system;
16-
config.allowUnfree = true;
17-
};
14+
pkgs = importPkgs nixpkgs;
1815

1916
in rec {
2017

src/commands/mod.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ pub(crate) const ERROR_COLOUR: Colour = Colour(0xe74c3c);
88
use crate::{Context, Error};
99

1010
use poise::serenity_prelude::{
11-
self as serenity, Colour, ComponentInteractionCollector, CreateActionRow, CreateButton,
12-
CreateEmbed, CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseMessage,
11+
self as serenity, Colour, ComponentInteraction, ComponentInteractionCollector, CreateActionRow,
12+
CreateButton, CreateEmbed, CreateEmbedFooter, CreateInteractionResponse,
13+
CreateInteractionResponseMessage,
1314
};
1415
use poise::CreateReply;
1516

@@ -49,6 +50,20 @@ pub async fn respond_err(ctx: &Context<'_>, title: &str, content: &str) {
4950
respond_embed(ctx, embed, false).await;
5051
}
5152

53+
pub async fn interaction_err(ctx: &serenity::Context, press: &ComponentInteraction, content: &str) {
54+
let builder = CreateInteractionResponse::Message(
55+
CreateInteractionResponseMessage::new()
56+
.embed(
57+
CreateEmbed::new()
58+
.title("Unable to execute interaction")
59+
.description(content)
60+
.colour(ERROR_COLOUR),
61+
)
62+
.ephemeral(true),
63+
);
64+
let _ = press.create_response(ctx, builder).await;
65+
}
66+
5267
pub async fn paginate_lists<U, E>(
5368
ctx: poise::Context<'_, U, E>,
5469
pages: &[Vec<(String, String, bool)>],

src/commands/snippets.rs

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use crate::{
44
Context, Error,
55
};
66
use ::serenity::futures::{Stream, StreamExt};
7-
use poise::serenity_prelude::{futures, CreateAttachment, CreateEmbed};
7+
use poise::serenity_prelude::{
8+
self as serenity, futures, CreateAttachment, CreateEmbed, CreateInteractionResponse,
9+
CreateInteractionResponseMessage,
10+
};
811

912
async fn autocomplete_snippet<'a>(
1013
ctx: Context<'a>,
@@ -17,14 +20,12 @@ async fn autocomplete_snippet<'a>(
1720
.unwrap()
1821
.snippets
1922
.iter()
20-
.take(25)
2123
.map(|s| format!("{}: {}", s.id, s.title))
2224
.collect()
2325
};
2426

2527
futures::stream::iter(snippet_list)
26-
.filter(move |name| futures::future::ready(name.starts_with(partial)))
27-
.map(|name| name.to_string())
28+
.filter(move |name| futures::future::ready(name.contains(partial)))
2829
}
2930

3031
/// Show a snippet
@@ -150,10 +151,7 @@ pub async fn remove_snippet(
150151
) -> Result<(), Error> {
151152
match get_snippet_lazy(&ctx, &id).await {
152153
Some(snippet) => {
153-
rm_snippet(&ctx, &snippet).await;
154-
let title = &"Snippet successfully removed";
155-
let content = &&format!("Removed snippet '{}: {}'", snippet.id, snippet.title);
156-
respond_ok(&ctx, title, content).await;
154+
remove_snippet_confirm(&ctx, &snippet).await?;
157155
}
158156
None => {
159157
let title = &"Failed to remove snippet";
@@ -213,10 +211,8 @@ pub async fn export_snippet(
213211
) -> Result<(), Error> {
214212
match get_snippet_lazy(&ctx, &id).await {
215213
Some(snippet) => {
216-
let attachment = CreateAttachment::bytes(
217-
format!("{}", &snippet.content.replace('\n', r"\n")),
218-
"snippet.txt",
219-
);
214+
let attachment =
215+
CreateAttachment::bytes(snippet.content.replace('\n', r"\n"), "snippet.txt");
220216
let message = poise::CreateReply::default()
221217
.attachment(attachment)
222218
.embed(snippet.embed());
@@ -280,3 +276,83 @@ async fn rm_snippet(ctx: &Context<'_>, snippet: &Snippet) {
280276
rwlock_guard.snippets.remove(index);
281277
rwlock_guard.write();
282278
}
279+
280+
async fn remove_snippet_confirm(ctx: &Context<'_>, snippet: &Snippet) -> Result<(), Error> {
281+
let snippet_embed = snippet.embed();
282+
283+
let ctx_id = ctx.id();
284+
let delete_id = format!("{}cancel", ctx_id);
285+
let cancel_id = format!("{}delete", ctx_id);
286+
287+
let components = serenity::CreateActionRow::Buttons(vec![
288+
serenity::CreateButton::new(&cancel_id).label("Cancel"),
289+
serenity::CreateButton::new(&delete_id)
290+
.label("Delete")
291+
.style(serenity::ButtonStyle::Danger),
292+
]);
293+
294+
let builder: poise::CreateReply = poise::CreateReply::default()
295+
.content(format!("Are you sure you want to delete snippet `{}`?", snippet.id))
296+
.ephemeral(true)
297+
.embed(snippet_embed)
298+
.components(vec![components]);
299+
300+
ctx.send(builder).await?;
301+
302+
while let Some(press) = serenity::ComponentInteractionCollector::new(ctx)
303+
.filter(move |press| press.data.custom_id.starts_with(&ctx_id.to_string()))
304+
.timeout(std::time::Duration::from_secs(60))
305+
.await
306+
{
307+
if press.data.custom_id == delete_id {
308+
handle_delete(ctx, snippet, press).await?;
309+
} else if press.data.custom_id == cancel_id {
310+
handle_cancel(ctx, press).await?;
311+
}
312+
}
313+
314+
Ok(())
315+
}
316+
317+
async fn handle_delete(
318+
ctx: &Context<'_>,
319+
snippet: &Snippet,
320+
interaction: serenity::ComponentInteraction,
321+
) -> Result<(), Error> {
322+
323+
rm_snippet(ctx, snippet).await;
324+
interaction
325+
.create_response(
326+
ctx,
327+
CreateInteractionResponse::UpdateMessage(
328+
CreateInteractionResponseMessage::new().content("Deleted!")
329+
.embeds(vec![])
330+
.components(vec![]),
331+
),
332+
)
333+
.await?;
334+
335+
let title = format!("{} removed a snippet", ctx.author().tag());
336+
let content = &&format!("Removed snippet `{}`", snippet.format_output());
337+
respond_ok(ctx, &title, content).await;
338+
339+
Ok(())
340+
}
341+
342+
async fn handle_cancel(
343+
ctx: &Context<'_>,
344+
interaction: serenity::ComponentInteraction,
345+
) -> Result<(), Error> {
346+
interaction
347+
.create_response(
348+
ctx,
349+
CreateInteractionResponse::UpdateMessage(
350+
CreateInteractionResponseMessage::new()
351+
.content("Aborted.")
352+
.embeds(vec![])
353+
.components(vec![]),
354+
),
355+
)
356+
.await?;
357+
Ok(())
358+
}

src/commands/utils.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@ async fn autocomplete_key<'a>(
1919
.unwrap()
2020
.issue_prefixes
2121
.iter()
22-
.take(25)
2322
.map(|s| s.0.clone())
2423
.collect()
2524
};
2625

2726
futures::stream::iter(snippet_list)
28-
.filter(move |name| futures::future::ready(name.starts_with(partial)))
29-
.map(|name| name.to_string())
27+
.filter(move |name| futures::future::ready(name.contains(partial)))
3028
}
3129

3230
/// Create an embed in the current channel.
@@ -379,7 +377,7 @@ pub async fn list_repos(ctx: Context<'_>) -> Result<(), Error> {
379377
.map(|token| {
380378
(
381379
token.0.clone(),
382-
format!("{}/{}", token.1.name, token.1.owner),
380+
format!("{}/{}", token.1.owner, token.1.name),
383381
true,
384382
)
385383
})

src/events/issue.rs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::time::Duration;
22

3-
use crate::{structures::Embeddable, Data};
3+
use crate::{commands::interaction_err, structures::Embeddable, Data};
44
use ::serenity::builder::CreateEmbedAuthor;
55
use octocrab::models::issues::Issue;
66
use octocrab::models::pulls::PullRequest;
77
use poise::serenity_prelude::{
8-
self as serenity, Colour, Context, CreateEmbed, Message, Permissions,
8+
self as serenity, ButtonStyle, Colour, Context, CreateActionRow, CreateButton, CreateEmbed,
9+
CreateInteractionResponse, Message, Permissions,
910
};
1011
use regex::Regex;
1112

@@ -23,11 +24,15 @@ pub async fn message(data: &Data, ctx: &Context, message: &Message) {
2324
let ctx_id = message.id.get(); // poise context isn't available here.
2425
let remove_id = format!("{}remove", ctx_id);
2526
let hide_body_id = format!("{}hide_body", ctx_id);
27+
let remove = CreateActionRow::Buttons(vec![CreateButton::new(&remove_id)
28+
.label("delete")
29+
.style(ButtonStyle::Danger)]);
30+
2631
let components = serenity::CreateActionRow::Buttons(vec![
27-
serenity::CreateButton::new(&remove_id)
32+
CreateButton::new(&remove_id)
2833
.label("delete")
29-
.style(serenity::ButtonStyle::Danger),
30-
serenity::CreateButton::new(&hide_body_id).label("hide body"),
34+
.style(ButtonStyle::Danger),
35+
CreateButton::new(&hide_body_id).label("hide body"),
3136
]);
3237

3338
let content: serenity::CreateMessage = serenity::CreateMessage::default()
@@ -56,12 +61,19 @@ pub async fn message(data: &Data, ctx: &Context, message: &Message) {
5661
&& (press.user.id == message.author.id || has_perms)
5762
{
5863
let _ = press
59-
.create_response(ctx, serenity::CreateInteractionResponse::Acknowledge)
64+
.create_response(ctx, CreateInteractionResponse::Acknowledge)
6065
.await;
6166
if let Ok(ref msg) = msg_result {
6267
let _ = msg.delete(ctx).await;
6368
}
6469
msg_deleted = true;
70+
} else {
71+
interaction_err(
72+
ctx,
73+
&press,
74+
"Unable to use interaction because you are missing `MANAGE_MESSAGES`.",
75+
)
76+
.await;
6577
}
6678

6779
if press.data.custom_id == hide_body_id
@@ -82,12 +94,20 @@ pub async fn message(data: &Data, ctx: &Context, message: &Message) {
8294
ctx,
8395
serenity::CreateInteractionResponse::UpdateMessage(
8496
serenity::CreateInteractionResponseMessage::new()
85-
.embeds(hid_body_embeds),
97+
.embeds(hid_body_embeds)
98+
.components(vec![remove.clone()]),
8699
),
87100
)
88101
.await;
89102
}
90103
body_hid = true;
104+
} else {
105+
interaction_err(
106+
ctx,
107+
&press,
108+
"Unable to use interaction because you are missing `MANAGE_MESSAGES`.",
109+
)
110+
.await;
91111
}
92112
}
93113
// Triggers on timeout.
@@ -98,7 +118,6 @@ pub async fn message(data: &Data, ctx: &Context, message: &Message) {
98118
.await;
99119
}
100120
}
101-
//
102121
}
103122
}
104123

@@ -107,8 +126,7 @@ async fn issue_embeds(data: &Data, message: &Message) -> Option<Vec<CreateEmbed>
107126
let client = octocrab::instance();
108127
let ratelimit = client.ratelimit();
109128

110-
let regex =
111-
Regex::new(r#" ?([a-zA-Z0-9-_.]+)?#([0-9]+[0-9]) ?"#).expect("Expected numbers regex");
129+
let regex = Regex::new(r#" ?([a-zA-Z0-9-_.]+)?#([0-9]+) ?"#).expect("Expected numbers regex");
112130

113131
let custom_repos = { data.state.read().unwrap().issue_prefixes.clone() };
114132

@@ -126,7 +144,9 @@ async fn issue_embeds(data: &Data, message: &Message) -> Option<Vec<CreateEmbed>
126144

127145
issues = client.issues(owner, repo);
128146
prs = client.pulls(owner, repo);
129-
}
147+
} else {
148+
continue; // discards when it doesn't match a repo.
149+
};
130150
}
131151

132152
let ratelimit = ratelimit

src/main.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,38 @@ type Context<'a> = poise::Context<'a, Data, Error>;
2121
async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
2222
match error {
2323
poise::FrameworkError::Setup { error, .. } => panic!("Failed to start bot: {:?}", error),
24-
poise::FrameworkError::Command { error, ctx, .. } => {
25-
println!("Error in command `{}`: {:?}", ctx.command().name, error,);
24+
poise::FrameworkError::Command { ctx, error, .. } => {
25+
let error = error.to_string();
26+
eprintln!("An error occured in a command: {}", error);
27+
commands::respond_err(&ctx, "Command Error", &error).await;
2628
}
29+
30+
poise::FrameworkError::ArgumentParse {
31+
error, input, ctx, ..
32+
} => {
33+
let usage = match &ctx.command().help_text {
34+
Some(help_text) => &**help_text,
35+
None => "Please check the help menu for usage information",
36+
};
37+
let response = if let Some(input) = input {
38+
format!(
39+
"**Cannot parse `{}` as argument: {}**\n{}",
40+
input, error, usage
41+
)
42+
} else {
43+
format!("### {}\n{}", error, usage)
44+
};
45+
commands::respond_err(&ctx, "Argument Parsing Error", &response).await;
46+
}
47+
poise::FrameworkError::GuildOnly { ctx, .. } => {
48+
commands::respond_err(
49+
&ctx,
50+
"This command cannot be ran in DMs.",
51+
"You cannot run this command in DMs.",
52+
)
53+
.await;
54+
}
55+
2756
error => {
2857
if let Err(e) = poise::builtins::on_error(error).await {
2958
println!("Error while handling error: {}", e)

0 commit comments

Comments
 (0)