wechat 0.6.5 → 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +19 -7
- data/bin/wechat +19 -1
- data/lib/action_controller/wechat_responder.rb +1 -1
- data/lib/generators/wechat/templates/app/controllers/wechats_controller.rb +9 -0
- data/lib/generators/wechat/templates/config/wechat.yml +2 -0
- data/lib/wechat.rb +1 -1
- data/lib/wechat/api.rb +1 -3
- data/lib/wechat/api_base.rb +1 -1
- data/lib/wechat/api_loader.rb +7 -14
- data/lib/wechat/corp_api.rb +3 -1
- data/lib/wechat/corp_jsapi_ticket.rb +13 -0
- data/lib/wechat/jsapi_base.rb +67 -0
- data/lib/wechat/jsapi_ticket.rb +3 -65
- data/lib/wechat/responder.rb +12 -3
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38d17166a10c49de46d4e87c787b0869163e13d3
|
4
|
+
data.tar.gz: 3c80f22d8adc8219c4e0b32824d2df63269a6aa6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf6b9c04a09cc228db3366c6b311731404af1a2e01581c5602d96897be3a3ddb3b52d9e0c3e4013c6307bc135f3ba8db7b85ecfa3f8880f3b9a5179e8c933619
|
7
|
+
data.tar.gz: d1091b3c0c44c974c42c9e88a16eadcf256d00c4108c527dc9b3969e24f22e4b7d24a559cf3f61779a09a5bbee90cdd744aa60f4d9522ee5ecb33e4239caa732
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v0.6.6 (released at 12/15/2015)
|
4
|
+
|
5
|
+
* Add jsapi_ticket support for Enterprise Account
|
6
|
+
* Default generated WechatsController < ActionController::Base, as many Rails application may having #authenticate_user or #set_current_user in ApplicationController, so easily affect the first time using experience.
|
7
|
+
* New syntax `on :view, with: 'VIEW_URL'` support.
|
8
|
+
* New command `upload_replaceparty` which combine three sub command to make uploading department easier.
|
9
|
+
* New command `upload_replaceuser` which combine three sub command to make uploading user easier.
|
10
|
+
|
3
11
|
## v0.6.5 (released at 11/24/2015)
|
4
12
|
|
5
13
|
* Handle 48001 error if token is expire/not valid, close #71
|
data/README.md
CHANGED
@@ -10,7 +10,11 @@ WeChat gem 可以帮助开发者方便地在Rails环境中集成微信[公众平
|
|
10
10
|
- [微信JS-SDK](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3) config接口注入权限验证
|
11
11
|
- OAuth 2.0认证机制
|
12
12
|
|
13
|
-
命令行工具`wechat`可以调用各种无需web环境的API。同时也提供了Rails Controller的responder DSL, 可以帮助开发者方便地在Rails
|
13
|
+
命令行工具`wechat`可以调用各种无需web环境的API。同时也提供了Rails Controller的responder DSL, 可以帮助开发者方便地在Rails应用中集成微信的消息处理机制。
|
14
|
+
|
15
|
+
如果你的App还需要集成微信OAuth2.0, 你可以考虑[omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2), 以便和devise集成,提供完整的用户认证。
|
16
|
+
|
17
|
+
如果你对如何制作微信网页UI没有灵感,可以参考官方的[weui](https://github.com/weui/weui),针对Rails的Gem是[weui-rails](https://github.com/Eric-Guo/weui-rails)。
|
14
18
|
|
15
19
|
|
16
20
|
## 安装
|
@@ -106,6 +110,7 @@ default: &default
|
|
106
110
|
access_token: "C:/Users/[user_name]/wechat_access_token"
|
107
111
|
token: ""
|
108
112
|
encoding_aes_key: ""
|
113
|
+
jsapi_ticket: "C:/Users/[user_name]/wechat_jsapi_ticket"
|
109
114
|
|
110
115
|
production:
|
111
116
|
corpid: <%= ENV['WECHAT_CORPID'] %>
|
@@ -115,6 +120,7 @@ production:
|
|
115
120
|
token: <%= ENV['WECHAT_TOKEN'] %>
|
116
121
|
skip_verify_ssl: false
|
117
122
|
encoding_aes_key: <%= ENV['WECHAT_ENCODING_AES_KEY'] %>
|
123
|
+
jsapi_ticket: <%= ENV['WECHAT_JSAPI_TICKET'] %>
|
118
124
|
|
119
125
|
development:
|
120
126
|
<<: *default
|
@@ -136,7 +142,7 @@ Wechat服务器有报道曾出现[RestClient::SSLCertificateNotVerified](http://
|
|
136
142
|
在个别情况下,单个Rails应用可能需要处理来自多个账号的消息,此时可以配置多个responder controller。
|
137
143
|
|
138
144
|
```ruby
|
139
|
-
class WechatFirstController <
|
145
|
+
class WechatFirstController < ActionController::Base
|
140
146
|
wechat_responder appid: "app1", secret: "secret1", token: "token1", access_token: Rails.root.join("tmp/access_token1")
|
141
147
|
|
142
148
|
on :text, with:"help", respond: "help content"
|
@@ -230,8 +236,6 @@ Wechat commands:
|
|
230
236
|
wechat menu_create [MENU_YAML_PATH] # 创建菜单
|
231
237
|
wechat menu_delete # 删除菜单
|
232
238
|
wechat message_send [OPENID, TEXT_MESSAGE] # 发送文字消息
|
233
|
-
wechat qrcode_create_limit_scene [SCENE_ID_OR_STR] # 请求永久二维码
|
234
|
-
wechat qrcode_create_scene [SCENE_ID, EXPIRE_SECONDS] # 请求临时二维码
|
235
239
|
wechat qrcode_download [TICKET, QR_CODE_PIC_PATH] # 通过ticket下载二维码
|
236
240
|
wechat tag [TAG_ID] # 获取标签成员
|
237
241
|
wechat tag_add_department [TAG_ID, PARTY_IDS] # 增加标签部门
|
@@ -243,6 +247,8 @@ Wechat commands:
|
|
243
247
|
wechat tag_update [TAG_ID, TAGNAME] # 更新标签名字
|
244
248
|
wechat tags # 获取标签列表
|
245
249
|
wechat template_message [OPENID, TEMPLATE_YAML_PATH] # 模板消息接口
|
250
|
+
wechat upload_replaceparty [BATCH_PARTY_CSV_PATH] # 上传文件方式全量覆盖部门
|
251
|
+
wechat upload_replaceuser [BATCH_USER_CSV_PATH] # 上传文件方式全量覆盖成员
|
246
252
|
wechat user [OPEN_ID] # 获取用户基本信息
|
247
253
|
wechat user_batchdelete [USER_ID_LIST] # 批量删除成员
|
248
254
|
wechat user_delete [USER_ID] # 删除成员
|
@@ -398,7 +404,7 @@ $ wechat template_message oCfEht9oM*********** template.yml
|
|
398
404
|
然后创建Controller class, 例如
|
399
405
|
|
400
406
|
```ruby
|
401
|
-
class WechatsController <
|
407
|
+
class WechatsController < ActionController::Base
|
402
408
|
wechat_responder
|
403
409
|
|
404
410
|
# 默认文字信息responder
|
@@ -459,6 +465,11 @@ class WechatsController < ApplicationController
|
|
459
465
|
request.reply.text "User: #{request[:FromUserName]} click #{key}"
|
460
466
|
end
|
461
467
|
|
468
|
+
# 当用户点击菜单时
|
469
|
+
on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view|
|
470
|
+
request.reply.text "#{request[:FromUserName]} view #{view}"
|
471
|
+
end
|
472
|
+
|
462
473
|
# 处理图片信息
|
463
474
|
on :image do |request|
|
464
475
|
request.reply.image(request[:MediaId]) #直接将图片返回给用户
|
@@ -533,7 +544,8 @@ end
|
|
533
544
|
- :video 响应视频消息
|
534
545
|
- :link 响应链接消息
|
535
546
|
- :event 响应事件消息, 可以用`:with`参数来匹配事件类型
|
536
|
-
- :click 虚拟响应事件消息,
|
547
|
+
- :click 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
548
|
+
- :view 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
537
549
|
- :scan 虚拟响应事件消息
|
538
550
|
- :batch_job 虚拟响应事件消息
|
539
551
|
- :location 虚拟响应上报地理位置事件消息
|
@@ -542,7 +554,7 @@ end
|
|
542
554
|
### 多客服消息转发
|
543
555
|
|
544
556
|
```ruby
|
545
|
-
class WechatsController <
|
557
|
+
class WechatsController < ActionController::Base
|
546
558
|
# 当无任何responder处理用户信息时,转发至客服处理。
|
547
559
|
on :fallback do |message|
|
548
560
|
message.reply.transfer_customer_service
|
data/bin/wechat
CHANGED
@@ -16,7 +16,7 @@ require 'wechat/api_loader'
|
|
16
16
|
|
17
17
|
class App < Thor
|
18
18
|
package_name 'Wechat'
|
19
|
-
option :
|
19
|
+
option :token_file, aliases: '-t', desc: 'File to store access token'
|
20
20
|
|
21
21
|
attr_reader :wechat_api_client
|
22
22
|
no_commands do
|
@@ -170,6 +170,15 @@ class App < Thor
|
|
170
170
|
puts wechat_api.batch_replaceparty(batch_party_csv_media_id)
|
171
171
|
end
|
172
172
|
|
173
|
+
desc 'upload_replaceparty [BATCH_PARTY_CSV_PATH]', '上传文件方式全量覆盖部门'
|
174
|
+
def upload_replaceparty(batch_party_csv_path)
|
175
|
+
file = File.new(batch_party_csv_path)
|
176
|
+
media_id = wechat_api.media_create('file', file)['media_id']
|
177
|
+
job_id = wechat_api.batch_replaceparty(media_id)['jobid']
|
178
|
+
puts "running job_id: #{job_id}"
|
179
|
+
puts wechat_api.batch_job_result(job_id)
|
180
|
+
end
|
181
|
+
|
173
182
|
desc 'batch_syncuser [SYNC_USER_CSV_MEDIA_ID]', '增量更新成员'
|
174
183
|
def batch_syncuser(sync_user_csv_media_id)
|
175
184
|
puts wechat_api.batch_syncuser(sync_user_csv_media_id)
|
@@ -180,6 +189,15 @@ class App < Thor
|
|
180
189
|
puts wechat_api.batch_replaceuser(batch_user_csv_media_id)
|
181
190
|
end
|
182
191
|
|
192
|
+
desc 'upload_replaceuser [BATCH_USER_CSV_PATH]', '上传文件方式全量覆盖成员'
|
193
|
+
def upload_replaceuser(batch_user_csv_path)
|
194
|
+
file = File.new(batch_user_csv_path)
|
195
|
+
media_id = wechat_api.media_create('file', file)['media_id']
|
196
|
+
job_id = wechat_api.batch_replaceuser(media_id)['jobid']
|
197
|
+
puts "running job_id: #{job_id}"
|
198
|
+
puts wechat_api.batch_job_result(job_id)
|
199
|
+
end
|
200
|
+
|
183
201
|
desc 'convert_to_openid [USER_ID]', 'userid转换成openid'
|
184
202
|
def convert_to_openid(userid)
|
185
203
|
puts wechat_api.convert_to_openid(userid)
|
@@ -14,7 +14,7 @@ module ActionController
|
|
14
14
|
self.wechat = Wechat.api
|
15
15
|
else
|
16
16
|
if corpid.present?
|
17
|
-
self.wechat = Wechat::CorpApi.new(corpid, opts[:corpsecret], opts[:access_token], agentid, skip_verify_ssl)
|
17
|
+
self.wechat = Wechat::CorpApi.new(corpid, opts[:corpsecret], opts[:access_token], agentid, skip_verify_ssl, opts[:jsapi_ticket])
|
18
18
|
else
|
19
19
|
self.wechat = Wechat::Api.new(opts[:appid], opts[:secret], opts[:access_token], skip_verify_ssl, opts[:jsapi_ticket])
|
20
20
|
end
|
@@ -1,4 +1,8 @@
|
|
1
|
+
<% if defined? ActionController::API -%>
|
1
2
|
class WechatsController < ApplicationController
|
3
|
+
<% else -%>
|
4
|
+
class WechatsController < ActionController::Base
|
5
|
+
<% end -%>
|
2
6
|
wechat_responder
|
3
7
|
|
4
8
|
# default text responder when no other match
|
@@ -59,6 +63,11 @@ class WechatsController < ApplicationController
|
|
59
63
|
request.reply.text "User: #{request[:FromUserName]} click #{key}"
|
60
64
|
end
|
61
65
|
|
66
|
+
# When user view URL in the menu button
|
67
|
+
on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view|
|
68
|
+
request.reply.text "#{request[:FromUserName]} view #{view}"
|
69
|
+
end
|
70
|
+
|
62
71
|
# When user sent the imsage
|
63
72
|
on :image do |request|
|
64
73
|
request.reply.image(request[:MediaId]) # Echo the sent image to user
|
@@ -9,6 +9,7 @@ default: &default
|
|
9
9
|
access_token: "C:/Users/[username]/wechat_access_token"
|
10
10
|
encrypt_mode: false # if true must fill encoding_aes_key
|
11
11
|
encoding_aes_key: "my_encoding_aes_key"
|
12
|
+
jsapi_ticket: "C:/Users/[user_name]/wechat_jsapi_ticket"
|
12
13
|
|
13
14
|
production:
|
14
15
|
corpid: <%%= ENV['WECHAT_CORPID'] %>
|
@@ -21,6 +22,7 @@ production:
|
|
21
22
|
access_token: <%%= ENV['WECHAT_ACCESS_TOKEN'] %>
|
22
23
|
encrypt_mode: false # if true must fill encoding_aes_key
|
23
24
|
encoding_aes_key: <%%= ENV['WECHAT_ENCODING_AES_KEY'] %>
|
25
|
+
jsapi_ticket: <%= ENV['WECHAT_JSAPI_TICKET'] %>
|
24
26
|
|
25
27
|
development:
|
26
28
|
<<: *default
|
data/lib/wechat.rb
CHANGED
data/lib/wechat/api.rb
CHANGED
@@ -5,12 +5,10 @@ require 'wechat/jsapi_ticket'
|
|
5
5
|
|
6
6
|
module Wechat
|
7
7
|
class Api < ApiBase
|
8
|
-
attr_reader :jsapi_ticket
|
9
|
-
|
10
8
|
API_BASE = 'https://api.weixin.qq.com/cgi-bin/'
|
11
9
|
OAUTH2_BASE = 'https://api.weixin.qq.com/sns/oauth2/'
|
12
10
|
|
13
|
-
def initialize(appid, secret, token_file, skip_verify_ssl, jsapi_ticket_file
|
11
|
+
def initialize(appid, secret, token_file, skip_verify_ssl, jsapi_ticket_file)
|
14
12
|
@client = Client.new(API_BASE, skip_verify_ssl)
|
15
13
|
@access_token = AccessToken.new(@client, appid, secret, token_file)
|
16
14
|
@jsapi_ticket = JsapiTicket.new(@client, @access_token, jsapi_ticket_file)
|
data/lib/wechat/api_base.rb
CHANGED
data/lib/wechat/api_loader.rb
CHANGED
@@ -1,23 +1,15 @@
|
|
1
1
|
module Wechat
|
2
2
|
module ApiLoader
|
3
|
-
def self.api
|
4
|
-
c = ApiLoader.config
|
5
|
-
if c.corpid.present?
|
6
|
-
@api ||= CorpApi.new(c.corpid, c.corpsecret, c.access_token, c.agentid, c.skip_verify_ssl)
|
7
|
-
else
|
8
|
-
@api ||= Api.new(c.appid, c.secret, c.access_token, c.skip_verify_ssl, c.jsapi_ticket)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
3
|
def self.with(options)
|
13
|
-
c = config
|
4
|
+
c = ApiLoader.config
|
14
5
|
|
15
|
-
token_file = options[:
|
6
|
+
token_file = options[:token_file] || c.access_token || '/var/tmp/wechat_access_token'
|
7
|
+
js_token_file = options[:js_token_file] || c.jsapi_ticket || '/var/tmp/wechat_jsapi_ticket'
|
16
8
|
|
17
9
|
if c.appid && c.secret && token_file.present?
|
18
|
-
Wechat::Api.new(c.appid, c.secret, token_file, c.skip_verify_ssl)
|
10
|
+
Wechat::Api.new(c.appid, c.secret, token_file, c.skip_verify_ssl, js_token_file)
|
19
11
|
elsif c.corpid && c.corpsecret && token_file.present?
|
20
|
-
Wechat::CorpApi.new(c.corpid, c.corpsecret, token_file, c.agentid, c.skip_verify_ssl)
|
12
|
+
Wechat::CorpApi.new(c.corpid, c.corpsecret, token_file, c.agentid, c.skip_verify_ssl, js_token_file)
|
21
13
|
else
|
22
14
|
puts <<-HELP
|
23
15
|
Need create ~/.wechat.yml with wechat appid and secret
|
@@ -77,7 +69,8 @@ HELP
|
|
77
69
|
access_token: ENV['WECHAT_ACCESS_TOKEN'],
|
78
70
|
encrypt_mode: ENV['WECHAT_ENCRYPT_MODE'],
|
79
71
|
skip_verify_ssl: ENV['WECHAT_SKIP_VERIFY_SSL'],
|
80
|
-
encoding_aes_key: ENV['WECHAT_ENCODING_AES_KEY']
|
72
|
+
encoding_aes_key: ENV['WECHAT_ENCODING_AES_KEY'],
|
73
|
+
jsapi_ticket: ENV['WECHAT_JSAPI_TICKET'] }
|
81
74
|
end
|
82
75
|
end
|
83
76
|
end
|
data/lib/wechat/corp_api.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'wechat/api_base'
|
2
2
|
require 'wechat/client'
|
3
3
|
require 'wechat/access_token'
|
4
|
+
require 'wechat/corp_jsapi_ticket'
|
4
5
|
|
5
6
|
module Wechat
|
6
7
|
class CorpAccessToken < AccessToken
|
@@ -17,10 +18,11 @@ module Wechat
|
|
17
18
|
|
18
19
|
API_BASE = 'https://qyapi.weixin.qq.com/cgi-bin/'
|
19
20
|
|
20
|
-
def initialize(appid, secret, token_file, agentid, skip_verify_ssl)
|
21
|
+
def initialize(appid, secret, token_file, agentid, skip_verify_ssl, jsapi_ticket_file)
|
21
22
|
@client = Client.new(API_BASE, skip_verify_ssl)
|
22
23
|
@access_token = CorpAccessToken.new(@client, appid, secret, token_file)
|
23
24
|
@agentid = agentid
|
25
|
+
@jsapi_ticket = CorpJsapiTicket.new(@client, @access_token, jsapi_ticket_file)
|
24
26
|
end
|
25
27
|
|
26
28
|
def agent_list
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'wechat/jsapi_base'
|
2
|
+
|
3
|
+
module Wechat
|
4
|
+
class CorpJsapiTicket < JsapiBase
|
5
|
+
# refresh jsapi ticket
|
6
|
+
def refresh
|
7
|
+
data = client.get('get_jsapi_ticket', params: { access_token: access_token.token })
|
8
|
+
data.merge!('created_at'.freeze => Time.now.to_i)
|
9
|
+
File.open(jsapi_ticket_file, 'w') { |f| f.write(data.to_json) } if valid_ticket(data)
|
10
|
+
@jsapi_ticket_data = data
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
module Wechat
|
5
|
+
class JsapiBase
|
6
|
+
attr_reader :client, :access_token, :jsapi_ticket_file, :jsapi_ticket_data
|
7
|
+
|
8
|
+
def initialize(client, access_token, jsapi_ticket_file)
|
9
|
+
@client = client
|
10
|
+
@access_token = access_token
|
11
|
+
@jsapi_ticket_file = jsapi_ticket_file
|
12
|
+
end
|
13
|
+
|
14
|
+
# 获取微信 jssdk 签名所需的 jsapi_ticket, 返回具有如下结构的 hash:
|
15
|
+
# {
|
16
|
+
# "errcode":0,
|
17
|
+
# "errmsg":"ok",
|
18
|
+
# "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
|
19
|
+
# "expires_in":7200
|
20
|
+
# }
|
21
|
+
def ticket
|
22
|
+
begin
|
23
|
+
@jsapi_ticket_data ||= JSON.parse(File.read(jsapi_ticket_file))
|
24
|
+
created_at = jsapi_ticket_data['created_at'].to_i
|
25
|
+
expires_in = jsapi_ticket_data['expires_in'].to_i
|
26
|
+
if Time.now.to_i - created_at >= expires_in - 3 * 60
|
27
|
+
fail 'jsapi_ticket may be expired'
|
28
|
+
end
|
29
|
+
rescue
|
30
|
+
refresh
|
31
|
+
end
|
32
|
+
valid_ticket(@jsapi_ticket_data)
|
33
|
+
end
|
34
|
+
|
35
|
+
# 获取 jssdk 签名及注册所需其他参数, 返回具有如下结构的 hash:
|
36
|
+
# params = {
|
37
|
+
# noncestr: noncestr,
|
38
|
+
# timestamp: timestamp,
|
39
|
+
# jsapi_ticket: ticket,
|
40
|
+
# url: url,
|
41
|
+
# signature: signature
|
42
|
+
# }
|
43
|
+
def signature(url)
|
44
|
+
timestamp = Time.now.to_i
|
45
|
+
noncestr = SecureRandom.base64(16)
|
46
|
+
params = {
|
47
|
+
noncestr: noncestr,
|
48
|
+
timestamp: timestamp,
|
49
|
+
jsapi_ticket: ticket,
|
50
|
+
url: url
|
51
|
+
}
|
52
|
+
pairs = params.keys.sort.map do |key|
|
53
|
+
"#{key}=#{params[key]}"
|
54
|
+
end
|
55
|
+
result = Digest::SHA1.hexdigest pairs.join('&')
|
56
|
+
params.merge(signature: result)
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def valid_ticket(jsapi_ticket_data)
|
62
|
+
ticket = jsapi_ticket_data['ticket'] || jsapi_ticket_data[:ticket]
|
63
|
+
fail "Response didn't have ticket" if ticket.blank?
|
64
|
+
ticket
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/wechat/jsapi_ticket.rb
CHANGED
@@ -1,75 +1,13 @@
|
|
1
|
-
|
2
|
-
require 'digest/sha1'
|
1
|
+
require 'wechat/jsapi_base'
|
3
2
|
|
4
3
|
module Wechat
|
5
|
-
class JsapiTicket
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(client, access_token, jsapi_ticket_file)
|
9
|
-
@client = client
|
10
|
-
@access_token = access_token
|
11
|
-
@jsapi_ticket_file = jsapi_ticket_file
|
12
|
-
end
|
13
|
-
|
14
|
-
# 获取微信 jssdk 签名所需的 jsapi_ticket, 返回具有如下结构的 hash:
|
15
|
-
# {
|
16
|
-
# "errcode":0,
|
17
|
-
# "errmsg":"ok",
|
18
|
-
# "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
|
19
|
-
# "expires_in":7200
|
20
|
-
# }
|
21
|
-
def ticket
|
22
|
-
begin
|
23
|
-
@jsapi_ticket_data ||= JSON.parse(File.read(jsapi_ticket_file))
|
24
|
-
created_at = jsapi_ticket_data['created_at'].to_i
|
25
|
-
expires_in = jsapi_ticket_data['expires_in'].to_i
|
26
|
-
if Time.now.to_i - created_at >= expires_in - 3 * 60
|
27
|
-
fail 'jsapi_ticket may be expired'
|
28
|
-
end
|
29
|
-
rescue
|
30
|
-
refresh
|
31
|
-
end
|
32
|
-
valid_ticket(@jsapi_ticket_data)
|
33
|
-
end
|
34
|
-
|
35
|
-
# 刷新 jsapi_ticket
|
4
|
+
class JsapiTicket < JsapiBase
|
5
|
+
# refresh jsapi ticket
|
36
6
|
def refresh
|
37
7
|
data = client.get('ticket/getticket', params: { access_token: access_token.token, type: 'jsapi' })
|
38
8
|
data.merge!('created_at'.freeze => Time.now.to_i)
|
39
9
|
File.open(jsapi_ticket_file, 'w') { |f| f.write(data.to_json) } if valid_ticket(data)
|
40
10
|
@jsapi_ticket_data = data
|
41
11
|
end
|
42
|
-
|
43
|
-
# 获取 jssdk 签名及注册所需其他参数, 返回具有如下结构的 hash:
|
44
|
-
# params = {
|
45
|
-
# noncestr: noncestr,
|
46
|
-
# timestamp: timestamp,
|
47
|
-
# jsapi_ticket: ticket,
|
48
|
-
# url: url,
|
49
|
-
# signature: signature
|
50
|
-
# }
|
51
|
-
def signature(url)
|
52
|
-
timestamp = Time.now.to_i
|
53
|
-
noncestr = SecureRandom.base64(16)
|
54
|
-
params = {
|
55
|
-
noncestr: noncestr,
|
56
|
-
timestamp: timestamp,
|
57
|
-
jsapi_ticket: ticket,
|
58
|
-
url: url
|
59
|
-
}
|
60
|
-
pairs = params.keys.sort.map do |key|
|
61
|
-
"#{key}=#{params[key]}"
|
62
|
-
end
|
63
|
-
result = Digest::SHA1.hexdigest pairs.join('&')
|
64
|
-
params.merge(signature: result)
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def valid_ticket(jsapi_ticket_data)
|
70
|
-
ticket = jsapi_ticket_data['ticket'] || jsapi_ticket_data[:ticket]
|
71
|
-
fail "Response didn't have ticket" if ticket.blank?
|
72
|
-
ticket
|
73
|
-
end
|
74
12
|
end
|
75
13
|
end
|
data/lib/wechat/responder.rb
CHANGED
@@ -15,21 +15,23 @@ module Wechat
|
|
15
15
|
attr_accessor :wechat, :token, :corpid, :agentid, :encrypt_mode, :skip_verify_ssl, :encoding_aes_key
|
16
16
|
|
17
17
|
def on(message_type, with: nil, respond: nil, &block)
|
18
|
-
fail 'Unknow message type' unless [:text, :image, :voice, :video, :link, :event, :click, :scan, :batch_job, :location, :fallback].include?(message_type)
|
18
|
+
fail 'Unknow message type' unless [:text, :image, :voice, :video, :link, :event, :click, :view, :scan, :batch_job, :location, :fallback].include?(message_type)
|
19
19
|
config = respond.nil? ? {} : { respond: respond }
|
20
20
|
config.merge!(proc: block) if block_given?
|
21
21
|
|
22
22
|
if with.present?
|
23
|
-
fail 'Only text, event, click, scan and batch_job can having :with parameters' unless [:text, :event, :click, :scan, :batch_job].include?(message_type)
|
23
|
+
fail 'Only text, event, click, view, scan and batch_job can having :with parameters' unless [:text, :event, :click, :view, :scan, :batch_job].include?(message_type)
|
24
24
|
config.merge!(with: with)
|
25
25
|
self.known_scan_key_lists = with if message_type == :scan
|
26
26
|
else
|
27
|
-
fail 'Message type click, scan and batch_job must specify :with parameters' if [:click, :scan, :batch_job].include?(message_type)
|
27
|
+
fail 'Message type click, view, scan and batch_job must specify :with parameters' if [:click, :view, :scan, :batch_job].include?(message_type)
|
28
28
|
end
|
29
29
|
|
30
30
|
case message_type
|
31
31
|
when :click
|
32
32
|
user_defined_click_responders(with) << config
|
33
|
+
when :view
|
34
|
+
user_defined_view_responders(with) << config
|
33
35
|
when :batch_job
|
34
36
|
user_defined_batch_job_responders(with) << config
|
35
37
|
when :location
|
@@ -45,6 +47,11 @@ module Wechat
|
|
45
47
|
@click_responders[with] ||= []
|
46
48
|
end
|
47
49
|
|
50
|
+
def user_defined_view_responders(with)
|
51
|
+
@view_responders ||= {}
|
52
|
+
@view_responders[with] ||= []
|
53
|
+
end
|
54
|
+
|
48
55
|
def user_defined_batch_job_responders(with)
|
49
56
|
@batch_job_responders ||= {}
|
50
57
|
@batch_job_responders[with] ||= []
|
@@ -69,6 +76,8 @@ module Wechat
|
|
69
76
|
when :event
|
70
77
|
if 'click' == message[:Event] && !user_defined_click_responders(message[:EventKey]).empty?
|
71
78
|
yield(* user_defined_click_responders(message[:EventKey]), message[:EventKey])
|
79
|
+
elsif 'view' == message[:Event] && !user_defined_view_responders(message[:EventKey]).empty?
|
80
|
+
yield(* user_defined_view_responders(message[:EventKey]), message[:EventKey])
|
72
81
|
elsif 'click' == message[:Event]
|
73
82
|
yield(* match_responders(responders, message[:EventKey]))
|
74
83
|
elsif known_scan_key_lists.include?(message[:EventKey])
|
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.6.
|
4
|
+
version: 0.6.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Skinnyworm
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-12-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -73,14 +73,14 @@ dependencies:
|
|
73
73
|
requirements:
|
74
74
|
- - "~>"
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version: '3.
|
76
|
+
version: '3.4'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
81
|
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '3.
|
83
|
+
version: '3.4'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: rails
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,6 +119,8 @@ files:
|
|
119
119
|
- lib/wechat/cipher.rb
|
120
120
|
- lib/wechat/client.rb
|
121
121
|
- lib/wechat/corp_api.rb
|
122
|
+
- lib/wechat/corp_jsapi_ticket.rb
|
123
|
+
- lib/wechat/jsapi_base.rb
|
122
124
|
- lib/wechat/jsapi_ticket.rb
|
123
125
|
- lib/wechat/message.rb
|
124
126
|
- lib/wechat/responder.rb
|