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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -0
- data/lib/blueticks/client.rb +47 -5
- data/lib/blueticks/resources/account.rb +2 -2
- data/lib/blueticks/resources/audiences.rb +30 -22
- data/lib/blueticks/resources/boards.rb +124 -0
- data/lib/blueticks/resources/campaigns.rb +28 -23
- data/lib/blueticks/resources/chats.rb +42 -86
- data/lib/blueticks/resources/contacts.rb +18 -11
- data/lib/blueticks/resources/engines.rb +12 -19
- data/lib/blueticks/resources/groups.rb +69 -28
- data/lib/blueticks/resources/identity.rb +27 -0
- data/lib/blueticks/resources/latest_chats.rb +22 -0
- data/lib/blueticks/resources/messages.rb +127 -31
- data/lib/blueticks/resources/newsletters.rb +43 -0
- data/lib/blueticks/resources/ping.rb +6 -3
- data/lib/blueticks/resources/realtime.rb +19 -0
- data/lib/blueticks/resources/scheduled_messages.rb +81 -32
- data/lib/blueticks/resources/suno.rb +57 -0
- data/lib/blueticks/resources/utils.rb +3 -13
- data/lib/blueticks/resources/webhooks.rb +20 -20
- data/lib/blueticks/types/account.rb +2 -1
- data/lib/blueticks/types/audiences.rb +15 -9
- data/lib/blueticks/types/boards.rb +110 -0
- data/lib/blueticks/types/campaigns.rb +13 -12
- data/lib/blueticks/types/chats.rb +20 -79
- data/lib/blueticks/types/contacts.rb +6 -7
- data/lib/blueticks/types/coverage.rb +14 -0
- data/lib/blueticks/types/engines.rb +9 -8
- data/lib/blueticks/types/groups.rb +33 -6
- data/lib/blueticks/types/identity.rb +28 -0
- data/lib/blueticks/types/latest_chats.rb +32 -0
- data/lib/blueticks/types/messages.rb +132 -11
- data/lib/blueticks/types/newsletter.rb +33 -0
- data/lib/blueticks/types/ok_result.rb +13 -0
- data/lib/blueticks/types/paginated.rb +75 -0
- data/lib/blueticks/types/ping.rb +11 -3
- data/lib/blueticks/types/realtime.rb +23 -0
- data/lib/blueticks/types/scheduled_messages.rb +28 -14
- data/lib/blueticks/types/suno.rb +42 -0
- data/lib/blueticks/types/utils.rb +5 -12
- data/lib/blueticks/types/webhooks.rb +14 -11
- data/lib/blueticks/version.rb +1 -1
- data/lib/blueticks.rb +2 -2
- 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/
|
|
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
|
-
#
|
|
11
|
-
|
|
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["
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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/
|
|
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
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
#
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
#
|
|
39
|
-
|
|
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["
|
|
42
|
-
params["
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|