gfd_wechat 0.0.1

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +321 -0
  3. data/LICENSE +21 -0
  4. data/README-CN.md +815 -0
  5. data/README.md +844 -0
  6. data/bin/wechat +520 -0
  7. data/lib/action_controller/wechat_responder.rb +72 -0
  8. data/lib/generators/wechat/config_generator.rb +36 -0
  9. data/lib/generators/wechat/install_generator.rb +20 -0
  10. data/lib/generators/wechat/menu_generator.rb +21 -0
  11. data/lib/generators/wechat/redis_store_generator.rb +16 -0
  12. data/lib/generators/wechat/session_generator.rb +36 -0
  13. data/lib/generators/wechat/templates/MENU_README +3 -0
  14. data/lib/generators/wechat/templates/app/controllers/wechats_controller.rb +12 -0
  15. data/lib/generators/wechat/templates/app/models/wechat_config.rb +46 -0
  16. data/lib/generators/wechat/templates/app/models/wechat_session.rb +17 -0
  17. data/lib/generators/wechat/templates/config/initializers/wechat_redis_store.rb +42 -0
  18. data/lib/generators/wechat/templates/config/wechat.yml +72 -0
  19. data/lib/generators/wechat/templates/config/wechat_menu.yml +6 -0
  20. data/lib/generators/wechat/templates/config/wechat_menu_android.yml +15 -0
  21. data/lib/generators/wechat/templates/db/config_migration.rb.erb +40 -0
  22. data/lib/generators/wechat/templates/db/session_migration.rb.erb +10 -0
  23. data/lib/wechat/api.rb +54 -0
  24. data/lib/wechat/api_base.rb +63 -0
  25. data/lib/wechat/api_loader.rb +145 -0
  26. data/lib/wechat/cipher.rb +66 -0
  27. data/lib/wechat/concern/common.rb +217 -0
  28. data/lib/wechat/controller_api.rb +96 -0
  29. data/lib/wechat/corp_api.rb +168 -0
  30. data/lib/wechat/helpers.rb +47 -0
  31. data/lib/wechat/http_client.rb +112 -0
  32. data/lib/wechat/message.rb +265 -0
  33. data/lib/wechat/mp_api.rb +46 -0
  34. data/lib/wechat/responder.rb +308 -0
  35. data/lib/wechat/signature.rb +10 -0
  36. data/lib/wechat/ticket/corp_jsapi_ticket.rb +14 -0
  37. data/lib/wechat/ticket/jsapi_base.rb +84 -0
  38. data/lib/wechat/ticket/public_jsapi_ticket.rb +14 -0
  39. data/lib/wechat/token/access_token_base.rb +53 -0
  40. data/lib/wechat/token/corp_access_token.rb +13 -0
  41. data/lib/wechat/token/public_access_token.rb +13 -0
  42. data/lib/wechat.rb +52 -0
  43. metadata +195 -0
data/bin/wechat ADDED
@@ -0,0 +1,520 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'thor'
7
+ require 'wechat'
8
+ require 'json'
9
+ require 'active_support' # To support Rails 4.2.1, see #17936
10
+ require 'active_support/dependencies/autoload'
11
+ require 'active_support/core_ext'
12
+ require 'active_support/json'
13
+ require 'fileutils'
14
+ require 'yaml'
15
+ require 'wechat/api_loader'
16
+ require 'cgi'
17
+
18
+ class App < Thor
19
+ class_option :account, aliases: '-a', default: :default, desc: 'Name of Wechat account configuration.'
20
+
21
+ attr_reader :wechat_api_client
22
+ no_commands do
23
+ def wechat_api
24
+ @wechat_api_client ||= Wechat::ApiLoader.with(options)
25
+ end
26
+ end
27
+
28
+ option :token_file, aliases: '-t', desc: 'File to store access token'
29
+ desc 'callbackip', '获取微信服务器IP地址'
30
+ def callbackip
31
+ puts wechat_api.callbackip
32
+ end
33
+
34
+ desc 'qrcode_download [TICKET, QR_CODE_PIC_PATH]', '通过ticket下载二维码'
35
+ def qrcode_download(ticket, qr_code_pic_path)
36
+ tmp_file = wechat_api.qrcode(ticket)
37
+ FileUtils.mv(tmp_file.path, qr_code_pic_path)
38
+ puts 'File downloaded'
39
+ end
40
+
41
+ in_corp_api_cmd = Wechat::ApiLoader.with(options).is_a?(Wechat::CorpApi)
42
+
43
+ if in_corp_api_cmd
44
+ package_name 'Wechat Enterprise Account'
45
+ desc 'department_create [NAME, PARENT_ID]', '创建部门'
46
+ method_option :parentid, aliases: '-p', desc: '父亲部门id。根部门id为1'
47
+ def department_create(name)
48
+ api_opts = options.slice(:parentid)
49
+ puts wechat_api.department_create(name, api_opts[:parentid] || '1')
50
+ end
51
+
52
+ desc 'department_delete [DEPARTMENT_ID]', '删除部门'
53
+ def department_delete(departmentid)
54
+ puts wechat_api.department_delete(departmentid)
55
+ end
56
+
57
+ desc 'department_update [DEPARTMENT_ID, NAME]', '更新部门'
58
+ method_option :parentid, aliases: '-p', desc: '父亲部门id。根部门id为1', default: nil
59
+ method_option :order, aliases: '-o', desc: '在父部门中的次序值。order值小的排序靠前。', default: nil
60
+ def department_update(departmentid, name)
61
+ api_opts = options.slice(:parentid, :order)
62
+ puts wechat_api.department_update(departmentid, name, api_opts[:parentid], api_opts[:order])
63
+ end
64
+
65
+ desc 'department [DEPARTMENT_ID]', '获取部门列表'
66
+ def department(departmentid = 1)
67
+ r = wechat_api.department(departmentid)
68
+ puts "errcode: #{r['errcode']} errmsg: #{r['errmsg']}"
69
+ puts 'Or# pid id name'
70
+ r['department'].sort_by { |d| d['order'].to_i + d['parentid'].to_i * 1000 } .each do |i|
71
+ puts format('%3d %3d %3d %s', i['order'], i['parentid'], i['id'], i['name'])
72
+ end
73
+ end
74
+
75
+ desc 'user_create [USER_ID, NAME]', '创建成员'
76
+ method_option :departmentid, aliases: '-d', desc: '所属部门id'
77
+ method_option :mobile, aliases: '-m', desc: '手机号码'
78
+ method_option :email, aliases: '-e', desc: '邮箱'
79
+ method_option :weixinid, aliases: '-w', desc: '微信号'
80
+ def user_create(userid, name)
81
+ api_opts = options.slice(:departmentid, :mobile, :email, :weixinid)
82
+ user = { userid: userid, name: name }
83
+ if api_opts[:departmentid].present?
84
+ user[:department] = Array(api_opts['departmentid'].to_i)
85
+ else
86
+ user[:department] = [1]
87
+ end
88
+ user[:mobile] = api_opts[:mobile] if api_opts[:mobile].present?
89
+ user[:email] = api_opts[:email] if api_opts[:email].present?
90
+ user[:weixinid] = api_opts[:weixinid] if api_opts[:weixinid].present?
91
+ puts wechat_api.user_create(user)
92
+ end
93
+
94
+ desc 'user_delete [USER_ID]', '删除成员'
95
+ def user_delete(userid)
96
+ puts wechat_api.user_delete(userid)
97
+ end
98
+
99
+ desc 'user_batchdelete [USER_ID_LIST]', '批量删除成员'
100
+ def user_batchdelete(useridlist)
101
+ puts wechat_api.user_batchdelete(useridlist.split(','))
102
+ end
103
+
104
+ desc 'user_simplelist [DEPARTMENT_ID]', '获取部门成员'
105
+ method_option :fetch_child, aliases: '-c', desc: '是否递归获取子部门下面的成员', default: 1
106
+ method_option :status, aliases: '-s', desc: '0 获取全部成员,1 获取已关注成员列表,2 获取禁用成员列表,4 获取未关注成员列表。status可叠加', default: 0
107
+ def user_simplelist(departmentid = 1)
108
+ api_opts = options.slice(:fetch_child, :status)
109
+
110
+ r = wechat_api.user_simplelist(departmentid, api_opts[:fetch_child], api_opts[:status])
111
+ puts "errcode: #{r['errcode']} errmsg: #{r['errmsg']}"
112
+ puts " userid Name #{' ' * 20} department_ids"
113
+ r['userlist'].sort_by { |d| d['userid'] } .each do |i|
114
+ puts format('%7s %-25s %-14s', i['userid'], i['name'], i['department'])
115
+ end
116
+ end
117
+
118
+ desc 'user_list [DEPARTMENT_ID]', '获取部门成员详情'
119
+ method_option :fetch_child, aliases: '-c', desc: '是否递归获取子部门下面的成员', default: 0
120
+ method_option :status, aliases: '-s', desc: '0 获取全部成员,1 获取已关注成员列表,2 获取禁用成员列表,4 获取未关注成员列表。status可叠加', default: 0
121
+ def user_list(departmentid = 1)
122
+ api_opts = options.slice(:fetch_child, :status)
123
+
124
+ r = wechat_api.user_list(departmentid, api_opts[:fetch_child], api_opts[:status])
125
+ puts "errcode: #{r['errcode']} errmsg: #{r['errmsg']}"
126
+ puts " userid Name #{' ' * 15} department_ids position mobile #{' ' * 5}gender email #{' ' * 10}weixinid status extattr"
127
+ r['userlist'].sort_by { |d| d['userid'] } .each do |i|
128
+ puts format('%7s %-20s %-14s %-8s %-11s %-6s %-15s %-15s %-6s %s',
129
+ i['userid'], i['name'], i['department'], i['position'], i['mobile'],
130
+ i['gender'], i['email'], i['weixinid'], i['status'], i['extattr'])
131
+ end
132
+ end
133
+
134
+ desc 'invite_user [USER_ID]', '邀请成员关注'
135
+ def invite_user(userid)
136
+ puts wechat_api.invite_user(userid)
137
+ end
138
+
139
+ desc 'tag_add_department [TAG_ID, PARTY_IDS]', '增加标签部门'
140
+ def tag_add_department(tagid, partyids)
141
+ puts wechat_api.tag_add_user(tagid, nil, partyids.split(','))
142
+ end
143
+
144
+ desc 'tag_del_department [TAG_ID, PARTY_IDS]', '删除标签部门'
145
+ def tag_del_department(tagid, partyids)
146
+ puts wechat_api.tag_del_user(tagid, nil, partyids.split(','))
147
+ end
148
+
149
+ desc 'batch_job_result [JOB_ID]', '获取异步任务结果'
150
+ def batch_job_result(job_id)
151
+ puts wechat_api.batch_job_result(job_id)
152
+ end
153
+
154
+ desc 'batch_replaceparty [BATCH_PARTY_CSV_MEDIA_ID]', '全量覆盖部门'
155
+ def batch_replaceparty(batch_party_csv_media_id)
156
+ puts wechat_api.batch_replaceparty(batch_party_csv_media_id)
157
+ end
158
+
159
+ desc 'upload_replaceparty [BATCH_PARTY_CSV_PATH]', '上传文件方式全量覆盖部门'
160
+ def upload_replaceparty(batch_party_csv_path)
161
+ media_id = wechat_api.media_create('file', batch_party_csv_path)['media_id']
162
+ job_id = wechat_api.batch_replaceparty(media_id)['jobid']
163
+ puts "running job_id: #{job_id}"
164
+ puts wechat_api.batch_job_result(job_id)
165
+ end
166
+
167
+ desc 'batch_syncuser [SYNC_USER_CSV_MEDIA_ID]', '增量更新成员'
168
+ def batch_syncuser(sync_user_csv_media_id)
169
+ puts wechat_api.batch_syncuser(sync_user_csv_media_id)
170
+ end
171
+
172
+ desc 'batch_replaceuser [BATCH_USER_CSV_MEDIA_ID]', '全量覆盖成员'
173
+ def batch_replaceuser(batch_user_csv_media_id)
174
+ puts wechat_api.batch_replaceuser(batch_user_csv_media_id)
175
+ end
176
+
177
+ desc 'upload_replaceuser [BATCH_USER_CSV_PATH]', '上传文件方式全量覆盖成员'
178
+ def upload_replaceuser(batch_user_csv_path)
179
+ media_id = wechat_api.media_create('file', batch_user_csv_path)['media_id']
180
+ job_id = wechat_api.batch_replaceuser(media_id)['jobid']
181
+ puts "running job_id: #{job_id}"
182
+ puts wechat_api.batch_job_result(job_id)
183
+ end
184
+
185
+ desc 'convert_to_openid [USER_ID]', 'userid转换成openid'
186
+ def convert_to_openid(userid)
187
+ puts wechat_api.convert_to_openid(userid)
188
+ end
189
+
190
+ desc 'agent_list', '获取应用概况列表'
191
+ def agent_list
192
+ r = wechat_api.agent_list
193
+ puts "errcode: #{r['errcode']} errmsg: #{r['errmsg']}"
194
+ puts 'ag# name square_logo_url round_logo_url'
195
+ r['agentlist'].sort_by { |d| d['agentid'] } .each do |i|
196
+ puts format('%3d %s %s %s', i['agentid'], i['name'], i['square_logo_url'], i['round_logo_url'])
197
+ end
198
+ end
199
+
200
+ desc 'agent [AGENT_ID]', '获取企业号应用详情'
201
+ def agent(agentid)
202
+ r = wechat_api.agent(agentid)
203
+ puts "agentid: #{r['agentid']} errcode: #{r['errcode']} errmsg: #{r['errmsg']}"
204
+ puts "name: #{r['name']}"
205
+ puts "description: #{r['description']}"
206
+ puts " square_logo_url: #{r['square_logo_url']}"
207
+ puts " round_logo_url: #{r['round_logo_url']}"
208
+ puts "allow_userinfos: #{r['allow_userinfos']}"
209
+ puts "allow_partys: #{r['allow_partys']}"
210
+ puts "allow_tags: #{r['allow_tags']}"
211
+ puts "close: #{r['close']} redirect_domain: #{r['redirect_domain']}"
212
+ puts "report_location_flag: #{r['report_location_flag']} isreportuser: #{r['isreportuser']} isreportenter: #{r['isreportenter']}"
213
+ end
214
+
215
+ desc 'message_send [OPENID, TEXT_MESSAGE]', '发送文字消息'
216
+ def message_send(openid, text_message)
217
+ puts wechat_api.message_send openid, text_message
218
+ end
219
+ else
220
+ package_name 'Wechat Public Account'
221
+ desc 'wxacode_download [WXA_CODE_PIC_PATH, PATH, WIDTH]', '下载小程序码'
222
+ def wxacode_download(wxa_code_pic_path, path = 'pages/index/index', width = 430)
223
+ tmp_file = wechat_api.wxa_get_wxacode(path, width)
224
+ FileUtils.mv(tmp_file.path, wxa_code_pic_path)
225
+ puts 'WXA Code File downloaded'
226
+ end
227
+
228
+ desc 'media_uploadnews [MPNEWS_YAML_PATH]', '上传图文消息素材'
229
+ def media_uploadnews(mpnews_yaml_path)
230
+ mpnew = YAML.load(File.read(mpnews_yaml_path))
231
+ puts wechat_api.media_uploadnews(Wechat::Message.new(MsgType: 'uploadnews').mpnews(mpnew[:articles]))
232
+ end
233
+
234
+ desc 'message_mass_delete [MSG_ID]', '删除群发消息'
235
+ def message_mass_delete(msg_id)
236
+ puts wechat_api.message_mass_delete(msg_id)
237
+ end
238
+
239
+ desc 'message_mass_preview [WX_NAME, MPNEWS_MEDIA_ID]', '预览图文消息素材'
240
+ def message_mass_preview(wx_name, mpnews_media_id)
241
+ puts wechat_api.message_mass_preview(Wechat::Message.to(towxname: wx_name).ref_mpnews(mpnews_media_id))
242
+ end
243
+
244
+ desc 'message_mass_get [MSG_ID]', '查询群发消息发送状态'
245
+ def message_mass_get(msg_id)
246
+ puts wechat_api.message_mass_get(msg_id)
247
+ end
248
+
249
+ desc 'group_create [GROUP_NAME]', '创建分组'
250
+ def group_create(group_name)
251
+ puts wechat_api.group_create(group_name)
252
+ end
253
+
254
+ desc 'groups', '查询所有分组'
255
+ def groups
256
+ puts wechat_api.groups
257
+ end
258
+
259
+ desc 'user_group [OPEN_ID]', '查询用户所在分组'
260
+ def user_group(openid)
261
+ puts wechat_api.user_group(openid)
262
+ end
263
+
264
+ desc 'group_update [GROUP_ID, NEW_GROUP_NAME]', '修改分组名'
265
+ def group_update(groupid, new_group_name)
266
+ puts wechat_api.group_update(groupid, new_group_name)
267
+ end
268
+
269
+ desc 'user_change_group [OPEN_ID, TO_GROUP_ID]', '移动用户分组'
270
+ def user_change_group(openid, to_groupid)
271
+ puts wechat_api.user_change_group(openid, to_groupid)
272
+ end
273
+
274
+ desc 'group_delete [GROUP_ID]', '删除分组'
275
+ def group_delete(groupid)
276
+ puts wechat_api.group_delete(groupid)
277
+ end
278
+
279
+ desc 'users', '关注者列表'
280
+ def users
281
+ puts wechat_api.users
282
+ end
283
+
284
+ desc 'user_batchget [OPEN_ID_LIST]', '批量获取用户基本信息'
285
+ def user_batchget(openidlist)
286
+ puts wechat_api.user_batchget(openidlist.split(','))
287
+ end
288
+
289
+ desc 'qrcode_create_scene [SCENE_ID_OR_STR, EXPIRE_SECONDS]', '请求临时二维码'
290
+ def qrcode_create_scene(scene_id_or_str, expire_seconds = 604800)
291
+ puts wechat_api.qrcode_create_scene(scene_id_or_str, expire_seconds)
292
+ end
293
+
294
+ desc 'qrcode_create_limit_scene [SCENE_ID_OR_STR]', '请求永久二维码'
295
+ def qrcode_create_limit_scene(scene_id_or_str)
296
+ puts wechat_api.qrcode_create_limit_scene(scene_id_or_str)
297
+ end
298
+
299
+ desc 'short_url [LONG_URL]', '长链接转短链接'
300
+ def short_url(long_url)
301
+ puts wechat_api.short_url(long_url)
302
+ end
303
+
304
+ desc 'customservice_getonlinekflist', '获取在线客服接待信息'
305
+ def customservice_getonlinekflist
306
+ puts wechat_api.customservice_getonlinekflist
307
+ end
308
+ end
309
+
310
+ desc 'user [OPEN_ID]', '获取用户基本信息'
311
+ def user(open_id)
312
+ puts wechat_api.user(open_id)
313
+ end
314
+
315
+ desc 'user_update_remark [OPEN_ID, REMARK]', '设置备注名'
316
+ def user_update_remark(openid, remark)
317
+ puts wechat_api.user_update_remark(openid, remark)
318
+ end
319
+
320
+ desc 'menu', '当前菜单'
321
+ def menu
322
+ puts wechat_api.menu
323
+ end
324
+
325
+ desc 'menu_delete', '删除菜单'
326
+ def menu_delete
327
+ puts 'Menu deleted' if wechat_api.menu_delete
328
+ end
329
+
330
+ desc 'menu_create [MENU_YAML_PATH]', '创建菜单'
331
+ def menu_create(menu_yaml_path)
332
+ menu = YAML.load(File.read(menu_yaml_path))
333
+ puts 'Menu created' if wechat_api.menu_create(menu)
334
+ end
335
+
336
+ desc 'menu_addconditional [CONDITIONAL_MENU_YAML_PATH]', '创建个性化菜单'
337
+ def menu_addconditional(conditional_menu_yaml_path)
338
+ conditional_menu = YAML.load(File.read(conditional_menu_yaml_path))
339
+ add_result = wechat_api.menu_addconditional(conditional_menu)
340
+ puts "Conditional menu created: #{add_result}" if add_result
341
+ end
342
+
343
+ desc 'menu_trymatch [USER_ID]', '测试个性化菜单匹配结果'
344
+ def menu_trymatch(user_id)
345
+ puts wechat_api.menu_trymatch(user_id)
346
+ end
347
+
348
+ desc 'menu_delconditional [MENU_ID]', '删除个性化菜单'
349
+ def menu_delconditional(menuid)
350
+ puts wechat_api.menu_delconditional(menuid)
351
+ end
352
+
353
+ desc 'media [MEDIA_ID, PATH]', '媒体下载'
354
+ def media(media_id, path)
355
+ tmp_file = wechat_api.media(media_id)
356
+ FileUtils.mv(tmp_file.path, path)
357
+ puts 'File downloaded'
358
+ end
359
+
360
+ desc 'media_hq [MEDIA_ID, PATH]', '高清音频媒体下载'
361
+ def media_hq(media_id, path)
362
+ tmp_file = wechat_api.media_hq(media_id)
363
+ FileUtils.mv(tmp_file.path, path)
364
+ puts 'File downloaded'
365
+ end
366
+
367
+ desc 'media_create [MEDIA_TYPE, PATH]', '媒体上传'
368
+ def media_create(type, path)
369
+ puts wechat_api.media_create(type, path)
370
+ end
371
+
372
+ desc 'media_uploadimg [IMAGE_PATH]', '上传图文消息内的图片'
373
+ def media_uploadimg(image_path)
374
+ puts wechat_api.media_uploadimg(image_path)
375
+ end
376
+
377
+ desc 'material [MEDIA_ID, PATH]', '永久媒体下载'
378
+ def material(media_id, path)
379
+ tmp_file = wechat_api.material(media_id)
380
+ FileUtils.mv(tmp_file.path, path)
381
+ puts 'File downloaded'
382
+ end
383
+
384
+ desc 'material_add [MEDIA_TYPE, PATH]', '永久媒体上传'
385
+ def material_add(type, path)
386
+ puts wechat_api.material_add(type, path)
387
+ end
388
+
389
+ desc 'material_delete [MEDIA_ID]', '删除永久素材'
390
+ def material_delete(media_id)
391
+ puts wechat_api.material_delete(media_id)
392
+ end
393
+
394
+ desc 'material_count', '获取永久素材总数'
395
+ def material_count
396
+ puts wechat_api.material_count
397
+ end
398
+
399
+ desc 'material_list [TYPE, OFFSET, COUNT]', '获取永久素材列表'
400
+ def material_list(type, offset, count)
401
+ r = wechat_api.material_list(type, offset, count)
402
+ if %w(image voice video file).include?(type)
403
+ puts "errcode: #{r['errcode']} errmsg: #{r['errmsg']} total_count: #{r['total_count']} item_count: #{r['item_count']}"
404
+ if wechat_api.is_a?(Wechat::CorpApi)
405
+ r['itemlist'].each { |i| puts "#{i['media_id']} #{i['filename']} #{Time.at(i['update_time'].to_i)}" }
406
+ else
407
+ r['item'].each { |i| puts "#{i['media_id']} #{i['name']} #{Time.at(i['update_time'].to_i)}" }
408
+ end
409
+ else
410
+ puts r
411
+ end
412
+ end
413
+
414
+ desc 'custom_text [OPENID, TEXT_MESSAGE]', '发送文字客服消息'
415
+ def custom_text(openid, text_message)
416
+ puts wechat_api.custom_message_send Wechat::Message.to(openid).text(text_message)
417
+ end
418
+
419
+ desc 'custom_image [OPENID, IMAGE_PATH]', '发送图片客服消息'
420
+ def custom_image(openid, image_path)
421
+ api = wechat_api
422
+ media_id = api.media_create('image', image_path)['media_id']
423
+ puts api.custom_message_send Wechat::Message.to(openid).image(media_id)
424
+ end
425
+
426
+ desc 'custom_voice [OPENID, VOICE_PATH]', '发送语音客服消息'
427
+ def custom_voice(openid, voice_path)
428
+ api = wechat_api
429
+ media_id = api.media_create('voice', voice_path)['media_id']
430
+ puts api.custom_message_send Wechat::Message.to(openid).voice(media_id)
431
+ end
432
+
433
+ desc 'custom_video [OPENID, VIDEO_PATH]', '发送视频客服消息'
434
+ method_option :title, aliases: '-h', desc: '视频标题'
435
+ method_option :description, aliases: '-d', desc: '视频描述'
436
+ def custom_video(openid, video_path)
437
+ api = wechat_api
438
+ api_opts = options.slice(:title, :description)
439
+ media_id = api.media_create('video', video_path)['media_id']
440
+ puts api.custom_message_send Wechat::Message.to(openid).video(media_id, api_opts)
441
+ end
442
+
443
+ desc 'custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL]', '发送音乐客服消息'
444
+ method_option :title, aliases: '-h', desc: '音乐标题'
445
+ method_option :description, aliases: '-d', desc: '音乐描述'
446
+ method_option :HQ_music_url, aliases: '-u', desc: '高质量音乐URL链接'
447
+ def custom_music(openid, thumbnail_path, music_url)
448
+ api = wechat_api
449
+ api_opts = options.slice(:title, :description, :HQ_music_url)
450
+ thumb_media_id = api.media_create('thumb', thumbnail_path)['thumb_media_id']
451
+ puts api.custom_message_send Wechat::Message.to(openid).music(thumb_media_id, music_url, api_opts)
452
+ end
453
+
454
+ desc 'custom_news [OPENID, NEWS_YAML_PATH]', '发送图文客服消息'
455
+ def custom_news(openid, news_yaml_path)
456
+ articles = YAML.load(File.read(news_yaml_path))
457
+ puts wechat_api.custom_message_send Wechat::Message.to(openid).news(articles['articles'])
458
+ end
459
+
460
+ desc 'template_message [OPENID, TEMPLATE_YAML_PATH]', '模板消息接口'
461
+ def template_message(openid, template_yaml_path)
462
+ template = YAML.load(File.read(template_yaml_path))
463
+ puts wechat_api.template_message_send Wechat::Message.to(openid).template(template['template'])
464
+ end
465
+
466
+ desc 'tags', '获取所有标签'
467
+ def tags
468
+ puts wechat_api.tags
469
+ end
470
+
471
+ desc 'tag_create [TAGNAME, TAG_ID]', '创建标签'
472
+ method_option :tagid, aliases: '-id', desc: '整型,指定此参数时新增的标签会生成对应的标签id,不指定时则以目前最大的id自增' if in_corp_api_cmd
473
+ def tag_create(tag_name)
474
+ if in_corp_api_cmd
475
+ api_opts = options.slice(:tagid)
476
+ puts wechat_api.tag_create(tag_name, api_opts[:tagid])
477
+ else
478
+ puts wechat_api.tag_create(tag_name)
479
+ end
480
+ end
481
+
482
+ desc 'tag_update [TAG_ID, TAGNAME]', '更新标签名字'
483
+ def tag_update(tagid, tagname)
484
+ puts wechat_api.tag_update(tagid, tagname)
485
+ end
486
+
487
+ desc 'tag_delete [TAG_ID]', '删除标签'
488
+ def tag_delete(tagid)
489
+ puts wechat_api.tag_delete(tagid)
490
+ end
491
+
492
+ if in_corp_api_cmd
493
+ desc 'tag_add_user [TAG_ID, USER_IDS]', '增加标签成员'
494
+ else
495
+ desc 'tag_add_user [TAG_ID, OPEN_IDS]', '批量为用户打标签'
496
+ end
497
+ def tag_add_user(tagid, open_or_user_ids)
498
+ puts wechat_api.tag_add_user(tagid, open_or_user_ids.split(','))
499
+ end
500
+
501
+ if in_corp_api_cmd
502
+ desc 'tag_del_user [TAG_ID, USER_IDS]', '删除标签成员'
503
+ else
504
+ desc 'tag_del_user [TAG_ID, OPEN_IDS]', '批量为用户取消标签'
505
+ end
506
+ def tag_del_user(tagid, open_or_user_ids)
507
+ puts wechat_api.tag_del_user(tagid, open_or_user_ids.split(','))
508
+ end
509
+
510
+ if in_corp_api_cmd
511
+ desc 'tag [TAG_ID]', '获取标签成员'
512
+ else
513
+ desc 'tag [TAGID]', '获取标签下粉丝列表'
514
+ end
515
+ def tag(tagid)
516
+ puts wechat_api.tag tagid
517
+ end
518
+ end
519
+
520
+ App.start
@@ -0,0 +1,72 @@
1
+ module ActionController
2
+ module WechatResponder
3
+ def wechat_api(opts = {})
4
+ include Wechat::ControllerApi
5
+ account = opts.delete(:account)
6
+ self.wechat_cfg_account = account ? account.to_sym : :default
7
+ self.wechat_api_client = load_controller_wechat(wechat_cfg_account, opts)
8
+ end
9
+
10
+ def wechat_responder(opts = {})
11
+ include Wechat::Responder
12
+ account = opts.delete(:account)
13
+ self.account_from_request = opts.delete(:account_from_request)
14
+ self.wechat_cfg_account = account ? account.to_sym : :default
15
+ self.wechat_api_client = load_controller_wechat(wechat_cfg_account, opts)
16
+ end
17
+
18
+ def wechat(account = nil)
19
+ if account && account != wechat_cfg_account
20
+ Wechat.api(account)
21
+ else
22
+ self.wechat_api_client ||= load_controller_wechat(wechat_cfg_account)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def load_controller_wechat(account, opts = {})
29
+ cfg = Wechat.config(account)
30
+ self.token = opts[:token] || cfg.token
31
+ self.appid = opts[:appid] || cfg.appid
32
+ self.corpid = opts[:corpid] || cfg.corpid
33
+ self.agentid = opts[:agentid] || cfg.agentid
34
+ self.encrypt_mode = opts[:encrypt_mode] || cfg.encrypt_mode || corpid.present?
35
+ self.encoding_aes_key = opts[:encoding_aes_key] || cfg.encoding_aes_key
36
+ self.trusted_domain_fullname = opts[:trusted_domain_fullname] || cfg.trusted_domain_fullname
37
+ self.oauth2_cookie_duration = opts[:oauth2_cookie_duration] || cfg.oauth2_cookie_duration.to_i.seconds
38
+ self.timeout = opts[:timeout] || cfg.timeout
39
+ if opts.key?(:skip_verify_ssl)
40
+ self.skip_verify_ssl = opts[:skip_verify_ssl]
41
+ else
42
+ self.skip_verify_ssl = cfg.skip_verify_ssl
43
+ end
44
+
45
+ return Wechat.api if account == :default && opts.empty?
46
+
47
+ access_token = opts[:access_token] || cfg.access_token
48
+ jsapi_ticket = opts[:jsapi_ticket] || cfg.jsapi_ticket
49
+
50
+ if corpid.present?
51
+ corpsecret = opts[:corpsecret] || cfg.corpsecret
52
+ Wechat::CorpApi.new(corpid, corpsecret, access_token, \
53
+ agentid, timeout, skip_verify_ssl, jsapi_ticket)
54
+ else
55
+ secret = opts[:secret] || cfg.secret
56
+ Wechat::Api.new(appid, secret, access_token, \
57
+ timeout, skip_verify_ssl, jsapi_ticket)
58
+ end
59
+ end
60
+ end
61
+
62
+ if defined? Base
63
+ class << Base
64
+ include WechatResponder
65
+ end
66
+ end
67
+ if defined? API
68
+ class << API
69
+ include WechatResponder
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,36 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module Wechat
4
+ module Generators
5
+ class ConfigGenerator < Rails::Generators::Base
6
+ include ::Rails::Generators::Migration
7
+
8
+ desc 'Generate wechat configs in database'
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def copy_wechat_config_migration
12
+ migration_template(
13
+ 'db/config_migration.rb.erb',
14
+ 'db/migrate/create_wechat_configs.rb',
15
+ {migration_version: migration_version}
16
+ )
17
+ end
18
+
19
+ def copy_wechat_config_model
20
+ template 'app/models/wechat_config.rb'
21
+ end
22
+
23
+ private
24
+
25
+ def self.next_migration_number(dirname)
26
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
27
+ end
28
+
29
+ def migration_version
30
+ if Rails.version >= '5.0.0'
31
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ module Wechat
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ desc 'Install Wechat support files'
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def copy_config
8
+ template 'config/wechat.yml'
9
+ end
10
+
11
+ def add_wechat_route
12
+ route 'resource :wechat, only: [:show, :create]'
13
+ end
14
+
15
+ def copy_wechat_controller
16
+ template 'app/controllers/wechats_controller.rb'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Wechat
2
+ module Generators
3
+ class MenuGenerator < Rails::Generators::Base
4
+ desc 'Generate wechat menu'
5
+ source_root File.expand_path('../templates', __FILE__)
6
+ class_option :conditional, desc: 'Generate conditional menu', type: :boolean, default: false
7
+
8
+ def copy_menu
9
+ if options.conditional?
10
+ template 'config/wechat_menu_android.yml'
11
+ else
12
+ template 'config/wechat_menu.yml'
13
+ end
14
+ end
15
+
16
+ def show_readme
17
+ readme 'MENU_README' if behavior == :invoke
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Wechat
2
+ module Generators
3
+ class RedisStoreGenerator < Rails::Generators::Base
4
+ desc 'Using redis as token/ticket store'
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def copy_wechat_redis_initializer
8
+ template 'config/initializers/wechat_redis_store.rb'
9
+ end
10
+
11
+ def add_redis_gem
12
+ gem 'redis'
13
+ end
14
+ end
15
+ end
16
+ end