whatsapp_sdk 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf1d072bf1bc9cd834a9917a863177cc0b5b2ddfac0c29b0d4e899ee23cb9c19
4
- data.tar.gz: 25dca7a61a5d80d19fcf32fa54e07edcb911901e936c5b315e46488d87c8b900
3
+ metadata.gz: 5fb8ebc1170878767c0800ab380f6cca2cd853fe59ec7496921781ef85fde8fb
4
+ data.tar.gz: 67e312ba37cc9c8e612af0a5d0840c1b7933eb3dfcebb9641faee41194387d21
5
5
  SHA512:
6
- metadata.gz: 5a1204308855d7fb8945a5fda9a3939928b0ec5d6cc6e65fe95abfb303eec2f3dfa4f667f6b9cfc3c264144cdf8290eb80e7aa2ed5e60b213da398d1beded5ef
7
- data.tar.gz: 82039c3038209445ad552cca6c23755d8acf2662af4ff6c3fb63464979a42533c92d5768cd376c72252e5b4058932b71a8b9d393471aeb7ba4903a1b62358c12
6
+ metadata.gz: 856af75ac80060b7f0c6e282fc64d396a102dc31c6cbd8bf09c15bcee55b265ee4e0e5ffbff7e3bab055659233cd4d2db087a6303598128d881ae935b5d45685
7
+ data.tar.gz: d709a2b39b758cb294a1cc682320bfa4be3a634d137561e01b79f66c3df042312a31e07925d774ab42e84b51e7597a1b20bb6ef1965503a84085cfb1827aca58
data/.gitignore CHANGED
@@ -7,6 +7,8 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ example_copy.rb
11
+
10
12
  DELETE_ME
11
13
  REMOVE.rb
12
14
 
data/.rubocop.yml CHANGED
@@ -31,6 +31,9 @@ Metrics/MethodLength:
31
31
  Metrics/BlockLength:
32
32
  Enabled: false
33
33
 
34
+ Metrics/CyclomaticComplexity:
35
+ Enabled: false
36
+
34
37
  Naming/VariableNumber:
35
38
  Enabled: false
36
39
 
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Unreleased
2
2
 
3
+ # v 0.9.0
4
+ - Use binary mode to download files @ignacio-chiazzo [#88](https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk/pull/87)
5
+ - Added support for downloading media by specifying the type @ignacio-chiazzo [#87](https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk/pull/87)
6
+
3
7
  # v 0.8.0
4
8
  - Added Send interactive message @alienware [#82](https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk/pull/82)
5
9
  - Support JRuby @ignacio-chiazzo [#83](https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk/pull/83)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- whatsapp_sdk (0.8.0)
4
+ whatsapp_sdk (0.9.0)
5
5
  faraday (~> 2.3.0)
6
6
  faraday-multipart (~> 1.0.4)
7
7
  sorbet-runtime (~> 0.5.1)
data/README.md CHANGED
@@ -183,7 +183,7 @@ media = medias_api.media(media_id: MEDIA_ID)
183
183
 
184
184
  Download media
185
185
  ```ruby
186
- medias_api.download(url: MEDIA_URL, file_path: 'tmp/downloaded_whatsapp.png')
186
+ medias_api.download(url: MEDIA_URL, file_path: 'tmp/downloaded_whatsapp.png', media_type: "image/png")
187
187
  ```
188
188
 
189
189
  Delete a media
data/example.rb CHANGED
@@ -22,11 +22,12 @@ require "pry-nav"
22
22
 
23
23
  ################# UPDATE CONSTANTS #################
24
24
 
25
- ACCESS_TOKEN = "EAAZAvvr0DZBs0BAIOsVV2FogD1qQhIyIrK9vfM7mmfdTlhbRqFZAZBnS2ciXPD35wb8d69siULaU5cqX9HonDNnZA9YLAbrCLUys1qt1E3n4d69v9RTZCoA8bkz9TZCV2PWzRR9DvBmwUOltCOrZBN0vGQxULlhsJE1mTeUrXlpVfFZCY5sWXCKxe04uyd7dzyK0hMgvkCR97OXi9AI3qJFYo"
26
- SENDER_ID = 111591145018464
27
- RECIPIENT_NUMBER = 13437772910
28
- BUSINESS_ID = 102261539298487
25
+ ACCESS_TOKEN = "EAAHlmy2rChwBAGELLYnNnJfhKOPuSuaX5cRrfYA65RLY2NlEsMQ4x4tO3fz2imwrhmyx2pvKnC07tm0sWRzFHEko7CtXoZBTSb3lrBrKlx86eDvtdZBm2P2ewEJPbotfMYhTYwLsfMyRdQqgNAmc0wij1hMTHOusZALovPKHsme3RvAo1Ag1wqZA3qrPB2WhZChhKWPOkVQZDZD"
26
+ SENDER_ID = 100219219709628
27
+ RECIPIENT_NUMBER = 15550429560
28
+ BUSINESS_ID = 102_261_539_298_487
29
29
  IMAGE_LINK = "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"
30
+ AUDIO_LINK = "https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2951440611667284&ext=1681491953&hash=ATsLUNWiMmGDndn5YlpWQFHm5CUXca0gdahSJTCp5XjgTQ"
30
31
 
31
32
  if ACCESS_TOKEN == "<TODO replace>"
32
33
  puts "\n\n**** Please update the ACCESS_TOKEN constant in this file. ****\n\n"
@@ -53,6 +54,23 @@ messages_api = WhatsappSdk::Api::Messages.new
53
54
  phone_numbers_api = WhatsappSdk::Api::PhoneNumbers.new
54
55
  business_profile_api = WhatsappSdk::Api::BusinessProfile.new
55
56
 
57
+ binding.pry
58
+ # upload an audio
59
+ uploaded_media = medias_api.upload(sender_id: SENDER_ID, file_path: "tmp/downloaded_audio.ogg", type: "audio/ogg")
60
+ media_id = uploaded_media.data&.id
61
+ puts "Uploaded media id: #{media_id}"
62
+
63
+ # get a media audio
64
+ media = medias_api.media(media_id: media_id).data
65
+ puts "Media info: #{media.raw_data_response}"
66
+
67
+ # get a media audio
68
+ audio_link = media.url
69
+ download_image = medias_api.download(url: audio_link, file_path: 'tmp/downloaded_audio2.ogg', media_type: "audio/ogg")
70
+ puts "Downloaded: #{download_image.data.success?}"
71
+
72
+
73
+
56
74
  ############################## Business API ##############################
57
75
  business_profile = business_profile_api.details(SENDER_ID)
58
76
  business_profile_api.update(phone_number_id: SENDER_ID, params: { about: "A very cool business" } )
@@ -63,23 +81,39 @@ registered_numbers = phone_numbers_api.registered_numbers(BUSINESS_ID)
63
81
 
64
82
  ############################## Media API ##############################
65
83
 
66
- # upload a media
84
+ ##### Image #####
85
+ # upload a Image
67
86
  uploaded_media = medias_api.upload(sender_id: SENDER_ID, file_path: "tmp/whatsapp.png", type: "image/png")
68
87
  media_id = uploaded_media.data&.id
69
88
  puts "Uploaded media id: #{media_id}"
70
89
 
71
- # get a media
90
+ # get a media Image
72
91
  media = medias_api.media(media_id: media_id).data
73
92
  puts "Media info: #{media.raw_data_response}"
74
93
 
75
- # download media
76
- download_image = medias_api.download(url: media&.url, file_path: 'tmp/downloaded_whatsapp.png')
94
+ # download media Image
95
+ download_image = medias_api.download(url: media.url, file_path: 'tmp/downloaded_image.png', media_type: "image/png")
77
96
  puts "Downloaded: #{download_image.data.success?}"
78
97
 
79
98
  # delete a media
80
99
  deleted_media = medias_api.delete(media_id: media&.id)
81
100
  puts "Deleted: #{deleted_media.data.success?}"
82
101
 
102
+ #### Audio ####
103
+ # upload an audio
104
+ uploaded_media = medias_api.upload(sender_id: SENDER_ID, file_path: "tmp/downloaded_audio.ogg", type: "audio/ogg")
105
+ media_id = uploaded_media.data&.id
106
+ puts "Uploaded media id: #{media_id}"
107
+
108
+ # get a media audio
109
+ media = medias_api.media(media_id: media_id).data
110
+ puts "Media info: #{media.raw_data_response}"
111
+
112
+ # get a media audio
113
+ audio_link = media.url
114
+ download_image = medias_api.download(url: audio_link, file_path: 'tmp/downloaded_audio2.ogg', media_type: "audio/ogg")
115
+ puts "Downloaded: #{download_image.data.success?}"
116
+
83
117
  ############################## Messages API ##############################
84
118
 
85
119
  ######### SEND A TEXT MESSAGE
@@ -35,18 +35,22 @@ module WhatsappSdk
35
35
  JSON.parse(response.body)
36
36
  end
37
37
 
38
- sig { params(url: String, path_to_file_name: T.nilable(String)).returns(Net::HTTPResponse) }
39
- 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)
40
43
  uri = URI.parse(url)
41
44
  request = Net::HTTP::Get.new(uri)
42
45
  request["Authorization"] = "Bearer #{@access_token}"
46
+ request.content_type = content_header
43
47
  req_options = { use_ssl: uri.scheme == "https" }
44
48
 
45
49
  response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
46
50
  http.request(request)
47
51
  end
48
52
 
49
- 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
50
54
 
51
55
  response
52
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
@@ -412,8 +412,8 @@ module WhatsappSdk
412
412
  )
413
413
  end
414
414
 
415
- alias :send_interactive_reply_buttons :send_interactive_message
416
- alias :send_interactive_list_messages :send_interactive_message
415
+ alias send_interactive_reply_buttons send_interactive_message
416
+ alias send_interactive_list_messages send_interactive_message
417
417
 
418
418
  # Mark a message as read.
419
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: {})
@@ -61,7 +61,7 @@ module WhatsappSdk
61
61
  sig do
62
62
  params(
63
63
  type: Type, buttons: T::Array[InteractiveActionReplyButton],
64
- button: String, sections: T::Array[InteractiveActionSection],
64
+ button: String, sections: T::Array[InteractiveActionSection]
65
65
  ).void
66
66
  end
67
67
  def initialize(type:, buttons: [], button: "", sections: [])
@@ -93,40 +93,48 @@ module WhatsappSdk
93
93
  def validate_fields
94
94
  case type.serialize
95
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 }
96
+ validate_list_message
116
97
  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
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
123
109
 
124
- button_ids = buttons.map(&:id)
125
- return if button_ids.length.eql?(button_ids.uniq.length)
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
126
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)
127
128
  raise WhatsappSdk::Resource::Error::InvalidInteractiveActionReplyButton,
128
- "Duplicate ids #{button_ids} for buttons in action. They should be unique."
129
+ "Invalid length #{buttons_count} for buttons in action. It should be between " \
130
+ "#{REPLY_BUTTONS_MINIMUM} and #{REPLY_BUTTONS_MAXIMUM}."
129
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."
130
138
  end
131
139
  end
132
140
  end
@@ -34,12 +34,10 @@ module WhatsappSdk
34
34
  end
35
35
 
36
36
  def to_json
37
- json = {
37
+ {
38
38
  title: title,
39
- rows: rows.map(&:to_json),
39
+ rows: rows.map(&:to_json)
40
40
  }
41
-
42
- json
43
41
  end
44
42
 
45
43
  sig { params(skip_rows: T.nilable(T::Boolean)).void }
@@ -72,4 +70,3 @@ module WhatsappSdk
72
70
  end
73
71
  end
74
72
  end
75
-
@@ -29,7 +29,7 @@ module WhatsappSdk
29
29
  ACTION_SECTION_DESCRIPTION_MAXIMUM = 72
30
30
  ACTION_SECTION_ID_MAXIMUM = 256
31
31
 
32
- sig { params(title: String, id: String, description: T::nilable(String)).void }
32
+ sig { params(title: String, id: String, description: T.nilable(String)).void }
33
33
  def initialize(title:, id:, description: "")
34
34
  @title = title
35
35
  @id = id
@@ -42,7 +42,7 @@ module WhatsappSdk
42
42
  id: id,
43
43
  title: title
44
44
  }
45
- json[:description] = description if description.length > 0
45
+ json[:description] = description if description.length.positive?
46
46
 
47
47
  json
48
48
  end
@@ -61,9 +61,9 @@ module WhatsappSdk
61
61
  title_length = title.length
62
62
  return if title_length <= ACTION_SECTION_TITLE_MAXIMUM
63
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
- )
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
67
  end
68
68
 
69
69
  sig { void }
@@ -74,9 +74,9 @@ module WhatsappSdk
74
74
  id_length = id.length
75
75
  return if id_length <= ACTION_SECTION_ID_MAXIMUM
76
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
- )
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
80
  end
81
81
 
82
82
  sig { void }
@@ -84,11 +84,10 @@ module WhatsappSdk
84
84
  description_length = description.length
85
85
  return if description_length <= ACTION_SECTION_DESCRIPTION_MAXIMUM
86
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
- )
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
90
  end
91
91
  end
92
92
  end
93
93
  end
94
-
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module WhatsappSdk
5
- VERSION = "0.8.0"
5
+ VERSION = "0.9.0"
6
6
  end
@@ -18,8 +18,8 @@ module Mocha::ClassMethods
18
18
  end
19
19
 
20
20
  class Mocha::Expectation
21
- sig { params(expected_parameters: T.untyped, matching_block: T.nilable(T.proc.params(actual_parameters: T.untyped).void)).returns(Mocha::Expectation) }
22
- def with(*expected_parameters, &matching_block); end
21
+ sig { params(expected_parameters_or_matchers: T.untyped, kwargs: T.untyped, matching_block: T.nilable(T.proc.params(actual_parameters: T.untyped).void)).returns(Mocha::Expectation) }
22
+ def with(*expected_parameters_or_matchers, **kwargs, &matching_block); end
23
23
 
24
24
  sig { params(values: T.untyped).returns(Mocha::Expectation) }
25
25
  def returns(*values); end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whatsapp_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ignacio-chiazzo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-17 00:00:00.000000000 Z
11
+ date: 2023-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler