0f61a7e6ca
Special thanks to @thorsteneckel for fixing the VCR/Twitter problems!
215 lines
7.4 KiB
Ruby
215 lines
7.4 KiB
Ruby
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
|
|
|
class ExternalCredential::Twitter
|
|
|
|
def self.app_verify(params)
|
|
register_webhook(params)
|
|
end
|
|
|
|
def self.request_account_to_link(credentials = {}, app_required = true)
|
|
external_credential = ExternalCredential.find_by(name: 'twitter')
|
|
raise Exceptions::UnprocessableEntity, 'No twitter app configured!' if !external_credential && app_required
|
|
|
|
if external_credential
|
|
if credentials[:consumer_key].blank?
|
|
credentials[:consumer_key] = external_credential.credentials['consumer_key']
|
|
end
|
|
if credentials[:consumer_secret].blank?
|
|
credentials[:consumer_secret] = external_credential.credentials['consumer_secret']
|
|
end
|
|
end
|
|
|
|
raise Exceptions::UnprocessableEntity, 'No consumer_key param!' if credentials[:consumer_key].blank?
|
|
raise Exceptions::UnprocessableEntity, 'No consumer_secret param!' if credentials[:consumer_secret].blank?
|
|
|
|
consumer = OAuth::Consumer.new(
|
|
credentials[:consumer_key],
|
|
credentials[:consumer_secret], {
|
|
site: 'https://api.twitter.com'
|
|
}
|
|
)
|
|
begin
|
|
request_token = consumer.get_request_token(oauth_callback: ExternalCredential.callback_url('twitter'))
|
|
rescue => e
|
|
case e.message
|
|
when '401 Unauthorized'
|
|
raise "#{e.message} (Invalid credentials may be to blame.)"
|
|
when '403 Forbidden'
|
|
raise "#{e.message} (Your app's callback URL configuration on developer.twitter.com may be to blame.)"
|
|
else
|
|
raise
|
|
end
|
|
end
|
|
|
|
{
|
|
request_token: request_token,
|
|
authorize_url: request_token.authorize_url,
|
|
}
|
|
end
|
|
|
|
def self.link_account(request_token, params)
|
|
external_credential = ExternalCredential.find_by(name: 'twitter')
|
|
raise Exceptions::UnprocessableEntity, 'No twitter app configured!' if !external_credential
|
|
raise Exceptions::UnprocessableEntity, 'No request_token for session found!' if !request_token
|
|
raise Exceptions::UnprocessableEntity, 'Invalid oauth_token given!' if request_token.params[:oauth_token] != params[:oauth_token]
|
|
|
|
access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier])
|
|
client = TwitterSync.new(
|
|
consumer_key: external_credential.credentials[:consumer_key],
|
|
consumer_secret: external_credential.credentials[:consumer_secret],
|
|
access_token: access_token.token,
|
|
access_token_secret: access_token.secret,
|
|
)
|
|
client_user = client.who_am_i
|
|
|
|
# check if account already exists
|
|
Channel.where(area: 'Twitter::Account').each do |channel|
|
|
next if !channel.options
|
|
next if !channel.options['user']
|
|
next if !channel.options['user']['id']
|
|
next if channel.options['user']['id'].to_s != client_user.id.to_s && channel.options['user']['screen_name'] != client_user.screen_name
|
|
|
|
channel.options['user']['id'] = client_user.id.to_s
|
|
channel.options['user']['screen_name'] = client_user.screen_name
|
|
channel.options['user']['name'] = client_user.name
|
|
|
|
# update access_token
|
|
channel.options['auth']['external_credential_id'] = external_credential.id
|
|
channel.options['auth']['oauth_token'] = access_token.token
|
|
channel.options['auth']['oauth_token_secret'] = access_token.secret
|
|
channel.save!
|
|
|
|
subscribe_webhook(
|
|
channel: channel,
|
|
client: client,
|
|
external_credential: external_credential,
|
|
)
|
|
|
|
return channel
|
|
end
|
|
|
|
# create channel
|
|
channel = Channel.create!(
|
|
area: 'Twitter::Account',
|
|
options: {
|
|
adapter: 'twitter',
|
|
user: {
|
|
id: client_user.id.to_s,
|
|
screen_name: client_user.screen_name,
|
|
name: client_user.name,
|
|
},
|
|
auth: {
|
|
external_credential_id: external_credential.id,
|
|
oauth_token: access_token.token,
|
|
oauth_token_secret: access_token.secret,
|
|
},
|
|
sync: {
|
|
limit: 20,
|
|
search: [],
|
|
mentions: {},
|
|
direct_messages: {},
|
|
track_retweets: false
|
|
}
|
|
},
|
|
active: true,
|
|
created_by_id: 1,
|
|
updated_by_id: 1,
|
|
)
|
|
|
|
subscribe_webhook(
|
|
channel: channel,
|
|
client: client,
|
|
external_credential: external_credential,
|
|
)
|
|
|
|
channel
|
|
end
|
|
|
|
def self.webhook_url
|
|
"#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{Rails.configuration.api_path}/channels_twitter_webhook"
|
|
end
|
|
|
|
def self.register_webhook(params)
|
|
request_account_to_link(params, false)
|
|
|
|
raise Exceptions::UnprocessableEntity, 'No consumer_key param!' if params[:consumer_key].blank?
|
|
raise Exceptions::UnprocessableEntity, 'No consumer_secret param!' if params[:consumer_secret].blank?
|
|
raise Exceptions::UnprocessableEntity, 'No oauth_token param!' if params[:oauth_token].blank?
|
|
raise Exceptions::UnprocessableEntity, 'No oauth_token_secret param!' if params[:oauth_token_secret].blank?
|
|
|
|
return if params[:env].blank?
|
|
|
|
env_name = params[:env]
|
|
|
|
client = TwitterSync.new(
|
|
consumer_key: params[:consumer_key],
|
|
consumer_secret: params[:consumer_secret],
|
|
access_token: params[:oauth_token],
|
|
access_token_secret: params[:oauth_token_secret],
|
|
)
|
|
|
|
# needed for verify callback
|
|
Cache.write('external_credential_twitter', {
|
|
consumer_key: params[:consumer_key],
|
|
consumer_secret: params[:consumer_secret],
|
|
access_token: params[:oauth_token],
|
|
access_token_secret: params[:oauth_token_secret],
|
|
})
|
|
|
|
# verify if webhook is already registered
|
|
begin
|
|
webhooks = client.webhooks_by_env_name(env_name)
|
|
rescue
|
|
begin
|
|
webhooks = client.webhooks
|
|
raise "Dev Environment Label invalid. Please use an existing one #{webhooks[:environments].pluck(:environment_name)}, or create a new one."
|
|
rescue Twitter::Error => e
|
|
raise "#{e.message} Are you sure you created a development environment on developer.twitter.com?"
|
|
end
|
|
end
|
|
webhook_id = nil
|
|
webhook_valid = nil
|
|
webhooks.each do |webhook|
|
|
next if webhook[:url] != webhook_url
|
|
|
|
webhook_id = webhook[:id]
|
|
webhook_valid = webhook[:valid]
|
|
end
|
|
|
|
# if webhook is already registered
|
|
# - in case if webhook is invalid, just send a new verification request
|
|
# - in case if webhook is valid return
|
|
if webhook_id
|
|
if webhook_valid == false
|
|
client.webhook_request_verification(webhook_id, env_name, webhook_url)
|
|
end
|
|
params[:webhook_id] = webhook_id
|
|
return params
|
|
end
|
|
|
|
# delete already registered webhooks
|
|
webhooks.each do |webhook|
|
|
client.webhook_delete(webhook[:id], env_name)
|
|
end
|
|
|
|
# register new webhook
|
|
response = client.webhook_register(env_name, webhook_url)
|
|
|
|
params[:webhook_id] = response[:id]
|
|
params
|
|
end
|
|
|
|
def self.subscribe_webhook(channel:, client:, external_credential:)
|
|
env_name = external_credential.credentials[:env]
|
|
webhook_id = external_credential.credentials[:webhook_id]
|
|
|
|
Rails.logger.debug { "Starting Twitter subscription for webhook_id #{webhook_id} and Channel #{channel.id}" }
|
|
client.webhook_subscribe(env_name)
|
|
|
|
channel.options['subscribed_to_webhook_id'] = webhook_id
|
|
channel.save!
|
|
|
|
true
|
|
end
|
|
|
|
end
|