wechat 0.11.4 → 0.11.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +5 -1
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +5 -0
- data/lib/action_controller/wechat_responder.rb +7 -5
- data/lib/generators/wechat/config_generator.rb +14 -10
- data/lib/generators/wechat/install_generator.rb +3 -1
- data/lib/generators/wechat/menu_generator.rb +3 -1
- data/lib/generators/wechat/redis_store_generator.rb +3 -1
- data/lib/generators/wechat/session_generator.rb +14 -10
- data/lib/generators/wechat/templates/app/models/wechat_config.rb +8 -13
- data/lib/generators/wechat/templates/app/models/wechat_session.rb +2 -0
- data/lib/generators/wechat/templates/config/initializers/wechat_redis_store.rb +5 -3
- data/lib/wechat.rb +5 -3
- data/lib/wechat/api.rb +2 -0
- data/lib/wechat/api_base.rb +8 -6
- data/lib/wechat/api_loader.rb +48 -49
- data/lib/wechat/cipher.rb +4 -2
- data/lib/wechat/concern/common.rb +2 -0
- data/lib/wechat/controller_api.rb +13 -10
- data/lib/wechat/corp_api.rb +2 -0
- data/lib/wechat/helpers.rb +14 -12
- data/lib/wechat/http_client.rb +21 -16
- data/lib/wechat/message.rb +17 -14
- data/lib/wechat/mp_api.rb +2 -0
- data/lib/wechat/responder.rb +37 -38
- data/lib/wechat/signature.rb +2 -0
- data/lib/wechat/ticket/corp_jsapi_ticket.rb +2 -0
- data/lib/wechat/ticket/jsapi_base.rb +4 -2
- data/lib/wechat/ticket/public_jsapi_ticket.rb +2 -0
- data/lib/wechat/token/access_token_base.rb +4 -2
- data/lib/wechat/token/corp_access_token.rb +2 -0
- data/lib/wechat/token/public_access_token.rb +2 -0
- metadata +42 -28
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 332ed5cc2f20211d70e994fc4549534748c9400074ece51debd7e4d147b4a1fd
|
4
|
+
data.tar.gz: 6cb347a738b602628c6dadae3ef2d90cddb9c1baff68e7e66b9bc88cc14a8643
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b293b5f93fcc0f304af98f7664c73469595028b4bc38d14c60bf2506243c288d59964e17acaea563c7922493b39e67bc56ede37e638fecaf4f83c332dbb60ca
|
7
|
+
data.tar.gz: 640d2a2b5fc743189f03f680c03404cc991ff6b5e50f84f802c520eed10d7df1690080ae11ac9f1b22ada9b4f45a5f1c6a8ba5e8f01a9043026ce20ed39569ee
|
checksums.yaml.gz.sig
CHANGED
@@ -1 +1,5 @@
|
|
1
|
-
|
1
|
+
�o����6�TK
|
2
|
+
[|%��)��֜��xʰJ���.�����"9?
|
3
|
+
�m�БF�XBq���:�D^��w���v|����ͿO�pn�+���a�����껅�h"�����)��1��p�A��5��C0����AWp��u�ˑ�\�x��ub�X�_S{N�F$%�a\�ZU��۟-�N
|
4
|
+
�曵.���k�����X�PI�ج��P�q�WT{ɷHRK�=q#��
|
5
|
+
� BKT$'��ck�V�6 Ѧ̈́*��Cl�[f�,,�v����8��#HD���P��|��C� �j�Dz�_
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionController
|
2
4
|
module WechatResponder
|
3
5
|
def wechat_api(opts = {})
|
@@ -36,11 +38,11 @@ module ActionController
|
|
36
38
|
self.trusted_domain_fullname = opts[:trusted_domain_fullname] || cfg.trusted_domain_fullname
|
37
39
|
self.oauth2_cookie_duration = opts[:oauth2_cookie_duration] || cfg.oauth2_cookie_duration.to_i.seconds
|
38
40
|
self.timeout = opts[:timeout] || cfg.timeout
|
39
|
-
if opts.key?(:skip_verify_ssl)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
self.skip_verify_ssl = if opts.key?(:skip_verify_ssl)
|
42
|
+
opts[:skip_verify_ssl]
|
43
|
+
else
|
44
|
+
cfg.skip_verify_ssl
|
45
|
+
end
|
44
46
|
|
45
47
|
return Wechat.api if account == :default && opts.empty?
|
46
48
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rails/generators/active_record'
|
2
4
|
|
3
5
|
module Wechat
|
@@ -6,13 +8,13 @@ module Wechat
|
|
6
8
|
include ::Rails::Generators::Migration
|
7
9
|
|
8
10
|
desc 'Generate wechat configs in database'
|
9
|
-
source_root File.expand_path('
|
11
|
+
source_root File.expand_path('templates', __dir__)
|
10
12
|
|
11
13
|
def copy_wechat_config_migration
|
12
14
|
migration_template(
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
'db/config_migration.rb.erb',
|
16
|
+
'db/migrate/create_wechat_configs.rb',
|
17
|
+
migration_version: migration_version
|
16
18
|
)
|
17
19
|
end
|
18
20
|
|
@@ -20,16 +22,18 @@ module Wechat
|
|
20
22
|
template 'app/models/wechat_config.rb'
|
21
23
|
end
|
22
24
|
|
23
|
-
|
25
|
+
class << self
|
26
|
+
private
|
24
27
|
|
25
|
-
|
26
|
-
|
28
|
+
def next_migration_number(dirname)
|
29
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
33
|
+
private
|
34
|
+
|
29
35
|
def migration_version
|
30
|
-
if Rails.version >= '5.0.0'
|
31
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
32
|
-
end
|
36
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if Rails.version >= '5.0.0'
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module Generators
|
3
5
|
class InstallGenerator < Rails::Generators::Base
|
4
6
|
desc 'Install Wechat support files'
|
5
|
-
source_root File.expand_path('
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
6
8
|
|
7
9
|
def copy_config
|
8
10
|
template 'config/wechat.yml'
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module Generators
|
3
5
|
class MenuGenerator < Rails::Generators::Base
|
4
6
|
desc 'Generate wechat menu'
|
5
|
-
source_root File.expand_path('
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
6
8
|
class_option :conditional, desc: 'Generate conditional menu', type: :boolean, default: false
|
7
9
|
|
8
10
|
def copy_menu
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module Generators
|
3
5
|
class RedisStoreGenerator < Rails::Generators::Base
|
4
6
|
desc 'Using redis as token/ticket store'
|
5
|
-
source_root File.expand_path('
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
6
8
|
|
7
9
|
def copy_wechat_redis_initializer
|
8
10
|
template 'config/initializers/wechat_redis_store.rb'
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rails/generators/active_record'
|
2
4
|
|
3
5
|
module Wechat
|
@@ -6,13 +8,13 @@ module Wechat
|
|
6
8
|
include ::Rails::Generators::Migration
|
7
9
|
|
8
10
|
desc 'Enable wechat session support'
|
9
|
-
source_root File.expand_path('
|
11
|
+
source_root File.expand_path('templates', __dir__)
|
10
12
|
|
11
13
|
def copy_wechat_sessions_migration
|
12
14
|
migration_template(
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
'db/session_migration.rb.erb',
|
16
|
+
'db/migrate/create_wechat_sessions.rb',
|
17
|
+
migration_version: migration_version
|
16
18
|
)
|
17
19
|
end
|
18
20
|
|
@@ -20,16 +22,18 @@ module Wechat
|
|
20
22
|
template 'app/models/wechat_session.rb'
|
21
23
|
end
|
22
24
|
|
23
|
-
|
25
|
+
class << self
|
26
|
+
private
|
24
27
|
|
25
|
-
|
26
|
-
|
28
|
+
def next_migration_number(dirname)
|
29
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
33
|
+
private
|
34
|
+
|
29
35
|
def migration_version
|
30
|
-
if Rails.version >= '5.0.0'
|
31
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
32
|
-
end
|
36
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if Rails.version >= '5.0.0'
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Used by wechat gems, do not rename WechatConfig to other name,
|
2
4
|
# Feel free to inherit from other class like ActiveModel::Model
|
3
5
|
class WechatConfig < ActiveRecord::Base
|
@@ -10,17 +12,16 @@ class WechatConfig < ActiveRecord::Base
|
|
10
12
|
|
11
13
|
validate :app_config_is_valid
|
12
14
|
|
13
|
-
ATTRIBUTES_TO_REMOVE = %w
|
15
|
+
ATTRIBUTES_TO_REMOVE = %w[environment account created_at updated_at enabled].freeze
|
14
16
|
|
15
17
|
def self.get_all_configs(environment)
|
16
|
-
WechatConfig.where(environment: environment, enabled: true).
|
18
|
+
WechatConfig.where(environment: environment, enabled: true).each_with_object({}) do |config, hash|
|
17
19
|
hash[config.account] = config.build_config_hash
|
18
|
-
hash
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
def build_config_hash
|
23
|
-
|
24
|
+
as_json(except: ATTRIBUTES_TO_REMOVE)
|
24
25
|
end
|
25
26
|
|
26
27
|
private
|
@@ -28,17 +29,11 @@ class WechatConfig < ActiveRecord::Base
|
|
28
29
|
def app_config_is_valid
|
29
30
|
if self[:appid].present?
|
30
31
|
# public account
|
31
|
-
if self[:secret].blank?
|
32
|
-
errors.add(:secret, 'cannot be nil when appid is set')
|
33
|
-
end
|
32
|
+
errors.add(:secret, 'cannot be nil when appid is set') if self[:secret].blank?
|
34
33
|
elsif self[:corpid].present?
|
35
34
|
# corp account
|
36
|
-
if self[:corpsecret].blank?
|
37
|
-
|
38
|
-
end
|
39
|
-
if self[:agentid].blank?
|
40
|
-
errors.add(:agentid, 'cannot be nil when corpid is set')
|
41
|
-
end
|
35
|
+
errors.add(:corpsecret, 'cannot be nil when corpid is set') if self[:corpsecret].blank?
|
36
|
+
errors.add(:agentid, 'cannot be nil when corpid is set') if self[:agentid].blank?
|
42
37
|
else
|
43
38
|
errors[:base] << 'Either appid or corpid must be set'
|
44
39
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
def self.redis
|
3
5
|
# You can reuse existing redis connection and remove this method if require
|
@@ -17,7 +19,7 @@ module Wechat
|
|
17
19
|
private
|
18
20
|
|
19
21
|
def redis_key
|
20
|
-
"my_app_wechat_token_#{
|
22
|
+
"my_app_wechat_token_#{secret}"
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
@@ -25,7 +27,7 @@ module Wechat
|
|
25
27
|
module Ticket
|
26
28
|
class JsapiBase
|
27
29
|
def read_ticket
|
28
|
-
JSON.parse(Wechat.redis.get(redis_key))
|
30
|
+
JSON.parse(Wechat.redis.get(redis_key)) || {}
|
29
31
|
end
|
30
32
|
|
31
33
|
def write_ticket(ticket_hash)
|
@@ -35,7 +37,7 @@ module Wechat
|
|
35
37
|
private
|
36
38
|
|
37
39
|
def redis_key
|
38
|
-
"my_app_wechat_ticket_#{
|
40
|
+
"my_app_wechat_ticket_#{access_token.secret}"
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
data/lib/wechat.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base64'
|
2
4
|
require 'openssl/cipher'
|
3
5
|
require 'wechat/api_loader'
|
@@ -36,15 +38,15 @@ module Wechat
|
|
36
38
|
ApiLoader.reload_config!
|
37
39
|
end
|
38
40
|
|
39
|
-
def self.decrypt(encrypted_data, session_key,
|
41
|
+
def self.decrypt(encrypted_data, session_key, ivector)
|
40
42
|
cipher = OpenSSL::Cipher.new('AES-128-CBC')
|
41
43
|
cipher.decrypt
|
42
44
|
|
43
45
|
cipher.key = Base64.decode64(session_key)
|
44
|
-
cipher.iv = Base64.decode64(
|
46
|
+
cipher.iv = Base64.decode64(ivector)
|
45
47
|
decrypted_data = Base64.decode64(encrypted_data)
|
46
48
|
JSON.parse(cipher.update(decrypted_data) + cipher.final)
|
47
|
-
rescue
|
49
|
+
rescue StandardError => e
|
48
50
|
{ 'errcode': 41003, 'errmsg': e.message }
|
49
51
|
end
|
50
52
|
end
|
data/lib/wechat/api.rb
CHANGED
data/lib/wechat/api_base.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
class ApiBase
|
3
5
|
attr_reader :access_token, :client, :jsapi_ticket
|
4
6
|
|
5
|
-
API_BASE
|
6
|
-
MP_BASE = 'https://mp.weixin.qq.com/cgi-bin/'
|
7
|
-
WXA_BASE = 'https://api.weixin.qq.com/wxa/'
|
8
|
-
OAUTH2_BASE = 'https://api.weixin.qq.com/sns/'
|
9
|
-
DATACUBE_BASE = 'https://api.weixin.qq.com/datacube/'
|
10
|
-
QYAPI_BASE = 'https://qyapi.weixin.qq.com/cgi-bin/'
|
7
|
+
API_BASE = 'https://api.weixin.qq.com/cgi-bin/'
|
8
|
+
MP_BASE = 'https://mp.weixin.qq.com/cgi-bin/'
|
9
|
+
WXA_BASE = 'https://api.weixin.qq.com/wxa/'
|
10
|
+
OAUTH2_BASE = 'https://api.weixin.qq.com/sns/'
|
11
|
+
DATACUBE_BASE = 'https://api.weixin.qq.com/datacube/'
|
12
|
+
QYAPI_BASE = 'https://qyapi.weixin.qq.com/cgi-bin/'
|
11
13
|
|
12
14
|
def callbackip
|
13
15
|
get 'getcallbackip'
|
data/lib/wechat/api_loader.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module ApiLoader
|
3
5
|
def self.with(options)
|
@@ -8,12 +10,12 @@ module Wechat
|
|
8
10
|
js_token_file = options[:js_token_file] || c.jsapi_ticket.presence || '/var/tmp/wechat_jsapi_ticket'
|
9
11
|
type = options[:type] || c.type
|
10
12
|
if c.appid && c.secret && token_file.present?
|
11
|
-
wx_class =
|
13
|
+
wx_class = type == 'mp' ? Wechat::MpApi : Wechat::Api
|
12
14
|
wx_class.new(c.appid, c.secret, token_file, c.timeout, c.skip_verify_ssl, js_token_file)
|
13
15
|
elsif c.corpid && c.corpsecret && token_file.present?
|
14
16
|
Wechat::CorpApi.new(c.corpid, c.corpsecret, token_file, c.agentid, c.timeout, c.skip_verify_ssl, js_token_file)
|
15
17
|
else
|
16
|
-
raise
|
18
|
+
raise 'Need create ~/.wechat.yml with wechat appid and secret or running at rails root folder so wechat can read config/wechat.yml'
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
@@ -35,11 +37,9 @@ module Wechat
|
|
35
37
|
|
36
38
|
configs.symbolize_keys!
|
37
39
|
configs.each do |key, cfg|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
raise "wrong wechat configuration format for #{key}"
|
42
|
-
end
|
40
|
+
raise "wrong wechat configuration format for #{key}" unless cfg.is_a?(Hash)
|
41
|
+
|
42
|
+
cfg.symbolize_keys!
|
43
43
|
end
|
44
44
|
|
45
45
|
if defined?(::Rails)
|
@@ -64,9 +64,7 @@ module Wechat
|
|
64
64
|
end
|
65
65
|
|
66
66
|
private_class_method def self.config_from_db
|
67
|
-
unless class_exists?('WechatConfig')
|
68
|
-
return {}
|
69
|
-
end
|
67
|
+
return {} unless class_exists?('WechatConfig')
|
70
68
|
|
71
69
|
environment = defined?(::Rails) ? Rails.env.to_s : ENV['RAILS_ENV'] || 'development'
|
72
70
|
WechatConfig.get_all_configs(environment)
|
@@ -74,11 +72,11 @@ module Wechat
|
|
74
72
|
|
75
73
|
private_class_method def self.config_from_file
|
76
74
|
if defined?(::Rails)
|
77
|
-
config_file = ENV['WECHAT_CONF_FILE'] || Rails.root.join('config
|
78
|
-
|
75
|
+
config_file = ENV['WECHAT_CONF_FILE'] || Rails.root.join('config', 'wechat.yml')
|
76
|
+
resolve_config_file(config_file, Rails.env.to_s)
|
79
77
|
else
|
80
|
-
rails_config_file = ENV['WECHAT_CONF_FILE'] || File.join(Dir.getwd, 'config
|
81
|
-
application_config_file = File.join(Dir.getwd, 'config
|
78
|
+
rails_config_file = ENV['WECHAT_CONF_FILE'] || File.join(Dir.getwd, 'config', 'wechat.yml')
|
79
|
+
application_config_file = File.join(Dir.getwd, 'config', 'application.yml')
|
82
80
|
home_config_file = File.join(Dir.home, '.wechat.yml')
|
83
81
|
if File.exist?(rails_config_file)
|
84
82
|
rails_env = ENV['RAILS_ENV'] || 'development'
|
@@ -86,54 +84,55 @@ module Wechat
|
|
86
84
|
require 'figaro'
|
87
85
|
Figaro::Application.new(path: application_config_file, environment: rails_env).load
|
88
86
|
end
|
89
|
-
config =
|
90
|
-
if config.present? && (default = config[:default])
|
91
|
-
puts "Using rails project #{ENV['WECHAT_CONF_FILE'] ||
|
87
|
+
config = resolve_config_file(rails_config_file, rails_env)
|
88
|
+
if config.present? && (default = config[:default]) && (default['appid'] || default['corpid'])
|
89
|
+
puts "Using rails project #{ENV['WECHAT_CONF_FILE'] || 'config/wechat.yml'} #{rails_env} setting..."
|
92
90
|
return config
|
93
91
|
end
|
94
92
|
end
|
95
|
-
if File.exist?(home_config_file)
|
96
|
-
return resovle_config_file(home_config_file, nil)
|
97
|
-
end
|
93
|
+
return resolve_config_file(home_config_file, nil) if File.exist?(home_config_file)
|
98
94
|
end
|
99
95
|
end
|
100
96
|
|
101
|
-
private_class_method def self.
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
97
|
+
private_class_method def self.resolve_config_file(config_file, env)
|
98
|
+
return unless File.exist?(config_file)
|
99
|
+
|
100
|
+
# rubocop:disable Security/YAMLLoad
|
101
|
+
raw_data = YAML.load(ERB.new(File.read(config_file)).result)
|
102
|
+
# rubocop:enable Security/YAMLLoad
|
103
|
+
configs = {}
|
104
|
+
if env
|
105
|
+
# Process multiple accounts when env is given
|
106
|
+
raw_data.each do |key, value|
|
107
|
+
if key == env
|
108
|
+
configs[:default] = value
|
109
|
+
else
|
110
|
+
m = /(.*?)_#{env}$/.match(key)
|
111
|
+
configs[m[1].to_sym] = value if m
|
113
112
|
end
|
114
|
-
else
|
115
|
-
# Treat is as one account when env is omitted
|
116
|
-
configs[:default] = raw_data
|
117
113
|
end
|
118
|
-
|
114
|
+
else
|
115
|
+
# Treat is as one account when env is omitted
|
116
|
+
configs[:default] = raw_data
|
119
117
|
end
|
118
|
+
configs
|
120
119
|
end
|
121
120
|
|
122
121
|
private_class_method def self.config_from_environment
|
123
122
|
value = { appid: ENV['WECHAT_APPID'],
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
{default: value}
|
123
|
+
secret: ENV['WECHAT_SECRET'],
|
124
|
+
corpid: ENV['WECHAT_CORPID'],
|
125
|
+
corpsecret: ENV['WECHAT_CORPSECRET'],
|
126
|
+
agentid: ENV['WECHAT_AGENTID'],
|
127
|
+
token: ENV['WECHAT_TOKEN'],
|
128
|
+
access_token: ENV['WECHAT_ACCESS_TOKEN'],
|
129
|
+
encrypt_mode: ENV['WECHAT_ENCRYPT_MODE'],
|
130
|
+
timeout: ENV['WECHAT_TIMEOUT'],
|
131
|
+
skip_verify_ssl: ENV['WECHAT_SKIP_VERIFY_SSL'],
|
132
|
+
encoding_aes_key: ENV['WECHAT_ENCODING_AES_KEY'],
|
133
|
+
jsapi_ticket: ENV['WECHAT_JSAPI_TICKET'],
|
134
|
+
trusted_domain_fullname: ENV['WECHAT_TRUSTED_DOMAIN_FULLNAME'] }
|
135
|
+
{ default: value }
|
137
136
|
end
|
138
137
|
|
139
138
|
private_class_method def self.class_exists?(class_name)
|
data/lib/wechat/cipher.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module Cipher
|
3
5
|
BLOCK_SIZE = 32
|
4
|
-
CIPHER = 'AES-256-CBC'
|
6
|
+
CIPHER = 'AES-256-CBC'
|
5
7
|
|
6
8
|
def encrypt(plain, encoding_aes_key)
|
7
9
|
cipher = OpenSSL::Cipher.new(CIPHER)
|
@@ -51,7 +53,7 @@ module Wechat
|
|
51
53
|
def encode_padding(data)
|
52
54
|
length = data.bytes.length
|
53
55
|
amount_to_pad = BLOCK_SIZE - (length % BLOCK_SIZE)
|
54
|
-
amount_to_pad = BLOCK_SIZE if amount_to_pad
|
56
|
+
amount_to_pad = BLOCK_SIZE if amount_to_pad.zero?
|
55
57
|
padding = ([amount_to_pad].pack('c') * amount_to_pad)
|
56
58
|
data + padding
|
57
59
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module ControllerApi
|
3
5
|
extend ActiveSupport::Concern
|
@@ -20,10 +22,10 @@ module Wechat
|
|
20
22
|
if account
|
21
23
|
config = Wechat.config(account)
|
22
24
|
appid = config.corpid || config.appid
|
23
|
-
is_crop_account =
|
25
|
+
is_crop_account = config.corpid.present?
|
24
26
|
else
|
25
27
|
appid = self.class.corpid || self.class.appid
|
26
|
-
is_crop_account =
|
28
|
+
is_crop_account = self.class.corpid.present?
|
27
29
|
end
|
28
30
|
|
29
31
|
raise 'Can not get corpid or appid, so please configure it first to using wechat_oauth2' if appid.blank?
|
@@ -37,6 +39,7 @@ module Wechat
|
|
37
39
|
}
|
38
40
|
|
39
41
|
return generate_oauth2_url(oauth2_params) unless block_given?
|
42
|
+
|
40
43
|
is_crop_account ? wechat_corp_oauth2(oauth2_params, account, &block) : wechat_public_oauth2(oauth2_params, account, &block)
|
41
44
|
end
|
42
45
|
|
@@ -47,7 +50,7 @@ module Wechat
|
|
47
50
|
unionid = cookies.signed_or_encrypted[:we_unionid]
|
48
51
|
we_token = cookies.signed_or_encrypted[:we_access_token]
|
49
52
|
if openid.present?
|
50
|
-
yield openid, { 'openid' => openid, 'unionid' => unionid, 'access_token' => we_token}
|
53
|
+
yield openid, { 'openid' => openid, 'unionid' => unionid, 'access_token' => we_token }
|
51
54
|
elsif params[:code].present? && params[:state] == oauth2_params[:state]
|
52
55
|
access_info = wechat(account).web_access_token(params[:code])
|
53
56
|
cookies.signed_or_encrypted[:we_openid] = { value: access_info['openid'], expires: self.class.oauth2_cookie_duration.from_now }
|
@@ -55,7 +58,7 @@ module Wechat
|
|
55
58
|
cookies.signed_or_encrypted[:we_access_token] = { value: access_info['access_token'], expires: self.class.oauth2_cookie_duration.from_now }
|
56
59
|
yield access_info['openid'], access_info
|
57
60
|
else
|
58
|
-
|
61
|
+
Rails::VERSION::MAJOR >= 6 ? (redirect_to generate_oauth2_url(oauth2_params), allow_other_host: true) : (redirect_to generate_oauth2_url(oauth2_params))
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
@@ -70,18 +73,18 @@ module Wechat
|
|
70
73
|
cookies.signed_or_encrypted[:we_deviceid] = { value: userinfo['DeviceId'], expires: self.class.oauth2_cookie_duration.from_now }
|
71
74
|
yield userinfo['UserId'], userinfo
|
72
75
|
else
|
73
|
-
|
76
|
+
Rails::VERSION::MAJOR >= 6 ? (redirect_to generate_oauth2_url(oauth2_params), allow_other_host: true) : (redirect_to generate_oauth2_url(oauth2_params))
|
74
77
|
end
|
75
78
|
end
|
76
79
|
|
77
80
|
def generate_redirect_uri(account = nil)
|
78
81
|
domain_name = if account
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
82
|
+
Wechat.config(account).trusted_domain_fullname
|
83
|
+
else
|
84
|
+
self.class.trusted_domain_fullname
|
85
|
+
end
|
83
86
|
page_url = domain_name ? "#{domain_name}#{request.original_fullpath}" : request.original_url
|
84
|
-
safe_query = request.query_parameters.reject { |k, _| %w
|
87
|
+
safe_query = request.query_parameters.reject { |k, _| %w[code state access_token].include? k }.to_query
|
85
88
|
page_url.sub(request.query_string, safe_query)
|
86
89
|
end
|
87
90
|
|
data/lib/wechat/corp_api.rb
CHANGED
data/lib/wechat/helpers.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module Helpers
|
3
5
|
def wechat_config_js(config_options = {})
|
@@ -22,25 +24,25 @@ module Wechat
|
|
22
24
|
else
|
23
25
|
controller.request.original_url
|
24
26
|
end
|
25
|
-
page_url = page_url.split('#').first if
|
27
|
+
page_url = page_url.split('#').first if ios?
|
26
28
|
js_hash = api.jsapi_ticket.signature(page_url)
|
27
29
|
|
28
|
-
config_js =
|
29
|
-
wx.config({
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
});
|
37
|
-
WECHAT_CONFIG_JS
|
30
|
+
config_js = <<~WECHAT_CONFIG_JS
|
31
|
+
wx.config({
|
32
|
+
debug: #{config_options[:debug]},
|
33
|
+
appId: "#{app_id}",
|
34
|
+
timestamp: "#{js_hash[:timestamp]}",
|
35
|
+
nonceStr: "#{js_hash[:noncestr]}",
|
36
|
+
signature: "#{js_hash[:signature]}",
|
37
|
+
jsApiList: ['#{config_options[:api].join("','")}']
|
38
|
+
});
|
39
|
+
WECHAT_CONFIG_JS
|
38
40
|
javascript_tag config_js, type: 'application/javascript'
|
39
41
|
end
|
40
42
|
|
41
43
|
private
|
42
44
|
|
43
|
-
def
|
45
|
+
def ios?
|
44
46
|
controller.request.user_agent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
|
45
47
|
end
|
46
48
|
end
|
data/lib/wechat/http_client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'http'
|
2
4
|
|
3
5
|
module Wechat
|
@@ -35,10 +37,10 @@ module Wechat
|
|
35
37
|
params = header.delete(:params)
|
36
38
|
form_file = file.is_a?(HTTP::FormData::File) ? file : HTTP::FormData::File.new(file)
|
37
39
|
httprb.headers(header)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
.post(url, params: params,
|
41
|
+
form: { media: form_file,
|
42
|
+
hack: 'X' }, # Existing here for http-form_data 1.0.1 handle single param improperly
|
43
|
+
ssl_context: ssl_context)
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
@@ -51,6 +53,7 @@ module Wechat
|
|
51
53
|
response = yield("#{url_base}#{path}", header)
|
52
54
|
|
53
55
|
raise "Request not OK, response status #{response.status}" if response.status != 200
|
56
|
+
|
54
57
|
parse_response(response, as || :json) do |parse_as, data|
|
55
58
|
break data unless parse_as == :json && data['errcode'].present?
|
56
59
|
|
@@ -71,25 +74,27 @@ module Wechat
|
|
71
74
|
end
|
72
75
|
end
|
73
76
|
|
74
|
-
def parse_response(response,
|
77
|
+
def parse_response(response, as_type)
|
75
78
|
content_type = response.headers[:content_type]
|
76
79
|
parse_as = {
|
77
80
|
%r{^application\/json} => :json,
|
78
|
-
%r{^image\/.*}
|
79
|
-
%r{^audio\/.*}
|
80
|
-
%r{^voice\/.*}
|
81
|
-
%r{^text\/html}
|
82
|
-
%r{^text\/plain}
|
83
|
-
}.each_with_object([]) { |match, memo| memo << match[1] if content_type =~ match[0] }.first ||
|
81
|
+
%r{^image\/.*} => :file,
|
82
|
+
%r{^audio\/.*} => :file,
|
83
|
+
%r{^voice\/.*} => :file,
|
84
|
+
%r{^text\/html} => :xml,
|
85
|
+
%r{^text\/plain} => :probably_json
|
86
|
+
}.each_with_object([]) { |match, memo| memo << match[1] if content_type =~ match[0] }.first || as_type || :text
|
84
87
|
|
85
88
|
# try to parse response as json, fallback to user-specified format or text if failed
|
86
89
|
if parse_as == :probably_json
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
parse_as = as || :text
|
90
|
+
begin
|
91
|
+
data = JSON.parse response.body.to_s.gsub(/[\u0000-\u001f]+/, '')
|
92
|
+
rescue StandardError
|
93
|
+
nil
|
92
94
|
end
|
95
|
+
return yield(:json, data) if data
|
96
|
+
|
97
|
+
parse_as = as_type || :text
|
93
98
|
end
|
94
99
|
|
95
100
|
case parse_as
|
data/lib/wechat/message.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
class Message
|
3
5
|
class << self
|
@@ -70,6 +72,7 @@ module Wechat
|
|
70
72
|
|
71
73
|
def session
|
72
74
|
return nil unless Wechat.config.have_session_class
|
75
|
+
|
73
76
|
@message_hash[:WechatSession] ||= WechatSession.find_or_initialize_session(underscore_hash_keys(message_hash))
|
74
77
|
end
|
75
78
|
|
@@ -120,8 +123,8 @@ module Wechat
|
|
120
123
|
|
121
124
|
def markdown(content)
|
122
125
|
update(MsgType: 'markdown', Markdown: {
|
123
|
-
|
124
|
-
|
126
|
+
content: content
|
127
|
+
})
|
125
128
|
end
|
126
129
|
|
127
130
|
def transfer_customer_service(kf_account = nil)
|
@@ -207,25 +210,25 @@ module Wechat
|
|
207
210
|
end
|
208
211
|
|
209
212
|
TO_JSON_KEY_MAP = {
|
210
|
-
'TextCard'
|
211
|
-
'Markdown'
|
212
|
-
'ToUserName'
|
213
|
-
'ToPartyName'
|
214
|
-
'ToWxName'
|
215
|
-
'MediaId'
|
216
|
-
'MpNews'
|
217
|
-
'ThumbMediaId'
|
218
|
-
'TemplateId'
|
219
|
-
'FormId'
|
213
|
+
'TextCard' => 'textcard',
|
214
|
+
'Markdown' => 'markdown',
|
215
|
+
'ToUserName' => 'touser',
|
216
|
+
'ToPartyName' => 'toparty',
|
217
|
+
'ToWxName' => 'towxname',
|
218
|
+
'MediaId' => 'media_id',
|
219
|
+
'MpNews' => 'mpnews',
|
220
|
+
'ThumbMediaId' => 'thumb_media_id',
|
221
|
+
'TemplateId' => 'template_id',
|
222
|
+
'FormId' => 'form_id',
|
220
223
|
'ContentSourceUrl' => 'content_source_url',
|
221
|
-
'ShowCoverPic'
|
224
|
+
'ShowCoverPic' => 'show_cover_pic'
|
222
225
|
}.freeze
|
223
226
|
|
224
227
|
TO_JSON_ALLOWED = %w[touser toparty msgtype content image voice video file textcard markdown
|
225
228
|
music news articles template agentid filter
|
226
229
|
send_ignore_reprint mpnews towxname].freeze
|
227
230
|
|
228
|
-
def to_json
|
231
|
+
def to_json(*_args)
|
229
232
|
keep_camel_case_key = message_hash[:MsgType] == 'template'
|
230
233
|
json_hash = deep_recursive(message_hash) do |key, value|
|
231
234
|
key = key.to_s
|
data/lib/wechat/mp_api.rb
CHANGED
data/lib/wechat/responder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'English'
|
2
4
|
require 'wechat/signature'
|
3
5
|
|
@@ -19,12 +21,12 @@ module Wechat
|
|
19
21
|
skip_before_action :verify_authenticity_token, raise: false
|
20
22
|
end
|
21
23
|
|
22
|
-
before_action :config_account, only: [
|
23
|
-
before_action :verify_signature, only: [
|
24
|
+
before_action :config_account, only: %i[show create]
|
25
|
+
before_action :verify_signature, only: %i[show create]
|
24
26
|
else
|
25
27
|
skip_before_filter :verify_authenticity_token
|
26
|
-
before_filter :config_account, only: [
|
27
|
-
before_filter :verify_signature, only: [
|
28
|
+
before_filter :config_account, only: %i[show create]
|
29
|
+
before_filter :verify_signature, only: %i[show create]
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
@@ -32,22 +34,22 @@ module Wechat
|
|
32
34
|
attr_accessor :account_from_request
|
33
35
|
|
34
36
|
def on(message_type, with: nil, respond: nil, &block)
|
35
|
-
raise 'Unknow message type' unless [
|
37
|
+
raise 'Unknow message type' unless %i[text image voice video shortvideo link event click view scan batch_job location label_location fallback].include?(message_type)
|
38
|
+
|
36
39
|
config = respond.nil? ? {} : { respond: respond }
|
37
40
|
config[:proc] = block if block_given?
|
38
41
|
|
39
42
|
if with.present?
|
40
|
-
raise 'Only text, event, click, view, scan and batch_job can having :with parameters' unless [
|
43
|
+
raise 'Only text, event, click, view, scan and batch_job can having :with parameters' unless %i[text event click view scan batch_job].include?(message_type)
|
44
|
+
|
41
45
|
config[:with] = with
|
42
46
|
if message_type == :scan
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
raise 'on :scan only support string in parameter with, detail see https://github.com/Eric-Guo/wechat/issues/84'
|
47
|
-
end
|
47
|
+
raise 'on :scan only support string in parameter with, detail see https://github.com/Eric-Guo/wechat/issues/84' unless with.is_a?(String)
|
48
|
+
|
49
|
+
self.known_scan_key_lists = with
|
48
50
|
end
|
49
|
-
|
50
|
-
raise 'Message type click, view, scan and batch_job must specify :with parameters'
|
51
|
+
elsif %i[click view scan batch_job].include?(message_type)
|
52
|
+
raise 'Message type click, view, scan and batch_job must specify :with parameters'
|
51
53
|
end
|
52
54
|
|
53
55
|
case message_type
|
@@ -86,15 +88,15 @@ module Wechat
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def user_defined_scan_responders
|
89
|
-
@
|
91
|
+
@user_defined_scan_responders ||= []
|
90
92
|
end
|
91
93
|
|
92
94
|
def user_defined_location_responders
|
93
|
-
@
|
95
|
+
@user_defined_location_responders ||= []
|
94
96
|
end
|
95
97
|
|
96
98
|
def user_defined_label_location_responders
|
97
|
-
@
|
99
|
+
@user_defined_label_location_responders ||= []
|
98
100
|
end
|
99
101
|
|
100
102
|
def user_defined_responders(type)
|
@@ -110,17 +112,17 @@ module Wechat
|
|
110
112
|
when :text
|
111
113
|
yield(* match_responders(responders, message[:Content]))
|
112
114
|
when :event
|
113
|
-
if
|
115
|
+
if message[:Event] == 'click' && !user_defined_click_responders(message[:EventKey]).empty?
|
114
116
|
yield(* user_defined_click_responders(message[:EventKey]), message[:EventKey])
|
115
|
-
elsif
|
117
|
+
elsif message[:Event] == 'view' && !user_defined_view_responders(message[:EventKey]).empty?
|
116
118
|
yield(* user_defined_view_responders(message[:EventKey]), message[:EventKey])
|
117
|
-
elsif
|
119
|
+
elsif message[:Event] == 'click'
|
118
120
|
yield(* match_responders(responders, message[:EventKey]))
|
119
|
-
elsif known_scan_key_lists.include?(message[:EventKey]) && %w
|
121
|
+
elsif known_scan_key_lists.include?(message[:EventKey]) && %w[scan subscribe scancode_push scancode_waitmsg].freeze.include?(message[:Event])
|
120
122
|
yield(* known_scan_with_match_responders(user_defined_scan_responders, message))
|
121
|
-
elsif
|
123
|
+
elsif message[:Event] == 'batch_job_result'
|
122
124
|
yield(* user_defined_batch_job_responders(message[:BatchJob][:JobType]), message[:BatchJob])
|
123
|
-
elsif
|
125
|
+
elsif message[:Event] == 'location'
|
124
126
|
yield(* user_defined_location_responders, message)
|
125
127
|
else
|
126
128
|
yield(* match_responders(responders, message[:Event]))
|
@@ -145,8 +147,8 @@ module Wechat
|
|
145
147
|
|
146
148
|
if condition.is_a? Regexp
|
147
149
|
memo[:scoped] ||= [responder] + $LAST_MATCH_INFO.captures if value =~ condition
|
148
|
-
|
149
|
-
memo[:scoped] ||= [responder, value]
|
150
|
+
elsif value == condition
|
151
|
+
memo[:scoped] ||= [responder, value]
|
150
152
|
end
|
151
153
|
end
|
152
154
|
matched[:scoped] || matched[:general]
|
@@ -154,9 +156,9 @@ module Wechat
|
|
154
156
|
|
155
157
|
def known_scan_with_match_responders(responders, message)
|
156
158
|
matched = responders.each_with_object({}) do |responder, memo|
|
157
|
-
if %w
|
159
|
+
if %w[scan subscribe].freeze.include?(message[:Event]) && message[:EventKey] == responder[:with]
|
158
160
|
memo[:scaned] ||= [responder, message[:Ticket]]
|
159
|
-
elsif %w
|
161
|
+
elsif %w[scancode_push scancode_waitmsg].freeze.include?(message[:Event]) && message[:EventKey] == responder[:with]
|
160
162
|
memo[:scaned] ||= [responder, message[:ScanCodeInfo][:ScanResult], message[:ScanCodeInfo][:ScanType]]
|
161
163
|
end
|
162
164
|
end
|
@@ -181,12 +183,10 @@ module Wechat
|
|
181
183
|
else
|
182
184
|
render text: echostr
|
183
185
|
end
|
186
|
+
elsif Rails::VERSION::MAJOR >= 4
|
187
|
+
render plain: params[:echostr]
|
184
188
|
else
|
185
|
-
|
186
|
-
render plain: params[:echostr]
|
187
|
-
else
|
188
|
-
render text: params[:echostr]
|
189
|
-
end
|
189
|
+
render text: params[:echostr]
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
@@ -249,11 +249,11 @@ module Wechat
|
|
249
249
|
if Rails::VERSION::MAJOR >= 5
|
250
250
|
data_hash = data_hash.to_unsafe_hash if data_hash.instance_of?(ActionController::Parameters)
|
251
251
|
HashWithIndifferentAccess.new(data_hash).tap do |msg|
|
252
|
-
msg[:Event]
|
252
|
+
msg[:Event]&.downcase!
|
253
253
|
end
|
254
254
|
else
|
255
255
|
HashWithIndifferentAccess.new_from_hash_copying_default(data_hash).tap do |msg|
|
256
|
-
msg[:Event]
|
256
|
+
msg[:Event]&.downcase!
|
257
257
|
end
|
258
258
|
end
|
259
259
|
end
|
@@ -263,10 +263,10 @@ module Wechat
|
|
263
263
|
responder ||= self.class.user_defined_responders(:fallback).first
|
264
264
|
|
265
265
|
next if responder.nil?
|
266
|
-
|
267
|
-
|
266
|
+
|
267
|
+
if responder[:respond]
|
268
268
|
request.reply.text responder[:respond]
|
269
|
-
|
269
|
+
elsif responder[:proc]
|
270
270
|
define_singleton_method :process, responder[:proc]
|
271
271
|
number_of_block_parameter = responder[:proc].arity
|
272
272
|
send(:process, *args.unshift(request).take(number_of_block_parameter))
|
@@ -293,8 +293,7 @@ module Wechat
|
|
293
293
|
{ Encrypt: encrypt,
|
294
294
|
MsgSignature: msg_sign,
|
295
295
|
TimeStamp: timestamp,
|
296
|
-
Nonce: nonce
|
297
|
-
}.to_xml(root: 'xml', children: 'item', skip_instruct: true, skip_types: true)
|
296
|
+
Nonce: nonce }.to_xml(root: 'xml', children: 'item', skip_instruct: true, skip_types: true)
|
298
297
|
end
|
299
298
|
|
300
299
|
def request_encrypt_content
|
data/lib/wechat/signature.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'digest/sha1'
|
2
4
|
require 'securerandom'
|
3
5
|
|
@@ -63,8 +65,8 @@ module Wechat
|
|
63
65
|
end
|
64
66
|
|
65
67
|
def write_ticket_to_store(ticket_hash)
|
66
|
-
ticket_hash['got_ticket_at'
|
67
|
-
ticket_hash['ticket_expires_in'
|
68
|
+
ticket_hash['got_ticket_at'] = Time.now.to_i
|
69
|
+
ticket_hash['ticket_expires_in'] = ticket_hash.delete('expires_in')
|
68
70
|
write_ticket(ticket_hash)
|
69
71
|
end
|
70
72
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Wechat
|
2
4
|
module Token
|
3
5
|
class AccessTokenBase
|
@@ -32,8 +34,8 @@ module Wechat
|
|
32
34
|
def write_token_to_store(token_hash)
|
33
35
|
raise InvalidCredentialError unless token_hash.is_a?(Hash) && token_hash['access_token']
|
34
36
|
|
35
|
-
token_hash['got_token_at'
|
36
|
-
token_hash['token_expires_in'
|
37
|
+
token_hash['got_token_at'] = Time.now.to_i
|
38
|
+
token_hash['token_expires_in'] = token_hash.delete('expires_in')
|
37
39
|
write_token(token_hash)
|
38
40
|
end
|
39
41
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wechat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Skinnyworm
|
@@ -12,30 +12,30 @@ cert_chain:
|
|
12
12
|
- |
|
13
13
|
-----BEGIN CERTIFICATE-----
|
14
14
|
MIIEQDCCAqigAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBplcmlj
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
+
|
24
|
-
|
25
|
-
|
26
|
-
|
15
|
+
Lmd1b2N6L0RDPWdtYWlsL0RDPWNvbTAeFw0xOTA4MzAwODIzMTVaFw0yMDA4Mjkw
|
16
|
+
ODIzMTVaMCUxIzAhBgNVBAMMGmVyaWMuZ3VvY3ovREM9Z21haWwvREM9Y29tMIIB
|
17
|
+
ojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA2ZlFyUImo53jN4+1Kk6cyGts
|
18
|
+
LcT2x+i3G4mvZeqrZFCyhgWHJ04oAsfKBLCLRmspRsUuDQ3GQ8P5tH9oTduu55qy
|
19
|
+
ohflwuuffJHwsJYJEQ4hykJSCQ8gd4qj8WrhAmNo15ujq6mjGXPRBtZltR142kKE
|
20
|
+
o1tUYtc4Fr3oIAh65m62ZC2kJ5GATLYQ+U3JiiHWga8iJ8KSHbrzhM4N3ppaeYxD
|
21
|
+
wk6Lwk2OyppO9Iiv9Ad2BEFFd2s7mvYtaMGPWYK2fYTUuq6yKupg3CppX3ttq+bN
|
22
|
+
Ejx6dXn4RwYmATfc/Iv4aVU8lZS9HABqmProgkmi5vE2AAW1rQihm2ACm9VGlX+B
|
23
|
+
J2uIIWFJC56+TmkyqcBM87ztRxFlNLJe1G2S2TZ/qhAyWfoSEDA9eW6OUsb6qvnz
|
24
|
+
PHFPo/Vgq9BEoYMItUsEoJHdmLBjzhiccQx2XWAhZrBREpH20jIC/ereVGRpAeVf
|
25
|
+
kYdFgW84K+lL9XTt9QJNM08YM/mFcpaGq4/2YbbLAgMBAAGjezB5MAkGA1UdEwQC
|
26
|
+
MAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQdvlZDewUSWN/OWf9bTuDHHc5r+TAf
|
27
27
|
BgNVHREEGDAWgRRlcmljLmd1b2N6QGdtYWlsLmNvbTAfBgNVHRIEGDAWgRRlcmlj
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
28
|
+
Lmd1b2N6QGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAkS8q1uex/jWTHtYi
|
29
|
+
6Rgv8hZugwpFCwGPb9i4VgSSA1qkfsFhNxQji1xcpo8hYlH6zW9L6CGC9BU4n9R2
|
30
|
+
iyuuvtHVnxt5i6u39fIw0dIHJdtswSXZ6Kq58RP1Lg1nbmXX0zHz1fXp+DXL7NoJ
|
31
|
+
bBHL9Oy6jps/Lz8fIanPIZ6iT/akA7Mts94v1F9dyeA3IIJVptvmuGFC9PexX4TJ
|
32
|
+
fZ/qmk93C6KEOGEQ0zJzjnKjRZT0KOnJxbpB6/4Xvr+SrTb0yx20s+YWWa28pb0C
|
33
|
+
gNr4HHRfhmqxoTKvM4Mm9oJSBO/ENi5zfLwvwOWRuriMuDGYmKCdwmn4Y9asgYS4
|
34
|
+
2febpVoh2LTA7GDc1VJOKhxisQ2OtYpcTR503eGyIrnS4NGs+B0rXOB7pEX+VOju
|
35
|
+
2Gz/SZ1dgCkJpJU45GNrhatKx3kyg8eC5IHekNrEvJNJqPl0VdRIs/GtUkQyk+np
|
36
|
+
3kTPkfLMsAZznXo3bzMe0V5TB4+a7pFEK1nN/htWrOVkIi/3
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2019-08-
|
38
|
+
date: 2019-08-30 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: activesupport
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
version: '3.2'
|
47
47
|
- - "<"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '
|
49
|
+
version: '7'
|
50
50
|
type: :runtime
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
version: '3.2'
|
57
57
|
- - "<"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
59
|
+
version: '7'
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: http
|
62
62
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,6 +105,20 @@ dependencies:
|
|
105
105
|
- - ">="
|
106
106
|
- !ruby/object:Gem::Version
|
107
107
|
version: '0'
|
108
|
+
- !ruby/object:Gem::Dependency
|
109
|
+
name: rubocop
|
110
|
+
requirement: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - "~>"
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0.74'
|
115
|
+
type: :development
|
116
|
+
prerelease: false
|
117
|
+
version_requirements: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0.74'
|
108
122
|
- !ruby/object:Gem::Dependency
|
109
123
|
name: rails
|
110
124
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,14 +153,14 @@ dependencies:
|
|
139
153
|
requirements:
|
140
154
|
- - "~>"
|
141
155
|
- !ruby/object:Gem::Version
|
142
|
-
version: 1.
|
156
|
+
version: '1.4'
|
143
157
|
type: :development
|
144
158
|
prerelease: false
|
145
159
|
version_requirements: !ruby/object:Gem::Requirement
|
146
160
|
requirements:
|
147
161
|
- - "~>"
|
148
162
|
- !ruby/object:Gem::Version
|
149
|
-
version: 1.
|
163
|
+
version: '1.4'
|
150
164
|
description: API, command and message handling for WeChat in Rails
|
151
165
|
email: eric.guocz@gmail.com
|
152
166
|
executables:
|
@@ -214,7 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
214
228
|
- !ruby/object:Gem::Version
|
215
229
|
version: '0'
|
216
230
|
requirements: []
|
217
|
-
rubygems_version: 3.0.
|
231
|
+
rubygems_version: 3.0.6
|
218
232
|
signing_key:
|
219
233
|
specification_version: 4
|
220
234
|
summary: DSL for wechat message handling and API
|
metadata.gz.sig
CHANGED
Binary file
|