koishi-plugin-guild-settings
v0.0.1
Published
提供一个可供其他插件使用的、层级化的群聊配置服务
Downloads
154
Readme
Guild Settings (guild-settings) 服务文档
koishi-plugin-guild-settings 是一个为 Koishi 插件开发者设计的底层服务,旨在提供一个统一、强大且类型安全的群聊配置管理方案。
通过该服务,你的插件可以轻松地为每个群聊注册和管理配置项,而无需自行处理数据库存储、类型校验和复杂的层级逻辑。它使得插件配置的开发变得标准化和模块化。
✨ 功能特性
- 中心化管理:所有插件的群聊配置都通过同一个服务进行管理。
- 层级化结构:支持无限层级的父子配置项,轻松组织复杂设置。
- 逻辑联动:
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📘 API 参考
guildSettings.register(definition)
注册一个新的配置项。插件应该在 apply 函数中立即调用此方法来声明自己需要的所有配置。
- 参数:
definition: ConfigDefinition- 配置项的定义对象。
- 返回值:
booleantrue: 注册成功。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的类型不匹配,将抛出Error或TypeError。
- 如果
- 示例:
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;
});🚀 最佳实践:综合示例
下面是一个虚构的 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 '设置失败,可能是因为欢迎功能被总开关关闭了。';
}
})
}