blueticks 1.1.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/lib/blueticks/client.rb +47 -5
  4. data/lib/blueticks/resources/account.rb +2 -2
  5. data/lib/blueticks/resources/audiences.rb +30 -22
  6. data/lib/blueticks/resources/boards.rb +124 -0
  7. data/lib/blueticks/resources/campaigns.rb +28 -23
  8. data/lib/blueticks/resources/chats.rb +42 -86
  9. data/lib/blueticks/resources/contacts.rb +18 -11
  10. data/lib/blueticks/resources/engines.rb +12 -19
  11. data/lib/blueticks/resources/groups.rb +69 -28
  12. data/lib/blueticks/resources/identity.rb +27 -0
  13. data/lib/blueticks/resources/latest_chats.rb +22 -0
  14. data/lib/blueticks/resources/messages.rb +127 -31
  15. data/lib/blueticks/resources/newsletters.rb +43 -0
  16. data/lib/blueticks/resources/ping.rb +6 -3
  17. data/lib/blueticks/resources/realtime.rb +19 -0
  18. data/lib/blueticks/resources/scheduled_messages.rb +81 -32
  19. data/lib/blueticks/resources/suno.rb +57 -0
  20. data/lib/blueticks/resources/utils.rb +3 -13
  21. data/lib/blueticks/resources/webhooks.rb +20 -20
  22. data/lib/blueticks/types/account.rb +2 -1
  23. data/lib/blueticks/types/audiences.rb +15 -9
  24. data/lib/blueticks/types/boards.rb +110 -0
  25. data/lib/blueticks/types/campaigns.rb +13 -12
  26. data/lib/blueticks/types/chats.rb +20 -79
  27. data/lib/blueticks/types/contacts.rb +6 -7
  28. data/lib/blueticks/types/coverage.rb +14 -0
  29. data/lib/blueticks/types/engines.rb +9 -8
  30. data/lib/blueticks/types/groups.rb +33 -6
  31. data/lib/blueticks/types/identity.rb +28 -0
  32. data/lib/blueticks/types/latest_chats.rb +32 -0
  33. data/lib/blueticks/types/messages.rb +132 -11
  34. data/lib/blueticks/types/newsletter.rb +33 -0
  35. data/lib/blueticks/types/ok_result.rb +13 -0
  36. data/lib/blueticks/types/paginated.rb +75 -0
  37. data/lib/blueticks/types/ping.rb +11 -3
  38. data/lib/blueticks/types/realtime.rb +23 -0
  39. data/lib/blueticks/types/scheduled_messages.rb +28 -14
  40. data/lib/blueticks/types/suno.rb +42 -0
  41. data/lib/blueticks/types/utils.rb +5 -12
  42. data/lib/blueticks/types/webhooks.rb +14 -11
  43. data/lib/blueticks/version.rb +1 -1
  44. data/lib/blueticks.rb +2 -2
  45. metadata +16 -1
@@ -4,17 +4,18 @@ require_relative "base"
4
4
 
5
5
  module Blueticks
6
6
  module Types
7
- class EngineStatus < Base
7
+ ENGINE_TYPES = %w[gateway regular].freeze
8
+
9
+ # A single engine session connected to the workspace (engines.list item).
10
+ # `type` is "gateway" (a remote server-side engine / Blueticks 24/7 gateway
11
+ # or baileys pod) or "regular" (the user's own WhatsApp Web extension).
12
+ class Engine < Base
13
+ field :id, :string
14
+ field :type, [:enum, ENGINE_TYPES]
8
15
  field :connected, :boolean
9
16
  field :state, :string, nullable: true
10
17
  field :stream, :string, nullable: true
11
- field :has_synced, :boolean, nullable: true
12
- end
13
-
14
- class WhatsAppMe < Base
15
- field :phone, :string, nullable: true
16
- field :name, :string, nullable: true
17
- field :platform, :string, nullable: true
18
+ field :has_synced, :boolean, nullable: true, wire_name: "hasSynced"
18
19
  end
19
20
  end
20
21
  end
@@ -4,24 +4,51 @@ require_relative "base"
4
4
 
5
5
  module Blueticks
6
6
  module Types
7
+ # A member of a group (GroupParticipant).
7
8
  class GroupParticipant < Base
8
- field :chat_id, :string
9
- field :is_admin, :boolean
10
- field :is_super_admin, :boolean
9
+ field :chat_id, :string, wire_name: "chatId"
10
+ field :is_admin, :boolean, wire_name: "isAdmin"
11
+ field :is_super_admin, :boolean, wire_name: "isSuperAdmin"
11
12
  field :name, :string, nullable: true
12
13
  end
13
14
 
15
+ # Full group metadata (Group) returned by group create/get/update and the
16
+ # membership-mutating endpoints.
14
17
  class Group < Base
15
18
  field :id, :string
16
19
  field :name, :string, nullable: true
17
20
  field :description, :string, nullable: true
18
21
  field :owner, :string, nullable: true
19
- field :created_at, :string, nullable: true
20
- field :last_message_at, :string, nullable: true
21
- field :participant_count, :integer, nullable: true
22
+ field :created_at, :string, nullable: true, wire_name: "createdAt"
23
+ field :last_message_at, :string, nullable: true, wire_name: "lastMessageAt"
24
+ field :participant_count, :integer, nullable: true, wire_name: "participantCount"
22
25
  field :announce, :boolean, nullable: true
23
26
  field :restrict, :boolean, nullable: true
24
27
  field :participants, [:array, GroupParticipant], nullable: true
25
28
  end
29
+
30
+ # Current group picture (GroupPicture). `url` is a short-lived WhatsApp CDN
31
+ # URL, or nil when the group has no picture.
32
+ class GroupPicture < Base
33
+ field :url, :string, nullable: true
34
+ end
35
+
36
+ # Result of removing a group picture (GroupPictureRemoved).
37
+ class GroupPictureRemoved < Base
38
+ field :ok, :boolean
39
+ end
40
+
41
+ # A group row in the groups.list / contacts.common_groups page
42
+ # (GroupListItem) — a slimmer projection without announce/participants.
43
+ class GroupListItem < Base
44
+ field :id, :string
45
+ field :name, :string, nullable: true
46
+ field :description, :string, nullable: true
47
+ field :owner, :string, nullable: true
48
+ field :created_at, :string, nullable: true, wire_name: "createdAt"
49
+ field :last_message_at, :string, nullable: true, wire_name: "lastMessageAt"
50
+ field :participant_count, :integer, nullable: true, wire_name: "participantCount"
51
+ field :restrict, :boolean, nullable: true
52
+ end
26
53
  end
27
54
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Blueticks
6
+ module Types
7
+ IDENTITY_TYPES = %w[user group unknown].freeze
8
+ IDENTITY_SOURCES = %w[engine cache unknown].freeze
9
+
10
+ # A resolved WhatsApp identity (Identity). `canonical`, `phone` and `lid`
11
+ # are absent/null when the identity could not be resolved.
12
+ class Identity < Base
13
+ field :input, :string
14
+ field :resolved, :boolean
15
+ field :canonical, :string, optional: true, nullable: true
16
+ field :phone, :string, optional: true, nullable: true
17
+ field :lid, :string, optional: true, nullable: true
18
+ field :aliases, %i[array string]
19
+ field :type, [:enum, IDENTITY_TYPES]
20
+ field :source, [:enum, IDENTITY_SOURCES]
21
+ end
22
+
23
+ # Batch identity-resolution result (IdentityBatchResponse).
24
+ class IdentityBatch < Base
25
+ field :data, [:array, Identity]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "chats"
5
+ require_relative "messages"
6
+
7
+ module Blueticks
8
+ module Types
9
+ # A chat row together with its latest messages (LatestChatWithMessages).
10
+ class LatestChat < Base
11
+ field :chat_id, :string, wire_name: "chatId"
12
+ field :name, :string, optional: true, nullable: true
13
+ field :chat_type, [:enum, CHAT_TYPES], wire_name: "chatType"
14
+ field :pinned, :boolean, optional: true
15
+ field :archived, :boolean, optional: true
16
+ field :last_message_at, :string, optional: true, nullable: true, wire_name: "lastMessageAt"
17
+ field :unread_count, :integer, optional: true, nullable: true, wire_name: "unreadCount"
18
+ field :marked_unread, :boolean, wire_name: "markedUnread"
19
+ field :last_message_text, :string, nullable: true, optional: true, wire_name: "lastMessageText"
20
+ field :last_message_from_me, :boolean, nullable: true, optional: true, wire_name: "lastMessageFromMe"
21
+ field :messages, [:array, PublicMessage]
22
+ end
23
+
24
+ # Latest chats and their messages (LatestChatsResponse) from GET /v1/latest-chats.
25
+ class LatestChats < Base
26
+ field :chats, [:array, LatestChat]
27
+ field :total_chats, :integer, wire_name: "totalChats"
28
+ field :number_of_chats, :integer, wire_name: "numberOfChats"
29
+ field :number_of_messages, :integer, wire_name: "numberOfMessages"
30
+ end
31
+ end
32
+ end
@@ -1,25 +1,146 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "base"
4
+ require_relative "coverage"
4
5
 
5
6
  module Blueticks
6
7
  module Types
7
- MESSAGE_STATUSES = %w[scheduled queued sending delivered read failed].freeze
8
+ MESSAGE_KINDS = %w[text media poll].freeze
9
+ MESSAGE_MEDIA_KINDS = %w[image video audio document sticker voice gif].freeze
10
+ MEDIA_UNAVAILABLE_REASONS = %w[expired fetching awaiting_sender error no_media].freeze
11
+ MESSAGE_SOURCES = %w[vault backup].freeze
12
+ MESSAGE_STATUSES = %w[
13
+ pending before-wa-send bt-sent sending sent sent_pending_ack confirmed
14
+ delivered received read played cancelled error failed expired
15
+ ].freeze
8
16
 
17
+ # WhatsApp wire message key (PublicWaMessageKey). `serialized` maps the
18
+ # wire's `_serialized` compact form.
19
+ class WaMessageKey < Base
20
+ field :from_me, :boolean, wire_name: "fromMe"
21
+ field :remote, :string, optional: true
22
+ field :id, :string, optional: true
23
+ field :serialized, :string, optional: true, wire_name: "_serialized"
24
+ field :participant, :string, optional: true
25
+ end
26
+
27
+ # Link preview attached to a queued/scheduled message (MessageResponse).
28
+ # Fields are always present but may be null.
29
+ class LinkPreview < Base
30
+ field :title, :string, nullable: true
31
+ field :description, :string, nullable: true
32
+ field :canonical_url, :string, nullable: true, wire_name: "canonicalUrl"
33
+ field :thumbnail, :string, nullable: true
34
+ end
35
+
36
+ # Link preview attached to a store message (PublicMessage). Fields are all
37
+ # optional here.
38
+ class PublicLinkPreview < Base
39
+ field :title, :string, optional: true
40
+ field :description, :string, optional: true
41
+ field :canonical_url, :string, optional: true, wire_name: "canonicalUrl"
42
+ field :thumbnail, :string, optional: true
43
+ end
44
+
45
+ # The message a store message replies to (PublicQuotedMessage).
46
+ class QuotedMessage < Base
47
+ field :wa_message_key, WaMessageKey, wire_name: "waMessageKey"
48
+ field :text, :string, optional: true
49
+ field :type, :string, optional: true
50
+ field :author, :string, optional: true
51
+ end
52
+
53
+ # A message read from the WhatsApp store (PublicMessage) — the shape
54
+ # returned by messages.list and messages.retrieve.
55
+ class PublicMessage < Base
56
+ field :wa_message_key, WaMessageKey, wire_name: "waMessageKey"
57
+ field :chat_id, :string, wire_name: "chatId"
58
+ field :from, :string
59
+ field :author, :string, optional: true
60
+ field :sender_name, :string, optional: true, wire_name: "senderName"
61
+ field :timestamp, :string, optional: true
62
+ field :text, :string, optional: true
63
+ field :type, :string
64
+ field :from_me, :boolean, wire_name: "fromMe"
65
+ field :ack, :integer, optional: true
66
+ field :media_url, :string, optional: true, wire_name: "mediaUrl"
67
+ field :filename, :string, optional: true
68
+ field :link_preview, PublicLinkPreview, optional: true, wire_name: "linkPreview"
69
+ field :quoted_message, QuotedMessage, optional: true, wire_name: "quotedMessage"
70
+ field :source, [:enum, MESSAGE_SOURCES], optional: true
71
+ field :secret, :string, optional: true
72
+ end
73
+
74
+ # Media payload for a message (Media) returned by messages.get_media.
75
+ class Media < Base
76
+ field :url, :string, optional: true
77
+ field :mimetype, :string, optional: true
78
+ field :filename, :string, optional: true
79
+ field :data_base64, :string, optional: true, wire_name: "dataBase64"
80
+ field :original_quality, :boolean, optional: true, wire_name: "originalQuality"
81
+ field :media_unavailable, [:enum, MEDIA_UNAVAILABLE_REASONS], optional: true, wire_name: "mediaUnavailable"
82
+ end
83
+
84
+ # Delivery acknowledgement (MessageAck). ack: -1=error, 0=pending,
85
+ # 1=server, 2=device, 3=read, 4=played.
86
+ class MessageAck < Base
87
+ field :ack, :integer, nullable: true
88
+ end
89
+
90
+ # A single entry in the messages.batch_acks page (BatchMessageAckEntry).
91
+ class BatchAckEntry < Base
92
+ field :key, :string
93
+ field :ack, :integer, nullable: true
94
+ field :found, :boolean
95
+ end
96
+
97
+ # A pinned message summary (PinnedMessage) in the messages.pinned page.
98
+ class PinnedMessage < Base
99
+ field :key, :string
100
+ field :chat_id, :string, wire_name: "chatId"
101
+ field :text, :string, nullable: true
102
+ end
103
+
104
+ # Result of a load-older request (LoadOlderResult). `pages_executed` is how
105
+ # many older pages this call pulled; `coverage` says how far back the chat is
106
+ # now loaded.
107
+ class LoadOlderResult < Base
108
+ field :total_messages, :integer, nullable: true, wire_name: "totalMessages"
109
+ field :added, :integer, nullable: true
110
+ field :can_load_more, :boolean, wire_name: "canLoadMore"
111
+ field :history_unavailable, :boolean, wire_name: "historyUnavailable"
112
+ field :error, :string, nullable: true
113
+ field :pages_executed, :integer, wire_name: "pagesExecuted"
114
+ field :coverage, Coverage
115
+ end
116
+
117
+ # A sent/queued outbound message record — the shape returned by
118
+ # messages.create (direct send). The spec's send response marks only
119
+ # `to`, `type`, `status`, `createdAt` required; `waMessageKey` is optional
120
+ # (absent until the message has a wire key). The scheduled/queued variant is
121
+ # modelled separately by Types::ScheduledMessage.
9
122
  class Message < Base
10
- field :id, :string
123
+ field :id, :string, optional: true
124
+ field :wa_message_key, WaMessageKey, optional: true, nullable: true, wire_name: "waMessageKey"
11
125
  field :to, :string
12
- field :from, :string, nullable: true
126
+ field :type, [:enum, MESSAGE_KINDS]
13
127
  field :text, :string, nullable: true
14
- field :media_url, :string, nullable: true
128
+ field :media_url, :string, nullable: true, wire_name: "mediaUrl"
129
+ field :media_kind, [:enum, MESSAGE_MEDIA_KINDS], nullable: true, wire_name: "mediaKind"
130
+ field :poll_question, :string, nullable: true, wire_name: "pollQuestion"
131
+ field :poll_options, %i[array string], nullable: true, wire_name: "pollOptions"
132
+ field :poll_allow_multiple, :boolean, nullable: true, wire_name: "pollAllowMultiple"
15
133
  field :status, [:enum, MESSAGE_STATUSES]
16
- field :send_at, :string, nullable: true
17
- field :created_at, :string
18
- field :sent_at, :string, nullable: true
19
- field :delivered_at, :string, nullable: true
20
- field :read_at, :string, nullable: true
21
- field :failed_at, :string, nullable: true
22
- field :failure_reason, :string, nullable: true
134
+ field :send_at, :string, nullable: true, wire_name: "sendAt"
135
+ field :created_at, :string, wire_name: "createdAt"
136
+ field :confirmed_at, :string, nullable: true, wire_name: "confirmedAt"
137
+ field :received_at, :string, nullable: true, wire_name: "receivedAt"
138
+ field :read_at, :string, nullable: true, wire_name: "readAt"
139
+ field :played_at, :string, nullable: true, wire_name: "playedAt"
140
+ field :failed_at, :string, nullable: true, wire_name: "failedAt"
141
+ field :failure_reason, :string, nullable: true, wire_name: "failureReason"
142
+ field :secret, :string, nullable: true
143
+ field :link_preview, LinkPreview, optional: true, wire_name: "linkPreview"
23
144
  end
24
145
  end
25
146
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Blueticks
6
+ module Types
7
+ NEWSLETTER_VERIFICATIONS = %w[VERIFIED UNVERIFIED].freeze
8
+
9
+ # A WhatsApp newsletter (channel) as returned by create/get
10
+ # (NewsletterResponse). The JID is carried on `newsletter_id`.
11
+ class Newsletter < Base
12
+ field :newsletter_id, :string, wire_name: "newsletterId"
13
+ field :name, :string
14
+ field :description, :string, nullable: true
15
+ field :created_at, :string, nullable: true, wire_name: "createdAt"
16
+ field :subscribers, :integer, nullable: true
17
+ field :invite, :string, nullable: true
18
+ field :verification, [:enum, NEWSLETTER_VERIFICATIONS], nullable: true
19
+ end
20
+
21
+ # A newsletter row in the newsletters.list page (NewsletterListItem). Here
22
+ # the JID is carried on `chat_id`.
23
+ class NewsletterListItem < Base
24
+ field :chat_id, :string, wire_name: "chatId"
25
+ field :name, :string
26
+ field :description, :string, nullable: true
27
+ field :created_at, :string, nullable: true, wire_name: "createdAt"
28
+ field :subscribers, :integer, nullable: true
29
+ field :invite, :string, nullable: true
30
+ field :verification, [:enum, NEWSLETTER_VERIFICATIONS], nullable: true
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Blueticks
6
+ module Types
7
+ # Generic `{ "ok": true }` acknowledgement returned by side-effecting
8
+ # endpoints (chat archive/mark-read, message pin/unpin/react).
9
+ class OkResult < Base
10
+ field :ok, [:enum, [true]]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "coverage"
5
+ require_relative "../errors"
6
+
7
+ module Blueticks
8
+ module Types
9
+ # Offset-paginated list envelope. Every v1 list endpoint returns this shape
10
+ # inside the success/data wrapper: `{ data: [...], limit, skip, total }`.
11
+ #
12
+ # Iterate `data` for the current page; advance by passing `skip: skip + limit`
13
+ # (or any offset) back on the next list call. `total` is the full match count
14
+ # across all pages. `has_more` is populated only by endpoints that echo a
15
+ # `hasMore` flag (e.g. messages.list); nil elsewhere. `coverage` (Types::Coverage) is
16
+ # likewise populated only by messages.list.
17
+ class PaginatedPage
18
+ attr_reader :data, :limit, :skip, :total, :has_more, :coverage
19
+
20
+ def initialize(data:, limit:, skip:, total:, has_more: nil, coverage: nil)
21
+ @data = data
22
+ @limit = limit
23
+ @skip = skip
24
+ @total = total
25
+ @has_more = has_more
26
+ @coverage = coverage
27
+ end
28
+
29
+ # Build a PaginatedPage from a raw list envelope, decoding each item via
30
+ # item_type.from_hash. Reads `data`/`limit`/`skip`/`total` (and optional
31
+ # `hasMore` / `coverage`) off the envelope; ignores the `success` flag.
32
+ def self.from_hash(raw, item_type:)
33
+ unless raw.is_a?(Hash)
34
+ raise Errors::ValidationError.new(message: "Expected Hash for PaginatedPage, got #{raw.class}")
35
+ end
36
+ unless raw["data"].is_a?(Array)
37
+ raise Errors::ValidationError.new(message: "Field 'data' must be array in PaginatedPage")
38
+ end
39
+
40
+ limit = require_integer(raw, "limit")
41
+ skip = require_integer(raw, "skip")
42
+ total = require_integer(raw, "total")
43
+
44
+ has_more = raw["hasMore"]
45
+ unless has_more.nil? || [true, false].include?(has_more)
46
+ raise Errors::ValidationError.new(message: "Field 'hasMore' must be boolean or absent in PaginatedPage")
47
+ end
48
+
49
+ coverage = raw["coverage"].nil? ? nil : Coverage.from_hash(raw["coverage"], context: "PaginatedPage.coverage")
50
+
51
+ decoded = raw["data"].each_with_index.map do |item, i|
52
+ item_type.from_hash(item, context: "PaginatedPage.data[#{i}]")
53
+ end
54
+
55
+ new(data: decoded, limit: limit, skip: skip, total: total, has_more: has_more, coverage: coverage)
56
+ end
57
+
58
+ def self.require_integer(raw, key)
59
+ value = raw[key]
60
+ unless value.is_a?(Integer) && !value.is_a?(TrueClass) && !value.is_a?(FalseClass)
61
+ raise Errors::ValidationError.new(message: "Field '#{key}' must be integer in PaginatedPage")
62
+ end
63
+
64
+ value
65
+ end
66
+
67
+ def to_h
68
+ {
69
+ data: data.map { |d| d.respond_to?(:to_h) ? d.to_h : d },
70
+ limit: limit, skip: skip, total: total, has_more: has_more, coverage: coverage&.to_h
71
+ }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -4,11 +4,19 @@ require_relative "base"
4
4
 
5
5
  module Blueticks
6
6
  module Types
7
+ # A connected WhatsApp engine as summarized on the ping response.
8
+ class PingConnection < Base
9
+ field :id, :string
10
+ field :type, [:enum, %w[gateway regular]]
11
+ field :connected, [:enum, [true]]
12
+ end
13
+
7
14
  # Response from GET /v1/ping.
8
15
  class Ping < Base
9
- field :account_id, :string
10
- field :key_prefix, :string
11
- field :scopes, %i[array string]
16
+ field :api, [:enum, %w[ok]]
17
+ field :account_id, :string, wire_name: "accountId"
18
+ field :whatsapp_connections, [:array, PingConnection], wire_name: "whatsappConnections"
19
+ field :message, :string, optional: true
12
20
  end
13
21
  end
14
22
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Blueticks
6
+ module Types
7
+ # MQTT topics the realtime credentials are authorised for.
8
+ class RealtimeTopics < Base
9
+ field :messages, :string
10
+ end
11
+
12
+ # Short-lived MQTT credentials for receiving messages in real time
13
+ # (RealtimeTokenResponse).
14
+ class RealtimeToken < Base
15
+ field :broker_url, :string, wire_name: "brokerUrl"
16
+ field :username, :string
17
+ field :password, :string
18
+ field :expires_at, :string, wire_name: "expiresAt"
19
+ field :expires_in_seconds, :integer, wire_name: "expiresInSeconds"
20
+ field :topics, RealtimeTopics
21
+ end
22
+ end
23
+ end
@@ -1,23 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
3
+ require_relative "messages"
4
4
 
5
5
  module Blueticks
6
6
  module Types
7
+ # A queued / scheduled outbound message record — the shape returned by
8
+ # every scheduled_messages.* endpoint. This is a DISTINCT inline schema in
9
+ # the spec (not MessageResponse): only `to`, `type`, `status`, `createdAt`
10
+ # are required. In particular `waMessageKey` is optional and ABSENT on a
11
+ # record that has not dispatched yet, so it must never be decoded through a
12
+ # type that requires it (see Types::Message, used only by messages.create).
7
13
  class ScheduledMessage < Base
8
- field :id, :string
9
- field :to, :string, nullable: true
10
- field :text, :string, nullable: true
11
- field :media_url, :string, nullable: true
12
- field :media_caption, :string, nullable: true
13
- field :media_filename, :string, nullable: true
14
- field :media_mime_type, :string, nullable: true
15
- field :send_at, :string, nullable: true
16
- field :status, :string, nullable: true
17
- field :is_recurring, :boolean
18
- field :recurrence_rule, :string, nullable: true
19
- field :created_at, :string, nullable: true
20
- field :updated_at, :string, nullable: true
14
+ field :id, :string, optional: true
15
+ field :wa_message_key, WaMessageKey, optional: true, nullable: true, wire_name: "waMessageKey"
16
+ field :to, :string
17
+ field :type, [:enum, MESSAGE_KINDS]
18
+ field :text, :string, optional: true, nullable: true
19
+ field :media_url, :string, optional: true, nullable: true, wire_name: "mediaUrl"
20
+ field :media_kind, [:enum, MESSAGE_MEDIA_KINDS], optional: true, nullable: true, wire_name: "mediaKind"
21
+ field :poll_question, :string, optional: true, nullable: true, wire_name: "pollQuestion"
22
+ field :poll_options, %i[array string], optional: true, nullable: true, wire_name: "pollOptions"
23
+ field :poll_allow_multiple, :boolean, optional: true, nullable: true, wire_name: "pollAllowMultiple"
24
+ field :status, [:enum, MESSAGE_STATUSES]
25
+ field :send_at, :string, optional: true, nullable: true, wire_name: "sendAt"
26
+ field :created_at, :string, wire_name: "createdAt"
27
+ field :confirmed_at, :string, optional: true, nullable: true, wire_name: "confirmedAt"
28
+ field :received_at, :string, optional: true, nullable: true, wire_name: "receivedAt"
29
+ field :read_at, :string, optional: true, nullable: true, wire_name: "readAt"
30
+ field :played_at, :string, optional: true, nullable: true, wire_name: "playedAt"
31
+ field :failed_at, :string, optional: true, nullable: true, wire_name: "failedAt"
32
+ field :failure_reason, :string, optional: true, nullable: true, wire_name: "failureReason"
33
+ field :secret, :string, optional: true, nullable: true
34
+ field :link_preview, LinkPreview, optional: true, wire_name: "linkPreview"
21
35
  end
22
36
  end
23
37
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Blueticks
6
+ module Types
7
+ SONG_CLIP_STATUSES = %w[submitted queued running streaming complete error].freeze
8
+
9
+ # Suno credit / plan usage (AccountUsageResponse) from GET /v1/suno/account.
10
+ class SunoAccount < Base
11
+ field :credits_left, :number, wire_name: "creditsLeft"
12
+ field :monthly_limit, :number, nullable: true, wire_name: "monthlyLimit"
13
+ field :monthly_usage, :number, nullable: true, wire_name: "monthlyUsage"
14
+ field :plan, :string, nullable: true
15
+ end
16
+
17
+ # A generated song clip (SongClip / GetSongResponse).
18
+ class SongClip < Base
19
+ field :id, :string
20
+ field :status, [:enum, SONG_CLIP_STATUSES]
21
+ field :audio_url, :string, nullable: true, wire_name: "audioUrl"
22
+ field :image_url, :string, nullable: true, wire_name: "imageUrl"
23
+ field :title, :string, nullable: true
24
+ field :duration_sec, :number, nullable: true, wire_name: "durationSec"
25
+ field :model, :string, nullable: true
26
+ field :error_type, :string, nullable: true, optional: true, wire_name: "errorType"
27
+ field :error_message, :string, nullable: true, optional: true, wire_name: "errorMessage"
28
+ end
29
+
30
+ # Result of submitting a song generation job (GenerateSongResponse).
31
+ class GenerateSongResult < Base
32
+ field :job_id, :string, wire_name: "jobId"
33
+ field :clips, [:array, SongClip]
34
+ end
35
+
36
+ # Result of registering a reference-audio upload (CreateUploadResponse).
37
+ class CreateUploadResult < Base
38
+ field :upload_id, :string, wire_name: "uploadId"
39
+ field :status, :string
40
+ end
41
+ end
42
+ end
@@ -1,19 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
-
3
+ # Placeholder — UtilsResource exposes no /v1/utils/* methods in the
4
+ # current spec. This file exists because lib/blueticks.rb (MUST-NOT-TOUCH)
5
+ # requires it. Once the spec adds util operations, regenerate this file
6
+ # with the matching response types.
5
7
  module Blueticks
6
8
  module Types
7
- class PhoneValidation < Base
8
- field :valid, :boolean
9
- field :formatted_chat_id, :string, nullable: true
10
- end
11
-
12
- class LinkPreview < Base
13
- field :title, :string, nullable: true
14
- field :description, :string, nullable: true
15
- field :thumbnail, :string, nullable: true
16
- field :canonical_url, :string, nullable: true
9
+ module Utils
17
10
  end
18
11
  end
19
12
  end
@@ -10,27 +10,30 @@ module Blueticks
10
10
  message.queued message.sending message.delivered message.failed message.read
11
11
  session.connected session.disconnected
12
12
  campaign.started campaign.paused campaign.resumed campaign.completed campaign.aborted
13
+ new_message_received_webhook message_reaction_webhook ack_changed_webhook
14
+ participant_joined_via_link_webhook participant_added_by_admin_webhook
15
+ participant_left_group_webhook participant_kicked_from_group_webhook
16
+ group_admin_changed_webhook group_name_changed_webhook group_description_changed_webhook
17
+ group_message_pinned_webhook poll_vote_webhook reply_to_my_message_webhook
18
+ message_deleted_revoked_webhook message_edited_webhook
19
+ new_message_received_from_me_webhook
13
20
  ].freeze
14
21
 
22
+ # A webhook subscription (WebhookResponse). `signing_secret` is present only
23
+ # when the API includes it in the response body.
15
24
  class Webhook < Base
16
25
  field :id, :string
17
26
  field :url, :string
18
27
  field :events, %i[array string]
19
28
  field :description, :string, nullable: true
20
29
  field :status, [:enum, WEBHOOK_STATUSES]
21
- field :created_at, :string
22
- end
23
-
24
- class WebhookCreateResult < Base
25
- field :id, :string
26
- field :url, :string
27
- field :events, %i[array string]
28
- field :description, :string, nullable: true
29
- field :status, [:enum, WEBHOOK_STATUSES]
30
- field :created_at, :string
31
- field :secret, :string
30
+ field :created_at, :string, wire_name: "createdAt"
31
+ field :signing_secret, :string, optional: true, wire_name: "signingSecret"
32
32
  end
33
33
 
34
+ # A delivered webhook event, as parsed by Blueticks::Webhooks.verify. Note
35
+ # the webhook DELIVERY payload is snake_case (distinct from the REST API's
36
+ # camelCase), so `created_at` here has no wire mapping.
34
37
  class WebhookEvent < Base
35
38
  field :id, :string
36
39
  field :type, :string
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Blueticks
4
- VERSION = "1.1.2"
4
+ VERSION = "2.1.1"
5
5
  end