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.
@@ -19,11 +19,22 @@ module PostProxy
19
19
  end
20
20
 
21
21
  class Profile < Model
22
- attr_accessor :id, :name, :status, :platform, :profile_group_id, :expires_at, :post_count
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, :status, :params, :error, :error_details, :attempted_at, :insights
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
- attr_accessor :id, :body, :media
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, :posted_at, :created_at, :replies
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
- attr_accessor :id, :profile_id, :platform, :participant_external_id,
403
- :participant_username, :participant_name, :participant_avatar_url,
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, :created_at
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, :platform_data,
517
- :external_posted_at, :external_delivered_at, :external_read_at,
518
- :external_edited_at, :reply_to_external_id, :reply_markup,
519
- :quick_replies, :buttons, :card, :tapped_action,
520
- :external_deleted_at, :reactions, :attachments,
521
- :is_unsupported, :created_at
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, :started_at,
655
- :completed_at, :posts_seen, :posts_imported, :backfill_from,
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
- :audio_name, :trial_strategy, :thumb_offset, :user_tags
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, :made_for_kids,
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, :thumb_offset
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
@@ -1,3 +1,3 @@
1
1
  module PostProxy
2
- VERSION = "1.13.0"
2
+ VERSION = "1.14.0"
3
3
  end