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

Merge branch 'master' of github.com:wechaty/wechaty

......@@ -16,22 +16,17 @@ Wechaty is a Bot Framework for Wechat **Personal** Account that help you easy cr
# ATTENTION
## Wechaty is just converted to Typescript from Javascript.
## Wechaty was converted to Typescript from Javascript on 12th Oct 2016.
Details: https://github.com/wechaty/wechaty/issues/40
```diff
+ Typescripted ...
- Please do not clone/pull until you are really want to play with typescript wechaty in alpha stage.
+ Typescriptilized ...
```
VSCode is recommended for typescript because we can get benifit of [intelligent code completion, parameter info, and member lists](https://code.visualstudio.com/docs/languages/javascript).
The last Javascript version is: [v0.4.0](https://github.com/wechaty/wechaty/releases/tag/v0.4.0) (2016/10/9)
Or install v0.4 via `npm install wechaty`
Thanks.
The last Javascript version is: [v0.4.0](https://github.com/wechaty/wechaty/releases/tag/v0.4.0) (2016/10/9) , or install v0.4 by `npm install wechaty`.
- See Also: [Why we decided to move from plain JavaScript to TypeScript for Babylon.js](https://www.eternalcoding.com/?p=103)
......@@ -713,8 +708,8 @@ npm test
## v0.5.0 master (2016/10) The First Typescript Version
1. [#40](https://github.com/wechaty/wechaty/issues/40) Converted to Typescript (2016/10/11)
1. [#41](https://github.com/wechaty/wechaty/issues/41) added `say()` method to Contact/Room instance, and to `this` inside wechaty event listeners, to make them `Sayable`
1. BREAKING CHANGE: global event `scan` arguments changed from 1 to 2: now is (url: string, code: number) instead of {url, code} before.
1. [#41](https://github.com/wechaty/wechaty/issues/41) Sayablization: Make Wechaty/Contact/Room `Sayable`, and all `this` inside wechaty event listeners are `Sayable` too.
1. BREAKING CHANGE: global event `scan` listener arguments changed from 1 to 2: now is `function(this: Sayable, url: string, code: number)` instead of `function({url, code})` before.
## [v0.4.0](https://github.com/wechaty/wechaty/releases/tag/v0.4.0) (2016/10/9) The Latest Javascript Version
1. [#32](https://github.com/wechaty/wechaty/issues/32) Extend Room Class with:
......@@ -724,9 +719,9 @@ npm test
1. Add/Del/Topic for Room
1. Other methods like nick/member/has/etc...
1. [#33](https://github.com/wechaty/wechaty/issues/33) New Class `FriendRequest` with:
1. `Wechaty.on('friend', (contact, request) => {})` with Wechaty new Event `friend`
1. `accept()` to accept a friend request
1. `send()` to send new friend request
1. `Wechaty.on('friend', function(contact: Contact, request: FriendRequest) {})` with Wechaty new Event `friend`
1. `request.accept()` to accept a friend request
1. `requestsend()` to send new friend request
## v0.3.13 (2016/09)
1. Managed by Cloud Manager: https://app.wechaty.io
......
......@@ -19,28 +19,20 @@
*
* put name of one of your friend here, or create room will not work.
*
* ::: ___CHANGE ME___ :::
* vvvvvvvvvvvvvvv
* ::::::::: ___CHANGE ME___ :::::::::
* vvvvvvvvv
* vvvvvvvvv
* vvvvvvvvv
*/
const HELPER_CONTACT_NAME = 'Bruce LEE'
/**
* ^^^^^^^^^
* ^^^^^^^^^
* ^^^^^^^^^
* ::::::::: ___CHANGE ME___ :::::::::
*
*/
import {
Config
......@@ -312,7 +304,6 @@ function checkRoomJoin(room: Room, invitee: Contact|Contact[] , inviter: Contact
, invitee
)
room.topic('ding - warn ' + inviter.name())
setTimeout(_ => {
Array.isArray(invitee)
......
......@@ -144,4 +144,6 @@ export interface Sayable {
say(content: string, replyTo?: any): Promise<any>
}
export * from './brolog-env'
export default Config
......@@ -29,8 +29,8 @@ type BrowserSetting = {
class Browser extends EventEmitter {
private _targetState
private _currentState
private _targetState: string
private _currentState: string
public driver: WebDriver
......@@ -54,7 +54,7 @@ class Browser extends EventEmitter {
}
// currentState : 'opening' | 'open' | 'closing' | 'close'
private currentState(newState?) {
public currentState(newState?) {
if (newState) {
log.verbose('PuppetWebBrowser', 'currentState(%s)', newState)
this._currentState = newState
......@@ -264,32 +264,35 @@ class Browser extends EventEmitter {
return driver
}
public async quit(restart?: boolean): Promise<any> {
public async restart(): Promise<void> {
log.verbose('PuppetWebBrowser', 'restart()')
await this.quit()
if (this.targetState() !== 'open'
&& this.currentState() !== 'opening') {
await this.init()
}
}
public async quit(): Promise<any> {
log.verbose('PuppetWebBrowser', 'quit()')
if (!restart) {
this.targetState('close')
this.currentState('closing')
}
// this.live = false
if (!this.driver) {
log.verbose('PuppetWebBrowser', 'driver.quit() skipped because no driver')
this.currentState('close')
return Promise.resolve('no driver')
return 'no driver'
} else if (!this.driver.getSession()) {
this.driver = null
log.verbose('PuppetWebBrowser', 'driver.quit() skipped because no driver session')
this.currentState('close')
return Promise.resolve('no driver session')
return 'no driver session'
}
// return co.call(this, function* () {
try {
log.silly('PuppetWebBrowser', 'quit() co()')
await this.driver.close() // http://stackoverflow.com/a/32341885/1123955
log.silly('PuppetWebBrowser', 'quit() driver.close()-ed')
await this.driver.quit()
......@@ -304,10 +307,8 @@ class Browser extends EventEmitter {
*
*/
await this.clean()
this.currentState('close')
log.silly('PuppetWebBrowser', 'quit() co() end')
// }).catch(e => {
} catch (e) {
// console.log(e)
// log.warn('PuppetWebBrowser', 'err: %s %s %s %s', e.code, e.errno, e.syscall, e.message)
......@@ -323,12 +324,11 @@ class Browser extends EventEmitter {
if (crashRegex.test(e.message)) { log.warn('PuppetWebBrowser', 'driver.quit() browser crashed') }
else { log.warn('PuppetWebBrowser', 'driver.quit() exception: %s', e.message) }
// XXX fail safe to `close` ?
this.currentState('close')
/* fail safe */
}
this.currentState('close')
return
}
......@@ -494,61 +494,58 @@ class Browser extends EventEmitter {
* check whether browser is full functional
*
*/
public readyLive(): Promise<any> {
public async readyLive(): Promise<boolean> {
log.verbose('PuppetWebBrowser', 'readyLive()')
if (this.dead()) {
return Promise.reject(new Error('this.dead() true'))
log.silly('PuppetWebBrowser', 'readyLive() dead() is true')
return false
}
return new Promise((resolve, reject) => {
this.execute('return 1+1')
.then(r => {
if (r === 2) {
resolve(true) // browser ok, living
return
let two
try {
two = await this.execute('return 1+1')
} catch (e) {
two = e && e.message
}
const errMsg = 'deadEx() found dead browser coz 1+1 = ' + r + ' (not 2)'
log.verbose('PuppetWebBrowser', errMsg)
this.dead(errMsg)
reject(new Error(errMsg)) // browser not ok, dead
return
})
.catch(e => {
const errMsg = 'deadEx() found dead browser coz 1+1 = ' + e.message
log.verbose('PuppetWebBrowser', errMsg)
if (two === 2) {
return true // browser ok, living
}
const errMsg = 'found dead browser coz 1+1 = ' + two + ' (not 2)'
log.warn('PuppetWebBrowser', 'readyLive() %s', errMsg)
this.dead(errMsg)
reject(new Error(errMsg)) // browser not live
return
})
})
return false // browser not ok, dead
}
public dead(forceReason?) {
let errMsg
public dead(forceReason?: string): boolean {
log.verbose('PuppetWebBrowser', 'dead(%s)', forceReason ? forceReason : '')
let msg
let dead = false
if (forceReason) {
dead = true
errMsg = forceReason
// } else if (!this.live) {
msg = forceReason
} else if (this.targetState() !== 'open') {
dead = true
// errMsg = 'browser not live'
errMsg = 'targetState not open'
msg = 'targetState not open'
} else if (!this.driver || !this.driver.getSession()) {
dead = true
errMsg = 'no driver or session'
msg = 'no driver or session'
}
if (dead) {
log.warn('PuppetWebBrowser', 'dead() because %s', errMsg)
// this.live = false
log.warn('PuppetWebBrowser', 'dead() because %s', msg)
this.currentState('closing')
this.quit().then(_ => this.currentState('close'))
this.restart().then(_ => this.currentState('close'))
// must use nextTick here, or promise will hang... 2016/6/10
process.nextTick(_ => {
log.verbose('PuppetWebBrowser', 'dead() emit a `dead` event because %s', errMsg)
this.emit('dead', errMsg)
setImmediate(_ => {
log.verbose('PuppetWebBrowser', 'dead() emit a `dead` event because %s', msg)
this.emit('dead', msg)
})
}
return dead
......
......@@ -47,7 +47,7 @@ const PuppetWebEvent = {
, onServerMessage
}
async function onBrowserDead(e): Promise<void> {
async function onBrowserDead(this: PuppetWeb, e): Promise<void> {
log.verbose('PuppetWebEvent', 'onBrowserDead(%s)', e && e.message || e)
// because this function is async, so maybe entry more than one times.
// guard by variable: isBrowserBirthing to prevent the 2nd time entrance.
......@@ -88,9 +88,9 @@ async function onBrowserDead(e): Promise<void> {
log.verbose('PuppetWebEvent', 'onBrowserDead() try to reborn browser')
await this.browser.quit(true)
.catch(error => { // fail safe
log.warn('PuppetWebEvent', 'browser.quit() exception: %s, %s', error.message, error.stack)
await this.browser.restart()
.catch((err: Error) => { // fail safe
log.warn('PuppetWebEvent', 'browser.quit() exception: %s', err.stack)
})
log.verbose('PuppetWebEvent', 'onBrowserDead() old browser quited')
......@@ -135,7 +135,7 @@ async function onBrowserDead(e): Promise<void> {
return
}
function onServerDing(data) {
function onServerDing(this: PuppetWeb, data) {
log.silly('PuppetWebEvent', 'onServerDing(%s)', data)
// this.watchDog(data)
this.emit('watchdog', { data })
......@@ -172,8 +172,8 @@ function onServerConnection(data) {
log.verbose('PuppetWebEvent', 'onServerConnection: %s', data)
}
function onServerDisconnect(data) {
log.verbose('PuppetWebEvent', 'onServerDisconnect: %s', data)
async function onServerDisconnect(this: PuppetWeb, data): Promise<void> {
log.verbose('PuppetWebEvent', 'onServerDisconnect(%s)', data)
if (this.userId) {
log.verbose('PuppetWebEvent', 'onServerDisconnect() there has userId set. emit a logout event and set userId to null')
......@@ -206,8 +206,14 @@ function onServerDisconnect(data) {
return
}
this.browser.readyLive()
.then(r => { // browser is alive, and we have a bridge to it
const live = await this.browser.readyLive()
if (!live) { // browser is in indeed dead, or almost dead. readyLive() will auto recover itself.
log.verbose('PuppetWebEvent', 'onServerDisconnect() browser dead after readyLive() check. waiting it recover itself')
return
}
// browser is alive, and we have a bridge to it
log.verbose('PuppetWebEvent', 'onServerDisconnect() re-initing bridge')
// must use setTimeout to wait a while.
// because the browser has just refreshed, need some time to re-init to be ready.
......@@ -217,20 +223,16 @@ function onServerDisconnect(data) {
if (!this.bridge) {
// XXX: sometimes this.bridge gone in this timeout. why?
// what's happend between the last if(!this.bridge) check and the timeout call?
throw new Error('bridge gone after setTimeout? why???')
const e = new Error('bridge gone after setTimeout? why???')
log.warn('PuppetWebEvent', 'onServerDisconnect() setTimeout() %s', e.message)
throw e
}
this.bridge.init()
.then(ret => {
log.verbose('PuppetWebEvent', 'onServerDisconnect() bridge re-inited: %s', ret)
})
.catch(e => log.error('PuppetWebEvent', 'onServerDisconnect() exception: [%s]', e))
.then(ret => log.verbose('PuppetWebEvent', 'onServerDisconnect() setTimeout() bridge re-inited: %s', ret))
.catch(e => log.error('PuppetWebEvent', 'onServerDisconnect() setTimeout() exception: [%s]', e))
}, 1000) // 1 second instead of 10 seconds? try. (should be enough to wait)
return
})
.catch(e => { // browser is in indeed dead, or almost dead. readyLive() will auto recover itself.
log.verbose('PuppetWebEvent', 'onServerDisconnect() browser dead, waiting it recover itself: %s', e.message)
return
})
}
/**
......@@ -246,7 +248,7 @@ function onServerDisconnect(data) {
* 3. browser quit(crash?)
* 4. ...
*/
function onServerUnload(data) {
function onServerUnload(this: PuppetWeb, data) {
log.warn('PuppetWebEvent', 'onServerUnload(%s)', data)
// onServerLogout.call(this, data) // XXX: should emit event[logout] from browser
......@@ -286,7 +288,7 @@ function onServerLog(data) {
log.silly('PuppetWebEvent', 'onServerLog(%s)', data)
}
async function onServerLogin(data, attempt = 0): Promise<void> {
async function onServerLogin(this: PuppetWeb, data, attempt = 0): Promise<void> {
log.verbose('PuppetWebEvent', 'onServerLogin(%s, %d)', data, attempt)
this.scan = null
......@@ -332,7 +334,7 @@ async function onServerLogin(data, attempt = 0): Promise<void> {
return
}
function onServerLogout(data) {
function onServerLogout(this: PuppetWeb, data) {
this.emit('logout', this.user || this.userId)
if (!this.user && !this.userId) {
......@@ -348,7 +350,7 @@ function onServerLogout(data) {
// })
}
async function onServerMessage(data): Promise<void> {
async function onServerMessage(this: PuppetWeb, data): Promise<void> {
let m = new Message(data)
// co.call(this, function* () {
......
......@@ -171,7 +171,7 @@ export class PuppetWeb extends Puppet {
// })
}
private async initBrowser(): Promise<Browser> {
public async initBrowser(): Promise<Browser> {
log.verbose('PuppetWeb', 'initBrowser()')
const browser = new Browser({
head: this.setting.head
......@@ -199,7 +199,7 @@ export class PuppetWeb extends Puppet {
return browser // follow func name meaning
}
private initBridge(): Promise<Bridge> {
public initBridge(): Promise<Bridge> {
log.verbose('PuppetWeb', 'initBridge()')
const bridge = new Bridge(
this // use puppet instead of browser, is because browser might change(die) duaring run time
......@@ -358,7 +358,7 @@ export class PuppetWeb extends Puppet {
})
}
public logined() { return !!(this.user) }
public ding(data: any): Promise<any> {
public ding(data?: any): Promise<any> {
if (!this.bridge) {
return Promise.reject(new Error('ding fail: no bridge(yet)!'))
}
......
......@@ -130,7 +130,7 @@ class Server extends EventEmitter {
this.emit('connection', client)
client.on('disconnect', e => {
log.silly('PuppetWebServer', 'socket.io disconnect: %s', e)
log.silly('PuppetWebServer', 'initEventsFromClient() on(discohnnect) socket.io disconnect: %s', e)
// 1. Browser reload / 2. Lost connection(Bad network)
this.socketClient = undefined
this.emit('disconnect', e)
......@@ -138,7 +138,7 @@ class Server extends EventEmitter {
client.on('error' , e => {
// log.error('PuppetWebServer', 'initEventsFromClient() client on error: %s', e)
log.error('PuppetWebServer', 'initEventsFromClient() client on error: %s', e.stack)
log.error('PuppetWebServer', 'initEventsFromClient() on(error): %s', e.stack)
})
// Events from Wechaty@Broswer --to--> Server
......
......@@ -10,6 +10,7 @@
*
* Helper Class for Manage State Change
*/
import { log } from './config'
/**
* A - State A
......@@ -21,7 +22,9 @@ export class StateMonitor <A, B>{
private _current: A|B
private _stable: boolean
constructor(initState: A|B) {
constructor(private client: string, initState: A|B) {
log.verbose('StateMonitor', 'constructor(%s, %s)', client, initState)
this.target(initState)
this.current(initState)
this.stable(true)
......@@ -29,22 +32,42 @@ export class StateMonitor <A, B>{
public target(newState?: A|B): A|B {
if (newState) {
log.verbose('StateMonitor', 'target(%s) %s state change from %s to %s'
, this.client, newState
, this._target, newState
)
this._target = newState
} else {
log.verbose('StateMonitor', 'target() %s state is %s', this.client, this._target)
}
return this._target
}
public current(newState?: A|B, stable = true): A|B {
if (newState) {
log.verbose('StateMonitor', 'current(%s, %s) %s state change from %s:%s to %s:%s'
, newState, stable
, this.client
, this._current, this._stable
, newState, stable
)
this._current = newState
this.stable(stable)
this._stable = stable
} else {
log.verbose('StateMonitor', 'current() %s state is %s', this.client, this._current)
}
return this._current
}
public stable(isStable?: boolean) {
if (typeof isStable === 'boolean') {
this._stable = isStable
public stable(stable?: boolean) {
if (typeof stable === 'boolean') {
log.verbose('StateMonitor', 'stable(%s) %s state change from %s to %s'
, stable
, this.client
, this._stable, stable)
this._stable = stable
} else {
log.verbose('StateMonitor', 'stable() %s state is %s', this.client, this._stable)
}
return this._stable
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册