From b74b4560221947584dc473ad82c0216a366bfa94 Mon Sep 17 00:00:00 2001 From: Yuki Date: Thu, 7 Dec 2023 15:19:03 +0800 Subject: [PATCH] docs: update --- .vitepress/config.mts | 34 ---- develop/application.md | 243 +++++++++++++------------- develop/auth.md | 38 ----- develop/event.md | 378 +++++++++++++++++------------------------ develop/option.md | 27 --- develop/overview.md | 45 ++--- develop/prefix.md | 24 --- develop/query.md | 70 ++++++-- develop/schedule.md | 25 --- develop/sugar.md | 25 --- guide/config.md | 2 +- guide/introduce.md | 32 ++-- guide/quick-start.md | 20 ++- 13 files changed, 387 insertions(+), 576 deletions(-) delete mode 100644 develop/auth.md delete mode 100644 develop/option.md delete mode 100644 develop/prefix.md delete mode 100644 develop/schedule.md delete mode 100644 develop/sugar.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 4ad6189..2e7418b 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -50,35 +50,10 @@ const sidebar: DefaultTheme.Sidebar = { text: '消息事件', link: '/develop/event', }, - { - text: '指令前缀', - link: '/develop/prefix', - }, - { - text: '语法糖', - link: '/develop/sugar', - }, { text: '参数获取', link: '/develop/query', }, - { - text: '指令鉴权', - link: '/develop/auth', - }, - { - text: '定时任务', - link: '/develop/schedule', - }, - ], - }, - { - text: '权限', - items: [ - { - text: '插件配置项', - link: '/develop/option', - }, ], }, ], @@ -94,15 +69,6 @@ const sidebar: DefaultTheme.Sidebar = { text: '使用', items: [{ text: '配置文件', link: '/guide/config' }], }, - { - text: '插件', - items: [ - { - text: '插件列表', - link: '/plugin/awesome', - }, - ], - }, ], }; diff --git a/develop/application.md b/develop/application.md index cbe1dd6..f48cbac 100644 --- a/develop/application.md +++ b/develop/application.md @@ -2,164 +2,161 @@ ::: tip 当前页面并不会对编程语言做深入讲解,即使你是小白也可以放心观看。 -之前从未接触过 js 也没关系,接下来的开发过程中会为你逐一讲解 \( ̄︶ ̄*\)) +之前从未接触过 JavaScript 也能放心食用,接下来的开发过程中会为你逐一讲解 \( ̄︶ ̄\*\)) ::: -## 编写插件 - 如果你对 npmjs 并不了解也没关系,在这里只会介绍本地插件的编写。 但是如果你想对 kokkoro 有一个更深入的了解,还是需要熟悉 nodejs 及 npmjs 的基本原理。 -::: info -在编写模块前,你需要先在 plugins 目录下,创建一个文件夹来存放代码文件。 -::: - -例如 `plugins/test`,test 为你的 **插件名**,命名规则无严格限制。 ~~(你用中文都大丈夫,锟斤拷警告)~~ +## 编写插件 -创建完成后开始在当前目录下编写 `index.js` 文件: +在我们使用 cli 创建插件模板后,将会为你生成以下的代码: ::: code-group -```typescript [typescript] -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin(); +```javascript [javascript] +import { useCommand, useEvent } from '@kokkoro/core'; + +/** + * @type {import('@kokkoro/core').Metadata} + */ +export const metadata = { + name: 'example', + description: '插件示例', +}; + +export default function Example() { + useEvent( + ctx => { + ctx.logger.mark('Bot online.'); + }, + ['session.ready'], + ); + + useCommand('/测试', () => 'hello world'); + useCommand('/复读 ', ctx => ctx.query.message); +} ``` -```javascript [javascript] -const { Plugin } = require('kokkoro'); +```typescript [typescript (Hook)] +import { Metadata, useCommand, useEvent } from '@kokkoro/core'; + +export const metadata: Metadata = { + name: 'example', + description: '示例插件', +}; + +export default function Example(): void { + useEvent( + ctx => { + ctx.logger.mark('Bot online.'); + }, + ['session.ready'], + ); + + useCommand('/测试', () => 'hello world'); + useCommand<{ message: string }>('/复读 ', ctx => ctx.query.message); +} +``` -const plugin = new Plugin(); +```typescript [typescript (Decorator)] +import { Command, CommandContext, Context, Event, Plugin } from '@kokkoro/core'; + +@Plugin({ + name: 'example', + description: '示例插件', +}) +export default class Example { + @Event('session.ready') + onReady(ctx: Context<'session.ready'>) { + ctx.logger.mark('Bot online.'); + } + + @Command('/测试') + sayHello() { + return 'hello world'; + } + + @Command('/复读 ') + replayMessage(ctx: CommandContext<{ message: string }>) { + return ctx.query.message; + } +} ``` ::: -这个时候你就已经写好了一个插件,不需要任何额外操作,该插件会在 **项目启动时** 自动将其 `require` 执行。 +这个时候你就已经写好了一个插件,该插件会在**项目启动时**自动将其进行挂载。 -不过目前这个插件还什么都不能干,我们没有为其编写相应的交互逻辑。 +## 进行交互 -## 实现交互 - -相信你这个时候一定有很多疑问,`Plugin` 到底是什么? +相信你这个时候一定有很多疑问,虽然我们前面有讲过,`metadata` 是用来作为插件的唯一标识,但是 `Event` 与 `Command` 又是什么? ::: info 当前章节仅提供示例,目的在于让你能自己编写出可以进行简单交互的插件。 目前你无需关心这段代码是什么意思,后面会逐一介绍,所以不用着急,让我们继续。 ::: -你可以参考下列代码段,添加 `message.private` 事件监听,在 `action` 回调函数里可以编写你的逻辑代码: - -::: code-group - -```typescript{5-9} [typescript] -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin(); - -plugin - .event('message.private') - .action((ctx, bot) => { - bot.sendPrivateMsg(ctx.user_id, 'hello world'); - }) -``` - -```javascript{5-9} [javascript] -const { Plugin } = require('@kokkoro/core'); - -const plugin = new Plugin(); - -plugin - .event('message.private') - .action((ctx, bot) => { - bot.sendPrivateMsg(ctx.user_id, 'hello world'); - }) -``` - -::: - -这下我们就编写好了一段指令完整的交互逻辑,你可以大致理解为只要有人给 bot 私发了消息,便会收到 `hello world`,是不是非常简单? (●'◡'●) - -## 快来试试 - - - hello - ... - - -什么都没有发生呐!前面我们有提到,插件会在 **项目启动时** 自动挂载,当前这个插件不是在项目启动时就存在的,而是你在当前服务运行时完成开发并 **后续添加** 的。 - -不过你并不需要为此就去重启整个服务,每次重启服务 bot 都将会重新登录,频繁上下线会导致登录异常甚至掉线,我们只需要 **手动挂载** 插件就可以正常使用。 - -## 挂载插件 - -你可以给机器人发送 `mount <...name>` 或者 `挂载 <...插件名>` 指令,插件名就是你刚才创建的 **文件夹名**。 +在 bot 建立通信连接后,该插件会在日志里输出 "Bot online.",并且会对指令消息进行响应。 - mount test - - {{ - JSON.stringify({ - test: { - message: 'plugin mount success.', - } - }, null, 2) - }} - - hello - hello world + @可可萝 /测试 + hello world + @可可萝 /复读 + 缺少指令参数,有效语句为:"/复读 <message>" + @可可萝 /复读 人的本质 + 人的本质 -所有插件都是通过 `plugins` 和 `node_module` 内的文件夹名来作为唯一标识的。 +这下我们就实现好了一个插件的完整交互,是不是非常简单? (●'◡'●) ## 插件权限 -除了 `mount` 指令外,还有 `unmount`、`enable` 等内置指令,你可以输入 help 查看所有内置指令。 +我们在快速开始一栏中有提到,项目内的所有插件,都是在启动时(bot 建立通信前)自动挂载的。 - - help - - {{ - [ - 'Commands: ', - ' print 打印测试', - ' state 查看 bot 运行信息', - ' plugin 插件模块列表', - ' mount <...names> 挂载插件', - ' unmount <...names> 卸载插件', - ' reload <...names> 重载插件', - ' enable <...names> 启用插件', - ' disable <...names> 禁用插件', - ' server 查看当前群服务列表', - ' apply <...names> 应用群服务', - ' exempt <...names> 免除群服务', - ' version 版本信息', - ' help 帮助信息', - '', - 'More: https://kokkoro.js.org', - ].join('\n') - }} - - +但是现在我们有一个需求,想要运行多个 bot,但是只需要特定的 bot 去使用特定的插件,应该如何实现自定义? -你一定有所疑问,`mount` 与 `enable` 有什么区别? +打开 `kokkoro.json` 配置文件,你可以在 bot 一栏中添加 `plugins` 属性: -当你启动项目时,程序会从 `plugins` 与 `node_modules` 目录中检索,若有与之匹配的模块包,就会将其自动挂载并执行对应代码,也就是 `mount` 操作。反之,`unmount` 会直接清除整个模块包缓存,并将插件对应的事件销毁。 +```json {7} +{ + "bots": [ + { + "appid": "1145141919", + "token": "38bc73e16208135fb111c0c573a44eaa", + "secret": "6208135fb111c0c5", + "plugins": [] + } + ] +} +``` -那么 `enable` 呢?kokkoro 支持多 bot 账号管理,若出现机器人 A 想要使用某个插件,机器人 B 却不需要这个插件时,就可以使用该项指令。 +`plugins` 传入的是一个字符串数组,数组值正是插件的 `metadata.name` 属性,当 `plugins` 没传入任何参数的时候,该 bot 就会响应全部插件。 + +例如我们现在安装了 hitokoto 和 pcr 这两个插件,假如机器人**可可萝**想要使用 pcr 插件,机器人**爱梅斯**却不需要这个插件时,就可以这样去修改: + +```json {7} +{ + "bots": [ + // 可可萝 + { + "plugins": ["hitokoto", "pcr"] + }, + // 爱梅斯 + { + "plugins": ["hitokoto"] + } + ] +} +``` - disable test - - {{ - JSON.stringify({ - test: { - message: 'plugin disable success.', - }, - }, null, 2) - }} - - hello - 将不再收到消息 + @可可萝 /来点骚话 + 『只有分离后才能懂的事,却没有了感慨的时间。』——「宝石之国」 + @爱梅斯 /来点骚话 + 『只要努力活下去,总有一天会笑着回忆。』——「不可思议游戏」 + @可可萝 /发起会战 + 已开启射手座会战 (*/ω\*) + @爱梅斯 /发起会战 + 爱梅斯在这里不会作出相应 - -现在 kokkoro 已经将 test 插件屏蔽,**该 bot** 将不会处理 test 插件下的任何消息事件,不过插件仍然在正常运行,若你登录了多个账号,其它 bot 仍然可以正常收发消息。 diff --git a/develop/auth.md b/develop/auth.md deleted file mode 100644 index 0ce6712..0000000 --- a/develop/auth.md +++ /dev/null @@ -1,38 +0,0 @@ -# 指令鉴权 - -::: warning TODO -不定期更新 -::: - -在 kokkoro 看来,任何发送消息都是成员都是有等级的 - -- level 0 群成员(随活跃度提升) -- level 1 群成员(随活跃度提升) -- level 2 群成员(随活跃度提升) -- level 3 管 理 -- level 4 群 主 -- level 5 主 人(你填写的 masters) -- level 6 维护组 - -## code - -```typescript{8} -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin('test'); - -plugin - .command('hello') - .sugar(/^你好$/) - .limit(4) - .action(ctx => { - ctx.reply('hello world'); - }) -``` - - - 你好 - 越权,指令 hello 的 level 范围:4 ~ 6,你当前的 level 为:0 - 你好 - hello world - diff --git a/develop/event.md b/develop/event.md index f5ba346..4e1d633 100644 --- a/develop/event.md +++ b/develop/event.md @@ -7,7 +7,7 @@ ## 上下文 -在上一章节,我们介绍了如何编写自己的第一个插件,在挂载插件后,`test` 监听了 `message.private` 事件。 +在上一章节,我们介绍了如何编写自己的第一个插件,我们使用了 `Event`,让 `example` 插件监听了 `session.ready` 事件。 那个那个,ctx 到底是什么呀? @@ -16,294 +16,220 @@ ::: code-group -```typescript [typescript] -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin(); - -plugin - .event('message.private') - .action(console.log) +```javascript [javascript] +import { useEvent } from '@kokkoro/core'; + +/** + * @type {import('@kokkoro/core').Metadata} + */ +export const metadata = { + name: 'example', + description: '插件示例', +}; + +export default function Example() { + useEvent(console.log, ['session.ready']); +} ``` -```javascript [javascript] -const { Plugin } = require('@kokkoro/core'); +```typescript [typescript (Hook)] +import { Metadata, useEvent } from '@kokkoro/core'; -const plugin = new Plugin(); +export const metadata: Metadata = { + name: 'example', + description: '示例插件', +}; -plugin - .event('message.private') - .action(console.log) +export default function Example(): void { + useEvent(console.log, ['session.ready']); +} +``` + +```typescript [typescript (Decorator)] +import { Context, Event, Plugin } from '@kokkoro/core'; + +@Plugin({ + name: 'example', + description: '示例插件', +}) +export default class Example { + @Event('session.ready') + onReady(ctx: Context<'session.ready'>) { + console.log(ctx); + } +} ``` ::: -给 bot 私发消息后,可以在控制台看到如下输出。初次你可能看不懂这里面的大部分字段,但是下面的这些属性,就算我不写注释你应该也知道代表着什么。 +在 bot 建立通信后,可以在控制台看到如下输出。初次你可能看不懂这里面的大部分字段,但是下面的这些属性,就算我不写注释你应该也知道代表着什么。 -```javascript +```shell:no-line-numbers { - raw_message: 'hello', - font: '微软雅黑', - sender: { - user_id: 2225151531, - nickname: 'yuki', + user: { + id: '1145141919810', + username: '可可萝', + bot: true, + status: 1 }, // ... } ``` -没错,你已经猜到了,属性 `ctx` 正是你所发送消息的 **事件上下文**,它里面还有着大量你用得到的属性与方法。 +没错,你已经猜到了,属性 `ctx` 正是你所发送消息的**事件上下文**,里面还有着大量你用得到的属性与方法。 -例如我们刚刚发消息触发了 **私聊事件**,在这个上下文就有着对方的 qq 号及账号资料的相关字段。 +例如我们刚刚触发的**会话事件**,在这个上下文中就有着 bot 的 id 及账号昵称的相关字段。 ## 事件监听 -刚刚 `plugin.event()` 便是监听 bot 事件的方法,刚刚编写的 test 插件只监听了 `message.private` 事件,所以只会在私聊时执行对应逻辑。 - -例如我们刚刚使用到的是 **消息事件**,共有以下三种: - -- 全部消息事件 message -- 群组消息事件 message.group -- 私聊消息事件 message.private +上面示例中的 `Event` 便是监听 bot 事件的方法,刚刚编写的 example 插件只监听了 `session.ready` 事件,所以只会在客户端建立通信时执行对应逻辑。 -而事件有很多很多,消息事件只是其中之一,其它比较常见的例如 **群事件**(群消息、新人入群)、**系统事件**(登录成功)都有相关事件。 - -::: info -kokkoro 是基于 oicq 协议库,事件名与协议库保持一致,更多事件可查看:https://github.com/takayama-lily/oicq/wiki/92.%E4%BA%8B%E4%BB%B6%E6%96%87%E6%A1%A3 -::: +而事件有很多很多种,会话事件只是其中之一,其它比较常见的例如**群事件**(群消息)、**频道事件**(子频道消息)都有相关事件名。kokkoro 是基于 amesu SDK,事件名与官方保持一致,更多事件可在腾讯 [官方文档](https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html#%E4%BA%8B%E4%BB%B6%E8%AE%A2%E9%98%85Intents) 查看。 在这里,你可以通过事件制作出各种各样有趣的插件,让 kokkoro 变得更加强大 o((>ω< ))o ## 代码规范 -刚刚编写的 test 插件虽然实现了基础的消息收发,但是我们并没有对消息文本做过滤,任何人对 bot 发送 **任意消息** 都将会自动回复。 +前面我们有提到,bot 是通过事件驱动的,任何动作都会产生与之相对应的事件,消息也不例外。 -如果我们想让 bot 收到 hello 就回复 hello world,收到 bye 就发送 good bye 该如何实现? +比如,你可以这样子去监听频道的消息事件: - - yuki yuki,听你这么一说,我完全懂了 - 啊?懂...懂什么哦? - 当我需要用到某个插件,在去挂载的时候,kokkoro 就帮我执行插件里面所有的代码 - 没错,至于插件的挂载就是 require 操作,与 npm 规范是保持一致的 - 那么在这个时候,我可以编写执行 plugin.event() 方法,去监听我想要得到的任何消息 - 是哦,消息事件没有你收不到,只有你想不到,目前来说足够满足日常使用 - 那我知道怎么改了! - +::: code-group -```typescript -import { Plugin } from '@kokkoro/core'; +```javascript [hook] +import { useEvent } from '@kokkoro/core'; -const plugin = new Plugin(); +export default function Example() { + useEvent(console.log, ['at.message.create']); +} +``` -plugin - .event('message.private') - .action((ctx, bot) => { - const { raw_message, user_id } = ctx; +```typescript [decorator] +import { Context, Event } from '@kokkoro/core'; - if (raw_message === 'hello') { - bot.sendPrivateMsg(user_id, 'hello world'); - } else if (raw_message === 'bye') { - bot.sendPrivateMsg(user_id, 'good bye'); - } else { - // ... - } - }) +export default class Example { + @Event('at.message.create') + onReady(ctx: Context<'at.message.create'>) { + console.log(ctx); + } +} ``` - - 蒋蒋~怎么样,是不是这样就可以解决问题了? - 蛤?! - - - - - -::: danger -尽量避免将 **指令过滤** 的逻辑代码直接写到 `action` 里! -为什么说是尽量避免,而不是严格禁止? ~~(你非要写我也拦不住啊,而且这样确实能达到效果)~~ ::: -不要仅看了前面一点内容就开始急于编写代码。 - -确实,kokkoro 的插件开发十分易上手,如果你有编程经验,甚至熟悉 js 的话,你现在完全可以独立写出自己的插件。 - -但是在这之前,请务将本章节看完,在了解整套体系之后,上手也不迟。 - -## 指令过滤 - -虽然这样写你可以实现自定义指令的效果,但是会导致插件后续的可维护性极差,不利于维护。 - -```typescript{10-16} -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin(); - -plugin - .event('message.private') - .action((ctx, bot) => { - const { raw_message, user_id } = ctx; + + @可可萝 /测试 + - if (raw_message === 'hello') { - bot.sendPrivateMsg(user_id, 'hello world'); - } else if (raw_message === 'bye') { - bot.sendPrivateMsg(user_id, 'good bye'); - } else { - // ... - } - }) +```shell:no-line-numbers {3} +{ + channel_id: '633481120', + content: '<@!3958153663914325267> /测试 ', + guild_id: '8187260533469556672', + id: '08c0df9ca8bcc2bfcf7110a0cf88ae0238df0548d3cac5ab06', + member: { + joined_at: '2022-04-05T11:55:52+08:00', + nick: 'Yuki', + roles: [ '4', '15' ] + }, + // ... +} ``` -kokkoro 在开坑之前,我的本意是满足自用,任何改动我都会自己先试一遍,验证过可行性再考虑后续开发。显然这种不断嵌套 if 的代码是不合理的,至少从开发者的角度出发,写起来非常的不舒服。 +这样一来,bot 就可以直接获取到消息的详情。 -所以在项目初期,我几乎每过几个月都会重构一次,毕竟连我自己写起来都觉得不舒服,谁还会想着来用呢?最后,经过不断地探索,针对指令需求,我封装了 `plugin.command()` 方法。 - -下列代码段与 `plugin.listen()` 的效果是一致的,但是更为简洁 + + yuki yuki,听你这么一说,我完全懂了 + 啊?懂...懂什么哦? + 既然通过事件就能获取到消息内容,那么指令的响应我是不是就可以这样去写 + ::: code-group -```typescript [typescript] -import { Plugin } from '@kokkoro/core'; +```javascript [hook] {6-11} +import { useEvent } from '@kokkoro/core'; + +export default function Example() { + useEvent( + ctx => { + const { content } = ctx; + const message = content.replace(/^.+(?=\/)/, '').trimEnd(); + + if (message === '测试') { + return 'hello world'; + } + }, + ['at.message.create'], + ); +} +``` -const plugin = new Plugin(); +```typescript [decorator] {6-11} +import { Context, Event } from '@kokkoro/core'; -plugin - .command('hello') - .action(ctx => { - ctx.reply('hello world'); - }) +export default class Example { + @Event('at.message.create') + onReady(ctx: Context<'at.message.create'>) { + const { content } = ctx; + const message = content.replace(/^.+(?=\/)/, '').trimEnd(); -plugin - .command('bye') - .action(ctx => { - ctx.reply('good bye'); - }) + if (message === '测试') { + return 'hello world'; + } + } +} ``` -```javascript [javascript] -const { Plugin } = require('@kokkoro/core'); - -const plugin = new Plugin(); - -plugin - .command('hello') - .action(ctx => { - ctx.reply('hello world'); - }) +::: -plugin - .command('bye') - .action(ctx => { - ctx.reply('good bye'); - }) -``` + + @可可萝 /测试 + hello world + 蒋蒋~怎么样,是不是这样就可以解决问题了? + 哈?! + + + + +::: danger +我们一定要避免将**指令逻辑**的代码,直接写到 `Event` 里! +为什么说是避免,而不是禁止? ~~(你非要写我也拦不住啊,而且这样确实能达到效果)~~ ::: -当然,你也可以指定指令所监听的消息类型,默认值是 **all** +## 指令处理 -```typescript -// 全部 -plugin.command('hello', 'all'); -// 群聊 -plugin.command('hello', 'group'); -// 私聊 -plugin.command('hello', 'private'); -``` +虽然这样写确实可以实现自定义指令的效果,但是会导致插件后续的可维护性极差,不利于维护。 -若你不喜欢链式调用,你也可以这样写 +所以,kokkoro 提供了 `Command` 来进行指令处理,与 `Event` 去手动监听**消息事件**实现的效果是等价的,但是更为简洁。 ::: code-group -```typescript [typescript] -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin(); -const hellpCommand = plugin.command('hello'); -const byeCommand = plugin.command('bye'); +```javascript [hook] {4} +import { useEvent } from '@kokkoro/core'; -hellpCommand.action(ctx => { - ctx.reply('hello world'); -}); -byeCommand.action(ctx => { - ctx.reply('good bye'); -}); +export default function Example() { + useCommand('/测试', () => 'hello world'); +} ``` -```javascript [javascript] -const { Plugin } = require('@kokkoro/core'); - -const plugin = new Plugin(); -const hellpCommand = plugin.command('hello'); -const byeCommand = plugin.command('bye'); - -hellpCommand.action(ctx => { - ctx.reply('hello world'); -}); -byeCommand.action(ctx => { - ctx.reply('good bye'); -}); +```typescript [decorator] {4,6} +import { Context, Command } from '@kokkoro/core'; + +export default class Example { + @Command('/测试') + sayHello() { + return 'hello world'; + } +} ``` ::: -`ctx.reply` 其实就是 `bot.sendPrivateMsg`、`bot.sendGroupMsg` 的语法糖。 - -## command 还是 event? +`Command` 其实就是 `Event` 的语法糖,`Command` 为我们监听了 at.message.create 和 group.at.message.create 这两个事件,分别对应群聊与频道,并在此基础上做了指令参数校验。 -尽管 `plugin.event()` 与 `plugin.command()` 很相似,但是它们还是有着本质上的区别。 +## Event 还是 Command? -event 是监听 bot 任意事件,而 command 是仅处理消息事件,当你调用 command 其实就是监听了与之对应的 message 事件,并针对传入的字符串做匹配处理,也只是二次封装。 - -例如你想在 bot 上线或下线时做一些 http 或者 io 操作,又或者在群内有新成员加入时消息提示, command 肯定是实现不了的 - -::: code-group - -```typescript [typescript] -import { segment } from 'amesu'; -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin(); - -plugin - .event('notice.group.increase') - .action(ctx, bot) => { - const { group_id, user_id } = ctx; - const message = ['欢迎新成员 ', segment.at(user_id), ' 的加入~']; - - bot.sendGroupMsg(group_id, message); - }) - -plugin - .event('system.offline') - .action(ctx => { - console.log('bot 已掉线,开始记录日志'); - // ... - }) -``` - -```javascript [javascript] -const { segment } = require('amesu'); -const { Plugin } = require('@kokkoro/core'); - -const plugin = new Plugin(); - -plugin - .event('notice.group.increase') - .action(ctx, bot) => { - const { group_id, user_id } = ctx; - const message = ['欢迎新成员 ', segment.at(user_id), ' 的加入~']; - - bot.sendGroupMsg(group_id, message); - }) - -plugin - .event('system.offline') - .action(ctx => { - console.log('bot 已掉线,开始记录日志'); - // ... - }) -``` - -::: +尽管 `Command` 看起来使用的会更加频繁,但是 `Event` 也同样重要,分别用于处理不同的业务场景。 -上列功能已有插件实现,你不用手动编写。`segment` 是构造消息段的方法,例如上面的代码我们使用了 `segment.at()` 来生成一个 at 信息,在新成员入群后 at 并提示,这个后面会讲解。 +例如你想在 bot 上线或下线时做一些 http 请求或者是 io 操作,又或者在群内有新成员加入时消息提示, `Command` 肯定是实现不了的。 diff --git a/develop/option.md b/develop/option.md deleted file mode 100644 index 0b55bbc..0000000 --- a/develop/option.md +++ /dev/null @@ -1,27 +0,0 @@ -# 插件配置项 - -::: warning TODO -不定期更新 -::: - -## code - -```typescript -import { Plugin, Option } from '@kokkoro/core'; - -interface TestOption extends Option { - -} - -const option: TestOption = { - apply: true, - lock: false, -}; -const plugin = new Plugin('test', option); - -plugin - .command('hello') - .action(ctx => { - ctx.reply(ctx.option); - }) -``` diff --git a/develop/overview.md b/develop/overview.md index b70afe7..f30f556 100644 --- a/develop/overview.md +++ b/develop/overview.md @@ -1,46 +1,51 @@ # 概述 -::: danger -当前页面是 v1 时期的文档,内容已过时,将会在近期更新。 -::: - ::: info 插件类型 -在编写插件之前,我们首先要了解项目结构。项目部署时就已经为大家做了初步介绍,插件共分为 **本地插件** 和 **npm 插件** 两大类。 +在编写插件之前,我们首先要了解项目结构。项目部署时就已经为大家做了初步介绍,插件共分为**本地插件**和 **npm 插件**两大类。 ::: ## 本地插件 - 本地插件将全部存放在根目录的 `plugins` 文件夹下。 -- 所有由你自己编写,并 **仅供个人使用** 的插件就可以称为本地插件。 +- 所有由你自己编写,并**仅供个人使用**的插件就可以称为本地插件。 ## npm 插件 - npm 插件都是直接使用 `npm install` 指令安装,存放在 `node_modules` 目录下。 -- 是由我或者其他开发者编写,上传至 [npmjs](https://www.npmjs.com/) 平台,为 **所有使用 kokkoro 框架的人** 提供服务。 +- 是由我或者其他开发者编写,上传至 [npmjs](https://www.npmjs.com/) 平台,为**所有使用 kokkoro 框架的人**提供服务。 还记得在初始化项目时输入的 `kokkoro init` 么,在界面会有一个选择安装插件的步骤,那些插件就全部属于 npm 插件。 ## 命名规范 -世界上不存在两片一模一样的叶子,插件也是如此。kokkoro 不存在两个一模一样的插件,所有插件都是使用其所在的文件名来作为自身的 **唯一标识**。 - -本地插件命名无任何限制,npm 插件命名都是以 `kokkoro-plugin` 作为前缀。任何插件相关的操作,例如 `kokkoro-plugin-base` 插件,使用 `base` 关键字就可以直接匹配到该插件实例。 +世界上不存在两片一模一样的叶子,插件也是如此。 -::: warning 不能同名! -所以一定要注意,你不能在 `node_modules` 已存在 `kokkoro-plugin-base` 插件的前提下,又去创建 `plugins/base` 目录。 -::: +kokkoro 不存在两个名字一模一样的插件,所有插件都是使用其模块的 `metadata.name`,来作为自身的**唯一标识**。 -万一重名了会怎么样?我也不知道,没试过。理论上这样操作会将前一个插件实例给覆盖掉,可以肯定的是这样一定会影响程序正常执行,这点是毋庸置疑的。 +如果你想开发插件并发布,那么要将插件以 `kokkoro-plugin` 作为前缀。例如 `example` 插件,使用 `kokkoro-plugin-example` 作为模块名,否则会导致插件无法被正常检索。 ## 目录结构 -你可以在项目根目录下使用 cli 指令 `kokkoro create ` 来快速创建插件模板。 +你可以在项目根目录下,使用 cli 指令 `kokkoro plugin ` 来快速创建插件模板。 + +```shell:no-line-numbers +kokkoro plugin +``` + +你可以自由选择你想使用的插件风格,这里我们以默认的 JavaScript 为例: + +```shell:no-line-numbers +✔ Which plugin style would you like to use: +> Javascript + Typescript (Hook) + Typescript (Decorator) + +Info: plugin module create successful. +``` ```tex:no-line-numbers plugins/ -└─ test/ 插件名 - ├─ index.js 程序主入口 - └─ package.json 包管理文件 (可选) +└─ example/ + ├─ index.js 程序入口 + └─ package.json 包管理文件 ``` - -这就是一个插件最基础的目录结构,它应当符合 `commonjs` 规范,除非你创建了 `package.json` 文件,否则 `index.js` 不能随意 **重命名** ,不然会导致插件无法被检索。 diff --git a/develop/prefix.md b/develop/prefix.md deleted file mode 100644 index f5ca7b0..0000000 --- a/develop/prefix.md +++ /dev/null @@ -1,24 +0,0 @@ -# 指令前缀 - -::: warning TODO -不定期更新 -::: - -## code - -```typescript{3} -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin('test'); - -plugin - .command('hello') - .action((ctx) => { - ctx.reply('hello world'); - }) -``` - - - test hello - hello world - diff --git a/develop/query.md b/develop/query.md index 7a16406..77bdde9 100644 --- a/develop/query.md +++ b/develop/query.md @@ -4,27 +4,65 @@ 不定期更新 ::: -## code +## 语法 -```typescript{6-7,9} -import { Plugin } from '@kokkoro/core'; +你可以通过 reset 语法来为指令添加参数,例如:[arg]、[...args]、<arg>、<...args>。 -const plugin = new Plugin('test'); +::: code-group -plugin - .command('say ') - .sugar(/^复述\s?(?.+)/) - .action(ctx => { - const { query } = ctx; - const { message } = query; +```javascript [javascript] {12} +import { useCommand } from '@kokkoro/core'; - ctx.reply(message); - }) +/** + * @type {import('@kokkoro/core').Metadata} + */ +export const metadata = { + name: 'example', + description: '插件示例', +}; + +export default function Example() { + useCommand('/复读 ', ctx => ctx.query.message); +} ``` +```typescript [typescript (Hook)] {9} +import { Metadata, useCommand } from '@kokkoro/core'; + +export const metadata: Metadata = { + name: 'example', + description: '示例插件', +}; + +export default function Example(): void { + useCommand<{ message: string }>('/复读 ', ctx => ctx.query.message); +} +``` + +```typescript [typescript (Decorator)] {8} +import { Command, CommandContext, Plugin } from '@kokkoro/core'; + +@Plugin({ + name: 'example', + description: '示例插件', +}) +export default class Example { + @Command('/复读 ') + replayMessage(ctx: CommandContext<{ message: string }>) { + return ctx.query.message; + } +} +``` + +::: + +指令参数会全部存储在 `ctx.query`,typescript 可以通过泛型来定义类型。需要注意的是,如果指令没提供任何参数,那么 `query` 的值是 `null` 而不是 `{}` 空对象。 + +## 参数校验 + +如果你为指令添加了必填参数(<arg>),那么当参数不匹配的时候,会自动发送语法提示。 + - test say hello world - hello world - 复述 hello world - hello world + @可可萝 /复读 + 缺少指令参数,有效语句为:"/复读 <message>" diff --git a/develop/schedule.md b/develop/schedule.md deleted file mode 100644 index 28c2d2b..0000000 --- a/develop/schedule.md +++ /dev/null @@ -1,25 +0,0 @@ -# 定时任务 - -::: warning TODO -不定期更新 -::: - -## code - -```typescript -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin('test'); - -plugin - .schedule('0 0 0 * * *', () => { - console.log('午时已到'); - }) - .schedule('0 0 12 * * 4', () => { - this.plugin.bl.forEach(bot => { - bot.gl.forEach(group => { - bot.sendGroupMsg(group.group_id, '今天疯狂星期四,谁请我吃?'); - }); - }); - }) -``` diff --git a/develop/sugar.md b/develop/sugar.md deleted file mode 100644 index ec9d829..0000000 --- a/develop/sugar.md +++ /dev/null @@ -1,25 +0,0 @@ -# 语法糖 - -::: warning TODO -不定期更新 -::: - -## code - -```typescript{7} -import { Plugin } from '@kokkoro/core'; - -const plugin = new Plugin('test'); - -plugin - .command('hello') - .sugar(/^你好$/) - .action(ctx => { - ctx.reply('hello world'); - }) -``` - - - 你好 - hello world - diff --git a/guide/config.md b/guide/config.md index 3740df8..ca876ff 100644 --- a/guide/config.md +++ b/guide/config.md @@ -18,7 +18,7 @@ "domain": null }, // 日志等级 - "logLevel": "INFO", + "log_level": "INFO", // bot 信息,与 amesu 保持一致 "bots": [ { diff --git a/guide/introduce.md b/guide/introduce.md index e05daec..b1028ed 100644 --- a/guide/introduce.md +++ b/guide/introduce.md @@ -4,8 +4,8 @@ import FlipCard from '../.vitepress/theme/components/FlipCard.vue'; # 简介 -::: info -你正在阅读的是 kokkoro v2 的文档,由于个人时间有限,内容与库的实际表现可能略有差异。 +::: info 这里是正在施工的 kokkoro v2 文档 +由于个人时间有限,部分内容可能会与框架的实际表现略有差异。 ::: ## 什么是 kokkoro? @@ -17,23 +17,24 @@ kokkoro (可可萝) 是一个基于 [amesu](https://github.com/xueelf/amesu) SDK ::: code-group ```javascript [javascript] -import { useCommand } from '@kokkoro/core'; +import { useCommand, useEvent } from '@kokkoro/core'; /** * @type {import('@kokkoro/core').Metadata} */ export const metadata = { - name: 'demo', + name: 'example', description: '插件示例', }; -export default function Demo() { +export default function Example() { + useEvent(() => console.log('Bot online.'), ['session.ready']); useCommand('/测试', () => 'hello world'); } ``` ```typescript [typescript (Hook)] -import { Metadata, useCommand } from '@kokkoro/core'; +import { Metadata, useCommand, useEvent } from '@kokkoro/core'; export const metadata: Metadata = { name: 'example', @@ -41,20 +42,26 @@ export const metadata: Metadata = { }; export default function Example(): void { + useEvent(() => console.log('Bot online.'), ['session.ready']); useCommand('/测试', () => 'hello world'); } ``` ```typescript [typescript (Decorator)] -import { Command, Plugin } from '@kokkoro/core'; +import { Command, Event, Plugin } from '@kokkoro/core'; @Plugin({ name: 'example', description: '示例插件', }) export default class Example { + @Event('session.ready') + onReady() { + console.log('Bot online.'); + } + @Command('/测试') - testMessage() { + sayHello() { return 'hello world'; } } @@ -65,15 +72,18 @@ export default class Example { #### 结果展示 - /测试 + @可可萝 /测试 hello world -上面的示例展示了 kokkoro 的指令交互,如果 `command` 函数返回了字符串,那么会直接作为消息回复出去。 +上面的示例展示了 kokkoro 插件的两个核心功能: + +- **事件监听:** 可批量监听客户端发出的任意事件。 +- **指令处理:** 如果 `command` 函数返回了**不为空**的变量,那么会将其直接作为字符串消息回复。 你可能已经有了些疑问 —— 先别急,在后续的文档中我们会详细介绍每一个细节。现在,请继续看下去,以确保你对 kokkoro 作为一个框架到底提供了什么有一个宏观的了解。 -::: info +::: info 预备知识 虽然 kokkoro 上手非常简单,但你应该对 **JavaScript** 有着基础的知识,如果你对开发完全陌生,最好不要直接从一个框架开始进行入门学习。你可以通过这篇 [JavaScript 概述](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Language_Overview) 来检验你的 **JavaScript** 知识水平。如果之前有其他框架的经验会很有帮助,但也不是必须的。 ::: diff --git a/guide/quick-start.md b/guide/quick-start.md index 3731ee0..1345e7d 100644 --- a/guide/quick-start.md +++ b/guide/quick-start.md @@ -1,6 +1,6 @@ # 快速上手 -::: info +::: info 准备工作 在开始前,请先确保你安装了 [Node.js](https://nodejs.org/zh-cn/),要求 **不能低于 node v20**。如果你对此有疑问,可以 [在这里](/about/faq) 查看关于版本问题。 ::: @@ -47,15 +47,23 @@ kokkoro start > 如果你不是全局安装的 cli,就要使用 `npx kokkoro start` 启动项目。 -如上述步骤无误,在项目启动的时候,程序会先挂载所有插件。待插件挂载完毕后,便会开始为 bot 建立通信。 +如上述步骤无误,在项目启动后,便会开始为 bot 建立通信。 -## 你好世界 +## 插件扩展 -输入相关指令,便可与插件进行交互,现在开启一段属于你的物语吧 ♪ q(≧▽≦q) +你可以直接安装 npm 插件来为自己的 bot 扩展相对应的功能,例如: + +```shell:no-line-numbers +npm i kokkoro-plugin-hitokoto +``` + +程序会在 bot 建立通信前,自动挂载项目内的所有插件。 - /测试 - hello world + @可可萝 /来点骚话 + 『大部分人并不想长大,只是没办法继续当一个小孩子。』——「小林家的龙女仆」 +现在,开启一段属于你的物语吧 ♪ q(≧▽≦q) + 当然,如果你有 JavaScript 的相关知识,随时都可以编写自己的插件,详情可在 [插件开发](/develop/application) 一栏查看,更多插件安装和使用说明可以在 [插件社区](/plugin/awesome) 中查找。