watchdog.ts 4.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/**
 *
 * wechaty: Wechat for Bot. and for human who talk to bot/robot
 *
 * Class PuppetWeb Watchdog
 *
 * monitor puppet
 *
 * Licenst: ISC
 * https://github.com/zixia/wechaty
 *
 */

/**************************************
 *
 * Class PuppetWeb
 *
 ***************************************/
const co    = require('co')

21
const log   = require('../brolog-env')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
22
const Event = require('./event')
23 24 25 26 27 28 29 30

const Watchdog = {
  onFeed
}

// feed me in time(after 1st feed), or I'll restart system
function onFeed({
  data
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
31
  , type = 'HEARTBEAT'
32 33 34
  , timeout = 60000  // 60s default. can be override in options but be careful about the number zero(0)
} = {}) {

35 36 37
  // change to tape instead of tap
  // type = type || 'HEARTBEAT'  // BUG compatible with issue: node-tap strange behaviour cause CircleCI & Travis-CI keep failing #11
  // timeout = timeout || 60000  // BUG compatible with issue: node-tap strange behaviour cause CircleCI & Travis-CI keep failing #11
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
38

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
39
  if (!this) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
40
    throw new Error('onFeed() must has `this` of instanceof PuppetWeb')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
41 42
  }

43
  const feed = `${type}:[${data}]`
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
44
  log.silly('PuppetWebWatchdog', 'onFeed: %d, %s', timeout, feed)
45

46
  if (this.currentState() === 'killing'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
47
  ) {
48
    log.warn('PuppetWebWatchdog', 'onFeed() is disabled because currentState is `killing`')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
49 50
    return
  }
51 52 53 54 55 56
  // if (this.readyState() === 'disconnecting'
  //     // || this.readyState() === 'disconnected'
  // ) {
  //   log.warn('PuppetWebWatchdog', 'onFeed() is disabled because readyState is `disconnecting`')
  //   return
  // }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
57

58
  setWatchDogTimer.call(this, timeout, feed)
59

60
  this.emit('heartbeat', feed)
61

62 63
  monitorScan.call(this, type)
  autoSaveSession.call(this)
64

65 66 67 68
  switch (type) {
    case 'POISON':
      clearWatchDogTimer.call(this)
      break
69

70 71 72
    case 'SCAN':
    case 'HEARTBEAT':
      break
73

74 75 76
    default:
      throw new Error('Watchdog onFeed: unsupport type ' + type)
  }
77 78 79 80 81 82
}

function clearWatchDogTimer() {
  if (this.watchDogTimer) {
    clearTimeout(this.watchDogTimer)
    this.watchDogTimer = null
83 84

    const timeLeft = this.watchDogTimerTime - Date.now()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
85
    log.silly('PuppetWebWatchdog', 'clearWatchDogTimer() [%d] seconds left', Math.ceil(timeLeft / 1000))
86
  } else {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
87
    log.silly('PuppetWebWatchdog', 'clearWatchDogTimer() nothing to clear')
88 89 90
  }
}

91
function setWatchDogTimer(timeout, feed) {
92 93 94

  clearWatchDogTimer.call(this)

95
  log.silly('PuppetWebWatchdog', 'setWatchDogTimer(%d, %s)', timeout, feed)
96

97
  this.watchDogTimer = setTimeout(watchDogReset.bind(this, timeout, feed), timeout)
98
  // this.watchDogTimer.unref()
99
  this.watchDogTimerTime = Date.now() + timeout
100 101 102
  // block quit, force to use quit() // this.watchDogTimer.unref() // dont block quit
}

103
function watchDogReset(timeout, lastFeed) {
104
  log.verbose('PuppetWebWatchdog', 'watchDogReset() timeout %d', timeout)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
105 106
  const e = new Error('watchdog reset after '
                        + Math.floor(timeout/1000)
107 108 109
                        + ' seconds, last feed:'
                        + '[' + lastFeed + ']'
                    )
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
  this.emit('error', e)
  return Event.onBrowserDead.call(this, e)
}

/**
 *
 * Deal with Sessions(cookies)
 * save every 5 mins
 *
 */
function autoSaveSession() {
  const SAVE_SESSION_INTERVAL = 5 * 60 * 1000 // 5 mins
  if (Date.now() - this.watchDogLastSaveSession > SAVE_SESSION_INTERVAL) {
    log.verbose('PuppetWebWatchdog', 'watchDog() saveSession(%s) after %d minutes', this.profile, Math.floor(SAVE_SESSION_INTERVAL/1000/60))
    this.browser.saveSession()
    this.watchDogLastSaveSession = Date.now()
  }
}

/**
 *
 * Deal with SCAN events
 *
 * if web browser stay at login qrcode page long time,
 * sometimes the qrcode will not refresh, leave there expired.
 * so we need to refresh the page after a while
 *
 */
function monitorScan(type) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
139
  log.silly('PuppetWebWatchdog', 'monitorScan(%s)', type)
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

  const scanTimeout = 10 * 60 * 1000 // 10 mins

  if (type === 'SCAN') { // watchDog was feed a 'scan' data
    this.lastScanEventTime = Date.now()
  }
  if (this.logined()) { // XXX: login status right?
    this.lastScanEventTime = null
  } else if (this.lastScanEventTime
              && Date.now() - this.lastScanEventTime > scanTimeout) {
    log.warn('PuppetWebWatchdog', 'monirotScan() refresh browser for no food of type scan after %s mins', Math.floor(scanTimeout/1000/60))
    // try to fix the problem
    this.browser.refresh()
    this.lastScanEventTime = Date.now()
  }
}

157
module.exports = Watchdog