diff --git a/examples/friend-bot.ts b/examples/friend-bot.ts index 807679e479e1b63655e3c129d73e8320612805a9..1c15ed1cc1e22692db2dddf8bc90686a44dc6f54 100644 --- a/examples/friend-bot.ts +++ b/examples/friend-bot.ts @@ -92,7 +92,7 @@ bot * when request is set, we can get verify message from `request.hello`, * and accept this request by `request.accept()` */ - if (request.hello === 'ding') { + if (request.hello() === 'ding') { logMsg = 'accepted because verify messsage is "ding"' request.accept() diff --git a/examples/gist-bot/on-friend.ts b/examples/gist-bot/on-friend.ts index 639574ef3bcd75acd5b07ac36712df5a45cecde1..3cc6bc03805f202e71ebc80401e4fd9a8c3bb643 100644 --- a/examples/gist-bot/on-friend.ts +++ b/examples/gist-bot/on-friend.ts @@ -53,7 +53,7 @@ export async function onFriend( 3000, ) - if (request.hello === 'ding') { + if (request.hello() === 'ding') { const myRoom = await this.Room.find({ topic: 'ding' }) if (!myRoom) return setTimeout( diff --git a/package.json b/package.json index ecc7c42756ea811cf5f4dee56dabfe437e030c8a..8ae66668d39a875e6f21382cc1c882db5ca14959 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wechaty", - "version": "0.15.27", + "version": "0.15.30", "description": "Wechat for Bot(Personal Account)", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -97,13 +97,14 @@ "node": ">= 8.5" }, "dependencies": { - "@types/ws": "^4.0.1", + "@types/ws": "^5.1.0", "bl": "^1.2.0", "brolog": "^1.2.0", - "clone-class": "^0.6.1", + "clone-class": "^0.6.3", "cuid": "^2.1.1", "hot-import": "^0.1.0", "mime": "^2.2.0", + "normalize-package-data": "^2.4.0", "puppeteer": "^1.2.0", "raven": "^2.2.0", "read-pkg-up": "^3.0.0", @@ -125,13 +126,14 @@ "@types/fluent-ffmpeg": "^2.1.0", "@types/glob": "^5.0.0p", "@types/mime": "^2.0.0", - "@types/node": "^9.6.7", + "@types/node": "^10.0.3", + "@types/normalize-package-data": "^2.4.0", "@types/puppeteer": "^1.0.0", "@types/raven": "^2.1.0", "@types/read-pkg-up": "^3.0.0", "@types/request": "^2.0.0", "@types/semver": "^5.5.0", - "@types/sinon": "^4.3.0", + "@types/sinon": "^4.3.1", "@types/xml2js": "^0.4.0", "apiai": "^4.0.0", "babel-cli": "^6.26.0", @@ -156,7 +158,7 @@ "qrcode-terminal": "^0.12.0", "semver": "^5.5.0", "shx": "^0.2.0", - "sinon": "^5.0.0", + "sinon": "^5.0.2", "sinon-test": "^2.1.2", "sloc": "^0.2.0", "ts-node": "^6.0.0", diff --git a/src/puppet-accessory.ts b/src/puppet-accessory.ts index 733b29da6c968956bb82fb00276984838e8576de..eac22170eb7e960ebf3aa1301d6cad109e21c079 100644 --- a/src/puppet-accessory.ts +++ b/src/puppet-accessory.ts @@ -49,12 +49,17 @@ export abstract class PuppetAccessory extends EventEmitter { private _puppet?: Puppet public set puppet(puppet: Puppet) { - log.silly('PuppetAccessory', '<%s> set puppet(%s)', this[PUPPET_ACCESSORY_NAME], puppet.constructor.name) + log.silly('PuppetAccessory', '<%s> set puppet(%s)', + this[PUPPET_ACCESSORY_NAME], + puppet.constructor.name, + ) this._puppet = puppet } public get puppet(): Puppet { - log.silly('PuppetAccessory', '<%s> get puppet()', this[PUPPET_ACCESSORY_NAME]) + log.silly('PuppetAccessory', '<%s> get puppet()', + this[PUPPET_ACCESSORY_NAME], + ) if (this._puppet) { return this._puppet diff --git a/src/puppet-mock/README.md b/src/puppet-mock/README.md new file mode 100644 index 0000000000000000000000000000000000000000..433dc15caf4760d403de093a7c4558f365a8630e --- /dev/null +++ b/src/puppet-mock/README.md @@ -0,0 +1,41 @@ +# PUPPET-MOCK + +```ts +import PuppetMock from 'wechaty-puppet-mock' + +const puppet = new PuppetMock({ + profile, +}) + +const wechaty = new Wechaty({ + puppet, +}) +``` + +## HELPER UTILITIES + +### StateSwitch + +```ts +this.state.on('pending') +this.state.on(true) +this.state.off('pending') +this.state.off(true) + +await this.state.ready('on') +await this.state.ready('off') + +``` + +### Watchdog + +```ts +``` + +### Profile + +```ts +this.profile.set('config', { id: 1, key: 'xxx' }) +const config = await this.profile.get('config') +``` + diff --git a/src/puppet-mock/mock-friend-request.ts b/src/puppet-mock/mock-friend-request.ts index 77dfe9292344c1e99dd1653cc668cb7f9734d14a..024bbefd17b1913ef938b01e1fa3b76b07a041dc 100644 --- a/src/puppet-mock/mock-friend-request.ts +++ b/src/puppet-mock/mock-friend-request.ts @@ -22,23 +22,27 @@ import { log, } from '../config' -import { FriendRequest } from '../puppet/' +import { FriendRequest, FriendRequestType } from '../puppet/' import MockContact from './mock-contact' export class MockFriendRequest extends FriendRequest { - public info: any + public payload: any private ticket: string + private _contact: MockContact + private _type: FriendRequestType + private _hello: string constructor() { log.verbose('MockFriendRequest', 'constructor()') super() } - public receive(info: any): void { - log.verbose('MockFriendRequest', 'receive(%s)', info) + public receive(payload: any): void { + log.verbose('MockFriendRequest', 'receive(%s)', payload) + this.payload = payload } public confirm(contact: MockContact): void { @@ -52,9 +56,24 @@ export class MockFriendRequest extends FriendRequest { public async accept(): Promise { log.verbose('FriendRequest', 'accept() %s', this.contact) - await this.puppet.friendRequestAccept(this.contact, this.ticket) + await this.puppet.friendRequestAccept(this.contact(), this.ticket) } + public contact(): MockContact { + return this._contact + } + + public type(): FriendRequestType { + return this._type + } + + public async reject(): Promise { + log.verbose('MockFriendRequest', 'reject()') + } + + public hello(): string { + return this._hello + } } export default MockFriendRequest diff --git a/src/puppet-mock/mock-message.ts b/src/puppet-mock/mock-message.ts index 45e75ae886c6aa11d1739adcb331b6242946b971..ee832334341d2db6ef4dffadefb68eac0a3590bb 100644 --- a/src/puppet-mock/mock-message.ts +++ b/src/puppet-mock/mock-message.ts @@ -91,6 +91,21 @@ export class MockMessage extends Message { return loadedContact } + public to(contact: MockContact): this + public to(id: string): this + public to(): MockContact | null // if to is not set, then room must had set + + public to(contact?: MockContact | string): MockContact | null | this { + if (contact) { + return this + } + + const to = MockContact.load('mockid') as MockContact + to.puppet = this.puppet + + return to + } + public room(room: MockRoom): this public room(): MockRoom | null @@ -112,12 +127,24 @@ export class MockMessage extends Message { } public async say( - textOrMessage: string | MockMessage, - replyTo?: MockContact | MockContact[], + textOrMessage: string | MockMessage, + replyTo?: MockContact | MockContact[], ): Promise { log.verbose('MockMessage', 'say(%s, %s)', textOrMessage, replyTo) - const m = new MockMessage() - await this.puppet.send(m) + + const message = new MockMessage() + + message.from(this.puppet.userSelf() as MockContact) + message.to(this.from()) + + const room = this.room() + if (room) { + message.room(room) + } + + // TODO: implement the replyTo + + await this.puppet.send(message) } public type(): MsgType { @@ -146,21 +173,6 @@ export class MockMessage extends Message { return this } - public to(contact: MockContact): this - public to(id: string): this - public to(): MockContact | null // if to is not set, then room must had set - - public to(contact?: MockContact | string): MockContact | null | this { - if (contact) { - return this - } - - const to = MockContact.load('mockid') as MockContact - to.puppet = this.puppet - - return to - } - public async readyStream(): Promise { log.verbose('MockMessage', 'readyStream()') throw new Error('to be mocked') @@ -178,7 +190,7 @@ export class MockMessage extends Message { return 'text/plain' } - public async forward(to: MockRoom|MockContact): Promise { + public async forward(to: MockRoom | MockContact): Promise { /** * 1. Text message */ diff --git a/src/puppet-mock/puppet-mock.ts b/src/puppet-mock/puppet-mock.ts index c6eb6da66aaba9057db77ae2d932614573175505..2c1f0effc654185537d434fc76c9d885443fd046 100644 --- a/src/puppet-mock/puppet-mock.ts +++ b/src/puppet-mock/puppet-mock.ts @@ -91,6 +91,7 @@ export class PuppetMock extends Puppet { await this.state.ready('off') return } + this.state.off('pending') // await some tasks... this.state.off(true) @@ -139,12 +140,6 @@ export class PuppetMock extends Puppet { return await this.userSelf().say(text) } - public async login(user: MockContact): Promise { - log.verbose('PuppetMock', 'login(%s)', user) - this.user = user - this.emit('login', user) - } - public async logout(): Promise { log.verbose('PuppetMock', 'logout()') diff --git a/src/puppet-puppeteer/event.ts b/src/puppet-puppeteer/event.ts index 6dcfee49e8f0762e5762e571a2a5958731b277f6..424325e4496e3e99f758e8adfb34918bf8a1f8a3 100644 --- a/src/puppet-puppeteer/event.ts +++ b/src/puppet-puppeteer/event.ts @@ -59,7 +59,10 @@ function onDing( this.emit('watchdog', { data }) } -async function onScan(this: PuppetPuppeteer, data: ScanData): Promise { +async function onScan( + this: PuppetPuppeteer, + data: ScanData, +): Promise { log.verbose('PuppetPuppeteerEvent', 'onScan({code: %d, url: %s})', data.code, data.url) if (this.state.off()) { @@ -93,7 +96,11 @@ function onLog(data: any): void { log.silly('PuppetPuppeteerEvent', 'onLog(%s)', data) } -async function onLogin(this: PuppetPuppeteer, note: string, ttl = 30): Promise { +async function onLogin( + this: PuppetPuppeteer, + note: string, + ttl = 30, +): Promise { log.verbose('PuppetPuppeteerEvent', 'onLogin(%s, %d)', note, ttl) const TTL_WAIT_MILLISECONDS = 1 * 1000 diff --git a/src/puppet-puppeteer/firer.spec.ts b/src/puppet-puppeteer/firer.spec.ts index 962527055a7b213d5ba13a88a1c113107068be07..c22c1200103420d548ae30473fdc1be998ee5c77 100755 --- a/src/puppet-puppeteer/firer.spec.ts +++ b/src/puppet-puppeteer/firer.spec.ts @@ -27,11 +27,12 @@ import * as test from 'blue-tape' // import * as sinon from 'sinon' // const sinonTest = require('sinon-test')(sinon) -import { Firer } from './firer' +import { PuppetPuppeteer } from './puppet-puppeteer' +import { Firer } from './firer' -test('Firer smoke testing', async t => { - t.true(true, 'should be true') -}) +const mockPuppetPuppeteer = { + userSelf: () => ({}), +} as any as PuppetPuppeteer test('parseFriendConfirm()', async t => { const contentList = [ @@ -55,11 +56,11 @@ test('parseFriendConfirm()', async t => { let result: boolean contentList.forEach(([content]) => { - result = Firer.parseFriendConfirm(content) + result = Firer.parseFriendConfirm.call(mockPuppetPuppeteer, content) t.true(result, 'should be truthy for confirm msg: ' + content) }) - result = Firer.parseFriendConfirm('fsdfsdfasdfasdfadsa') + result = Firer.parseFriendConfirm.call(mockPuppetPuppeteer, 'fsdfsdfasdfasdfadsa') t.false(result, 'should be falsy for other msg') }) @@ -119,14 +120,14 @@ test('parseRoomJoin()', async t => { let result contentList.forEach(([content, inviter, inviteeList]) => { - result = Firer.parseRoomJoin(content) + result = Firer.parseRoomJoin.call(mockPuppetPuppeteer, content) t.ok(result, 'should check room join message right for ' + content) t.deepEqual(result[0], inviteeList, 'should get inviteeList right') t.is(result[1], inviter, 'should get inviter right') }) t.throws(() => { - Firer.parseRoomJoin('fsadfsadfsdfsdfs') + Firer.parseRoomJoin.call(mockPuppetPuppeteer, 'fsadfsadfsdfsdfs') }, Error, 'should throws if message is not expected') }) @@ -154,19 +155,19 @@ test('parseRoomLeave()', async t => { ] contentLeaverList.forEach(([content, leaver]) => { - const resultLeaver = Firer.parseRoomLeave(content)[0] + const resultLeaver = Firer.parseRoomLeave.call(mockPuppetPuppeteer, content)[0] t.ok(resultLeaver, 'should get leaver for leave message: ' + content) t.is(resultLeaver, leaver, 'should get leaver name right') }) contentRemoverList.forEach(([content, remover]) => { - const resultRemover = Firer.parseRoomLeave(content)[1] + const resultRemover = Firer.parseRoomLeave.call(mockPuppetPuppeteer, content)[1] t.ok(resultRemover, 'should get remover for leave message: ' + content) t.is(resultRemover, remover, 'should get leaver name right') }) t.throws(() => { - Firer.parseRoomLeave('fafdsfsdfafa') + Firer.parseRoomLeave.call(mockPuppetPuppeteer, 'fafdsfsdfafa') }, Error, 'should throw if message is not expected') }) @@ -186,14 +187,14 @@ test('parseRoomTopic()', async t => { let result contentList.forEach(([content, changer, topic]) => { - result = Firer.parseRoomTopic(content) + result = Firer.parseRoomTopic.call(mockPuppetPuppeteer, content) t.ok(result, 'should check topic right for content: ' + content) t.is(topic , result[0], 'should get right topic') t.is(changer, result[1], 'should get right changer') }) t.throws(() => { - Firer.parseRoomTopic('fafdsfsdfafa') + Firer.parseRoomTopic.call(mockPuppetPuppeteer, 'fafdsfsdfafa') }, Error, 'should throw if message is not expected') }) diff --git a/src/puppet-puppeteer/firer.ts b/src/puppet-puppeteer/firer.ts index f08e18b702f39a305e867ac9fe837d4849bb9525..89ae56304b5e1f0b3e5a84e980428845f3722d81 100644 --- a/src/puppet-puppeteer/firer.ts +++ b/src/puppet-puppeteer/firer.ts @@ -102,11 +102,14 @@ const regexConfig = { ], } -async function checkFriendRequest(m: PuppeteerMessage) { - if (!m.rawObj) { +async function checkFriendRequest( + this: PuppetPuppeteer, + msg: PuppeteerMessage, +) { + if (!msg.rawObj) { throw new Error('message empty') } - const info = m.rawObj.RecommendInfo + const info = msg.rawObj.RecommendInfo log.verbose('PuppetPuppeteerFirer', 'fireFriendRequest(%s)', info) if (!info) { @@ -114,22 +117,25 @@ async function checkFriendRequest(m: PuppeteerMessage) { } const request = new PuppeteerFriendRequest() - request.puppet = m.puppet + request.puppet = msg.puppet request.receive(info) - await request.contact.ready() - if (!request.contact.isReady()) { + await request.contact().ready() + if (!request.contact().isReady()) { log.warn('PuppetPuppeteerFirer', 'fireFriendConfirm() contact still not ready after `ready()` call') } - this.emit('friend', request.contact, request) + this.emit('friend', request.contact(), request) } /** * try to find FriendRequest Confirmation Message */ -function parseFriendConfirm(content: string): boolean { +function parseFriendConfirm( + this: PuppetPuppeteer, + content: string, +): boolean { const reList = regexConfig.friendConfirm let found = false @@ -141,11 +147,14 @@ function parseFriendConfirm(content: string): boolean { } } -async function checkFriendConfirm(m: PuppeteerMessage) { +async function checkFriendConfirm( + this: PuppetPuppeteer, + m: PuppeteerMessage, +) { const content = m.text() log.silly('PuppetPuppeteerFirer', 'fireFriendConfirm(%s)', content) - if (!parseFriendConfirm(content)) { + if (!parseFriendConfirm.call(this, content)) { return } const request = new PuppeteerFriendRequest() @@ -171,7 +180,10 @@ async function checkFriendConfirm(m: PuppeteerMessage) { * 管理员 invited 小桔建群助手 to the group chat * 管理员 invited 庆次、小桔妹 to the group chat */ -function parseRoomJoin(content: string): [string[], string] { +function parseRoomJoin( + this: PuppetPuppeteer, + content: string, +): [string[], string] { log.verbose('PuppetPuppeteerFirer', 'checkRoomJoin(%s)', content) const reListInvite = regexConfig.roomJoinInvite @@ -194,19 +206,22 @@ function parseRoomJoin(content: string): [string[], string] { return [inviteeList, inviter] // put invitee at first place } -async function checkRoomJoin(m: PuppeteerMessage): Promise { +async function checkRoomJoin( + this: PuppetPuppeteer, + msg: PuppeteerMessage, +): Promise { - const room = m.room() + const room = msg.room() if (!room) { log.warn('PuppetPuppeteerFirer', 'fireRoomJoin() `room` not found') return false } - const text = m.text() + const text = msg.text() let inviteeList: string[], inviter: string try { - [inviteeList, inviter] = parseRoomJoin(text) + [inviteeList, inviter] = parseRoomJoin.call(this, text) } catch (e) { log.silly('PuppetPuppeteerFirer', 'fireRoomJoin() "%s" is not a join message', text) return false // not a room join message @@ -221,8 +236,7 @@ async function checkRoomJoin(m: PuppeteerMessage): Promise { try { if (inviter === 'You' || inviter === '你' || inviter === 'you') { - inviterContact = PuppeteerContact.load(this.userId) as PuppeteerContact - inviterContact.puppet = m.puppet + inviterContact = this.userSelf() } const max = 20 @@ -321,7 +335,10 @@ async function checkRoomJoin(m: PuppeteerMessage): Promise { } -function parseRoomLeave(content: string): [string, string] { +function parseRoomLeave( + this: PuppetPuppeteer, + content: string, +): [string, string] { const reListByBot = regexConfig.roomLeaveByBot const reListByOther = regexConfig.roomLeaveByOther let foundByBot: string[]|null = [] @@ -331,7 +348,7 @@ function parseRoomLeave(content: string): [string, string] { if ((!foundByBot || !foundByBot.length) && (!foundByOther || !foundByOther.length)) { throw new Error('checkRoomLeave() no matched re for ' + content) } - const [leaver, remover] = foundByBot ? [ foundByBot[1], this.userId ] : [ this.userId, foundByOther[1] ] + const [leaver, remover] = foundByBot ? [ foundByBot[1], this.userSelf().id ] : [ this.userSelf().id, foundByOther[1] ] return [leaver, remover] } @@ -346,7 +363,7 @@ async function checkRoomLeave( let leaver: string, remover: string try { - [leaver, remover] = parseRoomLeave(m.text()) + [leaver, remover] = parseRoomLeave.call(this, m.text()) } catch (e) { return false } @@ -363,8 +380,7 @@ async function checkRoomLeave( */ let leaverContact: PuppeteerContact | null, removerContact: PuppeteerContact | null if (leaver === this.userSelf().id) { - leaverContact = PuppeteerContact.load(this.userSelf().id) - leaverContact.puppet = m.puppet + leaverContact = this.userSelf() // not sure which is better // removerContact = room.member({contactAlias: remover}) || room.member({name: remover}) @@ -406,7 +422,10 @@ async function checkRoomLeave( return true } -function parseRoomTopic(content: string): [string, string] { +function parseRoomTopic( + this: PuppetPuppeteer, + content: string, +): [string, string] { const reList = regexConfig.roomTopic let found: string[]|null = [] @@ -418,10 +437,12 @@ function parseRoomTopic(content: string): [string, string] { return [topic, changer] } -async function checkRoomTopic(m: PuppeteerMessage): Promise { +async function checkRoomTopic( + this: PuppetPuppeteer, + m: PuppeteerMessage): Promise { let topic, changer try { - [topic, changer] = parseRoomTopic(m.text()) + [topic, changer] = parseRoomTopic.call(this, m.text()) } catch (e) { // not found return false } @@ -436,7 +457,7 @@ async function checkRoomTopic(m: PuppeteerMessage): Promise { let changerContact: PuppeteerContact | null if (/^You$/.test(changer) || /^你$/.test(changer)) { - changerContact = PuppeteerContact.load(this.userId) as PuppeteerContact + changerContact = this.userSelf() changerContact.puppet = m.puppet } else { changerContact = room.member(changer) diff --git a/src/puppet-puppeteer/puppet-puppeteer.spec.ts b/src/puppet-puppeteer/puppet-puppeteer.spec.ts index 29586de4ee73fe699c0df231042718cdca215c5c..9ad6df90d7767a8c5101b21bd6afe0d2b92bce61 100755 --- a/src/puppet-puppeteer/puppet-puppeteer.spec.ts +++ b/src/puppet-puppeteer/puppet-puppeteer.spec.ts @@ -58,15 +58,17 @@ test('Puppet smoke testing', async t => { test('login/logout events', sinonTest(async function (t: test.Test) { - sinon.stub(Contact, 'findAll') + const sandbox = sinon.sandbox.create() + + sandbox.stub(Contact, 'findAll') .onFirstCall().resolves([]) .onSecondCall().resolves([1]) .resolves([1, 2]) - sinon.stub(Event, 'onScan') // block the scan event to prevent reset logined user + sandbox.stub(Event, 'onScan') // block the scan event to prevent reset logined user - sinon.stub(Bridge.prototype, 'getUserName').resolves('mockedUserName') - sinon.stub(PuppetPuppeteer.prototype, 'getContact') .resolves({ + sandbox.stub(Bridge.prototype, 'getUserName').resolves('mockedUserName') + sandbox.stub(PuppetPuppeteer.prototype, 'getContact') .resolves({ NickName: 'mockedNickName', UserName: 'mockedUserName', }) @@ -110,6 +112,7 @@ test('login/logout events', sinonTest(async function (t: test.Test) { } catch (e) { t.fail(e) } finally { + sandbox.restore() t.end() } })) diff --git a/src/puppet-puppeteer/puppeteer-contact.spec.ts b/src/puppet-puppeteer/puppeteer-contact.spec.ts index db4e5825aca135043ea74ba2a97a37c789d8b584..786bef4d230fe0bcbaac56773a00c2b6f3a67888 100755 --- a/src/puppet-puppeteer/puppeteer-contact.spec.ts +++ b/src/puppet-puppeteer/puppeteer-contact.spec.ts @@ -24,18 +24,17 @@ import * as sinon from 'sinon' import cloneClass from 'clone-class' +import Profile from '../profile' +import Wechaty from '../wechaty' // `Wechaty` need to be imported before `Puppet` + import PuppetPuppeteer from './puppet-puppeteer' import PuppeteerContact from './puppeteer-contact' -import Profile from '../profile' -import Wechaty from '../wechaty' - test('Contact smoke testing', async t => { // tslint:disable-next-line:variable-name const MyContact = cloneClass(PuppeteerContact) - - MyContact.puppet = new PuppetPuppeteer({ + const puppet = new PuppetPuppeteer({ profile: new Profile(), wechaty: new Wechaty(), }) @@ -45,19 +44,24 @@ test('Contact smoke testing', async t => { const NickName = 'NickNameTest' const RemarkName = 'AliasTest' - sinon.stub((MyContact.puppet as PuppetPuppeteer), 'getContact', function(id: string) { + const sandbox = sinon.sandbox.create() + + sandbox.stub(puppet, 'getContact') + .callsFake(function(id: string) { return new Promise((resolve, reject) => { if (id !== UserName) return resolve({}) setTimeout(() => { return resolve({ - UserName: UserName, - NickName: NickName, + UserName: UserName, + NickName: NickName, RemarkName: RemarkName, }) - }, 200) + }, 10) }) }) + MyContact.puppet = puppet + const c = new MyContact(UserName) t.is(c.id, UserName, 'id/UserName right') @@ -66,8 +70,7 @@ test('Contact smoke testing', async t => { t.is(r.name(), NickName, 'NickName set') t.is(r.alias(), RemarkName, 'should get the right alias from Contact') - const s = r.toString() - t.is(typeof s, 'string', 'toString()') + sandbox.restore() // const contact1 = await Contact.find({name: 'NickNameTest'}) // t.is(contact1.id, UserName, 'should find contact by name') diff --git a/src/puppet-puppeteer/puppeteer-friend-request.spec.ts b/src/puppet-puppeteer/puppeteer-friend-request.spec.ts index 3dc7a1d9c8983ab87d378f493fdae2c9528958bb..6bc6025086d3cfcc476e5abe548d0edc2d6be5c9 100755 --- a/src/puppet-puppeteer/puppeteer-friend-request.spec.ts +++ b/src/puppet-puppeteer/puppeteer-friend-request.spec.ts @@ -29,10 +29,11 @@ import Wechaty from '../wechaty' import { Puppet, -} from '../puppet/' + FriendRequestType, +} from '../puppet/' import { PuppetMock, -} from '../puppet-mock/' +} from '../puppet-mock/' import PuppeteerContact from './puppeteer-contact' import PuppeteerMessage from './puppeteer-message' @@ -55,9 +56,9 @@ test('PuppetPuppeteerFriendRequest.receive smoke testing', async t => { fr.receive(rawObj.RecommendInfo) t.true(typeof fr.payload === 'object', 'should has info object') - t.is(fr.hello, '我是群聊"Wechaty"的李卓桓.PreAngel', 'should has right request message') - t.true(fr.contact instanceof PuppeteerContact, 'should have a Contact instance') - t.is(fr.type as any, 'receive', 'should be receive type') + t.is(fr.hello(), '我是群聊"Wechaty"的李卓桓.PreAngel', 'should has right request message') + t.true(fr.contact() instanceof PuppeteerContact, 'should have a Contact instance') + t.is(fr.type(), FriendRequestType.RECEIVE, 'should be receive type') }) test('PuppetPuppeteerFriendRequest.confirm smoke testing', async t => { @@ -89,6 +90,6 @@ test('PuppetPuppeteerFriendRequest.confirm smoke testing', async t => { const contact = m.from() fr.confirm(contact || new MyContact('xx')) - t.true(fr.contact instanceof PuppeteerContact, 'should have a Contact instance') - t.is(fr.type as any, 'confirm', 'should be confirm type') + t.true(fr.contact() instanceof PuppeteerContact, 'should have a Contact instance') + t.is(fr.type(), FriendRequestType.CONFIRM, 'should be confirm type') }) diff --git a/src/puppet-puppeteer/puppeteer-friend-request.ts b/src/puppet-puppeteer/puppeteer-friend-request.ts index faec269d21c4b80fa52b2b6ef1014f32bc0120aa..a77e85f015abcb5f8ce5f47c0fbb008da8aee098 100644 --- a/src/puppet-puppeteer/puppeteer-friend-request.ts +++ b/src/puppet-puppeteer/puppeteer-friend-request.ts @@ -35,6 +35,7 @@ import { } from '../config' import { FriendRequest, + FriendRequestType, } from '../puppet/' import { @@ -50,6 +51,9 @@ export class PuppeteerFriendRequest extends FriendRequest { public payload: RecommendPayload private ticket: string + private _contact: PuppeteerContact + private _hello: string + private _type: FriendRequestType constructor() { log.verbose('PuppeteerFriendRequest', 'constructor()') @@ -67,8 +71,8 @@ export class PuppeteerFriendRequest extends FriendRequest { const contact = PuppeteerContact.load(payload.UserName) contact.puppet = this.puppet - this.contact = contact - this.hello = payload.Content + this._contact = contact + this._hello = payload.Content this.ticket = payload.Ticket // ??? this.nick = info.NickName @@ -76,7 +80,7 @@ export class PuppeteerFriendRequest extends FriendRequest { throw new Error('ticket not found') } - this.type = 'receive' + this._type = FriendRequestType.RECEIVE return } @@ -87,8 +91,8 @@ export class PuppeteerFriendRequest extends FriendRequest { if (!contact) { throw new Error('contact not found') } - this.contact = contact - this.type = 'confirm' + this._contact = contact + this._type = FriendRequestType.CONFIRM } /** @@ -107,11 +111,11 @@ export class PuppeteerFriendRequest extends FriendRequest { if (!contact) { throw new Error('contact not found') } - this.contact = contact - this.type = 'send' + this._contact = contact + this._type = FriendRequestType.SEND if (hello) { - this.hello = hello + this._hello = hello } await this.puppet.friendRequestSend(contact, hello) @@ -125,11 +129,11 @@ export class PuppeteerFriendRequest extends FriendRequest { public async accept(): Promise { log.verbose('FriendRequest', 'accept() %s', this.contact) - if (this.type !== 'receive') { + if (this._type !== FriendRequestType.RECEIVE) { throw new Error('request is not a `receive` type. it is a ' + this.type + ' type') } - await this.puppet.friendRequestAccept(this.contact, this.ticket) + await this.puppet.friendRequestAccept(this.contact(), this.ticket) const max = 20 const backoff = 300 @@ -143,10 +147,10 @@ export class PuppeteerFriendRequest extends FriendRequest { await retryPromise({ max: max, backoff: backoff }, async (attempt: number) => { log.silly('PuppeteerFriendRequest', 'accept() retryPromise() attempt %d with timeout %d', attempt, timeout) - await this.contact.ready() + await this.contact().ready() - if ((this.contact as PuppeteerContact).isReady()) { - log.verbose('PuppeteerFriendRequest', 'accept() with contact %s ready()', this.contact.name()) + if (this.contact().isReady()) { + log.verbose('PuppeteerFriendRequest', 'accept() with contact %s ready()', this.contact().name()) return } throw new Error('FriendRequest.accept() content.ready() not ready') @@ -157,6 +161,22 @@ export class PuppeteerFriendRequest extends FriendRequest { } + public hello(): string { + return this._hello + } + + public contact(): PuppeteerContact { + return this._contact + } + + public async reject(): Promise { + log.warn('PuppeteerFriendRequest', 'reject() not necessary, NOP.') + return + } + + public type(): FriendRequestType { + return this._type + } } export default PuppeteerFriendRequest diff --git a/src/puppet-puppeteer/puppeteer-message.spec.ts b/src/puppet-puppeteer/puppeteer-message.spec.ts index 3e24461d42157a05355833ab4f9d4c59d832658c..6f13686f10af39de9f4087344bf0d691db4530f3 100755 --- a/src/puppet-puppeteer/puppeteer-message.spec.ts +++ b/src/puppet-puppeteer/puppeteer-message.spec.ts @@ -214,9 +214,12 @@ test('mentioned()', async t => { // puppet1 = { getContact: mockContactGetter } // config.puppetInstance(puppet1) // } - MyContact.puppet = MyRoom.puppet = MyMessage.puppet = { + const puppet = MyRoom.puppet = MyMessage.puppet = { getContact: mockContactGetter, - } as any + } as PuppetPuppeteer + + MyContact.puppet = puppet + const msg11 = new MyMessage(rawObj11) const room11 = msg11.room() if (room11) { diff --git a/src/puppet-puppeteer/puppeteer-message.ts b/src/puppet-puppeteer/puppeteer-message.ts index cc4a90de530e1e72972c074e9a6b128495df54c1..dfe38274d3826ae02d5fb9887f19120bcb3f3ae6 100644 --- a/src/puppet-puppeteer/puppeteer-message.ts +++ b/src/puppet-puppeteer/puppeteer-message.ts @@ -75,7 +75,7 @@ export class PuppeteerMessage extends Message { * @private */ constructor( - fileOrObj?: string | MsgRawPayload, + fileOrPayload?: string | MsgRawPayload, ) { super() log.silly('PuppeteerMessage', 'constructor()') @@ -83,14 +83,14 @@ export class PuppeteerMessage extends Message { this.obj = {} as MsgPayload // this.rawObj = {} as MsgRawObj - if (!fileOrObj) { + if (!fileOrPayload) { return } - if (typeof fileOrObj === 'string') { - this.parsedPath = path.parse(fileOrObj) - } else if (typeof fileOrObj === 'object') { - this.rawObj = fileOrObj + if (typeof fileOrPayload === 'string') { + this.parsedPath = path.parse(fileOrPayload) + } else if (typeof fileOrPayload === 'object') { + this.rawObj = fileOrPayload this.obj = this.parse(this.rawObj) this.id = this.obj.id } else { @@ -246,7 +246,7 @@ export class PuppeteerMessage extends Message { } if (this.obj.room) { - const r = PuppeteerRoom.load(this.obj.room) as PuppeteerRoom + const r = PuppeteerRoom.load(this.obj.room) r.puppet = this.puppet return r } @@ -506,7 +506,7 @@ export class PuppeteerMessage extends Message { ) if (contactList.length === 0) { - log.warn(`Message`, `message.mentioned() can not found member using room.member() from mentionList, metion string: ${JSON.stringify(mentionList)}`) + log.warn('PuppeteerMessage', `message.mentioned() can not found member using room.member() from mentionList, metion string: ${JSON.stringify(mentionList)}`) } return contactList } diff --git a/src/puppet-puppeteer/puppeteer-room.ts b/src/puppet-puppeteer/puppeteer-room.ts index e2de7d23718118d450328de079ea29a2e29fb14c..18d8f374edcd924e32e42ffebb6b45e595e44d6a 100644 --- a/src/puppet-puppeteer/puppeteer-room.ts +++ b/src/puppet-puppeteer/puppeteer-room.ts @@ -101,7 +101,7 @@ export class PuppeteerRoom extends Room { * @private */ public async ready(): Promise { - log.silly('PuppeteerRoom', 'ready(%s)') + log.silly('PuppeteerRoom', 'ready()') if (!this.id) { const e = new Error('ready() on a un-inited Room') log.warn('PuppeteerRoom', e.message) @@ -614,7 +614,7 @@ export class PuppeteerRoom extends Room { } const filterMap = this.obj[filterMapName] as Map - const idList = Object.keys(filterMap) + const idList = Array.from(filterMap.keys()) .filter(id => filterMap.get(id) === filterValue) log.silly('PuppeteerRoom', 'memberAll() check %s from %s: %s', filterValue, filterKey, JSON.stringify(filterMap)) diff --git a/src/puppet/friend-request.ts b/src/puppet/friend-request.ts index 2330f4162b15becb44db2e8bbb4631fbe64154d7..fd347458e514dfcaa4162d8b99d55e1e3b4bb74d 100644 --- a/src/puppet/friend-request.ts +++ b/src/puppet/friend-request.ts @@ -22,6 +22,12 @@ import PuppetAccessory from '../puppet-accessory' import Contact from './contact' +export enum FriendRequestType { + SEND, + RECEIVE, + CONFIRM, +} + /** * Send, receive friend request, and friend confirmation events. * @@ -33,12 +39,14 @@ import Contact from './contact' */ export abstract class FriendRequest extends PuppetAccessory { - public contact: Contact - public hello: string - public type: 'send' | 'receive' | 'confirm' - public abstract send(contact: Contact, hello: string): Promise + public abstract accept(): Promise + public abstract reject(): Promise + + public abstract contact() : Contact + public abstract hello() : string + public abstract type() : FriendRequestType } diff --git a/src/puppet/index.ts b/src/puppet/index.ts index 626da2a0b86329667f8ce98c329f987c4b2ccb83..b907943966da22f1e068a3f982ad1aea0e638f3c 100644 --- a/src/puppet/index.ts +++ b/src/puppet/index.ts @@ -5,18 +5,20 @@ export { } from './contact' export { FriendRequest, + FriendRequestType, } from './friend-request' export { Message, } from './message' +export { + Room, + RoomMemberQueryFilter, + RoomQueryFilter, +} from './room' + export { Puppet, PuppetEventName, PuppetOptions, ScanData, } from './puppet' -export { - Room, - RoomMemberQueryFilter, - RoomQueryFilter, -} from './room' diff --git a/src/puppet/message.ts b/src/puppet/message.ts index 18019bbcc8cd8e5b784c289d604091b634783359..435dd703cdc86431c5b1b9dd8202b7cf2616d09c 100644 --- a/src/puppet/message.ts +++ b/src/puppet/message.ts @@ -78,11 +78,11 @@ export abstract class Message extends PuppetAccessory implements Sayable { * @private */ constructor( - readonly fileOrObj?: string | Object, + readonly fileOrPayload?: string | Object, ) { super() - log.silly('Message', 'constructor(%s) for class %s', - fileOrObj || '', + log.silly('Message', 'constructor(%s) for child class %s', + fileOrPayload || '', this.constructor.name, ) } @@ -197,7 +197,7 @@ export abstract class Message extends PuppetAccessory implements Sayable { * if (/^ding$/i.test(m.text())) { * await m.say('hello world') * console.log('Bot REPLY: hello world') - * await m.say(new bot.Message.create(__dirname + '/wechaty.png')) + * await m.say(new bot.Message(__dirname + '/wechaty.png')) * console.log('Bot REPLY: Image') * } * }) diff --git a/src/puppet/puppet.ts b/src/puppet/puppet.ts index afaa05be73cfe029078be463437a8c70a1fe9ec2..8d9b74bf65aca20f2f5ab08c58a684b375d5b0fe 100644 --- a/src/puppet/puppet.ts +++ b/src/puppet/puppet.ts @@ -158,7 +158,7 @@ export abstract class Puppet extends EventEmitter implements Sayable { public emit(event: 'room-topic', room: Room, topic: string, oldTopic: string, changer: Contact) : boolean public emit(event: 'scan', url: string, code: number) : boolean public emit(event: 'watchdog', food: WatchdogFood) : boolean - public emit(event: never, ...args: never[]) : never + public emit(event: never, ...args: never[]): never public emit( event: PuppetEventName, @@ -216,7 +216,7 @@ export abstract class Puppet extends EventEmitter implements Sayable { * Login / Logout */ public abstract logonoff() : boolean - public abstract login(user: Contact): Promise + // public abstract login(user: Contact): Promise public abstract logout() : Promise /** diff --git a/src/puppet/room.ts b/src/puppet/room.ts index ed7a434cf7ee6ddb78da32f7a67a6d3b70efe005..370417a863ce6d70c9655a70f223a5e0996307bf 100644 --- a/src/puppet/room.ts +++ b/src/puppet/room.ts @@ -128,6 +128,7 @@ export abstract class Room extends PuppetAccessory implements Sayable { * @param {RoomQueryFilter} query * @returns {Promise} If can find the room, return Room, or return null */ + public static async find( this : T, query : RoomQueryFilter, diff --git a/tsconfig.json b/tsconfig.json index 120d12044f49f130098230dd885743e34f3468af..f4470a36af713ed294a0182e3c25c333d30f55a7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,12 +29,12 @@ , "dist/" ] , "include": [ - "bin/*.ts" - , "scripts/**/*.ts" + "app/**/*.ts" + , "bin/*.ts" + , "bot/**/*.ts" , "examples/**/*.ts" + , "scripts/**/*.ts" , "src/**/*.ts" , "tests/**/*.spec.ts" - , "bot/**/*.ts" - , "app/**/*.ts" ] }