feat: Support link unfurling for all the channels within the same connected channel account. (#8033)
This commit is contained in:
2
Gemfile
2
Gemfile
@@ -92,7 +92,7 @@ gem 'twitty', '~> 0.1.5'
|
|||||||
# facebook client
|
# facebook client
|
||||||
gem 'koala'
|
gem 'koala'
|
||||||
# slack client
|
# slack client
|
||||||
gem 'slack-ruby-client', '~> 2.0.0'
|
gem 'slack-ruby-client', '~> 2.2.0'
|
||||||
# for dialogflow integrations
|
# for dialogflow integrations
|
||||||
gem 'google-cloud-dialogflow-v2'
|
gem 'google-cloud-dialogflow-v2'
|
||||||
gem 'grpc'
|
gem 'grpc'
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ GEM
|
|||||||
googleauth (~> 1.0)
|
googleauth (~> 1.0)
|
||||||
grpc (~> 1.36)
|
grpc (~> 1.36)
|
||||||
geocoder (1.8.1)
|
geocoder (1.8.1)
|
||||||
gli (2.21.0)
|
gli (2.21.1)
|
||||||
globalid (1.2.1)
|
globalid (1.2.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
gmail_xoauth (0.4.2)
|
gmail_xoauth (0.4.2)
|
||||||
@@ -732,13 +732,12 @@ GEM
|
|||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.2)
|
simplecov-html (0.10.2)
|
||||||
slack-ruby-client (2.0.0)
|
slack-ruby-client (2.2.0)
|
||||||
faraday (>= 2.0)
|
faraday (>= 2.0)
|
||||||
faraday-mashify
|
faraday-mashify
|
||||||
faraday-multipart
|
faraday-multipart
|
||||||
gli
|
gli
|
||||||
hashie
|
hashie
|
||||||
websocket-driver
|
|
||||||
snaky_hash (2.0.1)
|
snaky_hash (2.0.1)
|
||||||
hashie
|
hashie
|
||||||
version_gem (~> 1.1, >= 1.1.1)
|
version_gem (~> 1.1, >= 1.1.1)
|
||||||
@@ -935,7 +934,7 @@ DEPENDENCIES
|
|||||||
sidekiq (>= 7.1.3)
|
sidekiq (>= 7.1.3)
|
||||||
sidekiq-cron (>= 1.10.1)
|
sidekiq-cron (>= 1.10.1)
|
||||||
simplecov (= 0.17.1)
|
simplecov (= 0.17.1)
|
||||||
slack-ruby-client (~> 2.0.0)
|
slack-ruby-client (~> 2.2.0)
|
||||||
spring
|
spring
|
||||||
spring-watcher-listen
|
spring-watcher-listen
|
||||||
squasher
|
squasher
|
||||||
|
|||||||
@@ -1,7 +1,49 @@
|
|||||||
class SlackUnfurlJob < ApplicationJob
|
class SlackUnfurlJob < ApplicationJob
|
||||||
queue_as :low
|
queue_as :low
|
||||||
|
|
||||||
def perform(params, integration_hook)
|
def perform(params)
|
||||||
Integrations::Slack::SlackLinkUnfurlService.new(params: params, integration_hook: integration_hook).perform
|
@params = params
|
||||||
|
set_integration_hook
|
||||||
|
|
||||||
|
return unless channel_has_access
|
||||||
|
|
||||||
|
Integrations::Slack::SlackLinkUnfurlService.new(params: @params, integration_hook: @integration_hook).perform
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Find the integration hook by taking first link from array of links
|
||||||
|
# Assume that all the links are from the same account, how ever there is a possibility that the links are from different accounts.
|
||||||
|
# TODO: Fix this edge case later
|
||||||
|
def set_integration_hook
|
||||||
|
url = extract_url
|
||||||
|
return unless url
|
||||||
|
|
||||||
|
account_id = extract_account_id(url)
|
||||||
|
@integration_hook = Integrations::Hook.find_by(account_id: account_id, app_id: 'slack')
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_url
|
||||||
|
@params.dig(:event, :links)&.first&.[](:url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_account_id(url)
|
||||||
|
account_id_regex = %r{/accounts/(\d+)}
|
||||||
|
match_data = url.match(account_id_regex)
|
||||||
|
match_data[1] if match_data
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check the channel has access to the bot to unfurl the links
|
||||||
|
def channel_has_access
|
||||||
|
return if @integration_hook.blank?
|
||||||
|
|
||||||
|
slack_client = Slack::Web::Client.new(token: @integration_hook.access_token)
|
||||||
|
response = slack_client.conversations_members(channel: @params.dig(:event, :channel))
|
||||||
|
response['ok']
|
||||||
|
rescue Slack::Web::Api::Errors::ChannelNotFound => e
|
||||||
|
# The link unfurl event will not work for private channels and other accounts channels
|
||||||
|
# So we can ignore the error
|
||||||
|
Rails.logger.error "Exception in SlackUnfurlJob: #{e.message}"
|
||||||
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Integrations::Slack::IncomingMessageBuilder
|
|||||||
elsif create_message?
|
elsif create_message?
|
||||||
create_message
|
create_message
|
||||||
elsif link_shared?
|
elsif link_shared?
|
||||||
SlackUnfurlJob.perform_later(params, integration_hook)
|
SlackUnfurlJob.perform_later(params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class Integrations::Slack::IncomingMessageBuilder
|
|||||||
end
|
end
|
||||||
|
|
||||||
def link_shared?
|
def link_shared?
|
||||||
params[:event][:type] == 'link_shared' && integration_hook
|
params[:event][:type] == 'link_shared'
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
def message
|
||||||
|
|||||||
@@ -7,15 +7,22 @@ RSpec.describe SlackUnfurlJob do
|
|||||||
let(:hook) { create(:integrations_hook, account: account) }
|
let(:hook) { create(:integrations_hook, account: account) }
|
||||||
let(:inbox) { create(:inbox, account: account) }
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
let!(:conversation) { create(:conversation, inbox: inbox) }
|
let!(:conversation) { create(:conversation, inbox: inbox) }
|
||||||
|
let(:slack_client) { double }
|
||||||
let(:link_shared) do
|
let(:link_shared) do
|
||||||
{
|
{
|
||||||
team_id: 'TLST3048H',
|
team_id: 'TLST3048H',
|
||||||
api_app_id: 'A012S5UETV4',
|
api_app_id: 'A012S5UETV4',
|
||||||
event: link_shared_event.merge(links: [{
|
event: {
|
||||||
url: "https://qa.chatwoot.com/app/accounts/1/conversations/#{conversation.display_id}",
|
type: 'link_shared',
|
||||||
domain: 'qa.chatwoot.com'
|
user: 'ULYPAKE5S',
|
||||||
}]),
|
source: 'conversations_history',
|
||||||
|
unfurl_id: 'C7NQEAE5Q.1695111587.937099.7e240338c6d2053fb49f56808e7c1f619f6ef317c39ebc59fc4af1cc30dce49b',
|
||||||
|
channel: 'G01354F6A6Q',
|
||||||
|
links: [{
|
||||||
|
url: "https://qa.chatwoot.com/app/accounts/#{hook.account_id}/conversations/#{conversation.display_id}",
|
||||||
|
domain: 'qa.chatwoot.com'
|
||||||
|
}]
|
||||||
|
},
|
||||||
type: 'event_callback',
|
type: 'event_callback',
|
||||||
event_time: 1_588_623_033
|
event_time: 1_588_623_033
|
||||||
}
|
}
|
||||||
@@ -26,13 +33,73 @@ RSpec.describe SlackUnfurlJob do
|
|||||||
.on_queue('low')
|
.on_queue('low')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'calls the SlackLinkUnfurlService' do
|
context 'when the calls the slack unfurl job' do
|
||||||
slack_link_unfurl_service = instance_double(Integrations::Slack::SlackLinkUnfurlService)
|
let(:slack_link_unfurl_service) { instance_double(Integrations::Slack::SlackLinkUnfurlService) }
|
||||||
|
|
||||||
expect(Integrations::Slack::SlackLinkUnfurlService).to receive(:new)
|
before do
|
||||||
.with(params: link_shared, integration_hook: hook)
|
allow(Integrations::Slack::SlackLinkUnfurlService).to receive(:new)
|
||||||
.and_return(slack_link_unfurl_service)
|
.with(params: link_shared, integration_hook: hook)
|
||||||
expect(slack_link_unfurl_service).to receive(:perform)
|
.and_return(slack_link_unfurl_service)
|
||||||
described_class.perform_now(link_shared, hook)
|
end
|
||||||
|
|
||||||
|
context 'when the URL is shared in the channel' do
|
||||||
|
let(:expected_body) { { channel: link_shared[:event][:channel] } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:post, 'https://slack.com/api/conversations.members')
|
||||||
|
.with(body: expected_body)
|
||||||
|
.to_return(status: 200, body: { 'ok' => true }.to_json, headers: {})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does the unfurl' do
|
||||||
|
expect(slack_link_unfurl_service).to receive(:perform)
|
||||||
|
described_class.perform_now(link_shared)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the URL is shared in a different channel under the same account' do
|
||||||
|
let(:expected_body) { { channel: 'XSDSFSFS' } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
link_shared[:event][:channel] = 'XSDSFSFS'
|
||||||
|
stub_request(:post, 'https://slack.com/api/conversations.members')
|
||||||
|
.with(body: expected_body)
|
||||||
|
.to_return(status: 200, body: { 'ok' => true }.to_json, headers: {})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does the unfurl' do
|
||||||
|
expect(slack_link_unfurl_service).to receive(:perform)
|
||||||
|
described_class.perform_now(link_shared)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when another account URL is shared' do
|
||||||
|
before do
|
||||||
|
link_shared[:event][:links][0][:url] = 'https://qa.chatwoot.com/app/accounts/123/conversations/123'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not unfurl' do
|
||||||
|
expect(Integrations::Slack::SlackLinkUnfurlService).not_to receive(:new)
|
||||||
|
expect(slack_link_unfurl_service).not_to receive(:perform)
|
||||||
|
described_class.perform_now(link_shared)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the URL is shared in a channel under a different account' do
|
||||||
|
let(:expected_body) { { channel: 'GDF4F6A6Q' } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
link_shared[:event][:channel] = 'GDF4F6A6Q'
|
||||||
|
stub_request(:post, 'https://slack.com/api/conversations.members')
|
||||||
|
.with(body: expected_body)
|
||||||
|
.to_return(status: 404, body: { 'ok' => false }.to_json, headers: {})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not unfurl' do
|
||||||
|
expect(Integrations::Slack::SlackLinkUnfurlService).not_to receive(:new)
|
||||||
|
expect(slack_link_unfurl_service).not_to receive(:perform)
|
||||||
|
described_class.perform_now(link_shared)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ describe Integrations::Slack::IncomingMessageBuilder do
|
|||||||
|
|
||||||
it 'unfurls link' do
|
it 'unfurls link' do
|
||||||
builder = described_class.new(link_shared)
|
builder = described_class.new(link_shared)
|
||||||
expect(SlackUnfurlJob).to receive(:perform_later).with(link_shared, hook)
|
expect(SlackUnfurlJob).to receive(:perform_later).with(link_shared)
|
||||||
builder.perform
|
builder.perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user