twitter.rb 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. class ExternalCredential::Twitter
  2. def self.app_verify(params)
  3. register_webhook(params)
  4. end
  5. def self.request_account_to_link(credentials = {}, app_required = true)
  6. external_credential = ExternalCredential.find_by(name: 'twitter')
  7. raise Exceptions::UnprocessableEntity, 'No twitter app configured!' if !external_credential && app_required
  8. if external_credential
  9. if credentials[:consumer_key].blank?
  10. credentials[:consumer_key] = external_credential.credentials['consumer_key']
  11. end
  12. if credentials[:consumer_secret].blank?
  13. credentials[:consumer_secret] = external_credential.credentials['consumer_secret']
  14. end
  15. end
  16. raise Exceptions::UnprocessableEntity, 'No consumer_key param!' if credentials[:consumer_key].blank?
  17. raise Exceptions::UnprocessableEntity, 'No consumer_secret param!' if credentials[:consumer_secret].blank?
  18. consumer = OAuth::Consumer.new(
  19. credentials[:consumer_key],
  20. credentials[:consumer_secret], {
  21. site: 'https://api.twitter.com'
  22. }
  23. )
  24. begin
  25. request_token = consumer.get_request_token(oauth_callback: ExternalCredential.callback_url('twitter'))
  26. rescue => e
  27. case e.message
  28. when '401 Authorization Required'
  29. raise "#{e.message} (Invalid credentials may be to blame.)"
  30. when '403 Forbidden'
  31. raise "#{e.message} (Your app's callback URL configuration on developer.twitter.com may be to blame.)"
  32. else
  33. raise
  34. end
  35. end
  36. {
  37. request_token: request_token,
  38. authorize_url: request_token.authorize_url,
  39. }
  40. end
  41. def self.link_account(request_token, params)
  42. external_credential = ExternalCredential.find_by(name: 'twitter')
  43. raise Exceptions::UnprocessableEntity, 'No twitter app configured!' if !external_credential
  44. raise Exceptions::UnprocessableEntity, 'No request_token for session found!' if !request_token
  45. raise Exceptions::UnprocessableEntity, 'Invalid oauth_token given!' if request_token.params[:oauth_token] != params[:oauth_token]
  46. access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier])
  47. client = TwitterSync.new(
  48. consumer_key: external_credential.credentials[:consumer_key],
  49. consumer_secret: external_credential.credentials[:consumer_secret],
  50. access_token: access_token.token,
  51. access_token_secret: access_token.secret,
  52. )
  53. client_user = client.who_am_i
  54. # check if account already exists
  55. Channel.where(area: 'Twitter::Account').each do |channel|
  56. next if !channel.options
  57. next if !channel.options['user']
  58. next if !channel.options['user']['id']
  59. next if channel.options['user']['id'].to_s != client_user.id.to_s && channel.options['user']['screen_name'] != client_user.screen_name
  60. channel.options['user']['id'] = client_user.id.to_s
  61. channel.options['user']['screen_name'] = client_user.screen_name
  62. channel.options['user']['name'] = client_user.name
  63. # update access_token
  64. channel.options['auth']['external_credential_id'] = external_credential.id
  65. channel.options['auth']['oauth_token'] = access_token.token
  66. channel.options['auth']['oauth_token_secret'] = access_token.secret
  67. channel.save!
  68. subscribe_webhook(
  69. channel: channel,
  70. client: client,
  71. external_credential: external_credential,
  72. )
  73. return channel
  74. end
  75. # create channel
  76. channel = Channel.create!(
  77. area: 'Twitter::Account',
  78. options: {
  79. adapter: 'twitter',
  80. user: {
  81. id: client_user.id.to_s,
  82. screen_name: client_user.screen_name,
  83. name: client_user.name,
  84. },
  85. auth: {
  86. external_credential_id: external_credential.id,
  87. oauth_token: access_token.token,
  88. oauth_token_secret: access_token.secret,
  89. },
  90. sync: {
  91. limit: 20,
  92. search: [],
  93. mentions: {},
  94. direct_messages: {},
  95. track_retweets: false
  96. }
  97. },
  98. active: true,
  99. created_by_id: 1,
  100. updated_by_id: 1,
  101. )
  102. subscribe_webhook(
  103. channel: channel,
  104. client: client,
  105. external_credential: external_credential,
  106. )
  107. channel
  108. end
  109. def self.webhook_url
  110. "#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{Rails.configuration.api_path}/channels_twitter_webhook"
  111. end
  112. def self.register_webhook(params)
  113. request_account_to_link(params, false)
  114. raise Exceptions::UnprocessableEntity, 'No consumer_key param!' if params[:consumer_key].blank?
  115. raise Exceptions::UnprocessableEntity, 'No consumer_secret param!' if params[:consumer_secret].blank?
  116. raise Exceptions::UnprocessableEntity, 'No oauth_token param!' if params[:oauth_token].blank?
  117. raise Exceptions::UnprocessableEntity, 'No oauth_token_secret param!' if params[:oauth_token_secret].blank?
  118. return if params[:env].blank?
  119. env_name = params[:env]
  120. client = TwitterSync.new(
  121. consumer_key: params[:consumer_key],
  122. consumer_secret: params[:consumer_secret],
  123. access_token: params[:oauth_token],
  124. access_token_secret: params[:oauth_token_secret],
  125. )
  126. # needed for verify callback
  127. Cache.write('external_credential_twitter', {
  128. consumer_key: params[:consumer_key],
  129. consumer_secret: params[:consumer_secret],
  130. access_token: params[:oauth_token],
  131. access_token_secret: params[:oauth_token_secret],
  132. })
  133. # verify if webhook is already registered
  134. begin
  135. webhooks = client.webhooks_by_env_name(env_name)
  136. rescue
  137. begin
  138. webhooks = client.webhooks
  139. raise "Dev Environment Label invalid. Please use an existing one #{webhooks[:environments].map { |e| e[:environment_name] }}, or create a new one."
  140. rescue Twitter::Error => e
  141. raise "#{e.message} Are you sure you created a development environment on developer.twitter.com?"
  142. end
  143. end
  144. webhook_id = nil
  145. webhook_valid = nil
  146. webhooks.each do |webhook|
  147. next if webhook[:url] != webhook_url
  148. webhook_id = webhook[:id]
  149. webhook_valid = webhook[:valid]
  150. end
  151. # if webhook is already registered
  152. # - in case if webhook is invalid, just send a new verification request
  153. # - in case if webhook is valid return
  154. if webhook_id
  155. if webhook_valid == false
  156. client.webhook_request_verification(webhook_id, env_name, webhook_url)
  157. end
  158. params[:webhook_id] = webhook_id
  159. return params
  160. end
  161. # delete already registered webhooks
  162. webhooks.each do |webhook|
  163. client.webhook_delete(webhook[:id], env_name)
  164. end
  165. # register new webhook
  166. response = client.webhook_register(env_name, webhook_url)
  167. params[:webhook_id] = response[:id]
  168. params
  169. end
  170. def self.subscribe_webhook(channel:, client:, external_credential:)
  171. env_name = external_credential.credentials[:env]
  172. webhook_id = external_credential.credentials[:webhook_id]
  173. Rails.logger.debug { "Starting Twitter subscription for webhook_id #{webhook_id} and Channel #{channel.id}" }
  174. client.webhook_subscribe(env_name)
  175. channel.options['subscribed_to_webhook_id'] = webhook_id
  176. channel.save!
  177. true
  178. end
  179. end