1+ use std:: borrow:: Cow ;
12use std:: time:: Duration ;
23
34use bytes:: Bytes ;
4- use serenity:: all:: prelude:: Context ;
5- use serenity:: builder:: {
6- CreateAllowedMentions , CreateCommand , CreateCommandOption , CreateInteractionResponseFollowup ,
7- CreateMessage , EditInteractionResponse ,
8- } ;
9- use serenity:: model:: Permissions ;
10- use serenity:: model:: application:: {
11- CommandInteraction , CommandOptionType , CommandType , MessageInteractionMetadata , ResolvedOption ,
12- ResolvedTarget , ResolvedValue ,
13- } ;
14- use serenity:: model:: channel:: { Message , MessageReference } ;
155use serenity:: {
6+ all:: Context ,
167 builder:: {
17- CreateActionRow , CreateAttachment , CreateButton , CreateInteractionResponse ,
18- CreateInteractionResponseMessage ,
8+ CreateActionRow , CreateAllowedMentions , CreateAttachment , CreateButton , CreateCommand ,
9+ CreateCommandOption , CreateInteractionResponse , CreateInteractionResponseFollowup ,
10+ CreateInteractionResponseMessage , CreateMessage , EditInteractionResponse ,
11+ } ,
12+ model:: {
13+ Permissions ,
14+ application:: {
15+ ButtonStyle , CommandInteraction , CommandOptionType , CommandType , ComponentInteraction ,
16+ MessageInteractionMetadata , ResolvedOption , ResolvedTarget , ResolvedValue ,
17+ } ,
18+ channel:: Message ,
1919 } ,
20- model:: application:: { ButtonStyle , ComponentInteraction } ,
2120} ;
2221
2322use crate :: commands:: eval:: code_block:: { CodeBlockResult , get_code_block} ;
2423use crate :: commands:: strip_bot_mention;
24+ use crate :: discord:: MessageExt ;
2525use crate :: { InteractionError , State } ;
2626
2727pub const ON_RERUN : & str = "on_rerun" ;
@@ -188,9 +188,11 @@ pub async fn run_slash(
188188
189189 interaction. defer ( & ctx) . await ?;
190190 let response = match submit_code ( state, CodeBlockResult :: One ( code. to_owned ( ) ) , false ) . await {
191- EvalResult :: Failure ( err) => CreateInteractionResponseFollowup :: new ( )
192- . content ( format ! ( ":bangbang: {err}" ) )
193- . ephemeral ( true ) ,
191+ EvalResult :: Failure ( err) => {
192+ // Ideally we'd use ephemeral() here, but that doesn't work. See
193+ // https://github.com/SquidDev-CC/FAQBot-CC/issues/66
194+ CreateInteractionResponseFollowup :: new ( ) . content ( format ! ( ":bangbang: {err}" ) )
195+ }
194196 EvalResult :: Success ( text, screenshot) => {
195197 // It's not possible to Rerun this code, as (AFAICT) there's no way to look
196198 // up the original code, so we just drop the buttons.
@@ -223,9 +225,11 @@ pub async fn run_message(
223225
224226 let code = code_block:: get_code_block ( & msg. content ) . owned ( ) ;
225227 let response = match submit_code ( state, code, false ) . await {
226- EvalResult :: Failure ( err) => CreateInteractionResponseFollowup :: new ( )
227- . content ( format ! ( ":bangbang: {err}" ) )
228- . ephemeral ( true ) ,
228+ EvalResult :: Failure ( err) => {
229+ // Ideally we'd use ephemeral() here, but that doesn't work. See
230+ // https://github.com/SquidDev-CC/FAQBot-CC/issues/66
231+ CreateInteractionResponseFollowup :: new ( ) . content ( format ! ( ":bangbang: {err}" ) )
232+ }
229233 EvalResult :: Success ( text, screenshot) => CreateInteractionResponseFollowup :: new ( )
230234 . content ( text)
231235 . add_file ( CreateAttachment :: bytes ( screenshot, "image.png" ) )
@@ -287,7 +291,7 @@ pub async fn eval_from_ping(
287291///
288292/// Moderators and the original author (either of the interaction or the target
289293/// message) can use these controls.
290- fn can_interact ( interaction : & ComponentInteraction ) -> bool {
294+ fn can_interact ( interaction : & ComponentInteraction , original_message : Option < & Message > ) -> bool {
291295 if interaction
292296 . member
293297 . as_ref ( )
@@ -311,23 +315,34 @@ fn can_interact(interaction: &ComponentInteraction) -> bool {
311315
312316 // If this is a message command, and then the author of the original message
313317 // can also run this.
314- if let Some ( message) = & interaction. message . referenced_message
315- && message. author == interaction. user
316- {
317- tracing:: info!( "User wrote the original message " ) ;
318+ if original_message. is_some_and ( |x| x. author == interaction. user ) {
319+ tracing:: debug!( "User wrote the original message " ) ;
318320 return true ;
319321 }
320322
321- tracing:: warn!( "User does not have permission" ) ;
323+ tracing:: warn!(
324+ author = original_message. map( |x| & x. author. name) ,
325+ "User does not have permission"
326+ ) ;
322327 false
323328}
324329
325- async fn check_interact (
326- ctx : & Context ,
327- interaction : & ComponentInteraction ,
328- ) -> Option < Result < ( ) , InteractionError > > {
329- if !can_interact ( interaction) {
330- Some (
330+ /// Get the original message containing the code for this component interaction,
331+ /// then check the user can actually perform the interaction.
332+ async fn check_interaction < ' a > (
333+ ctx : & ' a Context ,
334+ interaction : & ' a ComponentInteraction ,
335+ ) -> Result < Option < Cow < ' a , Message > > , Result < ( ) , InteractionError > > {
336+ let original_message = match interaction. message . find_reply ( ctx) . await {
337+ Ok ( x) => x,
338+ Err ( err) => {
339+ tracing:: error!( ?err, "Failed to find original message" ) ;
340+ None
341+ }
342+ } ;
343+
344+ if !can_interact ( interaction, original_message. as_deref ( ) ) {
345+ Err (
331346 interaction
332347 . create_response (
333348 & ctx,
@@ -341,40 +356,7 @@ async fn check_interact(
341356 . map_err ( Into :: into) ,
342357 )
343358 } else {
344- None
345- }
346- }
347-
348- /// Find the original code for a "Rerun" command.
349- async fn rerun_find_original_code (
350- ctx : & Context ,
351- interaction : & ComponentInteraction ,
352- ) -> Option < CodeBlockResult < String > > {
353- fn get_code ( ctx : & Context , message : & str ) -> CodeBlockResult < String > {
354- let message = strip_bot_mention ( ctx, message) . unwrap_or ( message) ;
355- get_code_block ( message) . owned ( )
356- }
357-
358- if let Some ( reply_to) = & interaction. message . referenced_message {
359- // If we're a reply and we have a referenced_message, use that.
360- Some ( get_code ( ctx, & reply_to. content ) )
361- } else if let Some ( MessageReference {
362- channel_id,
363- message_id : Some ( message_id) ,
364- ..
365- } ) = & interaction. message . message_reference
366- {
367- // If we're a reply (and don't have a referenced message), look the message up.
368- if let Some ( message) = ctx. cache . message ( channel_id, message_id) {
369- Some ( get_code ( ctx, & message. content ) )
370- } else if let Ok ( message) = ctx. http . get_message ( * channel_id, * message_id) . await {
371- Some ( get_code ( ctx, & message. content ) )
372- } else {
373- None
374- }
375- } else {
376- // Otherwise we can't find the message content.
377- None
359+ Ok ( original_message)
378360 }
379361}
380362
@@ -384,29 +366,31 @@ pub async fn rerun(
384366 ctx : & Context ,
385367 interaction : & ComponentInteraction ,
386368) -> Result < ( ) , InteractionError > {
387- if let Some ( res) = check_interact ( ctx, interaction) . await {
388- return res;
389- }
390-
391- let Some ( code) = rerun_find_original_code ( ctx, interaction) . await else {
392- interaction
393- . create_response (
394- & ctx,
395- CreateInteractionResponse :: Message (
396- CreateInteractionResponseMessage :: new ( )
397- . content ( "Cannot find the original message!" )
398- . ephemeral ( true ) ,
399- ) ,
400- )
401- . await ?;
402-
403- return Ok ( ( ) ) ;
369+ let message = match check_interaction ( ctx, interaction) . await {
370+ Ok ( Some ( x) ) => x,
371+ Ok ( None ) => {
372+ return interaction
373+ . create_response (
374+ & ctx,
375+ CreateInteractionResponse :: Message (
376+ CreateInteractionResponseMessage :: new ( )
377+ . content ( ":bangbang: Cannot find the original message!" )
378+ . ephemeral ( true ) ,
379+ ) ,
380+ )
381+ . await
382+ . map_err ( Into :: into) ;
383+ }
384+ Err ( err) => return err,
404385 } ;
405386
406387 interaction
407388 . create_response ( & ctx, CreateInteractionResponse :: Acknowledge )
408389 . await ?;
409390
391+ let message = strip_bot_mention ( ctx, & message. content ) . unwrap_or ( & message. content ) ;
392+ let code = get_code_block ( message) . owned ( ) ;
393+
410394 match submit_code ( state, code, false ) . await {
411395 EvalResult :: Failure ( err) => {
412396 interaction
@@ -433,9 +417,9 @@ pub async fn delete(
433417 ctx : & Context ,
434418 interaction : & ComponentInteraction ,
435419) -> Result < ( ) , InteractionError > {
436- if let Some ( res ) = check_interact ( ctx, interaction) . await {
437- return res ;
438- }
420+ if let Err ( err ) = check_interaction ( ctx, interaction) . await {
421+ return err ;
422+ } ;
439423
440424 interaction. message . delete ( & ctx) . await ?;
441425 interaction
0 commit comments