wechat 0.7.7 → 0.7.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1227afa2c59a9657b91eda79d71e05c582dfd13c
4
- data.tar.gz: 1ed732c34360e25275a35fa5ddbc62f9fe945e4c
3
+ metadata.gz: 53ffffaf24d8d96f29551c79c5de7ffa1829a918
4
+ data.tar.gz: d98f374eda600f2c61a1ce1dfcbe408663538b08
5
5
  SHA512:
6
- metadata.gz: 358ebfbe074a0a0f631c2966b4cb8f0d3357d37fa8c6a0e67c87863d1b98030bcd625de8cdbbd36cac923e1378ed395599e8199659c8ccf41065085e7d05e57d
7
- data.tar.gz: 95d512581d896582438b0a3c30d9994defaa485118ecf7c5fe90a0ad473ff4508f070aea1153abbabc2ce36bf3c8ad3a521319b9cf92b4cb98c2ed48999f9bc4
6
+ metadata.gz: f3a9d2da9c57c99b535c5f1f29a36f796100f23997386cfe922415df30aeb5d32e0e0634e99f6e5cf58e3ba2d1bf8b0e6a47bccdc4d3e1eb2db84d48fff9af61
7
+ data.tar.gz: f8913573a84ca830f21b14be23d446b336ef4daa010ec2501185dd3da102cc98244ef3e6ddc74d72b7b9781ce452e7065f8614b86690c62148742a564c771563
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.7.8 (released at 3/31/2016)
4
+
5
+ * New wechat_api, similar to wechat_responder, but without messange handle DSL, support web page only wechat application
6
+ * New media_uploadimg API.
7
+ * New file type of Message.
8
+ * Improved multi account support per different controller.
9
+
3
10
  ## v0.7.7 (released at 3/18/2016)
4
11
 
5
12
  * New wechat_oauth2, only support enterprise account still.
@@ -17,6 +17,8 @@ WeChat gem 可以帮助开发者方便地在Rails环境中集成微信[公众平
17
17
 
18
18
  如果您对如何制作微信网页UI没有灵感,可以参考官方的[weui](https://github.com/weui/weui),针对Rails的Gem是[weui-rails](https://github.com/Eric-Guo/weui-rails)。
19
19
 
20
+ 主页型应用请使用[`wechat_api`](#wechat_api---rails-controller-wechat-api),传统消息型应用请使用[`wechat_responder`](#wechat_responder---rails-responder-controller-dsl)。
21
+
20
22
  如果您想从一个稍微完整一些的示例开始微信开发,可以参考[wechat-starter](https://github.com/goofansu/wechat-starter),这个示例甚至包括了微信支付的内容。
21
23
 
22
24
  ## 安装
@@ -164,7 +166,7 @@ Wechat服务器有报道曾出现[RestClient::SSLCertificateNotVerified](http://
164
166
 
165
167
  #### 为每个Responder配置不同的appid和secret
166
168
 
167
- 在个别情况下,单个Rails应用可能需要处理来自多个账号的消息,此时可以配置多个responder controller。
169
+ 有些情况下,单个Rails应用可能需要处理来自多个微信公众号的消息,您可以通过在`wechat_responder`和`wechat_api`后配置多个相关参数来支持多账号。
168
170
 
169
171
  ```ruby
170
172
  class WechatFirstController < ActionController::Base
@@ -244,6 +246,7 @@ Wechat commands:
244
246
  wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
245
247
  wechat media [MEDIA_ID, PATH] # 媒体下载
246
248
  wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
249
+ wechat media_uploadimg [IMAGE_PATH] # 上传图文消息内的图片
247
250
  wechat menu # 当前菜单
248
251
  wechat menu_addconditional [CONDITIONAL_MENU_YAML_PATH] # 创建个性化菜单
249
252
  wechat menu_create [MENU_YAML_PATH] # 创建菜单
@@ -292,6 +295,7 @@ Wechat commands:
292
295
  wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
293
296
  wechat media [MEDIA_ID, PATH] # 媒体下载
294
297
  wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
298
+ wechat media_uploadimg [IMAGE_PATH] # 上传图文消息内的图片
295
299
  wechat menu # 当前菜单
296
300
  wechat menu_create [MENU_YAML_PATH] # 创建菜单
297
301
  wechat menu_delete # 删除菜单
@@ -447,7 +451,22 @@ template:
447
451
  $ wechat template_message oCfEht9oM*********** template.yml
448
452
  ```
449
453
 
450
- ## Rails Responder Controller DSL
454
+ ## wechat_api - Rails Controller Wechat API
455
+
456
+ 虽然用户可以随时通过`Wechat.api`在任意代码中访问wechat的API功能,但是更推荐的做法是仅在controller中,通过引入`wechat_api`,使用`wechat`调用API功能,不仅因为这样是支持多个微信公众号的必然要求,而且也避免了在模型层内过多引入微信相关代码。
457
+
458
+ ```ruby
459
+ class WechatReportsController < ApplicationController
460
+ wechat_api
461
+ layout 'wechat'
462
+
463
+ def index
464
+ @lots = Lot.with_preloading.wip_lot
465
+ end
466
+ end
467
+ ```
468
+
469
+ ## wechat_responder - Rails Responder Controller DSL
451
470
 
452
471
  为了在Rails app中响应用户的消息,开发者需要创建一个wechat responder controller. 首先在router中定义
453
472
 
data/README.md CHANGED
@@ -24,6 +24,8 @@ Wechat provide OAuth2.0 authentication method `wechat_oauth2`, possible the easi
24
24
 
25
25
  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.
26
26
 
27
+ For web page only wechat application, using [`wechat_api`](#wechat_api---rails-controller-wechat-api), which only contain web feature compare with traditional message type [`wechat_responder`](#wechat_responder---rails-responder-controller-dsl).
28
+
27
29
  There is a more complete [wechat-starter](https://github.com/goofansu/wechat-starter) demo available, even include the payment SDK feature.
28
30
 
29
31
  ## Installation
@@ -177,7 +179,7 @@ SSL Certification can also be corrupted by some reason in China, [it's reported]
177
179
 
178
180
  #### Configure individual responder with different appid
179
181
 
180
- 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`
182
+ Sometime, you may want to hosting more than one enterprise/public wechat account in one Rails application, so you can given those configuration info when calling `wechat_responder` or `wechat_api`
181
183
 
182
184
  ```ruby
183
185
  class WechatFirstController < ActionController::Base
@@ -260,6 +262,7 @@ Wechat commands:
260
262
  wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
261
263
  wechat media [MEDIA_ID, PATH] # 媒体下载
262
264
  wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
265
+ wechat media_uploadimg [IMAGE_PATH] # 上传图文消息内的图片
263
266
  wechat menu # 当前菜单
264
267
  wechat menu_addconditional [CONDITIONAL_MENU_YAML_PATH] # 创建个性化菜单
265
268
  wechat menu_create [MENU_YAML_PATH] # 创建菜单
@@ -308,6 +311,7 @@ Wechat commands:
308
311
  wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
309
312
  wechat media [MEDIA_ID, PATH] # 媒体下载
310
313
  wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
314
+ wechat media_uploadimg [IMAGE_PATH] # 上传图文消息内的图片
311
315
  wechat menu # 当前菜单
312
316
  wechat menu_create [MENU_YAML_PATH] # 创建菜单
313
317
  wechat menu_delete # 删除菜单
@@ -462,7 +466,22 @@ After that, can running command:
462
466
  $ wechat template_message oCfEht9oM*********** template.yml
463
467
  ```
464
468
 
465
- ## Rails Responder Controller DSL
469
+ ## wechat_api - Rails Controller Wechat API
470
+
471
+ Although user can always access all wechat feature via Wechat.api, but it's much more recommand to using `wechat` directly in controller. It's not only mandatory require if you plan to support multi-account, also help to seperate the wechat specific logic from the model layer.
472
+
473
+ ```ruby
474
+ class WechatReportsController < ApplicationController
475
+ wechat_api
476
+ layout 'wechat'
477
+
478
+ def index
479
+ @lots = Lot.with_preloading.wip_lot
480
+ end
481
+ end
482
+ ```
483
+
484
+ ## wechat_responder - Rails Responder Controller DSL
466
485
 
467
486
  In order to responding the message user sent, Rails developer need create a wechat responder controller and defined the routing in `routes.rb`
468
487
 
data/bin/wechat CHANGED
@@ -343,6 +343,11 @@ class App < Thor
343
343
  puts wechat_api.media_create(type, path)
344
344
  end
345
345
 
346
+ desc 'media_uploadimg [IMAGE_PATH]', '上传图文消息内的图片'
347
+ def media_uploadimg(image_path)
348
+ puts wechat_api.media_uploadimg(image_path)
349
+ end
350
+
346
351
  desc 'material [MEDIA_ID, PATH]', '永久媒体下载'
347
352
  def material(media_id, path)
348
353
  tmp_file = wechat_api.material(media_id)
@@ -1,22 +1,36 @@
1
1
  module ActionController
2
2
  module WechatResponder
3
+ def wechat_api(opts = {})
4
+ include Wechat::ControllerApi
5
+ self.wechat = load_controller_wechat(opts)
6
+ end
7
+
3
8
  def wechat_responder(opts = {})
4
9
  include Wechat::Responder
10
+ self.wechat = load_controller_wechat(opts)
11
+ end
5
12
 
13
+ private_class_method
14
+
15
+ def load_controller_wechat(opts = {})
16
+ self.token = opts[:token] || Wechat.config.token
17
+ self.appid = opts[:appid] || Wechat.config.appid
6
18
  self.corpid = opts[:corpid] || Wechat.config.corpid
7
19
  self.agentid = opts[:agentid] || Wechat.config.agentid
8
20
  self.encrypt_mode = opts[:encrypt_mode] || Wechat.config.encrypt_mode || corpid.present?
9
21
  self.timeout = opts[:timeout] || 20
10
22
  self.skip_verify_ssl = opts[:skip_verify_ssl]
11
- self.token = opts[:token] || Wechat.config.token
12
23
  self.encoding_aes_key = opts[:encoding_aes_key] || Wechat.config.encoding_aes_key
13
24
  self.trusted_domain_fullname = opts[:trusted_domain_fullname] || Wechat.config.trusted_domain_fullname
14
25
 
15
26
  return self.wechat = Wechat.api if opts.empty?
16
- return self.wechat = Wechat::CorpApi.new(corpid, opts[:corpsecret], opts[:access_token], \
17
- agentid, timeout, skip_verify_ssl, opts[:jsapi_ticket]) if corpid.present?
18
- self.wechat = Wechat::Api.new(opts[:appid], opts[:secret], opts[:access_token], \
19
- timeout, skip_verify_ssl, opts[:jsapi_ticket])
27
+ if corpid.present?
28
+ Wechat::CorpApi.new(corpid, opts[:corpsecret], opts[:access_token], \
29
+ agentid, timeout, skip_verify_ssl, opts[:jsapi_ticket])
30
+ else
31
+ Wechat::Api.new(appid, opts[:secret], opts[:access_token], \
32
+ timeout, skip_verify_ssl, opts[:jsapi_ticket])
33
+ end
20
34
  end
21
35
  end
22
36
 
@@ -8,6 +8,7 @@ module Wechat
8
8
  autoload :Message, 'wechat/message'
9
9
  autoload :Responder, 'wechat/responder'
10
10
  autoload :Cipher, 'wechat/cipher'
11
+ autoload :ControllerApi, 'wechat/controller_api'
11
12
 
12
13
  class AccessTokenExpiredError < StandardError; end
13
14
  class ResponseError < StandardError
@@ -20,6 +20,10 @@ module Wechat
20
20
  post_file 'media/upload', file, params: { type: type }
21
21
  end
22
22
 
23
+ def media_uploadimg(file)
24
+ post_file 'media/uploadimg', file
25
+ end
26
+
23
27
  protected
24
28
 
25
29
  def get(path, headers = {})
@@ -27,7 +27,7 @@ HELP
27
27
  @config
28
28
  end
29
29
 
30
- private
30
+ private_class_method
31
31
 
32
32
  def self.loading_config!
33
33
  config ||= config_from_file || config_from_environment
@@ -41,10 +41,10 @@ module Wechat
41
41
  def request(path, header = {}, &_block)
42
42
  url = "#{header.delete(:base) || base}#{path}"
43
43
  as = header.delete(:as)
44
- header.merge!('Accept' => 'application/json')
44
+ header['Accept'] = 'application/json'
45
45
  response = yield(url, header)
46
46
 
47
- fail "Request not OK, response status #{response.status}" if response.status != 200
47
+ raise "Request not OK, response status #{response.status}" if response.status != 200
48
48
  parse_response(response, as || :json) do |parse_as, data|
49
49
  break data unless parse_as == :json && data['errcode'].present?
50
50
 
@@ -56,9 +56,9 @@ module Wechat
56
56
  # 40001, invalid credential, access_token is invalid or not latest hint
57
57
  # 48001, api unauthorized hint, for qrcode creation # 71
58
58
  when 42001, 40014, 40001, 48001
59
- fail AccessTokenExpiredError
59
+ raise AccessTokenExpiredError
60
60
  else
61
- fail ResponseError.new(data['errcode'], data['errmsg'])
61
+ raise ResponseError.new(data['errcode'], data['errmsg'])
62
62
  end
63
63
  end
64
64
  end
@@ -0,0 +1,40 @@
1
+ module Wechat
2
+ module ControllerApi
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ attr_accessor :wechat, :token, :appid, :corpid, :agentid, :encrypt_mode, :timeout, :skip_verify_ssl, :encoding_aes_key, :trusted_domain_fullname
7
+ end
8
+
9
+ def wechat
10
+ self.class.wechat # Make sure user can continue access wechat at instance level similar to class level
11
+ end
12
+
13
+ def wechat_oauth2(scope = 'snsapi_base', page_url = nil)
14
+ appid = self.class.corpid || self.class.appid
15
+ page_url ||= if self.class.trusted_domain_fullname
16
+ "#{self.class.trusted_domain_fullname}#{request.original_fullpath}"
17
+ else
18
+ request.original_url
19
+ end
20
+ redirect_uri = CGI.escape(page_url)
21
+ oauth2_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{appid}&redirect_uri=#{redirect_uri}&response_type=code&scope=#{scope}#wechat_redirect"
22
+
23
+ return oauth2_url unless block_given?
24
+ raise 'Currently wechat_oauth2 only support enterprise account.' unless self.class.corpid
25
+ if cookies.signed_or_encrypted[:we_deviceid].blank? && params[:code].blank?
26
+ redirect_to oauth2_url
27
+ elsif cookies.signed_or_encrypted[:we_deviceid].blank? && params[:code].present?
28
+ userinfo = wechat.getuserinfo(params[:code])
29
+ cookies.signed_or_encrypted[:we_userid] = { value: userinfo['UserId'], expires: 1.hour.from_now }
30
+ cookies.signed_or_encrypted[:we_deviceid] = { value: userinfo['DeviceId'], expires: 1.hour.from_now }
31
+ cookies.signed_or_encrypted[:we_openid] = { value: userinfo['OpenId'], expires: 1.hour.from_now }
32
+ yield userinfo['UserId'], userinfo
33
+ else
34
+ yield cookies.signed_or_encrypted[:we_userid], { 'UserId' => cookies.signed_or_encrypted[:we_userid],
35
+ 'DeviceId' => cookies.signed_or_encrypted[:we_deviceid],
36
+ 'OpenId' => cookies.signed_or_encrypted[:we_openid] }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,16 +1,16 @@
1
1
  module Wechat
2
2
  module Helpers
3
3
  def wechat_config_js(config_options = {})
4
- page_url = if Wechat.config.trusted_domain_fullname
5
- "#{Wechat.config.trusted_domain_fullname}#{controller.request.original_fullpath}"
4
+ page_url = if controller.class.trusted_domain_fullname
5
+ "#{controller.class.trusted_domain_fullname}#{controller.request.original_fullpath}"
6
6
  else
7
7
  controller.request.original_url
8
8
  end
9
- js_hash = Wechat.api.jsapi_ticket.signature(page_url)
9
+ js_hash = controller.wechat.jsapi_ticket.signature(page_url)
10
10
  config_js = <<-WECHAT_CONFIG_JS
11
11
  wx.config({
12
12
  debug: #{config_options[:debug]},
13
- appId: "#{Wechat.config.corpid || Wechat.config.appid}",
13
+ appId: "#{controller.class.corpid || controller.class.appid}",
14
14
  timestamp: "#{js_hash[:timestamp]}",
15
15
  nonceStr: "#{js_hash[:noncestr]}",
16
16
  signature: "#{js_hash[:signature]}",
@@ -65,7 +65,7 @@ module Wechat
65
65
  results[value[0].to_s.underscore.to_sym] = value[1]
66
66
  end
67
67
  else
68
- fail "Don't know how to parse message as #{type}"
68
+ raise "Don't know how to parse message as #{type}"
69
69
  end
70
70
  end
71
71
 
@@ -106,6 +106,10 @@ module Wechat
106
106
  update(MsgType: 'video', Video: video_fields)
107
107
  end
108
108
 
109
+ def file(media_id)
110
+ update(MsgType: 'file', File: { MediaId: media_id })
111
+ end
112
+
109
113
  def music(thumb_media_id, music_url, opts = {})
110
114
  music_fields = camelize_hash_keys(opts.slice(:title, :description, :HQ_music_url).merge(music_url: music_url, thumb_media_id: thumb_media_id))
111
115
  update(MsgType: 'music', Music: music_fields)
@@ -145,7 +149,7 @@ module Wechat
145
149
  'TemplateId' => 'template_id'
146
150
  }.freeze
147
151
 
148
- TO_JSON_ALLOWED = %w(touser msgtype content image voice video music news articles template agentid).freeze
152
+ TO_JSON_ALLOWED = %w(touser msgtype content image voice video file music news articles template agentid).freeze
149
153
 
150
154
  def to_json
151
155
  json_hash = deep_recursive(message_hash) do |key, value|
@@ -4,6 +4,7 @@ require 'wechat/signature'
4
4
  module Wechat
5
5
  module Responder
6
6
  extend ActiveSupport::Concern
7
+ include Wechat::ControllerApi
7
8
  include Cipher
8
9
 
9
10
  included do
@@ -26,25 +27,23 @@ module Wechat
26
27
  end
27
28
 
28
29
  module ClassMethods
29
- attr_accessor :wechat, :token, :corpid, :agentid, :encrypt_mode, :timeout, :skip_verify_ssl, :encoding_aes_key, :trusted_domain_fullname
30
-
31
30
  def on(message_type, with: nil, respond: nil, &block)
32
- fail 'Unknow message type' unless [:text, :image, :voice, :video, :shortvideo, :link, :event, :click, :view, :scan, :batch_job, :location, :fallback].include?(message_type)
31
+ raise 'Unknow message type' unless [:text, :image, :voice, :video, :shortvideo, :link, :event, :click, :view, :scan, :batch_job, :location, :fallback].include?(message_type)
33
32
  config = respond.nil? ? {} : { respond: respond }
34
- config.merge!(proc: block) if block_given?
33
+ config[:proc] = block if block_given?
35
34
 
36
35
  if with.present?
37
- fail 'Only text, event, click, view, scan and batch_job can having :with parameters' unless [:text, :event, :click, :view, :scan, :batch_job].include?(message_type)
38
- config.merge!(with: with)
36
+ raise 'Only text, event, click, view, scan and batch_job can having :with parameters' unless [:text, :event, :click, :view, :scan, :batch_job].include?(message_type)
37
+ config[:with] = with
39
38
  if message_type == :scan
40
39
  if with.is_a?(String)
41
40
  self.known_scan_key_lists = with
42
41
  else
43
- fail 'on :scan only support string in parameter with, detail see https://github.com/Eric-Guo/wechat/issues/84'
42
+ raise 'on :scan only support string in parameter with, detail see https://github.com/Eric-Guo/wechat/issues/84'
44
43
  end
45
44
  end
46
45
  else
47
- fail 'Message type click, view, scan and batch_job must specify :with parameters' if [:click, :view, :scan, :batch_job].include?(message_type)
46
+ raise 'Message type click, view, scan and batch_job must specify :with parameters' if [:click, :view, :scan, :batch_job].include?(message_type)
48
47
  end
49
48
 
50
49
  case message_type
@@ -162,37 +161,6 @@ module Wechat
162
161
  end
163
162
  end
164
163
 
165
- def wechat
166
- self.class.wechat # Make sure user can continue access wechat at instance level similar to class level
167
- end
168
-
169
- def wechat_oauth2(scope = 'snsapi_base', page_url = nil)
170
- appid = self.class.corpid || self.class.appid
171
- page_url ||= if self.class.trusted_domain_fullname
172
- "#{self.class.trusted_domain_fullname}#{request.original_fullpath}"
173
- else
174
- request.original_url
175
- end
176
- redirect_uri = CGI.escape(page_url)
177
- oauth2_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{appid}&redirect_uri=#{redirect_uri}&response_type=code&scope=#{scope}#wechat_redirect"
178
-
179
- return oauth2_url unless block_given?
180
- raise 'Currently wechat_oauth2 only support enterprise account.' unless self.class.corpid
181
- if cookies.signed_or_encrypted[:we_deviceid].blank? && params[:code].blank?
182
- redirect_to oauth2_url
183
- elsif cookies.signed_or_encrypted[:we_deviceid].blank? && params[:code].present?
184
- userinfo = Wechat.api.getuserinfo(params[:code])
185
- cookies.signed_or_encrypted[:we_userid] = { value: userinfo['UserId'], expires: 1.hour.from_now }
186
- cookies.signed_or_encrypted[:we_deviceid] = { value: userinfo['DeviceId'], expires: 1.hour.from_now }
187
- cookies.signed_or_encrypted[:we_openid] = { value: userinfo['OpenId'], expires: 1.hour.from_now }
188
- yield userinfo['UserId'], userinfo
189
- else
190
- yield cookies.signed_or_encrypted[:we_userid], { 'UserId' => cookies.signed_or_encrypted[:we_userid],
191
- 'DeviceId' => cookies.signed_or_encrypted[:we_deviceid],
192
- 'OpenId' => cookies.signed_or_encrypted[:we_openid] }
193
- end
194
- end
195
-
196
164
  def show
197
165
  if self.class.corpid.present?
198
166
  echostr, _corp_id = unpack(decrypt(Base64.decode64(params[:echostr]), self.class.encoding_aes_key))
@@ -242,9 +210,9 @@ module Wechat
242
210
  msg_encrypt = nil unless self.class.corpid.present?
243
211
 
244
212
  render plain: 'Forbidden', status: 403 if signature != Signature.hexdigest(self.class.token,
245
- params[:timestamp],
246
- params[:nonce],
247
- msg_encrypt)
213
+ params[:timestamp],
214
+ params[:nonce],
215
+ msg_encrypt)
248
216
  end
249
217
 
250
218
  def post_xml
@@ -285,11 +253,7 @@ module Wechat
285
253
  end
286
254
 
287
255
  def process_response(response)
288
- if response[:MsgType] == 'success'
289
- msg = 'success'
290
- else
291
- msg = response.to_xml
292
- end
256
+ msg = response[:MsgType] == 'success' ? 'success' : response.to_xml
293
257
 
294
258
  if self.class.encrypt_mode
295
259
  encrypt = Base64.strict_encode64(encrypt(pack(msg, @app_id), self.class.encoding_aes_key))
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.7.7
4
+ version: 0.7.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: 2016-03-18 00:00:00.000000000 Z
12
+ date: 2016-03-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -153,6 +153,7 @@ files:
153
153
  - lib/wechat/api_loader.rb
154
154
  - lib/wechat/cipher.rb
155
155
  - lib/wechat/client.rb
156
+ - lib/wechat/controller_api.rb
156
157
  - lib/wechat/corp_api.rb
157
158
  - lib/wechat/helpers.rb
158
159
  - lib/wechat/message.rb