wechat 0.6.5 → 0.6.6
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 +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
|