whatsapp_sdk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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