gfd_wechat 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +321 -0
- data/LICENSE +21 -0
- data/README-CN.md +815 -0
- data/README.md +844 -0
- data/bin/wechat +520 -0
- data/lib/action_controller/wechat_responder.rb +72 -0
- data/lib/generators/wechat/config_generator.rb +36 -0
- data/lib/generators/wechat/install_generator.rb +20 -0
- data/lib/generators/wechat/menu_generator.rb +21 -0
- data/lib/generators/wechat/redis_store_generator.rb +16 -0
- data/lib/generators/wechat/session_generator.rb +36 -0
- data/lib/generators/wechat/templates/MENU_README +3 -0
- data/lib/generators/wechat/templates/app/controllers/wechats_controller.rb +12 -0
- data/lib/generators/wechat/templates/app/models/wechat_config.rb +46 -0
- data/lib/generators/wechat/templates/app/models/wechat_session.rb +17 -0
- data/lib/generators/wechat/templates/config/initializers/wechat_redis_store.rb +42 -0
- data/lib/generators/wechat/templates/config/wechat.yml +72 -0
- data/lib/generators/wechat/templates/config/wechat_menu.yml +6 -0
- data/lib/generators/wechat/templates/config/wechat_menu_android.yml +15 -0
- data/lib/generators/wechat/templates/db/config_migration.rb.erb +40 -0
- data/lib/generators/wechat/templates/db/session_migration.rb.erb +10 -0
- data/lib/wechat/api.rb +54 -0
- data/lib/wechat/api_base.rb +63 -0
- data/lib/wechat/api_loader.rb +145 -0
- data/lib/wechat/cipher.rb +66 -0
- data/lib/wechat/concern/common.rb +217 -0
- data/lib/wechat/controller_api.rb +96 -0
- data/lib/wechat/corp_api.rb +168 -0
- data/lib/wechat/helpers.rb +47 -0
- data/lib/wechat/http_client.rb +112 -0
- data/lib/wechat/message.rb +265 -0
- data/lib/wechat/mp_api.rb +46 -0
- data/lib/wechat/responder.rb +308 -0
- data/lib/wechat/signature.rb +10 -0
- data/lib/wechat/ticket/corp_jsapi_ticket.rb +14 -0
- data/lib/wechat/ticket/jsapi_base.rb +84 -0
- data/lib/wechat/ticket/public_jsapi_ticket.rb +14 -0
- data/lib/wechat/token/access_token_base.rb +53 -0
- data/lib/wechat/token/corp_access_token.rb +13 -0
- data/lib/wechat/token/public_access_token.rb +13 -0
- data/lib/wechat.rb +52 -0
- 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
|