@juzi/wechaty 1.0.18 → 1.0.22

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.
Files changed (108) hide show
  1. package/dist/cjs/src/package-json.js +4 -4
  2. package/dist/cjs/src/package-json.js.map +1 -1
  3. package/dist/cjs/src/pure-functions/update.d.ts +9 -0
  4. package/dist/cjs/src/pure-functions/update.d.ts.map +1 -0
  5. package/dist/cjs/src/pure-functions/update.js +74 -0
  6. package/dist/cjs/src/pure-functions/update.js.map +1 -0
  7. package/dist/cjs/src/schemas/contact-events.d.ts +17 -2
  8. package/dist/cjs/src/schemas/contact-events.d.ts.map +1 -1
  9. package/dist/cjs/src/schemas/contact-events.js.map +1 -1
  10. package/dist/cjs/src/schemas/room-events.d.ts +8 -1
  11. package/dist/cjs/src/schemas/room-events.d.ts.map +1 -1
  12. package/dist/cjs/src/schemas/room-events.js +2 -0
  13. package/dist/cjs/src/schemas/room-events.js.map +1 -1
  14. package/dist/cjs/src/schemas/update.d.ts +31 -0
  15. package/dist/cjs/src/schemas/update.d.ts.map +1 -0
  16. package/dist/cjs/src/schemas/update.js +35 -0
  17. package/dist/cjs/src/schemas/update.js.map +1 -0
  18. package/dist/cjs/src/schemas/wechaty-events.d.ts +27 -2
  19. package/dist/cjs/src/schemas/wechaty-events.d.ts.map +1 -1
  20. package/dist/cjs/src/schemas/wechaty-events.js +8 -0
  21. package/dist/cjs/src/schemas/wechaty-events.js.map +1 -1
  22. package/dist/cjs/src/user-modules/contact.d.ts +2 -11
  23. package/dist/cjs/src/user-modules/contact.d.ts.map +1 -1
  24. package/dist/cjs/src/user-modules/contact.js +30 -29
  25. package/dist/cjs/src/user-modules/contact.js.map +1 -1
  26. package/dist/cjs/src/user-modules/room.d.ts +2 -2
  27. package/dist/cjs/src/user-modules/room.d.ts.map +1 -1
  28. package/dist/cjs/src/user-modules/room.js +10 -3
  29. package/dist/cjs/src/user-modules/room.js.map +1 -1
  30. package/dist/cjs/src/user-modules/tag-group.d.ts +7 -1
  31. package/dist/cjs/src/user-modules/tag-group.d.ts.map +1 -1
  32. package/dist/cjs/src/user-modules/tag-group.js +40 -13
  33. package/dist/cjs/src/user-modules/tag-group.js.map +1 -1
  34. package/dist/cjs/src/user-modules/tag.d.ts +11 -4
  35. package/dist/cjs/src/user-modules/tag.d.ts.map +1 -1
  36. package/dist/cjs/src/user-modules/tag.js +53 -17
  37. package/dist/cjs/src/user-modules/tag.js.map +1 -1
  38. package/dist/cjs/src/wechaty/wechaty-base.d.ts +7 -7
  39. package/dist/cjs/src/wechaty-mixins/gerror-mixin.d.ts +1 -1
  40. package/dist/cjs/src/wechaty-mixins/io-mixin.d.ts +2 -2
  41. package/dist/cjs/src/wechaty-mixins/login-mixin.d.ts +5 -5
  42. package/dist/cjs/src/wechaty-mixins/misc-mixin.d.ts +5 -5
  43. package/dist/cjs/src/wechaty-mixins/plugin-mixin.d.ts +6 -6
  44. package/dist/cjs/src/wechaty-mixins/puppet-mixin.d.ts +4 -12
  45. package/dist/cjs/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  46. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js +83 -1
  47. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  48. package/dist/cjs/src/wechaty-mixins/wechatify-user-module-mixin.d.ts +1 -1
  49. package/dist/esm/src/package-json.js +4 -4
  50. package/dist/esm/src/package-json.js.map +1 -1
  51. package/dist/esm/src/pure-functions/update.d.ts +9 -0
  52. package/dist/esm/src/pure-functions/update.d.ts.map +1 -0
  53. package/dist/esm/src/pure-functions/update.js +70 -0
  54. package/dist/esm/src/pure-functions/update.js.map +1 -0
  55. package/dist/esm/src/schemas/contact-events.d.ts +17 -2
  56. package/dist/esm/src/schemas/contact-events.d.ts.map +1 -1
  57. package/dist/esm/src/schemas/contact-events.js.map +1 -1
  58. package/dist/esm/src/schemas/room-events.d.ts +8 -1
  59. package/dist/esm/src/schemas/room-events.d.ts.map +1 -1
  60. package/dist/esm/src/schemas/room-events.js +2 -0
  61. package/dist/esm/src/schemas/room-events.js.map +1 -1
  62. package/dist/esm/src/schemas/update.d.ts +31 -0
  63. package/dist/esm/src/schemas/update.d.ts.map +1 -0
  64. package/dist/esm/src/schemas/update.js +9 -0
  65. package/dist/esm/src/schemas/update.js.map +1 -0
  66. package/dist/esm/src/schemas/wechaty-events.d.ts +27 -2
  67. package/dist/esm/src/schemas/wechaty-events.d.ts.map +1 -1
  68. package/dist/esm/src/schemas/wechaty-events.js +8 -0
  69. package/dist/esm/src/schemas/wechaty-events.js.map +1 -1
  70. package/dist/esm/src/user-modules/contact.d.ts +2 -11
  71. package/dist/esm/src/user-modules/contact.d.ts.map +1 -1
  72. package/dist/esm/src/user-modules/contact.js +30 -29
  73. package/dist/esm/src/user-modules/contact.js.map +1 -1
  74. package/dist/esm/src/user-modules/room.d.ts +2 -2
  75. package/dist/esm/src/user-modules/room.d.ts.map +1 -1
  76. package/dist/esm/src/user-modules/room.js +10 -3
  77. package/dist/esm/src/user-modules/room.js.map +1 -1
  78. package/dist/esm/src/user-modules/tag-group.d.ts +7 -1
  79. package/dist/esm/src/user-modules/tag-group.d.ts.map +1 -1
  80. package/dist/esm/src/user-modules/tag-group.js +40 -13
  81. package/dist/esm/src/user-modules/tag-group.js.map +1 -1
  82. package/dist/esm/src/user-modules/tag.d.ts +11 -4
  83. package/dist/esm/src/user-modules/tag.d.ts.map +1 -1
  84. package/dist/esm/src/user-modules/tag.js +53 -17
  85. package/dist/esm/src/user-modules/tag.js.map +1 -1
  86. package/dist/esm/src/wechaty/wechaty-base.d.ts +7 -7
  87. package/dist/esm/src/wechaty-mixins/gerror-mixin.d.ts +1 -1
  88. package/dist/esm/src/wechaty-mixins/io-mixin.d.ts +2 -2
  89. package/dist/esm/src/wechaty-mixins/login-mixin.d.ts +5 -5
  90. package/dist/esm/src/wechaty-mixins/misc-mixin.d.ts +5 -5
  91. package/dist/esm/src/wechaty-mixins/plugin-mixin.d.ts +6 -6
  92. package/dist/esm/src/wechaty-mixins/puppet-mixin.d.ts +4 -12
  93. package/dist/esm/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  94. package/dist/esm/src/wechaty-mixins/puppet-mixin.js +84 -2
  95. package/dist/esm/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  96. package/dist/esm/src/wechaty-mixins/wechatify-user-module-mixin.d.ts +1 -1
  97. package/package.json +3 -3
  98. package/src/package-json.ts +4 -4
  99. package/src/pure-functions/update.ts +69 -0
  100. package/src/schemas/contact-events.ts +25 -2
  101. package/src/schemas/room-events.ts +9 -0
  102. package/src/schemas/update.ts +43 -0
  103. package/src/schemas/wechaty-events.ts +74 -40
  104. package/src/user-modules/contact.ts +34 -30
  105. package/src/user-modules/room.ts +11 -4
  106. package/src/user-modules/tag-group.ts +48 -12
  107. package/src/user-modules/tag.ts +63 -17
  108. package/src/wechaty-mixins/puppet-mixin.ts +86 -2
@@ -12,17 +12,27 @@ import type {
12
12
  ContactInterface,
13
13
  MessageInterface,
14
14
  PostInterface,
15
+ TagInterface,
15
16
  } from '../user-modules/mod.js'
17
+ import type { InfoUpdateInterface } from './update.js'
16
18
 
17
19
  const WECHATY_EVENT_DICT = {
18
20
  ...PUPPET.types.CHAT_EVENT_DICT,
19
- dong : 'Should be emitted after we call `Wechaty.ding()`',
20
- error : "Will be emitted when there's an Error occurred.",
21
- heartbeat : 'Will be emitted periodically after the Wechaty started. If not, means that the Wechaty had died.',
22
- puppet : 'Will be emitted when the puppet has been set.',
23
- ready : 'All underlined data source are ready for use.',
24
- start : 'Will be emitted after the Wechaty had been started.',
25
- stop : 'Will be emitted after the Wechaty had been stopped.',
21
+ dong : 'Should be emitted after we call `Wechaty.ding()`',
22
+ error : "Will be emitted when there's an Error occurred.",
23
+ heartbeat : 'Will be emitted periodically after the Wechaty started. If not, means that the Wechaty had died.',
24
+ puppet : 'Will be emitted when the puppet has been set.',
25
+ ready : 'All underlined data source are ready for use.',
26
+ start : 'Will be emitted after the Wechaty had been started.',
27
+ stop : 'Will be emitted after the Wechaty had been stopped.',
28
+ update : 'Will be emitted when some info has been changed.',
29
+ 'contact-tag-add' : 'Will be emitted when contact has new tags.',
30
+ 'contact-tag-remove' : 'Will be emitted when contact has some tags removed.',
31
+ 'contact-name' : 'Will be emitted when contact name has been changed.',
32
+ 'contact-alias' : 'Will be emitted when contact alias has been changed.',
33
+ 'contact-phone' : 'Will be emitted when contact phone has been changed.',
34
+ 'contact-description': 'Will be emitted when contact description has been changed.',
35
+ 'room-owner' : 'Will be emitted when room owner has been changed.',
26
36
  } as const
27
37
 
28
38
  type WechatyEventName = keyof typeof WECHATY_EVENT_DICT
@@ -30,22 +40,30 @@ type WechatyEventName = keyof typeof WECHATY_EVENT_DICT
30
40
  /**
31
41
  * Wechaty Event Listener Interfaces
32
42
  */
33
- type WechatyEventListenerDong = (data?: string) => void | Promise<void>
34
- type WechatyEventListenerError = (error: GError) => void | Promise<void>
35
- type WechatyEventListenerFriendship = (friendship: FriendshipInterface) => void | Promise<void>
36
- type WechatyEventListenerHeartbeat = (data: any) => void | Promise<void>
37
- type WechatyEventListenerLogin = (user: ContactSelfInterface) => void | Promise<void>
38
- type WechatyEventListenerLogout = (user: ContactSelfInterface, reason?: string) => void | Promise<void>
39
- type WechatyEventListenerMessage = (message: MessageInterface) => void | Promise<void>
40
- type WechatyEventListenerPost = (post: PostInterface) => void | Promise<void>
41
- type WechatyEventListenerPuppet = (puppet: PUPPET.impls.PuppetInterface) => void | Promise<void>
42
- type WechatyEventListenerReady = () => void | Promise<void>
43
- type WechatyEventListenerRoomInvite = (roomInvitation: RoomInvitationInterface) => void | Promise<void>
44
- type WechatyEventListenerRoomJoin = (room: RoomInterface, inviteeList: ContactInterface[], inviter: ContactInterface, date?: Date) => void | Promise<void>
45
- type WechatyEventListenerRoomLeave = (room: RoomInterface, leaverList: ContactInterface[], remover?: ContactInterface, date?: Date) => void | Promise<void>
46
- type WechatyEventListenerRoomTopic = (room: RoomInterface, newTopic: string, oldTopic: string, changer: ContactInterface, date?: Date) => void | Promise<void>
47
- type WechatyEventListenerScan = (qrcode: string, status: PUPPET.types.ScanStatus, data?: string) => void | Promise<void>
48
- type WechatyEventListenerStartStop = () => void | Promise<void>
43
+ type WechatyEventListenerDong = (data?: string) => void | Promise<void>
44
+ type WechatyEventListenerError = (error: GError) => void | Promise<void>
45
+ type WechatyEventListenerFriendship = (friendship: FriendshipInterface) => void | Promise<void>
46
+ type WechatyEventListenerHeartbeat = (data: any) => void | Promise<void>
47
+ type WechatyEventListenerLogin = (user: ContactSelfInterface) => void | Promise<void>
48
+ type WechatyEventListenerLogout = (user: ContactSelfInterface, reason?: string) => void | Promise<void>
49
+ type WechatyEventListenerMessage = (message: MessageInterface) => void | Promise<void>
50
+ type WechatyEventListenerPost = (post: PostInterface) => void | Promise<void>
51
+ type WechatyEventListenerPuppet = (puppet: PUPPET.impls.PuppetInterface) => void | Promise<void>
52
+ type WechatyEventListenerReady = () => void | Promise<void>
53
+ type WechatyEventListenerRoomInvite = (roomInvitation: RoomInvitationInterface) => void | Promise<void>
54
+ type WechatyEventListenerRoomJoin = (room: RoomInterface, inviteeList: ContactInterface[], inviter: ContactInterface, date?: Date) => void | Promise<void>
55
+ type WechatyEventListenerRoomLeave = (room: RoomInterface, leaverList: ContactInterface[], remover?: ContactInterface, date?: Date) => void | Promise<void>
56
+ type WechatyEventListenerRoomTopic = (room: RoomInterface, newTopic: string, oldTopic: string, changer: ContactInterface, date?: Date) => void | Promise<void>
57
+ type WechatyEventListenerRoomOwner = (room: RoomInterface, newOwner: ContactInterface, oldOwner: ContactInterface) => void | Promise<void>
58
+ type WechatyEventListenerScan = (qrcode: string, status: PUPPET.types.ScanStatus, data?: string) => void | Promise<void>
59
+ type WechatyEventListenerStartStop = () => void | Promise<void>
60
+ type WechatyEventListenerUpdate = (info: InfoUpdateInterface) => void | Promise<void>
61
+ type WechatyEventListenerContactTagAdd = (contact: ContactInterface, tagList: TagInterface[]) => void | Promise<void>
62
+ type WechatyEventListenerContactTagRemove = (contact: ContactInterface, tagList: TagInterface[]) => void | Promise<void>
63
+ type WechatyEventListenerContactName = (contact: ContactInterface, newName: string, oldName: string) => void | Promise<void>
64
+ type WechatyEventListenerContactPhone = (contact: ContactInterface, newPhoneList: string[], oldPhoneList: []) => void | Promise<void>
65
+ type WechatyEventListenerContactAlias = (contact: ContactInterface, newAlias: string, oldAlias: string) => void | Promise<void>
66
+ type WechatyEventListenerContactDescription = (contact: ContactInterface, newDescription: string, oldDescription: string) => void | Promise<void>
49
67
 
50
68
  /**
51
69
  * @desc Wechaty Class Event Type
@@ -202,23 +220,31 @@ type WechatyEventListenerStartStop = ()
202
220
  * })
203
221
  */
204
222
  interface WechatyEventListeners {
205
- 'room-invite' : WechatyEventListenerRoomInvite
206
- 'room-join' : WechatyEventListenerRoomJoin
207
- 'room-leave' : WechatyEventListenerRoomLeave
208
- 'room-topic' : WechatyEventListenerRoomTopic
209
- dong : WechatyEventListenerDong
210
- error : WechatyEventListenerError
211
- friendship : WechatyEventListenerFriendship
212
- heartbeat : WechatyEventListenerHeartbeat
213
- login : WechatyEventListenerLogin
214
- logout : WechatyEventListenerLogout
215
- message : WechatyEventListenerMessage
216
- post : WechatyEventListenerPost
217
- puppet : WechatyEventListenerPuppet
218
- ready : WechatyEventListenerReady
219
- scan : WechatyEventListenerScan
220
- start : WechatyEventListenerStartStop
221
- stop : WechatyEventListenerStartStop
223
+ 'room-invite' : WechatyEventListenerRoomInvite
224
+ 'room-join' : WechatyEventListenerRoomJoin
225
+ 'room-leave' : WechatyEventListenerRoomLeave
226
+ 'room-topic' : WechatyEventListenerRoomTopic
227
+ dong : WechatyEventListenerDong
228
+ error : WechatyEventListenerError
229
+ friendship : WechatyEventListenerFriendship
230
+ heartbeat : WechatyEventListenerHeartbeat
231
+ login : WechatyEventListenerLogin
232
+ logout : WechatyEventListenerLogout
233
+ message : WechatyEventListenerMessage
234
+ post : WechatyEventListenerPost
235
+ puppet : WechatyEventListenerPuppet
236
+ ready : WechatyEventListenerReady
237
+ scan : WechatyEventListenerScan
238
+ start : WechatyEventListenerStartStop
239
+ stop : WechatyEventListenerStartStop
240
+ update : WechatyEventListenerUpdate
241
+ 'contact-tag-add' : WechatyEventListenerContactTagAdd
242
+ 'contact-tag-remove' : WechatyEventListenerContactTagRemove
243
+ 'contact-name' : WechatyEventListenerContactName
244
+ 'contact-alias' : WechatyEventListenerContactAlias
245
+ 'contact-phone' : WechatyEventListenerContactPhone
246
+ 'contact-description': WechatyEventListenerContactDescription
247
+ 'room-owner' : WechatyEventListenerRoomOwner
222
248
  }
223
249
 
224
250
  const WechatyEventEmitter = EventEmitter as any as new () => TypedEventEmitter<
@@ -243,8 +269,16 @@ export type {
243
269
  WechatyEventListenerRoomJoin,
244
270
  WechatyEventListenerRoomLeave,
245
271
  WechatyEventListenerRoomTopic,
272
+ WechatyEventListenerRoomOwner,
246
273
  WechatyEventListenerScan,
247
274
  WechatyEventListenerStartStop,
275
+ WechatyEventListenerUpdate,
276
+ WechatyEventListenerContactTagAdd,
277
+ WechatyEventListenerContactTagRemove,
278
+ WechatyEventListenerContactName,
279
+ WechatyEventListenerContactAlias,
280
+ WechatyEventListenerContactPhone,
281
+ WechatyEventListenerContactDescription,
248
282
  }
249
283
  export {
250
284
  WechatyEventEmitter,
@@ -207,28 +207,6 @@ class ContactMixin extends MixinBase implements SayableSayer {
207
207
  log.verbose('Contact', 'static delete(%s)', contact.id)
208
208
  }
209
209
 
210
- /**
211
- * Get tags for all contact
212
- *
213
- * @static
214
- * @returns {Promise<TagInterface[]>}
215
- * @example
216
- * const tags = await wechaty.Contact.tags()
217
- */
218
- static async tags (): Promise<TagInterface[]> {
219
- log.verbose('Contact', 'static tags() for %s', this)
220
-
221
- try {
222
- const tagIdList = await this.wechaty.puppet.tagTagList()
223
- const tagList = tagIdList.map(id => this.wechaty.Tag.create(id))
224
- return tagList
225
- } catch (e) {
226
- this.wechaty.emitError(e)
227
- log.error('Contact', 'static tags() exception: %s', (e as Error).message)
228
- return []
229
- }
230
- }
231
-
232
210
  /**
233
211
  *
234
212
  * Instance properties
@@ -674,8 +652,13 @@ class ContactMixin extends MixinBase implements SayableSayer {
674
652
 
675
653
  try {
676
654
  const tagPayloadList = await this.wechaty.puppet.tagContactTagList(this.id)
677
- const tagList = tagPayloadList.map(tag => this.wechaty.Tag.create(tag))
678
- return tagList
655
+
656
+ let tagList = tagPayloadList.map(tag => this.wechaty.Tag.load(tag))
657
+ if (tagList.some(tag => typeof tag === 'undefined')) {
658
+ await this.wechaty.Tag.sync()
659
+ tagList = tagPayloadList.map(tag => this.wechaty.Tag.load(tag))
660
+ }
661
+ return tagList.filter(tag => !!tag) as TagInterface[]
679
662
  } catch (e) {
680
663
  this.wechaty.emitError(e)
681
664
  log.error('Contact', 'tags() exception: %s', (e as Error).message)
@@ -687,10 +670,20 @@ class ContactMixin extends MixinBase implements SayableSayer {
687
670
  * Add a Tag
688
671
  */
689
672
 
690
- async tag (tag: TagInterface): Promise<void> {
691
- log.verbose('Contact', 'tag(%s) for %s', tag, this)
673
+ async tag (tags: TagInterface | TagInterface[]): Promise<void> {
674
+ log.verbose('Contact', 'tag(%s) for %s', JSON.stringify(tags), this)
692
675
 
693
- await this.wechaty.puppet.tagContactTagAdd(tag.groupId(), tag.id(), this.id)
676
+ if (!Array.isArray(tags)) {
677
+ tags = [tags]
678
+ }
679
+
680
+ const tagIdentifiers = tags.map(tag => {
681
+ return {
682
+ id: tag.id(),
683
+ groupId: tag.groupId(),
684
+ }
685
+ })
686
+ await this.wechaty.puppet.tagContactTagAdd(tagIdentifiers, [this.id])
694
687
 
695
688
  }
696
689
 
@@ -698,10 +691,21 @@ class ContactMixin extends MixinBase implements SayableSayer {
698
691
  * Remove a Tag
699
692
  */
700
693
 
701
- async tagRemove (tag: TagInterface): Promise<void> {
702
- log.verbose('Contact', 'tagRemove(%s) for %s', tag, this)
694
+ async tagRemove (tags: TagInterface | TagInterface[]): Promise<void> {
695
+ log.verbose('Contact', 'tagRemove(%s) for %s', JSON.stringify(tags), this)
696
+
697
+ if (!Array.isArray(tags)) {
698
+ tags = [tags]
699
+ }
700
+
701
+ const tagIdentifiers = tags.map(tag => {
702
+ return {
703
+ id: tag.id(),
704
+ groupId: tag.groupId(),
705
+ }
706
+ })
703
707
 
704
- await this.wechaty.puppet.tagContactTagRemove(tag.groupId(), tag.id(), this.id)
708
+ await this.wechaty.puppet.tagContactTagRemove(tagIdentifiers, [this.id])
705
709
 
706
710
  }
707
711
 
@@ -728,9 +728,16 @@ class RoomMixin extends MixinBase implements SayableSayer {
728
728
  * }
729
729
  * }
730
730
  */
731
- async remove (contact: ContactInterface): Promise<void> {
732
- log.verbose('Room', 'del(%s)', contact)
733
- await this.wechaty.puppet.roomDel(this.id, contact.id)
731
+ async remove (contacts: ContactInterface | ContactInterface[]): Promise<void> {
732
+ log.verbose('Room', 'del(%s)', contacts)
733
+
734
+ let contactIds: string[]
735
+ if (Array.isArray(contacts)) {
736
+ contactIds = contacts.map(c => c.id)
737
+ } else {
738
+ contactIds = [contacts.id]
739
+ }
740
+ await this.wechaty.puppet.roomDel(this.id, contactIds)
734
741
  // this.delLocal(contact)
735
742
  }
736
743
 
@@ -738,7 +745,7 @@ class RoomMixin extends MixinBase implements SayableSayer {
738
745
  * Huan(202106): will be removed after Dec 31, 2023
739
746
  * @deprecated use remove(contact) instead.
740
747
  */
741
- async del (contact: ContactImpl): Promise<void> {
748
+ async del (contact: ContactImpl | ContactImpl[]): Promise<void> {
742
749
  log.warn('Room', 'del() is DEPRECATED, use remove() instead.\n%s', new Error().stack)
743
750
  return this.remove(contact)
744
751
  }
@@ -26,6 +26,7 @@ import { validationMixin } from '../user-mixins/validation.js'
26
26
  import {
27
27
  wechatifyMixinBase,
28
28
  } from '../user-mixins/wechatify.js'
29
+ import type { TagInterface } from './tag.js'
29
30
 
30
31
  class TagGroupMixin extends wechatifyMixinBase() {
31
32
 
@@ -58,13 +59,52 @@ class TagGroupMixin extends wechatifyMixinBase() {
58
59
  return this.payload.name
59
60
  }
60
61
 
62
+ private static pool: TagGroupInterface[] = []
63
+
64
+ static async list (forceSync = false): Promise<TagGroupInterface[]> {
65
+ log.verbose('TagGroup', 'list(%s)', forceSync)
66
+
67
+ if (this.pool.length > 0 && !forceSync) {
68
+ return this.pool
69
+ }
70
+
71
+ try {
72
+ const payloads = await this.wechaty.puppet.tagGroupList()
73
+ this.pool = payloads.map(payload => new this(payload))
74
+ return this.pool
75
+ } catch (e) {
76
+ this.wechaty.emitError(e)
77
+ log.error('TagGroup', 'list() exception: %s', (e as Error).message)
78
+ return []
79
+ }
80
+ }
81
+
82
+ static async sync (): Promise<void> {
83
+ log.verbose('TagGroup', 'sync()')
84
+
85
+ await this.list(true)
86
+ }
87
+
88
+ static load (tagGroupId: string): TagGroupInterface | undefined {
89
+ log.verbose('TagGroup', 'load(%s)', tagGroupId)
90
+
91
+ for (const item of this.pool) {
92
+ if (item.id() === tagGroupId) {
93
+ return item
94
+ }
95
+ }
96
+ return undefined
97
+ }
98
+
61
99
  static async createTagGroup (name: string): Promise<TagGroupInterface | void> {
62
100
  log.verbose('TagGroup', 'createTagGroup(%s, %s)', name)
63
101
 
64
102
  try {
65
103
  const payload = await this.wechaty.puppet.tagGroupAdd(name)
66
104
  if (payload) {
67
- return new this(payload)
105
+ const newTagGroup = new this(payload)
106
+ this.pool.push(newTagGroup)
107
+ return newTagGroup
68
108
  }
69
109
  } catch (e) {
70
110
  this.wechaty.emitError(e)
@@ -77,23 +117,19 @@ class TagGroupMixin extends wechatifyMixinBase() {
77
117
 
78
118
  try {
79
119
  await this.wechaty.puppet.tagGroupDelete(tagGroup.id())
120
+ this.pool.splice(this.pool.indexOf(tagGroup), 1)
80
121
  } catch (e) {
81
122
  this.wechaty.emitError(e)
82
- log.error('Contact', 'createTag() exception: %s', (e as Error).message)
123
+ log.error('TagGroup', 'deleteTagGroup() exception: %s', (e as Error).message)
83
124
  }
84
125
  }
85
126
 
86
- static async list (): Promise<TagGroupInterface[]> {
87
- log.verbose('TagGroup', 'list()')
127
+ async tags (): Promise<TagInterface[]> {
128
+ return (await this.wechaty.Tag.list()).filter(tag => tag.groupId() === this.id())
129
+ }
88
130
 
89
- try {
90
- const payloads = await this.wechaty.puppet.tagGroupList()
91
- return payloads.map(payload => new this(payload))
92
- } catch (e) {
93
- this.wechaty.emitError(e)
94
- log.error('TagGroup', 'list() exception: %s', (e as Error).message)
95
- return []
96
- }
131
+ override toString () {
132
+ return `<TagGroup#${this.name() || this.id()}>`
97
133
  }
98
134
 
99
135
  }
@@ -18,6 +18,7 @@
18
18
  *
19
19
  */
20
20
  import type * as PUPPET from '@juzi/wechaty-puppet'
21
+ import type { TagIdentifier } from '@juzi/wechaty-puppet/filters'
21
22
 
22
23
  import type { Constructor } from 'clone-class'
23
24
  import { log } from '../config.js'
@@ -27,6 +28,7 @@ import {
27
28
  wechatifyMixinBase,
28
29
  } from '../user-mixins/wechatify.js'
29
30
  import type { ContactInterface } from './contact.js'
31
+ import type { TagGroupInterface } from './tag-group.js'
30
32
 
31
33
  class TagMixin extends wechatifyMixinBase() {
32
34
 
@@ -67,12 +69,23 @@ class TagMixin extends wechatifyMixinBase() {
67
69
  return this.payload.name
68
70
  }
69
71
 
70
- static async list (): Promise<TagInterface[]> {
71
- log.verbose('Tag', 'list()')
72
+ group (): TagGroupInterface | undefined {
73
+ return this.wechaty.TagGroup.load(this.groupId())
74
+ }
75
+
76
+ private static pool: TagInterface[] = []
77
+
78
+ static async list (forceSync = false): Promise<TagInterface[]> {
79
+ log.verbose('Tag', 'list(%s)', forceSync)
80
+
81
+ if (this.pool.length > 0 && !forceSync) {
82
+ return this.pool
83
+ }
72
84
 
73
85
  try {
74
86
  const payloads = await this.wechaty.puppet.tagTagList()
75
- return payloads.map(payload => new this(payload))
87
+ this.pool = payloads.map(payload => new this(payload))
88
+ return this.pool
76
89
  } catch (e) {
77
90
  this.wechaty.emitError(e)
78
91
  log.error('Tag', 'list() exception: %s', (e as Error).message)
@@ -80,28 +93,54 @@ class TagMixin extends wechatifyMixinBase() {
80
93
  }
81
94
  }
82
95
 
96
+ static async sync (): Promise<void> {
97
+ log.verbose('Tag', 'sync()')
98
+
99
+ await this.list(true)
100
+ }
101
+
102
+ static load (tag: TagIdentifier): TagInterface | undefined {
103
+ log.verbose('TagGroup', 'load(%s)', tag)
104
+
105
+ for (const item of this.pool) {
106
+ if (item.id() === tag.id && (item.groupId() === tag.groupId || (!item.groupId() && !tag.groupId))) {
107
+ return item
108
+ }
109
+ }
110
+ return undefined
111
+ }
112
+
83
113
  async contactList (): Promise<ContactInterface[]> {
84
- log.verbose('Tag', 'contactList() for tag id: %s', this.id())
114
+ log.verbose('Tag', 'contactList() for tag : %s', this)
85
115
 
86
- const contactIds = await this.wechaty.puppet.tagTagContactList(this.groupId(), this.id())
116
+ const tag = { id: this.id(), groupId: this.groupId() } as TagIdentifier
117
+ const contactIds = await this.wechaty.puppet.tagTagContactList(tag)
87
118
  const contactPromises = contactIds.map(id => this.wechaty.Contact.find({ id })) as Promise<ContactInterface>[]
88
119
  return Promise.all(contactPromises)
89
120
  }
90
121
 
91
- async tag (contact: ContactInterface): Promise<void> {
92
- log.verbose('Tag', 'tag(%s) for tag id: %s', contact, this.id())
122
+ async tag (contacts: ContactInterface | ContactInterface[]): Promise<void> {
123
+ log.verbose('Tag', 'tag(%s) for tag : %s', contacts, this)
93
124
 
94
- const contactId = contact.id
95
- await this.wechaty.puppet.tagContactTagAdd(this.groupId(), this.id(), contactId)
125
+ const tag = { id: this.id(), groupId: this.groupId() } as TagIdentifier
126
+ let contactIds: string[]
127
+ if (Array.isArray(contacts)) {
128
+ contactIds = contacts.map(c => c.id)
129
+ } else {
130
+ contactIds = [contacts.id]
131
+ }
132
+ await this.wechaty.puppet.tagContactTagAdd([tag], contactIds)
96
133
  }
97
134
 
98
- static async createTag (tagGroupId: string | undefined, name: string): Promise<TagInterface | void> {
99
- log.verbose('Tag', 'createTag(%s, %s)', tagGroupId, name)
135
+ static async createTag (name: string, tagGroup?: TagGroupInterface): Promise<TagInterface | void> {
136
+ log.verbose('Tag', 'createTag(%s, %s)', tagGroup, name)
100
137
 
101
138
  try {
102
- const payload = await this.wechaty.puppet.tagTagAdd(tagGroupId, name)
139
+ const payload = await this.wechaty.puppet.tagTagAdd(name, tagGroup?.name())
103
140
  if (payload) {
104
- return new this(payload)
141
+ const newTag = new this(payload)
142
+ this.pool.push(newTag)
143
+ return newTag
105
144
  }
106
145
  } catch (e) {
107
146
  this.wechaty.emitError(e)
@@ -109,17 +148,24 @@ class TagMixin extends wechatifyMixinBase() {
109
148
  }
110
149
  }
111
150
 
112
- static async deleteTag (tag: TagInterface): Promise<void> {
113
- log.verbose('Tag', 'deleteTag(%s, %s)', tag)
151
+ static async deleteTag (tagInstance: TagInterface): Promise<void> {
152
+ log.verbose('Tag', 'deleteTag(%s, %s)', tagInstance)
153
+
154
+ const tag = { id: tagInstance.id(), groupId: tagInstance.groupId() } as TagIdentifier
114
155
 
115
156
  try {
116
- await this.wechaty.puppet.tagTagDelete(tag.groupId(), tag.id())
157
+ await this.wechaty.puppet.tagTagDelete(tag)
158
+ this.pool.splice(this.pool.indexOf(tagInstance), 1)
117
159
  } catch (e) {
118
160
  this.wechaty.emitError(e)
119
- log.error('Contact', 'deleteTag() exception: %s', (e as Error).message)
161
+ log.error('Tag', 'deleteTag() exception: %s', (e as Error).message)
120
162
  }
121
163
  }
122
164
 
165
+ override toString () {
166
+ return `<Tag#${this.name() || this.id()}>`
167
+ }
168
+
123
169
  }
124
170
 
125
171
  class TagImpl extends validationMixin(TagMixin)<TagInterface>() {}
@@ -11,7 +11,7 @@ import type {
11
11
  StateSwitchInterface,
12
12
  } from 'state-switch'
13
13
 
14
- import { config } from '../config.js'
14
+ import { config, FOUR_PER_EM_SPACE } from '../config.js'
15
15
  import { timestampToDate } from '../pure-functions/timestamp-to-date.js'
16
16
  import type {
17
17
  ContactImpl,
@@ -25,6 +25,8 @@ import type {
25
25
 
26
26
  import type { GErrorMixin } from './gerror-mixin.js'
27
27
  import type { IoMixin } from './io-mixin.js'
28
+ import { ContactImportantFields, ContactUpdatableValuePair, InfoUpdateInterface, RoomImportantFields, RoomUpdatableValuePair } from '../schemas/update.js'
29
+ import { diffPayload } from '../pure-functions/update.js'
28
30
 
29
31
  const PUPPET_MEMORY_NAME = 'puppet'
30
32
 
@@ -278,7 +280,7 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
278
280
  return
279
281
  }
280
282
 
281
- this.emit('message', msg)
283
+ this.emit('message', 'msg')
282
284
 
283
285
  const room = msg.room()
284
286
  const listener = msg.listener()
@@ -438,12 +440,94 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
438
440
  switch (payloadType) {
439
441
  case PUPPET.types.Payload.Contact: {
440
442
  const contact = await this.Contact.find({ id: payloadId }) as unknown as undefined | ContactImpl
443
+ const oldPayload = JSON.parse(JSON.stringify(contact?.payload || {}))
441
444
  await contact?.ready(true)
445
+ const newPayload = JSON.parse(JSON.stringify(contact?.payload || {}))
446
+
447
+ const differences = diffPayload<PUPPET.payloads.Contact>(oldPayload, newPayload)
448
+ const importangDifferences = differences.filter(ele => ele && ContactImportantFields.some(key => key === ele.key))
449
+ const regularDifferences = differences.filter(ele => ele && !ContactImportantFields.some(key => key === ele.key)) as ContactUpdatableValuePair[]
450
+ if (regularDifferences.length > 0) {
451
+ const updateEvent: InfoUpdateInterface = {
452
+ type: payloadType,
453
+ id: payloadId,
454
+ updates: regularDifferences,
455
+ }
456
+ this.emit('update', updateEvent)
457
+ contact?.emit('update', updateEvent)
458
+ }
459
+ for (const difference of importangDifferences) {
460
+ switch (difference?.key) {
461
+ case 'tags': {
462
+ const oldTagsSet = new Set(difference.oldValue?.map(ele => ele.groupId + FOUR_PER_EM_SPACE + ele.id))
463
+ const newTagsSet = new Set(difference.newValue?.map(ele => ele.groupId + FOUR_PER_EM_SPACE + ele.id))
464
+ const addedTags = difference.newValue?.filter(ele => !oldTagsSet.has(ele.groupId + FOUR_PER_EM_SPACE + ele.id)).map(ele => this.Tag.load(ele)) || []
465
+ const removedTags = difference.oldValue?.filter(ele => !newTagsSet.has(ele.groupId + FOUR_PER_EM_SPACE + ele.id)).map(ele => this.Tag.load(ele)) || []
466
+ if (addedTags.length > 0) {
467
+ this.emit('contact-tag-add', contact, addedTags)
468
+ }
469
+ if (removedTags.length > 0) {
470
+ this.emit('contact-tag-removed', contact, removedTags)
471
+ }
472
+ break
473
+ }
474
+ case 'name': {
475
+ this.emit('contact-name', contact, difference.newValue || '', difference.oldValue || '')
476
+ contact?.emit('name', difference.newValue || '', difference.oldValue || '')
477
+ break
478
+ }
479
+ case 'alias': {
480
+ this.emit('contact-alias', contact, difference.newValue || '', difference.oldValue || '')
481
+ contact?.emit('alias', difference.newValue || '', difference.oldValue || '')
482
+ break
483
+ }
484
+ case 'phone': {
485
+ this.emit('contact-phone', contact, difference.newValue || [], difference.oldValue || [])
486
+ contact?.emit('phone', difference.newValue || [], difference.oldValue || [])
487
+ break
488
+ }
489
+ case 'description': {
490
+ this.emit('contact-description', contact, difference.newValue || '', difference.oldValue || '')
491
+ contact?.emit('description', difference.newValue || '', difference.oldValue || '')
492
+ break
493
+ }
494
+ default:
495
+ log.warn('WechatyPuppetMixin', 'puppet dirty unsupported difference type: %s', JSON.stringify(difference))
496
+ }
497
+ }
442
498
  break
443
499
  }
444
500
  case PUPPET.types.Payload.Room: {
445
501
  const room = await this.Room.find({ id: payloadId }) as unknown as undefined | RoomImpl
502
+ const oldPayload = JSON.parse(JSON.stringify(room?.payload || {}))
446
503
  await room?.ready(true)
504
+ const newPayload = JSON.parse(JSON.stringify(room?.payload || {}))
505
+
506
+ const differences = diffPayload<PUPPET.payloads.Room>(oldPayload, newPayload)
507
+ const importangDifferences = differences.filter(ele => ele && RoomImportantFields.some(key => key === ele.key))
508
+ const regularDifferences = differences.filter(ele => ele && !RoomImportantFields.some(key => key === ele.key)) as RoomUpdatableValuePair[]
509
+ if (regularDifferences.length > 0) {
510
+ const updateEvent: InfoUpdateInterface = {
511
+ type: payloadType,
512
+ id: payloadId,
513
+ updates: regularDifferences,
514
+ }
515
+ this.emit('update', updateEvent)
516
+ room?.emit('update', updateEvent)
517
+ }
518
+ for (const difference of importangDifferences) {
519
+ switch (difference?.key) {
520
+ case 'ownerId': {
521
+ const oldOwner = (await this.Contact.find({ id: difference.oldValue }))!
522
+ const newOwner = (await this.Contact.find({ id: difference.newValue }))!
523
+ this.emit('room-owner', room, newOwner, oldOwner)
524
+ room?.emit('owner', newOwner, oldOwner)
525
+ break
526
+ }
527
+ default:
528
+ log.warn('WechatyPuppetMixin', 'puppet dirty unsupported difference type: %s', JSON.stringify(difference))
529
+ }
530
+ }
447
531
  break
448
532
  }
449
533
  case PUPPET.types.Payload.RoomMember: {