From 28c04a8ff1acf5841f13b3a7f31e980fbf9626c7 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Tue, 25 Jun 2024 15:46:49 +0900 Subject: [PATCH] Fix slack bot app for Magellan RAG --- lib/slack_bot/app.rb | 52 ++++++++----- test/integration/slack_events_test.rb | 102 ++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 20 deletions(-) diff --git a/lib/slack_bot/app.rb b/lib/slack_bot/app.rb index 65cd29c..79e73fc 100644 --- a/lib/slack_bot/app.rb +++ b/lib/slack_bot/app.rb @@ -137,24 +137,36 @@ def direct_message?(event) thread_ts = event["thread_ts"] text = event["text"] - case - when magellan_rag_channel?(channel) - logger.info "Event:\n" + event.pretty_inspect.each_line.map {|l| "> #{l}" }.join("") - logger.info "#{channel.slack_id}: #{text}" - if thread_ts and not thread_allowed_channel?(channel) - notify_do_not_allowed_thread_context(channel, user, ts) - else - process_magellan_rag_message(channel, user, ts, thread_ts, text) - end - when allowed_channel?(channel) - logger.info "Event:\n" + event.pretty_inspect.each_line.map {|l| "> #{l}" }.join("") - logger.info "#{channel.slack_id}: #{text}" - - if thread_ts and not thread_allowed_channel?(channel) - notify_do_not_allowed_thread_context(channel, user, ts) - else - process_message(channel, user, ts, thread_ts, text) + begin + case + when magellan_rag_channel?(channel) + logger.info "Event:\n" + event.pretty_inspect.each_line.map {|l| "> #{l}" }.join("") + logger.info "#{channel.slack_id}: #{text}" + if thread_ts and not thread_allowed_channel?(channel) + notify_do_not_allowed_thread_context(channel, user, ts) + else + process_magellan_rag_message(channel, user, ts, thread_ts, text) + end + when allowed_channel?(channel) + logger.info "Event:\n" + event.pretty_inspect.each_line.map {|l| "> #{l}" }.join("") + logger.info "#{channel.slack_id}: #{text}" + + if thread_ts and not thread_allowed_channel?(channel) + notify_do_not_allowed_thread_context(channel, user, ts) + else + process_message(channel, user, ts, thread_ts, text) + end end + rescue Exception => err + Utils.post_message( + channel: channel.slack_id, + thread_ts: thread_ts, + text: [ + ":bangbang: *ERROR*: #{err.message}", + *err.backtrace + ].join("\n"), + mrkdwn: true + ) end end @@ -413,7 +425,7 @@ def direct_message?(event) begin options.validate! - rescue MagellanRagQeuryJob::InvalidOptionError => error + rescue MagellanRagQueryJob::InvalidOptionError => error reply_as_ephemeral(channel, user, ts, error.message) return end @@ -425,12 +437,12 @@ def direct_message?(event) slack_ts: ts, slack_thread_ts: thread_ts || ts ) - MagellanRagQeuryJob.perform_later("message_id" => message.id, "options" => options.to_h) + MagellanRagQueryJob.perform_later("message_id" => message.id, "options" => options.to_h) end private def process_magellan_rag_options(message_body) # TODO: implement options - MagellanRagQeuryJob::Options.new + MagellanRagQueryJob::Options.new end private def check_command_permission!(channel, user) diff --git a/test/integration/slack_events_test.rb b/test/integration/slack_events_test.rb index 9d31330..f3ea0d8 100644 --- a/test/integration/slack_events_test.rb +++ b/test/integration/slack_events_test.rb @@ -440,4 +440,106 @@ class SlackEventsTest < ActionDispatch::IntegrationTest assert Message.find_by(conversation: channel, user: user, slack_ts: slack_ts).blank? end + + + test "app_mention event from a channel for Magellan RAG" do + slack_ts = Time.now.to_f.to_s + channel = conversations(:magellan_rag) + user = users(:one) + query_body = "ZZZ" + query = "<@TEST_BOT_ID> #{query_body}" + answer = "ABC" + expected_response = "<@#{user.slack_id}> #{answer}" + + any_instance_of(SlackBot::Application) do |klass| + stub(klass).magellan_rag_channel? do |ch| + ch.slack_id == channel.slack_id + end + end + + stub_slack_api(:post, "chat.postMessage").to_return(body: { "ok" => true, "ts" => Time.now.to_f.to_s }.to_json) + + # Check the case when missing reactions:write scope + stub_slack_api(:post, "reactions.add").to_return { raise Slack::Web::Api::Errors::MissingScope, "missing_scope" } + stub_slack_api(:post, "reactions.remove").to_return { raise Slack::Web::Api::Errors::MissingScope, "missing_scope" } + + assert Message.find_by(conversation: channel, user: user, slack_ts: slack_ts).blank? + + assert_enqueued_with(job: MagellanRagQueryJob) do + params = { + type: "event_callback", + event: { + type: "app_mention", + text: query, + channel: channel.slack_id, + user: user.slack_id, + ts: slack_ts + } + } + request_body = ActionDispatch::RequestEncoder.encoder(:json).encode_params(params) + timestamp = slack_ts.to_i + headers = { + "X-Slack-Request-Timestamp": timestamp, + "X-Slack-Signature": compute_request_signature(timestamp, request_body) + } + + post "/slack/events", params:, headers:, as: :json + end + + message = Message.find_by!(conversation: channel, user: user, slack_ts: slack_ts) + assert_equal([ + query_body, + slack_ts, + ], + [ + message.text, + message.slack_thread_ts, + ]) + + mock(Utils::MagellanRAG).endpoint do + "http://report-rag-api.test" + end + + stub_request( + :get, "http://report-rag-api.test/generate_answer" + ).with( + query: {"query" => "ZZZ"} + ).to_return_json( + status: 200, + body: {"answer": "ABC"} + ) + + perform_enqueued_jobs + + assert_response :success + + actual_body = nil + assert_slack_api_called(:post, "chat.postMessage") do |request| + actual_body = decode_slack_client_request_body(request.body) + end + + query = Query.find_by!(message: message) + assert_kind_of Hash, query.body + response = Response.find_by!(query: query) + assert_kind_of Hash, response.body + + assert_equal( + { + "channel" => channel.slack_id, + "text" => expected_response, + "thread_ts" => slack_ts, + "blocks" => [ + { + "type" => "section", + "text" => { + "type" => "mrkdwn", + "text" => expected_response + } + }, + feedback_action_block + ] + }, + actual_body + ) + end end