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
@@ -2,25 +2,32 @@
2
2
 
3
3
  require_relative "../base_resource"
4
4
  require_relative "../types/contacts"
5
- require_relative "../types/page"
5
+ require_relative "../types/groups"
6
+ require_relative "../types/paginated"
6
7
 
7
8
  module Blueticks
8
9
  module Resources
9
10
  class ContactsResource < BaseResource
10
- # Search the user's WhatsApp contacts. Cursor-paginated.
11
- def list(query: nil, limit: nil, cursor: nil)
11
+ # List contacts.
12
+ #
13
+ # List WhatsApp contacts known to the connected engine. Offset-paginated.
14
+ def list(search_token: nil, include_archive: nil, skip: nil, limit: nil)
12
15
  params = {}
13
- params["query"] = query unless query.nil?
16
+ params["searchToken"] = search_token unless search_token.nil?
17
+ params["includeArchive"] = include_archive unless include_archive.nil?
18
+ params["skip"] = skip unless skip.nil?
14
19
  params["limit"] = limit unless limit.nil?
15
- params["cursor"] = cursor unless cursor.nil?
16
- data = client.request("GET", "/v1/contacts", params: params.empty? ? nil : params)
17
- Types::Page.from_hash(data, item_type: Types::WhatsAppContact)
20
+ env = client.request("GET", "/v1/contacts", params: params.empty? ? nil : params)
21
+ Types::PaginatedPage.from_hash(env, item_type: Types::WhatsAppContact)
18
22
  end
19
23
 
20
- # Retrieve the CDN URL for a contact's profile picture.
21
- def get_profile_picture(chat_id)
22
- data = client.request("GET", "/v1/contacts/#{chat_id}/profile_picture")
23
- Types::ProfilePicture.from_hash(data)
24
+ # List groups in common.
25
+ #
26
+ # List the groups the authenticated user shares with the given contact.
27
+ # Offset-paginated (GroupListItem rows).
28
+ def common_groups(contact_id)
29
+ env = client.request("GET", "/v1/contacts/#{contact_id}/common_groups")
30
+ Types::PaginatedPage.from_hash(env, item_type: Types::GroupListItem)
24
31
  end
25
32
  end
26
33
  end
@@ -1,31 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../base_resource"
4
+ require_relative "../errors"
4
5
  require_relative "../types/engines"
5
6
 
6
7
  module Blueticks
7
8
  module Resources
8
9
  class EnginesResource < BaseResource
9
- # Current connection status of the user's WhatsApp engine.
10
- def status
11
- data = client.request("GET", "/v1/engines")
12
- Types::EngineStatus.from_hash(data)
13
- end
14
-
15
- # Authenticated WhatsApp account profile (phone, pushname, platform).
16
- def me
17
- data = client.request("GET", "/v1/engines/me")
18
- Types::WhatsAppMe.from_hash(data)
19
- end
20
-
21
- # Force the engine to log out of WhatsApp Web.
22
- def logout
23
- client.request("POST", "/v1/engines/logout")
24
- end
10
+ # List engines.
11
+ #
12
+ # List the WhatsApp engines connected to the workspace. Returns an Array of
13
+ # Engine (empty when no engine is paired) — this endpoint is not paginated.
14
+ def list
15
+ env = client.request("GET", "/v1/engines")
16
+ data = env.is_a?(Hash) ? env["data"] : nil
17
+ unless data.is_a?(Array)
18
+ raise Errors::ValidationError.new(message: "Expected array `data` in engines.list response")
19
+ end
25
20
 
26
- # Force the engine's WhatsApp Web tab to reload.
27
- def reload
28
- client.request("POST", "/v1/engines/reload")
21
+ data.map { |item| Types::Engine.from_hash(item) }
29
22
  end
30
23
  end
31
24
  end
@@ -2,64 +2,105 @@
2
2
 
3
3
  require_relative "../base_resource"
4
4
  require_relative "../types/groups"
5
+ require_relative "../types/paginated"
5
6
 
6
7
  module Blueticks
7
8
  module Resources
8
9
  class GroupsResource < BaseResource
10
+ # List groups.
11
+ #
12
+ # List the groups the connected WhatsApp engine sees. Offset-paginated
13
+ # (GroupListItem rows), with an optional case-insensitive name search via
14
+ # `search_token`.
15
+ def list(search_token: nil, include_archive: nil, skip: nil, limit: nil)
16
+ params = {}
17
+ params["searchToken"] = search_token unless search_token.nil?
18
+ params["includeArchive"] = include_archive unless include_archive.nil?
19
+ params["skip"] = skip unless skip.nil?
20
+ params["limit"] = limit unless limit.nil?
21
+ env = client.request("GET", "/v1/groups", params: params.empty? ? nil : params)
22
+ Types::PaginatedPage.from_hash(env, item_type: Types::GroupListItem)
23
+ end
24
+
9
25
  # Create a new group with an initial participant list.
10
26
  def create(name:, participants:)
11
- data = client.request("POST", "/v1/groups",
12
- body: { "name" => name, "participants" => participants })
13
- Types::Group.from_hash(data)
27
+ env = client.request("POST", "/v1/groups",
28
+ body: { "name" => name, "participants" => participants })
29
+ Types::Group.from_hash(env && env["data"])
14
30
  end
15
31
 
16
- # Retrieve group metadata by JID.
17
- def get(group_id)
18
- data = client.request("GET", "/v1/groups/#{group_id}")
19
- Types::Group.from_hash(data)
32
+ # Retrieve group metadata by id. Pass `include: "participants"` to embed
33
+ # the participant list.
34
+ def retrieve(group_id, include: nil)
35
+ params = include.nil? ? nil : { "include" => include }
36
+ env = client.request("GET", "/v1/groups/#{group_id}", params: params)
37
+ Types::Group.from_hash(env && env["data"])
20
38
  end
21
39
 
22
- # Rename the group and/or update admin-only settings.
40
+ # Rename the group and/or update admin-only settings. `settings` is a hash
41
+ # using the API's wire keys (announce, restrict, editInfoAdminsOnly,
42
+ # description).
23
43
  def update(group_id, name: nil, settings: nil)
24
44
  body = {}
25
45
  body["name"] = name unless name.nil?
26
46
  body["settings"] = settings unless settings.nil?
27
- data = client.request("PATCH", "/v1/groups/#{group_id}", body: body)
28
- Types::Group.from_hash(data)
47
+ env = client.request("PATCH", "/v1/groups/#{group_id}", body: body)
48
+ Types::Group.from_hash(env && env["data"])
29
49
  end
30
50
 
31
- # Add a participant to the group by chat_id (JID) or +E.164 phone.
32
- def add_member(group_id, chat_id:)
33
- data = client.request("POST", "/v1/groups/#{group_id}/members",
34
- body: { "chat_id" => chat_id })
35
- Types::Group.from_hash(data)
51
+ # Add members to the group by chat id (JID / +E.164) and/or a list of
52
+ # participants.
53
+ def add_members(group_id, chat_id: nil, participants: nil)
54
+ body = {}
55
+ body["chatId"] = chat_id unless chat_id.nil?
56
+ body["participants"] = participants unless participants.nil?
57
+ env = client.request("POST", "/v1/groups/#{group_id}/members", body: body)
58
+ Types::Group.from_hash(env && env["data"])
36
59
  end
37
60
 
38
61
  # Remove a participant from the group.
39
62
  def remove_member(group_id, chat_id)
40
- data = client.request("DELETE", "/v1/groups/#{group_id}/members/#{chat_id}")
41
- Types::Group.from_hash(data)
63
+ env = client.request("DELETE", "/v1/groups/#{group_id}/members/#{chat_id}")
64
+ Types::Group.from_hash(env && env["data"])
42
65
  end
43
66
 
44
67
  # Grant admin privileges to a group member.
45
68
  def promote_admin(group_id, chat_id)
46
- data = client.request("POST", "/v1/groups/#{group_id}/members/#{chat_id}/admin")
47
- Types::Group.from_hash(data)
69
+ env = client.request("POST", "/v1/groups/#{group_id}/members/#{chat_id}/admin")
70
+ Types::Group.from_hash(env && env["data"])
48
71
  end
49
72
 
50
73
  # Revoke admin privileges from a group member.
51
74
  def demote_admin(group_id, chat_id)
52
- data = client.request("DELETE", "/v1/groups/#{group_id}/members/#{chat_id}/admin")
53
- Types::Group.from_hash(data)
75
+ env = client.request("DELETE", "/v1/groups/#{group_id}/members/#{chat_id}/admin")
76
+ Types::Group.from_hash(env && env["data"])
77
+ end
78
+
79
+ # Replace the group picture. `file_data_url` is a base64 data URL, or pass
80
+ # a hosted `url`.
81
+ def set_picture(group_id, file_data_url: nil, url: nil, file_name: nil, file_mime_type: nil)
82
+ body = {}
83
+ body["fileDataUrl"] = file_data_url unless file_data_url.nil?
84
+ body["url"] = url unless url.nil?
85
+ body["fileName"] = file_name unless file_name.nil?
86
+ body["fileMimeType"] = file_mime_type unless file_mime_type.nil?
87
+ env = client.request("PUT", "/v1/groups/#{group_id}/picture", body: body)
88
+ Types::Group.from_hash(env && env["data"])
89
+ end
90
+
91
+ # Get the current group picture as a short-lived WhatsApp CDN `url`, or
92
+ # `url: nil` when the group has none. Requires `groups:read`.
93
+ def get_picture(group_id)
94
+ env = client.request("GET", "/v1/groups/#{group_id}/picture")
95
+ Types::GroupPicture.from_hash(env && env["data"])
54
96
  end
55
97
 
56
- # Replace the group picture. `file_data_url` is a base64 data URL (PNG/JPEG, <=20 MiB).
57
- def set_picture(group_id, file_data_url:, file_name: nil, file_mime_type: nil)
58
- body = { "file_data_url" => file_data_url }
59
- body["file_name"] = file_name unless file_name.nil?
60
- body["file_mime_type"] = file_mime_type unless file_mime_type.nil?
61
- data = client.request("PUT", "/v1/groups/#{group_id}/picture", body: body)
62
- Types::Group.from_hash(data)
98
+ # Remove the group picture. Gateway (Baileys) engine only: a
99
+ # Chrome-extension-only account gets 501 `unsupported_command`. Requires
100
+ # `groups:write`.
101
+ def remove_picture(group_id)
102
+ env = client.request("DELETE", "/v1/groups/#{group_id}/picture")
103
+ Types::GroupPictureRemoved.from_hash(env && env["data"])
63
104
  end
64
105
 
65
106
  # Leave the group as the authenticated identity. Idempotent (204).
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base_resource"
4
+ require_relative "../types/identity"
5
+
6
+ module Blueticks
7
+ module Resources
8
+ class IdentityResource < BaseResource
9
+ # Resolve an identity.
10
+ #
11
+ # Maps a WhatsApp JID (phone, `@lid` or group) to its canonical form and
12
+ # aliases. Requires the `contacts:read` scope.
13
+ def retrieve(id)
14
+ env = client.request("GET", "/v1/identity/#{id}")
15
+ Types::Identity.from_hash(env && env["data"])
16
+ end
17
+
18
+ # Resolve identities (batch).
19
+ #
20
+ # Resolves many JIDs in one call. Requires the `contacts:read` scope.
21
+ def resolve_batch(ids:)
22
+ env = client.request("POST", "/v1/identity/resolve", body: { "ids" => ids })
23
+ Types::IdentityBatch.from_hash(env && env["data"])
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base_resource"
4
+ require_relative "../types/latest_chats"
5
+
6
+ module Blueticks
7
+ module Resources
8
+ class LatestChatsResource < BaseResource
9
+ # Get latest chats & messages.
10
+ #
11
+ # Returns the most recent chats, each with its latest messages, in one
12
+ # call. Requires the `chats:read` scope.
13
+ def retrieve(number_of_chats: nil, number_of_messages: nil)
14
+ params = {}
15
+ params["numberOfChats"] = number_of_chats unless number_of_chats.nil?
16
+ params["numberOfMessages"] = number_of_messages unless number_of_messages.nil?
17
+ env = client.request("GET", "/v1/latest-chats", params: params.empty? ? nil : params)
18
+ Types::LatestChats.from_hash(env && env["data"])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -2,46 +2,142 @@
2
2
 
3
3
  require_relative "../base_resource"
4
4
  require_relative "../types/messages"
5
- require_relative "../types/page"
5
+ require_relative "../types/ok_result"
6
+ require_relative "../types/paginated"
6
7
 
7
8
  module Blueticks
8
9
  module Resources
9
10
  class MessagesResource < BaseResource
10
- # Send a message immediately, or schedule one for later with `send_at:`.
11
- #
12
- # @param to [String] WhatsApp chat-id or phone number
13
- # @param text [String, nil] Plain-text message body
14
- # @param media_url [String, nil] Public URL to media to attach
15
- # @param media_caption [String, nil] Caption for media
16
- # @param send_at [String, nil] ISO 8601 timestamp to schedule
17
- # @param from [String, nil] Multi-number engine identifier
18
- # @param idempotency_key [String, nil] Optional idempotency key
19
- # @return [Blueticks::Types::Message]
20
- def send(to:, text: nil, media_url: nil, media_caption: nil, send_at: nil,
21
- from: nil, idempotency_key: nil)
22
- body = { "to" => to }
23
- body["text"] = text unless text.nil?
24
- body["media_url"] = media_url unless media_url.nil?
25
- body["media_caption"] = media_caption unless media_caption.nil?
26
- body["send_at"] = send_at unless send_at.nil?
27
- body["from"] = from unless from.nil?
28
- data = client.request("POST", "/v1/messages", body: body, idempotency_key: idempotency_key)
29
- Types::Message.from_hash(data)
11
+ # Query messages from the WhatsApp store, newest first. Offset-paginated
12
+ # (PublicMessage rows). `message_types` is an Array of wire message types
13
+ # (e.g. `%w[chat image]`). `query_any` is an Array of keywords matched with OR
14
+ # semantics; `mime`, `filename_contains`, `has_media` and `sender` narrow the
15
+ # result further.
16
+ def list(chat_id: nil, search_token: nil, order: nil, since: nil, until_: nil,
17
+ message_types: nil, load_from_phone_if_needed: nil,
18
+ include_media_content: nil, skip: nil, limit: nil,
19
+ query_any: nil, mime: nil, filename_contains: nil, has_media: nil, sender: nil)
20
+ params = {}
21
+ params["chatId"] = chat_id unless chat_id.nil?
22
+ params["searchToken"] = search_token unless search_token.nil?
23
+ params["order"] = order unless order.nil?
24
+ params["since"] = since unless since.nil?
25
+ params["until"] = until_ unless until_.nil?
26
+ params["messageTypes"] = message_types.join(",") if message_types && !message_types.empty?
27
+ params["loadFromPhoneIfNeeded"] = load_from_phone_if_needed unless load_from_phone_if_needed.nil?
28
+ params["includeMediaContent"] = include_media_content unless include_media_content.nil?
29
+ params["skip"] = skip unless skip.nil?
30
+ params["limit"] = limit unless limit.nil?
31
+ params["queryAny"] = query_any unless query_any.nil? || query_any.empty?
32
+ params["mime"] = mime unless mime.nil?
33
+ params["filenameContains"] = filename_contains unless filename_contains.nil?
34
+ params["hasMedia"] = has_media unless has_media.nil?
35
+ params["sender"] = sender unless sender.nil?
36
+ env = client.request("GET", "/v1/messages", params: params.empty? ? nil : params)
37
+ Types::PaginatedPage.from_hash(env, item_type: Types::PublicMessage)
38
+ end
39
+
40
+ # Retrieve a single store message by its serialized wire key. Pass
41
+ # `chat_id` to scope the lookup.
42
+ def retrieve(wa_message_key, chat_id: nil)
43
+ params = chat_id.nil? ? nil : { "chatId" => chat_id }
44
+ env = client.request("GET", "/v1/messages/#{wa_message_key}", params: params)
45
+ Types::PublicMessage.from_hash(env && env["data"])
30
46
  end
31
47
 
32
- # Retrieve a single message by id.
33
- def get(message_id)
34
- data = client.request("GET", "/v1/messages/#{message_id}")
35
- Types::Message.from_hash(data)
48
+ # Get delivery acknowledgement for a single message.
49
+ def get_ack(wa_message_key, chat_id: nil)
50
+ params = chat_id.nil? ? nil : { "chatId" => chat_id }
51
+ env = client.request("GET", "/v1/messages/ack/#{wa_message_key}", params: params)
52
+ Types::MessageAck.from_hash(env && env["data"])
36
53
  end
37
54
 
38
- # List messages sent through the API, newest first. Cursor-paginated.
39
- def list(limit: nil, cursor: nil)
55
+ # Batch get delivery status for up to 200 sent messages. Offset-paginated
56
+ # (BatchAckEntry rows).
57
+ def batch_acks(message_keys:, chat_id: nil)
58
+ body = { "messageKeys" => message_keys }
59
+ body["chatId"] = chat_id unless chat_id.nil?
60
+ env = client.request("POST", "/v1/messages/acks", body: body)
61
+ Types::PaginatedPage.from_hash(env, item_type: Types::BatchAckEntry)
62
+ end
63
+
64
+ # Ask the engine to pull older history from the connected phone.
65
+ #
66
+ # `pages` (1-10, default 1) is how many older pages to pull in one call;
67
+ # `until_date` (ISO 8601 string or epoch seconds/ms) stops paging once the
68
+ # oldest loaded message is at/before that date.
69
+ def load_older(chat_id, pages: nil, until_date: nil)
70
+ body = {}
71
+ body["pages"] = pages unless pages.nil?
72
+ body["until_date"] = until_date unless until_date.nil?
73
+ env = client.request("POST", "/v1/messages/load_older/#{chat_id}", body: body.empty? ? nil : body)
74
+ Types::LoadOlderResult.from_hash(env && env["data"])
75
+ end
76
+
77
+ # Fetch media (URL and/or base64 bytes) for a message.
78
+ def get_media(wa_message_key, chat_id: nil, max_attempts: nil)
40
79
  params = {}
41
- params["limit"] = limit unless limit.nil?
42
- params["cursor"] = cursor unless cursor.nil?
43
- data = client.request("GET", "/v1/messages", params: params.empty? ? nil : params)
44
- Types::Page.from_hash(data, item_type: Types::Message)
80
+ params["chatId"] = chat_id unless chat_id.nil?
81
+ params["maxAttempts"] = max_attempts unless max_attempts.nil?
82
+ env = client.request("GET", "/v1/messages/media/#{wa_message_key}",
83
+ params: params.empty? ? nil : params)
84
+ Types::Media.from_hash(env && env["data"])
85
+ end
86
+
87
+ # Pin a message. Optional `duration` (seconds) sets the pin TTL.
88
+ def pin(wa_message_key, chat_id: nil, duration: nil)
89
+ params = chat_id.nil? ? nil : { "chatId" => chat_id }
90
+ body = {}
91
+ body["duration"] = duration unless duration.nil?
92
+ env = client.request("POST", "/v1/messages/pin/#{wa_message_key}", params: params, body: body)
93
+ Types::OkResult.from_hash(env && env["data"])
94
+ end
95
+
96
+ # Unpin a message.
97
+ def unpin(wa_message_key, chat_id: nil)
98
+ params = chat_id.nil? ? nil : { "chatId" => chat_id }
99
+ env = client.request("POST", "/v1/messages/unpin/#{wa_message_key}", params: params)
100
+ Types::OkResult.from_hash(env && env["data"])
101
+ end
102
+
103
+ # List the pinned messages in a chat. Offset-paginated (PinnedMessage rows).
104
+ def pinned(chat_id)
105
+ env = client.request("GET", "/v1/messages/pinned/#{chat_id}")
106
+ Types::PaginatedPage.from_hash(env, item_type: Types::PinnedMessage)
107
+ end
108
+
109
+ # React to a message. Pass an empty `emoji` string to remove a reaction.
110
+ def react(wa_message_key, emoji:, chat_id: nil)
111
+ params = chat_id.nil? ? nil : { "chatId" => chat_id }
112
+ env = client.request("POST", "/v1/messages/reactions/#{wa_message_key}",
113
+ params: params, body: { "emoji" => emoji })
114
+ Types::OkResult.from_hash(env && env["data"])
115
+ end
116
+
117
+ # Send a message immediately to a chat (direct dispatch — no queue row is
118
+ # persisted for the wait; the response carries the WhatsApp wire key). The
119
+ # body is a discriminated union keyed on `type` (`text`, `media`, `poll`).
120
+ def create(chat_id, type:, text: nil, media_url: nil, media_base64: nil,
121
+ media_kind: nil, media_filename: nil, poll_question: nil,
122
+ poll_options: nil, poll_allow_multiple: nil, reply_to: nil,
123
+ secret: nil, with_typing: nil, typing_seconds: nil, idempotency_key: nil,
124
+ mentions: nil)
125
+ body = { "type" => type }
126
+ body["text"] = text unless text.nil?
127
+ body["mediaUrl"] = media_url unless media_url.nil?
128
+ body["mediaBase64"] = media_base64 unless media_base64.nil?
129
+ body["mediaKind"] = media_kind unless media_kind.nil?
130
+ body["mediaFilename"] = media_filename unless media_filename.nil?
131
+ body["pollQuestion"] = poll_question unless poll_question.nil?
132
+ body["pollOptions"] = poll_options unless poll_options.nil?
133
+ body["pollAllowMultiple"] = poll_allow_multiple unless poll_allow_multiple.nil?
134
+ body["replyTo"] = reply_to unless reply_to.nil?
135
+ body["secret"] = secret unless secret.nil?
136
+ body["withTyping"] = with_typing unless with_typing.nil?
137
+ body["typingSeconds"] = typing_seconds unless typing_seconds.nil?
138
+ body["mentions"] = mentions unless mentions.nil?
139
+ env = client.request("POST", "/v1/messages/#{chat_id}", body: body, idempotency_key: idempotency_key)
140
+ Types::Message.from_hash(env && env["data"])
45
141
  end
46
142
  end
47
143
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base_resource"
4
+ require_relative "../types/newsletter"
5
+ require_relative "../types/paginated"
6
+
7
+ module Blueticks
8
+ module Resources
9
+ class NewslettersResource < BaseResource
10
+ # List newsletters.
11
+ #
12
+ # List newsletters visible to the connected WhatsApp engine. Offset-paginated
13
+ # (NewsletterListItem rows). Requires `newsletters:read` scope.
14
+ def list(search_token: nil, include_archive: nil, skip: nil, limit: nil)
15
+ params = {}
16
+ params["searchToken"] = search_token unless search_token.nil?
17
+ params["includeArchive"] = include_archive unless include_archive.nil?
18
+ params["skip"] = skip unless skip.nil?
19
+ params["limit"] = limit unless limit.nil?
20
+ env = client.request("GET", "/v1/newsletters", params: params.empty? ? nil : params)
21
+ Types::PaginatedPage.from_hash(env, item_type: Types::NewsletterListItem)
22
+ end
23
+
24
+ # Create newsletter.
25
+ #
26
+ # Create a new WhatsApp newsletter (channel). Requires `messages:write` scope.
27
+ def create(name:, description: nil)
28
+ body = { "name" => name }
29
+ body["description"] = description unless description.nil?
30
+ env = client.request("POST", "/v1/newsletters", body: body)
31
+ Types::Newsletter.from_hash(env && env["data"])
32
+ end
33
+
34
+ # Get newsletter.
35
+ #
36
+ # Retrieve a newsletter by its JID. Requires `newsletters:read` scope.
37
+ def retrieve(newsletter_id)
38
+ env = client.request("GET", "/v1/newsletters/#{newsletter_id}")
39
+ Types::Newsletter.from_hash(env && env["data"])
40
+ end
41
+ end
42
+ end
43
+ end
@@ -6,10 +6,13 @@ require_relative "../types/ping"
6
6
  module Blueticks
7
7
  module Resources
8
8
  class PingResource < BaseResource
9
- # Health check. Returns basic info about the authenticated API key.
9
+ # Ping.
10
+ #
11
+ # Probe the API: returns `api: "ok"`, the account id, and the currently
12
+ # connected WhatsApp engines. Useful as a connection/liveness test.
10
13
  def retrieve
11
- data = client.request("GET", "/v1/ping")
12
- Types::Ping.from_hash(data)
14
+ env = client.request("GET", "/v1/ping")
15
+ Types::Ping.from_hash(env && env["data"])
13
16
  end
14
17
  end
15
18
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base_resource"
4
+ require_relative "../types/realtime"
5
+
6
+ module Blueticks
7
+ module Resources
8
+ class RealtimeResource < BaseResource
9
+ # Get MQTT credentials for receiving messages in real time.
10
+ #
11
+ # Returns short-lived broker credentials and the topic to subscribe to.
12
+ # Requires the `chats:read` scope.
13
+ def create_token
14
+ env = client.request("POST", "/v1/realtime/token")
15
+ Types::RealtimeToken.from_hash(env && env["data"])
16
+ end
17
+ end
18
+ end
19
+ end