connector-ruby 0.1.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/CHANGELOG.md +61 -9
- data/README.md +157 -28
- data/connector-ruby.gemspec +5 -1
- data/lib/connector_ruby/batch_sender.rb +44 -0
- data/lib/connector_ruby/channels/line.rb +119 -0
- data/lib/connector_ruby/channels/livechat.rb +88 -0
- data/lib/connector_ruby/channels/messenger.rb +142 -0
- data/lib/connector_ruby/channels/slack.rb +115 -0
- data/lib/connector_ruby/channels/telegram.rb +18 -0
- data/lib/connector_ruby/channels/whatsapp.rb +97 -0
- data/lib/connector_ruby/configuration.rb +9 -0
- data/lib/connector_ruby/delivery_tracker.rb +58 -0
- data/lib/connector_ruby/message.rb +83 -2
- data/lib/connector_ruby/version.rb +1 -1
- data/lib/connector_ruby/webhook_verifier.rb +97 -3
- data/lib/connector_ruby.rb +10 -0
- metadata +23 -2
|
@@ -1,23 +1,117 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "openssl"
|
|
4
|
+
require "base64"
|
|
5
|
+
require "json"
|
|
4
6
|
|
|
5
7
|
module ConnectorRuby
|
|
6
8
|
class WebhookVerifier
|
|
9
|
+
# Verify a WhatsApp Cloud API webhook signature.
|
|
10
|
+
#
|
|
11
|
+
# WhatsApp sends the signature in the `X-Hub-Signature-256` header as
|
|
12
|
+
# `sha256=<hex>`, computed with HMAC-SHA256 over the raw request body
|
|
13
|
+
# using the app secret as the key.
|
|
7
14
|
def self.verify_whatsapp(payload, signature:, app_secret:)
|
|
8
15
|
expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", app_secret, payload)}"
|
|
9
16
|
secure_compare(expected, signature)
|
|
10
17
|
end
|
|
11
18
|
|
|
19
|
+
# Verify a Telegram webhook using the `X-Telegram-Bot-Api-Secret-Token`
|
|
20
|
+
# header configured via `setWebhook`.
|
|
12
21
|
def self.verify_telegram(token:, payload:, secret_token: nil, header_value: nil)
|
|
13
22
|
return false unless secret_token && header_value
|
|
14
23
|
computed = OpenSSL::HMAC.hexdigest("SHA256", secret_token, payload.to_s)
|
|
15
24
|
secure_compare(computed, header_value.to_s)
|
|
16
25
|
end
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
# Verify a Meta Messenger webhook signature.
|
|
28
|
+
#
|
|
29
|
+
# Messenger sends the signature in the `X-Hub-Signature-256` header as
|
|
30
|
+
# `sha256=<hex>`, computed with HMAC-SHA256 over the raw request body
|
|
31
|
+
# using the Facebook app secret as the key.
|
|
32
|
+
#
|
|
33
|
+
# Reference: https://developers.facebook.com/docs/messenger-platform/webhooks#security
|
|
34
|
+
def self.verify_messenger(payload, signature:, app_secret:)
|
|
35
|
+
return false if signature.nil? || app_secret.nil?
|
|
36
|
+
expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", app_secret, payload.to_s)}"
|
|
37
|
+
secure_compare(expected, signature)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Verify a LINE Messaging API webhook signature.
|
|
41
|
+
#
|
|
42
|
+
# LINE sends the signature in the `X-Line-Signature` header as the
|
|
43
|
+
# Base64-encoded HMAC-SHA256 digest of the raw request body, using the
|
|
44
|
+
# channel secret as the key.
|
|
45
|
+
#
|
|
46
|
+
# Because the signature is Base64 (which contains uppercase letters),
|
|
47
|
+
# the comparison is case-sensitive.
|
|
48
|
+
#
|
|
49
|
+
# Reference: https://developers.line.biz/en/reference/messaging-api/#signature-validation
|
|
50
|
+
def self.verify_line(payload, signature:, channel_secret:)
|
|
51
|
+
return false if signature.nil? || channel_secret.nil?
|
|
52
|
+
digest = OpenSSL::HMAC.digest("SHA256", channel_secret, payload.to_s)
|
|
53
|
+
expected = Base64.strict_encode64(digest)
|
|
54
|
+
secure_compare(expected, signature, case_sensitive: true)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Verify a Slack webhook signature with replay protection.
|
|
58
|
+
#
|
|
59
|
+
# Slack sends the signature in `X-Slack-Signature` as `v0=<hex>`, computed
|
|
60
|
+
# as HMAC-SHA256 over the base string `v0:{timestamp}:{body}` using the
|
|
61
|
+
# signing secret as the key. The `X-Slack-Request-Timestamp` value must
|
|
62
|
+
# also be checked for freshness to prevent replay attacks.
|
|
63
|
+
#
|
|
64
|
+
# @param tolerance [Integer] max allowed delta between the timestamp and
|
|
65
|
+
# the current time, in seconds (default 300 = 5 minutes, matching
|
|
66
|
+
# Slack's own recommendation).
|
|
67
|
+
#
|
|
68
|
+
# Reference: https://api.slack.com/authentication/verifying-requests-from-slack
|
|
69
|
+
def self.verify_slack(payload, timestamp:, signature:, signing_secret:, tolerance: 300)
|
|
70
|
+
return false if timestamp.nil? || signature.nil? || signing_secret.nil?
|
|
71
|
+
ts = timestamp.to_i
|
|
72
|
+
return false if ts.zero?
|
|
73
|
+
return false if (Time.now.to_i - ts).abs > tolerance
|
|
74
|
+
|
|
75
|
+
basestring = "v0:#{ts}:#{payload}"
|
|
76
|
+
expected = "v0=#{OpenSSL::HMAC.hexdigest("SHA256", signing_secret, basestring)}"
|
|
77
|
+
secure_compare(expected, signature)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Verify a LiveChat webhook using the shared secret embedded in the body.
|
|
81
|
+
#
|
|
82
|
+
# LiveChat does NOT sign webhooks with HMAC and does NOT use a signature
|
|
83
|
+
# header. Instead, every webhook body contains a `secret_key` field;
|
|
84
|
+
# verification is a constant-time compare between that field and the
|
|
85
|
+
# shared secret you configured in your LiveChat webhook settings.
|
|
86
|
+
#
|
|
87
|
+
# Reference: https://platform.text.com/docs/messaging/webhooks
|
|
88
|
+
# and (production reference) chatbotlic's webhooks_controller.rb
|
|
89
|
+
def self.verify_livechat(payload, expected_secret:)
|
|
90
|
+
return false if expected_secret.nil?
|
|
91
|
+
|
|
92
|
+
data = payload.is_a?(String) ? JSON.parse(payload) : payload
|
|
93
|
+
return false unless data.is_a?(Hash)
|
|
94
|
+
|
|
95
|
+
received = data["secret_key"]
|
|
96
|
+
return false if received.nil?
|
|
97
|
+
|
|
98
|
+
secure_compare(received.to_s, expected_secret.to_s, case_sensitive: true)
|
|
99
|
+
rescue JSON::ParserError
|
|
100
|
+
false
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Constant-time string comparison.
|
|
104
|
+
#
|
|
105
|
+
# By default, comparisons are case-insensitive (safe for hex signatures).
|
|
106
|
+
# Pass `case_sensitive: true` for Base64 or shared-secret comparisons where
|
|
107
|
+
# case carries meaning.
|
|
108
|
+
def self.secure_compare(a, b, case_sensitive: false)
|
|
109
|
+
a = a.to_s
|
|
110
|
+
b = b.to_s
|
|
111
|
+
unless case_sensitive
|
|
112
|
+
a = a.downcase
|
|
113
|
+
b = b.downcase
|
|
114
|
+
end
|
|
21
115
|
return false unless a.bytesize == b.bytesize
|
|
22
116
|
|
|
23
117
|
OpenSSL.fixed_length_secure_compare(a, b)
|
data/lib/connector_ruby.rb
CHANGED
|
@@ -10,6 +10,12 @@ require_relative "connector_ruby/webhook_verifier"
|
|
|
10
10
|
require_relative "connector_ruby/channels/base"
|
|
11
11
|
require_relative "connector_ruby/channels/whatsapp"
|
|
12
12
|
require_relative "connector_ruby/channels/telegram"
|
|
13
|
+
require_relative "connector_ruby/channels/messenger"
|
|
14
|
+
require_relative "connector_ruby/channels/line"
|
|
15
|
+
require_relative "connector_ruby/channels/slack"
|
|
16
|
+
require_relative "connector_ruby/channels/livechat"
|
|
17
|
+
require_relative "connector_ruby/batch_sender"
|
|
18
|
+
require_relative "connector_ruby/delivery_tracker"
|
|
13
19
|
|
|
14
20
|
module ConnectorRuby
|
|
15
21
|
class << self
|
|
@@ -29,4 +35,8 @@ module ConnectorRuby
|
|
|
29
35
|
# Convenience aliases
|
|
30
36
|
WhatsApp = Channels::WhatsApp
|
|
31
37
|
Telegram = Channels::Telegram
|
|
38
|
+
Messenger = Channels::Messenger
|
|
39
|
+
Line = Channels::Line
|
|
40
|
+
Slack = Channels::Slack
|
|
41
|
+
LiveChat = Channels::LiveChat
|
|
32
42
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: connector-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Johannes Dwi Cahyo
|
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.2'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: minitest
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,7 +66,8 @@ dependencies:
|
|
|
52
66
|
- !ruby/object:Gem::Version
|
|
53
67
|
version: '3.0'
|
|
54
68
|
description: Framework-agnostic SDK for sending/receiving messages across chat platforms.
|
|
55
|
-
Supports WhatsApp Cloud API, Telegram Bot API,
|
|
69
|
+
Supports WhatsApp Cloud API, Telegram Bot API, Facebook Messenger, LINE Messaging
|
|
70
|
+
API, Slack Web API, and LiveChat Agent API.
|
|
56
71
|
email:
|
|
57
72
|
- johannes@example.com
|
|
58
73
|
executables: []
|
|
@@ -65,10 +80,16 @@ files:
|
|
|
65
80
|
- Rakefile
|
|
66
81
|
- connector-ruby.gemspec
|
|
67
82
|
- lib/connector_ruby.rb
|
|
83
|
+
- lib/connector_ruby/batch_sender.rb
|
|
68
84
|
- lib/connector_ruby/channels/base.rb
|
|
85
|
+
- lib/connector_ruby/channels/line.rb
|
|
86
|
+
- lib/connector_ruby/channels/livechat.rb
|
|
87
|
+
- lib/connector_ruby/channels/messenger.rb
|
|
88
|
+
- lib/connector_ruby/channels/slack.rb
|
|
69
89
|
- lib/connector_ruby/channels/telegram.rb
|
|
70
90
|
- lib/connector_ruby/channels/whatsapp.rb
|
|
71
91
|
- lib/connector_ruby/configuration.rb
|
|
92
|
+
- lib/connector_ruby/delivery_tracker.rb
|
|
72
93
|
- lib/connector_ruby/error.rb
|
|
73
94
|
- lib/connector_ruby/event.rb
|
|
74
95
|
- lib/connector_ruby/http_client.rb
|