npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fraq-plugin-drift-bottle

v0.4.0

Published

Fraq 漂流瓶插件

Readme

fraq-plugin-drift-bottle

加载方式与公开 API

Fraq CLI 会生成独立的 ESM 应用,通过 ctx.install((await import('fraq-plugin-drift-bottle')).default, options) 安装本插件。因此默认导出只负责装配, 可复用的漂流瓶操作由命名导出的 DriftBottleApi 提供。

其他 Fraq 插件可以声明并注入该服务:

import { definePlugin } from '@fraqjs/fraq';
import { DriftBottleApi } from 'fraq-plugin-drift-bottle';

export default definePlugin({
  name: 'drift-bottle-consumer',
  requires: [DriftBottleApi],
  inject: { driftBottle: DriftBottleApi },
  async apply(ctx) {
    const count = await ctx.driftBottle.count();
    ctx.logger.info(`当前有 ${count} 个可捡取的漂流瓶`);
  },
});

DriftBottleApi 提供投递、捡取、评论、署名、删除、权限、重复捡取偏好和审核记录等操作;命令层只负责参数解析和回复构建。源码按职责分为:

  • src/api/:供命令和其他插件复用的业务 API。
  • src/persistence/:基于 Kysely 的 SQLite 持久化。
  • src/models/:公开数据模型。
  • src/processing/:消息转换、署名解析和 AI 审核处理。
  • src/commands/:Fraq 命令构建与用户交互。

Fraq 漂流瓶插件,支持投递、随机捡取、匿名、原名或别名署名,并可选择 AI 或人工审核投瓶内容。

需要 Node.js 22.13.0 或更高版本。

兼容 Fraq 0.14 与 Fraq CLI 0.7。

使用 Fraq CLI

fraq.yml 中先配置 fraqjs/honofraqjs/aifraqjs/kysely,再添加 drift-bottle

configVersion: 1
fraqVersion: 0.14.0

milky:
  url: http://localhost:30001/

plugins:
  fraqjs/hono:
    host: 127.0.0.1
    port: 4649

  fraqjs/ai:
    providers:
      deepseek:
        sdk: "@ai-sdk/deepseek"
        options:
          apiKey: ${{ env:DEEPSEEK_API_KEY }}
        models: [deepseek-chat]
    defaultModel: deepseek/deepseek-chat

  fraqjs/kysely:
    sqliteUrl: file:./data/drift-bottles.db

  drift-bottle:
    moderationMode: ai
    moderationModel: deepseek/deepseek-chat
    ownerIds: [123456789]

additionalDependencies:
  "@ai-sdk/deepseek": ^3
  ai: ^7
  zod: ^4

然后同步插件版本并启动:

fraq lock
fraq start

Fraq CLI 会把 drift-bottle 解析为 npm 包 fraq-plugin-drift-bottle,并检查其依赖的 fraqjs/honofraqjs/aifraqjs/kysely 插件是否已经配置。

WebUI 由 Fraq 宿主统一网关(@fraqjs/plugin-webui-gateway)挂载并提供登录入口,默认地址为 http://127.0.0.1:4649/webui/drift-bottle/。登录使用宿主级统一访问令牌(fraqjs/webui-gateway.accessToken),不再使用 QQ 账号、密码哈希或主人审批注册;登录后的宿主管理员可以管理全部瓶子、人工审核和插件配置。

登录后可以在“设置”页面修改投瓶审核方式、AI 审核模型和主人 QQ 号列表,保存后立即生效。数据库位置由 fraqjs/kysely.sqliteUrl 统一管理。

代码安装

pnpm add fraq-plugin-drift-bottle @fraqjs/plugin-hono @fraqjs/plugin-ai @fraqjs/plugin-kysely ai kysely zod

使用前需按照 Fraq AI 插件文档 安装并配置 @fraqjs/plugin-ai

import HonoPlugin from '@fraqjs/plugin-hono';
import KyselyPlugin from '@fraqjs/plugin-kysely';
import WebuiGatewayPlugin from '@fraqjs/plugin-webui-gateway';
import DriftBottlePlugin from 'fraq-plugin-drift-bottle';

ctx.install(HonoPlugin, { host: '127.0.0.1', port: 4649 });
ctx.install(KyselyPlugin, { sqliteUrl: 'file:./data/drift-bottles.db' });
ctx.install(WebuiGatewayPlugin, { accessToken: '请替换为宿主统一访问令牌' });
ctx.install(DriftBottlePlugin, {
  moderationMode: 'ai',
  moderationModel: 'fast',
  ownerIds: [123456789],
});

配置

所有配置项均为可选。

| 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | moderationMode | 'ai' \| 'manual' | 'ai' | 投瓶审核方式;人工模式会先进入待审核列表,通过后才公开。 | | moderationModel | string | AI 插件默认模型 | AI 模型别名或 提供商/模型;需支持所投递的图片或视频。 | | ownerIds | number[] | [] | 插件主人 QQ 号,用于机器人侧的删除、审核和管理权限。 |

WebUI 挂载路径固定为 /webui/drift-bottle,旧版 webuiPath 配置已不再生效;宿主必须安装 @fraqjs/plugin-webui-gateway 并配置非空 accessToken

从旧版数据库升级

0.3.17 及更早版本通过本插件的 storagePath 直接打开 SQLite。升级后,请删除 drift-bottle.storagePath,并把同一文件配置为 Kysely 数据库:

plugins:
  fraqjs/kysely:
    sqliteUrl: file:./data/drift-bottles.db

  drift-bottle:
    moderationMode: ai

首次启动时,Kysely 会在原文件中登记并执行漂流瓶 schema 迁移;现有表名和数据保持不变。插件不会自动复制或删除旧数据库文件。相对路径仍以 Fraq 进程的工作目录为基准,因此升级前应确认 sqliteUrl 指向原 storagePath 文件。

审核记录

审核记录保存在 SQLite 的 bottle_moderation_records 表中。

| 字段 | 说明 | | --- | --- | | id | 审核记录 ID。 | | created_at | 记录时间,Unix 毫秒时间戳。 | | content | 投稿消息段的 JSON 快照。 | | process | AI 返回的审核结果、错误信息,或等待人工审核状态。 | | input_tokens | 输入 Token,无法获得时为 NULL。 | | output_tokens | 输出 Token,无法获得时为 NULL。 | | total_tokens | 总 Token,无法获得时为 NULL。 | | success | AI 调用成功或成功进入人工队列为 1,AI 调用失败为 0。 | | approved | 内容通过为 1,被 AI 拒绝或等待人工审核为 0,调用失败为 NULL。 |

命令

| 命令 | 说明 | | --- | --- | | 扔瓶子 <内容> | 投递漂流瓶,支持文字、图片、视频、表情、动态表情和合并转发;非文字内容可通过回复投递。 | | 捡瓶子 | 随机捡取一个漂流瓶。 | | 评论漂流瓶 <ID> <内容> | 评论指定漂流瓶;评论仅支持文字,最多 500 个字符,并经过 AI 审核。 | | 评论漂流瓶 <ID> | 查看指定漂流瓶的评论,最多显示最新 20 条;也可使用 漂流瓶评论。 | | 回复后发送 评论漂流瓶 <内容> | 从被回复的投递确认或捡取消息读取瓶子 ID 并评论;省略内容可查看评论。 | | 漂流瓶重复捡 开启 | 个人捡取后保留瓶子,允许再次被捡取。 | | 漂流瓶重复捡 关闭 | 个人捡取后删除瓶子。 | | 漂流瓶重复捡 默认 | 清除个人设置,恢复默认的捡取后删除行为。 | | 漂流瓶署名 匿名 | 后续投递保持匿名。 | | 漂流瓶署名 原名 | 后续投递使用当前群昵称或 QQ 昵称。 | | 漂流瓶署名 <别名> | 后续投递使用别名,最多 20 个字符。 | | 删除漂流瓶 <ID> | 普通用户只能删除自己投递的瓶子;插件主人、群管和授权列表成员可删除任意瓶子。 | | 漂流瓶审核 通过 <审核记录ID> | 通过待审核投瓶并公开;仅插件主人和漂流瓶管理员可执行。 | | 漂流瓶审核 拒绝 <审核记录ID> <理由> | 拒绝待审核投瓶并归档,拒绝理由必填;仅插件主人和漂流瓶管理员可执行。 | | 漂流瓶权限 添加 <QQ号或@用户...> | 将一个或多个用户加入管理权限列表,使其可以删除和审核漂流瓶;支持 QQ 号、提及或混合输入,仅插件主人可执行。 | | 漂流瓶权限 删除 <QQ号或@用户...> | 将一个或多个用户移出管理权限列表,支持 QQ 号、提及或混合输入;仅插件主人可执行。 | | 漂流瓶权限 列表 | 查看数据库中的漂流瓶管理权限列表,仅插件主人可执行。 | | 漂流瓶帮助 | 以合并转发查看帮助,每条消息使用“指令”和“说明”展示一个命令。 |

行为

  • 默认匿名;署名模式按 QQ 用户保存。原名会在投递时读取当前群昵称或 QQ 昵称,旧瓶子不受后续改名影响。
  • AI 模式下,投瓶内容和署名通过 AI 审核后才会公开;人工模式下,新投瓶会保存完整草稿并进入待审核列表,通过后才会公开。
  • 人工模式只改变投瓶流程;评论、别名和原名署名仍使用 AI 审核。
  • 每次 AI 审核完成后,插件日志会记录输入、输出和总 Token 数量。
  • 每次 AI 审核都会写入 SQLite 的 bottle_moderation_records 表,包括时间、投稿内容 JSON、审核结果或错误、Token 用量、调用是否成功及内容是否通过。
  • R18 审核包含性暗示倾向、敏感部位聚焦或触摸等内容,卡通、动物和表情包采用相同标准。
  • AI 审核失败或服务不可用时不会绕过审核,相关投瓶记录会进入待审核列表供人工处理。
  • 数据存储在 SQLite 中,旧版数据库会自动迁移。
  • 漂流瓶管理权限列表存储在同一个 SQLite 数据库中;群主和群管理员无需加入列表即可删除瓶子,但人工审核仍需主人或列表授权。
  • 重复捡取设置按 QQ 用户存储在 SQLite 中,只影响该用户执行 捡瓶子 时的行为。
  • 捡取后删除只移除瓶子内容,评论索引会保留;使用管理删除命令会同时删除评论。
  • 包含图片、视频、表情、动态表情或合并转发的瓶子会先单独发送来源和 ID,再发送瓶子内容。