wechat 0.6.7 → 0.6.8
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-CN.md +584 -0
- data/README.md +139 -138
- data/bin/wechat +1 -1
- data/lib/wechat/responder.rb +10 -3
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eda1d074591c670445ef8e73291eb2131e0a595c
|
4
|
+
data.tar.gz: 576db68d3550ab9ac86c392e2ca6757184e0b114
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70af013d27c8a9a144012c564da1d02927d865fcc64837d1bfb371f525503527d830ac80cb53a512927980e717780a8e88d31f15853a4210eb70780d5a7c716e
|
7
|
+
data.tar.gz: c38169bac4eca6015b9be377d95b3b4ae44511ed3df80ae379d76d3b23077e44867da3c98a15437fb447b6a99757479a4e501cc393e2e87bfda8b5279eb4e9e8
|
data/CHANGELOG.md
CHANGED
data/README-CN.md
ADDED
@@ -0,0 +1,584 @@
|
|
1
|
+
WeChat [![Gem Version][version-badge]][rubygems] [![Build Status][travis-badge]][travis] [![Code Climate][codeclimate-badge]][codeclimate] [![Code Coverage][codecoverage-badge]][codecoverage]
|
2
|
+
======
|
3
|
+
|
4
|
+
[![Join the chat][gitter-badge]][gitter] [![Issue Stats][issue-badge]][issuestats] [![PR Stats][pr-badge]][issuestats]
|
5
|
+
|
6
|
+
WeChat gem 可以帮助开发者方便地在Rails环境中集成微信[公众平台](https://mp.weixin.qq.com/)和[企业平台](https://qy.weixin.qq.com)提供的服务,包括:
|
7
|
+
|
8
|
+
- 微信公众/企业平台[主动消息](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF)API(命令行和Web环境都可以使用)
|
9
|
+
- [回调消息](http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%B6%88%E6%81%AF%E4%B8%8E%E4%BA%8B%E4%BB%B6)(必须运行Web服务器)
|
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
|
+
- OAuth 2.0认证机制
|
12
|
+
|
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)。
|
18
|
+
|
19
|
+
|
20
|
+
## 安装
|
21
|
+
|
22
|
+
Using `gem install`
|
23
|
+
|
24
|
+
```
|
25
|
+
gem install "wechat"
|
26
|
+
```
|
27
|
+
|
28
|
+
Or add to your app's `Gemfile`:
|
29
|
+
|
30
|
+
```
|
31
|
+
gem 'wechat'
|
32
|
+
```
|
33
|
+
|
34
|
+
Run the following command to install it:
|
35
|
+
|
36
|
+
```console
|
37
|
+
bundle install
|
38
|
+
```
|
39
|
+
|
40
|
+
Run the generator:
|
41
|
+
|
42
|
+
```console
|
43
|
+
rails generate wechat:install
|
44
|
+
```
|
45
|
+
|
46
|
+
运行`rails g wechat:install`后会自动生成wechat.yml配置,还有wechat controller及相关路由配置到当前Rails项目。
|
47
|
+
|
48
|
+
|
49
|
+
## 配置
|
50
|
+
|
51
|
+
#### 命令行程序的配置
|
52
|
+
|
53
|
+
要使用命令行程序,需要在home目录中创建一个`~/.wechat.yml`,包含以下内容。其中`access_token`是存放access_token的文件位置。
|
54
|
+
|
55
|
+
```
|
56
|
+
appid: "my_appid"
|
57
|
+
secret: "my_secret"
|
58
|
+
access_token: "/var/tmp/wechat_access_token"
|
59
|
+
```
|
60
|
+
|
61
|
+
Windows或者使用企业号,需要存放在`C:/Users/[user_name]/`下,其中corpid和corpsecret可以从企业号管理界面的设置->权限管理,通过新建任意一个管理组后获取。
|
62
|
+
|
63
|
+
```
|
64
|
+
corpid: "my_appid"
|
65
|
+
corpsecret: "my_secret"
|
66
|
+
agentid: 1 # 企业应用的id,整型。可在应用的设置页面查看
|
67
|
+
access_token: "C:/Users/[user_name]/wechat_access_token"
|
68
|
+
```
|
69
|
+
|
70
|
+
#### Rails 全局配置
|
71
|
+
Rails应用程序中,需要将配置文件放在`config/wechat.yml`,可以为不同environment创建不同的配置。
|
72
|
+
|
73
|
+
公众号配置示例:
|
74
|
+
|
75
|
+
```
|
76
|
+
default: &default
|
77
|
+
appid: "app_id"
|
78
|
+
secret: "app_secret"
|
79
|
+
token: "app_token"
|
80
|
+
access_token: "/var/tmp/wechat_access_token"
|
81
|
+
|
82
|
+
production:
|
83
|
+
appid: <%= ENV['WECHAT_APPID'] %>
|
84
|
+
secret: <%= ENV['WECHAT_APP_SECRET'] %>
|
85
|
+
token: <%= ENV['WECHAT_TOKEN'] %>
|
86
|
+
access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
|
87
|
+
|
88
|
+
development:
|
89
|
+
<<: *default
|
90
|
+
|
91
|
+
test:
|
92
|
+
<<: *default
|
93
|
+
```
|
94
|
+
|
95
|
+
公众号可选安全模式(加密模式),通过添加如下配置可开启加密模式。
|
96
|
+
|
97
|
+
```
|
98
|
+
default: &default
|
99
|
+
encrypt_mode: true
|
100
|
+
encoding_aes_key: "my_encoding_aes_key"
|
101
|
+
```
|
102
|
+
|
103
|
+
企业号配置下必须使用加密模式,其中token和encoding_aes_key可以从企业号管理界面的应用中心->某个应用->模式选择,选择回调模式后获得。
|
104
|
+
|
105
|
+
```
|
106
|
+
default: &default
|
107
|
+
corpid: "corpid"
|
108
|
+
corpsecret: "corpsecret"
|
109
|
+
agentid: 1
|
110
|
+
access_token: "C:/Users/[user_name]/wechat_access_token"
|
111
|
+
token: ""
|
112
|
+
encoding_aes_key: ""
|
113
|
+
jsapi_ticket: "C:/Users/[user_name]/wechat_jsapi_ticket"
|
114
|
+
|
115
|
+
production:
|
116
|
+
corpid: <%= ENV['WECHAT_CORPID'] %>
|
117
|
+
corpsecret: <%= ENV['WECHAT_CORPSECRET'] %>
|
118
|
+
agentid: <%= ENV['WECHAT_AGENTID'] %>
|
119
|
+
access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
|
120
|
+
token: <%= ENV['WECHAT_TOKEN'] %>
|
121
|
+
timeout: 30,
|
122
|
+
skip_verify_ssl: true
|
123
|
+
encoding_aes_key: <%= ENV['WECHAT_ENCODING_AES_KEY'] %>
|
124
|
+
jsapi_ticket: <%= ENV['WECHAT_JSAPI_TICKET'] %>
|
125
|
+
|
126
|
+
development:
|
127
|
+
<<: *default
|
128
|
+
|
129
|
+
test:
|
130
|
+
<<: *default
|
131
|
+
```
|
132
|
+
|
133
|
+
##### 配置优先级
|
134
|
+
|
135
|
+
注意在Rails项目根目录下运行`wechat`命令行工具会优先使用`config/wechat.yml`中的`default`配置,如果失败则使用`~\.wechat.yml`中的配置,以便于在生产环境下管理多个微信账号应用。
|
136
|
+
|
137
|
+
##### 配置微信服务器超时
|
138
|
+
|
139
|
+
微信服务器有时请求会花很长时间,如果不配置,默认为20秒,可视情况配置。
|
140
|
+
|
141
|
+
##### 配置跳过SSL认证
|
142
|
+
|
143
|
+
Wechat服务器有报道曾出现[RestClient::SSLCertificateNotVerified](http://qydev.weixin.qq.com/qa/index.php?qa=11037)错误,此时可以选择关闭SSL验证。`skip_verify_ssl: true`
|
144
|
+
|
145
|
+
#### 为每个Responder配置不同的appid和secret
|
146
|
+
|
147
|
+
在个别情况下,单个Rails应用可能需要处理来自多个账号的消息,此时可以配置多个responder controller。
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class WechatFirstController < ActionController::Base
|
151
|
+
wechat_responder appid: "app1", secret: "secret1", token: "token1", access_token: Rails.root.join("tmp/access_token1")
|
152
|
+
|
153
|
+
on :text, with:"help", respond: "help content"
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
#### jssdk 支持
|
158
|
+
|
159
|
+
jssdk 使用前需通过config接口注入权限验证配置, 所需参数可以通过 signature 方法获取:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
WechatsController.wechat.jsapi_ticket.signature(request.original_url)
|
163
|
+
```
|
164
|
+
|
165
|
+
## 关于接口权限
|
166
|
+
|
167
|
+
wechat gems 内部不会检查权限。但因公众号类型不同,和微信服务器端通讯时,可能会被拒绝,详细权限控制可参考[官方文档](http://mp.weixin.qq.com/wiki/7/2d301d4b757dedc333b9a9854b457b47.html)。
|
168
|
+
|
169
|
+
## 使用命令行
|
170
|
+
|
171
|
+
根据企业号和公众号配置不同,wechat提供了的命令行命令。
|
172
|
+
|
173
|
+
#### 公众号命令行
|
174
|
+
|
175
|
+
```
|
176
|
+
$ wechat
|
177
|
+
Wechat commands:
|
178
|
+
wechat callbackip # 获取微信服务器IP地址
|
179
|
+
wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
|
180
|
+
wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
|
181
|
+
wechat custom_news [OPENID, NEWS_YAML_PATH] # 发送图文客服消息
|
182
|
+
wechat custom_text [OPENID, TEXT_MESSAGE] # 发送文字客服消息
|
183
|
+
wechat custom_video [OPENID, VIDEO_PATH] # 发送视频客服消息
|
184
|
+
wechat custom_voice [OPENID, VOICE_PATH] # 发送语音客服消息
|
185
|
+
wechat group_create [GROUP_NAME] # 创建分组
|
186
|
+
wechat group_delete [GROUP_ID] # 删除分组
|
187
|
+
wechat group_update [GROUP_ID, NEW_GROUP_NAME] # 修改分组名
|
188
|
+
wechat groups # 查询所有分组
|
189
|
+
wechat material [MEDIA_ID, PATH] # 永久媒体下载
|
190
|
+
wechat material_add [MEDIA_TYPE, PATH] # 永久媒体上传
|
191
|
+
wechat material_count # 获取永久素材总数
|
192
|
+
wechat material_delete [MEDIA_ID] # 删除永久素材
|
193
|
+
wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
|
194
|
+
wechat media [MEDIA_ID, PATH] # 媒体下载
|
195
|
+
wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
|
196
|
+
wechat menu # 当前菜单
|
197
|
+
wechat menu_create [MENU_YAML_PATH] # 创建菜单
|
198
|
+
wechat menu_delete # 删除菜单
|
199
|
+
wechat oauth2_url [REDIRECT_URI] # 生成OAuth2.0验证URL
|
200
|
+
wechat qrcode_create_limit_scene [SCENE_ID_OR_STR] # 请求永久二维码
|
201
|
+
wechat qrcode_create_scene [SCENE_ID, EXPIRE_SECONDS] # 请求临时二维码
|
202
|
+
wechat qrcode_download [TICKET, QR_CODE_PIC_PATH] # 通过ticket下载二维码
|
203
|
+
wechat template_message [OPENID, TEMPLATE_YAML_PATH] # 模板消息接口
|
204
|
+
wechat user [OPEN_ID] # 获取用户基本信息
|
205
|
+
wechat user_change_group [OPEN_ID, TO_GROUP_ID] # 移动用户分组
|
206
|
+
wechat user_group [OPEN_ID] # 查询用户所在分组
|
207
|
+
wechat user_update_remark [OPEN_ID, REMARK] # 设置备注名
|
208
|
+
wechat users # 关注者列表
|
209
|
+
```
|
210
|
+
|
211
|
+
#### 企业号命令行
|
212
|
+
```
|
213
|
+
$ wechat
|
214
|
+
Wechat commands:
|
215
|
+
wechat agent [AGENT_ID] # 获取企业号应用详情
|
216
|
+
wechat agent_list # 获取应用概况列表
|
217
|
+
wechat batch_job_result [JOB_ID] # 获取异步任务结果
|
218
|
+
wechat batch_replaceparty [BATCH_PARTY_CSV_MEDIA_ID] # 全量覆盖部门
|
219
|
+
wechat batch_replaceuser [BATCH_USER_CSV_MEDIA_ID] # 全量覆盖成员
|
220
|
+
wechat batch_syncuser [SYNC_USER_CSV_MEDIA_ID] # 增量更新成员
|
221
|
+
wechat callbackip # 获取微信服务器IP地址
|
222
|
+
wechat convert_to_openid [USER_ID] # userid转换成openid
|
223
|
+
wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
|
224
|
+
wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
|
225
|
+
wechat custom_news [OPENID, NEWS_YAML_PATH] # 发送图文客服消息
|
226
|
+
wechat custom_text [OPENID, TEXT_MESSAGE] # 发送文字客服消息
|
227
|
+
wechat custom_video [OPENID, VIDEO_PATH] # 发送视频客服消息
|
228
|
+
wechat custom_voice [OPENID, VOICE_PATH] # 发送语音客服消息
|
229
|
+
wechat department [DEPARTMENT_ID] # 获取部门列表
|
230
|
+
wechat department_create [NAME, PARENT_ID] # 创建部门
|
231
|
+
wechat department_delete [DEPARTMENT_ID] # 删除部门
|
232
|
+
wechat department_update [DEPARTMENT_ID, NAME] # 更新部门
|
233
|
+
wechat invite_user [USER_ID] # 邀请成员关注
|
234
|
+
wechat material [MEDIA_ID, PATH] # 永久媒体下载
|
235
|
+
wechat material_add [MEDIA_TYPE, PATH] # 永久媒体上传
|
236
|
+
wechat material_count # 获取永久素材总数
|
237
|
+
wechat material_delete [MEDIA_ID] # 删除永久素材
|
238
|
+
wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
|
239
|
+
wechat media [MEDIA_ID, PATH] # 媒体下载
|
240
|
+
wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
|
241
|
+
wechat menu # 当前菜单
|
242
|
+
wechat menu_create [MENU_YAML_PATH] # 创建菜单
|
243
|
+
wechat menu_delete # 删除菜单
|
244
|
+
wechat message_send [OPENID, TEXT_MESSAGE] # 发送文字消息
|
245
|
+
wechat oauth2_url [REDIRECT_URI] # 生成OAuth2.0验证URL
|
246
|
+
wechat qrcode_download [TICKET, QR_CODE_PIC_PATH] # 通过ticket下载二维码
|
247
|
+
wechat tag [TAG_ID] # 获取标签成员
|
248
|
+
wechat tag_add_department [TAG_ID, PARTY_IDS] # 增加标签部门
|
249
|
+
wechat tag_add_user [TAG_ID, USER_IDS] # 增加标签成员
|
250
|
+
wechat tag_create [TAGNAME, TAG_ID] # 创建标签
|
251
|
+
wechat tag_del_department [TAG_ID, PARTY_IDS] # 删除标签部门
|
252
|
+
wechat tag_del_user [TAG_ID, USER_IDS] # 删除标签成员
|
253
|
+
wechat tag_delete [TAG_ID] # 删除标签
|
254
|
+
wechat tag_update [TAG_ID, TAGNAME] # 更新标签名字
|
255
|
+
wechat tags # 获取标签列表
|
256
|
+
wechat template_message [OPENID, TEMPLATE_YAML_PATH] # 模板消息接口
|
257
|
+
wechat upload_replaceparty [BATCH_PARTY_CSV_PATH] # 上传文件方式全量覆盖部门
|
258
|
+
wechat upload_replaceuser [BATCH_USER_CSV_PATH] # 上传文件方式全量覆盖成员
|
259
|
+
wechat user [OPEN_ID] # 获取用户基本信息
|
260
|
+
wechat user_batchdelete [USER_ID_LIST] # 批量删除成员
|
261
|
+
wechat user_delete [USER_ID] # 删除成员
|
262
|
+
wechat user_list [DEPARTMENT_ID] # 获取部门成员详情
|
263
|
+
wechat user_simplelist [DEPARTMENT_ID] # 获取部门成员
|
264
|
+
wechat user_update_remark [OPEN_ID, REMARK] # 设置备注名
|
265
|
+
```
|
266
|
+
|
267
|
+
### 使用场景
|
268
|
+
以下是几种典型场景的使用方法
|
269
|
+
|
270
|
+
#####获取所有用户的OPENID
|
271
|
+
|
272
|
+
```
|
273
|
+
$ wechat users
|
274
|
+
|
275
|
+
{"total"=>4, "count"=>4, "data"=>{"openid"=>["oCfEht9***********", "oCfEhtwqa***********", "oCfEht9oMCqGo***********", "oCfEht_81H5o2***********"]}, "next_openid"=>"oCfEht_81H5o2***********"}
|
276
|
+
|
277
|
+
```
|
278
|
+
|
279
|
+
#####获取用户的信息
|
280
|
+
|
281
|
+
```
|
282
|
+
$ wechat user "oCfEht9***********"
|
283
|
+
|
284
|
+
{"subscribe"=>1, "openid"=>"oCfEht9***********", "nickname"=>"Nickname", "sex"=>1, "language"=>"zh_CN", "city"=>"徐汇", "province"=>"上海", "country"=>"中国", "headimgurl"=>"http://wx.qlogo.cn/mmopen/ajNVdqHZLLBd0SG8NjV3UpXZuiaGGPDcaKHebTKiaTyof*********/0", "subscribe_time"=>1395715239}
|
285
|
+
|
286
|
+
```
|
287
|
+
|
288
|
+
##### 获取当前菜单
|
289
|
+
```
|
290
|
+
$ wechat menu
|
291
|
+
|
292
|
+
{"menu"=>{"button"=>[{"type"=>"view", "name"=>"保护的", "url"=>"http://***/protected", "sub_button"=>[]}, {"type"=>"view", "name"=>"公开的", "url"=>"http://***", "sub_button"=>[]}]}}
|
293
|
+
|
294
|
+
```
|
295
|
+
|
296
|
+
##### 创建菜单
|
297
|
+
创建菜单需要一个定义菜单内容的yaml文件,比如
|
298
|
+
menu.yaml
|
299
|
+
|
300
|
+
```
|
301
|
+
button:
|
302
|
+
-
|
303
|
+
name: "我要"
|
304
|
+
sub_button:
|
305
|
+
-
|
306
|
+
type: "scancode_waitmsg"
|
307
|
+
name: "绑定用餐二维码"
|
308
|
+
key: "BINDING_QR_CODE"
|
309
|
+
-
|
310
|
+
type: "click"
|
311
|
+
name: "预订午餐"
|
312
|
+
key: "BOOK_LUNCH"
|
313
|
+
-
|
314
|
+
type: "click"
|
315
|
+
name: "预订晚餐"
|
316
|
+
key: "BOOK_DINNER"
|
317
|
+
-
|
318
|
+
name: "查询"
|
319
|
+
sub_button:
|
320
|
+
-
|
321
|
+
type: "click"
|
322
|
+
name: "进出记录"
|
323
|
+
key: "BADGE_IN_OUT"
|
324
|
+
-
|
325
|
+
type: "click"
|
326
|
+
name: "年假余额"
|
327
|
+
key: "ANNUAL_LEAVE"
|
328
|
+
-
|
329
|
+
type: "view"
|
330
|
+
name: "关于"
|
331
|
+
url: "http://blog.cloud-mes.com/"
|
332
|
+
```
|
333
|
+
|
334
|
+
然后执行命令行,需确保设置,权限管理中有对此应用的管理权限,否则会报[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)错。
|
335
|
+
|
336
|
+
```
|
337
|
+
$ wechat menu_create menu.yaml
|
338
|
+
|
339
|
+
```
|
340
|
+
|
341
|
+
##### 发送客服图文消息
|
342
|
+
需定义一个图文消息内容的yaml文件,比如
|
343
|
+
articles.yaml
|
344
|
+
|
345
|
+
```
|
346
|
+
articles:
|
347
|
+
-
|
348
|
+
title: "习近平在布鲁日欧洲学院演讲"
|
349
|
+
description: "新华网比利时布鲁日4月1日电 国家主席习近平1日在比利时布鲁日欧洲学院发表重要演讲"
|
350
|
+
url: "http://news.sina.com.cn/c/2014-04-01/232629843387.shtml"
|
351
|
+
pic_url: "http://i3.sinaimg.cn/dy/c/2014-04-01/1396366518_bYays1.jpg"
|
352
|
+
```
|
353
|
+
|
354
|
+
然后执行命令行
|
355
|
+
|
356
|
+
```
|
357
|
+
$ wechat custom_news oCfEht9oM*********** articles.yml
|
358
|
+
|
359
|
+
```
|
360
|
+
|
361
|
+
##### 发送模板消息
|
362
|
+
需定义一个模板消息内容的yaml文件,比如
|
363
|
+
template.yml
|
364
|
+
|
365
|
+
```
|
366
|
+
template:
|
367
|
+
template_id: "o64KQ62_xxxxxxxxxxxxxxx-Qz-MlNcRKteq8"
|
368
|
+
url: "http://weixin.qq.com/download"
|
369
|
+
topcolor: "#FF0000"
|
370
|
+
data:
|
371
|
+
first:
|
372
|
+
value: "你好,你已报名成功"
|
373
|
+
color: "#0A0A0A"
|
374
|
+
keynote1:
|
375
|
+
value: "XX活动"
|
376
|
+
color: "#CCCCCC"
|
377
|
+
keynote2:
|
378
|
+
value: "2014年9月16日"
|
379
|
+
color: "#CCCCCC"
|
380
|
+
keynote3:
|
381
|
+
value: "上海徐家汇xxx城"
|
382
|
+
color: "#CCCCCC"
|
383
|
+
remark:
|
384
|
+
value: "欢迎再次使用。"
|
385
|
+
color: "#173177"
|
386
|
+
|
387
|
+
```
|
388
|
+
|
389
|
+
然后执行命令行
|
390
|
+
|
391
|
+
```
|
392
|
+
$ wechat template_message oCfEht9oM*********** template.yml
|
393
|
+
```
|
394
|
+
|
395
|
+
## Rails Responder Controller DSL
|
396
|
+
|
397
|
+
为了在Rails app中响应用户的消息,开发者需要创建一个wechat responder controller. 首先在router中定义
|
398
|
+
|
399
|
+
```ruby
|
400
|
+
resource :wechat, only:[:show, :create]
|
401
|
+
```
|
402
|
+
|
403
|
+
然后创建Controller class, 例如
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
class WechatsController < ActionController::Base
|
407
|
+
wechat_responder
|
408
|
+
|
409
|
+
# 默认文字信息responder
|
410
|
+
on :text do |request, content|
|
411
|
+
request.reply.text "echo: #{content}" #Just echo
|
412
|
+
end
|
413
|
+
|
414
|
+
# 当请求的文字信息内容为'help'时, 使用这个responder处理
|
415
|
+
on :text, with: 'help' do |request|
|
416
|
+
request.reply.text 'help content' #回复帮助信息
|
417
|
+
end
|
418
|
+
|
419
|
+
# 当请求的文字信息内容为'<n>条新闻'时, 使用这个responder处理, 并将n作为第二个参数
|
420
|
+
on :text, with: /^(\d+)条新闻$/ do |request, count|
|
421
|
+
# 微信最多显示10条新闻,大于10条将只取前10条
|
422
|
+
news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: '新闻标题', content: "第#{n}条新闻的内容#{n.hash}" } }
|
423
|
+
request.reply.news(news) do |article, n, index| # 回复"articles"
|
424
|
+
article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/'
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# 当用户加关注
|
429
|
+
on :event, with: 'subscribe' do |request|
|
430
|
+
request.reply.text "User #{request[:FromUserName]} subscribe now"
|
431
|
+
end
|
432
|
+
|
433
|
+
# 公众号收到未关注用户扫描qrscene_xxxxxx二维码时。注意此次扫描事件将不再引发上条的用户加关注事件
|
434
|
+
on :scan, with: 'qrscene_xxxxxx' do |request, ticket|
|
435
|
+
request.reply.text "Unsubscribe user #{request[:FromUserName]} Ticket #{ticket}"
|
436
|
+
end
|
437
|
+
|
438
|
+
# 公众号收到已关注用户扫描创建二维码的scene_id事件时
|
439
|
+
on :scan, with: 'scene_id' do |request, ticket|
|
440
|
+
request.reply.text "Subscribe user #{request[:FromUserName]} Ticket #{ticket}"
|
441
|
+
end
|
442
|
+
|
443
|
+
# 当没有任何on :scan事件处理已关注用户扫描的scene_id时
|
444
|
+
on :event, with: 'scan' do |request|
|
445
|
+
if request[:EventKey].present?
|
446
|
+
request.reply.text "event scan got EventKey #{request[:EventKey]} Ticket #{request[:Ticket]}"
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# 企业号收到EventKey 为二维码扫描结果事件时
|
451
|
+
on :scan, with: 'BINDING_QR_CODE' do |request, scan_result, scan_type|
|
452
|
+
request.reply.text "User #{request[:FromUserName]} ScanResult #{scan_result} ScanType #{scan_type}"
|
453
|
+
end
|
454
|
+
|
455
|
+
# 企业号收到EventKey 为CODE 39码扫描结果事件时
|
456
|
+
on :scan, with: 'BINDING_BARCODE' do |message, scan_result|
|
457
|
+
if scan_result.start_with? 'CODE_39,'
|
458
|
+
message.reply.text "User: #{message[:FromUserName]} scan barcode, result is #{scan_result.split(',')[1]}"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
# 当用户点击菜单时
|
463
|
+
on :click, with: 'BOOK_LUNCH' do |request, key|
|
464
|
+
request.reply.text "User: #{request[:FromUserName]} click #{key}"
|
465
|
+
end
|
466
|
+
|
467
|
+
# 当用户点击菜单时
|
468
|
+
on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view|
|
469
|
+
request.reply.text "#{request[:FromUserName]} view #{view}"
|
470
|
+
end
|
471
|
+
|
472
|
+
# 处理图片信息
|
473
|
+
on :image do |request|
|
474
|
+
request.reply.image(request[:MediaId]) #直接将图片返回给用户
|
475
|
+
end
|
476
|
+
|
477
|
+
# 处理语音信息
|
478
|
+
on :voice do |request|
|
479
|
+
request.reply.voice(request[:MediaId]) #直接语音音返回给用户
|
480
|
+
end
|
481
|
+
|
482
|
+
# 处理视频信息
|
483
|
+
on :video do |request|
|
484
|
+
nickname = wechat.user(request[:FromUserName])['nickname'] #调用 api 获得发送者的nickname
|
485
|
+
request.reply.video(request[:MediaId], title: '回声', description: "#{nickname}发来的视频请求") #直接视频返回给用户
|
486
|
+
end
|
487
|
+
|
488
|
+
# 处理上报地理位置事件
|
489
|
+
on :location do |request|
|
490
|
+
request.reply.text("Latitude: #{message[:Latitude]} Longitude: #{message[:Longitude]} Precision: #{message[:Precision]}")
|
491
|
+
end
|
492
|
+
|
493
|
+
# 当用户取消关注订阅
|
494
|
+
on :event, with: 'unsubscribe' do |request|
|
495
|
+
request.reply.success # user can not receive this message
|
496
|
+
end
|
497
|
+
|
498
|
+
# 成员进入应用的事件推送
|
499
|
+
on :event, with: 'enter_agent' do |request|
|
500
|
+
request.reply.text "#{request[:FromUserName]} enter agent app now"
|
501
|
+
end
|
502
|
+
|
503
|
+
# 当异步任务增量更新成员完成时推送
|
504
|
+
on :batch_job, with: 'sync_user' do |request, batch_job|
|
505
|
+
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
506
|
+
end
|
507
|
+
|
508
|
+
# 当异步任务全量覆盖成员完成时推送
|
509
|
+
on :batch_job, with: 'replace_user' do |request, batch_job|
|
510
|
+
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
511
|
+
end
|
512
|
+
|
513
|
+
# 当异步任务邀请成员关注完成时推送
|
514
|
+
on :batch_job, with: 'invite_user' do |request, batch_job|
|
515
|
+
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
516
|
+
end
|
517
|
+
|
518
|
+
# 当异步任务全量覆盖部门完成时推送
|
519
|
+
on :batch_job, with: 'replace_party' do |request, batch_job|
|
520
|
+
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
521
|
+
end
|
522
|
+
|
523
|
+
# 当无任何responder处理用户信息时,使用这个responder处理
|
524
|
+
on :fallback, respond: 'fallback message'
|
525
|
+
end
|
526
|
+
```
|
527
|
+
|
528
|
+
在controller中使用`wechat_responder`引入Responder DSL, 之后可以用
|
529
|
+
|
530
|
+
```
|
531
|
+
on <message_type> do |message|
|
532
|
+
message.reply.text "some text"
|
533
|
+
end
|
534
|
+
```
|
535
|
+
|
536
|
+
来响应用户信息。
|
537
|
+
|
538
|
+
目前支持的message_type有如下几种
|
539
|
+
|
540
|
+
- :text 响应文字消息,可以用`:with`参数来匹配文本内容 `on(:text, with:'help'){|message, content| ...}`
|
541
|
+
- :image 响应图片消息
|
542
|
+
- :voice 响应语音消息
|
543
|
+
- :video 响应视频消息
|
544
|
+
- :link 响应链接消息
|
545
|
+
- :event 响应事件消息, 可以用`:with`参数来匹配事件类型
|
546
|
+
- :click 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
547
|
+
- :view 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
548
|
+
- :scan 虚拟响应事件消息
|
549
|
+
- :batch_job 虚拟响应事件消息
|
550
|
+
- :location 虚拟响应上报地理位置事件消息
|
551
|
+
- :fallback 默认响应,当收到的消息无法被其他responder响应时,会使用这个responder.
|
552
|
+
|
553
|
+
### 多客服消息转发
|
554
|
+
|
555
|
+
```ruby
|
556
|
+
class WechatsController < ActionController::Base
|
557
|
+
# 当无任何responder处理用户信息时,转发至客服处理。
|
558
|
+
on :fallback do |message|
|
559
|
+
message.reply.transfer_customer_service
|
560
|
+
end
|
561
|
+
end
|
562
|
+
```
|
563
|
+
|
564
|
+
注意设置了[多客服消息转发](http://dkf.qq.com/)后,不能再添加`默认文字信息responder`,否则文字消息将得不到转发。
|
565
|
+
|
566
|
+
## 已知问题
|
567
|
+
|
568
|
+
* 企业号接受菜单消息时,Wechat腾讯服务器无法解析部分域名,请使用IP绑定回调URL,用户的普通消息目前不受影响。
|
569
|
+
* 企业号全量覆盖成员使用的csv通讯录格式,直接将下载的模板导入[是不工作的](http://qydev.weixin.qq.com/qa/index.php?qa=13978),必须使用Excel打开,然后另存为csv格式才会变成合法格式。
|
570
|
+
|
571
|
+
|
572
|
+
[version-badge]: https://badge.fury.io/rb/wechat.svg
|
573
|
+
[rubygems]: https://rubygems.org/gems/wechat
|
574
|
+
[travis-badge]: https://travis-ci.org/Eric-Guo/wechat.svg
|
575
|
+
[travis]: https://travis-ci.org/Eric-Guo/wechat
|
576
|
+
[codeclimate-badge]: https://codeclimate.com/github/Eric-Guo/wechat.png
|
577
|
+
[codeclimate]: https://codeclimate.com/github/Eric-Guo/wechat
|
578
|
+
[codecoverage-badge]: https://codeclimate.com/github/Eric-Guo/wechat/coverage.png
|
579
|
+
[codecoverage]: https://codeclimate.com/github/Eric-Guo/wechat/coverage
|
580
|
+
[gitter-badge]: https://badges.gitter.im/Join%20Chat.svg
|
581
|
+
[gitter]: https://gitter.im/Eric-Guo/wechat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
582
|
+
[issue-badge]: http://issuestats.com/github/Eric-Guo/wechat/badge/issue
|
583
|
+
[pr-badge]: http://issuestats.com/github/Eric-Guo/wechat/badge/pr
|
584
|
+
[issuestats]: http://issuestats.com/github/Eric-Guo/wechat
|
data/README.md
CHANGED
@@ -3,21 +3,27 @@ WeChat [![Gem Version][version-badge]][rubygems] [![Build Status][travis-badge]]
|
|
3
3
|
|
4
4
|
[![Join the chat][gitter-badge]][gitter] [![Issue Stats][issue-badge]][issuestats] [![PR Stats][pr-badge]][issuestats]
|
5
5
|
|
6
|
-
|
6
|
+
[中文文档 Chinese document](/README-CN.md)
|
7
7
|
|
8
|
-
|
9
|
-
- [回调消息](http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%B6%88%E6%81%AF%E4%B8%8E%E4%BA%8B%E4%BB%B6)(必须运行Web服务器)
|
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
|
-
- OAuth 2.0认证机制
|
8
|
+
[Wechat](http://www.wechat.com/) is a free messaging and calling app developed by [Tencent](http://tencent.com/en-us/index.shtml), after linked billion people, Wechat become a platform of application
|
12
9
|
|
13
|
-
|
10
|
+
WeChat gem trying to helping Rails developer to integrated [enterprise account](https://qy.weixin.qq.com) / [public account](https://mp.weixin.qq.com/) easily. Below feature is ready and no need writing adapter code talking to wechat server directly.
|
14
11
|
|
15
|
-
|
12
|
+
- [Sending message](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF) API(Can access via console or in rails)
|
13
|
+
- [Receiving message](http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%B6%88%E6%81%AF%E4%B8%8E%E4%BA%8B%E4%BB%B6)(You must running on rails server to receiving message)
|
14
|
+
- [Wechat JS-SDK](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3) config signature
|
15
|
+
- OAuth 2.0 authentication
|
16
16
|
|
17
|
-
如果你对如何制作微信网页UI没有灵感,可以参考官方的[weui](https://github.com/weui/weui),针对Rails的Gem是[weui-rails](https://github.com/Eric-Guo/weui-rails)。
|
18
17
|
|
18
|
+
`wechat` command share the same API in console, so you can interactive with wechat server quickly, without starting up web environment/code.
|
19
19
|
|
20
|
-
|
20
|
+
A responder DSL can used in Rails controller, so giving a event based interface to handler message sent by end user from wechat server.
|
21
|
+
|
22
|
+
Wechat provide OAuth2.0 as authentication service and possible to intergrated with devise/other authorization gems, [omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2) is a good start
|
23
|
+
|
24
|
+
There is official [weui](https://github.com/weui/weui), which corresponding Rails gems called [weui-rails](https://github.com/Eric-Guo/weui-rails) available, if you prefer following the same UI design as wechat.
|
25
|
+
|
26
|
+
## Installation
|
21
27
|
|
22
28
|
Using `gem install`
|
23
29
|
|
@@ -43,14 +49,14 @@ Run the generator:
|
|
43
49
|
rails generate wechat:install
|
44
50
|
```
|
45
51
|
|
46
|
-
|
52
|
+
`rails g wechat:install` will generated the initial `wechat.yml` configuration, example wechat controller and corresponding routes.
|
47
53
|
|
48
54
|
|
49
|
-
##
|
55
|
+
## Configuration
|
50
56
|
|
51
|
-
####
|
57
|
+
#### Configure for command line
|
52
58
|
|
53
|
-
|
59
|
+
You can using `wechat` command solely, you need created configure file `~/.wechat.yml` and including below content for public account. the access_token will be write as a file.
|
54
60
|
|
55
61
|
```
|
56
62
|
appid: "my_appid"
|
@@ -58,19 +64,22 @@ secret: "my_secret"
|
|
58
64
|
access_token: "/var/tmp/wechat_access_token"
|
59
65
|
```
|
60
66
|
|
61
|
-
|
67
|
+
For enterprise account, need using `corpid` instead of `appid` as enterprise account support multiply application (Tencent called agent) in one enterprise account. Obtain the `corpsecret` is a little tricky, must created at management mode->privilege setting and create any of management group to obtain. Due to Tencent currently only provide Chinese interface for they management console, it's highly recommend you find a college knowing Mandarin to help you to obtain.
|
68
|
+
|
69
|
+
Windows user need store `.wechat.yml` at `C:/Users/[user_name]/` (replace your user name), also be caution the direction of folder separator.
|
62
70
|
|
63
71
|
```
|
64
72
|
corpid: "my_appid"
|
65
73
|
corpsecret: "my_secret"
|
66
|
-
agentid: 1 #
|
74
|
+
agentid: 1 # Integer, which can be obtain from application, settings
|
67
75
|
access_token: "C:/Users/[user_name]/wechat_access_token"
|
68
76
|
```
|
69
77
|
|
70
|
-
#### Rails
|
71
|
-
Rails应用程序中,需要将配置文件放在`config/wechat.yml`,可以为不同environment创建不同的配置。
|
78
|
+
#### Configure for Rails
|
72
79
|
|
73
|
-
|
80
|
+
Rails configuration files support different environment similar to database.yml, after running `rails generate wechat:install` you can find configuration files at `config/wechat.yml`
|
81
|
+
|
82
|
+
Public account congfigure example:
|
74
83
|
|
75
84
|
```
|
76
85
|
default: &default
|
@@ -92,7 +101,8 @@ test:
|
|
92
101
|
<<: *default
|
93
102
|
```
|
94
103
|
|
95
|
-
|
104
|
+
Although it's optional for public account, but highly recommend to enable encrypt mode by add below two items to `wechat.yml`
|
105
|
+
|
96
106
|
|
97
107
|
```
|
98
108
|
default: &default
|
@@ -100,7 +110,9 @@ default: &default
|
|
100
110
|
encoding_aes_key: "my_encoding_aes_key"
|
101
111
|
```
|
102
112
|
|
103
|
-
|
113
|
+
Enterprise account must using encrypt mode (`encrypt_mode: true` is default on, no need configure).
|
114
|
+
|
115
|
+
The `token` and `encoding_aes_key` can be obtain from management console -> one of the agent application -> Mode selection, select callback mode and get/set.
|
104
116
|
|
105
117
|
```
|
106
118
|
default: &default
|
@@ -119,7 +131,7 @@ production:
|
|
119
131
|
access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
|
120
132
|
token: <%= ENV['WECHAT_TOKEN'] %>
|
121
133
|
timeout: 30,
|
122
|
-
skip_verify_ssl: true
|
134
|
+
skip_verify_ssl: true # not recommend
|
123
135
|
encoding_aes_key: <%= ENV['WECHAT_ENCODING_AES_KEY'] %>
|
124
136
|
jsapi_ticket: <%= ENV['WECHAT_JSAPI_TICKET'] %>
|
125
137
|
|
@@ -130,21 +142,21 @@ test:
|
|
130
142
|
<<: *default
|
131
143
|
```
|
132
144
|
|
133
|
-
#####
|
145
|
+
##### Configure priority
|
134
146
|
|
135
|
-
|
147
|
+
Running `wechat` command in the root folder of Rails application will using the Rails configuration first (`default` section), if can not find it, will relay on `~\.wechat.yml`, such behavior enable manage more wechat public account and enterprise account without changing your home `~\.wechat.yml` file.
|
136
148
|
|
137
|
-
#####
|
149
|
+
##### Wechat server timeout setting
|
138
150
|
|
139
|
-
|
151
|
+
Stability various even for Tencent wechat server, so setting a long timeout may needed, default is 20 seconds if not set.
|
140
152
|
|
141
|
-
#####
|
153
|
+
##### Skip the SSL verification
|
142
154
|
|
143
|
-
|
155
|
+
SSL Certification can also be corrupted by some reason in China, [it's reported](http://qydev.weixin.qq.com/qa/index.php?qa=11037) and if it's happen to you, can setting `skip_verify_ssl: true`. (not recommend)
|
144
156
|
|
145
|
-
####
|
157
|
+
#### Configure individual responder with different appid
|
146
158
|
|
147
|
-
|
159
|
+
Rare case, you may want to hosting more than one wechat enterprise/public account in one Rails application, so you can given those configuration info when calling `wechat_responder`
|
148
160
|
|
149
161
|
```ruby
|
150
162
|
class WechatFirstController < ActionController::Base
|
@@ -154,23 +166,25 @@ class WechatFirstController < ActionController::Base
|
|
154
166
|
end
|
155
167
|
```
|
156
168
|
|
157
|
-
####
|
169
|
+
#### JS-SDK helper
|
158
170
|
|
159
|
-
|
171
|
+
JS-SDK enable you control wechat behavior in your web page, but need inject a config with signature methods first, you can obtain those signature hash via below
|
160
172
|
|
161
173
|
```ruby
|
162
174
|
WechatsController.wechat.jsapi_ticket.signature(request.original_url)
|
163
175
|
```
|
164
176
|
|
165
|
-
##
|
177
|
+
## The API privilege
|
166
178
|
|
167
|
-
wechat gems
|
179
|
+
wechat gems won't handle any privilege exception. (except token time out, but it's not important to you as it's auto retry/recovery in gems internally), but Tencent will control a lot of privilege based on your public account type and certification, more info, please reference [official document](http://mp.weixin.qq.com/wiki/7/2d301d4b757dedc333b9a9854b457b47.html).
|
168
180
|
|
169
|
-
##
|
181
|
+
## Command line mode
|
170
182
|
|
171
|
-
|
183
|
+
The available API is different between public account and enterprise account, so wechat gems provide different set of command.
|
172
184
|
|
173
|
-
|
185
|
+
Feel safe if you can not read Chinese in the comments, it's keep there in order to copy & find in official document more easier.
|
186
|
+
|
187
|
+
#### Public account command line
|
174
188
|
|
175
189
|
```
|
176
190
|
$ wechat
|
@@ -208,7 +222,7 @@ Wechat commands:
|
|
208
222
|
wechat users # 关注者列表
|
209
223
|
```
|
210
224
|
|
211
|
-
####
|
225
|
+
#### Enterprise account command line
|
212
226
|
```
|
213
227
|
$ wechat
|
214
228
|
Wechat commands:
|
@@ -264,10 +278,9 @@ Wechat commands:
|
|
264
278
|
wechat user_update_remark [OPEN_ID, REMARK] # 设置备注名
|
265
279
|
```
|
266
280
|
|
267
|
-
###
|
268
|
-
以下是几种典型场景的使用方法
|
281
|
+
### Command line usage demo (partially)
|
269
282
|
|
270
|
-
|
283
|
+
##### Fetch all users open id
|
271
284
|
|
272
285
|
```
|
273
286
|
$ wechat users
|
@@ -276,7 +289,7 @@ $ wechat users
|
|
276
289
|
|
277
290
|
```
|
278
291
|
|
279
|
-
|
292
|
+
##### Fetch user info
|
280
293
|
|
281
294
|
```
|
282
295
|
$ wechat user "oCfEht9***********"
|
@@ -285,15 +298,7 @@ $ wechat user "oCfEht9***********"
|
|
285
298
|
|
286
299
|
```
|
287
300
|
|
288
|
-
|
289
|
-
|
290
|
-
```
|
291
|
-
$ wechat user "oCfEht9***********"
|
292
|
-
|
293
|
-
{"subscribe"=>1, "openid"=>"oCfEht9***********", "nickname"=>"Nickname", "sex"=>1, "language"=>"zh_CN", "city"=>"徐汇", "province"=>"上海", "country"=>"中国", "headimgurl"=>"http://wx.qlogo.cn/mmopen/ajNVdqHZLLBd0SG8NjV3UpXZuiaGGPDcaKHebTKiaTyof*********/0", "subscribe_time"=>1395715239}
|
294
|
-
```
|
295
|
-
|
296
|
-
##### 获取当前菜单
|
301
|
+
##### Fetch menu
|
297
302
|
```
|
298
303
|
$ wechat menu
|
299
304
|
|
@@ -301,14 +306,14 @@ $ wechat menu
|
|
301
306
|
|
302
307
|
```
|
303
308
|
|
304
|
-
#####
|
305
|
-
|
306
|
-
menu.yaml
|
309
|
+
##### Menu create
|
310
|
+
|
311
|
+
menu content for a wechat application can be defined as a yaml files, like `menu.yaml`
|
307
312
|
|
308
313
|
```
|
309
314
|
button:
|
310
315
|
-
|
311
|
-
name: "
|
316
|
+
name: "Want"
|
312
317
|
sub_button:
|
313
318
|
-
|
314
319
|
type: "scancode_waitmsg"
|
@@ -323,7 +328,7 @@ button:
|
|
323
328
|
name: "预订晚餐"
|
324
329
|
key: "BOOK_DINNER"
|
325
330
|
-
|
326
|
-
name: "
|
331
|
+
name: "Query"
|
327
332
|
sub_button:
|
328
333
|
-
|
329
334
|
type: "click"
|
@@ -335,20 +340,20 @@ button:
|
|
335
340
|
key: "ANNUAL_LEAVE"
|
336
341
|
-
|
337
342
|
type: "view"
|
338
|
-
name: "
|
343
|
+
name: "About"
|
339
344
|
url: "http://blog.cloud-mes.com/"
|
340
345
|
```
|
341
346
|
|
342
|
-
|
347
|
+
Caution: make sure you having management privilege for those application below running below command, other will got [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) error.
|
343
348
|
|
344
349
|
```
|
345
350
|
$ wechat menu_create menu.yaml
|
346
|
-
|
347
351
|
```
|
348
352
|
|
349
|
-
#####
|
350
|
-
|
351
|
-
|
353
|
+
##### Sent custom news
|
354
|
+
|
355
|
+
|
356
|
+
Sending custom_news should also defined as a yaml file, like `articles.yaml`
|
352
357
|
|
353
358
|
```
|
354
359
|
articles:
|
@@ -359,16 +364,16 @@ articles:
|
|
359
364
|
pic_url: "http://i3.sinaimg.cn/dy/c/2014-04-01/1396366518_bYays1.jpg"
|
360
365
|
```
|
361
366
|
|
362
|
-
|
367
|
+
After that, can running command:
|
363
368
|
|
364
369
|
```
|
365
370
|
$ wechat custom_news oCfEht9oM*********** articles.yml
|
366
371
|
|
367
372
|
```
|
368
373
|
|
369
|
-
#####
|
370
|
-
|
371
|
-
template.yml
|
374
|
+
##### Sent template message
|
375
|
+
|
376
|
+
Sending template message via yaml file similar too, defined `template.yml` and content is just the template content.
|
372
377
|
|
373
378
|
```
|
374
379
|
template:
|
@@ -377,24 +382,24 @@ template:
|
|
377
382
|
topcolor: "#FF0000"
|
378
383
|
data:
|
379
384
|
first:
|
380
|
-
value: "
|
385
|
+
value: "Hello, you successfully registed"
|
381
386
|
color: "#0A0A0A"
|
382
387
|
keynote1:
|
383
|
-
value: "
|
388
|
+
value: "5km Health Running"
|
384
389
|
color: "#CCCCCC"
|
385
390
|
keynote2:
|
386
|
-
value: "2014
|
391
|
+
value: "2014-09-16"
|
387
392
|
color: "#CCCCCC"
|
388
393
|
keynote3:
|
389
|
-
value: "
|
394
|
+
value: "Centry Park, Pudong, Shanghai"
|
390
395
|
color: "#CCCCCC"
|
391
396
|
remark:
|
392
|
-
value: "
|
397
|
+
value: "Welcome back"
|
393
398
|
color: "#173177"
|
394
399
|
|
395
400
|
```
|
396
401
|
|
397
|
-
|
402
|
+
After that, can running command:
|
398
403
|
|
399
404
|
```
|
400
405
|
$ wechat template_message oCfEht9oM*********** template.yml
|
@@ -402,138 +407,138 @@ $ wechat template_message oCfEht9oM*********** template.yml
|
|
402
407
|
|
403
408
|
## Rails Responder Controller DSL
|
404
409
|
|
405
|
-
|
410
|
+
In order to responding the message user sent, Rails developer need create a wechat responder controller and defined the routing in `routes.rb`
|
406
411
|
|
407
412
|
```ruby
|
408
413
|
resource :wechat, only:[:show, :create]
|
409
414
|
```
|
410
415
|
|
411
|
-
|
416
|
+
So the ActionController should defined like below:
|
412
417
|
|
413
418
|
```ruby
|
414
419
|
class WechatsController < ActionController::Base
|
415
420
|
wechat_responder
|
416
|
-
|
417
|
-
|
421
|
+
wechat_responder
|
422
|
+
|
423
|
+
# default text responder when no other match
|
418
424
|
on :text do |request, content|
|
419
|
-
request.reply.text "echo: #{content}" #Just echo
|
425
|
+
request.reply.text "echo: #{content}" # Just echo
|
420
426
|
end
|
421
427
|
|
422
|
-
#
|
428
|
+
# When receive 'help', will trigger this responder
|
423
429
|
on :text, with: 'help' do |request|
|
424
|
-
request.reply.text 'help content'
|
430
|
+
request.reply.text 'help content'
|
425
431
|
end
|
426
432
|
|
427
|
-
#
|
428
|
-
on :text, with: /^(\d+)
|
429
|
-
#
|
430
|
-
news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: '
|
431
|
-
request.reply.news(news) do |article, n, index| #
|
433
|
+
# When receive '<n>news', will match and will got count as <n> as parameter
|
434
|
+
on :text, with: /^(\d+) news$/ do |request, count|
|
435
|
+
# Wechat article can only contain max 10 items, large than 10 will dropped.
|
436
|
+
news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: 'News title', content: "No. #{n} news content" } }
|
437
|
+
request.reply.news(news) do |article, n, index| # article is return object
|
432
438
|
article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/'
|
433
439
|
end
|
434
440
|
end
|
435
441
|
|
436
|
-
# 当用户加关注
|
437
442
|
on :event, with: 'subscribe' do |request|
|
438
|
-
request.reply.text "
|
443
|
+
request.reply.text "#{request[:FromUserName]} subscribe now"
|
439
444
|
end
|
440
445
|
|
441
|
-
#
|
446
|
+
# When unsubscribe user scan qrcode qrscene_xxxxxx to subscribe in public account
|
447
|
+
# notice user will subscribe public account at same time, so wechat won't trigger subscribe event any more
|
442
448
|
on :scan, with: 'qrscene_xxxxxx' do |request, ticket|
|
443
449
|
request.reply.text "Unsubscribe user #{request[:FromUserName]} Ticket #{ticket}"
|
444
450
|
end
|
445
451
|
|
446
|
-
#
|
452
|
+
# When subscribe user scan scene_id in public account
|
447
453
|
on :scan, with: 'scene_id' do |request, ticket|
|
448
454
|
request.reply.text "Subscribe user #{request[:FromUserName]} Ticket #{ticket}"
|
449
455
|
end
|
450
456
|
|
451
|
-
#
|
457
|
+
# When no any on :scan responder can match subscribe user scaned scene_id
|
452
458
|
on :event, with: 'scan' do |request|
|
453
459
|
if request[:EventKey].present?
|
454
460
|
request.reply.text "event scan got EventKey #{request[:EventKey]} Ticket #{request[:Ticket]}"
|
455
461
|
end
|
456
462
|
end
|
457
463
|
|
458
|
-
#
|
464
|
+
# When enterprise user press menu BINDING_QR_CODE and success to scan bar code
|
459
465
|
on :scan, with: 'BINDING_QR_CODE' do |request, scan_result, scan_type|
|
460
466
|
request.reply.text "User #{request[:FromUserName]} ScanResult #{scan_result} ScanType #{scan_type}"
|
461
467
|
end
|
462
468
|
|
463
|
-
#
|
469
|
+
# Except QR code, wechat can also scan CODE_39 bar code in enterprise account
|
464
470
|
on :scan, with: 'BINDING_BARCODE' do |message, scan_result|
|
465
471
|
if scan_result.start_with? 'CODE_39,'
|
466
472
|
message.reply.text "User: #{message[:FromUserName]} scan barcode, result is #{scan_result.split(',')[1]}"
|
467
473
|
end
|
468
474
|
end
|
469
475
|
|
470
|
-
#
|
476
|
+
# When user click the menu button
|
471
477
|
on :click, with: 'BOOK_LUNCH' do |request, key|
|
472
478
|
request.reply.text "User: #{request[:FromUserName]} click #{key}"
|
473
479
|
end
|
474
480
|
|
475
|
-
#
|
481
|
+
# When user view URL in the menu button
|
476
482
|
on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view|
|
477
483
|
request.reply.text "#{request[:FromUserName]} view #{view}"
|
478
484
|
end
|
479
485
|
|
480
|
-
#
|
486
|
+
# When user sent the imsage
|
481
487
|
on :image do |request|
|
482
|
-
request.reply.image(request[:MediaId])
|
488
|
+
request.reply.image(request[:MediaId]) # Echo the sent image to user
|
483
489
|
end
|
484
490
|
|
485
|
-
#
|
491
|
+
# When user sent the voice
|
486
492
|
on :voice do |request|
|
487
|
-
request.reply.voice(request[:MediaId])
|
493
|
+
request.reply.voice(request[:MediaId]) # Echo the sent voice to user
|
488
494
|
end
|
489
495
|
|
490
|
-
#
|
496
|
+
# When user sent the video
|
491
497
|
on :video do |request|
|
492
|
-
nickname = wechat.user(request[:FromUserName])['nickname']
|
493
|
-
request.reply.video(request[:MediaId], title: '
|
498
|
+
nickname = wechat.user(request[:FromUserName])['nickname'] # Call wechat api to get sender nickname
|
499
|
+
request.reply.video(request[:MediaId], title: 'Echo', description: "Got #{nickname} sent video") # Echo the sent video to user
|
494
500
|
end
|
495
501
|
|
496
|
-
#
|
502
|
+
# When user sent location
|
497
503
|
on :location do |request|
|
498
504
|
request.reply.text("Latitude: #{message[:Latitude]} Longitude: #{message[:Longitude]} Precision: #{message[:Precision]}")
|
499
505
|
end
|
500
506
|
|
501
|
-
# 当用户取消关注订阅
|
502
507
|
on :event, with: 'unsubscribe' do |request|
|
503
508
|
request.reply.success # user can not receive this message
|
504
509
|
end
|
505
510
|
|
506
|
-
#
|
511
|
+
# When user enter the app / agent app
|
507
512
|
on :event, with: 'enter_agent' do |request|
|
508
513
|
request.reply.text "#{request[:FromUserName]} enter agent app now"
|
509
514
|
end
|
510
515
|
|
511
|
-
#
|
516
|
+
# When batch job create/update user (incremental) finished.
|
512
517
|
on :batch_job, with: 'sync_user' do |request, batch_job|
|
513
|
-
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
518
|
+
request.reply.text "sync_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
514
519
|
end
|
515
520
|
|
516
|
-
#
|
521
|
+
# When batch job replace user (full sync) finished.
|
517
522
|
on :batch_job, with: 'replace_user' do |request, batch_job|
|
518
|
-
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
523
|
+
request.reply.text "replace_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
519
524
|
end
|
520
525
|
|
521
|
-
#
|
526
|
+
# When batch job invent user finished.
|
522
527
|
on :batch_job, with: 'invite_user' do |request, batch_job|
|
523
|
-
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
528
|
+
request.reply.text "invite_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
524
529
|
end
|
525
530
|
|
526
|
-
#
|
531
|
+
# When batch job replace department (full sync) finished.
|
527
532
|
on :batch_job, with: 'replace_party' do |request, batch_job|
|
528
|
-
request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
533
|
+
request.reply.text "replace_party job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
|
529
534
|
end
|
530
535
|
|
531
|
-
#
|
536
|
+
# Any not match above will fail to below
|
532
537
|
on :fallback, respond: 'fallback message'
|
533
538
|
end
|
534
539
|
```
|
535
540
|
|
536
|
-
|
541
|
+
So the importent statement is only `wechat_responder`, all other is just a DSL:
|
537
542
|
|
538
543
|
```
|
539
544
|
on <message_type> do |message|
|
@@ -541,46 +546,42 @@ on <message_type> do |message|
|
|
541
546
|
end
|
542
547
|
```
|
543
548
|
|
544
|
-
|
549
|
+
The block code will be running to responding user's message.
|
545
550
|
|
546
|
-
目前支持的message_type有如下几种
|
547
551
|
|
548
|
-
|
549
|
-
- :image 响应图片消息
|
550
|
-
- :voice 响应语音消息
|
551
|
-
- :video 响应视频消息
|
552
|
-
- :link 响应链接消息
|
553
|
-
- :event 响应事件消息, 可以用`:with`参数来匹配事件类型
|
554
|
-
- :click 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
555
|
-
- :view 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
556
|
-
- :scan 虚拟响应事件消息
|
557
|
-
- :batch_job 虚拟响应事件消息
|
558
|
-
- :location 虚拟响应上报地理位置事件消息
|
559
|
-
- :fallback 默认响应,当收到的消息无法被其他responder响应时,会使用这个responder.
|
552
|
+
Below is current supported message_type:
|
560
553
|
|
561
|
-
|
554
|
+
- :text text message, using `:with` to match text content like `on(:text, with:'help'){|message, content| ...}`
|
555
|
+
- :image image message
|
556
|
+
- :voice voice message
|
557
|
+
- :video video message
|
558
|
+
- :link link message
|
559
|
+
- :event event message, using `:with` to match particular event
|
560
|
+
- :click virtual event message, wechat still sent event message,but gems will mapping to menu click event.
|
561
|
+
- :view virtual view message, wechat still sent event message,but gems will mapping to menu view page event.
|
562
|
+
- :scan virtual scan message, wechat still sent event message, but gems will mapping on scan event
|
563
|
+
- :batch_job virtual batch job message
|
564
|
+
- :location virtual location message
|
565
|
+
- :fallback default message, when no other responder can handle incoming messsage, will using fallback to handle
|
566
|
+
|
567
|
+
### Transfer to customer service
|
562
568
|
|
563
569
|
```ruby
|
564
570
|
class WechatsController < ActionController::Base
|
565
|
-
#
|
571
|
+
# When no other responder can handle incoming message, will transfer to human customer service.
|
566
572
|
on :fallback do |message|
|
567
|
-
|
573
|
+
message.reply.transfer_customer_service
|
568
574
|
end
|
569
575
|
end
|
570
576
|
```
|
571
577
|
|
572
|
-
|
573
|
-
|
574
|
-
## Message DSL
|
575
|
-
|
576
|
-
Wechat 的核心是一个Message DSL,帮助开发者构建各种类型的消息,包括主动推送的和被动响应的。
|
577
|
-
....
|
578
|
+
Caution: do not setting default text responder if you want to using [multiply human customer service](http://dkf.qq.com/), other will lead text message can not transfer.
|
578
579
|
|
579
580
|
|
580
|
-
##
|
581
|
+
## Known Issue
|
581
582
|
|
582
|
-
*
|
583
|
-
*
|
583
|
+
* Sometime, enterprise account can not receive the menu message due to Tencent server can not resolved the DNS, so using IP as a callback URL more stable, but it's never happen for user sent text message.
|
584
|
+
* Enterprise batch replace users using a CSV format file, but if you using the download template directly, it's [not working](http://qydev.weixin.qq.com/qa/index.php?qa=13978), must open the CSV file in excel first, then save as CSV format again, seems Tencent only support Excel save as CSV file format.
|
584
585
|
|
585
586
|
|
586
587
|
[version-badge]: https://badge.fury.io/rb/wechat.svg
|
data/bin/wechat
CHANGED
@@ -288,7 +288,7 @@ class App < Thor
|
|
288
288
|
desc 'oauth2_url [REDIRECT_URI]', '生成OAuth2.0验证URL'
|
289
289
|
def oauth2_url(redirect_uri)
|
290
290
|
appid = Wechat.config.corpid || Wechat.config.appid
|
291
|
-
puts oauth2_url(redirect_uri, appid)
|
291
|
+
puts wechat_api.oauth2_url(redirect_uri, appid)
|
292
292
|
end
|
293
293
|
|
294
294
|
desc 'user_update_remark [OPEN_ID, REMARK]', '设置备注名'
|
data/lib/wechat/responder.rb
CHANGED
@@ -7,8 +7,15 @@ module Wechat
|
|
7
7
|
include Cipher
|
8
8
|
|
9
9
|
included do
|
10
|
-
skip_before_filter
|
11
|
-
|
10
|
+
# Rails 5 remove before_filter and skip_before_filter
|
11
|
+
if defined?(:skip_before_action)
|
12
|
+
# Rails 5 API mode won't define verify_authenticity_token
|
13
|
+
skip_before_action :verify_authenticity_token unless defined?(:verify_authenticity_token)
|
14
|
+
before_action :verify_signature, only: [:show, :create]
|
15
|
+
else
|
16
|
+
skip_before_filter :verify_authenticity_token
|
17
|
+
before_filter :verify_signature, only: [:show, :create]
|
18
|
+
end
|
12
19
|
end
|
13
20
|
|
14
21
|
module ClassMethods
|
@@ -153,7 +160,7 @@ module Wechat
|
|
153
160
|
response = run_responder(request)
|
154
161
|
|
155
162
|
if response.respond_to? :to_xml
|
156
|
-
render
|
163
|
+
render plain: process_response(response)
|
157
164
|
else
|
158
165
|
render nothing: true, status: 200, content_type: 'text/html'
|
159
166
|
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.6.
|
4
|
+
version: 0.6.8
|
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-
|
12
|
+
date: 2015-12-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -18,6 +18,9 @@ dependencies:
|
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '3.2'
|
21
|
+
- - "<"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 5.1.x
|
21
24
|
type: :runtime
|
22
25
|
prerelease: false
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -25,6 +28,9 @@ dependencies:
|
|
25
28
|
- - ">="
|
26
29
|
- !ruby/object:Gem::Version
|
27
30
|
version: '3.2'
|
31
|
+
- - "<"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 5.1.x
|
28
34
|
- !ruby/object:Gem::Dependency
|
29
35
|
name: nokogiri
|
30
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,6 +110,7 @@ extra_rdoc_files: []
|
|
104
110
|
files:
|
105
111
|
- CHANGELOG.md
|
106
112
|
- LICENSE
|
113
|
+
- README-CN.md
|
107
114
|
- README.md
|
108
115
|
- Rakefile
|
109
116
|
- bin/wechat
|