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

implement some pad functions

上级 62422547
......@@ -11,6 +11,10 @@ import {
PadchatRoomMemberRawPayload,
} from './padchat-schemas'
import {
ADDRESS,
} from './config'
export const resolverDict: {
[idx: string]: Function,
} = {}
......@@ -19,7 +23,7 @@ export interface BridgeOptions {
// head? : boolean,
userId: string,
// profile: Profile,
botWs: WebSocket,
// botWs: WebSocket,
// desperate in the future
autoData: AutoDataType,
}
......@@ -141,7 +145,9 @@ export class Bridge extends EventEmitter {
log.verbose('PuppetPadchatBridge', 'constructor()')
this.userId = options.userId
this.botWs = options.botWs
this.botWs = new WebSocket(ADDRESS, { perMessageDeflate: true })
this.autoData = options.autoData || {}
// this.state = new StateSwitch('PuppetPadchatBridge', log)
}
......@@ -175,6 +181,19 @@ export class Bridge extends EventEmitter {
})
}
public async initWs(): Promise<void> {
this.botWs.on('message', wsMsg => {
this.emit('ws', wsMsg)
})
this.botWs.on('open', () => {
this.emit('open')
})
}
public closeWs(): void {
this.botWs.close()
}
/**
* Init with WebSocket Server
*/
......
......@@ -69,7 +69,7 @@ async function onMessage(
}
await msg.ready()
this.emit('message', msg)
this.emit('message', msg.id)
} catch (e) {
log.error('PuppetPadchatEvent', 'onMessage() exception: %s', e.stack)
......
......@@ -227,7 +227,7 @@ async function checkRoomJoin(
try {
if (inviter === 'You' || inviter === '' || inviter === 'you') {
inviterContact = this.userSelf()
inviterContact = this.Contact.load(this.selfId())
}
const max = 20
......@@ -318,8 +318,8 @@ async function checkRoomJoin(
await inviterContact.ready()
await room.ready()
this.emit('room-join', room , inviteeContactList, inviterContact)
room.emit('join' , inviteeContactList, inviterContact)
this.emit('room-join', room.id, inviteeContactList.map(c => c.id), inviterContact.id)
room.emit('join' , inviteeContactList, inviterContact)
return true
} catch (e) {
......@@ -342,7 +342,7 @@ function parseRoomLeave(
if ((!foundByBot || !foundByBot.length) && (!foundByOther || !foundByOther.length)) {
throw new Error('checkRoomLeave() no matched re for ' + content)
}
const [leaver, remover] = foundByBot ? [ foundByBot[1], this.userSelf().id ] : [ this.userSelf().id, foundByOther[1] ]
const [leaver, remover] = foundByBot ? [ foundByBot[1], this.selfId() ] : [ this.selfId(), foundByOther[1] ]
return [leaver, remover]
}
......@@ -373,8 +373,8 @@ async function checkRoomLeave(
* @lijiarui: I have checked, leaver will never be a list. If the bot remove 2 leavers at the same time, it will be 2 sys message, instead of 1 sys message contains 2 leavers.
*/
let leaverContact: Contact | null, removerContact: Contact | null
if (leaver === this.userSelf().id) {
leaverContact = this.userSelf()
if (leaver === this.selfId()) {
leaverContact = this.Contact.load(this.selfId())
// not sure which is better
// removerContact = room.member({contactAlias: remover}) || room.member({name: remover})
......@@ -385,7 +385,7 @@ async function checkRoomLeave(
// }
} else {
removerContact = this.userSelf()
removerContact = this.Contact.load(this.selfId())
// not sure which is better
// leaverContact = room.member({contactAlias: remover}) || room.member({name: leaver})
......@@ -408,8 +408,8 @@ async function checkRoomLeave(
* it will be 2 sys message, instead of 1 sys message contains 2 leavers.
* @huan 2018 May: we need to generilize the pattern for future usage.
*/
this.emit('room-leave', room, [leaverContact] /* , [removerContact] */)
room.emit('leave' , [leaverContact], removerContact || undefined)
this.emit('room-leave', room.id , [leaverContact.id] /* , [removerContact] */)
room.emit('leave' , [leaverContact], removerContact || undefined)
setTimeout(_ => { room.refresh() }, 10000) // reload the room data, especially for memberList
return true
......@@ -450,7 +450,7 @@ async function checkRoomTopic(
let changerContact: Contact | null
if (/^You$/.test(changer) || /^你$/.test(changer)) {
changerContact = this.userSelf()
changerContact = this.Contact.load(this.selfId())
} else {
changerContact = room.member(changer)
}
......@@ -463,8 +463,8 @@ async function checkRoomTopic(
try {
await changerContact.ready()
await room.ready()
this.emit('room-topic', room, topic, oldTopic, changerContact)
room.emit('topic' , topic, oldTopic, changerContact)
this.emit('room-topic', room.id , topic, oldTopic, changerContact.id)
room.emit('topic' , topic, oldTopic, changerContact)
room.refresh()
return true
} catch (e) {
......
......@@ -18,7 +18,8 @@
*/
import * as path from 'path'
// import * as fs from 'fs'
import * as fs from 'fs'
import * as cuid from 'cuid'
import {
FileBox,
......@@ -64,10 +65,6 @@ import {
// Profile,
// } from '../profile'
import {
ADDRESS,
} from './config'
import {
Bridge,
resolverDict,
......@@ -101,23 +98,20 @@ const TOKEN = 'padchattest'
export class PuppetPadchat extends Puppet {
public bridge: Bridge
public botWs: WebSocket
// public botWs: WebSocket
constructor(
public options: PuppetOptions,
) {
super(options)
this.botWs = new WebSocket(ADDRESS, { perMessageDeflate: true })
this.bridge = new Bridge({
userId : TOKEN,
botWs : this.botWs,
autoData : {},
// profile: profile, // should be profile in the future
})
this.botWs.on('message', data => this.wsOnMessage(data))
this.bridge.on('ws', data => this.wsOnMessage(data))
}
......@@ -129,49 +123,74 @@ export class PuppetPadchat extends Puppet {
return data
}
// public initWatchdog(): void {
// log.verbose('PuppetPadchat', 'initWatchdogForPuppet()')
public initWatchdog(): void {
log.verbose('PuppetPadchat', 'initWatchdogForPuppet()')
const puppet = this
// const puppet = this
// clean the dog because this could be re-inited
this.watchdog.removeAllListeners()
// // clean the dog because this could be re-inited
// this.watchdog.removeAllListeners()
puppet.on('watchdog', food => this.watchdog.feed(food))
this.watchdog.on('feed', async food => {
log.silly('PuppetPadchat', 'initWatchdogForPuppet() dog.on(feed, food={type=%s, data=%s})', food.type, food.data)
// feed the dog, heartbeat the puppet.
puppet.emit('heartbeat', food.data)
// puppet.on('watchdog', food => this.watchdog.feed(food))
// this.watchdog.on('feed', food => {
// log.silly('PuppetPadchat', 'initWatchdogForPuppet() dog.on(feed, food={type=%s, data=%s})', food.type, food.data)
// // feed the dog, heartbeat the puppet.
// puppet.emit('heartbeat', food.data)
// })
const feedAfterTenSeconds = async () => {
this.bridge.WXHeartBeat()
.then(() => {
this.emit('watchdog', {
data: 'WXHeartBeat()',
})
})
.catch(e => {
log.warn('PuppetPadchat', 'initWatchdogForPuppet() feedAfterTenSeconds rejected: %s', e && e.message || '')
})
}
setTimeout(feedAfterTenSeconds, 15 * 1000)
})
// this.watchdog.on('reset', async (food, timeout) => {
// log.warn('PuppetPadchat', 'initWatchdogForPuppet() dog.on(reset) last food:%s, timeout:%s',
// food.data, timeout)
// try {
// await this.stop()
// await this.start()
// } catch (e) {
// puppet.emit('error', e)
// }
// })
// }
this.watchdog.on('reset', async (food, timeout) => {
log.warn('PuppetPadchat', 'initWatchdogForPuppet() dog.on(reset) last food:%s, timeout:%s',
food.data, timeout)
try {
await this.stop()
await this.start()
} catch (e) {
puppet.emit('error', e)
}
})
}
public async start(): Promise<void> {
// Connect with websocket server
const botWs = this.botWs
const bridge = this.bridge = await this.initBridge()
if (!this.bridge) {
throw Error('cannot init bridge successfully!')
}
/**
* state has two main state: ON / OFF
* ON (pending)
* OFF (pending)
*/
this.state.on('pending')
const bridge = this.bridge = await this.initBridge()
this.bridge.loginSucceed = false
log.verbose('PuppetPadchat', `start() with ${this.options.profile}`)
this.state.on('pending')
botWs.on('open', async() => {
this.bridge.once('open', async() => {
this.emit('watchdog', {
data: 'start',
})
// await some tasks...
await bridge.init()
await bridge.WXInitialize()
......@@ -219,11 +238,14 @@ export class PuppetPadchat extends Puppet {
public async initBridge(): Promise<Bridge> {
log.verbose('PuppetPadchat', 'initBridge()')
// if (this.state.off()) {
// const e = new Error('initBridge() found targetState != live, no init anymore')
// log.warn('PuppetPadchat', e.message)
// throw e
// }
if (this.state.off()) {
const e = new Error('initBridge() found targetState != live, no init anymore')
log.warn('PuppetPadchat', e.message)
throw e
}
await this.bridge.initWs()
const autoData: AutoDataType = await this.options.profile.get('autoData')
log.silly('PuppetPadchat', 'initBridge, get autoData: %s', JSON.stringify(autoData))
......@@ -277,14 +299,14 @@ export class PuppetPadchat extends Puppet {
const msg = this.Message.create(msgRawPayload['msg_id'], await this.messageRawPayloadParser(msgRawPayload))
await msg.ready()
this.emit('message', msg)
this.emit('message', msg.id)
})
// Data Return From WebSocket Client
} else {
// check logout:
if (rawWebSocketData.type === -1) {
this.emit('logout', this.userSelf())
this.emit('logout', this.selfId())
}
log.silly('PuppetPadchat', 'return apiName: %s, msgId: %s', rawWebSocketData.apiName, rawWebSocketData.msgId)
......@@ -362,12 +384,15 @@ export class PuppetPadchat extends Puppet {
this.userId = this.bridge.autoData.user_name // Puppet userId different with WebSocket userId
const user = this.Contact.load(this.userId)
await user.ready()
this.emit('login', user)
this.emit('login', this.userId)
log.verbose('PuppetPadchatBridge', 'loginSucceed: Send login to the bot, user_name: %s', this.bridge.username)
await this.bridge.WXSendMsg(this.bridge.autoData.user_name, 'Bot on line!')
this.state.on(true)
this.emit('start')
this.initWatchdog()
return
} else {
log.verbose('PuppetPadchatBridge', 'no enough data, save again, %s', JSON.stringify(this.bridge.autoData))
......@@ -388,21 +413,30 @@ export class PuppetPadchat extends Puppet {
}
this.state.off('pending')
this.watchdog.sleep()
setImmediate(() => this.bridge.removeAllListeners())
await this.logout()
this.bridge.closeWs()
// await some tasks...
this.state.off(true)
this.emit('stop')
}
public async logout(): Promise<void> {
log.verbose('PuppetPadchat', 'logout()')
if (!this.logonoff()) {
if (!this.userId) {
throw new Error('logout before login?')
}
// this.emit('logout', this.user!) // becore we will throw above by logonoff() when this.user===undefined
// this.user = undefined
this.emit('logout', this.userId) // becore we will throw above by logonoff() when this.user===undefined
this.userId = undefined
// TODO: do the logout job
// TODO: this.bridge.logout
}
/**
......@@ -416,24 +450,36 @@ export class PuppetPadchat extends Puppet {
public async contactAlias(contactId: string, alias?: string|null): Promise<void | string> {
log.verbose('PuppetPadchat', 'contactAlias(%s, %s)', contactId, alias)
const payload = await this.contactPayload(contactId)
if (typeof alias === 'undefined') {
return 'padchat alias'
return payload.alias || ''
}
// TODO: modify alias in bridge
return
}
public async contactFindAll(query: ContactQueryFilter): Promise<string[]> {
log.verbose('PuppetPadchat', 'contactFindAll(%s)', query)
// this.bridge.WX
return []
}
public async contactAvatar(contactId: string): Promise<FileBox> {
log.verbose('PuppetPadchat', 'contactAvatar(%s)', contactId)
const WECHATY_ICON_PNG = path.resolve('../../docs/images/wechaty-icon.png')
// TODO
return FileBox.packBase64('', WECHATY_ICON_PNG)
const rawPayload = await this.contactRawPayload(contactId)
const payload = await this.contactRawPayloadParser(rawPayload)
if (!payload.avatar) {
throw new Error('no avatar')
}
const file = FileBox.packRemote(payload.avatar)
return file
}
public async contactRawPayload(id: string): Promise<PadchatContactRawPayload> {
......@@ -484,6 +530,21 @@ export class PuppetPadchat extends Puppet {
* Message
*
*/
public async messageFile(id: string): Promise<FileBox> {
// const rawPayload = await this.messageRawPayload(id)
const base64 = 'cRH9qeL3XyVnaXJkppBuH20tf5JlcG9uFX1lL2IvdHRRRS9kMMQxOPLKNYIzQQ=='
const filename = 'test.txt'
const file = FileBox.packBase64(
base64,
filename,
)
return file
}
public async messageRawPayload(id: string): Promise<PadchatMessageRawPayload> {
throw Error('should not call messageRawPayload')
// log.verbose('PuppetPadchat', 'messageRawPayload(%s)', id)
......@@ -536,7 +597,7 @@ export class PuppetPadchat extends Puppet {
}
const payload: MessagePayload = {
date : new Date(),
timestamp : Date.now(),
fromId : rawPayload.from_user,
text : rawPayload.content,
toId : rawPayload.to_user,
......@@ -584,6 +645,22 @@ export class PuppetPadchat extends Puppet {
file : FileBox,
): Promise<void> {
log.verbose('PuppetPadchat', 'messageSend(%s, %s)', receiver, file)
const id = receiver.contactId || receiver.roomId
if (!id) {
throw new Error('no id!')
}
const xxx = cuid()
const tmpFile = path.join('/tmp/' + xxx)
file.save(tmpFile)
const bitmap = fs.readFileSync(tmpFile)
const base64 = new Buffer(bitmap).toString('base64')
fs.unlinkSync(tmpFile)
await this.bridge.WXSendImage(id, base64)
}
public async messageForward(
......@@ -594,6 +671,21 @@ export class PuppetPadchat extends Puppet {
receiver,
messageId,
)
const msg = this.Message.create(messageId)
await msg.ready()
if (msg.type() === this.Message.Type.Text) {
await this.messageSendText(
receiver,
msg.text(),
)
} else {
await this.messageSendFile(
receiver,
await msg.file(),
)
}
}
/**
......@@ -680,6 +772,7 @@ export class PuppetPadchat extends Puppet {
contactId : string,
): Promise<void> {
log.verbose('PuppetPadchat', 'roomDel(%s, %s)', roomId, contactId)
}
public async roomAdd(
......@@ -695,9 +788,14 @@ export class PuppetPadchat extends Puppet {
): Promise<void | string> {
log.verbose('PuppetPadchat', 'roomTopic(%s, %s)', roomId, topic)
const payload = await this.roomPayload(roomId)
if (typeof topic === 'undefined') {
return 'padchat room topic'
return payload.topic
}
// TODO: modify
return
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册