Commit 07d8eeec by Francis Zhou

add robot message builder

parent e89d6223
...@@ -25,8 +25,14 @@ module DingtalkSdk ...@@ -25,8 +25,14 @@ module DingtalkSdk
end end
class Signature class Signature
def initialize(signature) def initialize(signature, url_encoded = false)
@signature = signature if url_encoded
@signature = URI.decode_www_form_component signature
@encoded_signature = signature
else
@signature = signature
@encoded_signature = URI.encode_www_form_component signature
end
end end
def to_s def to_s
...@@ -34,7 +40,7 @@ module DingtalkSdk ...@@ -34,7 +40,7 @@ module DingtalkSdk
end end
def url_encoded def url_encoded
URI.encode_www_form_component @signature @encoded_signature
end end
end end
......
...@@ -21,22 +21,23 @@ module DingtalkSdk ...@@ -21,22 +21,23 @@ module DingtalkSdk
# 通过 Request.set_access_token_cache_method 定义缓存方法 # 通过 Request.set_access_token_cache_method 定义缓存方法
# @return [Hash] # @return [Hash]
def cached_access_token def cached_access_token
method_name = :@@ak_cache_method var_name = :@ak_cache_method
return get_access_token unless self.class.class_variable_defined? method_name return get_access_token unless self.class.instance_variable_defined?(var_name)
self.class.class_variable_get(method_name).call(self) self.class.instance_variable_get(var_name).call(self)
end end
module ClassMethods module ClassMethods
def set_access_token_cache_method def set_access_token_cache_method
raise ArgumentError, 'invalid access_token cache method' unless block_given? raise ArgumentError, 'invalid access_token cache method' unless block_given?
class_variable_set :@@ak_cache_method, ->(request) { yield request } @ak_cache_method = ->(request) { yield request }
end end
def unset_access_token_cache_method def unset_access_token_cache_method
remove_class_variable :@@ak_cache_method if class_variable_defined?(@@ak_cache_method) var_name = :@ak_cache_method
remove_instance_variable(var_name) if instance_variable_defined?(var_name)
end end
end end
......
...@@ -8,26 +8,49 @@ require 'dingtalk_sdk/core' ...@@ -8,26 +8,49 @@ require 'dingtalk_sdk/core'
require 'dingtalk_sdk/request_url' require 'dingtalk_sdk/request_url'
require 'active_support/core_ext/object' require 'active_support/core_ext/object'
require 'active_support/core_ext/integer' require 'active_support/core_ext/integer'
require 'active_support/core_ext/time/zones'
module DingtalkSdk module DingtalkSdk
module Robot module Robot
extend DingtalkSdk::Core extend DingtalkSdk::Core
def self.calculate_signature(secret, timestamp) class << self
raise ArgumentError, 'timestamp must in millis' if Math.log10(timestamp).ceil < 13 # 计算签名
# timestamp 为毫秒单位
# @return [DingtalkSdk::Signature]
def calculate_signature(secret, timestamp)
raise ArgumentError, 'timestamp must in millis' if Math.log10(timestamp).ceil < 13
origin_str = [timestamp, secret].join("\n") origin_str = [timestamp, secret].join("\n")
signature_str = OpenSSL::HMAC.digest('SHA256', secret, origin_str) signature_str = OpenSSL::HMAC.digest('SHA256', secret, origin_str)
signature_str_base64 = Base64.strict_encode64(signature_str) signature_str_base64 = Base64.strict_encode64(signature_str)
Signature.new(signature_str_base64) Signature.new(signature_str_base64)
end end
def self.verify_signature(secret, signature, timestamp = Time.zone.now) # 验证一个签名是否有效
return false if Time.zone.now - timestamp > 1.hour # @option timestamp 时间戳
# @option url_encoded 签名是否经过 url encode
# @option verify_timestamp 验证签名是否在有效时间段内 (1小时)
# @return [Boolean]
def verify_signature(secret, expected_signature, options = {})
options.with_defaults!(
timestamp: Time.now.to_i * 1000,
url_encoded: false,
verify_timestamp: true
)
if options[:verify_signature]
datetime_timestamp = Time.at(options[:timestamp] / 1000)
return false if Time.now - datetime_timestamp > 1.hour
end
calculate_signature(secret, timestamp.to_i * 1000) == signature actually_signature = calculate_signature(secret, options[:timestamp])
expected_signature == if options[:url_encoded]
actually_signature.url_encoded
else
actually_signature.to_s
end
end
end end
class MessageBuilder class MessageBuilder
...@@ -96,7 +119,7 @@ module DingtalkSdk ...@@ -96,7 +119,7 @@ module DingtalkSdk
def at_mobile(mobile) def at_mobile(mobile)
@is_at_all = false @is_at_all = false
@at_mobile_list = [*mobile] @at_mobile_list = [*mobile].map(&:to_s).uniq
end end
def at_all def at_all
...@@ -110,7 +133,7 @@ module DingtalkSdk ...@@ -110,7 +133,7 @@ module DingtalkSdk
if @is_at_all if @is_at_all
h[:isAtAll] = true h[:isAtAll] = true
elsif @at_mobile_list.try(:size).positive? elsif @at_mobile_list.try(:size).try(:positive?)
h[:atMobiles] = @at_mobile_list h[:atMobiles] = @at_mobile_list
end end
end end
......
...@@ -11,4 +11,45 @@ RSpec.describe DingtalkSdk::Robot do ...@@ -11,4 +11,45 @@ RSpec.describe DingtalkSdk::Robot do
expect(sign.url_encoded).to eq('3E1RZgQrn1ZLgQGU4C4n4SMei%2BObeZVym5BbqrwOJPs%3D') expect(sign.url_encoded).to eq('3E1RZgQrn1ZLgQGU4C4n4SMei%2BObeZVym5BbqrwOJPs%3D')
end end
it 'should return true in verify_signature' do
timestamp = 1_603_868_410_000
secret = 'this is secret'
signature = '3E1RZgQrn1ZLgQGU4C4n4SMei+ObeZVym5BbqrwOJPs='
is_valid = DingtalkSdk::Robot.verify_signature(
secret,
signature,
{
timestamp: timestamp,
url_encoded: false,
verify_timestamp: false
}
)
expect(is_valid).to be_truthy
end
describe DingtalkSdk::Robot::MessageBuilder do
it 'should compose text message' do
builder = DingtalkSdk::Robot::MessageBuilder.new
builder.text text: 'hello world'
mesg_h = builder.to_h
expect(mesg_h[:msgtype]).to eq('text')
expect(mesg_h[:text][:content]).to eq('hello world')
expect(mesg_h[:isAtAll]).to be_nil
expect(mesg_h[:atMobiles]).to be_nil
end
it 'should compose message with at mobiles' do
builder = DingtalkSdk::Robot::MessageBuilder.new
builder.text text: 'hello world'
builder.at_mobile [123, 456]
mesg_h = builder.to_h
expect(mesg_h[:isAtAll]).to be_nil
expect(mesg_h[:atMobiles]).to eq(%w[123 456])
end
end
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment