reywechat 1.0.52__py3-none-any.whl → 1.0.53__py3-none-any.whl
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.
- reywechat/rdb.py +21 -4
- reywechat/rreceive.py +571 -140
- {reywechat-1.0.52.dist-info → reywechat-1.0.53.dist-info}/METADATA +1 -1
- {reywechat-1.0.52.dist-info → reywechat-1.0.53.dist-info}/RECORD +6 -6
- {reywechat-1.0.52.dist-info → reywechat-1.0.53.dist-info}/WHEEL +0 -0
- {reywechat-1.0.52.dist-info → reywechat-1.0.53.dist-info}/licenses/LICENSE +0 -0
reywechat/rdb.py
CHANGED
@@ -296,14 +296,31 @@ class WeChatDatabase(WeChatBase):
|
|
296
296
|
'1 is text message, '
|
297
297
|
'3 is image message, '
|
298
298
|
'34 is voice message, '
|
299
|
-
'37 is new friend, '
|
300
|
-
'42 is business card, '
|
299
|
+
'37 is new friend invitation message, '
|
300
|
+
'42 is business card message, '
|
301
301
|
'43 is video message, '
|
302
302
|
'47 is emoticon message, '
|
303
303
|
'48 is position message, '
|
304
|
-
'49 is
|
304
|
+
'49 is share message ('
|
305
|
+
'data type, '
|
306
|
+
'1 is pure link text, '
|
307
|
+
'6 is other side upload file completed, '
|
308
|
+
'17 is initiate real time location, '
|
309
|
+
'19 or 40 is forward, '
|
310
|
+
'33 is mini program, '
|
311
|
+
'51 is video channel, '
|
312
|
+
'57 is quote, '
|
313
|
+
'74 is other side start uploading file, '
|
314
|
+
'2000 is transfer money, '
|
315
|
+
'include "<appname>[^<>]+</appname>" is app, '
|
316
|
+
'other omit'
|
317
|
+
'), '
|
318
|
+
'50 is voice call or video call invitation message, '
|
319
|
+
'51 is system synchronize data message, '
|
320
|
+
'56 is real time position data message, '
|
305
321
|
'1000 is system message, '
|
306
|
-
'1002 is recall message
|
322
|
+
'1002 is recall message, '
|
323
|
+
'other omit.'
|
307
324
|
)
|
308
325
|
},
|
309
326
|
{
|
reywechat/rreceive.py
CHANGED
@@ -14,8 +14,6 @@ from typing import Any, TypedDict, Literal, overload
|
|
14
14
|
from collections.abc import Callable
|
15
15
|
from queue import Queue
|
16
16
|
from json import loads as json_loads
|
17
|
-
from bs4 import BeautifulSoup as BSBeautifulSoup
|
18
|
-
from bs4.element import Tag as BSTag
|
19
17
|
from reykit.rbase import throw
|
20
18
|
from reykit.rimage import decode_qrcode
|
21
19
|
from reykit.rlog import Mark
|
@@ -47,19 +45,40 @@ MessageParameterFile = TypedDict(
|
|
47
45
|
}
|
48
46
|
)
|
49
47
|
MessageParameter = TypedDict(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
48
|
+
'MessageParameter',
|
49
|
+
{
|
50
|
+
'time': int,
|
51
|
+
'id': int,
|
52
|
+
'number': int,
|
53
|
+
'room': str | None,
|
54
|
+
'user': str | None,
|
55
|
+
'type': int,
|
56
|
+
'display': str,
|
57
|
+
'data': str,
|
58
|
+
'file': MessageParameterFile
|
59
|
+
}
|
60
|
+
)
|
61
|
+
MessageShareParameter = TypedDict(
|
62
|
+
'MessageShareParameter',
|
63
|
+
{
|
64
|
+
'name': str | None,
|
65
|
+
'title': str | None,
|
66
|
+
'des': str | None,
|
67
|
+
'url': str | None
|
68
|
+
}
|
69
|
+
)
|
70
|
+
MessageQuoteParameter = TypedDict(
|
71
|
+
'MessageQuoteParameter',
|
72
|
+
{
|
73
|
+
'text': str,
|
74
|
+
'quote_id': int,
|
75
|
+
'quote_time': int,
|
76
|
+
'quote_type': int,
|
77
|
+
'quote_user': str,
|
78
|
+
'quote_user_name': str,
|
79
|
+
'quote_data': str
|
80
|
+
}
|
81
|
+
)
|
63
82
|
|
64
83
|
|
65
84
|
class WeChatMessage(WeChatBase):
|
@@ -136,12 +155,28 @@ class WeChatMessage(WeChatBase):
|
|
136
155
|
self._user_name: str | None = None
|
137
156
|
self._room_name: str | None = None
|
138
157
|
self._window_name: str | None = None
|
158
|
+
self._text: str | None = None
|
159
|
+
self._voice_len: float | None = None
|
160
|
+
self._video_len: int | None = None
|
161
|
+
self._business_card_name: str | None = None
|
162
|
+
|
163
|
+
### Share.
|
164
|
+
self._share_type: int | None = None
|
165
|
+
self._share_params: MessageShareParameter | None = None
|
166
|
+
self._is_file_uploading: bool | None = None
|
167
|
+
self._is_file_uploaded: bool | None = None
|
168
|
+
self._is_forward: bool | None = None
|
169
|
+
self._is_mini_program: bool | None = None
|
139
170
|
self._is_quote: bool | None = None
|
140
|
-
self.
|
171
|
+
self._is_quote_me: bool | None = None
|
141
172
|
self._quote_params: dict[Literal['text', 'quote_id', 'quote_type', 'quote_user', 'quote_user_name', 'quote_data'], str] | None = None
|
173
|
+
self._is_money: bool | None = None
|
174
|
+
self._money_amount: float | None = None
|
175
|
+
self._is_app: bool | None = None
|
176
|
+
|
142
177
|
self._at_names: list[str] = None
|
143
178
|
self._is_at: bool | None = None
|
144
|
-
self.
|
179
|
+
self._is_at_me: bool | None = None
|
145
180
|
self._is_call: bool | None = None
|
146
181
|
self._call_text: str | None = None
|
147
182
|
self._is_call_next: bool | None = None
|
@@ -154,11 +189,9 @@ class WeChatMessage(WeChatBase):
|
|
154
189
|
self._change_room_name: str | None = None
|
155
190
|
self._is_kick_out_room: bool | None = None
|
156
191
|
self._is_dissolve_room: bool | None = None
|
157
|
-
self._is_image: bool | None = None
|
158
192
|
self._image_qrcodes: list[str] | None = None
|
193
|
+
self._is_html: bool | None = None
|
159
194
|
self._is_xml: bool | None = None
|
160
|
-
self._is_app: bool | None = None
|
161
|
-
self._app_params: dict | None = None
|
162
195
|
self._valid: bool | None = None
|
163
196
|
|
164
197
|
## Update call.
|
@@ -277,10 +310,403 @@ class WeChatMessage(WeChatBase):
|
|
277
310
|
return self._window_name
|
278
311
|
|
279
312
|
|
313
|
+
@property
|
314
|
+
def text(self) -> str:
|
315
|
+
"""
|
316
|
+
Text description of message content.
|
317
|
+
|
318
|
+
Returns
|
319
|
+
-------
|
320
|
+
Text.
|
321
|
+
"""
|
322
|
+
|
323
|
+
# Cache.
|
324
|
+
if self._text is not None:
|
325
|
+
return self._text
|
326
|
+
|
327
|
+
# Get.
|
328
|
+
match self.type:
|
329
|
+
|
330
|
+
## Text.
|
331
|
+
case 1:
|
332
|
+
self._text = self.data
|
333
|
+
|
334
|
+
## Image.
|
335
|
+
case 3:
|
336
|
+
self._text = '[图片]'
|
337
|
+
|
338
|
+
## Voice.
|
339
|
+
case 34:
|
340
|
+
voice_len = round(self.voice_len, 1)
|
341
|
+
self._text = f'[{voice_len}秒的语音]'
|
342
|
+
|
343
|
+
## New firend invitation.
|
344
|
+
case 37:
|
345
|
+
self._text = '[新好友邀请]'
|
346
|
+
|
347
|
+
## Business card.
|
348
|
+
case 42:
|
349
|
+
self._text = f'[分享名片"{self.business_card_name}"]'
|
350
|
+
|
351
|
+
## Video.
|
352
|
+
case 43:
|
353
|
+
self._text = f'[{self.video_len}秒的视频]'
|
354
|
+
|
355
|
+
## Emoticon.
|
356
|
+
case 47:
|
357
|
+
self._text = f'[动画表情]'
|
358
|
+
|
359
|
+
## Position.
|
360
|
+
case 48:
|
361
|
+
self._text = '[地图位置分享]'
|
362
|
+
|
363
|
+
## Share.
|
364
|
+
case 49:
|
365
|
+
|
366
|
+
### Pure URL text.
|
367
|
+
if self.share_type == 1:
|
368
|
+
self._text = '[网址]'
|
369
|
+
if self.share_params['title'] is not None:
|
370
|
+
self._text += f' {self.share_params['title']}'
|
371
|
+
|
372
|
+
### File uploaded.
|
373
|
+
elif self.is_file_uploaded:
|
374
|
+
self._text = f'[文件"{self.file['name']}"完成上传]'
|
375
|
+
|
376
|
+
### Initiate real time location.
|
377
|
+
elif self.share_type == 17:
|
378
|
+
self._text = '[开始实时地图位置分享]'
|
379
|
+
|
380
|
+
### Forword messages.
|
381
|
+
elif self.is_forword:
|
382
|
+
if self.share_params['title'] is None:
|
383
|
+
self._text = '[转发聊天记录]'
|
384
|
+
else:
|
385
|
+
self._text = f'[转发"{self.share_params['title']}"]'
|
386
|
+
if self.share_params['des'] is not None:
|
387
|
+
self._text += f' {self.share_params['des']}'
|
388
|
+
|
389
|
+
### Mini program.
|
390
|
+
elif self.is_mini_program:
|
391
|
+
if self.share_params['name'] is None:
|
392
|
+
self._text = '[小程序分享]'
|
393
|
+
else:
|
394
|
+
self._text = f'[小程序"{self.share_params['name']}"分享]'
|
395
|
+
if self.share_params['title'] is not None:
|
396
|
+
self._text += f' {self.share_params['title']}'
|
397
|
+
|
398
|
+
### Video channel.
|
399
|
+
elif self.share_type == 51:
|
400
|
+
if self.share_params['name'] is None:
|
401
|
+
self._text = '[视频号分享]'
|
402
|
+
else:
|
403
|
+
self._text = f'[视频号"{self.share_params['name']}"分享]'
|
404
|
+
if self.share_params['title'] is not None:
|
405
|
+
self._text += f' {self.share_params['title']}'
|
406
|
+
|
407
|
+
### Quote.
|
408
|
+
elif self.is_quote:
|
409
|
+
self._text = f'[引用了"{self.quote_params['quote_user_name']}"的消息并发言] {self.quote_params['text']}'
|
410
|
+
|
411
|
+
### Quote me.
|
412
|
+
elif self.is_quote_me:
|
413
|
+
self._text = f'[引用了你的消息并发言] {self.quote_params['text']}'
|
414
|
+
|
415
|
+
### File uploading.
|
416
|
+
elif self.is_file_uploading:
|
417
|
+
file_name: str = search('<title><![CDATA[([^<>]+)]]></title>', self.data)
|
418
|
+
self._text = f'[文件"{file_name}"开始上传]'
|
419
|
+
|
420
|
+
### Transfer money.
|
421
|
+
elif self.is_money:
|
422
|
+
self._text = f'[转账{self.money_amount}¥]'
|
423
|
+
|
424
|
+
### App.
|
425
|
+
elif self.is_app:
|
426
|
+
if self.share_params['name'] is None:
|
427
|
+
self._text = '[APP分享]'
|
428
|
+
else:
|
429
|
+
self._text = f'[APP"{self.share_params['name']}"分享]'
|
430
|
+
if self.share_params["title"] is not None:
|
431
|
+
self._text += f' {self.share_params["title"]}'
|
432
|
+
if self.share_params["des"] is not None:
|
433
|
+
self._text += f' {self.share_params["des"]}'
|
434
|
+
|
435
|
+
### Other.
|
436
|
+
else:
|
437
|
+
if self.share_params['name'] is None:
|
438
|
+
self._text = '[分享]'
|
439
|
+
else:
|
440
|
+
self._text = f'["{self.share_params['name']}"分享]'
|
441
|
+
if self.share_params["title"] is not None:
|
442
|
+
self._text += f' {self.share_params["title"]}'
|
443
|
+
if self.share_params["des"] is not None:
|
444
|
+
self._text += f' {self.share_params["des"]}'
|
445
|
+
|
446
|
+
## Voice call or video call.
|
447
|
+
case 50:
|
448
|
+
self._text = '[视频或语音通话]'
|
449
|
+
|
450
|
+
## System sync.
|
451
|
+
case 51:
|
452
|
+
self._text = '[系统同步]'
|
453
|
+
|
454
|
+
## Real time position.
|
455
|
+
case 56:
|
456
|
+
self._text = '[实时地图位置分享中]'
|
457
|
+
|
458
|
+
## System.
|
459
|
+
case 1000:
|
460
|
+
self._text = '[系统信息]'
|
461
|
+
|
462
|
+
## Recall.
|
463
|
+
case 1002:
|
464
|
+
self._text = '[撤回了一条消息]'
|
465
|
+
|
466
|
+
case _:
|
467
|
+
self._text = '[消息]'
|
468
|
+
|
469
|
+
return self._text
|
470
|
+
|
471
|
+
|
472
|
+
@property
|
473
|
+
def voice_len(self) -> float:
|
474
|
+
"""
|
475
|
+
Voice message length, unit is seconds.
|
476
|
+
|
477
|
+
Returns
|
478
|
+
-------
|
479
|
+
Voice message length.
|
480
|
+
"""
|
481
|
+
|
482
|
+
# Cache.
|
483
|
+
if self._voice_len is not None:
|
484
|
+
return self._voice_len
|
485
|
+
|
486
|
+
# Check.
|
487
|
+
if self.type != 34:
|
488
|
+
throw(AssertionError, self.type)
|
489
|
+
|
490
|
+
# Get.
|
491
|
+
pattern = r'voicelength="(\d+)"'
|
492
|
+
voice_len_us_str = search(pattern, self.data)
|
493
|
+
self._voice_len = int(voice_len_us_str) / 1000
|
494
|
+
|
495
|
+
return self._voice_len
|
496
|
+
|
497
|
+
|
498
|
+
@property
|
499
|
+
def video_len(self) -> int:
|
500
|
+
"""
|
501
|
+
Video message length, unit is seconds.
|
502
|
+
|
503
|
+
Returns
|
504
|
+
-------
|
505
|
+
Video message length.
|
506
|
+
"""
|
507
|
+
|
508
|
+
# Cache.
|
509
|
+
if self._video_len is not None:
|
510
|
+
return self._video_len
|
511
|
+
|
512
|
+
# Check.
|
513
|
+
if self.type != 43:
|
514
|
+
throw(AssertionError, self.type)
|
515
|
+
|
516
|
+
# Get.
|
517
|
+
pattern = r'playlength="(\d+)"'
|
518
|
+
video_len_s_str = search(pattern, self.data)
|
519
|
+
self._video_len = int(video_len_s_str)
|
520
|
+
|
521
|
+
return self._video_len
|
522
|
+
|
523
|
+
|
524
|
+
@property
|
525
|
+
def business_card_name(self) -> str:
|
526
|
+
"""
|
527
|
+
Nickname of business card message.
|
528
|
+
|
529
|
+
Returns
|
530
|
+
-------
|
531
|
+
Voice message length.
|
532
|
+
"""
|
533
|
+
|
534
|
+
# Cache.
|
535
|
+
if self._business_card_name is not None:
|
536
|
+
return self._business_card_name
|
537
|
+
|
538
|
+
# Check.
|
539
|
+
if self.type != 42:
|
540
|
+
throw(AssertionError, self.type)
|
541
|
+
|
542
|
+
# Get.
|
543
|
+
pattern = r'nickname="([^"]+)"'
|
544
|
+
self._business_card_name = search(pattern, self.data)
|
545
|
+
|
546
|
+
return self._business_card_name
|
547
|
+
|
548
|
+
|
549
|
+
@property
|
550
|
+
def share_type(self) -> int:
|
551
|
+
"""
|
552
|
+
Type number of share message.
|
553
|
+
|
554
|
+
Returns
|
555
|
+
-------
|
556
|
+
Type number.
|
557
|
+
"""
|
558
|
+
|
559
|
+
# Cache.
|
560
|
+
if self._share_type is not None:
|
561
|
+
return self._share_type
|
562
|
+
|
563
|
+
# Check.
|
564
|
+
if self.type != 49:
|
565
|
+
throw(AssertionError, self.type)
|
566
|
+
|
567
|
+
# Get.
|
568
|
+
pattern = r'<type>(\d+)</type>'
|
569
|
+
share_type_str: str = search(pattern, self.data)
|
570
|
+
self._share_type = int(share_type_str)
|
571
|
+
|
572
|
+
return self._share_type
|
573
|
+
|
574
|
+
|
575
|
+
@property
|
576
|
+
def share_params(self) -> MessageShareParameter:
|
577
|
+
"""
|
578
|
+
Share message parameters.
|
579
|
+
|
580
|
+
Returns
|
581
|
+
-------
|
582
|
+
Parameters.
|
583
|
+
"""
|
584
|
+
|
585
|
+
# Cache.
|
586
|
+
if self._share_params is not None:
|
587
|
+
return self._share_params
|
588
|
+
|
589
|
+
# Check.
|
590
|
+
if self.type != 49:
|
591
|
+
throw(AssertionError, self.type)
|
592
|
+
|
593
|
+
# Extract.
|
594
|
+
name: str | None = search('.*<appname>([^<>]+)</appname>', self.data)
|
595
|
+
if name is None:
|
596
|
+
name: str | None = search('.*<sourcedisplayname>([^<>]+)</sourcedisplayname>', self.data)
|
597
|
+
if name is None:
|
598
|
+
name: str | None = search('.*<nickname>([^<>]+)</nickname>', self.data)
|
599
|
+
title: str | None = search('<title>([^<>]+)</title>', self.data)
|
600
|
+
des: str | None = search('.*<des>([^<>]+)</des>', self.data)
|
601
|
+
if des is None:
|
602
|
+
des: str | None = search('.*<desc>([^<>]+)</desc>', self.data)
|
603
|
+
url: str | None = search('.*<url>([^<>]+)</url>', self.data)
|
604
|
+
self._share_params: MessageShareParameter = {
|
605
|
+
'name': name,
|
606
|
+
'title': title,
|
607
|
+
'des': des,
|
608
|
+
'url': url
|
609
|
+
}
|
610
|
+
|
611
|
+
return self._share_params
|
612
|
+
|
613
|
+
|
614
|
+
@property
|
615
|
+
def is_file_uploading(self) -> bool:
|
616
|
+
"""
|
617
|
+
Whether if is share message of other side file uploading.
|
618
|
+
|
619
|
+
Returns
|
620
|
+
-------
|
621
|
+
Judge result.
|
622
|
+
"""
|
623
|
+
|
624
|
+
# Cache.
|
625
|
+
if self._is_file_uploading is not None:
|
626
|
+
return self._is_file_uploading
|
627
|
+
|
628
|
+
# Judge.
|
629
|
+
self._is_file_uploading = (
|
630
|
+
self.type == 49
|
631
|
+
and self.share_type == 74
|
632
|
+
)
|
633
|
+
|
634
|
+
return self._is_file_uploading
|
635
|
+
|
636
|
+
|
637
|
+
@property
|
638
|
+
def is_file_uploaded(self) -> bool:
|
639
|
+
"""
|
640
|
+
Whether if is share message of other side file uploaded.
|
641
|
+
|
642
|
+
Returns
|
643
|
+
-------
|
644
|
+
Judge result.
|
645
|
+
"""
|
646
|
+
|
647
|
+
# Cache.
|
648
|
+
if self._is_file_uploaded is not None:
|
649
|
+
return self._is_file_uploaded
|
650
|
+
|
651
|
+
# Judge.
|
652
|
+
self._is_file_uploading = (
|
653
|
+
self.type == 49
|
654
|
+
and self.share_type == 6
|
655
|
+
)
|
656
|
+
|
657
|
+
return self._is_file_uploaded
|
658
|
+
|
659
|
+
|
660
|
+
@property
|
661
|
+
def is_forword(self) -> bool:
|
662
|
+
"""
|
663
|
+
Whether if is share message of forward messages.
|
664
|
+
|
665
|
+
Returns
|
666
|
+
-------
|
667
|
+
Judge result.
|
668
|
+
"""
|
669
|
+
|
670
|
+
# Cache.
|
671
|
+
if self._is_forward is not None:
|
672
|
+
return self._is_forward
|
673
|
+
|
674
|
+
# Judge.
|
675
|
+
self._is_forward = (
|
676
|
+
self.type == 49
|
677
|
+
and self.share_type in (19, 40)
|
678
|
+
)
|
679
|
+
|
680
|
+
return self._is_forward
|
681
|
+
|
682
|
+
|
683
|
+
@property
|
684
|
+
def is_mini_program(self) -> bool:
|
685
|
+
"""
|
686
|
+
Whether if is share message of mini program.
|
687
|
+
|
688
|
+
Returns
|
689
|
+
-------
|
690
|
+
Judge result.
|
691
|
+
"""
|
692
|
+
|
693
|
+
# Cache.
|
694
|
+
if self._is_mini_program is not None:
|
695
|
+
return self._is_mini_program
|
696
|
+
|
697
|
+
# Judge.
|
698
|
+
self._is_mini_program = (
|
699
|
+
self.type == 49
|
700
|
+
and self.type == 33
|
701
|
+
)
|
702
|
+
|
703
|
+
return self._is_mini_program
|
704
|
+
|
705
|
+
|
280
706
|
@property
|
281
707
|
def is_quote(self) -> bool:
|
282
708
|
"""
|
283
|
-
Whether if is message of quote
|
709
|
+
Whether if is share message of quote.
|
284
710
|
|
285
711
|
Returns
|
286
712
|
-------
|
@@ -294,16 +720,16 @@ class WeChatMessage(WeChatBase):
|
|
294
720
|
# Judge.
|
295
721
|
self._is_quote = (
|
296
722
|
self.type == 49
|
297
|
-
and
|
723
|
+
and self.share_type == 57
|
298
724
|
)
|
299
725
|
|
300
726
|
return self._is_quote
|
301
727
|
|
302
728
|
|
303
729
|
@property
|
304
|
-
def
|
730
|
+
def is_quote_me(self) -> bool:
|
305
731
|
"""
|
306
|
-
Whether if is message of quote
|
732
|
+
Whether if is share message of quote me.
|
307
733
|
|
308
734
|
Returns
|
309
735
|
-------
|
@@ -311,25 +737,22 @@ class WeChatMessage(WeChatBase):
|
|
311
737
|
"""
|
312
738
|
|
313
739
|
# Cache.
|
314
|
-
if self.
|
315
|
-
return self.
|
740
|
+
if self._is_quote_me is not None:
|
741
|
+
return self._is_quote_me
|
316
742
|
|
317
743
|
# Judge.
|
318
|
-
self.
|
744
|
+
self._is_quote_me = (
|
319
745
|
self.is_quote
|
320
746
|
and '<chatusr>%s</chatusr>' % self.receiver.wechat.client.login_info['id'] in self.data
|
321
747
|
)
|
322
748
|
|
323
|
-
return self.
|
749
|
+
return self._is_quote_me
|
324
750
|
|
325
751
|
|
326
752
|
@property
|
327
|
-
def quote_params(self) ->
|
328
|
-
Literal['text', 'quote_id', 'quote_time', 'quote_type', 'quote_user', 'quote_user_name', 'quote_data'],
|
329
|
-
str | None
|
330
|
-
]:
|
753
|
+
def quote_params(self) -> MessageQuoteParameter:
|
331
754
|
"""
|
332
|
-
|
755
|
+
Quote message parameters.
|
333
756
|
|
334
757
|
Returns
|
335
758
|
-------
|
@@ -343,13 +766,13 @@ class WeChatMessage(WeChatBase):
|
|
343
766
|
- `Key 'quote_data'`: Quote message data.
|
344
767
|
"""
|
345
768
|
|
346
|
-
#
|
769
|
+
# Cache.
|
347
770
|
if self._quote_params is not None:
|
348
771
|
return self._quote_params
|
349
772
|
|
350
773
|
# Check.
|
351
774
|
if not self.is_quote:
|
352
|
-
throw(AssertionError, self.
|
775
|
+
throw(AssertionError, self._is_quote)
|
353
776
|
|
354
777
|
# Extract.
|
355
778
|
pattern = '<title>(.+?)</title>'
|
@@ -369,7 +792,7 @@ class WeChatMessage(WeChatBase):
|
|
369
792
|
quote_user_name: str = search(pattern, self.data)
|
370
793
|
pattern = '<content>(.+?)</content>'
|
371
794
|
quote_data: str = search(pattern, self.data)
|
372
|
-
self._quote_params = {
|
795
|
+
self._quote_params: MessageQuoteParameter = {
|
373
796
|
'text': text,
|
374
797
|
'quote_id': quote_id,
|
375
798
|
'quote_time': quote_time,
|
@@ -382,6 +805,77 @@ class WeChatMessage(WeChatBase):
|
|
382
805
|
return self._quote_params
|
383
806
|
|
384
807
|
|
808
|
+
@property
|
809
|
+
def is_money(self) -> bool:
|
810
|
+
"""
|
811
|
+
Whether if is message of transfer money.
|
812
|
+
|
813
|
+
Returns
|
814
|
+
-------
|
815
|
+
Judge result.
|
816
|
+
"""
|
817
|
+
|
818
|
+
# Cache.
|
819
|
+
if self._is_money is not None:
|
820
|
+
return self._is_money
|
821
|
+
|
822
|
+
# Judge.
|
823
|
+
self._is_money = (
|
824
|
+
self.type == 49
|
825
|
+
and self.share_type == 2000
|
826
|
+
)
|
827
|
+
|
828
|
+
return self._is_money
|
829
|
+
|
830
|
+
|
831
|
+
@property
|
832
|
+
def money_amount(self) -> float:
|
833
|
+
"""
|
834
|
+
Transfer money amount.
|
835
|
+
|
836
|
+
Returns
|
837
|
+
-------
|
838
|
+
Amount.
|
839
|
+
"""
|
840
|
+
|
841
|
+
# Cache.
|
842
|
+
if self._money_amount is not None:
|
843
|
+
return self._money_amount
|
844
|
+
|
845
|
+
# Check.
|
846
|
+
if not self.is_money:
|
847
|
+
throw(AssertionError, self._is_money)
|
848
|
+
|
849
|
+
# Judge.
|
850
|
+
amount_str: str = search(r'<feedesc><![CDATA[¥([\d.,]+)]]></feedesc>', self.data)
|
851
|
+
self._money_amount = float(amount_str)
|
852
|
+
|
853
|
+
return self._money_amount
|
854
|
+
|
855
|
+
|
856
|
+
@property
|
857
|
+
def is_app(self) -> bool:
|
858
|
+
"""
|
859
|
+
Whether if is application share.
|
860
|
+
|
861
|
+
Returns
|
862
|
+
-------
|
863
|
+
Judge result.
|
864
|
+
"""
|
865
|
+
|
866
|
+
# Cache.
|
867
|
+
if self._is_app is not None:
|
868
|
+
return self._is_app
|
869
|
+
|
870
|
+
# Judge.
|
871
|
+
self._is_app = (
|
872
|
+
self.type == 49
|
873
|
+
and search('<appname>[^<>]+</appname>', self.data) is not None
|
874
|
+
)
|
875
|
+
|
876
|
+
return self._is_app
|
877
|
+
|
878
|
+
|
385
879
|
@property
|
386
880
|
def at_names(self) -> list[str]:
|
387
881
|
"""
|
@@ -428,9 +922,9 @@ class WeChatMessage(WeChatBase):
|
|
428
922
|
|
429
923
|
|
430
924
|
@property
|
431
|
-
def
|
925
|
+
def is_at_me(self) -> bool:
|
432
926
|
"""
|
433
|
-
Whether if is message of `@`
|
927
|
+
Whether if is message of `@` me.
|
434
928
|
|
435
929
|
Returns
|
436
930
|
-------
|
@@ -438,13 +932,13 @@ class WeChatMessage(WeChatBase):
|
|
438
932
|
"""
|
439
933
|
|
440
934
|
# Cache.
|
441
|
-
if self.
|
442
|
-
return self.
|
935
|
+
if self._is_at_me is not None:
|
936
|
+
return self._is_at_me
|
443
937
|
|
444
938
|
# Judge.
|
445
|
-
self.
|
939
|
+
self._is_at_me = self.receiver.wechat.client.login_info['name'] in self.at_names
|
446
940
|
|
447
|
-
return self.
|
941
|
+
return self._is_at_me
|
448
942
|
|
449
943
|
|
450
944
|
@property
|
@@ -472,15 +966,15 @@ class WeChatMessage(WeChatBase):
|
|
472
966
|
return self._is_call
|
473
967
|
text = text.strip()
|
474
968
|
|
475
|
-
## At
|
476
|
-
|
477
|
-
if
|
478
|
-
|
479
|
-
text = text.replace(
|
969
|
+
## At me.
|
970
|
+
at_me_keyword = '@%s\u2005' % self.receiver.wechat.client.login_info['name']
|
971
|
+
if at_me_keyword in text:
|
972
|
+
is_at_me = True
|
973
|
+
text = text.replace(at_me_keyword, '')
|
480
974
|
else:
|
481
|
-
|
975
|
+
is_at_me = False
|
482
976
|
|
483
|
-
## Call
|
977
|
+
## Call me.
|
484
978
|
pattern = fr'^{self.receiver.call_name}[\s,,]*(.*)$'
|
485
979
|
result: str | None = search(pattern, text)
|
486
980
|
if result is not None:
|
@@ -499,13 +993,13 @@ class WeChatMessage(WeChatBase):
|
|
499
993
|
or self.room is None
|
500
994
|
|
501
995
|
## At self.
|
502
|
-
or
|
996
|
+
or is_at_me
|
503
997
|
|
504
998
|
## Call self.
|
505
999
|
or is_call_name
|
506
1000
|
|
507
|
-
## Quote
|
508
|
-
or self.
|
1001
|
+
## Quote me.
|
1002
|
+
or self.is_quote_me
|
509
1003
|
|
510
1004
|
):
|
511
1005
|
is_call = True
|
@@ -549,13 +1043,13 @@ class WeChatMessage(WeChatBase):
|
|
549
1043
|
|
550
1044
|
# Check.
|
551
1045
|
if not self.is_call:
|
552
|
-
throw(AssertionError, self.
|
1046
|
+
throw(AssertionError, self._is_call)
|
553
1047
|
|
554
1048
|
return self._call_text
|
555
1049
|
|
556
1050
|
|
557
1051
|
@property
|
558
|
-
def is_call_next(self) ->
|
1052
|
+
def is_call_next(self) -> bool:
|
559
1053
|
"""
|
560
1054
|
Whether if is message of call next message.
|
561
1055
|
|
@@ -575,7 +1069,7 @@ class WeChatMessage(WeChatBase):
|
|
575
1069
|
|
576
1070
|
|
577
1071
|
@property
|
578
|
-
def is_last_call(self) ->
|
1072
|
+
def is_last_call(self) -> bool:
|
579
1073
|
"""
|
580
1074
|
Whether if is message of last message call this time.
|
581
1075
|
|
@@ -692,7 +1186,7 @@ class WeChatMessage(WeChatBase):
|
|
692
1186
|
|
693
1187
|
# Extract.
|
694
1188
|
pattern = '邀请"(.+?)"加入了群聊'
|
695
|
-
result = search(pattern, self.data)
|
1189
|
+
result: str = search(pattern, self.data)
|
696
1190
|
self._new_room_user_name = result
|
697
1191
|
|
698
1192
|
return result
|
@@ -737,7 +1231,7 @@ class WeChatMessage(WeChatBase):
|
|
737
1231
|
|
738
1232
|
# Extract.
|
739
1233
|
pattern = '修改群名为“(.+?)”'
|
740
|
-
result = search(pattern, self.data)
|
1234
|
+
result: str = search(pattern, self.data)
|
741
1235
|
self._change_room_name = result
|
742
1236
|
|
743
1237
|
return self._change_room_name
|
@@ -791,26 +1285,6 @@ class WeChatMessage(WeChatBase):
|
|
791
1285
|
return self._is_dissolve_room
|
792
1286
|
|
793
1287
|
|
794
|
-
@property
|
795
|
-
def is_image(self) -> bool:
|
796
|
-
"""
|
797
|
-
Whether if is image.
|
798
|
-
|
799
|
-
Returns
|
800
|
-
-------
|
801
|
-
Judge result.
|
802
|
-
"""
|
803
|
-
|
804
|
-
# Cache.
|
805
|
-
if self._is_image is not None:
|
806
|
-
return self._is_image
|
807
|
-
|
808
|
-
# Judge.
|
809
|
-
self._is_image = self.type == 3
|
810
|
-
|
811
|
-
return self._is_image
|
812
|
-
|
813
|
-
|
814
1288
|
@property
|
815
1289
|
def image_qrcodes(self) -> list[str]:
|
816
1290
|
"""
|
@@ -821,13 +1295,13 @@ class WeChatMessage(WeChatBase):
|
|
821
1295
|
Image QR code texts.
|
822
1296
|
"""
|
823
1297
|
|
824
|
-
#
|
1298
|
+
# Cache.
|
825
1299
|
if self._image_qrcodes is not None:
|
826
1300
|
return self._image_qrcodes
|
827
1301
|
|
828
1302
|
# Check.
|
829
|
-
if
|
830
|
-
throw(AssertionError, self.
|
1303
|
+
if self.type != 3:
|
1304
|
+
throw(AssertionError, self.type)
|
831
1305
|
|
832
1306
|
# Extract.
|
833
1307
|
self._image_qrcodes = decode_qrcode(self.file['path'])
|
@@ -836,9 +1310,9 @@ class WeChatMessage(WeChatBase):
|
|
836
1310
|
|
837
1311
|
|
838
1312
|
@property
|
839
|
-
def
|
1313
|
+
def is_html(self) -> bool:
|
840
1314
|
"""
|
841
|
-
Whether if is
|
1315
|
+
Whether if is HTML format.
|
842
1316
|
|
843
1317
|
Returns
|
844
1318
|
-------
|
@@ -846,22 +1320,22 @@ class WeChatMessage(WeChatBase):
|
|
846
1320
|
"""
|
847
1321
|
|
848
1322
|
# Cache.
|
849
|
-
if self.
|
850
|
-
return self.
|
1323
|
+
if self._is_html is not None:
|
1324
|
+
return self._is_html
|
851
1325
|
|
852
1326
|
# Judge.
|
853
|
-
self.
|
1327
|
+
self._is_html = (
|
854
1328
|
self.type != 1
|
855
|
-
and
|
1329
|
+
and search(r'^<(\S+)[ >].*</\1>\s*', self.data) is not None
|
856
1330
|
)
|
857
1331
|
|
858
|
-
return self.
|
1332
|
+
return self._is_html
|
859
1333
|
|
860
1334
|
|
861
1335
|
@property
|
862
|
-
def
|
1336
|
+
def is_xml(self) -> bool:
|
863
1337
|
"""
|
864
|
-
Whether if is
|
1338
|
+
Whether if is XML format.
|
865
1339
|
|
866
1340
|
Returns
|
867
1341
|
-------
|
@@ -869,50 +1343,17 @@ class WeChatMessage(WeChatBase):
|
|
869
1343
|
"""
|
870
1344
|
|
871
1345
|
# Cache.
|
872
|
-
if self.
|
873
|
-
return self.
|
1346
|
+
if self._is_xml is not None:
|
1347
|
+
return self._is_xml
|
874
1348
|
|
875
1349
|
# Judge.
|
876
|
-
self.
|
877
|
-
self.type
|
878
|
-
and self.
|
879
|
-
and
|
880
|
-
)
|
881
|
-
|
882
|
-
return self.is_app
|
883
|
-
|
884
|
-
|
885
|
-
@property
|
886
|
-
def app_params(self) -> dict:
|
887
|
-
"""
|
888
|
-
Return application share parameters.
|
889
|
-
|
890
|
-
Returns
|
891
|
-
-------
|
892
|
-
Application share parameters.
|
893
|
-
"""
|
894
|
-
|
895
|
-
# Extracted.
|
896
|
-
if self._app_params is not None:
|
897
|
-
return self._app_params
|
898
|
-
|
899
|
-
# Check.
|
900
|
-
if not self.is_app:
|
901
|
-
throw(AssertionError, self.is_app)
|
902
|
-
|
903
|
-
# Extract.
|
904
|
-
bs_document = BSBeautifulSoup(
|
905
|
-
self.data,
|
906
|
-
'xml'
|
1350
|
+
self._is_xml = (
|
1351
|
+
self.type != 1
|
1352
|
+
and self.data.startswith('<?xml ')
|
1353
|
+
and self.data.rstrip().endswith('</msg>')
|
907
1354
|
)
|
908
|
-
bs_appmsg = bs_document.find('appmsg')
|
909
|
-
self._app_params = {
|
910
|
-
bs_element.name: bs_element.text
|
911
|
-
for bs_element in bs_appmsg.contents
|
912
|
-
if type(bs_element) == BSTag
|
913
|
-
}
|
914
1355
|
|
915
|
-
return self.
|
1356
|
+
return self._is_xml
|
916
1357
|
|
917
1358
|
|
918
1359
|
@property
|
@@ -1336,17 +1777,7 @@ class WechatReceiver(WeChatBase):
|
|
1336
1777
|
)
|
1337
1778
|
|
1338
1779
|
## Other.
|
1339
|
-
case 49:
|
1340
|
-
|
1341
|
-
### Check.
|
1342
|
-
pattern = r'^.+? : \[文件\](.+)$'
|
1343
|
-
file_name: str | None = search(pattern, message.display)
|
1344
|
-
if (
|
1345
|
-
file_name is None
|
1346
|
-
or '<type>6</type>' not in message.data
|
1347
|
-
):
|
1348
|
-
return
|
1349
|
-
|
1780
|
+
case 49 if message.is_file_uploaded:
|
1350
1781
|
pattern = r'<md5>([\da-f]{32})</md5>'
|
1351
1782
|
file_md5: str = search(pattern, message.data)
|
1352
1783
|
cache_path = self.wechat.cache.index(file_md5, file_name, copy=True)
|
@@ -3,14 +3,14 @@ reywechat/rall.py,sha256=5J_X-XMOyb1Vp1jyS9-oRFXGOtp2vRPX1g3tJot_Eck,371
|
|
3
3
|
reywechat/rbase.py,sha256=hbxn5spvcl_C_Bw8A9teulOXT9GMlxUw145_YbXIOzc,1124
|
4
4
|
reywechat/rcache.py,sha256=7UsHHfgFOgxuSqlTSAO6CprgUUOeBCXYus0kxmRBQxk,908
|
5
5
|
reywechat/rclient.py,sha256=lc1CPle9h08mwP8NlJN0ybzcNJxtpV0ma6q6cz1RIxk,22518
|
6
|
-
reywechat/rdb.py,sha256=
|
6
|
+
reywechat/rdb.py,sha256=KfGo6OlTwh4p78VCGqw8lI6cNFGM2CSlpp1OpF7sqSI,48342
|
7
7
|
reywechat/rlog.py,sha256=4EsTgrgC05JvWeKAucUaWGga638CRRJISJN6qncBCAw,5249
|
8
|
-
reywechat/rreceive.py,sha256=
|
8
|
+
reywechat/rreceive.py,sha256=WoDKU_1BT3KdOD9y7M0sdfJFkzre6eTUMNCvWnyjYR8,48224
|
9
9
|
reywechat/rsend.py,sha256=aTmc1ycL8bon4KXoX7VpRWQKTsVER6f6bRLKWWvPspY,18049
|
10
10
|
reywechat/rtrigger.py,sha256=WdOQwobPdGPyyE9J-qtQFPd60713T0aWqKk02PLdCRE,4966
|
11
11
|
reywechat/rwechat.py,sha256=g0pbprMPv_qWb_lGFrPDAWsJO4vPSIgFLkw0Y28CZUo,4751
|
12
12
|
reywechat/data/client_api.dll,sha256=H9uj-x9Ztg0jFZK0yY6NsnyH5_119dQRFfoVVMidxRs,592384
|
13
|
-
reywechat-1.0.
|
14
|
-
reywechat-1.0.
|
15
|
-
reywechat-1.0.
|
16
|
-
reywechat-1.0.
|
13
|
+
reywechat-1.0.53.dist-info/METADATA,sha256=zzKQxHhpq4J4VZWdp2rDIuaJQXC8k6CAjhbAJpGuxoI,1551
|
14
|
+
reywechat-1.0.53.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
15
|
+
reywechat-1.0.53.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
|
16
|
+
reywechat-1.0.53.dist-info/RECORD,,
|
File without changes
|
File without changes
|