diff --git a/build/jest.ts b/build/jest.ts index ddd4e2b0ae..a30c888eca 100644 --- a/build/jest.ts +++ b/build/jest.ts @@ -1,6 +1,4 @@ import spawn from 'cross-spawn' -import open from 'open' -import { resolve } from 'path' const args = ['jest', '--coverage'] diff --git a/build/publish.ts b/build/publish.ts index 9b22dc8cfb..dbfe5aeee3 100644 --- a/build/publish.ts +++ b/build/publish.ts @@ -14,8 +14,12 @@ if (CI && (GITHUB_REF !== 'refs/heads/master' || GITHUB_EVENT_NAME !== 'push')) const headerMap = { feat: 'Features', fix: 'Bug Fixes', + dep: 'Dependencies', } +const prefixes = Object.keys(headerMap) +const prefixRegExp = new RegExp(`^(${prefixes.join('|')})(?:\\((\\S+)\\))?: (.+)$`) + ;(async () => { let folders = await getWorkspaces() if (process.argv[2]) { @@ -60,15 +64,16 @@ const headerMap = { return console.log(`Tag ${version} already exists.`) } - const updates = { fix: '', feat: '' } + const updates = {} const lastTag = tags[tags.length - 1] const commits = spawnSync(`git log ${lastTag}..HEAD --format=%H%s`).split(/\r?\n/).reverse() for (const commit of commits) { const hash = commit.slice(0, 40) - const details = /^(fix|feat)(?:\((\S+)\))?: (.+)$/.exec(commit.slice(40)) + const details = prefixRegExp.exec(commit.slice(40)) if (!details) continue let message = details[3] if (details[2]) message = `**${details[2]}:** ${message}` + if (!updates[details[1]]) updates[details[1]] = '' updates[details[1]] += `- ${message} (${hash})\n` } diff --git a/package.json b/package.json index b7e2e5eba2..bb8f6d1d12 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,12 @@ "devDependencies": { "@octokit/rest": "^16.43.1", "@types/cross-spawn": "^6.0.1", - "@types/fs-extra": "^8.0.1", + "@types/fs-extra": "^8.1.0", "@types/jest": "^25.1.2", - "@types/node": "^13.7.0", + "@types/node": "^13.7.1", "@types/semver": "^7.1.0", - "@typescript-eslint/eslint-plugin": "^2.19.0", - "@typescript-eslint/parser": "^2.19.0", + "@typescript-eslint/eslint-plugin": "^2.20.0", + "@typescript-eslint/parser": "^2.20.0", "cac": "^6.5.6", "cross-spawn": "^7.0.1", "del": "^5.1.0", @@ -50,8 +50,8 @@ "open": "^7.0.2", "ora": "^4.0.3", "p-map": "^3.0.0", - "prompts": "^2.3.0", - "semver": "^7.1.2", + "prompts": "^2.3.1", + "semver": "^7.1.3", "ts-jest": "^25.2.0", "ts-node": "^8.6.2", "typescript": "^3.7.5" diff --git a/packages/database-level/package.json b/packages/database-level/package.json index e9fed21094..48039a97fd 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.1.1", + "version": "1.1.2", "main": "dist/index.js", "files": [ "dist" @@ -33,10 +33,10 @@ "leveldb" ], "devDependencies": { - "koishi-test-utils": "^3.1.0" + "koishi-test-utils": "^3.1.1" }, "peerDependencies": { - "koishi-core": "^1.8.1" + "koishi-core": "^1.9.0" }, "dependencies": { "@types/leveldown": "^4.0.2", diff --git a/packages/database-memory/package.json b/packages/database-memory/package.json index 33177ee9bb..025c30e77f 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.1.1", + "version": "1.1.2", "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", "peerDependencies": { - "koishi-core": "^1.8.1" + "koishi-core": "^1.9.0" }, "dependencies": { "koishi-utils": "^1.0.3" diff --git a/packages/database-mysql/package.json b/packages/database-mysql/package.json index 742800a245..8d02b7ed82 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.1.1", + "version": "1.1.2", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -35,7 +35,7 @@ "@types/mysql": "^2.15.8" }, "peerDependencies": { - "koishi-core": "^1.8.1" + "koishi-core": "^1.9.0" }, "dependencies": { "koishi-utils": "^1.0.3", diff --git a/packages/database-mysql/src/group.ts b/packages/database-mysql/src/group.ts index 204c2c9887..94b9350533 100644 --- a/packages/database-mysql/src/group.ts +++ b/packages/database-mysql/src/group.ts @@ -41,7 +41,7 @@ injectMethods('mysql', 'group', { }, async getAllGroups (...args) { - let assignees: number[], fields: GroupField[] + let assignees: readonly number[], fields: readonly GroupField[] if (args.length > 1) { fields = args[0] assignees = args[1] diff --git a/packages/database-mysql/src/user.ts b/packages/database-mysql/src/user.ts index 746c2d087d..eff7771a28 100644 --- a/packages/database-mysql/src/user.ts +++ b/packages/database-mysql/src/user.ts @@ -27,7 +27,7 @@ injectMethods('mysql', 'user', { }, async getUsers (...args) { - let ids: number[], fields: UserField[] + let ids: readonly number[], fields: readonly UserField[] if (args.length > 1) { ids = args[0] fields = args[1] diff --git a/packages/database-sqlite/package.json b/packages/database-sqlite/package.json index b5de209301..32bda0e020 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.7", + "version": "1.0.0-alpha.8", "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": "^3.1.0" + "koishi-test-utils": "^3.1.1" }, "peerDependencies": { - "koishi-core": "^1.8.1" + "koishi-core": "^1.9.0" }, "dependencies": { "koishi-utils": "^1.0.3", diff --git a/packages/database-sqlite/src/group.ts b/packages/database-sqlite/src/group.ts index e3930e980b..7534388a0b 100644 --- a/packages/database-sqlite/src/group.ts +++ b/packages/database-sqlite/src/group.ts @@ -24,7 +24,7 @@ injectMethods('sqlite', 'group', { }, async getAllGroups (...args) { - let assignees: number[], fields: GroupField[] + let assignees: readonly number[], fields: readonly GroupField[] if (args.length > 1) { fields = args[0] assignees = args[1] diff --git a/packages/database-sqlite/src/user.ts b/packages/database-sqlite/src/user.ts index d8fc383b56..b65779c827 100644 --- a/packages/database-sqlite/src/user.ts +++ b/packages/database-sqlite/src/user.ts @@ -28,7 +28,7 @@ injectMethods('sqlite', 'user', { }, async getUsers (...args) { - let ids: number[], fields: UserField[] + let ids: readonly number[], fields: readonly UserField[] if (args.length > 1) { ids = args[0] fields = args[1] diff --git a/packages/koishi-cli/package.json b/packages/koishi-cli/package.json index 3ae53cde63..5661d1f0da 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.8.1", + "version": "1.9.0", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -38,10 +38,10 @@ "cac": "^6.5.6", "js-yaml": "^3.13.1", "kleur": "^3.0.3", - "koishi-core": "^1.8.1", - "koishi-plugin-common": "^2.1.2", - "koishi-plugin-schedule": "^1.0.7", + "koishi-core": "^1.9.0", + "koishi-plugin-common": "^2.1.3", + "koishi-plugin-schedule": "^1.0.8", "koishi-utils": "^1.0.3", - "prompts": "^2.3.0" + "prompts": "^2.3.1" } } diff --git a/packages/koishi-core/package.json b/packages/koishi-core/package.json index 022c5a6537..bda9495dfb 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.8.1", + "version": "1.9.0", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -35,8 +35,8 @@ "@types/debug": "^4.1.5", "@types/ws": "^7.2.1", "get-port": "^5.1.1", - "koishi-database-memory": "^1.1.1", - "koishi-test-utils": "^3.1.0" + "koishi-database-memory": "^1.1.2", + "koishi-test-utils": "^3.1.1" }, "dependencies": { "axios": "^0.19.2", diff --git a/packages/koishi-core/src/app.ts b/packages/koishi-core/src/app.ts index 24c2e6b837..6a3a190c65 100644 --- a/packages/koishi-core/src/app.ts +++ b/packages/koishi-core/src/app.ts @@ -128,9 +128,8 @@ export class App extends Context { this.receiver.on('before-group', Command.attachGroupFields) this.middleware(this._preprocess) - this.receiver.on('logger', (scope, message) => { - debug('koishi:' + scope)(message) - }) + // apply default logger + this.receiver.on('logger', (scope, message) => debug(scope)(message)) } get users () { @@ -236,7 +235,7 @@ export class App extends Context { } await Promise.all(tasks) this.status = Status.open - this.logger('app').debug('started') + this.logger('koishi:app').debug('started') this.receiver.emit('connect') if (this.selfId && !this._isReady) { this.receiver.emit('ready') @@ -261,7 +260,7 @@ export class App extends Context { this.server.close() } this.status = Status.closed - this.logger('app').debug('stopped') + this.logger('koishi:app').debug('stopped') this.receiver.emit('disconnect') if (appList.every(app => app.status === Status.closed)) { onStopHooks.forEach(hook => hook(...appList)) @@ -270,7 +269,7 @@ export class App extends Context { emitEvent (meta: Meta, event: K, ...payload: Parameters) { if (!meta.$ctxType) { - this.logger('receiver').debug('/', 'emits', event) + this.logger('koishi:receiver').debug('/', 'emits', event) this.receiver.emit(event, ...payload) return } @@ -278,7 +277,7 @@ export class App extends Context { for (const path in this._contexts) { const context = this._contexts[path] if (!context.match(meta)) continue - this.logger('receiver').debug(path, 'emits', event) + this.logger('koishi:receiver').debug(path, 'emits', event) context.receiver.emit(event, ...payload) } } @@ -434,7 +433,7 @@ export class App extends Context { let index = 0 const next = async (fallback?: NextFunction) => { if (!this._middlewareSet.has(counter)) { - return this.logger().warn(new Error(errors.ISOLATED_NEXT)) + return this.logger('koishi').warn(new Error(errors.ISOLATED_NEXT)) } if (fallback) middlewares.push((_, next) => fallback(next)) try { diff --git a/packages/koishi-core/src/command.ts b/packages/koishi-core/src/command.ts index 3db2f98e87..f4b0b6b888 100644 --- a/packages/koishi-core/src/command.ts +++ b/packages/koishi-core/src/command.ts @@ -23,6 +23,7 @@ export interface ParsedCommandLine extends Partial { } export type UserType = T | ((user: UserData) => T) +export type CommandUsage = string | ((this: Command, meta: Meta) => string | Promise) export interface CommandConfig { /** disallow unknown options */ @@ -39,7 +40,6 @@ export interface CommandConfig { authority?: number disable?: UserType maxUsage?: UserType - maxUsageText?: string minInterval?: UserType showWarning?: boolean | number noHelpOption?: boolean @@ -81,7 +81,7 @@ export class Command { _aliases: string[] = [] _options: CommandOption[] = [] - _usage?: string + _usage?: CommandUsage _examples: string[] = [] _shortcuts: Record = {} _userFields = new Set() @@ -186,7 +186,7 @@ export class Command { return this } - usage (text: string) { + usage (text: CommandUsage) { this._usage = text return this } @@ -285,7 +285,7 @@ export class Command { if (code) return this._sendHint(code, meta) // execute command - this.context.logger('command').debug('execute %s', this.name) + this.context.logger('koishi:command').debug('execute %s', this.name) this.app.emitEvent(meta, 'command', argv) let skipped = false diff --git a/packages/koishi-core/src/context.ts b/packages/koishi-core/src/context.ts index 5eb111508d..024916dc9c 100644 --- a/packages/koishi-core/src/context.ts +++ b/packages/koishi-core/src/context.ts @@ -11,8 +11,8 @@ import { format } from 'util' export type NextFunction = (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 } +type PluginFunction = (ctx: T, options: U) => void +type PluginObject = { name?: string, apply: PluginFunction } export type Plugin = PluginFunction | PluginObject type Subscope = [number[], number[]] @@ -65,7 +65,7 @@ export class Context { constructor (public readonly identifier: string, private readonly _scope: ContextScope) { this.receiver.on('error', (error) => { - this.logger().warn(error) + this.logger('koishi').warn(error) }) this.logger = (scope = '') => { @@ -127,15 +127,15 @@ export class Context { }) } - plugin (plugin: PluginFunction, options?: U): this - plugin (plugin: PluginObject, options?: U): this - plugin (plugin: Plugin, options: any) { + plugin > (plugin: T, options?: T extends PluginFunction ? U : never): this + plugin > (plugin: T, options?: T extends PluginObject ? U : never): this + plugin > (plugin: T, options?: T extends Plugin ? U : never) { if (options === false) return const ctx = Object.create(this) if (typeof plugin === 'function') { - plugin(ctx, options) + (plugin as PluginFunction)(ctx, options) } else if (plugin && typeof plugin === 'object' && typeof plugin.apply === 'function') { - plugin.apply(ctx, options) + (plugin as PluginObject).apply(ctx, options) } else { throw new Error(errors.INVALID_PLUGIN) } @@ -145,7 +145,7 @@ export class Context { middleware (middleware: Middleware) { const { maxMiddlewares } = this.app.options if (this.app._middlewares.length >= maxMiddlewares) { - this.logger().warn(new Error(format(errors.MAX_MIDDLEWARES, maxMiddlewares))) + this.logger('koishi').warn(new Error(format(errors.MAX_MIDDLEWARES, maxMiddlewares))) } else { this.app._middlewares.push([this, middleware]) } @@ -159,7 +159,7 @@ export class Context { prependMiddleware (middleware: Middleware) { const { maxMiddlewares } = this.app.options if (this.app._middlewares.length >= maxMiddlewares) { - this.logger().warn(new Error(format(errors.MAX_MIDDLEWARES, maxMiddlewares))) + this.logger('koishi').warn(new Error(format(errors.MAX_MIDDLEWARES, maxMiddlewares))) } else { this.app._middlewares.unshift([this, middleware]) } @@ -291,6 +291,7 @@ export interface EventMap { 'lifecycle' (meta: Meta<'meta_event'>): any 'lifecycle/enable' (meta: Meta<'meta_event'>): any 'lifecycle/disable' (meta: Meta<'meta_event'>): any + 'lifecycle/connect' (meta: Meta<'meta_event'>): any 'before-user' (fields: Set, argv: ParsedCommandLine): any 'before-group' (fields: Set, argv: ParsedCommandLine): any 'attach' (meta: Meta<'message'>): any diff --git a/packages/koishi-core/src/database.ts b/packages/koishi-core/src/database.ts index 06647bdcf2..c4b0f92de2 100644 --- a/packages/koishi-core/src/database.ts +++ b/packages/koishi-core/src/database.ts @@ -86,24 +86,24 @@ export function createGroup (id: number, assignee: number) { } export interface UserMethods { - getUser (userId: number, fields?: K[]): Promise> - getUser (userId: number, defaultAuthority?: number, fields?: K[]): Promise> + getUser (userId: number, fields?: readonly K[]): Promise> + getUser (userId: number, defaultAuthority?: number, fields?: readonly K[]): Promise> getUsers (fields?: K[]): Promise[]> - getUsers (ids: number[], fields?: K[]): Promise[]> + getUsers (ids: readonly number[], fields?: readonly K[]): Promise[]> setUser (userId: number, data: Partial): Promise - observeUser (user: number | UserData, fields?: K[]): Promise> - observeUser (user: number | UserData, defaultAuthority?: number, fields?: K[]): Promise> + observeUser (user: number | UserData, fields?: readonly K[]): Promise> + observeUser (user: number | UserData, defaultAuthority?: number, fields?: readonly K[]): Promise> getUserCount (): Promise } export interface GroupMethods { - getGroup (groupId: number, fields?: K[]): Promise> - getGroup (groupId: number, selfId?: number, fields?: K[]): Promise> - getAllGroups (assignees?: number[]): Promise[]> - getAllGroups (fields?: K[], assignees?: number[]): Promise[]> + getGroup (groupId: number, fields?: readonly K[]): Promise> + getGroup (groupId: number, selfId?: number, fields?: readonly K[]): Promise> + getAllGroups (assignees?: readonly number[]): Promise[]> + getAllGroups (fields?: readonly K[], assignees?: readonly number[]): Promise[]> setGroup (groupId: number, data: Partial): Promise - observeGroup (group: number | GroupData, fields?: K[]): Promise> - observeGroup (group: number | GroupData, selfId?: number, fields?: K[]): Promise> + observeGroup (group: number | GroupData, fields?: readonly K[]): Promise> + observeGroup (group: number | GroupData, selfId?: number, fields?: readonly K[]): Promise> getGroupCount (): Promise } diff --git a/packages/koishi-core/src/meta.ts b/packages/koishi-core/src/meta.ts index f3bbca3c44..09b02aa74e 100644 --- a/packages/koishi-core/src/meta.ts +++ b/packages/koishi-core/src/meta.ts @@ -17,7 +17,7 @@ export interface SubTypeMap { notice: 'set' | 'unset' | 'approve' | 'invite' | 'leave' | 'kick' | 'kick_me' | 'ban' | 'lift_ban' request: 'add' | 'invite' // eslint-disable-next-line camelcase - meta_event: 'enable' | 'disable' + meta_event: 'enable' | 'disable' | 'connect' send: never } diff --git a/packages/koishi-core/src/sender.ts b/packages/koishi-core/src/sender.ts index 94bbeb6ff0..45e642c718 100644 --- a/packages/koishi-core/src/sender.ts +++ b/packages/koishi-core/src/sender.ts @@ -21,10 +21,14 @@ import { } from './meta' export class SenderError extends Error { - readonly name = 'SenderError' - - constructor (readonly args: Record, readonly url: string, readonly retcode: number) { + constructor (args: Record, url: string, retcode: number) { super(`Error when trying to send to ${url}, args: ${JSON.stringify(args)}, retcode: ${retcode}`) + Object.defineProperties(this, { + name: { value: 'SenderError' }, + code: { value: retcode }, + args: { value: args }, + url: { value: url }, + }) } } @@ -66,9 +70,9 @@ export class Sender { } async get (action: string, params: Record = {}, silent = false): Promise { - this.app.logger('sender').debug('request %s %o', action, params) + this.app.logger('koishi:sender').debug('request %s %o', action, params) const response = await this._get(action, snakeCase(params)) - this.app.logger('sender').debug('response %o', response) + this.app.logger('koishi:sender').debug('response %o', response) const { data, retcode } = response if (retcode === 0 && !silent) { return camelCase(data) diff --git a/packages/koishi-core/src/server.ts b/packages/koishi-core/src/server.ts index 96d504eb58..799664a673 100644 --- a/packages/koishi-core/src/server.ts +++ b/packages/koishi-core/src/server.ts @@ -29,7 +29,7 @@ export abstract class Server { } protected debug (format: any, ...params: any[]) { - this.app?.logger('sender').debug(format, ...params) + this.app?.logger('koishi:sender').debug(format, ...params) } protected prepareMeta (data: any) { diff --git a/packages/koishi-core/src/utils.ts b/packages/koishi-core/src/utils.ts index 15da247c95..5beaec5b10 100644 --- a/packages/koishi-core/src/utils.ts +++ b/packages/koishi-core/src/utils.ts @@ -29,16 +29,13 @@ export function getUsage (name: string, user: Pick, time = Da if (key === '$date') continue const { last } = oldUsage[key] if (time.valueOf() - last < ONE_DAY) { - newUsage[key] = { last, count: 0 } + newUsage[key] = { last } } } user.usage = newUsage } - if (!user.usage[name]) { - user.usage[name] = { count: 0 } - } - return user.usage[name] + return user.usage[name] || (user.usage[name] = {}) } interface UsageOptions { @@ -52,15 +49,16 @@ export function updateUsage (name: string, user: Pick, option const { maxUsage = Infinity, minInterval = 0, timestamp = now } = options const usage = getUsage(name, user, now) - if (now - usage.last <= minInterval) { + if (now - usage.last < minInterval) { return CommandHint.TOO_FREQUENT + } else if (options.minInterval || options.timestamp) { + usage.last = timestamp } - usage.last = timestamp if (usage.count >= maxUsage) { return CommandHint.USAGE_EXHAUSTED - } else { - usage.count++ + } else if (options.maxUsage) { + usage.count = (usage.count || 0) + 1 } } @@ -86,28 +84,23 @@ export function showSuggestions (options: SuggestOptions): Promise { }) if (!suggestions.length) return next() - return next(async () => { - let message = prefix + format(messages.SUGGESTION_TEXT, suggestions.map(name => `“${name}”`).join('或')) - if (suggestions.length === 1) { - const [suggestion] = suggestions - const command = typeof options.command === 'function' ? options.command(suggestion) : options.command - const userFields = new Set() - const groupFields = new Set() - Command.attachUserFields(userFields, { command, meta }) - Command.attachGroupFields(groupFields, { command, meta }) - 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') { - meta.$group = await command.context.database?.observeGroup(meta.groupId, Array.from(groupFields)) - } - return execute(suggestions[0], meta, next) - } else { - return next() - } - }, meta) - message += suffix - } - await meta.$send(message) + return next(() => { + const message = prefix + format(messages.SUGGESTION_TEXT, suggestions.map(name => `“${name}”`).join('或')) + if (suggestions.length > 1) return meta.$send(message) + + const command = typeof options.command === 'function' ? options.command(suggestions[0]) : options.command + const userFields = new Set() + const groupFields = new Set() + Command.attachUserFields(userFields, { command, meta }) + Command.attachGroupFields(groupFields, { command, meta }) + command.context.onceMiddleware(async (meta, next) => { + if (meta.message.trim()) return next() + meta.$user = await command.context.database?.observeUser(meta.userId, Array.from(userFields)) + if (meta.messageType === 'group') { + meta.$group = await command.context.database?.observeGroup(meta.groupId, Array.from(groupFields)) + } + return execute(suggestions[0], meta, next) + }, meta) + return meta.$send(message + suffix) }) } diff --git a/packages/koishi-core/tests/utils.spec.ts b/packages/koishi-core/tests/utils.spec.ts index 1531d9fdb8..73c7e81783 100644 --- a/packages/koishi-core/tests/utils.spec.ts +++ b/packages/koishi-core/tests/utils.spec.ts @@ -19,7 +19,6 @@ describe('getTargetId', () => { }) const user = createUser(123, 1) -const realDateNow = Date.now const timestamp = 123456789 const mockedDateNow = Date.now = jest.fn().mockReturnValue(timestamp) @@ -27,29 +26,39 @@ describe('getUsage', () => { test('empty usage', () => { utils.getDateNumber.mockReturnValue(10000) const usage = getUsage('foo', user) - expect(usage.count).toBe(0) - expect(usage.last).toBeUndefined() + expect(usage).toEqual({}) }) test('update usage', () => { + expect(updateUsage('foo', user, { maxUsage: 1, minInterval: 1000 })).toBeFalsy() + const usage = getUsage('foo', user) + expect(usage).toEqual({ count: 1, last: timestamp }) + }) + + test('too frequent', () => { + mockedDateNow.mockReturnValue(timestamp - 1000) + expect(updateUsage('foo', user)).toBeTruthy() + const usage = getUsage('foo', user) + expect(usage).toEqual({ count: 1, last: timestamp }) + }) + + test('update usage 2', () => { + mockedDateNow.mockReturnValue(timestamp) expect(updateUsage('foo', user)).toBeFalsy() const usage = getUsage('foo', user) - expect(usage.count).toBe(1) - expect(usage.last).toBe(timestamp) + expect(usage).toEqual({ count: 1, last: timestamp }) }) test('another day', () => { utils.getDateNumber.mockReturnValue(10001) const usage = getUsage('foo', user) - expect(usage.count).toBe(0) - expect(usage.last).toBe(timestamp) + expect(usage).toEqual({ last: timestamp }) }) test('10 days later', () => { utils.getDateNumber.mockReturnValue(10010) mockedDateNow.mockReturnValue(864000000 + timestamp) const usage = getUsage('foo', user) - expect(usage.count).toBe(0) - expect(usage.last).toBeUndefined() + expect(usage).toEqual({}) }) }) diff --git a/packages/plugin-common/package.json b/packages/plugin-common/package.json index 0816ee3998..03635b3a18 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.1.2", + "version": "2.1.3", "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.1.1", - "koishi-test-utils": "^3.1.0" + "koishi-database-memory": "^1.1.2", + "koishi-test-utils": "^3.1.1" }, "dependencies": { - "koishi-core": "^1.8.1", + "koishi-core": "^1.9.0", "koishi-utils": "^1.0.3" } } diff --git a/packages/plugin-common/src/contextify.ts b/packages/plugin-common/src/contextify.ts index 40624a1b97..9e9954853a 100644 --- a/packages/plugin-common/src/contextify.ts +++ b/packages/plugin-common/src/contextify.ts @@ -1,4 +1,4 @@ -import { Context } from 'koishi-core' +import { Context, getTargetId } from 'koishi-core' export default function apply (ctx: Context) { ctx.command('contextify ', '在特定上下文中触发指令', { authority: 3 }) @@ -31,7 +31,8 @@ export default function apply (ctx: Context) { const newMeta = { ...meta } let user = meta.$user if (options.user) { - const id = +options.user + const id = getTargetId(options.user) + if (!id) return meta.$send('未指定目标。') user = await ctx.database.observeUser(id) if (meta.$user.authority <= user.authority) { return meta.$send('权限不足。') diff --git a/packages/plugin-common/src/help.ts b/packages/plugin-common/src/help.ts index 81d1a0a650..d47f66a5ed 100644 --- a/packages/plugin-common/src/help.ts +++ b/packages/plugin-common/src/help.ts @@ -69,15 +69,15 @@ export const GLOBAL_HELP_EPILOGUE = [ '输入“帮助+指令名”查看特定指令的语法和使用示例。', ].join('\n') -function showGlobalHelp (context: Context, meta: Meta<'message'>, options: any) { +function showGlobalHelp (context: Context, meta: Meta<'message'>, config: any) { return meta.$send([ GLOBAL_HELP_PROLOGUE, - ...getCommandList(context, meta, null, options.expand), + ...getCommandList(context, meta, null, config.expand), GLOBAL_HELP_EPILOGUE, ].join('\n')) } -async function showCommandHelp (command: Command, meta: Meta<'message'>, options: any) { +async function showCommandHelp (command: Command, meta: Meta<'message'>, config: any) { const output = [command.name + command.declaration, command.config.description] if (command.context.database) { meta.$user = await command.context.database.observeUser(meta.userId) @@ -94,34 +94,35 @@ async function showCommandHelp (command: Command, meta: Meta<'message'>, options const maxUsage = command.getConfig('maxUsage', meta) const minInterval = command.getConfig('minInterval', meta) if (meta.$user) { - const { authority, maxUsageText } = command.config const usage = getUsage(command.usageName, meta.$user) if (maxUsage !== Infinity) { - output.push(`已调用次数:${Math.min(usage.count, maxUsage)}/${maxUsageText || maxUsage}。`) + output.push(`已调用次数:${Math.min(usage.count || 0, maxUsage)}/${maxUsage}。`) } + if (minInterval > 0) { const nextUsage = usage.last ? (Math.max(0, minInterval + usage.last - Date.now()) / 1000).toFixed() : 0 output.push(`距离下次调用还需:${nextUsage}/${minInterval / 1000} 秒。`) } - if (authority > 1) { - output.push(`最低权限:${authority} 级。`) + if (command.config.authority > 1) { + output.push(`最低权限:${command.config.authority} 级。`) } } - if (command._usage) { - output.push(command._usage) + const usage = command._usage + if (usage) { + output.push(typeof usage === 'string' ? usage : await usage.call(command, meta)) } - const _options = command._options.filter(option => !option.hidden) - if (_options.length) { - if (_options.some(o => o.authority)) { + const options = command._options.filter(option => !option.hidden) + if (options.length) { + if (options.some(o => o.authority)) { output.push('可用的选项有(括号内为额外要求的权限等级):') } else { output.push('可用的选项有:') } - _options.forEach((option) => { + options.forEach((option) => { const authority = option.authority ? `(${option.authority}) ` : '' let line = ` ${authority}${option.rawName} ${option.description}` if (option.notUsage && maxUsage !== Infinity) { @@ -138,7 +139,7 @@ async function showCommandHelp (command: Command, meta: Meta<'message'>, options if (command.children.length) { output.push( '可用的子指令有(括号内为对应的最低权限等级,标有星号的表示含有子指令):', - ...getCommandList(command.context, meta, command, options.expand), + ...getCommandList(command.context, meta, command, config.expand), ) } diff --git a/packages/plugin-common/tests/contextify.spec.ts b/packages/plugin-common/tests/contextify.spec.ts index 930c440a07..cc04392b2a 100644 --- a/packages/plugin-common/tests/contextify.spec.ts +++ b/packages/plugin-common/tests/contextify.spec.ts @@ -22,6 +22,7 @@ beforeAll(async () => { test('check input', async () => { await session1.shouldHaveReply('ctxf -u 456', '请输入要发送的文本。') + await session2.shouldHaveReply('ctxf -m foo show-context', '未指定目标。') await session1.shouldHaveReply('ctxf show-context', '请提供新的上下文。') await session1.shouldHaveReply('ctxf -u 789 show-context', '权限不足。') await session1.shouldHaveReply('ctxf -m 456 show-context', '无法在私聊上下文使用 --member 选项。') diff --git a/packages/plugin-nlp/package.json b/packages/plugin-nlp/package.json index 85d38efc93..fb46861d16 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.2", + "version": "1.0.3", "main": "dist/index.js", "typings": "dist/index.d.ts", "author": "Shigma <1700011071@pku.edu.cn>", @@ -30,10 +30,10 @@ "jieba" ], "devDependencies": { - "koishi-test-utils": "^3.1.0" + "koishi-test-utils": "^3.1.1" }, "dependencies": { - "koishi-core": "^1.8.1", + "koishi-core": "^1.9.0", "koishi-utils": "^1.0.3", "nodejieba": "^2.4.0" } diff --git a/packages/plugin-nlp/src/index.ts b/packages/plugin-nlp/src/index.ts index 9482b19ee4..562254127a 100644 --- a/packages/plugin-nlp/src/index.ts +++ b/packages/plugin-nlp/src/index.ts @@ -1,4 +1,4 @@ -import { Command, Meta, Context, ParsedLine, ParsedCommandLine, App } from 'koishi-core' +import { Command, Meta, Context, ParsedLine, ParsedCommandLine } from 'koishi-core' import { tag, load } from 'nodejieba' import { resolve } from 'path' diff --git a/packages/plugin-recorder/package.json b/packages/plugin-recorder/package.json index ec2e84fed9..6289841f6d 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.5", + "version": "1.0.0-alpha.6", "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": "^3.1.0" + "koishi-test-utils": "^3.1.1" }, "dependencies": { - "koishi-core": "^1.8.1", + "koishi-core": "^1.9.0", "koishi-utils": "^1.0.3" } } diff --git a/packages/plugin-schedule/package.json b/packages/plugin-schedule/package.json index 093fe6e878..79121f4380 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.7", + "version": "1.0.8", "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.1.1", - "koishi-database-mysql": "^1.1.1", - "koishi-test-utils": "^3.1.0" + "koishi-database-level": "^1.1.2", + "koishi-database-mysql": "^1.1.2", + "koishi-test-utils": "^3.1.1" }, "dependencies": { - "koishi-core": "^1.8.1", + "koishi-core": "^1.9.0", "koishi-utils": "^1.0.3", "ms": "^2.1.2" } diff --git a/packages/plugin-teach/package.json b/packages/plugin-teach/package.json index e0873d7919..463da3f46e 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.16", + "version": "0.1.17", "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.1.1", - "koishi-database-mysql": "^1.1.1", - "koishi-test-utils": "^3.1.0" + "koishi-database-level": "^1.1.2", + "koishi-database-mysql": "^1.1.2", + "koishi-test-utils": "^3.1.1" }, "dependencies": { - "koishi-core": "^1.8.1", + "koishi-core": "^1.9.0", "koishi-utils": "^1.0.3" } } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 296be20442..691c32075f 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": "3.1.0", + "version": "3.1.1", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ @@ -43,7 +43,7 @@ "axios": "^0.19.2", "debug": "^4.1.1", "get-port": "^5.1.1", - "koishi-core": "^1.8.1", + "koishi-core": "^1.9.0", "koishi-utils": "^1.0.3" } }