wechat 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +27 -7
- data/bin/wechat +30 -2
- data/lib/wechat/access_token.rb +2 -2
- data/lib/wechat/api.rb +24 -0
- data/lib/wechat/jsapi_ticket.rb +1 -13
- data/lib/wechat/message.rb +7 -2
- data/lib/wechat/responder.rb +5 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1a9041e7d411063724f87e3a8393a120542e1bf
|
4
|
+
data.tar.gz: 1dfa35792fa8f41f9872b00e645196115658308c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14df5df5f86412bbb4076095903884c26a8a7942927d3afa5bdb34d2b0d4c65ff330f41fed0bdf38f970f80893117429efd48e531b6a8f9f566b169314cbfc3d
|
7
|
+
data.tar.gz: 7daf46b7df72eb76dc8508a6bbc6f575acd44e2beb214d9c81d6cb51bf551591ec0c1a172d942e89e0b65fe92fc5bd713454ccd7a4b93add0a40059500a9fc36
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v0.3.0 (released at 8/30/2015)
|
4
|
+
|
5
|
+
* New user group management API
|
6
|
+
* Allow transfer to customer service on fallback. #42
|
7
|
+
* Read and write access_token properly using file locking, #43
|
8
|
+
|
3
9
|
## v0.2.0 (released at 8/27/2015)
|
4
10
|
|
5
11
|
* Add wechat enterprise account support
|
data/README.md
CHANGED
@@ -136,18 +136,24 @@ $ wechat
|
|
136
136
|
Wechat commands:
|
137
137
|
wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
|
138
138
|
wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
|
139
|
-
wechat custom_news [OPENID,
|
139
|
+
wechat custom_news [OPENID, NEWS_YAML_PATH] # 发送图文客服消息
|
140
140
|
wechat custom_text [OPENID, TEXT_MESSAGE] # 发送文字客服消息
|
141
141
|
wechat custom_video [OPENID, VIDEO_PATH] # 发送视频客服消息
|
142
142
|
wechat custom_voice [OPENID, VOICE_PATH] # 发送语音客服消息
|
143
|
+
wechat group_create [GROUP_NAME] # 创建分组
|
144
|
+
wechat group_delete [GROUP_ID] # 删除分组
|
145
|
+
wechat group_update [GROUP_ID, NEW_GROUP_NAME] # 修改分组名
|
146
|
+
wechat groups # 所有用户分组列表
|
143
147
|
wechat media [MEDIA_ID, PATH] # 媒体下载
|
144
|
-
wechat media_create [
|
148
|
+
wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
|
145
149
|
wechat menu # 当前菜单
|
146
|
-
wechat menu_create [
|
150
|
+
wechat menu_create [MENU_YAML_PATH] # 创建菜单
|
147
151
|
wechat menu_delete # 删除菜单
|
148
152
|
wechat message_send [OPENID, TEXT_MESSAGE] # 发送文字消息(仅企业号)
|
149
|
-
wechat template_message [OPENID,
|
153
|
+
wechat template_message [OPENID, TEMPLATE_YAML_PATH] # 模板消息接口
|
150
154
|
wechat user [OPEN_ID] # 查找关注者
|
155
|
+
wechat user_change_group [OPEN_ID, TO_GROUP_ID] # 移动用户分组
|
156
|
+
wechat user_group [OPEN_ID] # 查询用户所在分组
|
151
157
|
wechat users # 关注者列表
|
152
158
|
```
|
153
159
|
|
@@ -237,7 +243,7 @@ button:
|
|
237
243
|
|
238
244
|
```
|
239
245
|
|
240
|
-
|
246
|
+
然后执行命令行,需确保设置,权限管理中有对此应用的管理权限,否则会报[60011](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%85%A8%E5%B1%80%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E)错。
|
241
247
|
|
242
248
|
```
|
243
249
|
$ wechat menu_create menu.yaml
|
@@ -311,11 +317,10 @@ $ wechat template_message oCfEht9oM*********** template.yml
|
|
311
317
|
然后创建Controller class, 例如
|
312
318
|
|
313
319
|
```ruby
|
314
|
-
|
315
320
|
class WechatsController < ApplicationController
|
316
321
|
wechat_responder
|
317
322
|
|
318
|
-
#
|
323
|
+
# 默认文字信息responder
|
319
324
|
on :text do |request, content|
|
320
325
|
request.reply.text "echo: #{content}" #Just echo
|
321
326
|
end
|
@@ -396,10 +401,25 @@ end
|
|
396
401
|
- :event 响应事件消息, 可以用`:with`参数来匹配事件类型
|
397
402
|
- :fallback 默认响应,当收到的消息无法被其他responder响应时,会使用这个responder.
|
398
403
|
|
404
|
+
### 多客服消息转发
|
405
|
+
|
406
|
+
```ruby
|
407
|
+
class WechatsController < ApplicationController
|
408
|
+
# 当无任何responder处理用户信息时,转发至客服处理。
|
409
|
+
on :fallback, respond: nil do |message|
|
410
|
+
message.reply.transfer_customer_service
|
411
|
+
end
|
412
|
+
end
|
413
|
+
```
|
414
|
+
|
415
|
+
注意设置了[多客服消息转发](http://dkf.qq.com/)后,不能再添加`默认文字信息responder`,否则文字消息将得不到转发。
|
416
|
+
|
399
417
|
## Message DSL
|
400
418
|
|
401
419
|
Wechat 的核心是一个Message DSL,帮助开发者构建各种类型的消息,包括主动推送的和被动响应的。
|
402
420
|
....
|
403
421
|
|
404
422
|
|
423
|
+
## 已知问题
|
405
424
|
|
425
|
+
企业号接受菜单消息时,Wechat腾讯服务器无法解析部分域名,请使用IP绑定回调URL,用户的普通消息目前不受影响。
|
data/bin/wechat
CHANGED
@@ -37,8 +37,6 @@ HELP
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
private
|
41
|
-
|
42
40
|
def self.loading_config
|
43
41
|
config = {}
|
44
42
|
|
@@ -64,6 +62,26 @@ HELP
|
|
64
62
|
package_name 'Wechat'
|
65
63
|
option :toke_file, aliases: '-t', desc: 'File to store access token'
|
66
64
|
|
65
|
+
desc 'groups', '所有用户分组列表'
|
66
|
+
def groups
|
67
|
+
puts Helper.with(options).groups
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'group_create [GROUP_NAME]', '创建分组'
|
71
|
+
def group_create(group_name)
|
72
|
+
puts Helper.with(options).group_create(group_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
desc 'group_update [GROUP_ID, NEW_GROUP_NAME]', '修改分组名'
|
76
|
+
def group_update(groupid, new_group_name)
|
77
|
+
puts Helper.with(options).group_update(groupid, new_group_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
desc 'group_delete [GROUP_ID]', '删除分组'
|
81
|
+
def group_delete(groupid)
|
82
|
+
puts Helper.with(options).group_delete(groupid)
|
83
|
+
end
|
84
|
+
|
67
85
|
desc 'users', '关注者列表'
|
68
86
|
def users
|
69
87
|
puts Helper.with(options).users
|
@@ -74,6 +92,16 @@ HELP
|
|
74
92
|
puts Helper.with(options).user(open_id)
|
75
93
|
end
|
76
94
|
|
95
|
+
desc 'user_group [OPEN_ID]', '查询用户所在分组'
|
96
|
+
def user_group(openid)
|
97
|
+
puts Helper.with(options).user_group(openid)
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'user_change_group [OPEN_ID, TO_GROUP_ID]', '移动用户分组'
|
101
|
+
def user_change_group(openid, to_groupid)
|
102
|
+
puts Helper.with(options).user_change_group(openid, to_groupid)
|
103
|
+
end
|
104
|
+
|
77
105
|
desc 'menu', '当前菜单'
|
78
106
|
def menu
|
79
107
|
puts Helper.with(options).menu
|
data/lib/wechat/access_token.rb
CHANGED
@@ -11,7 +11,7 @@ module Wechat
|
|
11
11
|
|
12
12
|
def token
|
13
13
|
begin
|
14
|
-
@token_data ||= JSON.parse(File.read(token_file))
|
14
|
+
@token_data ||= JSON.parse(File.read(token_file, open_args: File::LOCK_SH))
|
15
15
|
created_at = token_data['created_at'].to_i
|
16
16
|
expires_in = token_data['expires_in'].to_i
|
17
17
|
if Time.now.to_i - created_at >= expires_in - 3 * 60
|
@@ -26,7 +26,7 @@ module Wechat
|
|
26
26
|
def refresh
|
27
27
|
data = client.get('token', params: { grant_type: 'client_credential', appid: appid, secret: secret })
|
28
28
|
data.merge!(created_at: Time.now.to_i)
|
29
|
-
File.
|
29
|
+
File.write(token_file, data.to_json) if valid_token(data)
|
30
30
|
@token_data = data
|
31
31
|
end
|
32
32
|
|
data/lib/wechat/api.rb
CHANGED
@@ -15,6 +15,22 @@ class Wechat::Api
|
|
15
15
|
@jsapi_ticket = Wechat::JsapiTicket.new(@client, @access_token, jsapi_ticket_file)
|
16
16
|
end
|
17
17
|
|
18
|
+
def groups
|
19
|
+
get('groups/get')
|
20
|
+
end
|
21
|
+
|
22
|
+
def group_create(group_name)
|
23
|
+
post 'groups/create', JSON.generate(group: { name: group_name })
|
24
|
+
end
|
25
|
+
|
26
|
+
def group_update(groupid, new_group_name)
|
27
|
+
post 'groups/update', JSON.generate(group: { id: groupid, name: new_group_name })
|
28
|
+
end
|
29
|
+
|
30
|
+
def group_delete(groupid)
|
31
|
+
post 'groups/delete', JSON.generate(group: { id: groupid })
|
32
|
+
end
|
33
|
+
|
18
34
|
def users(nextid = nil)
|
19
35
|
params = { params: { next_openid: nextid } } if nextid.present?
|
20
36
|
get('user/get', params || {})
|
@@ -24,6 +40,14 @@ class Wechat::Api
|
|
24
40
|
get('user/info', params: { openid: openid })
|
25
41
|
end
|
26
42
|
|
43
|
+
def user_group(openid)
|
44
|
+
post 'groups/getid', JSON.generate(openid: openid)
|
45
|
+
end
|
46
|
+
|
47
|
+
def user_change_group(openid, to_groupid)
|
48
|
+
post 'groups/members/update', JSON.generate(openid: openid, to_groupid: to_groupid)
|
49
|
+
end
|
50
|
+
|
27
51
|
def menu
|
28
52
|
get('menu/get')
|
29
53
|
end
|
data/lib/wechat/jsapi_ticket.rb
CHANGED
@@ -49,7 +49,7 @@ module Wechat
|
|
49
49
|
# }
|
50
50
|
def signature(url)
|
51
51
|
timestamp = Time.now.to_i
|
52
|
-
noncestr =
|
52
|
+
noncestr = SecureRandom.base64(16)
|
53
53
|
params = {
|
54
54
|
noncestr: noncestr,
|
55
55
|
timestamp: timestamp,
|
@@ -70,17 +70,5 @@ module Wechat
|
|
70
70
|
raise "Response didn't have ticket" if ticket.blank?
|
71
71
|
ticket
|
72
72
|
end
|
73
|
-
|
74
|
-
# 生成随机字符串
|
75
|
-
# @param Integer length 长度, 默认为16
|
76
|
-
# @return String
|
77
|
-
def generate_noncestr(length = 16)
|
78
|
-
chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
79
|
-
str = ''
|
80
|
-
1.upto(length) do |i|
|
81
|
-
str += chars[rand(chars.length)]
|
82
|
-
end
|
83
|
-
str
|
84
|
-
end
|
85
73
|
end
|
86
74
|
end
|
data/lib/wechat/message.rb
CHANGED
@@ -49,8 +49,9 @@ module Wechat
|
|
49
49
|
Wechat.api.media(message_hash[:MediaId])
|
50
50
|
|
51
51
|
when :location
|
52
|
-
message_hash.slice(:Location_X, :Location_Y, :Scale, :Label).
|
53
|
-
results[value[0].to_s.underscore.to_sym] = value[1]
|
52
|
+
message_hash.slice(:Location_X, :Location_Y, :Scale, :Label).each_with_object({}) do |value, results|
|
53
|
+
results[value[0].to_s.underscore.to_sym] = value[1]
|
54
|
+
end
|
54
55
|
else
|
55
56
|
raise "Don't know how to parse message as #{type}"
|
56
57
|
end
|
@@ -68,6 +69,10 @@ module Wechat
|
|
68
69
|
update(MsgType: 'text', Content: content)
|
69
70
|
end
|
70
71
|
|
72
|
+
def transfer_customer_service
|
73
|
+
update(MsgType: 'transfer_customer_service')
|
74
|
+
end
|
75
|
+
|
71
76
|
def image(media_id)
|
72
77
|
update(MsgType: 'image', Image: { MediaId: media_id })
|
73
78
|
end
|
data/lib/wechat/responder.rb
CHANGED
@@ -56,20 +56,19 @@ module Wechat
|
|
56
56
|
private
|
57
57
|
|
58
58
|
def match_responders(responders, value)
|
59
|
-
matched = responders.
|
59
|
+
matched = responders.each_with_object({}) do |responder, memo|
|
60
60
|
condition = responder[:with]
|
61
61
|
|
62
62
|
if condition.nil?
|
63
|
-
|
64
|
-
next
|
63
|
+
memo[:general] ||= [responder, value]
|
64
|
+
next
|
65
65
|
end
|
66
66
|
|
67
67
|
if condition.is_a? Regexp
|
68
|
-
|
68
|
+
memo[:scoped] ||= [responder] + $LAST_MATCH_INFO.captures if value =~ condition
|
69
69
|
else
|
70
|
-
|
70
|
+
memo[:scoped] ||= [responder, value] if value == condition
|
71
71
|
end
|
72
|
-
matched
|
73
72
|
end
|
74
73
|
matched[:scoped] || matched[:general]
|
75
74
|
end
|
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.
|
4
|
+
version: 0.3.0
|
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-08-
|
12
|
+
date: 2015-08-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|