friendship.ts 9.2 KB
Newer Older
1
/**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
2
 *   Wechaty - https://github.com/chatie/wechaty
3
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
4
 *   @copyright 2016-2018 Huan LI <zixia@zixia.net>
5
 *
6 7 8
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
9
 *
10 11 12 13 14 15 16
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
L
lijiarui 已提交
17
 *   @ignore
18 19 20
 *
 */

21
 /* tslint:disable:no-var-requires */
22
// const retryPromise  = require('retry-promise').default
23 24 25
import {
  instanceToClass,
}                   from 'clone-class'
26

27 28
import {
  Accessory,
29
}                   from '../accessory'
30 31
import {
  log,
32
}                   from '../config'
33
import {
34 35
  tryWait,
}                   from '../helper-functions'
36

37
import {
38 39
  FriendshipPayload,
  FriendshipType,
40
}                         from 'wechaty-puppet'
41 42 43 44

import {
  Contact,
}                   from './contact'
45

L
lijiarui 已提交
46 47 48 49 50 51 52
/**
 * Send, receive friend request, and friend confirmation events.
 *
 * 1. send request
 * 2. receive request(in friend event)
 * 3. confirmation friendship(friend event)
 *
L
lijiarui 已提交
53
 * [Examples/Friend-Bot]{@link https://github.com/Chatie/wechaty/blob/1523c5e02be46ebe2cc172a744b2fbe53351540e/examples/friend-bot.ts}
L
lijiarui 已提交
54
 */
55
export class Friendship extends Accessory {
56

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
57
  // tslint:disable-next-line:variable-name
58
  public static Type = FriendshipType
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
59

60
  public static load<T extends typeof Friendship> (
61 62
    this : T,
    id   : string,
63
  ): T['prototype'] {
64
    const newFriendship = new (this as any)(id)
65
    // newFriendRequest.payload = this.puppet.cacheFriendRequestPayload.get(id)
66
    return newFriendship
67 68
  }

69
  /**
L
lijiarui 已提交
70 71 72
   * @description
   * use {@link Friendship#add} instead
   * @deprecated
73
   */
74
  public static async send (contact: Contact,  hello: string) {
75 76 77
    log.warn('Friendship', 'static send() DEPRECATED, use add() instead.')
    return this.add(contact, hello)
  }
L
lijiarui 已提交
78

79 80
  /**
   * Send a Friend Request to a `contact` with message `hello`.
L
lijiarui 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93
   *
   * The best practice is to send friend request once per minute.
   * Remeber not to do this too frequently, or your account may be blocked.
   *
   * @param {Contact} contact - Send friend request to contact
   * @param {string} hello    - The friend request content
   * @returns {Promise<void>}
   *
   * @example
   * const memberList = await room.memberList()
   * for (let i = 0; i < memberList.length; i++) {
   *   await bot.Friendship.add(member, 'Nice to meet you! I am wechaty bot!')
   * }
94
   */
95
  public static async add (
96 97
    contact : Contact,
    hello   : string,
98
  ): Promise<void> {
99
    log.verbose('Friendship', 'static add(%s, %s)',
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
100 101
                                  contact.id,
                                  hello,
102
                )
103 104 105
    await this.puppet.friendshipAdd(contact.id, hello)
  }

106
  public static async del (
107 108 109 110
    contact: Contact,
  ): Promise<void> {
    log.verbose('Friendship', 'static del(%s)', contact.id)
    throw new Error('to be implemented')
111 112
  }

113 114 115
  // public static createConfirm(
  //   contactId: string,
  // ): FriendRequestPayload {
116
  //   log.verbose('Friendship', 'createConfirm(%s)',
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
  //                                         contactId,
  //               )

  //   const payload: FriendRequestPayloadConfirm = {
  //     type : FriendRequestType.Confirm,
  //     contactId,
  //   }

  //   return payload
  // }

  // public static createReceive(
  //   contactId : string,
  //   hello     : string,
  //   ticket    : string,
  // ): FriendRequestPayload {
133
  //   log.verbose('Friendship', 'createReceive(%s, %s, %s)',
134 135 136 137 138 139 140 141 142 143 144 145 146 147
  //                                         contactId,
  //                                         hello,
  //                                         ticket,
  //               )

  //   const payload: FriendRequestPayloadReceive = {
  //     type : FriendRequestType.Receive,
  //     contactId,
  //     hello,
  //     ticket,
  //   }

  //   return payload
  // }
148

149 150 151 152 153
  /**
   *
   * Instance Properties
   *
   */
154

L
lijiarui 已提交
155 156 157
  /**
   * @ignore
   */
158
  protected get payload (): undefined | FriendshipPayload {
159 160 161 162
    if (!this.id) {
      return undefined
    }

163
    return this.puppet.friendshipPayloadCache(this.id)
164
  }
165

166
  constructor (
167
    public id: string,
168 169
  ) {
    super()
170
    log.verbose('Friendship', 'constructor(id=%s)', id)
171 172

    // tslint:disable-next-line:variable-name
173
    const MyClass = instanceToClass(this, Friendship)
174

175 176
    if (MyClass === Friendship) {
      throw new Error('Friendship class can not be instanciated directly! See: https://github.com/Chatie/wechaty/issues/1217')
177 178 179
    }

    if (!this.puppet) {
180
      throw new Error('Friendship class can not be instanciated without a puppet!')
181
    }
182 183
  }

184
  public toString () {
185
    if (!this.payload) {
186
      return this.constructor.name
187
    }
188
    return [
189 190
      'Friendship#',
      FriendshipType[this.payload.type],
191 192 193 194
      '<',
      this.payload.contactId,
      '>',
    ].join('')
195 196
  }

197
  public isReady (): boolean {
198 199 200 201
    return !!this.payload && (Object.keys(this.payload).length > 0)
  }

  /**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
202
   * no `dirty` support because Friendship has no rawPayload(yet)
L
lijiarui 已提交
203
   * @ignore
204
   */
205
  public async ready (): Promise<void> {
206 207 208 209
    if (this.payload) {
      return
    }

210
    await this.puppet.friendshipPayload(this.id)
211

212 213 214 215 216
    if (!this.payload) {
      throw new Error('no payload')
    }
  }

L
lijiarui 已提交
217 218 219 220 221 222 223 224 225 226 227 228
  /**
   * Accept Friend Request
   *
   * @returns {Promise<void>}
   *
   * @example
   * const bot = new Wechaty()
   * bot.on('friendship', async friendship => {
   *   try {
   *     console.log(`received friend event.`)
   *     switch (friendship.type()) {
   *
L
lijiarui 已提交
229
   *     // 1. New Friend Request
L
lijiarui 已提交
230 231 232 233 234
   *
   *     case Friendship.Type.Receive:
   *       await friendship.accept()
   *       break
   *
L
lijiarui 已提交
235
   *     // 2. Friend Ship Confirmed
L
lijiarui 已提交
236 237 238 239 240 241 242 243 244 245 246
   *
   *     case Friendship.Type.Confirm:
   *       console.log(`friend ship confirmed`)
   *       break
   *     }
   *   } catch (e) {
   *     console.error(e)
   *   }
   * }
   * .start()
   */
247
  public async accept (): Promise<void> {
248
    log.verbose('Friendship', 'accept()')
249

250 251 252 253
    if (!this.payload) {
      throw new Error('no payload')
    }

254
    if (this.payload.type !== Friendship.Type.Receive) {
255
      throw new Error('accept() need type to be FriendshipType.Receive, but it got a ' + Friendship.Type[this.payload.type])
256
    }
257

258
    log.silly('Friendship', 'accept() to %s', this.payload.contactId)
259

260
    await this.puppet.friendshipAccept(this.id)
261

262
    const contact = this.contact()
263

264
    await tryWait(async (retry, attempt) => {
265
      log.silly('Friendship', 'accept() retry() ready() attempt %d', attempt)
266

267
      await contact.ready()
268

269
      if (contact.isReady()) {
270
        log.verbose('Friendship', 'accept() with contact %s ready()', contact.name())
271 272
        return
      }
273
      retry(new Error('Friendship.accept() content.ready() not ready'))
274 275

    }).catch((e: Error) => {
276
      log.warn('Friendship', 'accept() contact %s not ready because of %s', contact, e && e.message || e)
277 278 279 280
    })

  }

L
lijiarui 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
  /**
   * Get verify message from
   *
   * @returns {string}
   * @example <caption>If request content is `ding`, then accept the friendship</caption>
   * const bot = new Wechaty()
   * bot.on('friendship', async friendship => {
   *   try {
   *     console.log(`received friend event from ${friendship.contact().name()}`)
   *     if (friendship.type() === Friendship.Type.Receive && friendship.hello() === 'ding') {
   *       await friendship.accept()
   *     }
   *   } catch (e) {
   *     console.error(e)
   *   }
   * }
   * .start()
   */
299
  public hello (): string {
300 301 302 303 304 305
    if (!this.payload) {
      throw new Error('no payload')
    }
    return this.payload.hello || ''
  }

L
lijiarui 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318
  /**
   * Get the contact from friendship
   *
   * @returns {Contact}
   * @example
   * const bot = new Wechaty()
   * bot.on('friendship', async friendship => {
   *   const contact = friendship.contact()
   *   const name = contact.name()
   *   console.log(`received friend event from ${name}`)
   * }
   * .start()
   */
319
  public contact (): Contact {
320 321 322
    if (!this.payload) {
      throw new Error('no payload')
    }
323

324
    const contact = this.wechaty.Contact.load(this.payload.contactId)
325
    return contact
326
  }
327

L
lijiarui 已提交
328 329 330
  /**
   * @ignore
   */
331
  public async reject (): Promise<void> {
332
    log.warn('Friendship', 'reject() not necessary, NOP.')
333 334
    return
  }
335

L
lijiarui 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
  /**
   * Return the Friendship Type
   * > Tips: FriendshipType is enum here. </br>
   * - FriendshipType.Unknown  </br>
   * - FriendshipType.Confirm  </br>
   * - FriendshipType.Receive  </br>
   * - FriendshipType.Verify   </br>
   *
   * @returns {FriendshipType}
   *
   * @example <caption>If request content is `ding`, then accept the friendship</caption>
   * const bot = new Wechaty()
   * bot.on('friendship', async friendship => {
   *   try {
   *     if (friendship.type() === Friendship.Type.Receive && friendship.hello() === 'ding') {
   *       await friendship.accept()
   *     }
   *   } catch (e) {
   *     console.error(e)
   *   }
   * }
   * .start()
   */
359
  public type (): FriendshipType {
360 361
    return this.payload
            ? this.payload.type
362
            : FriendshipType.Unknown
363
  }
364 365

}
Huan (李卓桓)'s avatar
merge  
Huan (李卓桓) 已提交
366

367
export default Friendship