Skip to content

Commit

Permalink
refa: refactor core to cordis v2 + satorijs
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jul 9, 2022
1 parent 7535ff7 commit 39fc058
Show file tree
Hide file tree
Showing 23 changed files with 276 additions and 1,215 deletions.
5 changes: 3 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@
},
"dependencies": {
"@koishijs/utils": "^5.4.5",
"cordis": "^1.6.0",
"@satorijs/core": "^1.0.0",
"cordis": "^2.0.3",
"fastest-levenshtein": "^1.0.12",
"minato": "^1.2.1"
}
}
}
40 changes: 40 additions & 0 deletions packages/core/src/bot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Dict, makeArray, sleep } from '@koishijs/utils'
import { Context } from './context'
import { Session } from './session'
import * as satori from '@satorijs/core'

declare module '@satorijs/core' {
interface Bot {
getGuildMemberMap(guildId: string): Promise<Dict<string>>
broadcast(channels: (string | [string, string])[], content: string, delay?: number): Promise<string[]>
}
}

export { satori }
export { Adapter } from '@satorijs/core'

export const Bot = satori.Bot<Context>
export type Bot = satori.Bot<Context>

Bot.prototype.session = function session(this: Bot, payload) {
return new Session(this, payload)
}

Bot.prototype.getGuildMemberMap = async function getGuildMemberMap(this: Bot, guildId) {
const list = await this.getGuildMemberList(guildId)
return Object.fromEntries(list.map(info => [info.userId, info.nickname || info.username]))
}

Bot.prototype.broadcast = async function broadcast(this: Bot, channels, content, delay = this.ctx.options.delay.broadcast) {
const messageIds: string[] = []
for (let index = 0; index < channels.length; index++) {
if (index && delay) await sleep(delay)
try {
const [channelId, guildId] = makeArray(channels[index])
messageIds.push(...await this.sendMessage(channelId, content, guildId))
} catch (error) {
this.ctx.logger('bot').warn(error)
}
}
return messageIds
}
9 changes: 4 additions & 5 deletions packages/core/src/command/command.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { coerce, Dict, Logger, remove, Schema } from '@koishijs/utils'
import { Awaitable } from 'cosmokit'
import { Context, Disposable } from 'cordis'
import { Awaitable, coerce, Dict, Logger, remove, Schema } from '@koishijs/utils'
import { Context, Disposable } from '../context'
import { Argv } from './parser'
import { Next } from '../protocol'
import { Next } from '../internal'
import { Channel, User } from '../database'
import { Computed, FieldCollector, Session } from '../protocol/session'
import { Computed, FieldCollector, Session } from '../session'

const logger = new Logger('command')

Expand Down
16 changes: 7 additions & 9 deletions packages/core/src/command/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Context } from 'cordis'
import { Awaitable } from 'cosmokit'
import { Awaitable, defineProperty } from '@koishijs/utils'
import { Context } from '../context'
import { Command } from './command'
import { Argv } from './parser'
import runtime from './runtime'
import validate from './validate'
import { Channel, User } from '../database'
import { Session } from '../protocol'
import { Session } from '../session'

export * from './command'
export * from './runtime'
Expand All @@ -16,7 +16,7 @@ interface CommandMap extends Map<string, Command> {
resolve(key: string): Command
}

declare module 'cordis' {
declare module '../context' {
interface Context extends Commander.Mixin {
$commander: Commander
}
Expand All @@ -43,13 +43,14 @@ export namespace Commander {

export class Commander {
static readonly key = '$commander'
static readonly methods = ['command']

_commandList: Command[] = []
_commands = new Map<string, Command>() as CommandMap
_shortcuts: Command.Shortcut[] = []

constructor(private ctx: Context, private config: Commander.Config = {}) {
this[Context.current] = ctx
defineProperty(this, Context.current, ctx)
ctx.plugin(runtime)
ctx.plugin(validate)
}
Expand Down Expand Up @@ -127,7 +128,4 @@ export class Commander {
}
}

Context.service(Commander.key, {
constructor: Commander,
methods: ['command'],
})
Context.service(Commander.key, Commander)
6 changes: 3 additions & 3 deletions packages/core/src/command/parser.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { camelCase, Dict, escapeRegExp, paramCase, segment, Time } from '@koishijs/utils'
import { Command } from './command'
import { Context } from 'cordis'
import { Context } from '../context'
import { Channel, User } from '../database'
import { Session } from '../protocol/session'
import { Next } from '../protocol'
import { Session } from '../session'
import { Next } from '../internal'

export interface Token {
rest?: string
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/command/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineProperty, valueMap } from '@koishijs/utils'
import { Argv } from './parser'
import { Context } from 'cordis'
import { Session } from '../protocol/session'
import { Context } from '../context'
import { Session } from '../session'

export default function runtime(ctx: Context) {
ctx.before('parse', (content, session) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/command/validate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Argv } from './parser'
import { Context } from 'cordis'
import { Context } from '../context'

export default function validate(ctx: Context) {
// add user fields
Expand Down
112 changes: 112 additions & 0 deletions packages/core/src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Awaitable, defineProperty, Schema, Time } from '@koishijs/utils'
import * as satori from '@satorijs/core'
import * as cordis from 'cordis'
import { Computed, Session } from './session'

export type Plugin = cordis.Plugin<Context>

export namespace Plugin {
export type Function<T = any> = cordis.Plugin.Function<T, Context>
export type Constructor<T = any> = cordis.Plugin.Constructor<T, Context>
export type Object<S = any, T = any> = cordis.Plugin.Object<S, T, Context>
}

export type State = cordis.State<Context>
export type Fork = cordis.Fork<Context>
export type Runtime = cordis.Runtime<Context>

export const Service = cordis.Service<Context>

export { Disposable } from 'cordis'

export interface Events<C extends Context = Context> extends satori.Events<C> {
'appellation'(name: string, session: Session): string
}

export interface Context {
[Context.events]: Events<this>
[Context.session]: Session
}

export class Context<T extends Context.Config = Context.Config> extends satori.Context<T> {
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 namespace Config {
export interface Basic {
locale?: string
prefix?: Computed<string | string[]>
nickname?: string | string[]
autoAssign?: Computed<Awaitable<boolean>>
autoAuthorize?: Computed<Awaitable<number>>
}

export interface Features {
delay?: DelayConfig
}

export interface DelayConfig {
character?: number
message?: number
cancel?: number
broadcast?: number
prompt?: number
}

export interface Advanced {
maxListeners?: number
prettyErrors?: boolean
}

export interface Static extends Schema<Config> {
Basic: Schema<Basic>
Features: Schema<Features>
Advanced: Schema<Advanced>
}
}

defineProperty(Context.Config, 'Basic', Schema.object({
locale: Schema.string().default('zh').description('默认使用的语言。'),
prefix: Schema.union([
Schema.array(String),
Schema.transform(String, (prefix) => [prefix]),
] as const).default(['']).description('指令前缀字符,可以是字符串或字符串数组。将用于指令前缀的匹配。'),
nickname: Schema.union([
Schema.array(String),
Schema.transform(String, (nickname) => [nickname]),
] as const).description('机器人的昵称,可以是字符串或字符串数组。将用于指令前缀的匹配。'),
autoAssign: Schema.union([Boolean, Function]).default(true).description('当获取不到频道数据时,是否使用接受者作为代理者。'),
autoAuthorize: Schema.union([Schema.natural(), Function]).default(1).description('当获取不到用户数据时默认使用的权限等级。'),
}).description('基础设置'))

defineProperty(Context.Config, 'Features', 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()` 时消息间发送的最小延迟,按固定值计算。'),
cancel: Schema.natural().role('ms').default(0).description('调用 `session.cancelQueued()` 时默认的延迟。'),
broadcast: Schema.natural().role('ms').default(0.5 * Time.second).description('调用 `bot.broadcast()` 时默认的延迟。'),
prompt: Schema.natural().role('ms').default(Time.minute).description('调用 `session.prompt()` 时默认的等待时间。'),
}),
}).description('消息设置'))

defineProperty(Context.Config, 'Advanced', Schema.object({
prettyErrors: Schema.boolean().default(true).description('启用报错优化模式。在此模式下 Koishi 会对程序抛出的异常进行整理,过滤掉框架内部的调用记录,输出更易读的提示信息。'),
maxListeners: Schema.natural().default(64).description('每种监听器的最大数量。如果超过这个数量,Koishi 会认定为发生了内存泄漏,将产生一个警告。'),
}).description('高级设置'))

Context.Config.list.push(Context.Config.Basic, Context.Config.Features, Context.Config.Advanced)
}

// for backward compatibility
export { Context as App }

export function defineConfig(config: Context.Config) {
return config
}
15 changes: 7 additions & 8 deletions packages/core/src/database.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as utils from '@koishijs/utils'
import { Dict, MaybeArray } from '@koishijs/utils'
import { defineProperty, Dict, MaybeArray } from '@koishijs/utils'
import { Database, Driver, Result, Update } from 'minato'
import { Context, Plugin } from 'cordis'
import { Context, Plugin } from './context'

declare module 'cordis' {
declare module './context' {
interface Events {
'model'(name: keyof Tables): void
}
Expand Down Expand Up @@ -67,9 +67,11 @@ export namespace DatabaseService {
}

export class DatabaseService extends Database<Tables> {
static readonly methods = ['getSelfIds', 'broadcast']

constructor(protected app: Context) {
super()
this[Context.current] = app
defineProperty(this, Context.current, app)

this.extend('user', {
// TODO v5: change to number
Expand Down Expand Up @@ -180,10 +182,7 @@ DatabaseService.prototype.extend = function extend(this: DatabaseService, name,
}

Context.service('database')
Context.service('model', {
constructor: DatabaseService,
methods: ['getSelfIds', 'broadcast'],
})
Context.service('model', DatabaseService)

export const defineDriver = <T>(constructor: Driver.Constructor<T>, schema?: utils.Schema<T>, prepare?: Plugin.Function<T>): Plugin.Object<T> => ({
name: constructor.name,
Expand Down
8 changes: 3 additions & 5 deletions packages/core/src/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Dict, isNullable, Logger, Random, Time } from '@koishijs/utils'
import { Context } from 'cordis'
import { Context } from './context'

const logger = new Logger('i18n')
const kTemplate = Symbol('template')

declare module 'cordis' {
declare module './context' {
interface Context {
i18n: I18n
}
Expand Down Expand Up @@ -171,6 +171,4 @@ export class I18n {
}
}

Context.service('i18n', {
constructor: I18n,
})
Context.service('i18n', I18n)
Loading

0 comments on commit 39fc058

Please sign in to comment.