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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19df800ede192ab6e262f3faeff874072d9c880466ad6d17cd5de461cdbf0b07
4
- data.tar.gz: 25804da58032e239b22ef1027365fb05f12dd778633a542d52b41910eab3bffb
3
+ metadata.gz: 80ac68082d0098a0ceae9ace729c7d019bbeca7f4714b5f8f11c4846af84ac04
4
+ data.tar.gz: f8d39230912328f38e8e9dda9034cc5d0d9192b47f7ed7857063d2a173fd32ec
5
5
  SHA512:
6
- metadata.gz: a2a0ab6b4d585f6a93a51a0a13d55dda3e87d2499c32ae932c36a29448ce09e2a886e97c9d8c4b8ba6457ae63aa9de15d79d5a8e8530eaee2451ce02a9d1bd97
7
- data.tar.gz: 05c7bcd1cc42f0604961fe157234a3dc1604304f93e276b57425504aea42453eaeb2f3e9da3c6b6f01350d1d7a33134d76cd8247c98f1c59928d45aea03becbc
6
+ metadata.gz: fc539e01c948edc214d396f4bb8aafb09eacc7a450a370c5e4675a23021c3705b829b467332c19a1e2c5b4c3ab53d88af64930e122e205b980f1a3354ca4cfb0
7
+ data.tar.gz: ebe45c16ef9311b23600ca225dcaffb1924e64f1df5681966383053a8be2d50d05791abc199db390de7fc30404d87a386892b0086631ecf269187e1ddef21736
data/CHANGELOG.md CHANGED
@@ -4,6 +4,58 @@ 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
+ ## 2.1.1 — 2026-11-07
8
+
9
+ ### Fixed
10
+ - Publish workflow now runs on a GitHub-hosted runner (`ubuntu-24.04`); the 2.1.0 publish failed on the self-hosted runner, so 2.1.1 is the first published release with `groups.get_picture` / `groups.remove_picture`.
11
+
12
+ ## 2.1.0 — 2026-11-07
13
+
14
+ ### Added
15
+
16
+ - Group picture: `groups.get_picture` (get the current picture URL) and `groups.remove_picture` (remove the picture; Gateway/Baileys engine only — Chrome-extension-only accounts get `501 unsupported_command`). `groups.set_picture` already existed.
17
+
18
+ ## 1.2.0 — 2026-05-22
19
+
20
+ OpenAPI parity pass. The SDK now matches `backend/openapi.json`
21
+ operation-for-operation; an engineless drift check
22
+ (`.github/workflows/sdk-spec-drift.yml`) gates future regressions. The
23
+ `/v1/*` surface is pre-release — none of these changes affect production
24
+ callers yet.
25
+
26
+ ### Changed
27
+
28
+ - `messages.send` now takes the discriminated body shape matching the
29
+ backend's strict `anyOf` (BE#50):
30
+ ```ruby
31
+ client.messages.send(to: '+1...', type: 'text', text: 'hi')
32
+ client.messages.send(to: '+1...', type: 'media', media: { url: '...', kind: 'image' })
33
+ client.messages.send(to: '+1...', type: 'poll', poll: { question: '...', options: [...] })
34
+ ```
35
+ - Single-item GETs now use `.retrieve(id)` instead of `.get(id)` where the
36
+ spec uses GET-by-id. Also `engines.status` → `engines.retrieve`.
37
+ - `newsletters.create` returns the typed `Blueticks::Types::Newsletter` (8
38
+ fields) instead of a raw Hash.
39
+
40
+ ### Added
41
+
42
+ - `newsletters.list(limit:, cursor:)` — `GET /v1/newsletters` (cursor-paginated → `Page<Newsletter>`)
43
+ - `newsletters.retrieve(id)` — `GET /v1/newsletters/{id}`
44
+ - `ping.retrieve` — typed `Ping` (`account_id`, `key_prefix`, `scopes`). The
45
+ `ping` resource had been deleted in a prior interim regen; restored here.
46
+ - `Message` now exposes `key`, `type`, `media_kind`, `poll_question`, `link_preview`
47
+
48
+ ### Removed
49
+
50
+ - `engines.me`, `engines.logout`, `engines.reload`
51
+ - `contacts.get_profile_picture`
52
+ - `utils.validate_phone`, `utils.link_preview` (the `utils` resource was emptied)
53
+
54
+ ### Fixed
55
+
56
+ - `groups.list` was documented at `dev.blueticks.co` but absent from the
57
+ SDK for ~9 days — now present.
58
+
7
59
  ## 1.0.0 — 2026-04-30
8
60
 
9
61
  Initial release.
@@ -2,13 +2,30 @@
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/boards"
8
+ require_relative "resources/campaigns"
9
+ require_relative "resources/chats"
10
+ require_relative "resources/contacts"
11
+ require_relative "resources/engines"
12
+ require_relative "resources/groups"
13
+ require_relative "resources/identity"
14
+ require_relative "resources/latest_chats"
15
+ require_relative "resources/messages"
16
+ require_relative "resources/newsletters"
17
+ require_relative "resources/ping"
18
+ require_relative "resources/realtime"
5
19
  require_relative "resources/scheduled_messages"
20
+ require_relative "resources/suno"
21
+ require_relative "resources/utils"
22
+ require_relative "resources/webhooks"
6
23
 
7
24
  module Blueticks
8
25
  # Top-level entry point. Construct one and access resources via accessor methods:
9
26
  #
10
27
  # client = Blueticks::Client.new(api_key: "bt_live_...")
11
- # client.messages.send(to: "12025550100", text: "Hello!")
28
+ # client.scheduled_messages.create(type: "text", to: "12025550100", text: "Hello!")
12
29
  #
13
30
  # Configuration order of precedence:
14
31
  # 1. Explicit keyword args.
@@ -47,10 +64,6 @@ module Blueticks
47
64
  @ping ||= Resources::PingResource.new(self)
48
65
  end
49
66
 
50
- def messages
51
- @messages ||= Resources::MessagesResource.new(self)
52
- end
53
-
54
67
  def scheduled_messages
55
68
  @scheduled_messages ||= Resources::ScheduledMessagesResource.new(self)
56
69
  end
@@ -83,6 +96,35 @@ module Blueticks
83
96
  @contacts ||= Resources::ContactsResource.new(self)
84
97
  end
85
98
 
99
+ def newsletters
100
+ @newsletters ||= Resources::NewslettersResource.new(self)
101
+ end
102
+
103
+ def messages
104
+ @messages ||= Resources::MessagesResource.new(self)
105
+ end
106
+
107
+ def boards
108
+ @boards ||= Resources::BoardsResource.new(self)
109
+ end
110
+
111
+ def identity
112
+ @identity ||= Resources::IdentityResource.new(self)
113
+ end
114
+
115
+ def latest_chats
116
+ @latest_chats ||= Resources::LatestChatsResource.new(self)
117
+ end
118
+
119
+ def realtime
120
+ @realtime ||= Resources::RealtimeResource.new(self)
121
+ end
122
+
123
+ def suno
124
+ @suno ||= Resources::SunoResource.new(self)
125
+ end
126
+
127
+ # Kept for backward compatibility. No spec-backed operations exposed.
86
128
  def utils
87
129
  @utils ||= Resources::UtilsResource.new(self)
88
130
  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
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base_resource"
4
+ require_relative "../types/boards"
5
+ require_relative "../types/paginated"
6
+
7
+ module Blueticks
8
+ module Resources
9
+ class BoardsResource < BaseResource
10
+ # List boards, newest first. Offset-paginated (Board rows).
11
+ #
12
+ # Requires the `boards:read` scope.
13
+ def list(order: nil, skip: nil, limit: nil)
14
+ params = {}
15
+ params["order"] = order unless order.nil?
16
+ params["skip"] = skip unless skip.nil?
17
+ params["limit"] = limit unless limit.nil?
18
+ env = client.request("GET", "/v1/boards", params: params.empty? ? nil : params)
19
+ Types::PaginatedPage.from_hash(env, item_type: Types::Board)
20
+ end
21
+
22
+ # Create or find a board.
23
+ #
24
+ # Returns the existing board for the given `domain`/`entity_kind` when one
25
+ # exists, otherwise creates it. Requires the `boards:write` scope.
26
+ def create(domain: nil, entity_kind: nil, name: nil, description: nil,
27
+ agent_id: nil, memory_retention_days: nil)
28
+ body = {}
29
+ body["domain"] = domain unless domain.nil?
30
+ body["entity_kind"] = entity_kind unless entity_kind.nil?
31
+ body["name"] = name unless name.nil?
32
+ body["description"] = description unless description.nil?
33
+ body["agent_id"] = agent_id unless agent_id.nil?
34
+ body["memory_retention_days"] = memory_retention_days unless memory_retention_days.nil?
35
+ env = client.request("POST", "/v1/boards", body: body)
36
+ Types::BoardDetail.from_hash(env && env["data"])
37
+ end
38
+
39
+ # Get a board (with its columns) by id.
40
+ #
41
+ # Requires the `boards:read` scope.
42
+ def retrieve(board_id)
43
+ env = client.request("GET", "/v1/boards/#{board_id}")
44
+ Types::BoardDetail.from_hash(env && env["data"])
45
+ end
46
+
47
+ # List a board's items (tasks). Offset-paginated (BoardItem rows).
48
+ #
49
+ # Pass `active_only: true` to return only items updated within the
50
+ # retention window whose status is non-terminal. Filter by `status` label
51
+ # or `updated_since`. Requires the `boards:read` scope.
52
+ def list_items(board_id, status: nil, order: nil, active_only: nil,
53
+ updated_since: nil, skip: nil, limit: nil)
54
+ params = {}
55
+ params["status"] = status unless status.nil?
56
+ params["order"] = order unless order.nil?
57
+ params["active_only"] = active_only unless active_only.nil?
58
+ params["updated_since"] = updated_since unless updated_since.nil?
59
+ params["skip"] = skip unless skip.nil?
60
+ params["limit"] = limit unless limit.nil?
61
+ env = client.request("GET", "/v1/boards/#{board_id}/items",
62
+ params: params.empty? ? nil : params)
63
+ Types::PaginatedPage.from_hash(env, item_type: Types::BoardItem)
64
+ end
65
+
66
+ # Create a board item.
67
+ #
68
+ # Requires the `boards:write` scope.
69
+ def create_item(board_id, text:, status: nil, priority: nil, due_date: nil, cells: nil,
70
+ source: nil, confidence: nil, allow_duplicate: nil)
71
+ body = { "text" => text }
72
+ body["status"] = status unless status.nil?
73
+ body["priority"] = priority unless priority.nil?
74
+ body["due_date"] = due_date unless due_date.nil?
75
+ body["cells"] = cells unless cells.nil?
76
+ body["source"] = source unless source.nil?
77
+ body["confidence"] = confidence unless confidence.nil?
78
+ body["allow_duplicate"] = allow_duplicate unless allow_duplicate.nil?
79
+ env = client.request("POST", "/v1/boards/#{board_id}/items", body: body)
80
+ Types::BoardItem.from_hash(env && env["data"])
81
+ end
82
+
83
+ # Update a board item. Pass any subset of the editable fields.
84
+ #
85
+ # Requires the `boards:write` scope.
86
+ def update_item(board_id, item_id, text: nil, status: nil, priority: nil, due_date: nil,
87
+ cells: nil, source: nil, confidence: nil, deleted: nil, note: nil)
88
+ body = {}
89
+ body["text"] = text unless text.nil?
90
+ body["status"] = status unless status.nil?
91
+ body["priority"] = priority unless priority.nil?
92
+ body["due_date"] = due_date unless due_date.nil?
93
+ body["cells"] = cells unless cells.nil?
94
+ body["source"] = source unless source.nil?
95
+ body["confidence"] = confidence unless confidence.nil?
96
+ body["deleted"] = deleted unless deleted.nil?
97
+ body["note"] = note unless note.nil?
98
+ env = client.request("PATCH", "/v1/boards/#{board_id}/items/#{item_id}", body: body)
99
+ Types::BoardItem.from_hash(env && env["data"])
100
+ end
101
+
102
+ # Bulk upsert board items.
103
+ #
104
+ # `items` is an Array of item Hashes (same keys as `create_item`, string
105
+ # keys on the wire). Requires the `boards:write` scope.
106
+ def bulk_upsert_items(board_id, items:, mode: nil)
107
+ body = { "items" => items }
108
+ body["mode"] = mode unless mode.nil?
109
+ env = client.request("POST", "/v1/boards/#{board_id}/items/bulk", body: body)
110
+ Types::BulkItemsResult.from_hash(env && env["data"])
111
+ end
112
+
113
+ # Find existing board items similar to the given candidates.
114
+ #
115
+ # `candidates` is an Array of `{ "text" => "..." }` Hashes. Requires the
116
+ # `boards:read` scope.
117
+ def find_similar_items(board_id, candidates:)
118
+ env = client.request("POST", "/v1/boards/#{board_id}/items/find-similar",
119
+ body: { "candidates" => candidates })
120
+ Types::FindSimilarResult.from_hash(env && env["data"])
121
+ end
122
+ end
123
+ end
124
+ 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,67 @@
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
+ #
13
+ # `kinds` is an Array of chat kinds to keep (`%w[contact group newsletter]`);
14
+ # `since` keeps only chats whose last message is at/after that ISO 8601 instant.
15
+ def list(search_token: nil, filter: nil, include_last_message: nil,
16
+ include_extended_info: nil, include_without_name: nil,
17
+ include_archive: nil, skip: nil, limit: nil, kinds: nil, since: nil)
12
18
  params = {}
13
- params["query"] = query unless query.nil?
19
+ params["searchToken"] = search_token unless search_token.nil?
20
+ params["filter"] = filter unless filter.nil?
21
+ params["includeLastMessage"] = include_last_message unless include_last_message.nil?
22
+ params["includeExtendedInfo"] = include_extended_info unless include_extended_info.nil?
23
+ params["includeWithoutName"] = include_without_name unless include_without_name.nil?
24
+ params["includeArchive"] = include_archive unless include_archive.nil?
25
+ params["skip"] = skip unless skip.nil?
14
26
  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)
27
+ params["kinds"] = kinds unless kinds.nil? || kinds.empty?
28
+ params["since"] = since unless since.nil?
29
+ env = client.request("GET", "/v1/chats", params: params.empty? ? nil : params)
30
+ Types::PaginatedPage.from_hash(env, item_type: Types::Chat)
18
31
  end
19
32
 
20
33
  # 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)
34
+ def retrieve(chat_id)
35
+ env = client.request("GET", "/v1/chats/#{chat_id}")
36
+ Types::Chat.from_hash(env && env["data"])
24
37
  end
25
38
 
26
- # List participants in a group chat. Cursor-paginated.
27
- def list_participants(chat_id, limit: nil, cursor: nil)
39
+ # List participants in a group/community chat. Offset-paginated.
40
+ def list_participants(chat_id, search_token: nil, skip: nil, limit: nil)
28
41
  params = {}
42
+ params["searchToken"] = search_token unless search_token.nil?
43
+ params["skip"] = skip unless skip.nil?
29
44
  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)
34
- end
35
-
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
+ env = client.request("GET", "/v1/chats/#{chat_id}/participants",
46
+ params: params.empty? ? nil : params)
47
+ Types::PaginatedPage.from_hash(env, item_type: Types::Participant)
60
48
  end
61
49
 
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)
50
+ # Archive a chat.
51
+ def archive(chat_id)
52
+ env = client.request("POST", "/v1/chats/#{chat_id}/archive")
53
+ Types::OkResult.from_hash(env && env["data"])
66
54
  end
67
55
 
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)
56
+ # Unarchive a chat.
57
+ def unarchive(chat_id)
58
+ env = client.request("POST", "/v1/chats/#{chat_id}/unarchive")
59
+ Types::OkResult.from_hash(env && env["data"])
72
60
  end
73
61
 
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)
62
+ # Mark a chat as read (sends read receipts if enabled).
63
+ def mark_read(chat_id)
64
+ env = client.request("POST", "/v1/chats/#{chat_id}/mark_read")
65
+ Types::OkResult.from_hash(env && env["data"])
110
66
  end
111
67
  end
112
68
  end