From d7ff3497ca299c10a6358cb3aab8d28b04434812 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Mon, 20 Jan 2020 16:09:31 +0800 Subject: [PATCH 01/21] chore: adjust --- .gitignore | 1 + package.json | 1 + packages/plugin-common/tests/repeater.spec.ts | 2 +- packages/test-utils/package.json | 4 ++-- tsconfig.json | 2 ++ 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index ad5df02d33..6045cb2988 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ temp /bots /coverage /plugins +/tsconfig.extend.json todo.md yarn.lock diff --git a/package.json b/package.json index 36abeaa9dc..c75942ed11 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ ], "scripts": { "build": "tsc -b", + "build:extend": "tsc -b tsconfig.extend.json", "bump": "ts-node build/bump", "dep": "ts-node build/dep", "docs": "cd docs & yarn dev", diff --git a/packages/plugin-common/tests/repeater.spec.ts b/packages/plugin-common/tests/repeater.spec.ts index 166c49de1a..8529f15730 100644 --- a/packages/plugin-common/tests/repeater.spec.ts +++ b/packages/plugin-common/tests/repeater.spec.ts @@ -1,5 +1,5 @@ import { MockedApp } from 'koishi-test-utils' -import { apply, repeater, RepeaterOptions } from '../src' +import { repeater, RepeaterOptions } from '../src' import 'koishi-database-memory' test('repeat', async () => { diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index bf4caa9348..487a6fa538 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -33,8 +33,7 @@ "utilities" ], "peerDependencies": { - "jest": "^24.9.0", - "koishi-core": "^1.5.0" + "jest": "^24.9.0" }, "devDependencies": { "@types/debug": "^4.1.5", @@ -44,6 +43,7 @@ "axios": "^0.19.1", "debug": "^4.1.1", "get-port": "^5.1.1", + "koishi-core": "^1.5.0", "koishi-utils": "^1.0.2" } } diff --git a/tsconfig.json b/tsconfig.json index 15a4412eb4..a57eb47593 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "es2019", "module": "commonjs", + "composite": true, "esModuleInterop": true, "moduleResolution": "node", }, @@ -13,6 +14,7 @@ { "path": "./packages/database-sqlite" }, { "path": "./packages/database-memory" }, { "path": "./packages/plugin-common" }, + { "path": "./packages/plugin-nlp" }, { "path": "./packages/plugin-recorder" }, { "path": "./packages/plugin-schedule" }, { "path": "./packages/plugin-teach" }, From 3ae7e7044d06daa2ea76e24af15358c5764b9887 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Mon, 20 Jan 2020 16:10:07 +0800 Subject: [PATCH 02/21] fix(core): create major context at demand --- packages/koishi-core/src/app.ts | 35 ++++++++++++++++++++--------- packages/koishi-core/src/context.ts | 6 ++--- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/koishi-core/src/app.ts b/packages/koishi-core/src/app.ts index f3eabb4b22..239a4a0b48 100644 --- a/packages/koishi-core/src/app.ts +++ b/packages/koishi-core/src/app.ts @@ -85,9 +85,7 @@ export class App extends Context { atMeRE: RegExp prefixRE: RegExp nicknameRE: RegExp - users: MajorContext - groups: MajorContext - discusses: MajorContext + status = Status.closed _commands: Command[] = [] _commandMap: Record = {} @@ -95,7 +93,9 @@ export class App extends Context { _shortcutMap: Record = {} _middlewares: [Context, Middleware][] = [] - private status = Status.closed + private _users: MajorContext + private _groups: MajorContext + private _discusses: MajorContext private _isReady = false private _middlewareCounter = 0 private _middlewareSet = new Set() @@ -126,14 +126,27 @@ export class App extends Context { this.receiver.on('before-user', Command.attachUserFields) this.receiver.on('before-group', Command.attachGroupFields) this.middleware(this._preprocess) + } + + get users () { + if (this._users) return this._users + const users = this.createContext([[null, []], [[], null], [[], null]]) as MajorContext + users.except = (...ids) => this.createContext([[null, ids], [[], null], [[], null]]) + return this._users = users + } + + get groups () { + if (this._groups) return this._groups + const groups = this.createContext([[[], null], [null, []], [[], null]]) as MajorContext + groups.except = (...ids) => this.createContext([[[], null], [null, ids], [[], null]]) + return this._groups = groups + } - // create built-in contexts - this.users = this.createContext([[null, []], [[], null], [[], null]]) as MajorContext - this.groups = this.createContext([[[], null], [null, []], [[], null]]) as MajorContext - this.discusses = this.createContext([[[], null], [[], null], [null, []]]) as MajorContext - this.users.except = (...ids) => this.createContext([[null, ids], [[], null], [[], null]]) - this.groups.except = (...ids) => this.createContext([[[], null], [null, ids], [[], null]]) - this.discusses.except = (...ids) => this.createContext([[[], null], [[], null], [null, ids]]) + get discusses () { + if (this._discusses) return this._discusses + const discusses = this.createContext([[[], null], [[], null], [null, []]]) as MajorContext + discusses.except = (...ids) => this.createContext([[[], null], [[], null], [null, ids]]) + return this._discusses = discusses } get selfId () { diff --git a/packages/koishi-core/src/context.ts b/packages/koishi-core/src/context.ts index 9cde0e2c11..18c616e773 100644 --- a/packages/koishi-core/src/context.ts +++ b/packages/koishi-core/src/context.ts @@ -131,11 +131,11 @@ export class Context { plugin (plugin: PluginObject, options?: U): this plugin (plugin: Plugin, options: any) { if (options === false) return - const app = Object.create(this) + const ctx = Object.create(this) if (typeof plugin === 'function') { - plugin(app, options) + plugin(ctx, options) } else if (plugin && typeof plugin === 'object' && typeof plugin.apply === 'function') { - plugin.apply(app, options) + plugin.apply(ctx, options) } else { throw new Error(errors.INVALID_PLUGIN) } From bff469eabe14d42683a4f7c3ccb659daec5e1c00 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Mon, 20 Jan 2020 16:29:20 +0800 Subject: [PATCH 03/21] chore: autoEscape default to false --- packages/koishi-core/src/sender.ts | 12 ++++++------ packages/koishi-core/tests/parser.spec.ts | 2 +- packages/koishi-core/tests/validation.spec.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/koishi-core/src/sender.ts b/packages/koishi-core/src/sender.ts index 47d235ff1d..9ff762a90a 100644 --- a/packages/koishi-core/src/sender.ts +++ b/packages/koishi-core/src/sender.ts @@ -115,7 +115,7 @@ export class Sender { } as Meta<'send'> } - async sendGroupMsg (groupId: number, message: string, autoEscape?: boolean) { + async sendGroupMsg (groupId: number, message: string, autoEscape = false) { this._assertInteger('groupId', groupId) if (!message) return const meta = this._createSendMeta('group', groupId, message) @@ -126,13 +126,13 @@ export class Sender { return messageId } - async sendGroupMsgAsync (groupId: number, message: string, autoEscape?: boolean) { + async sendGroupMsgAsync (groupId: number, message: string, autoEscape = false) { this._assertInteger('groupId', groupId) if (!message) return await this.get('send_group_msg_async', { groupId, message, autoEscape }) } - async sendDiscussMsg (discussId: number, message: string, autoEscape?: boolean) { + async sendDiscussMsg (discussId: number, message: string, autoEscape = false) { this._assertInteger('discussId', discussId) if (!message) return const meta = this._createSendMeta('discuss', discussId, message) @@ -143,13 +143,13 @@ export class Sender { return messageId } - async sendDiscussMsgAsync (discussId: number, message: string, autoEscape?: boolean) { + async sendDiscussMsgAsync (discussId: number, message: string, autoEscape = false) { this._assertInteger('discussId', discussId) if (!message) return await this.get('send_discuss_msg_async', { discussId, message, autoEscape }) } - async sendPrivateMsg (userId: number, message: string, autoEscape?: boolean) { + async sendPrivateMsg (userId: number, message: string, autoEscape = false) { this._assertInteger('userId', userId) if (!message) return const meta = this._createSendMeta('user', userId, message) @@ -160,7 +160,7 @@ export class Sender { return messageId } - async sendPrivateMsgAsync (userId: number, message: string, autoEscape?: boolean) { + async sendPrivateMsgAsync (userId: number, message: string, autoEscape = false) { this._assertInteger('userId', userId) if (!message) return await this.get('send_private_msg_async', { userId, message, autoEscape }) diff --git a/packages/koishi-core/tests/parser.spec.ts b/packages/koishi-core/tests/parser.spec.ts index 2c087392c0..462c9d38ba 100644 --- a/packages/koishi-core/tests/parser.spec.ts +++ b/packages/koishi-core/tests/parser.spec.ts @@ -95,7 +95,7 @@ describe('options', () => { describe('user fields', () => { const cmd = app.command('cmd-user-fields') expect(cmd._userFields).toHaveProperty('size', 0) - cmd.userFields(['id', 'name']) + cmd.userFields(['id']) expect(cmd._userFields).toHaveProperty('size', 2) cmd.userFields(new Set(['id', 'authority'])) expect(cmd._userFields).toHaveProperty('size', 3) diff --git a/packages/koishi-core/tests/validation.spec.ts b/packages/koishi-core/tests/validation.spec.ts index e012efe5fa..46e5066122 100644 --- a/packages/koishi-core/tests/validation.spec.ts +++ b/packages/koishi-core/tests/validation.spec.ts @@ -11,7 +11,7 @@ const session5 = app.createSession('group', 123, 654) app.command('cmd1', { authority: 2, maxUsage: 1 }) // make coverage happy - .userFields(['name', 'flag', 'authority', 'usage']) + .userFields(['flag', 'authority', 'usage']) .groupFields(['id', 'flag', 'assignee']) .option('--bar', '', { authority: 3 }) .option('--baz', '', { notUsage: true }) From 1739c646e72bd70ef2470a6a97290c682185afbf Mon Sep 17 00:00:00 2001 From: simon3000 Date: Mon, 20 Jan 2020 15:37:19 +0100 Subject: [PATCH 04/21] test: add experimental shouldMatchSnapshot (#20) Co-authored-by: Shigma <33423008+Shigma@users.noreply.github.com> --- package.json | 1 + packages/test-utils/src/mocks.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/package.json b/package.json index c75942ed11..6d88a3ce42 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "jest": "jest .*\\.spec\\.ts --coverage", "lint": "eslint packages/*/src/**/*.ts --cache", "pub": "ts-node build/publish", + "pub:plugins": "yarn pub plugins", "start": "ts-node build/start" }, "version": "1.0.0", diff --git a/packages/test-utils/src/mocks.ts b/packages/test-utils/src/mocks.ts index 1e1a4794a5..41697e842e 100644 --- a/packages/test-utils/src/mocks.ts +++ b/packages/test-utils/src/mocks.ts @@ -24,6 +24,11 @@ export class MockedServer { this.clearRequests() } + shouldHaveLastRequestMatchSnapshot (name?: string) { + expect(this.requests[0]).toMatchSnapshot(name) + this.clearRequests() + } + shouldHaveLastRequests (requests: RequestData[]) { expect(this.requests.slice(0, requests.length)).toMatchObject(requests.map(snakeCase).reverse()) this.clearRequests() @@ -154,6 +159,10 @@ export class MockedApp extends App { this.sender.mock.shouldHaveLastRequests(requests) } + shouldHaveLastRequestMatchSnapshot (name?: string) { + this.sender.mock.shouldHaveLastRequestMatchSnapshot(name) + } + setResponse (event: string, hanlder: RequestHandler): void setResponse (event: string, data: RequestParams, retcode?: number): void setResponse (event: string, arg1: any, arg2?: any) { From 1851190f79c5b9a80e5396bfb1bee6e45a220de9 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Tue, 21 Jan 2020 19:32:41 +0800 Subject: [PATCH 05/21] fix tests --- package.json | 2 +- packages/koishi-core/tests/parser.spec.ts | 4 +-- packages/koishi-core/tests/suggestion.spec.ts | 34 +++++++++++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6d88a3ce42..40ec8d1771 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "bump": "ts-node build/bump", "dep": "ts-node build/dep", "docs": "cd docs & yarn dev", - "jest": "jest .*\\.spec\\.ts --coverage", + "jest": "jest packages/.*\\.spec\\.ts --coverage", "lint": "eslint packages/*/src/**/*.ts --cache", "pub": "ts-node build/publish", "pub:plugins": "yarn pub plugins", diff --git a/packages/koishi-core/tests/parser.spec.ts b/packages/koishi-core/tests/parser.spec.ts index 462c9d38ba..8c9ab72a70 100644 --- a/packages/koishi-core/tests/parser.spec.ts +++ b/packages/koishi-core/tests/parser.spec.ts @@ -96,9 +96,9 @@ describe('user fields', () => { const cmd = app.command('cmd-user-fields') expect(cmd._userFields).toHaveProperty('size', 0) cmd.userFields(['id']) - expect(cmd._userFields).toHaveProperty('size', 2) + expect(cmd._userFields).toHaveProperty('size', 1) cmd.userFields(new Set(['id', 'authority'])) - expect(cmd._userFields).toHaveProperty('size', 3) + expect(cmd._userFields).toHaveProperty('size', 2) }) describe('group fields', () => { diff --git a/packages/koishi-core/tests/suggestion.spec.ts b/packages/koishi-core/tests/suggestion.spec.ts index 16b0fae438..c13d2235a1 100644 --- a/packages/koishi-core/tests/suggestion.spec.ts +++ b/packages/koishi-core/tests/suggestion.spec.ts @@ -75,7 +75,7 @@ describe('Command Suggestions', () => { }) }) -describe('Custom Suggestions', () => { +describe('Custom Suggestions with Arguments', () => { const app = new MockedApp({ database: { memory: {} } }) const session = app.createSession('group', 123, 456) const command = app.command('echo [message]', { authority: 0 }) @@ -89,7 +89,37 @@ describe('Custom Suggestions', () => { prefix: 'prefix', suffix: 'suffix', command, - execute: (suggestion, meta) => command.execute({ args: [suggestion], meta }), + execute: (message, meta) => command.execute({ args: [message], meta }), + })) + + beforeAll(async () => { + await app.start() + await app.database.getGroup(456, 514) + }) + + test('show suggestions', async () => { + await session.shouldHaveNoResponse(' ') + await session.shouldHaveReply('for', `prefix${format(messages.SUGGESTION_TEXT, '“foo”')}suffix`) + await session.shouldHaveReply(' ', 'text:foo') + }) +}) + +describe('Custom Suggestions with Options', () => { + const app = new MockedApp({ database: { memory: {} } }) + const session = app.createSession('group', 123, 456) + const command = app.command('echo', { authority: 0 }) + .option('-m, --message ') + .action(({ meta, options }) => meta.$send('text:' + options.message)) + + app.middleware((meta, next) => showSuggestions({ + target: meta.message, + items: ['foo', 'bar'], + meta, + next, + prefix: 'prefix', + suffix: 'suffix', + command, + execute: (message, meta) => command.execute({ options: { message }, meta }), })) beforeAll(async () => { From cf2f68daef2582b28b1439173696b40a7e8f5b69 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Tue, 21 Jan 2020 19:39:13 +0800 Subject: [PATCH 06/21] chore: cover app.start() with $tables --- packages/koishi-core/tests/database.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/koishi-core/tests/database.spec.ts b/packages/koishi-core/tests/database.spec.ts index c52d17f236..af317f5eb3 100644 --- a/packages/koishi-core/tests/database.spec.ts +++ b/packages/koishi-core/tests/database.spec.ts @@ -178,3 +178,11 @@ describe('extend fields', () => { }) }) }) + +test('Open & Close', async () => { + const app = new App({ + database: { $tables: {} }, + }) + await expect(app.start()).resolves.toBeUndefined() + await expect(app.stop()).resolves.toBeUndefined() +}) From 7a1dd9426bee8fc3440a7226aa93199482063dc4 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Tue, 21 Jan 2020 22:44:47 +0800 Subject: [PATCH 07/21] feat(core): onceMiddleware --- packages/koishi-core/src/context.ts | 14 +++++++ packages/koishi-core/src/utils.ts | 9 +---- packages/koishi-core/tests/middleware.spec.ts | 40 ++++++++++++------- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/packages/koishi-core/src/context.ts b/packages/koishi-core/src/context.ts index 18c616e773..57dea803c5 100644 --- a/packages/koishi-core/src/context.ts +++ b/packages/koishi-core/src/context.ts @@ -152,6 +152,10 @@ export class Context { return this } + addMiddleware (middleware: Middleware) { + return this.middleware(middleware) + } + prependMiddleware (middleware: Middleware) { const { maxMiddlewares } = this.app.options if (this.app._middlewares.length >= maxMiddlewares) { @@ -170,6 +174,16 @@ export class Context { } } + onceMiddleware (middleware: Middleware, meta?: Meta) { + const identifier = meta ? meta.$ctxId + meta.$ctxType + meta.userId : undefined + const listener: Middleware = async (meta, next) => { + if (identifier && meta.$ctxId + meta.$ctxType + meta.userId !== identifier) return next() + this.removeMiddleware(listener) + return middleware(meta, next) + } + return this.prependMiddleware(listener) + } + command (rawName: string, config?: CommandConfig): Command command (rawName: string, description: string, config?: CommandConfig): Command command (rawName: string, ...args: [CommandConfig?] | [string, CommandConfig?]) { diff --git a/packages/koishi-core/src/utils.ts b/packages/koishi-core/src/utils.ts index 7b21c1039a..d859a85f8f 100644 --- a/packages/koishi-core/src/utils.ts +++ b/packages/koishi-core/src/utils.ts @@ -44,15 +44,11 @@ export function showSuggestions (options: SuggestOptions): Promise { if (suggestions.length === 1) { const [suggestion] = suggestions const command = typeof options.command === 'function' ? options.command(suggestion) : options.command - const identifier = meta.userId + meta.$ctxType + meta.$ctxId const userFields = new Set() const groupFields = new Set() Command.attachUserFields(userFields, { command, meta }) Command.attachGroupFields(groupFields, { command, meta }) - - const middleware: Middleware = async (meta, next) => { - if (meta.userId + meta.$ctxType + meta.$ctxId !== identifier) return next() - command.context.removeMiddleware(middleware) + command.context.onceMiddleware(async (meta, next) => { if (!meta.message.trim()) { meta.$user = await command.context.database?.observeUser(meta.userId, Array.from(userFields)) if (meta.messageType === 'group') { @@ -62,8 +58,7 @@ export function showSuggestions (options: SuggestOptions): Promise { } else { return next() } - } - command.context.prependMiddleware(middleware) + }, meta) message += suffix } await meta.$send(message) diff --git a/packages/koishi-core/tests/middleware.spec.ts b/packages/koishi-core/tests/middleware.spec.ts index b6f2b2d319..163a8abff8 100644 --- a/packages/koishi-core/tests/middleware.spec.ts +++ b/packages/koishi-core/tests/middleware.spec.ts @@ -1,7 +1,6 @@ import { MockedApp, createArray } from 'koishi-test-utils' -import { errors, Middleware, NextFunction } from 'koishi-core' +import { Middleware, NextFunction } from 'koishi-core' import { sleep, noop } from 'koishi-utils' -import { format } from 'util' let callSequence: jest.Mock[] const app = new MockedApp() @@ -25,7 +24,7 @@ describe('Middleware API', () => { test('max middlewares', async () => { const warnCallback = jest.fn() app.receiver.on('logger/warn', warnCallback) - createArray(64 + extraCalls, () => app.middleware(noop)) + createArray(64 + extraCalls, () => app.addMiddleware(noop)) expect(app._middlewares.length).toBe(64) expect(warnCallback).toBeCalledTimes(extraCalls) }) @@ -39,7 +38,7 @@ describe('Middleware API', () => { }) test('remove middlewares', () => { - app.middleware(noop) + app.addMiddleware(noop) expect(app._middlewares.length).toBe(1) expect(app.removeMiddleware(noop)).toBeTruthy() expect(app._middlewares.length).toBe(0) @@ -52,8 +51,8 @@ describe('Middleware Runtime', () => { test('run asynchronously', async () => { const mid1 = wrap((_, next) => sleep(0).then(() => next())) const mid2 = wrap((_, next) => next()) - app.middleware(mid1) - app.middleware(mid2) + app.addMiddleware(mid1) + app.addMiddleware(mid2) await app.receiveMessage('user', 'foo', 123) expect(callSequence).toEqual([mid1, mid2]) }) @@ -61,18 +60,18 @@ describe('Middleware Runtime', () => { test('stop when no next is called', async () => { const mid1 = wrap(noop) const mid2 = wrap((_, next) => next()) - app.middleware(mid1) - app.middleware(mid2) + app.addMiddleware(mid1) + app.addMiddleware(mid2) expect(callSequence).toEqual([]) await app.receiveMessage('user', 'foo', 123) expect(callSequence).toEqual([mid1]) }) - test('prepend middleware', async () => { + test('prepend addMiddleware', async () => { const mid1 = wrap((_, next) => next()) const mid2 = wrap((_, next) => next()) const mid3 = wrap((_, next) => next()) - app.middleware(mid1) + app.addMiddleware(mid1) app.prependMiddleware(mid2) app.prependMiddleware(mid3) await app.receiveMessage('user', 'foo', 123) @@ -85,19 +84,30 @@ describe('Middleware Runtime', () => { const mid3 = wrap((next) => next(mid5)) const mid4 = wrap((next) => next()) const mid5 = wrap((next) => next()) - app.middleware(mid1) - app.middleware(mid2) + app.addMiddleware(mid1) + app.addMiddleware(mid2) await app.receiveMessage('user', 'foo', 123) expect(callSequence).toEqual([mid1, mid2, mid3, mid4, mid5]) }) + test('once middleware', async () => { + const mid1 = wrap((_, next) => next()) + const mid2 = wrap((_, next) => next()) + app.addMiddleware(mid1) + app.onceMiddleware(mid2) + await app.receiveMessage('user', 'foo', 123) + expect(callSequence).toEqual([mid2, mid1]) + await app.receiveMessage('user', 'foo', 123) + expect(callSequence).toEqual([mid2, mid1, mid1]) + }) + test('middleware error', async () => { const errorCallback = jest.fn() const middlewareErrorCallback = jest.fn() app.receiver.on('error', error => errorCallback(error.message)) app.receiver.on('error/middleware', error => middlewareErrorCallback(error.message)) const errorMessage = 'error message' - app.middleware(() => { throw new Error(errorMessage) }) + app.addMiddleware(() => { throw new Error(errorMessage) }) await app.receiveMessage('user', 'foo', 123) expect(errorCallback).toBeCalledTimes(1) expect(errorCallback).toBeCalledWith(errorMessage) @@ -108,8 +118,8 @@ describe('Middleware Runtime', () => { test('isolated next function', async () => { const warnCallback = jest.fn() app.receiver.on('logger/warn', warnCallback) - app.middleware((_, next) => (next(), undefined)) - app.middleware((_, next) => sleep(0).then(() => next())) + app.addMiddleware((_, next) => (next(), undefined)) + app.addMiddleware((_, next) => sleep(0).then(() => next())) await app.receiveMessage('user', 'foo', 123) await sleep(0) expect(warnCallback).toBeCalledTimes(1) From a45a0e2b59c3259f3e30f489198e496bf807b90c Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 22 Jan 2020 00:36:49 +0800 Subject: [PATCH 08/21] test: add mocked app spec --- packages/test-utils/src/app.ts | 114 ++++++++++++ packages/test-utils/src/index.ts | 1 + packages/test-utils/src/mocks.ts | 167 +----------------- packages/test-utils/src/session.ts | 48 +++++ .../tests/__snapshots__/app.spec.ts.snap | 12 ++ packages/test-utils/tests/app.spec.ts | 47 +++++ 6 files changed, 227 insertions(+), 162 deletions(-) create mode 100644 packages/test-utils/src/app.ts create mode 100644 packages/test-utils/src/session.ts create mode 100644 packages/test-utils/tests/__snapshots__/app.spec.ts.snap create mode 100644 packages/test-utils/tests/app.spec.ts diff --git a/packages/test-utils/src/app.ts b/packages/test-utils/src/app.ts new file mode 100644 index 0000000000..57ff087b41 --- /dev/null +++ b/packages/test-utils/src/app.ts @@ -0,0 +1,114 @@ +import { AppOptions, App, Sender, Server, ContextType, Meta } from 'koishi-core' +import { MockedServer, RequestParams, RequestData, RequestHandler } from './mocks' +import { Session, createMessageMeta } from './session' +import { BASE_SELF_ID } from './utils' +import debug from 'debug' + +class MockedAppServer extends Server { + constructor (app: App) { + super(app) + this.appMap[app.selfId] = app + } + + _close () {} + + async _listen () { + this.version = {} as any + } +} + +class MockedAppSender extends Sender { + mock = new MockedServer() + + constructor (app: App) { + super(app) + this._get = async (action, params) => { + return this.mock.receive(action.replace(/_async$/, ''), params) + } + } +} + +export class MockedApp extends App { + sender: MockedAppSender + server: MockedAppServer + + constructor (options: AppOptions = {}) { + super({ selfId: BASE_SELF_ID, ...options }) + this.sender = new MockedAppSender(this) + this.server = new MockedAppServer(this) + this.receiver.on('logger', (scope, message) => { + debug('koishi:' + scope)(message) + }) + } + + receive (meta: Meta) { + this.server.dispatchMeta({ + selfId: this.selfId, + ...meta, + }) + } + + receiveFriendRequest (userId: number, flag = 'flag') { + this.receive({ + postType: 'request', + requestType: 'friend', + userId, + flag, + }) + } + + receiveGroupRequest (userId: number, subType: 'add' | 'invite', groupId = 10000, flag = 'flag') { + this.receive({ + postType: 'request', + requestType: 'group', + subType, + userId, + groupId, + flag, + }) + } + + receiveMessage (meta: Meta): Promise + receiveMessage (type: 'user', message: string, userId: number): Promise + receiveMessage (type: 'group', message: string, userId: number, groupId: number): Promise + receiveMessage (type: 'discuss', message: string, userId: number, discussId: number): Promise + receiveMessage (type: ContextType | Meta, message?: string, userId?: number, ctxId: number = userId) { + return new Promise((resolve) => { + this.receiver.once('after-middleware', () => resolve()) + this.receive(typeof type === 'string' ? createMessageMeta(type, message, userId, ctxId) : type) + }) + } + + clearRequests () { + this.sender.mock.clearRequests() + } + + shouldHaveNoRequests () { + this.sender.mock.shouldHaveNoRequests() + } + + shouldHaveLastRequest (action: string, params: RequestParams = {}) { + this.sender.mock.shouldHaveLastRequest(action, params) + } + + shouldHaveLastRequests (requests: RequestData[]) { + this.sender.mock.shouldHaveLastRequests(requests) + } + + shouldMatchSnapshot (name?: string) { + this.sender.mock.shouldMatchSnapshot(name) + } + + setResponse (event: string, hanlder: RequestHandler): void + setResponse (event: string, data: RequestParams, retcode?: number): void + setResponse (event: string, arg1: any, arg2?: any) { + this.sender.mock.setResponse(event, arg1, arg2) + } + + createSession (type: 'user', userId: number): Session + createSession (type: 'group', userId: number, groupId: number): Session + createSession (type: 'discuss', userId: number, discussId: number): Session + createSession (type: ContextType, userId: number, ctxId: number = userId) { + return new Session(this, type, userId, ctxId) + } +} diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts index 587281cd00..f5d7200cbb 100644 --- a/packages/test-utils/src/index.ts +++ b/packages/test-utils/src/index.ts @@ -1,3 +1,4 @@ +export * from './app' export * from './database' export * from './mocks' export * from './utils' diff --git a/packages/test-utils/src/mocks.ts b/packages/test-utils/src/mocks.ts index 41697e842e..194c39f2f7 100644 --- a/packages/test-utils/src/mocks.ts +++ b/packages/test-utils/src/mocks.ts @@ -1,11 +1,9 @@ -import { BASE_SELF_ID } from './utils' import { snakeCase } from 'koishi-utils' -import { AppOptions, App, Sender, Server, ContextType, ResponsePayload, Meta, MessageMeta, CQResponse } from 'koishi-core' -import debug from 'debug' +import { CQResponse } from 'koishi-core' -type RequestParams = Record -type RequestData = readonly [string, RequestParams] -type RequestHandler = (params: RequestParams) => Partial +export type RequestParams = Record +export type RequestData = readonly [string, RequestParams] +export type RequestHandler = (params: RequestParams) => Partial export class MockedServer { requests: RequestData[] = [] @@ -24,7 +22,7 @@ export class MockedServer { this.clearRequests() } - shouldHaveLastRequestMatchSnapshot (name?: string) { + shouldMatchSnapshot (name?: string) { expect(this.requests[0]).toMatchSnapshot(name) this.clearRequests() } @@ -59,158 +57,3 @@ export class MockedServer { } } } - -class MockedAppServer extends Server { - constructor (app: App) { - super(app) - this.appMap[app.selfId] = app - } - - _close () {} - - async _listen () { - this.version = {} as any - } -} - -class MockedAppSender extends Sender { - mock = new MockedServer() - - constructor (app: App) { - super(app) - this._get = async (action, params) => { - return this.mock.receive(action.replace(/_async$/, ''), params) - } - } -} - -const createMessageMeta = (type: ContextType, message: string, userId: number, ctxId: number): MessageMeta => ({ - [type + 'Id']: ctxId, - postType: 'message', - messageType: type === 'user' ? 'private' : type, - message, - userId, -}) - -export class MockedApp extends App { - sender: MockedAppSender - server: MockedAppServer - - constructor (options: AppOptions = {}) { - super({ selfId: BASE_SELF_ID, ...options }) - this.sender = new MockedAppSender(this) - this.server = new MockedAppServer(this) - this.receiver.on('logger', (scope, message) => { - debug('koishi:' + scope)(message) - }) - } - - receive (meta: Meta) { - this.server.dispatchMeta({ - selfId: this.selfId, - ...meta, - }) - } - - receiveFriendRequest (userId: number, flag = 'flag') { - this.receive({ - postType: 'request', - requestType: 'friend', - userId, - flag, - }) - } - - receiveGroupRequest (userId: number, subType: 'add' | 'invite', groupId = 10000, flag = 'flag') { - this.receive({ - postType: 'request', - requestType: 'group', - subType, - userId, - groupId, - flag, - }) - } - - receiveMessage (meta: Meta): Promise - receiveMessage (type: 'user', message: string, userId: number): Promise - receiveMessage (type: 'group', message: string, userId: number, groupId: number): Promise - receiveMessage (type: 'discuss', message: string, userId: number, discussId: number): Promise - receiveMessage (type: ContextType | Meta, message?: string, userId?: number, ctxId: number = userId) { - return new Promise((resolve) => { - this.receiver.once('after-middleware', () => resolve()) - this.receive(typeof type === 'string' ? createMessageMeta(type, message, userId, ctxId) : type) - }) - } - - clearRequests () { - this.sender.mock.clearRequests() - } - - shouldHaveNoRequests () { - this.sender.mock.shouldHaveNoRequests() - } - - shouldHaveLastRequest (action: string, params: RequestParams = {}) { - this.sender.mock.shouldHaveLastRequest(action, params) - } - - shouldHaveLastRequests (requests: RequestData[]) { - this.sender.mock.shouldHaveLastRequests(requests) - } - - shouldHaveLastRequestMatchSnapshot (name?: string) { - this.sender.mock.shouldHaveLastRequestMatchSnapshot(name) - } - - setResponse (event: string, hanlder: RequestHandler): void - setResponse (event: string, data: RequestParams, retcode?: number): void - setResponse (event: string, arg1: any, arg2?: any) { - this.sender.mock.setResponse(event, arg1, arg2) - } - - createSession (type: 'user', userId: number): Session - createSession (type: 'group', userId: number, groupId: number): Session - createSession (type: 'discuss', userId: number, discussId: number): Session - createSession (type: ContextType, userId: number, ctxId: number = userId) { - return new Session(this, type, userId, ctxId) - } -} - -export class Session { - meta: MessageMeta - - constructor (public app: MockedApp, public type: ContextType, public userId: number, public ctxId: number) { - this.meta = createMessageMeta(type, null, userId, ctxId) - } - - async send (message: string) { - let payload: ResponsePayload = null - function $response (data: ResponsePayload) { - payload = data - } - await this.app.receiveMessage({ ...this.meta, message, $response }) - return payload - } - - async getReply (message: string) { - const response = await this.send(message) - return response?.reply - } - - shouldHaveReply (message: string, reply?: string) { - if (reply) { - return expect(this.getReply(message)).resolves.toBe(reply) - } else { - return expect(this.getReply(message)).resolves.toBeTruthy() - } - } - - shouldHaveNoResponse (message: string) { - return expect(this.send(message)).resolves.toBeNull() - } - - shouldMatchSnapshot (message: string) { - return expect(this.getReply(message)).resolves.toMatchSnapshot(message) - } -} diff --git a/packages/test-utils/src/session.ts b/packages/test-utils/src/session.ts new file mode 100644 index 0000000000..6da8fa5986 --- /dev/null +++ b/packages/test-utils/src/session.ts @@ -0,0 +1,48 @@ +import { Meta, ContextType, ResponsePayload } from 'koishi-core' +import { MockedApp } from './app' + +export const createMessageMeta = (type: ContextType, message: string, userId: number, ctxId: number): Meta<'message'> => ({ + [type + 'Id']: ctxId, + postType: 'message', + messageType: type === 'user' ? 'private' : type, + message, + userId, +}) + +export class Session { + meta: Meta<'message'> + + constructor (public app: MockedApp, public type: ContextType, public userId: number, public ctxId: number) { + this.meta = createMessageMeta(type, null, userId, ctxId) + } + + async send (message: string) { + let payload: ResponsePayload = null + function $response (data: ResponsePayload) { + payload = data + } + await this.app.receiveMessage({ ...this.meta, message, $response }) + return payload + } + + async getReply (message: string) { + const response = await this.send(message) + return response?.reply + } + + shouldHaveReply (message: string, reply?: string) { + if (reply) { + return expect(this.getReply(message)).resolves.toBe(reply) + } else { + return expect(this.getReply(message)).resolves.toBeTruthy() + } + } + + shouldHaveNoResponse (message: string) { + return expect(this.send(message)).resolves.toBeNull() + } + + shouldMatchSnapshot (message: string) { + return expect(this.getReply(message)).resolves.toMatchSnapshot(message) + } +} diff --git a/packages/test-utils/tests/__snapshots__/app.spec.ts.snap b/packages/test-utils/tests/__snapshots__/app.spec.ts.snap new file mode 100644 index 0000000000..988f79ea53 --- /dev/null +++ b/packages/test-utils/tests/__snapshots__/app.spec.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Mocked Server Implementation shouldMatchSnapshot 1`] = ` +Array [ + "send_private_msg", + Object { + "auto_escape": false, + "message": "foo", + "user_id": 123, + }, +] +`; diff --git a/packages/test-utils/tests/app.spec.ts b/packages/test-utils/tests/app.spec.ts new file mode 100644 index 0000000000..952373db6c --- /dev/null +++ b/packages/test-utils/tests/app.spec.ts @@ -0,0 +1,47 @@ +import { MockedApp } from '../src' + +const app = new MockedApp() + +beforeAll(() => app.start()) + +afterAll(() => app.stop()) + +describe('Mocked Server Implementation', () => { + test('shouldHaveLastRequest', async () => { + await app.sender.sendPrivateMsgAsync(123, 'foo') + app.shouldHaveLastRequest('send_private_msg') + }) + + test('shouldHaveLastRequests', async () => { + await app.sender.sendPrivateMsgAsync(123, 'foo') + app.shouldHaveLastRequests([ + ['send_private_msg', { userId: 123 }], + ]) + }) + + test('shouldMatchSnapshot', async () => { + await app.sender.sendPrivateMsgAsync(123, 'foo') + app.shouldMatchSnapshot() + }) + + test('clearRequests', async () => { + await app.sender.sendPrivateMsgAsync(123, 'foo') + app.clearRequests() + app.shouldHaveNoRequests() + }) + + test('setResponse (object, succeed)', async () => { + app.setResponse('send_private_msg', { messageId: 321 }) + await expect(app.sender.sendPrivateMsg(123, 'foo')).resolves.toBe(321) + }) + + test('setResponse (object, failed)', async () => { + app.setResponse('send_private_msg', { messageId: 321 }, 321) + await expect(app.sender.sendPrivateMsg(123, 'foo')).rejects.toBeTruthy() + }) + + test('setResponse (function)', async () => { + app.setResponse('send_private_msg', () => ({ data: { messageId: 321 } })) + await expect(app.sender.sendPrivateMsg(123, 'foo')).resolves.toBe(321) + }) +}) From 15271593097cf80b9b94de5f9968f2124b7637a8 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 22 Jan 2020 01:28:42 +0800 Subject: [PATCH 09/21] feat(core): Usage API --- packages/koishi-core/src/command.ts | 42 +++++++---------------------- packages/koishi-core/src/utils.ts | 36 +++++++++++++++++++++++-- packages/plugin-common/src/help.ts | 4 +-- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/packages/koishi-core/src/command.ts b/packages/koishi-core/src/command.ts index 7c79ec9d08..d9d010fc0b 100644 --- a/packages/koishi-core/src/command.ts +++ b/packages/koishi-core/src/command.ts @@ -4,6 +4,7 @@ import { messages, errors } from './messages' import { noop } from 'koishi-utils' import { MessageMeta } from './meta' import { format } from 'util' +import { updateUsage } from './utils' import { CommandOption, @@ -120,6 +121,10 @@ export class Command { return this.context.app } + get usageName () { + return this.config.usageName || this.name + } + private _registerAlias (name: string) { name = name.toLowerCase() this._aliases.push(name) @@ -225,20 +230,6 @@ export class Command { return typeof value === 'function' ? value(meta.$user) : value } - updateUsage (user: UserData, time = new Date()) { - const name = this.config.usageName || this.name - if (!user.usage[name]) { - user.usage[name] = {} - } - const usage = user.usage[name] - const date = time.toLocaleDateString() - if (date !== usage.date) { - usage.count = 0 - usage.date = date - } - return usage - } - parse (source: string) { return parseLine(source, this._argsDef, this._optsDef) } @@ -326,30 +317,15 @@ export class Command { // check usage const minInterval = this.getConfig('minInterval', meta) if (isUsage || minInterval > 0) { - const maxUsage = this.getConfig('maxUsage', meta) + const maxUsage = isUsage ? this.getConfig('maxUsage', meta) : Infinity if (maxUsage < Infinity || minInterval > 0) { - const date = new Date() - const usage = this.updateUsage(user, date) - - if (minInterval > 0) { - const now = date.valueOf() - if (now - usage.last <= minInterval) { - if (this.config.showWarning) { - await meta.$send(messages.TOO_FREQUENT) - } - return - } - usage.last = now - } - - if (usage.count >= maxUsage && isUsage) { + const message = updateUsage(this.usageName, user, maxUsage, minInterval) + if (message) { if (this.config.showWarning) { - await meta.$send(messages.USAGE_EXHAUSTED) + await meta.$send(message) } return - } else { - usage.count++ } } } diff --git a/packages/koishi-core/src/utils.ts b/packages/koishi-core/src/utils.ts index d859a85f8f..3cc46c3314 100644 --- a/packages/koishi-core/src/utils.ts +++ b/packages/koishi-core/src/utils.ts @@ -1,6 +1,6 @@ import { isInteger } from 'koishi-utils' -import { UserField, GroupField } from './database' -import { NextFunction, Middleware } from './context' +import { UserField, GroupField, UserData } from './database' +import { NextFunction } from './context' import { Command } from './command' import { MessageMeta } from './meta' import { messages } from './messages' @@ -18,6 +18,38 @@ export function getTargetId (target: string | number) { return qq } +export function getUsage (name: string, user: UserData, time = new Date()) { + if (!user.usage[name]) { + user.usage[name] = {} + } + const usage = user.usage[name] + const date = time.toLocaleDateString() + if (date !== usage.date) { + usage.count = 0 + usage.date = date + } + return usage +} + +export function updateUsage (name: string, user: UserData, maxUsage: number, minInterval: number) { + const date = new Date() + const usage = getUsage(name, user, date) + + if (minInterval > 0) { + const now = date.valueOf() + if (now - usage.last <= minInterval) { + return messages.TOO_FREQUENT + } + usage.last = now + } + + if (usage.count >= maxUsage) { + return messages.USAGE_EXHAUSTED + } else { + usage.count++ + } +} + interface SuggestOptions { target: string items: string[] diff --git a/packages/plugin-common/src/help.ts b/packages/plugin-common/src/help.ts index 215cd2a14f..7ab2f71693 100644 --- a/packages/plugin-common/src/help.ts +++ b/packages/plugin-common/src/help.ts @@ -1,4 +1,4 @@ -import { Context, Command, UserData, MessageMeta } from 'koishi-core' +import { Context, Command, UserData, MessageMeta, getUsage } from 'koishi-core' export default function apply (ctx: Context) { ctx.command('help [command]', '显示帮助信息', { authority: 0 }) @@ -95,7 +95,7 @@ async function showCommandHelp (command: Command, meta: MessageMeta, options: an const minInterval = command.getConfig('minInterval', meta) if (meta.$user) { const { authority, maxUsageText } = command.config - const usage = command.updateUsage(meta.$user) + const usage = getUsage(command.usageName, meta.$user) if (maxUsage !== Infinity) { output.push(`已调用次数:${Math.min(usage.count, maxUsage)}/${maxUsageText || maxUsage}。`) } From c677df4f66e621cc96463da71e60112276d1ee8a Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 22 Jan 2020 01:32:27 +0800 Subject: [PATCH 10/21] chore: deprecate MessageMeta --- packages/koishi-core/src/app.ts | 10 +++++----- packages/koishi-core/src/command.ts | 8 ++++---- packages/koishi-core/src/context.ts | 8 ++++---- packages/koishi-core/src/meta.ts | 1 - packages/koishi-core/src/utils.ts | 6 +++--- packages/koishi-core/tests/runtime.spec.ts | 4 ++-- packages/plugin-common/src/help.ts | 12 ++++++------ packages/plugin-schedule/src/database.ts | 4 ++-- packages/plugin-schedule/src/index.ts | 4 ++-- 9 files changed, 28 insertions(+), 29 deletions(-) diff --git a/packages/koishi-core/src/app.ts b/packages/koishi-core/src/app.ts index 239a4a0b48..602c26a2c2 100644 --- a/packages/koishi-core/src/app.ts +++ b/packages/koishi-core/src/app.ts @@ -5,7 +5,7 @@ import { Command, ShortcutConfig, ParsedCommandLine } from './command' import { Context, Middleware, NextFunction, ContextScope, Events, EventMap } from './context' import { GroupFlag, UserFlag, UserField, createDatabase, DatabaseConfig, GroupField } from './database' import { showSuggestions } from './utils' -import { Meta, MessageMeta } from './meta' +import { Meta } from './meta' import { simplify, noop } from 'koishi-utils' import { errors, messages } from './messages' import { ParsedLine } from './parser' @@ -272,7 +272,7 @@ export class App extends Context { } } - private _preprocess = async (meta: MessageMeta, next: NextFunction) => { + private _preprocess = async (meta: Meta<'message'>, next: NextFunction) => { // strip prefix let capture: RegExpMatchArray let atMe = false, nickname = '', prefix: string = null @@ -379,7 +379,7 @@ export class App extends Context { }) } - parseCommandLine (message: string, meta: MessageMeta): ParsedCommandLine { + parseCommandLine (message: string, meta: Meta<'message'>): ParsedCommandLine { const name = message.split(/\s/, 1)[0].toLowerCase() const command = this._commandMap[name] if (command?.context.match(meta)) { @@ -388,14 +388,14 @@ export class App extends Context { } } - executeCommandLine (message: string, meta: MessageMeta, next: NextFunction = noop) { + executeCommandLine (message: string, meta: Meta<'message'>, next: NextFunction = noop) { if (!('$ctxType' in meta)) this.server.parseMeta(meta) const argv = this.parseCommandLine(message, meta) if (argv) return argv.command.execute(argv, next) return next() } - private _applyMiddlewares = async (meta: MessageMeta) => { + private _applyMiddlewares = async (meta: Meta<'message'>) => { // preparation const counter = this._middlewareCounter++ this._middlewareSet.add(counter) diff --git a/packages/koishi-core/src/command.ts b/packages/koishi-core/src/command.ts index d9d010fc0b..6f9c042750 100644 --- a/packages/koishi-core/src/command.ts +++ b/packages/koishi-core/src/command.ts @@ -2,7 +2,7 @@ import { Context, NextFunction } from './context' import { UserData, UserField, GroupField } from './database' import { messages, errors } from './messages' import { noop } from 'koishi-utils' -import { MessageMeta } from './meta' +import { Meta } from './meta' import { format } from 'util' import { updateUsage } from './utils' @@ -17,7 +17,7 @@ import { } from './parser' export interface ParsedCommandLine extends Partial { - meta: MessageMeta + meta: Meta<'message'> command?: Command next?: NextFunction } @@ -225,7 +225,7 @@ export class Command { return this } - getConfig (key: K, meta: MessageMeta): Exclude any> { + getConfig (key: K, meta: Meta<'message'>): Exclude any> { const value = this.config[key] as any return typeof value === 'function' ? value(meta.$user) : value } @@ -293,7 +293,7 @@ export class Command { } /** check authority and usage */ - private async _checkUser (meta: MessageMeta, options: Record) { + private async _checkUser (meta: Meta<'message'>, options: Record) { const user = meta.$user if (!user) return true let isUsage = true diff --git a/packages/koishi-core/src/context.ts b/packages/koishi-core/src/context.ts index 57dea803c5..8ca1be3f64 100644 --- a/packages/koishi-core/src/context.ts +++ b/packages/koishi-core/src/context.ts @@ -1,6 +1,6 @@ import { contain, union, intersection, difference } from 'koishi-utils' import { Command, CommandConfig, ParsedCommandLine } from './command' -import { MessageMeta, Meta, contextTypes } from './meta' +import { Meta, contextTypes } from './meta' import { EventEmitter } from 'events' import { Sender } from './sender' import { App } from './app' @@ -9,7 +9,7 @@ import { messages, errors } from './messages' import { format } from 'util' export type NextFunction = (next?: NextFunction) => any -export type Middleware = (meta: MessageMeta, next: NextFunction) => any +export type Middleware = (meta: Meta<'message'>, next: NextFunction) => any type PluginFunction = (ctx: T, options: U) => void type PluginObject = { name?: string, apply: PluginFunction } @@ -240,12 +240,12 @@ export class Context { return this.app._commandMap[name.slice(index + 1).toLowerCase()] } - getCommand (name: string, meta: MessageMeta) { + getCommand (name: string, meta: Meta<'message'>) { const command = this._getCommandByRawName(name) if (command?.context.match(meta) && !command.getConfig('disable', meta)) return command } - runCommand (name: string, meta: MessageMeta, args: string[] = [], options: Record = {}, rest = '') { + runCommand (name: string, meta: Meta<'message'>, args: string[] = [], options: Record = {}, rest = '') { const command = this._getCommandByRawName(name) if (!command || !command.context.match(meta) || command.getConfig('disable', meta)) { return meta.$send(messages.COMMAND_NOT_FOUND) diff --git a/packages/koishi-core/src/meta.ts b/packages/koishi-core/src/meta.ts index 11e0bd3a6d..df979e12fc 100644 --- a/packages/koishi-core/src/meta.ts +++ b/packages/koishi-core/src/meta.ts @@ -26,7 +26,6 @@ export enum contextTypes { discuss = 2, } -export type MessageMeta = Meta<'message'> export type ContextType = keyof typeof contextTypes export interface ResponsePayload { diff --git a/packages/koishi-core/src/utils.ts b/packages/koishi-core/src/utils.ts index 3cc46c3314..1a1da7258a 100644 --- a/packages/koishi-core/src/utils.ts +++ b/packages/koishi-core/src/utils.ts @@ -2,7 +2,7 @@ import { isInteger } from 'koishi-utils' import { UserField, GroupField, UserData } from './database' import { NextFunction } from './context' import { Command } from './command' -import { MessageMeta } from './meta' +import { Meta } from './meta' import { messages } from './messages' import { format } from 'util' import leven from 'leven' @@ -53,13 +53,13 @@ export function updateUsage (name: string, user: UserData, maxUsage: number, min interface SuggestOptions { target: string items: string[] - meta: MessageMeta + meta: Meta<'message'> next: NextFunction prefix: string suffix: string coefficient?: number command: Command | ((suggestion: string) => Command) - execute: (suggestion: string, meta: MessageMeta, next: NextFunction) => any + execute: (suggestion: string, meta: Meta<'message'>, next: NextFunction) => any } function findSimilar (target: string, coefficient: number) { diff --git a/packages/koishi-core/tests/runtime.spec.ts b/packages/koishi-core/tests/runtime.spec.ts index 91365fba5c..ea47d50695 100644 --- a/packages/koishi-core/tests/runtime.spec.ts +++ b/packages/koishi-core/tests/runtime.spec.ts @@ -1,5 +1,5 @@ import { MockedApp } from 'koishi-test-utils' -import { messages, MessageMeta } from 'koishi-core' +import { messages, Meta } from 'koishi-core' import { format } from 'util' const app = new MockedApp() @@ -132,7 +132,7 @@ describe('nickname prefix', () => { }) describe('Command Execution', () => { - const meta: MessageMeta = { + const meta: Meta<'message'> = { userId: 789, selfId: app.selfId, postType: 'message', diff --git a/packages/plugin-common/src/help.ts b/packages/plugin-common/src/help.ts index 7ab2f71693..1495a04270 100644 --- a/packages/plugin-common/src/help.ts +++ b/packages/plugin-common/src/help.ts @@ -1,4 +1,4 @@ -import { Context, Command, UserData, MessageMeta, getUsage } from 'koishi-core' +import { Context, Command, UserData, Meta, getUsage } from 'koishi-core' export default function apply (ctx: Context) { ctx.command('help [command]', '显示帮助信息', { authority: 0 }) @@ -27,7 +27,7 @@ function getShortcuts (command: Command, user: UserData) { }) } -function getCommands (context: Context, meta: MessageMeta, parent?: Command) { +function getCommands (context: Context, meta: Meta<'message'>, parent?: Command) { const commands = parent ? parent.children : context.app._commands.filter(cmd => cmd.context.match(meta)) @@ -36,13 +36,13 @@ function getCommands (context: Context, meta: MessageMeta, parent?: Command) { .sort((a, b) => a.name > b.name ? 1 : -1) } -function showGlobalShortcut (context: Context, meta: MessageMeta) { +function showGlobalShortcut (context: Context, meta: Meta<'message'>) { const commands = getCommands(context, meta) const shortcuts = [].concat(...commands.map(command => getShortcuts(command, meta.$user))) return meta.$send(`当前可用的全局指令有:${shortcuts.join(',')}。`) } -function getCommandList (context: Context, meta: MessageMeta, parent: Command, expand: boolean) { +function getCommandList (context: Context, meta: Meta<'message'>, parent: Command, expand: boolean) { let commands = getCommands(context, meta, parent) if (!expand) { commands = commands.filter(cmd => cmd.parent === parent) @@ -69,7 +69,7 @@ export const GLOBAL_HELP_EPILOGUE = [ '输入“帮助+指令名”查看特定指令的语法和使用示例。', ].join('\n') -function showGlobalHelp (context: Context, meta: MessageMeta, options: any) { +function showGlobalHelp (context: Context, meta: Meta<'message'>, options: any) { return meta.$send([ GLOBAL_HELP_PROLOGUE, ...getCommandList(context, meta, null, options.expand), @@ -77,7 +77,7 @@ function showGlobalHelp (context: Context, meta: MessageMeta, options: any) { ].join('\n')) } -async function showCommandHelp (command: Command, meta: MessageMeta, options: any) { +async function showCommandHelp (command: Command, meta: Meta<'message'>, options: any) { const output = [command.name + command.declaration, command.config.description] if (command.context.database) { meta.$user = await command.context.database.observeUser(meta.userId) diff --git a/packages/plugin-schedule/src/database.ts b/packages/plugin-schedule/src/database.ts index b3b9b917b4..6d2a228b76 100644 --- a/packages/plugin-schedule/src/database.ts +++ b/packages/plugin-schedule/src/database.ts @@ -1,4 +1,4 @@ -import { MessageMeta, getSelfIds, injectMethods } from 'koishi-core' +import { Meta, getSelfIds, injectMethods } from 'koishi-core' import { noop } from 'koishi-utils' import {} from 'koishi-database-mysql' import {} from 'koishi-database-level' @@ -15,7 +15,7 @@ declare module 'koishi-core/dist/database' { } interface ScheduleMethods { - createSchedule (time: number, assignee: number, interval: number, command: string, meta: MessageMeta): Promise + createSchedule (time: number, assignee: number, interval: number, command: string, meta: Meta<'message'>): Promise removeSchedule (id: number): Promise getSchedule (id: number): Promise getAllSchedules (assignees?: number[]): Promise diff --git a/packages/plugin-schedule/src/index.ts b/packages/plugin-schedule/src/index.ts index 745ff96f51..9840e0dc81 100644 --- a/packages/plugin-schedule/src/index.ts +++ b/packages/plugin-schedule/src/index.ts @@ -1,4 +1,4 @@ -import { Context, appMap, Database, CommandConfig, MessageMeta } from 'koishi-core' +import { Context, appMap, Database, CommandConfig, Meta } from 'koishi-core' import ms from 'ms' import './database' @@ -8,7 +8,7 @@ export interface Schedule { time: number interval: number command: string - meta: MessageMeta + meta: Meta<'message'> } function inspectSchedule ({ id, assignee, meta, interval, command, time }: Schedule) { From c3b0b5094a6776050fcabf4f0d51b7231225f6de Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 22 Jan 2020 11:15:50 +0800 Subject: [PATCH 11/21] feat(core): $stripped -> $parsed --- package.json | 1 + packages/koishi-core/src/app.ts | 2 +- packages/koishi-core/src/meta.ts | 4 ++-- packages/plugin-nlp/src/index.ts | 9 +++++---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 40ec8d1771..09d68bf180 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "build": "tsc -b", "build:extend": "tsc -b tsconfig.extend.json", + "build:dict": "ts-node bots/shiki/build/dict", "bump": "ts-node build/bump", "dep": "ts-node build/dep", "docs": "cd docs & yarn dev", diff --git a/packages/koishi-core/src/app.ts b/packages/koishi-core/src/app.ts index 602c26a2c2..582b780164 100644 --- a/packages/koishi-core/src/app.ts +++ b/packages/koishi-core/src/app.ts @@ -297,7 +297,7 @@ export class App extends Context { } // store parsed message - meta.$stripped = { atMe, nickname, prefix, message } + meta.$parsed = { atMe, nickname, prefix, message } // parse as command if (prefix !== null || nickname || meta.messageType === 'private') { diff --git a/packages/koishi-core/src/meta.ts b/packages/koishi-core/src/meta.ts index df979e12fc..3406b54cca 100644 --- a/packages/koishi-core/src/meta.ts +++ b/packages/koishi-core/src/meta.ts @@ -41,7 +41,7 @@ export interface ResponsePayload { reason?: string } -export interface StrippedMessage { +export interface ParsedMessage { atMe?: boolean nickname?: string prefix?: string @@ -59,7 +59,7 @@ export interface Meta { $ctxType?: ContextType // other properties - $stripped?: StrippedMessage + $parsed?: ParsedMessage // quick operations $response?: (payload: ResponsePayload) => void diff --git a/packages/plugin-nlp/src/index.ts b/packages/plugin-nlp/src/index.ts index f42f0e2416..41194b1088 100644 --- a/packages/plugin-nlp/src/index.ts +++ b/packages/plugin-nlp/src/index.ts @@ -1,5 +1,5 @@ import { Command, Meta, Context, ParsedLine, ParsedCommandLine } from 'koishi-core' -import { tag } from 'nodejieba' +import { tag, load } from 'nodejieba' import { resolve } from 'path' declare module 'koishi-core/dist/command' { @@ -10,8 +10,8 @@ declare module 'koishi-core/dist/command' { } declare module 'koishi-core/dist/meta' { - interface Meta { - $tags?: TagResult[] + interface ParsedMessage { + tags?: TagResult[] } } @@ -65,6 +65,7 @@ export function apply (ctx: Context, options: NlpConfig = {}) { options.idfDict = resolvePathConfig(options.idfDict) options.userDict = resolvePathConfig(options.userDict) options.stopWordDict = resolvePathConfig(options.stopWordDict) + load(options) ctx.middleware((meta, next) => { let max = options.threshold @@ -76,7 +77,7 @@ export function apply (ctx: Context, options: NlpConfig = {}) { if (!keyword) continue // attach word tags - if (!meta.$tags) meta.$tags = tag(meta.message) + if (!meta.$parsed.tags) meta.$parsed.tags = tag(meta.message) // generate intension const intension = callback(meta, keyword) From b6988a4aaa41612ed5407449c1faf3b469892efc Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 22 Jan 2020 12:02:29 +0800 Subject: [PATCH 12/21] chore: adjust test script --- build/jest.ts | 14 ++++++++++++++ jest.config.js | 2 +- package.json | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 build/jest.ts diff --git a/build/jest.ts b/build/jest.ts new file mode 100644 index 0000000000..f70691e1c2 --- /dev/null +++ b/build/jest.ts @@ -0,0 +1,14 @@ +import spawn from 'cross-spawn' + +const args = ['jest', '--coverage'] + +if (process.argv[2]) { + args.push(process.argv[2]) + if (process.argv[3]) { + args.push('--collectCoverageFrom', `**/${process.argv[3]}/**.ts`) + } +} else { + args.push('packages/.+\\.spec\\.ts') +} + +spawn('npx', args, { stdio: 'inherit' }) diff --git a/jest.config.js b/jest.config.js index 6121facd56..50ceaaeb1a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,7 +10,7 @@ module.exports = { testEnvironment: 'node', moduleFileExtensions: ['ts', 'js', 'json'], moduleNameMapper: { - "koishi-(test-utils|(database|plugin)-[\\w-]+)": "/packages/$1/src", + "koishi-(test-utils|(database|plugin)-[\\w-]+)(/dist)?": "/packages/$1/src", "koishi-[\\w-]+": "/packages/$0/src", }, coverageReporters: ['text', 'lcov'], diff --git a/package.json b/package.json index 09d68bf180..6ad970aace 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "bump": "ts-node build/bump", "dep": "ts-node build/dep", "docs": "cd docs & yarn dev", - "jest": "jest packages/.*\\.spec\\.ts --coverage", + "jest": "ts-node build/jest", "lint": "eslint packages/*/src/**/*.ts --cache", "pub": "ts-node build/publish", "pub:plugins": "yarn pub plugins", From da65008d5120a8859e7dd6a23c588a3378bf52c9 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 22 Jan 2020 19:12:39 +0800 Subject: [PATCH 13/21] chore: adjust test script --- build/jest.ts | 10 ++++++++-- package.json | 2 ++ packages/koishi-core/src/utils.ts | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/build/jest.ts b/build/jest.ts index f70691e1c2..8c5edd4b96 100644 --- a/build/jest.ts +++ b/build/jest.ts @@ -1,14 +1,20 @@ import spawn from 'cross-spawn' +import open from 'open' +import { resolve } from 'path' const args = ['jest', '--coverage'] if (process.argv[2]) { args.push(process.argv[2]) if (process.argv[3]) { - args.push('--collectCoverageFrom', `**/${process.argv[3]}/**.ts`) + args.push('--collectCoverageFrom', `**/${process.argv[3]}/**/*.ts`) } } else { args.push('packages/.+\\.spec\\.ts') } -spawn('npx', args, { stdio: 'inherit' }) +const child = spawn('npx', args, { stdio: 'inherit' }) + +child.on('close', () => { + open(resolve(__dirname, '../coverage/lcov-report/index.html')) +}) diff --git a/package.json b/package.json index 6ad970aace..37d53c14ff 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "build": "tsc -b", "build:extend": "tsc -b tsconfig.extend.json", + "build:shiki": "ts-node bots/shiki/build", "build:dict": "ts-node bots/shiki/build/dict", "bump": "ts-node build/bump", "dep": "ts-node build/dep", @@ -46,6 +47,7 @@ "jest": "^24.9.0", "kleur": "^3.0.3", "latest-version": "^5.1.0", + "open": "^7.0.0", "ora": "^4.0.3", "p-map": "^3.0.0", "prompts": "^2.3.0", diff --git a/packages/koishi-core/src/utils.ts b/packages/koishi-core/src/utils.ts index 1a1da7258a..6cb8ea9bd6 100644 --- a/packages/koishi-core/src/utils.ts +++ b/packages/koishi-core/src/utils.ts @@ -31,7 +31,7 @@ export function getUsage (name: string, user: UserData, time = new Date()) { return usage } -export function updateUsage (name: string, user: UserData, maxUsage: number, minInterval: number) { +export function updateUsage (name: string, user: UserData, maxUsage: number, minInterval?: number) { const date = new Date() const usage = getUsage(name, user, date) From a0fd83101cc601049ec15dfc2ced826110fb1909 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 22 Jan 2020 19:56:27 +0800 Subject: [PATCH 14/21] feat(test-utils): add receiver api --- packages/test-utils/src/app.ts | 42 ++++-- .../tests/__snapshots__/app.spec.ts.snap | 2 +- packages/test-utils/tests/app.spec.ts | 130 +++++++++++++++++- 3 files changed, 157 insertions(+), 17 deletions(-) diff --git a/packages/test-utils/src/app.ts b/packages/test-utils/src/app.ts index 57ff087b41..3d56f438fd 100644 --- a/packages/test-utils/src/app.ts +++ b/packages/test-utils/src/app.ts @@ -1,4 +1,4 @@ -import { AppOptions, App, Sender, Server, ContextType, Meta } from 'koishi-core' +import { AppOptions, App, Sender, Server, ContextType, Meta, FileInfo } from 'koishi-core' import { MockedServer, RequestParams, RequestData, RequestHandler } from './mocks' import { Session, createMessageMeta } from './session' import { BASE_SELF_ID } from './utils' @@ -49,23 +49,35 @@ export class MockedApp extends App { } receiveFriendRequest (userId: number, flag = 'flag') { - this.receive({ - postType: 'request', - requestType: 'friend', - userId, - flag, - }) + this.receive({ postType: 'request', requestType: 'friend', userId, flag }) } receiveGroupRequest (userId: number, subType: 'add' | 'invite', groupId = 10000, flag = 'flag') { - this.receive({ - postType: 'request', - requestType: 'group', - subType, - userId, - groupId, - flag, - }) + this.receive({ postType: 'request', requestType: 'group', subType, userId, groupId, flag }) + } + + receiveGroupUpload (file: FileInfo, userId: number, groupId = 10000) { + this.receive({ postType: 'notice', noticeType: 'group_upload', file, userId, groupId }) + } + + receiveGroupAdmin (subType: 'set' | 'unset', userId: number, groupId = 10000) { + this.receive({ postType: 'notice', noticeType: 'group_admin', subType, userId, groupId }) + } + + receiveGroupIncrease (subType: 'approve' | 'invite', userId: number, groupId = 10000, operatorId = 1000) { + this.receive({ postType: 'notice', noticeType: 'group_increase', subType, userId, groupId, operatorId }) + } + + receiveGroupDecrease (subType: 'leave' | 'kick' | 'kick_me', userId: number, groupId = 10000, operatorId = 1000) { + this.receive({ postType: 'notice', noticeType: 'group_decrease', subType, userId, groupId, operatorId }) + } + + receiveGroupBan (subType: 'ban' | 'lift_ban', duration: number, userId: number, groupId = 10000, operatorId = 1000) { + this.receive({ postType: 'notice', noticeType: 'group_ban', subType, userId, groupId, operatorId, duration }) + } + + receiveFriendAdd (userId: number) { + this.receive({ postType: 'notice', noticeType: 'friend_add', userId }) } receiveMessage (meta: Meta): Promise diff --git a/packages/test-utils/tests/__snapshots__/app.spec.ts.snap b/packages/test-utils/tests/__snapshots__/app.spec.ts.snap index 988f79ea53..62ddd5ac1f 100644 --- a/packages/test-utils/tests/__snapshots__/app.spec.ts.snap +++ b/packages/test-utils/tests/__snapshots__/app.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Mocked Server Implementation shouldMatchSnapshot 1`] = ` +exports[`Sender shouldMatchSnapshot 1`] = ` Array [ "send_private_msg", Object { diff --git a/packages/test-utils/tests/app.spec.ts b/packages/test-utils/tests/app.spec.ts index 952373db6c..149e4da367 100644 --- a/packages/test-utils/tests/app.spec.ts +++ b/packages/test-utils/tests/app.spec.ts @@ -6,7 +6,7 @@ beforeAll(() => app.start()) afterAll(() => app.stop()) -describe('Mocked Server Implementation', () => { +describe('Sender', () => { test('shouldHaveLastRequest', async () => { await app.sender.sendPrivateMsgAsync(123, 'foo') app.shouldHaveLastRequest('send_private_msg') @@ -45,3 +45,131 @@ describe('Mocked Server Implementation', () => { await expect(app.sender.sendPrivateMsg(123, 'foo')).resolves.toBe(321) }) }) + +describe('Receiver', () => { + test('receiveFriendRequest', async () => { + const mock = jest.fn() + app.receiver.on('request/friend', mock) + app.receiveFriendRequest(123) + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + $approve: expect.anything(), + $reject: expect.anything(), + postType: 'request', + requestType: 'friend', + userId: 123, + selfId: 514, + flag: 'flag', + }) + }) + + test('receiveGroupRequest', async () => { + const mock = jest.fn() + app.receiver.on('request/group/add', mock) + app.receiveGroupRequest(123, 'add') + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + $approve: expect.anything(), + $reject: expect.anything(), + postType: 'request', + requestType: 'group', + subType: 'add', + userId: 123, + selfId: 514, + groupId: 10000, + flag: 'flag', + }) + }) + + test('receiveGroupUpload', async () => { + const mock = jest.fn() + app.receiver.on('group-upload', mock) + app.receiveGroupUpload({} as any, 123) + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + postType: 'notice', + noticeType: 'group_upload', + userId: 123, + selfId: 514, + groupId: 10000, + file: {}, + }) + }) + + test('receiveGroupAdmin', async () => { + const mock = jest.fn() + app.receiver.on('group-admin/set', mock) + app.receiveGroupAdmin('set', 123) + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + postType: 'notice', + noticeType: 'group_admin', + subType: 'set', + userId: 123, + selfId: 514, + groupId: 10000, + }) + }) + + test('receiveGroupIncrease', async () => { + const mock = jest.fn() + app.receiver.on('group-increase/invite', mock) + app.receiveGroupIncrease('invite', 123) + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + postType: 'notice', + noticeType: 'group_increase', + subType: 'invite', + userId: 123, + selfId: 514, + groupId: 10000, + operatorId: 1000, + }) + }) + + test('receiveGroupDecrease', async () => { + const mock = jest.fn() + app.receiver.on('group-decrease/kick', mock) + app.receiveGroupDecrease('kick', 123) + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + postType: 'notice', + noticeType: 'group_decrease', + subType: 'kick', + userId: 123, + selfId: 514, + groupId: 10000, + operatorId: 1000, + }) + }) + + test('receiveGroupBan', async () => { + const mock = jest.fn() + app.receiver.on('group-ban/ban', mock) + app.receiveGroupBan('ban', 60, 123) + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + postType: 'notice', + noticeType: 'group_ban', + subType: 'ban', + userId: 123, + selfId: 514, + groupId: 10000, + operatorId: 1000, + duration: 60, + }) + }) + + test('receiveFriendAdd', async () => { + const mock = jest.fn() + app.receiver.on('friend-add', mock) + app.receiveFriendAdd(123) + expect(mock).toBeCalledTimes(1) + expect(mock).toBeCalledWith({ + postType: 'notice', + noticeType: 'friend_add', + userId: 123, + selfId: 514, + }) + }) +}) From 533f4defc2ddcfb3064ca9a5b4af88eb42a1ac7c Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Thu, 23 Jan 2020 16:33:48 +0800 Subject: [PATCH 15/21] feat(test-utils): experimental random mocks api --- packages/test-utils/src/index.ts | 10 ++++++ packages/test-utils/src/random.ts | 58 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 packages/test-utils/src/random.ts diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts index f5d7200cbb..2bb567f4c9 100644 --- a/packages/test-utils/src/index.ts +++ b/packages/test-utils/src/index.ts @@ -1,5 +1,15 @@ +import * as utils from './random' + +export { utils } + export * from './app' export * from './database' export * from './mocks' export * from './utils' export * from './server' + +jest.mock('koishi-utils', () => { + const utils1 = jest.requireActual('koishi-utils') + const utils2 = jest.requireActual('./random') + return { ...utils1, ...utils2 } +}) diff --git a/packages/test-utils/src/random.ts b/packages/test-utils/src/random.ts new file mode 100644 index 0000000000..937e3f331a --- /dev/null +++ b/packages/test-utils/src/random.ts @@ -0,0 +1,58 @@ +import * as utils from 'koishi-utils' + +const _utils = jest.requireActual('koishi-utils') as typeof utils + +type RealRandomPick = typeof utils.randomPick +interface MockedRandomPick extends RealRandomPick, jest.Mock { + mockIndex (index: number): this + mockIndexOnce (index: number): this +} + +type RealRandomSplice = typeof utils.randomSplice +interface MockedRandomSplice extends RealRandomSplice, jest.Mock { + mockIndex (index: number): this + mockIndexOnce (index: number): this +} + +type RealRandomMultiPick = typeof utils.randomMultiPick +interface MockedRandomMultiPick extends RealRandomMultiPick, jest.Mock { + mockIndices (...indices: number[]): this + mockIndicesOnce (...indices: number[]): this +} + +export const randomBool = jest.fn(_utils.randomBool) +export const randomId = jest.fn(_utils.randomId) +export const randomReal = jest.fn(_utils.randomReal) +export const randomInt = jest.fn(_utils.randomInt) +export const randomPick: MockedRandomPick = jest.fn(_utils.randomPick) as any +export const randomSplice: MockedRandomSplice = jest.fn(_utils.randomSplice) as any +export const randomMultiPick: MockedRandomMultiPick = jest.fn(_utils.randomMultiPick) as any +export const randomWeightedPick = jest.fn(_utils.randomWeightedPick) + +randomPick.mockIndex = (index) => { + return randomPick.mockImplementation(source => source[index]) +} + +randomPick.mockIndexOnce = (index) => { + return randomPick.mockImplementationOnce(source => source[index]) +} + +randomSplice.mockIndex = (index) => { + return randomSplice.mockImplementation(source => source.splice(index, 1)[0]) +} + +randomSplice.mockIndexOnce = (index) => { + return randomSplice.mockImplementationOnce(source => source.splice(index, 1)[0]) +} + +randomMultiPick.mockIndices = (...indices) => { + return randomMultiPick.mockImplementation((source) => { + return indices.map(index => source[index]) + }) +} + +randomMultiPick.mockIndicesOnce = (...indices) => { + return randomMultiPick.mockImplementationOnce((source) => { + return indices.map(index => source[index]) + }) +} From ed505b1c9413121f26dfb42a692706124a3e71c4 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Thu, 23 Jan 2020 16:34:23 +0800 Subject: [PATCH 16/21] feat(cli): optimize ecosystem module resolution --- packages/koishi-cli/src/worker.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/koishi-cli/src/worker.ts b/packages/koishi-cli/src/worker.ts index e51d52f9bd..3caf088143 100644 --- a/packages/koishi-cli/src/worker.ts +++ b/packages/koishi-cli/src/worker.ts @@ -28,7 +28,7 @@ const cwd = process.cwd() function loadEcosystem (type: string, name: string) { const modules = [resolve(cwd, name)] const prefix = `koishi-${type}-` - if (name.includes(prefix)) { + if (name.includes(prefix) || name.startsWith('.')) { modules.unshift(name) } else { const index = name.lastIndexOf('/') @@ -37,7 +37,11 @@ function loadEcosystem (type: string, name: string) { for (const name of modules) { try { return require(name) - } catch {} + } catch (error) { + if (error.code !== 'MODULE_NOT_FOUND') { + throw error + } + } } throw new Error(`cannot resolve ${type} ${name}`) } From da0be681e476e2682632e561217608d379aace60 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Thu, 23 Jan 2020 16:34:53 +0800 Subject: [PATCH 17/21] chore: optimize command execution --- packages/koishi-core/src/command.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/koishi-core/src/command.ts b/packages/koishi-core/src/command.ts index 6f9c042750..85f6232767 100644 --- a/packages/koishi-core/src/command.ts +++ b/packages/koishi-core/src/command.ts @@ -315,9 +315,9 @@ export class Command { } // check usage - const minInterval = this.getConfig('minInterval', meta) - if (isUsage || minInterval > 0) { - const maxUsage = isUsage ? this.getConfig('maxUsage', meta) : Infinity + if (isUsage) { + const minInterval = this.getConfig('minInterval', meta) + const maxUsage = this.getConfig('maxUsage', meta) if (maxUsage < Infinity || minInterval > 0) { const message = updateUsage(this.usageName, user, maxUsage, minInterval) From ba28276c89f0ab0806aed934381c1f2590f197a1 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Thu, 23 Jan 2020 16:35:12 +0800 Subject: [PATCH 18/21] chore: update module name mapper --- jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jest.config.js b/jest.config.js index 50ceaaeb1a..30fb465b88 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,6 +12,7 @@ module.exports = { moduleNameMapper: { "koishi-(test-utils|(database|plugin)-[\\w-]+)(/dist)?": "/packages/$1/src", "koishi-[\\w-]+": "/packages/$0/src", + "shiki-universe": "/bots/shiki/lib/shiki-universe/src", }, coverageReporters: ['text', 'lcov'], coveragePathIgnorePatterns: [ From 1c3cfeee3d3231d4904fadbc0d27da30a3d8f1d7 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Thu, 23 Jan 2020 16:59:38 +0800 Subject: [PATCH 19/21] feat(core): emit before-send event; sender.sendMsg() --- packages/koishi-core/src/meta.ts | 3 +- packages/koishi-core/src/sender.ts | 41 ++++++++++++++++++++--- packages/koishi-core/src/server.ts | 2 +- packages/koishi-core/tests/sender.spec.ts | 31 +++++++++++++++++ 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/packages/koishi-core/src/meta.ts b/packages/koishi-core/src/meta.ts index 3406b54cca..f3bbca3c44 100644 --- a/packages/koishi-core/src/meta.ts +++ b/packages/koishi-core/src/meta.ts @@ -1,9 +1,10 @@ import { User, Group } from './database' export type PostType = 'message' | 'notice' | 'request' | 'meta_event' | 'send' +export type MessageType = 'private' | 'group' | 'discuss' export interface MetaTypeMap { - message: 'private' | 'group' | 'discuss' + message: MessageType notice: 'group_upload' | 'group_admin' | 'group_increase' | 'group_decrease' | 'group_ban' | 'friend_add' request: 'friend' | 'group' // eslint-disable-next-line camelcase diff --git a/packages/koishi-core/src/sender.ts b/packages/koishi-core/src/sender.ts index 9ff762a90a..ccc6edb2c7 100644 --- a/packages/koishi-core/src/sender.ts +++ b/packages/koishi-core/src/sender.ts @@ -17,6 +17,7 @@ import { VipInfo, GroupNoticeInfo, ContextType, + MessageType, } from './meta' export class SenderError extends Error { @@ -102,8 +103,7 @@ export class Sender { } } - _createSendMeta ($ctxType: ContextType, $ctxId: number, message: string) { - const sendType = $ctxType === 'user' ? 'private' : $ctxType as any + _createSendMeta (sendType: MessageType, $ctxType: ContextType, $ctxId: number, message: string) { return { $ctxId, $ctxType, @@ -115,10 +115,35 @@ export class Sender { } as Meta<'send'> } + async sendMsg (type: MessageType, ctxId: number, message: string, autoEscape = false) { + this._assertElement('type', type, ['private', 'group', 'discuss']) + const ctxType = type === 'private' ? 'user' : type + const ctxIdKey = ctxType + 'Id' + this._assertInteger(ctxIdKey, ctxId) + if (!message) return + const meta = this._createSendMeta(type, ctxType, ctxId, message) + this.app.emitEvent(meta, 'before-send', meta) + const { messageId } = await this.get('send_msg', { [ctxIdKey]: ctxId, message, autoEscape }) + meta.messageId = messageId + this.app.emitEvent(meta, 'send', meta) + return messageId + } + + async sendMsgAsync (type: MessageType, ctxId: number, message: string, autoEscape = false) { + this._assertElement('type', type, ['private', 'group', 'discuss']) + const ctxType = type === 'private' ? 'user' : type + const ctxIdKey = ctxType + 'Id' + this._assertInteger(ctxIdKey, ctxId) + if (!message) return + const meta = this._createSendMeta(type, ctxType, ctxId, message) + this.app.emitEvent(meta, 'before-send', meta) + await this.get('send_msg_async', { [ctxIdKey]: ctxId, message, autoEscape }) + } + async sendGroupMsg (groupId: number, message: string, autoEscape = false) { this._assertInteger('groupId', groupId) if (!message) return - const meta = this._createSendMeta('group', groupId, message) + const meta = this._createSendMeta('group', 'group', groupId, message) this.app.emitEvent(meta, 'before-send', meta) const { messageId } = await this.get('send_group_msg', { groupId, message, autoEscape }) meta.messageId = messageId @@ -129,13 +154,15 @@ export class Sender { async sendGroupMsgAsync (groupId: number, message: string, autoEscape = false) { this._assertInteger('groupId', groupId) if (!message) return + const meta = this._createSendMeta('group', 'group', groupId, message) + this.app.emitEvent(meta, 'before-send', meta) await this.get('send_group_msg_async', { groupId, message, autoEscape }) } async sendDiscussMsg (discussId: number, message: string, autoEscape = false) { this._assertInteger('discussId', discussId) if (!message) return - const meta = this._createSendMeta('discuss', discussId, message) + const meta = this._createSendMeta('discuss', 'discuss', discussId, message) this.app.emitEvent(meta, 'before-send', meta) const { messageId } = await this.get('send_discuss_msg', { discussId, message, autoEscape }) meta.messageId = messageId @@ -146,13 +173,15 @@ export class Sender { async sendDiscussMsgAsync (discussId: number, message: string, autoEscape = false) { this._assertInteger('discussId', discussId) if (!message) return + const meta = this._createSendMeta('discuss', 'discuss', discussId, message) + this.app.emitEvent(meta, 'before-send', meta) await this.get('send_discuss_msg_async', { discussId, message, autoEscape }) } async sendPrivateMsg (userId: number, message: string, autoEscape = false) { this._assertInteger('userId', userId) if (!message) return - const meta = this._createSendMeta('user', userId, message) + const meta = this._createSendMeta('private', 'user', userId, message) this.app.emitEvent(meta, 'before-send', meta) const { messageId } = await this.get('send_private_msg', { userId, message, autoEscape }) meta.messageId = messageId @@ -163,6 +192,8 @@ export class Sender { async sendPrivateMsgAsync (userId: number, message: string, autoEscape = false) { this._assertInteger('userId', userId) if (!message) return + const meta = this._createSendMeta('private', 'user', userId, message) + this.app.emitEvent(meta, 'before-send', meta) await this.get('send_private_msg_async', { userId, message, autoEscape }) } diff --git a/packages/koishi-core/src/server.ts b/packages/koishi-core/src/server.ts index d3e63fe649..96d504eb58 100644 --- a/packages/koishi-core/src/server.ts +++ b/packages/koishi-core/src/server.ts @@ -124,7 +124,7 @@ export abstract class Server { } meta.$send = async (message, autoEscape = false) => { if (meta.$response) { - app.emitEvent(meta, 'before-send', app.sender._createSendMeta(ctxType, ctxId, message)) + app.emitEvent(meta, 'before-send', app.sender._createSendMeta(meta.messageType, ctxType, ctxId, message)) return meta.$response({ reply: message, autoEscape, atSender: false }) } return app.sender[`send${capitalize(meta.messageType)}MsgAsync`](ctxId, message, autoEscape) diff --git a/packages/koishi-core/tests/sender.spec.ts b/packages/koishi-core/tests/sender.spec.ts index 5f83b66210..7a4e2f999f 100644 --- a/packages/koishi-core/tests/sender.spec.ts +++ b/packages/koishi-core/tests/sender.spec.ts @@ -53,6 +53,37 @@ describe('Sender API', () => { const messageId = 456 + test('sendMsg', async () => { + await expect(sender.sendMsg(undefined, undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: type') + await expect(sender.sendMsg('foo' as any, undefined, undefined)).rejects.toHaveProperty('message', 'invalid argument: type') + await expect(sender.sendMsg('private', undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: userId') + await expect(sender.sendMsg('group', undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: groupId') + await expect(sender.sendMsg('discuss', undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: discussId') + await expect(sender.sendMsgAsync(undefined, undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: type') + await expect(sender.sendMsgAsync('foo' as any, undefined, undefined)).rejects.toHaveProperty('message', 'invalid argument: type') + await expect(sender.sendMsgAsync('private', undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: userId') + await expect(sender.sendMsgAsync('group', undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: groupId') + await expect(sender.sendMsgAsync('discuss', undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: discussId') + + server.setResponse('send_msg', { messageId }) + await expect(sender.sendMsg('group', 123, '')).resolves.toBeUndefined() + server.shouldHaveNoRequests() + await expect(sender.sendMsg('group', 123, 'foo')).resolves.toBe(messageId) + server.shouldHaveLastRequest('send_msg', { groupId: '123', message: 'foo' }) + await expect(sender.sendMsg('private', 123, 'foo')).resolves.toBe(messageId) + server.shouldHaveLastRequest('send_msg', { userId: '123', message: 'foo' }) + await expect(sender.sendMsg('private', 123, 'foo', true)).resolves.toBe(messageId) + server.shouldHaveLastRequest('send_msg', { userId: '123', message: 'foo', autoEscape: 'true' }) + await expect(sender.sendMsgAsync('group', 123, '')).resolves.toBeUndefined() + server.shouldHaveNoRequests() + await expect(sender.sendMsgAsync('group', 123, 'foo')).resolves.toBeUndefined() + server.shouldHaveLastRequest('send_msg_async', { groupId: '123', message: 'foo' }) + await expect(sender.sendMsgAsync('private', 123, 'foo')).resolves.toBeUndefined() + server.shouldHaveLastRequest('send_msg_async', { userId: '123', message: 'foo' }) + await expect(sender.sendMsgAsync('private', 123, 'foo', true)).resolves.toBeUndefined() + server.shouldHaveLastRequest('send_msg_async', { userId: '123', message: 'foo', autoEscape: 'true' }) + }) + test('sendGroupMsg', async () => { await expect(sender.sendGroupMsg(undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: groupId') await expect(sender.sendGroupMsgAsync(undefined, undefined)).rejects.toHaveProperty('message', 'missing argument: groupId') From c7ea1d1bebe0a6644f22c0bf8e16a13fc4ca380c Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Thu, 23 Jan 2020 17:01:39 +0800 Subject: [PATCH 20/21] chore: bump versions --- packages/database-level/package.json | 6 +++--- packages/database-memory/package.json | 4 ++-- packages/database-mysql/package.json | 4 ++-- packages/database-sqlite/package.json | 6 +++--- packages/koishi-cli/package.json | 8 ++++---- packages/koishi-core/package.json | 6 +++--- packages/plugin-common/package.json | 8 ++++---- packages/plugin-nlp/package.json | 6 +++--- packages/plugin-recorder/package.json | 6 +++--- packages/plugin-schedule/package.json | 10 +++++----- packages/plugin-teach/package.json | 10 +++++----- packages/test-utils/package.json | 4 ++-- 12 files changed, 39 insertions(+), 39 deletions(-) diff --git a/packages/database-level/package.json b/packages/database-level/package.json index 9884065354..eae6e3789a 100644 --- a/packages/database-level/package.json +++ b/packages/database-level/package.json @@ -1,7 +1,7 @@ { "name": "koishi-database-level", "description": "Leveldb support for Koishi", - "version": "1.0.7", + "version": "1.0.8", "main": "dist/index.js", "files": [ "dist" @@ -33,12 +33,12 @@ "leveldb" ], "devDependencies": { - "koishi-test-utils": "^2.0.0" + "koishi-test-utils": "^2.1.0" }, "dependencies": { "@types/leveldown": "^4.0.2", "@types/levelup": "^4.3.0", - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2", "leveldown": "^5.4.1", "levelup": "^4.3.2", diff --git a/packages/database-memory/package.json b/packages/database-memory/package.json index e533a24636..4d18468a3f 100644 --- a/packages/database-memory/package.json +++ b/packages/database-memory/package.json @@ -1,7 +1,7 @@ { "name": "koishi-database-memory", "description": "An in-memory database implementation for Koishi", - "version": "1.0.0", + "version": "1.0.1", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -22,7 +22,7 @@ }, "homepage": "https://github.com/koishijs/koishi/tree/master/packages/database-memory#readme", "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2" } } diff --git a/packages/database-mysql/package.json b/packages/database-mysql/package.json index eafbf5bc95..e342334620 100644 --- a/packages/database-mysql/package.json +++ b/packages/database-mysql/package.json @@ -1,7 +1,7 @@ { "name": "koishi-database-mysql", "description": "MySQL support for Koishi", - "version": "1.0.7", + "version": "1.0.8", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -35,7 +35,7 @@ "@types/mysql": "^2.15.8" }, "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2", "mysql": "^2.17.1" } diff --git a/packages/database-sqlite/package.json b/packages/database-sqlite/package.json index 6d60ba4119..1cf28b4666 100644 --- a/packages/database-sqlite/package.json +++ b/packages/database-sqlite/package.json @@ -1,6 +1,6 @@ { "name": "koishi-database-sqlite", - "version": "1.0.0-alpha.3", + "version": "1.0.0-alpha.4", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -22,10 +22,10 @@ "homepage": "https://github.com/koishijs/koishi/tree/master/packages/database-sqlite#readme", "devDependencies": { "@types/sqlite3": "^3.1.6", - "koishi-test-utils": "^2.0.0" + "koishi-test-utils": "^2.1.0" }, "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2", "sqlite3": "^4.1.1" } diff --git a/packages/koishi-cli/package.json b/packages/koishi-cli/package.json index e125793ba2..5fa620f31e 100644 --- a/packages/koishi-cli/package.json +++ b/packages/koishi-cli/package.json @@ -1,7 +1,7 @@ { "name": "koishi", "description": "A QQ bot framework based on CQHTTP", - "version": "1.5.0", + "version": "1.6.0", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -40,9 +40,9 @@ "cac": "^6.5.5", "js-yaml": "^3.13.1", "kleur": "^3.0.3", - "koishi-core": "^1.5.0", - "koishi-plugin-common": "^2.0.0", - "koishi-plugin-schedule": "^1.0.3", + "koishi-core": "^1.6.0", + "koishi-plugin-common": "^2.0.1", + "koishi-plugin-schedule": "^1.0.4", "koishi-utils": "^1.0.2", "prompts": "^2.3.0" } diff --git a/packages/koishi-core/package.json b/packages/koishi-core/package.json index ed7c417e37..9a4373a117 100644 --- a/packages/koishi-core/package.json +++ b/packages/koishi-core/package.json @@ -1,7 +1,7 @@ { "name": "koishi-core", "description": "Core features for Koishi", - "version": "1.5.0", + "version": "1.6.0", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -35,8 +35,8 @@ "@types/debug": "^4.1.5", "@types/ws": "^7.2.0", "get-port": "^5.1.1", - "koishi-database-memory": "^1.0.0", - "koishi-test-utils": "^2.0.0" + "koishi-database-memory": "^1.0.1", + "koishi-test-utils": "^2.1.0" }, "dependencies": { "axios": "^0.19.1", diff --git a/packages/plugin-common/package.json b/packages/plugin-common/package.json index d4f2a7fa2a..910c052ba8 100644 --- a/packages/plugin-common/package.json +++ b/packages/plugin-common/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-common", "description": "Common plugins for Koishi", - "version": "2.0.0", + "version": "2.0.1", "main": "dist/index.js", "typings": "dist/index.d.ts", "author": "Shigma <1700011071@pku.edu.cn>", @@ -28,11 +28,11 @@ "plugin" ], "devDependencies": { - "koishi-database-memory": "^1.0.0", - "koishi-test-utils": "^2.0.0" + "koishi-database-memory": "^1.0.1", + "koishi-test-utils": "^2.1.0" }, "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2" } } diff --git a/packages/plugin-nlp/package.json b/packages/plugin-nlp/package.json index 0f684fe8fe..2b1eb0d20d 100644 --- a/packages/plugin-nlp/package.json +++ b/packages/plugin-nlp/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-nlp", "description": "Natural Language Processor for Koishi", - "version": "1.0.0-alpha.1", + "version": "1.0.0-alpha.2", "main": "dist/index.js", "typings": "dist/index.d.ts", "author": "Shigma <1700011071@pku.edu.cn>", @@ -30,10 +30,10 @@ "jieba" ], "devDependencies": { - "koishi-test-utils": "^1.2.2" + "koishi-test-utils": "^2.1.0" }, "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2", "nodejieba": "^2.4.0" } diff --git a/packages/plugin-recorder/package.json b/packages/plugin-recorder/package.json index e07fe51140..b88d184b31 100644 --- a/packages/plugin-recorder/package.json +++ b/packages/plugin-recorder/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-recorder", "description": "Save Chat Records for Koishi", - "version": "1.0.0-alpha.1", + "version": "1.0.0-alpha.2", "main": "dist/index.js", "typings": "dist/index.d.ts", "author": "Shigma <1700011071@pku.edu.cn>", @@ -31,10 +31,10 @@ ], "devDependencies": { "del": "^5.1.0", - "koishi-test-utils": "^1.2.2" + "koishi-test-utils": "^2.1.0" }, "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2" } } diff --git a/packages/plugin-schedule/package.json b/packages/plugin-schedule/package.json index 28705b2a22..6c25e9d52b 100644 --- a/packages/plugin-schedule/package.json +++ b/packages/plugin-schedule/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-schedule", "description": "Schedule plugin for Koishi", - "version": "1.0.3", + "version": "1.0.4", "main": "dist/index.js", "typings": "dist/index.d.ts", "author": "Shigma <1700011071@pku.edu.cn>", @@ -31,12 +31,12 @@ ], "devDependencies": { "@types/ms": "^0.7.31", - "koishi-database-level": "^1.0.7", - "koishi-database-mysql": "^1.0.7", - "koishi-test-utils": "^2.0.0" + "koishi-database-level": "^1.0.8", + "koishi-database-mysql": "^1.0.8", + "koishi-test-utils": "^2.1.0" }, "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2", "ms": "^2.1.2" } diff --git a/packages/plugin-teach/package.json b/packages/plugin-teach/package.json index 4940f6eedd..f5989531ad 100644 --- a/packages/plugin-teach/package.json +++ b/packages/plugin-teach/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-teach", "description": "Teach plugin for Koishi", - "version": "0.1.12", + "version": "0.1.13", "main": "dist/index.js", "typings": "dist/index.d.ts", "author": "Shigma <1700011071@pku.edu.cn>", @@ -31,12 +31,12 @@ "conversation" ], "devDependencies": { - "koishi-database-level": "^1.0.7", - "koishi-database-mysql": "^1.0.7", - "koishi-test-utils": "^2.0.0" + "koishi-database-level": "^1.0.8", + "koishi-database-mysql": "^1.0.8", + "koishi-test-utils": "^2.1.0" }, "dependencies": { - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 487a6fa538..76988306e4 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "koishi-test-utils", "description": "Test utilities for Koishi", - "version": "2.0.0", + "version": "2.1.0", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -43,7 +43,7 @@ "axios": "^0.19.1", "debug": "^4.1.1", "get-port": "^5.1.1", - "koishi-core": "^1.5.0", + "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2" } } From 6889e5ded06996b5e3e0cebbeaf0a8200937c5a7 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Thu, 23 Jan 2020 17:26:21 +0800 Subject: [PATCH 21/21] chore: bump dependencies (mysql: 2.17.1->2.18.0, axios: 0.19.1->0.19.2) --- build/dep.ts | 3 ++- package.json | 6 +++--- packages/database-mysql/package.json | 2 +- packages/koishi-core/package.json | 2 +- packages/test-utils/package.json | 2 +- packages/test-utils/src/index.ts | 1 + 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/build/dep.ts b/build/dep.ts index f9dee0f8e7..ee5e0ca2c5 100644 --- a/build/dep.ts +++ b/build/dep.ts @@ -38,7 +38,8 @@ interface Dependency { for (const type of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'] as DependencyType[]) { for (const dep in meta[type] || {}) { - if (workspaces[dep]) continue + // skip workspaces and symlinks + if (workspaces[dep] || meta[type][dep].startsWith('file:')) continue if (!dependencies[dep]) dependencies[dep] = { dependents: [] } dependencies[dep].dependents.push({ name, type }) } diff --git a/package.json b/package.json index 37d53c14ff..8698ca923b 100644 --- a/package.json +++ b/package.json @@ -23,14 +23,14 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@octokit/rest": "^16.37.0", + "@octokit/rest": "^16.38.1", "@types/cross-spawn": "^6.0.1", "@types/fs-extra": "^8.0.1", "@types/jest": "^24.9.0", "@types/node": "^13.1.8", "@types/semver": "^6.2.0", - "@typescript-eslint/eslint-plugin": "^2.16.0", - "@typescript-eslint/parser": "^2.16.0", + "@typescript-eslint/eslint-plugin": "^2.17.0", + "@typescript-eslint/parser": "^2.17.0", "cac": "^6.5.5", "cross-spawn": "^7.0.1", "del": "^5.1.0", diff --git a/packages/database-mysql/package.json b/packages/database-mysql/package.json index e342334620..f172b0acbf 100644 --- a/packages/database-mysql/package.json +++ b/packages/database-mysql/package.json @@ -37,6 +37,6 @@ "dependencies": { "koishi-core": "^1.6.0", "koishi-utils": "^1.0.2", - "mysql": "^2.17.1" + "mysql": "^2.18.0" } } diff --git a/packages/koishi-core/package.json b/packages/koishi-core/package.json index 9a4373a117..c113b6524c 100644 --- a/packages/koishi-core/package.json +++ b/packages/koishi-core/package.json @@ -39,7 +39,7 @@ "koishi-test-utils": "^2.1.0" }, "dependencies": { - "axios": "^0.19.1", + "axios": "^0.19.2", "escape-string-regexp": "^2.0.0", "koishi-utils": "^1.0.2", "leven": "^3.1.0", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 76988306e4..d601e65e8f 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -40,7 +40,7 @@ "@types/jest": "^24.9.0" }, "dependencies": { - "axios": "^0.19.1", + "axios": "^0.19.2", "debug": "^4.1.1", "get-port": "^5.1.1", "koishi-core": "^1.6.0", diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts index 2bb567f4c9..f25e976072 100644 --- a/packages/test-utils/src/index.ts +++ b/packages/test-utils/src/index.ts @@ -7,6 +7,7 @@ export * from './database' export * from './mocks' export * from './utils' export * from './server' +export * from './session' jest.mock('koishi-utils', () => { const utils1 = jest.requireActual('koishi-utils')