slack-ruby-bot icon indicating copy to clipboard operation
slack-ruby-bot copied to clipboard

Q: Long-running operation is blocking message?

Open jonnyarnold opened this issue 9 years ago • 6 comments

Hello!

I'm putting together a Slack client (shock horror) and running into the following issue. If there's a long-running operation in my bot, I want to post a message before it starts, asking the user to wait:

class LongRunningOperation < SlackRubyBot::Commands::Base
  command 'run' do |client, data, _match|
    client.say(channel: data.channel, text: 'Please wait')
    result = long_running_operation
    client.say(channel: data.channel, text: result)
  end

  def self.long_running_operation
    sleep 10
    "Done!"
  end
end

When I talk to my bot, I am expecting "Please wait" to show first, and then "Done!" 10 seconds later. However, I receive both messages at the same time, after 10 seconds.

Is there something different I should be doing, or is this a known issue?

jonnyarnold avatar Feb 19 '16 13:02 jonnyarnold

I think this is because the buffer for the web socket is flushed later. I see two options:

EM.next_tick do
 client.say ...
end

Or EM.defer for a long running thing.

Or use a web client, client.web_client.chat_postMessage(channel: ..., text: ...) which will use the web API to post immmediately.

Generally I think you don't want to block, so I think wrapping the long operation in EM.defer is probably the best solution.

Le us know which one works best?

dblock avatar Feb 19 '16 23:02 dblock

I noticed this is the behavior with faye-websocket but if you use celluloid-io gem then you get one message, then the pause while it works, then the next response.

gcraig99 avatar Sep 14 '17 16:09 gcraig99

I've also noticed that while doing said long running operation that the bot doesn't respond to other requests. So I've taken to running the blocks for any commands that take a while in a new thread. This allows the bot to answer other questions at the same time. One thing to note, that will break test cases as the test case doesn't know to wait for the detached thread. To solve this I enclose the block in a lambda and then check if the environment is test, if so execute the block without the thread, otherwise pass the block to the thread to be run.

gcraig99 avatar Sep 14 '17 16:09 gcraig99

I found that using Celluloid.defer seems to work for allowing the bot to respond to other commands while processing a long-running command. It also allows me to use client.say in the deferred block. Maybe this (or something similar) should become a standard feature in how SlackRubyBot responds to commands?

kstole avatar Oct 09 '17 00:10 kstole

I am down with a global option to defer execution of all commands, including regex parsing. Would love a PR, please!

dblock avatar Oct 09 '17 21:10 dblock

I suppose it would then need to be something that works w/ both Celluloid and EventMachine. Possibly not too much code to implement but would certainly require major changes to the specs.

kstole avatar Oct 10 '17 02:10 kstole