diff --git a/package.json b/package.json index 04f3d92336141ca7d439151d82a0b89ef8c0cff3..d9804ba0e710873fa388395dc34cc55585562259 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "express": "^4.16.3", "file-box": "^0.6.10", "hot-import": "^0.2.1", + "lru-cache": "^4.1.3", "mime": "^2.2.0", "normalize-package-data": "^2.4.0", "promise-retry": "^1.1.1", @@ -124,6 +125,7 @@ "@types/express": "^4.11.1", "@types/fluent-ffmpeg": "^2.1.0", "@types/glob": "^5.0.0p", + "@types/lru-cache": "^4.1.0", "@types/mime": "^2.0.0", "@types/node": "^10.1.2", "@types/normalize-package-data": "^2.4.0", diff --git a/src/contact.ts b/src/contact.ts index 28c1f4211cfc79a3c6d21ccc90deda4b1327231e..713be818d7c53bfee04dc181760088c5c20e287b 100644 --- a/src/contact.ts +++ b/src/contact.ts @@ -105,7 +105,6 @@ export class Contact extends PuppetAccessory implements Sayable { public static load( this : T, id : string, - payload? : ContactPayload, ): T['prototype'] { if (!this.pool) { log.verbose('Contact', 'load(%s) init pool', id) @@ -122,8 +121,8 @@ export class Contact extends PuppetAccessory implements Sayable { } const existingContact = this.pool.get(id) if (existingContact) { - if (payload) { - existingContact.payload = payload + if (!existingContact.payload) { + existingContact.payload = this.puppet.cacheContactPayload.get(id) } return existingContact } @@ -131,11 +130,9 @@ export class Contact extends PuppetAccessory implements Sayable { // when we call `load()`, `this` should already be extend-ed a child class. // so we force `this as any` at here to make the call. const newContact = new (this as any)(id) - this.pool.set(id, newContact) + newContact.payload = this.puppet.cacheContactPayload.get(id) - if (payload) { - newContact.payload = payload - } + this.pool.set(id, newContact) return newContact } diff --git a/src/friend-request.ts b/src/friend-request.ts index b732c8de73c3fa5cc0e76162e471b99ea98eb788..47758cdcb73dd881d0a741498a8008305d293d08 100644 --- a/src/friend-request.ts +++ b/src/friend-request.ts @@ -59,6 +59,20 @@ export class FriendRequest extends PuppetAccessory { // tslint:disable-next-line:variable-name public static Type = FriendRequestType + public static load( + this : T, + id : string, + payload? : FriendRequestPayload, + ): T['prototype'] { + const newFriendRequest = new (this as any)(id) + + const hitPayload = this.puppet.cacheFriendRequestPayload.get(id) + if (hitPayload) { + newFriendRequest.payload = hitPayload + } + return newFriendRequest + } + /** * Send a Friend Request to a `contact` with message `hello`. * @param contact @@ -68,21 +82,21 @@ export class FriendRequest extends PuppetAccessory { contact : Contact, hello : string, ): FriendRequest { - return this.createSend(contact, hello) + return this.createSend(contact.id, hello) } private static createSend( - contact : Contact, - hello : string, + contactId : string, + hello : string, ): FriendRequest { log.verbose('PuppeteerFriendRequest', 'createSend(%s, %s)', - contact, + contactId, hello, ) const sentRequest = new this({ type : FriendRequestType.Send, - contactId : contact.id, + contactId, hello, }) @@ -90,34 +104,34 @@ export class FriendRequest extends PuppetAccessory { } public static createConfirm( - contact: Contact, + contactId: string, ): FriendRequest { log.verbose('PuppeteerFriendRequest', 'createConfirm(%s)', - contact, + contactId, ) const confirmedRequest = new this({ type : FriendRequestType.Confirm, - contactId : contact.id, + contactId, }) return confirmedRequest } public static createReceive( - contact : Contact, - hello : string, - ticket : string, + contactId : string, + hello : string, + ticket : string, ): FriendRequest { log.verbose('PuppeteerFriendRequest', 'createReceive(%s, %s, %s)', - contact, + contactId, hello, ticket, ) const receivedRequest = new this({ type : FriendRequestType.Receive, - contactId : contact.id, + contactId, hello, ticket, }) @@ -125,8 +139,8 @@ export class FriendRequest extends PuppetAccessory { return receivedRequest } - public static fromJSON(payloadJsonStr: string): FriendRequest { - const payload: FriendRequestPayload = JSON.parse(payloadJsonStr) + public static fromJSON(payloadJsonText: string): FriendRequest { + const payload: FriendRequestPayload = JSON.parse(payloadJsonText) return new this(payload) } diff --git a/src/message.ts b/src/message.ts index 766d36e8a949db2f77dee99ff2e5561e5c75d981..ca1e5ea3873216bc85fa993260903f08371d3eb5 100644 --- a/src/message.ts +++ b/src/message.ts @@ -107,14 +107,9 @@ export class Message extends PuppetAccessory implements Sayable { * "mobile originated" or "mobile terminated" * https://www.tatango.com/resources/video-lessons/video-mo-mt-sms-messaging/ */ - public static create( - id : string, - payload? : MessagePayload, - ): Message { - log.verbose('Message', 'static create(%s, %s)', - id, - payload ? payload : '', - ) + public static create(id: string): Message { + log.verbose('Message', 'static create(%s)', id) + /** * Must NOT use `Message` at here * MUST use `this` at here @@ -123,9 +118,7 @@ export class Message extends PuppetAccessory implements Sayable { */ const msg = new this(id) - if (payload) { - msg.payload = payload - } + msg.payload = this.puppet.cacheMessagePayload.get(id) return msg } diff --git a/src/puppet-puppeteer/puppet-puppeteer.ts b/src/puppet-puppeteer/puppet-puppeteer.ts index 1830a345cda5c3eb60ef557b44b3f33f5107f47f..8eca114da45df571bc603caec1e181851e877caf 100644 --- a/src/puppet-puppeteer/puppet-puppeteer.ts +++ b/src/puppet-puppeteer/puppet-puppeteer.ts @@ -700,10 +700,10 @@ export class PuppetPuppeteer extends Puppet { } // return Misc.urlStream(avatarUrl, cookies) - const contact = this.Contact.load(contactId) - await contact.ready() + // const contact = this.Contact.load(contactId) + // await contact.ready() - const fileName = (contact.name() || 'unknown') + '-avatar.jpg' + const fileName = (payload.name || 'unknown') + '-avatar.jpg' return FileBox.packRemote( avatarUrl, fileName, @@ -791,7 +791,9 @@ export class PuppetPuppeteer extends Puppet { return filterFunction } - public async contactFindAll(query: ContactQueryFilter): Promise { + public async contactFindAll( + query: ContactQueryFilter = { name: /.*/ }, + ): Promise { const filterFunc = this.contactQueryFilterToFunctionString(query) @@ -819,36 +821,37 @@ export class PuppetPuppeteer extends Puppet { // let currNum = rawPayload.MemberList && rawPayload.MemberList.length || 0 // let prevNum = room.memberList().length // rawPayload && rawPayload.MemberList && this.rawObj.MemberList.length || 0 - let prevNum = 0 + let prevLength = -1 + /** + * @todo use Misc.retry() to replace the following loop + */ let ttl = 7 while (ttl--/* && currNum !== prevNum */) { - rawPayload = await this.bridge.getContact(id) as WebRoomRawPayload + rawPayload = await this.bridge.getContact(id) as undefined | WebRoomRawPayload - const currNum = rawPayload.MemberList && rawPayload.MemberList.length || 0 + if (rawPayload) { + const currLength = rawPayload.MemberList && rawPayload.MemberList.length || 0 - log.silly('PuppetPuppeteer', `roomPayload() this.bridge.getContact(%s) MemberList.length:%d at ttl:%d`, - id, - currNum, - ttl, - ) + log.silly('PuppetPuppeteer', `roomPayload() this.bridge.getContact(%s) MemberList.length:%d at ttl:%d`, + id, + currLength, + ttl, + ) - if (currNum > 0 && prevNum === currNum) { - log.silly('PuppetPuppeteer', `roomPayload() puppet.getContact(${id}) done at ttl:%d`, ttl) - break + if (prevLength === currLength) { + log.silly('PuppetPuppeteer', `roomPayload() puppet.getContact(${id}) done at ttl:%d`, ttl) + return rawPayload + } + prevLength = currLength } - prevNum = currNum log.silly('PuppetPuppeteer', `roomPayload() puppet.getContact(${id}) retry at ttl:%d`, ttl) await new Promise(r => setTimeout(r, 1000)) // wait for 1 second } - // await this.readyAllMembers(rawPayload && rawPayload.MemberList || []) - if (!rawPayload) { - throw new Error('no payload') - } + throw new Error('no payload') - return rawPayload } catch (e) { log.error('PuppetPuppeteer', 'roomRawPayload(%s) exception: %s', id, e.message) Raven.captureException(e) @@ -861,21 +864,23 @@ export class PuppetPuppeteer extends Puppet { ): Promise { log.verbose('PuppetPuppeteer', 'roomRawPayloadParser(%s)', rawPayload) + // const payload = await this.roomPayload(rawPayload.UserName) + // console.log(rawPayload) - const memberList = (rawPayload.MemberList || []) - .map(m => this.Contact.load(m.UserName)) - await Promise.all(memberList.map(c => c.ready())) + // const memberList = (rawPayload.MemberList || []) + // .map(m => this.Contact.load(m.UserName)) + // await Promise.all(memberList.map(c => c.ready())) - const nameMap = this.roomParseMap('name' , rawPayload.MemberList) - const roomAliasMap = this.roomParseMap('roomAlias' , rawPayload.MemberList) - const contactAliasMap = this.roomParseMap('contactAlias', rawPayload.MemberList) + const rawMemberList = rawPayload.MemberList || [] + const memberIdList = rawMemberList.map(rawMember => rawMember.UserName) + + const nameMap = await this.roomParseMap('name' , rawPayload.MemberList) + const roomAliasMap = await this.roomParseMap('roomAlias' , rawPayload.MemberList) + const contactAliasMap = await this.roomParseMap('contactAlias', rawPayload.MemberList) const roomPayload: RoomPayload = { - // id: rawPayload.UserName, - // encryId: rawPayload.EncryChatRoomId, // ??? topic: Misc.plainText(rawPayload.NickName || ''), - // ownerUin: rawPayload.OwnerUin, - memberIdList: memberList.map(c => c.id), + memberIdList, nameMap, roomAliasMap, @@ -885,47 +890,49 @@ export class PuppetPuppeteer extends Puppet { return roomPayload } - private roomParseMap( + private async roomParseMap( parseSection: keyof RoomMemberQueryFilter, memberList?: WebRoomRawMember[], - ): Map { + ): Promise> { log.verbose('PuppetPuppeteer', 'roomParseMap(%s, memberList.length=%d)', parseSection, memberList && memberList.length, ) const dict: Map = new Map() - if (memberList && memberList.map) { - memberList.forEach(member => { - let tmpName: string - // console.log(member) - const contact = this.Contact.load(member.UserName) - // contact.ready().then(() => console.log('###############', contact.name())) - // console.log(contact) - // log.silly('PuppetPuppeteer', 'roomParseMap() memberList.forEach(contact=%s)', contact) - - switch (parseSection) { - case 'name': - tmpName = contact.name() - break - case 'roomAlias': - tmpName = member.DisplayName - break - case 'contactAlias': - tmpName = contact.alias() || '' - break - default: - throw new Error('parseMap failed, member not found') - } - /** - * ISSUE #64 emoji need to be striped - * ISSUE #104 never use remark name because sys group message will never use that - * @rui: Wrong for 'never use remark name because sys group message will never use that', see more in the latest comment in #104 - * @rui: webwx's NickName here return contactAlias, if not set contactAlias, return name - * @rui: 2017-7-2 webwx's NickName just ruturn name, no contactAlias - */ - dict.set(member.UserName, Misc.stripEmoji(tmpName)) - }) + + const updateMember = async (member: WebRoomRawMember) => { + let tmpName: string + // console.log(member) + const payload = await this.contactPayload(member.UserName) + // contact.ready().then(() => console.log('###############', contact.name())) + // console.log(contact) + // log.silly('PuppetPuppeteer', 'roomParseMap() memberList.forEach(contact=%s)', contact) + + switch (parseSection) { + case 'name': + tmpName = payload.name || '' + break + case 'roomAlias': + tmpName = member.DisplayName + break + case 'contactAlias': + tmpName = payload.alias || '' + break + default: + throw new Error('parseMap failed, member not found') + } + /** + * ISSUE #64 emoji need to be striped + * ISSUE #104 never use remark name because sys group message will never use that + * @rui: Wrong for 'never use remark name because sys group message will never use that', see more in the latest comment in #104 + * @rui: webwx's NickName here return contactAlias, if not set contactAlias, return name + * @rui: 2017-7-2 webwx's NickName just ruturn name, no contactAlias + */ + dict.set(member.UserName, Misc.stripEmoji(tmpName)) + } + if (Array.isArray(memberList)) { + await Promise.all(memberList.map(updateMember)) } return dict } @@ -1057,38 +1064,25 @@ export class PuppetPuppeteer extends Puppet { * @private * For issue #668 */ - public async readyStable(): Promise { + public async waitStable(): Promise { log.verbose('PuppetPuppeteer', 'readyStable()') - let counter = -1 - const stable = async (done: Function): Promise => { - log.silly('PuppetPuppeteer', 'readyStable() stable() counter=%d', counter) + let prevLength = -1 + let ttl = 60 + const sleepTime = 60 * 1000 / ttl - const contactList = await this.Contact.findAll() - if (counter === contactList.length) { - log.verbose('PuppetPuppeteer', 'readyStable() stable() READY counter=%d', counter) - return done() + while (ttl-- > 0) { + const contactIdList = await this.contactFindAll() + if (prevLength === contactIdList.length) { + log.verbose('PuppetPuppeteer', 'readyStable() stable() READY length=%d', prevLength) + return } - counter = contactList.length - setTimeout(() => stable(done), 1000) - .unref() - } + prevLength = contactIdList.length - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - log.warn('PuppetPuppeteer', 'readyStable() stable() reject at counter=%d', counter) - return reject(new Error('timeout after 60 seconds')) - }, 60 * 1000) - timer.unref() - - const done = () => { - clearTimeout(timer) - return resolve() - } - - return stable(done) - }) + await new Promise(r => setTimeout(r, sleepTime)) + } + log.warn('PuppetPuppeteer', 'readyStable() TTL expired. Final length=%d', prevLength) } /** diff --git a/src/puppet/puppet.ts b/src/puppet/puppet.ts index 42cd2c06604744836caf63d253a340d96cc844e8..fdde7924241786612996e7f67ebddd3294dca27f 100644 --- a/src/puppet/puppet.ts +++ b/src/puppet/puppet.ts @@ -52,7 +52,7 @@ import { ContactQueryFilter, } from '../contact' import { - FriendRequestType, + // FriendRequestType, FriendRequestPayload, } from '../friend-request' import { @@ -95,27 +95,18 @@ let PUPPET_COUNTER = 0 */ export abstract class Puppet extends EventEmitter implements Sayable { + public readonly cacheContactPayload : LRU.Cache + public readonly cacheFriendRequestPayload : LRU.Cache + public readonly cacheMessagePayload : LRU.Cache + public readonly cacheRoomPayload : LRU.Cache + + public readonly state : StateSwitch + protected readonly watchdog : Watchdog protected readonly counter : number - protected readonly cacheContactPayload : LRU.Cache - protected readonly cacheFriendRequestPayload : LRU.Cache - protected readonly cacheMessagePayload : LRU.Cache - protected readonly cacheRoomPayload : LRU.Cache - protected id?: string - // /* tslint:disable:variable-name */ - // public readonly Contact : typeof Contact - // /* tslint:disable:variable-name */ - // public readonly FriendRequest : typeof FriendRequest - // /* tslint:disable:variable-name */ - // public readonly Message : typeof Message - // /* tslint:disable:variable-name */ - // public readonly Room : typeof Room - - public readonly state : StateSwitch - /** * childPkg stores the `package.json` that the NPM module who extends the `Puppet` */ @@ -262,7 +253,7 @@ export abstract class Puppet extends EventEmitter implements Sayable { // public on(event: 'watchdog', listener: (data: WatchdogFood) => void) : this public on(event: 'error', listener: (error: string) => void) : this - public on(event: 'friend', listener: (payload: string) => void) : this + public on(event: 'friend', listener: (requestId: string) => void) : this public on(event: 'heartbeat', listener: (data: string) => void) : this public on(event: 'login', listener: (contactId: string) => void) : this public on(event: 'logout', listener: (contactId: string) => void) : this @@ -380,40 +371,43 @@ export abstract class Puppet extends EventEmitter implements Sayable { /** * - * Message + * Contact * */ - public abstract async messageFile(messageId: string) : Promise - public abstract async messageForward(to: Receiver, messageId: string) : Promise - public abstract async messageSendText(to: Receiver, text: string) : Promise - public abstract async messageSendFile(to: Receiver, file: FileBox) : Promise + public abstract async contactAlias(contactId: string) : Promise + public abstract async contactAlias(contactId: string, alias: string | null) : Promise + public abstract async contactAlias(contactId: string, alias?: string|null) : Promise + public abstract async contactAvatar(contactId: string) : Promise + public abstract async contactFindAll(query?: ContactQueryFilter) : Promise - public abstract async messageRawPayload(id: string) : Promise - public abstract async messageRawPayloadParser(rawPayload: any) : Promise + public abstract async contactRawPayload(id: string) : Promise + public abstract async contactRawPayloadParser(rawPayload: any) : Promise - public async messagePayload( + public async contactPayload( id: string, noCache = false, - ): Promise { - log.verbose('Puppet', 'messagePayload(id=%s, noCache=%s)', id, noCache) + ): Promise { + log.silly('Puppet', 'contactPayload(id=%s, noCache=%s) @ %s', id, noCache, this) if (noCache) { - log.silly('Puppet', 'messagePayload() cache PURGE') - this.cacheMessagePayload.del(id) + log.silly('Puppet', 'contactPayload() cache PURGE') + this.cacheContactPayload.del(id) } - const hitPayload = this.cacheMessagePayload.get(id) + const hitPayload = this.cacheContactPayload.get(id) if (hitPayload) { - log.silly('Puppet', 'messagePayload() cache HIT') - + log.silly('Puppet', 'contactPayload() cache HIT') return hitPayload } - log.silly('Puppet', 'messagePayload() cache MISS') + log.silly('Puppet', 'contactPayload() cache MISS') - const rawPayload = await this.messageRawPayload(id) - const payload = await this.messageRawPayloadParser(rawPayload) + const rawPayload = await this.contactRawPayload(id) + const payload = await this.contactRawPayloadParser(rawPayload) + + this.cacheContactPayload.set(id, payload) + log.silly('Puppet', 'contactPayload() cache SET') return payload } @@ -448,82 +442,91 @@ export abstract class Puppet extends EventEmitter implements Sayable { throw new Error('no payload') } + /** * - * Room + * Message * */ - public abstract async roomAdd(roomId: string, contactId: string) : Promise - public abstract async roomCreate(contactIdList: string[], topic?: string) : Promise - public abstract async roomDel(roomId: string, contactId: string) : Promise - public abstract async roomFindAll(query?: RoomQueryFilter) : Promise - public abstract async roomQuit(roomId: string) : Promise - public abstract async roomTopic(roomId: string, topic?: string) : Promise + public abstract async messageFile(messageId: string) : Promise + public abstract async messageForward(to: Receiver, messageId: string) : Promise + public abstract async messageSendText(to: Receiver, text: string) : Promise + public abstract async messageSendFile(to: Receiver, file: FileBox) : Promise - public abstract async roomRawPayload(id: string) : Promise - public abstract async roomRawPayloadParser(rawPayload: any) : Promise + public abstract async messageRawPayload(id: string) : Promise + public abstract async messageRawPayloadParser(rawPayload: any) : Promise - public async roomPayload( + public async messagePayload( id: string, noCache = false, - ): Promise { - log.verbose('Puppet', 'roomPayload(id=%s, noCache=%s)', id, noCache) + ): Promise { + log.verbose('Puppet', 'messagePayload(id=%s, noCache=%s)', id, noCache) if (noCache) { - log.silly('Puppet', 'roomPayload() cache PURGE') - this.cacheRoomPayload.del(id) + log.silly('Puppet', 'messagePayload() cache PURGE') + this.cacheMessagePayload.del(id) } - const hitPayload = this.cacheRoomPayload.get(id) + const hitPayload = this.cacheMessagePayload.get(id) if (hitPayload) { - log.silly('Puppet', 'roomPayload() cache HIT') + log.silly('Puppet', 'messagePayload() cache HIT') + return hitPayload } - log.silly('Puppet', 'roomPayload() cache MISS') + log.silly('Puppet', 'messagePayload() cache MISS') + + const rawPayload = await this.messageRawPayload(id) + const payload = await this.messageRawPayloadParser(rawPayload) + + this.cacheMessagePayload.set(id, payload) + log.silly('Puppet', 'messagePayload() cache SET') - const rawPayload = await this.roomRawPayload(id) - const payload = await this.roomRawPayloadParser(rawPayload) return payload } /** * - * Contact + * Room * */ - public abstract async contactAlias(contactId: string) : Promise - public abstract async contactAlias(contactId: string, alias: string | null) : Promise - public abstract async contactAlias(contactId: string, alias?: string|null) : Promise - public abstract async contactAvatar(contactId: string) : Promise - public abstract async contactFindAll(query?: ContactQueryFilter) : Promise + public abstract async roomAdd(roomId: string, contactId: string) : Promise + public abstract async roomCreate(contactIdList: string[], topic?: string) : Promise + public abstract async roomDel(roomId: string, contactId: string) : Promise + public abstract async roomFindAll(query?: RoomQueryFilter) : Promise + public abstract async roomQuit(roomId: string) : Promise + public abstract async roomTopic(roomId: string, topic?: string) : Promise - public abstract async contactRawPayload(id: string) : Promise - public abstract async contactRawPayloadParser(rawPayload: any) : Promise + public abstract async roomRawPayload(id: string) : Promise + public abstract async roomRawPayloadParser(rawPayload: any) : Promise - public async contactPayload( + public async roomPayload( id: string, noCache = false, - ): Promise { - log.silly('Puppet', 'contactPayload(id=%s, noCache=%s) @ %s', id, noCache, this) + ): Promise { + log.verbose('Puppet', 'roomPayload(id=%s, noCache=%s)', id, noCache) if (noCache) { - log.silly('Puppet', 'contactPayload() cache PURGE') - this.cacheContactPayload.del(id) + log.silly('Puppet', 'roomPayload() cache PURGE') + this.cacheRoomPayload.del(id) } - const hitPayload = this.cacheContactPayload.get(id) + const hitPayload = this.cacheRoomPayload.get(id) if (hitPayload) { - log.silly('Puppet', 'contactPayload() cache HIT') + log.silly('Puppet', 'roomPayload() cache HIT') return hitPayload } - log.silly('Puppet', 'contactPayload() cache MISS') + log.silly('Puppet', 'roomPayload() cache MISS') + + const rawPayload = await this.roomRawPayload(id) + const payload = await this.roomRawPayloadParser(rawPayload) + + this.cacheRoomPayload.set(id, payload) + log.silly('Puppet', 'roomPayload() cache SET') - const rawPayload = await this.contactRawPayload(id) - const payload = await this.contactRawPayloadParser(rawPayload) return payload } diff --git a/src/room.ts b/src/room.ts index a1dae54c3d4ba59df460e81cd2f6583f9a950165..e0641f88ddf61db95d5724b2846097ff34d9a550 100644 --- a/src/room.ts +++ b/src/room.ts @@ -55,8 +55,6 @@ export interface RoomQueryFilter { } export interface RoomPayload { - // id: string, - // encryId: string, topic : string, memberIdList : string[], ownerId? : string, @@ -174,9 +172,8 @@ export class Room extends PuppetAccessory implements Sayable { * About the Generic: https://stackoverflow.com/q/43003970/1123955 */ public static load( - this : T, - id : string, - payload? : RoomPayload, + this : T, + id : string, ): T['prototype'] { if (!this.pool) { this.pool = new Map() @@ -184,30 +181,19 @@ export class Room extends PuppetAccessory implements Sayable { const existingRoom = this.pool.get(id) if (existingRoom) { - if (payload) { - existingRoom.payload = payload + if (!existingRoom.payload) { + existingRoom.payload = this.puppet.cacheRoomPayload.get(id) } return existingRoom } const newRoom = new (this as any)(id) - if (payload) { - newRoom.payload = payload - } + newRoom.payload = this.puppet.cacheRoomPayload.get(id) this.pool.set(id, newRoom) return newRoom } - // public load( - // this : Room, - // id : string, - // ): Room { - // const klass = instanceToClass(this, Room) - // const room = klass.load(id) - // return room - // } - /** * * @@ -349,7 +335,7 @@ export class Room extends PuppetAccessory implements Sayable { } } - public emit(event: 'leave', leaver: Contact[], remover?: Contact) : boolean + public emit(event: 'leave', leaverList: Contact[], remover?: Contact) : boolean public emit(event: 'join' , inviteeList: Contact[] , inviter: Contact) : boolean public emit(event: 'topic', topic: string, oldTopic: string, changer: Contact) : boolean public emit(event: never, ...args: never[]): never diff --git a/src/wechaty.ts b/src/wechaty.ts index 5b9740a7066781b620302905589a23de21ae1801..6cb8a1ebecec8aa17b9d911086baf45e89efba06 100644 --- a/src/wechaty.ts +++ b/src/wechaty.ts @@ -550,9 +550,11 @@ export class Wechaty extends PuppetAccessory implements Sayable { case 'friend': puppet.removeAllListeners('friend') - puppet.on('friend', payload => { - const request = this.FriendRequest.fromJSON(payload) + puppet.on('friend', async requestId => { + const request = this.FriendRequest.load(requestId) + await request.ready() this.emit('friend', request) + request.contact().emit('friend', request) }) break @@ -596,6 +598,7 @@ export class Wechaty extends PuppetAccessory implements Sayable { await inviter.ready() this.emit('room-join', room, inviteeList, inviter) + room.emit('join', inviteeList, inviter) }) break @@ -609,6 +612,7 @@ export class Wechaty extends PuppetAccessory implements Sayable { await Promise.all(leaverList.map(c => c.ready())) this.emit('room-leave', room, leaverList) + room.emit('leave', leaverList) }) break @@ -622,6 +626,7 @@ export class Wechaty extends PuppetAccessory implements Sayable { await changer.ready() this.emit('room-topic', room, topic, oldTopic, changer) + room.emit('topic', topic, oldTopic, changer) }) break