From 1175ea9cc5d2f60a906552c0eace1618a02e8800 Mon Sep 17 00:00:00 2001 From: Huan LI Date: Sat, 11 Aug 2018 13:00:48 +0800 Subject: [PATCH] add message.find() support https://github.com/Chatie/wechaty/issues/1534 --- src/puppet.spec.ts | 120 +++++++++++++++++++++++++++++++++++++++++ src/puppet.ts | 75 ++++++++++++++++++++++++++ src/schemas/message.ts | 14 +++++ 3 files changed, 209 insertions(+) diff --git a/src/puppet.spec.ts b/src/puppet.spec.ts index 84560c1b..e5dffcfe 100755 --- a/src/puppet.spec.ts +++ b/src/puppet.spec.ts @@ -27,6 +27,9 @@ import { } from './schemas/friendship' import { MessagePayload, + MessagePayloadFilterFunction, + MessageQueryFilter, + MessageType, } from './schemas/message' import { RoomMemberPayload, @@ -112,6 +115,12 @@ class PuppetTest extends Puppet { public async messageRawPayload (id: string) : Promise { return { id } as any } public async messageRawPayloadParser (rawPayload: any) : Promise { return { rawPayload } as any } + public messageQueryFilterFactory ( + query: MessageQueryFilter, + ): MessagePayloadFilterFunction { + return super.messageQueryFilterFactory(query) + } + /** * * Room Invitation @@ -413,3 +422,114 @@ test('setMemory() memory with a name', async t => { t.doesNotThrow(() => puppet.setMemory(memory), 'should not throw when set a named memory first time ') t.throws(() => puppet.setMemory(memory), 'should throw when set a named memory second time') }) + +test('messageQueryFilterFactory() one condition', async t => { + const EXPECTED_TEXT1 = 'text' + const EXPECTED_TEXT2 = 'regexp' + const EXPECTED_TEXT3 = 'fdsafasdfsdakljhj;lds' + + const TEXT_QUERY_TEXT = EXPECTED_TEXT1 + const TEXT_QUERY_RE = new RegExp(EXPECTED_TEXT2) + + const QUERY_TEXT: MessageQueryFilter = { + text: TEXT_QUERY_TEXT, + } + + const QUERY_RE: MessageQueryFilter = { + text: TEXT_QUERY_RE, + } + + const PAYLOAD_LIST = [ + { + text: EXPECTED_TEXT1, + }, + { + text: EXPECTED_TEXT2, + }, + { + text: EXPECTED_TEXT3, + }, + ] as MessagePayload[] + + const puppet = new PuppetTest() + + let filterFuncText + let resultPayload + + filterFuncText = puppet.messageQueryFilterFactory(QUERY_TEXT) + resultPayload = PAYLOAD_LIST.filter(filterFuncText) + t.equal(resultPayload.length, 1, 'should get one result') + t.equal(resultPayload[0].text, EXPECTED_TEXT1, 'should get text1') + + filterFuncText = puppet.messageQueryFilterFactory(QUERY_RE) + resultPayload = PAYLOAD_LIST.filter(filterFuncText) + t.equal(resultPayload.length, 1, 'should get one result') + t.equal(resultPayload[0].text, EXPECTED_TEXT2, 'should get text2') +}) + +test('messageQueryFilterFactory() two condition', async t => { + const EXPECTED_TEXT_DATA = 'data' + const EXPECTED_TEXT_LINK = 'https://google.com' + + const EXPECTED_TYPE_URL = MessageType.Url + const EXPECTED_TYPE_TEXT = MessageType.Text + + const QUERY_TEXT: MessageQueryFilter = { + text: EXPECTED_TEXT_DATA, + } + + const QUERY_TYPE: MessageQueryFilter = { + type: EXPECTED_TYPE_URL, + } + + const QUERY_TYPE_TEXT: MessageQueryFilter = { + text: EXPECTED_TEXT_DATA, + type: EXPECTED_TYPE_URL, + } + + const PAYLOAD_LIST = [ + { + text: EXPECTED_TEXT_DATA, + type: MessageType.Text + }, + { + text: EXPECTED_TEXT_DATA, + type: MessageType.Url + }, + { + text: EXPECTED_TEXT_LINK, + type: MessageType.Text + }, + { + text: EXPECTED_TEXT_LINK, + type: MessageType.Url, + }, + ] as MessagePayload[] + + const puppet = new PuppetTest() + + let filterFuncText + let resultPayload + + filterFuncText = puppet.messageQueryFilterFactory(QUERY_TEXT) + resultPayload = PAYLOAD_LIST.filter(filterFuncText) + t.equal(resultPayload.length, 2, 'should get two result') + t.equal(resultPayload[0].text, EXPECTED_TEXT_DATA, 'should get text data') + t.equal(resultPayload[0].type, EXPECTED_TYPE_TEXT, 'should get type text') + t.equal(resultPayload[1].text, EXPECTED_TEXT_DATA, 'should get text data') + t.equal(resultPayload[1].type, EXPECTED_TYPE_URL, 'should get type url') + + filterFuncText = puppet.messageQueryFilterFactory(QUERY_TYPE) + resultPayload = PAYLOAD_LIST.filter(filterFuncText) + t.equal(resultPayload.length, 2, 'should get two result') + t.equal(resultPayload[0].text, EXPECTED_TEXT_DATA, 'should get text data') + t.equal(resultPayload[0].type, EXPECTED_TYPE_URL, 'should get type url') + t.equal(resultPayload[1].text, EXPECTED_TEXT_LINK, 'should get text link') + t.equal(resultPayload[1].type, EXPECTED_TYPE_URL, 'should get type url ') + + filterFuncText = puppet.messageQueryFilterFactory(QUERY_TYPE_TEXT) + resultPayload = PAYLOAD_LIST.filter(filterFuncText) + t.equal(resultPayload.length, 1, 'should get one result') + t.equal(resultPayload[0].text, EXPECTED_TEXT_DATA, 'should get text data') + t.equal(resultPayload[0].type, EXPECTED_TYPE_URL, 'should get type url') +}) diff --git a/src/puppet.ts b/src/puppet.ts index 22f59958..15c342d4 100644 --- a/src/puppet.ts +++ b/src/puppet.ts @@ -65,6 +65,9 @@ import { } from './schemas/friendship' import { MessagePayload, + MessagePayloadFilterFunction, + MessageQueryFilter, + MessageType, } from './schemas/message' import { RoomMemberPayload, @@ -811,6 +814,78 @@ export abstract class Puppet extends EventEmitter { return payload } + public messageList (): string[] { + log.verbose('Puppet', 'messageList()') + return this.cacheMessagePayload.keys() + } + + public async messageSearch ( + query?: MessageQueryFilter, + ): Promise { + log.verbose('Puppet', 'messageSearch(%s)', JSON.stringify(query)) + + const allMessageIdList: string[] = this.messageList() + log.silly('Puppet', 'messageSearch() allMessageIdList.length=%d', allMessageIdList.length) + + if (!query || Object.keys(query).length <= 0) { + return allMessageIdList + } + + const messagePayloadList: MessagePayload[] = await Promise.all( + allMessageIdList.map( + id => this.messagePayload(id) + ), + ) + + const filterFunction = this.messageQueryFilterFactory(query) + + const messageIdList = messagePayloadList + .filter(filterFunction) + .map(payload => payload.id) + + log.silly('Puppet', 'messageSearch() messageIdList filtered. result length=%d', messageIdList.length) + + return messageIdList + } + + protected messageQueryFilterFactory ( + query: MessageQueryFilter, + ): MessagePayloadFilterFunction { + log.verbose('Puppet', 'messageQueryFilterFactory(%s)', + JSON.stringify(query), + ) + + if (Object.keys(query).length < 1) { + throw new Error('query empty') + } + + const filterFunctionList: MessagePayloadFilterFunction[] = [] + + // TypeScript bug: have to set `undefined | string | RegExp` at here, or the later code type check will get error + const filterKeyList = Object.keys(query) as Array + + for (const filterKey of filterKeyList) { + const filterValue: undefined | string | MessageType | RegExp = query[filterKey] + if (!filterValue) { + throw new Error('filterValue not found for filterKey: ' + filterKey) + } + + let filterFunction: MessagePayloadFilterFunction + + if (filterValue instanceof RegExp) { + filterFunction = (payload: MessagePayload) => filterValue.test(payload[filterKey] as string) + } else { // if (typeof filterValue === 'string') { + filterFunction = (payload: MessagePayload) => filterValue === payload[filterKey] + } + + filterFunctionList.push(filterFunction) + } + + const allFilterFunction: MessagePayloadFilterFunction = payload => filterFunctionList.every(func => func(payload)) + + return allFilterFunction + } + /** * * Room Invitation diff --git a/src/schemas/message.ts b/src/schemas/message.ts index 1f6d9657..72d9d3f1 100644 --- a/src/schemas/message.ts +++ b/src/schemas/message.ts @@ -97,3 +97,17 @@ export type MessagePayload = MessagePayloadBase MessagePayloadRoom | MessagePayloadTo ) + +export interface MessageQueryFilter { + fromId? : string, + text? : string | RegExp, + roomId? : string + type? : MessageType, + toId? : string, +} + +/** @hidden */ +export type MessagePayloadFilterFunction = (payload: MessagePayload) => boolean + +/** @hidden */ +export type MessagePayloadFilterFactory = (query: MessageQueryFilter) => MessagePayloadFilterFunction