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.
Files changed (166) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +54 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +15 -0
  6. data/CODE_OF_CONDUCT.md +132 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +643 -0
  9. data/Rakefile +12 -0
  10. data/lib/ruby/whatsapp/client.rb +82 -0
  11. data/lib/ruby/whatsapp/configuration.rb +79 -0
  12. data/lib/ruby/whatsapp/instrumentation.rb +34 -0
  13. data/lib/ruby/whatsapp/media.rb +134 -0
  14. data/lib/ruby/whatsapp/message_templates/button/base.rb +26 -0
  15. data/lib/ruby/whatsapp/message_templates/button/copy_code.rb +59 -0
  16. data/lib/ruby/whatsapp/message_templates/button/otp/supported_app.rb +44 -0
  17. data/lib/ruby/whatsapp/message_templates/button/otp.rb +138 -0
  18. data/lib/ruby/whatsapp/message_templates/button/phone_number.rb +45 -0
  19. data/lib/ruby/whatsapp/message_templates/button/quick_reply.rb +38 -0
  20. data/lib/ruby/whatsapp/message_templates/button/url.rb +110 -0
  21. data/lib/ruby/whatsapp/message_templates/button.rb +56 -0
  22. data/lib/ruby/whatsapp/message_templates/categories.rb +47 -0
  23. data/lib/ruby/whatsapp/message_templates/component/base.rb +112 -0
  24. data/lib/ruby/whatsapp/message_templates/component/body.rb +139 -0
  25. data/lib/ruby/whatsapp/message_templates/component/buttons.rb +95 -0
  26. data/lib/ruby/whatsapp/message_templates/component/carousel/card.rb +100 -0
  27. data/lib/ruby/whatsapp/message_templates/component/carousel.rb +71 -0
  28. data/lib/ruby/whatsapp/message_templates/component/footer.rb +99 -0
  29. data/lib/ruby/whatsapp/message_templates/component/header.rb +188 -0
  30. data/lib/ruby/whatsapp/message_templates/component/limited_time_offer.rb +57 -0
  31. data/lib/ruby/whatsapp/message_templates/component.rb +62 -0
  32. data/lib/ruby/whatsapp/message_templates/component_set.rb +221 -0
  33. data/lib/ruby/whatsapp/message_templates/example.rb +148 -0
  34. data/lib/ruby/whatsapp/message_templates/library_template/body_inputs.rb +81 -0
  35. data/lib/ruby/whatsapp/message_templates/library_template/button_inputs.rb +146 -0
  36. data/lib/ruby/whatsapp/message_templates/library_template.rb +114 -0
  37. data/lib/ruby/whatsapp/message_templates/parameter_formats.rb +49 -0
  38. data/lib/ruby/whatsapp/message_templates/placeholders.rb +86 -0
  39. data/lib/ruby/whatsapp/message_templates/response/collection.rb +74 -0
  40. data/lib/ruby/whatsapp/message_templates/response/created.rb +65 -0
  41. data/lib/ruby/whatsapp/message_templates/response/node.rb +152 -0
  42. data/lib/ruby/whatsapp/message_templates/response/paging.rb +39 -0
  43. data/lib/ruby/whatsapp/message_templates/response/quality_score.rb +49 -0
  44. data/lib/ruby/whatsapp/message_templates/response/summary.rb +68 -0
  45. data/lib/ruby/whatsapp/message_templates/response.rb +22 -0
  46. data/lib/ruby/whatsapp/message_templates/statuses.rb +66 -0
  47. data/lib/ruby/whatsapp/message_templates/template.rb +180 -0
  48. data/lib/ruby/whatsapp/message_templates/value_object.rb +62 -0
  49. data/lib/ruby/whatsapp/message_templates.rb +283 -0
  50. data/lib/ruby/whatsapp/messages/address.rb +103 -0
  51. data/lib/ruby/whatsapp/messages/audio.rb +64 -0
  52. data/lib/ruby/whatsapp/messages/base.rb +46 -0
  53. data/lib/ruby/whatsapp/messages/contacts/address.rb +84 -0
  54. data/lib/ruby/whatsapp/messages/contacts/contact.rb +78 -0
  55. data/lib/ruby/whatsapp/messages/contacts/email.rb +49 -0
  56. data/lib/ruby/whatsapp/messages/contacts/name.rb +68 -0
  57. data/lib/ruby/whatsapp/messages/contacts/org.rb +41 -0
  58. data/lib/ruby/whatsapp/messages/contacts/phone.rb +59 -0
  59. data/lib/ruby/whatsapp/messages/contacts/url.rb +51 -0
  60. data/lib/ruby/whatsapp/messages/contacts.rb +37 -0
  61. data/lib/ruby/whatsapp/messages/document.rb +79 -0
  62. data/lib/ruby/whatsapp/messages/image.rb +72 -0
  63. data/lib/ruby/whatsapp/messages/interactive/base.rb +15 -0
  64. data/lib/ruby/whatsapp/messages/interactive/body.rb +39 -0
  65. data/lib/ruby/whatsapp/messages/interactive/footer.rb +40 -0
  66. data/lib/ruby/whatsapp/messages/interactive/header.rb +102 -0
  67. data/lib/ruby/whatsapp/messages/interactive/list_buttons/section/row.rb +66 -0
  68. data/lib/ruby/whatsapp/messages/interactive/list_buttons/section.rb +64 -0
  69. data/lib/ruby/whatsapp/messages/interactive/list_buttons.rb +63 -0
  70. data/lib/ruby/whatsapp/messages/interactive/media_carousel/card/button/quick_reply.rb +50 -0
  71. data/lib/ruby/whatsapp/messages/interactive/media_carousel/card/button.rb +47 -0
  72. data/lib/ruby/whatsapp/messages/interactive/media_carousel/card.rb +78 -0
  73. data/lib/ruby/whatsapp/messages/interactive/media_carousel.rb +50 -0
  74. data/lib/ruby/whatsapp/messages/interactive/product_carousel/card.rb +55 -0
  75. data/lib/ruby/whatsapp/messages/interactive/product_carousel.rb +50 -0
  76. data/lib/ruby/whatsapp/messages/interactive/reply_buttons/button.rb +59 -0
  77. data/lib/ruby/whatsapp/messages/interactive/reply_buttons.rb +47 -0
  78. data/lib/ruby/whatsapp/messages/interactive/url_button.rb +68 -0
  79. data/lib/ruby/whatsapp/messages/interactive.rb +112 -0
  80. data/lib/ruby/whatsapp/messages/location.rb +67 -0
  81. data/lib/ruby/whatsapp/messages/location_request.rb +41 -0
  82. data/lib/ruby/whatsapp/messages/mark_message_as_read.rb +42 -0
  83. data/lib/ruby/whatsapp/messages/reaction.rb +61 -0
  84. data/lib/ruby/whatsapp/messages/response/contacts.rb +36 -0
  85. data/lib/ruby/whatsapp/messages/response/messages.rb +27 -0
  86. data/lib/ruby/whatsapp/messages/response.rb +49 -0
  87. data/lib/ruby/whatsapp/messages/sticker.rb +62 -0
  88. data/lib/ruby/whatsapp/messages/template/component.rb +67 -0
  89. data/lib/ruby/whatsapp/messages/template/language.rb +55 -0
  90. data/lib/ruby/whatsapp/messages/template/parameter.rb +113 -0
  91. data/lib/ruby/whatsapp/messages/template.rb +62 -0
  92. data/lib/ruby/whatsapp/messages/text.rb +43 -0
  93. data/lib/ruby/whatsapp/messages/video.rb +68 -0
  94. data/lib/ruby/whatsapp/messages.rb +116 -0
  95. data/lib/ruby/whatsapp/railtie.rb +14 -0
  96. data/lib/ruby/whatsapp/request_error.rb +5 -0
  97. data/lib/ruby/whatsapp/response_handling.rb +44 -0
  98. data/lib/ruby/whatsapp/utils/language_codes.rb +96 -0
  99. data/lib/ruby/whatsapp/version.rb +5 -0
  100. data/lib/ruby/whatsapp/webhook/account_alerts.rb +58 -0
  101. data/lib/ruby/whatsapp/webhook/account_review_update.rb +27 -0
  102. data/lib/ruby/whatsapp/webhook/account_update/ban_info.rb +33 -0
  103. data/lib/ruby/whatsapp/webhook/account_update.rb +46 -0
  104. data/lib/ruby/whatsapp/webhook/automatic_events.rb +43 -0
  105. data/lib/ruby/whatsapp/webhook/business_capability_update.rb +36 -0
  106. data/lib/ruby/whatsapp/webhook/change.rb +61 -0
  107. data/lib/ruby/whatsapp/webhook/contact.rb +36 -0
  108. data/lib/ruby/whatsapp/webhook/entry.rb +35 -0
  109. data/lib/ruby/whatsapp/webhook/error.rb +47 -0
  110. data/lib/ruby/whatsapp/webhook/history.rb +54 -0
  111. data/lib/ruby/whatsapp/webhook/installer.rb +61 -0
  112. data/lib/ruby/whatsapp/webhook/message/audio.rb +28 -0
  113. data/lib/ruby/whatsapp/webhook/message/base.rb +61 -0
  114. data/lib/ruby/whatsapp/webhook/message/button.rb +33 -0
  115. data/lib/ruby/whatsapp/webhook/message/contacts/address.rb +68 -0
  116. data/lib/ruby/whatsapp/webhook/message/contacts/contact.rb +68 -0
  117. data/lib/ruby/whatsapp/webhook/message/contacts/email.rb +35 -0
  118. data/lib/ruby/whatsapp/webhook/message/contacts/name.rb +62 -0
  119. data/lib/ruby/whatsapp/webhook/message/contacts/org.rb +40 -0
  120. data/lib/ruby/whatsapp/webhook/message/contacts/phone.rb +40 -0
  121. data/lib/ruby/whatsapp/webhook/message/contacts/url.rb +35 -0
  122. data/lib/ruby/whatsapp/webhook/message/contacts.rb +31 -0
  123. data/lib/ruby/whatsapp/webhook/message/context.rb +49 -0
  124. data/lib/ruby/whatsapp/webhook/message/document.rb +32 -0
  125. data/lib/ruby/whatsapp/webhook/message/image.rb +18 -0
  126. data/lib/ruby/whatsapp/webhook/message/interactive.rb +53 -0
  127. data/lib/ruby/whatsapp/webhook/message/location.rb +49 -0
  128. data/lib/ruby/whatsapp/webhook/message/media.rb +54 -0
  129. data/lib/ruby/whatsapp/webhook/message/order/product_item.rb +50 -0
  130. data/lib/ruby/whatsapp/webhook/message/order.rb +43 -0
  131. data/lib/ruby/whatsapp/webhook/message/reaction.rb +37 -0
  132. data/lib/ruby/whatsapp/webhook/message/referral.rb +80 -0
  133. data/lib/ruby/whatsapp/webhook/message/sticker.rb +32 -0
  134. data/lib/ruby/whatsapp/webhook/message/system.rb +49 -0
  135. data/lib/ruby/whatsapp/webhook/message/text.rb +28 -0
  136. data/lib/ruby/whatsapp/webhook/message/unknown.rb +39 -0
  137. data/lib/ruby/whatsapp/webhook/message/video.rb +18 -0
  138. data/lib/ruby/whatsapp/webhook/message.rb +39 -0
  139. data/lib/ruby/whatsapp/webhook/message_template_components_update.rb +53 -0
  140. data/lib/ruby/whatsapp/webhook/message_template_quality_update.rb +59 -0
  141. data/lib/ruby/whatsapp/webhook/message_template_status_update.rb +54 -0
  142. data/lib/ruby/whatsapp/webhook/messages.rb +55 -0
  143. data/lib/ruby/whatsapp/webhook/metadata.rb +35 -0
  144. data/lib/ruby/whatsapp/webhook/notification.rb +36 -0
  145. data/lib/ruby/whatsapp/webhook/partner_solutions.rb +36 -0
  146. data/lib/ruby/whatsapp/webhook/payment_configuration_update.rb +51 -0
  147. data/lib/ruby/whatsapp/webhook/phone_number_name_update.rb +51 -0
  148. data/lib/ruby/whatsapp/webhook/phone_number_quality_update.rb +45 -0
  149. data/lib/ruby/whatsapp/webhook/security.rb +41 -0
  150. data/lib/ruby/whatsapp/webhook/signature.rb +28 -0
  151. data/lib/ruby/whatsapp/webhook/smb_app_state_sync/state_sync.rb +38 -0
  152. data/lib/ruby/whatsapp/webhook/smb_app_state_sync.rb +33 -0
  153. data/lib/ruby/whatsapp/webhook/smb_message_echoes.rb +48 -0
  154. data/lib/ruby/whatsapp/webhook/status/conversation.rb +42 -0
  155. data/lib/ruby/whatsapp/webhook/status/pricing.rb +38 -0
  156. data/lib/ruby/whatsapp/webhook/status.rb +65 -0
  157. data/lib/ruby/whatsapp/webhook/template_category_update.rb +64 -0
  158. data/lib/ruby/whatsapp/webhook/templates/webhooks_controller.rb.tt +31 -0
  159. data/lib/ruby/whatsapp/webhook/unknown_field.rb +37 -0
  160. data/lib/ruby/whatsapp/webhook/user_preferences.rb +47 -0
  161. data/lib/ruby/whatsapp/webhook/verification.rb +27 -0
  162. data/lib/ruby/whatsapp/webhook.rb +8 -0
  163. data/lib/ruby/whatsapp.rb +49 -0
  164. data/lib/tasks/whatsapp.rake +10 -0
  165. data/sig/ruby/whatsapp.rbs +6 -0
  166. metadata +267 -0
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # Shared shape for the media-backed message types (image, video, audio,
7
+ # document, sticker) — a media asset id you can resolve via {Whatsapp::Media},
8
+ # plus its mime type and content hash.
9
+ class Media < Base
10
+ # @!attribute [rw] media_id
11
+ # @return [String, nil] The media asset id (resolve via {Whatsapp::Media#url}).
12
+ attr_accessor :media_id
13
+
14
+ # @!attribute [rw] mime_type
15
+ # @return [String, nil]
16
+ attr_accessor :mime_type
17
+
18
+ # @!attribute [rw] sha256
19
+ # @return [String, nil]
20
+ attr_accessor :sha256
21
+
22
+ # @!attribute [rw] caption
23
+ # @return [String, nil]
24
+ attr_accessor :caption
25
+
26
+ def initialize(media_id:, mime_type:, sha256:, caption: nil, **base_attributes)
27
+ super(**base_attributes)
28
+
29
+ @media_id = media_id
30
+ @mime_type = mime_type
31
+ @sha256 = sha256
32
+ @caption = caption
33
+ end
34
+
35
+ class << self
36
+ # Extracts the media-specific fields nested under the given key
37
+ # (e.g. `"image"`, `"video"`), for subclasses to merge with their own
38
+ # extra attributes before calling `new`.
39
+ # @param data [Hash] The raw message hash.
40
+ # @param key [String] The type-specific nested key (e.g. `"image"`).
41
+ # @return [Hash]
42
+ def media_attributes(data, key)
43
+ {
44
+ media_id: data.dig(key, "id"),
45
+ mime_type: data.dig(key, "mime_type"),
46
+ sha256: data.dig(key, "sha256"),
47
+ caption: data.dig(key, "caption"),
48
+ }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ class Order < Base
7
+ # A single line item within an inbound order message.
8
+ class ProductItem
9
+ # @!attribute [rw] product_retailer_id
10
+ # @return [String, nil] The catalog SKU.
11
+ attr_accessor :product_retailer_id
12
+
13
+ # @!attribute [rw] quantity
14
+ # @return [Integer, nil]
15
+ attr_accessor :quantity
16
+
17
+ # @!attribute [rw] item_price
18
+ # @return [Float, nil]
19
+ attr_accessor :item_price
20
+
21
+ # @!attribute [rw] currency
22
+ # @return [String, nil]
23
+ attr_accessor :currency
24
+
25
+ def initialize(product_retailer_id:, quantity:, item_price:, currency:)
26
+ @product_retailer_id = product_retailer_id
27
+ @quantity = quantity
28
+ @item_price = item_price
29
+ @currency = currency
30
+ end
31
+
32
+ class << self
33
+ # @param data [Hash] A raw `product_items[]` entry.
34
+ # @return [ProductItem]
35
+ def deserialize(data)
36
+ data ||= {}
37
+
38
+ new(
39
+ product_retailer_id: data["product_retailer_id"],
40
+ quantity: data["quantity"],
41
+ item_price: data["item_price"],
42
+ currency: data["currency"]
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # An inbound order placed against a Meta catalog.
7
+ class Order < Base
8
+ # @!attribute [rw] catalog_id
9
+ # @return [String, nil]
10
+ attr_accessor :catalog_id
11
+
12
+ # @!attribute [rw] text
13
+ # @return [String, nil] Optional note the customer attached to the order.
14
+ attr_accessor :text
15
+
16
+ # @!attribute [rw] product_items
17
+ # @return [Array<ProductItem>]
18
+ attr_accessor :product_items
19
+
20
+ def initialize(catalog_id:, product_items:, text: nil, **base_attributes)
21
+ super(**base_attributes)
22
+
23
+ @catalog_id = catalog_id
24
+ @text = text
25
+ @product_items = product_items
26
+ end
27
+
28
+ class << self
29
+ # @param data [Hash] The raw message hash.
30
+ # @return [Order]
31
+ def deserialize(data)
32
+ new(
33
+ catalog_id: data.dig("order", "catalog_id"),
34
+ text: data.dig("order", "text"),
35
+ product_items: Array(data.dig("order", "product_items")).map { |item| ProductItem.deserialize(item) },
36
+ **common_attributes(data)
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # An inbound emoji reaction to a previous message.
7
+ class Reaction < Base
8
+ # @!attribute [rw] message_id
9
+ # @return [String, nil] The wamid of the message being reacted to.
10
+ attr_accessor :message_id
11
+
12
+ # @!attribute [rw] emoji
13
+ # @return [String, nil] Empty when the user removed a previous reaction.
14
+ attr_accessor :emoji
15
+
16
+ def initialize(message_id:, emoji:, **base_attributes)
17
+ super(**base_attributes)
18
+
19
+ @message_id = message_id
20
+ @emoji = emoji
21
+ end
22
+
23
+ class << self
24
+ # @param data [Hash] The raw message hash.
25
+ # @return [Reaction]
26
+ def deserialize(data)
27
+ new(
28
+ message_id: data.dig("reaction", "message_id"),
29
+ emoji: data.dig("reaction", "emoji"),
30
+ **common_attributes(data)
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # Present when an inbound message originated from a Click-to-WhatsApp ad or a
7
+ # post/story with an embedded WhatsApp link.
8
+ class Referral
9
+ # @!attribute [rw] source_url
10
+ # @return [String, nil]
11
+ attr_accessor :source_url
12
+
13
+ # @!attribute [rw] source_type
14
+ # @return [String, nil]
15
+ attr_accessor :source_type
16
+
17
+ # @!attribute [rw] source_id
18
+ # @return [String, nil]
19
+ attr_accessor :source_id
20
+
21
+ # @!attribute [rw] headline
22
+ # @return [String, nil]
23
+ attr_accessor :headline
24
+
25
+ # @!attribute [rw] body
26
+ # @return [String, nil]
27
+ attr_accessor :body
28
+
29
+ # @!attribute [rw] media_type
30
+ # @return [String, nil]
31
+ attr_accessor :media_type
32
+
33
+ # @!attribute [rw] image_url
34
+ # @return [String, nil]
35
+ attr_accessor :image_url
36
+
37
+ # @!attribute [rw] video_url
38
+ # @return [String, nil]
39
+ attr_accessor :video_url
40
+
41
+ # @!attribute [rw] thumbnail_url
42
+ # @return [String, nil]
43
+ attr_accessor :thumbnail_url
44
+
45
+ def initialize(source_url:, source_type:, source_id:, headline:, body:, media_type:, image_url: nil,
46
+ video_url: nil, thumbnail_url: nil)
47
+ @source_url = source_url
48
+ @source_type = source_type
49
+ @source_id = source_id
50
+ @headline = headline
51
+ @body = body
52
+ @media_type = media_type
53
+ @image_url = image_url
54
+ @video_url = video_url
55
+ @thumbnail_url = thumbnail_url
56
+ end
57
+
58
+ class << self
59
+ # @param data [Hash, nil] The raw `referral` hash.
60
+ # @return [Referral, nil]
61
+ def deserialize(data)
62
+ return if data.nil?
63
+
64
+ new(
65
+ source_url: data["source_url"],
66
+ source_type: data["source_type"],
67
+ source_id: data["source_id"],
68
+ headline: data["headline"],
69
+ body: data["body"],
70
+ media_type: data["media_type"],
71
+ image_url: data["image_url"],
72
+ video_url: data["video_url"],
73
+ thumbnail_url: data["thumbnail_url"]
74
+ )
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # An inbound sticker message.
7
+ class Sticker < Media
8
+ # @!attribute [rw] animated
9
+ # @return [Boolean, nil]
10
+ attr_accessor :animated
11
+
12
+ def initialize(animated: nil, **media_and_base_attributes)
13
+ super(**media_and_base_attributes)
14
+
15
+ @animated = animated
16
+ end
17
+
18
+ class << self
19
+ # @param data [Hash] The raw message hash.
20
+ # @return [Sticker]
21
+ def deserialize(data)
22
+ new(
23
+ animated: data.dig("sticker", "animated"),
24
+ **media_attributes(data, "sticker"),
25
+ **common_attributes(data)
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # A system notice, e.g. that the customer changed their phone number.
7
+ class System < Base
8
+ # @!attribute [rw] body
9
+ # @return [String, nil] Human-readable description of the change.
10
+ attr_accessor :body
11
+
12
+ # @!attribute [rw] identity
13
+ # @return [String, nil]
14
+ attr_accessor :identity
15
+
16
+ # @!attribute [rw] wa_id
17
+ # @return [String, nil] The customer's (possibly new) WhatsApp ID.
18
+ attr_accessor :wa_id
19
+
20
+ # @!attribute [rw] change_type
21
+ # @return [String, nil] e.g. `"customer_changed_number"`, `"customer_identity_changed"`.
22
+ attr_accessor :change_type
23
+
24
+ def initialize(body:, identity:, wa_id:, change_type:, **base_attributes)
25
+ super(**base_attributes)
26
+
27
+ @body = body
28
+ @identity = identity
29
+ @wa_id = wa_id
30
+ @change_type = change_type
31
+ end
32
+
33
+ class << self
34
+ # @param data [Hash] The raw message hash.
35
+ # @return [System]
36
+ def deserialize(data)
37
+ new(
38
+ body: data.dig("system", "body"),
39
+ identity: data.dig("system", "identity"),
40
+ wa_id: data.dig("system", "wa_id"),
41
+ change_type: data.dig("system", "type"),
42
+ **common_attributes(data)
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # An inbound plain-text message.
7
+ class Text < Base
8
+ # @!attribute [rw] body
9
+ # @return [String, nil]
10
+ attr_accessor :body
11
+
12
+ def initialize(body:, **base_attributes)
13
+ super(**base_attributes)
14
+
15
+ @body = body
16
+ end
17
+
18
+ class << self
19
+ # @param data [Hash] The raw message hash.
20
+ # @return [Text]
21
+ def deserialize(data)
22
+ new(body: data.dig("text", "body"), **common_attributes(data))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # Fallback for a message `type` not in {Message::MESSAGE_TYPES} — either a
7
+ # type Meta added after this gem was released, or a message the Cloud API
8
+ # itself couldn't process (carries an `errors[]` array in that case).
9
+ class Unknown < Base
10
+ # @!attribute [rw] raw
11
+ # @return [Hash] The full, untyped raw message hash.
12
+ attr_accessor :raw
13
+
14
+ # @!attribute [rw] errors
15
+ # @return [Array<Error>]
16
+ attr_accessor :errors
17
+
18
+ def initialize(raw:, errors:, **base_attributes)
19
+ super(**base_attributes)
20
+
21
+ @raw = raw
22
+ @errors = errors
23
+ end
24
+
25
+ class << self
26
+ # @param data [Hash] The raw message hash.
27
+ # @return [Unknown]
28
+ def deserialize(data)
29
+ new(
30
+ raw: data,
31
+ errors: Array(data["errors"]).map { |error_data| Error.deserialize(error_data) },
32
+ **common_attributes(data)
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ class Message
6
+ # An inbound video message.
7
+ class Video < Media
8
+ class << self
9
+ # @param data [Hash] The raw message hash.
10
+ # @return [Video]
11
+ def deserialize(data)
12
+ new(**media_attributes(data, "video"), **common_attributes(data))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ # Dispatches a single `messages[]` entry from the `messages` webhook field to
6
+ # the class matching its `type`. Mirrors `Whatsapp::Messages::KINDS` — resolution
7
+ # goes through this frozen whitelist (never `const_get` on the `type` string,
8
+ # which comes straight from the internet), so an unrecognized type falls back to
9
+ # {Message::Unknown} rather than raising.
10
+ class Message
11
+ MESSAGE_TYPES = {
12
+ text: Text,
13
+ image: Image,
14
+ video: Video,
15
+ audio: Audio,
16
+ document: Document,
17
+ sticker: Sticker,
18
+ location: Location,
19
+ contacts: Contacts,
20
+ interactive: Interactive,
21
+ button: Button,
22
+ order: Order,
23
+ system: System,
24
+ reaction: Reaction,
25
+ }.freeze
26
+
27
+ class << self
28
+ # @param data [Hash] A raw `messages[]` entry.
29
+ # @return [Message::Base] One of the classes in {MESSAGE_TYPES}, or {Message::Unknown}.
30
+ def deserialize(data)
31
+ data ||= {}
32
+ klass = MESSAGE_TYPES.fetch(data["type"].to_s.to_sym) { Unknown }
33
+
34
+ klass.deserialize(data)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ # Template component modifications (e.g. Meta editing a template's header,
6
+ # body, or button on the business's behalf).
7
+ #
8
+ # Best-effort schema: Meta's public docs describe this field in one line with
9
+ # no published JSON example. Validate against a real payload (App Dashboard
10
+ # "send test payload") before relying on these attribute names in production.
11
+ # Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
12
+ class MessageTemplateComponentsUpdate
13
+ # @!attribute [rw] message_template_id
14
+ # @return [Integer, nil]
15
+ attr_accessor :message_template_id
16
+
17
+ # @!attribute [rw] message_template_name
18
+ # @return [String, nil]
19
+ attr_accessor :message_template_name
20
+
21
+ # @!attribute [rw] message_template_language
22
+ # @return [String, nil]
23
+ attr_accessor :message_template_language
24
+
25
+ # @!attribute [rw] message_template_element
26
+ # @return [String, nil] The component that changed, e.g. `"BODY"`.
27
+ attr_accessor :message_template_element
28
+
29
+ def initialize(message_template_id:, message_template_name:, message_template_language:,
30
+ message_template_element:)
31
+ @message_template_id = message_template_id
32
+ @message_template_name = message_template_name
33
+ @message_template_language = message_template_language
34
+ @message_template_element = message_template_element
35
+ end
36
+
37
+ class << self
38
+ # @param data [Hash] The raw `value` hash.
39
+ # @return [MessageTemplateComponentsUpdate]
40
+ def deserialize(data)
41
+ data ||= {}
42
+
43
+ new(
44
+ message_template_id: data["message_template_id"],
45
+ message_template_name: data["message_template_name"],
46
+ message_template_language: data["message_template_language"],
47
+ message_template_element: data["message_template_element"]
48
+ )
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ # Template quality score changes (e.g. green/yellow/red rating shifts based
6
+ # on recipient feedback).
7
+ #
8
+ # Best-effort schema: Meta's public docs describe this field in one line with
9
+ # no published JSON example. Validate against a real payload (App Dashboard
10
+ # "send test payload") before relying on these attribute names in production.
11
+ # Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
12
+ class MessageTemplateQualityUpdate
13
+ # @!attribute [rw] message_template_id
14
+ # @return [Integer, nil]
15
+ attr_accessor :message_template_id
16
+
17
+ # @!attribute [rw] message_template_name
18
+ # @return [String, nil]
19
+ attr_accessor :message_template_name
20
+
21
+ # @!attribute [rw] message_template_language
22
+ # @return [String, nil]
23
+ attr_accessor :message_template_language
24
+
25
+ # @!attribute [rw] previous_quality_score
26
+ # @return [String, nil] e.g. `"GREEN"`, `"YELLOW"`, `"RED"`.
27
+ attr_accessor :previous_quality_score
28
+
29
+ # @!attribute [rw] new_quality_score
30
+ # @return [String, nil]
31
+ attr_accessor :new_quality_score
32
+
33
+ def initialize(message_template_id:, message_template_name:, message_template_language:,
34
+ previous_quality_score:, new_quality_score:)
35
+ @message_template_id = message_template_id
36
+ @message_template_name = message_template_name
37
+ @message_template_language = message_template_language
38
+ @previous_quality_score = previous_quality_score
39
+ @new_quality_score = new_quality_score
40
+ end
41
+
42
+ class << self
43
+ # @param data [Hash] The raw `value` hash.
44
+ # @return [MessageTemplateQualityUpdate]
45
+ def deserialize(data)
46
+ data ||= {}
47
+
48
+ new(
49
+ message_template_id: data["message_template_id"],
50
+ message_template_name: data["message_template_name"],
51
+ message_template_language: data["message_template_language"],
52
+ previous_quality_score: data["previous_quality_score"],
53
+ new_quality_score: data["new_quality_score"]
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ module Webhook
5
+ # Template status modifications — approved, rejected, flagged, paused, or
6
+ # disabled by Meta.
7
+ # Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
8
+ class MessageTemplateStatusUpdate
9
+ # @!attribute [rw] message_template_id
10
+ # @return [Integer, nil]
11
+ attr_accessor :message_template_id
12
+
13
+ # @!attribute [rw] message_template_name
14
+ # @return [String, nil]
15
+ attr_accessor :message_template_name
16
+
17
+ # @!attribute [rw] message_template_language
18
+ # @return [String, nil]
19
+ attr_accessor :message_template_language
20
+
21
+ # @!attribute [rw] event
22
+ # @return [String, nil] e.g. `"APPROVED"`, `"REJECTED"`, `"FLAGGED"`, `"PAUSED"`, `"DISABLED"`.
23
+ attr_accessor :event
24
+
25
+ # @!attribute [rw] reason
26
+ # @return [String, nil]
27
+ attr_accessor :reason
28
+
29
+ def initialize(message_template_id:, message_template_name:, message_template_language:, event:, reason: nil)
30
+ @message_template_id = message_template_id
31
+ @message_template_name = message_template_name
32
+ @message_template_language = message_template_language
33
+ @event = event
34
+ @reason = reason
35
+ end
36
+
37
+ class << self
38
+ # @param data [Hash] The raw `value` hash.
39
+ # @return [MessageTemplateStatusUpdate]
40
+ def deserialize(data)
41
+ data ||= {}
42
+
43
+ new(
44
+ message_template_id: data["message_template_id"],
45
+ message_template_name: data["message_template_name"],
46
+ message_template_language: data["message_template_language"],
47
+ event: data["event"],
48
+ reason: data["reason"]
49
+ )
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end