wechat 0.8.4 → 0.8.5

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: 3e0f3e13da7012b66a0db4454a02588e18b197b0
4
- data.tar.gz: 8f20da9e9e385f3a47f07fdd74857b6be7065ac8
3
+ metadata.gz: 7a76622259e3d0a5ea26bab1dfb80463a0bc6587
4
+ data.tar.gz: 1e2fbf216e23989e7ff3151be9942e552b427199
5
5
  SHA512:
6
- metadata.gz: c248645bc11c2b0973cb2a05a0db3ba0611de7173c2077632884be5097706b7906b7ae48c81e3d5094d628b7850278dc9f64d4851ee976fb8ac4e1ce0fbc70ff
7
- data.tar.gz: fc0758f38f48240b64a74b475802259b98df2606952b2c6fbd7addb89b8dee177243789e5acd83adf76c8e8795864905610821ff6b71e6311a3badd157dd8586
6
+ metadata.gz: 8b5a068b1a5f3846335d693121e47c227bfe8a272f2d44e056d3354b0e873e696ae1588e32f7518aef1fb5e2be8ddafa8d9b271a7690724d526ebf6bde0c4bba
7
+ data.tar.gz: e9ee9effb1a9373b914e023f27b1b82afd5ca688382fa5237fca88b2908a2a582fab995ff40aa4b6fc1dc5748dfe13858bc2c2c65d7dd9a3eeefeb1314904b22
Binary file
data.tar.gz.sig CHANGED
@@ -1,2 +1,2 @@
1
- [w�C�Nn��8��&�&���' ����,�XTe��ڂ)Rr� $��/8�
2
- �`��
1
+ |0�����g&�vUkY���������NF�� �,����V67&����,��$�'�fo�����ߔ�pXY�-k�n�.J�!�G+�rv��HDQP*
2
+ G�}a>�$��S��4�՚e��Iք�A�3�Ph�� i2M��n3}.��1g&NK��:ö�? ��U'[���Af�:mJ}i�<��v��c��^x��`�;�����5L���~v+>{.ǡ�:�uWb'[X,���_��Gj��z�W��B��'
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.8.5 (released at 3/14/2017)
4
+
5
+ * Support mass send API #176
6
+ * Support new media_hq API
7
+ * Support new createwxaqrcode API for miniapp
8
+ * Fix wechat_responder not proper injected in rails 5 API #165
9
+ * parse response support XML return, by @zhangbin #167
10
+ * WeChat only allow 8 article per one news, by @kikyous #175
11
+ * Store token at cookies, by @jstdoit #174
12
+
3
13
  ## v0.8.4 (released at 1/12/2017)
4
14
 
5
15
  # Support Ruby 2.4.0
@@ -289,7 +289,7 @@ wechat gems 内部不会检查权限。但因公众号类型不同,和微信
289
289
 
290
290
  ```
291
291
  $ wechat
292
- Wechat commands:
292
+ Wechat Public Account commands:
293
293
  wechat callbackip # 获取微信服务器IP地址
294
294
  wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
295
295
  wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
@@ -308,14 +308,19 @@ Wechat commands:
308
308
  wechat material_delete [MEDIA_ID] # 删除永久素材
309
309
  wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
310
310
  wechat media [MEDIA_ID, PATH] # 媒体下载
311
+ wechat media_hq [MEDIA_ID, PATH] # 高清音频下载
311
312
  wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
312
313
  wechat media_uploadimg [IMAGE_PATH] # 上传图文消息内的图片
314
+ wechat media_uploadnews [MPNEWS_YAML_PATH] # 上传图文消息素材
313
315
  wechat menu # 当前菜单
314
316
  wechat menu_addconditional [CONDITIONAL_MENU_YAML_PATH] # 创建个性化菜单
315
317
  wechat menu_create [MENU_YAML_PATH] # 创建菜单
316
318
  wechat menu_delconditional [MENU_ID] # 删除个性化菜单
317
319
  wechat menu_delete # 删除菜单
318
320
  wechat menu_trymatch [USER_ID] # 测试个性化菜单匹配结果
321
+ wechat message_mass_delete [MSG_ID] # 删除群发消息
322
+ wechat message_mass_get [MSG_ID] # 查询群发消息发送状态
323
+ wechat message_mass_preview [WX_NAME, MPNEWS_MEDIA_ID] # 预览图文消息素材
319
324
  wechat qrcode_create_limit_scene [SCENE_ID_OR_STR] # 请求永久二维码
320
325
  wechat qrcode_create_scene [SCENE_ID, EXPIRE_SECONDS] # 请求临时二维码
321
326
  wechat qrcode_download [TICKET, QR_CODE_PIC_PATH] # 通过ticket下载二维码
@@ -339,7 +344,7 @@ Wechat commands:
339
344
  #### 企业号命令行
340
345
  ```
341
346
  $ wechat
342
- Wechat commands:
347
+ Wechat Enterprise Account commands:
343
348
  wechat agent [AGENT_ID] # 获取企业号应用详情
344
349
  wechat agent_list # 获取应用概况列表
345
350
  wechat batch_job_result [JOB_ID] # 获取异步任务结果
@@ -580,7 +585,7 @@ class WechatsController < ActionController::Base
580
585
 
581
586
  # 当请求的文字信息内容为'<n>条新闻'时, 使用这个responder处理, 并将n作为第二个参数
582
587
  on :text, with: /^(\d+)条新闻$/ do |request, count|
583
- # 微信最多显示10条新闻,大于10条将只取前10
588
+ # 微信最多显示8条新闻,大于8条将只取前8
584
589
  news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: '新闻标题', content: "第#{n}条新闻的内容#{n.hash}" } }
585
590
  request.reply.news(news) do |article, n, index| # 回复"articles"
586
591
  article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/'
@@ -687,6 +692,12 @@ class WechatsController < ActionController::Base
687
692
  request.reply.text "job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
688
693
  end
689
694
 
695
+ # 事件推送群发结果
696
+ on :event, with: 'masssendjobfinish' do |request|
697
+ # https://mp.weixin.qq.com/wiki?action=doc&id=mp1481187827_i0l21&t=0.03571905015619936#8
698
+ request.reply.success # request is XML result hash.
699
+ end
700
+
690
701
  # 当无任何responder处理用户信息时,使用这个responder处理
691
702
  on :fallback, respond: 'fallback message'
692
703
  end
data/README.md CHANGED
@@ -304,7 +304,7 @@ Feel safe if you can not read Chinese in the comments, it's kept there in order
304
304
 
305
305
  ```
306
306
  $ wechat
307
- Wechat commands:
307
+ Wechat Public Account commands:
308
308
  wechat callbackip # 获取微信服务器IP地址
309
309
  wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
310
310
  wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
@@ -323,14 +323,19 @@ Wechat commands:
323
323
  wechat material_delete [MEDIA_ID] # 删除永久素材
324
324
  wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
325
325
  wechat media [MEDIA_ID, PATH] # 媒体下载
326
+ wechat media_hq [MEDIA_ID, PATH] # 高清音频下载
326
327
  wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
327
328
  wechat media_uploadimg [IMAGE_PATH] # 上传图文消息内的图片
329
+ wechat media_uploadnews [MPNEWS_YAML_PATH] # 上传图文消息素材
328
330
  wechat menu # 当前菜单
329
331
  wechat menu_addconditional [CONDITIONAL_MENU_YAML_PATH] # 创建个性化菜单
330
332
  wechat menu_create [MENU_YAML_PATH] # 创建菜单
331
333
  wechat menu_delconditional [MENU_ID] # 删除个性化菜单
332
334
  wechat menu_delete # 删除菜单
333
335
  wechat menu_trymatch [USER_ID] # 测试个性化菜单匹配结果
336
+ wechat message_mass_delete [MSG_ID] # 删除群发消息
337
+ wechat message_mass_get [MSG_ID] # 查询群发消息发送状态
338
+ wechat message_mass_preview [WX_NAME, MPNEWS_MEDIA_ID] # 预览图文消息素材
334
339
  wechat qrcode_create_limit_scene [SCENE_ID_OR_STR] # 请求永久二维码
335
340
  wechat qrcode_create_scene [SCENE_ID, EXPIRE_SECONDS] # 请求临时二维码
336
341
  wechat qrcode_download [TICKET, QR_CODE_PIC_PATH] # 通过ticket下载二维码
@@ -354,7 +359,7 @@ Wechat commands:
354
359
  #### Enterprise account command line
355
360
  ```
356
361
  $ wechat
357
- Wechat commands:
362
+ Wechat Enterprise Account commands:
358
363
  wechat agent [AGENT_ID] # 获取企业号应用详情
359
364
  wechat agent_list # 获取应用概况列表
360
365
  wechat batch_job_result [JOB_ID] # 获取异步任务结果
@@ -594,7 +599,7 @@ class WechatsController < ActionController::Base
594
599
 
595
600
  # When receive '<n>news', will match and will get count as <n> as parameter
596
601
  on :text, with: /^(\d+) news$/ do |request, count|
597
- # Wechat article can only contain max 10 items, large than 10 will be dropped.
602
+ # Wechat article can only contain max 8 items, large than 8 will be dropped.
598
603
  news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: 'News title', content: "No. #{n} news content" } }
599
604
  request.reply.news(news) do |article, n, index| # article is return object
600
605
  article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/'
@@ -700,6 +705,12 @@ class WechatsController < ActionController::Base
700
705
  request.reply.text "replace_party job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
701
706
  end
702
707
 
708
+ # mass sent job finish result notification
709
+ on :event, with: 'masssendjobfinish' do |request|
710
+ # https://mp.weixin.qq.com/wiki?action=doc&id=mp1481187827_i0l21&t=0.03571905015619936#8
711
+ request.reply.success # request is XML result hash.
712
+ end
713
+
703
714
  # If no match above will fallback to below
704
715
  on :fallback, respond: 'fallback message'
705
716
  end
data/bin/wechat CHANGED
@@ -16,7 +16,6 @@ require 'wechat/api_loader'
16
16
  require 'cgi'
17
17
 
18
18
  class App < Thor
19
- package_name 'Wechat'
20
19
  class_option :account, aliases: '-a', default: :default, desc: 'Name of Wechat account configuration.'
21
20
 
22
21
  attr_reader :wechat_api_client
@@ -42,6 +41,7 @@ class App < Thor
42
41
  in_corp_api_cmd = Wechat::ApiLoader.with(options).is_a?(Wechat::CorpApi)
43
42
 
44
43
  if in_corp_api_cmd
44
+ package_name 'Wechat Enterprise Account'
45
45
  desc 'department_create [NAME, PARENT_ID]', '创建部门'
46
46
  method_option :parentid, aliases: '-p', desc: '父亲部门id。根部门id为1'
47
47
  def department_create(name)
@@ -198,6 +198,28 @@ class App < Thor
198
198
  puts wechat_api.message_send openid, text_message
199
199
  end
200
200
  else
201
+ package_name 'Wechat Public Account'
202
+ desc 'media_uploadnews [MPNEWS_YAML_PATH]', '上传图文消息素材'
203
+ def media_uploadnews(mpnews_yaml_path)
204
+ mpnew = YAML.load(File.read(mpnews_yaml_path))
205
+ puts wechat_api.media_uploadnews(Wechat::Message.new(MsgType: 'uploadnews').mpnews(mpnew[:articles]))
206
+ end
207
+
208
+ desc 'message_mass_delete [MSG_ID]', '删除群发消息'
209
+ def message_mass_delete(msg_id)
210
+ puts wechat_api.message_mass_delete(msg_id)
211
+ end
212
+
213
+ desc 'message_mass_preview [WX_NAME, MPNEWS_MEDIA_ID]', '预览图文消息素材'
214
+ def message_mass_preview(wx_name, mpnews_media_id)
215
+ puts wechat_api.message_mass_preview(Wechat::Message.to(towxname: wx_name).ref_mpnews(mpnews_media_id))
216
+ end
217
+
218
+ desc 'message_mass_get [MSG_ID]', '查询群发消息发送状态'
219
+ def message_mass_get(msg_id)
220
+ puts wechat_api.message_mass_get(msg_id)
221
+ end
222
+
201
223
  desc 'group_create [GROUP_NAME]', '创建分组'
202
224
  def group_create(group_name)
203
225
  puts wechat_api.group_create(group_name)
@@ -309,6 +331,13 @@ class App < Thor
309
331
  puts 'File downloaded'
310
332
  end
311
333
 
334
+ desc 'media_hq [MEDIA_ID, PATH]', '高清音频媒体下载'
335
+ def media_hq(media_id, path)
336
+ tmp_file = wechat_api.media_hq(media_id)
337
+ FileUtils.mv(tmp_file.path, path)
338
+ puts 'File downloaded'
339
+ end
340
+
312
341
  desc 'media_create [MEDIA_TYPE, PATH]', '媒体上传'
313
342
  def media_create(type, path)
314
343
  puts wechat_api.media_create(type, path)
@@ -52,7 +52,8 @@ module ActionController
52
52
  class << Base
53
53
  include WechatResponder
54
54
  end
55
- elsif defined? API
55
+ end
56
+ if defined? API
56
57
  class << API
57
58
  include WechatResponder
58
59
  end
@@ -75,6 +75,26 @@ module Wechat
75
75
  post 'shorturl', JSON.generate(action: 'long2short', long_url: long_url)
76
76
  end
77
77
 
78
+ def message_mass_sendall(message)
79
+ post 'message/mass/sendall', message.to_json
80
+ end
81
+
82
+ def message_mass_delete(msg_id)
83
+ post 'message/mass/delete', JSON.generate(msg_id: msg_id)
84
+ end
85
+
86
+ def message_mass_preview(message)
87
+ post 'message/mass/preview', message.to_json
88
+ end
89
+
90
+ def message_mass_get(msg_id)
91
+ post 'message/mass/get', JSON.generate(msg_id: msg_id)
92
+ end
93
+
94
+ def wxa_create_qrcode(path, width = 430)
95
+ post 'wxaapp/createwxaqrcode', JSON.generate(path: path, width: width)
96
+ end
97
+
78
98
  def menu
79
99
  get 'menu/get'
80
100
  end
@@ -16,6 +16,10 @@ module Wechat
16
16
  get 'media/get', params: { media_id: media_id }, as: :file
17
17
  end
18
18
 
19
+ def media_hq(media_id)
20
+ get 'media/get/jssdk', params: { media_id: media_id }, as: :file
21
+ end
22
+
19
23
  def media_create(type, file)
20
24
  post_file 'media/upload', file, params: { type: type }
21
25
  end
@@ -24,6 +28,10 @@ module Wechat
24
28
  post_file 'media/uploadimg', file
25
29
  end
26
30
 
31
+ def media_uploadnews(mpnews_message)
32
+ post 'media/uploadnews', mpnews_message.to_json
33
+ end
34
+
27
35
  protected
28
36
 
29
37
  def get(path, headers = {})
@@ -36,12 +36,14 @@ module Wechat
36
36
  def wechat_public_oauth2(oauth2_params)
37
37
  openid = cookies.signed_or_encrypted[:we_openid]
38
38
  unionid = cookies.signed_or_encrypted[:we_unionid]
39
+ we_token = cookies.signed_or_encrypted[:we_access_token]
39
40
  if openid.present?
40
- yield openid, { 'openid' => openid, 'unionid' => unionid }
41
+ yield openid, { 'openid' => openid, 'unionid' => unionid, 'access_token' => we_token}
41
42
  elsif params[:code].present? && params[:state] == oauth2_params[:state]
42
43
  access_info = wechat.web_access_token(params[:code])
43
44
  cookies.signed_or_encrypted[:we_openid] = { value: access_info['openid'], expires: self.class.oauth2_cookie_duration.from_now }
44
45
  cookies.signed_or_encrypted[:we_unionid] = { value: access_info['unionid'], expires: self.class.oauth2_cookie_duration.from_now }
46
+ cookies.signed_or_encrypted[:we_access_token] = { value: access_info['access_token'], expires: self.class.oauth2_cookie_duration.from_now }
45
47
  yield access_info['openid'], access_info
46
48
  else
47
49
  redirect_to generate_oauth2_url(oauth2_params)
@@ -42,7 +42,7 @@ module Wechat
42
42
  def request(path, header = {}, &_block)
43
43
  url_base = header.delete(:base) || base
44
44
  as = header.delete(:as)
45
- header['Accept'] = 'application/json'
45
+ header['Accept'] ||= 'application/json'
46
46
  response = yield("#{url_base}#{path}", header)
47
47
 
48
48
  raise "Request not OK, response status #{response.status}" if response.status != 200
@@ -5,8 +5,22 @@ module Wechat
5
5
  new(message_hash)
6
6
  end
7
7
 
8
- def to(to_user)
9
- new(ToUserName: to_user, CreateTime: Time.now.to_i)
8
+ def to(to_users = '', towxname: nil, send_ignore_reprint: 0)
9
+ if towxname.present?
10
+ new(ToWxName: towxname, CreateTime: Time.now.to_i)
11
+ elsif send_ignore_reprint == 1
12
+ new(ToUserName: to_users, CreateTime: Time.now.to_i, send_ignore_reprint: send_ignore_reprint)
13
+ else
14
+ new(ToUserName: to_users, CreateTime: Time.now.to_i)
15
+ end
16
+ end
17
+
18
+ def to_mass(tag_id: nil, send_ignore_reprint: 0)
19
+ if tag_id
20
+ new(filter: { is_to_all: false, tag_id: tag_id }, send_ignore_reprint: send_ignore_reprint)
21
+ else
22
+ new(filter: { is_to_all: true }, send_ignore_reprint: send_ignore_reprint)
23
+ end
10
24
  end
11
25
  end
12
26
 
@@ -16,12 +30,21 @@ module Wechat
16
30
  def initialize
17
31
  @items = []
18
32
  end
33
+ end
19
34
 
35
+ class NewsArticleBuilder < ArticleBuilder
20
36
  def item(title: 'title', description: nil, pic_url: nil, url: nil)
21
37
  items << { Title: title, Description: description, PicUrl: pic_url, Url: url }.reject { |_k, v| v.nil? }
22
38
  end
23
39
  end
24
40
 
41
+ class MpNewsArticleBuilder < ArticleBuilder
42
+ def item(thumb_media_id:, title:, content:, author: nil, content_source_url: nil, digest: nil, show_cover_pic: '0')
43
+ items << { Thumb_Media_ID: thumb_media_id, Author: author, Title: title, ContentSourceUrl: content_source_url,
44
+ Content: content, Digest: digest, ShowCoverPic: show_cover_pic }.reject { |_k, v| v.nil? }
45
+ end
46
+ end
47
+
25
48
  attr_reader :message_hash
26
49
 
27
50
  def initialize(message_hash)
@@ -117,8 +140,8 @@ module Wechat
117
140
 
118
141
  def news(collection, &_block)
119
142
  if block_given?
120
- article = ArticleBuilder.new
121
- collection.take(10).each_with_index { |item, index| yield(article, item, index) }
143
+ article = NewsArticleBuilder.new
144
+ collection.take(8).each_with_index { |item, index| yield(article, item, index) }
122
145
  items = article.items
123
146
  else
124
147
  items = collection.collect do |item|
@@ -130,6 +153,24 @@ module Wechat
130
153
  Articles: items.collect { |item| camelize_hash_keys(item) })
131
154
  end
132
155
 
156
+ def mpnews(collection, &_block)
157
+ if block_given?
158
+ article = MpNewsArticleBuilder.new
159
+ collection.take(8).each_with_index { |item, index| yield(article, item, index) }
160
+ items = article.items
161
+ else
162
+ items = collection.collect do |item|
163
+ camelize_hash_keys(item.symbolize_keys.slice(:thumb_media_id, :title, :content, :author, :content_source_url, :digest, :show_cover_pic).reject { |_k, v| v.nil? })
164
+ end
165
+ end
166
+
167
+ update(MsgType: 'mpnews', Articles: items.collect { |item| camelize_hash_keys(item) })
168
+ end
169
+
170
+ def ref_mpnews(media_id)
171
+ update(MsgType: 'ref_mpnews', MpNews: { MediaId: media_id })
172
+ end
173
+
133
174
  def template(opts = {})
134
175
  template_fields = opts.symbolize_keys.slice(:template_id, :topcolor, :url, :data)
135
176
  update(MsgType: 'template', Template: template_fields)
@@ -144,12 +185,16 @@ module Wechat
144
185
 
145
186
  TO_JSON_KEY_MAP = {
146
187
  'ToUserName' => 'touser',
188
+ 'ToWxName' => 'towxname',
147
189
  'MediaId' => 'media_id',
190
+ 'MpNews' => 'mpnews',
148
191
  'ThumbMediaId' => 'thumb_media_id',
149
- 'TemplateId' => 'template_id'
192
+ 'TemplateId' => 'template_id',
193
+ 'ContentSourceUrl' => 'content_source_url',
194
+ 'ShowCoverPic' => 'show_cover_pic'
150
195
  }.freeze
151
196
 
152
- TO_JSON_ALLOWED = %w(touser msgtype content image voice video file music news articles template agentid).freeze
197
+ TO_JSON_ALLOWED = %w(touser msgtype content image voice video file music news articles template agentid filter send_ignore_reprint mpnews towxname).freeze
153
198
 
154
199
  def to_json
155
200
  keep_camel_case_key = message_hash[:MsgType] == 'template'
@@ -164,6 +209,11 @@ module Wechat
164
209
  json_hash['text'] = { 'content' => json_hash.delete('content') }
165
210
  when 'news'
166
211
  json_hash['news'] = { 'articles' => json_hash.delete('articles') }
212
+ when 'mpnews'
213
+ json_hash = { 'articles' => json_hash['articles'] }
214
+ when 'ref_mpnews'
215
+ json_hash['msgtype'] = 'mpnews'
216
+ json_hash.delete('articles')
167
217
  when 'template'
168
218
  json_hash = { 'touser' => json_hash['touser'] }.merge!(json_hash['template'])
169
219
  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.8.4
4
+ version: 0.8.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Skinnyworm
@@ -31,7 +31,7 @@ cert_chain:
31
31
  R5k6Ma92sW8jupX4cqbSu9rntdVQkNRpoHIrfU0MZT0cKsg/D1zMteylxrO3KMsz
32
32
  SPQRv+nrI1J0zevFqb8010heoR8SDyUA0Mm3+Q==
33
33
  -----END CERTIFICATE-----
34
- date: 2017-01-12 00:00:00.000000000 Z
34
+ date: 2017-03-14 00:00:00.000000000 Z
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: activesupport
@@ -206,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
206
  version: '0'
207
207
  requirements: []
208
208
  rubyforge_project:
209
- rubygems_version: 2.6.8
209
+ rubygems_version: 2.6.10
210
210
  signing_key:
211
211
  specification_version: 4
212
212
  summary: DSL for wechat message handling and API
metadata.gz.sig CHANGED
Binary file