Delayed response to slash command with Mojolicious in Perl

Viewed 175

I am trying to create a slack application in Perl with mojolicious and I am having the following use case:

Slack sends a request to my API from a slash command and needs a response in a 3 seconds timeframe. However, Slack also gives me the opportunity to send up to 5 more responses in a 30 minute timeframe but still needs an initial response in 3 seconds (it just sends a "late_response_url" in the initial call back so that I could POST something to that url later on). In my case I would like to send an initial response to slack to inform the user that the operation is "running" and after a while send the actual outcome of my slow function to Slack.

Currently, I can do this by spawning a second process using fork() and using one process to respond imidiately as Slack dictates and the second to do the rest of the work and respond later on.

I am trying to do this with Mojolicious' subprocesses to avoid using fork(). However I can't find a way to get this to work....

a sample code of what I am already doing with fork is like this:

sub withpath
{
     my $c = shift;

     my $user   = $c->param('user_name');

     my $response_body = {
                response_type => "ephemeral",
                text          => "Running for $user:",
                attachments   => [
                     { text => 'analyze' },
                 ],
             };
     my $pid = fork();
     if($pid != 0){
         $c->render( json => $response_body );
     }else{
         $output = do_time_consuming_things()
         $response_body = {
             response_type => "in-channel",
             text          => "Result for $user:",
             attachments   => [
                { text => $output },
             ],
         };

         my $ua = Mojo::UserAgent->new;
         my $tx = $ua->post(
               $response_url,
               { Accept => '*/*' },
               json => $response_body,
               );
        if( my $res = $tx->success )
        {
                print "\n success \n";
        }
        else
        {
                my $err = $tx->error;
                print "$err->{code} response: $err->{message}\n" if $err->{code};
                print "Connection error: $err->{message}\n";
        }
    }
}

So the problem is that no matter how I tried I couldn't replicate the exact same code with Mojolicious' subproccesses. Any ideas?

Thanks in advance!

1 Answers

Actually I just found a solution to my problem!

So here is my solution:

my $c = shift; #receive request

my $user   = $c->param('user_name'); #get parameters
my $response_url = $c->param('response_url');
my $text = $c->param('text');

my $response_body = { #create the imidiate response that Slack is waiting for
            response_type => "ephemeral",
            text          => "Running for $user:",
            attachments   => [
                { text => 'analyze' },
            ],
        };

my $subprocess = Mojo::IOLoop::Subprocess->new; #create the subprocesses
$subprocess->run(
sub {do_time_consuming_things($user,$response_url,$text)}, #this callback is the
#actuall subprocess that will run in background and contains the POST request
#from my "fork" code (with the output) that should send a late response to Slack
sub {# this is a dummy subprocess doing nothing as this is needed by Mojo.
    my ($subprocess, $err, @results) = @_;
    say $err if $err;
    say "\n\nok\n\n";
}
);
#and here is the actual imidiate response outside of the subprocesses in order
#to avoid making the server wait for the subprocess to finish before responding!
$c->render( json => $response_body ); 

So I actually simply had to put my code of do_time_consuming_things in the first callback (in order for it to run as a subprocess) and use the second callback (that is actually linked to the parent process) as a dummy one and keep my "imidiate" response in the main body of the whole function instead of putting it inside one of the subprocesses. See code comments for more information!

Related