chat_sdk-whatsapp 1.1.0 → 1.2.0
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 +4 -4
- data/lib/chat_sdk/whatsapp/adapter.rb +69 -20
- data/lib/chat_sdk/whatsapp/api_client.rb +10 -9
- data/lib/chat_sdk/whatsapp/event_parser.rb +21 -4
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d3aae7e3166ec158db8bb11d3a5fbd88e6df312846caf3208ba154ffbd65c200
|
|
4
|
+
data.tar.gz: 99c40c41b5cb2677c56add71a5717c18b8d91fcc3725b579e81e58f537d55186
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9848398b73448697e4432c5a669607a151823c479021e825558d73b1be6e48cae051be2572c9501d3f9060b95e9b38223082cc942e76cd9464261dd4432b7bb6
|
|
7
|
+
data.tar.gz: 0b5d5837929611cd82558baffdcace85decc3ed2dac403f5a32c1bc10c0c465d3002644a29be7e8a5b105c8e7c3a422010c74ddbf10208a5636d9c595e79cd9a
|
|
@@ -44,19 +44,20 @@ module ChatSDK
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Outbound
|
|
47
|
-
def post_message(channel_id:, message:, thread_id: nil)
|
|
47
|
+
def post_message(channel_id:, message:, thread_id: nil)
|
|
48
|
+
phone_number_id = outbound_phone_number_id(channel_id, thread_id)
|
|
48
49
|
payload = prepare_message_payload(message)
|
|
49
50
|
|
|
50
51
|
result = if payload[:type] == "text" && payload.dig(:text, :body)
|
|
51
52
|
chunks = split_message(payload[:text][:body])
|
|
52
53
|
chunks.reduce(nil) do |_, chunk|
|
|
53
|
-
@client.send_message(to: channel_id, type: "text", text: {body: chunk})
|
|
54
|
+
@client.send_message(to: channel_id, type: "text", phone_number_id: phone_number_id, text: {body: chunk})
|
|
54
55
|
end
|
|
55
56
|
else
|
|
56
|
-
@client.send_message(to: channel_id, **payload)
|
|
57
|
+
@client.send_message(to: channel_id, phone_number_id: phone_number_id, **payload)
|
|
57
58
|
end
|
|
58
59
|
|
|
59
|
-
parse_whatsapp_message(result, channel_id)
|
|
60
|
+
parse_whatsapp_message(result, channel_id, phone_number_id: phone_number_id)
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
def self.template_components(header: nil, body_params: [], button_payloads: [])
|
|
@@ -88,19 +89,30 @@ module ChatSDK
|
|
|
88
89
|
components
|
|
89
90
|
end
|
|
90
91
|
|
|
91
|
-
def post_template(channel_id:, template_name:, language_code: "en", components: nil)
|
|
92
|
-
|
|
92
|
+
def post_template(channel_id:, template_name:, language_code: "en", components: nil, thread_id: nil)
|
|
93
|
+
phone_number_id = outbound_phone_number_id(channel_id, thread_id)
|
|
94
|
+
@client.send_template(
|
|
95
|
+
to: channel_id,
|
|
96
|
+
template_name: template_name,
|
|
97
|
+
language_code: language_code,
|
|
98
|
+
components: components,
|
|
99
|
+
phone_number_id: phone_number_id
|
|
100
|
+
)
|
|
93
101
|
end
|
|
94
102
|
|
|
95
103
|
def mark_as_read(message_id:, channel_id: nil, thread_id: nil, message: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
96
|
-
@client.mark_as_read(
|
|
104
|
+
@client.mark_as_read(
|
|
105
|
+
message_id: message_id,
|
|
106
|
+
phone_number_id: outbound_phone_number_id(channel_id, thread_id)
|
|
107
|
+
)
|
|
97
108
|
end
|
|
98
109
|
|
|
99
|
-
def reply_message(channel_id:, message_id:, message:, thread_id: nil)
|
|
110
|
+
def reply_message(channel_id:, message_id:, message:, thread_id: nil)
|
|
111
|
+
phone_number_id = outbound_phone_number_id(channel_id, thread_id)
|
|
100
112
|
payload = prepare_message_payload(message)
|
|
101
113
|
payload[:context] = {message_id: message_id}
|
|
102
|
-
result = @client.send_message(to: channel_id, **payload)
|
|
103
|
-
parse_whatsapp_message(result, channel_id)
|
|
114
|
+
result = @client.send_message(to: channel_id, phone_number_id: phone_number_id, **payload)
|
|
115
|
+
parse_whatsapp_message(result, channel_id, phone_number_id: phone_number_id)
|
|
104
116
|
end
|
|
105
117
|
|
|
106
118
|
def download_media(media_id:)
|
|
@@ -112,27 +124,48 @@ module ChatSDK
|
|
|
112
124
|
response.body
|
|
113
125
|
end
|
|
114
126
|
|
|
115
|
-
def upload_file(channel_id:, io:, filename:, thread_id: nil, comment: nil)
|
|
127
|
+
def upload_file(channel_id:, io:, filename:, thread_id: nil, comment: nil)
|
|
128
|
+
phone_number_id = outbound_phone_number_id(channel_id, thread_id)
|
|
116
129
|
content_type = detect_content_type(filename)
|
|
117
130
|
media_type = media_type_for(content_type)
|
|
118
131
|
|
|
119
132
|
# Upload media first
|
|
120
|
-
media_result = @client.upload_media(
|
|
133
|
+
media_result = @client.upload_media(
|
|
134
|
+
io: io,
|
|
135
|
+
filename: filename,
|
|
136
|
+
content_type: content_type,
|
|
137
|
+
phone_number_id: phone_number_id
|
|
138
|
+
)
|
|
121
139
|
media_id = media_result["id"]
|
|
122
140
|
|
|
123
141
|
# Send media message
|
|
124
142
|
media_payload = {caption: comment}.compact
|
|
125
|
-
result = @client.send_message(
|
|
143
|
+
result = @client.send_message(
|
|
144
|
+
to: channel_id,
|
|
145
|
+
type: media_type,
|
|
146
|
+
phone_number_id: phone_number_id,
|
|
147
|
+
**{media_type => media_payload.merge(id: media_id)}
|
|
148
|
+
)
|
|
126
149
|
|
|
127
|
-
parse_whatsapp_message(result, channel_id)
|
|
150
|
+
parse_whatsapp_message(result, channel_id, phone_number_id: phone_number_id)
|
|
128
151
|
end
|
|
129
152
|
|
|
130
|
-
def add_reaction(channel_id:, message_id:, emoji:)
|
|
131
|
-
@client.send_reaction(
|
|
153
|
+
def add_reaction(channel_id:, message_id:, emoji:, thread_id: nil)
|
|
154
|
+
@client.send_reaction(
|
|
155
|
+
to: channel_id,
|
|
156
|
+
message_id: message_id,
|
|
157
|
+
emoji: emoji,
|
|
158
|
+
phone_number_id: outbound_phone_number_id(channel_id, thread_id)
|
|
159
|
+
)
|
|
132
160
|
end
|
|
133
161
|
|
|
134
|
-
def remove_reaction(channel_id:, message_id:, emoji: "") # rubocop:disable Lint/UnusedMethodArgument
|
|
135
|
-
@client.send_reaction(
|
|
162
|
+
def remove_reaction(channel_id:, message_id:, emoji: "", thread_id: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
163
|
+
@client.send_reaction(
|
|
164
|
+
to: channel_id,
|
|
165
|
+
message_id: message_id,
|
|
166
|
+
emoji: "",
|
|
167
|
+
phone_number_id: outbound_phone_number_id(channel_id, thread_id)
|
|
168
|
+
)
|
|
136
169
|
end
|
|
137
170
|
|
|
138
171
|
def open_dm(user_id)
|
|
@@ -186,7 +219,7 @@ module ChatSDK
|
|
|
186
219
|
end
|
|
187
220
|
end
|
|
188
221
|
|
|
189
|
-
def parse_whatsapp_message(data, channel_id)
|
|
222
|
+
def parse_whatsapp_message(data, channel_id, phone_number_id: @phone_number_id)
|
|
190
223
|
message_id = data.dig("messages", 0, "id")
|
|
191
224
|
|
|
192
225
|
ChatSDK::Message.new(
|
|
@@ -198,12 +231,28 @@ module ChatSDK
|
|
|
198
231
|
platform: :whatsapp,
|
|
199
232
|
bot: true
|
|
200
233
|
),
|
|
201
|
-
thread_id: "whatsapp:#{
|
|
234
|
+
thread_id: "whatsapp:#{phone_number_id}:#{channel_id}",
|
|
202
235
|
channel_id: channel_id,
|
|
203
236
|
platform: :whatsapp,
|
|
204
237
|
raw: data
|
|
205
238
|
)
|
|
206
239
|
end
|
|
240
|
+
|
|
241
|
+
def outbound_phone_number_id(channel_id, thread_id)
|
|
242
|
+
return @phone_number_id unless thread_id
|
|
243
|
+
|
|
244
|
+
match = thread_id.match(/\Awhatsapp:([^:]+):(.+)\z/) if thread_id.is_a?(String)
|
|
245
|
+
phone_number_id = match && match[1]
|
|
246
|
+
unless phone_number_id&.match?(/\A\d+\z/)
|
|
247
|
+
raise ChatSDK::Error, "Invalid WhatsApp phone number ID in thread ID: #{thread_id.inspect}"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
unless channel_id.nil? || match[2] == channel_id.to_s
|
|
251
|
+
raise ChatSDK::Error, "WhatsApp thread recipient does not match channel ID: #{thread_id.inspect}"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
phone_number_id
|
|
255
|
+
end
|
|
207
256
|
end
|
|
208
257
|
end
|
|
209
258
|
end
|
|
@@ -15,7 +15,7 @@ module ChatSDK
|
|
|
15
15
|
@phone_number_id = phone_number_id
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def send_message(to:, type:, **payload)
|
|
18
|
+
def send_message(to:, type:, phone_number_id: @phone_number_id, **payload)
|
|
19
19
|
body = {
|
|
20
20
|
"messaging_product" => "whatsapp",
|
|
21
21
|
"recipient_type" => "individual",
|
|
@@ -23,18 +23,19 @@ module ChatSDK
|
|
|
23
23
|
"type" => type
|
|
24
24
|
}.merge(payload)
|
|
25
25
|
|
|
26
|
-
request(:post, "#{
|
|
26
|
+
request(:post, "#{phone_number_id}/messages", body)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def send_reaction(to:, message_id:, emoji:)
|
|
29
|
+
def send_reaction(to:, message_id:, emoji:, phone_number_id: @phone_number_id)
|
|
30
30
|
send_message(
|
|
31
31
|
to: to,
|
|
32
32
|
type: "reaction",
|
|
33
|
+
phone_number_id: phone_number_id,
|
|
33
34
|
reaction: {message_id: message_id, emoji: emoji}
|
|
34
35
|
)
|
|
35
36
|
end
|
|
36
37
|
|
|
37
|
-
def send_template(to:, template_name:, language_code: "en", components: nil)
|
|
38
|
+
def send_template(to:, template_name:, language_code: "en", components: nil, phone_number_id: @phone_number_id)
|
|
38
39
|
body = {
|
|
39
40
|
"messaging_product" => "whatsapp",
|
|
40
41
|
"to" => to,
|
|
@@ -45,11 +46,11 @@ module ChatSDK
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
body["template"]["components"] = components if components
|
|
48
|
-
request(:post, "#{
|
|
49
|
+
request(:post, "#{phone_number_id}/messages", body)
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
def mark_as_read(message_id:)
|
|
52
|
-
request(:post, "#{
|
|
52
|
+
def mark_as_read(message_id:, phone_number_id: @phone_number_id)
|
|
53
|
+
request(:post, "#{phone_number_id}/messages", {
|
|
53
54
|
"messaging_product" => "whatsapp",
|
|
54
55
|
"status" => "read",
|
|
55
56
|
"message_id" => message_id
|
|
@@ -69,8 +70,8 @@ module ChatSDK
|
|
|
69
70
|
.tap { |response| validate_media_response!(response) }
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
def upload_media(io:, filename:, content_type:)
|
|
73
|
-
response = upload_connection.post("#{
|
|
73
|
+
def upload_media(io:, filename:, content_type:, phone_number_id: @phone_number_id)
|
|
74
|
+
response = upload_connection.post("#{phone_number_id}/media") do |req|
|
|
74
75
|
req.body = {
|
|
75
76
|
"messaging_product" => "whatsapp",
|
|
76
77
|
"file" => Faraday::Multipart::FilePart.new(io, content_type, filename),
|
|
@@ -11,14 +11,21 @@ module ChatSDK
|
|
|
11
11
|
(payload["entry"] || [])
|
|
12
12
|
.flat_map { |entry| entry["changes"] || [] }
|
|
13
13
|
.select { |change| change["field"] == "messages" }
|
|
14
|
-
.flat_map
|
|
15
|
-
|
|
14
|
+
.flat_map do |change|
|
|
15
|
+
value = change["value"] || {}
|
|
16
|
+
contacts = value["contacts"] || []
|
|
17
|
+
(value["messages"] || []).filter_map do |msg|
|
|
18
|
+
contact = contact_for(msg, contacts)
|
|
19
|
+
parse_message(msg, phone_number_id, contact)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
16
22
|
end
|
|
17
23
|
|
|
18
24
|
private
|
|
19
25
|
|
|
20
|
-
def parse_message(msg, phone_number_id)
|
|
21
|
-
from = msg["from"]&.
|
|
26
|
+
def parse_message(msg, phone_number_id, contact)
|
|
27
|
+
from = first_present(msg["from"], msg["wa_id"], contact&.dig("wa_id"),
|
|
28
|
+
msg["from_user_id"], contact&.dig("user_id"))
|
|
22
29
|
return nil unless from
|
|
23
30
|
|
|
24
31
|
author = ChatSDK::Author.new(id: from, name: from, platform: :whatsapp, bot: false)
|
|
@@ -36,6 +43,16 @@ module ChatSDK
|
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
|
|
46
|
+
def contact_for(message, contacts)
|
|
47
|
+
from = message["from"].to_s
|
|
48
|
+
contacts.find { |contact| contact["wa_id"].to_s == from } ||
|
|
49
|
+
(contacts.first if contacts.length == 1)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def first_present(*values)
|
|
53
|
+
values.map { |value| value&.to_s }.find { |value| value && !value.empty? }
|
|
54
|
+
end
|
|
55
|
+
|
|
39
56
|
def parse_reaction_message(msg, from, thread_id)
|
|
40
57
|
reaction = msg["reaction"] || {}
|
|
41
58
|
emoji = reaction["emoji"] || ""
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: chat_sdk-whatsapp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Quentin Rousseau
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '1.2'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
25
|
+
version: '1.2'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: faraday
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|