Commit ed7d3aec authored by nanahira's avatar nanahira

update prompt resolve

parent 944abead
......@@ -28,8 +28,8 @@ app.plugin(ExtrasInDev);
// Target plugin
app.plugin(TargetPlugin, {});
app.command('dress').action((a) => {
a.session.send('你好,我是小美女,我可以帮你穿衣服哦!');
app.command('dress').action(async (a) => {
await a.session.send('你好,我是小美女,我可以帮你穿衣服哦!');
return `${a.session.username} 让梦梦女装!`;
});
......@@ -37,4 +37,14 @@ app.command('boom').action((a) => {
throw new Error('Boom!');
});
app.command('ask').action(async (a) => {
await a.session.send(
'你好,我是小美女,请问能告诉我你今天穿了什么颜色的衣服吗?',
);
const colorOfShirt = await a.session.prompt(100000);
await a.session.send(`那你能告诉我你今天穿了什么颜色的裙子吗?`);
const colorOfSkirt = await a.session.prompt(100000);
return `原来 ${a.session.userId} 今天穿的是 ${colorOfShirt} 的衣服,和 ${colorOfSkirt} 的裙子!`;
});
app.start();
import { Random, Session } from 'koishi';
import { getSessionId, Random, Session } from 'koishi';
import { ApiBot } from './index';
import { Prompt } from './def/prompt';
export class ApiSession extends Session {
storedMessages: string[] = [];
private midResolver: () => void;
bot: ApiBot;
currentPromise: Promise<void>;
midPromise = new Promise<void>((resolve) => {
this.midResolver = resolve;
});
async waitForPattern() {
await Promise.race([
this.currentPromise.then(() => this.midResolve(true)),
this.midPromise,
]);
return this.gatherResponseMessages();
}
midResolve(finish = false) {
if (!this.midResolver) {
return;
}
this.midResolver();
if (finish) {
delete this.midResolver;
} else {
this.midPromise = new Promise<void>((resolve) => {
this.midResolver = resolve;
});
}
}
async send(content: string) {
this.storedMessages.push(content);
return [Random.id()];
}
getReponseMessages() {
return this.storedMessages.filter((m) => !!m);
gatherResponseMessages() {
const result = this.storedMessages.filter((m) => !!m);
this.storedMessages = [];
return result;
}
prompt(timeout = this.app.options.delay.prompt) {
const identifier = getSessionId(this);
const prom = new Promise<string>((resolve) => {
const prompt: Prompt = {
resolver: resolve,
timeout: setTimeout(() => {
this.bot.resolvePrompt(identifier, undefined);
}, timeout),
session: this,
};
this.bot.prompts.set(identifier, prompt);
});
this.midResolve();
return prom;
}
}
import { ApiSession } from '../api-session';
export interface Prompt {
resolver: (value: string) => void;
timeout: NodeJS.Timeout;
session: ApiSession;
}
......@@ -6,17 +6,45 @@ import {
Inject,
InjectConfig,
} from 'koishi-thirdeye';
import { Adapter, Bot, Context, Random, Router } from 'koishi';
import { Adapter, Bot, Context, getSessionId, Random, Router } from 'koishi';
import { ApiSession } from './api-session';
import { Prompt } from './def/prompt';
export * from './config';
export class ApiBot extends Bot {
username = 'koishi';
selfId = 'koishi';
hidden = true;
prompts = new Map<string, Prompt>();
resolvePrompt(key: string, value: string) {
const prompt = this.prompts.get(key);
if (prompt) {
prompt.resolver(value);
clearTimeout(prompt.timeout);
this.prompts.delete(key);
return prompt;
}
return;
}
constructor(public adapter: ApiAdapter, config: Bot.BaseConfig) {
super(adapter, config);
this.adapter.ctx
.any()
.platform('api')
.self(this.selfId)
.middleware(async (session: ApiSession, next) => {
const identifier = getSessionId(session);
const prompt = this.resolvePrompt(identifier, session.content);
if (!prompt) {
return next();
}
session.storedMessages = session.storedMessages.concat(
await prompt.session.waitForPattern(),
);
return;
}, true);
}
async sendMessage(channelId: string, content: string) {
......@@ -60,11 +88,10 @@ export default class ApiAdapter extends Adapter implements LifecycleEvents {
this.router.post(this.pluginConfig.path, async (ctx) => {
if (this.pluginConfig.token) {
const tokenFromRequest = ctx.request.headers[
'authorization'
]?.startsWith('Bearer ')
? ctx.request.headers['authorization'].slice(7)
: '';
const header = ctx.request.headers['authorization'];
const tokenFromRequest = header?.startsWith('Bearer ')
? header.slice(7)
: header;
if (tokenFromRequest !== this.pluginConfig.token) {
ctx.status = 401;
ctx.body = { error: 'Invalid token' };
......@@ -99,9 +126,10 @@ export default class ApiAdapter extends Adapter implements LifecycleEvents {
username: body.username || userId,
},
});
await this.ctx.app.parallel('message', session);
const currentPromise = this.ctx.app.parallel('message', session);
session.currentPromise = currentPromise;
ctx.status = 200;
ctx.body = { messages: session.getReponseMessages() };
ctx.body = { messages: await session.waitForPattern() };
});
}
......
......@@ -72,6 +72,76 @@ describe('Test of plugin.', () => {
});
});
it('should correctly resolve prompt', async () => {
app.command('ask').action(async (a) => {
await a.session.send(
'你好,我是小美女,请问能告诉我你今天穿了什么颜色的衣服吗?',
);
const colorOfShirt = await a.session.prompt(100000);
await a.session.send(`那你能告诉我你今天穿了什么颜色的裙子吗?`);
const colorOfSkirt = await a.session.prompt(100000);
return `原来 ${a.session.userId} 今天穿的是 ${colorOfShirt} 的衣服,和 ${colorOfSkirt} 的裙子!`;
});
await request(app._httpServer)
.post('/api')
.send({ userId: '111111', content: 'ask' })
.set('Authorization', 'dress')
.expect(200)
.expect({
messages: [
'你好,我是小美女,请问能告诉我你今天穿了什么颜色的衣服吗?',
],
})
.then();
await request(app._httpServer)
.post('/api')
.send({ userId: '111112', content: 'ask' })
.set('Authorization', 'dress')
.expect(200)
.expect({
messages: [
'你好,我是小美女,请问能告诉我你今天穿了什么颜色的衣服吗?',
],
})
.then();
await request(app._httpServer)
.post('/api')
.send({ userId: '111111', content: '红色' })
.set('Authorization', 'dress')
.expect(200)
.expect({
messages: ['那你能告诉我你今天穿了什么颜色的裙子吗?'],
})
.then();
await request(app._httpServer)
.post('/api')
.send({ userId: '111112', content: '蓝色' })
.set('Authorization', 'dress')
.expect(200)
.expect({
messages: ['那你能告诉我你今天穿了什么颜色的裙子吗?'],
})
.then();
await request(app._httpServer)
.post('/api')
.send({ userId: '111111', content: '白色' })
.set('Authorization', 'dress')
.expect(200)
.expect({
messages: ['原来 111111 今天穿的是 红色 的衣服,和 白色 的裙子!'],
})
.then();
await request(app._httpServer)
.post('/api')
.send({ userId: '111112', content: '黑色' })
.set('Authorization', 'dress')
.expect(200)
.expect({
messages: ['原来 111112 今天穿的是 蓝色 的衣服,和 黑色 的裙子!'],
})
.then();
});
afterEach(async () => {
await app.stop();
app._httpServer.close();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment