123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- module TwitterConcern
- extend ActiveSupport::Concern
- included do
- include Oauthable
- validate :validate_twitter_options
- valid_oauth_providers :twitter
- gem_dependency_check {
- defined?(Twitter) &&
- Devise.omniauth_providers.include?(:twitter) &&
- ENV['TWITTER_OAUTH_KEY'].present? &&
- ENV['TWITTER_OAUTH_SECRET'].present?
- }
- end
- def validate_twitter_options
- unless twitter_consumer_key.present? &&
- twitter_consumer_secret.present? &&
- twitter_oauth_token.present? &&
- twitter_oauth_token_secret.present?
- errors.add(
- :base,
- "Twitter consumer_key, consumer_secret, oauth_token, and oauth_token_secret are required to authenticate with the Twitter API. You can provide these as options to this Agent, or as Credentials with the same names, but starting with 'twitter_'."
- )
- end
- end
- def twitter_consumer_key
- (config = Devise.omniauth_configs[:twitter]) && config.strategy.consumer_key
- end
- def twitter_consumer_secret
- (config = Devise.omniauth_configs[:twitter]) && config.strategy.consumer_secret
- end
- def twitter_oauth_token
- service && service.token
- end
- def twitter_oauth_token_secret
- service && service.secret
- end
- def twitter
- @twitter ||= Twitter::REST::Client.new do |config|
- config.consumer_key = twitter_consumer_key
- config.consumer_secret = twitter_consumer_secret
- config.access_token = twitter_oauth_token
- config.access_token_secret = twitter_oauth_token_secret
- end
- end
- HTML_ENTITIES = {
- '&' => '&',
- '<' => '<',
- '>' => '>',
- }
- RE_HTML_ENTITIES = Regexp.union(HTML_ENTITIES.keys)
- def format_tweet(tweet)
- attrs =
- case tweet
- when Twitter::Tweet
- tweet.attrs
- when Hash
- if tweet.key?(:id)
- tweet
- else
- tweet.deep_symbolize_keys
- end
- else
- raise TypeError, "Unexpected tweet type: #{tweet.class}"
- end
- text = (attrs[:full_text] || attrs[:text])&.dup or return attrs
- expanded_text = text.dup.tap { |text|
- attrs.dig(:entities, :urls)&.reverse_each do |entity|
- from, to = entity[:indices]
- text[from...to] = entity[:expanded_url]
- end
- }
- text.gsub!(RE_HTML_ENTITIES, HTML_ENTITIES)
- expanded_text.gsub!(RE_HTML_ENTITIES, HTML_ENTITIES)
- attrs[:text] &&= text
- attrs[:full_text] &&= text
- attrs.update(expanded_text:)
- end
- module_function :format_tweet
- module ClassMethods
- def twitter_dependencies_missing
- if ENV['TWITTER_OAUTH_KEY'].blank? || ENV['TWITTER_OAUTH_SECRET'].blank?
- "## Set TWITTER_OAUTH_KEY and TWITTER_OAUTH_SECRET in your environment to use Twitter Agents."
- elsif !defined?(Twitter) || !Devise.omniauth_providers.include?(:twitter)
- "## Include the `twitter`, `omniauth-twitter`, and `cantino-twitter-stream` gems in your Gemfile to use Twitter Agents."
- end
- end
- def tweet_event_description(text_key, extra_fields = nil)
- <<~MD.indent(4)
- {
- #{extra_fields&.indent(2)}// ... every Tweet field, including ...
- // Huginn automatically decodes "<", ">", and "&" to "<", ">", and "&".
- "#{text_key}": "something https://t.co/XXXX",
- "user": {
- "name": "Mr. Someone",
- "screen_name": "Someone",
- "location": "Vancouver BC Canada",
- "description": "...",
- "followers_count": 486,
- "friends_count": 1983,
- "created_at": "Mon Aug 29 23:38:14 +0000 2011",
- "time_zone": "Pacific Time (US & Canada)",
- "statuses_count": 3807,
- "lang": "en"
- },
- "retweet_count": 0,
- "entities": ...
- "lang": "en",
- // Huginn adds this field, expanding all shortened t.co URLs in "#{text_key}".
- "expanded_text": "something https://example.org/foo/bar"
- }
- MD
- end
- end
- end
- class Twitter::Error
- remove_const :FORBIDDEN_MESSAGES
- FORBIDDEN_MESSAGES = proc do |message|
- case message
- when /(?=.*status).*duplicate/i
- # - "Status is a duplicate."
- Twitter::Error::DuplicateStatus
- when /already favorited/i
- # - "You have already favorited this status."
- Twitter::Error::AlreadyFavorited
- when /already retweeted|Share validations failed/i
- # - "You have already retweeted this Tweet." (Nov 2017-)
- # - "You have already retweeted this tweet." (?-Nov 2017)
- # - "sharing is not permissible for this status (Share validations failed)" (-? 2017)
- Twitter::Error::AlreadyRetweeted
- end
- end
- end
|