开始
简介
Saukko (发音为 /ˈsaukːo/) 是一款可拓展、轻量级的聊天机器人框架。
Saukko 一词取自芬兰语,意为“水獭”。水獭是一种机敏、灵活的动物,具有细长、流线型的身体结构,擅长在水中快速移动和捕食。Saukko 的设计灵感来源于水獭的这些特点,旨在为用户提供一个上手简单、部署快速的聊天机器人框架。
Saukko Core 使用依赖注入管理框架内部服务。这给予了用户与开发者极大的灵活性,让框架易于拓展与维护。
创建项目
首先,你需要创建一个空白的项目。我们推荐使用的包管理器是 pnpm。
pnpm init安装 CLI 依赖。
pnpm add -D saukko随后,手动创建配置文件 saukko.toml。
[project]
name = "project"
[plugin]
config = {}
[service]
config = {}接着,就可以启动框架了:
pnpm saukko当然,你也可以手动安装 Core,并自行编写脚本管理框架。
pnpm i @saukkojs/core不稳定的 API
Saukko Core 的 API 尚处于开发阶段,可能会发生重大变化。
import { Container, injectionProvider } from '@saukkojs/core';
const container = new Container();
// 配置信息将会被框架所使用
const config = {};
// @saukkojs/core 提供的 injectionProvider 方法,用于一键注入依赖。
injectionProvider(container, config, {
headless: false, // 是否以无头模式运行,即是否装载 `plugin` 与 `app` 服务。
});
// 这里,你还可以注入你自己所需的服务。
// 注意,服务需要向 `ServiceRegistry` 进行声明扩展,才能获得正确的语法提示。
declare module '@saukkojs/core' {
interface ServiceRegistry {
'my-service': /* ... */;
}
}
container.register('my-service', /* ... */);
const app = container.get('app');
// 请在 app.start() 之前注入所需要的所有服务。
await app.start();CLI
Saukko CLI 是用于管理框架的命令行工具。
不稳定的 API
Saukko CLI 尚处于开发阶段,可能会发生重大变化。
对于已经初始化完成的项目,可以使用 CLI 快速启动框架:
npx saukko此时,CLI 会启动一个 Daemon 进程,用于初始化框架容器并加载服务与插件,随后启动框架。
CLI 内部保留了以下命令:
start: 等价于npx saukko,启动框架stop: 停止框架的运行
其他的命令,将被传入 CLI 启动的 Daemon 进程中进行处理。
不稳定的 API
Saukko CLI 的 Daemon 进程 尚处于设计阶段,可能会发生重大变化。
Daemon 进程可以管理框架所加载的服务与插件,还可以与插件交互,实现更复杂的管理逻辑。
在 Daemon 进程意外终止时,框架会自动重启。
Daemon 进程与 CLI 间通过 .saukko.sock 文件(Windows 环境下为 pipe)进行通信。
对于 Daemon 进程的功能,请参阅 saukko README.md。
配置文件
Saukko CLI 支持读取 TOML 格式的配置文件。你可以通过配置文件,精准控制框架的行为,也可以管理服务与插件的配置。
不稳定的 API
Saukko CLI 的配置文件 API 尚处于开发阶段,可能会发生重大变化。
type Config = {
project: {
name: string;
};
plugin: {
scopes?: string[];
files?: string[];
config: {
[key in keyof PluginConfigRegistry]?: PluginConfigRegistry[key];
};
};
service: {
scopes?: string[];
files?: string[];
config: {
[key in keyof ServiceConfigRegistry]?: ServiceConfigRegistry[key];
};
}
}环境变量属于配置之一,只不过其影响的是 CLI 的行为。
type SaukkoEnv = {
SAUKKO_LOG_LEVEL?: 'trace' | 'debug' | 'info' | 'notice' | 'warn' | 'error' | 'silent';
SAUKKO_CONFIG_PATH?: string;
SAUKKO_SOCKET_PATH?: string;
}服务
Saukko Core 通过服务实现特定的功能。框架内置了如下的服务:
不稳定的 API
Saukko Core 的内置服务尚处于开发阶段,可能会发生重大变化。
app: 应用服务,用于管理框架的生命周期。logger: 日志服务,用于记录框架的日志。config: 配置服务,用于管理框架的配置。database(WIP): 数据库服务,用于管理框架的数据库连接。plugin: 插件服务,用于管理框架的插件。
关于 app 服务
为了让框架更加灵活,我们将 app 也设计为一个服务。这使得用户可以更加方便地管理框架的生命周期。
你也可以轻松地实现自己的服务。
// 这一行代码用于导入所依赖的 logger 服务的类型定义。
import type { LoggerService } from '@saukkojs/core';
// 通过对 ServiceRegistry 进行扩展,让依赖于此服务的服务与插件能够获得正确的语法提示。
declare module '@saukkojs/core' {
interface ServiceRegistry {
myService: MyService;
}
}
export const inject = ['logger'];
export class MyService {
constructor(private readonly logger: LoggerService) {}
method() {
this.logger.log('my-service', 'info', 'hello, world!');
}
}随后,在配置文件中注册此服务。如果你需要操作 Core 的 Container 获取服务实例,那么需要手动引入类型:
// 这一行代码用于引入 MyService 对 ServiceRegistry 的扩展。
import {} from './path/to/my-service';
const myService = container.get('my-service');
myService.method();需要注意的是,当前版本的 Saukko Core 不计划自动处理副作用。你需要在服务卸载时,手动处理副作用。
插件
Saukko Core 通过插件自定义处理聊天平台的事件与消息。
不稳定的 API
Saukko Core 的插件 API 尚处于开发阶段,可能会发生重大变化。
Saukko Core 的插件系统由 plugin 服务实现。插件语法类似于:
export const inject = []; // 声明所依赖的服务
export const name = 'my-plugin';
export default function (context: Context) {}plugin 服务使用 Context 向插件提供上下文。
上下文拥有 dependencies 属性,包含声明的依赖。上下文仅会向插件提供其所依赖的服务与指定给插件的配置,不会向插件暴露其他服务。
// 这一行代码用于导入所依赖的 external-service 服务的类型定义。
import {} from 'saukko-service-external';
// 这一行代码用于声明所依赖的服务。
export const inject = ['external-service'];
export const name = 'my-plugin';
export default function (context: Context) {
console.log(context.dependencies['external-service']); // external-service 服务的实例
}上下文拥有 config 属性,包含指定给插件的配置。
export const name = 'my-plugin';
export default function (context: Context) {
console.log(context.config); // my-plugin 插件在配置文件中的属性
}上下文拥有一个 bots 数组,包含已挂载的聊天平台适配器实例。
export default function (context: Context) {
for (const bot of context.bots) {
console.log(bot); // 已挂载的聊天平台适配器实例
}
}上下文还实现了与 EventEmitter 相似的API,用于处理来自框架内部与聊天平台的事件。
export default function (context: Context) {
context.on('message.private', (event) => {
event.bot.sendPrivateMessage(event.data.author.id, event.data.content);
});
}值得注意的是,Saukko 的聊天平台适配器属于插件。为了统一 API 并方便开发,我们提供了 Bot 抽象类,供适配器插件继承与实现。
import { Bot, Context } from '@saukkojs/core';
import eventbus from /* ... */;
declare module '@saukkojs/core' {
interface Events {
'message.private': {
/* ... */
// 此处无需声明 bot 属性。
}
}
}
class MyBot extends Bot {
constructor(context: Context) {
super(context);
eventbus.on('message', (message) => {
// 来自 `Bot` 的 `emit` 方法会将 `MyBot` 对象放在事件的 `bot` 属性里传递给上下文的事件。
this.emit('message.private', message);
});
}
sendPrivateMessage(userId: string, content: string): Promise<void> {
return /* ... */;
}
}
export default function (context: Context) {
const bot = new MyBot(context);
context.mountBot(bot);
}需要注意的是,当前版本的 Saukko Core 不计划自动处理副作用。你需要在插件卸载时,手动处理副作用。
感谢
Cocotais Bot 是 Saukko.js 系列项目的缘起,为 Saukko.js 系列项目提供了丰富的灵感与参考。