Commit b496ad79 authored by nanahira's avatar nanahira

template

parent 7ec11a42
......@@ -35,7 +35,7 @@
"typescript": "^4.5.5"
},
"peerDependencies": {
"koishi": "^4.2.1"
"koishi": "^4.4.1"
}
},
"node_modules/@babel/code-frame": {
......@@ -998,14 +998,14 @@
}
},
"node_modules/@koishijs/core": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@koishijs/core/-/core-4.2.1.tgz",
"integrity": "sha512-ZbFquhAP+IPzRp6qHmiMJdkhqR2ai90ROqRiZZijfDliwlsHwsavYx22b+H9eUthCYBbMBjjGjxtdzX5tdpWVg==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@koishijs/core/-/core-4.4.1.tgz",
"integrity": "sha512-nd/QAm9ejk4KQP8BjnyuDCxg4rpyq4fyYtTvFOuafj9G/vN6gHL11YLF5R7g2WimnN1S2sN1txB7WNx8dnGI1A==",
"peer": true,
"dependencies": {
"@koishijs/utils": "^5.0.1",
"@koishijs/utils": "^5.2.0",
"fastest-levenshtein": "^1.0.12",
"schemastery": "^2.4.2"
"schemastery": "^3.1.0"
},
"engines": {
"node": ">=12.0.0"
......@@ -1054,12 +1054,19 @@
"koishi": "^4.0.0-rc.3"
}
},
"node_modules/@koishijs/segment": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@koishijs/segment/-/segment-1.1.1.tgz",
"integrity": "sha512-HmLRKJiIX++U+ow+RP0BVGwgAFzDRqwHusUzjoZcZdcnG8yudTsvB6MXnzBMnBL7k9LBxUlfB5P8ukkM1roZFQ==",
"peer": true
},
"node_modules/@koishijs/utils": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@koishijs/utils/-/utils-5.0.1.tgz",
"integrity": "sha512-usWsR//YsvepI65+KN7ma2sxYF8Nlq5V88BbJS0X8l2+E/NEetSKYJqtwUX2TgtDW+tHxbje0IoHVccJMDRmyg==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@koishijs/utils/-/utils-5.2.0.tgz",
"integrity": "sha512-KqZS/zK415XkFZn1EQ9BpT4ydaNS0ZFCAS64RT4pR8pzCUWz5eyN81Z8A7AmLKdJWgNDiT6mQ08EyUvMq0l/7Q==",
"peer": true,
"dependencies": {
"@koishijs/segment": "^1.1.1",
"supports-color": "^8.1.0"
}
},
......@@ -1416,9 +1423,9 @@
}
},
"node_modules/@types/ws": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz",
"integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==",
"version": "8.5.2",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.2.tgz",
"integrity": "sha512-VXI82ykONr5tacHEojnErTQk+KQSoYbW1NB6iz6wUwrNd+BqfkfggQNoNdCqhJSzbNumShPERbM+Pc5zpfhlbw==",
"peer": true,
"dependencies": {
"@types/node": "*"
......@@ -4730,17 +4737,17 @@
}
},
"node_modules/koishi": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/koishi/-/koishi-4.2.1.tgz",
"integrity": "sha512-E68YfIMUnL05gIX7KXsyj2IXSCu+QKyOaANbyHAy0t+FO0o7CepsFxRP4BHTtcX/KwoELLOu670KT5DMQvb2nw==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/koishi/-/koishi-4.4.1.tgz",
"integrity": "sha512-yTJlAwsbF7ecjqRGUX5l2LT8HROusAZj1UjzxEevXGWklRrzABWTifIXDEKd9H+oC5xq4hJQeXRCQCogiPe+SQ==",
"peer": true,
"dependencies": {
"@koa/router": "^10.1.1",
"@koishijs/core": "^4.2.1",
"@koishijs/utils": "^5.0.1",
"@koishijs/core": "^4.4.1",
"@koishijs/utils": "^5.2.0",
"@types/koa": "*",
"@types/koa__router": "*",
"@types/ws": "^8.2.2",
"@types/ws": "^8.5.2",
"axios": "^0.24.0",
"file-type": "^16.5.3",
"koa": "^2.13.4",
......@@ -4748,16 +4755,16 @@
"parseurl": "^1.3.3",
"path-to-regexp": "^6.2.0",
"proxy-agent": "^5.0.0",
"ws": "^8.4.2"
"ws": "^8.5.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/koishi/node_modules/ws": {
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
"integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==",
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"peer": true,
"engines": {
"node": ">=10.0.0"
......@@ -5749,9 +5756,9 @@
}
},
"node_modules/schemastery": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/schemastery/-/schemastery-2.4.2.tgz",
"integrity": "sha512-0sCJuGa7LyAtnoXEVwUKSPSTMfizA0zpK0kX7dzGPLpeLuxgJg7VVyR2KFsfHg+WlqYbUOvoGY7JSNPk5I5uHQ==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/schemastery/-/schemastery-3.1.0.tgz",
"integrity": "sha512-I1KaYmKfB+buINqBXydV8bC7N+BaC8y/V+609oSJNDO3mP3DY0zoNcDCIaY95KR0a5Cr9+CxgKl5BLEgFoikzw==",
"peer": true
},
"node_modules/semver": {
......@@ -7527,14 +7534,14 @@
}
},
"@koishijs/core": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@koishijs/core/-/core-4.2.1.tgz",
"integrity": "sha512-ZbFquhAP+IPzRp6qHmiMJdkhqR2ai90ROqRiZZijfDliwlsHwsavYx22b+H9eUthCYBbMBjjGjxtdzX5tdpWVg==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@koishijs/core/-/core-4.4.1.tgz",
"integrity": "sha512-nd/QAm9ejk4KQP8BjnyuDCxg4rpyq4fyYtTvFOuafj9G/vN6gHL11YLF5R7g2WimnN1S2sN1txB7WNx8dnGI1A==",
"peer": true,
"requires": {
"@koishijs/utils": "^5.0.1",
"@koishijs/utils": "^5.2.0",
"fastest-levenshtein": "^1.0.12",
"schemastery": "^2.4.2"
"schemastery": "^3.1.0"
}
},
"@koishijs/plugin-adapter-onebot": {
......@@ -7563,12 +7570,19 @@
"dev": true,
"requires": {}
},
"@koishijs/segment": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@koishijs/segment/-/segment-1.1.1.tgz",
"integrity": "sha512-HmLRKJiIX++U+ow+RP0BVGwgAFzDRqwHusUzjoZcZdcnG8yudTsvB6MXnzBMnBL7k9LBxUlfB5P8ukkM1roZFQ==",
"peer": true
},
"@koishijs/utils": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@koishijs/utils/-/utils-5.0.1.tgz",
"integrity": "sha512-usWsR//YsvepI65+KN7ma2sxYF8Nlq5V88BbJS0X8l2+E/NEetSKYJqtwUX2TgtDW+tHxbje0IoHVccJMDRmyg==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@koishijs/utils/-/utils-5.2.0.tgz",
"integrity": "sha512-KqZS/zK415XkFZn1EQ9BpT4ydaNS0ZFCAS64RT4pR8pzCUWz5eyN81Z8A7AmLKdJWgNDiT6mQ08EyUvMq0l/7Q==",
"peer": true,
"requires": {
"@koishijs/segment": "^1.1.1",
"supports-color": "^8.1.0"
},
"dependencies": {
......@@ -7909,9 +7923,9 @@
}
},
"@types/ws": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz",
"integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==",
"version": "8.5.2",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.2.tgz",
"integrity": "sha512-VXI82ykONr5tacHEojnErTQk+KQSoYbW1NB6iz6wUwrNd+BqfkfggQNoNdCqhJSzbNumShPERbM+Pc5zpfhlbw==",
"peer": true,
"requires": {
"@types/node": "*"
......@@ -10392,17 +10406,17 @@
}
},
"koishi": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/koishi/-/koishi-4.2.1.tgz",
"integrity": "sha512-E68YfIMUnL05gIX7KXsyj2IXSCu+QKyOaANbyHAy0t+FO0o7CepsFxRP4BHTtcX/KwoELLOu670KT5DMQvb2nw==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/koishi/-/koishi-4.4.1.tgz",
"integrity": "sha512-yTJlAwsbF7ecjqRGUX5l2LT8HROusAZj1UjzxEevXGWklRrzABWTifIXDEKd9H+oC5xq4hJQeXRCQCogiPe+SQ==",
"peer": true,
"requires": {
"@koa/router": "^10.1.1",
"@koishijs/core": "^4.2.1",
"@koishijs/utils": "^5.0.1",
"@koishijs/core": "^4.4.1",
"@koishijs/utils": "^5.2.0",
"@types/koa": "*",
"@types/koa__router": "*",
"@types/ws": "^8.2.2",
"@types/ws": "^8.5.2",
"axios": "^0.24.0",
"file-type": "^16.5.3",
"koa": "^2.13.4",
......@@ -10410,13 +10424,13 @@
"parseurl": "^1.3.3",
"path-to-regexp": "^6.2.0",
"proxy-agent": "^5.0.0",
"ws": "^8.4.2"
"ws": "^8.5.0"
},
"dependencies": {
"ws": {
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
"integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==",
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"peer": true,
"requires": {}
}
......@@ -11136,9 +11150,9 @@
}
},
"schemastery": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/schemastery/-/schemastery-2.4.2.tgz",
"integrity": "sha512-0sCJuGa7LyAtnoXEVwUKSPSTMfizA0zpK0kX7dzGPLpeLuxgJg7VVyR2KFsfHg+WlqYbUOvoGY7JSNPk5I5uHQ==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/schemastery/-/schemastery-3.1.0.tgz",
"integrity": "sha512-I1KaYmKfB+buINqBXydV8bC7N+BaC8y/V+609oSJNDO3mP3DY0zoNcDCIaY95KR0a5Cr9+CxgKl5BLEgFoikzw==",
"peer": true
},
"semver": {
......
......@@ -65,6 +65,6 @@
"typed-reflector": "^1.0.9"
},
"peerDependencies": {
"koishi": "^4.2.1"
"koishi": "^4.4.1"
}
}
......@@ -9,9 +9,17 @@ import {
OnContextFunction,
} from '../def';
import 'reflect-metadata';
import { Argv, Command, FieldCollector, Selection, Session } from 'koishi';
import {
Argv,
Command,
Dict,
FieldCollector,
Selection,
Session,
} from 'koishi';
import { Metadata } from '../meta/metadata.decorators';
import { CommandPut, DoRegister } from '../registry';
import { adaptLocaleDict, registerTemplate } from '../utility';
// Register method
......@@ -98,11 +106,15 @@ export const CommandUse = <T extends Command, R extends any[]>(
...args: R
) => CommandDef((cmd) => callback(cmd, ...args));
export const CommandDescription = (desc: string) =>
CommandDef((cmd) => {
cmd.description = desc;
export const CommandDescription = (desc: string | Dict<string>) => {
return CommandDef((cmd, ctx) => {
for (const localData of Object.entries(adaptLocaleDict(desc))) {
const [locale, text] = localData;
ctx.i18n.define(locale, `commands.${cmd.name}.description`, text);
}
return cmd;
});
};
export const CommandAlias = (...names: string[]) =>
CommandDef((cmd) => cmd.alias(...names));
......@@ -136,6 +148,12 @@ export const CommandBefore = (callback: Command.Action, append = false) =>
export const CommandAction = (callback: Command.Action, prepend = false) =>
CommandDef((cmd) => cmd.action(callback, prepend));
export const CommandTemplate = (name: string, text: string | Dict<string>) =>
CommandDef((cmd, ctx) => {
registerTemplate({ name, text: adaptLocaleDict(text) }, ctx, cmd);
return cmd;
});
// Command put config
export const PutArgv = (field?: keyof Argv) =>
......@@ -170,3 +188,11 @@ export const PutChannelName = () => PutSession('channelName');
export const PutSelfId = () => PutSession('selfId');
export const PutBot = () => PutSession('bot');
export const PutNext = () => PutArgv('next');
export const PutRenderer = (path: string) =>
CommandPut.decorate('renderer', path);
export const PutCommonRenderer = () => CommandPut.decorate('renderer');
export const PutTemplate = (name: string, text: string | Dict<string>) =>
CommandPut.decorate('template', {
name,
text: adaptLocaleDict(text),
});
import { Argv, Command, Context, EventMap, Plugin, Selection } from 'koishi';
import {
Argv,
Command,
Context,
Dict,
EventMap,
Plugin,
Selection,
} from 'koishi';
import type { DefaultContext, DefaultState, ParameterizedContext } from 'koa';
import type { RouterParamContext } from '@koa/router';
import { CommandPut } from '../registry/registries/command-put';
......@@ -106,7 +114,7 @@ export interface CommandOptionConfig {
config?: Argv.OptionConfig;
}
export type CommandDefinitionFun = (cmd: Command) => Command;
export type CommandDefinitionFun = (cmd: Command, ctx: Context) => Command;
export interface KoishiRouteDef {
path: string;
......@@ -126,3 +134,13 @@ export type KoaContext = ParameterizedContext<
DefaultContext & RouterParamContext<DefaultState, DefaultContext>,
any
>;
// eslint-disable-next-line @typescript-eslint/ban-types
export type Renderer<T = any> = (params: T) => string;
// eslint-disable-next-line @typescript-eslint/ban-types
export type CRenderer = (path: string, params: object) => string;
export interface TemplateConfig {
name: string;
text: Dict<string>;
}
import { Argv, Command, FieldCollector, Session, User } from 'koishi';
import { Argv, Command, Context, FieldCollector, Session, User } from 'koishi';
import {
CommandOptionConfig,
GenerateMappingStruct,
KoishiCommandPutDef,
MappingStruct,
TemplateConfig,
} from '../../def';
import { MethodRegistry } from '../abstract-registry';
import { registerTemplate } from '../../utility';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace CommandPut {
......@@ -20,12 +22,18 @@ export namespace CommandPut {
guild: FieldCollector<'channel'>;
username: boolean;
sessionField: keyof Session;
renderer: string | undefined;
template: TemplateConfig;
}
export type Config<K extends keyof ConfigMap = keyof ConfigMap> =
MappingStruct<ConfigMap, K>;
export const preRegistry = new MethodRegistry<ConfigMap, void, [Command]>();
export const preRegistry = new MethodRegistry<
ConfigMap,
void,
[Command, Context]
>();
preRegistry.extend('option', (data, cmd) =>
cmd.option(data.name, data.desc, data.config),
......@@ -49,6 +57,10 @@ export namespace CommandPut {
}
});
preRegistry.extend('template', (data, cmd, ctx) =>
registerTemplate(data, ctx, cmd),
);
export const registry = new MethodRegistry<ConfigMap, any, [Argv, any[]]>();
registry.extend('args', (data, argv, args) => args);
......@@ -74,6 +86,20 @@ export namespace CommandPut {
});
registry.extend('sessionField', (data, argv, args) => argv.session[data]);
registry.extend('renderer', (data, argv, args) =>
data
? // eslint-disable-next-line @typescript-eslint/ban-types
(params: object) => argv.session.text(data, params)
: // eslint-disable-next-line @typescript-eslint/ban-types
(path: string, params: object) => argv.session.text(path, params),
);
registry.extend(
'template',
// eslint-disable-next-line @typescript-eslint/ban-types
(data, argv, args) => (params: object) =>
argv.session.text(`.${data.name}`, params),
);
export function decorate<T extends keyof ConfigMap>(
type: T,
data?: ConfigMap[T],
......
......@@ -125,7 +125,7 @@ export namespace DoRegister {
extraObj,
);
for (const commandDef of commandDefs) {
command = commandDef(command) || command;
command = commandDef(command, ctx) || command;
}
if (!data.config?.empty) {
if (!data.putOptions) {
......@@ -134,7 +134,7 @@ export namespace DoRegister {
);
} else {
for (const putOption of data.putOptions) {
CommandPut.preRegistry.execute(putOption, command);
CommandPut.preRegistry.execute(putOption, command, ctx);
}
command.action((argv: Argv, ...args: any[]) => {
const params = data.putOptions.map((o) =>
......
import { Context } from 'koishi';
import { ContextSelector, OnContextFunction } from '../def';
import { Command, Context, Dict } from 'koishi';
import { ContextSelector, OnContextFunction, TemplateConfig } from '../def';
export function applySelector(
ctx: Context,
......@@ -28,3 +28,24 @@ export function getContextFromFilters(
}
return targetCtx;
}
export function adaptLocaleDict(value: string | Dict<string>): Dict<string> {
if (typeof value === 'string') {
return {
'': value,
};
}
return value;
}
export const registerTemplate = (
templateConfig: TemplateConfig,
ctx: Context,
command?: Command,
) => {
const key =
(command ? `commands.${command.name}.messages.` : '') + templateConfig.name;
for (const [locale, text] of Object.entries(templateConfig.text)) {
ctx.i18n.define(locale, key, text);
}
};
import { App } from 'koishi';
import { Get } from '../src/decorators';
import { Get } from '../src/http-decorators';
import { KoaContext } from '../src/def';
import request from 'supertest';
import { Registrar } from '../src/register';
......
import {
CommandDescription,
PutOption,
PutTemplate,
UseCommand,
} from '../src/decorators';
import { Renderer } from '../src/def';
import { Registrar } from '../src/register';
import { App, Command } from 'koishi';
import { Session } from 'koishi';
class MyClass {
@UseCommand('echo')
@CommandDescription({ zh: '描述', en: 'description' })
async onEcho(
@PutOption('content', '-c <content:string>') content: string,
@PutTemplate('content', { zh: '机器人: {content}', en: 'Robot: {content}' })
renderer: Renderer<{ content: string }>,
) {
return renderer({ content });
}
}
const registrar = new Registrar(new MyClass());
describe('Register', () => {
for (const locale of [
{ locale: 'zh', prefix: '机器人' },
{ locale: 'en', prefix: 'Robot' },
]) {
it('should register command', async () => {
const app = new App({ locale: locale.locale });
await app.start();
const result = registrar.register(app, 'onEcho');
const command: Command = result.result;
expect(
command.execute({
options: { content: 'hello' },
session: {
text(path, params) {
return app.i18n.render(
[locale.locale],
`commands.echo.messages${path}`,
params,
);
},
} as Session,
}),
).resolves.toBe(`${locale.prefix}: hello`);
});
}
});
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