slack-ruby / slack-ruby-bot-server-events

Slack commands, interactive buttons, and events extension for slack-ruby-bot-server.
MIT License
71 stars 10 forks source link

How to have events trigger slow things? #23

Closed mattalbright closed 8 months ago

mattalbright commented 1 year ago

From Slack Docs:

Your app should respond to the event request with an HTTP 2xx within three seconds. If it does not, we'll consider the event delivery attempt failed. After a failure, we'll retry three times, backing off exponentially.

I'd like to have a mention or a direct message to a bot do things in Jira, and then respond when it's done. Jira API is intermittently slow so I get double responses occasionally, due to the retry Slack does. Any recommendations on how to do this within slack-ruby-bot-server-events, or should I just roll my own background job queue? Has anyone else hit this? I'd really like a simple in-memory Rails Active Job sort of thing, but not sure how best to accomplish that here.

dombarnes commented 1 year ago

Yeah just respond OK immediately to event requests and queue up the details you'll need to respond accordingly once your API call has happened. If you're in Rails, Sidekiq, Resque, or any other ActiveJob system is fine. Then just create your worker to call the api, then send a Slack client postMessage with your original message IDs.

Faked up example

SlackRubyBotServer::Events.configure do |config|
  config.on :event, 'event_callback', 'link_shared' do |event|
    event[:event][:links].each do |link|
      PostJiraMessge.perform_async
      true
    end
  end
end

class PostJiraMessageWorker
  def perform(event_team_id, channel_id, message_ts, unfurl_id, source, some_jira_param)
    team = Team.where(team_id: event[:team_id]).first || raise("Cannot find team with ID #{event[:team_id]}.")
    slack_client = Slack::Web::Client.new(token: team.activated_user_access_token)
    message = JiraClient.get('/some-api', some_jira_param)
    slack_client.chat_postMessage(channel: event[:event][:channel], blocks: message)
       # OR maybe you're unfurling a link
    slack_client.chat_unfurl(
      channel: channel_id,
      ts: message_ts,
      unfurl_id: unfurl_id,
      source: source,
      unfurls: message.to_json
    )
  rescue StandardError => e
    # handle your failures
  end
end
mattalbright commented 1 year ago

Sorry, I was unclear in my initial question and didn't state that I'm not using Rails, so was looking for the simplest non-Rails solution. For future folks who have the same question, I ended up using Sidekiq directly. Here's how to extend the ping_with_number example in slack-ruby-bot-server-events-app-mentions-sample:

lib/mentions/ping_with_number.rb:

class PingWithNumber < SlackRubyBotServer::Events::AppMentions::Mention
  mention(/ping[[:space:]]+(?<number>[[:digit:]]+)$/)

  def self.call(data)
    PingWithNumberResponder.perform_async(data.team.token, data.channel, data.match['number'])
  end
end

Responder would then be:

class PingWithNumberResponder
  include Sidekiq::Job
  def perform(token, channel, number)
    client = Slack::Web::Client.new(token: token)
    client.chat_postMessage(channel: channel, text: "pong #{number}")
  end
end

Add sidekiq to your Procfile: worker: bundle exec sidekiq -r ./app.rb and to your Gemfile: gem 'sidekiq' and you're up and running.

dombarnes commented 1 year ago

Yeah sorry I could’ve been clearer that Sidekiq doesn’t need Rails (and I use it myself in a Sinatra app). But works just the same with plain ruby since they’re just standard PORO. Glad you got it sorted.