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,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class Body < Base
7
+ module Defaults
8
+ MAX_TEXT_LENGTH = 1024
9
+ end
10
+
11
+ # @!attribute [rw] text
12
+ # @return [String]
13
+ attr_accessor :text
14
+
15
+ validates :text, presence: true, length: { maximum: Defaults::MAX_TEXT_LENGTH }
16
+ # @param text [String] The footer text.
17
+ def initialize(text)
18
+ @text = text
19
+
20
+ validate!
21
+ end
22
+
23
+ class << self
24
+ # @return [Hash] Serialized representation of the Header.
25
+ def serialize(text)
26
+ new(text).serialize
27
+ end
28
+ end
29
+
30
+ # @return [Hash] Serialized representation of the Header.
31
+ def serialize
32
+ {
33
+ text:,
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class Footer < Base
7
+ module Defaults
8
+ MAX_TEXT_LENGTH = 60
9
+ end
10
+
11
+ # @!attribute [rw] text
12
+ # @return [String]
13
+ attr_accessor :text
14
+
15
+ validates :text, presence: true, length: { maximum: Defaults::MAX_TEXT_LENGTH }
16
+
17
+ # @param text [String] The footer text.
18
+ def initialize(text)
19
+ @text = text
20
+
21
+ validate!
22
+ end
23
+
24
+ class << self
25
+ # @return [Hash] Serialized representation of the Header.
26
+ def serialize(text:)
27
+ new(text).serialize
28
+ end
29
+ end
30
+
31
+ # @return [Hash] Serialized representation of the Header.
32
+ def serialize
33
+ {
34
+ text:,
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class Header < Base
7
+ class HeaderError < Whatsapp::Error; end
8
+
9
+ module Types
10
+ IMAGE = "image"
11
+ VIDEO = "video"
12
+ DOCUMENT = "document"
13
+ TEXT = "text"
14
+ end
15
+
16
+ # @!attribute [rw] type
17
+ # @return [String]
18
+ attr_accessor :type
19
+
20
+ validates :type, presence: true, inclusion: { in: Types.constants.map { |c| Types.const_get(c) } }
21
+ validate :validate_text_length
22
+
23
+ # @param type [String] The type of the header (e.g., "text", "image", "video", "document").
24
+ # @param kwargs [Hash] Additional keyword arguments for the header content.
25
+ def initialize(type:, **kwargs)
26
+ @type = type
27
+ @kwargs = kwargs
28
+
29
+ validate!
30
+ end
31
+
32
+ class << self
33
+ # @return [Hash] Serialized representation of the Header.
34
+ def serialize(type:, **)
35
+ new(type:, **).serialize
36
+ end
37
+ end
38
+
39
+ # @return [Hash] Serialized representation of the Header.
40
+ def serialize
41
+ case type
42
+ when Types::TEXT then serialize_text
43
+ when Types::IMAGE then serialize_image
44
+ when Types::DOCUMENT then serialize_document
45
+ when Types::VIDEO then serialize_video
46
+ else
47
+ raise HeaderError, "Unsupported header type: #{type.inspect}"
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # @return [Hash] Serialized representation of text header.
54
+ def serialize_text
55
+ raise HeaderError, "Text content is required for text header" unless @kwargs[:text]
56
+
57
+ {
58
+ type: Types::TEXT,
59
+ text: @kwargs[:text],
60
+ }
61
+ end
62
+
63
+ # @return [Hash] Serialized representation of media header.
64
+ def serialize_image
65
+ handle_media(Types::IMAGE)
66
+ end
67
+
68
+ # @return [Hash] Serialized representation of media header.
69
+ def serialize_document
70
+ handle_media(Types::DOCUMENT)
71
+ end
72
+
73
+ # @return [Hash] Serialized representation of media header.
74
+ def serialize_video
75
+ handle_media(Types::VIDEO)
76
+ end
77
+
78
+ # @return [Hash] Serialized representation of media header.
79
+ # @raise [HeaderError] if required link is missing.
80
+ def handle_media(type)
81
+ raise HeaderError, "#{type.capitalize} link is required for #{type} header" unless @kwargs[:link]
82
+
83
+ {
84
+ type:,
85
+ type.to_sym => {
86
+ link: @kwargs[:link],
87
+ },
88
+ }
89
+ end
90
+
91
+ # Validates that text headers don't exceed 60 characters
92
+ def validate_text_length
93
+ return unless type == Types::TEXT && @kwargs[:text]
94
+
95
+ return unless @kwargs[:text].length > 60
96
+
97
+ errors.add(:text, "must be 60 characters or less")
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class ListButtons
7
+ class Section
8
+ class Row
9
+ include ActiveModel::Validations
10
+
11
+ module Defaults
12
+ MAX_ID_LENGTH = 200
13
+ MAX_TITLE_LENGTH = 24
14
+ MAX_DESCRIPTION_LENGTH = 72
15
+ end
16
+
17
+ # @!attribute [rw] id
18
+ # @return [String]
19
+ attr_accessor :id
20
+
21
+ # @!attribute [rw] title
22
+ # @return [String]
23
+ attr_accessor :title
24
+
25
+ # @!attribute [rw] description
26
+ # @return [String, nil]
27
+ attr_accessor :description
28
+
29
+ validates :id, presence: true, length: { maximum: Defaults::MAX_ID_LENGTH }
30
+ validates :title, presence: true, length: { maximum: Defaults::MAX_TITLE_LENGTH }
31
+ validates :description, length: { maximum: Defaults::MAX_DESCRIPTION_LENGTH }, allow_nil: true
32
+
33
+ # @param id [String] The unique identifier for the row.
34
+ # @param title [String] The title of the row.
35
+ # @param description [String, nil] The description of the row.
36
+ # @raise [ActiveModel::ValidationError] if validation fails.
37
+ def initialize(id:, title:, description: nil)
38
+ @id = id
39
+ @title = title
40
+ @description = description
41
+
42
+ validate!
43
+ end
44
+
45
+ class << self
46
+ # @return [Hash] Serialized representation of the Row.
47
+ # @raise [ActiveModel::ValidationError] if validation fails.
48
+ def serialize(**row)
49
+ new(**row).serialize
50
+ end
51
+ end
52
+
53
+ # @return [Hash] Serialized representation of the Row.
54
+ def serialize
55
+ {
56
+ id:,
57
+ title:,
58
+ description:,
59
+ }.compact
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class ListButtons
7
+ class Section
8
+ include ActiveModel::Validations
9
+
10
+ module Defaults
11
+ MAX_TITLE_LENGTH = 24
12
+ MIN_ROWS_LENGTH = 1
13
+ MAX_ROWS_LENGTH = 10
14
+ end
15
+
16
+ # @!attribute [rw] title
17
+ # @return [String]
18
+ attr_accessor :title
19
+
20
+ # @!attribute [rw] rows
21
+ # @return [Array<Hash>]
22
+ attr_accessor :rows
23
+
24
+ validates :title, presence: true, length: { maximum: Defaults::MAX_TITLE_LENGTH }
25
+ validates :rows, presence: true, length: { minimum: Defaults::MIN_ROWS_LENGTH, maximum: Defaults::MAX_ROWS_LENGTH }
26
+
27
+ # @param title [String] The title of the section.
28
+ # @param rows [Array<Hash>] The rows in the section.
29
+ # @raise [ActiveModel::ValidationError] if validation fails.
30
+ def initialize(title:, rows:)
31
+ @title = title
32
+ @rows = rows
33
+
34
+ validate!
35
+ end
36
+
37
+ class << self
38
+ # @return [Hash] Serialized representation of the Section.
39
+ # @raise [ActiveModel::ValidationError] if validation fails.
40
+ def serialize(**section)
41
+ new(**section).serialize
42
+ end
43
+ end
44
+
45
+ # @return [Hash] Serialized representation of the Section.
46
+ def serialize
47
+ {
48
+ title:,
49
+ rows: serialized_rows,
50
+ }
51
+ end
52
+
53
+ private
54
+
55
+ # @return [Array<Hash>] Serialized rows.
56
+ # @raise [ActiveModel::ValidationError] if validation fails.
57
+ def serialized_rows
58
+ rows.map { |row| Row.serialize(**row) }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ # Interactive list messages allow you to present WhatsApp users with a list of options to choose from
7
+ # Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/messages/interactive-list-messages
8
+ class ListButtons < Base
9
+ module Defaults
10
+ MAX_BUTTON_LENGTH = 20
11
+ MIN_SECTIONS_COUNT = 1
12
+ MAX_SECTIONS_COUNT = 10
13
+ end
14
+
15
+ # @!attribute [rw] button
16
+ # @return [String]
17
+ attr_accessor :button
18
+
19
+ # @!attribute [rw] sections
20
+ # @return [Array<Hash>]
21
+ attr_accessor :sections
22
+
23
+ validates :button, presence: true, length: { maximum: Defaults::MAX_BUTTON_LENGTH }
24
+ validates :sections, presence: true, length: { minimum: Defaults::MIN_SECTIONS_COUNT,
25
+ maximum: Defaults::MAX_SECTIONS_COUNT, }
26
+
27
+ # @param button [String] The text for the list button.
28
+ # @param sections [Array<Hash>] The sections of the list button.
29
+ # @raise [ActiveModel::ValidationError] if validation fails.
30
+ def initialize(button:, sections:)
31
+ @button = button
32
+ @sections = sections
33
+
34
+ validate!
35
+ end
36
+
37
+ class << self
38
+ # @return [Hash] Serialized representation of the ListButtons.
39
+ # @raise [ActiveModel::ValidationError] if validation fails.
40
+ def serialize(**list_buttons)
41
+ new(**list_buttons).serialize
42
+ end
43
+ end
44
+
45
+ # @return [Hash] Serialized representation of the ListButtons.
46
+ def serialize
47
+ {
48
+ button:,
49
+ sections: serialized_sections,
50
+ }
51
+ end
52
+
53
+ private
54
+
55
+ # @return [Array<Hash>] Serialized sections.
56
+ # @raise [ActiveModel::ValidationError] if validation fails.
57
+ def serialized_sections
58
+ sections.map { |section| Section.serialize(**section) }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class MediaCarousel
7
+ class Card
8
+ class Button
9
+ class QuickReply
10
+ include ActiveModel::Validations
11
+
12
+ # @!attribute [rw] id
13
+ # @return [String]
14
+ attr_accessor :id
15
+
16
+ # @!attribute [rw] title
17
+ # @return [String]
18
+ attr_accessor :title
19
+
20
+ validates :id, presence: true, length: { maximum: 20 }
21
+ validates :title, presence: true, length: { maximum: 20 }
22
+
23
+ # @param id [String] The ID of the quick reply button.
24
+ # @param title [String] The title of the quick reply button.
25
+ def initialize(id:, title:)
26
+ @id = id
27
+ @title = title
28
+ end
29
+
30
+ class << self
31
+ # @return [Hash] Serialized representation of the QuickReply.
32
+ def serialize(id:, title:)
33
+ new(id:, title:).serialize
34
+ end
35
+ end
36
+
37
+ # @return [Hash] Serialized representation of the QuickReply.
38
+ def serialize
39
+ {
40
+ id:,
41
+ title:,
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class MediaCarousel
7
+ class Card
8
+ class Button
9
+ module Defaults
10
+ BUTTON_TYPE = "quick_reply"
11
+ end
12
+
13
+ # @!attribute [rw] quick_reply
14
+ # @return [Hash]
15
+ attr_accessor :quick_reply
16
+
17
+ # @param quick_reply [Hash] The quick reply content of the button.
18
+ def initialize(quick_reply:)
19
+ @quick_reply = quick_reply
20
+ end
21
+
22
+ class << self
23
+ # @return [Hash] Serialized representation of the Button.
24
+ def serialize(quick_reply:)
25
+ new(quick_reply:).serialize
26
+ end
27
+ end
28
+
29
+ # @return [Hash] Serialized representation of the Button.
30
+ def serialize
31
+ {
32
+ type: Defaults::BUTTON_TYPE,
33
+ quick_reply: serialized_quick_reply,
34
+ }
35
+ end
36
+
37
+ private
38
+
39
+ def serialized_quick_reply
40
+ QuickReply.serialize(**quick_reply)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class MediaCarousel
7
+ class Card
8
+ include ActiveModel::Validations
9
+
10
+ module Defaults
11
+ TYPE = "cta_url"
12
+ end
13
+
14
+ # @!attribute [rw] card_index
15
+ # @return [Integer]
16
+ attr_accessor :card_index
17
+
18
+ # @!attribute [rw] header
19
+ # @return [Hash]
20
+ attr_accessor :header
21
+
22
+ # @!attribute [rw] body
23
+ # @return [String]
24
+ attr_accessor :body
25
+
26
+ # @!attribute [rw] action
27
+ # @return [Hash]
28
+ attr_accessor :action
29
+
30
+ # @!attribute [rw] buttons
31
+ # @return [Array<Hash>]
32
+ attr_accessor :buttons
33
+
34
+ validates :card_index, presence: true
35
+
36
+ # @param card_index [Integer] The index of the card.
37
+ # @param header [Hash] The header content of the card.
38
+ # @param body [String] The body content of the card.
39
+ # @param action [Hash] The action content of the card.
40
+ def initialize(card_index:, header:, body:, action:, buttons:)
41
+ @card_index = card_index
42
+ @header = header
43
+ @body = body
44
+ @action = action
45
+ @buttons = buttons
46
+
47
+ validate!
48
+ end
49
+
50
+ class << self
51
+ # @return [Hash] Serialized representation of the Card.
52
+ def serialize(card_index:, header:, body:, action:, buttons:)
53
+ new(card_index:, header:, body:, action:, buttons:).serialize
54
+ end
55
+ end
56
+
57
+ # @return [Hash] Serialized representation of the Card.
58
+ def serialize
59
+ {
60
+ card_index:,
61
+ type: Defaults::TYPE,
62
+ header: Whatsapp::Messages::Interactive::Header.serialize(**header),
63
+ body: Whatsapp::Messages::Interactive::Body.serialize(body),
64
+ action: Whatsapp::Messages::Interactive::UrlButton.serialize(**action),
65
+ buttons: serialized_buttons,
66
+ }
67
+ end
68
+
69
+ private
70
+
71
+ def serialized_buttons
72
+ buttons.map { |button| Button.serialize(**button) }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class MediaCarousel < Base
7
+ module Defaults
8
+ MIN_CARDS_LENGTH = 2
9
+ MAX_CARDS_LENGTH = 10
10
+ end
11
+
12
+ # @!attribute [rw] cards
13
+ # @return [Array<Hash>]
14
+ attr_accessor :cards
15
+
16
+ validates :cards, presence: true, length: { minimum: Defaults::MIN_CARDS_LENGTH,
17
+ maximum: Defaults::MAX_CARDS_LENGTH, }
18
+
19
+ # @param cards [Array<Hash>] The media cards.
20
+ def initialize(cards:)
21
+ @cards = cards
22
+
23
+ validate!
24
+ end
25
+
26
+ class << self
27
+ # @return [Hash] Serialized representation of the MediaCarousel.
28
+ def serialize(cards:)
29
+ new(cards:).serialize
30
+ end
31
+ end
32
+
33
+ # @return [Hash] Serialized representation of the MediaCarousel.
34
+ def serialize
35
+ {
36
+ cards: serialized_cards,
37
+ }
38
+ end
39
+
40
+ private
41
+
42
+ # @return [Array<Hash>] Serialized cards.
43
+ # @raise [ActiveModel::ValidationError] if validation fails.
44
+ def serialized_cards
45
+ cards.map.with_index { |card, index| Card.serialize(card_index: index, **card) }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whatsapp
4
+ class Messages
5
+ class Interactive
6
+ class ProductCarousel
7
+ class Card
8
+ module Defaults
9
+ TYPE = "product"
10
+ end
11
+
12
+ # @!attribute [rw] card_index
13
+ # @return [Integer]
14
+ attr_accessor :card_index
15
+
16
+ # @!attribute [rw] catalog_id
17
+ # @return [String]
18
+ attr_accessor :catalog_id
19
+
20
+ # @!attribute [rw] product_retailer_id
21
+ # @return [String]
22
+ attr_accessor :product_retailer_id
23
+
24
+ # @param card_index [Integer] The index of the card in the carousel.
25
+ # @param catalog_id [String] The catalog ID associated with the product.
26
+ # @param product_retailer_id [String] The retailer ID of the product.
27
+ def initialize(card_index:, catalog_id:, product_retailer_id:)
28
+ @card_index = card_index
29
+ @catalog_id = catalog_id
30
+ @product_retailer_id = product_retailer_id
31
+ end
32
+
33
+ class << self
34
+ # @return [Hash] Serialized representation of the Card.
35
+ def serialize(card_index:, catalog_id:, product_retailer_id:)
36
+ new(card_index:, catalog_id:, product_retailer_id:).serialize
37
+ end
38
+ end
39
+
40
+ # @return [Hash] Serialized representation of the Card.
41
+ def serialize
42
+ {
43
+ type: Defaults::TYPE,
44
+ card_index:,
45
+ action: {
46
+ catalog_id:,
47
+ product_retailer_id:,
48
+ },
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end