# 服务器插件

这些插件允许添加使用 UI 扩展单独无法实现的功能,例如创建新的 API 端点或使用在浏览器环境中不可用的 Node.JS 包。

插件包含在 SillyTavern 的 plugins 目录中,并在服务器启动时加载,但仅当 config.yaml 文件中的 enableServerPlugins 设置为 true 时才会加载。

有关所有官方服务器插件的列表,请参见 GitHub 组织列表:https://github.com/search?q=topic%3Aplugin+org%3ASillyTavern&type=Repositories

# 插件类型

# 文件

一个可执行的 JavaScript 文件,带有 ".js"(用于 CommonJS 模块)或 ".mjs"(用于 ES 模块)扩展名,包含一个导出 init 函数的模块。此函数接受一个 Express 路由器(专门为您的插件创建)作为参数并返回一个 Promise。

该模块还应导出一个包含有关插件信息的 info 对象(idnamedescription 字符串)。这将为加载器提供有关插件的信息。

您可以通过路由器注册路由,这些路由将在 /api/plugins/{id}/{route} 路径下注册。例如,example 插件的 router.get('/foo') 将产生这样的路由:/api/plugins/example/foo

插件还可以可选地导出一个 exit 函数,该函数在关闭服务器时执行清理。它应该没有参数并且必须返回一个 Promise。

插件导出的 TypeScript 合约:

interface PluginInfo {
    id: string;
    name: string;
    description: string;
}

interface Plugin {
    init: (router: Router) => Promise<void>;
    exit: () => Promise<void>;
    info: PluginInfo;
}

请参见下面的"Hello world!"插件示例:

/**
 * 初始化插件。
 * @param {import('express').Router} router Express 路由器
 * @returns {Promise<any>} Promise 在插件初始化时解析
 */
async function init(router) {
    // 在这里进行初始化...
    router.get('/foo', req, res, function () {
       res.send('bar');
    });
    console.log('Example plugin loaded!');
    return Promise.resolve();
}

async function exit() {
    // 在这里进行一些清理...
    return Promise.resolve();
}

module.exports = {
    init,
    exit,
    info: {
        id: 'example',
        name: 'Example',
        description: 'My cool plugin!',
    },
};

# 目录

您可以通过以下方式之一(按优先级顺序)从 plugins 目录中的子目录加载插件:

  1. 一个 package.json 文件,其中包含在 "main" 字段中指向可执行文件的路径。
  2. CommonJS 模块的 index.js 文件。
  3. ES 模块的 index.mjs 文件。

结果文件必须导出一个 init 函数和一个 info 对象,具有与单个文件相同的要求。

目录插件示例(带有 index.js 文件):https://github.com/SillyTavern/SillyTavern-DiscordRichPresence-Server

# 捆绑

最好使用捆绑器(如 Webpack 或 Browserify),它将所有需求打包到一个文件中。确保将 "Node" 设置为构建目标。

使用 Webpack 和 TypeScript 的插件模板仓库:https://github.com/SillyTavern/Plugin-WebpackTemplate