Skip to content

Commit

Permalink
Merge branch 'develop' into teach-fix-mongo
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Aug 29, 2020
2 parents 5a4a811 + 607bd58 commit cac2bea
Show file tree
Hide file tree
Showing 72 changed files with 739 additions and 637 deletions.
Binary file added .github/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Koishi 是一个在 [Node.js](https://nodejs.org/) 环境下运行的机器人

这个项目的名字和图标来源于东方 Project 中的角色古明地恋 (Komeiji Koishi)。

![demo](./.github/demo.png)

## 安装

```sh
Expand Down Expand Up @@ -74,9 +76,22 @@ MySQL 5.7 支持。

### [koishi-plugin-common](./packages/plugin-common) [![npm](https://img.shields.io/npm/v/koishi-plugin-common/next?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-common)

### [koishi-plugin-eval](./packages/plugin-eval) [![npm](https://img.shields.io/npm/v/koishi-plugin-eval/next?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-eval)
koishi-plugin-common 包含了一些常用功能,它们在你使用 koishi 库时是默认安装的。包含下列功能:

- 显示用户信息
- 管理用户和群数据
- 向一个或多个上下文发送消息
- 模拟来自其他会话的输入
- 输出聊天记录到控制台
- 欢迎入群,复读,处理申请,频率限制,自定义回复……

### [koishi-plugin-eval](https://koishi.js.org/plugins/eval.html) [![npm](https://img.shields.io/npm/v/koishi-plugin-eval/next?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-eval)

koishi-plugin-eval 允许用户直接使用机器人执行脚本。它利用了 Node.js 的 [vm](https://nodejs.org/api/vm.html)[worker_threads](https://nodejs.org/api/worker_threads.html) 模块,在保护执行安全的前提下能够获得较快的响应速度。同时,插件还提供了一些内置的 API 供用户调用,结合教学功能可以在客户端实现复杂的行为。

### [koishi-plugin-eval-addons](https://koishi.js.org/plugins/eval.html) [![npm](https://img.shields.io/npm/v/koishi-plugin-eval-addons/next?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-eval-addons)

### [koishi-plugin-eval-addons](./packages/plugin-eval-addons) [![npm](https://img.shields.io/npm/v/koishi-plugin-eval-addons/next?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-eval-addons)
koishi-plugin-eval-addons 在前一个插件的基础上,允许用户编写自己的模块并永久保存。插件将自动加载特定目录下的文件,并将其作为机器人的内置功能。用户可以利用此功能存储较为复杂的代码,甚至扩展新的指令。同时,如果上述目录是一个 git 目录,该插件也提供了自动更新等机制。

### [koishi-plugin-github](./packages/plugin-github) [![npm](https://img.shields.io/npm/v/koishi-plugin-github/next?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-github)

Expand Down
2 changes: 1 addition & 1 deletion build/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const prefixRegExp = new RegExp(`^(${prefixes.join('|')})(?:\\((\\S+)\\))?: (.+)
auth: GITHUB_TOKEN,
})

const { version } = require('../packages/koishi-cli/package') as PackageJson
const { version } = require('../packages/koishi/package') as PackageJson
const tags = spawnSync('git tag -l').split(/\r?\n/)
if (tags.includes(version)) {
return console.log(`Tag ${version} already exists.`)
Expand Down
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"shiki/core",
"shiki/cosmos",
"shiki/games",
"shiki/others",
"packages/*"
],
"scripts": {
Expand All @@ -29,16 +30,16 @@
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"@octokit/rest": "^18.0.3",
"@octokit/rest": "^18.0.4",
"@types/chai": "^4.2.12",
"@types/chai-as-promised": "^7.1.3",
"@types/cross-spawn": "^6.0.2",
"@types/fs-extra": "^9.0.1",
"@types/mocha": "^8.0.3",
"@types/node": "^14.6.0",
"@types/node": "^14.6.1",
"@types/semver": "^7.3.3",
"@typescript-eslint/eslint-plugin": "^3.9.1",
"@typescript-eslint/parser": "^3.9.1",
"@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1",
"c8": "^7.3.0",
"cac": "^6.6.1",
"chai": "^4.2.0",
Expand All @@ -59,8 +60,8 @@
"jest-mock": "^26.3.0",
"kleur": "^4.1.1",
"latest-version": "^5.1.0",
"mocha": "^8.1.1",
"open": "^7.2.0",
"mocha": "^8.1.2",
"open": "^7.2.1",
"ora": "^5.0.0",
"p-map": "^4.0.0",
"prompts": "^2.3.2",
Expand Down
6 changes: 3 additions & 3 deletions packages/adapter-cqhttp/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "koishi-adapter-cqhttp",
"description": "CQHTTP adapter for Koishi",
"version": "1.0.0",
"version": "1.0.1",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
Expand Down Expand Up @@ -31,7 +31,7 @@
"koishi"
],
"peerDependencies": {
"koishi-core": "^2.0.0"
"koishi-core": "^2.0.1"
},
"devDependencies": {
"@types/ms": "^0.7.31",
Expand All @@ -43,6 +43,6 @@
"axios": "^0.20.0",
"ms": "^2.1.2",
"ws": "^7.3.1",
"koishi-utils": "^3.1.0"
"koishi-utils": "^3.1.1"
}
}
1 change: 0 additions & 1 deletion packages/adapter-cqhttp/tests/mock.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { snakeCase } from 'koishi-utils'
import { CQResponse } from 'koishi-adapter-cqhttp'
import { expect } from 'chai'
import '@shigma/chai-extended'

export type RequestParams = Record<string, any>
export type RequestData = readonly [string, RequestParams]
Expand Down
25 changes: 0 additions & 25 deletions packages/chai-extended/package.json

This file was deleted.

43 changes: 0 additions & 43 deletions packages/chai-extended/src/index.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/koishi-core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "koishi-core",
"description": "Core features for Koishi",
"version": "2.0.0",
"version": "2.0.1",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"engines": {
Expand Down Expand Up @@ -44,7 +44,7 @@
"koa": "^2.13.0",
"koa-bodyparser": "^4.3.0",
"koa-router": "^9.4.0",
"koishi-utils": "^3.1.0",
"koishi-utils": "^3.1.1",
"leven": "^3.1.0",
"lru-cache": "^6.0.0"
}
Expand Down
4 changes: 2 additions & 2 deletions packages/koishi-core/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,9 @@ export class App extends Context {
await session.$group?._update()
}

private _parse(message: string, { $prefix, $appel, messageType }: Session, builtin: boolean, terminator = '') {
private _parse(message: string, { $reply, $prefix, $appel, messageType }: Session, builtin: boolean, terminator = '') {
// group message should have prefix or appel to be interpreted as a command call
if (builtin && messageType !== 'private' && $prefix === null && !$appel) return
if (builtin && ($reply || messageType !== 'private' && $prefix === null && !$appel)) return
terminator = escapeRegExp(terminator)
const name = message.split(new RegExp(`[\\s${terminator}]`), 1)[0]
const index = name.lastIndexOf('/')
Expand Down
36 changes: 18 additions & 18 deletions packages/koishi-core/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ 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)
}

type ArgvInferred<T> = Iterable<T> | ((argv: ParsedArgv, fields: Set<T>) => Iterable<T>)
export type FieldCollector<T extends TableType, K = keyof Tables[T], O extends {} = {}> =
| Iterable<K>
| ((argv: ParsedArgv<never, never, O>, fields: Set<keyof Tables[T]>) => void)

export type CommandAction<U extends User.Field = never, G extends Group.Field = never, O = {}> =
(this: Command<U, G>, config: ParsedArgv<U, G, O>, ...args: string[]) => void | string | Promise<void | string>

Expand All @@ -117,36 +120,37 @@ export class Command<U extends User.Field = never, G extends Group.Field = never

private _optionNameMap: Record<string, CommandOption> = {}
private _optionSymbolMap: Record<string, CommandOption> = {}
private _userFields: ArgvInferred<User.Field>[] = []
private _groupFields: ArgvInferred<Group.Field>[] = []
private _userFields: FieldCollector<'user'>[] = []
private _groupFields: FieldCollector<'group'>[] = []

_action?: CommandAction<U, G>

static defaultConfig: CommandConfig = {}
static defaultOptionConfig: OptionConfig = {}

private static _userFields: ArgvInferred<User.Field>[] = []
private static _groupFields: ArgvInferred<Group.Field>[] = []
private static _userFields: FieldCollector<'user'>[] = []
private static _groupFields: FieldCollector<'group'>[] = []

static userFields(fields: ArgvInferred<User.Field>) {
static userFields(fields: FieldCollector<'user'>) {
this._userFields.push(fields)
return this
}

static groupFields(fields: ArgvInferred<Group.Field>) {
static groupFields(fields: FieldCollector<'group'>) {
this._groupFields.push(fields)
return this
}

static collect<T extends TableType>(argv: ParsedArgv, key: T, fields = new Set<keyof Tables[T]>()) {
if (!argv) return
const values: ArgvInferred<keyof Tables[T]>[] = [
const values: FieldCollector<T>[] = [
...this[`_${key}Fields`],
...argv.command[`_${key}Fields`],
]
for (let value of values) {
for (const value of values) {
if (typeof value === 'function') {
value = value(argv, fields)
value(argv, fields)
continue
}
for (const field of value) {
fields.add(field)
Expand Down Expand Up @@ -183,18 +187,14 @@ export class Command<U extends User.Field = never, G extends Group.Field = never
return `Command <${this.name}>`
}

userFields<T extends User.Field = never>(fields: Iterable<T>): Command<U | T, G, O>
userFields<T extends User.Field = never>(fields: (argv: ParsedArgv<never, never, O>, fields: Set<User.Field>) => Iterable<T>): Command<U | T, G, O>
userFields(fields: ArgvInferred<User.Field>) {
userFields<T extends User.Field = never>(fields: FieldCollector<'user', T, O>): Command<U | T, G, O> {
this._userFields.push(fields)
return this
return this as any
}

groupFields<T extends Group.Field = never>(fields: Iterable<T>): Command<U, G | T, O>
groupFields<T extends Group.Field = never>(fields: (argv: ParsedArgv<never, never, O>, fields: Set<Group.Field>) => Iterable<T>): Command<U, G | T, O>
groupFields(fields: ArgvInferred<Group.Field>) {
groupFields<T extends Group.Field = never>(fields: FieldCollector<'group', T, O>): Command<U, G | T, O> {
this._groupFields.push(fields)
return this
return this as any
}

alias(...names: string[]) {
Expand Down
8 changes: 4 additions & 4 deletions packages/koishi-core/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export namespace User {

export type Field = keyof User
export const fields: Field[] = []
export type Observed<K extends Field = Field> = utils.Observed<Pick<User, K>>
export type Observed<K extends Field = Field> = utils.Observed<Pick<User, K>, Promise<void>>
type Getter = (id: number, authority: number) => Partial<User>
const getters: Getter[] = []

Expand Down Expand Up @@ -65,7 +65,7 @@ export namespace Group {

export type Field = keyof Group
export const fields: Field[] = []
export type Observed<K extends Field = Field> = utils.Observed<Pick<Group, K>>
export type Observed<K extends Field = Field> = utils.Observed<Pick<Group, K>, Promise<void>>
type Getter = (id: number, authority: number) => Partial<Group>
const getters: Getter[] = []

Expand Down Expand Up @@ -94,13 +94,13 @@ export interface Database {
getUser<K extends User.Field>(userId: number, defaultAuthority?: number, fields?: readonly K[]): Promise<Pick<User, K | 'id'>>
getUsers<K extends User.Field>(fields?: readonly K[]): Promise<Pick<User, K>[]>
getUsers<K extends User.Field>(ids: readonly number[], fields?: readonly K[]): Promise<Pick<User, K>[]>
setUser(userId: number, data: Partial<User>): Promise<any>
setUser(userId: number, data: Partial<User>): Promise<void>

getGroup<K extends Group.Field>(groupId: number, fields?: readonly K[]): Promise<Pick<Group, K | 'id'>>
getGroup<K extends Group.Field>(groupId: number, selfId?: number, fields?: readonly K[]): Promise<Pick<Group, K | 'id'>>
getAllGroups<K extends Group.Field>(assignees?: readonly number[]): Promise<Pick<Group, K>[]>
getAllGroups<K extends Group.Field>(fields?: readonly K[], assignees?: readonly number[]): Promise<Pick<Group, K>[]>
setGroup(groupId: number, data: Partial<Group>): Promise<any>
setGroup(groupId: number, data: Partial<Group>): Promise<void>
}

type DatabaseExtensionMethods<I> = {
Expand Down
17 changes: 8 additions & 9 deletions packages/koishi-core/src/plugins/help.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getUsage, getUsageName, ValidationField } from './validate'
import { User, Group, TableType, Tables } from '../database'
import { Command, ParsedArgv } from '../command'
import { Command, FieldCollector, ParsedArgv } from '../command'
import { Session } from '../session'
import { App } from '../app'
import { Message } from './message'
Expand Down Expand Up @@ -59,17 +59,15 @@ export default function apply(app: App) {
return ''
})

function createCollector<T extends TableType>(key: T) {
return function* (argv: ParsedArgv, fields: Set<keyof Tables[T]>) {
const { args: [name] } = argv
const command = app._commandMap[name] || app._shortcutMap[name]
if (!command) return
yield* Command.collect({ ...argv, args: [], options: { help: true } }, key, fields)
}
const createCollector = <T extends TableType>(key: T): FieldCollector<T> => (argv, fields) => {
const { args: [name] } = argv
const command = app._commandMap[name] || app._shortcutMap[name]
if (!command) return
Command.collect({ ...argv, command, args: [], options: { help: true } }, key, fields)
}

app.command('help [command]', '显示帮助信息', { authority: 0 })
.userFields(['authority'])
.userFields<ValidationField>(['authority'])
.userFields(createCollector('user'))
.groupFields(createCollector('group'))
.shortcut('帮助', { fuzzy: true })
Expand Down Expand Up @@ -98,6 +96,7 @@ export default function apply(app: App) {
},
})
}

return showHelp(command, session, options)
})
}
Expand Down
4 changes: 2 additions & 2 deletions packages/koishi-core/src/plugins/shortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export default function apply(ctx: Context) {
}
})

ctx.on('parse', (message, { $prefix, $appel }, builtin) => {
if (!builtin || $prefix) return
ctx.on('parse', (message, { $reply, $prefix, $appel }, builtin) => {
if (!builtin || $prefix || $reply) return
for (const shortcut of ctx.app._shortcuts) {
const { name, fuzzy, command, oneArg, prefix, options, args = [] } = shortcut
if (prefix && !$appel) continue
Expand Down
Loading

0 comments on commit cac2bea

Please sign in to comment.