postproxy-sdk 1.13.0 → 1.14.0
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.
- checksums.yaml +4 -4
- data/README.md +415 -11
- data/lib/postproxy/client.rb +72 -7
- data/lib/postproxy/constants.rb +37 -2
- data/lib/postproxy/errors.rb +15 -0
- data/lib/postproxy/resources/api_keys.rb +30 -0
- data/lib/postproxy/resources/chats.rb +30 -1
- data/lib/postproxy/resources/comments.rb +52 -4
- data/lib/postproxy/resources/google_business.rb +194 -0
- data/lib/postproxy/resources/messages.rb +25 -1
- data/lib/postproxy/resources/placements.rb +35 -0
- data/lib/postproxy/resources/posts.rb +86 -25
- data/lib/postproxy/resources/profile_groups.rb +8 -1
- data/lib/postproxy/resources/profiles.rb +131 -0
- data/lib/postproxy/resources/whatsapp.rb +278 -0
- data/lib/postproxy/resources/whatsapp_templates.rb +108 -0
- data/lib/postproxy/types.rb +408 -29
- data/lib/postproxy/version.rb +1 -1
- data/lib/postproxy/webhook_events.rb +90 -4
- metadata +9 -4
data/lib/postproxy/types.rb
CHANGED
|
@@ -19,11 +19,22 @@ module PostProxy
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
class Profile < Model
|
|
22
|
-
|
|
22
|
+
# `inactive_reason` says why an "inactive" profile was deactivated —
|
|
23
|
+
# "network_taken" means the same account was connected elsewhere.
|
|
24
|
+
# `platform_id` is the account's id on the platform.
|
|
25
|
+
attr_accessor :id, :name, :username, :status, :inactive_reason, :platform,
|
|
26
|
+
:platform_id, :profile_group_id, :expires_at, :post_count,
|
|
27
|
+
:avatar_url, :platform_url, :placements_sync_status
|
|
23
28
|
|
|
24
29
|
def initialize(**attrs)
|
|
30
|
+
@username = nil
|
|
31
|
+
@inactive_reason = nil
|
|
32
|
+
@platform_id = nil
|
|
25
33
|
@expires_at = nil
|
|
26
34
|
@post_count = 0
|
|
35
|
+
@avatar_url = nil
|
|
36
|
+
@platform_url = nil
|
|
37
|
+
@placements_sync_status = nil
|
|
27
38
|
super
|
|
28
39
|
@expires_at = parse_time(@expires_at)
|
|
29
40
|
end
|
|
@@ -75,19 +86,39 @@ module PostProxy
|
|
|
75
86
|
end
|
|
76
87
|
end
|
|
77
88
|
|
|
89
|
+
# A file PostProxy generated for one platform, e.g. a TikTok cover.
|
|
90
|
+
class PlatformAttachment < Model
|
|
91
|
+
attr_accessor :id, :type, :status, :content_type, :url
|
|
92
|
+
|
|
93
|
+
def initialize(**attrs)
|
|
94
|
+
@content_type = nil
|
|
95
|
+
@url = nil
|
|
96
|
+
super
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
78
100
|
class PlatformResult < Model
|
|
79
|
-
attr_accessor :platform, :
|
|
101
|
+
attr_accessor :platform, :profile_id, :profile_name, :status, :permalink,
|
|
102
|
+
:params, :error, :error_details, :attempted_at, :insights,
|
|
103
|
+
:attachments
|
|
80
104
|
|
|
81
105
|
def initialize(**attrs)
|
|
106
|
+
@profile_id = nil
|
|
107
|
+
@profile_name = nil
|
|
108
|
+
@permalink = nil
|
|
82
109
|
@params = nil
|
|
83
110
|
@error = nil
|
|
84
111
|
@error_details = nil
|
|
85
112
|
@attempted_at = nil
|
|
86
113
|
@insights = nil
|
|
114
|
+
@attachments = nil
|
|
87
115
|
super
|
|
88
116
|
@attempted_at = parse_time(@attempted_at)
|
|
89
117
|
@insights = Insights.new(**@insights) if @insights.is_a?(Hash)
|
|
90
118
|
@error_details = ErrorDetails.new(**@error_details.transform_keys(&:to_sym)) if @error_details.is_a?(Hash)
|
|
119
|
+
@attachments = @attachments&.map do |a|
|
|
120
|
+
a.is_a?(PlatformAttachment) ? a : PlatformAttachment.new(**a.transform_keys(&:to_sym))
|
|
121
|
+
end
|
|
91
122
|
end
|
|
92
123
|
|
|
93
124
|
private
|
|
@@ -127,14 +158,20 @@ module PostProxy
|
|
|
127
158
|
end
|
|
128
159
|
|
|
129
160
|
class ThreadChild < Model
|
|
130
|
-
|
|
161
|
+
# Each thread item is delivered separately; `platforms` shows whether it
|
|
162
|
+
# went out.
|
|
163
|
+
attr_accessor :id, :body, :media, :platforms
|
|
131
164
|
|
|
132
165
|
def initialize(**attrs)
|
|
133
166
|
@media = []
|
|
167
|
+
@platforms = []
|
|
134
168
|
super
|
|
135
169
|
@media = (@media || []).map do |m|
|
|
136
170
|
m.is_a?(Media) ? m : Media.new(**m.transform_keys(&:to_sym))
|
|
137
171
|
end
|
|
172
|
+
@platforms = (@platforms || []).map do |p|
|
|
173
|
+
p.is_a?(PlatformResult) ? p : PlatformResult.new(**p.transform_keys(&:to_sym))
|
|
174
|
+
end
|
|
138
175
|
end
|
|
139
176
|
end
|
|
140
177
|
|
|
@@ -162,10 +199,13 @@ module PostProxy
|
|
|
162
199
|
end
|
|
163
200
|
|
|
164
201
|
class Post < Model
|
|
202
|
+
# `source` is "postproxy" or "imported".
|
|
165
203
|
attr_accessor :id, :body, :status, :scheduled_at, :created_at, :media, :platforms, :thread,
|
|
166
|
-
:queue_id, :queue_priority
|
|
204
|
+
:queue_id, :queue_priority, :draft, :source
|
|
167
205
|
|
|
168
206
|
def initialize(**attrs)
|
|
207
|
+
@draft = nil
|
|
208
|
+
@source = nil
|
|
169
209
|
@scheduled_at = nil
|
|
170
210
|
@media = []
|
|
171
211
|
@platforms = []
|
|
@@ -237,7 +277,53 @@ module PostProxy
|
|
|
237
277
|
class Placement < Model
|
|
238
278
|
# `metadata` and `profile_group_id` are present in placements and
|
|
239
279
|
# assign_placement_to_group responses.
|
|
240
|
-
attr_accessor :id, :name, :metadata, :profile_group_id
|
|
280
|
+
attr_accessor :id, :name, :avatar_url, :metadata, :profile_group_id
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
class AccountPlacementProfile < Model
|
|
284
|
+
attr_accessor :id, :name, :connected
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Account-level placement, returned by Placements#list. A placement (a
|
|
288
|
+
# Facebook Page, LinkedIn organization, WhatsApp number, ...) can be
|
|
289
|
+
# reachable through several connected profiles; one of them is active.
|
|
290
|
+
class AccountPlacement < Model
|
|
291
|
+
attr_accessor :id, :platform, :external_id, :name, :avatar_url, :status,
|
|
292
|
+
:active_profile, :linked_profiles
|
|
293
|
+
|
|
294
|
+
def initialize(**attrs)
|
|
295
|
+
@avatar_url = nil
|
|
296
|
+
@active_profile = nil
|
|
297
|
+
@linked_profiles = []
|
|
298
|
+
super
|
|
299
|
+
if @active_profile.is_a?(Hash)
|
|
300
|
+
@active_profile = AccountPlacementProfile.new(**@active_profile.transform_keys(&:to_sym))
|
|
301
|
+
end
|
|
302
|
+
@linked_profiles = (@linked_profiles || []).map do |p|
|
|
303
|
+
p.is_a?(AccountPlacementProfile) ? p : AccountPlacementProfile.new(**p.transform_keys(&:to_sym))
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
class ApiKey < Model
|
|
309
|
+
# `token` is only returned by ApiKeys#create — store it, it can't be read
|
|
310
|
+
# again.
|
|
311
|
+
attr_accessor :id, :name, :profile_group_id, :sandbox, :token, :created_at
|
|
312
|
+
|
|
313
|
+
def initialize(**attrs)
|
|
314
|
+
@profile_group_id = nil
|
|
315
|
+
@sandbox = false
|
|
316
|
+
@token = nil
|
|
317
|
+
super
|
|
318
|
+
@created_at = parse_time(@created_at)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
private
|
|
322
|
+
|
|
323
|
+
def parse_time(value)
|
|
324
|
+
return nil if value.nil?
|
|
325
|
+
value.is_a?(Time) ? value : Time.parse(value.to_s)
|
|
326
|
+
end
|
|
241
327
|
end
|
|
242
328
|
|
|
243
329
|
# Instagram DM ice breaker (FAQ prompt). May carry extra platform fields.
|
|
@@ -365,7 +451,8 @@ module PostProxy
|
|
|
365
451
|
attr_accessor :id, :external_id, :body, :status, :author_username,
|
|
366
452
|
:author_avatar_url, :author_external_id, :metadata,
|
|
367
453
|
:parent_external_id, :like_count, :is_hidden, :permalink,
|
|
368
|
-
:platform_data, :attachments, :
|
|
454
|
+
:platform_data, :attachments, :external_edited_at, :posted_at,
|
|
455
|
+
:created_at, :replies
|
|
369
456
|
|
|
370
457
|
def initialize(**attrs)
|
|
371
458
|
@external_id = nil
|
|
@@ -378,8 +465,10 @@ module PostProxy
|
|
|
378
465
|
@permalink = nil
|
|
379
466
|
@platform_data = nil
|
|
380
467
|
@attachments = []
|
|
468
|
+
@external_edited_at = nil
|
|
381
469
|
@replies = []
|
|
382
470
|
super
|
|
471
|
+
@external_edited_at = parse_time(@external_edited_at)
|
|
383
472
|
@posted_at = parse_time(@posted_at)
|
|
384
473
|
@created_at = parse_time(@created_at)
|
|
385
474
|
@attachments = (@attachments || []).map do |a|
|
|
@@ -399,12 +488,21 @@ module PostProxy
|
|
|
399
488
|
end
|
|
400
489
|
|
|
401
490
|
class Chat < Model
|
|
402
|
-
|
|
403
|
-
|
|
491
|
+
# `external_placement_id` is the page / phone number the chat runs through;
|
|
492
|
+
# `group` marks a WhatsApp group chat. `within_messaging_window` is
|
|
493
|
+
# WhatsApp-only: whether the 24-hour customer service window is open —
|
|
494
|
+
# when it's closed only a template can be sent.
|
|
495
|
+
attr_accessor :id, :profile_id, :platform, :external_placement_id, :group,
|
|
496
|
+
:participant_external_id, :participant_username,
|
|
497
|
+
:participant_name, :participant_avatar_url,
|
|
404
498
|
:external_conversation_id, :last_inbound_at, :last_outbound_at,
|
|
405
|
-
:last_message_at, :metadata, :archived,
|
|
499
|
+
:last_message_at, :metadata, :archived,
|
|
500
|
+
:within_messaging_window, :created_at
|
|
406
501
|
|
|
407
502
|
def initialize(**attrs)
|
|
503
|
+
@external_placement_id = nil
|
|
504
|
+
@group = false
|
|
505
|
+
@within_messaging_window = nil
|
|
408
506
|
@participant_username = nil
|
|
409
507
|
@participant_name = nil
|
|
410
508
|
@participant_avatar_url = nil
|
|
@@ -497,6 +595,85 @@ module PostProxy
|
|
|
497
595
|
end
|
|
498
596
|
end
|
|
499
597
|
|
|
598
|
+
# One card of a horizontally scrolling carousel. Facebook and Instagram only;
|
|
599
|
+
# up to 10 per send. +title+ (80 characters or fewer) plus at least one
|
|
600
|
+
# other field is required; +buttons+ takes up to 3.
|
|
601
|
+
class MessageElement < Model
|
|
602
|
+
attr_accessor :title, :subtitle, :image_url, :default_action, :buttons
|
|
603
|
+
|
|
604
|
+
def initialize(**attrs)
|
|
605
|
+
@subtitle = nil
|
|
606
|
+
@image_url = nil
|
|
607
|
+
@default_action = nil
|
|
608
|
+
@buttons = nil
|
|
609
|
+
super
|
|
610
|
+
if @default_action && !@default_action.is_a?(CardDefaultAction)
|
|
611
|
+
@default_action = CardDefaultAction.new(**@default_action.transform_keys(&:to_sym))
|
|
612
|
+
end
|
|
613
|
+
@buttons = @buttons&.map do |b|
|
|
614
|
+
b.is_a?(MessageButton) ? b : MessageButton.new(**b.transform_keys(&:to_sym))
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
def to_h
|
|
619
|
+
{
|
|
620
|
+
title: @title,
|
|
621
|
+
subtitle: @subtitle,
|
|
622
|
+
image_url: @image_url,
|
|
623
|
+
default_action: @default_action&.to_h,
|
|
624
|
+
buttons: @buttons&.map(&:to_h)
|
|
625
|
+
}.compact
|
|
626
|
+
end
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
class MessageLocation < Model
|
|
630
|
+
attr_accessor :latitude, :longitude, :name, :address
|
|
631
|
+
|
|
632
|
+
def initialize(**attrs)
|
|
633
|
+
@name = nil
|
|
634
|
+
@address = nil
|
|
635
|
+
super
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def to_h
|
|
639
|
+
{ latitude: @latitude, longitude: @longitude, name: @name, address: @address }.compact
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# WhatsApp template send. Address the template by +id+ (PostProxy or Meta
|
|
644
|
+
# id) or by +name+ (+ +language+ when it exists in several). +variables+ are
|
|
645
|
+
# positional: header, then body, then dynamic URL buttons. +button_params+
|
|
646
|
+
# are hashes with +index+ and +sub_type+ ("url", "copy_code", "flow",
|
|
647
|
+
# "quick_reply", "voice_call"); +header_media+ is { link: } or { id: } for
|
|
648
|
+
# an image / video / document header.
|
|
649
|
+
class MessageTemplateInput < Model
|
|
650
|
+
attr_accessor :id, :name, :language, :variables, :button_params,
|
|
651
|
+
:header_media, :header_location
|
|
652
|
+
|
|
653
|
+
def to_h
|
|
654
|
+
{
|
|
655
|
+
id: @id,
|
|
656
|
+
name: @name,
|
|
657
|
+
language: @language,
|
|
658
|
+
variables: @variables,
|
|
659
|
+
button_params: @button_params,
|
|
660
|
+
header_media: @header_media,
|
|
661
|
+
header_location: @header_location.is_a?(MessageLocation) ? @header_location.to_h : @header_location
|
|
662
|
+
}.compact
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# The template an outbound WhatsApp message was sent with.
|
|
667
|
+
class MessageTemplateRef < Model
|
|
668
|
+
attr_accessor :id, :external_id, :name, :language, :variables
|
|
669
|
+
|
|
670
|
+
def initialize(**attrs)
|
|
671
|
+
@external_id = nil
|
|
672
|
+
@variables = nil
|
|
673
|
+
super
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
|
|
500
677
|
# Set on inbound messages created by a tap on an element you sent. Derived
|
|
501
678
|
# from platform_data rather than stored, so it also resolves for taps ingested
|
|
502
679
|
# before PostProxy exposed this field. +kind+ is one of "quick_reply",
|
|
@@ -513,12 +690,13 @@ module PostProxy
|
|
|
513
690
|
|
|
514
691
|
class Message < Model
|
|
515
692
|
attr_accessor :id, :chat_id, :external_id, :direction, :body, :status,
|
|
516
|
-
:tag, :external_comment_id, :error_message, :
|
|
517
|
-
:
|
|
518
|
-
:
|
|
519
|
-
:quick_replies, :buttons, :card, :
|
|
520
|
-
:
|
|
521
|
-
:
|
|
693
|
+
:tag, :external_comment_id, :error_message, :error_details,
|
|
694
|
+
:platform_data, :external_posted_at, :external_delivered_at,
|
|
695
|
+
:external_read_at, :external_edited_at, :reply_to_external_id,
|
|
696
|
+
:reply_markup, :quick_replies, :buttons, :card, :elements,
|
|
697
|
+
:tapped_action, :story_reply, :interactive, :location,
|
|
698
|
+
:contacts, :template, :external_deleted_at, :reactions,
|
|
699
|
+
:attachments, :is_unsupported, :created_at
|
|
522
700
|
|
|
523
701
|
def initialize(**attrs)
|
|
524
702
|
@external_id = nil
|
|
@@ -536,7 +714,15 @@ module PostProxy
|
|
|
536
714
|
@quick_replies = nil
|
|
537
715
|
@buttons = nil
|
|
538
716
|
@card = nil
|
|
717
|
+
@elements = nil
|
|
539
718
|
@tapped_action = nil
|
|
719
|
+
@error_details = nil
|
|
720
|
+
@story_reply = nil
|
|
721
|
+
# WhatsApp-only payloads.
|
|
722
|
+
@interactive = nil
|
|
723
|
+
@location = nil
|
|
724
|
+
@contacts = nil
|
|
725
|
+
@template = nil
|
|
540
726
|
@external_deleted_at = nil
|
|
541
727
|
@reactions = []
|
|
542
728
|
@attachments = []
|
|
@@ -568,6 +754,16 @@ module PostProxy
|
|
|
568
754
|
if @tapped_action && !@tapped_action.is_a?(TappedAction)
|
|
569
755
|
@tapped_action = TappedAction.new(**@tapped_action.transform_keys(&:to_sym))
|
|
570
756
|
end
|
|
757
|
+
@elements = @elements&.map do |e|
|
|
758
|
+
e.is_a?(MessageElement) ? e : MessageElement.new(**e.transform_keys(&:to_sym))
|
|
759
|
+
end
|
|
760
|
+
if @location.is_a?(Hash)
|
|
761
|
+
@location = MessageLocation.new(**@location.transform_keys(&:to_sym))
|
|
762
|
+
end
|
|
763
|
+
if @template.is_a?(Hash)
|
|
764
|
+
@template = MessageTemplateRef.new(**@template.transform_keys(&:to_sym))
|
|
765
|
+
end
|
|
766
|
+
@error_details = ErrorDetails.new(**@error_details.transform_keys(&:to_sym)) if @error_details.is_a?(Hash)
|
|
571
767
|
end
|
|
572
768
|
|
|
573
769
|
private
|
|
@@ -617,7 +813,7 @@ module PostProxy
|
|
|
617
813
|
:status, :author_username, :author_avatar_url,
|
|
618
814
|
:author_external_id, :metadata, :parent_external_id,
|
|
619
815
|
:like_count, :is_hidden, :permalink, :platform_data,
|
|
620
|
-
:attachments, :posted_at, :created_at
|
|
816
|
+
:attachments, :external_edited_at, :posted_at, :created_at
|
|
621
817
|
|
|
622
818
|
def initialize(**attrs)
|
|
623
819
|
@external_id = nil
|
|
@@ -631,8 +827,10 @@ module PostProxy
|
|
|
631
827
|
@permalink = nil
|
|
632
828
|
@platform_data = nil
|
|
633
829
|
@attachments = []
|
|
830
|
+
@external_edited_at = nil
|
|
634
831
|
@posted_at = nil
|
|
635
832
|
super
|
|
833
|
+
@external_edited_at = parse_time(@external_edited_at)
|
|
636
834
|
@posted_at = parse_time(@posted_at)
|
|
637
835
|
@created_at = parse_time(@created_at)
|
|
638
836
|
@attachments = (@attachments || []).map do |a|
|
|
@@ -651,11 +849,12 @@ module PostProxy
|
|
|
651
849
|
# A record of one post pull for a profile — the sync fired when the profile
|
|
652
850
|
# connects, the recurring poll, or a backfill.
|
|
653
851
|
class PostSync < Model
|
|
654
|
-
attr_accessor :id, :profile_id, :kind, :trigger, :status,
|
|
655
|
-
:completed_at, :posts_seen, :posts_imported,
|
|
656
|
-
:oldest_posted_at, :error, :created_at
|
|
852
|
+
attr_accessor :id, :profile_id, :placement_id, :kind, :trigger, :status,
|
|
853
|
+
:started_at, :completed_at, :posts_seen, :posts_imported,
|
|
854
|
+
:backfill_from, :oldest_posted_at, :error, :created_at
|
|
657
855
|
|
|
658
856
|
def initialize(**attrs)
|
|
857
|
+
@placement_id = nil
|
|
659
858
|
@started_at = nil
|
|
660
859
|
@completed_at = nil
|
|
661
860
|
@posts_seen = 0
|
|
@@ -682,10 +881,46 @@ module PostProxy
|
|
|
682
881
|
end
|
|
683
882
|
end
|
|
684
883
|
|
|
884
|
+
# A chat (DM) sync run, started by Profiles#backfill_chats or on connect.
|
|
885
|
+
class ChatSync < Model
|
|
886
|
+
attr_accessor :id, :profile_id, :placement_id, :kind, :trigger, :status,
|
|
887
|
+
:started_at, :completed_at, :chats_seen, :messages_imported,
|
|
888
|
+
:backfill_from, :oldest_updated_at, :error, :created_at
|
|
889
|
+
|
|
890
|
+
def initialize(**attrs)
|
|
891
|
+
@placement_id = nil
|
|
892
|
+
@started_at = nil
|
|
893
|
+
@completed_at = nil
|
|
894
|
+
@chats_seen = 0
|
|
895
|
+
@messages_imported = 0
|
|
896
|
+
@backfill_from = nil
|
|
897
|
+
@oldest_updated_at = nil
|
|
898
|
+
@error = nil
|
|
899
|
+
super
|
|
900
|
+
@started_at = parse_time(@started_at)
|
|
901
|
+
@completed_at = parse_time(@completed_at)
|
|
902
|
+
@backfill_from = parse_time(@backfill_from)
|
|
903
|
+
@oldest_updated_at = parse_time(@oldest_updated_at)
|
|
904
|
+
@created_at = parse_time(@created_at)
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
private
|
|
908
|
+
|
|
909
|
+
def parse_time(value)
|
|
910
|
+
return nil if value.nil?
|
|
911
|
+
value.is_a?(Time) ? value : Time.parse(value.to_s)
|
|
912
|
+
end
|
|
913
|
+
end
|
|
914
|
+
|
|
685
915
|
class DeleteResponse < Model
|
|
686
916
|
attr_accessor :deleted
|
|
687
917
|
end
|
|
688
918
|
|
|
919
|
+
# Body bytes and content type of a binary download (WhatsApp#media).
|
|
920
|
+
class RawResponse < Model
|
|
921
|
+
attr_accessor :data, :content_type
|
|
922
|
+
end
|
|
923
|
+
|
|
689
924
|
class DeletingPlatform < Model
|
|
690
925
|
attr_accessor :post_profile_id, :platform
|
|
691
926
|
end
|
|
@@ -760,10 +995,101 @@ module PostProxy
|
|
|
760
995
|
end
|
|
761
996
|
end
|
|
762
997
|
|
|
998
|
+
# One day of a DailyStats series. Normalized metrics (e.g. `impressions`,
|
|
999
|
+
# `viewers`) vary by platform, so they live in `values` and are also
|
|
1000
|
+
# readable as `record[:impressions]`.
|
|
1001
|
+
class DailyStatsRecord
|
|
1002
|
+
attr_reader :date, :platform_stats, :values
|
|
1003
|
+
|
|
1004
|
+
def initialize(**attrs)
|
|
1005
|
+
attrs = attrs.transform_keys(&:to_sym)
|
|
1006
|
+
@date = attrs.delete(:date)
|
|
1007
|
+
@platform_stats = attrs.delete(:platform_stats)
|
|
1008
|
+
@values = attrs
|
|
1009
|
+
end
|
|
1010
|
+
|
|
1011
|
+
def [](key)
|
|
1012
|
+
@values[key.to_sym]
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
def to_h
|
|
1016
|
+
{ date: @date, platform_stats: @platform_stats }.merge(@values)
|
|
1017
|
+
end
|
|
1018
|
+
end
|
|
1019
|
+
|
|
1020
|
+
class DailyStats < Model
|
|
1021
|
+
# `metrics` describes each metric in `records`.
|
|
1022
|
+
attr_accessor :profile_id, :platform, :placement_id, :attribution, :from,
|
|
1023
|
+
:to, :data_timezone, :records, :metrics
|
|
1024
|
+
|
|
1025
|
+
def initialize(**attrs)
|
|
1026
|
+
@placement_id = nil
|
|
1027
|
+
@data_timezone = nil
|
|
1028
|
+
@records = []
|
|
1029
|
+
@metrics = {}
|
|
1030
|
+
super
|
|
1031
|
+
@records = (@records || []).map do |r|
|
|
1032
|
+
r.is_a?(DailyStatsRecord) ? r : DailyStatsRecord.new(**r)
|
|
1033
|
+
end
|
|
1034
|
+
end
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
class DailyStatsResponse
|
|
1038
|
+
attr_reader :data
|
|
1039
|
+
|
|
1040
|
+
def initialize(data:)
|
|
1041
|
+
@data = data.is_a?(DailyStats) ? data : DailyStats.new(**data.transform_keys(&:to_sym))
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
class FollowerHistoryRecord < Model
|
|
1046
|
+
attr_accessor :date, :followers_gained, :followers_lost, :followers_change, :followers_total
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
class FollowerHistory < Model
|
|
1050
|
+
attr_accessor :profile_id, :platform, :placement_id, :from, :to, :records
|
|
1051
|
+
|
|
1052
|
+
def initialize(**attrs)
|
|
1053
|
+
@placement_id = nil
|
|
1054
|
+
@records = []
|
|
1055
|
+
super
|
|
1056
|
+
@records = (@records || []).map do |r|
|
|
1057
|
+
r.is_a?(FollowerHistoryRecord) ? r : FollowerHistoryRecord.new(**r.transform_keys(&:to_sym))
|
|
1058
|
+
end
|
|
1059
|
+
end
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
class FollowerHistoryResponse
|
|
1063
|
+
attr_reader :data
|
|
1064
|
+
|
|
1065
|
+
def initialize(data:)
|
|
1066
|
+
@data = data.is_a?(FollowerHistory) ? data : FollowerHistory.new(**data.transform_keys(&:to_sym))
|
|
1067
|
+
end
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
# Instagram only. Cached for a short while server-side.
|
|
1071
|
+
class ParticipantStatus < Model
|
|
1072
|
+
attr_accessor :is_verified_user, :is_user_follow_business,
|
|
1073
|
+
:is_business_follow_user, :fetched_at
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
# LinkedIn only: a company page URL (or vanity name) resolved to the URN you
|
|
1077
|
+
# @-mention it with in a post body.
|
|
1078
|
+
class ResolvedMention < Model
|
|
1079
|
+
attr_accessor :urn, :type, :name, :vanity_name
|
|
1080
|
+
end
|
|
1081
|
+
|
|
763
1082
|
# Platform-specific parameter structs
|
|
764
1083
|
|
|
1084
|
+
# `alt_text` on every params class that has it takes a single string or an
|
|
1085
|
+
# array, one per media item in order.
|
|
1086
|
+
#
|
|
1087
|
+
# `link` makes a text-only link post (Facebook renders the preview card).
|
|
1088
|
+
# `made_with_ai` applies to photo posts only. `countries` are ISO 3166-1
|
|
1089
|
+
# alpha-2 codes; viewers elsewhere won't see the post.
|
|
765
1090
|
class FacebookParams < Model
|
|
766
|
-
attr_accessor :format, :title, :first_comment, :page_id
|
|
1091
|
+
attr_accessor :format, :title, :first_comment, :page_id, :link, :alt_text,
|
|
1092
|
+
:made_with_ai, :countries
|
|
767
1093
|
end
|
|
768
1094
|
|
|
769
1095
|
# An Instagram account to tag in a post. Images require `x` and `y`; reels
|
|
@@ -773,9 +1099,20 @@ module PostProxy
|
|
|
773
1099
|
attr_accessor :username, :x, :y, :media_index
|
|
774
1100
|
end
|
|
775
1101
|
|
|
1102
|
+
# `cover_file` is a local path to a cover image, uploaded with the post (the
|
|
1103
|
+
# request switches to multipart); a base64 data URL
|
|
1104
|
+
# (`data:image/jpeg;base64,...`) is sent as-is. The same holds for TikTok,
|
|
1105
|
+
# YouTube and Pinterest.
|
|
1106
|
+
#
|
|
1107
|
+
# `audio_id` comes from Profiles#instagram_available_audio; it and
|
|
1108
|
+
# `audio_volume` / `video_volume` (1-100) need a profile connected via
|
|
1109
|
+
# Facebook login. `strip_audio` removes the video's own track (reel, story).
|
|
1110
|
+
# `trial_strategy` is "MANUAL" or "SS_PERFORMANCE".
|
|
776
1111
|
class InstagramParams < Model
|
|
777
1112
|
attr_accessor :format, :first_comment, :collaborators, :cover_url,
|
|
778
|
-
:
|
|
1113
|
+
:cover_file, :audio_name, :audio_id, :audio_volume,
|
|
1114
|
+
:video_volume, :strip_audio, :trial_strategy, :thumb_offset,
|
|
1115
|
+
:user_tags, :alt_text, :made_with_ai
|
|
779
1116
|
|
|
780
1117
|
def initialize(**attrs)
|
|
781
1118
|
@user_tags = nil
|
|
@@ -799,37 +1136,50 @@ module PostProxy
|
|
|
799
1136
|
end
|
|
800
1137
|
end
|
|
801
1138
|
|
|
1139
|
+
# `upload_as_draft` sends the video to the creator's TikTok inbox instead of
|
|
1140
|
+
# publishing. `cover_url` needs profiles connected after 2026-09-30;
|
|
1141
|
+
# `thumb_offset` picks a frame in milliseconds, and `cover_url` wins if both
|
|
1142
|
+
# are set.
|
|
802
1143
|
class TikTokParams < Model
|
|
803
1144
|
attr_accessor :format, :privacy_status, :photo_cover_index, :auto_add_music,
|
|
804
1145
|
:made_with_ai, :disable_comment, :disable_duet, :disable_stitch,
|
|
805
|
-
:brand_content_toggle, :brand_organic_toggle
|
|
1146
|
+
:brand_content_toggle, :brand_organic_toggle, :upload_as_draft,
|
|
1147
|
+
:cover_url, :cover_file, :thumb_offset
|
|
806
1148
|
end
|
|
807
1149
|
|
|
1150
|
+
# `reshare_urn` reshares a post (e.g. "urn:li:share:123"). `countries` is
|
|
1151
|
+
# for organization pages only (up to 25 codes). For format "poll",
|
|
1152
|
+
# `poll_question` (140 chars max) and `poll_options` (2-4, 30 chars max
|
|
1153
|
+
# each) are required; `poll_duration_days` is 1, 3, 7 (default) or 14.
|
|
808
1154
|
class LinkedInParams < Model
|
|
809
|
-
attr_accessor :format, :organization_id
|
|
1155
|
+
attr_accessor :format, :organization_id, :reshare_urn, :alt_text, :countries,
|
|
1156
|
+
:poll_question, :poll_options, :poll_duration_days
|
|
810
1157
|
end
|
|
811
1158
|
|
|
812
1159
|
class YouTubeParams < Model
|
|
813
|
-
attr_accessor :format, :title, :privacy_status, :cover_url, :
|
|
814
|
-
:tags, :category_id, :contains_synthetic_media
|
|
1160
|
+
attr_accessor :format, :title, :privacy_status, :cover_url, :cover_file,
|
|
1161
|
+
:made_for_kids, :tags, :category_id, :contains_synthetic_media
|
|
815
1162
|
end
|
|
816
1163
|
|
|
817
1164
|
class PinterestParams < Model
|
|
818
|
-
attr_accessor :format, :title, :board_id, :destination_link, :cover_url,
|
|
1165
|
+
attr_accessor :format, :title, :board_id, :destination_link, :cover_url,
|
|
1166
|
+
:cover_file, :thumb_offset, :alt_text
|
|
819
1167
|
end
|
|
820
1168
|
|
|
1169
|
+
# `topic_tag` is one tag, 50 characters or fewer, without the leading # and
|
|
1170
|
+
# with no "." or "&".
|
|
821
1171
|
class ThreadsParams < Model
|
|
822
|
-
attr_accessor :format
|
|
1172
|
+
attr_accessor :format, :alt_text, :topic_tag
|
|
823
1173
|
end
|
|
824
1174
|
|
|
825
1175
|
class TwitterParams < Model
|
|
826
1176
|
# poll_options and poll_duration_minutes are required when format is
|
|
827
1177
|
# "poll": 2-4 options (max 25 chars each), duration 5 to 10080 minutes.
|
|
828
|
-
attr_accessor :format, :poll_options, :poll_duration_minutes
|
|
1178
|
+
attr_accessor :format, :alt_text, :poll_options, :poll_duration_minutes
|
|
829
1179
|
end
|
|
830
1180
|
|
|
831
1181
|
class BlueskyParams < Model
|
|
832
|
-
attr_accessor :format
|
|
1182
|
+
attr_accessor :format, :alt_text
|
|
833
1183
|
end
|
|
834
1184
|
|
|
835
1185
|
class TelegramParams < Model
|
|
@@ -852,4 +1202,33 @@ module PostProxy
|
|
|
852
1202
|
result
|
|
853
1203
|
end
|
|
854
1204
|
end
|
|
1205
|
+
|
|
1206
|
+
# --- WhatsApp ---
|
|
1207
|
+
|
|
1208
|
+
class WhatsAppTemplate < Model
|
|
1209
|
+
attr_accessor :id, :external_id, :profile_id, :name, :language, :category,
|
|
1210
|
+
:status, :parameter_format, :components,
|
|
1211
|
+
:message_send_ttl_seconds, :rejected_reason, :quality_score,
|
|
1212
|
+
:variable_count, :created_at, :updated_at
|
|
1213
|
+
|
|
1214
|
+
def initialize(**attrs)
|
|
1215
|
+
@external_id = nil
|
|
1216
|
+
@parameter_format = nil
|
|
1217
|
+
@components = []
|
|
1218
|
+
@message_send_ttl_seconds = nil
|
|
1219
|
+
@rejected_reason = nil
|
|
1220
|
+
@quality_score = nil
|
|
1221
|
+
@variable_count = 0
|
|
1222
|
+
super
|
|
1223
|
+
@created_at = parse_time(@created_at)
|
|
1224
|
+
@updated_at = parse_time(@updated_at)
|
|
1225
|
+
end
|
|
1226
|
+
|
|
1227
|
+
private
|
|
1228
|
+
|
|
1229
|
+
def parse_time(value)
|
|
1230
|
+
return nil if value.nil?
|
|
1231
|
+
value.is_a?(Time) ? value : Time.parse(value.to_s)
|
|
1232
|
+
end
|
|
1233
|
+
end
|
|
855
1234
|
end
|
data/lib/postproxy/version.rb
CHANGED