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

Refactoring: linting the new design

上级 db85a250
......@@ -24,21 +24,20 @@ import {
import {
MessagePayload,
} from '../message'
import {
ContactQueryFilter,
Gender,
// ContactQueryFilter,
ContactGender,
ContactType,
ContactPayload,
} from '../contact'
import {
RoomPayload,
RoomQueryFilter,
} from '../room'
// RoomQueryFilter,
} from '../puppet/'
import {
Puppet,
PuppetOptions,
Receiver,
MessageType,
} from '../puppet/'
import {
......@@ -53,6 +52,7 @@ export interface MockContactRawPayload {
}
export interface MockMessageRawPayload {
id : string,
from : string,
to : string,
text : string
......@@ -73,22 +73,27 @@ export class PuppetMock extends Puppet {
}
public async start(): Promise<void> {
log.verbose('PuppetMock', `start() with ${this.options.profile}`)
log.verbose('PuppetMock', `start() with ${this.options.memory.name}`)
this.state.on('pending')
// await some tasks...
this.state.on(true)
this.id = 'logined_user_id'
const user = this.Contact.load(this.id)
this.emit('login', user.id)
const msg = this.Message.create('mock_id')
await msg.ready()
// const user = this.Contact.load(this.id)
this.emit('login', this.id)
const MOCK_MSG_ID = 'mockid'
this.cacheMessagePayload.set(MOCK_MSG_ID, {
id : MOCK_MSG_ID,
type : MessageType.Text,
text : 'mock text',
timestamp : Date.now(),
})
setInterval(() => {
log.verbose('PuppetMock', `start() setInterval() pretending received a new message: ${msg + ''}`)
this.emit('message', msg.id)
log.verbose('PuppetMock', `start() setInterval() pretending received a new message: ${MOCK_MSG_ID}`)
this.emit('message', MOCK_MSG_ID)
}, 3000)
}
......@@ -137,8 +142,8 @@ export class PuppetMock extends Puppet {
return
}
public async contactFindAll(query: ContactQueryFilter): Promise<string[]> {
log.verbose('PuppetMock', 'contactFindAll(%s)', query)
public async contactList(): Promise<string[]> {
log.verbose('PuppetMock', 'contactList()')
return []
}
......@@ -147,7 +152,7 @@ export class PuppetMock extends Puppet {
log.verbose('PuppetMock', 'contactAvatar(%s)', contactId)
const WECHATY_ICON_PNG = path.resolve('../../docs/images/wechaty-icon.png')
return FileBox.packLocal(WECHATY_ICON_PNG)
return FileBox.fromLocal(WECHATY_ICON_PNG)
}
public async contactRawPayload(id: string): Promise<MockContactRawPayload> {
......@@ -162,8 +167,9 @@ export class PuppetMock extends Puppet {
log.verbose('PuppetMock', 'contactRawPayloadParser(%s)', rawPayload)
const payload: ContactPayload = {
gender: Gender.Unknown,
type: ContactType.Unknown,
id : 'id',
gender : ContactGender.Unknown,
type : ContactType.Unknown,
}
return payload
}
......@@ -174,15 +180,16 @@ export class PuppetMock extends Puppet {
*
*/
public async messageFile(id: string): Promise<FileBox> {
return FileBox.packBase64(
return FileBox.fromBase64(
'cRH9qeL3XyVnaXJkppBuH20tf5JlcG9uFX1lL2IvdHRRRS9kMMQxOPLKNYIzQQ==',
'mock-file.txt',
'mock-file' + id + '.txt',
)
}
public async messageRawPayload(id: string): Promise<MockMessageRawPayload> {
log.verbose('PuppetMock', 'messageRawPayload(%s)', id)
const rawPayload: MockMessageRawPayload = {
id : 'id',
from : 'from_id',
text : 'mock message text',
to : 'to_id',
......@@ -193,11 +200,12 @@ export class PuppetMock extends Puppet {
public async messageRawPayloadParser(rawPayload: MockMessageRawPayload): Promise<MessagePayload> {
log.verbose('PuppetMock', 'messagePayload(%s)', rawPayload)
const payload: MessagePayload = {
id : rawPayload.id,
timestamp : Date.now(),
fromId : 'xxx',
text : 'mock message text',
toId : this.selfId(),
type : this.Message.Type.Text,
type : MessageType.Text,
}
return payload
}
......@@ -250,20 +258,17 @@ export class PuppetMock extends Puppet {
log.verbose('PuppetMock', 'roomRawPayloadParser(%s)', rawPayload)
const payload: RoomPayload = {
topic : 'mock topic',
memberIdList : [],
nameMap : new Map<string, string>(),
roomAliasMap : new Map<string, string>(),
contactAliasMap : new Map<string, string>(),
id : 'id',
topic : 'mock topic',
memberIdList : [],
aliasDict : {},
}
return payload
}
public async roomFindAll(
query: RoomQueryFilter = { topic: /.*/ },
): Promise<string[]> {
log.verbose('PuppetMock', 'roomFindAll(%s)', query)
public async roomList(): Promise<string[]> {
log.verbose('PuppetMock', 'roomList()')
return []
}
......
......@@ -26,45 +26,29 @@ import {
} from 'file-box'
import {
// Message,
MessagePayload, MessageType,
} from '../message'
MessagePayload,
MessageType,
import Misc from '../misc'
import {
Contact,
ContactQueryFilter,
Gender,
ContactGender,
ContactType,
ContactPayload,
} from '../contact'
import {
// Room,
RoomPayload,
RoomQueryFilter,
RoomMemberQueryFilter,
} from '../room'
// RoomQueryFilter,
// RoomMemberQueryFilter,
// import {
// FriendRequest,
// } from '../puppet/friend-request'
import {
Puppet,
PuppetOptions,
Receiver,
} from '../puppet/'
// import Misc from '../misc'
import {
log,
} from '../config'
// import {
// Profile,
// } from '../profile'
import {
Bridge,
resolverDict,
......@@ -76,7 +60,7 @@ import {
PadchatMessageRawPayload,
PadchatMessageType,
PadchatRoomRawPayload,
PadchatRoomRawMember,
// PadchatRoomRawMember,
} from './padchat-schemas'
export type PuppetFoodType = 'scan' | 'ding'
......@@ -116,7 +100,7 @@ export class PuppetPadchat extends Puppet {
}
public toString() {
return `PuppetPadchat<${this.options.profile.name}>`
return `PuppetPadchat<${this.options.memory.name}>`
}
public ding(data?: any): Promise<string> {
......@@ -135,7 +119,7 @@ export class PuppetPadchat extends Puppet {
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.emit('heartbeat', food.data)
const feedAfterTenSeconds = async () => {
this.bridge.WXHeartBeat()
......@@ -183,7 +167,7 @@ export class PuppetPadchat extends Puppet {
this.bridge.loginSucceed = false
log.verbose('PuppetPadchat', `start() with ${this.options.profile}`)
log.verbose('PuppetPadchat', `start() with ${this.options.memory.name}`)
this.bridge.once('open', async() => {
......@@ -247,7 +231,7 @@ export class PuppetPadchat extends Puppet {
await this.bridge.initWs()
const autoData: AutoDataType = await this.options.profile.get('autoData')
const autoData: AutoDataType = await this.options.memory.get('autoData')
log.silly('PuppetPadchat', 'initBridge, get autoData: %s', JSON.stringify(autoData))
this.bridge.autoData = autoData
......@@ -378,8 +362,8 @@ export class PuppetPadchat extends Puppet {
log.verbose('PuppetPadchatBridge', 'saveConfig, autoData: %s', JSON.stringify(this.bridge.autoData))
if (this.bridge.autoData.wxData && this.bridge.autoData.token && this.bridge.autoData.user_name) {
log.verbose('PuppetPadchatBridge', 'saveConfig: begin to save data to file')
await this.options.profile.set('autoData', this.bridge.autoData)
await this.options.profile.save()
await this.options.memory.set('autoData', this.bridge.autoData)
await this.options.memory.save()
log.verbose('PuppetPadchatBridge', 'loginSucceed: Send ding to the bot, username: %s', this.bridge.username)
await this.bridge.WXSendMsg(this.bridge.autoData.user_name, 'ding')
......@@ -432,12 +416,12 @@ export class PuppetPadchat extends Puppet {
public async logout(): Promise<void> {
log.verbose('PuppetPadchat', 'logout()')
if (!this.userId) {
if (!this.id) {
throw new Error('logout before login?')
}
this.emit('logout', this.userId) // becore we will throw above by logonoff() when this.user===undefined
this.userId = undefined
this.emit('logout', this.id) // becore we will throw above by logonoff() when this.user===undefined
this.id = undefined
// TODO: this.bridge.logout
}
......@@ -464,8 +448,9 @@ export class PuppetPadchat extends Puppet {
return
}
public async contactFindAll(query: ContactQueryFilter): Promise<string[]> {
log.verbose('PuppetPadchat', 'contactFindAll(%s)', query)
// public async contactFindAll(query: ContactQueryFilter): Promise<string[]> {
public async contactList(): Promise<string[]> {
log.verbose('PuppetPadchat', 'contactList()')
// const contactRawPayloadMap = (await this.bridge.checkSyncContactOrRoom()).contactMap
......@@ -543,7 +528,7 @@ export class PuppetPadchat extends Puppet {
throw new Error('no avatar')
}
const file = FileBox.packRemote(payload.avatar)
const file = FileBox.fromRemote(payload.avatar)
return file
}
......@@ -561,9 +546,9 @@ export class PuppetPadchat extends Puppet {
}
const gender = {
0: Gender.Unknown,
1: Gender.Male,
2: Gender.Female,
0: ContactGender.Unknown,
1: ContactGender.Male,
2: ContactGender.Female,
}
if (/@chatroom$/.test(rawPayload.user_name)) {
......@@ -578,6 +563,7 @@ export class PuppetPadchat extends Puppet {
}
const payload: ContactPayload = {
id : rawPayload.user_name,
gender : gender[rawPayload.sex],
type : contactType,
alias : rawPayload.remark,
......@@ -600,9 +586,9 @@ export class PuppetPadchat extends Puppet {
// const rawPayload = await this.messageRawPayload(id)
const base64 = 'cRH9qeL3XyVnaXJkppBuH20tf5JlcG9uFX1lL2IvdHRRRS9kMMQxOPLKNYIzQQ=='
const filename = 'test.txt'
const filename = 'test-' + id + '.txt'
const file = FileBox.packBase64(
const file = FileBox.fromBase64(
base64,
filename,
)
......@@ -611,7 +597,7 @@ export class PuppetPadchat extends Puppet {
}
public async messageRawPayload(id: string): Promise<PadchatMessageRawPayload> {
throw Error('should not call messageRawPayload')
throw Error('should not call messageRawPayload: ' + id)
// log.verbose('PuppetPadchat', 'messageRawPayload(%s)', id)
// const rawPayload: PadchatMessageRawPayload = {
// content: '',
......@@ -658,10 +644,11 @@ export class PuppetPadchat extends Puppet {
type = MessageType.Video
break
default:
type = this.Message.Type.Unknown
type = MessageType.Unknown
}
const payload: MessagePayload = {
id : rawPayload.msg_id,
timestamp : Date.now(),
fromId : rawPayload.from_user,
text : rawPayload.content,
......@@ -718,7 +705,7 @@ export class PuppetPadchat extends Puppet {
await this.bridge.WXSendImage(
id,
await file.base64(),
await file.toBase64(),
)
}
......@@ -730,19 +717,20 @@ export class PuppetPadchat extends Puppet {
receiver,
messageId,
)
const payload = await this.messagePayload(messageId)
const msg = this.Message.create(messageId)
await msg.ready()
if (msg.type() === this.Message.Type.Text) {
if (payload.type === MessageType.Text) {
if (!payload.text) {
throw new Error('no text')
}
await this.messageSendText(
receiver,
msg.text(),
payload.text,
)
} else {
await this.messageSendFile(
receiver,
await msg.file(),
await this.messageFile(messageId),
)
}
}
......@@ -768,62 +756,75 @@ export class PuppetPadchat extends Puppet {
const padchatRoomRawMemberList = await this.bridge.WXGetChatRoomMember(rawPayload.user_name)
const nameMap = await this.roomParseMap('name' , padchatRoomRawMemberList.member)
const roomAliasMap = await this.roomParseMap('roomAlias' , padchatRoomRawMemberList.member)
const contactAliasMap = await this.roomParseMap('contactAlias', padchatRoomRawMemberList.member)
// const nameMap = await this.roomParseMap('name' , padchatRoomRawMemberList.member)
// const roomAliasMap = await this.roomParseMap('roomAlias' , padchatRoomRawMemberList.member)
// const contactAliasMap = await this.roomParseMap('contactAlias', padchatRoomRawMemberList.member)
const aliasDict = {}
if (Array.isArray(padchatRoomRawMemberList.member)) {
const memberListPayload = await Promise.all(
padchatRoomRawMemberList.member
.map(rawMember => rawMember.user_name)
.map(contactId => this.contactPayload(contactId)),
)
memberListPayload.forEach(contactPayload => aliasDict[contactPayload.id] = contactPayload.alias)
}
const payload: RoomPayload = {
topic : rawPayload.nick_name,
memberIdList : rawPayload.member,
nameMap : nameMap,
roomAliasMap : roomAliasMap,
contactAliasMap: contactAliasMap,
id : rawPayload.user_name,
topic : rawPayload.nick_name,
memberIdList : rawPayload.member,
aliasDict,
// nameMap : nameMap,
// roomAliasMap : roomAliasMap,
// contactAliasMap: contactAliasMap,
}
return payload
}
private async roomParseMap(
parseSection: keyof RoomMemberQueryFilter,
memberList?: PadchatRoomRawMember[],
): Promise<Map<string, string>> {
log.verbose('PuppetPadchat', 'roomParseMap(%s, memberList.length=%d)',
parseSection,
memberList && memberList.length,
)
const dict: Map<string, string> = new Map<string, string>()
if (memberList && Array.isArray(memberList)) {
for (const member of memberList) {
let tmpName: string
switch (parseSection) {
case 'name':
tmpName = member.nick_name
break
case 'roomAlias':
tmpName = member.chatroom_nick_name
break
case 'contactAlias':
const payload = await this.contactPayload(member.user_name)
tmpName = payload.alias || ''
// const contact = this.Contact.load(member.user_name)
// tmpName = contact.alias() || ''
break
default:
throw new Error('PuppetPadchat parseMap failed, member not found')
}
dict.set(member.user_name, Misc.stripEmoji(tmpName))
}
}
return dict
}
public async roomFindAll(
query: RoomQueryFilter = { topic: /.*/ },
): Promise<string[]> {
log.verbose('PuppetPadchat', 'roomFindAll(%s)', query)
// private async roomParseMap(
// parseSection: keyof RoomMemberQueryFilter,
// memberList?: PadchatRoomRawMember[],
// ): Promise<Map<string, string>> {
// log.verbose('PuppetPadchat', 'roomParseMap(%s, memberList.length=%d)',
// parseSection,
// memberList && memberList.length,
// )
// const dict: Map<string, string> = new Map<string, string>()
// if (memberList && Array.isArray(memberList)) {
// for (const member of memberList) {
// let tmpName: string
// switch (parseSection) {
// case 'name':
// tmpName = member.nick_name
// break
// case 'roomAlias':
// tmpName = member.chatroom_nick_name
// break
// case 'contactAlias':
// const payload = await this.contactPayload(member.user_name)
// tmpName = payload.alias || ''
// // const contact = this.Contact.load(member.user_name)
// // tmpName = contact.alias() || ''
// break
// default:
// throw new Error('PuppetPadchat parseMap failed, member not found')
// }
// dict.set(member.user_name, Misc.stripEmoji(tmpName))
// }
// }
// return dict
// }
public async roomList(): Promise<string[]> {
// query: RoomQueryFilter = { topic: /.*/ },
log.verbose('PuppetPadchat', 'roomFindAll() TBD')
// TODO: query
// const rooomMap = (await this.bridge.checkSyncContactOrRoom()).roomMap
......
......@@ -30,11 +30,14 @@ import {
import StateSwitch from 'state-switch'
import { parseString } from 'xml2js'
import {
MemoryCard,
} from 'memory-card'
/* tslint:disable:no-var-requires */
// const retryPromise = require('retry-promise').default
import { log } from '../config'
import Profile from '../profile'
import Misc from '../misc'
import {
......@@ -53,7 +56,7 @@ export interface InjectResult {
export interface BridgeOptions {
head? : boolean,
profile : Profile,
profile : MemoryCard,
}
export class Bridge extends EventEmitter {
......@@ -336,11 +339,11 @@ export class Bridge extends EventEmitter {
}
}
public async roomFind(filterFunc: string): Promise<string[]> {
public async roomList(): Promise<string[]> {
try {
return await this.proxyWechaty('roomFind', filterFunc)
return await this.proxyWechaty('roomList')
} catch (e) {
log.error('PuppetPuppeteerBridge', 'roomFind() exception: %s', e.message)
log.error('PuppetPuppeteerBridge', 'roomList() exception: %s', e.message)
throw e
}
}
......
......@@ -40,7 +40,6 @@ import {
Puppet,
PuppetOptions,
Receiver,
ScanPayload,
} from '../puppet/'
import {
config,
......@@ -63,26 +62,33 @@ import {
WebMessageRawPayload,
WebMediaType,
WebMessageType,
WebRoomRawMember,
// WebRoomRawMember,
WebRoomRawPayload,
} from './web-schemas'
import {
Contact,
ContactPayload,
ContactQueryFilter,
// Gender,
// ContactQueryFilter,
ContactType,
MessagePayload,
MessageType,
// RoomMemberQueryFilter,
RoomPayload,
// RoomQueryFilter,
ScanPayload,
} from '../puppet/'
import {
// Contact,
} from '../contact'
import {
// Messageirection,
MessagePayload,
MessageType,
} from '../message'
import {
Room,
RoomMemberQueryFilter,
RoomPayload,
RoomQueryFilter,
// Room,
} from '../room'
export type PuppetFoodType = 'scan' | 'ding'
......@@ -90,6 +96,7 @@ export type ScanFoodType = 'scan' | 'login' | 'logout'
export class PuppetPuppeteer extends Puppet {
public bridge : Bridge
public scanPayload? : ScanPayload
public scanWatchdog: Watchdog<ScanFoodType>
......@@ -104,7 +111,7 @@ export class PuppetPuppeteer extends Puppet {
this.fileId = 0
this.bridge = new Bridge({
head : config.head,
profile : this.options.profile,
profile : this.options.memory,
})
const SCAN_TIMEOUT = 2 * 60 * 1000 // 2 minutes
......@@ -112,7 +119,7 @@ export class PuppetPuppeteer extends Puppet {
}
public async start(): Promise<void> {
log.verbose('PuppetPuppeteer', `start() with ${this.options.profile}`)
log.verbose('PuppetPuppeteer', `start() with ${this.options.memory.name}`)
this.state.on('pending')
......@@ -142,7 +149,7 @@ export class PuppetPuppeteer extends Puppet {
* Save cookie for every 5 minutes
*/
const throttleQueue = new ThrottleQueue(5 * 60 * 1000)
this.on('heartbeat', data => throttleQueue.next(data))
this.on('watchdog', data => throttleQueue.next(data))
throttleQueue.subscribe(async data => {
log.verbose('Wechaty', 'start() throttleQueue.subscribe() new item: %s', data)
await this.saveCookie()
......@@ -180,7 +187,8 @@ export class PuppetPuppeteer extends Puppet {
this.watchdog.on('feed', food => {
log.silly('PuppetPuppeteer', 'initWatchdogForPuppet() dog.on(feed, food={type=%s, data=%s})', food.type, food.data)
// feed the dog, heartbeat the puppet.
puppet.emit('heartbeat', food.data)
// 201805 puppet no need to `heartbeat`?
// puppet.emit('heartbeat', food.data)
})
this.watchdog.on('reset', async (food, timeout) => {
......@@ -262,8 +270,15 @@ export class PuppetPuppeteer extends Puppet {
this.state.off('pending')
log.verbose('PuppetPuppeteer', 'quit() make watchdog sleep before do quit')
/**
* Clean listeners for `watchdog`
*/
this.watchdog.sleep()
this.scanWatchdog.sleep()
this.watchdog.removeAllListeners()
this.scanWatchdog.removeAllListeners()
this.removeAllListeners('watchdog')
try {
await this.bridge.quit()
......@@ -275,9 +290,9 @@ export class PuppetPuppeteer extends Puppet {
throw e
} finally {
this.state.off(true)
this.emit('stop')
}
this.emit('stop')
}
private async initBridge(): Promise<Bridge> {
......@@ -322,6 +337,7 @@ export class PuppetPuppeteer extends Puppet {
): Promise<MessagePayload> {
log.verbose('PuppetPuppeteer', 'messageRawPayloadParser(%s) @ %s', rawPayload, this)
const id = rawPayload.MsgId
const fromId = rawPayload.MMActualSender // MMPeerUserName
const text: string = rawPayload.MMActualContent // Content has @id prefix added by wx
const timestamp: number = rawPayload.MMDisplayTime // Javascript timestamp of milliseconds
......@@ -350,6 +366,7 @@ export class PuppetPuppeteer extends Puppet {
const type: MessageType = this.messageTypeFromWeb(rawPayload.MsgType)
const payload: MessagePayload = {
id,
type,
fromId,
filename,
......@@ -417,7 +434,7 @@ export class PuppetPuppeteer extends Puppet {
Cookie: cookies.map(c => `${c['name']}=${c['value']}`).join('; '),
}
const fileBox = FileBox.packRemote(url, filename, headers)
const fileBox = FileBox.fromRemote(url, filename, headers)
return fileBox
}
......@@ -511,7 +528,7 @@ export class PuppetPuppeteer extends Puppet {
newMsg.MMSourceMsgId = rawPayload.MsgId
// In room msg, the content prefix sender:, need to be removed, otherwise the forwarded sender will display the source message sender, causing self () to determine the error
newMsg.Content = Misc.unescapeHtml(rawPayload.Content.replace(/^@\w+:<br\/>/, '')).replace(/^[\w\-]+:<br\/>/, '')
newMsg.MMIsChatRoom = receiver instanceof Room ? true : false
newMsg.MMIsChatRoom = receiver.roomId ? true : false
// The following parameters need to be overridden after calling createMessage()
......@@ -637,6 +654,7 @@ export class PuppetPuppeteer extends Puppet {
// uin: rawPayload.Uin, // stable id: 4763975 || getCookie("wxuin")
return {
id: rawPayload.UserName,
weixin: rawPayload.Alias, // Wechat ID
name: Misc.plainText(rawPayload.NickName || ''),
alias: rawPayload.RemarkName,
......@@ -659,8 +677,8 @@ export class PuppetPuppeteer extends Puppet {
*/
// tslint:disable-next-line
type: (!!rawPayload.UserName && !rawPayload.UserName.startsWith('@@') && !!(rawPayload.VerifyFlag & 8))
? Contact.Type.Official
: Contact.Type.Personal,
? ContactType.Official
: ContactType.Personal,
/**
* @see 1. https://github.com/Chatie/webwx-app-tracker/blob/7c59d35c6ea0cff38426a4c5c912a086c4c512b2/formatted/webwxApp.js#L3246
* @ignore
......@@ -704,7 +722,7 @@ export class PuppetPuppeteer extends Puppet {
// await contact.ready()
const fileName = (payload.name || 'unknown') + '-avatar.jpg'
return FileBox.packRemote(
return FileBox.fromRemote(
avatarUrl,
fileName,
headers,
......@@ -743,69 +761,74 @@ export class PuppetPuppeteer extends Puppet {
}
}
private contactQueryFilterToFunctionString(
query: ContactQueryFilter,
): string {
log.verbose('PuppetPuppeteer', 'contactQueryFilterToFunctionString({ %s })',
Object.keys(query)
.map(k => `${k}: ${query[k as keyof ContactQueryFilter]}`)
.join(', '),
)
if (Object.keys(query).length !== 1) {
throw new Error('query only support one key. multi key support is not availble now.')
}
const filterKey = Object.keys(query)[0] as keyof ContactQueryFilter
let filterValue: string | RegExp | undefined = query[filterKey]
if (!filterValue) {
throw new Error('filterValue not found')
}
const protocolKeyMap = {
name: 'NickName',
alias: 'RemarkName',
}
const protocolFilterKey = protocolKeyMap[filterKey]
if (!protocolFilterKey) {
throw new Error('unsupport protocol filter key')
}
/**
* must be string because we need inject variable value
* into code as variable namespecialContactList
*/
let filterFunction: string
if (filterValue instanceof RegExp) {
filterFunction = `(function (c) { return ${filterValue.toString()}.test(c.${protocolFilterKey}) })`
} else if (typeof filterValue === 'string') {
filterValue = filterValue.replace(/'/g, '\\\'')
filterFunction = `(function (c) { return c.${protocolFilterKey} === '${filterValue}' })`
} else {
throw new Error('unsupport name type')
}
return filterFunction
// private contactQueryFilterToFunctionString(
// query: ContactQueryFilter,
// ): string {
// log.verbose('PuppetPuppeteer', 'contactQueryFilterToFunctionString({ %s })',
// Object.keys(query)
// .map(k => `${k}: ${query[k as keyof ContactQueryFilter]}`)
// .join(', '),
// )
// if (Object.keys(query).length !== 1) {
// throw new Error('query only support one key. multi key support is not availble now.')
// }
// const filterKey = Object.keys(query)[0] as keyof ContactQueryFilter
// let filterValue: string | RegExp | undefined = query[filterKey]
// if (!filterValue) {
// throw new Error('filterValue not found')
// }
// const protocolKeyMap = {
// name: 'NickName',
// alias: 'RemarkName',
// }
// const protocolFilterKey = protocolKeyMap[filterKey]
// if (!protocolFilterKey) {
// throw new Error('unsupport protocol filter key')
// }
// /**
// * must be string because we need inject variable value
// * into code as variable namespecialContactList
// */
// let filterFunction: string
// if (filterValue instanceof RegExp) {
// filterFunction = `(function (c) { return ${filterValue.toString()}.test(c.${protocolFilterKey}) })`
// } else if (typeof filterValue === 'string') {
// filterValue = filterValue.replace(/'/g, '\\\'')
// filterFunction = `(function (c) { return c.${protocolFilterKey} === '${filterValue}' })`
// } else {
// throw new Error('unsupport name type')
// }
// return filterFunction
// }
public async contactList(): Promise<string[]> {
const idList = await this.bridge.contactList()
return idList
}
public async contactFindAll(
query: ContactQueryFilter = { name: /.*/ },
): Promise<string[]> {
// public async contactFindAll(
// query: ContactQueryFilter = { name: /.*/ },
// ): Promise<string[]> {
const filterFunc = this.contactQueryFilterToFunctionString(query)
// const filterFunc = this.contactQueryFilterToFunctionString(query)
try {
const idList = await this.bridge.contactFind(filterFunc)
return idList
} catch (e) {
log.warn('PuppetPuppeteer', 'contactFind(%s) rejected: %s', filterFunc, e.message)
Raven.captureException(e)
throw e
}
}
// try {
// const idList = await this.bridge.contactFind(filterFunc)
// return idList
// } catch (e) {
// log.warn('PuppetPuppeteer', 'contactFind(%s) rejected: %s', filterFunc, e.message)
// Raven.captureException(e)
// throw e
// }
// }
/**
*
......@@ -871,102 +894,122 @@ export class PuppetPuppeteer extends Puppet {
// .map(m => this.Contact.load(m.UserName))
// await Promise.all(memberList.map(c => c.ready()))
const id = rawPayload.UserName
const rawMemberList = rawPayload.MemberList || []
const memberIdList = rawMemberList.map(rawMember => rawMember.UserName)
const nameMap = await this.roomParseMap('name' , rawPayload.MemberList)
const roomAliasMap = await this.roomParseMap('roomAlias' , rawPayload.MemberList)
const contactAliasMap = await this.roomParseMap('contactAlias', rawPayload.MemberList)
// const nameMap = await this.roomParseMap('name' , rawPayload.MemberList)
// const roomAliasMap = await this.roomParseMap('roomAlias' , rawPayload.MemberList)
// const contactAliasMap = await this.roomParseMap('contactAlias', rawPayload.MemberList)
const aliasDict = {}
if (Array.isArray(rawPayload.MemberList)) {
const memberListPayload = await Promise.all(
rawPayload.MemberList
.map(rawMember => rawMember.UserName)
.map(contactId => this.contactPayload(contactId)),
)
memberListPayload.forEach(payload => aliasDict[payload.id] = payload.alias)
}
const roomPayload: RoomPayload = {
id,
topic: Misc.plainText(rawPayload.NickName || ''),
memberIdList,
nameMap,
roomAliasMap,
contactAliasMap,
aliasDict,
// nameMap,
// roomAliasMap,
// contactAliasMap,
}
// console.log(roomPayload)
return roomPayload
}
private async roomParseMap(
parseSection: keyof RoomMemberQueryFilter,
memberList?: WebRoomRawMember[],
): Promise<Map<string, string>> {
log.verbose('PuppetPuppeteer', 'roomParseMap(%s, memberList.length=%d)',
parseSection,
memberList && memberList.length,
)
const dict: Map<string, string> = new Map<string, string>()
const updateMember = async (member: WebRoomRawMember) => {
let tmpName: string
// console.log(member)
const payload = await this.contactPayload(member.UserName)
// contact.ready().then(() => console.log('###############', contact.name()))
// console.log(contact)
// log.silly('PuppetPuppeteer', 'roomParseMap() memberList.forEach(contact=%s)', contact)
switch (parseSection) {
case 'name':
tmpName = payload.name || ''
break
case 'roomAlias':
tmpName = member.DisplayName
break
case 'contactAlias':
tmpName = payload.alias || ''
break
default:
throw new Error('parseMap failed, member not found')
}
/**
* ISSUE #64 emoji need to be striped
* ISSUE #104 never use remark name because sys group message will never use that
* @rui: Wrong for 'never use remark name because sys group message will never use that', see more in the latest comment in #104
* @rui: webwx's NickName here return contactAlias, if not set contactAlias, return name
* @rui: 2017-7-2 webwx's NickName just ruturn name, no contactAlias
*/
dict.set(member.UserName, Misc.stripEmoji(tmpName))
}
if (Array.isArray(memberList)) {
await Promise.all(memberList.map(updateMember))
}
return dict
// private async roomParseMap(
// parseSection : keyof RoomMemberQueryFilter,
// rawMemberList? : WebRoomRawMember[],
// ): Promise<{ string, string }> {
// log.verbose('PuppetPuppeteer', 'roomParseMap(%s, rawMemberList.length=%d)',
// parseSection,
// rawMemberList && rawMemberList.length,
// )
// const dict: Map<string, string> = new Map<string, string>()
// const updateMember = async (member: WebRoomRawMember) => {
// let tmpName: string
// // console.log(member)
// const payload = await this.contactPayload(member.UserName)
// // contact.ready().then(() => console.log('###############', contact.name()))
// // console.log(contact)
// // log.silly('PuppetPuppeteer', 'roomParseMap() memberList.forEach(contact=%s)', contact)
// switch (parseSection) {
// case 'name':
// tmpName = payload.name || ''
// break
// case 'roomAlias':
// tmpName = member.DisplayName
// break
// case 'contactAlias':
// tmpName = payload.alias || ''
// break
// default:
// throw new Error('parseMap failed, member not found')
// }
// /**
// * ISSUE #64 emoji need to be striped
// * ISSUE #104 never use remark name because sys group message will never use that
// * @rui: Wrong for 'never use remark name because sys group message will never use that', see more in the latest comment in #104
// * @rui: webwx's NickName here return contactAlias, if not set contactAlias, return name
// * @rui: 2017-7-2 webwx's NickName just ruturn name, no contactAlias
// */
// dict.set(member.UserName, Misc.stripEmoji(tmpName))
// }
// if (Array.isArray(rawMemberList)) {
// await Promise.all(rawMemberList.map(updateMember))
// }
// return dict
// }
public async roomList(): Promise<string[]> {
log.verbose('PuppetPupppeteer', 'roomList()')
const idList = await this.bridge.roomList()
return idList
}
public async roomFindAll(
query: RoomQueryFilter = { topic: /.*/ },
): Promise<string[]> {
let topicFilter = query.topic
if (!topicFilter) {
throw new Error('topicFilter not found')
}
let filterFunction: string
if (topicFilter instanceof RegExp) {
filterFunction = `(function (c) { return ${topicFilter.toString()}.test(c) })`
} else if (typeof topicFilter === 'string') {
topicFilter = topicFilter.replace(/'/g, '\\\'')
filterFunction = `(function (c) { return c === '${topicFilter}' })`
} else {
throw new Error('unsupport topic type')
}
try {
const idList = await this.bridge.roomFind(filterFunction)
return idList
} catch (e) {
log.warn('PuppetPuppeteer', 'roomFind(%s) rejected: %s', filterFunction, e.message)
Raven.captureException(e)
throw e
}
}
// public async roomFindAll(
// query: RoomQueryFilter = { topic: /.*/ },
// ): Promise<string[]> {
// let topicFilter = query.topic
// if (!topicFilter) {
// throw new Error('topicFilter not found')
// }
// let filterFunction: string
// if (topicFilter instanceof RegExp) {
// filterFunction = `(function (c) { return ${topicFilter.toString()}.test(c) })`
// } else if (typeof topicFilter === 'string') {
// topicFilter = topicFilter.replace(/'/g, '\\\'')
// filterFunction = `(function (c) { return c === '${topicFilter}' })`
// } else {
// throw new Error('unsupport topic type')
// }
// try {
// const idList = await this.bridge.roomFind(filterFunction)
// return idList
// } catch (e) {
// log.warn('PuppetPuppeteer', 'roomFind(%s) rejected: %s', filterFunction, e.message)
// Raven.captureException(e)
// throw e
// }
// }
public async roomDel(
roomId : string,
......@@ -1072,7 +1115,7 @@ export class PuppetPuppeteer extends Puppet {
const sleepTime = 60 * 1000 / ttl
while (ttl-- > 0) {
const contactIdList = await this.contactFindAll()
const contactIdList = await this.contactList()
if (prevLength === contactIdList.length) {
log.verbose('PuppetPuppeteer', 'readyStable() stable() READY length=%d', prevLength)
return
......@@ -1111,8 +1154,8 @@ export class PuppetPuppeteer extends Puppet {
public async saveCookie(): Promise<void> {
const cookieList = await this.bridge.cookies()
this.options.profile.set('cookies', cookieList)
this.options.profile.save()
this.options.memory.set('cookies', cookieList)
this.options.memory.save()
}
private extToType(ext: string): WebMessageType {
......
......@@ -644,18 +644,19 @@
})
}
function roomFind(filterFunction) {
// function roomFind(filterFunction) {
function roomList() {
var contactFactory = WechatyBro.glue.contactFactory
var match
if (!filterFunction) {
match = () => true
} else {
match = eval(filterFunction)
}
// var match
// if (!filterFunction) {
// match = () => true
// } else {
// match = eval(filterFunction)
// }
// log(match.toString())
return contactFactory.getAllChatroomContact()
.filter(r => match(r.NickName))
// .filter(r => match(r.NickName))
.map(r => r.UserName)
}
......@@ -855,7 +856,7 @@
// for Wechaty Room Class
roomCreate,
roomAddMember,
roomFind,
roomList,
roomDelMember,
roomModTopic,
......
......@@ -37,7 +37,7 @@ class Fixture extends Puppet {
public async start() : Promise<void> { return {} as any }
public async stop() : Promise<void> { return {} as any }
public async ding(data?: any) : Promise<string> { return {} as any }
public async ding(data?: any) : Promise<string> { return {data} as any }
public async logout(): Promise<void> { return {} as any }
/**
......@@ -47,49 +47,49 @@ class Fixture extends Puppet {
*/
public async contactAlias(contactId: string) : Promise<string>
public async contactAlias(contactId: string, alias: string | null) : Promise<void>
public async contactAlias(contactId: string, alias?: string|null) : Promise<string | void> { return {} as any }
public async contactAvatar(contactId: string) : Promise<FileBox> { return {} as any }
public async contactAlias(contactId: string, alias?: string|null) : Promise<string | void> { return {contactId, alias} as any }
public async contactAvatar(contactId: string) : Promise<FileBox> { return {contactId} as any }
public async contactList() : Promise<string[]> { return {} as any }
public async contactRawPayload(id: string) : Promise<any> { return {} as any }
public async contactRawPayloadParser(rawPayload: any) : Promise<ContactPayload> { return {} as any }
public async contactRawPayload(id: string) : Promise<any> { return {id} as any }
public async contactRawPayloadParser(rawPayload: any) : Promise<ContactPayload> { return {rawPayload} as any }
/**
*
* FriendRequest
*
*/
public async friendRequestSend(contactId: string, hello?: string) : Promise<void> { return {} as any }
public async friendRequestAccept(contactId: string, ticket: string) : Promise<void> { return {} as any }
public async friendRequestSend(contactId: string, hello?: string) : Promise<void> { return {contactId, hello} as any }
public async friendRequestAccept(contactId: string, ticket: string) : Promise<void> { return {contactId, ticket} as any }
/**
*
* Message
*
*/
public async messageFile(messageId: string) : Promise<FileBox> { return {} as any }
public async messageForward(to: Receiver, messageId: string) : Promise<void> { return {} as any }
public async messageSendText(to: Receiver, text: string) : Promise<void> { return {} as any }
public async messageSendFile(to: Receiver, file: FileBox) : Promise<void> { return {} as any }
public async messageFile(messageId: string) : Promise<FileBox> { return {messageId} as any }
public async messageForward(to: Receiver, messageId: string) : Promise<void> { return {to, messageId} as any }
public async messageSendText(to: Receiver, text: string) : Promise<void> { return {to, text} as any }
public async messageSendFile(to: Receiver, file: FileBox) : Promise<void> { return {to, file} as any }
public async messageRawPayload(id: string) : Promise<any> { return {} as any }
public async messageRawPayloadParser(rawPayload: any) : Promise<MessagePayload> { return {} as any }
public async messageRawPayload(id: string) : Promise<any> { return {id} as any }
public async messageRawPayloadParser(rawPayload: any) : Promise<MessagePayload> { return {rawPayload} as any }
/**
*
* Room
*
*/
public async roomAdd(roomId: string, contactId: string) : Promise<void> { return {} as any }
public async roomCreate(contactIdList: string[], topic?: string) : Promise<string> { return {} as any }
public async roomDel(roomId: string, contactId: string) : Promise<void> { return {} as any }
public async roomQuit(roomId: string) : Promise<void> { return {} as any }
public async roomTopic(roomId: string, topic?: string) : Promise<string | void> { return {} as any }
public async roomAdd(roomId: string, contactId: string) : Promise<void> { return {roomId, contactId} as any }
public async roomCreate(contactIdList: string[], topic?: string) : Promise<string> { return {contactIdList, topic} as any }
public async roomDel(roomId: string, contactId: string) : Promise<void> { return {roomId, contactId} as any }
public async roomQuit(roomId: string) : Promise<void> { return {roomId} as any }
public async roomTopic(roomId: string, topic?: string) : Promise<string | void> { return {roomId, topic} as any }
public async roomList() : Promise<string[]> { return {} as any }
public async roomRawPayload(id: string) : Promise<any> { return {} as any }
public async roomRawPayloadParser(rawPayload: any) : Promise<RoomPayload> { return {} as any }
public async roomRawPayload(id: string) : Promise<any> { return {id} as any }
public async roomRawPayloadParser(rawPayload: any) : Promise<RoomPayload> { return {rawPayload} as any }
}
......
......@@ -2,11 +2,11 @@ import {
MemoryCard,
} from 'memory-card'
// export interface ScanPayload {
// code : number, // Code
// data? : string, // Image Data URL
// url : string, // QR Code URL
// }
export interface ScanPayload {
code : number, // Code
data? : string, // Image Data URL
url : string, // QR Code URL
}
export const CHAT_EVENT_DICT = {
friend : 'document can be writen at here',
......
......@@ -121,7 +121,7 @@ export class Room extends Accessory implements Sayable {
}
try {
const roomIdList = await this.puppet.roomFindAll(query)
const roomIdList = await this.puppet.roomSearch(query)
const roomList = roomIdList.map(id => this.load(id))
await Promise.all(roomList.map(room => room.ready()))
......@@ -554,10 +554,10 @@ export class Room extends Accessory implements Sayable {
* @returns {(string | null)}
*/
public roomAlias(contact: Contact): null | string {
if (!this.payload || !this.payload.roomAliasMap) {
if (!this.payload || !this.payload.aliasDict) {
return null
}
return this.payload.roomAliasMap.get(contact.id) || null
return this.payload.aliasDict[contact.id] || null
}
/**
......@@ -585,8 +585,8 @@ export class Room extends Accessory implements Sayable {
.length > 0
}
public memberAll(filter: RoomMemberQueryFilter): Contact[]
public memberAll(name: string): Contact[]
public async memberAll(filter: RoomMemberQueryFilter): Promise<Contact[]>
public async memberAll(name: string): Promise<Contact[]>
/**
* The way to search member by Room.member()
......@@ -609,21 +609,23 @@ export class Room extends Accessory implements Sayable {
* @returns {Contact[]}
* @memberof Room
*/
public memberAll(queryArg: RoomMemberQueryFilter | string): Contact[] {
public async memberAll(
queryArg: string | RoomMemberQueryFilter,
): Promise<Contact[]> {
if (typeof queryArg === 'string') {
// TODO: filter the duplicated result
return ([] as Contact[]).concat(
this.memberAll({name: queryArg}),
this.memberAll({roomAlias: queryArg}),
this.memberAll({contactAlias: queryArg}),
await this.memberAll({name: queryArg}),
await this.memberAll({roomAlias: queryArg}),
await this.memberAll({contactAlias: queryArg}),
)
}
/**
* We got filter parameter
*/
log.silly('Room', 'memberAll({ %s })',
JSON.stringify(queryArg),
log.silly('Room', 'memberAll(%s)',
JSON.stringify(queryArg),
// Object.keys(queryArg)
// .map((k: keyof RoomMemberQueryFilter) => `${k}: ${queryArg[k]}`)
// .join(', '),
......@@ -646,23 +648,24 @@ export class Room extends Accessory implements Sayable {
throw new Error('filterValue not found')
}
const keyMap = {
contactAlias: 'contactAliasMap',
name: 'nameMap',
alias: 'roomAliasMap',
roomAlias: 'roomAliasMap',
}
const idList = await this.puppet.roomMemberSearch(this.id, queryArg)
// const keyMap = {
// contactAlias: 'contactAliasMap',
// name: 'nameMap',
// alias: 'roomAliasMap',
// roomAlias: 'roomAliasMap',
// }
const filterMapName = keyMap[filterKey] as keyof RoomPayload
if (!filterMapName) {
throw new Error('unsupport filter key: ' + filterKey)
}
// const filterMapName = keyMap[filterKey] as keyof RoomPayload
// if (!filterMapName) {
// throw new Error('unsupport filter key: ' + filterKey)
// }
const filterMap = this.payload[filterMapName] as Map<string, string>
const idList = Array.from(filterMap.keys())
.filter(id => filterMap.get(id) === filterValue)
// const filterMap = this.payload[filterMapName] as Map<string, string>
// const idList = Array.from(filterMap.keys())
// .filter(id => filterMap.get(id) === filterValue)
log.silly('Room', 'memberAll() check %s from %s: %s', filterValue, filterKey, JSON.stringify(filterMap))
// log.silly('Room', 'memberAll() check %s from %s: %s', filterValue, filterKey, JSON.stringify(filterMap))
if (idList.length) {
return idList.map(id => this.wechaty.Contact.load(id))
......@@ -671,8 +674,8 @@ export class Room extends Accessory implements Sayable {
}
}
public member(name : string) : null | Contact
public member(filter: RoomMemberQueryFilter): null | Contact
public async member(name : string) : Promise<null | Contact>
public async member(filter: RoomMemberQueryFilter): Promise<null | Contact>
/**
* Find all contacts in a room, if get many, return the first one.
......@@ -702,18 +705,18 @@ export class Room extends Accessory implements Sayable {
* }
* }
*/
public member(
public async member(
queryArg: string | RoomMemberQueryFilter,
): null | Contact {
): Promise<null | Contact> {
log.verbose('Room', 'member(%s)', JSON.stringify(queryArg))
let memberList: Contact[]
// ISSUE #622
// error TS2345: Argument of type 'string | MemberQueryFilter' is not assignable to parameter of type 'MemberQueryFilter' #622
if (typeof queryArg === 'string') {
memberList = this.memberAll(queryArg)
memberList = await this.memberAll(queryArg)
} else {
memberList = this.memberAll(queryArg)
memberList = await this.memberAll(queryArg)
}
if (!memberList || !memberList.length) {
......@@ -742,8 +745,8 @@ export class Room extends Accessory implements Sayable {
})
return []
}
const memberList = this.payload.memberIdList.map(id => this.wechaty.Contact.load(id))
return memberList
const contactList = this.payload.memberIdList.map(id => this.wechaty.Contact.load(id))
return contactList
}
/**
......
{
"compilerOptions": {
"target": "es6"
"target": "es6"
, "module": "commonjs"
, "outDir": "dist"
, "strict": true
, "traceResolution": false
, "noLib": false
, "experimentalDecorators": true
, "emitDecoratorMetadata": true
, "declaration": true
, "sourceMap": true
, "traceResolution": false
, "noEmitOnError": true
, "noUnusedLocals": true
......@@ -17,19 +19,19 @@
, "noFallthroughCasesInSwitch": true
, "strictNullChecks": true
, "noImplicitAny": false
, "noUnusedParameters": false
, "noImplicitThis": false
, "noImplicitAny": true
, "noUnusedParameters": true
, "noImplicitThis": true
, "noLib": false
, "skipLibCheck": true
, "lib": [
"esnext", "dom"
"esnext"
]
}
, "exclude": [
"node_modules/"
, "dist/"
, "tests/fixtures/"
]
, "include": [
"app/**/*.ts"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册