ruby-whatsapp 0.4.0 → 0.6.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 +152 -0
- data/README.md +322 -617
- data/docs/README.md +76 -0
- data/docs/business_phone_number/README.md +243 -0
- data/docs/business_phone_number/account.md +170 -0
- data/docs/business_phone_number/contact_book.md +90 -0
- data/docs/business_phone_number/profile.md +220 -0
- data/docs/business_phone_number/username.md +157 -0
- data/docs/configuration.md +177 -0
- data/docs/errors.md +219 -0
- data/docs/media/README.md +161 -0
- data/docs/message_templates/README.md +206 -0
- data/docs/message_templates/authentication.md +107 -0
- data/docs/message_templates/carousel.md +103 -0
- data/docs/message_templates/components.md +215 -0
- data/docs/message_templates/library.md +98 -0
- data/docs/message_templates/limited_time_offer.md +94 -0
- data/docs/message_templates/responses.md +173 -0
- data/docs/message_templates/standard.md +138 -0
- data/docs/messages/README.md +212 -0
- data/docs/messages/address.md +86 -0
- data/docs/messages/audio.md +43 -0
- data/docs/messages/contacts.md +129 -0
- data/docs/messages/document.md +49 -0
- data/docs/messages/image.md +59 -0
- data/docs/messages/interactive.md +342 -0
- data/docs/messages/location.md +51 -0
- data/docs/messages/location_request.md +59 -0
- data/docs/messages/mark_message_as_read.md +84 -0
- data/docs/messages/reaction.md +59 -0
- data/docs/messages/sticker.md +40 -0
- data/docs/messages/template.md +193 -0
- data/docs/messages/text.md +55 -0
- data/docs/messages/video.md +41 -0
- data/docs/schedule/README.md +183 -0
- data/docs/subscribed_app/README.md +139 -0
- data/docs/webhooks/README.md +217 -0
- data/docs/webhooks/account_alerts.md +44 -0
- data/docs/webhooks/account_review_update.md +29 -0
- data/docs/webhooks/account_update.md +54 -0
- data/docs/webhooks/automatic_events.md +47 -0
- data/docs/webhooks/business_capability_update.md +40 -0
- data/docs/webhooks/business_username_updates.md +55 -0
- data/docs/webhooks/history.md +107 -0
- data/docs/webhooks/message_template_components_update.md +46 -0
- data/docs/webhooks/message_template_quality_update.md +49 -0
- data/docs/webhooks/message_template_status_update.md +55 -0
- data/docs/webhooks/messages.md +520 -0
- data/docs/webhooks/partner_solutions.md +35 -0
- data/docs/webhooks/payment_configuration_update.md +40 -0
- data/docs/webhooks/phone_number_name_update.md +44 -0
- data/docs/webhooks/phone_number_quality_update.md +40 -0
- data/docs/webhooks/security.md +43 -0
- data/docs/webhooks/smb_app_state_sync.md +91 -0
- data/docs/webhooks/smb_message_echoes.md +91 -0
- data/docs/webhooks/template_category_update.md +52 -0
- data/docs/webhooks/user_id_update.md +84 -0
- data/docs/webhooks/user_preferences.md +87 -0
- data/lib/ruby/whatsapp/business_phone_number/account/details.rb +149 -0
- data/lib/ruby/whatsapp/business_phone_number/account/get.rb +53 -0
- data/lib/ruby/whatsapp/business_phone_number/account/transport.rb +30 -0
- data/lib/ruby/whatsapp/business_phone_number/account/update.rb +76 -0
- data/lib/ruby/whatsapp/business_phone_number/account.rb +23 -0
- data/lib/ruby/whatsapp/business_phone_number/contact_book/delete.rb +87 -0
- data/lib/ruby/whatsapp/business_phone_number/contact_book/deletion.rb +50 -0
- data/lib/ruby/whatsapp/business_phone_number/contact_book.rb +25 -0
- data/lib/ruby/whatsapp/business_phone_number/profile/details.rb +105 -0
- data/lib/ruby/whatsapp/business_phone_number/profile/get.rb +56 -0
- data/lib/ruby/whatsapp/business_phone_number/profile/update.rb +148 -0
- data/lib/ruby/whatsapp/business_phone_number/profile/verticals.rb +54 -0
- data/lib/ruby/whatsapp/business_phone_number/profile.rb +22 -0
- data/lib/ruby/whatsapp/business_phone_number/response.rb +3 -3
- data/lib/ruby/whatsapp/business_phone_number/transport.rb +12 -12
- data/lib/ruby/whatsapp/business_phone_number/username/claim.rb +180 -0
- data/lib/ruby/whatsapp/business_phone_number/username/delete.rb +36 -0
- data/lib/ruby/whatsapp/business_phone_number/username/details.rb +66 -0
- data/lib/ruby/whatsapp/business_phone_number/username/get.rb +33 -0
- data/lib/ruby/whatsapp/business_phone_number/username/suggest.rb +37 -0
- data/lib/ruby/whatsapp/business_phone_number/username/suggestions.rb +50 -0
- data/lib/ruby/whatsapp/business_phone_number/username.rb +27 -0
- data/lib/ruby/whatsapp/business_phone_number.rb +20 -4
- data/lib/ruby/whatsapp/message_templates/button/request_contact_info.rb +66 -0
- data/lib/ruby/whatsapp/message_templates/button.rb +5 -2
- data/lib/ruby/whatsapp/message_templates/component_set.rb +28 -0
- data/lib/ruby/whatsapp/message_templates/response/paging.rb +2 -1
- data/lib/ruby/whatsapp/message_templates.rb +2 -6
- data/lib/ruby/whatsapp/messages/base.rb +47 -5
- data/lib/ruby/whatsapp/messages/interactive/request_contact_info.rb +59 -0
- data/lib/ruby/whatsapp/messages/interactive.rb +22 -4
- data/lib/ruby/whatsapp/messages/response/contacts.rb +22 -5
- data/lib/ruby/whatsapp/path_building.rb +33 -0
- data/lib/ruby/whatsapp/schedule/create.rb +101 -0
- data/lib/ruby/whatsapp/schedule/list.rb +50 -0
- data/lib/ruby/whatsapp/schedule/response/collection.rb +61 -0
- data/lib/ruby/whatsapp/schedule/response/created.rb +30 -0
- data/lib/ruby/whatsapp/schedule/response/node.rb +85 -0
- data/lib/ruby/whatsapp/schedule/response.rb +18 -0
- data/lib/ruby/whatsapp/schedule/statuses.rb +19 -0
- data/lib/ruby/whatsapp/schedule/transport.rb +24 -0
- data/lib/ruby/whatsapp/schedule.rb +15 -0
- data/lib/ruby/whatsapp/subscribed_app/transport.rb +6 -5
- data/lib/ruby/whatsapp/version.rb +1 -1
- data/lib/ruby/whatsapp/webhook/business_username_updates.rb +51 -0
- data/lib/ruby/whatsapp/webhook/change.rb +3 -1
- data/lib/ruby/whatsapp/webhook/contact.rb +26 -3
- data/lib/ruby/whatsapp/webhook/history/chunk.rb +50 -0
- data/lib/ruby/whatsapp/webhook/history/thread.rb +64 -0
- data/lib/ruby/whatsapp/webhook/history.rb +73 -34
- data/lib/ruby/whatsapp/webhook/message/base.rb +27 -2
- data/lib/ruby/whatsapp/webhook/message/contacts/contact.rb +15 -2
- data/lib/ruby/whatsapp/webhook/message/context.rb +23 -2
- data/lib/ruby/whatsapp/webhook/message/edit.rb +50 -0
- data/lib/ruby/whatsapp/webhook/message/revoke.rb +36 -0
- data/lib/ruby/whatsapp/webhook/message/system.rb +19 -2
- data/lib/ruby/whatsapp/webhook/message/unknown.rb +2 -2
- data/lib/ruby/whatsapp/webhook/message.rb +3 -0
- data/lib/ruby/whatsapp/webhook/messages.rb +4 -3
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync/contact.rb +76 -0
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync/state_sync.rb +18 -4
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync.rb +33 -9
- data/lib/ruby/whatsapp/webhook/smb_message_echoes/echo.rb +60 -0
- data/lib/ruby/whatsapp/webhook/smb_message_echoes.rb +26 -11
- data/lib/ruby/whatsapp/webhook/status.rb +23 -2
- data/lib/ruby/whatsapp/webhook/user_id_update/entry.rb +56 -0
- data/lib/ruby/whatsapp/webhook/user_id_update/id_change.rb +35 -0
- data/lib/ruby/whatsapp/webhook/user_id_update.rb +61 -0
- data/lib/ruby/whatsapp/webhook/user_preferences/entry.rb +76 -0
- data/lib/ruby/whatsapp/webhook/user_preferences.rb +44 -23
- metadata +100 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class SmbMessageEchoes
|
|
6
|
+
# One echoed message: who the business sent it *to*, plus the message itself.
|
|
7
|
+
#
|
|
8
|
+
# A wrapper rather than three more accessors on {Message::Base}, deliberately.
|
|
9
|
+
# `Base` is the envelope shared by every inbound message type, and an inbound
|
|
10
|
+
# message can never carry a recipient — Meta's own convention is that a keyword
|
|
11
|
+
# defaults to `nil` when a field is *conditional*, not when it is impossible.
|
|
12
|
+
# Putting `to`/`to_user_id`/`to_parent_user_id` there to serve this one field would
|
|
13
|
+
# hand fifteen classes three permanently-`nil` readers, and would turn the
|
|
14
|
+
# `NoMethodError` that currently tells you `message.to` is meaningless into a silent
|
|
15
|
+
# `nil`. Composing instead keeps {Message.deserialize}'s dispatch untouched, so an
|
|
16
|
+
# echoed `revoke` or `edit` resolves exactly as an inbound one does.
|
|
17
|
+
class Echo
|
|
18
|
+
# @!attribute [rw] to
|
|
19
|
+
# @return [String, nil] The customer's phone number. Omitted once they adopt a
|
|
20
|
+
# username and it is no longer shareable — fall back to {#to_user_id}.
|
|
21
|
+
attr_accessor :to
|
|
22
|
+
|
|
23
|
+
# @!attribute [rw] to_user_id
|
|
24
|
+
# @return [String, nil] The customer's business-scoped user ID (BSUID).
|
|
25
|
+
attr_accessor :to_user_id
|
|
26
|
+
|
|
27
|
+
# @!attribute [rw] to_parent_user_id
|
|
28
|
+
# @return [String, nil] The customer's parent BSUID, present only when enrolled.
|
|
29
|
+
attr_accessor :to_parent_user_id
|
|
30
|
+
|
|
31
|
+
# @!attribute [rw] message
|
|
32
|
+
# @return [Message::Base] The echoed message, dispatched by `type` like any
|
|
33
|
+
# inbound one. Its `from` is the *business* number, not the customer's.
|
|
34
|
+
attr_accessor :message
|
|
35
|
+
|
|
36
|
+
def initialize(message:, to: nil, to_user_id: nil, to_parent_user_id: nil)
|
|
37
|
+
@to = to
|
|
38
|
+
@to_user_id = to_user_id
|
|
39
|
+
@to_parent_user_id = to_parent_user_id
|
|
40
|
+
@message = message
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
# @param data [Hash, nil] A raw `message_echoes[]` entry.
|
|
45
|
+
# @return [Echo]
|
|
46
|
+
def deserialize(data)
|
|
47
|
+
data ||= {}
|
|
48
|
+
|
|
49
|
+
new(
|
|
50
|
+
to: data["to"],
|
|
51
|
+
to_user_id: data["to_user_id"],
|
|
52
|
+
to_parent_user_id: data["to_parent_user_id"],
|
|
53
|
+
message: Message.deserialize(data)
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module Whatsapp
|
|
4
4
|
module Webhook
|
|
5
|
-
# Messages sent via the WhatsApp Business App or a linked device, echoed back
|
|
6
|
-
#
|
|
5
|
+
# Messages sent via the WhatsApp Business App or a linked device, echoed back so a
|
|
6
|
+
# Cloud API integration stays in sync with what the business itself sent — including
|
|
7
|
+
# messages the business revoked or edited from the app.
|
|
7
8
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# payload before relying on it in production.
|
|
13
|
-
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
9
|
+
# Meta's BSUID documentation publishes the value-level `contacts` array and the
|
|
10
|
+
# per-echo recipient identifiers. The array key itself (`message_echoes`) remains
|
|
11
|
+
# inferred, hence {#raw}.
|
|
12
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids
|
|
14
13
|
class SmbMessageEchoes
|
|
15
14
|
# @!attribute [rw] messaging_product
|
|
16
15
|
# @return [String, nil]
|
|
@@ -20,14 +19,28 @@ module Whatsapp
|
|
|
20
19
|
# @return [Metadata, nil]
|
|
21
20
|
attr_accessor :metadata
|
|
22
21
|
|
|
22
|
+
# @!attribute [rw] contacts
|
|
23
|
+
# @return [Array<Contact>] The customers these messages went to.
|
|
24
|
+
attr_accessor :contacts
|
|
25
|
+
|
|
23
26
|
# @!attribute [rw] message_echoes
|
|
24
|
-
# @return [Array<
|
|
27
|
+
# @return [Array<Echo>] Each pairs the echoed message with who it was sent to.
|
|
25
28
|
attr_accessor :message_echoes
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
# @!attribute [r] raw
|
|
31
|
+
# @return [Hash] The untouched `value` hash. A diagnostic escape hatch, not part of
|
|
32
|
+
# the supported API: {#message_echoes} reads `value["message_echoes"]`, a key
|
|
33
|
+
# inferred from the field's own name rather than confirmed by a published
|
|
34
|
+
# example, so a key that drifts would leave the array empty with no other trace.
|
|
35
|
+
# Read-only — it is the object `JSON.parse` returned, not a copy.
|
|
36
|
+
attr_reader :raw
|
|
37
|
+
|
|
38
|
+
def initialize(messaging_product:, metadata:, message_echoes:, contacts: [], raw: {})
|
|
28
39
|
@messaging_product = messaging_product
|
|
29
40
|
@metadata = metadata
|
|
41
|
+
@contacts = contacts
|
|
30
42
|
@message_echoes = message_echoes
|
|
43
|
+
@raw = raw
|
|
31
44
|
end
|
|
32
45
|
|
|
33
46
|
class << self
|
|
@@ -39,7 +52,9 @@ module Whatsapp
|
|
|
39
52
|
new(
|
|
40
53
|
messaging_product: data["messaging_product"],
|
|
41
54
|
metadata: data["metadata"] ? Metadata.deserialize(data["metadata"]) : nil,
|
|
42
|
-
|
|
55
|
+
contacts: Array(data["contacts"]).map { |contact_data| Contact.deserialize(contact_data) },
|
|
56
|
+
message_echoes: Array(data["message_echoes"]).map { |echo_data| Echo.deserialize(echo_data) },
|
|
57
|
+
raw: data
|
|
43
58
|
)
|
|
44
59
|
end
|
|
45
60
|
end
|
|
@@ -18,9 +18,22 @@ module Whatsapp
|
|
|
18
18
|
attr_accessor :timestamp
|
|
19
19
|
|
|
20
20
|
# @!attribute [rw] recipient_id
|
|
21
|
-
# @return [String, nil]
|
|
21
|
+
# @return [String, nil] Omitted when sent to the recipient's BSUID and their
|
|
22
|
+
# phone number is unavailable.
|
|
22
23
|
attr_accessor :recipient_id
|
|
23
24
|
|
|
25
|
+
# @!attribute [rw] recipient_user_id
|
|
26
|
+
# @return [String, nil] The recipient's business-scoped user ID (BSUID). Meta sends
|
|
27
|
+
# it on every status, however the message was addressed — a phone number in `to`
|
|
28
|
+
# does not suppress it. The single exception is a `"failed"` status for a message
|
|
29
|
+
# sent to a phone number, where it is omitted; a `"failed"` status for a
|
|
30
|
+
# BSUID-addressed message still carries it.
|
|
31
|
+
attr_accessor :recipient_user_id
|
|
32
|
+
|
|
33
|
+
# @!attribute [rw] recipient_parent_user_id
|
|
34
|
+
# @return [String, nil] The recipient's parent BSUID, present only when enrolled.
|
|
35
|
+
attr_accessor :recipient_parent_user_id
|
|
36
|
+
|
|
24
37
|
# @!attribute [rw] conversation
|
|
25
38
|
# @return [Conversation, nil]
|
|
26
39
|
attr_accessor :conversation
|
|
@@ -33,11 +46,17 @@ module Whatsapp
|
|
|
33
46
|
# @return [Array<Error>]
|
|
34
47
|
attr_accessor :errors
|
|
35
48
|
|
|
36
|
-
def initialize(
|
|
49
|
+
def initialize(
|
|
50
|
+
id:, status:, timestamp:,
|
|
51
|
+
recipient_id: nil, recipient_user_id: nil, recipient_parent_user_id: nil,
|
|
52
|
+
conversation: nil, pricing: nil, errors: []
|
|
53
|
+
)
|
|
37
54
|
@id = id
|
|
38
55
|
@status = status
|
|
39
56
|
@timestamp = timestamp
|
|
40
57
|
@recipient_id = recipient_id
|
|
58
|
+
@recipient_user_id = recipient_user_id
|
|
59
|
+
@recipient_parent_user_id = recipient_parent_user_id
|
|
41
60
|
@conversation = conversation
|
|
42
61
|
@pricing = pricing
|
|
43
62
|
@errors = errors
|
|
@@ -54,6 +73,8 @@ module Whatsapp
|
|
|
54
73
|
status: data["status"],
|
|
55
74
|
timestamp: data["timestamp"],
|
|
56
75
|
recipient_id: data["recipient_id"],
|
|
76
|
+
recipient_user_id: data["recipient_user_id"],
|
|
77
|
+
recipient_parent_user_id: data["recipient_parent_user_id"],
|
|
57
78
|
conversation: data["conversation"] ? Conversation.deserialize(data["conversation"]) : nil,
|
|
58
79
|
pricing: data["pricing"] ? Pricing.deserialize(data["pricing"]) : nil,
|
|
59
80
|
errors: Array(data["errors"]).map { |error_data| Error.deserialize(error_data) }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class UserIdUpdate
|
|
6
|
+
# A single `user_id_update[]` entry: one user's BSUID (and parent BSUID, if
|
|
7
|
+
# enrolled) changed.
|
|
8
|
+
class Entry
|
|
9
|
+
# @!attribute [rw] wa_id
|
|
10
|
+
# @return [String, nil] The user's phone number, if available.
|
|
11
|
+
attr_accessor :wa_id
|
|
12
|
+
|
|
13
|
+
# @!attribute [rw] detail
|
|
14
|
+
# @return [String, nil] Human-readable description of the change.
|
|
15
|
+
attr_accessor :detail
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] user_id
|
|
18
|
+
# @return [IdChange, nil] The user's previous and current BSUID.
|
|
19
|
+
attr_accessor :user_id
|
|
20
|
+
|
|
21
|
+
# @!attribute [rw] parent_user_id
|
|
22
|
+
# @return [IdChange, nil] The user's previous and current parent BSUID,
|
|
23
|
+
# present only when enrolled.
|
|
24
|
+
attr_accessor :parent_user_id
|
|
25
|
+
|
|
26
|
+
# @!attribute [rw] timestamp
|
|
27
|
+
# @return [String, nil]
|
|
28
|
+
attr_accessor :timestamp
|
|
29
|
+
|
|
30
|
+
def initialize(timestamp:, wa_id: nil, detail: nil, user_id: nil, parent_user_id: nil)
|
|
31
|
+
@wa_id = wa_id
|
|
32
|
+
@detail = detail
|
|
33
|
+
@user_id = user_id
|
|
34
|
+
@parent_user_id = parent_user_id
|
|
35
|
+
@timestamp = timestamp
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
# @param data [Hash] A raw `user_id_update[]` entry.
|
|
40
|
+
# @return [Entry]
|
|
41
|
+
def deserialize(data)
|
|
42
|
+
data ||= {}
|
|
43
|
+
|
|
44
|
+
new(
|
|
45
|
+
wa_id: data["wa_id"],
|
|
46
|
+
detail: data["detail"],
|
|
47
|
+
user_id: IdChange.deserialize(data["user_id"]),
|
|
48
|
+
parent_user_id: IdChange.deserialize(data["parent_user_id"]),
|
|
49
|
+
timestamp: data["timestamp"]
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class UserIdUpdate
|
|
6
|
+
# The `{previous, current}` shape shared by `user_id` and `parent_user_id` on a
|
|
7
|
+
# `user_id_update[]` entry.
|
|
8
|
+
class IdChange
|
|
9
|
+
# @!attribute [rw] previous
|
|
10
|
+
# @return [String, nil]
|
|
11
|
+
attr_accessor :previous
|
|
12
|
+
|
|
13
|
+
# @!attribute [rw] current
|
|
14
|
+
# @return [String, nil]
|
|
15
|
+
attr_accessor :current
|
|
16
|
+
|
|
17
|
+
def initialize(previous:, current:)
|
|
18
|
+
@previous = previous
|
|
19
|
+
@current = current
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# @param data [Hash, nil] The raw `user_id` or `parent_user_id` hash.
|
|
24
|
+
# @return [IdChange, nil] `nil` when the key is absent — an id change that did
|
|
25
|
+
# not happen is not a change from nothing to nothing.
|
|
26
|
+
def deserialize(data)
|
|
27
|
+
return if data.nil?
|
|
28
|
+
|
|
29
|
+
new(previous: data["previous"], current: data["current"])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# A WhatsApp user's business-scoped user ID (BSUID) changed — for example, after
|
|
6
|
+
# they changed their phone number (see the `system` message type's
|
|
7
|
+
# `user_changed_user_id` event) or were re-scoped by Meta.
|
|
8
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids
|
|
9
|
+
class UserIdUpdate
|
|
10
|
+
# @!attribute [rw] messaging_product
|
|
11
|
+
# @return [String, nil]
|
|
12
|
+
attr_accessor :messaging_product
|
|
13
|
+
|
|
14
|
+
# @!attribute [rw] metadata
|
|
15
|
+
# @return [Metadata, nil]
|
|
16
|
+
attr_accessor :metadata
|
|
17
|
+
|
|
18
|
+
# @!attribute [rw] contacts
|
|
19
|
+
# @return [Array<Contact>]
|
|
20
|
+
attr_accessor :contacts
|
|
21
|
+
|
|
22
|
+
# @!attribute [rw] updates
|
|
23
|
+
# @return [Array<Entry>]
|
|
24
|
+
attr_accessor :updates
|
|
25
|
+
|
|
26
|
+
# @!attribute [r] raw
|
|
27
|
+
# @return [Hash] The untouched `value` hash. A diagnostic escape hatch, not part
|
|
28
|
+
# of the supported API: {#updates} reads `value["user_id_update"]` — entries
|
|
29
|
+
# nested under a repeat of the field's own name, which is what Meta's published
|
|
30
|
+
# example shows — so a key that drifts would otherwise leave `updates` empty
|
|
31
|
+
# with no other trace. Reach for it when a typed accessor is unexpectedly
|
|
32
|
+
# `nil`, log what you find, and treat it as read-only: it is the object
|
|
33
|
+
# `JSON.parse` returned, not a copy.
|
|
34
|
+
attr_reader :raw
|
|
35
|
+
|
|
36
|
+
def initialize(messaging_product:, metadata:, contacts:, updates:, raw: {})
|
|
37
|
+
@messaging_product = messaging_product
|
|
38
|
+
@metadata = metadata
|
|
39
|
+
@contacts = contacts
|
|
40
|
+
@updates = updates
|
|
41
|
+
@raw = raw
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
# @param data [Hash] The raw `user_id_update` field's `value` hash.
|
|
46
|
+
# @return [UserIdUpdate]
|
|
47
|
+
def deserialize(data)
|
|
48
|
+
data ||= {}
|
|
49
|
+
|
|
50
|
+
new(
|
|
51
|
+
messaging_product: data["messaging_product"],
|
|
52
|
+
metadata: data["metadata"] ? Metadata.deserialize(data["metadata"]) : nil,
|
|
53
|
+
contacts: Array(data["contacts"]).map { |contact_data| Contact.deserialize(contact_data) },
|
|
54
|
+
updates: Array(data["user_id_update"]).map { |entry_data| Entry.deserialize(entry_data) },
|
|
55
|
+
raw: data
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class UserPreferences
|
|
6
|
+
# A single `user_preferences[]` entry: one user changed one preference.
|
|
7
|
+
class Entry
|
|
8
|
+
# @!attribute [rw] wa_id
|
|
9
|
+
# @return [String, nil] The user's phone number, omitted once they adopt a
|
|
10
|
+
# username and it is no longer shareable. Fall back to {#user_id}.
|
|
11
|
+
attr_accessor :wa_id
|
|
12
|
+
|
|
13
|
+
# @!attribute [rw] user_id
|
|
14
|
+
# @return [String, nil] The user's business-scoped user ID (BSUID).
|
|
15
|
+
attr_accessor :user_id
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] parent_user_id
|
|
18
|
+
# @return [String, nil] The user's parent BSUID, present only when enrolled.
|
|
19
|
+
attr_accessor :parent_user_id
|
|
20
|
+
|
|
21
|
+
# @!attribute [rw] detail
|
|
22
|
+
# @return [String, nil] Human-readable description of the change.
|
|
23
|
+
attr_accessor :detail
|
|
24
|
+
|
|
25
|
+
# @!attribute [rw] category
|
|
26
|
+
# @return [String, nil] The preference category. `"marketing_messages"` is the
|
|
27
|
+
# only value Meta documents; kept a raw String, never validated on the way in,
|
|
28
|
+
# so a category added later still round-trips.
|
|
29
|
+
attr_accessor :category
|
|
30
|
+
|
|
31
|
+
# @!attribute [rw] value
|
|
32
|
+
# @return [String, nil] The preference itself, e.g. `"stop"` or `"resume"`. Note
|
|
33
|
+
# the name: this sits *inside* the change's own `value` hash, so the full path
|
|
34
|
+
# reads `change.value.preferences.first.value`. Meta's naming, not ours.
|
|
35
|
+
attr_accessor :value
|
|
36
|
+
|
|
37
|
+
# @!attribute [rw] timestamp
|
|
38
|
+
# @return [Integer, String, nil] Unix seconds. Meta's example sends a number
|
|
39
|
+
# here, unlike the string timestamps on `messages`, so it is passed through
|
|
40
|
+
# as-is rather than coerced.
|
|
41
|
+
attr_accessor :timestamp
|
|
42
|
+
|
|
43
|
+
def initialize(
|
|
44
|
+
wa_id: nil, user_id: nil, parent_user_id: nil,
|
|
45
|
+
detail: nil, category: nil, value: nil, timestamp: nil
|
|
46
|
+
)
|
|
47
|
+
@wa_id = wa_id
|
|
48
|
+
@user_id = user_id
|
|
49
|
+
@parent_user_id = parent_user_id
|
|
50
|
+
@detail = detail
|
|
51
|
+
@category = category
|
|
52
|
+
@value = value
|
|
53
|
+
@timestamp = timestamp
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class << self
|
|
57
|
+
# @param data [Hash, nil] A raw `user_preferences[]` entry.
|
|
58
|
+
# @return [Entry]
|
|
59
|
+
def deserialize(data)
|
|
60
|
+
data ||= {}
|
|
61
|
+
|
|
62
|
+
new(
|
|
63
|
+
wa_id: data["wa_id"],
|
|
64
|
+
user_id: data["user_id"],
|
|
65
|
+
parent_user_id: data["parent_user_id"],
|
|
66
|
+
detail: data["detail"],
|
|
67
|
+
category: data["category"],
|
|
68
|
+
value: data["value"],
|
|
69
|
+
timestamp: data["timestamp"]
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -3,43 +3,64 @@
|
|
|
3
3
|
module Whatsapp
|
|
4
4
|
module Webhook
|
|
5
5
|
# User marketing message preference changes (e.g. a customer opting out of
|
|
6
|
-
# marketing messages)
|
|
6
|
+
# marketing messages) — one of the fields with a confirmed, JSON-example-backed
|
|
7
|
+
# schema, alongside {Messages}, {UserIdUpdate}, and {BusinessUsernameUpdates}.
|
|
7
8
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/
|
|
9
|
+
# Structurally identical to {UserIdUpdate}: the value carries the notification's
|
|
10
|
+
# own identity (`messaging_product`, `metadata`, `contacts`) plus an array of
|
|
11
|
+
# entries nested under a repeat of the field's own name.
|
|
12
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids
|
|
12
13
|
class UserPreferences
|
|
13
|
-
# @!attribute [rw]
|
|
14
|
+
# @!attribute [rw] messaging_product
|
|
14
15
|
# @return [String, nil]
|
|
15
|
-
attr_accessor :
|
|
16
|
+
attr_accessor :messaging_product
|
|
16
17
|
|
|
17
|
-
# @!attribute [rw]
|
|
18
|
-
# @return [
|
|
19
|
-
attr_accessor :
|
|
18
|
+
# @!attribute [rw] metadata
|
|
19
|
+
# @return [Metadata, nil]
|
|
20
|
+
attr_accessor :metadata
|
|
20
21
|
|
|
21
|
-
# @!attribute [rw]
|
|
22
|
-
# @return [
|
|
23
|
-
|
|
22
|
+
# @!attribute [rw] contacts
|
|
23
|
+
# @return [Array<Contact>] The users whose preferences changed, carrying the same
|
|
24
|
+
# BSUID identifiers as every other `contacts[]` entry — which is how a
|
|
25
|
+
# username-adopted user is identifiable here at all once their phone number
|
|
26
|
+
# stops being shared.
|
|
27
|
+
attr_accessor :contacts
|
|
24
28
|
|
|
25
|
-
# @!attribute [rw]
|
|
26
|
-
# @return [
|
|
27
|
-
attr_accessor :
|
|
29
|
+
# @!attribute [rw] preferences
|
|
30
|
+
# @return [Array<Entry>]
|
|
31
|
+
attr_accessor :preferences
|
|
32
|
+
|
|
33
|
+
# @!attribute [r] raw
|
|
34
|
+
# @return [Hash] The untouched `value` hash. A diagnostic escape hatch, not part
|
|
35
|
+
# of the supported API: {#preferences} reads `value["user_preferences"]` —
|
|
36
|
+
# entries nested under a repeat of the field's own name, which is what Meta's
|
|
37
|
+
# published example shows — so a key that drifts would otherwise leave
|
|
38
|
+
# `preferences` empty with no other trace. Reach for it when a typed accessor is
|
|
39
|
+
# unexpectedly `nil`, log what you find, and treat it as read-only: it is the
|
|
40
|
+
# object `JSON.parse` returned, not a copy.
|
|
41
|
+
attr_reader :raw
|
|
28
42
|
|
|
29
|
-
def initialize(
|
|
30
|
-
@
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@
|
|
43
|
+
def initialize(messaging_product:, metadata:, contacts:, preferences:, raw: {})
|
|
44
|
+
@messaging_product = messaging_product
|
|
45
|
+
@metadata = metadata
|
|
46
|
+
@contacts = contacts
|
|
47
|
+
@preferences = preferences
|
|
48
|
+
@raw = raw
|
|
34
49
|
end
|
|
35
50
|
|
|
36
51
|
class << self
|
|
37
|
-
# @param data [Hash] The raw `value` hash.
|
|
52
|
+
# @param data [Hash] The raw `user_preferences` field's `value` hash.
|
|
38
53
|
# @return [UserPreferences]
|
|
39
54
|
def deserialize(data)
|
|
40
55
|
data ||= {}
|
|
41
56
|
|
|
42
|
-
new(
|
|
57
|
+
new(
|
|
58
|
+
messaging_product: data["messaging_product"],
|
|
59
|
+
metadata: data["metadata"] ? Metadata.deserialize(data["metadata"]) : nil,
|
|
60
|
+
contacts: Array(data["contacts"]).map { |contact_data| Contact.deserialize(contact_data) },
|
|
61
|
+
preferences: Array(data["user_preferences"]).map { |entry_data| Entry.deserialize(entry_data) },
|
|
62
|
+
raw: data
|
|
63
|
+
)
|
|
43
64
|
end
|
|
44
65
|
end
|
|
45
66
|
end
|