wechat 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e8ac0d453ab3b09ebc953fc377d87e3e4faaa691
4
+ data.tar.gz: db19e37a02a7361aed18294bc10782f276af4bb3
5
+ SHA512:
6
+ metadata.gz: c7ed1bc6f07ded18760833ecfc8a19f5daba4ccb8ad0d6e1ed0ce484ef58ae08558c097c37aae5b014101400786d3b76c9a572ef2ce21c3cc56a8c9a3cf71224
7
+ data.tar.gz: 205d9c379adec3226e9fa51600ead4905a259133ff724eb49a56d7dec18b14304232a1e75aa92553e4c7048f83837295b2c1ce65c37b2789c40ca1f4fa5e21e3
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## v0.2.0 (released at 8/27/2015)
4
+
5
+ * Add wechat enterprise account support
6
+ * Make responder execute in action context, by @lazing #15
7
+ * jsapi_ticket support, by @feitian124 #27
8
+ * Rename gems to wechat and ambitious to being #1 gems about development wechat. thanks Xiaoning transfer this gem name.
9
+ * Original gem `wechat-rails` author skinnyworm trasfer to Eric-Guo as maintainer
10
+
11
+ ## v0.1.1
12
+
13
+ * Initial release from [wechat-rails](https://github.com/skinnyworm/wechat-rails).
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 skinnyworm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,405 @@
1
+ WeChat
2
+ ======
3
+
4
+ [![Build Status](https://travis-ci.org/Eric-Guo/wechat.svg)](https://travis-ci.org/Eric-Guo/wechat) [![Code Climate](https://codeclimate.com/github/Eric-Guo/wechat.png)](https://codeclimate.com/github/Eric-Guo/wechat) [![Code Coverage](https://codeclimate.com/github/Eric-Guo/wechat/coverage.png)](https://codeclimate.com/github/Eric-Guo/wechat) [![Gem Version](https://badge.fury.io/rb/wechat.svg)](https://badge.fury.io/for/rb/wechat)
5
+
6
+
7
+ WeChat gem 可以帮助开发者方便地在Rails环境中集成微信[公众平台](https://mp.weixin.qq.com/)和[企业平台](https://qy.weixin.qq.com)提供的服务,包括:
8
+
9
+ - 微信公众/企业平台[主动消息](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF)API(命令行和Web环境都可以使用)
10
+ - [回调消息](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服务器)
11
+ - [微信JS-SDK](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3) config接口注入权限验证
12
+ - OAuth 2.0认证机制
13
+
14
+ 命令行工具`wechat`可以调用各种无需web环境的API。同时也提供了Rails Controller的responder DSL, 可以帮助开发者方便地在Rails应用中集成微信的消息处理机制。如果你的App还需要集成微信OAuth2.0, 你可以考虑[omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2), 以便和devise集成,提供完整的用户认证。
15
+
16
+
17
+ ## 安装
18
+
19
+ Using `gem install`
20
+
21
+ ```
22
+ gem install "wechat"
23
+ ```
24
+
25
+ Or add to your app's `Gemfile`:
26
+
27
+ ```
28
+ gem 'wechat'
29
+ ```
30
+
31
+
32
+ ## 配置
33
+
34
+ #### 命令行程序的配置
35
+
36
+ 要使用命令行程序,需要在home目录中创建一个`~/.wechat.yml`,包含以下内容。其中`access_token`是存放access_token的文件位置。
37
+
38
+ ```
39
+ appid: "my_appid"
40
+ secret: "my_secret"
41
+ access_token: "/var/tmp/wechat_access_token"
42
+ ```
43
+
44
+ Windows或者使用企业号,需要存放在`C:/Users/[user_name]/`下,其中corpid和corpsecret可以从企业号管理界面的设置->权限管理,通过新建任意一个管理组后获取。
45
+
46
+ ```
47
+ corpid: "my_appid"
48
+ corpsecret: "my_secret"
49
+ agentid: "1" # 企业应用的id,整型。可在应用的设置页面查看
50
+ access_token: "C:/Users/[user_name]/wechat_access_token"
51
+ ```
52
+
53
+ #### Rails 全局配置
54
+ Rails应用程序中,需要将配置文件放在`config/wechat.yml`,可以为不同environment创建不同的配置。
55
+
56
+ 公众号配置示例:
57
+
58
+ ```
59
+ default: &default
60
+ appid: "app_id"
61
+ secret: "app_secret"
62
+ token: "app_token"
63
+ access_token: "/var/tmp/wechat_access_token"
64
+
65
+ production:
66
+ appid: <%= ENV['WECHAT_APPID'] %>
67
+ secret: <%= ENV['WECHAT_APP_SECRET'] %>
68
+ token: <%= ENV['WECHAT_TOKEN'] %>
69
+ access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
70
+
71
+ development:
72
+ <<: *default
73
+
74
+ test:
75
+ <<: *default
76
+ ```
77
+
78
+ 企业号配置下必须使用加密模式,其中token和encoding_aes_key可以从企业号管理界面的应用中心->某个应用->模式选择,选择回调模式后获得。
79
+
80
+ ```
81
+ default: &default
82
+ corpid: "corpid"
83
+ corpsecret: "corpsecret"
84
+ agentid: "1"
85
+ access_token: "C:/Users/[user_name]/wechat_access_token"
86
+ encrypt_mode: true
87
+ token: ""
88
+ encoding_aes_key: ""
89
+
90
+ production:
91
+ corpid: <%= ENV['WECHAT_CORPID'] %>
92
+ corpsecret: <%= ENV['WECHAT_CORPSECRET'] %>
93
+ agentid: <%= ENV['WECHAT_AGENTID'] %>
94
+ access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
95
+ encrypt_mode: <%= ENV['WECHAT_ENCRYPT_MODE'] %>
96
+ token: <%= ENV['WECHAT_TOKEN'] %>
97
+ encoding_aes_key: <%= ENV['WECHAT_ENCODING_AES_KEY'] %>
98
+
99
+ development:
100
+ <<: *default
101
+
102
+ test:
103
+ <<: *default
104
+ ```
105
+
106
+ 注意在Rails项目根目录下运行`wechat`命令行工具会优先使用`config/wechat.yml`中的`default`配置,如果失败则使用`~\.wechat.yml`中的配置,以便于在生产环境下管理多个微信账号应用。
107
+
108
+ #### 为每个Responder配置不同的appid和secret
109
+
110
+ 在个别情况下,单个Rails应用可能需要处理来自多个账号的消息,此时可以配置多个responder controller。
111
+
112
+ ```ruby
113
+ class WechatFirstController < ApplicationController
114
+ wechat_responder appid: "app1", secret: "secret1", token: "token1", access_token: Rails.root.join("tmp/access_token1")
115
+
116
+ on :text, with:"help", respond: "help content"
117
+ end
118
+ ```
119
+
120
+ #### jssdk 支持
121
+
122
+ jssdk 使用前需通过config接口注入权限验证配置, 所需参数可以通过 signature 方法获取:
123
+
124
+ ```ruby
125
+ WechatsController.wechat.jsapi_ticket.signature(request.original_url)
126
+ ```
127
+
128
+ ## 关于接口权限
129
+
130
+ wechat gems 内部不会检查权限。但因公众号类型不同,和微信服务器端通讯时,可能会被拒绝,详细权限控制可参考[官方文档](http://mp.weixin.qq.com/wiki/7/2d301d4b757dedc333b9a9854b457b47.html)。
131
+
132
+ ## 使用命令行
133
+
134
+ ```
135
+ $ wechat
136
+ Wechat commands:
137
+ wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
138
+ wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
139
+ wechat custom_news [OPENID, NEWS_YAML_FILE] # 发送图文客服消息
140
+ wechat custom_text [OPENID, TEXT_MESSAGE] # 发送文字客服消息
141
+ wechat custom_video [OPENID, VIDEO_PATH] # 发送视频客服消息
142
+ wechat custom_voice [OPENID, VOICE_PATH] # 发送语音客服消息
143
+ wechat media [MEDIA_ID, PATH] # 媒体下载
144
+ wechat media_create [MEDIA_ID, PATH] # 媒体上传
145
+ wechat menu # 当前菜单
146
+ wechat menu_create [MENU_YAML] # 创建菜单
147
+ wechat menu_delete # 删除菜单
148
+ wechat message_send [OPENID, TEXT_MESSAGE] # 发送文字消息(仅企业号)
149
+ wechat template_message [OPENID, TEMPLATE_YAML_FILE] # 模板消息接口
150
+ wechat user [OPEN_ID] # 查找关注者
151
+ wechat users # 关注者列表
152
+ ```
153
+
154
+ ### 使用场景
155
+ 以下是几种典型场景的使用方法
156
+
157
+ #####获取所有用户的OPENID
158
+
159
+ ```
160
+ $ wechat users
161
+
162
+ {"total"=>4, "count"=>4, "data"=>{"openid"=>["oCfEht9***********", "oCfEhtwqa***********", "oCfEht9oMCqGo***********", "oCfEht_81H5o2***********"]}, "next_openid"=>"oCfEht_81H5o2***********"}
163
+
164
+ ```
165
+
166
+ #####获取用户的信息
167
+
168
+ ```
169
+ $ wechat user "oCfEht9***********"
170
+
171
+ {"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}
172
+
173
+ ```
174
+
175
+ #####获取用户的信息
176
+
177
+ ```
178
+ $ wechat user "oCfEht9***********"
179
+
180
+ {"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}
181
+ ```
182
+
183
+ ##### 获取当前菜单
184
+ ```
185
+ $ wechat menu
186
+
187
+ {"menu"=>{"button"=>[{"type"=>"view", "name"=>"保护的", "url"=>"http://***/protected", "sub_button"=>[]}, {"type"=>"view", "name"=>"公开的", "url"=>"http://***", "sub_button"=>[]}]}}
188
+
189
+ ```
190
+
191
+ ##### 创建菜单
192
+ 创建菜单需要一个定义菜单内容的yaml文件,比如
193
+ menu.yaml
194
+
195
+ ```
196
+ button:
197
+ -
198
+ name: "我要"
199
+ sub_button:
200
+ -
201
+ type: "click"
202
+ name: "预订午餐"
203
+ key: "BOOK_LUNCH"
204
+ sub_button:
205
+ -
206
+ -
207
+ type: "click"
208
+ name: "预订晚餐"
209
+ key: "BOOK_DINNER"
210
+ sub_button:
211
+ -
212
+ -
213
+ type: "click"
214
+ name: "预订半夜餐"
215
+ key: "BOOK_NIGHT_SNACK"
216
+ sub_button:
217
+ -
218
+ -
219
+ name: "查询"
220
+ sub_button:
221
+ -
222
+ type: "click"
223
+ name: "进出记录"
224
+ key: "BADGE_IN_OUT"
225
+ sub_button:
226
+ -
227
+ -
228
+ type: "click"
229
+ name: "年假余额"
230
+ key: "ANNUAL_LEAVE"
231
+ sub_button:
232
+ -
233
+ -
234
+ type: "view"
235
+ name: "关于"
236
+ url: "http://blog.cloud-mes.com/"
237
+
238
+ ```
239
+
240
+ 然后执行命令行
241
+
242
+ ```
243
+ $ wechat menu_create menu.yaml
244
+
245
+ ```
246
+
247
+ ##### 发送客服图文消息
248
+ 需定义一个图文消息内容的yaml文件,比如
249
+ articles.yaml
250
+
251
+ ```
252
+ articles:
253
+ -
254
+ title: "习近平在布鲁日欧洲学院演讲"
255
+ description: "新华网比利时布鲁日4月1日电 国家主席习近平1日在比利时布鲁日欧洲学院发表重要演讲"
256
+ url: "http://news.sina.com.cn/c/2014-04-01/232629843387.shtml"
257
+ pic_url: "http://i3.sinaimg.cn/dy/c/2014-04-01/1396366518_bYays1.jpg"
258
+ ```
259
+
260
+ 然后执行命令行
261
+
262
+ ```
263
+ $ wechat custom_news oCfEht9oM*********** articles.yml
264
+
265
+ ```
266
+
267
+ ##### 发送模板消息
268
+ 需定义一个模板消息内容的yaml文件,比如
269
+ template.yml
270
+
271
+ ```
272
+ template:
273
+ template_id: "o64KQ62_xxxxxxxxxxxxxxx-Qz-MlNcRKteq8"
274
+ url: "http://weixin.qq.com/download"
275
+ topcolor: "#FF0000"
276
+ data:
277
+ first:
278
+ value: "你好,你已报名成功"
279
+ color: "#0A0A0A"
280
+ keynote1:
281
+ value: "XX活动"
282
+ color: "#CCCCCC"
283
+ keynote2:
284
+ value: "2014年9月16日"
285
+ color: "#CCCCCC"
286
+ keynote3:
287
+ value: "上海徐家汇xxx城"
288
+ color: "#CCCCCC"
289
+ remark:
290
+ value: "欢迎再次使用。"
291
+ color: "#173177"
292
+
293
+ ```
294
+
295
+ 然后执行命令行
296
+
297
+ ```
298
+ $ wechat template_message oCfEht9oM*********** template.yml
299
+
300
+ ```
301
+
302
+ ## Rails Responder Controller DSL
303
+
304
+ 为了在Rails app中响应用户的消息,开发者需要创建一个wechat responder controller. 首先在router中定义
305
+
306
+ ```ruby
307
+ resource :wechat, only:[:show, :create]
308
+
309
+ ```
310
+
311
+ 然后创建Controller class, 例如
312
+
313
+ ```ruby
314
+
315
+ class WechatsController < ApplicationController
316
+ wechat_responder
317
+
318
+ # 默认的文字信息responder
319
+ on :text do |request, content|
320
+ request.reply.text "echo: #{content}" #Just echo
321
+ end
322
+
323
+ # 当请求的文字信息内容为'help'时, 使用这个responder处理
324
+ on :text, with:"help" do |request, help|
325
+ request.reply.text "help content" #回复帮助信息
326
+ end
327
+
328
+ # 当请求的文字信息内容为'<n>条新闻'时, 使用这个responder处理, 并将n作为第二个参数
329
+ on :text, with: /^(\d+)条新闻$/ do |request, count|
330
+ articles_range = (0... [count.to_i, 10].min)
331
+ request.reply.news(articles_range) do |article, i| #回复"articles"
332
+ article.item title: "标题#{i}", description:"内容描述#{i}", pic_url: "http://www.baidu.com/img/bdlogo.gif", url:"http://www.baidu.com/"
333
+ end
334
+ end
335
+
336
+ # 当收到 EventKey 为 mykey 的事件时
337
+ on :event, with: "mykey" do |request, key|
338
+ request.reply.text "收到来自#{request[:FromUserName]} 的EventKey 为 #{key} 的事件"
339
+ end
340
+
341
+ # 处理图片信息
342
+ on :image do |request|
343
+ request.reply.image(request[:MediaId]) #直接将图片返回给用户
344
+ end
345
+
346
+ # 处理语音信息
347
+ on :voice do |request|
348
+ request.reply.voice(request[:MediaId]) #直接语音音返回给用户
349
+ end
350
+
351
+ # 处理视频信息
352
+ on :video do |request|
353
+ nickname = wechat.user(request[:FromUserName])["nickname"] #调用 api 获得发送者的nickname
354
+ request.reply.video(request[:MediaId], title: "回声", description: "#{nickname}发来的视频请求") #直接视频返回给用户
355
+ end
356
+
357
+ # 处理地理位置信息
358
+ on :location do |request|
359
+ request.reply.text("#{request[:Location_X]}, #{request[:Location_Y]}") #回复地理位置
360
+ end
361
+
362
+ # 当用户加关注
363
+ on :event, with: 'subscribe' do |request, key|
364
+ request.reply.text "#{request[:FromUserName]} #{key} now"
365
+ end
366
+
367
+ # 当用户取消关注订阅
368
+ on :event, with: 'unsubscribe' do |request, key|
369
+ request.reply.text "#{request[:FromUserName]}无法收到这条消息。"
370
+ end
371
+
372
+ # 当无任何responder处理用户信息时,使用这个responder处理
373
+ on :fallback, respond: "fallback message"
374
+ end
375
+
376
+ ```
377
+
378
+ 在controller中使用`wechat_responder`引入Responder DSL, 之后可以用
379
+
380
+ ```
381
+ on <message_type> do |message|
382
+ message.reply.text "some text"
383
+ end
384
+
385
+ ```
386
+ 来响应用户信息。
387
+
388
+ 目前支持的message_type有如下几种
389
+
390
+ - :text 响应文字消息,可以用`:with`参数来匹配文本内容 `on(:text, with:'help'){|message, content| ...}`
391
+ - :image 响应图片消息
392
+ - :voice 响应语音消息
393
+ - :video 响应视频消息
394
+ - :location 响应地理位置消息
395
+ - :link 响应链接消息
396
+ - :event 响应事件消息, 可以用`:with`参数来匹配事件类型
397
+ - :fallback 默认响应,当收到的消息无法被其他responder响应时,会使用这个responder.
398
+
399
+ ## Message DSL
400
+
401
+ Wechat 的核心是一个Message DSL,帮助开发者构建各种类型的消息,包括主动推送的和被动响应的。
402
+ ....
403
+
404
+
405
+
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Wechat'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+ require File.join('bundler', 'gem_tasks')
25
+ require File.join('rspec', 'core', 'rake_task')
26
+ RSpec::Core::RakeTask.new(:spec)
27
+
28
+
29
+ task :default => :spec