koishi-core
Advanced tools
Comparing version 0.1.3 to 0.1.4
@@ -7,3 +7,3 @@ /// <reference types="node" /> | ||
import { Context, Middleware } from './context'; | ||
import { Command, ShortcutConfig, ParsedArgv } from './command'; | ||
import { Command, ShortcutConfig } from './command'; | ||
import { Database, DatabaseConfig } from './database'; | ||
@@ -30,3 +30,2 @@ import { EventEmitter } from 'events'; | ||
receiver: AppReceiver; | ||
atMeRE: RegExp; | ||
prefixRE: RegExp; | ||
@@ -39,3 +38,3 @@ userPrefixRE: RegExp; | ||
_middlewares: [string, Middleware][]; | ||
contexts: Record<string, Context>; | ||
_contexts: Record<string, Context>; | ||
users: Context; | ||
@@ -45,3 +44,3 @@ groups: Context; | ||
constructor(options?: AppOptions); | ||
private _context; | ||
private _createContext; | ||
discuss(id: number, options?: DiscussOptions): DiscussContext; | ||
@@ -53,4 +52,3 @@ group(id: number, options?: GroupOptions): GroupContext; | ||
private _preprocess; | ||
_registerCommand(name: string, command: Command): void; | ||
_parseCommandLine(message: string, meta: Meta): ParsedArgv; | ||
private _parseCommandLine; | ||
private _applyMiddlewares; | ||
@@ -57,0 +55,0 @@ } |
@@ -5,9 +5,2 @@ "use strict"; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -25,3 +18,2 @@ const escape_string_regexp_1 = __importDefault(require("escape-string-regexp")); | ||
const koishi_utils_1 = require("koishi-utils"); | ||
const errors = __importStar(require("./errors")); | ||
const defaultOptions = { | ||
@@ -42,6 +34,6 @@ port: 8080, | ||
this._middlewares = []; | ||
this.contexts = { '/': this }; | ||
this.users = this._context('/user/'); | ||
this.groups = this._context('/group/'); | ||
this.discusses = this._context('/discuss/'); | ||
this._contexts = { '/': this }; | ||
this.users = this._createContext('/user/'); | ||
this.groups = this._createContext('/group/'); | ||
this.discusses = this._createContext('/discuss/'); | ||
this.options = { ...defaultOptions, ...options }; | ||
@@ -54,3 +46,4 @@ if (database && options.shareConnection) { | ||
} | ||
this.server = new server_1.Server(this); | ||
if (this.options.port) | ||
this.server = server_1.createServer(this); | ||
this.sender = new sender_1.Sender(this.options.sendURL, this.options.token, this.receiver); | ||
@@ -71,5 +64,5 @@ // TODO: handle without selfId (standalone server) | ||
} | ||
_context(path, create = () => new context_1.Context(path)) { | ||
if (!this.contexts[path]) { | ||
const ctx = this.contexts[path] = create(); | ||
_createContext(path, create = () => new context_1.Context(path)) { | ||
if (!this._contexts[path]) { | ||
const ctx = this._contexts[path] = create(); | ||
ctx.database = this.database; | ||
@@ -79,12 +72,12 @@ ctx.sender = this.sender; | ||
} | ||
return this.contexts[path]; | ||
return this._contexts[path]; | ||
} | ||
discuss(id, options = {}) { | ||
return this._context(`/discuss/${id}/`, () => new discuss_1.DiscussContext(id, options, this)); | ||
return this._createContext(`/discuss/${id}/`, () => new discuss_1.DiscussContext(id, options, this)); | ||
} | ||
group(id, options = {}) { | ||
return this._context(`/group/${id}/`, () => new group_1.GroupContext(id, options, this)); | ||
return this._createContext(`/group/${id}/`, () => new group_1.GroupContext(id, options, this)); | ||
} | ||
user(id, options = {}) { | ||
return this._context(`/user/${id}/`, () => new user_1.UserContext(id, options, this)); | ||
return this._createContext(`/user/${id}/`, () => new user_1.UserContext(id, options, this)); | ||
} | ||
@@ -118,2 +111,3 @@ start() { | ||
const canBeShortcut = prefix !== '.'; | ||
// parse as command | ||
if (canBeCommand && (parsedArgv = this._parseCommandLine(message, meta))) { | ||
@@ -136,6 +130,6 @@ fields.push(...parsedArgv.command._userFields); | ||
_message = `'${_message.trim()}'`; | ||
const { args, options: parsedOptions, rest, unknown } = command.parse(_message); | ||
const options = { ...parsedOptions, ...shortcut.options }; | ||
const result = command.parse(_message); | ||
Object.assign(result.options, shortcut.options); | ||
fields.push(...command._userFields); | ||
parsedArgv = { name, meta, message, options, args, command }; | ||
parsedArgv = { meta, command, ...result }; | ||
break; | ||
@@ -194,6 +188,4 @@ } | ||
// execute command | ||
if (parsedArgv) { | ||
parsedArgv.next = next; | ||
return parsedArgv.command.run(parsedArgv); | ||
} | ||
if (parsedArgv) | ||
return parsedArgv.command.execute(parsedArgv, next); | ||
// show suggestions | ||
@@ -211,18 +203,10 @@ const target = message.split(/\s/, 1)[0].toLowerCase(); | ||
command: suggestion => this._commandMap[suggestion], | ||
execute(suggestion, meta) { | ||
execute: (suggestion, meta, next) => { | ||
const newMessage = suggestion + message.slice(target.length); | ||
const parsedArgv = this._parseCommandLine(newMessage, meta); | ||
return parsedArgv.command.run(parsedArgv); | ||
// TODO: fields | ||
return parsedArgv.command.execute(parsedArgv, next); | ||
}, | ||
}); | ||
} | ||
_registerCommand(name, command) { | ||
const previous = this._commandMap[name]; | ||
if (!previous) { | ||
this._commandMap[name] = command; | ||
} | ||
else if (previous !== command) { | ||
throw new Error(errors.ERR_DUPLICATE_COMMAND); | ||
} | ||
} | ||
_parseCommandLine(message, meta) { | ||
@@ -232,7 +216,5 @@ const name = message.split(/\s/, 1)[0].toLowerCase(); | ||
if (command && context_1.isAncestor(command.context.path, meta.$path)) { | ||
// parse as command | ||
command_1.showCommandLog('command: %s', name); | ||
message = message.slice(name.length).trimStart(); | ||
const { options, unknown, rest, args } = command.parse(message); | ||
return { name, meta, message, options, args, command }; | ||
command_1.showCommandLog('[matched] %s', message); | ||
const result = command.parse(message.slice(name.length).trimStart()); | ||
return { meta, command, ...result }; | ||
} | ||
@@ -239,0 +221,0 @@ } |
@@ -1,2 +0,1 @@ | ||
import { CommandOption, CommandArgument, OptionConfig } from './parser'; | ||
import { Context, NextFunction } from './context'; | ||
@@ -6,12 +5,8 @@ import { UserData, UserField } from './database'; | ||
import debug from 'debug'; | ||
import { CommandOption, CommandArgument, OptionConfig, ParsedLine } from './parser'; | ||
export declare const showCommandLog: debug.Debugger; | ||
export interface ParsedArgv { | ||
export interface ParsedCommandLine extends ParsedLine { | ||
meta: Meta; | ||
name?: string; | ||
message?: string; | ||
args?: string[]; | ||
rawOptions?: Record<string, any>; | ||
options?: Record<string, any>; | ||
command: Command; | ||
next?: NextFunction; | ||
command?: Command; | ||
} | ||
@@ -30,2 +25,3 @@ export declare type UserType<T> = T | ((user: UserData, meta: Meta) => T); | ||
description?: string; | ||
/** min authority */ | ||
authority?: number; | ||
@@ -56,3 +52,3 @@ authorityHint?: string; | ||
parent: Command; | ||
_action?: (this: Command, config: ParsedArgv, ...args: any[]) => any; | ||
_action?: (this: Command, config: ParsedCommandLine, ...args: string[]) => any; | ||
_options: CommandOption[]; | ||
@@ -64,6 +60,5 @@ _argsDef: CommandArgument[]; | ||
_optsDef: Record<string, CommandOption>; | ||
_aliases: Set<string>; | ||
_userFields: Set<"id" | "name" | "flag" | "ignoreEnd" | "authority" | "usage" | "talkativeness">; | ||
_userFields: Set<"id" | "flag" | "name" | "ignoreEnd" | "authority" | "usage" | "talkativeness">; | ||
constructor(rawName: string, context: Context, config?: CommandConfig); | ||
get authority(): number; | ||
private _registerAlias; | ||
userFields(fields: Iterable<UserField>): this; | ||
@@ -82,13 +77,10 @@ alias(...names: string[]): this; | ||
option(rawName: string, description?: string, config?: OptionConfig): this; | ||
action(callback: (this: this, options: ParsedArgv, ...args: any[]) => any): this; | ||
/** | ||
* Check if a command name is matched by this command | ||
* @param name Command name | ||
*/ | ||
match(name: string, path?: string): boolean; | ||
action(callback: (this: this, options: ParsedCommandLine, ...args: string[]) => any): this; | ||
getConfig<K extends keyof CommandConfig>(key: K, meta: Meta): Exclude<CommandConfig[K], (user: UserData, meta: Meta) => any>; | ||
updateUsage(user: UserData, time?: Date): import("./database").Usage; | ||
parse(source: string): import("./parser").ParsedResult; | ||
run(config: ParsedArgv): any; | ||
parse(source: string): ParsedLine; | ||
execute(config: ParsedCommandLine, next?: NextFunction): any; | ||
/** check authority and usage */ | ||
private _checkUser; | ||
end(): Context; | ||
} |
@@ -13,6 +13,8 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const parser_1 = require("./parser"); | ||
const context_1 = require("./context"); | ||
const koishi_utils_1 = require("koishi-utils"); | ||
const util_1 = require("util"); | ||
const messages = __importStar(require("./messages")); | ||
const errors = __importStar(require("./errors")); | ||
const debug_1 = __importDefault(require("debug")); | ||
const parser_1 = require("./parser"); | ||
exports.showCommandLog = debug_1.default('app:command'); | ||
@@ -34,18 +36,24 @@ const defaultConfig = { | ||
this._optsDef = {}; | ||
this._aliases = new Set(); | ||
this._userFields = new Set(); | ||
this.name = parser_1.removeBrackets(rawName); | ||
if (!this.name) { | ||
throw new Error(errors.ERR_EXPECT_COMMAND_NAME); | ||
throw new Error(errors.EXPECT_COMMAND_NAME); | ||
} | ||
else if (!/^[\w.-]+$/.exec(this.name)) { | ||
throw new Error(errors.ERR_INVALID_CHARACTER); | ||
throw new Error(errors.INVALID_CHARACTER); | ||
} | ||
this._argsDef = parser_1.parseArguments(rawName); | ||
this.config = { ...defaultConfig, ...config }; | ||
context.app._registerCommand(this.name, this); | ||
this._registerAlias(this.name); | ||
this.option('-h, --help', messages.SHOW_THIS_MESSAGE); | ||
context.app._commands.push(this); | ||
} | ||
get authority() { | ||
return this.config.authority; | ||
_registerAlias(name) { | ||
const previous = this.context.app._commandMap[name]; | ||
if (!previous) { | ||
this.context.app._commandMap[name] = this; | ||
} | ||
else if (previous !== this) { | ||
throw new Error(errors.DUPLICATE_COMMAND); | ||
} | ||
} | ||
@@ -60,4 +68,3 @@ userFields(fields) { | ||
for (const name of names) { | ||
this.context.app._registerCommand(name, this); | ||
this._aliases.add(name); | ||
this._registerAlias(name); | ||
} | ||
@@ -72,3 +79,3 @@ return this; | ||
if (this.context.app._commandMap[firstName]) { | ||
throw new Error(errors.ERR_EXISTING_SUBCOMMAND); | ||
throw new Error(errors.EXISTING_SUBCOMMAND); | ||
} | ||
@@ -84,3 +91,3 @@ if (!dotPrefixed) | ||
command: this, | ||
authority: this.authority, | ||
authority: this.config.authority, | ||
...config, | ||
@@ -111,3 +118,3 @@ }; | ||
if (name in this._optsDef) { | ||
throw new Error(errors.ERR_DUPLICATE_OPTION); | ||
throw new Error(errors.DUPLICATE_OPTION); | ||
} | ||
@@ -122,11 +129,2 @@ this._optsDef[name] = option; | ||
} | ||
/** | ||
* Check if a command name is matched by this command | ||
* @param name Command name | ||
*/ | ||
match(name, path) { | ||
if (this.name !== name && !this._aliases.has(name)) | ||
return false; | ||
return !path || context_1.isAncestor(this.context.path, path); | ||
} | ||
getConfig(key, meta) { | ||
@@ -152,25 +150,51 @@ const value = this.config[key]; | ||
} | ||
async run(config) { | ||
const { meta } = config; | ||
if (this.children.length && !this._action) { | ||
return this.context.runCommand('help', { meta, args: [this.name] }); | ||
async execute(config, next = koishi_utils_1.noop) { | ||
const { meta, options, args, unknown } = config; | ||
config.next = next; | ||
// show help when use `-h, --help` or when there is no action | ||
if (!this._action || options.help) { | ||
return this.context.runCommand('help', meta, [this.name]); | ||
} | ||
// check argument count | ||
if (this.config.checkArgCount) { | ||
const nextArg = this._argsDef[args.length]; | ||
if (nextArg && nextArg.required) { | ||
return meta.$send(messages.INSUFFICIENT_ARGUMENTS); | ||
} | ||
const finalArg = this._argsDef[this._argsDef.length - 1]; | ||
if (args.length > this._argsDef.length && !finalArg.noSegment && !finalArg.variadic) { | ||
return meta.$send(messages.REDUNANT_ARGUMENTS); | ||
} | ||
} | ||
// check unknown options | ||
if (this.config.checkUnknown && unknown.length) { | ||
return meta.$send(util_1.format(messages.UNKNOWN_OPTIONS, unknown.join(', '))); | ||
} | ||
// check required options | ||
if (this.config.checkRequired) { | ||
const absent = this._options.filter((option) => { | ||
return option.required && !(option.camels[0] in options); | ||
}); | ||
if (absent.length) { | ||
return meta.$send(util_1.format(messages.REQUIRED_OPTIONS, absent.join(', '))); | ||
} | ||
} | ||
if (this._checkUser(meta, options)) { | ||
return this._action(config, ...args); | ||
} | ||
} | ||
/** check authority and usage */ | ||
async _checkUser(meta, options) { | ||
const user = meta.$user; | ||
if (!user) | ||
return true; | ||
let isUsage = true; | ||
const user = meta.$user; | ||
config = { | ||
args: [], | ||
name: this.name, | ||
rawOptions: {}, | ||
options: {}, | ||
next: () => { }, | ||
...config, | ||
}; | ||
// 检查使用权限 | ||
// check authority | ||
if (this.config.authority > user.authority) { | ||
return config.meta.$send('权限不足'); | ||
return meta.$send(messages.LOW_AUTHORITY); | ||
} | ||
for (const option of this._options) { | ||
if (option.camels[0] in config.options) { | ||
if (option.camels[0] in options) { | ||
if (option.authority > user.authority) | ||
return config.meta.$send('权限不足'); | ||
return meta.$send(messages.LOW_AUTHORITY); | ||
if (option.notUsage) | ||
@@ -180,6 +204,6 @@ isUsage = false; | ||
} | ||
// 检查触发次数与间隔 | ||
const minInterval = this.getConfig('minInterval', config.meta); | ||
// check usage | ||
const minInterval = this.getConfig('minInterval', meta); | ||
if (isUsage || minInterval > 0) { | ||
const maxUsage = this.getConfig('maxUsage', config.meta); | ||
const maxUsage = this.getConfig('maxUsage', meta); | ||
if (maxUsage < Infinity || minInterval > 0) { | ||
@@ -192,3 +216,3 @@ const date = new Date(); | ||
if (this.config.showWarning) { | ||
await config.meta.$send('调用过于频繁,请稍后再试'); | ||
await meta.$send(messages.TOO_FREQUENT); | ||
} | ||
@@ -200,3 +224,3 @@ return; | ||
if (usage.count >= maxUsage && isUsage) { | ||
await config.meta.$send('调用次数已达上限'); | ||
await meta.$send(messages.USAGE_EXHAUSTED); | ||
return; | ||
@@ -209,4 +233,3 @@ } | ||
} | ||
const args = this._argsDef.map((arg, index) => arg.variadic ? config.args.slice(index) : config.args[index]); | ||
return this._action(config, ...args); | ||
return true; | ||
} | ||
@@ -213,0 +236,0 @@ end() { |
/// <reference types="node" /> | ||
import { Command, CommandConfig, ParsedArgv } from './command'; | ||
import { Command, CommandConfig } from './command'; | ||
import { EventEmitter } from 'events'; | ||
@@ -14,3 +14,2 @@ import { Meta } from './meta'; | ||
export declare function isAncestor(ancestor: string, path: string): boolean; | ||
export declare const MESSAGE_COMMAND_NOT_FOUND = "\u6307\u4EE4\u672A\u627E\u5230\u3002"; | ||
export declare const prefixTypes: string[]; | ||
@@ -31,5 +30,5 @@ export declare class Context { | ||
getCommand(name: string, meta?: Meta): Command; | ||
runCommand(name: string, parsedArgv: ParsedArgv): any; | ||
runCommand(name: string, meta: Meta, args?: string[], options?: Record<string, any>, rest?: string): any; | ||
end(): App; | ||
_getEventTypes(path: string): string[]; | ||
} |
@@ -12,2 +12,3 @@ "use strict"; | ||
const events_1 = require("events"); | ||
const messages = __importStar(require("./messages")); | ||
const errors = __importStar(require("./errors")); | ||
@@ -18,3 +19,2 @@ function isAncestor(ancestor, path) { | ||
exports.isAncestor = isAncestor; | ||
exports.MESSAGE_COMMAND_NOT_FOUND = '指令未找到。'; | ||
exports.prefixTypes = ['user', 'discuss', 'group']; | ||
@@ -52,6 +52,6 @@ class Context { | ||
if (parent && command.parent !== parent) { | ||
throw new Error(errors.ERR_WRONG_SUBCOMMAND); | ||
throw new Error(errors.WRONG_SUBCOMMAND); | ||
} | ||
if (!isAncestor(command.context.path, this.path)) { | ||
throw new Error(errors.ERR_WRONG_CONTEXT); | ||
throw new Error(errors.WRONG_CONTEXT); | ||
} | ||
@@ -61,3 +61,3 @@ return command; | ||
if (parent && !isAncestor(parent.context.path, this.path)) { | ||
throw new Error(errors.ERR_WRONG_CONTEXT); | ||
throw new Error(errors.WRONG_CONTEXT); | ||
} | ||
@@ -96,10 +96,11 @@ command = new command_1.Command(name, this); | ||
const path = meta ? meta.$path : this.path; | ||
// TODO: use _commandMap | ||
return this.app._commands.find(cmd => cmd.match(name, path)); | ||
const command = this.app._commandMap[name]; | ||
return command && isAncestor(command.context.path, path) && command; | ||
} | ||
runCommand(name, parsedArgv) { | ||
const command = this.app._commands.find(cmd => cmd.match(name, parsedArgv.meta.$path)); | ||
if (command) | ||
return command.run(parsedArgv); | ||
return parsedArgv.meta.$send(exports.MESSAGE_COMMAND_NOT_FOUND); | ||
runCommand(name, meta, args = [], options = {}, rest = '') { | ||
const command = this.app._commandMap[name]; | ||
if (!command || !isAncestor(command.context.path, meta.$path)) { | ||
meta.$send(messages.COMMAND_NOT_FOUND); | ||
} | ||
return command.execute({ meta, command, args, options, rest, unknown: [] }); | ||
} | ||
@@ -106,0 +107,0 @@ end() { |
@@ -1,7 +0,8 @@ | ||
export declare const ERR_DUPLICATE_COMMAND = "duplicate command names"; | ||
export declare const ERR_DUPLICATE_OPTION = "duplicate option names"; | ||
export declare const ERR_EXISTING_SUBCOMMAND = "cannot set existing command as subcommand"; | ||
export declare const ERR_EXPECT_COMMAND_NAME = "expect a command name"; | ||
export declare const ERR_INVALID_CHARACTER = "invalid characters"; | ||
export declare const ERR_WRONG_CONTEXT = "invalid context path"; | ||
export declare const ERR_WRONG_SUBCOMMAND = "invalid subcommand"; | ||
export declare const DUPLICATE_COMMAND = "duplicate command names"; | ||
export declare const DUPLICATE_OPTION = "duplicate option names"; | ||
export declare const EXISTING_SUBCOMMAND = "cannot set existing command as subcommand"; | ||
export declare const EXPECT_COMMAND_NAME = "expect a command name"; | ||
export declare const INVALID_CHARACTER = "invalid characters"; | ||
export declare const NO_CORRESPONDING_APP = "no corresponding app"; | ||
export declare const WRONG_CONTEXT = "invalid context path"; | ||
export declare const WRONG_SUBCOMMAND = "invalid subcommand"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ERR_DUPLICATE_COMMAND = 'duplicate command names'; | ||
exports.ERR_DUPLICATE_OPTION = 'duplicate option names'; | ||
exports.ERR_EXISTING_SUBCOMMAND = 'cannot set existing command as subcommand'; | ||
exports.ERR_EXPECT_COMMAND_NAME = 'expect a command name'; | ||
exports.ERR_INVALID_CHARACTER = 'invalid characters'; | ||
exports.ERR_WRONG_CONTEXT = 'invalid context path'; | ||
exports.ERR_WRONG_SUBCOMMAND = 'invalid subcommand'; | ||
exports.DUPLICATE_COMMAND = 'duplicate command names'; | ||
exports.DUPLICATE_OPTION = 'duplicate option names'; | ||
exports.EXISTING_SUBCOMMAND = 'cannot set existing command as subcommand'; | ||
exports.EXPECT_COMMAND_NAME = 'expect a command name'; | ||
exports.INVALID_CHARACTER = 'invalid characters'; | ||
exports.NO_CORRESPONDING_APP = 'no corresponding app'; | ||
exports.WRONG_CONTEXT = 'invalid context path'; | ||
exports.WRONG_SUBCOMMAND = 'invalid subcommand'; |
import * as errors from './errors'; | ||
export { errors }; | ||
import * as messages from './messages'; | ||
export { errors, messages }; | ||
export * from './app'; | ||
@@ -4,0 +5,0 @@ export * from './command'; |
@@ -15,2 +15,4 @@ "use strict"; | ||
exports.errors = errors; | ||
const messages = __importStar(require("./messages")); | ||
exports.messages = messages; | ||
__export(require("./app")); | ||
@@ -17,0 +19,0 @@ __export(require("./command")); |
@@ -9,2 +9,3 @@ import { App, AppOptions } from './app'; | ||
export declare function onStart(hook: (...app: App[]) => void): void; | ||
export declare function start(): void; | ||
export declare function startAll(): void; | ||
export declare function closeAll(): void; |
@@ -31,3 +31,3 @@ "use strict"; | ||
exports.onStart = onStart; | ||
function start() { | ||
function startAll() { | ||
const appList = []; | ||
@@ -42,2 +42,8 @@ for (const id in exports.apps) { | ||
} | ||
exports.start = start; | ||
exports.startAll = startAll; | ||
function closeAll() { | ||
for (const id in exports.apps) { | ||
exports.apps[id].close(); | ||
} | ||
} | ||
exports.closeAll = closeAll; |
@@ -38,2 +38,3 @@ import { GroupData, User } from './database'; | ||
metaEventType?: 'lifecycle' | 'heartbeat'; | ||
time?: number; | ||
} | ||
@@ -40,0 +41,0 @@ export interface GroupMember { |
@@ -18,3 +18,3 @@ export declare function removeBrackets(source: string): string; | ||
export interface CommandOption extends OptionConfig { | ||
rawName: string; | ||
longest: string; | ||
names: string[]; | ||
@@ -29,4 +29,3 @@ camels: string[]; | ||
export declare function parseValue(source: string | true, quoted: boolean, config: CommandOption): any; | ||
export interface ParsedResult { | ||
source: string; | ||
export interface ParsedLine { | ||
rest: string; | ||
@@ -37,2 +36,2 @@ args: string[]; | ||
} | ||
export declare function parseLine(source: string, argsDef: CommandArgument[], optsDef: Record<string, CommandOption>): ParsedResult; | ||
export declare function parseLine(source: string, argsDef: CommandArgument[], optsDef: Record<string, CommandOption>): ParsedLine; |
@@ -43,5 +43,8 @@ "use strict"; | ||
const camels = []; | ||
let required = false, isBoolean = false; | ||
let required = false, isBoolean = false, longest = ''; | ||
const names = removeBrackets(rawName).split(',').map((name) => { | ||
name = name.trim().replace(/^-{1,2}/, ''); | ||
name = name.trim(); | ||
if (name.length > longest.length) | ||
longest = name; | ||
name = name.replace(/^-{1,2}/, ''); | ||
if (name.startsWith('no-') && !config.noNegated && !optsDef[name.slice(3)]) { | ||
@@ -66,3 +69,3 @@ name = name.slice(3); | ||
...config, | ||
rawName, | ||
longest, | ||
names, | ||
@@ -110,7 +113,6 @@ camels, | ||
function parseLine(source, argsDef, optsDef) { | ||
let arg, name, arg0; | ||
let arg, name, arg0, rest = ''; | ||
const args = []; | ||
const unknown = []; | ||
const options = {}; | ||
const result = { source, args, unknown, options, rest: '' }; | ||
function handleOption(name, knownValue, unknownValue) { | ||
@@ -148,3 +150,3 @@ const config = optsDef[name]; | ||
// rest part | ||
result.rest = arg0.rest; | ||
rest = arg0.rest; | ||
break; | ||
@@ -189,4 +191,10 @@ } | ||
} | ||
return result; | ||
// assign default values | ||
for (const name in optsDef) { | ||
if (optsDef[name].default !== undefined && !(name in options)) { | ||
options[name] = optsDef[name].default; | ||
} | ||
} | ||
return { options, rest, unknown, args }; | ||
} | ||
exports.parseLine = parseLine; |
import { Meta } from './meta'; | ||
import { App } from './app'; | ||
export declare function createServer(app: App): Server; | ||
export declare class Server { | ||
app: App; | ||
private _apps; | ||
private _server; | ||
private _socket; | ||
private _httpServer; | ||
private _isListening; | ||
constructor(app: App); | ||
emitEvents(meta: Meta): void; | ||
addProperties(meta: Meta): Promise<void>; | ||
bind(app: App): this; | ||
emitEvents(meta: Meta, app: App): void; | ||
addProperties(meta: Meta, app: App): Promise<void>; | ||
listen(port: number): void; | ||
close(): void; | ||
} |
@@ -16,10 +16,20 @@ "use strict"; | ||
showServerLog.inspectOpts.depth = 0; | ||
const serverMap = {}; | ||
function createServer(app) { | ||
const { port } = app.options; | ||
if (port in serverMap) { | ||
return serverMap[port].bind(app); | ||
} | ||
return serverMap[port] = new Server(app); | ||
} | ||
exports.createServer = createServer; | ||
class Server { | ||
constructor(app) { | ||
this.app = app; | ||
this._apps = []; | ||
this._server = express_1.default().use(body_parser_1.json()); | ||
this._isListening = false; | ||
if (app.options.wsServer) { | ||
this._socket = new ws_1.default(app.options.wsServer + '/event', { | ||
headers: { | ||
Authorization: `Token ${this.app.options.token}`, | ||
Authorization: `Token ${app.options.token}`, | ||
}, | ||
@@ -45,7 +55,10 @@ }); | ||
const meta = koishi_utils_1.camelCase(req.body); | ||
const app = this._apps.find(app => app.options.selfId === meta.selfId); | ||
if (!app) | ||
return res.sendStatus(403); | ||
res.sendStatus(200); | ||
showServerLog('receive %o', meta); | ||
res.sendStatus(200); | ||
try { | ||
await this.addProperties(meta); | ||
this.emitEvents(meta); | ||
await this.addProperties(meta, app); | ||
this.emitEvents(meta, app); | ||
} | ||
@@ -56,6 +69,11 @@ catch (error) { | ||
}); | ||
this.bind(app); | ||
} | ||
emitEvents(meta) { | ||
for (const path in this.app.contexts) { | ||
const context = this.app.contexts[path]; | ||
bind(app) { | ||
this._apps.push(app); | ||
return this; | ||
} | ||
emitEvents(meta, app) { | ||
for (const path in app._contexts) { | ||
const context = app._contexts[path]; | ||
const types = context._getEventTypes(meta.$path); | ||
@@ -67,3 +85,3 @@ if (types.length) | ||
} | ||
async addProperties(meta) { | ||
async addProperties(meta, app) { | ||
Object.defineProperty(meta, '$path', { | ||
@@ -91,15 +109,15 @@ value: '/', | ||
if (meta.messageType === 'group') { | ||
if (this.app.database) { | ||
if (app.database) { | ||
Object.defineProperty(meta, '$group', { | ||
value: await this.app.database.getGroup(meta.groupId), | ||
value: await app.database.getGroup(meta.groupId), | ||
writable: true, | ||
}); | ||
} | ||
meta.$send = message => this.app.sender.sendGroupMsg(meta.groupId, message); | ||
meta.$send = message => app.sender.sendGroupMsg(meta.groupId, message); | ||
} | ||
else if (meta.messageType === 'discuss') { | ||
meta.$send = message => this.app.sender.sendDiscussMsg(meta.discussId, message); | ||
meta.$send = message => app.sender.sendDiscussMsg(meta.discussId, message); | ||
} | ||
else { | ||
meta.$send = message => this.app.sender.sendPrivateMsg(meta.userId, message); | ||
meta.$send = message => app.sender.sendPrivateMsg(meta.userId, message); | ||
} | ||
@@ -109,7 +127,9 @@ } | ||
listen(port) { | ||
if (this._isListening) | ||
return; | ||
this._isListening = true; | ||
this._httpServer = this._server.listen(port); | ||
showServerLog('listen to port', port); | ||
for (const path in this.app.contexts) { | ||
const context = this.app.contexts[path]; | ||
context.receiver.emit('connected', this.app); | ||
for (const app of this._apps) { | ||
app.receiver.emit('connected', app); | ||
} | ||
@@ -116,0 +136,0 @@ } |
@@ -20,3 +20,3 @@ import { NextFunction } from './context'; | ||
command: Command | ((suggestion: string) => Command); | ||
execute: (suggestion: string, meta: Meta) => any; | ||
execute: (suggestion: string, meta: Meta, next: NextFunction) => any; | ||
} | ||
@@ -23,0 +23,0 @@ export declare const SIMILARITY_COEFFICIENT = 0.4; |
@@ -100,3 +100,3 @@ "use strict"; | ||
meta.$user = await command.context.database.observeUser(userId, 0, fields); | ||
return execute(suggestions[0], meta); | ||
return execute(suggestions[0], meta, next); | ||
} | ||
@@ -103,0 +103,0 @@ else { |
{ | ||
"name": "koishi-core", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"main": "dist/index.js", | ||
@@ -12,5 +12,5 @@ "typings": "dist/index.d.ts", | ||
"scripts": { | ||
"jest": "jest tests/.*\\.spec\\.ts", | ||
"jest": "jest tests/.*\\.spec\\.ts --runInBand", | ||
"lint": "eslint src --ext .ts", | ||
"prepublish": "tsc -b" | ||
"prepack": "tsc -b" | ||
}, | ||
@@ -17,0 +17,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
245848
48
2671