koishi-core
Version:
Core features for Koishi
944 lines (943 loc) • 40.4 kB
TypeScript
/// <reference types="lru-cache" />
/// <reference types="node" />
/// <reference types="koa__router" />
/// <reference types="ws" />
import * as utils from 'koishi-utils';
import { MaybeArray, Get, Extract, Observed, Logger, Promisify, Awaitable } from 'koishi-utils';
import LruCache from 'lru-cache';
import { AxiosRequestConfig } from 'axios';
import { Server } from 'http';
import { inspect } from 'util';
import Router from '@koa/router';
import WebSocket from 'ws';
export type TableType = keyof Tables;
type Primitive = string | number;
type Comparable = Primitive | Date;
type Keys<O, T = any> = string & {
[K in keyof O]: O[K] extends T ? K : never;
}[keyof O];
export interface Tables {
user: User;
channel: Channel;
}
export namespace Tables {
interface Field<T = any> {
type: Field.Type<T>;
length?: number;
nullable?: boolean;
initial?: T;
precision?: number;
scale?: number;
}
namespace Field {
export const number: Type[];
export const string: Type[];
export const date: Type[];
export const object: Type[];
export type Type<T = any> = T extends number ? 'integer' | 'unsigned' | 'float' | 'double' | 'decimal' : T extends string ? 'char' | 'string' | 'text' : T extends Date ? 'timestamp' | 'date' | 'time' : T extends any[] ? 'list' | 'json' : T extends object ? 'json' : never;
type WithParam<S extends string> = S | `${S}(${any})`;
export type Extension<O = any> = {
[K in keyof O]?: Field<O[K]> | WithParam<Type<O[K]>>;
};
export type Config<O = any> = {
[K in keyof O]?: Field<O[K]>;
};
export function parse(source: string | Field): Field;
export {};
}
interface Extension<O = any> {
type?: 'random' | 'incremental';
primary?: Keys<O>;
unique?: MaybeArray<Keys<O>>[];
foreign?: {
[K in keyof O]?: [TableType, string];
};
fields?: Field.Extension<O>;
}
interface Config<O = any> extends Extension<O> {
fields?: Field.Config<O>;
}
const config: Record<string, Config>;
function extend<T extends TableType>(name: T, meta?: Extension<Tables[T]>): void;
function create<T extends TableType>(name: T): Tables[T];
}
export type Query<T extends TableType> = Query.Expr<Tables[T]> | Query.Shorthand<Primitive>;
export namespace Query {
export type Field<T extends TableType> = string & keyof Tables[T];
export type Index<T extends TableType> = Keys<Tables[T], Primitive>;
export interface FieldExpr<T = any> {
$in?: Extract<T, Primitive, T[]>;
$nin?: Extract<T, Primitive, T[]>;
$eq?: Extract<T, Comparable>;
$ne?: Extract<T, Comparable>;
$gt?: Extract<T, Comparable>;
$gte?: Extract<T, Comparable>;
$lt?: Extract<T, Comparable>;
$lte?: Extract<T, Comparable>;
$el?: T extends (infer U)[] ? FieldQuery<U> : never;
$size?: Extract<T, any[], number>;
$regex?: Extract<T, string, RegExp>;
$regexFor?: Extract<T, string>;
$bitsAllClear?: Extract<T, number>;
$bitsAllSet?: Extract<T, number>;
$bitsAnyClear?: Extract<T, number>;
$bitsAnySet?: Extract<T, number>;
}
export interface LogicalExpr<T = any> {
$or?: Expr<T>[];
$and?: Expr<T>[];
$not?: Expr<T>;
$expr?: Eval.Boolean<T>;
}
export type Shorthand<T = any> = Extract<T, Comparable> | Extract<T, Primitive, T[]> | Extract<T, string, RegExp>;
export type FieldQuery<T = any> = FieldExpr<T> | Shorthand<T>;
export type Expr<T = any> = LogicalExpr<T> & {
[K in keyof T]?: FieldQuery<T[K]>;
};
export function resolve<T extends TableType>(name: T, query?: Query<T>): Expr<Tables[T]>;
export interface ModifierExpr<K extends string> {
limit?: number;
offset?: number;
fields?: K[];
}
export type Modifier<T extends string> = T[] | ModifierExpr<T>;
export function resolveModifier<K extends string>(modifier: Modifier<K>): ModifierExpr<K>;
type Projection<T extends TableType> = Record<string, Eval.Aggregation<Tables[T]>>;
type MapEval<T, P> = {
[K in keyof P]: Eval<T, P[K]>;
};
export interface Database {
drop(table?: TableType): Promise<void>;
get<T extends TableType, K extends Field<T>>(table: T, query: Query<T>, modifier?: Modifier<K>): Promise<Pick<Tables[T], K>[]>;
remove<T extends TableType>(table: T, query: Query<T>): Promise<void>;
create<T extends TableType>(table: T, data: Partial<Tables[T]>): Promise<Tables[T]>;
update<T extends TableType>(table: T, data: Partial<Tables[T]>[], key?: Index<T>): Promise<void>;
aggregate<T extends TableType, P extends Projection<T>>(table: T, fields: P, query?: Query<T>): Promise<MapEval<T, P>>;
}
export {};
}
export type Eval<T, U> = U extends number ? number : U extends boolean ? boolean : U extends string ? Get<T, U> : U extends Eval.NumericExpr ? number : U extends Eval.BooleanExpr ? boolean : U extends Eval.AggregationExpr ? number : never;
export namespace Eval {
type Any<T = any, A = never> = A | number | boolean | Keys<T> | NumericExpr<T, A> | BooleanExpr<T, A>;
type GeneralExpr = NumericExpr & BooleanExpr & AggregationExpr;
type Numeric<T = any, A = never> = A | number | Keys<T, number> | NumericExpr<T, A>;
type Boolean<T = any, A = never> = boolean | Keys<T, boolean> | BooleanExpr<T, A>;
type Aggregation<T = any> = Any<{}, AggregationExpr<T>>;
interface NumericExpr<T = any, A = never> {
$add?: Numeric<T, A>[];
$multiply?: Numeric<T, A>[];
$subtract?: [Numeric<T, A>, Numeric<T, A>];
$divide?: [Numeric<T, A>, Numeric<T, A>];
}
interface BooleanExpr<T = any, A = never> {
$eq?: [Numeric<T, A>, Numeric<T, A>];
$ne?: [Numeric<T, A>, Numeric<T, A>];
$gt?: [Numeric<T, A>, Numeric<T, A>];
$gte?: [Numeric<T, A>, Numeric<T, A>];
$lt?: [Numeric<T, A>, Numeric<T, A>];
$lte?: [Numeric<T, A>, Numeric<T, A>];
}
interface AggregationExpr<T = any> {
$sum?: Any<T>;
$avg?: Any<T>;
$max?: Any<T>;
$min?: Any<T>;
$count?: Any<T>;
}
}
export interface User extends Record<Platform, string> {
id: string;
flag: number;
authority: number;
name: string;
usage: Record<string, number>;
timers: Record<string, number>;
}
export namespace User {
export enum Flag {
ignore = 1
}
export type Field = keyof User;
export const fields: Field[];
export type Index = Platform | 'name' | 'id';
export type Observed<K extends Field = Field> = utils.Observed<Pick<User, K>, Promise<void>>;
type Getter = <T extends Index>(type: T, id: string) => Partial<User>;
/**
* @deprecated use `Tables.extend('user', { fields })` instead
*/
export function extend(getter: Getter): void;
export function create<T extends Index>(type: T, id: string): User;
export interface Database {
getUser<K extends Field, T extends Index>(type: T, id: string, modifier?: Query.Modifier<K>): Promise<Pick<User, K | T>>;
getUser<K extends Field, T extends Index>(type: T, ids: string[], modifier?: Query.Modifier<K>): Promise<Pick<User, K>[]>;
getUser<K extends Field, T extends Index>(type: T, id: MaybeArray<string>, modifier?: Query.Modifier<K>): Promise<any>;
setUser<T extends Index>(type: T, id: string, data: Partial<User>): Promise<void>;
createUser<T extends Index>(type: T, id: string, data: Partial<User>): Promise<void>;
}
export {};
}
export interface Channel {
id: string;
flag: number;
assignee: string;
disable: string[];
}
export namespace Channel {
export enum Flag {
ignore = 1,
silent = 4
}
export type Field = keyof Channel;
export const fields: Field[];
export type Observed<K extends Field = Field> = utils.Observed<Pick<Channel, K>, Promise<void>>;
type Getter = (type: Platform, id: string) => Partial<Channel>;
/**
* @deprecated use `Tables.extend('user', { fields })` instead
*/
export function extend(getter: Getter): void;
export function create(type: Platform, id: string): Channel;
export interface Database {
getChannel<K extends Field>(type: Platform, id: string, modifier?: Query.Modifier<K>): Promise<Pick<Channel, K | 'id'>>;
getChannel<K extends Field>(type: Platform, ids: readonly string[], modifier?: Query.Modifier<K>): Promise<Pick<Channel, K>[]>;
getChannel<K extends Field>(type: Platform, id: MaybeArray<string>, modifier?: Query.Modifier<K>): Promise<any>;
getAssignedChannels<K extends Field>(fields?: K[], assignMap?: Record<string, readonly string[]>): Promise<Pick<Channel, K>[]>;
setChannel(type: Platform, id: string, data: Partial<Channel>): Promise<void>;
createChannel(type: Platform, id: string, data: Partial<Channel>): Promise<void>;
}
export {};
}
export interface Database extends Query.Database, User.Database, Channel.Database {
}
type Methods<S, T> = {
[K in keyof S]?: S[K] extends (...args: infer R) => infer U ? (this: T, ...args: R) => U : S[K];
};
export namespace Database {
export interface Statics {
}
type Constructor<T> = new (...args: any[]) => T;
type ExtensionMethods<T> = Methods<Database, T extends Constructor<infer I> ? I : never>;
type Extension<T> = ((Database: T) => void) | ExtensionMethods<T>;
export function extend<K extends keyof Statics>(module: K, extension: Extension<Statics[K]>): void;
export function extend<T extends Constructor<unknown>>(module: T, extension: Extension<T>): void;
export {};
}
export interface Assets {
types: readonly Assets.Type[];
upload(url: string, file: string): Promise<string>;
stats(): Promise<Assets.Stats>;
}
export namespace Assets {
type Type = 'image' | 'audio' | 'video' | 'file';
interface Stats {
assetCount?: number;
assetSize?: number;
}
}
export interface HelpConfig extends Command.Config {
shortcut?: boolean;
options?: boolean;
}
export function getCommandNames(session: Session): string[];
export interface DelayOptions {
character?: number;
message?: number;
cancel?: number;
broadcast?: number;
prompt?: number;
}
export interface AppOptions extends BotOptions {
port?: number;
host?: string;
bots?: BotOptions[];
prefix?: string | string[] | ((session: Session.Message) => void | string | string[]);
nickname?: string | string[];
maxListeners?: number;
prettyErrors?: boolean;
processMessage?: (message: string) => string;
delay?: DelayOptions;
help?: boolean | HelpConfig;
autoAssign?: boolean | ((session: Session) => boolean);
autoAuthorize?: number | ((session: Session) => number);
userCacheAge?: number;
userCacheLength?: number;
channelCacheLength?: number;
channelCacheAge?: number;
minSimilarity?: number;
selfUrl?: string;
axiosConfig?: AxiosRequestConfig;
}
interface CommandMap extends Map<string, Command> {
resolve(key: string): Command;
}
export class App extends Context {
app: this;
options: AppOptions;
status: App.Status;
adapters: Adapter.Instances;
registry: Plugin.Registry;
_bots: Bot<never>[] & Record<string, Bot<never>>;
_commandList: Command[];
_commands: CommandMap;
_shortcuts: Command.Shortcut[];
_hooks: Record<keyof any, [Context, (...args: any[]) => any][]>;
_userCache: Record<string, LruCache<string, Observed<Partial<User>, Promise<void>>>>;
_channelCache: LruCache<string, Observed<Partial<Channel>, Promise<void>>>;
_httpServer?: Server;
_sessions: Record<string, Session>;
private _nameRE;
static defaultConfig: AppOptions;
constructor(options?: AppOptions);
createServer(): void;
prepare(): void;
start(): Promise<void>;
private _listen;
stop(): Promise<void>;
private _close;
private _resolvePrefixes;
private _process;
private _suggest;
private _handleMessage;
private _handleArgv;
private _handleShortcut;
}
export namespace App {
enum Status {
closed = 0,
opening = 1,
open = 2,
closing = 3
}
}
export type NextFunction = (next?: NextFunction) => Promise<void>;
export type Middleware = (session: Session, next: NextFunction) => any;
export type Disposable = () => void;
export type Plugin<T = any> = Plugin.Function<T> | Plugin.Object<T>;
export namespace Plugin {
export type Function<T = any> = (ctx: Context, options: T) => void;
export interface Meta {
name?: string;
sideEffect?: boolean;
}
export interface Object<T = any> extends Meta {
apply: Function<T>;
}
export type Config<T extends Plugin> = T extends Function<infer U> ? U : T extends Object<infer U> ? U : never;
export interface State<T = any> extends Meta {
id?: string;
parent?: State;
context?: Context;
config?: T;
plugin?: Plugin;
children: Plugin[];
disposables: Disposable[];
}
export interface Packages {
}
export type Teleporter<D extends readonly (keyof Packages)[]> = (ctx: Context, ...modules: From<D>) => void;
type From<D extends readonly unknown[]> = D extends readonly [infer L, ...infer R] ? [L extends keyof Packages ? Packages[L] : unknown, ...From<R>] : [];
export class Registry extends Map<Plugin, State> {
resolve(plugin: Plugin): Function<any>;
get(plugin: Plugin): State<any>;
set(plugin: Plugin, state: State): this;
has(plugin: Plugin): boolean;
delete(plugin: Plugin): boolean;
}
export {};
}
type Filter = (session: Session) => boolean;
type PartialSeletor<T> = (...values: T[]) => Context;
interface Selector<T> extends PartialSeletor<T> {
except?: PartialSeletor<T>;
}
export interface Context extends Context.Delegates {
}
export class Context {
filter: Filter;
app?: App;
private _plugin;
static readonly middleware: unique symbol;
static readonly current: unique symbol;
protected _bots: Bot[] & Record<string, Bot>;
protected constructor(filter: Filter, app?: App, _plugin?: Plugin);
private static inspect;
[inspect.custom](): string;
private createSelector;
get user(): Selector<string>;
get self(): Selector<string>;
get group(): Selector<string>;
get channel(): Selector<string>;
get platform(): Selector<never>;
get private(): Selector<string>;
get bots(): Bot<never>[] & Record<string, Bot<never>>;
logger(name: string): Logger;
select<K extends keyof Session>(key: K, ...values: Session[K][]): Context;
unselect<K extends keyof Session>(key: K, ...values: Session[K][]): Context;
all(): Context;
union(arg: Filter | Context): Context;
intersect(arg: Filter | Context): Context;
except(arg: Filter | Context): Context;
match(session?: Session): boolean;
get state(): Plugin.State<any>;
addSideEffect(state?: Plugin.State<any>): void;
private teleport;
with<D extends readonly (keyof Plugin.Packages)[]>(deps: D, callback: Plugin.Teleporter<D>): this;
plugin<T extends Plugin>(plugin: T, options?: boolean | Plugin.Config<T>): this;
dispose(plugin?: Plugin<any>): Promise<void>;
parallel<K extends EventName>(name: K, ...args: Parameters<EventMap[K]>): Promise<void>;
parallel<K extends EventName>(session: Session, name: K, ...args: Parameters<EventMap[K]>): Promise<void>;
emit<K extends EventName>(name: K, ...args: Parameters<EventMap[K]>): void;
emit<K extends EventName>(session: Session, name: K, ...args: Parameters<EventMap[K]>): void;
waterfall<K extends EventName>(name: K, ...args: Parameters<EventMap[K]>): Promisify<ReturnType<EventMap[K]>>;
waterfall<K extends EventName>(session: Session, name: K, ...args: Parameters<EventMap[K]>): Promisify<ReturnType<EventMap[K]>>;
chain<K extends EventName>(name: K, ...args: Parameters<EventMap[K]>): ReturnType<EventMap[K]>;
chain<K extends EventName>(session: Session, name: K, ...args: Parameters<EventMap[K]>): ReturnType<EventMap[K]>;
serial<K extends EventName>(name: K, ...args: Parameters<EventMap[K]>): Promisify<ReturnType<EventMap[K]>>;
serial<K extends EventName>(session: Session, name: K, ...args: Parameters<EventMap[K]>): Promisify<ReturnType<EventMap[K]>>;
bail<K extends EventName>(name: K, ...args: Parameters<EventMap[K]>): ReturnType<EventMap[K]>;
bail<K extends EventName>(session: Session, name: K, ...args: Parameters<EventMap[K]>): ReturnType<EventMap[K]>;
on<K extends EventName>(name: K, listener: EventMap[K], prepend?: boolean): () => boolean;
before<K extends BeforeEventName>(name: K, listener: BeforeEventMap[K], append?: boolean): () => boolean;
once<K extends EventName>(name: K, listener: EventMap[K], prepend?: boolean): () => boolean;
off<K extends EventName>(name: K, listener: EventMap[K]): boolean;
middleware(middleware: Middleware, prepend?: boolean): () => boolean;
private createTimerDispose;
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): () => boolean;
setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): () => boolean;
command<D extends string>(def: D, config?: Command.Config): Command<never, never, Argv.ArgumentType<D>>;
command<D extends string>(def: D, desc: string, config?: Command.Config): Command<never, never, Argv.ArgumentType<D>>;
transformAssets(content: string, assets?: Assets): Promise<string>;
getBot(platform: Platform, selfId?: string): Bot<never>;
getSelfIds(type?: Platform, assignees?: readonly string[]): Record<string, readonly string[]>;
broadcast(content: string, forced?: boolean): Promise<string[]>;
broadcast(channels: readonly string[], content: string, forced?: boolean): Promise<string[]>;
static delegate(key: string & keyof Context): void;
}
export namespace Context {
interface Delegates {
database: Database;
assets: Assets;
router: Router;
}
}
type FlattenEvents<T> = {
[K in keyof T & string]: K | `${K}/${FlattenEvents<T[K]>}`;
}[keyof T & string];
type SessionEventMap = {
[K in FlattenEvents<Session.Events>]: K extends `${infer X}/${infer R}` ? R extends `${infer Y}/${any}` ? (session: Session.Payload<X, Y>) => void : (session: Session.Payload<X, R>) => void : (session: Session.Payload<K>) => void;
};
type DelegateEventMap = {
[K in keyof Context.Delegates as `delegate/${K}`]: () => void;
};
type EventName = keyof EventMap;
type OmitSubstring<S extends string, T extends string> = S extends `${infer L}${T}${infer R}` ? `${L}${R}` : never;
type BeforeEventName = OmitSubstring<EventName & string, 'before-'>;
export type BeforeEventMap = {
[E in EventName & string as OmitSubstring<E, 'before-'>]: EventMap[E];
};
export interface EventMap extends SessionEventMap, DelegateEventMap {
[Context.middleware]: Middleware;
'appellation'(name: string, session: Session): string;
'before-parse'(content: string, session: Session): Argv;
'parse'(argv: Argv, session: Session): string;
'before-attach-channel'(session: Session, fields: Set<Channel.Field>): void;
'attach-channel'(session: Session): Awaitable<void | boolean>;
'before-attach-user'(session: Session, fields: Set<User.Field>): void;
'attach-user'(session: Session): Awaitable<void | boolean>;
'before-attach'(session: Session): void;
'attach'(session: Session): void;
'before-send'(session: Session<never, never, Platform, 'send'>): Awaitable<void | boolean>;
'before-command'(argv: Argv): Awaitable<void | string>;
'command'(argv: Argv): Awaitable<void>;
'command-added'(command: Command): void;
'command-removed'(command: Command): void;
'middleware'(session: Session): void;
'plugin-added'(plugin: Plugin, registry: Map<Plugin, Plugin.State>): void;
'plugin-removed'(plugin: Plugin, registry: Map<Plugin, Plugin.State>): void;
'before-connect'(): Awaitable<void>;
'connect'(): void;
'before-disconnect'(): Awaitable<void>;
'disconnect'(): void;
}
export interface Token {
rest?: string;
content: string;
quoted: boolean;
terminator: string;
inters: Argv[];
}
export interface Argv<U extends User.Field = never, G extends Channel.Field = never, A extends any[] = any[], O = {}> {
args?: A;
options?: O;
error?: string;
source?: string;
initiator?: string;
terminator?: string;
session?: Session<U, G>;
command?: Command<U, G, A, O>;
rest?: string;
pos?: number;
root?: boolean;
tokens?: Token[];
name?: string;
next?: NextFunction;
}
export namespace Argv {
export interface Interpolation {
terminator?: string;
parse?(source: string): Argv;
}
export function interpolate(initiator: string, terminator: string, parse?: (source: string) => Argv): void;
export class Tokenizer {
private bracs;
constructor();
interpolate(initiator: string, terminator: string, parse?: (source: string) => Argv): void;
parseToken(source: string, stopReg?: string): Token;
parse(source: string, terminator?: string): Argv;
stringify(argv: Argv): string;
}
export function parse(source: string, terminator?: string): Argv<never, never, any[], {}>;
export function stringify(argv: Argv): string;
export function revert(token: Token): void;
export function parsePid(target: string): [Platform, string];
export interface Domain {
string: string;
number: number;
boolean: boolean;
text: string;
rawtext: string;
user: string;
channel: string;
integer: number;
posint: number;
natural: number;
date: Date;
}
type DomainType = keyof Domain;
type ParamType<S extends string, F> = S extends `${any}:${infer T}` ? T extends DomainType ? Domain[T] : F : F;
type Replace<S extends string, X extends string, Y extends string> = S extends `${infer L}${X}${infer R}` ? `${L}${Y}${Replace<R, X, Y>}` : S;
type ExtractAll<S extends string, F> = S extends `${infer L}]${infer R}` ? [ParamType<L, F>, ...ExtractAll<R, F>] : [];
type ExtractFirst<S extends string, F> = S extends `${infer L}]${any}` ? ParamType<L, F> : boolean;
type ExtractSpread<S extends string> = S extends `${infer L}...${infer R}` ? [...ExtractAll<L, string>, ...ExtractFirst<R, string>[]] : [...ExtractAll<S, string>, ...string[]];
export type ArgumentType<S extends string> = ExtractSpread<Replace<S, '>', ']'>>;
export type OptionType<S extends string> = ExtractFirst<Replace<S, '>', ']'>, any>;
export type Type = DomainType | RegExp | string[] | Transform<any>;
export interface Declaration {
name?: string;
type?: Type;
fallback?: any;
variadic?: boolean;
required?: boolean;
}
export type Transform<T> = (source: string, session: Session) => T;
export interface DomainConfig<T> {
transform?: Transform<T>;
greedy?: boolean;
}
export function createDomain<K extends keyof Domain>(name: K, transform: Transform<Domain[K]>, options?: DomainConfig<Domain[K]>): void;
export function parseValue(source: string, quoted: boolean, kind: string, argv: Argv, decl?: Declaration): any;
export interface OptionConfig<T extends Type = Type> {
value?: any;
fallback?: any;
type?: T;
/** hide the option by default */
hidden?: boolean | ((session: Session) => boolean);
authority?: number;
notUsage?: boolean;
}
export interface TypedOptionConfig<T extends Type> extends OptionConfig<T> {
type: T;
}
export interface OptionDeclaration extends Declaration, OptionConfig {
description?: string;
values?: Record<string, any>;
}
type OptionDeclarationMap = Record<string, OptionDeclaration>;
export class CommandBase {
name: string;
description: string;
declaration: string;
_arguments: Declaration[];
_options: OptionDeclarationMap;
private _namedOptions;
private _symbolicOptions;
constructor(name: string, declaration: string, description: string);
_createOption(name: string, def: string, config: OptionConfig): void;
private _assignOption;
removeOption<K extends string>(name: K): boolean;
parse(argv: Argv): Argv;
parse(source: string, terminator?: string, args?: any[], options?: Record<string, any>): Argv;
private stringifyArg;
stringify(args: readonly string[], options: any): string;
}
export {};
}
export type UserType<T, U extends User.Field = User.Field> = T | ((user: Pick<User, U>) => T);
export type Extend<O extends {}, K extends string, T> = {
[P in K | keyof O]?: (P extends keyof O ? O[P] : unknown) & (P extends K ? T : unknown);
};
export namespace Command {
interface Config {
/** hide all options by default */
hideOptions?: boolean;
/** hide command */
hidden?: boolean;
/** min authority */
authority?: number;
/** disallow unknown options */
checkUnknown?: boolean;
/** check argument count */
checkArgCount?: boolean;
/** show command warnings */
showWarning?: boolean;
/** usage identifier */
usageName?: string;
/** max usage per day */
maxUsage?: UserType<number>;
/** min interval */
minInterval?: UserType<number>;
/** depend on existing commands */
patch?: boolean;
}
interface Shortcut {
name?: string | RegExp;
command?: Command;
authority?: number;
prefix?: boolean;
fuzzy?: boolean;
args?: string[];
options?: Record<string, any>;
}
type Action<U extends User.Field = never, G extends Channel.Field = never, A extends any[] = any[], O extends {} = {}> = (argv: Argv<U, G, A, O>, ...args: A) => Awaitable<void | string>;
type Usage<U extends User.Field = never, G extends Channel.Field = never> = string | ((session: Session<U, G>) => Awaitable<string>);
}
export class Command<U extends User.Field = never, G extends Channel.Field = never, A extends any[] = any[], O extends {} = {}> extends Argv.CommandBase {
context: Context;
config: Command.Config;
children: Command[];
parent: Command;
_aliases: string[];
_examples: string[];
_usage?: Command.Usage;
_disposed?: boolean;
_disposables?: Disposable[];
private _userFields;
private _channelFields;
private _actions;
private _checkers;
static defaultConfig: Command.Config;
static defaultOptionConfig: Argv.OptionConfig;
private static _userFields;
private static _channelFields;
static userFields(fields: FieldCollector<'user'>): typeof Command;
static channelFields(fields: FieldCollector<'channel'>): typeof Command;
constructor(name: string, decl: string, desc: string, context: Context);
get app(): App;
private _registerAlias;
[inspect.custom](): string;
userFields<T extends User.Field = never>(fields: FieldCollector<'user', T, A, O>): Command<U | T, G, A, O>;
channelFields<T extends Channel.Field = never>(fields: FieldCollector<'channel', T, A, O>): Command<U, G | T, A, O>;
alias(...names: string[]): this;
shortcut(name: string | RegExp, config?: Command.Shortcut): this;
subcommand<D extends string>(def: D, config?: Command.Config): Command<never, never, Argv.ArgumentType<D>>;
subcommand<D extends string>(def: D, desc: string, config?: Command.Config): Command<never, never, Argv.ArgumentType<D>>;
usage(text: Command.Usage<U, G>): this;
example(example: string): this;
option<K extends string>(name: K, desc: string, config: Argv.TypedOptionConfig<RegExp>): Command<U, G, A, Extend<O, K, string>>;
option<K extends string, R>(name: K, desc: string, config: Argv.TypedOptionConfig<(source: string) => R>): Command<U, G, A, Extend<O, K, R>>;
option<K extends string, R extends string>(name: K, desc: string, config: Argv.TypedOptionConfig<R[]>): Command<U, G, A, Extend<O, K, R>>;
option<K extends string, D extends string>(name: K, desc: D, config?: Argv.OptionConfig): Command<U, G, A, Extend<O, K, Argv.OptionType<D>>>;
match(session: Session): boolean;
getConfig<K extends keyof Command.Config>(key: K, session: Session): Exclude<Command.Config[K], (user: User) => any>;
check(callback: Command.Action<U, G, A, O>, prepend?: boolean): this;
action(callback: Command.Action<U, G, A, O>, append?: boolean): this;
execute(argv0: Argv<U, G, A, O>, next?: NextFunction): Promise<string>;
dispose(): void;
}
export function getUsageName(command: Command): string;
export type ValidationField = 'authority' | 'usage' | 'timers';
export function getUsage(name: string, user: Pick<User, 'usage'>): number;
export function checkUsage(name: string, user: Pick<User, 'usage'>, maxUsage?: number): boolean;
export function checkTimer(name: string, { timers }: Pick<User, 'timers'>, offset?: number): boolean;
type UnionToIntersection<U> = (U extends any ? (key: U) => void : never) extends (key: infer I) => void ? I : never;
type Flatten<T, K extends keyof T = keyof T> = UnionToIntersection<T[K]>;
type InnerKeys<T, K extends keyof T = keyof T> = keyof Flatten<T> & keyof Flatten<T, K>;
export interface Session<U, G, P, X, Y> extends MessageBase, Partial<ChannelInfo>, Partial<GroupInfo> {
}
export namespace Session {
type Genres = 'friend' | 'channel' | 'group' | 'group-member' | 'group-role' | 'group-file' | 'group-emoji';
type Actions = 'added' | 'deleted' | 'updated';
export interface Events extends Record<`${Genres}-${Actions}`, {}> {
}
export type MessageAction = 'message' | 'message-deleted' | 'message-updated' | 'send';
export type Message = Session<never, never, Platform, MessageAction>;
export interface Events extends Record<MessageAction, MessageType> {
}
export type RequestAction = 'friend-request' | 'group-request' | 'group-member-request';
export type Request = Session<never, never, Platform, RequestAction>;
export interface Events extends Record<RequestAction, {}> {
}
export interface Events {
'friend-request': {};
'group-request': {};
'group-member-request': {};
'group-added': GroupMemberChangeType;
'group-member-added': GroupMemberChangeType;
'group-deleted': GroupMemberChangeType;
'group-member-deleted': GroupMemberChangeType;
'group-member': {
'role': {};
'ban': {};
'nickname': {};
};
'notice': {
'poke': {};
'lucky-king': {};
'honor': {
'talkative': {};
'performer': {};
'emotion': {};
};
};
'reaction-added': {};
'reaction-deleted': {
'one': {};
'all': {};
'emoji': {};
};
}
export interface GroupMemberChangeType {
'active': {};
'passive': {};
}
export interface MessageType {
'private': {};
'group': {};
}
type ParamX<X> = Extract<keyof Events, X>;
type ParamY<X, Y> = Extract<InnerKeys<Events, ParamX<X>>, Y>;
export type Payload<X, Y = any> = Session<never, never, Platform, ParamX<X>, ParamY<X, Y>>;
export {};
}
export interface Parsed {
content: string;
prefix: string;
appel: boolean;
}
export class Session<U extends User.Field = never, G extends Channel.Field = never, P extends Platform = Platform, X extends keyof Session.Events = keyof Session.Events, Y extends InnerKeys<Session.Events, X> = InnerKeys<Session.Events, X>> {
type?: X;
subtype?: Y;
subsubtype?: InnerKeys<UnionToIntersection<Session.Events[X]>, Y>;
platform?: P;
selfId?: string;
operatorId?: string;
targetId?: string;
duration?: number;
file?: FileInfo;
readonly app: App;
readonly bot: Bot.Instance<P>;
readonly sid: string;
uid: string;
cid: string;
gid: string;
id?: string;
argv?: Argv<U, G>;
user?: User.Observed<U>;
channel?: Channel.Observed<G>;
parsed?: Parsed;
private _delay?;
private _queued;
private _hooks;
private _promise;
static readonly send: unique symbol;
constructor(app: App, session: Partial<Session>);
toJSON(): Partial<Session>;
private _preprocess;
preprocess(): Promise<string>;
get username(): string;
get database(): Database;
send(message: string): Promise<void>;
cancelQueued(delay?: number): void;
sendQueued(content: string, delay?: number): Promise<void>;
resolveValue<T>(source: T | ((session: Session) => T)): T;
getChannel<K extends Channel.Field = never>(id?: string, assignee?: string, fields?: K[]): Promise<Pick<Channel, "id" | K>>;
/** 在当前会话上绑定一个可观测频道实例 */
observeChannel<T extends Channel.Field = never>(fields?: Iterable<T>): Promise<Channel.Observed<T | G>>;
getUser<K extends User.Field = never>(id?: string, authority?: number, fields?: K[]): Promise<Pick<User, P | K>>;
/** 在当前会话上绑定一个可观测用户实例 */
observeUser<T extends User.Field = never>(fields?: Iterable<T>): Promise<User.Observed<T | U>>;
collect<T extends TableType>(key: T, argv: Argv, fields?: Set<keyof Tables[T]>): Set<keyof Tables[T]>;
resolve(argv: Argv): Command<never, never, any[], {}>;
execute(content: string, next?: true | NextFunction): Promise<string>;
execute(argv: Argv, next?: true | NextFunction): Promise<string>;
middleware(middleware: Middleware): () => boolean;
prompt(timeout?: number): Promise<string>;
suggest(options: SuggestOptions): Promise<void>;
}
export interface SuggestOptions {
target: string;
items: string[];
next?: NextFunction;
prefix?: string;
suffix: string;
minSimilarity?: number;
apply: (this: Session, suggestion: string, next: NextFunction) => void;
}
export function getSessionId(session: Session): string;
export type FieldCollector<T extends TableType, K = keyof Tables[T], A extends any[] = any[], O = {}> = Iterable<K> | ((argv: Argv<never, never, A, O>, fields: Set<keyof Tables[T]>) => void);
export interface FileInfo {
id: string;
name: string;
size: number;
busid: number;
}
export interface BotOptions {
type?: string;
token?: string;
selfId?: string;
}
type BotList<T extends Bot> = Array<T> & Record<string, T>;
export function createBots<T extends Bot>(key: 'selfId' | 'sid'): BotList<T>;
export abstract class Adapter<P extends Platform = Platform> {
app: App;
private Bot?;
bots: BotList<Bot.Instance<P>>;
abstract start(): Promise<void>;
abstract stop?(): void;
constructor(app: App, Bot?: Bot.Constructor<P>);
create(options: BotOptions, constructor?: Bot.Constructor<P>): Bot.Instance<P>;
dispatch(session: Session): void;
}
export namespace Adapter {
type Constructor<T extends Platform = Platform> = new (app: App, bot: BotOptions) => Adapter<T>;
type Instances = {
[K in string]: K extends `${infer T}:${any}` ? Adapter<T & Platform> : Adapter<K & Platform>;
};
const types: Record<string, Constructor>;
function from(app: App, bot: BotOptions): Adapter<never>;
function redirect(target: string | ((bot: BotOptions) => string)): Constructor<never>;
interface WsClientOptions {
retryLazy?: number;
retryTimes?: number;
retryInterval?: number;
}
abstract class WsClient<P extends Platform = Platform> extends Adapter<P> {
abstract prepare(bot: Bot.Instance<P>): WebSocket | Promise<WebSocket>;
abstract connect(bot: Bot.Instance<P>): Promise<void>;
private _listening;
options: WsClientOptions;
static options: WsClientOptions;
constructor(app: App, Bot: Bot.Constructor<P>, options?: WsClientOptions);
private _listen;
start(): Promise<void>;
stop(): void;
}
}
export interface Bot<P = Platform> extends BotOptions, UserBase {
[Session.send](session: Session, message: string): Promise<void>;
status: Bot.Status;
socket?: WebSocket;
version?: string;
getStatus(): Promise<Bot.Status>;
sendMessage(channelId: string, content: string, groupId?: string): Promise<string>;
sendPrivateMessage(userId: string, content: string): Promise<string>;
getMessage(channelId: string, messageId: string): Promise<MessageInfo>;
editMessage(channelId: string, messageId: string, content: string): Promise<void>;
deleteMessage(channelId: string, messageId: string): Promise<void>;
getSelf(): Promise<UserInfo>;
getUser(userId: string): Promise<UserInfo>;
getFriendList(): Promise<UserInfo[]>;
deleteFriend(userId: string): Promise<void>;
getGroup(groupId: string): Promise<GroupInfo>;
getGroupList(): Promise<GroupInfo[]>;
getGroupMember(groupId: string, userId: string): Promise<GroupMemberInfo>;
getGroupMemberList(groupId: string): Promise<GroupMemberInfo[]>;
getChannel(channelId: string): Promise<ChannelInfo>;
getChannelList(groupId: string): Promise<ChannelInfo[]>;
handleFriendRequest(messageId: string, approve: boolean, comment?: string): Promise<void>;
handleGroupRequest(messageId: string, approve: boolean, comment?: string): Promise<void>;
handleGroupMemberRequest(messageId: string, approve: boolean, comment?: string): Promise<void>;
}
export class Bot<P extends Platform> {
adapter: Adapter<P>;
readonly app: App;
readonly logger: Logger;
readonly platform: P;
constructor(adapter: Adapter<P>, options: BotOptions);
get sid(): string;
createSession(session: Partial<Session<never, never, P, 'send'>>): Session<never, never, P, "send", "group" | "private">;
getGroupMemberMap(groupId: string): Promise<{
[k: string]: string;
}>;
broadcast(channels: string[], content: string, delay?: number): Promise<string[]>;
}
export namespace Bot {
interface Platforms {
}
type Instance<T extends Platform> = [T] extends [never] ? Bot<T> : Platforms[T];
type Constructor<T extends Platform> = new (adapter: Adapter, options: BotOptions) => Instance<T>;
enum Status {
/** 正常运行 */
GOOD = 0,
/** 机器人处于闲置状态 */
BOT_IDLE = 1,
/** 机器人离线 */
BOT_OFFLINE = 2,
/** 无法获得状态 */
NET_ERROR = 3,
/** 服务器状态异常 */
SERVER_ERROR = 4,
/** 机器人被封禁 */
BANNED = 5,
/** 正在尝试连接 */
CONNECTING = 6
}
}
export type Platform = keyof Bot.Platforms;
export interface ChannelInfo {
channelId: string;
channelName?: string;
}
export interface GroupInfo {
groupId: string;
groupName?: string;
}
export interface UserBase {
username?: string;
nickname?: string;
avatar?: string;
discriminator?: string;
isBot?: boolean;
}
export interface UserInfo extends UserBase {
userId: string;
}
export interface GroupMemberInfo extends UserInfo {
roles?: string[];
}
export interface AuthorInfo extends GroupMemberInfo {
anonymous?: string;
}
export interface RoleInfo {
id: string;
}
export interface MessageBase {
messageId?: string;
channelId?: string;
groupId?: string;
userId?: string;
content?: string;
timestamp?: number;
author?: AuthorInfo;
quote?: MessageInfo;
}
export interface MessageInfo extends MessageBase {
subtype?: keyof Session.Events['message'];
}
export * from 'koishi-utils';
export const version: string;
declare module 'koa' {
interface Request {
body?: any;
rawBody: string;
}
}export default function apply(ctx: Context): void;