@juzi/wechaty 1.0.23 → 1.0.24

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 (77) hide show
  1. package/dist/cjs/src/config.d.ts +2 -0
  2. package/dist/cjs/src/config.d.ts.map +1 -1
  3. package/dist/cjs/src/config.js +3 -1
  4. package/dist/cjs/src/config.js.map +1 -1
  5. package/dist/cjs/src/package-json.js +4 -4
  6. package/dist/cjs/src/pure-functions/update.d.ts +1 -0
  7. package/dist/cjs/src/pure-functions/update.d.ts.map +1 -1
  8. package/dist/cjs/src/pure-functions/update.js +12 -1
  9. package/dist/cjs/src/pure-functions/update.js.map +1 -1
  10. package/dist/cjs/src/schemas/wechaty-events.d.ts +5 -2
  11. package/dist/cjs/src/schemas/wechaty-events.d.ts.map +1 -1
  12. package/dist/cjs/src/schemas/wechaty-events.js.map +1 -1
  13. package/dist/cjs/src/user-modules/contact.d.ts.map +1 -1
  14. package/dist/cjs/src/user-modules/contact.js +7 -10
  15. package/dist/cjs/src/user-modules/contact.js.map +1 -1
  16. package/dist/cjs/src/user-modules/tag-group.d.ts +54 -20
  17. package/dist/cjs/src/user-modules/tag-group.d.ts.map +1 -1
  18. package/dist/cjs/src/user-modules/tag-group.js +103 -44
  19. package/dist/cjs/src/user-modules/tag-group.js.map +1 -1
  20. package/dist/cjs/src/user-modules/tag.d.ts +57 -22
  21. package/dist/cjs/src/user-modules/tag.d.ts.map +1 -1
  22. package/dist/cjs/src/user-modules/tag.js +140 -48
  23. package/dist/cjs/src/user-modules/tag.js.map +1 -1
  24. package/dist/cjs/src/wechaty/wechaty-base.d.ts +7 -7
  25. package/dist/cjs/src/wechaty-mixins/gerror-mixin.d.ts +1 -1
  26. package/dist/cjs/src/wechaty-mixins/io-mixin.d.ts +2 -2
  27. package/dist/cjs/src/wechaty-mixins/login-mixin.d.ts +5 -5
  28. package/dist/cjs/src/wechaty-mixins/misc-mixin.d.ts +5 -5
  29. package/dist/cjs/src/wechaty-mixins/plugin-mixin.d.ts +6 -6
  30. package/dist/cjs/src/wechaty-mixins/puppet-mixin.d.ts +4 -4
  31. package/dist/cjs/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  32. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js +88 -8
  33. package/dist/cjs/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  34. package/dist/cjs/src/wechaty-mixins/wechatify-user-module-mixin.d.ts +1 -1
  35. package/dist/esm/src/config.d.ts +2 -0
  36. package/dist/esm/src/config.d.ts.map +1 -1
  37. package/dist/esm/src/config.js +2 -0
  38. package/dist/esm/src/config.js.map +1 -1
  39. package/dist/esm/src/package-json.js +4 -4
  40. package/dist/esm/src/pure-functions/update.d.ts +1 -0
  41. package/dist/esm/src/pure-functions/update.d.ts.map +1 -1
  42. package/dist/esm/src/pure-functions/update.js +10 -0
  43. package/dist/esm/src/pure-functions/update.js.map +1 -1
  44. package/dist/esm/src/schemas/wechaty-events.d.ts +5 -2
  45. package/dist/esm/src/schemas/wechaty-events.d.ts.map +1 -1
  46. package/dist/esm/src/schemas/wechaty-events.js.map +1 -1
  47. package/dist/esm/src/user-modules/contact.d.ts.map +1 -1
  48. package/dist/esm/src/user-modules/contact.js +7 -10
  49. package/dist/esm/src/user-modules/contact.js.map +1 -1
  50. package/dist/esm/src/user-modules/tag-group.d.ts +54 -20
  51. package/dist/esm/src/user-modules/tag-group.d.ts.map +1 -1
  52. package/dist/esm/src/user-modules/tag-group.js +104 -45
  53. package/dist/esm/src/user-modules/tag-group.js.map +1 -1
  54. package/dist/esm/src/user-modules/tag.d.ts +57 -22
  55. package/dist/esm/src/user-modules/tag.d.ts.map +1 -1
  56. package/dist/esm/src/user-modules/tag.js +119 -50
  57. package/dist/esm/src/user-modules/tag.js.map +1 -1
  58. package/dist/esm/src/wechaty/wechaty-base.d.ts +7 -7
  59. package/dist/esm/src/wechaty-mixins/gerror-mixin.d.ts +1 -1
  60. package/dist/esm/src/wechaty-mixins/io-mixin.d.ts +2 -2
  61. package/dist/esm/src/wechaty-mixins/login-mixin.d.ts +5 -5
  62. package/dist/esm/src/wechaty-mixins/misc-mixin.d.ts +5 -5
  63. package/dist/esm/src/wechaty-mixins/plugin-mixin.d.ts +6 -6
  64. package/dist/esm/src/wechaty-mixins/puppet-mixin.d.ts +4 -4
  65. package/dist/esm/src/wechaty-mixins/puppet-mixin.d.ts.map +1 -1
  66. package/dist/esm/src/wechaty-mixins/puppet-mixin.js +90 -10
  67. package/dist/esm/src/wechaty-mixins/puppet-mixin.js.map +1 -1
  68. package/dist/esm/src/wechaty-mixins/wechatify-user-module-mixin.d.ts +1 -1
  69. package/package.json +3 -3
  70. package/src/config.ts +3 -0
  71. package/src/package-json.ts +4 -4
  72. package/src/pure-functions/update.ts +11 -0
  73. package/src/schemas/wechaty-events.ts +4 -0
  74. package/src/user-modules/contact.ts +7 -10
  75. package/src/user-modules/tag-group.ts +135 -53
  76. package/src/user-modules/tag.ts +124 -57
  77. package/src/wechaty-mixins/puppet-mixin.ts +106 -10
@@ -17,75 +17,84 @@
17
17
  * limitations under the License.
18
18
  *
19
19
  */
20
- import type * as PUPPET from '@juzi/wechaty-puppet'
20
+ import * as PUPPET from '@juzi/wechaty-puppet'
21
21
  import type { TagIdentifier } from '@juzi/wechaty-puppet/filters'
22
+ import { getTagKey } from '@juzi/wechaty-puppet/helpers'
22
23
 
23
24
  import type { Constructor } from 'clone-class'
24
- import { log } from '../config.js'
25
+ import { concurrencyExecuter } from 'rx-queue'
26
+ import { FOUR_PER_EM_SPACE, log } from '../config.js'
27
+ import { poolifyMixin } from '../user-mixins/poolify.js'
25
28
 
26
29
  import { validationMixin } from '../user-mixins/validation.js'
27
30
  import {
28
- wechatifyMixinBase,
31
+ wechatifyMixin,
29
32
  } from '../user-mixins/wechatify.js'
30
33
  import type { ContactInterface } from './contact.js'
31
34
  import type { TagGroupInterface } from './tag-group.js'
32
35
 
33
- class TagMixin extends wechatifyMixinBase() {
36
+ const MixinBase = wechatifyMixin(
37
+ poolifyMixin(
38
+ Object,
39
+ )<TagImplInterface>(),
40
+ )
41
+
42
+ class TagMixin extends MixinBase {
34
43
 
35
44
  /**
36
45
  *
37
- * Create
46
+ * Instance properties
47
+ * @ignore
38
48
  *
39
49
  */
40
- static create (payload: PUPPET.payloads.Tag): TagInterface {
41
- log.verbose('Tag', 'create()')
42
-
43
- return new this(payload)
44
- }
50
+ payload?: PUPPET.payloads.Tag
51
+ public readonly id: string
52
+ public readonly groupId?: string
45
53
 
46
54
  /**
47
55
  * @hideconstructor
48
56
  */
49
57
  constructor (
50
- public readonly payload: PUPPET.payloads.Tag,
58
+ public readonly key: string,
51
59
  ) {
52
60
  super()
61
+ this.groupId = this.key.split(FOUR_PER_EM_SPACE)[0]
62
+ this.id = this.key.split(FOUR_PER_EM_SPACE)[1]!
53
63
  log.silly('Tag', 'constructor()')
54
64
  }
55
65
 
56
- id (): string {
57
- return this.payload.id
58
- }
59
-
60
66
  type (): PUPPET.types.Tag {
61
- return this.payload.type
62
- }
63
-
64
- groupId (): string {
65
- return this.payload.groupId || ''
67
+ return (this.payload && this.payload.type) || PUPPET.types.Tag.Personal
66
68
  }
67
69
 
68
70
  name (): string {
69
- return this.payload.name
71
+ return (this.payload && this.payload.name) || ''
70
72
  }
71
73
 
72
- group (): TagGroupInterface | undefined {
73
- return this.wechaty.TagGroup.load(this.groupId())
74
+ async group (): Promise<TagGroupInterface | undefined> {
75
+ return this.groupId ? this.wechaty.TagGroup.find(this.groupId) : undefined
74
76
  }
75
77
 
76
- private static pool: TagInterface[] = []
77
-
78
78
  static async list (forceSync = false): Promise<TagInterface[]> {
79
79
  log.verbose('Tag', 'list(%s)', forceSync)
80
80
 
81
- if (this.pool.length > 0 && !forceSync) {
82
- return this.pool
83
- }
84
-
85
81
  try {
86
- const payloads = await this.wechaty.puppet.tagTagList()
87
- this.pool = payloads.map(payload => new this(payload))
88
- return this.pool
82
+ const tagIdentifierList = await this.wechaty.puppet.tagTagList()
83
+
84
+ const identifierToTag = async (identifier: TagIdentifier) => this.find(identifier).catch(e => this.wechaty.emitError(e))
85
+
86
+ const CONCURRENCY = 17
87
+ const tagIterator = concurrencyExecuter(CONCURRENCY)(identifierToTag)(tagIdentifierList)
88
+
89
+ const tagList: TagInterface[] = []
90
+ for await (const tag of tagIterator) {
91
+ if (tag) {
92
+ tagList.push(tag)
93
+ }
94
+ }
95
+
96
+ return tagList
97
+
89
98
  } catch (e) {
90
99
  this.wechaty.emitError(e)
91
100
  log.error('Tag', 'list() exception: %s', (e as Error).message)
@@ -93,27 +102,80 @@ class TagMixin extends wechatifyMixinBase() {
93
102
  }
94
103
  }
95
104
 
96
- static async sync (): Promise<void> {
97
- log.verbose('Tag', 'sync()')
105
+ static async find (identifier: TagIdentifier): Promise<TagInterface | undefined> {
106
+ log.silly('Tag', 'find(%s)', JSON.stringify(identifier))
107
+
108
+ const tag = (this.wechaty.Tag as any as typeof TagImpl).load(getTagKey(identifier))
98
109
 
99
- await this.list(true)
110
+ try {
111
+ await tag.ready()
112
+ } catch (e) {
113
+ this.wechaty.emitError(e)
114
+ return undefined
115
+ }
116
+ return tag
100
117
  }
101
118
 
102
- static load (tag: TagIdentifier): TagInterface | undefined {
103
- log.verbose('TagGroup', 'load(%s)', tag)
119
+ /**
120
+ * Force reload data for Tag, Sync data from low-level API again.
121
+ *
122
+ * @returns {Promise<this>}
123
+ * @example
124
+ * await tag.sync()
125
+ */
126
+ async sync (): Promise<void> {
127
+ await this.wechaty.puppet.tagPayloadDirty(getTagKey({
128
+ id: this.id,
129
+ groupId: this.groupId,
130
+ }))
131
+ await this.ready(true)
132
+ }
104
133
 
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
- }
134
+ /**
135
+ * @ignore
136
+ */
137
+ isReady (): boolean {
138
+ return !!(this.payload && this.payload.name)
139
+ }
140
+
141
+ /**
142
+ * `ready()` is For FrameWork ONLY!
143
+ *
144
+ * Please not to use `ready()` at the user land.
145
+ * If you want to sync data, use `sync()` instead.
146
+ *
147
+ * @ignore
148
+ */
149
+ async ready (
150
+ forceSync = false,
151
+ ): Promise<void> {
152
+ log.silly('Tag', 'ready() @ %s with Tag key="%s"', this.wechaty.puppet, this.key)
153
+
154
+ if (!forceSync && this.isReady()) { // already ready
155
+ log.silly('Tag', 'ready() isReady() true')
156
+ return
157
+ }
158
+
159
+ try {
160
+ this.payload = await this.wechaty.puppet.tagPayload({
161
+ id: this.id,
162
+ groupId: this.groupId,
163
+ })
164
+
165
+ } catch (e) {
166
+ this.wechaty.emitError(e)
167
+ log.verbose('Tag', 'ready() this.wechaty.puppet.tagPayload(%s) exception: %s',
168
+ this.id,
169
+ (e as Error).message,
170
+ )
171
+ throw e
109
172
  }
110
- return undefined
111
173
  }
112
174
 
113
175
  async contactList (): Promise<ContactInterface[]> {
114
176
  log.verbose('Tag', 'contactList() for tag : %s', this)
115
177
 
116
- const tag = { id: this.id(), groupId: this.groupId() } as TagIdentifier
178
+ const tag = { id: this.id, groupId: this.groupId } as TagIdentifier
117
179
  const contactIds = await this.wechaty.puppet.tagTagContactList(tag)
118
180
  const contactPromises = contactIds.map(id => this.wechaty.Contact.find({ id })) as Promise<ContactInterface>[]
119
181
  return Promise.all(contactPromises)
@@ -122,7 +184,7 @@ class TagMixin extends wechatifyMixinBase() {
122
184
  async tag (contacts: ContactInterface | ContactInterface[]): Promise<void> {
123
185
  log.verbose('Tag', 'tag(%s) for tag : %s', contacts, this)
124
186
 
125
- const tag = { id: this.id(), groupId: this.groupId() } as TagIdentifier
187
+ const tag = { id: this.id, groupId: this.groupId } as TagIdentifier
126
188
  let contactIds: string[]
127
189
  if (Array.isArray(contacts)) {
128
190
  contactIds = contacts.map(c => c.id)
@@ -136,26 +198,24 @@ class TagMixin extends wechatifyMixinBase() {
136
198
  log.verbose('Tag', 'createTag(%s, %s)', tagGroup, name)
137
199
 
138
200
  try {
139
- const payload = await this.wechaty.puppet.tagTagAdd(name, tagGroup?.name())
140
- if (payload) {
141
- const newTag = new this(payload)
142
- this.pool.push(newTag)
201
+ const tagIdentifier = await this.wechaty.puppet.tagTagAdd(name, tagGroup?.name())
202
+ if (tagIdentifier) {
203
+ const newTag = await this.find(tagIdentifier)
143
204
  return newTag
144
205
  }
145
206
  } catch (e) {
146
207
  this.wechaty.emitError(e)
147
- log.error('Contact', 'createTag() exception: %s', (e as Error).message)
208
+ log.error('Tag', 'createTag() exception: %s', (e as Error).message)
148
209
  }
149
210
  }
150
211
 
151
212
  static async deleteTag (tagInstance: TagInterface): Promise<void> {
152
213
  log.verbose('Tag', 'deleteTag(%s, %s)', tagInstance)
153
214
 
154
- const tag = { id: tagInstance.id(), groupId: tagInstance.groupId() } as TagIdentifier
215
+ const tagIdentifier = { id: tagInstance.id, groupId: tagInstance.groupId } as TagIdentifier
155
216
 
156
217
  try {
157
- await this.wechaty.puppet.tagTagDelete(tag)
158
- this.pool.splice(this.pool.indexOf(tagInstance), 1)
218
+ await this.wechaty.puppet.tagTagDelete(tagIdentifier)
159
219
  } catch (e) {
160
220
  this.wechaty.emitError(e)
161
221
  log.error('Tag', 'deleteTag() exception: %s', (e as Error).message)
@@ -163,21 +223,28 @@ class TagMixin extends wechatifyMixinBase() {
163
223
  }
164
224
 
165
225
  override toString () {
166
- return `<Tag#${this.name() || this.id()}>`
226
+ return `<Tag#${this.name() || this.id}>`
167
227
  }
168
228
 
169
229
  }
170
230
 
171
- class TagImpl extends validationMixin(TagMixin)<TagInterface>() {}
172
- interface TagInterface extends TagImpl {}
231
+ class TagImplBase extends validationMixin(TagMixin)<TagImplInterface>() {}
232
+ interface TagImplInterface extends TagImplBase {}
233
+
234
+ type TagProtectedProperty =
235
+ | 'ready'
236
+
237
+ type TagInterface = Omit<TagImplInterface, TagProtectedProperty>
238
+ class TagImpl extends validationMixin(TagImplBase)<TagInterface>() {}
173
239
 
174
240
  type TagConstructor = Constructor<
175
- TagInterface,
176
- typeof TagImpl
241
+ TagImplInterface,
242
+ Omit<typeof TagImpl, 'load'>
177
243
  >
178
244
 
179
245
  export type {
180
246
  TagConstructor,
247
+ TagProtectedProperty,
181
248
  TagInterface,
182
249
  }
183
250
  export {
@@ -11,12 +11,14 @@ import type {
11
11
  StateSwitchInterface,
12
12
  } from 'state-switch'
13
13
 
14
- import { config, FOUR_PER_EM_SPACE } from '../config.js'
14
+ import { config, PUPPET_PAYLOAD_SYNC_GAP, PUPPET_PAYLOAD_SYNC_MAX_RETRY } from '../config.js'
15
15
  import { timestampToDate } from '../pure-functions/timestamp-to-date.js'
16
16
  import type {
17
17
  ContactImpl,
18
18
  ContactInterface,
19
19
  RoomImpl,
20
+ TagGroupInterface,
21
+ TagInterface,
20
22
  } from '../user-modules/mod.js'
21
23
 
22
24
  import type {
@@ -26,7 +28,8 @@ import type {
26
28
  import type { GErrorMixin } from './gerror-mixin.js'
27
29
  import type { IoMixin } from './io-mixin.js'
28
30
  import { ContactImportantFields, ContactUpdatableValuePair, InfoUpdateInterface, RoomImportantFields, RoomUpdatableValuePair } from '../schemas/update.js'
29
- import { diffPayload } from '../pure-functions/update.js'
31
+ import { checkUntilChanged, diffPayload } from '../pure-functions/update.js'
32
+ import { getTagKey } from '@juzi/wechaty-puppet/helpers'
30
33
 
31
34
  const PUPPET_MEMORY_NAME = 'puppet'
32
35
 
@@ -427,6 +430,95 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
427
430
  })
428
431
  break
429
432
 
433
+ case 'tag':
434
+ puppet.on('tag', async payload => {
435
+ switch (payload.tagEventType) {
436
+ case PUPPET.types.TagEvent.TagCreate: {
437
+ const newTagPromises = payload.tagEventPayload.map(tag =>
438
+ this.Tag.find({
439
+ id: tag.tagId,
440
+ groupId: tag.tagGroupId,
441
+ }),
442
+ )
443
+ const newTags = await Promise.all(newTagPromises)
444
+ this.emit('tag', payload.tagEventType, newTags)
445
+ break
446
+ }
447
+ case PUPPET.types.TagEvent.TagDelete: {
448
+ const deletedTagPromises = payload.tagEventPayload.map(tag =>
449
+ this.Tag.find({
450
+ id: tag.tagId,
451
+ groupId: tag.tagGroupId,
452
+ }),
453
+ )
454
+ const deletedTags = await Promise.all(deletedTagPromises)
455
+ this.emit('tag', payload.tagEventType, deletedTags)
456
+ // TODO: bind tag-delete to tag instance
457
+ break
458
+ }
459
+ case PUPPET.types.TagEvent.TagRename: {
460
+ const renamedTagPromises = payload.tagEventPayload.map(tag =>
461
+ this.Tag.find({
462
+ id: tag.tagId,
463
+ groupId: tag.tagGroupId,
464
+ }),
465
+ )
466
+ const renamedTags = (await Promise.all(renamedTagPromises)) as TagInterface[]
467
+ await Promise.all(renamedTags.map(async tag => {
468
+ const oldName = tag.name()
469
+ const result = await checkUntilChanged(PUPPET_PAYLOAD_SYNC_GAP, PUPPET_PAYLOAD_SYNC_MAX_RETRY, async () => {
470
+ await tag.sync()
471
+ return tag.name() === oldName
472
+ })
473
+ if (!result) {
474
+ log.warn('WechatyPuppetMixin', 'tagRenameEvent still get old name after %s retries for tag %s', PUPPET_PAYLOAD_SYNC_MAX_RETRY, tag.key)
475
+ }
476
+ }))
477
+ this.emit('tag', payload.tagEventType, renamedTags)
478
+ // TODO: bind tag-rename to tag instance
479
+ break
480
+ }
481
+ case PUPPET.types.TagEvent.TagGroupCreate: {
482
+ const newTagGroupPromises = payload.tagEventPayload.map(tagGroup =>
483
+ this.TagGroup.find(tagGroup.tagGroupId),
484
+ )
485
+ const newTagGroups = await Promise.all(newTagGroupPromises)
486
+ this.emit('tag', payload.tagEventType, newTagGroups)
487
+ break
488
+ }
489
+ case PUPPET.types.TagEvent.TagGroupDelete: {
490
+ const deletedTagGroupPromises = payload.tagEventPayload.map(tagGroup =>
491
+ this.TagGroup.find(tagGroup.tagGroupId),
492
+ )
493
+ const deletedTagGroups = await Promise.all(deletedTagGroupPromises)
494
+ this.emit('tag', payload.tagEventType, deletedTagGroups)
495
+ break
496
+ // TODO: bind tagGroup-delete to tagGroup instance
497
+ }
498
+ case PUPPET.types.TagEvent.TagGroupRename: {
499
+ const renamedTagGroupPromises = payload.tagEventPayload.map(tagGroup =>
500
+ this.TagGroup.find(tagGroup.tagGroupId),
501
+ )
502
+ const renamedTagGroups = (await Promise.all(renamedTagGroupPromises)) as TagGroupInterface[]
503
+ await Promise.all(renamedTagGroups.map(async tagGroup => {
504
+ const oldName = tagGroup.name()
505
+ const result = await checkUntilChanged(PUPPET_PAYLOAD_SYNC_GAP, PUPPET_PAYLOAD_SYNC_MAX_RETRY, async () => {
506
+ await tagGroup.sync()
507
+ return tagGroup.name() === oldName
508
+ })
509
+ if (!result) {
510
+ log.warn('WechatyPuppetMixin', 'tagGroupRenameEvent still get old name after %s retries for tagGroup %s', PUPPET_PAYLOAD_SYNC_MAX_RETRY, tagGroup.id)
511
+ }
512
+ }))
513
+ this.emit('tag', payload.tagEventType, renamedTagGroups)
514
+ // TODO: bind tagGroup-rename to tagGroup instance
515
+ break
516
+ }
517
+ }
518
+
519
+ })
520
+ break
521
+
430
522
  case 'reset':
431
523
  // Do not propagation `reset` event from puppet
432
524
  break
@@ -445,7 +537,7 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
445
537
  const newPayload = JSON.parse(JSON.stringify(contact?.payload || {}))
446
538
 
447
539
  const differences = diffPayload<PUPPET.payloads.Contact>(oldPayload, newPayload)
448
- const importangDifferences = differences.filter(ele => ele && ContactImportantFields.some(key => key === ele.key))
540
+ const importantDifferences = differences.filter(ele => ele && ContactImportantFields.some(key => key === ele.key))
449
541
  const regularDifferences = differences.filter(ele => ele && !ContactImportantFields.some(key => key === ele.key)) as ContactUpdatableValuePair[]
450
542
  if (regularDifferences.length > 0) {
451
543
  const updateEvent: InfoUpdateInterface = {
@@ -456,13 +548,13 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
456
548
  this.emit('update', updateEvent)
457
549
  contact?.emit('update', updateEvent)
458
550
  }
459
- for (const difference of importangDifferences) {
551
+ for (const difference of importantDifferences) {
460
552
  switch (difference?.key) {
461
553
  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)) || []
554
+ const oldTagsSet = new Set(difference.oldValue?.map(ele => getTagKey(ele)))
555
+ const newTagsSet = new Set(difference.newValue?.map(ele => getTagKey(ele)))
556
+ const addedTags = difference.newValue?.filter(ele => !oldTagsSet.has(getTagKey(ele))).map(ele => this.Tag.find(ele)) || []
557
+ const removedTags = difference.oldValue?.filter(ele => !newTagsSet.has(getTagKey(ele))).map(ele => this.Tag.find(ele)) || []
466
558
  if (addedTags.length > 0) {
467
559
  this.emit('contact-tag-add', contact, addedTags)
468
560
  }
@@ -504,7 +596,7 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
504
596
  const newPayload = JSON.parse(JSON.stringify(room?.payload || {}))
505
597
 
506
598
  const differences = diffPayload<PUPPET.payloads.Room>(oldPayload, newPayload)
507
- const importangDifferences = differences.filter(ele => ele && RoomImportantFields.some(key => key === ele.key))
599
+ const importantDifferences = differences.filter(ele => ele && RoomImportantFields.some(key => key === ele.key))
508
600
  const regularDifferences = differences.filter(ele => ele && !RoomImportantFields.some(key => key === ele.key)) as RoomUpdatableValuePair[]
509
601
  if (regularDifferences.length > 0) {
510
602
  const updateEvent: InfoUpdateInterface = {
@@ -515,7 +607,7 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
515
607
  this.emit('update', updateEvent)
516
608
  room?.emit('update', updateEvent)
517
609
  }
518
- for (const difference of importangDifferences) {
610
+ for (const difference of importantDifferences) {
519
611
  switch (difference?.key) {
520
612
  case 'ownerId': {
521
613
  const oldOwner = (await this.Contact.find({ id: difference.oldValue }))!
@@ -545,6 +637,10 @@ const puppetMixin = <MixinBase extends WechatifyUserModuleMixin & GErrorMixin &
545
637
  case PUPPET.types.Payload.Message:
546
638
  // Message does not need to dirty (?)
547
639
  break
640
+ case PUPPET.types.Payload.Tag:
641
+ break
642
+ case PUPPET.types.Payload.TagGroup:
643
+ break
548
644
 
549
645
  case PUPPET.types.Payload.Unspecified:
550
646
  default: