wechat-bot2 0.1.3 → 0.1.4
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 +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
|