diff --git a/packages/core/package.json b/packages/core/package.json index 79832be687..33b60a7551 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,16 +25,14 @@ "koishi" ], "devDependencies": { - "@koishijs/plugin-database-memory": "^1.4.0", - "@koishijs/plugin-mock": "^1.0.6", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", "chai-shape": "^1.0.0" }, "dependencies": { "@koishijs/utils": "^5.4.5", - "@satorijs/core": "^1.0.0", - "cordis": "^2.0.3", + "@satorijs/core": "^1.0.1", + "cordis": "^2.0.4", "fastest-levenshtein": "^1.0.12", "minato": "^1.2.2" } diff --git a/packages/core/src/bot.ts b/packages/core/src/bot.ts index 557437ce58..ed10919862 100644 --- a/packages/core/src/bot.ts +++ b/packages/core/src/bot.ts @@ -10,12 +10,18 @@ declare module '@satorijs/core' { } } -export { satori } -export { Adapter } from '@satorijs/core' +export { Adapter, h, Message, segment } from '@satorijs/core' + +export type Filter = satori.Filter export const Bot = satori.Bot export type Bot = satori.Bot +export namespace Bot { + export type Status = satori.Bot.Status + export type Config = satori.Bot.Config +} + Bot.prototype.session = function session(this: Bot, payload) { return new Session(this, payload) } diff --git a/packages/core/src/command/index.ts b/packages/core/src/command/index.ts index b1ae454cae..6bd381a5c0 100644 --- a/packages/core/src/command/index.ts +++ b/packages/core/src/command/index.ts @@ -17,8 +17,10 @@ interface CommandMap extends Map { } declare module '../context' { - interface Context extends Commander.Mixin { + interface Context { $commander: Commander + command(def: D, config?: Command.Config): Command> + command(def: D, desc: string, config?: Command.Config): Command> } interface Events { @@ -34,11 +36,6 @@ declare module '../context' { export namespace Commander { export interface Config {} - - export interface Mixin { - command(def: D, config?: Command.Config): Command> - command(def: D, desc: string, config?: Command.Config): Command> - } } export class Commander { diff --git a/packages/core/src/command/parser.ts b/packages/core/src/command/parser.ts index 700f19b355..bb5bad97ff 100644 --- a/packages/core/src/command/parser.ts +++ b/packages/core/src/command/parser.ts @@ -1,4 +1,5 @@ -import { camelCase, Dict, escapeRegExp, paramCase, segment, Time } from '@koishijs/utils' +import { camelCase, Dict, escapeRegExp, paramCase, Time } from '@koishijs/utils' +import { segment } from '@satorijs/core' import { Command } from './command' import { Context } from '../context' import { Channel, User } from '../database' diff --git a/packages/core/src/context.ts b/packages/core/src/context.ts index 44af2922fb..f8692725fe 100644 --- a/packages/core/src/context.ts +++ b/packages/core/src/context.ts @@ -26,18 +26,21 @@ export interface Events extends satori.Events { export interface Context { [Context.events]: Events [Context.session]: Session + options: Context.Config } -export class Context extends satori.Context { +export class Context extends satori.Context { + constructor(options?: Context.Config) { + super(options) + } + get app() { return this.root } } export namespace Context { - export interface Config extends satori.Context.Config, Config.Basic, Config.Features, Config.Advanced {} - - export const Config = Schema.intersect([]) as Config.Static + export interface Config extends satori.Context.Config, Config.Basic, Config.Message, Config.Advanced {} export namespace Config { export interface Basic { @@ -48,7 +51,7 @@ export namespace Context { autoAuthorize?: Computed> } - export interface Features { + export interface Message { delay?: DelayConfig } @@ -67,11 +70,13 @@ export namespace Context { export interface Static extends Schema { Basic: Schema - Features: Schema + Message: Schema Advanced: Schema } } + export const Config = Schema.intersect([]) as Config.Static + defineProperty(Context.Config, 'Basic', Schema.object({ locale: Schema.string().default('zh').description('默认使用的语言。'), prefix: Schema.union([ @@ -86,7 +91,7 @@ export namespace Context { autoAuthorize: Schema.union([Schema.natural(), Function]).default(1).description('当获取不到用户数据时默认使用的权限等级。'), }).description('基础设置')) - defineProperty(Context.Config, 'Features', Schema.object({ + defineProperty(Context.Config, 'Message', Schema.object({ delay: Schema.object({ character: Schema.natural().role('ms').default(0).description('调用 `session.sendQueued()` 时消息间发送的最小延迟,按前一条消息的字数计算。'), message: Schema.natural().role('ms').default(0.1 * Time.second).description('调用 `session.sendQueued()` 时消息间发送的最小延迟,按固定值计算。'), @@ -101,7 +106,7 @@ export namespace Context { maxListeners: Schema.natural().default(64).description('每种监听器的最大数量。如果超过这个数量,Koishi 会认定为发生了内存泄漏,将产生一个警告。'), }).description('高级设置')) - Context.Config.list.push(Context.Config.Basic, Context.Config.Features, Context.Config.Advanced) + Context.Config.list.push(Context.Config.Basic, Context.Config.Message, Context.Config.Advanced) } // for backward compatibility diff --git a/packages/core/src/database.ts b/packages/core/src/database.ts index d9b12a4bda..472c3bc430 100644 --- a/packages/core/src/database.ts +++ b/packages/core/src/database.ts @@ -8,9 +8,12 @@ declare module './context' { 'model'(name: keyof Tables): void } - interface Context extends DatabaseService.Mixin { + interface Context { database: DatabaseService model: DatabaseService + getSelfIds(type?: string, assignees?: string[]): Dict + broadcast(content: string, forced?: boolean): Promise + broadcast(channels: readonly string[], content: string, forced?: boolean): Promise } } @@ -58,14 +61,6 @@ export interface Tables { channel: Channel } -export namespace DatabaseService { - export interface Mixin { - getSelfIds(type?: string, assignees?: string[]): Dict - broadcast(content: string, forced?: boolean): Promise - broadcast(channels: readonly string[], content: string, forced?: boolean): Promise - } -} - export class DatabaseService extends Database { static readonly methods = ['getSelfIds', 'broadcast'] @@ -94,6 +89,15 @@ export class DatabaseService extends Database { }, { primary: ['id', 'platform'], }) + + app.on('bot-added', (bot) => { + if (bot.platform in this.tables.user.fields) return + this.extend('user', { + [bot.platform]: { type: 'string', length: 63 }, + }, { + unique: [bot.platform as never], + }) + }) } getUser(platform: T, id: string, modifier?: Driver.Cursor): Promise & Record> diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index ce74ae1e87..1906a86add 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -4,8 +4,9 @@ import { Channel, User } from './database' import { Context } from './context' declare module './context' { - interface Context extends Internal.Mixin { + interface Context { $internal: Internal + middleware(middleware: Middleware, prepend?: boolean): () => boolean } interface Events { @@ -42,10 +43,6 @@ export namespace Internal { nickname?: string | string[] prefix?: Computed } - - export interface Mixin { - middleware(middleware: Middleware, prepend?: boolean): () => boolean - } } export class Internal { diff --git a/packages/core/src/selector.ts b/packages/core/src/selector.ts index 8f5db21ad6..99145d17c6 100644 --- a/packages/core/src/selector.ts +++ b/packages/core/src/selector.ts @@ -1,27 +1,11 @@ import { defineProperty, Logger, Promisify, remove } from '@koishijs/utils' import { GetEvents, Parameters, ReturnType, ThisType } from 'cordis' import { Context, Events } from './context' -import { Session } from './session' - -export type Filter = (session: Session) => boolean /* eslint-disable max-len */ declare module './context' { interface Context { - filter: Filter - selector: SelectorService logger(name: string): Logger - any(): this - never(): this - union(arg: Filter | Context): this - intersect(arg: Filter | Context): this - exclude(arg: Filter | Context): this - user(...values: string[]): this - self(...values: string[]): this - guild(...values: string[]): this - channel(...values: string[]): this - platform(...values: string[]): this - private(...values: string[]): this waterfall>(name: K, ...args: Parameters[K]>): Promisify[K]>> waterfall>(thisArg: ThisType[K]>, name: K, ...args: Parameters[K]>): Promisify[K]>> chain>(name: K, ...args: Parameters[K]>): ReturnType[K]> @@ -38,94 +22,25 @@ type BeforeEventName = OmitSubstring export type BeforeEventMap = { [E in keyof Events & string as OmitSubstring]: Events[E] } -function property(ctx: Context, key: K, ...values: Session[K][]) { - return ctx.intersect((session: Session) => { - return values.length ? values.includes(session[key]) : !!session[key] - }) -} - export class SelectorService { - static readonly methods = [ - 'any', 'never', 'union', 'intersect', 'exclude', 'select', - 'user', 'self', 'guild', 'channel', 'platform', 'private', - 'chain', 'waterfall', 'before', 'logger', 'setTimeout', 'setInterval', - ] + static readonly methods = ['chain', 'waterfall', 'before', 'logger', 'setTimeout', 'setInterval'] constructor(private app: C) { defineProperty(this, Context.current, app) - app.filter = () => true - app.on('internal/warning', (format, ...args) => { this.logger('app').warn(format, ...args) }) - - app.on('internal/runtime', (runtime) => { - if (!runtime.uid) return - runtime.ctx.filter = (session) => { - return runtime.children.some(p => p.ctx.filter(session)) - } - }) } protected get caller() { return this[Context.current] as Context } - any() { - return this.caller.extend({ filter: () => true }) - } - - never() { - return this.caller.extend({ filter: () => false }) - } - - union(arg: Filter | C) { - const caller = this.caller - const filter = typeof arg === 'function' ? arg : arg.filter - return this.caller.extend({ filter: s => caller.filter(s) || filter(s) }) - } - - intersect(arg: Filter | C) { - const caller = this.caller - const filter = typeof arg === 'function' ? arg : arg.filter - return this.caller.extend({ filter: s => caller.filter(s) && filter(s) }) - } - - exclude(arg: Filter | C) { - const caller = this.caller - const filter = typeof arg === 'function' ? arg : arg.filter - return this.caller.extend({ filter: s => caller.filter(s) && !filter(s) }) - } - logger(name: string) { return new Logger(name) } - user(...values: string[]) { - return property(this.caller, 'userId', ...values) - } - - self(...values: string[]) { - return property(this.caller, 'selfId', ...values) - } - - guild(...values: string[]) { - return property(this.caller, 'guildId', ...values) - } - - channel(...values: string[]) { - return property(this.caller, 'channelId', ...values) - } - - platform(...values: string[]) { - return property(this.caller, 'platform', ...values) - } - - private(...values: string[]) { - return property(this.caller.exclude(property(this.caller, 'guildId')), 'userId', ...values) - } - async waterfall(...args: [any, ...any[]]) { const thisArg = typeof args[0] === 'object' ? args.shift() : null const name = args.shift() diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index b0b1eea244..73c73031d8 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -1,4 +1,4 @@ -import { defineProperty, isNullable, Logger, makeArray, observe, Promisify, segment } from '@koishijs/utils' +import { defineProperty, isNullable, Logger, makeArray, observe, Promisify } from '@koishijs/utils' import * as satori from '@satorijs/core' import { Argv, Command } from './command' import { Context } from './context' @@ -49,10 +49,10 @@ export class Session { logger.warn(error) diff --git a/packages/core/tests/middleware.spec.ts b/packages/core/tests/middleware.spec.ts index 8cb39ab368..63ea4fbcfe 100644 --- a/packages/core/tests/middleware.spec.ts +++ b/packages/core/tests/middleware.spec.ts @@ -19,7 +19,7 @@ before(() => Logger.levels.base = 1) after(() => Logger.levels.base = 2) describe('Middleware Runtime', () => { - let callSequence: jest.Mock[] + let callSequence: jest.Mock[] beforeEach(() => { app.$internal._hooks = [] diff --git a/packages/core/tests/selector.spec.ts b/packages/core/tests/selector.spec.ts deleted file mode 100644 index 8fed49610f..0000000000 --- a/packages/core/tests/selector.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { App } from 'koishi' -import { expect } from 'chai' -import mock from '@koishijs/plugin-mock' - -const app = new App() -app.plugin(mock) -const guildSession = app.mock.session({ userId: '123', guildId: '456', subtype: 'group' }) -const privateSession = app.mock.session({ userId: '123', subtype: 'private' }) - -describe('Selector API', () => { - it('root context', () => { - expect(app.filter(guildSession)).to.be.true - expect(app.filter(privateSession)).to.be.true - }) - - it('context.prototype.user', () => { - expect(app.user().filter(guildSession)).to.be.true - expect(app.user().filter(privateSession)).to.be.true - expect(app.user('123').filter(guildSession)).to.be.true - expect(app.user('123').filter(privateSession)).to.be.true - expect(app.user('456').filter(guildSession)).to.be.false - expect(app.user('456').filter(privateSession)).to.be.false - }) - - it('context.prototype.private', () => { - expect(app.private().filter(guildSession)).to.be.false - expect(app.private().filter(privateSession)).to.be.true - expect(app.private().user('123').filter(guildSession)).to.be.false - expect(app.private().user('123').filter(privateSession)).to.be.true - expect(app.private().user('456').filter(guildSession)).to.be.false - expect(app.private().user('456').filter(privateSession)).to.be.false - }) - - it('context.prototype.guild', () => { - expect(app.guild().filter(guildSession)).to.be.true - expect(app.guild().filter(privateSession)).to.be.false - expect(app.guild('123').filter(guildSession)).to.be.false - expect(app.guild('123').filter(privateSession)).to.be.false - expect(app.guild('456').filter(guildSession)).to.be.true - expect(app.guild('456').filter(privateSession)).to.be.false - }) - - it('context chaining', () => { - expect(app.guild('456').user('123').filter(guildSession)).to.be.true - expect(app.guild('456').user('456').filter(guildSession)).to.be.false - expect(app.guild('123').user('123').filter(guildSession)).to.be.false - expect(app.user('123').guild('456').filter(guildSession)).to.be.true - expect(app.user('456').guild('456').filter(guildSession)).to.be.false - expect(app.user('123').guild('123').filter(guildSession)).to.be.false - }) - - it('context intersection', () => { - expect(app.guild('456', '789').guild('123', '456').filter(guildSession)).to.be.true - expect(app.guild('456', '789').guild('123', '789').filter(guildSession)).to.be.false - expect(app.guild('123', '789').guild('123', '456').filter(guildSession)).to.be.false - expect(app.user('123', '789').user('123', '456').filter(guildSession)).to.be.true - expect(app.user('456', '789').user('123', '456').filter(guildSession)).to.be.false - expect(app.user('123', '789').user('456', '789').filter(guildSession)).to.be.false - }) -}) diff --git a/packages/core/tests/tsconfig.json b/packages/core/tests/tsconfig.json new file mode 100644 index 0000000000..cbacece868 --- /dev/null +++ b/packages/core/tests/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tsconfig.base", +} \ No newline at end of file diff --git a/packages/koishi/package.json b/packages/koishi/package.json index 4cba98240f..f7c9d61d58 100644 --- a/packages/koishi/package.json +++ b/packages/koishi/package.json @@ -36,7 +36,7 @@ "dependencies": { "@koishijs/core": "^4.7.6", "@koishijs/utils": "^5.4.5", - "@satorijs/env-node": "^1.0.0", + "@satorijs/satori": "^1.0.1", "file-type": "^16.5.3", "ns-require": "^1.1.4" } diff --git a/packages/koishi/src/assets.ts b/packages/koishi/src/assets.ts index 76fb4a5b11..6f06e48c99 100644 --- a/packages/koishi/src/assets.ts +++ b/packages/koishi/src/assets.ts @@ -1,5 +1,5 @@ -import { Context, Schema, Service } from '@koishijs/core' -import { defineProperty, segment } from '@koishijs/utils' +import { Context, Schema, segment, Service } from '@koishijs/core' +import { defineProperty } from '@koishijs/utils' import { createHash } from 'crypto' import { basename } from 'path' import FileType from 'file-type' diff --git a/packages/koishi/src/index.ts b/packages/koishi/src/index.ts index 9125a24fe8..ca6e56215e 100644 --- a/packages/koishi/src/index.ts +++ b/packages/koishi/src/index.ts @@ -1,8 +1,11 @@ import { Context, Schema } from '@koishijs/core' +import { defineProperty } from '@koishijs/utils' import { Cache } from './cache' import { Assets } from './assets' +import * as satori from '@satorijs/satori' + +export { Quester, Router, WebSocketLayer } from '@satorijs/satori' -export { Quester, Router } from '@satorijs/env-node' export * from './assets' export * from './cache' export * from './patch' @@ -15,11 +18,23 @@ declare module '@koishijs/core' { assets: Assets cache: Cache } + + namespace Context { + namespace Config { + interface Static extends satori.Context.Config.Static {} + } + } } +defineProperty(Context.Config, 'Network', satori.Context.Config.Network) + +Context.Config.list.unshift(satori.Context.Config.Network) + Context.Config.list.push(Schema.object({ assets: Context.Config.Assets, })) +Context.Config.list.push(satori.Quester.Config) + Context.service('assets') Context.service('cache') diff --git a/packages/koishi/src/patch.ts b/packages/koishi/src/patch.ts index 956e53a2fe..d7da2d8b7d 100644 --- a/packages/koishi/src/patch.ts +++ b/packages/koishi/src/patch.ts @@ -1,15 +1,15 @@ import { Context } from '@koishijs/core' import ns from 'ns-require' -declare module '@koishijs/core' { +declare module 'cordis' { interface Context { - baseDir: string + plugin(path: string, config?: any): Fork } +} - namespace Registry { - interface Mixin { - plugin(path: string, config?: any): Fork - } +declare module '@koishijs/core' { + interface Context { + baseDir: string } }