guild-settings:提供一个可供其他插件使用的、层级化的群聊配置服务

自用插件,如果你愿意对接,下面是文档(AI写的)

Guild Settings (guild-settings) 服务文档

koishi-plugin-guild-settings 是一个为 Koishi 插件开发者设计的底层服务,旨在提供一个统一、强大且类型安全的群聊配置管理方案。

通过该服务,你的插件可以轻松地为每个群聊注册和管理配置项,而无需自行处理数据库存储、类型校验和复杂的层级逻辑。它使得插件配置的开发变得标准化和模块化。

:sparkles: 功能特性

  • 中心化管理:所有插件的群聊配置都通过同一个服务进行管理。
  • 层级化结构:支持无限层级的父子配置项,轻松组织复杂设置。
  • 逻辑联动boolean 类型的父项可以作为开关,一键禁用其下所有子项。
  • 类型安全:在注册和设置时进行严格的类型校验,杜绝非法数据。
  • 持久化存储:所有配置值都通过 Koishi 的 database 服务进行持久化。
  • 开发者友好:提供简洁的 API,易于集成到任何插件中。

核心概念

在使用 API 之前,请先理解以下几个核心概念。

1. 服务依赖

本插件向 Koishi 上下文注入一个名为 guildSettings 的服务。任何需要使用此功能的插件,都必须声明对其的依赖。

import 'koishi-plugin-guild-settings'

export const name = 'my-plugin'
export const inject = ['guildSettings'] // 声明必需依赖

export function apply(ctx) {
  // 现在你可以通过 ctx.guildSettings 访问服务了
}

2. 配置项定义 (ConfigDefinition)

当你的插件需要一个可配置的选项时,你需要先“注册”它。注册时需要提供一个 ConfigDefinition 对象,它描述了这个配置项的一切。

interface ConfigDefinition {
  name: string;
  type: 'string' | 'number' | 'boolean' | 'null';
  parent?: string;
  description?: string;
  defaultValue: any;
}
  • name: (必需) 配置项的唯一标识符。强烈建议使用 "插件名.分组名.配置名" 的格式,以避免冲突。例如:welcome.message.text
  • type: (必需) 配置项的值类型。
    • string, number, boolean: 基本数据类型。
    • null: 特殊类型,表示此配置项是一个“文件夹”或“分组”,它本身不能被赋值,仅用于组织其他配置项。
  • parent: (可选) 父配置项的 name。如果指定,此配置项将成为其子项。
  • description: (可选) 对此配置项的描述,方便未来的管理插件(如 CLI 或 Web UI)展示给用户。
  • defaultValue: (必需) 该配置项的默认值。当群聊未进行任何设置时,get 方法将返回此值。其类型必须与 type 字段严格匹配。

3. 层级结构与逻辑

父子关系是本服务的核心。

组织型父项 (type: 'null')

当一个父项的类型为 null 时,它主要起组织和分类的作用,就像文件系统中的文件夹。它本身没有值,只是一个路径节点。

// 'welcome.message' 是一个组织型父项
ctx.guildSettings.register({ name: 'welcome.message', type: 'null', defaultValue: null })
ctx.guildSettings.register({ name: 'welcome.message.text', type: 'string', parent: 'welcome.message', defaultValue: '欢迎!' })

逻辑型父项 (type: 'boolean')

当一个父项的类型为 boolean 时,它扮演着一个“总开关”的角色。

  • 当这个父项的值在某个群聊中为 true (或默认值为 true 且未被修改) 时,其所有子项功能正常。
  • 当这个父项的值在某个群聊中被设为 false 时,获取其任何子项的值都会直接返回 null,无论子项本身被设置成了什么。

这对于实现一键启用/禁用整个模块的功能非常有用。

// 'welcome.enabled' 是一个逻辑型父项
ctx.guildSettings.register({ name: 'welcome.enabled', type: 'boolean', defaultValue: true })
ctx.guildSettings.register({ name: 'welcome.text', type: 'string', parent: 'welcome.enabled', defaultValue: '...' })

// 在某个群聊中
await ctx.guildSettings.set(guildId, 'welcome.enabled', false)
// 此时,即使 'welcome.text' 曾被设为其他值,获取它也会得到 null
const text = await ctx.guildSettings.get(guildId, 'welcome.text') // text === null

:blue_book: API 参考

guildSettings.register(definition)

注册一个新的配置项。插件应该在 apply 函数中立即调用此方法来声明自己需要的所有配置。

  • 参数:
    • definition: ConfigDefinition - 配置项的定义对象。
  • 返回值: boolean
    • true: 注册成功。
    • false: 注册失败。通常是因为 name 冲突、parent 不存在或不合法、defaultValue 类型不匹配等。失败信息会通过 ctx.logger.warn 打印。
  • 示例:
export function apply(ctx) {
  const success = ctx.guildSettings.register({
    name: 'myPlugin.feature.enabled',
    type: 'boolean',
    description: '控制 myPlugin 的核心功能开关',
    defaultValue: true,
  });

  if (!success) {
    ctx.logger.error('插件配置项注册失败,部分功能可能无法使用!');
  }
}

guildSettings.get(guildId, key)

获取在指定群聊中,某个配置项的当前值。

  • 参数:
    • guildId: string - 目标群聊的 ID。
    • key: string - 要获取的配置项的 name
  • 返回值: Promise<T | null>
    • 返回该配置项的值,类型为 T
    • 如果数据库中没有记录,则返回其 defaultValue
    • 如果其任何一个 boolean 类型的父项为 false,则返回 null
    • 如果 key 未被注册,也会返回 null
  • 示例:
ctx.command('my-feature')
  .action(async ({ session }) => {
    const isEnabled = await ctx.guildSettings.get<boolean>(session.guildId, 'myPlugin.feature.enabled');
    if (!isEnabled) { // 包含了 false 和 null 两种情况
      return '此功能当前已禁用。';
    }
    // ... 执行功能
  });

guildSettings.set(guildId, key, value)

为指定群聊设置一个配置项的值。

  • 参数:
    • guildId: string - 目标群聊的 ID。
    • key: string - 要设置的配置项的 name
    • value: any - 要设置的值。其类型必须与注册时定义的 type 严格匹配。
  • 返回值: Promise<void>
  • 抛出错误:
    • 如果 key 未注册、key 的类型是 null、或 value 的类型不匹配,将抛出 ErrorTypeError
  • 示例:
ctx.command('my-feature.enable', '启用 my-feature 功能')
  .action(async ({ session }) => {
    try {
      await ctx.guildSettings.set(session.guildId, 'myPlugin.feature.enabled', true);
      return '功能已启用。';
    } catch (e) {
      ctx.logger.error(e);
      return '设置失败,请联系管理员。';
    }
  });

guildSettings.list()

获取所有已注册的配置项定义的列表。这对于开发设置管理插件非常有用。

  • 参数: 无。
  • 返回值: ConfigInfo[] - 一个包含所有 ConfigDefinition 对象的数组。
  • 示例:
ctx.command('settings.listall', '列出所有可配置项')
  .action(({ session }) => {
    const allSettings = ctx.guildSettings.list();
    const output = allSettings.map(s => `${s.name} (${s.type}) - 默认值: ${s.defaultValue}`).join('\n');
    return output;
  });

:rocket: 最佳实践:综合示例

下面是一个虚构的 welcome 插件,展示了如何综合运用所有 API。

import { Context } from 'koishi'
import 'koishi-plugin-guild-settings'

export const name = 'welcome'
export const inject = ['guildSettings']

export function apply(ctx: Context) {
  // --- 1. 注册配置项 ---
  ctx.guildSettings.register({
    name: 'welcome.enabled',
    type: 'boolean',
    description: '是否启用欢迎新成员功能',
    defaultValue: true,
  })

  ctx.guildSettings.register({
    name: 'welcome.message',
    type: 'string',
    parent: 'welcome.enabled',
    description: '欢迎语内容,{user} 会被替换为新成员的艾特',
    defaultValue: '欢迎新成员 {user}!',
  })

  // --- 2. 在事件中使用配置 ---
  ctx.on('guild-member-added', async (session) => {
    // get() 自动处理了父项 `welcome.enabled` 为 false 的情况
    const messageTemplate = await ctx.guildSettings.get<string>(session.guildId, 'welcome.message');

    if (messageTemplate) {
      const output = messageTemplate.replace('{user}', `<at id="${session.userId}"/>`);
      await session.send(output);
    }
  })

  // --- 3. 提供管理命令 ---
  ctx.command('welcome.set <message:text>', '设置欢迎语')
    .action(async ({ session }, message) => {
      if (!message) return '请输入欢迎语内容。';
      try {
        await ctx.guildSettings.set(session.guildId, 'welcome.message', message);
        return '欢迎语设置成功!';
      } catch (e) {
        return '设置失败,可能是因为欢迎功能被总开关关闭了。';
      }
    })
}
1 个赞