chat_sdk-whatsapp 0.2.1 → 0.3.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 +5 -33
- data/lib/chat_sdk/whatsapp/api_client.rb +10 -41
- data/lib/chat_sdk/whatsapp/interactive_renderer.rb +2 -26
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6962b015545c6913686c232e76994a10ddf91200df410866c65b717fa2b1fc34
|
|
4
|
+
data.tar.gz: 607a03f130b22a02077456128ec0490dd54d2df0d4789739a185c6ce72725191
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 507cd5d05f6947a537fa0315d298f3bd30330e9a8428da21066f2131ea29bf67bd95e83770ec4d074f575ba390e88c4226a5ed881cedafa0ee9077b12be55579
|
|
7
|
+
data.tar.gz: e9e4577b34adeaaeded3213517a3c7ccfb41e4cda3f75577bd02f15134c2d9dd62a757114c284f27bd167ffac40df6b5876be77719d7cea6262bdaa6766f23aa
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openssl"
|
|
4
|
-
require "rack/utils"
|
|
5
|
-
|
|
6
3
|
module ChatSDK
|
|
7
4
|
module WhatsApp
|
|
8
5
|
class Adapter < ChatSDK::Adapter::Base
|
|
6
|
+
include ChatSDK::Adapter::MetaVerification
|
|
7
|
+
|
|
9
8
|
capabilities :direct_messages, :file_uploads, :reactions
|
|
10
9
|
|
|
11
10
|
attr_reader :client
|
|
@@ -29,42 +28,15 @@ module ChatSDK
|
|
|
29
28
|
|
|
30
29
|
# Inbound
|
|
31
30
|
def verify_request!(rack_request)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
unless signature
|
|
35
|
-
raise ChatSDK::SignatureVerificationError, "Missing WhatsApp signature header"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
body = rack_request.body.read
|
|
39
|
-
rack_request.body.rewind
|
|
40
|
-
|
|
41
|
-
expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", @app_secret, body)}"
|
|
42
|
-
|
|
43
|
-
unless Rack::Utils.secure_compare(signature, expected)
|
|
44
|
-
raise ChatSDK::SignatureVerificationError, "Invalid WhatsApp signature"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
true
|
|
31
|
+
verify_meta_signature!(rack_request, secret: @app_secret, platform_name: "WhatsApp")
|
|
48
32
|
end
|
|
49
33
|
|
|
50
34
|
def ack_response(rack_request)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
params = rack_request.params
|
|
54
|
-
mode = params["hub.mode"]
|
|
55
|
-
token = params["hub.verify_token"]
|
|
56
|
-
challenge = params["hub.challenge"]
|
|
57
|
-
|
|
58
|
-
if mode == "subscribe" && token == @verify_token
|
|
59
|
-
[200, {}, [challenge.to_s]]
|
|
60
|
-
end
|
|
35
|
+
meta_ack_response(rack_request, verify_token: @verify_token)
|
|
61
36
|
end
|
|
62
37
|
|
|
63
38
|
def parse_events(rack_request)
|
|
64
|
-
|
|
65
|
-
rack_request.body.rewind
|
|
66
|
-
|
|
67
|
-
payload = JSON.parse(body)
|
|
39
|
+
payload = read_json_body(rack_request)
|
|
68
40
|
EventParser.parse(payload, @phone_number_id)
|
|
69
41
|
rescue JSON::ParserError
|
|
70
42
|
[]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
module WhatsApp
|
|
5
|
-
class ApiClient
|
|
5
|
+
class ApiClient < ChatSDK::ApiClient::Base
|
|
6
6
|
BASE_URL = "https://graph.facebook.com/v21.0"
|
|
7
7
|
|
|
8
8
|
def initialize(access_token, phone_number_id)
|
|
@@ -30,7 +30,7 @@ module ChatSDK
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def upload_media(io:, filename:, content_type:)
|
|
33
|
-
response =
|
|
33
|
+
response = upload_connection.post("#{@phone_number_id}/media") do |req|
|
|
34
34
|
req.body = {
|
|
35
35
|
"messaging_product" => "whatsapp",
|
|
36
36
|
"file" => Faraday::Multipart::FilePart.new(io, content_type, filename),
|
|
@@ -43,52 +43,21 @@ module ChatSDK
|
|
|
43
43
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
|
-
def
|
|
47
|
-
|
|
46
|
+
def base_url
|
|
47
|
+
BASE_URL
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def
|
|
51
|
-
|
|
50
|
+
def adapter_name
|
|
51
|
+
:whatsapp
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
yield f
|
|
57
|
-
f.response :json
|
|
58
|
-
f.adapter :net_http
|
|
59
|
-
f.headers["Authorization"] = "Bearer #{@access_token}"
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def request(method, path, body = nil)
|
|
64
|
-
response = connection.public_send(method, path) do |req|
|
|
65
|
-
req.body = body if body && method != :get
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
handle_response(response)
|
|
54
|
+
def configure_auth(faraday)
|
|
55
|
+
faraday.headers["Authorization"] = "Bearer #{@access_token}"
|
|
69
56
|
end
|
|
70
57
|
|
|
71
|
-
def
|
|
58
|
+
def extract_error_message(response)
|
|
72
59
|
body = response.body
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if response.status == 429
|
|
76
|
-
raise ChatSDK::RateLimitedError.new(
|
|
77
|
-
"WhatsApp API rate limited",
|
|
78
|
-
retry_after: nil,
|
|
79
|
-
status: response.status,
|
|
80
|
-
body: body,
|
|
81
|
-
adapter_name: :whatsapp
|
|
82
|
-
)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
error_message = body.is_a?(Hash) ? body.dig("error", "message") : response.status
|
|
86
|
-
raise ChatSDK::PlatformError.new(
|
|
87
|
-
"WhatsApp API error: #{error_message}",
|
|
88
|
-
status: response.status,
|
|
89
|
-
body: body,
|
|
90
|
-
adapter_name: :whatsapp
|
|
91
|
-
)
|
|
60
|
+
body.is_a?(Hash) ? body.dig("error", "message") : response.status.to_s
|
|
92
61
|
end
|
|
93
62
|
end
|
|
94
63
|
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
module WhatsApp
|
|
5
5
|
class InteractiveRenderer
|
|
6
|
+
include ChatSDK::Cards::TextHelpers
|
|
7
|
+
|
|
6
8
|
def render(node)
|
|
7
9
|
case node.type
|
|
8
10
|
when :card then render_card(node)
|
|
@@ -75,26 +77,6 @@ module ChatSDK
|
|
|
75
77
|
buttons
|
|
76
78
|
end
|
|
77
79
|
|
|
78
|
-
def collect_text_parts(node)
|
|
79
|
-
parts = []
|
|
80
|
-
node.children.each do |child|
|
|
81
|
-
case child.type
|
|
82
|
-
when :text
|
|
83
|
-
parts << child.attributes[:content]
|
|
84
|
-
when :divider
|
|
85
|
-
parts << "---"
|
|
86
|
-
when :fields
|
|
87
|
-
child.children.each do |field|
|
|
88
|
-
parts << "#{field.attributes[:label]}: #{field.attributes[:value]}"
|
|
89
|
-
end
|
|
90
|
-
when :section
|
|
91
|
-
parts << child.attributes[:title] if child.attributes[:title]
|
|
92
|
-
parts.concat(collect_text_parts(child))
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
parts
|
|
96
|
-
end
|
|
97
|
-
|
|
98
80
|
def render_single(node)
|
|
99
81
|
case node.type
|
|
100
82
|
when :text
|
|
@@ -103,12 +85,6 @@ module ChatSDK
|
|
|
103
85
|
{text: node.fallback_text}
|
|
104
86
|
end
|
|
105
87
|
end
|
|
106
|
-
|
|
107
|
-
def truncate(text, max)
|
|
108
|
-
return "" unless text
|
|
109
|
-
|
|
110
|
-
(text.length > max) ? "#{text[0..max - 4]}..." : text
|
|
111
|
-
end
|
|
112
88
|
end
|
|
113
89
|
end
|
|
114
90
|
end
|