提交 382cedff 编写于 作者: Y Yuan Gao 提交者: Huan (李卓桓)

revise room.say() mention function (#1729)

* revise room.say() mention function

* add test for new mention func

* more change according to review

* make sub tests independent from each other

* bump package version

* add tagged template code example for room.say()
上级 e6500292
{
"name": "wechaty",
"version": "0.23.40",
"version": "0.25.1",
"description": "Wechaty is a Bot SDK for Wechat Personal Account",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
......
......@@ -23,10 +23,14 @@
import test from 'blue-tape'
import sinon from 'sinon'
import { RoomPayload } from 'wechaty-puppet'
import {
ContactPayload,
RoomMemberPayload,
RoomPayload
} from 'wechaty-puppet'
import { PuppetMock } from 'wechaty-puppet-mock'
import { Wechaty } from '../wechaty'
import { Wechaty } from '../wechaty'
test('findAll()', async t => {
const EXPECTED_ROOM_ID = 'test-id'
......@@ -54,3 +58,88 @@ test('findAll()', async t => {
await wechaty.stop()
})
test('say()', async _ => {
const sandbox = sinon.createSandbox()
const callback = sinon.spy()
const puppet = new PuppetMock()
const wechaty = new Wechaty({ puppet })
await wechaty.start()
const EXPECTED_ROOM_ID = 'roomId'
const EXPECTED_ROOM_TOPIC = 'test-topic'
const EXPECTED_CONTACT_1_ID = 'contact1'
const EXPECTED_CONTACT_1_ALIAS = 'little1'
const EXPECTED_CONTACT_2_ID = 'contact2'
const EXPECTED_CONTACT_2_ALIAS = 'big2'
const CONTACT_MAP: { [contactId: string]: string } = {}
CONTACT_MAP[EXPECTED_CONTACT_1_ID] = EXPECTED_CONTACT_1_ALIAS
CONTACT_MAP[EXPECTED_CONTACT_2_ID] = EXPECTED_CONTACT_2_ALIAS
sandbox.stub(puppet, 'roomMemberPayload').callsFake(async (_, contactId) => {
await new Promise(r => setImmediate(r))
return {
id: contactId,
roomAlias: CONTACT_MAP[contactId],
} as RoomMemberPayload
})
sandbox.stub(puppet, 'roomPayload').callsFake(async () => {
await new Promise(r => setImmediate(r))
return {
topic: EXPECTED_ROOM_TOPIC,
} as RoomPayload
})
sandbox.stub(puppet, 'contactPayload').callsFake(async (contactId) => {
await new Promise(r => setImmediate(r))
return {
id: contactId,
} as ContactPayload
})
// sandbox.spy(puppet, 'messageSendText')
sandbox.stub(puppet, 'messageSendText').callsFake(callback)
const room = wechaty.Room.load(EXPECTED_ROOM_ID)
const contact1 = wechaty.Contact.load(EXPECTED_CONTACT_1_ID)
const contact2 = wechaty.Contact.load(EXPECTED_CONTACT_2_ID)
await contact1.sync()
await contact2.sync()
await room.sync()
test('say with Tagged Template', async t => {
callback.resetHistory()
await room.say`To be ${contact1} or not to be ${contact2}`
t.deepEqual(callback.getCall(0).args, [
{ contactId: EXPECTED_CONTACT_1_ID, roomId: EXPECTED_ROOM_ID },
'To be @little1 or not to be @big2',
[EXPECTED_CONTACT_1_ID, EXPECTED_CONTACT_2_ID],
], 'Tagged Template say should be matched')
})
test('say with regular mention contact', async t => {
callback.resetHistory()
await room.say('Yo', contact1)
t.deepEqual(callback.getCall(0).args, [
{ contactId: EXPECTED_CONTACT_1_ID, roomId: EXPECTED_ROOM_ID },
'@little1 Yo',
[EXPECTED_CONTACT_1_ID],
], 'Single mention should work with old ways')
})
test('say with multiple mention contact', async t => {
callback.resetHistory()
await room.say('hey buddies, let\'s party', contact1, contact2)
t.deepEqual(callback.getCall(0).args, [
{ contactId: EXPECTED_CONTACT_1_ID, roomId: EXPECTED_ROOM_ID },
'@little1 @big2 hey buddies, let\'s party',
[EXPECTED_CONTACT_1_ID, EXPECTED_CONTACT_2_ID],
], 'Multiple mention should work with new way')
})
await wechaty.stop()
})
......@@ -382,10 +382,10 @@ export class Room extends Accessory implements Sayable {
}
public say (text: string) : Promise<void>
public say (text: string, mention: Contact) : Promise<void>
public say (text: string, mention: Contact[]) : Promise<void>
public say (text: string, ...mentionList: Contact[]) : Promise<void>
public say (file: FileBox) : Promise<void>
public say (url: UrlLink) : Promise<void>
public say (textList: TemplateStringsArray, ...mentionList: Contact[]): Promise<void>
public say (...args: never[]): never
......@@ -423,65 +423,121 @@ export class Room extends Accessory implements Sayable {
* // 4. Send text inside room and mention @mention contact
* const contact = await bot.Contact.find({name: 'lijiarui'}) // change 'lijiarui' to any of the room member
* await room.say('Hello world!', contact)
*
* // 5. Send text inside room and mention someone with Tagged Template
* const contact2 = await bot.Contact.find({name: 'zixia'}) // change 'zixia' to any of the room member
* await room.say`Hello ${contact}, here is the world ${contact2}`
*/
public async say (
textOrContactOrFileOrUrl : string | Contact | FileBox | UrlLink,
mention? : Contact | Contact[],
textOrListOrContactOrFileOrUrl : string | Contact | FileBox | UrlLink | TemplateStringsArray,
...mentionList : Contact[]
): Promise<void> {
let replyToList: Contact[] = []
replyToList = replyToList.concat(mention || [])
const mentionAliasList = await Promise.all(
replyToList.map(
async c => await this.alias(c) || c.name()
)
)
log.verbose('Room', 'say(%s, %s)',
textOrContactOrFileOrUrl,
mentionAliasList.join(', '),
textOrListOrContactOrFileOrUrl,
mentionList.join(', '),
)
let text: string
if (typeof textOrContactOrFileOrUrl === 'string') {
if (typeof textOrListOrContactOrFileOrUrl === 'string') {
if (mentionAliasList.length > 0) {
// const AT_SEPRATOR = String.fromCharCode(8197)
const AT_SEPRATOR = FOUR_PER_EM_SPACE
const mentionList = mentionAliasList.map(roomAlias => '@' + roomAlias).join(AT_SEPRATOR)
if (mentionList.length > 0) {
const AT_SEPARATOR = FOUR_PER_EM_SPACE
const mentionAlias = await Promise.all(mentionList.map(async contact =>
'@' + (await this.alias(contact) || contact.name())
))
const mentionText = mentionAlias.join(AT_SEPARATOR)
text = mentionList + ' ' + textOrContactOrFileOrUrl
text = mentionText + ' ' + textOrListOrContactOrFileOrUrl
} else {
text = textOrContactOrFileOrUrl
text = textOrListOrContactOrFileOrUrl
}
const receiver = {
contactId : replyToList.length && replyToList[0].id || undefined,
contactId : mentionList.length && mentionList[0].id || undefined,
roomId : this.id,
}
await this.puppet.messageSendText(
receiver,
text,
replyToList.map(c => c.id),
mentionList.map(c => c.id),
)
} else if (textOrContactOrFileOrUrl instanceof FileBox) {
} else if (textOrListOrContactOrFileOrUrl instanceof FileBox) {
/**
* 2. File Message
*/
await this.puppet.messageSendFile({
roomId: this.id,
}, textOrContactOrFileOrUrl)
} else if (textOrContactOrFileOrUrl instanceof Contact) {
}, textOrListOrContactOrFileOrUrl)
} else if (textOrListOrContactOrFileOrUrl instanceof Contact) {
/**
* 3. Contact Card
*/
await this.puppet.messageSendContact({
roomId: this.id,
}, textOrContactOrFileOrUrl.id)
} else if (textOrContactOrFileOrUrl instanceof UrlLink) {
}, textOrListOrContactOrFileOrUrl.id)
} else if (textOrListOrContactOrFileOrUrl instanceof UrlLink) {
/**
* 4. Link Message
*/
await this.puppet.messageSendUrl({
contactId : this.id
}, textOrContactOrFileOrUrl.payload)
}, textOrListOrContactOrFileOrUrl.payload)
} else if (textOrListOrContactOrFileOrUrl instanceof Array) {
await this.sayTemplateStringsArray(
textOrListOrContactOrFileOrUrl,
...mentionList,
)
} else {
throw new Error('arg unsupported: ' + textOrContactOrFileOrUrl)
throw new Error('arg unsupported: ' + textOrListOrContactOrFileOrUrl)
}
}
private async sayTemplateStringsArray (
textList: TemplateStringsArray,
...mentionList: Contact[]
) {
const receiver = {
contactId : mentionList.length && mentionList[0].id || undefined,
roomId : this.id,
}
if (mentionList.length === 0) {
/**
* No mention in the string
*/
await this.puppet.messageSendText(
receiver,
textList[0],
)
} else if (textList.length === 1) {
/**
* Constructed mention string, skip inserting @ signs
*/
await this.puppet.messageSendText(
receiver,
textList[0],
mentionList.map(c => c.id),
)
} else {
/**
* Mention in the string
*/
const strLength = textList.length
const mentionLength = mentionList.length
if (strLength - mentionLength !== 1) {
throw new Error(`Can not say message, invalid Tagged Template.`)
}
let constructedString = ''
let i = 0
for (; i < mentionLength; i++) {
constructedString += textList[i] + '@' + (await this.alias(mentionList[i]) || mentionList[i].name())
}
constructedString += textList[i]
await this.puppet.messageSendText(
receiver,
constructedString,
mentionList.map(c => c.id),
)
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册