gfd_wechat 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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