提交 c98f7f27 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓)

WIP: refactoring puppet with lru-cache & id payload

上级 7f01b0be
......@@ -105,7 +105,6 @@ export class Contact extends PuppetAccessory implements Sayable {
public static load<T extends typeof Contact>(
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
}
......
......@@ -59,6 +59,20 @@ export class FriendRequest extends PuppetAccessory {
// tslint:disable-next-line:variable-name
public static Type = FriendRequestType
public static load<T extends typeof FriendRequest>(
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)
}
......
......@@ -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
}
......
......@@ -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<string[]> {
public async contactFindAll(
query: ContactQueryFilter = { name: /.*/ },
): Promise<string[]> {
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<RoomPayload> {
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<string, string> {
): Promise<Map<string, string>> {
log.verbose('PuppetPuppeteer', 'roomParseMap(%s, memberList.length=%d)',
parseSection,
memberList && memberList.length,
)
const dict: Map<string, string> = new Map<string, string>()
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<void> {
public async waitStable(): Promise<void> {
log.verbose('PuppetPuppeteer', 'readyStable()')
let counter = -1
const stable = async (done: Function): Promise<void> => {
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<void>((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)
}
/**
......
......@@ -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<string, ContactPayload>
public readonly cacheFriendRequestPayload : LRU.Cache<string, FriendRequestPayload>
public readonly cacheMessagePayload : LRU.Cache<string, MessagePayload>
public readonly cacheRoomPayload : LRU.Cache<string, RoomPayload>
public readonly state : StateSwitch
protected readonly watchdog : Watchdog
protected readonly counter : number
protected readonly cacheContactPayload : LRU.Cache<string, ContactPayload>
protected readonly cacheFriendRequestPayload : LRU.Cache<string, FriendRequestPayload>
protected readonly cacheMessagePayload : LRU.Cache<string, MessagePayload>
protected readonly cacheRoomPayload : LRU.Cache<string, RoomPayload>
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<FileBox>
public abstract async messageForward(to: Receiver, messageId: string) : Promise<void>
public abstract async messageSendText(to: Receiver, text: string) : Promise<void>
public abstract async messageSendFile(to: Receiver, file: FileBox) : Promise<void>
public abstract async contactAlias(contactId: string) : Promise<string>
public abstract async contactAlias(contactId: string, alias: string | null) : Promise<void>
public abstract async contactAlias(contactId: string, alias?: string|null) : Promise<string | void>
public abstract async contactAvatar(contactId: string) : Promise<FileBox>
public abstract async contactFindAll(query?: ContactQueryFilter) : Promise<string[]>
public abstract async messageRawPayload(id: string) : Promise<any>
public abstract async messageRawPayloadParser(rawPayload: any) : Promise<MessagePayload>
public abstract async contactRawPayload(id: string) : Promise<any>
public abstract async contactRawPayloadParser(rawPayload: any) : Promise<ContactPayload>
public async messagePayload(
public async contactPayload(
id: string,
noCache = false,
): Promise<MessagePayload> {
log.verbose('Puppet', 'messagePayload(id=%s, noCache=%s)', id, noCache)
): Promise<ContactPayload> {
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<void>
public abstract async roomCreate(contactIdList: string[], topic?: string) : Promise<string>
public abstract async roomDel(roomId: string, contactId: string) : Promise<void>
public abstract async roomFindAll(query?: RoomQueryFilter) : Promise<string[]>
public abstract async roomQuit(roomId: string) : Promise<void>
public abstract async roomTopic(roomId: string, topic?: string) : Promise<string | void>
public abstract async messageFile(messageId: string) : Promise<FileBox>
public abstract async messageForward(to: Receiver, messageId: string) : Promise<void>
public abstract async messageSendText(to: Receiver, text: string) : Promise<void>
public abstract async messageSendFile(to: Receiver, file: FileBox) : Promise<void>
public abstract async roomRawPayload(id: string) : Promise<any>
public abstract async roomRawPayloadParser(rawPayload: any) : Promise<RoomPayload>
public abstract async messageRawPayload(id: string) : Promise<any>
public abstract async messageRawPayloadParser(rawPayload: any) : Promise<MessagePayload>
public async roomPayload(
public async messagePayload(
id: string,
noCache = false,
): Promise<RoomPayload> {
log.verbose('Puppet', 'roomPayload(id=%s, noCache=%s)', id, noCache)
): Promise<MessagePayload> {
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<string>
public abstract async contactAlias(contactId: string, alias: string | null) : Promise<void>
public abstract async contactAlias(contactId: string, alias?: string|null) : Promise<string | void>
public abstract async contactAvatar(contactId: string) : Promise<FileBox>
public abstract async contactFindAll(query?: ContactQueryFilter) : Promise<string[]>
public abstract async roomAdd(roomId: string, contactId: string) : Promise<void>
public abstract async roomCreate(contactIdList: string[], topic?: string) : Promise<string>
public abstract async roomDel(roomId: string, contactId: string) : Promise<void>
public abstract async roomFindAll(query?: RoomQueryFilter) : Promise<string[]>
public abstract async roomQuit(roomId: string) : Promise<void>
public abstract async roomTopic(roomId: string, topic?: string) : Promise<string | void>
public abstract async contactRawPayload(id: string) : Promise<any>
public abstract async contactRawPayloadParser(rawPayload: any) : Promise<ContactPayload>
public abstract async roomRawPayload(id: string) : Promise<any>
public abstract async roomRawPayloadParser(rawPayload: any) : Promise<RoomPayload>
public async contactPayload(
public async roomPayload(
id: string,
noCache = false,
): Promise<ContactPayload> {
log.silly('Puppet', 'contactPayload(id=%s, noCache=%s) @ %s', id, noCache, this)
): Promise<RoomPayload> {
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
}
......
......@@ -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<T extends typeof Room>(
this : T,
id : string,
payload? : RoomPayload,
this : T,
id : string,
): T['prototype'] {
if (!this.pool) {
this.pool = new Map<string, Room>()
......@@ -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
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册