friend-request.ts 6.8 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
import { instanceToClass } from 'clone-class'
Huan (李卓桓)'s avatar
wip...  
Huan (李卓桓) 已提交
24

25 26 27
import { PuppetAccessory }  from './puppet-accessory'

import { Contact }          from './contact'
28

29 30
import {
  log,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
31
}                       from './config'
32
import { Misc }         from './misc'
33

34
export enum FriendRequestType {
35 36 37 38
  Unknown = 0,
  Send,
  Receive,
  Confirm,
39 40
}

41
export interface FriendRequestPayload {
42 43 44 45
  contactId? : string,
  hello?     : string,
  ticket?    : string
  type?      : FriendRequestType,
46 47
}

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

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

62 63 64 65 66 67 68 69 70 71
  public static createSend(
    contact : Contact,
    hello   : string,
    ): FriendRequest {
    log.verbose('PuppeteerFriendRequest', 'createSend(%s, %s)',
                                          contact,
                                          hello,
                )

    const sentRequest = new this({
72 73
      type      : FriendRequestType.Send,
      contactId : contact.id,
74 75 76 77 78 79 80 81 82 83 84 85 86 87
      hello,
    })

    return sentRequest
  }

  public static createConfirm(
    contact: Contact,
  ): FriendRequest {
    log.verbose('PuppeteerFriendRequest', 'createConfirm(%s)',
                                          contact,
                )

    const confirmedRequest = new this({
88 89
      type      : FriendRequestType.Confirm,
      contactId : contact.id,
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    })

    return confirmedRequest
  }

  public static createReceive(
    contact : Contact,
    hello   : string,
    ticket  : string,
  ): FriendRequest {
    log.verbose('PuppeteerFriendRequest', 'createReceive(%s, %s, %s)',
                                          contact,
                                          hello,
                                          ticket,
                )

    const receivedRequest = new this({
107 108
      type      : FriendRequestType.Receive,
      contactId : contact.id,
109 110 111 112 113 114 115 116 117 118 119 120
      hello,
      ticket,
    })

    return receivedRequest
  }

  /**
   *
   * Instance Properties
   *
   */
121
  constructor(
122 123 124 125
    protected payload?: FriendRequestPayload,
  ) {
    super()
    log.verbose('PuppeteerFriendRequest', 'constructor(%s)', payload)
126 127 128 129 130 131 132 133 134 135 136

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

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

    if (!this.puppet) {
      throw new Error('FriendRequest class can not be instanciated without a puppet!')
    }
137 138 139 140 141
  }

  public async send(): Promise<void> {
    if (!this.payload) {
      throw new Error('no payload')
142
    } else if (!this.payload.contactId) {
143
      throw new Error('no contact')
144
    } else if (this.payload.type !== FriendRequest.Type.Send) {
145 146
      throw new Error('not a send request')
    }
147
    log.verbose('PuppeteerFriendRequest', 'send() to %s', this.payload.contactId)
148 149

    await this.puppet.friendRequestSend(
150
      this.payload.contactId,
151 152 153 154 155 156 157
      this.payload.hello,
    )
  }

  public async accept(): Promise<void> {
    if (!this.payload) {
      throw new Error('no payload')
158 159
    } else if (!this.payload.contactId) {
      throw new Error('no contactId')
160 161
    } else if (!this.payload.ticket) {
      throw new Error('no ticket')
162
    } else if (this.payload.type !== FriendRequest.Type.Receive) {
163 164
      throw new Error('not a receive request, its a ' + FriendRequest.Type[this.payload.type!])
    }
165
    log.verbose('FriendRequest', 'accept() to %s', this.payload.contactId)
166

167
    await this.puppet.friendRequestAccept(this.payload.contactId, this.payload.ticket)
168

169 170 171
    // const max = 20
    // const backoff = 300
    // const timeout = max * (backoff * max) / 2
172 173 174 175 176 177
    // 20 / 300 => 63,000
    // max = (2*totalTime/backoff) ^ (1/2)
    // timeout = 11,250 for {max: 15, backoff: 100}

    // refresh to wait contact ready

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    // await retryPromise({ max: max, backoff: backoff }, async (attempt: number) => {
    //   log.silly('PuppeteerFriendRequest', 'accept() retryPromise() attempt %d with timeout %d', attempt, timeout)

    //   await this.contact().ready()

    //   if (this.contact().isReady()) {
    //     log.verbose('PuppeteerFriendRequest', 'accept() with contact %s ready()', this.contact().name())
    //     return
    //   }
    //   throw new Error('FriendRequest.accept() content.ready() not ready')

    // }).catch((e: Error) => {
    //   log.warn('PuppeteerFriendRequest', 'accept() rejected for contact %s because %s', this.contact, e && e.message || e)
    // })
    await Misc.retry(async (retry, attempt) => {
      log.silly('PuppeteerFriendRequest', 'accept() retryPromise() attempt %d', attempt)
194 195 196 197 198 199 200

      await this.contact().ready()

      if (this.contact().isReady()) {
        log.verbose('PuppeteerFriendRequest', 'accept() with contact %s ready()', this.contact().name())
        return
      }
201
      retry(new Error('FriendRequest.accept() content.ready() not ready'))
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

    }).catch((e: Error) => {
      log.warn('PuppeteerFriendRequest', 'accept() rejected for contact %s because %s', this.contact, e && e.message || e)
    })

  }

  public hello(): string {
    if (!this.payload) {
      throw new Error('no payload')
    }
    return this.payload.hello || ''
  }

  public contact(): Contact {
    if (!this.payload) {
      throw new Error('no payload')
219
    } else if (!this.payload.contactId) {
220 221
      throw new Error('no contact')
    }
222 223 224

    const contact = this.puppet.Contact.load(this.payload.contactId)
    return contact
225
  }
226

227 228 229 230
  public async reject(): Promise<void> {
    log.warn('PuppeteerFriendRequest', 'reject() not necessary, NOP.')
    return
  }
231

232 233 234 235 236 237 238 239
  public type(): FriendRequestType {
    if (!this.payload) {
      throw new Error('no payload')
    } else if (!this.payload.type) {
      throw new Error('no type')
    }
    return this.payload.type
  }
240 241

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

export default FriendRequest