whatsapp_sdk 0.7.2 → 0.8.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.
@@ -0,0 +1,89 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class Interactive
7
+ extend T::Sig
8
+
9
+ class Type < T::Enum
10
+ extend T::Sig
11
+
12
+ enums do
13
+ ListMessage = new("list")
14
+ ReplyButton = new("button")
15
+ SingleProductMessage = new("product")
16
+ MultiProductMessage = new("product_list")
17
+ end
18
+ end
19
+
20
+ # Returns the Interactive type of message you want to send.
21
+ #
22
+ # @returns type [Type]. Supported Options are list, button, product and product_list.
23
+ sig { returns(Type) }
24
+ attr_accessor :type
25
+
26
+ # Returns the interactive header if present. Required for type product_list.
27
+ #
28
+ # @returns type [InteractiveHeader] It can be nil.
29
+ sig { returns(T.nilable(InteractiveHeader)) }
30
+ attr_accessor :header
31
+
32
+ # Returns the interactive body.
33
+ #
34
+ # @returns type [InteractiveBody] Valid option is of type text only.
35
+ sig { returns(InteractiveBody) }
36
+ attr_accessor :body
37
+
38
+ # Returns the interactive footer if present.
39
+ #
40
+ # @returns type [InteractiveFooter] Valid option is of type text only. It can be nil.
41
+ sig { returns(T.nilable(InteractiveFooter)) }
42
+ attr_accessor :footer
43
+
44
+ # Returns the interactive action.
45
+ #
46
+ # @returns type [InteractiveBody] Valid condition is buttons of length of 1, 2 or 3 if type is button.
47
+ sig { returns(InteractiveAction) }
48
+ attr_accessor :action
49
+
50
+ sig do
51
+ params(
52
+ type: Type, body: InteractiveBody, action: InteractiveAction,
53
+ header: T.nilable(InteractiveHeader), footer: T.nilable(InteractiveFooter)
54
+ ).void
55
+ end
56
+ def initialize(type:, body:, action:, header: nil, footer: nil)
57
+ @type = type
58
+ @body = body
59
+ @action = action
60
+ @header = header
61
+ @footer = footer
62
+ validate
63
+ end
64
+
65
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
66
+ def to_json
67
+ json = { type: type.serialize }
68
+ json[:header] = header.to_json if header
69
+ json[:body] = body.to_json
70
+ json[:footer] = footer.to_json if footer
71
+ json[:action] = action.to_json
72
+
73
+ json
74
+ end
75
+
76
+ private
77
+
78
+ sig { void }
79
+ def validate
80
+ validate_action
81
+ end
82
+
83
+ sig { void }
84
+ def validate_action
85
+ action.validate
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,133 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class InteractiveAction
7
+ extend T::Sig
8
+
9
+ class Type < T::Enum
10
+ extend T::Sig
11
+
12
+ enums do
13
+ ListMessage = new("list_message")
14
+ ReplyButton = new("reply_button")
15
+ end
16
+ end
17
+
18
+ # Returns the type of interactive action you want to send.
19
+ #
20
+ # @returns type [Type]. Supported Options are list_message and reply_button.
21
+ sig { returns(Type) }
22
+ attr_accessor :type
23
+
24
+ # Returns the buttons of the Action. For reply_button type, it's required.
25
+ #
26
+ # @returns buttons [Array<InteractiveActionReplyButton>] .
27
+ sig { returns(T::Array[InteractiveActionReplyButton]) }
28
+ attr_accessor :buttons
29
+
30
+ # Returns the button of the Action. For list_message type, it's required.
31
+ #
32
+ # @returns button [String] .
33
+ sig { returns(String) }
34
+ attr_accessor :button
35
+
36
+ # Returns the sections of the Action. For list_message type, it's required.
37
+ #
38
+ # @returns sections [Array<InteractiveActionSection>] .
39
+ sig { returns(T::Array[InteractiveActionSection]) }
40
+ attr_accessor :sections
41
+
42
+ # TODO: attr_accessor :catalog_id
43
+ # TODO: attr_accessor :product_retailer_id
44
+
45
+ sig { params(reply_button: InteractiveActionReplyButton).void }
46
+ def add_reply_button(reply_button)
47
+ @buttons << reply_button
48
+ end
49
+
50
+ sig { params(section: InteractiveActionSection).void }
51
+ def add_section(section)
52
+ @sections << section
53
+ end
54
+
55
+ REPLY_BUTTONS_MINIMUM = 1
56
+ REPLY_BUTTONS_MAXIMUM = 3
57
+ LIST_BUTTON_TITLE_MAXIMUM = 20
58
+ LIST_SECTIONS_MINIMUM = 1
59
+ LIST_SECTIONS_MAXIMUM = 10
60
+
61
+ sig do
62
+ params(
63
+ type: Type, buttons: T::Array[InteractiveActionReplyButton],
64
+ button: String, sections: T::Array[InteractiveActionSection],
65
+ ).void
66
+ end
67
+ def initialize(type:, buttons: [], button: "", sections: [])
68
+ @type = type
69
+ @buttons = buttons
70
+ @button = button
71
+ @sections = sections
72
+ end
73
+
74
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
75
+ def to_json
76
+ json = {}
77
+ case type.serialize
78
+ when "list_message"
79
+ json = { button: button, sections: sections.map(&:to_json) }
80
+ when "reply_button"
81
+ json = { buttons: buttons.map(&:to_json) }
82
+ end
83
+
84
+ json
85
+ end
86
+
87
+ def validate
88
+ validate_fields
89
+ end
90
+
91
+ private
92
+
93
+ def validate_fields
94
+ case type.serialize
95
+ when "list_message"
96
+ button_length = button.length
97
+ sections_count = sections.length
98
+ unless button_length > 0
99
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionButton,
100
+ "Invalid button in action. Button label is required."
101
+ end
102
+
103
+ unless button_length <= LIST_BUTTON_TITLE_MAXIMUM
104
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionButton,
105
+ "Invalid length #{button_length} for button. Maximum length: " \
106
+ "#{LIST_BUTTON_TITLE_MAXIMUM} characters."
107
+ end
108
+
109
+ unless (LIST_SECTIONS_MINIMUM..LIST_SECTIONS_MAXIMUM).cover?(sections_count)
110
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSection,
111
+ "Invalid length #{sections_count} for sections in action. It should be between " \
112
+ "#{LIST_SECTIONS_MINIMUM} and #{LIST_SECTIONS_MAXIMUM}."
113
+ end
114
+
115
+ sections.each { |section| section.validate }
116
+ when "reply_button"
117
+ buttons_count = buttons.length
118
+ unless (REPLY_BUTTONS_MINIMUM..REPLY_BUTTONS_MAXIMUM).cover?(buttons_count)
119
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionReplyButton,
120
+ "Invalid length #{buttons_count} for buttons in action. It should be between " \
121
+ "#{REPLY_BUTTONS_MINIMUM} and #{REPLY_BUTTONS_MAXIMUM}."
122
+ end
123
+
124
+ button_ids = buttons.map(&:id)
125
+ return if button_ids.length.eql?(button_ids.uniq.length)
126
+
127
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionReplyButton,
128
+ "Duplicate ids #{button_ids} for buttons in action. They should be unique."
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,88 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class InteractiveActionReplyButton
7
+ extend T::Sig
8
+
9
+ class Type < T::Enum
10
+ extend T::Sig
11
+
12
+ enums do
13
+ Reply = new("reply")
14
+ end
15
+ end
16
+
17
+ # Returns the ActionButton type of message you want to send.
18
+ #
19
+ # @returns type [String]. Supported Options are reply only.
20
+ sig { returns(Type) }
21
+ attr_accessor :type
22
+
23
+ # Returns the ActionButton title you want to send.
24
+ #
25
+ # @returns title [String]. The character limit is 20 characters.
26
+ sig { returns(String) }
27
+ attr_accessor :title
28
+
29
+ # Returns the ActionButton unique identifier you want to send.
30
+ # This ID is returned in the webhook when the button is clicked by the user.
31
+ #
32
+ # @returns id [String]. The character limit is 256 characters.
33
+ sig { returns(String) }
34
+ attr_accessor :id
35
+
36
+ ACTION_BUTTON_TITLE_MAXIMUM = 20
37
+ ACTION_BUTTON_ID_MAXIMUM = 256
38
+
39
+ sig { params(title: String, id: String).void }
40
+ def initialize(title:, id:)
41
+ @type = Type::Reply
42
+ @title = title
43
+ @id = id
44
+ validate
45
+ end
46
+
47
+ def to_json
48
+ json = { type: type.serialize }
49
+ json[type.serialize.to_sym] = {
50
+ id: id,
51
+ title: title
52
+ }
53
+
54
+ json
55
+ end
56
+
57
+ private
58
+
59
+ sig { void }
60
+ def validate
61
+ validate_title
62
+ validate_id
63
+ end
64
+
65
+ sig { void }
66
+ def validate_title
67
+ title_length = title.length
68
+ return if title_length <= ACTION_BUTTON_TITLE_MAXIMUM
69
+
70
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionReplyButton,
71
+ "Invalid length #{title_length} for title in button. " \
72
+ "Maximum length: #{ACTION_BUTTON_TITLE_MAXIMUM} characters."
73
+ end
74
+
75
+ sig { void }
76
+ def validate_id
77
+ id_unfrozen = id.dup
78
+ id_unfrozen.strip! # You cannot have leading or trailing spaces when setting the ID.
79
+ id = id_unfrozen.freeze
80
+ id_length = id.length
81
+ return if id_length <= ACTION_BUTTON_ID_MAXIMUM
82
+
83
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionReplyButton,
84
+ "Invalid length #{id_length} for id in button. Maximum length: #{ACTION_BUTTON_ID_MAXIMUM} characters."
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,75 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class InteractiveActionSection
7
+ extend T::Sig
8
+
9
+ # Returns the ActionSection title you want to send.
10
+ #
11
+ # @returns title [String]. The character limit is 24 characters.
12
+ sig { returns(String) }
13
+ attr_accessor :title
14
+
15
+ # Returns the ActionSection rows you want to send.
16
+ #
17
+ # @returns id [T::Array[InteractiveActionSectionRow]]. There must be at least one rows object.
18
+ sig { returns(T::Array[InteractiveActionSectionRow]) }
19
+ attr_accessor :rows
20
+
21
+ sig { params(row: InteractiveActionSectionRow).void }
22
+ def add_row(row)
23
+ @rows << row
24
+ end
25
+
26
+ ACTION_SECTION_TITLE_MAXIMUM = 24
27
+ ACTION_SECTION_ROWS_MAXIMUM = 10
28
+
29
+ sig { params(title: String, rows: T::Array[InteractiveActionSectionRow]).void }
30
+ def initialize(title:, rows: [])
31
+ @title = title
32
+ @rows = rows
33
+ validate(skip_rows: true)
34
+ end
35
+
36
+ def to_json
37
+ json = {
38
+ title: title,
39
+ rows: rows.map(&:to_json),
40
+ }
41
+
42
+ json
43
+ end
44
+
45
+ sig { params(skip_rows: T.nilable(T::Boolean)).void }
46
+ def validate(skip_rows: false)
47
+ validate_title
48
+ validate_rows unless skip_rows
49
+ end
50
+
51
+ private
52
+
53
+ sig { void }
54
+ def validate_title
55
+ title_length = title.length
56
+ return if title_length <= ACTION_SECTION_TITLE_MAXIMUM
57
+
58
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSection,
59
+ "Invalid length #{title_length} for title in section. Maximum length: " \
60
+ "#{ACTION_SECTION_TITLE_MAXIMUM} characters."
61
+ end
62
+
63
+ sig { void }
64
+ def validate_rows
65
+ rows_length = rows.length
66
+ return if rows_length <= ACTION_SECTION_ROWS_MAXIMUM
67
+
68
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSection,
69
+ "Invalid number of rows #{rows_length} in section. Maximum count: " \
70
+ "#{ACTION_SECTION_ROWS_MAXIMUM}."
71
+ end
72
+ end
73
+ end
74
+ end
75
+
@@ -0,0 +1,94 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class InteractiveActionSectionRow
7
+ extend T::Sig
8
+
9
+ # Returns the ActionSection title you want to send.
10
+ #
11
+ # @returns title [String]. The character limit is 24 characters.
12
+ sig { returns(String) }
13
+ attr_accessor :title
14
+
15
+ # Returns the ActionSection description you want to send.
16
+ #
17
+ # @returns description [String]. The character limit is 72 characters if present.
18
+ sig { returns(String) }
19
+ attr_accessor :description
20
+
21
+ # Returns the ActionSection unique identifier you want to send.
22
+ # This ID is returned in the webhook when the section is selected by the user.
23
+ #
24
+ # @returns id [String]. The character limit is 256 characters.
25
+ sig { returns(String) }
26
+ attr_accessor :id
27
+
28
+ ACTION_SECTION_TITLE_MAXIMUM = 24
29
+ ACTION_SECTION_DESCRIPTION_MAXIMUM = 72
30
+ ACTION_SECTION_ID_MAXIMUM = 256
31
+
32
+ sig { params(title: String, id: String, description: T::nilable(String)).void }
33
+ def initialize(title:, id:, description: "")
34
+ @title = title
35
+ @id = id
36
+ @description = description
37
+ validate
38
+ end
39
+
40
+ def to_json
41
+ json = {
42
+ id: id,
43
+ title: title
44
+ }
45
+ json[:description] = description if description.length > 0
46
+
47
+ json
48
+ end
49
+
50
+ private
51
+
52
+ sig { void }
53
+ def validate
54
+ validate_title
55
+ validate_id
56
+ validate_description
57
+ end
58
+
59
+ sig { void }
60
+ def validate_title
61
+ title_length = title.length
62
+ return if title_length <= ACTION_SECTION_TITLE_MAXIMUM
63
+
64
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSectionRow.new(
65
+ "Invalid length #{title_length} for title in section row. Maximum length: #{ACTION_SECTION_TITLE_MAXIMUM} characters.",
66
+ )
67
+ end
68
+
69
+ sig { void }
70
+ def validate_id
71
+ id_unfrozen = id.dup
72
+ id_unfrozen.strip! # You cannot have leading or trailing spaces when setting the ID.
73
+ id = id_unfrozen.freeze
74
+ id_length = id.length
75
+ return if id_length <= ACTION_SECTION_ID_MAXIMUM
76
+
77
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSectionRow.new(
78
+ "Invalid length #{id_length} for id in section row. Maximum length: #{ACTION_SECTION_ID_MAXIMUM} characters.",
79
+ )
80
+ end
81
+
82
+ sig { void }
83
+ def validate_description
84
+ description_length = description.length
85
+ return if description_length <= ACTION_SECTION_DESCRIPTION_MAXIMUM
86
+
87
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSectionRow.new(
88
+ "Invalid length #{description_length} for description in section row. Maximum length: #{ACTION_SECTION_DESCRIPTION_MAXIMUM} characters.",
89
+ )
90
+ end
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,48 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class InteractiveBody
7
+ extend T::Sig
8
+
9
+ # Returns Text string if the parameter object type is text.
10
+ # For the body interactive, the character limit is 1024 characters.
11
+ #
12
+ # @returns text [String]
13
+ sig { returns(String) }
14
+ attr_accessor :text
15
+
16
+ sig do
17
+ params(text: String).void
18
+ end
19
+ def initialize(text:)
20
+ @text = text
21
+ validate
22
+ end
23
+
24
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
25
+ def to_json
26
+ { text: text }
27
+ end
28
+
29
+ MAXIMUM_LENGTH = 1024
30
+
31
+ private
32
+
33
+ sig { void }
34
+ def validate
35
+ validate_text
36
+ end
37
+
38
+ sig { void }
39
+ def validate_text
40
+ text_length = text.length
41
+ return if text_length <= MAXIMUM_LENGTH
42
+
43
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveBody,
44
+ "Invalid length #{text_length} for text in body. Maximum length: #{MAXIMUM_LENGTH} characters."
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class InteractiveFooter
7
+ extend T::Sig
8
+
9
+ # Returns Text string if the parameter object type is text.
10
+ # For the body interactive, the character limit is 60 characters.
11
+ #
12
+ # @returns text [String]
13
+ sig { returns(String) }
14
+ attr_accessor :text
15
+
16
+ sig do
17
+ params(text: String).void
18
+ end
19
+ def initialize(text:)
20
+ @text = text
21
+ validate
22
+ end
23
+
24
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
25
+ def to_json
26
+ { text: text }
27
+ end
28
+
29
+ MAXIMUM_LENGTH = 60
30
+
31
+ private
32
+
33
+ sig { void }
34
+ def validate
35
+ validate_text
36
+ end
37
+
38
+ sig { void }
39
+ def validate_text
40
+ text_length = text.length
41
+ return if text_length <= MAXIMUM_LENGTH
42
+
43
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveFooter,
44
+ "Invalid length #{text_length} for text in footer. Maximum length: #{MAXIMUM_LENGTH} characters."
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,120 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ class InteractiveHeader
7
+ extend T::Sig
8
+
9
+ # Returns the interactive header type.
10
+ #
11
+ # @returns type [String] Valid options are text, image, document, video.
12
+ sig { returns(Type) }
13
+ attr_accessor :type
14
+
15
+ class Type < T::Enum
16
+ extend T::Sig
17
+
18
+ enums do
19
+ Text = new("text")
20
+ Image = new("image")
21
+ Document = new("document")
22
+ Video = new("video")
23
+ end
24
+ end
25
+
26
+ # Returns Text string if the interactive header type is text.
27
+ # For the header interactive, the character limit is 60 characters.
28
+ # For the body interactive, the character limit is 1024 characters.
29
+ #
30
+ # @returns text [String]
31
+ sig { returns(T.nilable(String)) }
32
+ attr_accessor :text
33
+
34
+ # Returns image if the interactive header type is image.
35
+ #
36
+ # @returns image [Media]
37
+ sig { returns(T.nilable(Media)) }
38
+ attr_accessor :image
39
+
40
+ # Returns document if the interactive header type is document.
41
+ #
42
+ # @returns document [Media]
43
+ sig { returns(T.nilable(Media)) }
44
+ attr_accessor :document
45
+
46
+ # Returns video if the interactive header type is video.
47
+ #
48
+ # @returns video [Media]
49
+ sig { returns(T.nilable(Media)) }
50
+ attr_accessor :video
51
+
52
+ sig do
53
+ params(
54
+ type: T.any(Type, String), text: T.nilable(String), image: T.nilable(Media),
55
+ document: T.nilable(Media), video: T.nilable(Media)
56
+ ).void
57
+ end
58
+ def initialize(type:, text: nil, image: nil, document: nil, video: nil)
59
+ @type = T.let(deserialize_type(type), Type)
60
+ @text = text
61
+ @image = image
62
+ @document = document
63
+ @video = video
64
+ validate
65
+ end
66
+
67
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
68
+ def to_json
69
+ json = { type: type.serialize }
70
+ json[type.serialize.to_sym] = case type.serialize
71
+ when "text"
72
+ text
73
+ when "image"
74
+ T.must(image).to_json
75
+ when "document"
76
+ T.must(document).to_json
77
+ when "video"
78
+ T.must(video).to_json
79
+ else
80
+ raise "Invalid type: #{type}"
81
+ end
82
+
83
+ json
84
+ end
85
+
86
+ private
87
+
88
+ sig { params(type: T.any(String, Type)).returns(Type) }
89
+ def deserialize_type(type)
90
+ return type if type.is_a?(Type)
91
+
92
+ Type.deserialize(type)
93
+ end
94
+
95
+ sig { void }
96
+ def validate
97
+ validate_attributes
98
+ end
99
+
100
+ sig { void }
101
+ def validate_attributes
102
+ [
103
+ [Type::Text, text],
104
+ [Type::Image, image],
105
+ [Type::Document, document],
106
+ [Type::Video, video]
107
+ ].each do |type_b, value|
108
+ next unless type == type_b
109
+
110
+ next unless value.nil?
111
+
112
+ raise WhatsappSdk::Resource::Error::MissingValue.new(
113
+ type.serialize,
114
+ "#{type.serialize} is required when the type is #{type_b}"
115
+ )
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end