room.ts 17.3 KB
Newer Older
1
import { EventEmitter } from 'events'
2

3
import {
L
lijiarui 已提交
4 5 6
  Config,
  Sayable,
  log,
M
Mukaiu 已提交
7
}                     from './config'
Huan (李卓桓)'s avatar
merge  
Huan (李卓桓) 已提交
8
import Contact        from './contact'
M
Mukaiu 已提交
9 10 11 12
import {
  Message,
  MediaMessage,
}                     from './message'
Huan (李卓桓)'s avatar
merge  
Huan (李卓桓) 已提交
13
import UtilLib        from './util-lib'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
14

15
interface RoomObj {
16 17 18 19 20 21 22 23
  id:               string,
  encryId:          string,
  topic:            string,
  ownerUin:         number,
  memberList:       Contact[],
  nameMap:          Map<string, string>,
  roomAliasMap:     Map<string, string>,
  contactAliasMap:  Map<string, string>,
24 25
}

26
type NameType = 'name' | 'alias' | 'roomAlias' | 'contactAlias'
27

28
export interface RoomRawMember {
L
lijiarui 已提交
29 30 31
  UserName:     string,
  NickName:     string,
  DisplayName:  string,
32 33
}

34
export interface RoomRawObj {
L
lijiarui 已提交
35 36 37 38 39
  UserName:         string,
  EncryChatRoomId:  string,
  NickName:         string,
  OwnerUin:         number,
  ChatRoomOwner:    string,
40
  MemberList?:      RoomRawMember[],
41 42
}

43 44 45
export type RoomEventName = 'join'
                          | 'leave'
                          | 'topic'
46 47
                          | 'EVENT_PARAM_ERROR'

48
export interface RoomQueryFilter {
L
lijiarui 已提交
49
  topic: string | RegExp,
50 51
}

52
export interface MemberQueryFilter {
53 54 55 56
  name?:         string,
  alias?:        string,
  roomAlias?:    string,
  contactAlias?: string,
57 58
}

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
59 60 61 62 63 64 65 66 67 68
/**
 *
 * wechaty: Wechat for Bot. and for human who talk to bot/robot
 *
 * Licenst: ISC
 * https://github.com/zixia/wechaty
 *
 * Add/Del/Topic: https://github.com/wechaty/wechaty/issues/32
 *
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
69
export class Room extends EventEmitter implements Sayable {
70 71
  private static pool = new Map<string, Room>()

72 73
  private dirtyObj: RoomObj | null // when refresh, use this to save dirty data for query
  private obj:      RoomObj | null
74 75
  private rawObj:   RoomRawObj

76
  constructor(public id: string) {
77
    super()
78
    log.silly('Room', `constructor(${id})`)
79
  }
80

81
  public toString()    { return this.id }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
82
  public toStringEx()  { return `Room(${this.obj && this.obj.topic}[${this.id}])` }
83

84
  public isReady(): boolean {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
85
    return !!(this.obj && this.obj.memberList && this.obj.memberList.length)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
86 87
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
88
  public async refresh(): Promise<void> {
89 90 91
    if (this.isReady()) {
      this.dirtyObj = this.obj
    }
92
    this.obj = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
93 94
    await this.ready()
    return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
95 96
  }

97
  private async readyAllMembers(memberList: RoomRawMember[]): Promise<void> {
98 99
    for (const member of memberList) {
      const contact = Contact.load(member.UserName)
100
      await contact.ready()
101 102 103 104
    }
    return
  }

105
  public async ready(contactGetter?: (id: string) => Promise<any>): Promise<Room> {
106
    log.silly('Room', 'ready(%s)', contactGetter ? contactGetter.constructor.name : '')
107
    if (!this.id) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
108 109
      const e = new Error('ready() on a un-inited Room')
      log.warn('Room', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
110
      throw e
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
111
    } else if (this.isReady()) {
112
      return this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
113
    } else if (this.obj && this.obj.id) {
114
      log.warn('Room', 'ready() has obj.id but memberList empty in room %s. reloading', this.obj.topic)
115
    }
116

117 118 119 120 121 122 123 124
    if (!contactGetter) {
      contactGetter = Config.puppetInstance()
                            .getContact.bind(Config.puppetInstance())
    }
    if (!contactGetter) {
      throw new Error('no contactGetter')
    }

125 126
    try {
      const data = await contactGetter(this.id)
127
      log.silly('Room', `contactGetter(${this.id}) resolved`)
128
      this.rawObj = data
129
      await this.readyAllMembers(this.rawObj.MemberList || [])
130
      this.obj    = this.parse(this.rawObj)
131 132 133
      if (!this.obj) {
        throw new Error('no this.obj set after contactGetter')
      }
134
      await Promise.all(this.obj.memberList.map(c => c.ready(contactGetter)))
135

136
      return this
137

138
    } catch (e) {
139 140
      log.error('Room', 'contactGetter(%s) exception: %s', this.id, e.message)
      throw e
141
    }
142 143
  }

144 145 146
  public on(event: 'leave', listener: (this: Room, leaver: Contact) => void): this
  public on(event: 'join' , listener: (this: Room, inviteeList: Contact[] , inviter: Contact)  => void): this
  public on(event: 'topic', listener: (this: Room, topic: string, oldTopic: string, changer: Contact) => void): this
147
  public on(event: 'EVENT_PARAM_ERROR', listener: () => void): this
148

149
  public on(event: RoomEventName, listener: Function): this {
150
    log.verbose('Room', 'on(%s, %s)', event, typeof listener)
151

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
152 153 154 155 156 157 158 159 160 161 162
    // const thisWithSay = {
    //   say: (content: string) => {
    //     return Config.puppetInstance()
    //                   .say(content)
    //   }
    // }
    // super.on(event, function() {
    //   return listener.apply(thisWithSay, arguments)
    // })

    super.on(event, listener) // Room is `Sayable`
163
    return this
164 165
  }

M
Mukaiu 已提交
166
  public say(mediaMessage: MediaMessage): Promise<any>
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
167 168 169
  public say(content: string): Promise<any>
  public say(content: string, replyTo: Contact): Promise<void>
  public say(content: string, replyTo: Contact[]): Promise<void>
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
170

M
Mukaiu 已提交
171 172
  public say(textOrMedia: string | MediaMessage, replyTo?: Contact|Contact[]): Promise<void> {
    const content = textOrMedia instanceof MediaMessage ? textOrMedia.filename() : textOrMedia
L
lijiarui 已提交
173 174 175
    log.verbose('Room', 'say(%s, %s)',
                        content,
                        Array.isArray(replyTo)
176
                        ? replyTo.map(c => c.name()).join(', ')
L
lijiarui 已提交
177
                        : replyTo ? replyTo.name() : '',
178
    )
179

M
Mukaiu 已提交
180 181 182
    let m
    if (typeof textOrMedia === 'string') {
      m = new Message()
183

M
Mukaiu 已提交
184
      const replyToList: Contact[] = [].concat(replyTo as any || [])
185

M
Mukaiu 已提交
186
      if (replyToList.length > 0) {
187 188
        const AT_SEPRATOR = String.fromCharCode(8197)
        const mentionList = replyToList.map(c => '@' + c.name()).join(AT_SEPRATOR)
M
Mukaiu 已提交
189 190 191 192 193 194 195 196 197
        m.content(mentionList + ' ' + content)
      } else {
        m.content(content)
      }
      // m.to(replyToList[0])
    } else
      m = textOrMedia

    m.room(this)
198 199 200 201 202

    return Config.puppetInstance()
                  .send(m)
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
203
  public get(prop): string { return (this.obj && this.obj[prop]) || (this.dirtyObj && this.dirtyObj[prop]) }
204

205
  private parse(rawObj: RoomRawObj): RoomObj | null {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
206
    if (!rawObj) {
207
      log.warn('Room', 'parse() on a empty rawObj?')
208
      return null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
209
    }
Huan (李卓桓)'s avatar
#104  
Huan (李卓桓) 已提交
210

211 212 213
    const memberList = (rawObj.MemberList || [])
                        .map(m => Contact.load(m.UserName))

L
lijiarui 已提交
214
    const nameMap    = this.parseMap('name', rawObj.MemberList)
215 216
    const roomAliasMap   = this.parseMap('roomAlias', rawObj.MemberList)
    const contactAliasMap   = this.parseMap('contactAlias', rawObj.MemberList)
Huan (李卓桓)'s avatar
#104  
Huan (李卓桓) 已提交
217

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
218
    return {
Huan (李卓桓)'s avatar
#104  
Huan (李卓桓) 已提交
219 220 221 222 223
      id:         rawObj.UserName,
      encryId:    rawObj.EncryChatRoomId, // ???
      topic:      rawObj.NickName,
      ownerUin:   rawObj.OwnerUin,
      memberList,
224
      nameMap,
225 226
      roomAliasMap,
      contactAliasMap,
227 228 229
    }
  }

L
lijiarui 已提交
230
  private parseMap(parseContent: NameType, memberList?: RoomRawMember[]): Map<string, string> {
231
    const mapList: Map<string, string> = new Map<string, string>()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
232
    if (memberList && memberList.map) {
Huan (李卓桓)'s avatar
#104  
Huan (李卓桓) 已提交
233
      memberList.forEach(member => {
234
        let tmpName: string
235
        const contact = Contact.load(member.UserName)
236
        switch (parseContent) {
ruiruibupt's avatar
2  
ruiruibupt 已提交
237
          case 'name':
238
            tmpName = contact.name()
239
            break
240
          case 'roomAlias':
L
lijiarui 已提交
241
            tmpName = member.DisplayName
242
            break
243 244 245
          case 'contactAlias':
            tmpName = contact.alias() || ''
            break
246 247 248
          default:
            throw new Error('parseMap failed, member not found')
        }
249 250
        /**
         * ISSUE #64 emoji need to be striped
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
251
         * ISSUE #104 never use remark name because sys group message will never use that
ruiruibupt's avatar
#217  
ruiruibupt 已提交
252
         * @rui: Wrong for 'never use remark name because sys group message will never use that', see more in the latest comment in #104
ruiruibupt's avatar
1  
ruiruibupt 已提交
253 254
         * @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
255
         */
256
        mapList[member.UserName] = UtilLib.stripEmoji(tmpName)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
257
      })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
258
    }
259
    return mapList
260 261
  }

262
  public dumpRaw() {
263
    console.error('======= dump raw Room =======')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
264
    Object.keys(this.rawObj).forEach(k => console.error(`${k}: ${this.rawObj[k]}`))
265
  }
266
  public dump() {
267
    console.error('======= dump Room =======')
268
    Object.keys(this.obj).forEach(k => console.error(`${k}: ${this.obj && this.obj[k]}`))
269 270
  }

Huan (李卓桓)'s avatar
#119  
Huan (李卓桓) 已提交
271
  public async add(contact: Contact): Promise<number> {
272
    log.verbose('Room', 'add(%s)', contact)
273 274 275 276 277

    if (!contact) {
      throw new Error('contact not found')
    }

Huan (李卓桓)'s avatar
#119  
Huan (李卓桓) 已提交
278 279 280
    const n = Config.puppetInstance()
                      .roomAdd(this, contact)
    return n
281 282
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
283
  public async del(contact: Contact): Promise<number> {
284
    log.verbose('Room', 'del(%s)', contact.name())
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
285 286 287 288

    if (!contact) {
      throw new Error('contact not found')
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
289
    const n = await Config.puppetInstance()
Huan (李卓桓)'s avatar
#119  
Huan (李卓桓) 已提交
290 291
                            .roomDel(this, contact)
                            .then(_ => this.delLocal(contact))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
292
    return n
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
293 294
  }

295
  private delLocal(contact: Contact): number {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
296 297
    log.verbose('Room', 'delLocal(%s)', contact)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
298
    const memberList = this.obj && this.obj.memberList
299
    if (!memberList || memberList.length === 0) {
300
      return 0 // already in refreshing
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
301 302 303
    }

    let i
304 305
    for (i = 0; i < memberList.length; i++) {
      if (memberList[i].id === contact.id) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
306 307 308
        break
      }
    }
309 310
    if (i < memberList.length) {
      memberList.splice(i, 1)
311
      return 1
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
312
    }
313
    return 0
314
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
315

316
  public quit() {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
317 318
    throw new Error('wx web not implement yet')
    // WechatyBro.glue.chatroomFactory.quit("@@1c066dfcab4ef467cd0a8da8bec90880035aa46526c44f504a83172a9086a5f7"
319
  }
320

321 322 323 324 325 326 327 328 329 330
  /**
   * get topic
   */
  public topic(): string
  /**
   * set topic
   */
  public topic(newTopic: string): void

  public topic(newTopic?: string): string | void {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
331
    if (!this.isReady()) {
332
      log.warn('Room', 'topic() room not ready')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
333 334
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
335 336
    if (newTopic) {
      log.verbose('Room', 'topic(%s)', newTopic)
337
      Config.puppetInstance().roomTopic(this, newTopic)
338 339
                              .catch(e => {
                                log.warn('Room', 'topic(newTopic=%s) exception: %s',
L
lijiarui 已提交
340
                                                  newTopic, e && e.message || e,
341 342 343 344 345 346
                                )
                              })
      if (!this.obj) {
        this.obj = <RoomObj>{}
      }
      Object.assign(this.obj, { topic: newTopic })
347
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
348
    }
349
    return UtilLib.plainText(this.obj ? this.obj.topic : '')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
350 351
  }

352 353 354 355
  /**
   * should be deprecated
   * @deprecated
   */
ruiruibupt's avatar
2  
ruiruibupt 已提交
356
  public nick(contact: Contact): string | null {
ruiruibupt's avatar
1  
ruiruibupt 已提交
357
    log.warn('Room', 'nick(Contact) DEPRECATED, use alias(Contact) instead.')
ruiruibupt's avatar
#217  
ruiruibupt 已提交
358
    return this.alias(contact)
359 360
  }

L
lijiarui 已提交
361
  /**
362
   * return contact's roomAlias in the room, the same as roomAlias
L
lijiarui 已提交
363
   * @param {Contact} contact
364
   * @returns {string | null} If a contact has an alias in room, return string, otherwise return null
L
lijiarui 已提交
365
   */
ruiruibupt's avatar
2  
ruiruibupt 已提交
366
  public alias(contact: Contact): string | null {
367 368 369 370 371
    return this.roomAlias(contact)
  }

  public roomAlias(contact: Contact): string | null {
    if (!this.obj || !this.obj.roomAliasMap) {
ruiruibupt's avatar
2  
ruiruibupt 已提交
372
      return null
373
    }
374
    return this.obj.roomAliasMap[contact.id] || null
375 376
  }

377
  public has(contact: Contact): boolean {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
378
    if (!this.obj || !this.obj.memberList) {
379 380 381 382 383 384 385
      return false
    }
    return this.obj.memberList
                    .filter(c => c.id === contact.id)
                    .length > 0
  }

386
  public owner(): Contact | null {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
387
    const ownerUin = this.obj && this.obj.ownerUin
388

389
    const user = Config.puppetInstance()
390 391 392 393 394 395
                      .user

    if (user && user.get('uin') === ownerUin) {
      return user
    }

J
jaslin 已提交
396
    if (this.rawObj.ChatRoomOwner) {
397 398 399 400
      return Contact.load(this.rawObj.ChatRoomOwner)
    }

    return null
401 402
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
403
  /**
404 405
   * find member by name | roomAlias(alias) | contactAlias
   * when use memberAll(name:string), return all matched members, including name, roomAlias, contactAlias
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
406
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
407 408
  public memberAll(name: string): Contact[]
  public memberAll(filter: MemberQueryFilter): Contact[]
409

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
410
  public memberAll(queryArg: MemberQueryFilter | string): Contact[] {
411
    if (typeof queryArg === 'string') {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
      //
      // use the following `return` statement to do this job.
      //

      // const nameList = this.memberAll({name: queryArg})
      // const roomAliasList = this.memberAll({roomAlias: queryArg})
      // const contactAliasList = this.memberAll({contactAlias: queryArg})

      // if (nameList) {
      //   contactList = contactList.concat(nameList)
      // }
      // if (roomAliasList) {
      //   contactList = contactList.concat(roomAliasList)
      // }
      // if (contactAliasList) {
      //   contactList = contactList.concat(contactAliasList)
      // }

      return ([] as Contact[]).concat(
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
431 432
        this.memberAll({name:         queryArg}),
        this.memberAll({roomAlias:    queryArg}),
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
433 434
        this.memberAll({contactAlias: queryArg}),
      )
435 436
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
437 438 439
    /**
     * We got filter parameter
     */
L
lijiarui 已提交
440 441
    log.silly('Room', 'member({ %s })',
                         Object.keys(queryArg)
442
                                .map(k => `${k}: ${queryArg[k]}`)
L
lijiarui 已提交
443
                                .join(', '),
444 445 446
            )

    if (Object.keys(queryArg).length !== 1) {
ruiruibupt's avatar
1  
ruiruibupt 已提交
447
      throw new Error('Room member find queryArg only support one key. multi key support is not availble now.')
448
    }
449

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
450
    if (!this.obj || !this.obj.memberList) {
451
      log.warn('Room', 'member() not ready')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
452
      return []
453
    }
454
    let filterKey            = Object.keys(queryArg)[0]
455 456 457
    /**
     * ISSUE #64 emoji need to be striped
     */
458
    const filterValue: string  = UtilLib.stripEmoji(queryArg[filterKey])
459 460

    const keyMap = {
461 462 463 464
      name:         'nameMap',
      roomAlias:    'roomAliasMap',
      alias:        'roomAliasMap',
      contactAlias: 'contactAliasMap',
465 466 467 468 469 470 471 472 473 474
    }

    filterKey = keyMap[filterKey]
    if (!filterKey) {
      throw new Error('unsupport filter key')
    }

    if (!filterValue) {
      throw new Error('filterValue not found')
    }
475

476 477 478
    const filterMap = this.obj[filterKey]
    const idList = Object.keys(filterMap)
                          .filter(k => filterMap[k] === filterValue)
479

ruiruibupt's avatar
1  
ruiruibupt 已提交
480
    log.silly('Room', 'member() check %s from %s: %s', filterValue, filterKey, JSON.stringify(filterMap))
481

482
    if (idList.length) {
483
      return idList.map(id => Contact.load(id))
484
    } else {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
485
      return []
486
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
487 488
  }

489
  public member(name: string): Contact | null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
490
  public member(filter: MemberQueryFilter): Contact | null
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505

  public member(queryArg: MemberQueryFilter | string): Contact | null {
    log.verbose('Room', 'member(%s)', JSON.stringify(queryArg))

    const memberList =  this.memberAll(queryArg)
    if (!memberList || !memberList.length) {
      return null
    }

    if (memberList.length > 1) {
      log.warn('Room', 'function member(%s) get %d contacts, use the first one by default', JSON.stringify(queryArg), memberList.length)
    }
    return memberList[0]
  }

506
  public memberList(): Contact[] {
507
    log.verbose('Room', 'memberList')
508 509 510

    if (!this.obj || !this.obj.memberList || this.obj.memberList.length < 1) {
      log.warn('Room', 'memberList() not ready')
511
      return []
512 513 514 515
    }
    return this.obj.memberList
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
516
  public static create(contactList: Contact[], topic?: string): Promise<Room> {
517
    log.verbose('Room', 'create(%s, %s)', contactList.join(','), topic)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
518

Huan (李卓桓)'s avatar
bug fix  
Huan (李卓桓) 已提交
519
    if (!contactList || !Array.isArray(contactList)) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
520 521
      throw new Error('contactList not found')
    }
522

523
    return Config.puppetInstance()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
524
                  .roomCreate(contactList, topic)
525 526 527
                  .catch(e => {
                    log.error('Room', 'create() exception: %s', e && e.stack || e.message || e)
                    throw e
528
                  })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
529 530
  }

531 532 533 534
  public static async findAll(query?: RoomQueryFilter): Promise<Room[]> {
    if (!query) {
      query = { topic: /.*/ }
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
535
    log.verbose('Room', 'findAll({ topic: %s })', query.topic)
536

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
537
    let topicFilter = query.topic
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
538

539 540
    if (!topicFilter) {
      throw new Error('topicFilter not found')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
541 542
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
543 544
    let filterFunction: string

545
    if (topicFilter instanceof RegExp) {
546
      filterFunction = `(function (c) { return ${topicFilter.toString()}.test(c) })`
547
    } else if (typeof topicFilter === 'string') {
548
      topicFilter = topicFilter.replace(/'/g, '\\\'')
549
      filterFunction = `(function (c) { return c === '${topicFilter}' })`
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
550
    } else {
551
      throw new Error('unsupport topic type')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
552 553
    }

554 555 556 557 558 559 560 561
    const roomList = await Config.puppetInstance()
                                  .roomFind(filterFunction)
                                  .catch(e => {
                                    log.verbose('Room', 'findAll() rejected: %s', e.message)
                                    return [] // fail safe
                                  })

    for (let i = 0; i < roomList.length; i++) {
562 563 564 565
      await roomList[i].ready()
    }

    return roomList
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
566 567
  }

568 569 570 571 572 573
  /**
   * try to find a room by filter: {topic: string | RegExp}
   * @param {RoomQueryFilter} query
   * @returns {Promise<Room | null>} If can find the room, return Room, or return null
   */
  public static async find(query: RoomQueryFilter): Promise<Room | null> {
574
    log.verbose('Room', 'find({ topic: %s })', query.topic)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
575

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
576 577
    const roomList = await Room.findAll(query)
    if (!roomList || roomList.length < 1) {
578
      return null
579 580
    } else if (roomList.length > 1) {
      log.warn('Room', 'find() got more than one result, return the 1st one.')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
581
    }
582
    return roomList[0]
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
583 584
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
585 586 587
  /**
   * @todo document me
   */
588 589 590 591
  public static load(id: string): Room {
    if (!id) {
      throw new Error('Room.load() no id')
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
592 593 594 595 596 597 598

    if (id in Room.pool) {
      return Room.pool[id]
    }
    return Room.pool[id] = new Room(id)
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
599
}
Huan (李卓桓)'s avatar
merge  
Huan (李卓桓) 已提交
600 601

export default Room