【杂谈】Koishi插件教程 #6 编程基础与插件写作第二弹

asakurasayori 发布于 2025-10-07 350 次阅读


接着上一章继续写基础。想了想好像JS基础知识没什么要介绍的了,直接写插件教程了,如果有补充(比如说完全没有讲的TS)会穿插在插件教程里。

Koishi 插件写作

之前提到过怎么创建插件,这里不赘述了,不记得的可以翻#4,接下来逐个介绍Koishi的各个机能。

ctx

ctx的类型是Context(上下文),绝大部分Koishi功能都通过ctx提供,比如指令command,监听on,服务器server等等。接下来会先介绍最重要的指令开发。

指令开发

import { Context, Schema } from 'koishi'

export const name = 'echo'

export interface Config {}

export const Config: Schema<Config> = Schema.object({})

export function apply(ctx: Context) {
  ctx.command('echo <msg>')
    .action((_, message) => message);
}

如上,这是一个插件index.ts文件,用Typescript语言编写。

与JS不同的是,TS是一门强类型语言。我们注意到:ctx: Context 以及 Config: Schema ,冒号之前表示变量,冒号之后表示类型。比如说我们在ts中声明一个number变量我们可以这样写:

<em>let a: number = 1</em>

不过我们也不需要每时每刻都手动写出类型,ts具有强大的类型推断功能,我们一般情况下只需要和js一样写作,省略类型即可。

然后注意到文件中的import export,表示引入的类型或者功能对象,以及导出的函数(比如说apply函数,作为插件的入口)。

指令开发的格式如下:

ctx.command('指令名 <参数1> <参数2> [参数3]')
    .action((_, arg1, arg2, arg3) => { /* 功能 */ })

看到第一行,我们传一个定义指令的字符串到函数参数内:用尖括号括起来的是必选参数,用中括号括起来的是可选参数。意思是,如果一个用户发送该指令没有包含所有的必选参数,Koishi是不会响应的。另外,所有的可选参数肯定只能放在必选参数后面(识别顺序问题)。

看到第二行,跟一个.action表示定义该指令的行为,我们会传一个函数来定义指令的行为。我们也称这个函数叫做回调函数,意思是当你触发了这条指令,就会回调相应的功能。这里我们采用箭头函数的简便写法,首先第一个是保留参数 _ ,里面包含了许多消息中包含的信息,这里我们暂时还用不到,后面arg1~3表示的是我们定义指令时传进来的三个参数。

  ctx.command('echo <msg>')
    .action((_, message) => message);

意思是: 我们定义的指令名称叫echo,有一个必选参数msg,如果不包含参数将不会触发指令。指令的行为是:返回这个msg。所以说,当我们发送:echo 123时,bot就会回复123。

例子:我们定义一个指令add,有三个可选参数,分别表示可能的加数。例如 add 1 2 将会返回 3,add 1 2 3 将会返回 6,add 将会返回 0。我们可以这样写我们的指令:

ctx.command('add [arg1] [arg2] [arg3]')
    .action((_, arg1, arg2, arg3) => {
        let sum = 0;
        if(arg1) sum += arg1;
        if(arg2) sum += arg2;
        if(arg3) sum += arg3;
        return sum;
    });

它的意思是:定义一个变量记录最终的答案,如果arg1存在,则答案加上arg1,以此类推,最后返回sum。

更多和指令开发有关的知识可以学习官方文档:指令开发

监听

它的格式是:

ctx.on(事件名称, 回调函数)

监听的效果是:当事件发生时,会执行什么样的步骤。事件名称比如说有:message,当监听收到一条消息;friend-request,当有好友请求时;等等。所有事件的参考可以看这里

例子一:当收到天王盖地虎,会回复宝塔镇河妖。(引用自Koishi官网)

ctx.on('message', (session) => {
  if (session.content === '天王盖地虎') { // session.content取出收到消息的内容
    session.send('宝塔镇河妖') // session.send在当前对话环境下发送消息
  }
})

例子二:bot唤醒信息(引用自Koishi官网)

// bot-status-updated 不是会话事件
// 所以回调函数接受的参数不是 session 而是 bot
ctx.on('bot-status-updated', (bot) => {
  if (bot.status === Status.ONLINE) { // bot.status 取出机器人实例的在线状态 Status.ONLINE表示机器人在线
    // 这里的 userId 换成你的账号
    bot.sendPrivateMessage(userId, '我上线了~')
  }
})

更复杂的消息 - 消息元素

有时我们发送的不只是照片,还有可能是一张照片,一个视频或者一个文件;消息中还可能包含引用消息或者艾特用户。我们将用h元素来构造更加复杂的消息。

首先我们在源文件最顶端引入h元素:

import { h } from 'koishi';

然后我们可以在任何发送消息的场景使用h元素,包括command中的return,监听message时的session.send等等。我们使用示例如下, 应该包含了大部分的情况:

import { h } from 'koishi'

// 最简单:纯文本消息(等价于发送 "你好")
h('message', '你好')  // 用于构造一条普通文本消息

// 发送一张图片
h('img', { src: 'https://koishi.chat/logo.png' })  // src 是图片链接

// @某个用户
h('at', { id: 123456 })  // id 是要提及的用户ID

// 引用一条消息
h('quote', { id: 'abcd1234' })  // 引用消息ID,用于回复或上下文引用

// 组合多元素消息
h('message', [
  '你好,',                      // 普通文本
  h('at', { id: 123456 }),       // 提及用户
  ',欢迎回来!'                 // 普通文本
])  // children 可为数组,表示组合发送多个元素

// 图片与文本混合
h('message', [
  '给你看张图:',
  h('img', { src: 'https://example.com/pic.png' }),
  '好看吧?'
])

// 嵌套结构
h('message', [
  h('quote', { id: 'm123' }, [
    '上一条消息内容'
  ]),
  '这是我的回复'
])

例子:当一个用户进群之后,发送一条欢迎消息,包含:欢迎 xxx 进群!+ 该用户的头像

export function apply(ctx: Context) {
  // 监听用户入群事件
  ctx.on('guild-member-added', async (session) => {
    // 使用session.userId获取QQ号
    const userId = session.userId;

    // 构造欢迎消息
    const message = h('message', [
      "欢迎 ",
      h('at', { id: userId }),
      "进群!",
      h('img', { src: `http://q2.qlogo.cn/headimg_dl?dst_uin=${userId}&spec=100` })      // 头像API
    ])

    // 发送消息到当前群
    await session.send(message)
  })
}

效果如下:

最后

下一节会解答群友的疑惑,以及实现群友需求的部分插件。

此作者没有提供个人介绍。
最后更新于 2025-10-07