wechat 0.2.0 → 0.3.0
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 +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
|