blueticks 1.1.2 → 2.0.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/lib/blueticks/client.rb +27 -5
  4. data/lib/blueticks/resources/account.rb +2 -2
  5. data/lib/blueticks/resources/audiences.rb +30 -22
  6. data/lib/blueticks/resources/campaigns.rb +28 -23
  7. data/lib/blueticks/resources/chats.rb +37 -86
  8. data/lib/blueticks/resources/contacts.rb +18 -11
  9. data/lib/blueticks/resources/engines.rb +12 -19
  10. data/lib/blueticks/resources/groups.rb +54 -28
  11. data/lib/blueticks/resources/messages.rb +110 -31
  12. data/lib/blueticks/resources/newsletters.rb +43 -0
  13. data/lib/blueticks/resources/ping.rb +6 -3
  14. data/lib/blueticks/resources/scheduled_messages.rb +74 -32
  15. data/lib/blueticks/resources/suno.rb +56 -0
  16. data/lib/blueticks/resources/utils.rb +3 -13
  17. data/lib/blueticks/resources/webhooks.rb +20 -20
  18. data/lib/blueticks/types/account.rb +2 -1
  19. data/lib/blueticks/types/audiences.rb +15 -9
  20. data/lib/blueticks/types/campaigns.rb +13 -12
  21. data/lib/blueticks/types/chats.rb +19 -78
  22. data/lib/blueticks/types/contacts.rb +6 -7
  23. data/lib/blueticks/types/engines.rb +9 -8
  24. data/lib/blueticks/types/groups.rb +22 -6
  25. data/lib/blueticks/types/messages.rb +124 -11
  26. data/lib/blueticks/types/newsletter.rb +33 -0
  27. data/lib/blueticks/types/ok_result.rb +13 -0
  28. data/lib/blueticks/types/paginated.rb +70 -0
  29. data/lib/blueticks/types/ping.rb +11 -3
  30. data/lib/blueticks/types/scheduled_messages.rb +28 -14
  31. data/lib/blueticks/types/suno.rb +42 -0
  32. data/lib/blueticks/types/utils.rb +5 -12
  33. data/lib/blueticks/types/webhooks.rb +13 -11
  34. data/lib/blueticks/version.rb +1 -1
  35. data/lib/blueticks.rb +2 -2
  36. metadata +7 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19df800ede192ab6e262f3faeff874072d9c880466ad6d17cd5de461cdbf0b07
4
- data.tar.gz: 25804da58032e239b22ef1027365fb05f12dd778633a542d52b41910eab3bffb
3
+ metadata.gz: 62b2a3ae7e59ee684634e2e2e26883fa47db18da62aa74eda711efd088d4e868
4
+ data.tar.gz: 392f498378faa47fa4be6f73fbefad15a5642ab16718ec2fdd9787a8aa5951a4
5
5
  SHA512:
6
- metadata.gz: a2a0ab6b4d585f6a93a51a0a13d55dda3e87d2499c32ae932c36a29448ce09e2a886e97c9d8c4b8ba6457ae63aa9de15d79d5a8e8530eaee2451ce02a9d1bd97
7
- data.tar.gz: 05c7bcd1cc42f0604961fe157234a3dc1604304f93e276b57425504aea42453eaeb2f3e9da3c6b6f01350d1d7a33134d76cd8247c98f1c59928d45aea03becbc
6
+ metadata.gz: e3a873bb77d76820cf1946483f11eabf2c53169c3930f1578fbbaa86679193e6428071aed414d4d9c41f5f99beb3dfd7bac411e9db84ebcad3e66b364e5f9def
7
+ data.tar.gz: 681cf759086355b74cdba8fec69cf515b62705b7a8a0039916c25a267dc34928d2f24c2a62dc948e3c04ea909c45f618cb0803677cc3efe0a56a2568447576d4
data/CHANGELOG.md CHANGED
@@ -4,6 +4,47 @@ All notable changes to the Blueticks Ruby SDK are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this
5
5
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.2.0 — 2026-05-22
8
+
9
+ OpenAPI parity pass. The SDK now matches `backend/openapi.json`
10
+ operation-for-operation; an engineless drift check
11
+ (`.github/workflows/sdk-spec-drift.yml`) gates future regressions. The
12
+ `/v1/*` surface is pre-release — none of these changes affect production
13
+ callers yet.
14
+
15
+ ### Changed
16
+
17
+ - `messages.send` now takes the discriminated body shape matching the
18
+ backend's strict `anyOf` (BE#50):
19
+ ```ruby
20
+ client.messages.send(to: '+1...', type: 'text', text: 'hi')
21
+ client.messages.send(to: '+1...', type: 'media', media: { url: '...', kind: 'image' })
22
+ client.messages.send(to: '+1...', type: 'poll', poll: { question: '...', options: [...] })
23
+ ```
24
+ - Single-item GETs now use `.retrieve(id)` instead of `.get(id)` where the
25
+ spec uses GET-by-id. Also `engines.status` → `engines.retrieve`.
26
+ - `newsletters.create` returns the typed `Blueticks::Types::Newsletter` (8
27
+ fields) instead of a raw Hash.
28
+
29
+ ### Added
30
+
31
+ - `newsletters.list(limit:, cursor:)` — `GET /v1/newsletters` (cursor-paginated → `Page<Newsletter>`)
32
+ - `newsletters.retrieve(id)` — `GET /v1/newsletters/{id}`
33
+ - `ping.retrieve` — typed `Ping` (`account_id`, `key_prefix`, `scopes`). The
34
+ `ping` resource had been deleted in a prior interim regen; restored here.
35
+ - `Message` now exposes `key`, `type`, `media_kind`, `poll_question`, `link_preview`
36
+
37
+ ### Removed
38
+
39
+ - `engines.me`, `engines.logout`, `engines.reload`
40
+ - `contacts.get_profile_picture`
41
+ - `utils.validate_phone`, `utils.link_preview` (the `utils` resource was emptied)
42
+
43
+ ### Fixed
44
+
45
+ - `groups.list` was documented at `dev.blueticks.co` but absent from the
46
+ SDK for ~9 days — now present.
47
+
7
48
  ## 1.0.0 — 2026-04-30
8
49
 
9
50
  Initial release.
@@ -2,13 +2,26 @@
2
2
 
3
3
  require_relative "errors"
4
4
  require_relative "transport"
5
+ require_relative "resources/account"
6
+ require_relative "resources/audiences"
7
+ require_relative "resources/campaigns"
8
+ require_relative "resources/chats"
9
+ require_relative "resources/contacts"
10
+ require_relative "resources/engines"
11
+ require_relative "resources/groups"
12
+ require_relative "resources/messages"
13
+ require_relative "resources/newsletters"
14
+ require_relative "resources/ping"
5
15
  require_relative "resources/scheduled_messages"
16
+ require_relative "resources/suno"
17
+ require_relative "resources/utils"
18
+ require_relative "resources/webhooks"
6
19
 
7
20
  module Blueticks
8
21
  # Top-level entry point. Construct one and access resources via accessor methods:
9
22
  #
10
23
  # client = Blueticks::Client.new(api_key: "bt_live_...")
11
- # client.messages.send(to: "12025550100", text: "Hello!")
24
+ # client.scheduled_messages.create(type: "text", to: "12025550100", text: "Hello!")
12
25
  #
13
26
  # Configuration order of precedence:
14
27
  # 1. Explicit keyword args.
@@ -47,10 +60,6 @@ module Blueticks
47
60
  @ping ||= Resources::PingResource.new(self)
48
61
  end
49
62
 
50
- def messages
51
- @messages ||= Resources::MessagesResource.new(self)
52
- end
53
-
54
63
  def scheduled_messages
55
64
  @scheduled_messages ||= Resources::ScheduledMessagesResource.new(self)
56
65
  end
@@ -83,6 +92,19 @@ module Blueticks
83
92
  @contacts ||= Resources::ContactsResource.new(self)
84
93
  end
85
94
 
95
+ def newsletters
96
+ @newsletters ||= Resources::NewslettersResource.new(self)
97
+ end
98
+
99
+ def messages
100
+ @messages ||= Resources::MessagesResource.new(self)
101
+ end
102
+
103
+ def suno
104
+ @suno ||= Resources::SunoResource.new(self)
105
+ end
106
+
107
+ # Kept for backward compatibility. No spec-backed operations exposed.
86
108
  def utils
87
109
  @utils ||= Resources::UtilsResource.new(self)
88
110
  end
@@ -10,8 +10,8 @@ module Blueticks
10
10
  #
11
11
  # Returns the account associated with the API key used for this request.
12
12
  def retrieve
13
- data = client.request("GET", "/v1/account")
14
- Types::Account.from_hash(data)
13
+ env = client.request("GET", "/v1/account")
14
+ Types::Account.from_hash(env && env["data"])
15
15
  end
16
16
  end
17
17
  end
@@ -3,67 +3,75 @@
3
3
  require_relative "../base_resource"
4
4
  require_relative "../types/audiences"
5
5
  require_relative "../types/deleted_resource"
6
- require_relative "../types/page"
6
+ require_relative "../types/paginated"
7
7
 
8
8
  module Blueticks
9
9
  module Resources
10
10
  class AudiencesResource < BaseResource
11
+ # Create an audience, optionally seeding it with contacts.
12
+ # `contacts` is an Array of `{ "to" => ..., "variables" => {...} }` hashes.
11
13
  def create(name:, contacts: nil)
12
14
  body = { "name" => name }
13
15
  body["contacts"] = contacts unless contacts.nil?
14
- data = client.request("POST", "/v1/audiences", body: body)
15
- Types::Audience.from_hash(data)
16
+ env = client.request("POST", "/v1/audiences", body: body)
17
+ Types::Audience.from_hash(env && env["data"])
16
18
  end
17
19
 
18
- # List audiences, newest first. Cursor-paginated.
19
- def list(limit: nil, cursor: nil)
20
+ # List audiences, newest first. Offset-paginated.
21
+ def list(order: nil, skip: nil, limit: nil)
20
22
  params = {}
23
+ params["order"] = order unless order.nil?
24
+ params["skip"] = skip unless skip.nil?
21
25
  params["limit"] = limit unless limit.nil?
22
- params["cursor"] = cursor unless cursor.nil?
23
- data = client.request("GET", "/v1/audiences", params: params.empty? ? nil : params)
24
- Types::Page.from_hash(data, item_type: Types::Audience)
26
+ env = client.request("GET", "/v1/audiences", params: params.empty? ? nil : params)
27
+ Types::PaginatedPage.from_hash(env, item_type: Types::Audience)
25
28
  end
26
29
 
27
- def get(audience_id, page: nil)
28
- params = page.nil? ? nil : { "page" => page }
29
- data = client.request("GET", "/v1/audiences/#{audience_id}", params: params)
30
- Types::Audience.from_hash(data)
30
+ # Retrieve an audience by id.
31
+ def retrieve(audience_id)
32
+ env = client.request("GET", "/v1/audiences/#{audience_id}")
33
+ Types::Audience.from_hash(env && env["data"])
31
34
  end
32
35
 
36
+ # Rename an audience.
33
37
  def update(audience_id, name:)
34
- data = client.request("PATCH", "/v1/audiences/#{audience_id}", body: { "name" => name })
35
- Types::Audience.from_hash(data)
38
+ env = client.request("PATCH", "/v1/audiences/#{audience_id}", body: { "name" => name })
39
+ Types::Audience.from_hash(env && env["data"])
36
40
  end
37
41
 
42
+ # Delete an audience.
38
43
  def delete(audience_id)
39
- data = client.request("DELETE", "/v1/audiences/#{audience_id}")
40
- Types::DeletedResource.from_hash(data)
44
+ env = client.request("DELETE", "/v1/audiences/#{audience_id}")
45
+ Types::DeletedResource.from_hash(env && env["data"])
41
46
  end
42
47
 
48
+ # Append contacts to an audience.
43
49
  def append_contacts(audience_id, contacts:)
44
- data = client.request(
50
+ env = client.request(
45
51
  "POST",
46
52
  "/v1/audiences/#{audience_id}/contacts",
47
53
  body: { "contacts" => contacts }
48
54
  )
49
- Types::AppendContactsResult.from_hash(data)
55
+ Types::AppendContactsResult.from_hash(env && env["data"])
50
56
  end
51
57
 
58
+ # Update a single contact's recipient and/or merge-field variables.
52
59
  def update_contact(audience_id, contact_id, to: nil, variables: nil)
53
60
  body = {}
54
61
  body["to"] = to unless to.nil?
55
62
  body["variables"] = variables unless variables.nil?
56
- data = client.request(
63
+ env = client.request(
57
64
  "PATCH",
58
65
  "/v1/audiences/#{audience_id}/contacts/#{contact_id}",
59
66
  body: body
60
67
  )
61
- Types::Contact.from_hash(data)
68
+ Types::AudienceContact.from_hash(env && env["data"])
62
69
  end
63
70
 
71
+ # Remove a contact from an audience.
64
72
  def delete_contact(audience_id, contact_id)
65
- client.request("DELETE", "/v1/audiences/#{audience_id}/contacts/#{contact_id}")
66
- nil
73
+ env = client.request("DELETE", "/v1/audiences/#{audience_id}/contacts/#{contact_id}")
74
+ Types::ContactRemoval.from_hash(env && env["data"])
67
75
  end
68
76
  end
69
77
  end
@@ -2,50 +2,55 @@
2
2
 
3
3
  require_relative "../base_resource"
4
4
  require_relative "../types/campaigns"
5
- require_relative "../types/page"
5
+ require_relative "../types/paginated"
6
6
 
7
7
  module Blueticks
8
8
  module Resources
9
9
  class CampaignsResource < BaseResource
10
+ # Create a campaign against an audience.
10
11
  def create(name:, audience_id:, text: nil, media_url: nil, media_caption: nil,
11
- from: nil, on_missing_variable: nil)
12
- body = { "name" => name, "audience_id" => audience_id }
12
+ on_missing_variable: nil)
13
+ body = { "name" => name, "audienceId" => audience_id }
13
14
  body["text"] = text unless text.nil?
14
- body["media_url"] = media_url unless media_url.nil?
15
- body["media_caption"] = media_caption unless media_caption.nil?
16
- body["from"] = from unless from.nil?
17
- body["on_missing_variable"] = on_missing_variable unless on_missing_variable.nil?
18
- data = client.request("POST", "/v1/campaigns", body: body)
19
- Types::Campaign.from_hash(data)
15
+ body["mediaUrl"] = media_url unless media_url.nil?
16
+ body["mediaCaption"] = media_caption unless media_caption.nil?
17
+ body["onMissingVariable"] = on_missing_variable unless on_missing_variable.nil?
18
+ env = client.request("POST", "/v1/campaigns", body: body)
19
+ Types::Campaign.from_hash(env && env["data"])
20
20
  end
21
21
 
22
- # List campaigns, newest first. Cursor-paginated.
23
- def list(limit: nil, cursor: nil)
22
+ # List campaigns, newest first. Offset-paginated.
23
+ def list(order: nil, skip: nil, limit: nil)
24
24
  params = {}
25
+ params["order"] = order unless order.nil?
26
+ params["skip"] = skip unless skip.nil?
25
27
  params["limit"] = limit unless limit.nil?
26
- params["cursor"] = cursor unless cursor.nil?
27
- data = client.request("GET", "/v1/campaigns", params: params.empty? ? nil : params)
28
- Types::Page.from_hash(data, item_type: Types::Campaign)
28
+ env = client.request("GET", "/v1/campaigns", params: params.empty? ? nil : params)
29
+ Types::PaginatedPage.from_hash(env, item_type: Types::Campaign)
29
30
  end
30
31
 
31
- def get(campaign_id)
32
- data = client.request("GET", "/v1/campaigns/#{campaign_id}")
33
- Types::Campaign.from_hash(data)
32
+ # Retrieve a campaign by id.
33
+ def retrieve(campaign_id)
34
+ env = client.request("GET", "/v1/campaigns/#{campaign_id}")
35
+ Types::Campaign.from_hash(env && env["data"])
34
36
  end
35
37
 
38
+ # Pause a running campaign.
36
39
  def pause(campaign_id)
37
- data = client.request("POST", "/v1/campaigns/#{campaign_id}/pause")
38
- Types::Campaign.from_hash(data)
40
+ env = client.request("POST", "/v1/campaigns/#{campaign_id}/pause")
41
+ Types::Campaign.from_hash(env && env["data"])
39
42
  end
40
43
 
44
+ # Resume a paused campaign.
41
45
  def resume(campaign_id)
42
- data = client.request("POST", "/v1/campaigns/#{campaign_id}/resume")
43
- Types::Campaign.from_hash(data)
46
+ env = client.request("POST", "/v1/campaigns/#{campaign_id}/resume")
47
+ Types::Campaign.from_hash(env && env["data"])
44
48
  end
45
49
 
50
+ # Cancel a campaign.
46
51
  def cancel(campaign_id)
47
- data = client.request("POST", "/v1/campaigns/#{campaign_id}/cancel")
48
- Types::Campaign.from_hash(data)
52
+ env = client.request("POST", "/v1/campaigns/#{campaign_id}/cancel")
53
+ Types::Campaign.from_hash(env && env["data"])
49
54
  end
50
55
  end
51
56
  end
@@ -2,111 +2,62 @@
2
2
 
3
3
  require_relative "../base_resource"
4
4
  require_relative "../types/chats"
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 ChatsResource < BaseResource
10
- # List/search chats, newest first. Cursor-paginated.
11
- def list(query: nil, limit: nil, cursor: nil)
11
+ # List/search chats, newest first. Offset-paginated.
12
+ def list(search_token: nil, filter: nil, include_last_message: nil,
13
+ include_extended_info: nil, include_without_name: nil,
14
+ 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["filter"] = filter unless filter.nil?
18
+ params["includeLastMessage"] = include_last_message unless include_last_message.nil?
19
+ params["includeExtendedInfo"] = include_extended_info unless include_extended_info.nil?
20
+ params["includeWithoutName"] = include_without_name unless include_without_name.nil?
21
+ params["includeArchive"] = include_archive unless include_archive.nil?
22
+ params["skip"] = skip unless skip.nil?
14
23
  params["limit"] = limit unless limit.nil?
15
- params["cursor"] = cursor unless cursor.nil?
16
- data = client.request("GET", "/v1/chats", params: params.empty? ? nil : params)
17
- Types::Page.from_hash(data, item_type: Types::Chat)
24
+ env = client.request("GET", "/v1/chats", params: params.empty? ? nil : params)
25
+ Types::PaginatedPage.from_hash(env, item_type: Types::Chat)
18
26
  end
19
27
 
20
28
  # Retrieve a chat by its JID.
21
- def get(chat_id)
22
- data = client.request("GET", "/v1/chats/#{chat_id}")
23
- Types::Chat.from_hash(data)
29
+ def retrieve(chat_id)
30
+ env = client.request("GET", "/v1/chats/#{chat_id}")
31
+ Types::Chat.from_hash(env && env["data"])
24
32
  end
25
33
 
26
- # List participants in a group chat. Cursor-paginated.
27
- def list_participants(chat_id, limit: nil, cursor: nil)
34
+ # List participants in a group/community chat. Offset-paginated.
35
+ def list_participants(chat_id, search_token: nil, skip: nil, limit: nil)
28
36
  params = {}
37
+ params["searchToken"] = search_token unless search_token.nil?
38
+ params["skip"] = skip unless skip.nil?
29
39
  params["limit"] = limit unless limit.nil?
30
- params["cursor"] = cursor unless cursor.nil?
31
- data = client.request("GET", "/v1/chats/#{chat_id}/participants",
32
- params: params.empty? ? nil : params)
33
- Types::Page.from_hash(data, item_type: Types::Participant)
40
+ env = client.request("GET", "/v1/chats/#{chat_id}/participants",
41
+ params: params.empty? ? nil : params)
42
+ Types::PaginatedPage.from_hash(env, item_type: Types::Participant)
34
43
  end
35
44
 
36
- # Mark a chat as read (sends read receipts if enabled).
37
- def mark_read(chat_id)
38
- data = client.request("POST", "/v1/chats/#{chat_id}/mark_read")
39
- Types::OkResponse.from_hash(data)
40
- end
41
-
42
- # Open a chat on the engine (useful for UI-assisted workflows).
43
- def open(chat_id)
44
- data = client.request("POST", "/v1/chats/#{chat_id}/open")
45
- Types::ChatRef.from_hash(data)
46
- end
47
-
48
- # List messages in a chat. `mode` is "latest" or "history".
49
- def list_messages(chat_id, mode: "latest", query: nil, since: nil, until_: nil,
50
- message_types: nil, limit: nil, cursor: nil)
51
- params = { "mode" => mode }
52
- params["query"] = query unless query.nil?
53
- params["since"] = since unless since.nil?
54
- params["until"] = until_ unless until_.nil?
55
- params["message_types"] = message_types.join(",") if message_types && !message_types.empty?
56
- params["limit"] = limit unless limit.nil?
57
- params["cursor"] = cursor unless cursor.nil?
58
- data = client.request("GET", "/v1/chats/#{chat_id}/messages", params: params)
59
- Types::Page.from_hash(data, item_type: Types::ChatMessage)
45
+ # Archive a chat.
46
+ def archive(chat_id)
47
+ env = client.request("POST", "/v1/chats/#{chat_id}/archive")
48
+ Types::OkResult.from_hash(env && env["data"])
60
49
  end
61
50
 
62
- # Retrieve a single message in a chat by its key.
63
- def get_message(chat_id, key)
64
- data = client.request("GET", "/v1/chats/#{chat_id}/messages/#{key}")
65
- Types::ChatMessage.from_hash(data)
51
+ # Unarchive a chat.
52
+ def unarchive(chat_id)
53
+ env = client.request("POST", "/v1/chats/#{chat_id}/unarchive")
54
+ Types::OkResult.from_hash(env && env["data"])
66
55
  end
67
56
 
68
- # Get message delivery status (ack value: -1=error, 0=pending, 1=server, 2=device, 3=read, 4=played).
69
- def get_message_ack(chat_id, key)
70
- data = client.request("GET", "/v1/chats/#{chat_id}/messages/#{key}/ack")
71
- Types::MessageAck.from_hash(data)
72
- end
73
-
74
- # React to a message. Pass an empty `emoji` string to remove a reaction.
75
- def react(chat_id, key, emoji:)
76
- data = client.request(
77
- "POST",
78
- "/v1/chats/#{chat_id}/messages/#{key}/reactions",
79
- body: { "emoji" => emoji }
80
- )
81
- Types::OkResponse.from_hash(data)
82
- end
83
-
84
- # Ask the engine to pull older history from the connected phone.
85
- def load_older_messages(chat_id)
86
- data = client.request("POST", "/v1/chats/#{chat_id}/messages/load_older")
87
- Types::LoadOlderMessagesResponse.from_hash(data)
88
- end
89
-
90
- # Get a hosted URL for the message media without inlining bytes.
91
- def get_media_url(chat_id, key)
92
- data = client.request("GET", "/v1/chats/#{chat_id}/messages/#{key}/media_url")
93
- Types::MediaUrlResponse.from_hash(data)
94
- end
95
-
96
- # Fetch full media bytes (base64) for a message.
97
- def get_media(chat_id, key)
98
- data = client.request("GET", "/v1/chats/#{chat_id}/messages/#{key}/media")
99
- Types::ChatMedia.from_hash(data)
100
- end
101
-
102
- # Batch get delivery status for up to 200 sent messages.
103
- def batch_message_acks(message_keys:)
104
- data = client.request(
105
- "POST",
106
- "/v1/chats/message_acks",
107
- body: { "message_keys" => message_keys }
108
- )
109
- Types::BatchMessageAcksResponse.from_hash(data)
57
+ # Mark a chat as read (sends read receipts if enabled).
58
+ def mark_read(chat_id)
59
+ env = client.request("POST", "/v1/chats/#{chat_id}/mark_read")
60
+ Types::OkResult.from_hash(env && env["data"])
110
61
  end
111
62
  end
112
63
  end
@@ -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