未验证 提交 e9c0e448 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓) 提交者: GitHub

Merge branch 'master' into puppet-0602

{
"name": "wechaty",
"version": "0.15.107",
"version": "0.15.110",
"description": "Wechat for Bot(Personal Account)",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
......
......@@ -74,8 +74,6 @@ export class Bridge extends EventEmitter {
// private password? : string
// private nickname? : string
// private loginSucceed = false
private cacheRoomRawPayload : { [id: string]: PadchatRoomPayload }
private cacheContactRawPayload : { [id: string]: PadchatContactPayload }
......@@ -88,12 +86,12 @@ export class Bridge extends EventEmitter {
log.verbose('PuppetPadchatBridge', 'constructor()')
// this.userId = options.token
this.cacheRoomRawPayload = {}
this.cacheRoomRawPayload = {}
this.cacheContactRawPayload = {}
this.autoData = {}
this.state = new StateSwitch('PuppetPadchatBridge')
this.autoData = {}
this.padchatRpc = new PadchatRpc(options.endpoint, options.token)
this.state = new StateSwitch('PuppetPadchatBridge')
}
public async start(): Promise<void> {
......@@ -103,7 +101,7 @@ export class Bridge extends EventEmitter {
this.cacheContactRawPayload = {}
if (this.selfId) {
throw new Error('username exist')
throw new Error('selfId exist')
}
this.state.on('pending')
......@@ -153,10 +151,11 @@ export class Bridge extends EventEmitter {
this.stopLogin()
this.saveAutoData()
this.selfId = username
this.emit('login', this.selfId)
this.saveAutoData(this.selfId)
}
public logout(): void {
......@@ -272,47 +271,59 @@ export class Bridge extends EventEmitter {
return
}
/**
* Offline, then relogin
* emit qrcode or send login request to the user.
*/
protected async restoreLogin(): Promise<boolean> {
/**
* 1. The following `if/else` block: emit qrcode or send login request to the user.
*/
if (this.autoData && this.autoData.token) {
log.silly('PuppetPadchatBridge', `initLogin() autoData.token exist for %s`,
this.autoData.nick_name || 'no nick_name',
)
// Offline, then relogin
const autoLoginResult = await this.padchatRpc.WXAutoLogin(this.autoData.token)
if (autoLoginResult) {
if (autoLoginResult.status === 0) {
/**
* 1.1 Auto Login Success, return username as the result
*/
this.login(autoLoginResult.user_name)
return true
log.verbose('PuppetPadchatBridge', `initLogin()`)
} else {
/**
* 1.2. Send Login Request to User to be confirm(the same as the user had scaned the QrCode)
*/
const loginRequestResult = await this.padchatRpc.WXLoginRequest(this.autoData.token)
if (!loginRequestResult || loginRequestResult.status !== 0) {
/**
* 1.2.1 Login Request Not Valid, emit QrCode for scan.
*/
await this.emitLoginQrCode()
}
}
} else {
if (!this.autoData || !this.autoData.token) {
return false
}
log.silly('PuppetPadchatBridge', `initLogin() autoData.token exist for %s`,
this.autoData.nick_name || 'no nick_name',
)
const autoLoginResult = await this.padchatRpc.WXAutoLogin(this.autoData.token)
if (!autoLoginResult) {
/**
* 1. No Auto Login, emit QrCode for scan
*/
await this.emitLoginQrCode()
return false
}
if (autoLoginResult.status === 0) {
/**
* 2 Auto Login Success
*/
this.login(autoLoginResult.user_name)
return true
} else {
/**
* 3. Send Login Request to User to be confirm(the same as the user had scaned the QrCode)
*/
const loginRequestResult = await this.padchatRpc.WXLoginRequest(this.autoData.token)
if (!loginRequestResult || loginRequestResult.status !== 0) {
/**
* 1.3. No Auto Login, emit QrCode for scan
* 3.1 Login Request Not Valid, emit QrCode for scan.
*/
await this.padchatRpc.WXInitialize()
await this.emitLoginQrCode()
return false
} else {
/**
* 3.2 Login Request Valid, wait user to confirm on the phone.
*/
return false
}
}
return false
}
protected async emitLoginQrCode(): Promise<void> {
......@@ -324,8 +335,9 @@ export class Bridge extends EventEmitter {
const result = await this.padchatRpc.WXGetQRCode()
if (!result) {
// if fail, do we need to await this.WXInitialize() again???
throw new Error('waitLogin() WXGetQrCode() return nothing')
log.verbose('PuppetPadchatBridge', `emitLoginQrCode() result not found. Call WXInitialize() and try again ...`)
await this.padchatRpc.WXInitialize()
return await this.emitLoginQrCode()
}
const qrCodeText = await pfHelper.imageBase64ToQrCode(result.qr_code)
......@@ -341,15 +353,22 @@ export class Bridge extends EventEmitter {
}
protected async saveAutoData(): Promise<void> {
log.verbose('PuppetPadchatBridge', `loadAutoData()`)
protected async saveAutoData(selfId: string): Promise<void> {
log.verbose('PuppetPadchatBridge', `loadAutoData(%s)`, selfId)
await this.padchatRpc.WXHeartBeat()
if (!this.autoData.wxData || this.autoData.user_name !== selfId) {
log.verbose('PuppetPadchatBridge', `loadAutoData() user_name(%s) !== selfId(%s)`,
this.autoData.user_name,
selfId,
)
this.autoData.wxData = (await this.padchatRpc.WXGenerateWxDat()).data
}
// Check 62 data. If has then use, or save 62 data here.
this.autoData.token = (await this.padchatRpc.WXGetLoginToken()).token
// TODO: should not WXGenerateWxDat immediately, check user_name
this.autoData.wxData = (await this.padchatRpc.WXGenerateWxDat()).data
if (!this.autoData.user_name || !this.autoData.wxData || !this.autoData.token) {
throw new Error('autoData error')
}
......@@ -368,7 +387,7 @@ export class Bridge extends EventEmitter {
// Check for 62 data, if has, then use WXLoadWxDat
// TODO: should check this.autoData.user_name here
if (this.autoData.wxData) {
log.silly('PuppetPadchatBridge', `start(), get 62 data`)
log.silly('PuppetPadchatBridge', `loadAutoData() load 62 data`)
await this.padchatRpc.WXLoadWxDat(this.autoData.wxData)
}
}
......@@ -538,8 +557,8 @@ export class Bridge extends EventEmitter {
return rawPayload
}
public async ding(data: string): Promise<string> {
const result = await this.padchatRpc.WXHeartBeat(data)
public async ding(): Promise<string> {
const result = await this.padchatRpc.WXHeartBeat()
return result.message
}
......@@ -583,9 +602,4 @@ export class Bridge extends EventEmitter {
public async WXAddUser(strangerV1: string, strangerV2: string, type: string, verify: string): Promise<void> {
await this.padchatRpc.WXAddUser(strangerV1, strangerV2, type, verify)
}
public async WXHeartBeat(data = 'ding'): Promise<string> {
const result = await this.padchatRpc.WXHeartBeat(data)
return result.message
}
}
export const WECHATY_PUPPET_PADCHAT_ENDPOINT = process.env['WECHATY_PUPPET_PADCHAT_ENDPOINT'] || 'ws://101.132.129.155:9091/wx'
export const WECHATY_PUPPET_PADCHAT_ENDPOINT = process.env['WECHATY_PUPPET_PADCHAT_ENDPOINT'] || 'ws://54.223.36.77:9091/wx'
export const WECHATY_PUPPET_PADCHAT_TOKEN = process.env['WECHATY_PUPPET_PADCHAT_TOKEN'] || 'padchattest'
......@@ -65,14 +65,10 @@ import {
import { log } from '../config'
// const AUTO_DATA_SLOT = 'autoData'
export class PadchatRpc extends EventEmitter {
private socket? : WebSocket
private readonly jsonRpc : any // Peer
// private readonly rpcPromiseWaittingDict: PadChatRpcPromiseDict
constructor(
protected endpoint : string,
protected token : string,
......@@ -87,20 +83,21 @@ export class PadchatRpc extends EventEmitter {
log.verbose('PadchatRpc', 'start()')
await this.initWebSocket()
await this.initJsonRpcPeer()
await this.initJsonRpc()
await this.init()
await this.WXInitialize()
}
protected async initJsonRpcPeer(): Promise<void> {
log.verbose('PadchatRpc', 'initJsonRpcPeer()')
protected async initJsonRpc(): Promise<void> {
log.verbose('PadchatRpc', 'initJsonRpc()')
if (!this.socket) {
throw new Error('socket had not been opened yet!')
}
this.jsonRpc.on('data', (buffer: string | Buffer) => {
log.silly('PadchatRpc', 'initJsonRpcPeer() jsonRpc.on(data)')
log.silly('PadchatRpc', 'initJsonRpc() jsonRpc.on(data)')
if (!this.socket) {
throw new Error('no web socket')
......@@ -109,7 +106,7 @@ export class PadchatRpc extends EventEmitter {
const text = String(buffer)
const payload = parse(text) // as JsonRpcPayloadRequest
log.silly('PadchatRpc', 'initJsonRpcPeer() jsonRpc.on(data) buffer="%s"', text)
log.silly('PadchatRpc', 'initJsonRpc() jsonRpc.on(data) buffer="%s"', text)
/**
* A Gateway at here:
......@@ -128,7 +125,7 @@ export class PadchatRpc extends EventEmitter {
param: encodedParam,
}
log.silly('PadchatRpc', 'initJsonRpcPeer() jsonRpc.on(data) converted to padchat payload="%s"', JSON.stringify(message))
log.silly('PadchatRpc', 'initJsonRpc() jsonRpc.on(data) converted to padchat payload="%s"', JSON.stringify(message))
this.socket.send(JSON.stringify(message))
})
......@@ -152,7 +149,7 @@ export class PadchatRpc extends EventEmitter {
log.silly('PadchatRpc', 'initWebSocket() ws.on(message)')
try {
const payload: PadchatPayload = JSON.parse(data)
this.onServer(payload)
this.onSocket(payload)
} catch (e) {
log.warn('PadchatRpc', 'startJsonRpc() ws.on(message) exception: %s', e)
this.emit('error', e)
......@@ -191,54 +188,11 @@ export class PadchatRpc extends EventEmitter {
apiName : string,
...params : string[]
): Promise<any> {
log.silly('PadchatRpc', 'rpcCall(%s, %s)', apiName, params.join(', '))
return await this.jsonRpc.request(apiName, params)
// if (!this.socket) {
// throw new Error('no web socket')
// }
// const msgId = cuid()
// const paramEncodedList = params.map(
// arg => encodeURIComponent(arg),
// )
// const request: PadchatRpcRequest = {
// userId: this.token,
// msgId,
// apiName,
// param: paramEncodedList,
// }
// const payload = JSON.stringify(request)
// log.silly('PadchatRpc', 'sendToWebSocket: %s', payload)
// this.socket.send(payload)
// return new Promise((resolve, reject) => {
// const timer = setTimeout(() => {
// delete this.rpcPromiseWaittingDict[msgId]
// reject('PadChat Server timeout for msgId: ' + msgId + ', apiName: ' + apiName + ', args: ' + params.join(', '))
// // TODO: send json again or detect init()
// }, 30000)
// this.rpcPromiseWaittingDict[msgId] = {
// resolver: (...args: any[]) => {
// delete this.rpcPromiseWaittingDict[msgId]
// clearTimeout(timer)
// resolve(...args)
// },
// reject: (...args: any[]) => {
// delete this.rpcPromiseWaittingDict[msgId]
// clearTimeout(timer)
// reject(...args)
// },
// }
// })
}
protected onServer(payload: PadchatPayload) {
protected onSocket(payload: PadchatPayload) {
log.verbose('PadchatRpc', 'onServer(payload.length=%d)',
JSON.stringify(payload).length,
)
......@@ -259,26 +213,36 @@ export class PadchatRpc extends EventEmitter {
}
if (payload.msgId) {
// Data Return From WebSocket Client
this.onServerPadchat(payload)
// 1. Padchat Payload
//
// padchatPayload:
// {
// "apiName": "WXHeartBeat",
// "data": "%7B%22status%22%3A0%2C%22message%22%3A%22ok%22%7D",
// "msgId": "abc231923912983",
// "userId": "test"
// }
this.onSocketPadchat(payload)
} else {
// Data From Tencent
// 2. Tencent Payload
//
// messagePayload:
// {
// "apiName": "",
// "data": "XXXX",
// "msgId": "",
// "userId": "test"
// }
const tencentPayloadList: PadchatMessagePayload[] = JSON.parse(decodeURIComponent(payload.data))
this.onServerTencent(tencentPayloadList)
this.onSocketTencent(tencentPayloadList)
}
}
protected onServerTencent(messagePayloadList: PadchatMessagePayload[]) {
protected onSocketTencent(messagePayloadList: PadchatMessagePayload[]) {
console.log('tencent messagePayloadList:', messagePayloadList)
// if ( payload.continue === PadchatContinue.Done
// && payload.msg_type === PadchatMsgType.N15_32768
// && payload.status === PadchatStatus.One
// ) {
// // Skip empty message. "continue":0,"msg_type":32768,"status":1,"
// return
// }
// rawWebSocketData:
// messagePayload:
// {
// "apiName": "",
// "data": "XXXX",
......@@ -286,6 +250,14 @@ export class PadchatRpc extends EventEmitter {
// "userId": "test"
// }
// if ( payload.continue === PadchatContinue.Done
// && payload.msg_type === PadchatMsgType.N15_32768
// && payload.status === PadchatStatus.One
// ) {
// // Skip empty message. "continue":0,"msg_type":32768,"status":1,"
// return
// }
// if (!payload.data) {
// console.error('data: no payload.data')
// return
......@@ -306,18 +278,11 @@ export class PadchatRpc extends EventEmitter {
}
}
protected onServerPadchat(padchatPayload: PadchatPayload) {
protected onSocketPadchat(padchatPayload: PadchatPayload) {
log.verbose('PadchatRpc', 'onServerPadchat({apiName="%s", msgId="%s", ...})',
padchatPayload.apiName,
padchatPayload.msgId,
)
// padchatPayload:
// {
// "apiName": "WXHeartBeat",
// "data": "%7B%22status%22%3A0%2C%22message%22%3A%22ok%22%7D",
// "msgId": "abc231923912983",
// "userId": "test"
// }
log.silly('PadchatRpc', 'onServerPadchat(%s)', JSON.stringify(padchatPayload).substr(0, 500))
// check logout:
......@@ -410,8 +375,8 @@ export class PadchatRpc extends EventEmitter {
return result
}
public async WXHeartBeat(data: string): Promise<WXHeartBeatType> {
const result = await this.rpcCall('WXHeartBeat', data)
public async WXHeartBeat(): Promise<WXHeartBeatType> {
const result = await this.rpcCall('WXHeartBeat')
log.silly('PadchatRpc', 'WXHeartBeat result: %s', JSON.stringify(result))
if (!result || result.status !== 0) {
throw Error('WXHeartBeat error! canot get result from websocket server')
......
......@@ -115,7 +115,6 @@ export class PuppetPadchat extends Puppet {
memory : this.options.memory,
token : WECHATY_PUPPET_PADCHAT_TOKEN,
endpoint: WECHATY_PUPPET_PADCHAT_ENDPOINT,
// profile: profile, // should be profile in the future
})
}
......@@ -263,7 +262,7 @@ export class PuppetPadchat extends Puppet {
this.emit('logout', this.id) // becore we will throw above by logonoff() when this.user===undefined
this.id = undefined
// TODO: this.bridge.logout
await this.bridge.logout()
}
/**
......@@ -381,7 +380,7 @@ export class PuppetPadchat extends Puppet {
log.verbose('PuppetPadchat', 'messageSend(%s, %s)', receiver, text)
const id = receiver.contactId || receiver.roomId
if (!id) {
throw Error('No id')
throw Error('no id')
}
await this.bridge.WXSendMsg(id, text)
}
......
......@@ -116,7 +116,8 @@ export class PadchatPureFunctionHelper {
switch (rawPayload.sub_type) {
case PadchatMessageType.Sys: // fall down
case PadchatMessageType.StatusNotify: // fall down
case PadchatMessageType.Sys: // fall down
case PadchatMessageType.Text:
type = MessageType.Text
break
......@@ -142,7 +143,7 @@ export class PadchatPureFunctionHelper {
break
default:
throw new Error('unsupported type')
throw new Error('unsupported type: ' + PadchatMessageType[rawPayload.sub_type] + '(' + rawPayload.sub_type + ')')
}
const payloadBase = {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册