twitter-ads 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CONTRIBUTING.md +77 -0
  5. data/LICENSE +22 -0
  6. data/README.md +111 -0
  7. data/Rakefile +86 -0
  8. data/bin/twitter-ads +42 -0
  9. data/lib/twitter-ads.rb +54 -0
  10. data/lib/twitter-ads/account.rb +229 -0
  11. data/lib/twitter-ads/audiences/tailored_audience.rb +177 -0
  12. data/lib/twitter-ads/campaign/app_list.rb +42 -0
  13. data/lib/twitter-ads/campaign/campaign.rb +40 -0
  14. data/lib/twitter-ads/campaign/funding_instrument.rb +33 -0
  15. data/lib/twitter-ads/campaign/line_item.rb +91 -0
  16. data/lib/twitter-ads/campaign/promotable_user.rb +28 -0
  17. data/lib/twitter-ads/campaign/targeting_criteria.rb +77 -0
  18. data/lib/twitter-ads/campaign/tweet.rb +83 -0
  19. data/lib/twitter-ads/client.rb +92 -0
  20. data/lib/twitter-ads/creative/app_download_card.rb +44 -0
  21. data/lib/twitter-ads/creative/image_app_download_card.rb +44 -0
  22. data/lib/twitter-ads/creative/image_conversation_card.rb +44 -0
  23. data/lib/twitter-ads/creative/lead_gen_card.rb +46 -0
  24. data/lib/twitter-ads/creative/promoted_account.rb +38 -0
  25. data/lib/twitter-ads/creative/promoted_tweet.rb +87 -0
  26. data/lib/twitter-ads/creative/video.rb +43 -0
  27. data/lib/twitter-ads/creative/video_app_download_card.rb +47 -0
  28. data/lib/twitter-ads/creative/video_conversation_card.rb +46 -0
  29. data/lib/twitter-ads/creative/website_card.rb +48 -0
  30. data/lib/twitter-ads/cursor.rb +127 -0
  31. data/lib/twitter-ads/enum.rb +135 -0
  32. data/lib/twitter-ads/error.rb +93 -0
  33. data/lib/twitter-ads/http/request.rb +127 -0
  34. data/lib/twitter-ads/http/response.rb +74 -0
  35. data/lib/twitter-ads/http/ton_upload.rb +140 -0
  36. data/lib/twitter-ads/legacy.rb +7 -0
  37. data/lib/twitter-ads/resources/analytics.rb +90 -0
  38. data/lib/twitter-ads/resources/dsl.rb +108 -0
  39. data/lib/twitter-ads/resources/persistence.rb +43 -0
  40. data/lib/twitter-ads/resources/resource.rb +92 -0
  41. data/lib/twitter-ads/targeting/reach_estimate.rb +69 -0
  42. data/lib/twitter-ads/utils.rb +76 -0
  43. data/lib/twitter-ads/version.rb +6 -0
  44. data/spec/fixtures/accounts_all.json +65 -0
  45. data/spec/fixtures/accounts_features.json +18 -0
  46. data/spec/fixtures/accounts_load.json +19 -0
  47. data/spec/fixtures/app_lists_all.json +22 -0
  48. data/spec/fixtures/app_lists_load.json +31 -0
  49. data/spec/fixtures/campaigns_all.json +208 -0
  50. data/spec/fixtures/campaigns_load.json +27 -0
  51. data/spec/fixtures/funding_instruments_all.json +74 -0
  52. data/spec/fixtures/funding_instruments_load.json +28 -0
  53. data/spec/fixtures/line_items_all.json +292 -0
  54. data/spec/fixtures/line_items_load.json +36 -0
  55. data/spec/fixtures/placements.json +35 -0
  56. data/spec/fixtures/promotable_users_all.json +57 -0
  57. data/spec/fixtures/promotable_users_load.json +18 -0
  58. data/spec/fixtures/promoted_tweets_all.json +212 -0
  59. data/spec/fixtures/promoted_tweets_load.json +19 -0
  60. data/spec/fixtures/reach_estimate.json +19 -0
  61. data/spec/fixtures/tailored_audiences_all.json +67 -0
  62. data/spec/fixtures/tailored_audiences_load.json +29 -0
  63. data/spec/fixtures/tweet_preview.json +24 -0
  64. data/spec/fixtures/videos_all.json +50 -0
  65. data/spec/fixtures/videos_load.json +22 -0
  66. data/spec/quality_spec.rb +15 -0
  67. data/spec/shared/properties.rb +20 -0
  68. data/spec/spec_helper.rb +61 -0
  69. data/spec/support/helpers.rb +42 -0
  70. data/spec/twitter-ads/account_spec.rb +315 -0
  71. data/spec/twitter-ads/audiences/tailored_audience_spec.rb +45 -0
  72. data/spec/twitter-ads/campaign/app_list_spec.rb +108 -0
  73. data/spec/twitter-ads/campaign/line_item_spec.rb +95 -0
  74. data/spec/twitter-ads/campaign/reach_estimate_spec.rb +98 -0
  75. data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +39 -0
  76. data/spec/twitter-ads/campaign/tweet_spec.rb +83 -0
  77. data/spec/twitter-ads/client_spec.rb +115 -0
  78. data/spec/twitter-ads/creative/app_download_card_spec.rb +44 -0
  79. data/spec/twitter-ads/creative/image_app_download_card_spec.rb +43 -0
  80. data/spec/twitter-ads/creative/image_conversation_card_spec.rb +40 -0
  81. data/spec/twitter-ads/creative/lead_gen_card_spec.rb +46 -0
  82. data/spec/twitter-ads/creative/promoted_account_spec.rb +30 -0
  83. data/spec/twitter-ads/creative/promoted_tweet_spec.rb +46 -0
  84. data/spec/twitter-ads/creative/video_app_download_card_spec.rb +42 -0
  85. data/spec/twitter-ads/creative/video_conversation_card_spec.rb +52 -0
  86. data/spec/twitter-ads/creative/video_legacy_spec.rb +43 -0
  87. data/spec/twitter-ads/creative/video_spec.rb +43 -0
  88. data/spec/twitter-ads/creative/website_card_spec.rb +37 -0
  89. data/spec/twitter-ads/cursor_spec.rb +67 -0
  90. data/spec/twitter-ads/placements_spec.rb +36 -0
  91. data/spec/twitter-ads/utils_spec.rb +101 -0
  92. data/twitter-ads.gemspec +37 -0
  93. metadata +247 -0
  94. metadata.gz.sig +0 -0
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module Tweet
6
+
7
+ # cannot instaniate Tweet, only including class methods for stats
8
+ extend TwitterAds::Analytics::ClassMethods
9
+
10
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/tweet/preview'.freeze # @api private
11
+ RESOURCE_STATS = '/0/stats/accounts/%{account_id}/organic_tweets'.freeze # @api private
12
+ RESOURCE = '/0/accounts/%{account_id}/tweet/preview/%{id}'.freeze # @api private
13
+ RESOURCE_CREATE = '/0/accounts/%{account_id}/tweet'.freeze # @api private
14
+
15
+ class << self
16
+
17
+ # Returns an HTML preview of a tweet, either new or existing
18
+ #
19
+ # @example
20
+ # Tweet.preview(account, status: 'potatoes can be deadly...')
21
+ # Tweet.preview(account, id: 634798319504617472)
22
+ #
23
+ # @param client [Client] The Client object instance.
24
+ # @param account [Account] The Account object instance.
25
+ # @param opts [Hash] A hash of options.
26
+ #
27
+ # @option opts [Integer] :id The ID of an existing Tweet you want to preview.
28
+ # @option opts [String] :status The text of your status update, up to 140 characters.
29
+ # @option opts [Array] :media_ids A list of up to four media IDs to associate with the Tweet.
30
+ # @option opts [String] :card_id The base-36 ID of a revenue card to be embedded in the Tweet.
31
+ # @option opts [String] :preview_target The target to render the Tweet preview
32
+ # for (eg. TWITTER_TIMELINES).
33
+ #
34
+ # @return [Array] An array containing platforms & their respective tweet previews
35
+ #
36
+ # @since 0.2.0
37
+ # @see https://dev.twitter.com/ads/reference/get/accounts/%3Aaccount_id/tweet/preview
38
+ def preview(account, opts = {})
39
+ resource = opts.key?(:id) ? RESOURCE : RESOURCE_COLLECTION
40
+ resource = resource % { account_id: account.id, id: opts.delete(:id) }
41
+
42
+ # url encodes status message if present
43
+ opts[:status] = URI.escape(opts[:status]) if opts.key?(:status)
44
+
45
+ # handles array to string conversion for media IDs
46
+ if opts.key?(:media_ids) && opts[:media_ids].respond_to?(:join)
47
+ opts[:media_ids] = opts[:media_ids].join(',')
48
+ end
49
+
50
+ response = TwitterAds::Request.new(account.client, :get, resource, params: opts).perform
51
+ response.body[:data]
52
+ end
53
+
54
+ # Creates a "Promoted-Only" Tweet using the specialized Ads API end point.
55
+ #
56
+ # @param status [String] The main Tweet body (max: 140 characters).
57
+ # @param opts [Hash] A hash of options.
58
+ #
59
+ # @option opts [Array] :media_ids A list of up to four media IDs to associate with the Tweet.
60
+ # @option opts [Integer] :as_user_id The user ID whom you are posting the Tweet on behalf of.
61
+ # @option opts [Boolean] :trim_user Excludes the user object from the hydrated Tweet response.
62
+ # @option opts [String] :video_id The Video UUID to be associated with thie Tweet.
63
+ # @option opts [String] :video_title An optional title to be included.
64
+ # @option opts [String] :video_description An optional description to be included.
65
+ # @option opts [String] :video_cta An optional CTA value for the associated video.
66
+ # @option opts [String] :video_cta_value The value for the corresponding CTA.
67
+ #
68
+ # @since 0.3.0
69
+ #
70
+ # @see https://dev.twitter.com/ads/reference/post/accounts/%3Aaccount_id/tweet
71
+ #
72
+ # @return [Hash] A hash containing the newly created Tweet object.
73
+ def create(account, status, opts = {})
74
+ params = { status: status }.merge!(opts)
75
+ resource = RESOURCE_CREATE % { account_id: account.id }
76
+ response = TwitterAds::Request.new(account.client, :post, resource, params: params).perform
77
+ response.body[:data]
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+
6
+ # The Ads API Client class which functions as a
7
+ # container for basic API consumer information.
8
+ class Client
9
+
10
+ attr_accessor :consumer_key,
11
+ :consumer_secret,
12
+ :access_token,
13
+ :access_token_secret,
14
+ :options,
15
+ :logger
16
+
17
+ # Creates a new Ads API client instance.
18
+ #
19
+ # @param consumer_key nil [String] The application consumer key value.
20
+ # @param consumer_secret nil [String] The application consumer secret value.
21
+ # @param access_token nil [String] The access token value.
22
+ # @param access_token_secret nil [String] The access token secret value.
23
+ #
24
+ # @param opts [Hash] An optional Hash of extended options.
25
+ # @option opts [Boolean] :sandbox When true, enables sandbox mode for all requests.
26
+ # @option opts [Boolean] :trace When true, enables verbose request tracing for all requests.
27
+ #
28
+ # @since 0.1.0
29
+ #
30
+ # @return [Client] The newly created client instance.
31
+ def initialize(consumer_key, consumer_secret, access_token, access_token_secret, opts = {})
32
+ @consumer_key = consumer_key
33
+ @consumer_secret = consumer_secret
34
+ @access_token = access_token
35
+ @access_token_secret = access_token_secret
36
+ @options = opts
37
+ validate
38
+ self
39
+ end
40
+
41
+ # Returns the Logger instance for request logging.
42
+ #
43
+ # @since 0.2.0
44
+ #
45
+ # @return [Logger] The logger instance.
46
+ def logger
47
+ @logger ||= Logger.new(STDOUT)
48
+ @logger.progname = 'twitter-ads' unless @logger.progname
49
+ @logger
50
+ end
51
+
52
+ # Returns an inspection string for the current Client instance.
53
+ #
54
+ # @example
55
+ # client.inspect
56
+ #
57
+ # @since 0.1.0
58
+ #
59
+ # @return [String] The inspection string.
60
+ def inspect
61
+ "#<#{self.class.name}:0x#{object_id} consumer_key=\"#{@consumer_key}\">"
62
+ end
63
+
64
+ # Returns a collection of advertiser Accounts available to the current access token.
65
+ #
66
+ # @example
67
+ # client.accounts
68
+ # client.accounts('3ofs6l')
69
+ # client.accounts('3ofs6l', with_deleted: true)
70
+ #
71
+ # @param id=nil [String] The account ID string.
72
+ # @param opts={} [Hash] Hash of optional values.
73
+ #
74
+ # @option opts [String] :with_deleted Indicates whether or not to included deleted objects.
75
+ #
76
+ # @since 0.1.0
77
+ #
78
+ # @return [Account] The instance of the Account object.
79
+ def accounts(id = nil, opts = {})
80
+ id ? Account.load(self, id) : Account.all(self, opts)
81
+ end
82
+
83
+ private
84
+
85
+ def validate
86
+ [:consumer_key, :consumer_secret, :access_token, :access_token_secret].each do |name|
87
+ fail(ArgumentError, "Error! Missing required #{name}.") unless send(name)
88
+ end
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module Creative
6
+
7
+ class AppDownloadCard
8
+
9
+ include TwitterAds::DSL
10
+ include TwitterAds::Resource
11
+ include TwitterAds::Persistence
12
+
13
+ attr_reader :account
14
+
15
+ property :id, read_only: true
16
+ property :preview_url, read_only: true
17
+ property :deleted, type: :bool, read_only: true
18
+ property :created_at, type: :time, read_only: true
19
+ property :updated_at, type: :time, read_only: true
20
+
21
+ property :name
22
+ property :app_country_code
23
+ property :iphone_app_id
24
+ property :iphone_deep_link
25
+ property :ipad_app_id
26
+ property :ipad_deep_link
27
+ property :googleplay_app_id
28
+ property :googleplay_deep_link
29
+ property :app_cta
30
+ property :custom_icon_media_id
31
+ property :custom_app_description
32
+
33
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/cards/app_download'.freeze # @api private
34
+ RESOURCE = '/0/accounts/%{account_id}/cards/app_download/%{id}'.freeze # @api private
35
+
36
+ def initialize(account)
37
+ @account = account
38
+ self
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module Creative
6
+
7
+ class ImageAppDownloadCard
8
+
9
+ include TwitterAds::DSL
10
+ include TwitterAds::Resource
11
+ include TwitterAds::Persistence
12
+
13
+ attr_reader :account
14
+
15
+ property :id, read_only: true
16
+ property :preview_url, read_only: true
17
+ property :deleted, type: :bool, read_only: true
18
+ property :created_at, type: :time, read_only: true
19
+ property :updated_at, type: :time, read_only: true
20
+
21
+ property :name
22
+ property :app_country_code
23
+ property :iphone_app_id
24
+ property :iphone_deep_link
25
+ property :ipad_app_id
26
+ property :ipad_deep_link
27
+ property :googleplay_app_id
28
+ property :googleplay_deep_link
29
+ property :app_cta
30
+ property :wide_app_image_media_id
31
+
32
+ RESOURCE_COLLECTION =
33
+ '/0/accounts/%{account_id}/cards/image_app_download'.freeze # @api private
34
+ RESOURCE = '/0/accounts/%{account_id}/cards/image_app_download/%{id}'.freeze # @api private
35
+
36
+ def initialize(account)
37
+ @account = account
38
+ self
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module Creative
6
+
7
+ class ImageConversationCard
8
+
9
+ include TwitterAds::DSL
10
+ include TwitterAds::Resource
11
+ include TwitterAds::Persistence
12
+
13
+ attr_reader :account
14
+
15
+ property :id, read_only: true
16
+ property :preview_url, read_only: true
17
+ property :image, read_only: true
18
+ property :deleted, type: :bool, read_only: true
19
+ property :created_at, type: :time, read_only: true
20
+ property :updated_at, type: :time, read_only: true
21
+
22
+ property :name
23
+ property :title
24
+ property :first_cta
25
+ property :first_cta_tweet
26
+ property :second_cta
27
+ property :second_cta_tweet
28
+ property :thank_you_text
29
+ property :thank_you_url
30
+ property :image_media_id
31
+
32
+ RESOURCE_COLLECTION =
33
+ '/0/accounts/%{account_id}/cards/image_conversation'.freeze # @api private
34
+ RESOURCE = '/0/accounts/%{account_id}/cards/image_conversation/%{id}'.freeze # @api private
35
+
36
+ def initialize(account)
37
+ @account = account
38
+ self
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module Creative
6
+
7
+ class LeadGenCard
8
+
9
+ include TwitterAds::DSL
10
+ include TwitterAds::Resource
11
+ include TwitterAds::Persistence
12
+
13
+ attr_reader :account
14
+
15
+ property :id, read_only: true
16
+ property :preview_url, read_only: true
17
+ property :deleted, type: :bool, read_only: true
18
+ property :created_at, type: :time, read_only: true
19
+ property :updated_at, type: :time, read_only: true
20
+
21
+ property :name
22
+ property :image_media_id
23
+ property :cta
24
+ property :fallback_url
25
+ property :privacy_policy_url
26
+ property :title
27
+ property :submit_url
28
+ property :submit_method
29
+ property :custom_destination_url
30
+ property :custom_destination_text
31
+ property :custom_key_screen_name
32
+ property :custom_key_name
33
+ property :custom_key_email
34
+
35
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/cards/lead_gen'.freeze # @api private
36
+ RESOURCE = '/0/accounts/%{account_id}/cards/lead_gen/%{id}'.freeze # @api private
37
+
38
+ def initialize(account)
39
+ @account = account
40
+ self
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module Creative
6
+
7
+ class PromotedAccount
8
+
9
+ include TwitterAds::DSL
10
+ include TwitterAds::Resource
11
+ include TwitterAds::Persistence
12
+ include TwitterAds::Analytics
13
+
14
+ attr_reader :account
15
+
16
+ property :id, read_only: true
17
+ property :approval_status, read_only: true
18
+ property :created_at, type: :time, read_only: true
19
+ property :updated_at, type: :time, read_only: true
20
+ property :deleted, type: :bool, read_only: true
21
+
22
+ property :line_item_id
23
+ property :user_id
24
+ property :paused, type: :bool
25
+
26
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/promoted_accounts'.freeze # @api private
27
+ RESOURCE_STATS = '/0/stats/accounts/%{account_id}/promoted_accounts'.freeze # @api private
28
+ RESOURCE = '/0/accounts/%{account_id}/promoted_accounts/%{id}'.freeze # @api private
29
+
30
+ def initialize(account)
31
+ @account = account
32
+ self
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module Creative
6
+
7
+ class PromotedTweet
8
+
9
+ include TwitterAds::DSL
10
+ include TwitterAds::Resource
11
+ include TwitterAds::Persistence
12
+ include TwitterAds::Analytics
13
+
14
+ attr_reader :account
15
+
16
+ property :id, read_only: true
17
+ property :approval_status, read_only: true
18
+ property :created_at, type: :time, read_only: true
19
+ property :updated_at, type: :time, read_only: true
20
+ property :deleted, type: :bool, read_only: true
21
+
22
+ property :line_item_id
23
+ property :tweet_id
24
+ property :paused, type: :bool
25
+
26
+ RESOURCE_COLLECTION = '/0/accounts/%{account_id}/promoted_tweets'.freeze # @api private
27
+ RESOURCE_STATS = '/0/stats/accounts/%{account_id}/promoted_tweets'.freeze # @api private
28
+ RESOURCE = '/0/accounts/%{account_id}/promoted_tweets/%{id}'.freeze # @api private
29
+
30
+ def initialize(account)
31
+ @account = account
32
+ self
33
+ end
34
+
35
+ # Saves or updates the current object instance depending on the presence of `object.id`.
36
+ #
37
+ # @example
38
+ # object.save
39
+ #
40
+ # @return [self] Returns the instance refreshed from the API.
41
+ #
42
+ # Note: override to handle the inconsistency of the promoted tweet endpoint. (see REVAPI-5348)
43
+ #
44
+ # @since 0.2.4
45
+ def save
46
+ # manually check for missing params (due to API discrepancy)
47
+ validate
48
+
49
+ # convert to `tweet_ids` param
50
+ params = to_params
51
+ params[:tweet_ids] = *params.delete(:tweet_id) if params.key?(:tweet_id)
52
+
53
+ if @id
54
+ resource = self.class::RESOURCE % { account_id: account.id, id: id }
55
+ response = Request.new(account.client, :put, resource, params: params).perform
56
+ from_response(response.body[:data])
57
+ else
58
+ resource = self.class::RESOURCE_COLLECTION % { account_id: account.id }
59
+ response = Request.new(account.client, :post, resource, params: params).perform
60
+ from_response(response.body[:data].first)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def validate
67
+ details = []
68
+
69
+ unless @line_item_id
70
+ details << { code: 'MISSING_PARAMETER',
71
+ message: '"line_item_id" is a required parameter',
72
+ parameter: 'line_item_id' }
73
+ end
74
+
75
+ unless @tweet_id
76
+ details << { code: 'MISSING_PARAMETER',
77
+ message: '"tweet_id" is a required parameter',
78
+ parameter: 'tweet_id' }
79
+ end
80
+
81
+ raise TwitterAds::ClientError.new(nil, details, 400) unless details.empty?
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end