twitter-ads 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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