wechat-bot2 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +18 -0
- data/lib/wechat/bot/client.rb +162 -38
- data/lib/wechat/bot/contact.rb +2 -0
- data/lib/wechat/bot/contact_list.rb +17 -0
- data/lib/wechat/bot/core.rb +1 -1
- data/lib/wechat/bot/http/session.rb +7 -0
- data/lib/wechat/bot/message.rb +3 -8
- data/lib/wechat/bot/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 653832898538ffcb6e9d48f9415fbd3ee1da167e4e5d43a4d97ad8da3649e37e
|
4
|
+
data.tar.gz: '08a1a65f53a4aa5c479d85fd29605c4c358dbebbae48c00055124818564ce480'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97487df4a67f2591420baa1e5aea86f60f89dd7f27f1c6162bd57225204085be3b21f88a78ab2369807b89f62b2c0ba19e0ea77b1e62843a664ecb8ddeb2192d
|
7
|
+
data.tar.gz: '07833b7ba0df389c343929405bb6bfbaa97f23e248bfc2b81c0f3b5cdd5d3787e850167452da9045b9735079884b4f464cb609e5b2540e542b4b5275c9c87775'
|
data/Rakefile
CHANGED
@@ -8,6 +8,8 @@ RSpec::Core::RakeTask.new(:spec)
|
|
8
8
|
require 'rubocop/rake_task'
|
9
9
|
RuboCop::RakeTask.new
|
10
10
|
|
11
|
+
require 'irb'
|
12
|
+
|
11
13
|
task :default => [:rubocop, :spec]
|
12
14
|
|
13
15
|
desc 'Run a sample wechat bot'
|
@@ -39,3 +41,19 @@ task :bot do
|
|
39
41
|
|
40
42
|
bot.start
|
41
43
|
end
|
44
|
+
|
45
|
+
desc 'Enable irb with var `bot` & `client`'
|
46
|
+
task :irb do
|
47
|
+
bot = WeChat::Bot.new do
|
48
|
+
logger = self.logger
|
49
|
+
on :message do |m|
|
50
|
+
logger.info "Message Raw: #{m.raw}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
client = bot.client
|
55
|
+
client.login
|
56
|
+
client.contacts
|
57
|
+
|
58
|
+
binding.irb # since ruby-2.4
|
59
|
+
end
|
data/lib/wechat/bot/client.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "rqrcode"
|
2
2
|
require "logger"
|
3
3
|
require "uri"
|
4
|
+
require "digest"
|
5
|
+
require "json"
|
4
6
|
|
5
7
|
module WeChat::Bot
|
6
8
|
# 微信 API 类
|
@@ -141,7 +143,7 @@ module WeChat::Bot
|
|
141
143
|
#
|
142
144
|
# @return [Array]
|
143
145
|
def login_status(uuid)
|
144
|
-
timestamp = timestamp
|
146
|
+
timestamp = timestamp()
|
145
147
|
params = {
|
146
148
|
"loginicon" => "true",
|
147
149
|
"uuid" => uuid,
|
@@ -195,7 +197,7 @@ module WeChat::Bot
|
|
195
197
|
#
|
196
198
|
# 掉线后 300 秒可以重新使用此 api 登录获取的联系人和群ID保持不变
|
197
199
|
def login_loading
|
198
|
-
url =
|
200
|
+
url = api_url('webwxinit', r: timestamp)
|
199
201
|
r = @session.post(url, json: params_base_request)
|
200
202
|
data = r.parse(:json)
|
201
203
|
|
@@ -215,7 +217,7 @@ module WeChat::Bot
|
|
215
217
|
#
|
216
218
|
# 需要解密参数 Code 的值的作用,目前都用的是 3
|
217
219
|
def update_notice_status
|
218
|
-
url =
|
220
|
+
url = api_url('webwxstatusnotify', lang: 'zh_CN', pass_ticket: store(:pass_ticket))
|
219
221
|
params = params_base_request.merge({
|
220
222
|
"Code" => 3,
|
221
223
|
"FromUserName" => @bot.profile.username,
|
@@ -267,12 +269,11 @@ module WeChat::Bot
|
|
267
269
|
# 根据 {#sync_check} 接口返回有数据时需要调用该接口
|
268
270
|
# @return [void]
|
269
271
|
def sync_messages
|
270
|
-
|
272
|
+
url = api_url('webwxsync', {
|
271
273
|
"sid" => store(:sid),
|
272
274
|
"skey" => store(:skey),
|
273
275
|
"pass_ticket" => store(:pass_ticket)
|
274
|
-
}
|
275
|
-
url = "#{store(:index_url)}/webwxsync?#{URI.encode_www_form(query)}"
|
276
|
+
})
|
276
277
|
params = params_base_request.merge({
|
277
278
|
"SyncKey" => store(:sync_key),
|
278
279
|
"rr" => "-#{timestamp}"
|
@@ -313,12 +314,11 @@ module WeChat::Bot
|
|
313
314
|
#
|
314
315
|
# @return [Hash] 联系人列表
|
315
316
|
def contacts
|
316
|
-
|
317
|
+
url = api_url('webwxgetcontact', {
|
317
318
|
"r" => timestamp,
|
318
319
|
"pass_ticket" => store(:pass_ticket),
|
319
320
|
"skey" => store(:skey)
|
320
|
-
}
|
321
|
-
url = "#{store(:index_url)}/webwxgetcontact?#{URI.encode_www_form(query)}"
|
321
|
+
})
|
322
322
|
|
323
323
|
r = @session.post(url, json: {})
|
324
324
|
data = r.parse(:json)
|
@@ -326,6 +326,8 @@ module WeChat::Bot
|
|
326
326
|
@bot.contact_list.batch_sync(data["MemberList"])
|
327
327
|
end
|
328
328
|
|
329
|
+
alias_method :_send, :send
|
330
|
+
|
329
331
|
# 消息发送
|
330
332
|
#
|
331
333
|
# @param [Symbol] type 消息类型,未知类型默认走 :text
|
@@ -339,6 +341,8 @@ module WeChat::Bot
|
|
339
341
|
case type
|
340
342
|
when :emoticon
|
341
343
|
send_emoticon(username, content)
|
344
|
+
when :image
|
345
|
+
send_image(username, content: content)
|
342
346
|
else
|
343
347
|
send_text(username, content)
|
344
348
|
end
|
@@ -350,7 +354,7 @@ module WeChat::Bot
|
|
350
354
|
# @param [String] text 消息内容
|
351
355
|
# @return [Hash<Object,Object>] 发送结果状态
|
352
356
|
def send_text(username, text)
|
353
|
-
url =
|
357
|
+
url = api_url('webwxsendmsg')
|
354
358
|
params = params_base_request.merge({
|
355
359
|
"Scene" => 0,
|
356
360
|
"Msg" => {
|
@@ -367,34 +371,96 @@ module WeChat::Bot
|
|
367
371
|
r.parse(:json)
|
368
372
|
end
|
369
373
|
|
374
|
+
# FIXME: 上传图片出问题,未能解决
|
375
|
+
def upload_image(username, file)
|
376
|
+
url = "#{store(:file_url)}/webwxuploadmedia?f=json"
|
377
|
+
|
378
|
+
filename = File.basename(file.path)
|
379
|
+
content_type = {'png'=>'image/png', 'jpg'=>'image/jpeg', 'jpeg'=>'image/jpeg'}[filename.split('.').last.downcase] || 'application/octet-stream'
|
380
|
+
md5 = Digest::MD5.file(file.path).hexdigest
|
381
|
+
|
382
|
+
headers = {
|
383
|
+
'Host' => 'file.wx.qq.com',
|
384
|
+
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0',
|
385
|
+
'Accept' => '*/*',
|
386
|
+
'Accept-Language' => 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
|
387
|
+
'Accept-Encoding' => 'gzip, deflate, br',
|
388
|
+
'Referer' => 'https://wx.qq.com/',
|
389
|
+
'Origin' => 'https://wx.qq.com',
|
390
|
+
'Connection' => 'Keep-Alive'
|
391
|
+
}
|
392
|
+
|
393
|
+
@media_cnt = 1 + (@media_cnt || -1)
|
394
|
+
|
395
|
+
params = {
|
396
|
+
'id' => "WU_FILE_#{@media_cnt}",
|
397
|
+
'name' => filename,
|
398
|
+
'type' => content_type,
|
399
|
+
'lastModifiedDate' => 'Tue Sep 09 2014 17:47:23 GMT+0800 (CST)',
|
400
|
+
'size' => file.size,
|
401
|
+
'mediatype' => 'pic', # pic/video/doc
|
402
|
+
'uploadmediarequest' => JSON.generate(
|
403
|
+
params_base_request.merge({
|
404
|
+
'UploadType' => 2,
|
405
|
+
'ClientMediaId' => timestamp,
|
406
|
+
'TotalLen' => file.size,
|
407
|
+
'StartPos' => 0,
|
408
|
+
'DataLen' => file.size,
|
409
|
+
'MediaType' => 4,
|
410
|
+
'FromUserName' => @bot.profile.username,
|
411
|
+
'ToUserName' => username,
|
412
|
+
'FileMd5' => md5
|
413
|
+
})
|
414
|
+
),
|
415
|
+
'webwx_data_ticket' => @session.cookie_of('webwx_data_ticket'),
|
416
|
+
'pass_ticket' => store(:pass_ticket),
|
417
|
+
'filename' => ::HTTP::FormData::File.new(file, content_type: content_type)
|
418
|
+
}
|
419
|
+
|
420
|
+
r = @session.post(url, form: params, headers: headers)
|
421
|
+
|
422
|
+
# @bot.logger.info "Response: #{r.inspect}"
|
423
|
+
|
424
|
+
r.parse(:json)
|
425
|
+
end
|
426
|
+
|
370
427
|
# 发送图片
|
371
428
|
#
|
372
429
|
# @param [String] username 目标 UserName
|
373
430
|
# @param [String, File] 图片名或图片文件
|
374
431
|
# @param [Hash] 非文本消息的参数(可选)
|
375
432
|
# @return [Boolean] 发送结果状态
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
433
|
+
def send_image(username, **opts)
|
434
|
+
# if media_id.nil?
|
435
|
+
# media_id = upload_file(image)
|
436
|
+
# end
|
437
|
+
if opts[:media_id]
|
438
|
+
conf = {"MediaId" => opts[:media_id], "Content" => ""}
|
439
|
+
elsif opts[:image]
|
440
|
+
media_id = upload_image(username, opts[:image])
|
441
|
+
conf = {"MediaId" => media_id, "Content" => ""}
|
442
|
+
elsif opts[:content]
|
443
|
+
conf = {"MediaId" => "", "Content" => opts[:content]}
|
444
|
+
else
|
445
|
+
raise RuntimeException, "发送图片参数错误,须提供media_id或content"
|
446
|
+
end
|
447
|
+
|
448
|
+
url = "#{store(:index_url)}/webwxsendmsgimg?fun=async&f=json"
|
449
|
+
|
450
|
+
params = params_base_request.merge({
|
451
|
+
"Scene" => 0,
|
452
|
+
"Msg" => {
|
453
|
+
"Type" => 3,
|
454
|
+
"FromUserName" => @bot.profile.username,
|
455
|
+
"ToUserName" => username,
|
456
|
+
"LocalID" => timestamp,
|
457
|
+
"ClientMsgId" => timestamp,
|
458
|
+
}.merge(conf)
|
459
|
+
})
|
460
|
+
|
461
|
+
r = @session.post(url, json: params)
|
462
|
+
r.parse(:json)
|
463
|
+
end
|
398
464
|
|
399
465
|
# 发送表情
|
400
466
|
#
|
@@ -405,12 +471,11 @@ module WeChat::Bot
|
|
405
471
|
#
|
406
472
|
# @return [Hash<Object,Object>] 发送结果状态
|
407
473
|
def send_emoticon(username, emoticon_id)
|
408
|
-
|
474
|
+
url = api_url('webwxsendemoticon', {
|
409
475
|
'fun' => 'sys',
|
410
476
|
'pass_ticket' => store(:pass_ticket),
|
411
477
|
'lang' => 'zh_CN'
|
412
|
-
}
|
413
|
-
url = "#{store(:index_url)}/webwxsendemoticon?#{URI.encode_www_form(query)}"
|
478
|
+
})
|
414
479
|
params = params_base_request.merge({
|
415
480
|
"Scene" => 0,
|
416
481
|
"Msg" => {
|
@@ -435,7 +500,7 @@ module WeChat::Bot
|
|
435
500
|
# @param [String] message_id
|
436
501
|
# @return [TempFile]
|
437
502
|
def download_image(message_id)
|
438
|
-
url =
|
503
|
+
url = api_url('webwxgetmsgimg')
|
439
504
|
params = {
|
440
505
|
"msgid" => message_id,
|
441
506
|
"skey" => store(:skey)
|
@@ -460,7 +525,7 @@ module WeChat::Bot
|
|
460
525
|
# @param [Array<String>] users
|
461
526
|
# @return [Hash<Object, Object>]
|
462
527
|
def create_group(*users)
|
463
|
-
url =
|
528
|
+
url = api_url('webwxcreatechatroom', r: timestamp, pass_ticket: store(:pass_ticket))
|
464
529
|
params = params_base_request.merge({
|
465
530
|
"Topic" => "",
|
466
531
|
"MemberCount" => users.size,
|
@@ -471,11 +536,66 @@ module WeChat::Bot
|
|
471
536
|
r.parse(:json)
|
472
537
|
end
|
473
538
|
|
539
|
+
#####
|
540
|
+
# 以下接口都参考:https://github.com/littlecodersh/ItChat/blob/master/itchat/components/contact.py
|
541
|
+
|
542
|
+
# 更新群组
|
543
|
+
def update_group(username, fun, update_key, update_value)
|
544
|
+
url = api_url('webwxupdatechatroom', {fun: fun, pass_ticket: store(:pass_ticket)})
|
545
|
+
params = params_base_request.merge({
|
546
|
+
"ChatRoomName" => username,
|
547
|
+
update_key => update_value
|
548
|
+
})
|
549
|
+
r = @session.post(url, json: params)
|
550
|
+
r.parse(:json)
|
551
|
+
end
|
552
|
+
|
553
|
+
# 修改群组名称
|
554
|
+
def set_group_name(username, name)
|
555
|
+
update_group(username, 'modtopic', 'NewTopic', name)
|
556
|
+
end
|
557
|
+
|
558
|
+
# 删除群组成员
|
559
|
+
def delete_group_member(username, *users)
|
560
|
+
update_group(username, 'delmember', 'DelMemberList', users.join(","))
|
561
|
+
end
|
562
|
+
|
563
|
+
# 群组邀请
|
564
|
+
def invite_group_member(username, *users)
|
565
|
+
update_group(username, 'invitemember', 'InviteMemberList', users.join(","))
|
566
|
+
end
|
567
|
+
|
568
|
+
# 群组添加
|
569
|
+
def add_group_member(username, *users)
|
570
|
+
update_group(username, 'addmember', 'AddMemberList', users.join(","))
|
571
|
+
end
|
572
|
+
|
573
|
+
# 添加好友
|
574
|
+
#
|
575
|
+
# @param [Integer] status: 2-添加 3-接受
|
576
|
+
def add_friend(username, status = 2, verify_content='')
|
577
|
+
url = api_url('webwxverifyuser', {r: timestamp, pass_ticket: store(:pass_ticket)})
|
578
|
+
params = params_base_request.merge({
|
579
|
+
"Opcode" => status, # 3
|
580
|
+
"VerifyUserListSize" => 1,
|
581
|
+
"VerifyUserList" => [{
|
582
|
+
"Value" => username,
|
583
|
+
"VerifyUserTicket" => ''}],
|
584
|
+
"VerifyContent" => verify_content,
|
585
|
+
"SceneListCount" => 1,
|
586
|
+
"SceneList" => [33],
|
587
|
+
"skey" => store(:skey)
|
588
|
+
})
|
589
|
+
r = @session.post(url, json: params)
|
590
|
+
r.parse(:json)
|
591
|
+
end
|
592
|
+
#####
|
593
|
+
|
474
594
|
# 登出
|
475
595
|
#
|
476
596
|
# @return [void]
|
477
597
|
def logout
|
478
|
-
url =
|
598
|
+
url = api_url('webwxlogout')
|
479
599
|
params = {
|
480
600
|
"redirect" => 1,
|
481
601
|
"type" => 1,
|
@@ -504,6 +624,10 @@ module WeChat::Bot
|
|
504
624
|
|
505
625
|
private
|
506
626
|
|
627
|
+
def api_url(path, query = {})
|
628
|
+
"#{store(:index_url)}/#{path}#{query.empty? ? '' : '?'+URI.encode_www_form(query)}"
|
629
|
+
end
|
630
|
+
|
507
631
|
# 保存和获取存储数据
|
508
632
|
#
|
509
633
|
# @return [Object] 获取数据返回该变量对应的值类型
|
data/lib/wechat/bot/contact.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "wechat/bot/contact"
|
2
|
+
|
1
3
|
module WeChat::Bot
|
2
4
|
# 微信联系人列表
|
3
5
|
class ContactList < CachedList
|
@@ -35,6 +37,21 @@ module WeChat::Bot
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
40
|
+
Contact::Kind.constants.each do |const|
|
41
|
+
val = Contact::Kind.const_get(const)
|
42
|
+
|
43
|
+
# 查找用户分类: find_user/group/mp/special
|
44
|
+
define_method "find_#{val}", ->(pattern = nil) do
|
45
|
+
@mutex.synchronize do
|
46
|
+
return @cache.values.select do |contact|
|
47
|
+
contact.kind == val && (
|
48
|
+
pattern.nil? || (pattern.is_a?(Regexp) && pattern.match?(contact.nickname.scrub)) || contact.nickname == pattern
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
38
55
|
# 查找用户
|
39
56
|
#
|
40
57
|
# @param [Hash] args 接受两个参数:
|
data/lib/wechat/bot/core.rb
CHANGED
data/lib/wechat/bot/message.rb
CHANGED
@@ -104,17 +104,17 @@ module WeChat::Bot
|
|
104
104
|
|
105
105
|
if match = group_message(message)
|
106
106
|
message = match[1]
|
107
|
-
@from_user = @from.find_member(username: match[0])
|
107
|
+
@from_user = @from.find_member(username: match[0]) unless @from.nil?
|
108
108
|
@at_message_names = match[2]
|
109
109
|
else
|
110
110
|
@from_user = @from
|
111
111
|
end
|
112
112
|
|
113
113
|
@message = message
|
114
|
-
|
115
|
-
parse_emoticon if @kind == Message::Kind::Emoticon
|
116
114
|
|
117
115
|
case @kind
|
116
|
+
when Message::Kind::Emoticon
|
117
|
+
parse_emoticon
|
118
118
|
when Message::Kind::ShareCard
|
119
119
|
@meta_data = MessageData::ShareCard.parse(@message)
|
120
120
|
end
|
@@ -158,11 +158,6 @@ module WeChat::Bot
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
-
# 私聊或者群聊被@
|
162
|
-
def talked_to?
|
163
|
-
(source == Contact::Kind::User) || at_members.any?{|member| !!member && (member.username == @raw['ToUserName']) }
|
164
|
-
end
|
165
|
-
|
166
161
|
private
|
167
162
|
|
168
163
|
# 解析消息来源
|
data/lib/wechat/bot/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wechat-bot2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- icyleaf
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|