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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d129e298145340b212f0f23de82dbf6c571580d29984553c68e77339fa8ce4d
4
- data.tar.gz: 15d8e87b1bebc29ff8b8727f64dbe62b3dad74d5b61bd2920050e7d9b320b50e
3
+ metadata.gz: 653832898538ffcb6e9d48f9415fbd3ee1da167e4e5d43a4d97ad8da3649e37e
4
+ data.tar.gz: '08a1a65f53a4aa5c479d85fd29605c4c358dbebbae48c00055124818564ce480'
5
5
  SHA512:
6
- metadata.gz: 2fb047ab3e2829968a859c16e38ba8ad5cb51f74d21f6395903ecc94a363727dd4b11d4e54bf8e408db75ba3112a6a3b921f6f0ffc1ec431df2b0c91ed59e0f8
7
- data.tar.gz: 5de0e2c9a843309fc2ecf67aeff30ad71377193d5b0e72c2160a740b9470c25a8d78fd141d5adc6c6c4f702bc156b6ea0af8cd8cfc475421196bd8d0c9d47442
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
@@ -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 = "#{store(:index_url)}/webwxinit?r=#{timestamp}"
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 = "#{store(:index_url)}/webwxstatusnotify?lang=zh_CN&pass_ticket=#{store(:pass_ticket)}"
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
- query = {
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
- query = {
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 = "#{store(:index_url)}/webwxsendmsg"
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
- # def send_image(username, image, media_id = nil)
377
- # if media_id.nil?
378
- # media_id = upload_file(image)
379
- # end
380
-
381
- # url = "#{store(:index_url)}/webwxsendmsgimg?fun=async&f=json"
382
-
383
- # params = params_base_request.merge({
384
- # "Scene" => 0,
385
- # "Msg" => {
386
- # "Type" => type,
387
- # "FromUserName" => @bot.profile.username,
388
- # "ToUserName" => username,
389
- # "MediaId" => mediaId,
390
- # "LocalID" => timestamp,
391
- # "ClientMsgId" => timestamp,
392
- # },
393
- # })
394
-
395
- # r = @session.post(url, json: params)
396
- # r.parse(:json)
397
- # end
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
- query = {
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 = "#{store(:index_url)}/webwxgetmsgimg"
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 = "#{store(:index_url)}/webwxcreatechatroom?r=#{timestamp}"
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 = "#{store(:index_url)}/webwxlogout"
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] 获取数据返回该变量对应的值类型
@@ -128,6 +128,8 @@ module WeChat::Bot
128
128
  "#<#{self.class}:#{object_id.to_s(16)} username='#{username}' nickname='#{nickname}' kind='#{kind}' members=#{members.size}>"
129
129
  end
130
130
 
131
+ alias :inspect :to_s
132
+
131
133
  private
132
134
 
133
135
  # 更新或新写入变量值
@@ -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 接受两个参数:
@@ -68,7 +68,7 @@ module WeChat::Bot
68
68
  # @param [Array<Object>] args
69
69
  # @yieldparam [Array<String>]
70
70
  # @return [Handler]
71
- def on(event, regexp = //, *args, &block)
71
+ def on(event, regexp = %r{}, *args, &block)
72
72
  event = event.to_s.to_sym
73
73
 
74
74
  pattern = case regexp
@@ -54,6 +54,13 @@ module WeChat::Bot
54
54
  response
55
55
  end
56
56
 
57
+ def cookie_of(name)
58
+ cookies.cookies.each do |cookie|
59
+ return cookie.value if name == cookie.name
60
+ end
61
+ nil
62
+ end
63
+
57
64
  private
58
65
 
59
66
  # 组装 request 基础请求参数
@@ -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
  # 解析消息来源
@@ -1,5 +1,5 @@
1
1
  module WeChat
2
2
  module Bot
3
- VERSION = "0.1.3"
3
+ VERSION = "0.1.4"
4
4
  end
5
5
  end
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.3
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-02 00:00:00.000000000 Z
11
+ date: 2018-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler