wechat 0.6.7 → 0.6.8
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-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
|