whatsapp_sdk 0.7.3 → 0.9.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.
@@ -3,7 +3,6 @@
3
3
 
4
4
  require "faraday"
5
5
  require "faraday/multipart"
6
- require "oj"
7
6
 
8
7
  module WhatsappSdk
9
8
  module Api
@@ -31,21 +30,27 @@ module WhatsappSdk
31
30
 
32
31
  response = faraday_request.public_send(http_method, endpoint, request_params(params, headers), headers)
33
32
 
34
- Oj.load(response.body)
33
+ return nil if response.body == ""
34
+
35
+ JSON.parse(response.body)
35
36
  end
36
37
 
37
- sig { params(url: String, path_to_file_name: T.nilable(String)).returns(Net::HTTPResponse) }
38
- def download_file(url, path_to_file_name = nil)
38
+ sig do
39
+ params(url: String, content_header: String, file_path: T.nilable(String))
40
+ .returns(Net::HTTPResponse)
41
+ end
42
+ def download_file(url:, content_header:, file_path: nil)
39
43
  uri = URI.parse(url)
40
44
  request = Net::HTTP::Get.new(uri)
41
45
  request["Authorization"] = "Bearer #{@access_token}"
46
+ request.content_type = content_header
42
47
  req_options = { use_ssl: uri.scheme == "https" }
43
48
 
44
49
  response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
45
50
  http.request(request)
46
51
  end
47
52
 
48
- File.write(path_to_file_name, response.body) if response.code == "200" && path_to_file_name
53
+ File.write(file_path, response.body, mode: 'wb') if response.code == "200" && file_path
49
54
 
50
55
  response
51
56
  end
@@ -25,6 +25,21 @@ module WhatsappSdk
25
25
  end
26
26
  end
27
27
 
28
+ class InvalidMediaTypeError < StandardError
29
+ extend T::Sig
30
+
31
+ sig { returns(String) }
32
+ attr_reader :media_type
33
+
34
+ sig { params(media_type: String).void }
35
+ def initialize(_media_type)
36
+ @file_path = file_path
37
+ message = "Invalid Media Type. See the supported types" \
38
+ "see the official documentation https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#supported-media-types."
39
+ super(message)
40
+ end
41
+ end
42
+
28
43
  # Get Media by ID.
29
44
  #
30
45
  # @param media_id [String] Media Id.
@@ -46,11 +61,16 @@ module WhatsappSdk
46
61
  #
47
62
  # @param url URL.
48
63
  # @param file_path [String] The file_path to download the media e.g. "tmp/downloaded_image.png".
64
+ # @param media_type [String] The media type e.g. "audio/mp4". See the supported types in the official
65
+ # documentation https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#supported-media-types.
49
66
  # @return [WhatsappSdk::Api::Response] Response object.
50
- sig { params(url: String, file_path: String).returns(WhatsappSdk::Api::Response) }
51
- def download(url:, file_path:)
52
- response = download_file(url, file_path)
67
+ sig { params(url: String, file_path: String, media_type: String).returns(WhatsappSdk::Api::Response) }
68
+ def download(url:, file_path:, media_type:)
69
+ return InvalidMediaTypeError(media_type) if media_type && !valid_content_header?(media_type)
70
+
71
+ content_header = media_type
53
72
 
73
+ response = download_file(url: url, file_path: file_path, content_header: content_header)
54
74
  response = if response.code.to_i == 200
55
75
  { "success" => true }
56
76
  else
@@ -105,6 +125,14 @@ module WhatsappSdk
105
125
  data_class_type: WhatsappSdk::Api::Responses::SuccessResponse
106
126
  )
107
127
  end
128
+
129
+ private
130
+
131
+ def valid_content_header?(_media_type)
132
+ # TODO: Add validations for media types. See available types in the official documentation
133
+ # https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#supported-media-types.
134
+ true
135
+ end
108
136
  end
109
137
  end
110
138
  end
@@ -363,13 +363,57 @@ module WhatsappSdk
363
363
  # # TODO: https://developers.facebook.com/docs/whatsapp_sdk/cloud-api/reference/messages#contacts-object
364
364
  # end
365
365
 
366
- # def send_interactive_reply_buttons
367
- # # TODO: https://developers.facebook.com/docs/whatsapp_sdk/cloud-api/reference/messages#contacts-object
368
- # end
366
+ # Send interactive reply buttons.
367
+ # https://developers.facebook.com/docs/whatsapp/guides/interactive-messages#reply-buttons
368
+ # You can either send interactive object or as JSON.
369
+ #
370
+ # @param sender_id [Integer] Sender' phone number.
371
+ # @param recipient_number [Integer] Recipient' Phone number.
372
+ # @param interactive [Interactive] Interactive.
373
+ # @param interactive_json [Json] The interactive object as a Json.
374
+ # If you pass interactive_json, you can't pass interactive.
375
+ # @param message_id [String] The id of the message to reply to.
376
+ # @return [WhatsappSdk::Api::Response] Response object.
377
+ sig do
378
+ params(
379
+ sender_id: Integer, recipient_number: Integer,
380
+ interactive: T.nilable(WhatsappSdk::Resource::Interactive),
381
+ interactive_json: T.nilable(T::Hash[T.untyped, T.untyped]), message_id: T.nilable(String)
382
+ ).returns(WhatsappSdk::Api::Response)
383
+ end
384
+ def send_interactive_message(
385
+ sender_id:, recipient_number:, interactive: nil, interactive_json: nil, message_id: nil
386
+ )
387
+ raise MissingArgumentError, "interactive or interactive_json is required" if !interactive && !interactive_json
369
388
 
370
- # def send_interactive_section
371
- # # TODO: https://developers.facebook.com/docs/whatsapp_sdk/cloud-api/reference/messages#contacts-object
372
- # end
389
+ params = {
390
+ messaging_product: "whatsapp",
391
+ to: recipient_number,
392
+ recipient_type: "individual",
393
+ type: "interactive"
394
+ }
395
+
396
+ params[:interactive] = if interactive.nil?
397
+ interactive_json
398
+ else
399
+ interactive.to_json
400
+ end
401
+ params[:context] = { message_id: message_id } if message_id
402
+
403
+ response = send_request(
404
+ endpoint: endpoint(sender_id),
405
+ params: params,
406
+ headers: DEFAULT_HEADERS
407
+ )
408
+
409
+ WhatsappSdk::Api::Response.new(
410
+ response: response,
411
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
412
+ )
413
+ end
414
+
415
+ alias send_interactive_reply_buttons send_interactive_message
416
+ alias send_interactive_list_messages send_interactive_message
373
417
 
374
418
  # Mark a message as read.
375
419
  #
@@ -10,8 +10,8 @@ module WhatsappSdk
10
10
  @client = client
11
11
  end
12
12
 
13
- def download_file(url, path_to_file_name = nil)
14
- @client.download_file(url, path_to_file_name)
13
+ def download_file(url:, content_header:, file_path: nil)
14
+ @client.download_file(url: url, content_header: content_header, file_path: file_path)
15
15
  end
16
16
 
17
17
  def send_request(endpoint: nil, full_url: nil, http_method: "post", params: {}, headers: {})
@@ -0,0 +1,39 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module WhatsappSdk
5
+ module Resource
6
+ module Error
7
+ class MissingValue < WhatsappSdk::Error
8
+ extend T::Sig
9
+
10
+ sig { returns(String) }
11
+ attr_reader :field
12
+
13
+ sig { returns(String) }
14
+ attr_reader :message
15
+
16
+ sig { params(field: String, message: String).void }
17
+ def initialize(field, message)
18
+ @field = field
19
+ @message = message
20
+ super(message)
21
+ end
22
+ end
23
+
24
+ class InvalidField < MissingValue; end
25
+
26
+ class InvalidInteractiveBody < WhatsappSdk::Error; end
27
+
28
+ class InvalidInteractiveActionReplyButton < WhatsappSdk::Error; end
29
+
30
+ class InvalidInteractiveActionButton < WhatsappSdk::Error; end
31
+
32
+ class InvalidInteractiveActionSection < WhatsappSdk::Error; end
33
+
34
+ class InvalidInteractiveActionSectionRow < WhatsappSdk::Error; end
35
+
36
+ class InvalidInteractiveFooter < WhatsappSdk::Error; end
37
+ end
38
+ end
39
+ end
@@ -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,141 @@
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
+ validate_list_message
97
+ when "reply_button"
98
+ validate_reply_button
99
+ end
100
+ end
101
+
102
+ def validate_list_message
103
+ button_length = button.length
104
+ sections_count = sections.length
105
+ unless button_length.positive?
106
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionButton,
107
+ "Invalid button in action. Button label is required."
108
+ end
109
+
110
+ unless button_length <= LIST_BUTTON_TITLE_MAXIMUM
111
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionButton,
112
+ "Invalid length #{button_length} for button. Maximum length: " \
113
+ "#{LIST_BUTTON_TITLE_MAXIMUM} characters."
114
+ end
115
+
116
+ unless (LIST_SECTIONS_MINIMUM..LIST_SECTIONS_MAXIMUM).cover?(sections_count)
117
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSection,
118
+ "Invalid length #{sections_count} for sections in action. It should be between " \
119
+ "#{LIST_SECTIONS_MINIMUM} and #{LIST_SECTIONS_MAXIMUM}."
120
+ end
121
+
122
+ sections.each(&:validate)
123
+ end
124
+
125
+ def validate_reply_button
126
+ buttons_count = buttons.length
127
+ unless (REPLY_BUTTONS_MINIMUM..REPLY_BUTTONS_MAXIMUM).cover?(buttons_count)
128
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionReplyButton,
129
+ "Invalid length #{buttons_count} for buttons in action. It should be between " \
130
+ "#{REPLY_BUTTONS_MINIMUM} and #{REPLY_BUTTONS_MAXIMUM}."
131
+ end
132
+
133
+ button_ids = buttons.map(&:id)
134
+ return if button_ids.length.eql?(button_ids.uniq.length)
135
+
136
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionReplyButton,
137
+ "Duplicate ids #{button_ids} for buttons in action. They should be unique."
138
+ end
139
+ end
140
+ end
141
+ 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,72 @@
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
+ {
38
+ title: title,
39
+ rows: rows.map(&:to_json)
40
+ }
41
+ end
42
+
43
+ sig { params(skip_rows: T.nilable(T::Boolean)).void }
44
+ def validate(skip_rows: false)
45
+ validate_title
46
+ validate_rows unless skip_rows
47
+ end
48
+
49
+ private
50
+
51
+ sig { void }
52
+ def validate_title
53
+ title_length = title.length
54
+ return if title_length <= ACTION_SECTION_TITLE_MAXIMUM
55
+
56
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSection,
57
+ "Invalid length #{title_length} for title in section. Maximum length: " \
58
+ "#{ACTION_SECTION_TITLE_MAXIMUM} characters."
59
+ end
60
+
61
+ sig { void }
62
+ def validate_rows
63
+ rows_length = rows.length
64
+ return if rows_length <= ACTION_SECTION_ROWS_MAXIMUM
65
+
66
+ raise WhatsappSdk::Resource::Error::InvalidInteractiveActionSection,
67
+ "Invalid number of rows #{rows_length} in section. Maximum count: " \
68
+ "#{ACTION_SECTION_ROWS_MAXIMUM}."
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,93 @@
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.positive?
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,
65
+ "Invalid length #{title_length} for title in section row. "\
66
+ "Maximum length: #{ACTION_SECTION_TITLE_MAXIMUM} characters."
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,
78
+ "Invalid length #{id_length} for id in section row. Maximum length: "\
79
+ "#{ACTION_SECTION_ID_MAXIMUM} characters."
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,
88
+ "Invalid length #{description_length} for description in section " \
89
+ "row. Maximum length: #{ACTION_SECTION_DESCRIPTION_MAXIMUM} characters."
90
+ end
91
+ end
92
+ end
93
+ end