cloud-waba-ruby-client 0.0.3 → 0.0.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: add418afadc7db2c4e164bc742a67bb82b5b372d7bf84f956fbc8d7c2d948930
4
- data.tar.gz: c210daa37769103a78c3f290d75e5af35cc92104b1f2b1b6f21918d24a2e8cac
3
+ metadata.gz: 776fe9551683b144357021a087daf8be4600e8bed893552017ded5d0ddbf9306
4
+ data.tar.gz: 9f6118a9123f470b303a255ba782a7db3349abfcc649c4d391f04c4e4aeef473
5
5
  SHA512:
6
- metadata.gz: f1c12beadeedf7f0fa3b8268b9b8d548ebb362c11e1626b88a4b7de3ef28f55a131c89dbb2d1c3f894e68c8485177fa7b490927ddb82cc87ba5475b4b17465d2
7
- data.tar.gz: 43de35076e075ceb5eb08307b975f78326d6a7ab97bc89bf8ce07498319be284ec777548e6ec6023b36178f8166c463c91639ba7c71dc7e474d8e0b60fb3c268
6
+ metadata.gz: 849442cf4206504c78a3552d75eb87c27c3a9e0b68ac67e28ff4644706cc3d3048d9a634aadbc7dc4abf2fa0566848165e800ce27275adb70ab359f706ecd395
7
+ data.tar.gz: da26c3bc79ef14949ae2aba44816685a9e10a02bbb49f52892dc72ee9608a117131ccffdf55ce0076dc4f9eae1637a3319c22e3664e68101258650626f2b6d84
data/README.md CHANGED
@@ -7,7 +7,7 @@ Ruby wrapper for [Cloud WhatsApp Business API](https://developers.facebook.com/d
7
7
  Add following line to your Gemfile:
8
8
 
9
9
  ```ruby
10
- gem 'cloud-waba-ruby-client', '~> 0.0.2'
10
+ gem 'cloud-waba-ruby-client', '~> 0.0.5'
11
11
  ```
12
12
 
13
13
  then run
@@ -134,6 +134,14 @@ phone_1 = ::CloudWaba::Models::Messages::ContactPhone.new(type: ::CloudWaba::Mod
134
134
  contacts = [::CloudWaba::Models::Messages::Contact.new(name: name, phones: [phone_1])]
135
135
  client.messages.send_contact(contacts: contacts,recipient: "+201XXXXXXXXX")
136
136
  ```
137
+
138
+ ### Sending reaction on a message
139
+ ```ruby
140
+ require "cloud_waba/client"
141
+
142
+ client = CloudWaba::Client.new
143
+ client.messages.send_reaction(recipient: "+201XXXXXXXXX", emoji: "💙", reply_message_id: "wamid.HBgMMjAxMjAxMzIyMzMxFQIAEZgSQTU5QkExMUUyQlRCNTU1NTVEAA==")
144
+ ```
137
145
  <!-- ### Sending template with header, body, footer and buttons
138
146
 
139
147
  ### Sending template with header, body, footer and buttons (variables)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cloud-waba-ruby-client"
3
- s.version = "0.0.3"
3
+ s.version = "0.0.5"
4
4
  s.summary = "Cloud Waba Client for Ruby."
5
5
  s.description = "A simple API wrapper for Cloud Whatsapp Business API"
6
6
  s.authors = ["Ahmed Bassell"]
@@ -1,7 +1,7 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- # require 'pry'
4
+ require 'pry'
5
5
 
6
6
  module API
7
7
  module Messages
@@ -37,7 +37,8 @@ module API
37
37
 
38
38
  payload["context"] = { "message_id": reply_message_id } unless reply_message_id.nil?
39
39
 
40
- response = http_client.post(body: payload, headers: {})
40
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
41
+
41
42
  parsed_response = JSON.parse(response.body.to_s)
42
43
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
43
44
  end
@@ -58,7 +59,7 @@ module API
58
59
  payload["image"] = { link: link, caption: caption }
59
60
  payload["context"] = { "message_id": reply_message_id } unless reply_message_id.nil?
60
61
 
61
- response = http_client.post(body: payload, headers: {})
62
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
62
63
  parsed_response = JSON.parse(response.body.to_s)
63
64
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
64
65
  end
@@ -79,7 +80,7 @@ module API
79
80
  payload["audio"] = { link: link }
80
81
  payload["context"] = { "message_id": reply_message_id } unless reply_message_id.nil?
81
82
 
82
- response = http_client.post(body: payload, headers: {})
83
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
83
84
  parsed_response = JSON.parse(response.body.to_s)
84
85
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
85
86
  end
@@ -100,7 +101,7 @@ module API
100
101
  payload["video"] = { link: link, caption: caption }
101
102
  payload["context"] = { "message_id": reply_message_id } unless reply_message_id.nil?
102
103
 
103
- response = http_client.post(body: payload, headers: {})
104
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
104
105
  parsed_response = JSON.parse(response.body.to_s)
105
106
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
106
107
  end
@@ -121,7 +122,7 @@ module API
121
122
  payload["document"] = { link: link, caption: caption }
122
123
  payload["context"] = { "message_id": reply_message_id } unless reply_message_id.nil?
123
124
 
124
- response = http_client.post(body: payload, headers: {})
125
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
125
126
  parsed_response = JSON.parse(response.body.to_s)
126
127
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
127
128
  end
@@ -147,7 +148,7 @@ module API
147
148
  }
148
149
  payload["context"] = { "message_id": reply_message_id } unless reply_message_id.nil?
149
150
 
150
- response = http_client.post(body: payload, headers: {})
151
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
151
152
  parsed_response = JSON.parse(response.body.to_s)
152
153
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
153
154
  end
@@ -168,11 +169,49 @@ module API
168
169
  payload["contacts"] = contacts.map(&:serialize)
169
170
  payload["context"] = { "message_id": reply_message_id } unless reply_message_id.nil?
170
171
 
171
- response = http_client.post(body: payload, headers: {})
172
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
173
+ parsed_response = JSON.parse(response.body.to_s)
174
+ ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
175
+ end
176
+
177
+ # curl -X POST \
178
+ # 'https://graph.facebook.com/v18.0/FROM_PHONE_NUMBER_ID/messages' \
179
+ # -H 'Authorization: Bearer ACCESS_TOKEN' \
180
+ # -H 'Content-Type: application/json' \
181
+ # -d '{
182
+ # "messaging_product": "whatsapp",
183
+ # "recipient_type": "individual",
184
+ # "to": "PHONE_NUMBER",
185
+ # "type": "reaction",
186
+ # "reaction": {
187
+ # "message_id": "wamid.HBgLM...",
188
+ # "emoji": "\uD83D\uDE00"
189
+ # }
190
+ # }'
191
+ sig do
192
+ params(recipient: ::String, emoji: ::String, reply_message_id: ::String).returns(::CloudWaba::Models::Messages::Response)
193
+ end
194
+ def send_reaction(recipient:, emoji:, reply_message_id: nil)
195
+ reaction_type = "reaction"
196
+
197
+ payload = {
198
+ "messaging_product": MESSAGING_PRODUCT,
199
+ "recipient_type": RECIPIENT_TYPE,
200
+ "to": recipient,
201
+ "type": reaction_type,
202
+ }
203
+
204
+ payload["reaction"] = {
205
+ "message_id": reply_message_id,
206
+ "emoji": emoji
207
+ }
208
+
209
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
172
210
  parsed_response = JSON.parse(response.body.to_s)
173
211
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
174
212
  end
175
213
 
214
+
176
215
  sig do
177
216
  params(
178
217
  recipient: ::String,
@@ -198,11 +237,21 @@ module API
198
237
  }
199
238
  }
200
239
 
201
- response = http_client.post(body: payload, headers: {})
240
+ response = with_error_handling { http_client.post(body: payload, headers: {}) }
202
241
  parsed_response = JSON.parse(response.body.to_s)
203
242
  ::CloudWaba::Models::Messages::Response.parse(hash: parsed_response)
204
243
  end
205
244
 
245
+ sig do
246
+ params(media_id: ::String).returns(::CloudWaba::Models::Media::Response)
247
+ end
248
+ def fetch_media_url(media_id:)
249
+ url = "#{@config.base_url}/#{@config.api_version}/#{media_id}"
250
+ response = with_error_handling { http_client.get(url) }
251
+ parsed_response = JSON.parse(response.body.to_s)
252
+ ::CloudWaba::Models::Media::Response.parse(hash: parsed_response)
253
+ end
254
+
206
255
  private
207
256
 
208
257
  def http_client
@@ -212,6 +261,25 @@ module API
212
261
  def messages_endpoint
213
262
  "#{@config.base_url}/#{@config.api_version}/#{@config.phone_number_id}/messages"
214
263
  end
264
+
265
+ def with_error_handling
266
+ return unless block_given?
267
+
268
+ response = yield
269
+ parsed_response = JSON.parse(response.body.to_s)
270
+ error_message = parsed_response.dig("error", "message")
271
+
272
+ case response.code
273
+ when 400
274
+ raise ::CloudWaba::Errors::BadRequest, error_message
275
+ when 401
276
+ raise ::CloudWaba::Errors::Unauthorized, error_message
277
+ else
278
+ # Success
279
+ end
280
+
281
+ response
282
+ end
215
283
  end
216
284
  end
217
285
  end
@@ -21,7 +21,7 @@ module API
21
21
  end
22
22
  def list(limit: 20)
23
23
  fields = "id,name,category,language,status"
24
- response = templates_client.get(params: { fields: fields, limit: limit })
24
+ response = with_error_handling { templates_client.get(params: { fields: fields, limit: limit }) }
25
25
 
26
26
  parsed_response = JSON.parse(response.body.to_s)
27
27
  templates = parsed_response["data"].map{|hash| ::CloudWaba::Models::Templates::Response.parse(template_hash: hash)}
@@ -47,7 +47,7 @@ module API
47
47
  "components": components.map(&:serialize)
48
48
  }
49
49
 
50
- response = templates_client.post(body: payload)
50
+ response = with_error_handling { templates_client.post(body: payload) }
51
51
  ::CloudWaba::Models::Templates::Response.parse(response: response)
52
52
  end
53
53
 
@@ -64,7 +64,7 @@ module API
64
64
  "category": category.serialize,
65
65
  "components": components.map(&:serialize)
66
66
  }
67
- response = client.post(body: payload)
67
+ response = with_error_handling { client.post(body: payload) }
68
68
  parsed_response = JSON.parse(response.body.to_s)
69
69
 
70
70
  parsed_response["success"] || false
@@ -80,7 +80,7 @@ module API
80
80
  params = { name: name }
81
81
  params[:hsm_id] = template_id unless template_id.nil?
82
82
 
83
- response = templates_client.delete(params: params)
83
+ response = with_error_handling { templates_client.delete(params: params) }
84
84
  parsed_response = JSON.parse(response.body.to_s)
85
85
 
86
86
  parsed_response["success"] || false
@@ -103,6 +103,25 @@ module API
103
103
  def template_endpoint(template_id:)
104
104
  "#{@config.base_url}/#{@config.api_version}/#{template_id}"
105
105
  end
106
+
107
+ def with_error_handling
108
+ return unless block_given?
109
+
110
+ response = yield
111
+ parsed_response = JSON.parse(response.body.to_s)
112
+ error_message = parsed_response.dig("error", "message")
113
+
114
+ case response.code
115
+ when 400
116
+ raise ::CloudWaba::Errors::BadRequest, error_message
117
+ when 401
118
+ raise ::CloudWaba::Errors::Unauthorized, error_message
119
+ else
120
+ # Success
121
+ end
122
+
123
+ response
124
+ end
106
125
  end
107
126
  end
108
127
  end
@@ -10,6 +10,7 @@ require_relative "models/enums/templates/component_type"
10
10
  require_relative "models/enums/templates/button_type"
11
11
  require_relative "models/enums/templates/format"
12
12
  require_relative "models/enums/contact_phone_type"
13
+
13
14
  # Models
14
15
  require_relative "models/config"
15
16
  require_relative "models/contact"
@@ -17,6 +18,7 @@ require_relative "models/messages/response"
17
18
  require_relative "models/messages/contact_name"
18
19
  require_relative "models/messages/contact_phone"
19
20
  require_relative "models/messages/contact"
21
+ require_relative "models/media/response"
20
22
 
21
23
  require_relative "models/templates/example"
22
24
  require_relative "models/templates/component"
@@ -35,6 +37,12 @@ require_relative "models/templates/buttons_component"
35
37
  require_relative "models/templates/message_template"
36
38
  require_relative "models/templates/response"
37
39
  require_relative "models/templates/list"
40
+
41
+ # Errors
42
+ require_relative "errors/unauthorized"
43
+ require_relative "errors/bad_request"
44
+ require_relative "errors/missing_parameter"
45
+
38
46
  # Helpers
39
47
  require_relative "utils"
40
48
  require_relative "http_client"
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module CloudWaba
5
+ module Errors
6
+ class BadRequest < ::StandardError
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module CloudWaba
5
+ module Errors
6
+ class MissingParameter < ::StandardError
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module CloudWaba
5
+ module Errors
6
+ class Unauthorized < ::StandardError
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ # require 'pry'
5
+
6
+ module CloudWaba
7
+ module Models
8
+ module Media
9
+ class Response < T::Struct
10
+ extend ::T::Sig
11
+ extend ::T::Helpers
12
+ ##
13
+ # {
14
+ # "messaging_product": "whatsapp",
15
+ # "url": "<URL>",
16
+ # "mime_type": "<MIME_TYPE>",
17
+ # "sha256": "<HASH>",
18
+ # "file_size": "<FILE_SIZE>",
19
+ # "id": "<MEDIA_ID>"
20
+ # }
21
+ prop :messaging_product, ::CloudWaba::Models::Enums::MessagingProduct
22
+ prop :url, ::String
23
+ prop :mime_type, ::String
24
+ prop :sha256, ::String
25
+ prop :file_size, ::Integer
26
+ prop :id, ::String
27
+
28
+ sig { params(hash: ::T::Hash[::T.untyped, ::T.untyped]).returns(::CloudWaba::Models::Media::Response) }
29
+ def self.parse(hash:)
30
+ id = hash["id"]
31
+ url = hash["url"]
32
+ mime_type = hash["mime_type"]
33
+ sha256 = hash["sha256"]
34
+ file_size = hash["file_size"]
35
+
36
+ self.new(
37
+ messaging_product: ::CloudWaba::Models::Enums::MessagingProduct::WhatsApp,
38
+ url: url,
39
+ mime_type: mime_type,
40
+ sha256: sha256,
41
+ file_size: file_size,
42
+ id: id,
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -18,7 +18,6 @@ module CloudWaba
18
18
  end
19
19
  def self.import_config(app_id:, app_secret:, phone_number_id:, business_account_id:, access_token:, api_version:)
20
20
  Dotenv.load
21
- validate_config!
22
21
 
23
22
  base_url = ENV.fetch("WA_BASE_URL", "https://graph.facebook.com")
24
23
  app_id = app_id || ENV.fetch("M4D_APP_ID", "")
@@ -28,6 +27,14 @@ module CloudWaba
28
27
  access_token = access_token || ENV.fetch("CLOUD_API_ACCESS_TOKEN", "")
29
28
  api_version = api_version || ENV.fetch("CLOUD_API_VERSION", "v16.0")
30
29
 
30
+ validate_config!(
31
+ app_id: app_id,
32
+ app_secret: app_secret,
33
+ phone_number_id: phone_number_id,
34
+ business_account_id: business_account_id,
35
+ access_token: access_token
36
+ )
37
+
31
38
  ::CloudWaba::Models::Config.new(
32
39
  base_url: base_url,
33
40
  app_id: app_id,
@@ -41,9 +48,28 @@ module CloudWaba
41
48
 
42
49
  private
43
50
 
44
- sig { void }
45
- def self.validate_config!
46
- # TODO: Validate configuration
51
+ sig { params(
52
+ app_id: ::T.nilable(::String),
53
+ app_secret: ::T.nilable(::String),
54
+ phone_number_id: ::T.nilable(::String),
55
+ business_account_id: ::T.nilable(::String),
56
+ access_token: ::T.nilable(::String)).void }
57
+ def self.validate_config!(app_id:, app_secret:, phone_number_id:, business_account_id:, access_token:)
58
+ error_message = nil
59
+
60
+ if app_id.nil? || app_id.empty?
61
+ error_message = "app_id parameter is missing"
62
+ elsif app_secret.nil? || app_secret.empty?
63
+ error_message = "app_secret parameter is missing"
64
+ elsif phone_number_id.nil? || phone_number_id.empty?
65
+ error_message = "phone_number_id parameter is missing"
66
+ elsif business_account_id.nil? || business_account_id.empty?
67
+ error_message = "business_account_id parameter is missing"
68
+ elsif access_token.nil? || access_token.empty?
69
+ error_message = "access_token parameter is missing"
70
+ end
71
+
72
+ raise ::CloudWaba::Errors::MissingParameter, error_message unless error_message.nil?
47
73
  end
48
74
  end
49
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloud-waba-ruby-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ahmed Bassell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-05 00:00:00.000000000 Z
11
+ date: 2024-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -112,6 +112,9 @@ files:
112
112
  - lib/api/messages/service.rb
113
113
  - lib/api/templates/service.rb
114
114
  - lib/cloud_waba/client.rb
115
+ - lib/cloud_waba/errors/bad_request.rb
116
+ - lib/cloud_waba/errors/missing_parameter.rb
117
+ - lib/cloud_waba/errors/unauthorized.rb
115
118
  - lib/cloud_waba/http_client.rb
116
119
  - lib/cloud_waba/models/config.rb
117
120
  - lib/cloud_waba/models/contact.rb
@@ -121,6 +124,7 @@ files:
121
124
  - lib/cloud_waba/models/enums/templates/category.rb
122
125
  - lib/cloud_waba/models/enums/templates/component_type.rb
123
126
  - lib/cloud_waba/models/enums/templates/format.rb
127
+ - lib/cloud_waba/models/media/response.rb
124
128
  - lib/cloud_waba/models/messages/contact.rb
125
129
  - lib/cloud_waba/models/messages/contact_name.rb
126
130
  - lib/cloud_waba/models/messages/contact_phone.rb