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.
- 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
|