mp_weixin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +7 -0
  4. data/.travis.yml +20 -0
  5. data/CHANGELOG.md +17 -0
  6. data/Gemfile +5 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +308 -0
  9. data/Rakefile +31 -0
  10. data/lib/config/mp_weixin_error.yml +82 -0
  11. data/lib/mp_weixin.rb +59 -0
  12. data/lib/mp_weixin/access_token.rb +172 -0
  13. data/lib/mp_weixin/client.rb +199 -0
  14. data/lib/mp_weixin/config.rb +36 -0
  15. data/lib/mp_weixin/error.rb +27 -0
  16. data/lib/mp_weixin/interface/base.rb +43 -0
  17. data/lib/mp_weixin/interface/group.rb +92 -0
  18. data/lib/mp_weixin/interface/menu.rb +73 -0
  19. data/lib/mp_weixin/interface/message.rb +38 -0
  20. data/lib/mp_weixin/interface/promotion.rb +48 -0
  21. data/lib/mp_weixin/interface/user.rb +39 -0
  22. data/lib/mp_weixin/models/event.rb +123 -0
  23. data/lib/mp_weixin/models/message.rb +227 -0
  24. data/lib/mp_weixin/models/reply_message.rb +180 -0
  25. data/lib/mp_weixin/response.rb +93 -0
  26. data/lib/mp_weixin/response_rule.rb +46 -0
  27. data/lib/mp_weixin/server.rb +39 -0
  28. data/lib/mp_weixin/server_helper.rb +94 -0
  29. data/lib/mp_weixin/version.rb +3 -0
  30. data/lib/support/active_model.rb +3 -0
  31. data/lib/support/active_model/model.rb +99 -0
  32. data/mp_weixin.gemspec +44 -0
  33. data/spec/client_spec.rb +87 -0
  34. data/spec/mp_weixin/access_token_spec.rb +140 -0
  35. data/spec/mp_weixin/client_spec.rb +111 -0
  36. data/spec/mp_weixin/config_spec.rb +24 -0
  37. data/spec/mp_weixin/interface/base_spec.rb +16 -0
  38. data/spec/mp_weixin/interface/group_spec.rb +133 -0
  39. data/spec/mp_weixin/interface/menu_spec.rb +72 -0
  40. data/spec/mp_weixin/interface/message_spec.rb +36 -0
  41. data/spec/mp_weixin/interface/promotion_spec.rb +48 -0
  42. data/spec/mp_weixin/interface/user_spec.rb +76 -0
  43. data/spec/mp_weixin/models/event_spec.rb +94 -0
  44. data/spec/mp_weixin/models/message_spec.rb +300 -0
  45. data/spec/mp_weixin/models/reply_message_spec.rb +365 -0
  46. data/spec/mp_weixin/server_helper_spec.rb +165 -0
  47. data/spec/mp_weixin/server_spec.rb +56 -0
  48. data/spec/spec_helper.rb +51 -0
  49. data/spec/support/mp_weixin.rb +7 -0
  50. data/spec/support/rspec_mixin.rb +8 -0
  51. data/spec/support/weixin.yml +12 -0
  52. metadata +363 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjVkYTRiYThlOTJmOTE3NjUyMmI1MDgyYmI4NDQ0MzIyNTg0NGRhMQ==
5
+ data.tar.gz: !binary |-
6
+ ZGM0MDk0ZTY2NDI3N2Y4MzdlMGU0NTJjZDI4ZTQxYjc5NTFkMzY1Nw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YjVkOWVmZWRkZWRkNmEwOTlkNWE5MjEwNzE3NWY3MDlkZmQ2NjRhMmZlYzdl
10
+ ODA5ODcxZDAyN2IxNzNjYTE4NjE1NDAzMzQzZTAzN2M1ZDM0YjUxNWRkMGEx
11
+ ZTYxOTFmYzhhYzgwOGFiMzQ1Y2EyZmNmZTkzZTdhMDU5OThmZDk=
12
+ data.tar.gz: !binary |-
13
+ NTBmYWY3MTgxMDQ2ZGNkNDM4NTBkM2M0ZWI1MzVhYjY4ZjFjMDhlZjJjNDk2
14
+ NDk0ZTU3OTliODJhZGNmNTNiNGUzNmM5YWU2MjE2YzY2ZmIxYjMxODE5OGVm
15
+ MTQ2ZmU0M2I4OTVhYzgyNDNjNWJjNzJhMDgyZGNmYjc2MmEzNTY=
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ log/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+
20
+ # editor temp file
21
+ *~
22
+ .vim
data/.rspec ADDED
@@ -0,0 +1,7 @@
1
+ --color
2
+ --format progress
3
+ #--format nested
4
+ --debug
5
+ --drb
6
+ -f documentation
7
+ #-I controllers
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ language: ruby
2
+ # bundler_args: --without development
3
+
4
+ services:
5
+ # - mongodb
6
+ # - redis-server
7
+
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
11
+
12
+ rvm:
13
+ - 2.0.0
14
+
15
+ before_script:
16
+ # - mongo ruby_china_test --eval 'db.addUser("travis", "test");'
17
+ #- cp config/config.yml.default config/config.yml
18
+
19
+ # script: RAILS_ENV=test bundle exec rake spec spec:javascript
20
+ script: bundle exec rake spec
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Overview
2
+
3
+ ## 0.1.0
4
+
5
+ ### Major Changes
6
+
7
+ * support 微信公众平台 '基础支持'
8
+
9
+ * support 微信公众平台 '接收消息'
10
+
11
+ * support 微信公众平台 '发送消息'
12
+
13
+ * support 微信公众平台 '用户管理'
14
+
15
+ * support 微信公众平台 '自定义菜单'
16
+
17
+ * support 微信公众平台 '推广支持'
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ #source 'http://rubygems.org'
2
+ source 'http://ruby.taobao.org/'
3
+
4
+ # Specify your gem's dependencies in mp_weixin.gemspec
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 jhjguxin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,308 @@
1
+ # MpWeixin
2
+
3
+ A wrapper for weiXin MP platform
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/mp_weixin.png)][gem]
6
+ [![Build Status](https://travis-ci.org/jhjguxin/mp_weixin.png?branch=master)][travis]
7
+ [![Dependency Status](https://gemnasium.com/jhjguxin/mp_weixin.png?travis)][gemnasium]
8
+ [![Code Climate](https://codeclimate.com/github/jhjguxin/mp_weixin.png)][codeclimate]
9
+ [![Coverage Status](https://coveralls.io/repos/jhjguxin/mp_weixin/badge.png?branch=master)][coveralls]
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'mp_weixin'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install mp_weixin
24
+
25
+ ## Functions & Features
26
+
27
+ ### 基础支持
28
+
29
+ - 获取access token [DONE]
30
+ - 全局返回码说明 [TODO]
31
+ - [接口频率限制说明](http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%8F%A3%E9%A2%91%E7%8E%87%E9%99%90%E5%88%B6%E8%AF%B4%E6%98%8E) [NOPLAN]
32
+ - 上传下载多媒体文件 [TODO]
33
+
34
+ ### 接收消息
35
+ - 验证消息真实性 [DONE] [prefer](spec/mp_weixin/server_spec.rb)
36
+ - 接收普通消息 [DONE] [prefer](spec/mp_weixin/server_helper_spec.rb)
37
+ - 接收事件推送 [DONE] [prefer](spec/mp_weixin/server_helper_spec.rb)
38
+ - 接收语音识别结果 [DONE] [prefer](spec/mp_weixin/server_helper_spec.rb)
39
+
40
+ ### 发送消息
41
+
42
+ - 发送被动响应消息 [DONE] [prefer](spec/mp_weixin/server_helper_spec.rb)
43
+ - 发送客服消息 [DONE] [prefer](spec/mp_weixin/interface/message_spec.rb)
44
+
45
+ ### 用户管理
46
+
47
+ - 分组管理接口 [DONE] [prefer](spec/mp_weixin/interface/user_spec.rb)
48
+ - 获取用户基本信息 [DONE] [prefer](spec/mp_weixin/interface/user_spec.rb)
49
+ - 获取关注者列表 [DONE] [prefer](spec/mp_weixin/interface/user_spec.rb)
50
+ - 获取用户地理位置 [DONE] [prefer](spec/mp_weixin/models/event.rb)
51
+ - 网页授权获取用户基本信息 [DONE] [prefer](https://github.com/jhjguxin/open_weixin/)
52
+ - 网页获取用户网络状态(JS接口)[NOPLAN]
53
+
54
+ ### 自定义菜单
55
+
56
+ - 自定义菜单创建接口 [DONE] [prefer](spec/mp_weixin/interface/menu_spec.rb)
57
+ - 自定义菜单查询接口 [DONE] [prefer](spec/mp_weixin/interface/menu_spec.rb)
58
+ - 自定义菜单删除接口 [DONE] [prefer](spec/mp_weixin/interface/menu_spec.rb)
59
+ - 自定义菜单事件推送 [DONE] [prefer](spec/mp_weixin/interface/menu_spec.rb)
60
+
61
+ ### 推广支持
62
+
63
+ - 生成带参数的二维码 [DONE] [prefer](spec/mp_weixin/interface/promotion_spec.rb)
64
+
65
+
66
+ ## The Structure
67
+
68
+ ```shell
69
+ $ tree lib/ -L 3
70
+ lib/
71
+ ├── config
72
+ │   └── mp_weixin_error.yml
73
+ ├── mp_weixin
74
+ │   ├── access_token.rb
75
+ │   ├── client.rb
76
+ │   ├── config.rb
77
+ │   ├── error.rb
78
+ │   ├── interface
79
+ │   │   ├── base.rb
80
+ │   │   ├── group.rb
81
+ │   │   ├── menu.rb
82
+ │   │   ├── message.rb
83
+ │   │   ├── promotion.rb
84
+ │   │   └── user.rb
85
+ │   ├── models
86
+ │   │   ├── event.rb
87
+ │   │   ├── message.rb
88
+ │   │   └── reply_message.rb
89
+ │   ├── response.rb
90
+ │   ├── response_rule.rb
91
+ │   ├── server_helper.rb
92
+ │   ├── server.rb
93
+ │   └── version.rb
94
+ └── mp_weixin.rb
95
+ ```
96
+
97
+ ### client:
98
+
99
+ which initiate with an `app_id` and `app_secret` who have ability to request interface provider by 'https://api.weixin.qq.com'. Of course at first you must been authorize. Those include `发送客服消息`, `用户管理`, `自定义菜单`.
100
+
101
+ ### server
102
+
103
+ It's a web service, used to receive all the http request from 'weixin server', and give and rigth response. those response include `接收消息`, `发送被动响应消息`.
104
+
105
+ want it should been like?
106
+
107
+ 1. first it should been a web service, it should have ability to '验证消息真实性', '接收普通消息
108
+ ', '发送被动响应消息', '接收事件推送', '接收语音识别结果'.
109
+ 2. then I want it should can match some custom rules(like rails route)
110
+ 3. all of those rule's return will response to 'https://api.weixin.qq.com'
111
+
112
+ ### models
113
+
114
+ some base data model used by [mp_weixin] eg `message`, `event`. Those model should have some usefully method been defined.
115
+
116
+ ## Usage
117
+
118
+ - [server](lib/mp_weixin/server.rb)
119
+ - [client](lib/mp_weixin/client.rb)
120
+
121
+ ### how boot mp_weixin
122
+
123
+ config `mp_weixin.yml` (eg `spec/support/weixin.yml`)
124
+
125
+ ```yml
126
+ defaults: &defaults
127
+ app_id: "12341432134"
128
+ app_secret: "app_secret"
129
+ url: "http://www.example.com"
130
+ token: "secret"
131
+
132
+ development:
133
+ <<: *defaults
134
+ production:
135
+ <<: *defaults
136
+ test:
137
+ <<: *defaults
138
+ ```
139
+
140
+ initializer the MpWeixin (eg `spec/support/mp_weixin.rb`)
141
+
142
+ ### launch `MpWeixin::Server`
143
+
144
+ create file `config.ru`
145
+
146
+ ```ruby
147
+ require 'rubygems'
148
+ require 'bundler'
149
+
150
+ Bundler.require
151
+
152
+ # require 'mp_weixin'
153
+
154
+ run MpWeixin::Server
155
+ # rackup config.ru
156
+ ```
157
+
158
+ ### request weixin api
159
+
160
+ through `MpWeixin::Client` module request some api interface at 'weixin'
161
+
162
+ ```ruby
163
+ # initalize and get new access_token
164
+ client = MpWeixin::Client.new
165
+ client.get_token
166
+ # initalize with exists access_token
167
+ token_hash = {"expires_in" => "7200", "access_token" => access_token}
168
+ client = MpWeixin::Client.from_hash(token_hash)
169
+
170
+ client.menu.get_menus
171
+ # more detail check 'spec/mp_weixin/interface/'
172
+ ```
173
+
174
+ #### demo how guanxi use MpWeixin::Client
175
+
176
+ ```ruby
177
+ # encoding: utf-8
178
+ class WechatClient
179
+ include Mongoid::Document
180
+ include Mongoid::Timestamps
181
+
182
+ field :access_token, type: String
183
+ field :expires_at, type: Integer
184
+
185
+ class << self
186
+ attr_accessor :wechat_client
187
+
188
+ def get_access_token
189
+ wechat_client = self.first || self.new
190
+
191
+ {
192
+ access_token: wechat_client.access_token,
193
+ expires_at: wechat_client.expires_at.try(:to_i)
194
+ }.keep_if{|_, v| v.present?}
195
+ end
196
+ def set_access_token(access_token, expires_at)
197
+ wechat_client = self.first || self.new
198
+
199
+ wechat_client.update_attributes(
200
+ access_token: access_token,
201
+ expires_at: expires_at
202
+ )
203
+ end
204
+
205
+ # return an instance of MpWeixin#client with 'is_authorized?' is true
206
+ #
207
+ # client = WechatClient.this_client
208
+ # client.message.custom_send(touser: "ozIMPt8wb8I8iYm-IjD1DHP-IQ9s", msgtype: "text", text: {content: "hey, we found you!"})
209
+ def this_client
210
+ unless @wechat_client.try("is_authorized?")
211
+ @wechat_client = fresh_client
212
+ end
213
+
214
+ @wechat_client
215
+ end
216
+
217
+ def fresh_client
218
+ token_hash = WechatClient.get_access_token
219
+ expires_at_time = Time.at(token_hash[:expires_at]) rescue 2.hours.from_now
220
+
221
+ if token_hash[:access_token].present? and (expires_at_time > Time.now)
222
+ client = MpWeixin::Client.from_hash(token_hash)
223
+ else
224
+ client = MpWeixin::Client.new
225
+ client.get_token
226
+
227
+ WechatClient.set_access_token(client.token.token, client.token.expires_at)
228
+ end
229
+
230
+ client
231
+ end
232
+ end
233
+ end
234
+ ```
235
+
236
+ ### start weixin mp server
237
+
238
+ mount with Rails application
239
+
240
+ ```ruby
241
+ # /app/services/wechat/response_rule.rb
242
+ # encoding: utf-8
243
+ module ResponseRule
244
+ # handle corrent data post from weixin
245
+ #
246
+ # please @rewrite me
247
+ def handle_message(request, message)
248
+ #
249
+ logger.info "Hey, one request from '#{request.url}' been detected, and content is #{message.as_json}"
250
+ # did you want save this message ???
251
+ end
252
+
253
+ def response_text_message(request, message)
254
+ case message.Content
255
+ when /greet/
256
+ reply_message = message.reply_text_message({Content: "whosyourdaddy!"})
257
+ logger.info "response with #{reply_message.to_xml}"
258
+ else
259
+ reply_message = message.reply_text_message({Content: "hey, guy you are welcome!"})
260
+ logger.info "response with #{reply_message.to_xml}"
261
+ end
262
+ end
263
+
264
+ # 发送被动响应消息'
265
+ #
266
+ # please @rewrite me
267
+ #
268
+ #
269
+ # can rely with instance of those class eg, TextReplyMessage, ImageReplyMessage, VoiceReplyMessage
270
+ # VideoReplyMessage, MusicReplyMessage, NewsReplyMessage
271
+ # quickly generate reply content through call 'reply_#{msg_type}_message(attributes).to_xml' @see 'spec/mp_weixin/server_helper_spec.rb'
272
+ #
273
+ def response_message(request, message, &block)
274
+ if block_given?
275
+ block.call(request, message)
276
+ end
277
+ send("response_#{message.MsgType}_message", request, message)
278
+
279
+ # reply with
280
+ # reply_#{msg_type}_message(attributes).to_xml
281
+ end
282
+ end
283
+
284
+ # load custom response_rule
285
+ class MpWeixin::Server
286
+ helpers ResponseRule
287
+ end
288
+ ```
289
+
290
+ ```ruby
291
+ # routes.rb
292
+ Gxservice::Application.routes.draw do
293
+ require "mp_weixin"
294
+ require "#{Rails.root}/app/services/wechat/response_rule"
295
+ mount MpWeixin::Server, :at => '/weixin'
296
+ end
297
+ ```
298
+
299
+ more detail pls check `spec/`
300
+
301
+ ## Contributing
302
+
303
+ 1. Fork it
304
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
305
+ 3. test changes (`rake spec`)
306
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
307
+ 5. Push to the branch (`git push origin my-new-feature`)
308
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,31 @@
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
+
8
+ require "bundler/gem_tasks"
9
+
10
+ begin
11
+ require 'rdoc/task'
12
+ rescue LoadError
13
+ require 'rdoc/rdoc'
14
+ require 'rake/rdoctask'
15
+ RDoc::Task = Rake::RDocTask
16
+ end
17
+
18
+ RDoc::Task.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'MpWeixin'
21
+ rdoc.options << '--line-numbers'
22
+ rdoc.rdoc_files.include('README.md')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
25
+
26
+ require 'rspec/core/rake_task'
27
+ # rspec --init # Initialize your project with RSpec
28
+ RSpec::Core::RakeTask.new(:spec)
29
+
30
+ # make spec as the default task
31
+ task :default => :spec