ruby-whatsapp 0.3.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +54 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +15 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +643 -0
- data/Rakefile +12 -0
- data/lib/ruby/whatsapp/client.rb +82 -0
- data/lib/ruby/whatsapp/configuration.rb +79 -0
- data/lib/ruby/whatsapp/instrumentation.rb +34 -0
- data/lib/ruby/whatsapp/media.rb +134 -0
- data/lib/ruby/whatsapp/message_templates/button/base.rb +26 -0
- data/lib/ruby/whatsapp/message_templates/button/copy_code.rb +59 -0
- data/lib/ruby/whatsapp/message_templates/button/otp/supported_app.rb +44 -0
- data/lib/ruby/whatsapp/message_templates/button/otp.rb +138 -0
- data/lib/ruby/whatsapp/message_templates/button/phone_number.rb +45 -0
- data/lib/ruby/whatsapp/message_templates/button/quick_reply.rb +38 -0
- data/lib/ruby/whatsapp/message_templates/button/url.rb +110 -0
- data/lib/ruby/whatsapp/message_templates/button.rb +56 -0
- data/lib/ruby/whatsapp/message_templates/categories.rb +47 -0
- data/lib/ruby/whatsapp/message_templates/component/base.rb +112 -0
- data/lib/ruby/whatsapp/message_templates/component/body.rb +139 -0
- data/lib/ruby/whatsapp/message_templates/component/buttons.rb +95 -0
- data/lib/ruby/whatsapp/message_templates/component/carousel/card.rb +100 -0
- data/lib/ruby/whatsapp/message_templates/component/carousel.rb +71 -0
- data/lib/ruby/whatsapp/message_templates/component/footer.rb +99 -0
- data/lib/ruby/whatsapp/message_templates/component/header.rb +188 -0
- data/lib/ruby/whatsapp/message_templates/component/limited_time_offer.rb +57 -0
- data/lib/ruby/whatsapp/message_templates/component.rb +62 -0
- data/lib/ruby/whatsapp/message_templates/component_set.rb +221 -0
- data/lib/ruby/whatsapp/message_templates/example.rb +148 -0
- data/lib/ruby/whatsapp/message_templates/library_template/body_inputs.rb +81 -0
- data/lib/ruby/whatsapp/message_templates/library_template/button_inputs.rb +146 -0
- data/lib/ruby/whatsapp/message_templates/library_template.rb +114 -0
- data/lib/ruby/whatsapp/message_templates/parameter_formats.rb +49 -0
- data/lib/ruby/whatsapp/message_templates/placeholders.rb +86 -0
- data/lib/ruby/whatsapp/message_templates/response/collection.rb +74 -0
- data/lib/ruby/whatsapp/message_templates/response/created.rb +65 -0
- data/lib/ruby/whatsapp/message_templates/response/node.rb +152 -0
- data/lib/ruby/whatsapp/message_templates/response/paging.rb +39 -0
- data/lib/ruby/whatsapp/message_templates/response/quality_score.rb +49 -0
- data/lib/ruby/whatsapp/message_templates/response/summary.rb +68 -0
- data/lib/ruby/whatsapp/message_templates/response.rb +22 -0
- data/lib/ruby/whatsapp/message_templates/statuses.rb +66 -0
- data/lib/ruby/whatsapp/message_templates/template.rb +180 -0
- data/lib/ruby/whatsapp/message_templates/value_object.rb +62 -0
- data/lib/ruby/whatsapp/message_templates.rb +283 -0
- data/lib/ruby/whatsapp/messages/address.rb +103 -0
- data/lib/ruby/whatsapp/messages/audio.rb +64 -0
- data/lib/ruby/whatsapp/messages/base.rb +46 -0
- data/lib/ruby/whatsapp/messages/contacts/address.rb +84 -0
- data/lib/ruby/whatsapp/messages/contacts/contact.rb +78 -0
- data/lib/ruby/whatsapp/messages/contacts/email.rb +49 -0
- data/lib/ruby/whatsapp/messages/contacts/name.rb +68 -0
- data/lib/ruby/whatsapp/messages/contacts/org.rb +41 -0
- data/lib/ruby/whatsapp/messages/contacts/phone.rb +59 -0
- data/lib/ruby/whatsapp/messages/contacts/url.rb +51 -0
- data/lib/ruby/whatsapp/messages/contacts.rb +37 -0
- data/lib/ruby/whatsapp/messages/document.rb +79 -0
- data/lib/ruby/whatsapp/messages/image.rb +72 -0
- data/lib/ruby/whatsapp/messages/interactive/base.rb +15 -0
- data/lib/ruby/whatsapp/messages/interactive/body.rb +39 -0
- data/lib/ruby/whatsapp/messages/interactive/footer.rb +40 -0
- data/lib/ruby/whatsapp/messages/interactive/header.rb +102 -0
- data/lib/ruby/whatsapp/messages/interactive/list_buttons/section/row.rb +66 -0
- data/lib/ruby/whatsapp/messages/interactive/list_buttons/section.rb +64 -0
- data/lib/ruby/whatsapp/messages/interactive/list_buttons.rb +63 -0
- data/lib/ruby/whatsapp/messages/interactive/media_carousel/card/button/quick_reply.rb +50 -0
- data/lib/ruby/whatsapp/messages/interactive/media_carousel/card/button.rb +47 -0
- data/lib/ruby/whatsapp/messages/interactive/media_carousel/card.rb +78 -0
- data/lib/ruby/whatsapp/messages/interactive/media_carousel.rb +50 -0
- data/lib/ruby/whatsapp/messages/interactive/product_carousel/card.rb +55 -0
- data/lib/ruby/whatsapp/messages/interactive/product_carousel.rb +50 -0
- data/lib/ruby/whatsapp/messages/interactive/reply_buttons/button.rb +59 -0
- data/lib/ruby/whatsapp/messages/interactive/reply_buttons.rb +47 -0
- data/lib/ruby/whatsapp/messages/interactive/url_button.rb +68 -0
- data/lib/ruby/whatsapp/messages/interactive.rb +112 -0
- data/lib/ruby/whatsapp/messages/location.rb +67 -0
- data/lib/ruby/whatsapp/messages/location_request.rb +41 -0
- data/lib/ruby/whatsapp/messages/mark_message_as_read.rb +42 -0
- data/lib/ruby/whatsapp/messages/reaction.rb +61 -0
- data/lib/ruby/whatsapp/messages/response/contacts.rb +36 -0
- data/lib/ruby/whatsapp/messages/response/messages.rb +27 -0
- data/lib/ruby/whatsapp/messages/response.rb +49 -0
- data/lib/ruby/whatsapp/messages/sticker.rb +62 -0
- data/lib/ruby/whatsapp/messages/template/component.rb +67 -0
- data/lib/ruby/whatsapp/messages/template/language.rb +55 -0
- data/lib/ruby/whatsapp/messages/template/parameter.rb +113 -0
- data/lib/ruby/whatsapp/messages/template.rb +62 -0
- data/lib/ruby/whatsapp/messages/text.rb +43 -0
- data/lib/ruby/whatsapp/messages/video.rb +68 -0
- data/lib/ruby/whatsapp/messages.rb +116 -0
- data/lib/ruby/whatsapp/railtie.rb +14 -0
- data/lib/ruby/whatsapp/request_error.rb +5 -0
- data/lib/ruby/whatsapp/response_handling.rb +44 -0
- data/lib/ruby/whatsapp/utils/language_codes.rb +96 -0
- data/lib/ruby/whatsapp/version.rb +5 -0
- data/lib/ruby/whatsapp/webhook/account_alerts.rb +58 -0
- data/lib/ruby/whatsapp/webhook/account_review_update.rb +27 -0
- data/lib/ruby/whatsapp/webhook/account_update/ban_info.rb +33 -0
- data/lib/ruby/whatsapp/webhook/account_update.rb +46 -0
- data/lib/ruby/whatsapp/webhook/automatic_events.rb +43 -0
- data/lib/ruby/whatsapp/webhook/business_capability_update.rb +36 -0
- data/lib/ruby/whatsapp/webhook/change.rb +61 -0
- data/lib/ruby/whatsapp/webhook/contact.rb +36 -0
- data/lib/ruby/whatsapp/webhook/entry.rb +35 -0
- data/lib/ruby/whatsapp/webhook/error.rb +47 -0
- data/lib/ruby/whatsapp/webhook/history.rb +54 -0
- data/lib/ruby/whatsapp/webhook/installer.rb +61 -0
- data/lib/ruby/whatsapp/webhook/message/audio.rb +28 -0
- data/lib/ruby/whatsapp/webhook/message/base.rb +61 -0
- data/lib/ruby/whatsapp/webhook/message/button.rb +33 -0
- data/lib/ruby/whatsapp/webhook/message/contacts/address.rb +68 -0
- data/lib/ruby/whatsapp/webhook/message/contacts/contact.rb +68 -0
- data/lib/ruby/whatsapp/webhook/message/contacts/email.rb +35 -0
- data/lib/ruby/whatsapp/webhook/message/contacts/name.rb +62 -0
- data/lib/ruby/whatsapp/webhook/message/contacts/org.rb +40 -0
- data/lib/ruby/whatsapp/webhook/message/contacts/phone.rb +40 -0
- data/lib/ruby/whatsapp/webhook/message/contacts/url.rb +35 -0
- data/lib/ruby/whatsapp/webhook/message/contacts.rb +31 -0
- data/lib/ruby/whatsapp/webhook/message/context.rb +49 -0
- data/lib/ruby/whatsapp/webhook/message/document.rb +32 -0
- data/lib/ruby/whatsapp/webhook/message/image.rb +18 -0
- data/lib/ruby/whatsapp/webhook/message/interactive.rb +53 -0
- data/lib/ruby/whatsapp/webhook/message/location.rb +49 -0
- data/lib/ruby/whatsapp/webhook/message/media.rb +54 -0
- data/lib/ruby/whatsapp/webhook/message/order/product_item.rb +50 -0
- data/lib/ruby/whatsapp/webhook/message/order.rb +43 -0
- data/lib/ruby/whatsapp/webhook/message/reaction.rb +37 -0
- data/lib/ruby/whatsapp/webhook/message/referral.rb +80 -0
- data/lib/ruby/whatsapp/webhook/message/sticker.rb +32 -0
- data/lib/ruby/whatsapp/webhook/message/system.rb +49 -0
- data/lib/ruby/whatsapp/webhook/message/text.rb +28 -0
- data/lib/ruby/whatsapp/webhook/message/unknown.rb +39 -0
- data/lib/ruby/whatsapp/webhook/message/video.rb +18 -0
- data/lib/ruby/whatsapp/webhook/message.rb +39 -0
- data/lib/ruby/whatsapp/webhook/message_template_components_update.rb +53 -0
- data/lib/ruby/whatsapp/webhook/message_template_quality_update.rb +59 -0
- data/lib/ruby/whatsapp/webhook/message_template_status_update.rb +54 -0
- data/lib/ruby/whatsapp/webhook/messages.rb +55 -0
- data/lib/ruby/whatsapp/webhook/metadata.rb +35 -0
- data/lib/ruby/whatsapp/webhook/notification.rb +36 -0
- data/lib/ruby/whatsapp/webhook/partner_solutions.rb +36 -0
- data/lib/ruby/whatsapp/webhook/payment_configuration_update.rb +51 -0
- data/lib/ruby/whatsapp/webhook/phone_number_name_update.rb +51 -0
- data/lib/ruby/whatsapp/webhook/phone_number_quality_update.rb +45 -0
- data/lib/ruby/whatsapp/webhook/security.rb +41 -0
- data/lib/ruby/whatsapp/webhook/signature.rb +28 -0
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync/state_sync.rb +38 -0
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync.rb +33 -0
- data/lib/ruby/whatsapp/webhook/smb_message_echoes.rb +48 -0
- data/lib/ruby/whatsapp/webhook/status/conversation.rb +42 -0
- data/lib/ruby/whatsapp/webhook/status/pricing.rb +38 -0
- data/lib/ruby/whatsapp/webhook/status.rb +65 -0
- data/lib/ruby/whatsapp/webhook/template_category_update.rb +64 -0
- data/lib/ruby/whatsapp/webhook/templates/webhooks_controller.rb.tt +31 -0
- data/lib/ruby/whatsapp/webhook/unknown_field.rb +37 -0
- data/lib/ruby/whatsapp/webhook/user_preferences.rb +47 -0
- data/lib/ruby/whatsapp/webhook/verification.rb +27 -0
- data/lib/ruby/whatsapp/webhook.rb +8 -0
- data/lib/ruby/whatsapp.rb +49 -0
- data/lib/tasks/whatsapp.rake +10 -0
- data/sig/ruby/whatsapp.rbs +6 -0
- metadata +267 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# The `messages` webhook field's `value` payload — the only field with a
|
|
6
|
+
# confirmed, JSON-example-backed schema in Meta's docs. Carries inbound
|
|
7
|
+
# messages and/or delivery status updates for one of your business phone numbers.
|
|
8
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
9
|
+
class Messages
|
|
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] messages
|
|
23
|
+
# @return [Array<Message::Base>]
|
|
24
|
+
attr_accessor :messages
|
|
25
|
+
|
|
26
|
+
# @!attribute [rw] statuses
|
|
27
|
+
# @return [Array<Status>]
|
|
28
|
+
attr_accessor :statuses
|
|
29
|
+
|
|
30
|
+
def initialize(messaging_product:, metadata:, contacts:, messages:, statuses:)
|
|
31
|
+
@messaging_product = messaging_product
|
|
32
|
+
@metadata = metadata
|
|
33
|
+
@contacts = contacts
|
|
34
|
+
@messages = messages
|
|
35
|
+
@statuses = statuses
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
# @param data [Hash] The raw `messages` field's `value` hash.
|
|
40
|
+
# @return [Messages]
|
|
41
|
+
def deserialize(data)
|
|
42
|
+
data ||= {}
|
|
43
|
+
|
|
44
|
+
new(
|
|
45
|
+
messaging_product: data["messaging_product"],
|
|
46
|
+
metadata: data["metadata"] ? Metadata.deserialize(data["metadata"]) : nil,
|
|
47
|
+
contacts: Array(data["contacts"]).map { |contact_data| Contact.deserialize(contact_data) },
|
|
48
|
+
messages: Array(data["messages"]).map { |message_data| Message.deserialize(message_data) },
|
|
49
|
+
statuses: Array(data["statuses"]).map { |status_data| Status.deserialize(status_data) }
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# The `metadata` object present on most webhook `value` payloads, identifying
|
|
6
|
+
# which of your business phone numbers the notification is about.
|
|
7
|
+
class Metadata
|
|
8
|
+
# @!attribute [rw] display_phone_number
|
|
9
|
+
# @return [String, nil]
|
|
10
|
+
attr_accessor :display_phone_number
|
|
11
|
+
|
|
12
|
+
# @!attribute [rw] phone_number_id
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :phone_number_id
|
|
15
|
+
|
|
16
|
+
def initialize(display_phone_number:, phone_number_id:)
|
|
17
|
+
@display_phone_number = display_phone_number
|
|
18
|
+
@phone_number_id = phone_number_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# @param data [Hash] The raw `metadata` hash.
|
|
23
|
+
# @return [Metadata]
|
|
24
|
+
def deserialize(data)
|
|
25
|
+
data ||= {}
|
|
26
|
+
|
|
27
|
+
new(
|
|
28
|
+
display_phone_number: data["display_phone_number"],
|
|
29
|
+
phone_number_id: data["phone_number_id"]
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Top-level deserialization entry point for an incoming webhook POST body.
|
|
6
|
+
# @example
|
|
7
|
+
# Whatsapp::Webhook::Notification.deserialize(JSON.parse(request.body.read))
|
|
8
|
+
class Notification
|
|
9
|
+
# @!attribute [rw] object
|
|
10
|
+
# @return [String]
|
|
11
|
+
attr_accessor :object
|
|
12
|
+
|
|
13
|
+
# @!attribute [rw] entry
|
|
14
|
+
# @return [Array<Entry>]
|
|
15
|
+
attr_accessor :entry
|
|
16
|
+
|
|
17
|
+
def initialize(object:, entry:)
|
|
18
|
+
@object = object
|
|
19
|
+
@entry = entry
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# @param data [Hash] The parsed JSON request body.
|
|
24
|
+
# @return [Notification]
|
|
25
|
+
def deserialize(data)
|
|
26
|
+
data ||= {}
|
|
27
|
+
|
|
28
|
+
new(
|
|
29
|
+
object: data["object"],
|
|
30
|
+
entry: Array(data["entry"]).map { |entry_data| Entry.deserialize(entry_data) }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Multi-Partner Solution status changes.
|
|
6
|
+
#
|
|
7
|
+
# Best-effort schema: Meta's public docs describe this field in one line with
|
|
8
|
+
# no published JSON example. Validate against a real payload (App Dashboard
|
|
9
|
+
# "send test payload") before relying on these attribute names in production.
|
|
10
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
11
|
+
class PartnerSolutions
|
|
12
|
+
# @!attribute [rw] solution_id
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :solution_id
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] event
|
|
17
|
+
# @return [String, nil]
|
|
18
|
+
attr_accessor :event
|
|
19
|
+
|
|
20
|
+
def initialize(solution_id:, event:)
|
|
21
|
+
@solution_id = solution_id
|
|
22
|
+
@event = event
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# @param data [Hash] The raw `value` hash.
|
|
27
|
+
# @return [PartnerSolutions]
|
|
28
|
+
def deserialize(data)
|
|
29
|
+
data ||= {}
|
|
30
|
+
|
|
31
|
+
new(solution_id: data["solution_id"], event: data["event"])
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Payment configuration changes (India/Brazil).
|
|
6
|
+
#
|
|
7
|
+
# Best-effort schema: Meta's public docs describe this field in one line with
|
|
8
|
+
# no published JSON example. Validate against a real payload (App Dashboard
|
|
9
|
+
# "send test payload") before relying on these attribute names in production.
|
|
10
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
11
|
+
class PaymentConfigurationUpdate
|
|
12
|
+
# @!attribute [rw] configuration_name
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :configuration_name
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] provider_name
|
|
17
|
+
# @return [String, nil]
|
|
18
|
+
attr_accessor :provider_name
|
|
19
|
+
|
|
20
|
+
# @!attribute [rw] provider_mid
|
|
21
|
+
# @return [String, nil]
|
|
22
|
+
attr_accessor :provider_mid
|
|
23
|
+
|
|
24
|
+
# @!attribute [rw] status
|
|
25
|
+
# @return [String, nil]
|
|
26
|
+
attr_accessor :status
|
|
27
|
+
|
|
28
|
+
def initialize(configuration_name:, provider_name:, provider_mid:, status:)
|
|
29
|
+
@configuration_name = configuration_name
|
|
30
|
+
@provider_name = provider_name
|
|
31
|
+
@provider_mid = provider_mid
|
|
32
|
+
@status = status
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
# @param data [Hash] The raw `value` hash.
|
|
37
|
+
# @return [PaymentConfigurationUpdate]
|
|
38
|
+
def deserialize(data)
|
|
39
|
+
data ||= {}
|
|
40
|
+
|
|
41
|
+
new(
|
|
42
|
+
configuration_name: data["configuration_name"],
|
|
43
|
+
provider_name: data["provider_name"],
|
|
44
|
+
provider_mid: data["provider_mid"],
|
|
45
|
+
status: data["status"]
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Display name verification outcomes for a business phone number.
|
|
6
|
+
#
|
|
7
|
+
# Best-effort schema: Meta's public docs describe this field in one line with
|
|
8
|
+
# no published JSON example. Validate against a real payload (App Dashboard
|
|
9
|
+
# "send test payload") before relying on these attribute names in production.
|
|
10
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
11
|
+
class PhoneNumberNameUpdate
|
|
12
|
+
# @!attribute [rw] phone_number
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :phone_number
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] decision
|
|
17
|
+
# @return [String, nil] e.g. `"APPROVED"`, `"REJECTED"`.
|
|
18
|
+
attr_accessor :decision
|
|
19
|
+
|
|
20
|
+
# @!attribute [rw] requested_verified_name
|
|
21
|
+
# @return [String, nil]
|
|
22
|
+
attr_accessor :requested_verified_name
|
|
23
|
+
|
|
24
|
+
# @!attribute [rw] rejection_reason
|
|
25
|
+
# @return [String, nil]
|
|
26
|
+
attr_accessor :rejection_reason
|
|
27
|
+
|
|
28
|
+
def initialize(phone_number:, decision:, requested_verified_name:, rejection_reason: nil)
|
|
29
|
+
@phone_number = phone_number
|
|
30
|
+
@decision = decision
|
|
31
|
+
@requested_verified_name = requested_verified_name
|
|
32
|
+
@rejection_reason = rejection_reason
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
# @param data [Hash] The raw `value` hash.
|
|
37
|
+
# @return [PhoneNumberNameUpdate]
|
|
38
|
+
def deserialize(data)
|
|
39
|
+
data ||= {}
|
|
40
|
+
|
|
41
|
+
new(
|
|
42
|
+
phone_number: data["phone_number"],
|
|
43
|
+
decision: data["decision"],
|
|
44
|
+
requested_verified_name: data["requested_verified_name"],
|
|
45
|
+
rejection_reason: data["rejection_reason"]
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Business phone throughput/quality level changes.
|
|
6
|
+
#
|
|
7
|
+
# Best-effort schema: Meta's public docs describe this field in one line with
|
|
8
|
+
# no published JSON example. Validate against a real payload (App Dashboard
|
|
9
|
+
# "send test payload") before relying on these attribute names in production.
|
|
10
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
11
|
+
class PhoneNumberQualityUpdate
|
|
12
|
+
# @!attribute [rw] display_phone_number
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :display_phone_number
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] event
|
|
17
|
+
# @return [String, nil] e.g. `"UPGRADE"`, `"DOWNGRADE"`.
|
|
18
|
+
attr_accessor :event
|
|
19
|
+
|
|
20
|
+
# @!attribute [rw] current_limit
|
|
21
|
+
# @return [String, nil] e.g. `"TIER_50"`, `"TIER_250"`, `"TIER_1K"`.
|
|
22
|
+
attr_accessor :current_limit
|
|
23
|
+
|
|
24
|
+
def initialize(display_phone_number:, event:, current_limit:)
|
|
25
|
+
@display_phone_number = display_phone_number
|
|
26
|
+
@event = event
|
|
27
|
+
@current_limit = current_limit
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# @param data [Hash] The raw `value` hash.
|
|
32
|
+
# @return [PhoneNumberQualityUpdate]
|
|
33
|
+
def deserialize(data)
|
|
34
|
+
data ||= {}
|
|
35
|
+
|
|
36
|
+
new(
|
|
37
|
+
display_phone_number: data["display_phone_number"],
|
|
38
|
+
event: data["event"],
|
|
39
|
+
current_limit: data["current_limit"]
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Phone number security settings modifications (e.g. two-step verification changes).
|
|
6
|
+
#
|
|
7
|
+
# Best-effort schema: Meta's public docs describe this field in one line with
|
|
8
|
+
# no published JSON example. Validate against a real payload (App Dashboard
|
|
9
|
+
# "send test payload") before relying on these attribute names in production.
|
|
10
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
11
|
+
class Security
|
|
12
|
+
# @!attribute [rw] display_phone_number
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :display_phone_number
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] event
|
|
17
|
+
# @return [String, nil]
|
|
18
|
+
attr_accessor :event
|
|
19
|
+
|
|
20
|
+
# @!attribute [rw] requester
|
|
21
|
+
# @return [String, nil]
|
|
22
|
+
attr_accessor :requester
|
|
23
|
+
|
|
24
|
+
def initialize(display_phone_number:, event:, requester: nil)
|
|
25
|
+
@display_phone_number = display_phone_number
|
|
26
|
+
@event = event
|
|
27
|
+
@requester = requester
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# @param data [Hash] The raw `value` hash.
|
|
32
|
+
# @return [Security]
|
|
33
|
+
def deserialize(data)
|
|
34
|
+
data ||= {}
|
|
35
|
+
|
|
36
|
+
new(display_phone_number: data["display_phone_number"], event: data["event"], requester: data["requester"])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Verifies the `X-Hub-Signature-256` header Meta attaches to every webhook POST,
|
|
6
|
+
# an HMAC-SHA256 of the raw request body keyed by the app secret.
|
|
7
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
8
|
+
class Signature
|
|
9
|
+
PREFIX = "sha256="
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# @param payload [String] The raw (unparsed) request body.
|
|
13
|
+
# @param header [String, nil] The `X-Hub-Signature-256` header value.
|
|
14
|
+
# @param app_secret [String, nil] The secret to verify against. Defaults to the
|
|
15
|
+
# globally configured secret; pass an explicit value for multi-tenant apps where
|
|
16
|
+
# each account has its own Meta App (and therefore its own secret).
|
|
17
|
+
# @return [Boolean] Whether the header matches the computed signature.
|
|
18
|
+
def valid?(payload:, header:, app_secret: Whatsapp.configuration.app_secret)
|
|
19
|
+
return false if header.nil? || header.empty?
|
|
20
|
+
return false if app_secret.nil? || app_secret.empty?
|
|
21
|
+
|
|
22
|
+
expected = PREFIX + OpenSSL::HMAC.hexdigest("SHA256", app_secret, payload)
|
|
23
|
+
ActiveSupport::SecurityUtils.secure_compare(expected, header)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class SmbAppStateSync
|
|
6
|
+
# A single contact-sync entry within an `smb_app_state_sync` notification.
|
|
7
|
+
class StateSync
|
|
8
|
+
# @!attribute [rw] type
|
|
9
|
+
# @return [String, nil]
|
|
10
|
+
attr_accessor :type
|
|
11
|
+
|
|
12
|
+
# @!attribute [rw] action
|
|
13
|
+
# @return [String, nil] e.g. `"add"`, `"remove"`, `"update"`.
|
|
14
|
+
attr_accessor :action
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] contact
|
|
17
|
+
# @return [Hash] Left untyped — Meta's docs don't publish this entry's schema.
|
|
18
|
+
attr_accessor :contact
|
|
19
|
+
|
|
20
|
+
def initialize(type:, action:, contact:)
|
|
21
|
+
@type = type
|
|
22
|
+
@action = action
|
|
23
|
+
@contact = contact
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
# @param data [Hash] A raw `state_sync[]` entry.
|
|
28
|
+
# @return [StateSync]
|
|
29
|
+
def deserialize(data)
|
|
30
|
+
data ||= {}
|
|
31
|
+
|
|
32
|
+
new(type: data["type"], action: data["action"], contact: data["contact"] || {})
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Contact synchronization for onboarded WhatsApp Business app users.
|
|
6
|
+
#
|
|
7
|
+
# Best-effort schema: Meta's public docs describe this field in one line with
|
|
8
|
+
# no published JSON example; each entry's `contact` shape isn't confidently
|
|
9
|
+
# typeable from that description alone, so it's kept as a raw hash rather
|
|
10
|
+
# than guessing further. Validate against a real payload before relying on
|
|
11
|
+
# this in production.
|
|
12
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
13
|
+
class SmbAppStateSync
|
|
14
|
+
# @!attribute [rw] state_sync
|
|
15
|
+
# @return [Array<StateSync>]
|
|
16
|
+
attr_accessor :state_sync
|
|
17
|
+
|
|
18
|
+
def initialize(state_sync:)
|
|
19
|
+
@state_sync = state_sync
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# @param data [Hash] The raw `value` hash.
|
|
24
|
+
# @return [SmbAppStateSync]
|
|
25
|
+
def deserialize(data)
|
|
26
|
+
data ||= {}
|
|
27
|
+
|
|
28
|
+
new(state_sync: Array(data["state_sync"]).map { |entry| StateSync.deserialize(entry) })
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# Messages sent via the WhatsApp Business App or a linked device, echoed back
|
|
6
|
+
# so a Cloud API integration stays in sync with what the business itself sent.
|
|
7
|
+
#
|
|
8
|
+
# Best-effort schema: Meta's public docs describe this field in one line with
|
|
9
|
+
# no published JSON example, but do state these mirror the standard inbound
|
|
10
|
+
# `messages` shape — hence reusing {Message.deserialize} here. The exact key
|
|
11
|
+
# name (`message_echoes`) is a best-effort guess; validate against a real
|
|
12
|
+
# payload before relying on it in production.
|
|
13
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
14
|
+
class SmbMessageEchoes
|
|
15
|
+
# @!attribute [rw] messaging_product
|
|
16
|
+
# @return [String, nil]
|
|
17
|
+
attr_accessor :messaging_product
|
|
18
|
+
|
|
19
|
+
# @!attribute [rw] metadata
|
|
20
|
+
# @return [Metadata, nil]
|
|
21
|
+
attr_accessor :metadata
|
|
22
|
+
|
|
23
|
+
# @!attribute [rw] message_echoes
|
|
24
|
+
# @return [Array<Message::Base>]
|
|
25
|
+
attr_accessor :message_echoes
|
|
26
|
+
|
|
27
|
+
def initialize(messaging_product:, metadata:, message_echoes:)
|
|
28
|
+
@messaging_product = messaging_product
|
|
29
|
+
@metadata = metadata
|
|
30
|
+
@message_echoes = message_echoes
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
# @param data [Hash] The raw `value` hash.
|
|
35
|
+
# @return [SmbMessageEchoes]
|
|
36
|
+
def deserialize(data)
|
|
37
|
+
data ||= {}
|
|
38
|
+
|
|
39
|
+
new(
|
|
40
|
+
messaging_product: data["messaging_product"],
|
|
41
|
+
metadata: data["metadata"] ? Metadata.deserialize(data["metadata"]) : nil,
|
|
42
|
+
message_echoes: Array(data["message_echoes"]).map { |echo_data| Message.deserialize(echo_data) }
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class Status
|
|
6
|
+
# The conversation window a status update was billed/attributed to.
|
|
7
|
+
class Conversation
|
|
8
|
+
# @!attribute [rw] id
|
|
9
|
+
# @return [String, nil]
|
|
10
|
+
attr_accessor :id
|
|
11
|
+
|
|
12
|
+
# @!attribute [rw] origin_type
|
|
13
|
+
# @return [String, nil] e.g. `"service"`, `"marketing"`, `"utility"`, `"authentication"`.
|
|
14
|
+
attr_accessor :origin_type
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] expiration_timestamp
|
|
17
|
+
# @return [String, nil]
|
|
18
|
+
attr_accessor :expiration_timestamp
|
|
19
|
+
|
|
20
|
+
def initialize(id:, origin_type:, expiration_timestamp: nil)
|
|
21
|
+
@id = id
|
|
22
|
+
@origin_type = origin_type
|
|
23
|
+
@expiration_timestamp = expiration_timestamp
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
# @param data [Hash] The raw `conversation` hash.
|
|
28
|
+
# @return [Conversation]
|
|
29
|
+
def deserialize(data)
|
|
30
|
+
data ||= {}
|
|
31
|
+
|
|
32
|
+
new(
|
|
33
|
+
id: data["id"],
|
|
34
|
+
origin_type: data.dig("origin", "type"),
|
|
35
|
+
expiration_timestamp: data["expiration_timestamp"]
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class Status
|
|
6
|
+
# The billing details attached to a status update.
|
|
7
|
+
class Pricing
|
|
8
|
+
# @!attribute [rw] billable
|
|
9
|
+
# @return [Boolean, nil]
|
|
10
|
+
attr_accessor :billable
|
|
11
|
+
|
|
12
|
+
# @!attribute [rw] pricing_model
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :pricing_model
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] category
|
|
17
|
+
# @return [String, nil] e.g. `"service"`, `"marketing"`, `"utility"`, `"authentication"`.
|
|
18
|
+
attr_accessor :category
|
|
19
|
+
|
|
20
|
+
def initialize(billable:, pricing_model:, category:)
|
|
21
|
+
@billable = billable
|
|
22
|
+
@pricing_model = pricing_model
|
|
23
|
+
@category = category
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
# @param data [Hash] The raw `pricing` hash.
|
|
28
|
+
# @return [Pricing]
|
|
29
|
+
def deserialize(data)
|
|
30
|
+
data ||= {}
|
|
31
|
+
|
|
32
|
+
new(billable: data["billable"], pricing_model: data["pricing_model"], category: data["category"])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
# A single `statuses[]` entry from the `messages` webhook field — a delivery
|
|
6
|
+
# status update (sent/delivered/read/failed) for a message you sent.
|
|
7
|
+
class Status
|
|
8
|
+
# @!attribute [rw] id
|
|
9
|
+
# @return [String, nil] The wamid of the message this status is about.
|
|
10
|
+
attr_accessor :id
|
|
11
|
+
|
|
12
|
+
# @!attribute [rw] status
|
|
13
|
+
# @return [String, nil] `"sent"`, `"delivered"`, `"read"`, or `"failed"`.
|
|
14
|
+
attr_accessor :status
|
|
15
|
+
|
|
16
|
+
# @!attribute [rw] timestamp
|
|
17
|
+
# @return [String, nil]
|
|
18
|
+
attr_accessor :timestamp
|
|
19
|
+
|
|
20
|
+
# @!attribute [rw] recipient_id
|
|
21
|
+
# @return [String, nil]
|
|
22
|
+
attr_accessor :recipient_id
|
|
23
|
+
|
|
24
|
+
# @!attribute [rw] conversation
|
|
25
|
+
# @return [Conversation, nil]
|
|
26
|
+
attr_accessor :conversation
|
|
27
|
+
|
|
28
|
+
# @!attribute [rw] pricing
|
|
29
|
+
# @return [Pricing, nil]
|
|
30
|
+
attr_accessor :pricing
|
|
31
|
+
|
|
32
|
+
# @!attribute [rw] errors
|
|
33
|
+
# @return [Array<Error>]
|
|
34
|
+
attr_accessor :errors
|
|
35
|
+
|
|
36
|
+
def initialize(id:, status:, timestamp:, recipient_id:, conversation: nil, pricing: nil, errors: [])
|
|
37
|
+
@id = id
|
|
38
|
+
@status = status
|
|
39
|
+
@timestamp = timestamp
|
|
40
|
+
@recipient_id = recipient_id
|
|
41
|
+
@conversation = conversation
|
|
42
|
+
@pricing = pricing
|
|
43
|
+
@errors = errors
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
# @param data [Hash] A raw `statuses[]` entry.
|
|
48
|
+
# @return [Status]
|
|
49
|
+
def deserialize(data)
|
|
50
|
+
data ||= {}
|
|
51
|
+
|
|
52
|
+
new(
|
|
53
|
+
id: data["id"],
|
|
54
|
+
status: data["status"],
|
|
55
|
+
timestamp: data["timestamp"],
|
|
56
|
+
recipient_id: data["recipient_id"],
|
|
57
|
+
conversation: data["conversation"] ? Conversation.deserialize(data["conversation"]) : nil,
|
|
58
|
+
pricing: data["pricing"] ? Pricing.deserialize(data["pricing"]) : nil,
|
|
59
|
+
errors: Array(data["errors"]).map { |error_data| Error.deserialize(error_data) }
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|