whatsapp_sdk 0.0.1

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +22 -0
  3. data/.github/workflows/dependency-review.yml +20 -0
  4. data/.github/workflows/ruby.yml +38 -0
  5. data/.gitignore +23 -0
  6. data/.rubocop.yml +24 -0
  7. data/.travis.yml +7 -0
  8. data/CHANGELOG.md +0 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/Gemfile +21 -0
  11. data/Gemfile.lock +64 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +225 -0
  14. data/Rakefile +12 -0
  15. data/bin/console +15 -0
  16. data/bin/setup +8 -0
  17. data/lib/version.rb +5 -0
  18. data/lib/whatsapp_sdk/api/client.rb +30 -0
  19. data/lib/whatsapp_sdk/api/messages.rb +200 -0
  20. data/lib/whatsapp_sdk/api/phone_numbers.rb +30 -0
  21. data/lib/whatsapp_sdk/api/request.rb +18 -0
  22. data/lib/whatsapp_sdk/api/response.rb +33 -0
  23. data/lib/whatsapp_sdk/api/responses/data_response.rb +19 -0
  24. data/lib/whatsapp_sdk/api/responses/error_response.rb +34 -0
  25. data/lib/whatsapp_sdk/api/responses/message_data_response.rb +38 -0
  26. data/lib/whatsapp_sdk/api/responses/phone_number_data_response.rb +27 -0
  27. data/lib/whatsapp_sdk/api/responses/phone_numbers_data_response.rb +31 -0
  28. data/lib/whatsapp_sdk/error.rb +5 -0
  29. data/lib/whatsapp_sdk/resource/address.rb +36 -0
  30. data/lib/whatsapp_sdk/resource/contact.rb +31 -0
  31. data/lib/whatsapp_sdk/resource/contact_response.rb +14 -0
  32. data/lib/whatsapp_sdk/resource/email.rb +26 -0
  33. data/lib/whatsapp_sdk/resource/message.rb +13 -0
  34. data/lib/whatsapp_sdk/resource/name.rb +32 -0
  35. data/lib/whatsapp_sdk/resource/org.rb +23 -0
  36. data/lib/whatsapp_sdk/resource/phone_number.rb +28 -0
  37. data/lib/whatsapp_sdk/resource/url.rb +26 -0
  38. data/lib/whatsapp_sdk.rb +24 -0
  39. data/whatsapp_sdk.gemspec +45 -0
  40. metadata +154 -0
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "request"
4
+ require_relative "response"
5
+
6
+ module WhatsappSdk
7
+ module Api
8
+ class Messages < Request
9
+ class MissingArgumentError < StandardError
10
+ attr_reader :message
11
+
12
+ def initialize(message)
13
+ @message = message
14
+ super(message)
15
+ end
16
+ end
17
+
18
+ def send_text(sender_id:, recipient_number:, message:)
19
+ params = {
20
+ messaging_product: "whatsapp",
21
+ to: recipient_number,
22
+ recepient_type: "individual",
23
+ type: "text",
24
+ "text": { body: message }
25
+ }
26
+
27
+ response = send_request(
28
+ endpoint: endpoint(sender_id),
29
+ params: params
30
+ )
31
+
32
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
33
+ end
34
+
35
+ def send_location(sender_id:, recipient_number:, longitude:, latitude:, name:, address:)
36
+ params = {
37
+ messaging_product: "whatsapp",
38
+ to: recipient_number,
39
+ recepient_type: "individual",
40
+ type: "location",
41
+ "location": {
42
+ "longitude": longitude,
43
+ "latitude": latitude,
44
+ "name": name,
45
+ "address": address
46
+ }
47
+ }
48
+
49
+ response = send_request(
50
+ endpoint: endpoint(sender_id),
51
+ params: params
52
+ )
53
+
54
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
55
+ end
56
+
57
+ def send_image(sender_id:, recipient_number:, image_id: nil, link: nil, caption: "")
58
+ raise MissingArgumentError, "image_id or link is required" if !image_id && !link
59
+
60
+ params = {
61
+ messaging_product: "whatsapp",
62
+ to: recipient_number,
63
+ recepient_type: "individual",
64
+ type: "image"
65
+ }
66
+ params[:image] = if link
67
+ { link: link, caption: caption }
68
+ else
69
+ { id: image_id, caption: caption }
70
+ end
71
+
72
+ response = send_request(
73
+ endpoint: endpoint(sender_id),
74
+ params: params
75
+ )
76
+
77
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
78
+ end
79
+
80
+ def send_audio(sender_id:, recipient_number:, audio_id: nil, link: nil)
81
+ raise MissingArgumentError, "audio_id or link is required" if !audio_id && !link
82
+
83
+ params = {
84
+ messaging_product: "whatsapp",
85
+ to: recipient_number,
86
+ recepient_type: "individual",
87
+ type: "audio"
88
+ }
89
+ params[:audio] = link ? { link: link } : { id: audio_id }
90
+
91
+ response = send_request(
92
+ endpoint: endpoint(sender_id),
93
+ params: params
94
+ )
95
+
96
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
97
+ end
98
+
99
+ def send_video(sender_id:, recipient_number:, video_id: nil, link: nil, caption: "")
100
+ raise MissingArgumentError, "video_id or link is required" if !video_id && !link
101
+
102
+ params = {
103
+ messaging_product: "whatsapp",
104
+ to: recipient_number,
105
+ recepient_type: "individual",
106
+ type: "video"
107
+ }
108
+ params[:video] = if link
109
+ { link: link, caption: caption }
110
+ else
111
+ { id: video_id, caption: caption }
112
+ end
113
+
114
+ response = send_request(
115
+ endpoint: endpoint(sender_id),
116
+ params: params
117
+ )
118
+
119
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
120
+ end
121
+
122
+ def send_document(sender_id:, recipient_number:, document_id: nil, link: nil, caption: "")
123
+ raise MissingArgumentError, "document or link is required" if !document_id && !link
124
+
125
+ params = {
126
+ messaging_product: "whatsapp",
127
+ to: recipient_number,
128
+ recepient_type: "individual",
129
+ type: "document"
130
+ }
131
+ params[:document] = if link
132
+ { link: link, caption: caption }
133
+ else
134
+ { id: document_id, caption: caption }
135
+ end
136
+
137
+ response = send_request(
138
+ endpoint: endpoint(sender_id),
139
+ params: params
140
+ )
141
+
142
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
143
+ end
144
+
145
+ def send_sticker(sender_id:, recipient_number:, sticker_id: nil, link: nil)
146
+ raise MissingArgumentError, "sticker or link is required" if !sticker_id && !link
147
+
148
+ params = {
149
+ messaging_product: "whatsapp",
150
+ to: recipient_number,
151
+ recepient_type: "individual",
152
+ type: "sticker"
153
+ }
154
+ params[:sticker] = link ? { link: link } : { id: sticker_id }
155
+
156
+ response = send_request(
157
+ endpoint: endpoint(sender_id),
158
+ params: params
159
+ )
160
+
161
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
162
+ end
163
+
164
+ def send_contacts(sender_id:, recipient_number:, contacts: nil, contacts_json: {})
165
+ params = {
166
+ messaging_product: "whatsapp",
167
+ to: recipient_number,
168
+ recepient_type: "individual",
169
+ type: "contacts"
170
+ }
171
+ params[:contacts] = contacts ? contacts.map(&:to_h) : contacts_json
172
+
173
+ response = send_request(
174
+ endpoint: endpoint(sender_id),
175
+ params: params
176
+ )
177
+
178
+ WhatsappSdk::Api::Response.new(response: response, class_type: WhatsappSdk::Api::Responses::MessageDataResponse)
179
+ end
180
+
181
+ def send_interactive_button
182
+ # TODO: https://developers.facebook.com/docs/whatsapp_sdk/cloud-api/reference/messages#contacts-object
183
+ end
184
+
185
+ def send_interactive_reply_buttons
186
+ # TODO: https://developers.facebook.com/docs/whatsapp_sdk/cloud-api/reference/messages#contacts-object
187
+ end
188
+
189
+ def send_interactive_section
190
+ # TODO: https://developers.facebook.com/docs/whatsapp_sdk/cloud-api/reference/messages#contacts-object
191
+ end
192
+
193
+ private
194
+
195
+ def endpoint(sender_id)
196
+ "#{sender_id}/messages"
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "request"
4
+ require_relative "response"
5
+
6
+ module WhatsappSdk
7
+ module Api
8
+ class PhoneNumbers < Request
9
+ def registered_numbers(business_id)
10
+ response = send_request(
11
+ http_method: "get",
12
+ endpoint: "#{business_id}/phone_numbers"
13
+ )
14
+
15
+ WhatsappSdk::Api::Response.new(response: response,
16
+ class_type: WhatsappSdk::Api::Responses::PhoneNumbersDataResponse)
17
+ end
18
+
19
+ def registered_number(phone_number_id)
20
+ response = send_request(
21
+ http_method: "get",
22
+ endpoint: phone_number_id.to_s
23
+ )
24
+
25
+ WhatsappSdk::Api::Response.new(response: response,
26
+ class_type: WhatsappSdk::Api::Responses::PhoneNumberDataResponse)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Api
5
+ class Request
6
+ API_VERSION = "v13.0"
7
+ API_CLIENT = "https://graph.facebook.com/#{API_VERSION}/"
8
+
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def send_request(endpoint:, http_method: "post", params: {})
14
+ @client.send_request(http_method: http_method, endpoint: endpoint, params: params)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "responses/message_data_response"
4
+ require_relative "responses/phone_number_data_response"
5
+ require_relative "responses/phone_numbers_data_response"
6
+ require_relative "responses/error_response"
7
+
8
+ module WhatsappSdk
9
+ module Api
10
+ class Response
11
+ attr_accessor :error, :data
12
+
13
+ CLASS_TYPE = {
14
+ message_data_response: Responses::MessageDataResponse,
15
+ phone_number_data_response: Responses::PhoneNumberDataResponse,
16
+ phone_numbers_data_response: Responses::PhoneNumbersDataResponse
17
+ }.freeze
18
+
19
+ def initialize(response:, class_type:)
20
+ @data = class_type.build_from_response(response: response)
21
+ @error = Responses::ErrorResponse.build_from_response(response: response)
22
+ end
23
+
24
+ def ok?
25
+ @error.nil?
26
+ end
27
+
28
+ def error?
29
+ !!@error
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Api
5
+ module Responses
6
+ class DataResponse
7
+ attr_reader :raw_data_response
8
+
9
+ def initialize(response)
10
+ @raw_data_response = response
11
+ end
12
+
13
+ def self.build_from_response(response:) # rubocop:disable Lint/UnusedMethodArgument:
14
+ raise NotImplemented
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Api
5
+ module Responses
6
+ class ErrorResponse
7
+ attr_reader :code, :subcode, :message, :type, :data, :fbtrace_id
8
+
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
16
+ end
17
+
18
+ def self.build_from_response(response:)
19
+ error_response = response["error"]
20
+ return unless error_response
21
+
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
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../request"
4
+ require_relative "data_response"
5
+ require_relative "../../resource/message"
6
+ require_relative "../../resource/contact_response"
7
+
8
+ module WhatsappSdk
9
+ module Api
10
+ module Responses
11
+ class MessageDataResponse < DataResponse
12
+ attr_reader :contacts, :messages
13
+
14
+ def initialize(response:)
15
+ @contacts = response["contacts"]&.map { |contact_json| parse_contact(contact_json) }
16
+ @messages = response["messages"]&.map { |contact_json| parse_message(contact_json) }
17
+ super(response)
18
+ end
19
+
20
+ def self.build_from_response(response:)
21
+ return unless response["messages"]
22
+
23
+ new(response: response)
24
+ end
25
+
26
+ private
27
+
28
+ def parse_message(message_json)
29
+ ::WhatsappSdk::Resource::Message.new(id: message_json["id"])
30
+ end
31
+
32
+ def parse_contact(contact_json)
33
+ ::WhatsappSdk::Resource::ContactResponse.new(input: contact_json["input"], wa_id: contact_json["wa_id"])
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "data_response"
4
+
5
+ module WhatsappSdk
6
+ module Api
7
+ module Responses
8
+ class PhoneNumberDataResponse < DataResponse
9
+ attr_accessor :id, :verified_name, :display_phone_number, :quality_rating
10
+
11
+ def initialize(response)
12
+ @id = response["id"]
13
+ @verified_name = response["verified_name"]
14
+ @display_phone_number = response["display_phone_number"]
15
+ @quality_rating = response["quality_rating"]
16
+ super(response)
17
+ end
18
+
19
+ def self.build_from_response(response:)
20
+ return unless response["id"]
21
+
22
+ new(response)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "data_response"
4
+ require_relative "phone_number_data_response"
5
+
6
+ module WhatsappSdk
7
+ module Api
8
+ module Responses
9
+ class PhoneNumbersDataResponse < DataResponse
10
+ attr_reader :phone_numbers
11
+
12
+ def initialize(response)
13
+ @phone_numbers = response['data']&.map { |phone_number| parse_phone_number(phone_number) }
14
+ super(response)
15
+ end
16
+
17
+ def self.build_from_response(response:)
18
+ return unless response["data"]
19
+
20
+ new(response)
21
+ end
22
+
23
+ private
24
+
25
+ def parse_phone_number(phone_number)
26
+ PhoneNumberDataResponse.new(phone_number)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class Address
6
+ attr_accessor :street, :city, :state, :zip, :country, :country_code, :typ
7
+
8
+ ADDRESS_TYPE = {
9
+ home: "HOME",
10
+ work: "WORK"
11
+ }.freeze
12
+
13
+ def initialize(street:, city:, state:, zip:, country:, country_code:, type: ADDRESS_TYPE::HOME)
14
+ @street = street
15
+ @city = city
16
+ @state = state
17
+ @zip = zip
18
+ @country = country
19
+ @country_code = country_code
20
+ @type = type
21
+ end
22
+
23
+ def to_h
24
+ {
25
+ street: @street,
26
+ city: @city,
27
+ state: @state,
28
+ zip: @zip,
29
+ country: @country,
30
+ country_code: @country_code,
31
+ type: @type
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class Contact
6
+ attr_accessor :addresses, :birthday, :emails, :name, :org, :phones, :urls
7
+
8
+ def initialize(addresses:, birthday:, emails:, name:, org:, phones:, urls:)
9
+ @addresses = addresses
10
+ @birthday = birthday
11
+ @emails = emails
12
+ @name = name
13
+ @org = org
14
+ @phones = phones
15
+ @urls = urls
16
+ end
17
+
18
+ def to_h
19
+ {
20
+ addresses: addresses.map(&:to_h),
21
+ birthday: birthday,
22
+ emails: emails.map(&:to_h),
23
+ name: name.to_h,
24
+ org: org.to_h,
25
+ phones: phones.map(&:to_h),
26
+ urls: urls.map(&:to_h)
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class ContactResponse
6
+ attr_accessor :input, :wa_id
7
+
8
+ def initialize(input:, wa_id:)
9
+ @input = input
10
+ @wa_id = wa_id
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class Email
6
+ attr_accessor :email, :type
7
+
8
+ EMAIL_TYPE = {
9
+ home: "HOME",
10
+ work: "WORK"
11
+ }.freeze
12
+
13
+ def initialize(email:, type:)
14
+ @email = email
15
+ @type = type
16
+ end
17
+
18
+ def to_h
19
+ {
20
+ email: @email,
21
+ type: @type
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class Message
6
+ attr_reader :id
7
+
8
+ def initialize(id:)
9
+ @id = id
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class Name
6
+ attr_accessor :formatted_name, :first_name, :last_name, :middle_name, :suffix, :prefix
7
+
8
+ def initialize(
9
+ formatted_name: nil, first_name: nil,
10
+ last_name: nil, middle_name: nil, suffix: nil, prefix: nil
11
+ )
12
+ @formatted_name = formatted_name
13
+ @first_name = first_name
14
+ @last_name = last_name
15
+ @middle_name = middle_name
16
+ @suffix = suffix
17
+ @prefix = prefix
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ formatted_name: @formatted_name,
23
+ first_name: @first_name,
24
+ last_name: @last_name,
25
+ middle_name: @middle_name,
26
+ suffix: @suffix,
27
+ prefix: @prefix
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class Org
6
+ attr_accessor :company, :department, :title
7
+
8
+ def initialize(company:, department:, title:)
9
+ @company = company
10
+ @department = department
11
+ @title = title
12
+ end
13
+
14
+ def to_h
15
+ {
16
+ company: @company,
17
+ department: @department,
18
+ title: @title
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class PhoneNumber
6
+ attr_accessor :phone, :type, :wa_id
7
+
8
+ PHONE_NUMBER_TYPE = {
9
+ home: "HOME",
10
+ work: "WORK"
11
+ }.freeze
12
+
13
+ def initialize(phone:, type:, wa_id:)
14
+ @phone = phone
15
+ @type = type
16
+ @wa_id = wa_id
17
+ end
18
+
19
+ def to_h
20
+ {
21
+ phone: @phone,
22
+ type: @type,
23
+ wa_id: @wa_id
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WhatsappSdk
4
+ module Resource
5
+ class Url
6
+ attr_accessor :url, :type
7
+
8
+ ADDRESS_TYPE = {
9
+ home: "HOME",
10
+ work: "WORK"
11
+ }.freeze
12
+
13
+ def initialize(url:, type:)
14
+ @url = url
15
+ @type = type
16
+ end
17
+
18
+ def to_h
19
+ {
20
+ url: @url,
21
+ type: @type
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end