whatsapp_sdk 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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