From 11aa5c9ced3bfc12fea4d8db1e6798136c22cab5 Mon Sep 17 00:00:00 2001 From: atpons Date: Thu, 18 Feb 2021 00:17:23 +0900 Subject: [PATCH 01/17] feat: Supporting Socket Mode (experimental) --- README.md | 2 + lib/lita/adapters/slack.rb | 2 + lib/lita/adapters/slack/api.rb | 46 +++++++++++++++++----- lib/lita/adapters/slack/message_handler.rb | 4 +- lib/lita/adapters/slack/rtm_connection.rb | 16 +++++--- 5 files changed, 53 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 69d478a..110dd8c 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Lita.configure do |config| config.robot.admins = ["U012A3BCD"] config.adapters.slack.token = "abcd-1234567890-hWYd21AmMH2UHAkx29vb5c1Y" + config.adapters.slack.app_token = "xapp-1234567890-hWYd21AmMH2UHAkx29vb5c1Y" + config.adapters.slack.self_id = "B012ABCDE" config.adapters.slack.link_names = true config.adapters.slack.parse = "full" diff --git a/lib/lita/adapters/slack.rb b/lib/lita/adapters/slack.rb index 8457186..a65bacd 100644 --- a/lib/lita/adapters/slack.rb +++ b/lib/lita/adapters/slack.rb @@ -8,6 +8,8 @@ module Adapters class Slack < Adapter # Required configuration attributes. config :token, type: String, required: true + config :app_token, type: String, required: true + config :self_id, type: String, required: true config :proxy, type: String config :parse, type: [String] config :link_names, type: [true, false] diff --git a/lib/lita/adapters/slack/api.rb b/lib/lita/adapters/slack/api.rb index b496829..f9b104f 100644 --- a/lib/lita/adapters/slack/api.rb +++ b/lib/lita/adapters/slack/api.rb @@ -53,6 +53,14 @@ def im_list(params: {}) response end + def users_list(params: {}) + call_paginated_api(method: 'users.list', params: params, result_field: 'members') + end + + def users_profile_get(user) + call_api("users.profile.get", user: user) + end + def conversations_list(types: ["public_channel"], params: {}) params.merge!({ types: types.join(',') @@ -145,15 +153,18 @@ def set_topic(channel, topic) def rtm_start Lita.logger.debug("Starting `rtm_start` method") - response_data = call_api("rtm.start") + response_data = call_app_api("apps.connections.open") Lita.logger.debug("Started building TeamData") + + ws_url = response_data["url"] + team_data = TeamData.new( - SlackIM.from_data_array(response_data["ims"]), - SlackUser.from_data(response_data["self"]), - SlackUser.from_data_array(response_data["users"]), - SlackChannel.from_data_array(response_data["channels"]) + - SlackChannel.from_data_array(response_data["groups"]), - response_data["url"], + SlackIM.from_data_array(im_list["ims"]), + SlackUser.from_data(users_profile_get(@config.self_id)["profile"]), + SlackUser.from_data_array(users_list["members"]), + SlackChannel.from_data_array(channels_list["channels"]) + + SlackChannel.from_data_array(groups_list["groups"]), + ws_url, ) Lita.logger.debug("Finished building TeamData") Lita.logger.debug("Finishing method `rtm_start`") @@ -167,14 +178,29 @@ def rtm_start attr_reader :post_message_config def call_api(method, post_data = {}) - Lita.logger.debug("Starting request to Slack API with rtm.start") + Lita.logger.debug("Starting request to Slack API") response = connection.post( "https://slack.com/api/#{method}", { token: config.token }.merge(post_data) ) - Lita.logger.debug("Finished request to Slack API rtm.start") + Lita.logger.debug("Finished request to Slack API") + data = parse_response(response, method) + Lita.logger.debug("Finished parsing response") + raise "Slack API call to #{method} returned an error: #{data["error"]}." if data["error"] + + data + end + + def call_app_api(method, post_data = {}) + Lita.logger.debug("Starting request to Slack API with App Token") + response = connection.post( + "https://slack.com/api/#{method}", + post_data, + {"Authorization": "Bearer #{config.app_token}"} + ) + Lita.logger.debug("Finished request to Slack API App Token") data = parse_response(response, method) - Lita.logger.debug("Finished parsing rtm.start response") + Lita.logger.debug("Finished parsing response") raise "Slack API call to #{method} returned an error: #{data["error"]}." if data["error"] data diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index 37be4ad..50b3d9c 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -6,8 +6,8 @@ class MessageHandler def initialize(robot, robot_id, data) @robot = robot @robot_id = robot_id - @data = data - @type = data["type"] + @data = data["payload"]["event"] + @type = data["payload"]["event"]["type"] end def handle diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 63e4831..23d495a 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -86,10 +86,9 @@ def run(queue = nil, options = {}, &block) end end - def send_messages(channel, strings) - strings.each do |string| - EventLoop.defer { websocket.send(safe_payload_for(channel, string)) } - end + def ack(envelope_id) + payload = MultiJson.dump({envelope_id: envelope_id, payload: {}}) + EventLoop.defer { websocket.send(payload) } end def shut_down @@ -126,7 +125,14 @@ def payload_for(channel, string) def receive_message(event) data = MultiJson.load(event.data) - EventLoop.defer { MessageHandler.new(robot, robot_id, data).handle } + EventLoop.defer do + case data["type"] + when "events_api" + MessageHandler.new(robot, robot_id, data).handle + ack(data["envelope_id"]) + log.debug("Acknowledging #{data["envelope_id"]}") + end + end end def safe_payload_for(channel, string) From 1bb2154e7aabf80f85ea6ce4db7b2e0c10544f63 Mon Sep 17 00:00:00 2001 From: atpons Date: Thu, 18 Feb 2021 01:23:47 +0900 Subject: [PATCH 02/17] feat: Add RSpec for migrating Socket Mode and new API --- lib/lita/adapters/slack/message_handler.rb | 4 +- lib/lita/adapters/slack/rtm_connection.rb | 2 +- spec/lita/adapters/slack/api_spec.rb | 51 ++++++++++++++++--- .../adapters/slack/rtm_connection_spec.rb | 34 ++----------- spec/lita/adapters/slack_spec.rb | 15 ------ 5 files changed, 49 insertions(+), 57 deletions(-) diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index 50b3d9c..37be4ad 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -6,8 +6,8 @@ class MessageHandler def initialize(robot, robot_id, data) @robot = robot @robot_id = robot_id - @data = data["payload"]["event"] - @type = data["payload"]["event"]["type"] + @data = data + @type = data["type"] end def handle diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 23d495a..24778b7 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -128,7 +128,7 @@ def receive_message(event) EventLoop.defer do case data["type"] when "events_api" - MessageHandler.new(robot, robot_id, data).handle + MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle ack(data["envelope_id"]) log.debug("Acknowledging #{data["envelope_id"]}") end diff --git a/spec/lita/adapters/slack/api_spec.rb b/spec/lita/adapters/slack/api_spec.rb index 8706f47..202ef73 100644 --- a/spec/lita/adapters/slack/api_spec.rb +++ b/spec/lita/adapters/slack/api_spec.rb @@ -5,10 +5,14 @@ let(:http_status) { 200 } let(:token) { 'abcd-1234567890-hWYd21AmMH2UHAkx29vb5c1Y' } + let(:app_token) { 'apptoken' } let(:config) { Lita::Adapters::Slack.configuration_builder.build } + let(:self_id) { "self_id" } before do config.token = token + config.app_token = app_token + config.self_id = self_id end describe "#im_open" do @@ -796,9 +800,19 @@ def stubs(postMessage_options = {}) describe "#rtm_start" do let(:http_status) { 200 } + + before do + allow(subject).to receive(:im_list) { {"ims" => [{ "id" => 'D024BFF1M' }]} } + allow(subject).to receive(:users_profile_get).with(self_id) { {"profile" => { "id" => self_id }} } + allow(subject).to receive(:users_list) { {"members" => [{ "id" => 'U023BECGF' }]} } + allow(subject).to receive(:channels_list) { {"channels" => [{ "id" => 'D024BFF1M' }]} } + allow(subject).to receive(:groups_list) { {"groups" => [{ "id" => 'G024BFF1M' }]} } + end + + let(:stubs) do Faraday::Adapter::Test::Stubs.new do |stub| - stub.post('https://slack.com/api/rtm.start', token: token) do + stub.post('https://slack.com/api/apps.connections.open', {}) do [http_status, {}, http_response] end end @@ -809,18 +823,13 @@ def stubs(postMessage_options = {}) MultiJson.dump({ ok: true, url: 'wss://example.com/', - users: [{ id: 'U023BECGF' }], - ims: [{ id: 'D024BFF1M' }], - self: { id: 'U12345678' }, - channels: [{ id: 'C1234567890' }], - groups: [{ id: 'G0987654321' }], }) end it "has data on the bot user" do response = subject.rtm_start - expect(response.self.id).to eq('U12345678') + expect(response.self.id).to eq(self_id) end it "has an array of IMs" do @@ -843,6 +852,32 @@ def stubs(postMessage_options = {}) end end + describe "#users_list" do + let(:http_status) { 200 } + let(:stubs) do + Faraday::Adapter::Test::Stubs.new do |stub| + stub.post('https://slack.com/api/users.list', token: token) do + [http_status, {}, http_response] + end + end + end + + describe "with a successful response" do + let(:http_response) do + MultiJson.dump({ + ok: true, + members: [{ id: 'U023BECGF', name: 'spengler', real_name: 'Egon Spengler' }], + }) + end + + it "has an array of members" do + response = subject.users_list + + expect(response["members"][0]["name"]).to eq 'spengler' + end + end + end + describe "#conversations_list" do describe "#conversations_list" do let(:channel_id) { 'C024G4BGW' } @@ -969,7 +1004,7 @@ def stubs(postMessage_options = {}) expect(response['channels'][1]['id']).to eq(channel_id_2) end end - + describe "with a Slack error" do let(:http_response) do MultiJson.dump({ diff --git a/spec/lita/adapters/slack/rtm_connection_spec.rb b/spec/lita/adapters/slack/rtm_connection_spec.rb index b13fc41..5693452 100644 --- a/spec/lita/adapters/slack/rtm_connection_spec.rb +++ b/spec/lita/adapters/slack/rtm_connection_spec.rb @@ -85,7 +85,7 @@ def with_websocket(subject, queue) end describe "#run" do - let(:event) { double('Event', data: '{}') } + let(:event) { double('Event', data: '{"type": "events_api", "payload": {"event": {}}, "envelope_id": ""}') } let(:message_handler) { instance_double('Lita::Adapters::Slack::MessageHandler') } it "creates the WebSocket" do @@ -114,6 +114,8 @@ def with_websocket(subject, queue) {}, ).and_return(message_handler) + allow(subject).to receive(:ack) + expect(message_handler).to receive(:handle) # Testing private methods directly is bad, but it's difficult to get @@ -133,34 +135,4 @@ def with_websocket(subject, queue) end end - - describe "#send_messages" do - let(:message_json) { MultiJson.dump(id: 1, type: 'message', text: 'hi', channel: channel_id) } - let(:channel_id) { 'C024BE91L' } - let(:websocket) { instance_double("Faye::WebSocket::Client") } - - before do - # TODO: Don't stub what you don't own! - allow(Faye::WebSocket::Client).to receive(:new).and_return(websocket) - allow(websocket).to receive(:on) - allow(websocket).to receive(:close) - allow(Lita::Adapters::Slack::EventLoop).to receive(:defer).and_yield - end - - it "writes messages to the WebSocket" do - with_websocket(subject, queue) do |websocket| - expect(websocket).to receive(:send).with(message_json) - - subject.send_messages(channel_id, ['hi']) - end - end - - it "raises an ArgumentError if the payload is too large" do - with_websocket(subject, queue) do |websocket| - expect do - subject.send_messages(channel_id, ['x' * 16_001]) - end.to raise_error(ArgumentError) - end - end - end end diff --git a/spec/lita/adapters/slack_spec.rb b/spec/lita/adapters/slack_spec.rb index 7a28522..82025ca 100644 --- a/spec/lita/adapters/slack_spec.rb +++ b/spec/lita/adapters/slack_spec.rb @@ -112,21 +112,6 @@ Lita::Adapters::Slack::SlackSource.new(room: 'C024BE91L', user: user, private_message: true) end - describe "via the Web API" do - let(:api) { instance_double('Lita::Adapters::Slack::API') } - - before do - allow(Lita::Adapters::Slack::API).to receive(:new).with(subject.config).and_return(api) - end - - it "does not send via the RTM api" do - expect(rtm_connection).to_not receive(:send_messages) - expect(api).to receive(:send_messages).with(room_source.room, ['foo']) - - subject.send_messages(room_source, ['foo']) - end - end - describe "with an ellipsis" do let(:room_source) { Lita::Adapters::Slack::SlackSource.new(room: 'C024BE91L', extensions: { timestamp: "12345" } ) } let(:api) { instance_double('Lita::Adapters::Slack::API') } From 823e60fcef4405045d1e157b115d07a331522545 Mon Sep 17 00:00:00 2001 From: atpons Date: Thu, 18 Feb 2021 13:34:29 +0900 Subject: [PATCH 03/17] feat: auth.test method can fetch bot's own id --- README.md | 1 - lib/lita/adapters/slack.rb | 1 - lib/lita/adapters/slack/api.rb | 14 +++++++++++++- spec/lita/adapters/slack/api_spec.rb | 1 - 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 110dd8c..55eebdf 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,6 @@ Lita.configure do |config| config.adapters.slack.token = "abcd-1234567890-hWYd21AmMH2UHAkx29vb5c1Y" config.adapters.slack.app_token = "xapp-1234567890-hWYd21AmMH2UHAkx29vb5c1Y" - config.adapters.slack.self_id = "B012ABCDE" config.adapters.slack.link_names = true config.adapters.slack.parse = "full" diff --git a/lib/lita/adapters/slack.rb b/lib/lita/adapters/slack.rb index a65bacd..f836936 100644 --- a/lib/lita/adapters/slack.rb +++ b/lib/lita/adapters/slack.rb @@ -9,7 +9,6 @@ class Slack < Adapter # Required configuration attributes. config :token, type: String, required: true config :app_token, type: String, required: true - config :self_id, type: String, required: true config :proxy, type: String config :parse, type: [String] config :link_names, type: [true, false] diff --git a/lib/lita/adapters/slack/api.rb b/lib/lita/adapters/slack/api.rb index f9b104f..62fcf99 100644 --- a/lib/lita/adapters/slack/api.rb +++ b/lib/lita/adapters/slack/api.rb @@ -61,6 +61,10 @@ def users_profile_get(user) call_api("users.profile.get", user: user) end + def auth_test + call_api("auth.test") + end + def conversations_list(types: ["public_channel"], params: {}) params.merge!({ types: types.join(',') @@ -160,12 +164,13 @@ def rtm_start team_data = TeamData.new( SlackIM.from_data_array(im_list["ims"]), - SlackUser.from_data(users_profile_get(@config.self_id)["profile"]), + SlackUser.from_data(get_identity), SlackUser.from_data_array(users_list["members"]), SlackChannel.from_data_array(channels_list["channels"]) + SlackChannel.from_data_array(groups_list["groups"]), ws_url, ) + Lita.logger.debug("Finished building TeamData") Lita.logger.debug("Finishing method `rtm_start`") team_data @@ -177,6 +182,13 @@ def rtm_start attr_reader :config attr_reader :post_message_config + def get_identity + user_id = auth_test["user_id"] + profile = users_profile_get(user_id)["profile"] + profile["id"] = user_id + profile + end + def call_api(method, post_data = {}) Lita.logger.debug("Starting request to Slack API") response = connection.post( diff --git a/spec/lita/adapters/slack/api_spec.rb b/spec/lita/adapters/slack/api_spec.rb index 202ef73..1e844bd 100644 --- a/spec/lita/adapters/slack/api_spec.rb +++ b/spec/lita/adapters/slack/api_spec.rb @@ -12,7 +12,6 @@ before do config.token = token config.app_token = app_token - config.self_id = self_id end describe "#im_open" do From 728b2e58f5d6c761d68d26d1cb24c0cf1df21802 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Thu, 18 Feb 2021 15:24:25 +0900 Subject: [PATCH 04/17] add debug print --- lib/lita/adapters/slack/message_handler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index 37be4ad..9a16976 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -133,7 +133,7 @@ def dispatch_me_message(user) message = Message.new(robot, body, source) message.command! if source.private_message? message.extensions[:slack] = extensions - log.debug("Dispatching message to Lita from #{user.id}.") + log.debug("Dispatching message to Lita from #{user.id}. #{message.inspect}") robot.receive(message) end From fa895aa1345a8d317327ae429d5c35c7adddc6a7 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Thu, 18 Feb 2021 15:57:21 +0900 Subject: [PATCH 05/17] more debug print --- lib/lita/adapters/slack/rtm_connection.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 24778b7..d0bd201 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -128,9 +128,10 @@ def receive_message(event) EventLoop.defer do case data["type"] when "events_api" + log.debug("Invoked MessageHandler #{data}") MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle ack(data["envelope_id"]) - log.debug("Acknowledging #{data["envelope_id"]}") + log.debug("Acknowledging #{data["envelope_id"]} #{data}") end end end From c7831560ad9b7fcb13ee8378a5fe047263c2ab02 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Thu, 18 Feb 2021 16:18:49 +0900 Subject: [PATCH 06/17] fix: early acknowledging and ignoring retry event --- lib/lita/adapters/slack/rtm_connection.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index d0bd201..96bd4cc 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -126,12 +126,12 @@ def receive_message(event) data = MultiJson.load(event.data) EventLoop.defer do + return if data["retry_attempt"].to_i > 0 case data["type"] when "events_api" - log.debug("Invoked MessageHandler #{data}") - MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle - ack(data["envelope_id"]) log.debug("Acknowledging #{data["envelope_id"]} #{data}") + ack(data["envelope_id"]) + MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle end end end From c44bc158abd8f9996e650bc19d00bbb1484e284d Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Thu, 18 Feb 2021 17:01:25 +0900 Subject: [PATCH 07/17] fix: less debug print --- lib/lita/adapters/slack/message_handler.rb | 2 +- lib/lita/adapters/slack/rtm_connection.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index 9a16976..37be4ad 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -133,7 +133,7 @@ def dispatch_me_message(user) message = Message.new(robot, body, source) message.command! if source.private_message? message.extensions[:slack] = extensions - log.debug("Dispatching message to Lita from #{user.id}. #{message.inspect}") + log.debug("Dispatching message to Lita from #{user.id}.") robot.receive(message) end diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 96bd4cc..1c649e3 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -129,7 +129,7 @@ def receive_message(event) return if data["retry_attempt"].to_i > 0 case data["type"] when "events_api" - log.debug("Acknowledging #{data["envelope_id"]} #{data}") + log.debug("Acknowledging #{data["envelope_id"]}") ack(data["envelope_id"]) MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle end From 5c8024a50c62ede51cb68b5e961a64ac527bfa88 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Mon, 22 Feb 2021 11:32:00 +0900 Subject: [PATCH 08/17] add spec for rtm_start --- spec/lita/adapters/slack/api_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/lita/adapters/slack/api_spec.rb b/spec/lita/adapters/slack/api_spec.rb index 1e844bd..00d40f0 100644 --- a/spec/lita/adapters/slack/api_spec.rb +++ b/spec/lita/adapters/slack/api_spec.rb @@ -801,6 +801,7 @@ def stubs(postMessage_options = {}) let(:http_status) { 200 } before do + allow(subject).to receive(:auth_test) { {"user_id" => self_id} } allow(subject).to receive(:im_list) { {"ims" => [{ "id" => 'D024BFF1M' }]} } allow(subject).to receive(:users_profile_get).with(self_id) { {"profile" => { "id" => self_id }} } allow(subject).to receive(:users_list) { {"members" => [{ "id" => 'U023BECGF' }]} } From d8907347c94951a62a05a8ff3cad939cff27e128 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Mon, 22 Feb 2021 12:04:03 +0900 Subject: [PATCH 09/17] treat 429 and Retry-After --- lib/lita/adapters/slack/api.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/lita/adapters/slack/api.rb b/lib/lita/adapters/slack/api.rb index 62fcf99..0467e9f 100644 --- a/lib/lita/adapters/slack/api.rb +++ b/lib/lita/adapters/slack/api.rb @@ -219,14 +219,23 @@ def call_app_api(method, post_data = {}) end def connection + retry_options = { + retry_statuses: [429], + methods: %i[get post] + } if stubs - Faraday.new { |faraday| faraday.adapter(:test, stubs) } + Faraday.new do |faraday| + faraday.request :retry, retry_options + faraday.adapter(:test, stubs) + end else options = {} unless config.proxy.nil? options = { proxy: config.proxy } end - Faraday.new(options) + Faraday.new(options) do |faraday| + faraday.request :retry, retry_options + end end end From a717a8fb5e2c8737249389cd505095bcc43d5997 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Mon, 22 Feb 2021 12:20:50 +0900 Subject: [PATCH 10/17] add spec for max retries --- spec/lita/adapters/slack/api_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/lita/adapters/slack/api_spec.rb b/spec/lita/adapters/slack/api_spec.rb index 00d40f0..d00742f 100644 --- a/spec/lita/adapters/slack/api_spec.rb +++ b/spec/lita/adapters/slack/api_spec.rb @@ -1018,6 +1018,28 @@ def stubs(postMessage_options = {}) "Slack API call to conversations.list returned an error: invalid_auth." ) end + + context "ratelimited" do + let(:calls) { [] } + let(:http_status) { 429 } + let(:http_response) { MultiJson.dump({ok: false, error: "ratelimited"}) } + let(:stubs) do + Faraday::Adapter::Test::Stubs.new do |stub| + stub.post('https://slack.com/api/conversations.list', token: token, limit: 1, types: "public_channel") do |env| + calls << env.dup + [http_status, {}, http_response] + end + end + end + + # Faraday retries max 2 times + it "raises RuntimeError after retry" do + expect { subject.conversations_list(params: { limit: 1 })}.to raise_error( + "Slack API call to conversations.list returned an error: ratelimited." + ) + expect(calls.size).to eq(3) + end + end end describe "with an HTTP error" do From f44c395f95742322f95ff499e3a50db793ba09ee Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Mon, 22 Feb 2021 13:26:38 +0900 Subject: [PATCH 11/17] fix request body url_encoded --- lib/lita/adapters/slack/api.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/lita/adapters/slack/api.rb b/lib/lita/adapters/slack/api.rb index 0467e9f..abbae97 100644 --- a/lib/lita/adapters/slack/api.rb +++ b/lib/lita/adapters/slack/api.rb @@ -225,6 +225,7 @@ def connection } if stubs Faraday.new do |faraday| + faraday.request :url_encoded faraday.request :retry, retry_options faraday.adapter(:test, stubs) end @@ -234,6 +235,7 @@ def connection options = { proxy: config.proxy } end Faraday.new(options) do |faraday| + faraday.request :url_encoded faraday.request :retry, retry_options end end From 89a17ef5a2f914bf954d296f2075833accce7869 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Mon, 22 Feb 2021 14:48:16 +0900 Subject: [PATCH 12/17] early return without block --- lib/lita/adapters/slack/rtm_connection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 1c649e3..87cce51 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -124,9 +124,9 @@ def payload_for(channel, string) def receive_message(event) data = MultiJson.load(event.data) + return if data["retry_attempt"].to_i > 0 EventLoop.defer do - return if data["retry_attempt"].to_i > 0 case data["type"] when "events_api" log.debug("Acknowledging #{data["envelope_id"]}") From 7a3dc8d1e827e511cdd45262209903a2432c2d48 Mon Sep 17 00:00:00 2001 From: Taiga ASANO Date: Thu, 11 Mar 2021 07:52:14 +0900 Subject: [PATCH 13/17] Not handle is_bot? enabled on SlackUser --- lib/lita/adapters/slack/api.rb | 3 ++- lib/lita/adapters/slack/message_handler.rb | 6 ++++++ lib/lita/adapters/slack/user_creator.rb | 11 +++++++++-- spec/lita/adapters/slack/message_handler_spec.rb | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/lita/adapters/slack/api.rb b/lib/lita/adapters/slack/api.rb index abbae97..3112645 100644 --- a/lib/lita/adapters/slack/api.rb +++ b/lib/lita/adapters/slack/api.rb @@ -225,7 +225,8 @@ def connection } if stubs Faraday.new do |faraday| - faraday.request :url_encoded + # test stubs with not URL encoded form-parameters and passing it + # faraday.request :url_encoded faraday.request :retry, retry_options faraday.adapter(:test, stubs) end diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index 37be4ad..d323bd2 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -10,7 +10,9 @@ def initialize(robot, robot_id, data) @type = data["type"] end + def handle + return unless from_bot? case type when "hello" handle_hello @@ -38,6 +40,10 @@ def handle attr_reader :robot_id attr_reader :type + def from_bot? + User.find_by_id(data["user"]).metadata["is_bot"] + end + def body normalized_text = nil if data["text"] diff --git a/lib/lita/adapters/slack/user_creator.rb b/lib/lita/adapters/slack/user_creator.rb index 5cbedb1..5025e06 100644 --- a/lib/lita/adapters/slack/user_creator.rb +++ b/lib/lita/adapters/slack/user_creator.rb @@ -5,10 +5,17 @@ class Slack < Adapter class UserCreator class << self def create_user(slack_user, robot, robot_id) + metadata = { + name: real_name(slack_user), + mention_name: slack_user.name, + } + + is_bot = slack_user.raw_data["is_bot"] + metadata.merge!(is_bot: is_bot) if is_bot + User.create( slack_user.id, - name: real_name(slack_user), - mention_name: slack_user.name + metadata ) update_robot(robot, slack_user) if slack_user.id == robot_id diff --git a/spec/lita/adapters/slack/message_handler_spec.rb b/spec/lita/adapters/slack/message_handler_spec.rb index aca88f0..a74f77e 100644 --- a/spec/lita/adapters/slack/message_handler_spec.rb +++ b/spec/lita/adapters/slack/message_handler_spec.rb @@ -6,6 +6,7 @@ before do allow(robot).to receive(:trigger) Lita::Adapters::Slack::RoomCreator.create_room(channel, robot) + allow_any_instance_of(Lita::Adapters::Slack::MessageHandler).to receive(:from_bot?).and_return(true) end let(:robot) { instance_double('Lita::Robot', name: 'Lita', mention_name: 'lita') } From 7952a88b79572f6848922159a71b1944aece88c6 Mon Sep 17 00:00:00 2001 From: atpons Date: Thu, 11 Mar 2021 11:21:44 +0900 Subject: [PATCH 14/17] fix is_bot method for proper work --- lib/lita/adapters/slack/message_handler.rb | 21 +++++++++++++++------ lib/lita/adapters/slack/rtm_connection.rb | 2 +- lib/lita/adapters/slack/user_creator.rb | 10 +++++++++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index d323bd2..9919241 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -3,16 +3,17 @@ module Adapters class Slack < Adapter # @api private class MessageHandler - def initialize(robot, robot_id, data) + def initialize(robot, robot_id, data, slack_api) @robot = robot @robot_id = robot_id @data = data @type = data["type"] + @slack_api = slack_api end def handle - return unless from_bot? + return if from_bot? case type when "hello" handle_hello @@ -39,9 +40,11 @@ def handle attr_reader :robot attr_reader :robot_id attr_reader :type + attr_reader :slack_api def from_bot? - User.find_by_id(data["user"]).metadata["is_bot"] + u = get_user(data["user"]) + u.metadata["is_bot"] == "true" end def body @@ -169,11 +172,17 @@ def handle_hello robot.trigger(:connected) end + def get_user(user_id) + user = User.find_by_id(user_id) + return user if user + UserCreator.create_from_api(data["user"], robot, robot_id, slack_api) + end + def handle_message return unless supported_subtype? return if data["user"] == 'USLACKBOT' - user = User.find_by_id(data["user"]) || User.create(data["user"]) + user = get_user(data["user"]) return if from_self?(user) @@ -194,13 +203,13 @@ def handle_reaction log.debug "#{type} event received from Slack" # find or create user - user = User.find_by_id(data["user"]) || User.create(data["user"]) + user = get_user(data["user"]) # avoid processing reactions added/removed by self return if from_self?(user) # find or create item_user - item_user = User.find_by_id(data["item_user"]) || User.create(data["item_user"]) + item_user = get_user(data["item_user"]) # build a payload following slack convention for reactions payload = { diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 87cce51..69c0e58 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -131,7 +131,7 @@ def receive_message(event) when "events_api" log.debug("Acknowledging #{data["envelope_id"]}") ack(data["envelope_id"]) - MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle + MessageHandler.new(robot, robot_id, data["payload"]["event"], API.new(config)).handle end end end diff --git a/lib/lita/adapters/slack/user_creator.rb b/lib/lita/adapters/slack/user_creator.rb index 5025e06..2d464f4 100644 --- a/lib/lita/adapters/slack/user_creator.rb +++ b/lib/lita/adapters/slack/user_creator.rb @@ -11,7 +11,7 @@ def create_user(slack_user, robot, robot_id) } is_bot = slack_user.raw_data["is_bot"] - metadata.merge!(is_bot: is_bot) if is_bot + metadata.merge!(is_bot: is_bot) User.create( slack_user.id, @@ -22,6 +22,14 @@ def create_user(slack_user, robot, robot_id) robot.trigger(:slack_user_created, slack_user: slack_user) end + def create_from_api(slack_user, robot, robot_id, api) + Lita.logger.debug("called UserCreator#create_from_api #{slack_user}") + user = api.users_profile_get(slack_user) + create_user(SlackUser.from_data(user), robot, robot_id) + lita_user = User.find_by_id(slack_user) + lita_user + end + def create_users(slack_users, robot, robot_id) slack_users.each { |slack_user| create_user(slack_user, robot, robot_id) } end From e97a85e173a7f613ab4659ff9c64b3f0a98d3dc2 Mon Sep 17 00:00:00 2001 From: atpons Date: Thu, 11 Mar 2021 17:51:20 +0900 Subject: [PATCH 15/17] Revert "fix is_bot method for proper work" This reverts commit 7952a88b79572f6848922159a71b1944aece88c6. --- lib/lita/adapters/slack/message_handler.rb | 21 ++++++--------------- lib/lita/adapters/slack/rtm_connection.rb | 2 +- lib/lita/adapters/slack/user_creator.rb | 10 +--------- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index 9919241..d323bd2 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -3,17 +3,16 @@ module Adapters class Slack < Adapter # @api private class MessageHandler - def initialize(robot, robot_id, data, slack_api) + def initialize(robot, robot_id, data) @robot = robot @robot_id = robot_id @data = data @type = data["type"] - @slack_api = slack_api end def handle - return if from_bot? + return unless from_bot? case type when "hello" handle_hello @@ -40,11 +39,9 @@ def handle attr_reader :robot attr_reader :robot_id attr_reader :type - attr_reader :slack_api def from_bot? - u = get_user(data["user"]) - u.metadata["is_bot"] == "true" + User.find_by_id(data["user"]).metadata["is_bot"] end def body @@ -172,17 +169,11 @@ def handle_hello robot.trigger(:connected) end - def get_user(user_id) - user = User.find_by_id(user_id) - return user if user - UserCreator.create_from_api(data["user"], robot, robot_id, slack_api) - end - def handle_message return unless supported_subtype? return if data["user"] == 'USLACKBOT' - user = get_user(data["user"]) + user = User.find_by_id(data["user"]) || User.create(data["user"]) return if from_self?(user) @@ -203,13 +194,13 @@ def handle_reaction log.debug "#{type} event received from Slack" # find or create user - user = get_user(data["user"]) + user = User.find_by_id(data["user"]) || User.create(data["user"]) # avoid processing reactions added/removed by self return if from_self?(user) # find or create item_user - item_user = get_user(data["item_user"]) + item_user = User.find_by_id(data["item_user"]) || User.create(data["item_user"]) # build a payload following slack convention for reactions payload = { diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 69c0e58..87cce51 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -131,7 +131,7 @@ def receive_message(event) when "events_api" log.debug("Acknowledging #{data["envelope_id"]}") ack(data["envelope_id"]) - MessageHandler.new(robot, robot_id, data["payload"]["event"], API.new(config)).handle + MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle end end end diff --git a/lib/lita/adapters/slack/user_creator.rb b/lib/lita/adapters/slack/user_creator.rb index 2d464f4..5025e06 100644 --- a/lib/lita/adapters/slack/user_creator.rb +++ b/lib/lita/adapters/slack/user_creator.rb @@ -11,7 +11,7 @@ def create_user(slack_user, robot, robot_id) } is_bot = slack_user.raw_data["is_bot"] - metadata.merge!(is_bot: is_bot) + metadata.merge!(is_bot: is_bot) if is_bot User.create( slack_user.id, @@ -22,14 +22,6 @@ def create_user(slack_user, robot, robot_id) robot.trigger(:slack_user_created, slack_user: slack_user) end - def create_from_api(slack_user, robot, robot_id, api) - Lita.logger.debug("called UserCreator#create_from_api #{slack_user}") - user = api.users_profile_get(slack_user) - create_user(SlackUser.from_data(user), robot, robot_id) - lita_user = User.find_by_id(slack_user) - lita_user - end - def create_users(slack_users, robot, robot_id) slack_users.each { |slack_user| create_user(slack_user, robot, robot_id) } end From 7d294df236d3418f0567acedaee2f058105db7a2 Mon Sep 17 00:00:00 2001 From: atpons Date: Thu, 11 Mar 2021 17:51:26 +0900 Subject: [PATCH 16/17] Revert "Not handle is_bot? enabled on SlackUser" This reverts commit 7a3dc8d1e827e511cdd45262209903a2432c2d48. --- lib/lita/adapters/slack/api.rb | 3 +-- lib/lita/adapters/slack/message_handler.rb | 6 ------ lib/lita/adapters/slack/user_creator.rb | 11 ++--------- spec/lita/adapters/slack/message_handler_spec.rb | 1 - 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/lib/lita/adapters/slack/api.rb b/lib/lita/adapters/slack/api.rb index 3112645..abbae97 100644 --- a/lib/lita/adapters/slack/api.rb +++ b/lib/lita/adapters/slack/api.rb @@ -225,8 +225,7 @@ def connection } if stubs Faraday.new do |faraday| - # test stubs with not URL encoded form-parameters and passing it - # faraday.request :url_encoded + faraday.request :url_encoded faraday.request :retry, retry_options faraday.adapter(:test, stubs) end diff --git a/lib/lita/adapters/slack/message_handler.rb b/lib/lita/adapters/slack/message_handler.rb index d323bd2..37be4ad 100644 --- a/lib/lita/adapters/slack/message_handler.rb +++ b/lib/lita/adapters/slack/message_handler.rb @@ -10,9 +10,7 @@ def initialize(robot, robot_id, data) @type = data["type"] end - def handle - return unless from_bot? case type when "hello" handle_hello @@ -40,10 +38,6 @@ def handle attr_reader :robot_id attr_reader :type - def from_bot? - User.find_by_id(data["user"]).metadata["is_bot"] - end - def body normalized_text = nil if data["text"] diff --git a/lib/lita/adapters/slack/user_creator.rb b/lib/lita/adapters/slack/user_creator.rb index 5025e06..5cbedb1 100644 --- a/lib/lita/adapters/slack/user_creator.rb +++ b/lib/lita/adapters/slack/user_creator.rb @@ -5,17 +5,10 @@ class Slack < Adapter class UserCreator class << self def create_user(slack_user, robot, robot_id) - metadata = { - name: real_name(slack_user), - mention_name: slack_user.name, - } - - is_bot = slack_user.raw_data["is_bot"] - metadata.merge!(is_bot: is_bot) if is_bot - User.create( slack_user.id, - metadata + name: real_name(slack_user), + mention_name: slack_user.name ) update_robot(robot, slack_user) if slack_user.id == robot_id diff --git a/spec/lita/adapters/slack/message_handler_spec.rb b/spec/lita/adapters/slack/message_handler_spec.rb index a74f77e..aca88f0 100644 --- a/spec/lita/adapters/slack/message_handler_spec.rb +++ b/spec/lita/adapters/slack/message_handler_spec.rb @@ -6,7 +6,6 @@ before do allow(robot).to receive(:trigger) Lita::Adapters::Slack::RoomCreator.create_room(channel, robot) - allow_any_instance_of(Lita::Adapters::Slack::MessageHandler).to receive(:from_bot?).and_return(true) end let(:robot) { instance_double('Lita::Robot', name: 'Lita', mention_name: 'lita') } From 028375b064ff5f15e9eac56ec73b6aeeaa70ee49 Mon Sep 17 00:00:00 2001 From: atpons Date: Thu, 11 Mar 2021 17:56:43 +0900 Subject: [PATCH 17/17] Prevent handle message if from event with bot_id --- lib/lita/adapters/slack/rtm_connection.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/lita/adapters/slack/rtm_connection.rb b/lib/lita/adapters/slack/rtm_connection.rb index 87cce51..22111fa 100644 --- a/lib/lita/adapters/slack/rtm_connection.rb +++ b/lib/lita/adapters/slack/rtm_connection.rb @@ -131,6 +131,7 @@ def receive_message(event) when "events_api" log.debug("Acknowledging #{data["envelope_id"]}") ack(data["envelope_id"]) + next unless data["payload"]["event"]["bot_id"].nil? MessageHandler.new(robot, robot_id, data["payload"]["event"]).handle end end