koishi-plugin-user-settings
v0.0.1
Published
提供一个可供其他插件使用的、层级化的用户个性化配置服务
Readme
User Settings (user-settings) 服务文档
koishi-plugin-user-settings 是一个为 Koishi 插件开发者设计的底层服务,旨在提供一个统一、强大且类型安全的用户个性化配置管理方案。
与 guild-settings 专注于群聊配置不同,本服务允许用户为自己设置全局配置(在所有群聊中生效)和分群配置(仅在特定群聊中生效,且优先级更高)。
通过该服务,你的插件可以轻松地为每个用户注册和管理配置项,而无需自行处理数据库存储、作用域优先级和复杂的层级逻辑。
✨ 功能特性
- 双重作用域:支持用户设置全局配置,或针对特定群聊进行覆盖。
- 优先级策略:获取配置时,自动遵循
分群配置 > 全局配置 > 默认值的优先级。 - 中心化管理:所有插件的用户配置都通过同一个服务进行管理。
- 层级化结构:支持无限层级的父子配置项,轻松组织复杂设置。
- 逻辑联动:
boolean类型的父项可以作为开关,一键禁用其下所有子项。 - 类型安全:在注册和设置时进行严格的类型校验,杜绝非法数据。
- 持久化存储:所有配置值都通过 Koishi 的
database服务进行持久化。 - 开发者友好:提供简洁的 API,易于集成到任何插件中。
核心概念
在使用 API 之前,请先理解以下几个核心概念。
1. 服务依赖
本插件向 Koishi 上下文注入一个名为 userSettings 的服务。任何需要使用此功能的插件,都必须声明对其的依赖。
import 'koishi-plugin-user-settings'
export const name = 'my-plugin'
export const inject = ['userSettings'] // 声明必需依赖
export function apply(ctx) {
// 现在你可以通过 ctx.userSettings 访问服务了
}2. 配置项定义 (ConfigDefinition)
这与 guild-settings 完全相同。注册配置时需要提供一个 ConfigDefinition 对象。
interface ConfigDefinition {
name: string;
type: 'string' | 'number' | 'boolean' | 'null';
parent?: string;
description?: string;
defaultValue: any;
}name: (必需) 配置项的唯一标识符。建议使用"插件名.分组名.配置名"的格式。type: (必需) 配置项的值类型。null类型代表一个“文件夹”,用于组织。parent: (可选) 父配置项的name。description: (可选) 配置项的描述。defaultValue: (必需) 配置项的默认值,类型必须与type匹配。
3. 作用域与优先级 (Scope & Precedence)
这是 user-settings 的核心特性。
- 全局配置 (Global Setting): 用户的一个配置项,不关联任何特定群聊。这是用户的通用偏好。
- 分群配置 (Per-Guild Setting): 用户在特定群聊中为同一个配置项设置的值。这会覆盖用户的全局配置。
当调用 get 方法时,服务会自动按以下顺序查找并返回第一个找到的值:
- 分群配置:用户在指定群聊中的设置。
- 全局配置:用户的全局设置。
- 默认值:在
ConfigDefinition中定义的defaultValue。
4. 层级结构与逻辑
这与 guild-settings 完全相同。boolean 类型的父项可以作为其所有子项的总开关。如果一个父项在某个作用域(分群或全局)下被设为 false,那么获取其子项的值时将直接返回 null,无视子项自身的配置和更低优先级的配置。
📘 API 参考
userSettings.register(definition)
注册一个新的用户配置项。
- 参数:
definition: ConfigDefinition - 返回值:
boolean(成功或失败) - 与
guild-settings的register方法完全相同。
userSettings.get(userId, key, guildId?)
获取指定用户某个配置项的当前值,自动处理作用域优先级。
- 参数:
userId: string- 目标用户的 ID。key: string- 要获取的配置项的name。guildId?: string- (可选) 目标群聊的 ID。如果提供此参数,将启用“分群配置”的查找。
- 返回值:
Promise<T | null>- 返回根据优先级策略计算出的最终值。
- 如果其任何一个
boolean类型的父项为false,则返回null。 - 如果
key未被注册,也会返回null。
- 示例:
ctx.command('my-profile')
.action(async ({ session }) => {
// 获取用户简介。这是一个全局设置,所以不传入 guildId。
const bio = await ctx.userSettings.get<string>(session.userId, 'profile.bio');
// 检查用户是否希望在本群展示简介。这是一个分群设置。
const showInGuild = await ctx.userSettings.get<boolean>(session.userId, 'profile.showInGuild', session.guildId);
if (!showInGuild) return '用户已设置在本群隐藏简介。';
return `用户 ${session.author.name} 的简介:${bio || '未设置'}`;
});userSettings.set(userId, key, value, guildId?)
为指定用户设置一个配置项的值。
- 参数:
userId: string- 目标用户的 ID。key: string- 要设置的配置项的name。value: any- 要设置的值。guildId?: string- (可选) 目标群聊的 ID。- 如果提供
guildId,则设置的是分群配置。 - 如果省略
guildId,则设置的是全局配置。
- 如果提供
- 返回值:
Promise<void> - 抛出错误: 与
guild-settings相同,在 key 未注册、类型为null或value类型不匹配时抛出。 - 示例:
// 设置全局简介
ctx.command('profile.bio <text:text>', '设置你的全局简介')
.action(async ({ session }, text) => {
// 省略 guildId,设置为全局配置
await ctx.userSettings.set(session.userId, 'profile.bio', text);
return '全局简介设置成功!';
});
// 在当前群聊隐藏简介
ctx.command('profile.hide', '在本群隐藏你的简介')
.action(async ({ session }) => {
// 传入 guildId,设置为分群配置
await ctx.userSettings.set(session.userId, 'profile.showInGuild', false, session.guildId);
return '已在本群隐藏你的简介。';
});userSettings.list()
获取所有已注册的配置项定义的列表。
- 与
guild-settings的list方法完全相同。
🚀 最佳实践:综合示例
下面是一个虚构的 user-profile 插件,展示了如何综合运用所有 API。
import { Context } from 'koishi'
import 'koishi-plugin-user-settings'
export const name = 'user-profile'
export const inject = ['userSettings']
export function apply(ctx: Context) {
// --- 1. 注册配置项 ---
ctx.userSettings.register({
name: 'profile.enabled',
type: 'boolean',
description: '是否启用个人资料卡功能',
defaultValue: true,
})
ctx.userSettings.register({
name: 'profile.bio',
type: 'string',
parent: 'profile.enabled',
description: '用户的个人简介(全局)',
defaultValue: '这个人很懒,什么都没留下。',
})
ctx.userSettings.register({
name: 'profile.showInGuild',
type: 'boolean',
parent: 'profile.enabled',
description: '是否在当前群聊显示个人资料卡(分群设置)',
defaultValue: true,
})
// --- 2. 在命令中使用配置 ---
ctx.command('profile', '查看你的个人资料')
.action(async ({ session }) => {
// get() 自动处理了父项 `profile.enabled` 和作用域优先级
const show = await ctx.userSettings.get<boolean>(session.userId, 'profile.showInGuild', session.guildId)
if (!show) return // 包含 false 和 null (被父项禁用)
const bio = await ctx.userSettings.get<string>(session.userId, 'profile.bio')
return `[个人资料]\n简介:${bio}`
})
// --- 3. 提供管理命令 ---
ctx.command('profile.set.bio <bio:text>', '设置你的全局简介')
.action(async ({ session }, bio) => {
if (!bio) return '请输入简介内容。'
// 省略 guildId,设置全局配置
await ctx.userSettings.set(session.userId, 'profile.bio', bio)
return '简介设置成功!'
})
ctx.command('profile.toggle', '切换在本群是否显示你的资料')
.action(async ({ session }) => {
// 获取当前分群设置(如果不存在则获取全局,最后是默认值)
const current = await ctx.userSettings.get<boolean>(session.userId, 'profile.showInGuild', session.guildId)
// 传入 guildId,设置分群配置
await ctx.userSettings.set(session.userId, 'profile.showInGuild', !current, session.guildId)
return `资料卡在本群的显示状态已切换为:${!current}`
})
}