diff --git a/README.md b/README.md index e1e5a590b6..02ad1fc05a 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ Koishi 在开发时借助了下面的工具: | 特性 | [koishi
1.3.0](https://www.npmjs.com/package/koishi/v/1.3.0) | [cqhttp
1.2.0](https://www.npmjs.com/package/cqhttp/v/1.2.0) | [cq-websocket
2.0.2](https://www.npmjs.com/package/cq-websocket/v/2.0.2) | [lemon-bot
0.6.0](https://www.npmjs.com/package/lemon-bot/v/0.6.0) | [@ionjs/core
0.6.5](https://www.npmjs.com/package/@ionjs/core/v/0.6.5) | |:--:|:--:|:--:|:--:|:--:|:--:| -| 依赖数量 | [22](http://npm.anvaka.com/#/view/2d/koishi/1.3.0) / [10](http://npm.anvaka.com/#/view/2d/koishi-core/1.3.0) | [62](http://npm.anvaka.com/#/view/2d/cqhttp/1.1.1) | [37](http://npm.anvaka.com/#/view/2d/cq-websocket/2.0.2) | [65](http://npm.anvaka.com/#/view/2d/lemon-bot/0.6.0) | [73](http://npm.anvaka.com/#/view/2d/%2540ionjs%252Fcore/0.6.5) | +| 依赖数量 | [10](http://npm.anvaka.com/#/view/2d/koishi-core/1.3.0) / [21](http://npm.anvaka.com/#/view/2d/koishi/1.3.0) | [62](http://npm.anvaka.com/#/view/2d/cqhttp/1.1.1) | [37](http://npm.anvaka.com/#/view/2d/cq-websocket/2.0.2) | [65](http://npm.anvaka.com/#/view/2d/lemon-bot/0.6.0) | [73](http://npm.anvaka.com/#/view/2d/%2540ionjs%252Fcore/0.6.5) | +| 安装体积 (MB) | [0.78](https://packagephobia.now.sh/result?p=koishi-core@1.3.0) / [2.14](https://packagephobia.now.sh/result?p=koishi@1.3.0) | [1.86](https://packagephobia.now.sh/result?p=cqhttp@1.1.1) | [2.82](https://packagephobia.now.sh/result?p=cq-websocket) | [2.56](https://packagephobia.now.sh/result?p=lemon-bot) | [2.32](https://packagephobia.now.sh/result?p=@ionjs/core) | | HTTP | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | | WebSocket | ✔️ | ❌ | ✔️ | ❌ | ❌ | | 反向 WebSocket | ❌ | ❌ | ❌ | ❌ | ❌ | @@ -78,7 +79,7 @@ Koishi 在开发时借助了下面的工具: | 命令行 | ✔️ | ❌ | ❌ | ❌ | ❌ | | 指令 | ✔️ | ❌ | ❌ | ✔️ | ❌ | -注:依赖数量如果表示为 X/Y,则 X 表示命令行工具依赖数量, Y 表示核心库依赖数量。 +注:依赖数量如果表示为 X/Y,则 X 表示核心库依赖数量,Y 表示命令行工具依赖数量。 ## 安装 diff --git a/build/bump.ts b/build/bump.ts index cb5d965d01..b95eb3d871 100644 --- a/build/bump.ts +++ b/build/bump.ts @@ -89,6 +89,7 @@ function getPackage (name: string) { || packages[`packages/koishi-${name}`] || packages[`packages/database-${name}`] || packages[`packages/plugin-${name}`] + || packages[`plugins/plugin-${name}`] } function each (callback: (pkg: Package, name: string) => T) { diff --git a/build/start.ts b/build/start.ts index 9ebfb9907f..3f4fcf1a28 100644 --- a/build/start.ts +++ b/build/start.ts @@ -4,7 +4,7 @@ import spawn from 'cross-spawn' const name = process.argv[2] if (!name) process.exit(0) -spawn('npx', ['koishi', 'run', ...process.argv.slice(3)], { +spawn('npx', ['koishi', 'run', ...process.argv.slice(3), '--', '-r', 'ts-node/register'], { cwd: resolve(__dirname, '../bots', name), stdio: 'inherit', }) diff --git a/packages/database-level/package.json b/packages/database-level/package.json index 3c75971e64..75f21d27e6 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.4", + "version": "1.0.5", "main": "dist/index.js", "files": [ "dist" @@ -33,12 +33,12 @@ "leveldb" ], "devDependencies": { - "koishi-test-utils": "^1.0.3" + "koishi-test-utils": "^1.2.1" }, "dependencies": { "@types/leveldown": "^4.0.2", "@types/levelup": "^4.3.0", - "koishi-core": "^1.3.0", + "koishi-core": "^1.3.1", "koishi-utils": "^1.0.2", "leveldown": "^5.4.1", "levelup": "^4.3.2", diff --git a/packages/database-mysql/package.json b/packages/database-mysql/package.json index 8b36638b6d..74e1334290 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.4", + "version": "1.0.5", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -35,7 +35,7 @@ "@types/mysql": "^2.15.8" }, "dependencies": { - "koishi-core": "^1.3.0", + "koishi-core": "^1.3.1", "koishi-utils": "^1.0.2", "mysql": "^2.17.1" } diff --git a/packages/database-sqlite/package.json b/packages/database-sqlite/package.json index 3ca76136c2..0ab3624a36 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.0", + "version": "1.0.0-alpha.1", "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": "^1.1.0" + "koishi-test-utils": "^1.2.1" }, "dependencies": { - "koishi-core": "^1.3.0", + "koishi-core": "^1.3.1", "koishi-utils": "^1.0.2", "sqlite3": "^4.1.1" } diff --git a/packages/database-sqlite/src/database.ts b/packages/database-sqlite/src/database.ts index 1da608af41..22fc4dc780 100644 --- a/packages/database-sqlite/src/database.ts +++ b/packages/database-sqlite/src/database.ts @@ -8,7 +8,7 @@ declare module 'koishi-core/dist/database' { } interface DatabaseConfig { - sqlite: SqliteConfig + sqlite?: SqliteConfig } } diff --git a/packages/koishi-cli/README.md b/packages/koishi-cli/README.md index 482b85d8a1..fd3e344048 100644 --- a/packages/koishi-cli/README.md +++ b/packages/koishi-cli/README.md @@ -61,7 +61,8 @@ Koishi 在开发时借助了下面的工具: | 特性 | [koishi
1.3.0](https://www.npmjs.com/package/koishi/v/1.3.0) | [cqhttp
1.2.0](https://www.npmjs.com/package/cqhttp/v/1.2.0) | [cq-websocket
2.0.2](https://www.npmjs.com/package/cq-websocket/v/2.0.2) | [lemon-bot
0.6.0](https://www.npmjs.com/package/lemon-bot/v/0.6.0) | [@ionjs/core
0.6.5](https://www.npmjs.com/package/@ionjs/core/v/0.6.5) | |:--:|:--:|:--:|:--:|:--:|:--:| -| 依赖数量 | [22](http://npm.anvaka.com/#/view/2d/koishi/1.3.0) / [10](http://npm.anvaka.com/#/view/2d/koishi-core/1.3.0) | [62](http://npm.anvaka.com/#/view/2d/cqhttp/1.1.1) | [37](http://npm.anvaka.com/#/view/2d/cq-websocket/2.0.2) | [65](http://npm.anvaka.com/#/view/2d/lemon-bot/0.6.0) | [73](http://npm.anvaka.com/#/view/2d/%2540ionjs%252Fcore/0.6.5) | +| 依赖数量 | [10](http://npm.anvaka.com/#/view/2d/koishi-core/1.3.0) / [21](http://npm.anvaka.com/#/view/2d/koishi/1.3.0) | [62](http://npm.anvaka.com/#/view/2d/cqhttp/1.1.1) | [37](http://npm.anvaka.com/#/view/2d/cq-websocket/2.0.2) | [65](http://npm.anvaka.com/#/view/2d/lemon-bot/0.6.0) | [73](http://npm.anvaka.com/#/view/2d/%2540ionjs%252Fcore/0.6.5) | +| 安装体积 (MB) | [0.78](https://packagephobia.now.sh/result?p=koishi-core@1.3.0) / [2.14](https://packagephobia.now.sh/result?p=koishi@1.3.0) | [1.86](https://packagephobia.now.sh/result?p=cqhttp@1.1.1) | [2.82](https://packagephobia.now.sh/result?p=cq-websocket) | [2.56](https://packagephobia.now.sh/result?p=lemon-bot) | [2.32](https://packagephobia.now.sh/result?p=@ionjs/core) | | HTTP | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | | WebSocket | ✔️ | ❌ | ✔️ | ❌ | ❌ | | 反向 WebSocket | ❌ | ❌ | ❌ | ❌ | ❌ | @@ -71,7 +72,7 @@ Koishi 在开发时借助了下面的工具: | 命令行 | ✔️ | ❌ | ❌ | ❌ | ❌ | | 指令 | ✔️ | ❌ | ❌ | ✔️ | ❌ | -注:依赖数量如果表示为 X/Y,则 X 表示命令行工具依赖数量, Y 表示核心库依赖数量。 +注:依赖数量如果表示为 X/Y,则 X 表示核心库依赖数量,Y 表示命令行工具依赖数量。 ## 安装 diff --git a/packages/koishi-cli/package.json b/packages/koishi-cli/package.json index 8fd4ca0ef9..b483f295dc 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.3.0", + "version": "1.3.1", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -40,9 +40,9 @@ "cac": "^6.5.4", "js-yaml": "^3.13.1", "kleur": "^3.0.3", - "koishi-core": "^1.3.0", - "koishi-plugin-common": "^1.0.4", - "koishi-plugin-schedule": "^1.0.0", + "koishi-core": "^1.3.1", + "koishi-plugin-common": "^1.0.5", + "koishi-plugin-schedule": "^1.0.1", "koishi-utils": "^1.0.2", "prompts": "^2.3.0" } diff --git a/packages/koishi-cli/src/run.ts b/packages/koishi-cli/src/run.ts index 79349c7b7e..1a35de07c2 100644 --- a/packages/koishi-cli/src/run.ts +++ b/packages/koishi-cli/src/run.ts @@ -7,8 +7,14 @@ import CAC from 'cac/types/CAC' process.env.KOISHI_START_TIME = '' + performance.now() -function createWorker () { - const child = fork(resolve(__dirname, 'worker')) +interface WorkerOptions { + '--'?: string[] +} + +function createWorker (options: WorkerOptions) { + const child = fork(resolve(__dirname, 'worker'), [], { + execArgv: options['--'], + }) let started = false child.on('message', (data: any) => { @@ -28,20 +34,20 @@ function createWorker () { } else { logger.warn('an error was encounted. restarting...') } - createWorker() + createWorker(options) }) } export default function (cli: CAC) { cli.command('run [file]', 'start a koishi bot') .alias('start') - .option('--log-level ', 'specify log level (default: 3)') + .option('--log-level ', 'specify log level (default: 2)') .option('--silent', 'use log level 0 (print no message)') - .option('--debug', 'use log level 4 (print all messages)') + .option('--debug', 'use log level 3 (print all messages)') .action((file, options) => { let logLevel = options.logLevel if (options.silent) logLevel = 0 - if (options.debug) logLevel = 4 + if (options.debug) logLevel = 3 if (logLevel !== undefined) { if (!isInteger(logLevel) || logLevel < 0) { logger.error('log level should be a positive integer.') @@ -50,6 +56,6 @@ export default function (cli: CAC) { process.env.KOISHI_LOG_LEVEL = '' + logLevel } process.env.KOISHI_CONFIG_FILE = file || '' - createWorker() + createWorker(options) }) } diff --git a/packages/koishi-cli/src/utils.ts b/packages/koishi-cli/src/utils.ts index a9388838b8..eea24a1206 100644 --- a/packages/koishi-cli/src/utils.ts +++ b/packages/koishi-cli/src/utils.ts @@ -2,7 +2,7 @@ import kleur from 'kleur' export namespace logger { export function info (message: string, logLevel?: number) { - if (logLevel < 3) return + if (logLevel < 2) return console.log(`${kleur.blue('info')}`, message) } @@ -22,7 +22,7 @@ export namespace logger { } export function debug (message: string, logLevel?: number) { - if (logLevel < 4) return + if (logLevel < 3) return console.log(`${kleur.magenta('debug')}`, message) } } diff --git a/packages/koishi-cli/src/worker.ts b/packages/koishi-cli/src/worker.ts index 7b3503af27..662933cfa7 100644 --- a/packages/koishi-cli/src/worker.ts +++ b/packages/koishi-cli/src/worker.ts @@ -136,13 +136,11 @@ process.on('unhandledRejection', (error) => { }) appList.forEach((app) => { - const { logLevel = 0, logFilter = {} } = app.options as AppConfig + const { logLevel = 2, logFilter = {} } = app.options as AppConfig - for (const type of logTypes) { - app.receiver.on(`logger/${type}` as LogEvents, (scope, message) => { - logger[type](message, Math.min(logFilter[scope] ?? logLevel, baseLogLevel)) - }) - } + app.receiver.on('logger', (scope, message, type) => { + logger[type](message, Math.min(logFilter[scope] ?? logLevel, baseLogLevel)) + }) }) startAll().catch((error) => { diff --git a/packages/koishi-core/package.json b/packages/koishi-core/package.json index c950bf1ecf..49f80434a6 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.3.0", + "version": "1.3.1", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -35,7 +35,7 @@ "@types/debug": "^4.1.5", "@types/ws": "^6.0.4", "get-port": "^5.1.0", - "koishi-test-utils": "^1.0.3" + "koishi-test-utils": "^1.2.1" }, "dependencies": { "axios": "^0.19.1", diff --git a/packages/koishi-core/src/app.ts b/packages/koishi-core/src/app.ts index 8e4edfcd49..a02e730950 100644 --- a/packages/koishi-core/src/app.ts +++ b/packages/koishi-core/src/app.ts @@ -8,6 +8,7 @@ import { showSuggestions } from './utils' import { Meta, MessageMeta } from './meta' import { simplify, noop } from 'koishi-utils' import { errors, messages } from './messages' +import { ParsedLine } from './parser' export interface AppOptions { port?: number @@ -287,10 +288,11 @@ export class App extends Context { if (prefix && !nickname) continue if (!fuzzy && message !== name) continue if (message.startsWith(name)) { - let _message = message.slice(name.length) + const _message = message.slice(name.length) if (fuzzy && !nickname && _message.match(/^\S/)) continue - if (oneArg) _message = `'${_message.trim()}'` - const result = command.parse(_message) + const result: ParsedLine = oneArg + ? { rest: '', options: {}, unknown: [], args: [_message.trim()] } + : command.parse(_message) result.options = { ...options, ...result.options } result.args.unshift(...args) parsedArgv = { meta, command, ...result } @@ -304,7 +306,7 @@ export class App extends Context { // attach group data const groupFields = new Set(['flag', 'assignee']) this.receiver.emit('before-group', groupFields, parsedArgv || { meta }) - const group = await this.database.observeGroup(meta.groupId, 0, Array.from(groupFields)) + const group = await this.database.observeGroup(meta.groupId, Array.from(groupFields)) Object.defineProperty(meta, '$group', { value: group, writable: true }) // ignore some group calls @@ -320,7 +322,7 @@ export class App extends Context { // attach user data const userFields = new Set(['name', 'flag']) this.receiver.emit('before-user', userFields, parsedArgv || { meta }) - const user = await this.database.observeUser(meta.userId, 0, Array.from(userFields)) + const user = await this.database.observeUser(meta.userId, Array.from(userFields)) Object.defineProperty(meta, '$user', { value: user, writable: true }) // ignore some user calls diff --git a/packages/koishi-core/src/database.ts b/packages/koishi-core/src/database.ts index 9b17ba8e4c..7fc04ac9aa 100644 --- a/packages/koishi-core/src/database.ts +++ b/packages/koishi-core/src/database.ts @@ -60,6 +60,8 @@ export enum GroupFlag { noEmit = 4, } +export const groupFlags: (keyof typeof GroupFlag)[] = ['noCommand', 'noResponse', 'noEmit'] + export type Group = Observed> export type GroupField = keyof GroupData export const groupFields: GroupField[] = [] diff --git a/packages/koishi-core/tests/middleware.spec.ts b/packages/koishi-core/tests/middleware.spec.ts index 2aa87b4659..2dde439767 100644 --- a/packages/koishi-core/tests/middleware.spec.ts +++ b/packages/koishi-core/tests/middleware.spec.ts @@ -43,48 +43,22 @@ describe('Middleware API', () => { }) test('middleware-1', async () => { - await app.receive({ - ...shared, - messageType: 'private', - subType: 'friend', - message: 'foo', - }) - + await app.receiveMessage('user', 'foo', 10000) expect(flag.toString(2).split('').reverse().join('')).toBe('1101') }) test('middleware-2', async () => { - await app.receive({ - ...shared, - messageType: 'group', - subType: 'normal', - message: 'bar', - groupId: 20000, - }) - + await app.receiveMessage('group', 'bar', 10000, 20000) expect(flag.toString(2).split('').reverse().join('')).toBe('1011') }) test('middleware-3', async () => { - await app.receive({ - ...shared, - messageType: 'private', - subType: 'friend', - message: 'baz', - }) - + await app.receiveMessage('user', 'baz', 10000) expect(flag.toString(2).split('').reverse().join('')).toBe('1101011') }) test('middleware-4', async () => { - await app.receive({ - ...shared, - messageType: 'group', - subType: 'normal', - message: 'baz', - groupId: 20000, - }) - + await app.receiveMessage('group', 'baz', 10000, 20000) expect(flag.toString(2).split('').reverse().join('')).toBe('10111') }) }) @@ -110,14 +84,7 @@ describe('runtime checks', () => { next() }) - await app.receive({ - ...shared, - messageType: 'group', - subType: 'normal', - message: 'bar', - }) - - await sleep(0) + await app.receiveMessage('group', 'bar', 10000, 20000) expect(errorCallback).toBeCalledTimes(1) expect(errorCallback).toBeCalledWith(errors.ISOLATED_NEXT) @@ -135,12 +102,7 @@ describe('runtime checks', () => { throw new Error(errorMessage) }) - await app.receive({ - ...shared, - messageType: 'group', - subType: 'normal', - message: 'bar', - }) + await app.receiveMessage('group', 'bar', 10000, 20000) expect(errorCallback).toBeCalledTimes(1) expect(errorCallback).toBeCalledWith(errorMessage) diff --git a/packages/koishi-core/tests/receiver.spec.ts b/packages/koishi-core/tests/receiver.spec.ts index 34c1e61888..0107afdeab 100644 --- a/packages/koishi-core/tests/receiver.spec.ts +++ b/packages/koishi-core/tests/receiver.spec.ts @@ -1,7 +1,17 @@ -import { createMeta, MockedApp } from 'koishi-test-utils' +import { MockedApp, BASE_SELF_ID } from 'koishi-test-utils' +import { PostType, MetaTypeMap, SubTypeMap, Meta } from '../dist' +import { camelCase } from 'koishi-utils' const app = new MockedApp() +function createMeta (postType: T, type: MetaTypeMap[T], subType: SubTypeMap[T], meta: Meta = {}) { + if (!meta.selfId) meta.selfId = BASE_SELF_ID + meta.postType = postType + meta[camelCase(postType) + 'Type'] = type + meta.subType = subType + return meta +} + describe('Receiver API', () => { test('user/*/message/friend', async () => { const meta = createMeta('message', 'private', 'friend', { userId: 10000 }) diff --git a/packages/plugin-common/package.json b/packages/plugin-common/package.json index 7cb5e99aae..5a2829f98a 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": "1.0.4", + "version": "1.0.5", "main": "dist/index.js", "typings": "dist/index.d.ts", "author": "Shigma <1700011071@pku.edu.cn>", @@ -28,10 +28,10 @@ "plugin" ], "devDependencies": { - "koishi-test-utils": "^1.1.0" + "koishi-test-utils": "^1.2.1" }, "dependencies": { - "koishi-core": "^1.3.0", + "koishi-core": "^1.3.1", "koishi-utils": "^1.0.2" } } diff --git a/packages/plugin-common/src/admin.ts b/packages/plugin-common/src/admin.ts index 5de6db7dab..f808468782 100644 --- a/packages/plugin-common/src/admin.ts +++ b/packages/plugin-common/src/admin.ts @@ -1,4 +1,4 @@ -import { Context, User, userFlags, UserFlag, Meta, UserField, getTargetId, CommandConfig, GroupField, UserData, GroupData } from 'koishi-core' +import { Context, User, userFlags, UserFlag, Meta, UserField, getTargetId, CommandConfig, GroupField, UserData, GroupData, GroupFlag, groupFlags, Group, userFields, groupFields } from 'koishi-core' import { isInteger, difference, Observed, paramCase } from 'koishi-utils' type ActionCallback = @@ -17,11 +17,11 @@ export interface GroupAction { const userActionMap: Record = {} const groupActionMap: Record = {} -export function registerUserAction (name: string, callback: ActionCallback, fields: K[] = []) { +export function registerUserAction (name: string, callback: ActionCallback, fields?: K[]) { userActionMap[paramCase(name)] = { callback, fields } } -export function registerGroupAction (name: string, callback: ActionCallback, fields: K[] = []) { +export function registerGroupAction (name: string, callback: ActionCallback, fields?: K[]) { groupActionMap[paramCase(name)] = { callback, fields } } @@ -36,7 +36,7 @@ registerUserAction('setAuth', async (meta, user, value) => { await user._update() return meta.$send('用户权限已修改。') } -}) +}, ['authority']) registerUserAction('setFlag', async (meta, user, ...flags) => { if (!flags.length) return meta.$send(`可用的标记有 ${userFlags.join(', ')}。`) @@ -47,10 +47,10 @@ registerUserAction('setFlag', async (meta, user, ...flags) => { } await user._update() return meta.$send('用户信息已修改。') -}) +}, ['flag']) registerUserAction('unsetFlag', async (meta, user, ...flags) => { - if (!flags.length) return meta.$send(`可用的 flag 有:${userFlags.join(', ')}。`) + if (!flags.length) return meta.$send(`可用的标记有 ${userFlags.join(', ')}。`) const notFound = difference(flags, userFlags) if (notFound.length) return meta.$send(`未找到标记 ${notFound.join(', ')}。`) for (const name of flags) { @@ -58,7 +58,7 @@ registerUserAction('unsetFlag', async (meta, user, ...flags) => { } await user._update() return meta.$send('用户信息已修改。') -}) +}, ['flag']) registerUserAction('clearUsage', async (meta, user, ...commands) => { if (commands.length) { @@ -70,7 +70,7 @@ registerUserAction('clearUsage', async (meta, user, ...commands) => { } await user._update() return meta.$send('用户信息已修改。') -}) +}, ['usage']) registerUserAction('showUsage', async (meta, user, ...commands) => { const { usage } = user @@ -80,7 +80,29 @@ registerUserAction('showUsage', async (meta, user, ...commands) => { '用户今日各指令的调用次数为:', ...commands.sort().map(name => `${name}:${usage[name] ? usage[name].count : 0} 次`), ].join('\n')) -}) +}, ['usage']) + +registerGroupAction('setFlag', async (meta, group, ...flags) => { + if (!flags.length) return meta.$send(`可用的标记有 ${groupFlags.join(', ')}。`) + const notFound = difference(flags, groupFlags) + if (notFound.length) return meta.$send(`未找到标记 ${notFound.join(', ')}。`) + for (const name of flags) { + group.flag |= GroupFlag[name] + } + await group._update() + return meta.$send('群信息已修改。') +}, ['flag']) + +registerGroupAction('unsetFlag', async (meta, group, ...flags) => { + if (!flags.length) return meta.$send(`可用的标记有 ${groupFlags.join(', ')}。`) + const notFound = difference(flags, groupFlags) + if (notFound.length) return meta.$send(`未找到标记 ${notFound.join(', ')}。`) + for (const name of flags) { + group.flag &= ~GroupFlag[name] + } + await group._update() + return meta.$send('群信息已修改。') +}, ['flag']) export default function apply (ctx: Context, options: CommandConfig) { const userActions = Object.keys(userActionMap).map(paramCase).join(', ') @@ -92,35 +114,34 @@ export default function apply (ctx: Context, options: CommandConfig) { .option('-G, --this-group', '指定目标群为本群') .action(async ({ meta, options }, name: string, ...args: string[]) => { const isGroup = 'g' in options || 'G' in options - if ('user' in options && isGroup) { - return meta.$send('不能同时目标为指定用户和群。') - } + if ('user' in options && isGroup) return meta.$send('不能同时目标为指定用户和群。') + const actionList = isGroup ? groupActions : userActions const actionMap = isGroup ? groupActionMap : userActionMap - if (!name) { - return meta.$send(`当前的可用指令有:${actionList}。`) - } + if (!name) return meta.$send(`当前的可用指令有:${actionList}。`) + const action = actionMap[paramCase(name)] if (!action) return meta.$send(`指令未找到。当前的可用指令有:${actionList}。`) if (isGroup) { - let group: Observed + const fields = action.fields ? action.fields.slice() as GroupField[] : groupFields + let group: Group if (options.thisGroup) { - group = await ctx.database.observeGroup(meta.$group) - } else if (typeof options.group === 'number') { - group = await ctx.database.observeGroup(options.group) + group = await ctx.database.observeGroup(meta.$group, fields) + } else if (isInteger(options.group) && options.group > 0) { + group = await ctx.database.observeGroup(options.group, fields) } if (!group) return meta.$send('未找到指定的群。') return action.callback.call(ctx, meta, group, ...args) } else { - const fields = action.fields.slice() as UserField[] + const fields = action.fields ? action.fields.slice() as UserField[] : userFields if (!fields.includes('authority')) fields.push('authority') let user: User if (options.user) { const qq = getTargetId(options.user) if (!qq) return meta.$send('未指定目标。') user = await ctx.database.observeUser(qq, -1, fields) - if (!user) return meta.$send('未找到用户。') + if (!user) return meta.$send('未找到指定的用户。') if (qq !== meta.$user.id && meta.$user.authority <= user.authority) return meta.$send('权限不足。') } else { user = await ctx.database.observeUser(meta.$user, 0, fields) diff --git a/packages/plugin-common/src/authorize.ts b/packages/plugin-common/src/authorize.ts index e71909538d..2a0356f81f 100644 --- a/packages/plugin-common/src/authorize.ts +++ b/packages/plugin-common/src/authorize.ts @@ -66,7 +66,6 @@ export default function apply (ctx: Context, config: AuthorizeConfig) { } app.receiver.once('ready', async () => { - logger.info('foo bar') await Promise.all([ ...Object.keys(authorizeUserInverseMap).map(key => updateAuthorizeInfo(+key, authorizeUserInverseMap[+key])), ...Object.entries(authorizeGroup).map(async ([key, value]) => { diff --git a/packages/plugin-common/src/help.ts b/packages/plugin-common/src/help.ts index 948478801e..8896ea13c0 100644 --- a/packages/plugin-common/src/help.ts +++ b/packages/plugin-common/src/help.ts @@ -2,6 +2,7 @@ import { Context, Command, UserData, CommandConfig, MessageMeta } from 'koishi-c export default function apply (ctx: Context, options: CommandConfig) { ctx.command('help [command]', '显示帮助信息', { authority: 0, ...options }) + .userFields(['authority', 'usage']) .shortcut('帮助', { fuzzy: true }) .shortcut('全局指令', { options: { shortcut: true } }) .option('-e, --expand', '展开指令列表') diff --git a/packages/plugin-common/src/index.ts b/packages/plugin-common/src/index.ts index 99d73e4579..9573ea787b 100644 --- a/packages/plugin-common/src/index.ts +++ b/packages/plugin-common/src/index.ts @@ -11,7 +11,7 @@ import info from './info' import likeme, { LikemeOptions } from './likeme' import rank from './rank' import repeater, { RepeaterOptions } from './repeater' -import requestHandler, { HandlerConfig } from './requestHandler' +import requestHandler, { HandlerConfig } from './request-handler' import respondent, { Respondent } from './respondent' import welcome, { WelcomeMessage } from './welcome' diff --git a/packages/plugin-common/src/request-handler.ts b/packages/plugin-common/src/request-handler.ts new file mode 100644 index 0000000000..180b3a3735 --- /dev/null +++ b/packages/plugin-common/src/request-handler.ts @@ -0,0 +1,61 @@ +import { App, Meta } from 'koishi-core' + +type RequestHandler = string | boolean | ((meta: Meta<'request'>, app: App) => string | boolean | void | Promise) + +export interface HandlerConfig { + handleFriend?: RequestHandler + handleGroupAdd?: RequestHandler + handleGroupInvite?: RequestHandler +} + +const defaultHandlers: HandlerConfig = { + async handleFriend (meta, app) { + if (!app.database) return + const user = await app.database.getUser(meta.userId, ['authority']) + if (user.authority >= 1) return true + }, + async handleGroupInvite (meta, app) { + if (!app.database) return + const user = await app.database.getUser(meta.userId, ['authority']) + if (user.authority >= 4) return true + }, +} + +async function getHandleResult (handler: RequestHandler, meta: Meta<'request'>, ctx: App) { + return typeof handler === 'function' ? handler(meta, ctx) : handler +} + +function setFriendResult (meta: Meta, result: string | boolean | void) { + if (typeof result === 'boolean') { + return result ? meta.$approve() : meta.$reject() + } else if (typeof result === 'string') { + return meta.$approve(result) + } +} + +function setGroupResult (meta: Meta, result: string | boolean | void) { + if (typeof result === 'boolean') { + return result ? meta.$approve() : meta.$reject() + } else if (typeof result === 'string') { + return meta.$reject(result) + } +} + +export default function apply (ctx: App, options: HandlerConfig = {}) { + const { handleFriend, handleGroupAdd, handleGroupInvite } = { ...defaultHandlers, ...options } + + ctx.receiver.on('request/friend', async (meta) => { + const result = await getHandleResult(handleFriend, meta, ctx) + return setFriendResult(meta, result) + }) + + ctx.receiver.on('request/group/add', async (meta) => { + const result = await getHandleResult(handleGroupAdd, meta, ctx) + return setGroupResult(meta, result) + }) + + ctx.receiver.on('request/group/invite', async (meta) => { + const result = await getHandleResult(handleGroupInvite, meta, ctx) + return setGroupResult(meta, result) + }) +} diff --git a/packages/plugin-common/src/requestHandler.ts b/packages/plugin-common/src/requestHandler.ts deleted file mode 100644 index 4d4c49467a..0000000000 --- a/packages/plugin-common/src/requestHandler.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { App, Meta } from 'koishi-core' - -type RequestHandler = boolean | ((meta: Meta<'request'>, app: App) => string | boolean | void | Promise) - -export interface HandlerConfig { - handleFriend?: RequestHandler - handleGroupAdd?: RequestHandler - handleGroupInvite?: RequestHandler -} - -const defaultHandlers: HandlerConfig = { - async handleFriend (meta, app) { - if (!app.database) return - const user = await app.database.getUser(meta.userId, 0, ['authority']) - if (user.authority >= 1) return true - }, - async handleGroupInvite (meta, app) { - if (!app.database) return - const user = await app.database.getUser(meta.userId, 0, ['authority']) - if (user.authority >= 4) return true - }, -} - -async function getHandleResult (handler: RequestHandler, meta: Meta<'request'>, ctx: App) { - return typeof handler === 'function' ? handler(meta, ctx) : handler -} - -export default function apply (ctx: App, options: HandlerConfig = {}) { - const { handleFriend, handleGroupAdd, handleGroupInvite } = { ...defaultHandlers, ...options } - - ctx.users.receiver.on('request/friend', async (meta) => { - const result = await getHandleResult(handleFriend, meta, ctx) - if (typeof result === 'boolean') { - return result ? meta.$approve() : meta.$reject() - } else if (typeof result === 'string') { - return meta.$approve(result) - } - }) - - ctx.groups.receiver.on('request/group/add', async (meta) => { - const result = await getHandleResult(handleGroupAdd, meta, ctx) - if (typeof result === 'boolean') { - return result ? meta.$approve() : meta.$reject() - } else if (typeof result === 'string') { - return meta.$reject(result) - } - }) - - ctx.groups.receiver.on('request/group/invite', async (meta) => { - const result = await getHandleResult(handleGroupInvite, meta, ctx) - if (typeof result === 'boolean') { - return result ? meta.$approve() : meta.$reject() - } else if (typeof result === 'string') { - return meta.$reject(result) - } - }) -} diff --git a/packages/plugin-common/tests/__snapshots__/admin.spec.ts.snap b/packages/plugin-common/tests/__snapshots__/admin.spec.ts.snap new file mode 100644 index 0000000000..df5f3f4613 --- /dev/null +++ b/packages/plugin-common/tests/__snapshots__/admin.spec.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`group operations list actions: admin -G 1`] = `"当前的可用指令有:set-flag, unset-flag。"`; + +exports[`group operations list actions: admin -G foo 1`] = `"指令未找到。当前的可用指令有:set-flag, unset-flag。"`; + +exports[`user operations list actions: admin 1`] = `"当前的可用指令有:set-auth, set-flag, unset-flag, clear-usage, show-usage。"`; + +exports[`user operations list actions: admin foo 1`] = `"指令未找到。当前的可用指令有:set-auth, set-flag, unset-flag, clear-usage, show-usage。"`; diff --git a/packages/plugin-common/tests/admin.spec.ts b/packages/plugin-common/tests/admin.spec.ts new file mode 100644 index 0000000000..93608cf55a --- /dev/null +++ b/packages/plugin-common/tests/admin.spec.ts @@ -0,0 +1,113 @@ +import { MockedApp, MemoryDatabase } from 'koishi-test-utils' +import { registerDatabase, userFlags, groupFlags, UserFlag, GroupFlag } from 'koishi-core' +import admin, { registerUserAction, registerGroupAction } from '../src/admin' + +registerDatabase('memory', MemoryDatabase) + +const app = new MockedApp({ database: { memory: {} } }) +const session = app.createSession('group', 123, 321) + +app.plugin(admin) +app.command('foo', { maxUsage: 10 }).action(({ meta }) => meta.$send('bar')) +app.command('bar', { maxUsage: 10 }).action(({ meta }) => meta.$send('foo')) + +beforeAll(async () => { + await app.start() + await app.database.getUser(123, 4) + await app.database.getUser(456, 3) + await app.database.getUser(789, 4) + await app.database.getGroup(321, app.selfId) + await app.database.getGroup(654, app.selfId) +}) + +describe('basic features', () => { + test('check', async () => { + await session.shouldHaveReply('admin -u 456 -g 321', '不能同时目标为指定用户和群。') + }) +}) + +describe('user operations', () => { + test('list actions', async () => { + await session.shouldMatchSnapshot('admin') + await session.shouldMatchSnapshot('admin foo') + }) + + test('check target', async () => { + await session.shouldHaveReply('admin -u bar set-flag', '未指定目标。') + await session.shouldHaveReply('admin -u 233 set-flag', '未找到指定的用户。') + await session.shouldHaveReply('admin -u 789 show-usage', '权限不足。') + }) + + test('setAuth', async () => { + await session.shouldHaveReply('admin -u 456 set-auth -1', '参数错误。') + await session.shouldHaveReply('admin -u 456 set-auth 3', '用户权限未改动。') + await session.shouldHaveReply('admin -u 456 set-auth 2', '用户权限已修改。') + await session.shouldHaveReply('admin -u 456 set-auth 4', '权限不足。') + }) + + test('setFlag', async () => { + await session.shouldHaveReply('admin -u 456 set-flag', `可用的标记有 ${userFlags.join(', ')}。`) + await session.shouldHaveReply('admin -u 456 set-flag foo', '未找到标记 foo。') + await session.shouldHaveReply('admin -u 456 set-flag ignore', '用户信息已修改。') + await expect(app.database.getUser(456)).resolves.toHaveProperty('flag', UserFlag.ignore) + }) + + test('unsetFlag', async () => { + await session.shouldHaveReply('admin -u 456 unset-flag', `可用的标记有 ${userFlags.join(', ')}。`) + await session.shouldHaveReply('admin -u 456 unset-flag foo', '未找到标记 foo。') + await session.shouldHaveReply('admin -u 456 unset-flag ignore', '用户信息已修改。') + await expect(app.database.getUser(456)).resolves.toHaveProperty('flag', 0) + }) + + test('showUsage', async () => { + await session.shouldHaveReply('admin show-usage', '用户今日没有调用过指令。') + await session.shouldHaveReply('foo', 'bar') + await session.shouldHaveReply('admin show-usage', '用户今日各指令的调用次数为:\nfoo:1 次') + await session.shouldHaveReply('admin show-usage foo bar', '用户今日各指令的调用次数为:\nbar:0 次\nfoo:1 次') + }) + + test('clearUsage', async () => { + await session.shouldHaveReply('bar', 'foo') + await session.shouldHaveReply('admin clear-usage foo', '用户信息已修改。') + await session.shouldHaveReply('admin show-usage', '用户今日各指令的调用次数为:\nbar:1 次') + await session.shouldHaveReply('admin clear-usage', '用户信息已修改。') + await session.shouldHaveReply('admin show-usage', '用户今日没有调用过指令。') + }) +}) + +describe('group operations', () => { + test('list actions', async () => { + await session.shouldMatchSnapshot('admin -G') + await session.shouldMatchSnapshot('admin -G foo') + }) + + test('check target', async () => { + await session.shouldHaveReply('admin -g bar set-flag', '未找到指定的群。') + }) + + test('setFlag', async () => { + await session.shouldHaveReply('admin -G set-flag', `可用的标记有 ${groupFlags.join(', ')}。`) + await session.shouldHaveReply('admin -g 654 set-flag foo', '未找到标记 foo。') + await session.shouldHaveReply('admin -g 654 set-flag noCommand noEmit', '群信息已修改。') + await expect(app.database.getGroup(654)).resolves.toHaveProperty('flag', GroupFlag.noCommand | GroupFlag.noEmit) + }) + + test('unsetFlag', async () => { + await session.shouldHaveReply('admin -G unset-flag', `可用的标记有 ${groupFlags.join(', ')}。`) + await session.shouldHaveReply('admin -g 654 unset-flag foo', '未找到标记 foo。') + await session.shouldHaveReply('admin -g 654 unset-flag noEmit noResponse', '群信息已修改。') + await expect(app.database.getGroup(654)).resolves.toHaveProperty('flag', GroupFlag.noCommand) + }) +}) + +describe('custom actions', () => { + test('user action', async () => { + registerUserAction('test', meta => meta.$send('foo')) + await session.shouldHaveReply('admin test', 'foo') + }) + + test('group action', async () => { + registerGroupAction('test', meta => meta.$send('bar')) + await session.shouldHaveReply('admin -G test', 'bar') + }) +}) diff --git a/packages/plugin-common/tests/echo.spec.ts b/packages/plugin-common/tests/echo.spec.ts index 1f3f38ddff..5d6ac3e4ce 100644 --- a/packages/plugin-common/tests/echo.spec.ts +++ b/packages/plugin-common/tests/echo.spec.ts @@ -1,4 +1,4 @@ -import { MockedApp, createMeta } from 'koishi-test-utils' +import { MockedApp } from 'koishi-test-utils' import echo from '../src/echo' const app = new MockedApp() @@ -7,18 +7,18 @@ app.plugin(echo) describe('echo command', () => { test('basic support', async () => { - await app.receive(createMeta('message', 'private', 'friend', { message: 'echo foo', userId: 123 })) + await app.receiveMessage('user', 'echo foo', 123) app.shouldHaveLastRequest('send_private_msg', { message: 'foo', userId: 123 }) - await app.receive(createMeta('message', 'group', 'normal', { message: 'echo foo', groupId: 123 })) - app.shouldHaveLastRequest('send_group_msg', { message: 'foo', groupId: 123 }) - await app.receive(createMeta('message', 'discuss', null, { message: 'echo foo', discussId: 123 })) - app.shouldHaveLastRequest('send_discuss_msg', { message: 'foo', discussId: 123 }) + await app.receiveMessage('group', 'echo foo', 123, 456) + app.shouldHaveLastRequest('send_group_msg', { message: 'foo', groupId: 456 }) + await app.receiveMessage('discuss', 'echo foo', 123, 789) + app.shouldHaveLastRequest('send_discuss_msg', { message: 'foo', discussId: 789 }) }) test('send to other contexts', async () => { - await app.receive(createMeta('message', 'private', 'friend', { message: 'echo -u 456 foo', userId: 123 })) + await app.receiveMessage('user', 'echo -u 456 foo', 123) app.shouldHaveLastRequest('send_private_msg', { message: 'foo', userId: 456 }) - await app.receive(createMeta('message', 'private', 'friend', { message: 'echo -g 456 -d 789 foo', userId: 123 })) + await app.receiveMessage('user', 'echo -g 456 -d 789 foo', 123) app.shouldHaveLastRequests([ ['send_group_msg', { message: 'foo', groupId: 456 }], ['send_discuss_msg', { message: 'foo', discussId: 789 }], diff --git a/packages/plugin-common/tests/handler.spec.ts b/packages/plugin-common/tests/handler.spec.ts new file mode 100644 index 0000000000..c4bfe3e300 --- /dev/null +++ b/packages/plugin-common/tests/handler.spec.ts @@ -0,0 +1,106 @@ +import { MockedApp, MemoryDatabase } from 'koishi-test-utils' +import requestHandler, { HandlerConfig } from '../src/request-handler' +import { registerDatabase } from 'koishi-core' + +registerDatabase('memory', MemoryDatabase) + +let app: MockedApp + +describe('support string', () => { + beforeAll(async () => { + app = new MockedApp() + app.plugin(requestHandler, { + handleFriend: 'foo', + handleGroupAdd: 'bar', + }) + }) + + test('friend add', async () => { + await app.receiveFriendRequest(321) + app.shouldHaveLastRequest('set_friend_add_request', { approve: true, remark: 'foo' }) + }) + + test('group add', async () => { + await app.receiveGroupRequest(321, 'add') + app.shouldHaveLastRequest('set_group_add_request', { approve: false, reason: 'bar' }) + }) +}) + +describe('support boolean', () => { + beforeAll(async () => { + app = new MockedApp() + app.plugin(requestHandler, { + handleFriend: false, + handleGroupInvite: false, + }) + }) + + test('friend add', async () => { + await app.receiveFriendRequest(321) + app.shouldHaveLastRequest('set_friend_add_request', { approve: false }) + }) + + test('group invite', async () => { + await app.receiveGroupRequest(321, 'invite') + app.shouldHaveLastRequest('set_group_add_request', { approve: false }) + }) +}) + +describe('default behaviour without database', () => { + beforeAll(async () => { + app = new MockedApp() + app.plugin(requestHandler) + }) + + test('friend add', async () => { + await app.receiveFriendRequest(321) + app.shouldHaveNoRequests() + }) + + test('group invite', async () => { + await app.receiveGroupRequest(654, 'invite') + app.shouldHaveNoRequests() + }) + + test('group add', async () => { + await app.receiveGroupRequest(456, 'add') + app.shouldHaveNoRequests() + }) +}) + +describe('default behaviour with database', () => { + beforeAll(async () => { + app = new MockedApp({ database: { memory: {} } }) + app.plugin(requestHandler) + + await app.start() + await app.database.getUser(123, 1) + await app.database.getUser(456, 4) + await app.database.getUser(654, 3) + }) + + test('friend add with authority 0', async () => { + await app.receiveFriendRequest(321) + app.shouldHaveNoRequests() + }) + + test('friend add with authority 1', async () => { + await app.receiveFriendRequest(123) + app.shouldHaveLastRequest('set_friend_add_request', { approve: true }) + }) + + test('group invite with authority 3', async () => { + await app.receiveGroupRequest(654, 'invite') + app.shouldHaveNoRequests() + }) + + test('group invite with authority 4', async () => { + await app.receiveGroupRequest(456, 'invite') + app.shouldHaveLastRequest('set_group_add_request', { approve: true, subType: 'invite' }) + }) + + test('group add with authority 4', async () => { + await app.receiveGroupRequest(456, 'add') + app.shouldHaveNoRequests() + }) +}) diff --git a/packages/plugin-schedule/package.json b/packages/plugin-schedule/package.json index d5bbfb35e4..340010d036 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.0", + "version": "1.0.1", "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.4", - "koishi-database-mysql": "^1.0.4", - "koishi-test-utils": "^1.1.0" + "koishi-database-level": "^1.0.5", + "koishi-database-mysql": "^1.0.5", + "koishi-test-utils": "^1.2.1" }, "dependencies": { - "koishi-core": "^1.3.0", + "koishi-core": "^1.3.1", "koishi-utils": "^1.0.2", "ms": "^2.1.2" } diff --git a/packages/plugin-teach/package.json b/packages/plugin-teach/package.json index 2c7d7fbe8b..bee7206305 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.9", + "version": "0.1.10", "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.4", - "koishi-database-mysql": "^1.0.4", - "koishi-test-utils": "^1.1.0" + "koishi-database-level": "^1.0.5", + "koishi-database-mysql": "^1.0.5", + "koishi-test-utils": "^1.2.1" }, "dependencies": { - "koishi-core": "^1.3.0", + "koishi-core": "^1.3.1", "koishi-utils": "^1.0.2" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 471163602b..9eb8fc7f16 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": "1.1.0", + "version": "1.2.1", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -34,7 +34,7 @@ ], "peerDependencies": { "jest": "^24.9.0", - "koishi-core": "^1.3.0" + "koishi-core": "^1.3.1" }, "devDependencies": { "@types/debug": "^4.1.5", diff --git a/packages/test-utils/src/mocks.ts b/packages/test-utils/src/mocks.ts index e9d97d80e5..16e5b56230 100644 --- a/packages/test-utils/src/mocks.ts +++ b/packages/test-utils/src/mocks.ts @@ -1,6 +1,7 @@ import { BASE_SELF_ID, RequestData } from './utils' import { snakeCase, sleep } from 'koishi-utils' -import { AppOptions, App, Sender, Server, ContextType, ResponsePayload, MessageMeta, Meta } from 'koishi-core' +import { AppOptions, App, Sender, Server, ContextType, ResponsePayload, MessageMeta, Meta, MetaTypeMap } from 'koishi-core' +import debug from 'debug' class MockedServer extends Server { constructor (app: App) { @@ -39,9 +40,12 @@ export class MockedApp extends App { super({ selfId: BASE_SELF_ID, ...options }) this.sender = new MockedSender(this) this.server = new MockedServer(this) + this.receiver.on('logger', (scope, message) => { + debug('koishi:' + scope)(message) + }) } - receive (meta: Meta): Promise { + receive (meta: Meta) { this.server.dispatchMeta({ selfId: this.selfId, ...meta, @@ -49,6 +53,39 @@ export class MockedApp extends App { return sleep(0) } + receiveFriendRequest (userId: number, flag = 'flag') { + return this.receive({ + postType: 'request', + requestType: 'friend', + userId, + flag, + }) + } + + receiveGroupRequest (userId: number, subType: 'add' | 'invite', groupId = 10000, flag = 'flag') { + return this.receive({ + postType: 'request', + requestType: 'group', + subType, + userId, + groupId, + flag, + }) + } + + 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 (ctxType: ContextType, message: string, userId: number, ctxId?: number) { + return this.receive({ + [ctxType + 'Id']: ctxId, + postType: 'message', + messageType: ctxType === 'user' ? 'private' : ctxType, + message, + userId, + }) + } + clearRequests () { this.sender.requests = [] } diff --git a/packages/test-utils/src/utils.ts b/packages/test-utils/src/utils.ts index 701d497463..db55b2b17a 100644 --- a/packages/test-utils/src/utils.ts +++ b/packages/test-utils/src/utils.ts @@ -1,11 +1,10 @@ -import { SenderInfo, PostType, MetaTypeMap, SubTypeMap, Meta } from 'koishi-core' -import { camelCase } from 'koishi-utils' +import { SenderInfo } from 'koishi-core' import debug from 'debug' export const BASE_SELF_ID = 514 export const showTestLog = debug('koishi:test') -export type RequestData = readonly [string, Record] +export type RequestData = readonly [string, Record] /** * polyfill for node < 12.0 @@ -18,14 +17,6 @@ export function fromEntries (entries: Iterable) { return result } -export function createMeta (postType: T, type: MetaTypeMap[T], subType: SubTypeMap[T], meta: Meta = {}) { - if (!meta.selfId) meta.selfId = BASE_SELF_ID - meta.postType = postType - meta[camelCase(postType) + 'Type'] = type - meta.subType = subType - return meta -} - export function createArray (length: number, create: (index: number) => T) { return Array(length).fill(undefined).map((_, index) => create(index)) }