reywechat 1.0.52__py3-none-any.whl → 1.0.54__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 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 file or quote or forward or share link or transfer money or real time location message, '
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
- 'MessageParameter',
51
- {
52
- 'time': int,
53
- 'id': int,
54
- 'number': int,
55
- 'room': str | None,
56
- 'user': str | None,
57
- 'type': int,
58
- 'display': str,
59
- 'data': str,
60
- 'file': MessageParameterFile
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._is_quote_self: bool | None = None
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._is_at_self: bool | None = None
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 message.
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 '<type>57</type>' in self.data
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 is_quote_self(self) -> bool:
730
+ def is_quote_me(self) -> bool:
305
731
  """
306
- Whether if is message of quote self.
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._is_quote_self is not None:
315
- return self._is_quote_self
740
+ if self._is_quote_me is not None:
741
+ return self._is_quote_me
316
742
 
317
743
  # Judge.
318
- self._is_quote_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._is_quote_self
749
+ return self._is_quote_me
324
750
 
325
751
 
326
752
  @property
327
- def quote_params(self) -> dict[
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
- Return quote parameters of message.
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
- # Extracted.
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.is_quote)
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 is_at_self(self) -> bool:
925
+ def is_at_me(self) -> bool:
432
926
  """
433
- Whether if is message of `@` self.
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._is_at_self is not None:
442
- return self._is_at_self
935
+ if self._is_at_me is not None:
936
+ return self._is_at_me
443
937
 
444
938
  # Judge.
445
- self._is_at_self = self.receiver.wechat.client.login_info['name'] in self.at_names
939
+ self._is_at_me = self.receiver.wechat.client.login_info['name'] in self.at_names
446
940
 
447
- return self._is_at_self
941
+ return self._is_at_me
448
942
 
449
943
 
450
944
  @property
@@ -462,25 +956,24 @@ class WeChatMessage(WeChatBase):
462
956
  return self._is_call
463
957
 
464
958
  # Text.
465
- if self.type == 1:
466
- text = self.data
467
- elif self.is_quote:
468
- text = self.quote_params['text']
959
+ if self.type in (1, 3, 34, 42, 43, 47, 48, 49, 50, 56, 1002):
960
+ text = self.text
961
+ text = text.strip()
469
962
  else:
470
963
  self._is_call = False
471
964
  self._call_text = None
472
965
  return self._is_call
473
966
  text = text.strip()
474
967
 
475
- ## At self.
476
- at_self_keyword = '@%s\u2005' % self.receiver.wechat.client.login_info['name']
477
- if at_self_keyword in text:
478
- is_at_self = True
479
- text = text.replace(at_self_keyword, '')
968
+ ## At me.
969
+ at_me_keyword = '@%s\u2005' % self.receiver.wechat.client.login_info['name']
970
+ if at_me_keyword in text:
971
+ is_at_me = True
972
+ text = text.replace(at_me_keyword, '')
480
973
  else:
481
- is_at_self = False
974
+ is_at_me = False
482
975
 
483
- ## Call self.
976
+ ## Call me.
484
977
  pattern = fr'^{self.receiver.call_name}[\s,,]*(.*)$'
485
978
  result: str | None = search(pattern, text)
486
979
  if result is not None:
@@ -499,13 +992,13 @@ class WeChatMessage(WeChatBase):
499
992
  or self.room is None
500
993
 
501
994
  ## At self.
502
- or is_at_self
995
+ or is_at_me
503
996
 
504
997
  ## Call self.
505
998
  or is_call_name
506
999
 
507
- ## Quote self.
508
- or self.is_quote_self
1000
+ ## Quote me.
1001
+ or self.is_quote_me
509
1002
 
510
1003
  ):
511
1004
  is_call = True
@@ -549,13 +1042,13 @@ class WeChatMessage(WeChatBase):
549
1042
 
550
1043
  # Check.
551
1044
  if not self.is_call:
552
- throw(AssertionError, self.is_call)
1045
+ throw(AssertionError, self._is_call)
553
1046
 
554
1047
  return self._call_text
555
1048
 
556
1049
 
557
1050
  @property
558
- def is_call_next(self) -> str:
1051
+ def is_call_next(self) -> bool:
559
1052
  """
560
1053
  Whether if is message of call next message.
561
1054
 
@@ -575,7 +1068,7 @@ class WeChatMessage(WeChatBase):
575
1068
 
576
1069
 
577
1070
  @property
578
- def is_last_call(self) -> str:
1071
+ def is_last_call(self) -> bool:
579
1072
  """
580
1073
  Whether if is message of last message call this time.
581
1074
 
@@ -692,7 +1185,7 @@ class WeChatMessage(WeChatBase):
692
1185
 
693
1186
  # Extract.
694
1187
  pattern = '邀请"(.+?)"加入了群聊'
695
- result = search(pattern, self.data)
1188
+ result: str = search(pattern, self.data)
696
1189
  self._new_room_user_name = result
697
1190
 
698
1191
  return result
@@ -737,7 +1230,7 @@ class WeChatMessage(WeChatBase):
737
1230
 
738
1231
  # Extract.
739
1232
  pattern = '修改群名为“(.+?)”'
740
- result = search(pattern, self.data)
1233
+ result: str = search(pattern, self.data)
741
1234
  self._change_room_name = result
742
1235
 
743
1236
  return self._change_room_name
@@ -791,26 +1284,6 @@ class WeChatMessage(WeChatBase):
791
1284
  return self._is_dissolve_room
792
1285
 
793
1286
 
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
1287
  @property
815
1288
  def image_qrcodes(self) -> list[str]:
816
1289
  """
@@ -821,13 +1294,13 @@ class WeChatMessage(WeChatBase):
821
1294
  Image QR code texts.
822
1295
  """
823
1296
 
824
- # Extracted.
1297
+ # Cache.
825
1298
  if self._image_qrcodes is not None:
826
1299
  return self._image_qrcodes
827
1300
 
828
1301
  # Check.
829
- if not self.is_image:
830
- throw(AssertionError, self.is_image)
1302
+ if self.type != 3:
1303
+ throw(AssertionError, self.type)
831
1304
 
832
1305
  # Extract.
833
1306
  self._image_qrcodes = decode_qrcode(self.file['path'])
@@ -836,9 +1309,9 @@ class WeChatMessage(WeChatBase):
836
1309
 
837
1310
 
838
1311
  @property
839
- def is_xml(self) -> bool:
1312
+ def is_html(self) -> bool:
840
1313
  """
841
- Whether if is XML format.
1314
+ Whether if is HTML format.
842
1315
 
843
1316
  Returns
844
1317
  -------
@@ -846,22 +1319,22 @@ class WeChatMessage(WeChatBase):
846
1319
  """
847
1320
 
848
1321
  # Cache.
849
- if self._is_xml is not None:
850
- return self._is_xml
1322
+ if self._is_html is not None:
1323
+ return self._is_html
851
1324
 
852
1325
  # Judge.
853
- self._is_xml = (
1326
+ self._is_html = (
854
1327
  self.type != 1
855
- and self.data.startswith('<?xml ')
1328
+ and search(r'^<(\S+)[ >].*</\1>\s*', self.data) is not None
856
1329
  )
857
1330
 
858
- return self._is_xml
1331
+ return self._is_html
859
1332
 
860
1333
 
861
1334
  @property
862
- def is_app(self) -> bool:
1335
+ def is_xml(self) -> bool:
863
1336
  """
864
- Whether if is application share.
1337
+ Whether if is XML format.
865
1338
 
866
1339
  Returns
867
1340
  -------
@@ -869,50 +1342,17 @@ class WeChatMessage(WeChatBase):
869
1342
  """
870
1343
 
871
1344
  # Cache.
872
- if self._is_app is not None:
873
- return self._is_app
1345
+ if self._is_xml is not None:
1346
+ return self._is_xml
874
1347
 
875
1348
  # Judge.
876
- self.is_app = (
877
- self.type == 49
878
- and self.is_xml
879
- and '<appmsg ' in self.data[:50]
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'
1349
+ self._is_xml = (
1350
+ self.type != 1
1351
+ and self.data.startswith('<?xml ')
1352
+ and self.data.rstrip().endswith('</msg>')
907
1353
  )
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
1354
 
915
- return self._app_params
1355
+ return self._is_xml
916
1356
 
917
1357
 
918
1358
  @property
@@ -1336,17 +1776,7 @@ class WechatReceiver(WeChatBase):
1336
1776
  )
1337
1777
 
1338
1778
  ## 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
-
1779
+ case 49 if message.is_file_uploaded:
1350
1780
  pattern = r'<md5>([\da-f]{32})</md5>'
1351
1781
  file_md5: str = search(pattern, message.data)
1352
1782
  cache_path = self.wechat.cache.index(file_md5, file_name, copy=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reywechat
3
- Version: 1.0.52
3
+ Version: 1.0.54
4
4
  Summary: WeChat method set.
5
5
  Project-URL: homepage, https://github.com/reyxbo/reywechat/
6
6
  Author-email: Rey <reyxbo@163.com>
@@ -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=YHkW81RZIdHCt2vvjgFKOZxU-EHyVf9F_XT_nOc7jxg,47354
6
+ reywechat/rdb.py,sha256=KfGo6OlTwh4p78VCGqw8lI6cNFGM2CSlpp1OpF7sqSI,48342
7
7
  reywechat/rlog.py,sha256=4EsTgrgC05JvWeKAucUaWGga638CRRJISJN6qncBCAw,5249
8
- reywechat/rreceive.py,sha256=REeHY4MiiZDcQx9j0SbDYPRqqyJVfLtQUBDwhkQOPBs,36231
8
+ reywechat/rreceive.py,sha256=AubilVG1e95gukkhlFL9V7WjhRTbkGaqPxSPehTbf4Q,48225
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.52.dist-info/METADATA,sha256=ThSWIXwyuEA6nuOow9T1wKZRmXKQENSGOqjbU3AtdMk,1551
14
- reywechat-1.0.52.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
- reywechat-1.0.52.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
16
- reywechat-1.0.52.dist-info/RECORD,,
13
+ reywechat-1.0.54.dist-info/METADATA,sha256=qDFNVoRlHH9WQ1ej-wQR_eTifKOeGmn8eNo6aTjlWmA,1551
14
+ reywechat-1.0.54.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ reywechat-1.0.54.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
16
+ reywechat-1.0.54.dist-info/RECORD,,