whatsapp_sdk 0.1.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,25 +6,45 @@ require "oj"
6
6
  module WhatsappSdk
7
7
  module Api
8
8
  class Client
9
- API_VERSION = "v13.0"
9
+ API_VERSION = "v14.0"
10
10
  API_CLIENT = "https://graph.facebook.com/#{API_VERSION}/"
11
11
 
12
12
  def initialize(access_token)
13
13
  @access_token = access_token
14
14
  end
15
15
 
16
- def client
17
- @client ||= ::Faraday.new(API_CLIENT) do |client|
16
+ def send_request(endpoint: "", full_url: nil, http_method: "post", params: {})
17
+ url = full_url || API_CLIENT
18
+
19
+ response = faraday(url).public_send(http_method, endpoint, params)
20
+ Oj.load(response.body)
21
+ end
22
+
23
+ def download_file(url, path_to_file_name = nil)
24
+ uri = URI.parse(url)
25
+ request = Net::HTTP::Get.new(uri)
26
+ request["Authorization"] = "Bearer #{@access_token}"
27
+ req_options = { use_ssl: uri.scheme == "https" }
28
+
29
+ response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
30
+ http.request(request)
31
+ end
32
+
33
+ File.write(path_to_file_name, response.body) if response.code == "200" && path_to_file_name
34
+
35
+ response
36
+ end
37
+
38
+ private
39
+
40
+ def faraday(url)
41
+ ::Faraday.new(url) do |client|
42
+ client.request :multipart
18
43
  client.request :url_encoded
19
44
  client.adapter ::Faraday.default_adapter
20
45
  client.headers['Authorization'] = "Bearer #{@access_token}" unless @access_token.nil?
21
46
  end
22
47
  end
23
-
24
- def send_request(endpoint:, http_method: "post", params: {})
25
- response = client.public_send(http_method, endpoint, params)
26
- Oj.load(response.body)
27
- end
28
48
  end
29
49
  end
30
50
  end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "faraday/multipart"
5
+
6
+ require_relative "request"
7
+ require_relative "response"
8
+ require_relative '../../../lib/whatsapp_sdk/api/responses/media_data_response'
9
+ require_relative '../../../lib/whatsapp_sdk/api/responses/success_response'
10
+
11
+ module WhatsappSdk
12
+ module Api
13
+ class Medias < Request
14
+ class FileNotFoundError < StandardError
15
+ attr_reader :file_path
16
+
17
+ def initialize(file_path)
18
+ @file_path = file_path
19
+ super("Couldn't find file_path: #{file_path}")
20
+ end
21
+ end
22
+
23
+ # Get Media by ID.
24
+ #
25
+ # @param media_id [Integer] Media Id.
26
+ # @return [WhatsappSdk::Api::Response] Response object.
27
+ def media(media_id:)
28
+ response = send_request(
29
+ http_method: "get",
30
+ endpoint: "/#{media_id}"
31
+ )
32
+
33
+ WhatsappSdk::Api::Response.new(
34
+ response: response,
35
+ data_class_type: WhatsappSdk::Api::Responses::MediaDataResponse
36
+ )
37
+ end
38
+
39
+ # Download Media by URL.
40
+ #
41
+ # @param media_id [Integer] Media Id.
42
+ # @param file_path [String] The file_path to download the media e.g. "tmp/downloaded_image.png".
43
+ # @return [WhatsappSdk::Api::Response] Response object.
44
+ def download(url:, file_path:)
45
+ response = download_file(url, file_path)
46
+
47
+ response = if response.code.to_i == 200
48
+ { "success" => true }
49
+ else
50
+ { "error" => true, "status" => response.code }
51
+ end
52
+
53
+ WhatsappSdk::Api::Response.new(
54
+ response: response,
55
+ data_class_type: WhatsappSdk::Api::Responses::SuccessResponse,
56
+ error_class_type: WhatsappSdk::Api::Responses::ErrorResponse
57
+ )
58
+ end
59
+
60
+ # Upload a media.
61
+ # @param sender_id [Integer] Sender' phone number.
62
+ # @param file_path [String] Path to the file stored in your local directory. For example: "tmp/whatsapp.png".
63
+ # @param type [String] Media type e.g. text/plain, video/3gp, image/jpeg, image/png. For more information,
64
+ # see the official documentation https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#supported-media-types.
65
+ #
66
+ # @return [WhatsappSdk::Api::Response] Response object.
67
+ def upload(sender_id:, file_path:, type:)
68
+ raise FileNotFoundError, file_path unless File.file?(file_path)
69
+
70
+ params = {
71
+ messaging_product: "whatsapp",
72
+ file: Faraday::FilePart.new(file_path, type),
73
+ type: type
74
+ }
75
+
76
+ response = send_request(http_method: "post", endpoint: "#{sender_id}/media", params: params)
77
+
78
+ WhatsappSdk::Api::Response.new(
79
+ response: response,
80
+ data_class_type: WhatsappSdk::Api::Responses::MediaDataResponse
81
+ )
82
+ end
83
+
84
+ # Delete a Media by ID.
85
+ #
86
+ # @param media_id [Integer] Media Id.
87
+ # @return [WhatsappSdk::Api::Response] Response object.
88
+ def delete(media_id:)
89
+ response = send_request(
90
+ http_method: "delete",
91
+ endpoint: "/#{media_id}"
92
+ )
93
+
94
+ WhatsappSdk::Api::Response.new(
95
+ response: response,
96
+ data_class_type: WhatsappSdk::Api::Responses::SuccessResponse
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end
@@ -27,7 +27,7 @@ module WhatsappSdk
27
27
  to: recipient_number,
28
28
  recipient_type: "individual",
29
29
  type: "text",
30
- "text": { body: message }
30
+ text: { body: message }
31
31
  }
32
32
 
33
33
  response = send_request(
@@ -35,7 +35,10 @@ module WhatsappSdk
35
35
  params: params
36
36
  )
37
37
 
38
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
38
+ WhatsappSdk::Api::Response.new(
39
+ response: response,
40
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
41
+ )
39
42
  end
40
43
 
41
44
  # Send location.
@@ -53,11 +56,11 @@ module WhatsappSdk
53
56
  to: recipient_number,
54
57
  recipient_type: "individual",
55
58
  type: "location",
56
- "location": {
57
- "longitude": longitude,
58
- "latitude": latitude,
59
- "name": name,
60
- "address": address
59
+ location: {
60
+ longitude: longitude,
61
+ latitude: latitude,
62
+ name: name,
63
+ address: address
61
64
  }
62
65
  }
63
66
 
@@ -66,7 +69,10 @@ module WhatsappSdk
66
69
  params: params
67
70
  )
68
71
 
69
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
72
+ WhatsappSdk::Api::Response.new(
73
+ response: response,
74
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
75
+ )
70
76
  end
71
77
 
72
78
  # Send an image.
@@ -97,7 +103,10 @@ module WhatsappSdk
97
103
  params: params
98
104
  )
99
105
 
100
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
106
+ WhatsappSdk::Api::Response.new(
107
+ response: response,
108
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
109
+ )
101
110
  end
102
111
 
103
112
  # Send an audio.
@@ -123,7 +132,10 @@ module WhatsappSdk
123
132
  params: params
124
133
  )
125
134
 
126
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
135
+ WhatsappSdk::Api::Response.new(
136
+ response: response,
137
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
138
+ )
127
139
  end
128
140
 
129
141
  # Send a video.
@@ -154,7 +166,10 @@ module WhatsappSdk
154
166
  params: params
155
167
  )
156
168
 
157
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
169
+ WhatsappSdk::Api::Response.new(
170
+ response: response,
171
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
172
+ )
158
173
  end
159
174
 
160
175
  # Send a document.
@@ -185,7 +200,10 @@ module WhatsappSdk
185
200
  params: params
186
201
  )
187
202
 
188
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
203
+ WhatsappSdk::Api::Response.new(
204
+ response: response,
205
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
206
+ )
189
207
  end
190
208
 
191
209
  # Send a document.
@@ -210,7 +228,10 @@ module WhatsappSdk
210
228
  params: params
211
229
  )
212
230
 
213
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
231
+ WhatsappSdk::Api::Response.new(
232
+ response: response,
233
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
234
+ )
214
235
  end
215
236
 
216
237
  # Send contacts.
@@ -235,7 +256,10 @@ module WhatsappSdk
235
256
  params: params
236
257
  )
237
258
 
238
- WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
259
+ WhatsappSdk::Api::Response.new(
260
+ response: response,
261
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
262
+ )
239
263
  end
240
264
 
241
265
  def send_interactive_button
@@ -269,7 +293,7 @@ module WhatsappSdk
269
293
 
270
294
  WhatsappSdk::Api::Response.new(
271
295
  response: response,
272
- class_type: WhatsappSdk::Api::Responses::ReadMessageDataResponse
296
+ data_class_type: WhatsappSdk::Api::Responses::ReadMessageDataResponse
273
297
  )
274
298
  end
275
299
 
@@ -309,7 +333,7 @@ module WhatsappSdk
309
333
 
310
334
  WhatsappSdk::Api::Response.new(
311
335
  response: response,
312
- class_type: WhatsappSdk::Api::Responses::MessageDataResponse
336
+ data_class_type: WhatsappSdk::Api::Responses::MessageDataResponse
313
337
  )
314
338
  end
315
339
 
@@ -16,8 +16,10 @@ module WhatsappSdk
16
16
  endpoint: "#{business_id}/phone_numbers"
17
17
  )
18
18
 
19
- WhatsappSdk::Api::Response.new(response: response,
20
- class_type: WhatsappSdk::Api::Responses::PhoneNumbersDataResponse)
19
+ WhatsappSdk::Api::Response.new(
20
+ response: response,
21
+ data_class_type: WhatsappSdk::Api::Responses::PhoneNumbersDataResponse
22
+ )
21
23
  end
22
24
 
23
25
  # Get the registered number id.
@@ -30,8 +32,10 @@ module WhatsappSdk
30
32
  endpoint: phone_number_id.to_s
31
33
  )
32
34
 
33
- WhatsappSdk::Api::Response.new(response: response,
34
- class_type: WhatsappSdk::Api::Responses::PhoneNumberDataResponse)
35
+ WhatsappSdk::Api::Response.new(
36
+ response: response,
37
+ data_class_type: WhatsappSdk::Api::Responses::PhoneNumberDataResponse
38
+ )
35
39
  end
36
40
  end
37
41
  end
@@ -3,15 +3,21 @@
3
3
  module WhatsappSdk
4
4
  module Api
5
5
  class Request
6
- API_VERSION = "v13.0"
6
+ API_VERSION = "v14.0"
7
7
  API_CLIENT = "https://graph.facebook.com/#{API_VERSION}/"
8
8
 
9
- def initialize(client)
9
+ def initialize(client = WhatsappSdk.configuration.client)
10
10
  @client = client
11
11
  end
12
12
 
13
- def send_request(endpoint:, http_method: "post", params: {})
14
- @client.send_request(http_method: http_method, endpoint: endpoint, params: params)
13
+ def download_file(url, path_to_file_name = nil)
14
+ @client.download_file(url, path_to_file_name)
15
+ end
16
+
17
+ def send_request(endpoint: nil, full_url: nil, http_method: "post", params: {})
18
+ @client.send_request(
19
+ http_method: http_method, full_url: full_url, endpoint: endpoint, params: params
20
+ )
15
21
  end
16
22
  end
17
23
  end
@@ -4,31 +4,24 @@ require_relative "responses/message_data_response"
4
4
  require_relative "responses/phone_number_data_response"
5
5
  require_relative "responses/phone_numbers_data_response"
6
6
  require_relative "responses/read_message_data_response"
7
- require_relative "responses/error_response"
7
+ require_relative "responses/message_error_response"
8
8
 
9
9
  module WhatsappSdk
10
10
  module Api
11
11
  class Response
12
12
  attr_accessor :error, :data
13
13
 
14
- CLASS_TYPE = {
15
- message_data_response: Responses::MessageDataResponse,
16
- phone_number_data_response: Responses::PhoneNumberDataResponse,
17
- phone_numbers_data_response: Responses::PhoneNumbersDataResponse,
18
- read_message_data_response: Responses::ReadMessageDataResponse
19
- }.freeze
20
-
21
- def initialize(response:, class_type:)
22
- @data = class_type.build_from_response(response: response)
23
- @error = Responses::ErrorResponse.build_from_response(response: response)
14
+ def initialize(response:, data_class_type:, error_class_type: Responses::MessageErrorResponse)
15
+ @data = data_class_type.build_from_response(response: response)
16
+ @error = error_class_type.build_from_response(response: response)
24
17
  end
25
18
 
26
- # Whether or not the response is successful.
19
+ # @return [Boolean] Whether or not the response is successful.
27
20
  def ok?
28
21
  @error.nil?
29
22
  end
30
23
 
31
- # Whether or not the response has an error.
24
+ # @return [Boolean] Whether or not the response has an error.
32
25
  def error?
33
26
  !!@error
34
27
  end
@@ -1,32 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "data_response"
4
+
3
5
  module WhatsappSdk
4
6
  module Api
5
7
  module Responses
6
- class ErrorResponse
7
- attr_reader :code, :subcode, :message, :type, :data, :fbtrace_id
8
+ class ErrorResponse < DataResponse
9
+ attr_accessor :error, :status
8
10
 
9
- def initialize(code:, subcode:, message:, type:, data:, fbtrace_id:)
10
- @code = code
11
- @subcode = subcode
12
- @message = message
13
- @type = type
14
- @data = data
15
- @fbtrace_id = fbtrace_id
11
+ def initialize(response:)
12
+ @error = response["error"]
13
+ @status = response["status"]
14
+ super(response)
16
15
  end
17
16
 
18
17
  def self.build_from_response(response:)
19
- error_response = response["error"]
20
- return unless error_response
18
+ return unless response["error"]
21
19
 
22
- new(
23
- code: error_response["code"],
24
- subcode: error_response["error_subcode"],
25
- message: error_response["message"],
26
- type: error_response["type"],
27
- data: error_response["data"],
28
- fbtrace_id: error_response["fbtrace_id"]
29
- )
20
+ new(response: response)
30
21
  end
31
22
  end
32
23
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "data_response"
4
+
5
+ module WhatsappSdk
6
+ module Api
7
+ module Responses
8
+ class MediaDataResponse < DataResponse
9
+ attr_accessor :id, :url, :mime_type, :sha256, :file_size, :messaging_product
10
+
11
+ def initialize(response)
12
+ @id = response["id"]
13
+ @messaging_product = response["messaging_product"]
14
+ @url = response["url"]
15
+ @mime_type = response["mime_type"]
16
+ @sha256 = response["sha256"]
17
+ @file_size = response["file_size"]
18
+ super(response)
19
+ end
20
+
21
+ def self.build_from_response(response:)
22
+ return unless response["id"]
23
+
24
+ new(response)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error_response"
4
+
5
+ module WhatsappSdk
6
+ module Api
7
+ module Responses
8
+ class MessageErrorResponse < ErrorResponse
9
+ attr_reader :code, :subcode, :message, :type, :data, :fbtrace_id
10
+
11
+ def initialize(response:)
12
+ @code = response["code"]
13
+ @subcode = response["error_subcode"]
14
+ @message = response["message"]
15
+ @type = response["type"]
16
+ @data = response["data"]
17
+ @fbtrace_id = response["fbtrace_id"]
18
+ super(response: response)
19
+ end
20
+
21
+ def self.build_from_response(response:)
22
+ error_response = response["error"]
23
+ return unless error_response
24
+
25
+ new(response: error_response)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -9,10 +9,10 @@ module WhatsappSdk
9
9
  module Api
10
10
  module Responses
11
11
  class ReadMessageDataResponse < DataResponse
12
- attr_reader :sucess
12
+ attr_reader :success
13
13
 
14
14
  def initialize(response:)
15
- @sucess = response["sucess"]
15
+ @success = response["success"]
16
16
  super(response)
17
17
  end
18
18
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "data_response"
4
+
5
+ module WhatsappSdk
6
+ module Api
7
+ module Responses
8
+ class SuccessResponse < DataResponse
9
+ def initialize(response:)
10
+ @success = response["success"]
11
+ super(response)
12
+ end
13
+
14
+ def self.build_from_response(response:)
15
+ return unless response["success"]
16
+
17
+ new(response: response)
18
+ end
19
+
20
+ def success?
21
+ @success
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ # This module allows client instantiating the client as a singleton like the following example:
5
+ # WhatsappSdk.configure do |config|
6
+ # config.access_token = ACCESS_TOKEN
7
+ # end
8
+ #
9
+ # The gem have access to the client through WhatsappSdk.configuration.client
10
+ class Configuration
11
+ attr_accessor :access_token
12
+
13
+ def initialize(access_token = nil)
14
+ @access_token = access_token
15
+ end
16
+
17
+ def client
18
+ return unless access_token
19
+
20
+ WhatsappSdk::Api::Client.new(access_token)
21
+ end
22
+ end
23
+ end
data/lib/whatsapp_sdk.rb CHANGED
@@ -1,31 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # APIs
4
- require_relative "whatsapp_sdk/api/phone_numbers"
5
- require_relative "whatsapp_sdk/api/messages"
6
- require_relative "whatsapp_sdk/api/client"
3
+ require "zeitwerk"
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.setup # ready!
7
6
 
8
- # APIs responses
9
- require_relative "whatsapp_sdk/api/responses/message_data_response"
10
- require_relative "whatsapp_sdk/api/responses/phone_number_data_response"
11
- require_relative "whatsapp_sdk/api/responses/phone_numbers_data_response"
12
- require_relative "whatsapp_sdk/api/responses/error_response"
13
- require_relative "whatsapp_sdk/api/responses/data_response"
14
- require_relative "whatsapp_sdk/api/responses/read_message_data_response"
7
+ module WhatsappSdk
8
+ class << self
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
15
12
 
16
- # Resources
17
- require_relative "whatsapp_sdk/resource/address"
18
- require_relative "whatsapp_sdk/resource/button_parameter"
19
- require_relative "whatsapp_sdk/resource/component"
20
- require_relative "whatsapp_sdk/resource/contact_response"
21
- require_relative "whatsapp_sdk/resource/contact"
22
- require_relative "whatsapp_sdk/resource/currency"
23
- require_relative "whatsapp_sdk/resource/date_time"
24
- require_relative "whatsapp_sdk/resource/email"
25
- require_relative "whatsapp_sdk/resource/media"
26
- require_relative "whatsapp_sdk/resource/message"
27
- require_relative "whatsapp_sdk/resource/name"
28
- require_relative "whatsapp_sdk/resource/org"
29
- require_relative "whatsapp_sdk/resource/parameter_object"
30
- require_relative "whatsapp_sdk/resource/phone_number"
31
- require_relative "whatsapp_sdk/resource/url"
13
+ def configure
14
+ yield(configuration)
15
+ end
16
+ end
17
+ end
data/tmp/whatsapp.png ADDED
Binary file
data/whatsapp_sdk.gemspec CHANGED
@@ -9,10 +9,12 @@ Gem::Specification.new do |spec|
9
9
  spec.version = WhatsappSdk::VERSION
10
10
  spec.authors = ["ignacio-chiazzo"]
11
11
  spec.email = ["ignaciochiazzo@gmail.com"]
12
-
13
- spec.summary = "Use the Whatsapp SDK to comunicate with Whatsapp API"
14
- spec.description = "Ruby Whatsapp SDK. Use the Whatsapp API through this library"
15
- spec.homepage = "https://github.com/ignacio-chiazzo/whatsapp_sdk"
12
+ spec.summary = "Use the Ruby Whatsapp SDK to comunicate with Whatsapp API using the Cloud API"
13
+ spec.description = <<-DESCRIPTION
14
+ Use the Ruby Whatsapp SDK to comunicate with Whatsapp API using the Cloud API.
15
+ Create bots to send and receive messages using the Whatsapp SDK in a few minutes.
16
+ DESCRIPTION
17
+ spec.homepage = "https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk"
16
18
  spec.license = "MIT"
17
19
  spec.required_ruby_version = '>= 1.8.6'
18
20
 
@@ -20,11 +22,11 @@ Gem::Specification.new do |spec|
20
22
  # to allow pushing to a single host or delete this section to allow pushing to any host.
21
23
  if spec.respond_to?(:metadata)
22
24
  spec.metadata["homepage_uri"] = spec.homepage
23
- spec.metadata["source_code_uri"] = "https://github.com/ignacio-chiazzo/whatsapp_sdk"
24
- spec.metadata["changelog_uri"] = "https://github.com/ignacio-chiazzo/whatsapp_sdk/blob/main/CHANGELOG.md"
25
+ spec.metadata["source_code_uri"] = "https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk"
26
+ spec.metadata["changelog_uri"] = "https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk/blob/main/CHANGELOG.md"
25
27
  else
26
28
  raise "RubyGems 2.0 or newer is required to protect against " \
27
- "public gem pushes."
29
+ "public gem pushes."
28
30
  end
29
31
 
30
32
  # Specify which files should be added to the gem when it is released.
@@ -38,8 +40,11 @@ Gem::Specification.new do |spec|
38
40
 
39
41
  spec.add_development_dependency "bundler", "~> 2.3"
40
42
  spec.add_development_dependency "minitest", "~> 5.0"
41
- spec.add_development_dependency "rake", "~> 10.0"
43
+ spec.add_development_dependency "rake", "~> 12.3.3"
42
44
 
43
45
  spec.add_dependency("faraday", "~> 2.3.0")
46
+ spec.add_dependency("faraday-multipart", "~> 1.0.4")
44
47
  spec.add_dependency("oj", "~> 3.13.13")
48
+ spec.add_dependency("zeitwerk", "~> 2.6.0")
49
+ spec.metadata['rubygems_mfa_required'] = 'true'
45
50
  end