twitter-ads 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/CONTRIBUTING.md +77 -0
- data/LICENSE +22 -0
- data/README.md +111 -0
- data/Rakefile +86 -0
- data/bin/twitter-ads +42 -0
- data/lib/twitter-ads.rb +54 -0
- data/lib/twitter-ads/account.rb +229 -0
- data/lib/twitter-ads/audiences/tailored_audience.rb +177 -0
- data/lib/twitter-ads/campaign/app_list.rb +42 -0
- data/lib/twitter-ads/campaign/campaign.rb +40 -0
- data/lib/twitter-ads/campaign/funding_instrument.rb +33 -0
- data/lib/twitter-ads/campaign/line_item.rb +91 -0
- data/lib/twitter-ads/campaign/promotable_user.rb +28 -0
- data/lib/twitter-ads/campaign/targeting_criteria.rb +77 -0
- data/lib/twitter-ads/campaign/tweet.rb +83 -0
- data/lib/twitter-ads/client.rb +92 -0
- data/lib/twitter-ads/creative/app_download_card.rb +44 -0
- data/lib/twitter-ads/creative/image_app_download_card.rb +44 -0
- data/lib/twitter-ads/creative/image_conversation_card.rb +44 -0
- data/lib/twitter-ads/creative/lead_gen_card.rb +46 -0
- data/lib/twitter-ads/creative/promoted_account.rb +38 -0
- data/lib/twitter-ads/creative/promoted_tweet.rb +87 -0
- data/lib/twitter-ads/creative/video.rb +43 -0
- data/lib/twitter-ads/creative/video_app_download_card.rb +47 -0
- data/lib/twitter-ads/creative/video_conversation_card.rb +46 -0
- data/lib/twitter-ads/creative/website_card.rb +48 -0
- data/lib/twitter-ads/cursor.rb +127 -0
- data/lib/twitter-ads/enum.rb +135 -0
- data/lib/twitter-ads/error.rb +93 -0
- data/lib/twitter-ads/http/request.rb +127 -0
- data/lib/twitter-ads/http/response.rb +74 -0
- data/lib/twitter-ads/http/ton_upload.rb +140 -0
- data/lib/twitter-ads/legacy.rb +7 -0
- data/lib/twitter-ads/resources/analytics.rb +90 -0
- data/lib/twitter-ads/resources/dsl.rb +108 -0
- data/lib/twitter-ads/resources/persistence.rb +43 -0
- data/lib/twitter-ads/resources/resource.rb +92 -0
- data/lib/twitter-ads/targeting/reach_estimate.rb +69 -0
- data/lib/twitter-ads/utils.rb +76 -0
- data/lib/twitter-ads/version.rb +6 -0
- data/spec/fixtures/accounts_all.json +65 -0
- data/spec/fixtures/accounts_features.json +18 -0
- data/spec/fixtures/accounts_load.json +19 -0
- data/spec/fixtures/app_lists_all.json +22 -0
- data/spec/fixtures/app_lists_load.json +31 -0
- data/spec/fixtures/campaigns_all.json +208 -0
- data/spec/fixtures/campaigns_load.json +27 -0
- data/spec/fixtures/funding_instruments_all.json +74 -0
- data/spec/fixtures/funding_instruments_load.json +28 -0
- data/spec/fixtures/line_items_all.json +292 -0
- data/spec/fixtures/line_items_load.json +36 -0
- data/spec/fixtures/placements.json +35 -0
- data/spec/fixtures/promotable_users_all.json +57 -0
- data/spec/fixtures/promotable_users_load.json +18 -0
- data/spec/fixtures/promoted_tweets_all.json +212 -0
- data/spec/fixtures/promoted_tweets_load.json +19 -0
- data/spec/fixtures/reach_estimate.json +19 -0
- data/spec/fixtures/tailored_audiences_all.json +67 -0
- data/spec/fixtures/tailored_audiences_load.json +29 -0
- data/spec/fixtures/tweet_preview.json +24 -0
- data/spec/fixtures/videos_all.json +50 -0
- data/spec/fixtures/videos_load.json +22 -0
- data/spec/quality_spec.rb +15 -0
- data/spec/shared/properties.rb +20 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/helpers.rb +42 -0
- data/spec/twitter-ads/account_spec.rb +315 -0
- data/spec/twitter-ads/audiences/tailored_audience_spec.rb +45 -0
- data/spec/twitter-ads/campaign/app_list_spec.rb +108 -0
- data/spec/twitter-ads/campaign/line_item_spec.rb +95 -0
- data/spec/twitter-ads/campaign/reach_estimate_spec.rb +98 -0
- data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +39 -0
- data/spec/twitter-ads/campaign/tweet_spec.rb +83 -0
- data/spec/twitter-ads/client_spec.rb +115 -0
- data/spec/twitter-ads/creative/app_download_card_spec.rb +44 -0
- data/spec/twitter-ads/creative/image_app_download_card_spec.rb +43 -0
- data/spec/twitter-ads/creative/image_conversation_card_spec.rb +40 -0
- data/spec/twitter-ads/creative/lead_gen_card_spec.rb +46 -0
- data/spec/twitter-ads/creative/promoted_account_spec.rb +30 -0
- data/spec/twitter-ads/creative/promoted_tweet_spec.rb +46 -0
- data/spec/twitter-ads/creative/video_app_download_card_spec.rb +42 -0
- data/spec/twitter-ads/creative/video_conversation_card_spec.rb +52 -0
- data/spec/twitter-ads/creative/video_legacy_spec.rb +43 -0
- data/spec/twitter-ads/creative/video_spec.rb +43 -0
- data/spec/twitter-ads/creative/website_card_spec.rb +37 -0
- data/spec/twitter-ads/cursor_spec.rb +67 -0
- data/spec/twitter-ads/placements_spec.rb +36 -0
- data/spec/twitter-ads/utils_spec.rb +101 -0
- data/twitter-ads.gemspec +37 -0
- metadata +247 -0
- 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
|