diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 935d3c4cc1..175fa9bb60 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -8,7 +8,7 @@ module.exports = { head: [ ['link', { rel: 'icon', href: `/koishi.png` }], - // ['link', { rel: 'manifest', href: '/manifest.json' }], + ['link', { rel: 'manifest', href: '/manifest.json' }], ['meta', { name: 'theme-color', content: '#5546a3' }], // ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], // ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], diff --git a/docs/.vuepress/public/manifest.json b/docs/.vuepress/public/manifest.json new file mode 100644 index 0000000000..504e26717e --- /dev/null +++ b/docs/.vuepress/public/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "Koishi", + "short_name": "Koishi", + "description": "跨平台机器人框架", + "start_url": "/index.html", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#5546a3", + "icons": [ + { + "src": "/koishi.png", + "sizes": "384x384", + "type": "image/png" + } + ] +} diff --git a/docs/.vuepress/theme/index.js b/docs/.vuepress/theme/index.js index 1d11eda456..ea6c823149 100644 --- a/docs/.vuepress/theme/index.js +++ b/docs/.vuepress/theme/index.js @@ -14,6 +14,16 @@ module.exports = { ['medium-zoom', { selector: '.theme-default-content :not(a) > img:not(.no-zooming)', }], + ['@vuepress/pwa', { + skipWaiting: true, + }], + ['@vuepress/docsearch', { + apiKey: '24a872e49e34cdb7736d132917a308c6', + indexName: 'koishi', + searchParameters: { + facetFilters: ['tags:latest'], + }, + }], ['@vuepress/container', { type: 'code-group', before: (info) => { diff --git a/docs/api/changelog.md b/docs/api/changelog.md index 640dc0cdc1..9263068db1 100644 --- a/docs/api/changelog.md +++ b/docs/api/changelog.md @@ -5,6 +5,42 @@ externalIcon: false # v3 更新日志 +## [Koishi 3.10.2](https://github.com/koishijs/koishi/releases/tag/3.10.2) + +### Features + +- **chat:** 优化了 at 消息段的处理 (=d80af91267986e1455c1f52998248c2d460c5a3c) +- **common:** assign 指令支持了 -T 选项,用于取消频道代理者 (=40afea00a027b62b2a5e51375c7036a75309d5f2) +- **core:** 支持了函数类型的 `app.options.prefix` (=578008098031fc7942a074e907d8c9ca8601a46b) +- **discord:** 为 at, sharp 消息段添加了 name 属性 (=43a23cae0c096d257dcd4aa944455d16215956c2) +- **kaiheila:** 导出了 `KaiheilaBot` 构造函数 (#258) (=8fb118e57b8bfafda1711e2b50a7cb9cdd5c6dc6) +- **puppeteer:** 使用 inline-block 布局以获得更好的截图区域 (=8f3c98dd7bcbf4ab41289db025894d228f9f438b) +- **webui:** 将自动删除距今过于遥远的统计数据 (=767853efadae08db3c1ba06fe4daedc085ee3e20) +- **webui:** 新增了指令调用频率的统计图表 (#252) (=4d5d87a533e02ecab5f573247f4732ae40156feb) + +### Bug Fixes + +- **common:** 修复了错误的类型标注 (#254) (=ab64feec34f59aab3a21f25409d089398604d41b) +- **discord:** 修复了 message-deleted 事件缺少 `id` 属性的问题 (#251) (=bbd8d6c3e9684ddb3bc6ca2e71ab839c7b1d6aa3) +- **webui:** 修复了群聊消息频率统计图表当鼠标悬浮时出现报错的问题 (=361ea7c145b1e247f30fed394511a9c4e0756c9b) + +## [Koishi 3.10.1](https://github.com/koishijs/koishi/releases/tag/3.10.1) + +### Features + +- **cli:** 优化了 koishi init 时可供选择的插件列表 (=a9b7000ac6c2da383bb3fe679cc84f78be2f5f1b) +- **kaiheila:** 新增了 `kaiheila.attachMode` 配置项 (=7bc59993f59bea5e2b4d3e751965fa58e4c85d58) +- **github:** 新增了 github.issue 和 github.star 指令 (=104841c9be749ade6c44dab6bcc59d5d4e32a596) +- **webui:** 将「机器人」页面合并入「仪表盘」页面 (=d70fdcff63690ae4ec0e3afb8d2c57c4f08d8652) + +### Bug Fixes + +- **cli:** 修复了插件热重载功能将小概率触发调用爆栈的问题 (=45690007150441cbb568f85049395085756a301e) +- **discord:** 修复了不符合规范的 `user.avatar` (=14c92ca96723f7f96ebda93e20f7c5ce8436d9b1) +- **discord:** 修复了 `bot.getMessage()` 返回值缺少 `groupId` 的问题 (=c5a012437744e396d500d67a3996fc5d9dfc2c3c) +- **image-search:** 修复了消息段错误导致在部分平台上无法发送的问题 (=ff82bd60fa2959f5387d71d84028e046a04f93aa) +- **webui:** 修复了配合 MySQL 使用时小概率上传数据失败的问题 (#248) (=2a0bbfb2d53520ba14a9014e9015f511a5d92d24) + ## [Koishi 3.10.0](https://github.com/koishijs/koishi/releases/tag/3.10.0) ### Notable Changes diff --git a/docs/guide/database.md b/docs/guide/database.md index c54bf90012..a661c5a802 100644 --- a/docs/guide/database.md +++ b/docs/guide/database.md @@ -107,8 +107,38 @@ User.extend(() => ({ foo: 'bar' })) ``` ::: +如果你是插件开发者,你还需要手动处理 MySQL 字段的定义: + +::: code-group language +```js +const { Database } = require('koishi-core') + +Database.extend('koishi-plugin-mysql', ({ tables }) => { + tables.user.foo = 'varchar(100)' // MySQL 类型 +}) +``` +```ts +import { Database } from 'koishi-core' + +// 引入 koishi-plugin-mysql 的类型定义 +// 如果你是插件开发者,你应该将 koishi-plugin-mysql 作为你的 devDep +// 这行代码不会真正 require 这个依赖,因此即使用户使用的不是 MySQL 也没有关系 +import {} from 'koishi-plugin-mysql' + +Database.extend('koishi-plugin-mysql', ({ tables }) => { + tables.user.foo = 'varchar(100)' // MySQL 类型 +}) +``` +::: + 向 Channel 注入字段同理。 +::: tip +#### 为什么 MySQL 需要编写两份代码 + +看起来这是不必要的重复,但其实不然。`User.extend()` 定义的是用户表中各列的**默认值**,而 `Database.extend()` 定义的是数据库的**字段类型**,会被用于自动建表和补全字段。换句话说,如果你已经手动建好表了,那么你确实不需要编写后面的额外代码。但是反过来,如果你是插件开发者,你的用户很可能不知道这个插件需要哪些用户字段,因此这样的写法可以在用户安装插件的时候就自动创建字段。 +::: + ## 使用 ORM API Koishi 设计了一套对象关系映射(ORM)接口,它易于扩展并广泛地运用于各种插件中。 @@ -212,6 +242,34 @@ Tables.extend('schedule', { }) ``` +与上面一致,如果你是插件开发者,你还需要手动处理 MySQL 字段的定义: + +::: code-group language +```js +const { Database } = require('koishi-core') + +Database.extend('koishi-plugin-mysql', ({ tables }) => { + tables.schedule = { + id: 'int', + assignee: 'varchar(50)', + // 其他字段定义 + } +}) +``` +```ts +import { Database } from 'koishi-core' +import {} from 'koishi-plugin-mysql' + +Database.extend('koishi-plugin-mysql', ({ tables }) => { + tables.schedule = { + id: 'int', + assignee: 'varchar(50)', + // 其他字段定义 + } +}) +``` +::: + ## 扩展数据库 由于 Koishi 的数据库实现使用了注入策略,因此无论是字段,表,方法还是数据库都是可以扩展的。 diff --git a/docs/package.json b/docs/package.json index daf12072f0..cf5e23096b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,11 +11,13 @@ "build": "vuepress build ." }, "devDependencies": { - "@vuepress/plugin-register-components": "^2.0.0-beta.11", + "@vuepress/plugin-docsearch": "^2.0.0-beta.12", + "@vuepress/plugin-pwa": "^2.0.0-beta.12", + "@vuepress/plugin-register-components": "^2.0.0-beta.12", "diacritics": "^1.3.0", "shiki": "^0.9.3", "typescript": "^4.2.4", "vuepress-plugin-medium-zoom": "^1.1.9", - "vuepress-vite": "^2.0.0-beta.11" + "vuepress-vite": "^2.0.0-beta.12" } } diff --git a/package.json b/package.json index 2d104622a0..45315fa9c8 100644 --- a/package.json +++ b/package.json @@ -44,21 +44,21 @@ "@types/estree": "^0.0.47", "@types/fs-extra": "^9.0.11", "@types/mocha": "^8.2.2", - "@types/node": "^15.0.1", + "@types/node": "^15.0.2", "@types/rimraf": "^3.0.0", "@types/semver": "^7.3.5", "@types/sinonjs__fake-timers": "^6.0.2", "@types/source-map-support": "^0.5.3", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", - "c8": "^7.7.1", + "c8": "^7.7.2", "cac": "^6.7.3", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "cross-env": "^7.0.3", "cross-spawn": "^7.0.3", "del": "^6.0.0", - "esbuild": "^0.11.15", + "esbuild": "^0.11.18", "eslint": "^7.25.0", "eslint-config-standard": "^16.0.2", "eslint-import-resolver-typescript": "^2.4.0", diff --git a/packages/adapter-discord/package.json b/packages/adapter-discord/package.json index 7c6edeb337..5d1f41b116 100644 --- a/packages/adapter-discord/package.json +++ b/packages/adapter-discord/package.json @@ -1,7 +1,7 @@ { "name": "koishi-adapter-discord", "description": "Discord adapter for Koishi", - "version": "1.1.1", + "version": "1.1.2", "main": "lib/index.js", "typings": "lib/index.d.ts", "files": [ @@ -28,7 +28,7 @@ "koishi" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "@types/ws": "^7.4.2", diff --git a/packages/adapter-discord/src/utils.ts b/packages/adapter-discord/src/utils.ts index 838c095359..61199e7445 100644 --- a/packages/adapter-discord/src/utils.ts +++ b/packages/adapter-discord/src/utils.ts @@ -46,14 +46,18 @@ export async function adaptMessage(bot: DiscordBot, meta: DC.Message, session: P if (meta.mention_roles.includes(id)) { return segment('at', { role: id }) } else { - return segment('at', { id }) + const user = meta.mentions?.find(u => u.id === id) + return segment.at(id, { name: user?.username }) } }) .replace(/<:(.*):(.+?)>/, (_, name, id) => segment('face', { id: id, name })) .replace(//, (_, name, id) => segment('face', { id: id, name, animated: true })) .replace(/@everyone/, () => segment('at', { type: 'all' })) .replace(/@here/, () => segment('at', { type: 'here' })) - .replace(/<#(.+?)>/, (_, id) => segment.sharp(id)) + .replace(/<#(.+?)>/, (_, id) => { + const channel = meta.mention_channels?.find(c => c.id === id) + return segment.sharp(id, { name: channel?.name }) + }) } // embed 的 update event 太阴间了 只有 id embeds channel_id guild_id 四个成员 @@ -100,10 +104,10 @@ export async function adaptMessage(bot: DiscordBot, meta: DC.Message, session: P return session } -async function adaptMessageSession(bot: DiscordBot, meta: DC.Message, session: Partial> = {}) { +async function adaptMessageSession(bot: DiscordBot, meta: DC.Message, session: Partial = {}) { await adaptMessage(bot, meta, session) session.messageId = meta.id - session.timestamp = new Date(meta.timestamp).valueOf() || new Date().valueOf() + session.timestamp = new Date(meta.timestamp).valueOf() || Date.now() // 遇到过 cross post 的消息在这里不会传消息 id if (meta.message_reference) { const { message_id, channel_id } = meta.message_reference @@ -142,6 +146,8 @@ export async function adaptSession(bot: DiscordBot, input: DC.Payload) { } else if (input.t === 'MESSAGE_DELETE') { session.type = 'message-deleted' session.messageId = input.d.id + session.groupId = input.d.guild_id + session.channelId = input.d.channel_id } return new Session(bot.app, session) } diff --git a/packages/adapter-kaiheila/package.json b/packages/adapter-kaiheila/package.json index 4269cb0210..7f35b8de94 100644 --- a/packages/adapter-kaiheila/package.json +++ b/packages/adapter-kaiheila/package.json @@ -1,7 +1,7 @@ { "name": "koishi-adapter-kaiheila", "description": "Kaiheila adapter for Koishi", - "version": "1.1.1", + "version": "1.1.2", "main": "lib/index.js", "typings": "lib/index.d.ts", "files": [ @@ -24,7 +24,7 @@ "koishi" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "koishi-test-utils": "^6.0.0-beta.12" diff --git a/packages/adapter-kaiheila/src/index.ts b/packages/adapter-kaiheila/src/index.ts index a4cf62dfa4..9ce3bbcee6 100644 --- a/packages/adapter-kaiheila/src/index.ts +++ b/packages/adapter-kaiheila/src/index.ts @@ -27,6 +27,7 @@ declare module 'koishi-core' { export * from './types' export * from './utils' +export * from './bot' Adapter.types['kaiheila:http'] = HttpServer Adapter.types['kaiheila:ws'] = WsClient diff --git a/packages/adapter-onebot/package.json b/packages/adapter-onebot/package.json index fee91eb833..bd5d67125d 100644 --- a/packages/adapter-onebot/package.json +++ b/packages/adapter-onebot/package.json @@ -27,7 +27,7 @@ "koishi" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "@types/ws": "^7.4.2", diff --git a/packages/adapter-telegram/package.json b/packages/adapter-telegram/package.json index ddb8006023..46f689fac8 100644 --- a/packages/adapter-telegram/package.json +++ b/packages/adapter-telegram/package.json @@ -28,7 +28,7 @@ "koishi" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "koishi-test-utils": "^6.0.0-beta.12" diff --git a/packages/adapter-tomon/package.json b/packages/adapter-tomon/package.json index e3fbe6b2c9..540f336519 100644 --- a/packages/adapter-tomon/package.json +++ b/packages/adapter-tomon/package.json @@ -24,7 +24,7 @@ "koishi" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "@types/pako": "^1.0.1", diff --git a/packages/koishi-core/package.json b/packages/koishi-core/package.json index af06c45baf..c0e7b9ddcd 100644 --- a/packages/koishi-core/package.json +++ b/packages/koishi-core/package.json @@ -1,7 +1,7 @@ { "name": "koishi-core", "description": "Core features for Koishi", - "version": "3.10.1", + "version": "3.10.2", "main": "lib/index.js", "typings": "lib/index.d.ts", "engines": { diff --git a/packages/koishi-core/src/app.ts b/packages/koishi-core/src/app.ts index 7de870a0c3..5b2023f399 100644 --- a/packages/koishi-core/src/app.ts +++ b/packages/koishi-core/src/app.ts @@ -22,7 +22,7 @@ export interface DelayOptions { export interface AppOptions extends BotOptions { port?: number bots?: BotOptions[] - prefix?: string | string[] + prefix?: string | string[] | ((session: Session.Message) => void | string | string[]) nickname?: string | string[] maxListeners?: number prettyErrors?: boolean @@ -65,7 +65,6 @@ export class App extends Context { _sessions: Record = {} private _nameRE: RegExp - private _prefixRE: RegExp static defaultConfig: AppOptions = { maxListeners: 64, @@ -161,11 +160,9 @@ export class App extends Context { } prepare() { - const { nickname, prefix } = this.options + const { nickname } = this.options this.options.nickname = makeArray(nickname) - this.options.prefix = Array.isArray(prefix) ? prefix : [prefix || ''] this._nameRE = createLeadingRE(this.options.nickname, '@?', '([,,]\\s*|\\s+)') - this._prefixRE = createLeadingRE(this.options.prefix) } async start() { @@ -205,7 +202,13 @@ export class App extends Context { this._httpServer?.close() } - private async _process(session: Session, next: NextFunction) { + private _resolvePrefixes(session: Session.Message) { + const { prefix } = this.options + const temp = typeof prefix === 'function' ? prefix(session) : prefix + return Array.isArray(temp) ? temp : [temp || ''] + } + + private async _process(session: Session.Message, next: NextFunction) { let capture: RegExpMatchArray let atSelf = false, appel = false, prefix: string = null const pattern = /^\[CQ:(\w+)((,\w+=[^,\]]*)*)\]/ @@ -221,10 +224,10 @@ export class App extends Context { content = content.slice(capture[0].length) } - // eslint-disable-next-line no-cond-assign - if (capture = content.match(this._prefixRE)) { - prefix = capture[0] - content = content.slice(capture[0].length) + for (const _prefix of this._resolvePrefixes(session)) { + if (!content.startsWith(_prefix)) continue + prefix = _prefix + content = content.slice(_prefix.length) } // store parsed message diff --git a/packages/koishi-core/tests/runtime.spec.ts b/packages/koishi-core/tests/runtime.spec.ts index d251ca8bf1..caa1111e5d 100644 --- a/packages/koishi-core/tests/runtime.spec.ts +++ b/packages/koishi-core/tests/runtime.spec.ts @@ -51,8 +51,8 @@ after(() => app.stop()) describe('Runtime', () => { describe('Command Prefix', () => { it('single prefix', async () => { - app.options.prefix = '!' - app.prepare() + // also support functions + app.options.prefix = () => '!' await session1.shouldReply('cmd2', 'cmd2:123') await session4.shouldNotReply('cmd2') @@ -64,7 +64,6 @@ describe('Runtime', () => { it('multiple prefixes', async () => { app.options.prefix = ['!', '.'] - app.prepare() await session1.shouldReply('cmd2', 'cmd2:123') await session4.shouldNotReply('cmd2') @@ -76,7 +75,6 @@ describe('Runtime', () => { it('optional prefix', async () => { app.options.prefix = ['.', ''] - app.prepare() await session1.shouldReply('cmd2', 'cmd2:123') await session4.shouldReply('cmd2', 'cmd2:123') @@ -88,7 +86,6 @@ describe('Runtime', () => { it('no prefix', async () => { app.options.prefix = null - app.prepare() await session1.shouldReply('cmd2', 'cmd2:123') await session4.shouldReply('cmd2', 'cmd2:123') diff --git a/packages/koishi-test-utils/package.json b/packages/koishi-test-utils/package.json index 8c82e1457e..96fe567ec1 100644 --- a/packages/koishi-test-utils/package.json +++ b/packages/koishi-test-utils/package.json @@ -37,7 +37,7 @@ "dependencies": { "chai": "^4.3.4", "chai-as-promised": "^7.1.1", - "koishi-core": "^3.10.1", + "koishi-core": "^3.10.2", "koishi-utils": "^4.2.2" }, "devDependencies": { diff --git a/packages/koishi/ecosystem.json b/packages/koishi/ecosystem.json index 7e8e255a1f..06ff59dc20 100644 --- a/packages/koishi/ecosystem.json +++ b/packages/koishi/ecosystem.json @@ -1,10 +1,10 @@ { "koishi-adapter-discord": { - "version": "1.1.1", + "version": "1.1.2", "description": "Discord adapter for Koishi" }, "koishi-adapter-kaiheila": { - "version": "1.1.1", + "version": "1.1.2", "description": "Kaiheila adapter for Koishi" }, "koishi-adapter-onebot": { @@ -20,7 +20,7 @@ "description": "Asset provider plugin for Koishi" }, "koishi-plugin-chat": { - "version": "0.4.0", + "version": "0.4.1", "description": "Chat plugin for Koishi" }, "koishi-plugin-chess": { @@ -28,7 +28,7 @@ "description": "Chess Plugin for Koishi" }, "koishi-plugin-common": { - "version": "4.2.4", + "version": "4.2.5", "description": "Common plugins for Koishi" }, "koishi-plugin-eval": { @@ -52,7 +52,7 @@ "description": "MySQL support for Koishi" }, "koishi-plugin-puppeteer": { - "version": "2.1.1", + "version": "2.1.2", "description": "Take Screenshots in Koishi" }, "koishi-plugin-schedule": { @@ -68,7 +68,7 @@ "description": "Some simple tools for Koishi" }, "koishi-plugin-webui": { - "version": "4.4.2", + "version": "4.5.0", "description": "Show Web UI of Koishi" } } diff --git a/packages/koishi/package.json b/packages/koishi/package.json index 78aa437af8..aa381b03c9 100644 --- a/packages/koishi/package.json +++ b/packages/koishi/package.json @@ -1,7 +1,7 @@ { "name": "koishi", "description": "A QQ bot framework based on CQHTTP", - "version": "3.10.1", + "version": "3.10.2", "main": "index.js", "typings": "index.d.ts", "engines": { @@ -33,14 +33,14 @@ ], "devDependencies": { "@types/js-yaml": "^4.0.1", - "@types/prompts": "^2.0.10" + "@types/prompts": "^2.0.11" }, "dependencies": { "cac": "^6.7.3", "chokidar": "^3.5.1", "js-yaml": "^4.1.0", "kleur": "^4.1.4", - "koishi-core": "^3.10.1", + "koishi-core": "^3.10.2", "koishi-utils": "^4.2.2", "prompts": "^2.4.1" } diff --git a/packages/plugin-adventure/package.json b/packages/plugin-adventure/package.json index 205e2b49e0..7bb254af63 100644 --- a/packages/plugin-adventure/package.json +++ b/packages/plugin-adventure/package.json @@ -28,8 +28,8 @@ "adventure" ], "peerDependencies": { - "koishi-core": "^3.10.1", - "koishi-plugin-common": "^4.2.4", + "koishi-core": "^3.10.2", + "koishi-plugin-common": "^4.2.5", "koishi-plugin-mysql": "^3.3.2", "koishi-plugin-teach": "^2.1.5", "koishi-utils": "^4.2.2" diff --git a/packages/plugin-assets/package.json b/packages/plugin-assets/package.json index c52854d22f..46803d7c8b 100644 --- a/packages/plugin-assets/package.json +++ b/packages/plugin-assets/package.json @@ -29,7 +29,7 @@ "server" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "dependencies": { "axios": "^0.21.1", diff --git a/packages/plugin-chat/package.json b/packages/plugin-chat/package.json index 1cd50fc4e2..d59d60b1a9 100644 --- a/packages/plugin-chat/package.json +++ b/packages/plugin-chat/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-chat", "description": "Chat plugin for Koishi", - "version": "0.4.0", + "version": "0.4.1", "main": "lib/index.js", "typings": "lib/index.d.ts", "files": [ @@ -30,9 +30,9 @@ "server" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { - "koishi-plugin-webui": "^4.4.2" + "koishi-plugin-webui": "^4.5.0" } } diff --git a/packages/plugin-chat/src/receiver.ts b/packages/plugin-chat/src/receiver.ts index ecdde68571..14858dc7ea 100644 --- a/packages/plugin-chat/src/receiver.ts +++ b/packages/plugin-chat/src/receiver.ts @@ -121,9 +121,15 @@ export default function apply(ctx: Context, config: ReceiverConfig = {}) { } else if (code.type === 'at') { if (code.data.type === 'all') { params.abstract += '@全体成员' + } else if (code.data.type === 'here') { + params.abstract += '@在线成员' + } else if (code.data.role) { + params.abstract += '@角色组' } else if (session.subtype === 'group') { const id = `${session.platform}:${code.data.id}` - if (!userMap[id] || timestamp - userMap[id][1] >= refreshUserName) { + if (code.data.name) { + userMap[session.uid] = [Promise.resolve(code.data.name), timestamp] + } else if (!userMap[id] || timestamp - userMap[id][1] >= refreshUserName) { userMap[id] = [getUserName(session.bot, session.groupId, code.data.id), timestamp] } params.abstract += '@' + await userMap[id][0] diff --git a/packages/plugin-chess/package.json b/packages/plugin-chess/package.json index 696d130336..8e62fcf074 100644 --- a/packages/plugin-chess/package.json +++ b/packages/plugin-chess/package.json @@ -29,8 +29,8 @@ "game" ], "peerDependencies": { - "koishi-core": "^3.10.1", - "koishi-plugin-puppeteer": "^2.1.1", + "koishi-core": "^3.10.2", + "koishi-plugin-puppeteer": "^2.1.2", "koishi-utils": "^4.2.2" } } diff --git a/packages/plugin-common/package.json b/packages/plugin-common/package.json index e0c2a2cef8..268d73cd1b 100644 --- a/packages/plugin-common/package.json +++ b/packages/plugin-common/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-common", "description": "Common plugins for Koishi", - "version": "4.2.4", + "version": "4.2.5", "main": "lib/index.js", "typings": "lib/index.d.ts", "files": [ @@ -30,7 +30,7 @@ "plugin" ], "peerDependencies": { - "koishi-core": "^3.10.1", + "koishi-core": "^3.10.2", "koishi-utils": "^4.2.2" }, "devDependencies": { diff --git a/packages/plugin-common/src/admin.ts b/packages/plugin-common/src/admin.ts index f2b78e2dbe..b8cf4f6c05 100644 --- a/packages/plugin-common/src/admin.ts +++ b/packages/plugin-common/src/admin.ts @@ -371,8 +371,9 @@ export default function apply(ctx: Context, config: AdminConfig = {}) { ctx.command('channel/assign [bot:user]', '受理者账号', { authority: 4 }) .channelFields(['assignee']) - .adminChannel(async ({ session, target }, value) => { - const assignee = value ? Argv.parsePid(value)[1] : session.selfId + .option('noTarget', '-T 移除受理者') + .adminChannel(async ({ session, options, target }, value) => { + const assignee = options.noTarget ? null : value ? Argv.parsePid(value)[1] : session.selfId if (assignee === target.assignee) return template('admin.channel-unchanged') await ctx.database.createChannel(session.platform, session.channelId, { assignee }) target._merge({ assignee }) diff --git a/packages/plugin-dice/package.json b/packages/plugin-dice/package.json index 376b4e3e4a..ac2bbe42d0 100644 --- a/packages/plugin-dice/package.json +++ b/packages/plugin-dice/package.json @@ -31,7 +31,7 @@ "dice" ], "peerDependencies": { - "koishi-core": "^3.10.1", + "koishi-core": "^3.10.2", "koishi-utils": "^4.2.2" }, "devDependencies": { diff --git a/packages/plugin-eval/package.json b/packages/plugin-eval/package.json index 4d098fc474..57d4785686 100644 --- a/packages/plugin-eval/package.json +++ b/packages/plugin-eval/package.json @@ -33,13 +33,13 @@ "code" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "optionalDependencies": { - "@babel/core": "^7.13.16", + "@babel/core": "^7.14.0", "@babel/plugin-transform-react-jsx": "^7.13.12", "coffeescript": "^2.5.1", - "esbuild": "^0.11.15", + "esbuild": "^0.11.18", "json5": "^2.2.0", "typescript": "^4.2.4" }, diff --git a/packages/plugin-github/package.json b/packages/plugin-github/package.json index 959fba5827..873b0e85eb 100644 --- a/packages/plugin-github/package.json +++ b/packages/plugin-github/package.json @@ -32,14 +32,14 @@ "@types/marked": "^2.0.2", "koishi-plugin-mongo": "^2.2.3", "koishi-plugin-mysql": "^3.3.2", - "koishi-plugin-puppeteer": "^2.1.1", + "koishi-plugin-puppeteer": "^2.1.2", "koishi-test-utils": "^6.0.0-beta.12" }, "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "dependencies": { - "@octokit/webhooks-types": "^3.70.1", + "@octokit/webhooks-types": "^3.71.2", "axios": "^0.21.1", "marked": "^2.0.3" } diff --git a/packages/plugin-image-search/package.json b/packages/plugin-image-search/package.json index 0fa49b8024..1722907362 100644 --- a/packages/plugin-image-search/package.json +++ b/packages/plugin-image-search/package.json @@ -33,7 +33,7 @@ "pixiv" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "dependencies": { "axios": "^0.21.1", diff --git a/packages/plugin-mongo/package.json b/packages/plugin-mongo/package.json index 4fc0ba4a11..8ce0190b9e 100644 --- a/packages/plugin-mongo/package.json +++ b/packages/plugin-mongo/package.json @@ -32,7 +32,7 @@ "mysql" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "dependencies": { "@types/mongodb": "^3.6.12", diff --git a/packages/plugin-monitor/package.json b/packages/plugin-monitor/package.json index cd180de5dc..9cdd7e2292 100644 --- a/packages/plugin-monitor/package.json +++ b/packages/plugin-monitor/package.json @@ -17,6 +17,6 @@ }, "homepage": "https://github.com/koishijs/koishi#readme", "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" } } diff --git a/packages/plugin-mysql/package.json b/packages/plugin-mysql/package.json index 48eb54af0b..45feeac547 100644 --- a/packages/plugin-mysql/package.json +++ b/packages/plugin-mysql/package.json @@ -31,7 +31,7 @@ "mysql" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "dependencies": { "@types/mysql": "^2.15.18", diff --git a/packages/plugin-puppeteer/package.json b/packages/plugin-puppeteer/package.json index c4c937dad6..b1d496b053 100644 --- a/packages/plugin-puppeteer/package.json +++ b/packages/plugin-puppeteer/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-puppeteer", "description": "Take Screenshots in Koishi", - "version": "2.1.1", + "version": "2.1.2", "main": "lib/index.js", "typings": "lib/index.d.ts", "files": [ @@ -30,18 +30,18 @@ ], "devDependencies": { "@types/pngjs": "^6.0.0", - "@types/react": "^17.0.4", + "@types/react": "^17.0.5", "@types/react-dom": "^17.0.3", "koishi-plugin-eval": "^3.1.2", "koishi-test-utils": "^6.0.0-beta.12" }, "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "dependencies": { "chrome-finder": "^1.0.7", "pngjs": "^6.0.0", - "puppeteer-core": "^9.0.0", + "puppeteer-core": "^9.1.0", "react": "^17.0.2", "react-dom": "^17.0.2" } diff --git a/packages/plugin-puppeteer/src/index.ts b/packages/plugin-puppeteer/src/index.ts index 851c6becd2..6b6b606a88 100644 --- a/packages/plugin-puppeteer/src/index.ts +++ b/packages/plugin-puppeteer/src/index.ts @@ -258,7 +258,7 @@ export function apply(ctx: Context, config: Config = {}) { }) }) - ctx.with(['koishi-plugin-eval'], (ctx) => { + ctx1.with(['koishi-plugin-eval'], (ctx) => { ctx.worker.config.loaderConfig.jsxFactory = 'jsxFactory' ctx.worker.config.loaderConfig.jsxFragment = 'jsxFragment' ctx.worker.config.setupFiles['puppeteer.ts'] = resolve(__dirname, 'worker') @@ -266,9 +266,9 @@ export function apply(ctx: Context, config: Config = {}) { ctx.before('eval/send', (content) => { return segment.transformAsync(content, { async fragment({ content }) { - return segment.image(await ctx.puppeteer.render(` - ${content} - `)) + return await ctx.puppeteer.render(` + ${content} + `, async (page, next) => next(await page.$('body'))) }, }) }) diff --git a/packages/plugin-rss/package.json b/packages/plugin-rss/package.json index 6b98ea7bbd..f8d205a87a 100644 --- a/packages/plugin-rss/package.json +++ b/packages/plugin-rss/package.json @@ -31,7 +31,7 @@ "rss" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "koishi-test-utils": "^6.0.0-beta.12" diff --git a/packages/plugin-schedule/package.json b/packages/plugin-schedule/package.json index 21271a5269..5b09c83700 100644 --- a/packages/plugin-schedule/package.json +++ b/packages/plugin-schedule/package.json @@ -34,6 +34,6 @@ "koishi-test-utils": "^6.0.0-beta.12" }, "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" } } diff --git a/packages/plugin-teach/package.json b/packages/plugin-teach/package.json index 4799d1f4c5..9a937d1c57 100644 --- a/packages/plugin-teach/package.json +++ b/packages/plugin-teach/package.json @@ -34,12 +34,12 @@ "conversation" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "koishi-plugin-mongo": "^2.2.3", "koishi-plugin-mysql": "^3.3.2", - "koishi-plugin-webui": "^4.4.2", + "koishi-plugin-webui": "^4.5.0", "koishi-test-utils": "^6.0.0-beta.12" }, "dependencies": { diff --git a/packages/plugin-tools/package.json b/packages/plugin-tools/package.json index 9eb2f1468f..6d83c3cc54 100644 --- a/packages/plugin-tools/package.json +++ b/packages/plugin-tools/package.json @@ -21,7 +21,7 @@ "@types/qrcode": "^1.4.0" }, "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "dependencies": { "axios": "^0.21.1", diff --git a/packages/plugin-webui/client/views/home/command-chart.vue b/packages/plugin-webui/client/views/home/command-chart.vue new file mode 100644 index 0000000000..ed23971634 --- /dev/null +++ b/packages/plugin-webui/client/views/home/command-chart.vue @@ -0,0 +1,32 @@ + + + diff --git a/packages/plugin-webui/client/views/home/home.vue b/packages/plugin-webui/client/views/home/home.vue index e5970c3140..96ec85bf89 100644 --- a/packages/plugin-webui/client/views/home/home.vue +++ b/packages/plugin-webui/client/views/home/home.vue @@ -31,6 +31,7 @@ + @@ -38,6 +39,7 @@ import { computed } from 'vue' import { stats, profile, meta } from '~/client' +import CommandChart from './command-chart.vue' import GroupChart from './group-chart.vue' import HistoryChart from './history-chart.vue' import HourChart from './hour-chart.vue' diff --git a/packages/plugin-webui/package.json b/packages/plugin-webui/package.json index 7daad8a468..cb0066a151 100644 --- a/packages/plugin-webui/package.json +++ b/packages/plugin-webui/package.json @@ -1,7 +1,7 @@ { "name": "koishi-plugin-webui", "description": "Show Web UI of Koishi", - "version": "4.4.2", + "version": "4.5.0", "main": "lib/index.js", "typings": "lib/index.d.ts", "files": [ @@ -29,7 +29,7 @@ "webui" ], "peerDependencies": { - "koishi-core": "^3.10.1" + "koishi-core": "^3.10.2" }, "devDependencies": { "@fortawesome/fontawesome-free": "^5.15.3", @@ -40,14 +40,14 @@ "koishi-plugin-mongo": "^2.2.3", "koishi-plugin-mysql": "^3.3.2", "koishi-test-utils": "^6.0.0-beta.12", - "sass": "^1.32.11", - "vite": "^2.2.3", + "sass": "^1.32.12", + "vite": "^2.2.4", "vue": "^3.0.11", - "vue-echarts": "^6.0.0-rc.4", + "vue-echarts": "^6.0.0-rc.5", "vue-router": "^4.0.5" }, "dependencies": { - "systeminformation": "^5.6.12", + "systeminformation": "^5.6.14", "ws": "^7.4.5" } } diff --git a/packages/plugin-webui/src/data.ts b/packages/plugin-webui/src/data.ts index 6759e3acec..00563d3945 100644 --- a/packages/plugin-webui/src/data.ts +++ b/packages/plugin-webui/src/data.ts @@ -1,4 +1,4 @@ -import { Argv, Assets, Bot, Context, noop, Platform, Time } from 'koishi-core' +import { Argv, Assets, Bot, Context, noop, Platform } from 'koishi-core' import { cpus } from 'os' import { mem } from 'systeminformation' @@ -126,7 +126,6 @@ export class Profile implements DataSource { export namespace Profile { export interface Config { tickInterval?: number - refreshInterval?: number } export interface Payload { @@ -153,7 +152,7 @@ export class Meta implements DataSource { async get(): Promise { const now = Date.now() if (this.timestamp > now) return this.cached - this.timestamp = now + Time.hour + this.timestamp = now + this.config.metaInterval return this.cached = Promise .all(this.callbacks.map(cb => cb().catch(noop))) .then(data => Object.assign({}, ...data)) @@ -167,6 +166,7 @@ export class Meta implements DataSource { export namespace Meta { export interface Config { + metaInterval?: number } export interface Stats { diff --git a/packages/plugin-webui/src/index.ts b/packages/plugin-webui/src/index.ts index cb94f0e719..d2642088a7 100644 --- a/packages/plugin-webui/src/index.ts +++ b/packages/plugin-webui/src/index.ts @@ -83,7 +83,8 @@ const defaultConfig: Config = { title: 'Koishi 控制台', expiration: Time.week, tickInterval: Time.second * 5, - refreshInterval: Time.hour, + statsInternal: Time.minute * 10, + metaInterval: Time.hour, } export const name = 'webui' diff --git a/packages/plugin-webui/src/mysql.ts b/packages/plugin-webui/src/mysql.ts index 13d9447ee8..66ca772984 100644 --- a/packages/plugin-webui/src/mysql.ts +++ b/packages/plugin-webui/src/mysql.ts @@ -12,7 +12,7 @@ abstract class Stat { public data = {} as Record private key: string = null - constructor(private table: string, private fields: readonly K[]) { + constructor(private table: string, private fields: readonly K[], private preserve: boolean) { this.clear() } @@ -49,18 +49,19 @@ INSERT INTO \`${this.table}\` (\`time\`, ${joinKeys(Object.keys(this.data))}) \ VALUES ("${date}", ${Object.values(this.data).map(this.create).join(', ')}) \ ON DUPLICATE KEY UPDATE ${updates.join(', ')}`) } + if (!this.preserve) sqls.push(`DELETE FROM \`${this.table}\` WHERE datediff("${date}", \`time\`) > 10`) this.clear() } } namespace Stat { export class Recorded extends Stat { - constructor(table: string, fields: readonly K[], timeDomain: string) { - super(table, fields) + constructor(table: string, fields: readonly K[], preserve: boolean) { + super(table, fields, preserve) Tables.extend(table as never, { primary: 'time' }) Database.extend('koishi-plugin-mysql', ({ tables, Domain }) => { tables[table] = Object.fromEntries(fields.map(key => [key, new Domain.Json()])) - tables[table].time = timeDomain + tables[table].time = 'datetime' }) } @@ -82,12 +83,12 @@ namespace Stat { } export class Numerical extends Stat { - constructor(table: string, fields: readonly K[], timeDomain: string) { - super(table, fields) + constructor(table: string, fields: readonly K[], preserve: boolean) { + super(table, fields, preserve) Tables.extend(table as never, { primary: 'time' }) Database.extend('koishi-plugin-mysql', ({ tables }) => { tables[table] = Object.fromEntries(fields.map(key => [key, 'int unsigned'])) - tables[table].time = timeDomain + tables[table].time = 'datetime' }) } @@ -107,9 +108,9 @@ namespace Stat { } class MysqlSynchronizer implements Synchronizer { - private _daily = new Stat.Recorded('stats_daily', Synchronizer.dailyFields, 'date') - private _hourly = new Stat.Numerical('stats_hourly', Synchronizer.hourlyFields, 'datetime') - private _longterm = new Stat.Numerical('stats_longterm', Synchronizer.longtermFields, 'date') + private _daily = new Stat.Recorded('stats_daily', Synchronizer.dailyFields, false) + private _hourly = new Stat.Numerical('stats_hourly', Synchronizer.hourlyFields, false) + private _longterm = new Stat.Numerical('stats_longterm', Synchronizer.longtermFields, true) groups: StatRecord = {} daily = this._daily.data diff --git a/packages/plugin-webui/src/registry.ts b/packages/plugin-webui/src/registry.ts index b9e6711ebe..d19d781f08 100644 --- a/packages/plugin-webui/src/registry.ts +++ b/packages/plugin-webui/src/registry.ts @@ -46,23 +46,16 @@ export class Registry implements DataSource { this.ctx.webui.broadcast('registry', await this.get(true)) }, 0) - private countDescendant(plugin: Registry.PluginData): number { - return 1 + plugin.children.reduce((prev, curr) => prev + this.countDescendant(curr), 0) - } - async get(forced = false) { if (this.cached && !forced) return this.cached return this.cached = this.getForced() } private async getForced() { - const root = this.traverse(null) - const payload = { - plugins: root.children, - pluginCount: this.countDescendant(root), + return { + plugins: this.traverse(null).children, packages: await this.getPackages(), } as Registry.Payload - return payload } async switch(id: string) { @@ -175,6 +168,5 @@ export namespace Registry { export interface Payload { packages: PackageData[] plugins: PluginData[] - pluginCount: number } } diff --git a/packages/plugin-webui/src/stats.ts b/packages/plugin-webui/src/stats.ts index 84730e1b08..95703cab88 100644 --- a/packages/plugin-webui/src/stats.ts +++ b/packages/plugin-webui/src/stats.ts @@ -39,7 +39,6 @@ export namespace Synchronizer { } export const RECENT_LENGTH = 5 -export const REFRESH_INTERVAL = 60000 export function average(stats: {}[]) { const result: StatRecord = {} @@ -151,7 +150,7 @@ export class Statistics implements DataSource { async upload(forced = false) { const date = new Date() const dateHour = date.getHours() - if (forced || +date - +this.lastUpdate > REFRESH_INTERVAL || dateHour !== this.updateHour) { + if (forced || +date - +this.lastUpdate > this.config.statsInternal || dateHour !== this.updateHour) { this.lastUpdate = date this.updateHour = dateHour await this.sync?.upload(date) @@ -201,7 +200,7 @@ export class Statistics implements DataSource { platform, assignee, value: messageMap[id], - last: data.daily[0].group[id], + last: data.daily[0].group[id] || 0, }) } } @@ -216,7 +215,7 @@ export class Statistics implements DataSource { platform, name: name || key, value: messageMap[key], - last: data.daily[0].group[key], + last: data.daily[0].group[key] || 0, assignee: this.ctx.bots[`${platform}:${assignee}`]?.selfId || '', }) } @@ -256,6 +255,7 @@ export namespace Statistics { export interface Config { handleSignals?: boolean + statsInternal?: number } export type Extension = (payload: Payload, data: Synchronizer.Data) => Promise