Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
207a013239 |
@@ -1,39 +1,37 @@
|
|||||||
# AI回评 / Comment AI Autopilot
|
# AI回评 / Comment AI Autopilot
|
||||||
|
|
||||||
基于 AI 的 Halo 博客评论自动回复插件,支持多 AI 角色、合规检测、自审核、自动发布和对话式连续回复。
|
> 基于 AI 的 Halo 博客评论自动回复插件,支持多 AI 角色、合规检测、自动发布和对话式连续回复。
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
- **多 AI 角色** — 支持创建多个 AI 角色,每个角色有独立的昵称、人格提示词、性别、语气风格和 Gravatar 头像,可为不同文章分类指定不同角色,人格提示词留空时使用基础配置
|
- **多 AI 角色** — 独立昵称、人格提示词、性别、语气和 Gravatar 头像,支持按文章分类指定角色,人格提示词留空时使用基础配置
|
||||||
- **唤醒词** — 评论以唤醒词开头可唤醒指定角色回复,支持自定义唤醒词,可在未启用AI回评的页面使用唤醒词召唤AI
|
- **唤醒词** — 评论以唤醒词开头可唤醒指定角色,支持跨页面唤醒和二级评论
|
||||||
- **自动回复** — 监听新评论,自动调用 AI 生成回复,支持多轮对话上下文
|
- **自动回复** — 监听新评论自动调用 AI 生成回复,支持多轮对话上下文
|
||||||
- **多语言适配** — 根据评论语言自动用对应语言回复,语言要求可自定义
|
- **多语言适配** — 根据评论语言自动用对应语言回复,语言要求提示词可自定义
|
||||||
- **情感分析** — 分析评论情感倾向(非常正面/正面/中性/负面/非常负面),根据情感调整回复语气
|
- **情感分析** — 5 级情感分类(非常正面/正面/中性/负面/非常负面),根据情感调整回复语气
|
||||||
- **前置过滤(合规检测)** — AI 回复前对评论进行合规性分类,自动拦截广告/辱骂攻击/敏感内容/乱码,违规评论停止生成 AI 回复以节省 Token,可选自动将违规评论设为待审核状态
|
- **前置过滤(合规检测)** — AI 回复前对评论进行合规性分类,自动拦截广告/辱骂/敏感/乱码内容,节省 Token
|
||||||
- **误报反馈** — 被误拦截的评论可进行误报反馈,支持"AI回复"和"仅通过"两种处理方式,"仅通过"后可随时补触发 AI 回复
|
- **误报反馈** — 被误拦截的评论可进行误报反馈,支持"AI回复"和"仅通过"两种方式
|
||||||
- **草稿模式** — AI 回复先存为草稿,管理员审核后再发布,支持批量通过/拒绝/删除
|
- **草稿模式** — AI 回复先存为草稿,管理员审核后再发布,支持批量操作
|
||||||
- **失败重试** — AI 生成失败时自动重试,最大重试次数可配置(0-10次,默认3次)
|
- **失败重试** — AI 生成失败时自动重试,最大重试次数可配置(0-10 次,默认 3 次)
|
||||||
- **对话轮次限制** — 同一评论线程中限制 AI 最多回复轮次,0为不限制,默认10轮
|
- **对话轮次限制** — 限制 AI 最多回复轮次,0 为不限制,默认 10 轮
|
||||||
- **速率限制** — 每小时最大 AI 回复数量,0为不限制,防止批量评论消耗过多额度
|
- **速率限制** — 每小时最大 AI 回复数量,0 为不限制
|
||||||
- **文章/页面级开关** — 在文章编辑器中直接控制是否启用 AI 回复,支持全局页面开关一键启用/禁用所有页面(包括新建页面),文章默认开启,页面默认关闭
|
- **文章/页面级开关** — 文章默认开启、页面默认关闭,支持全局页面 AI 回复开关一键控制
|
||||||
- **评论者黑/白名单** — 支持按名称、邮箱屏蔽/信任指定评论者,支持批量添加和批量移除,站长不可手动移除
|
- **评论者黑/白名单** — 批量添加/移除评论者,白名单跳过前置过滤,黑名单不触发 AI 回复
|
||||||
- **手动触发** — 在评论管理页面对历史评论手动触发 AI 回复
|
- **手动触发** — 在评论管理页面对历史评论手动触发 AI 回复
|
||||||
- **安全审核** — 安全检查与违规内容拦截
|
- **模块化提示词** — 5 个独立模块(角色身份/安全审核/情感适配/输出规范/语言要求),均为可选,留空使用默认值
|
||||||
- **Prompt 模板** — 支持分模块自定义 Prompt 模板(角色身份/安全审核/情感适配/输出规范/语言要求),提供多种模板变量(文章标题、发布日期、评论内容、对话历史等)
|
- **智能状态检测** — 自动检测 AI Foundation 安装/启用/模型配置状态,检测 Comment Next 冲突
|
||||||
- **插件健康检查** — 实时检测 AI Foundation 安装状态、启用状态、模型配置状态,针对不同异常状态提供快捷跳转按钮
|
- **日志与实时刷新** — 按状态、情感、角色筛选,关键词搜索,实时刷新偏好自动保存
|
||||||
- **日志筛选与实时刷新** — 按状态、情感、角色筛选,关键词搜索,支持查看拦截原因和分类标签;实时刷新偏好自动保存,默认开启10秒间隔
|
- **数据清理** — 自动清理旧记录,手动清理支持自定义时间节点和二次确认
|
||||||
- **数据清理** — 自动清理超过指定天数的旧记录,手动清理支持自定义时间节点(默认清理7天前的记录)
|
- **配置导入导出** — JSON 格式备份和迁移配置
|
||||||
- **Comment Next 兼容** — 检测到 Comment Next 插件时显示提醒,避免冲突
|
|
||||||
- **AI Foundation 集成** — 通过 Halo 官方推荐的 `ExtensionGetter` 获取 AI 服务,需安装 AI Foundation 插件
|
|
||||||
|
|
||||||
## 前置要求
|
## 前置要求
|
||||||
|
|
||||||
- Halo 2.25+
|
- Halo 2.25+
|
||||||
- AI Foundation 插件(必须)
|
- [AI Foundation](https://www.halo.run/store/apps/app-acslk9nu) 插件(必须)
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
### 应用商店安装
|
### 应用商店
|
||||||
|
|
||||||
进入 **插件** → **安装** → 应用市场搜索 **AI回评** → 安装,或前往 [Halo 应用商店](https://www.halo.run/store/apps/app-mo5tivjt) 一键安装。
|
进入 **插件** → **安装** → 应用市场搜索 **AI回评** → 安装,或前往 [Halo 应用商店](https://www.halo.run/store/apps/app-mo5tivjt) 一键安装。
|
||||||
|
|
||||||
@@ -45,85 +43,78 @@
|
|||||||
4. 选择下载的 `.jar` 文件上传
|
4. 选择下载的 `.jar` 文件上传
|
||||||
5. 安装完成后启用插件
|
5. 安装完成后启用插件
|
||||||
|
|
||||||
## 首次使用配置指南
|
## 首次使用
|
||||||
|
|
||||||
安装并启用插件后,请按以下步骤完成初始配置:
|
安装并启用插件后,请按以下步骤完成初始配置:
|
||||||
|
|
||||||
### 第一步:安装 AI Foundation 插件
|
### 1. 安装 AI Foundation 插件
|
||||||
|
|
||||||
AI回评依赖 [AI Foundation](https://www.halo.run/store/apps/app-acslk9nu) 插件提供 AI 能力。请确保:
|
AI 回评依赖 AI Foundation 插件提供 AI 能力。请确保:
|
||||||
1. 已在 **插件** 页面安装并启用了 AI Foundation 插件
|
|
||||||
|
1. 已在 **插件** 页面安装并启用 AI Foundation 插件
|
||||||
2. 在 AI Foundation 中添加了至少一个 AI 模型(如 OpenAI、Ollama、DeepSeek 等)
|
2. 在 AI Foundation 中添加了至少一个 AI 模型(如 OpenAI、Ollama、DeepSeek 等)
|
||||||
3. 在 **AI Foundation → 默认模型** 页面(`/console/ai-foundation/defaults`)设置了默认模型
|
3. 在 **AI Foundation → 默认模型** 页面设置了默认模型
|
||||||
|
|
||||||
> 💡 插件首页会自动检测 AI Foundation 状态,如未安装/未启用/未配置模型,将显示对应的提示和快捷跳转按钮。
|
> 插件首页会自动检测 AI Foundation 状态,如未安装/未启用/未配置模型,将显示对应的提示和快捷跳转按钮。
|
||||||
|
|
||||||
### 第二步:配置基础设置
|
### 2. 配置基础设置
|
||||||
|
|
||||||
进入 **评论 → AI回评 → 设置**,在"基本设置"面板中:
|
进入 **评论 → AI回评 → 设置**,在"基本设置"面板中:
|
||||||
- **自动回复**:默认开启,关闭则需手动触发回复
|
|
||||||
- **自动发布**:开启后AI回复直接发布,关闭则进入草稿待审核
|
|
||||||
- **最大重试次数**:默认3次(0-10),AI生成失败时的重试次数
|
|
||||||
- **最大对话轮次**:默认10轮,0为不限制
|
|
||||||
- **速率限制(每小时)**:默认0(不限制),防止批量评论消耗过多额度
|
|
||||||
- **启用页面AI回复**:默认关闭,开启后所有独立页面(非文章)默认启用AI回复
|
|
||||||
- **前置过滤(合规检测)**:建议开启,自动拦截广告/辱骂/敏感内容
|
|
||||||
- **白名单启用**:白名单内的评论者跳过前置过滤
|
|
||||||
|
|
||||||
### 第三步:配置提示词
|
- **自动回复**:默认开启,关闭则需手动触发
|
||||||
|
- **自动发布**:开启后 AI 回复直接发布,关闭则进入草稿待审核
|
||||||
|
- **最大重试次数**:默认 3 次(0-10)
|
||||||
|
- **最大对话轮次**:默认 10 轮,0 为不限制
|
||||||
|
- **速率限制(每小时)**:默认 0(不限制)
|
||||||
|
- **启用页面 AI 回复**:默认关闭,开启后所有页面一键启用
|
||||||
|
- **前置过滤**:建议开启,自动拦截广告/辱骂/敏感内容
|
||||||
|
- **白名单**:白名单内评论者跳过前置过滤
|
||||||
|
|
||||||
切换到"提示词设置"面板:
|
### 3. 配置提示词(可选)
|
||||||
- **角色身份提示词(personaIdentity)**:定义 AI 的基础身份和回复风格,这是全局默认角色
|
|
||||||
- 其他提示词模块(安全审核/情感适配/输出规范/语言要求)可按需调整,留空使用默认值
|
|
||||||
|
|
||||||
### 第四步:(可选)创建 AI 角色
|
切换到"提示词设置"面板,5 个模块均为可选,留空使用默认值:
|
||||||
|
|
||||||
在"AI角色管理"面板点击"添加角色",可创建多个AI角色:
|
- **角色身份提示词**:定义 AI 的基础身份和回复风格
|
||||||
- 为角色设置昵称、邮箱(用于Gravatar头像)、性别、语气风格
|
- 其他模块(安全审核/情感适配/输出规范/语言要求)可按需调整
|
||||||
- **人格提示词**:独立角色的人格设定,留空则使用基础配置中的角色身份提示词
|
|
||||||
- **唤醒词**:评论以唤醒词开头可直接召唤该角色回复
|
### 4. 创建 AI 角色(可选)
|
||||||
|
|
||||||
|
在"AI角色管理"面板点击"添加角色":
|
||||||
|
|
||||||
|
- 设置昵称、邮箱(Gravatar 头像)、性别、语气风格
|
||||||
|
- **人格提示词**:留空则使用基础配置的角色身份提示词
|
||||||
|
- **唤醒词**:评论以唤醒词开头可召唤该角色
|
||||||
- 可为不同文章分类指定不同角色
|
- 可为不同文章分类指定不同角色
|
||||||
|
|
||||||
### 第五步:(可选)配置黑/白名单
|
### 5. 配置黑/白名单(可选)
|
||||||
|
|
||||||
在"基本设置"面板的评论者黑/白名单区域:
|
在"基本设置"面板的评论者黑/白名单区域:
|
||||||
- **黑名单**:被屏蔽的评论者不会触发AI回复,支持批量添加/移除
|
|
||||||
- **白名单**:信任的评论者优先处理并跳过前置过滤
|
|
||||||
- 点击"添加评论者"可从历史评论者中多选批量添加,也支持手动输入名称或邮箱
|
|
||||||
|
|
||||||
### 第六步:开始使用
|
- 支持从历史评论者中多选批量添加,也支持手动输入
|
||||||
|
- 黑名单评论者不触发 AI 回复,白名单评论者跳过前置过滤
|
||||||
|
|
||||||
配置完成后:
|
### 6. 开始使用
|
||||||
- 新评论会自动触发AI回复(如果文章/页面启用了AI回评)
|
|
||||||
- 在 **评论 → AI回评 → 回复日志** 中查看所有AI回复记录和状态
|
- 新评论会自动触发 AI 回复(如果文章/页面启用了 AI 回评)
|
||||||
- 可在日志页面手动触发历史评论的AI回复
|
- 在 **评论 → AI回评 → 回复日志** 中查看所有记录
|
||||||
- 如需临时关闭,在"基本设置"中关闭"自动回复"开关即可
|
- 如需临时关闭,在"基本设置"中关闭"自动回复"开关即可
|
||||||
|
|
||||||
> 💡 文章级开关:编辑文章时,右侧设置面板中有"AI回评"开关,可单独控制每篇文章是否启用AI回复。
|
> 编辑文章时,右侧设置面板中有"AI回评"开关,可单独控制每篇文章。
|
||||||
|
|
||||||
## 从源码构建
|
## 从源码构建
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 克隆仓库
|
|
||||||
git clone https://github.com/sunny-335/plugin-comment-ai-autopilot.git
|
git clone https://github.com/sunny-335/plugin-comment-ai-autopilot.git
|
||||||
cd plugin-comment-ai-autopilot
|
cd plugin-comment-ai-autopilot
|
||||||
|
|
||||||
# 构建
|
|
||||||
./gradlew build -x test
|
./gradlew build -x test
|
||||||
|
|
||||||
# 构建产物位于 build/libs/ 目录
|
# 构建产物位于 build/libs/ 目录
|
||||||
```
|
```
|
||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 启用插件开发服务器
|
./gradlew haloServer # 启动开发服务器
|
||||||
./gradlew haloServer
|
cd ui && pnpm install && pnpm dev # 前端开发
|
||||||
|
|
||||||
# 开发前端
|
|
||||||
cd ui
|
|
||||||
pnpm install
|
|
||||||
pnpm dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
@@ -133,3 +124,19 @@ pnpm dev
|
|||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
[GPL-3.0](https://github.com/sunny-335/plugin-comment-ai-autopilot/blob/main/LICENSE) © 暖心向阳335
|
[GPL-3.0](https://github.com/sunny-335/plugin-comment-ai-autopilot/blob/main/LICENSE) © 暖心向阳335
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>AI 辅助开发声明</summary>
|
||||||
|
|
||||||
|
本项目在开发过程中使用了以下 AI 模型辅助编码:
|
||||||
|
|
||||||
|
- **GLM-5.1 / GLM-5.2**(智谱AI)
|
||||||
|
- **Qwen3.6 / Qwen3.7 Plus**(阿里通义千问)
|
||||||
|
- **Doubao Seed 2.1 Pro**(字节豆包)
|
||||||
|
- **MIMO V2.5**(小米)
|
||||||
|
|
||||||
|
AI 辅助范围包括:代码生成、Bug 修复、代码审查、文档撰写。所有代码均经人工审查和测试验证后合并。
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
|
## v1.4.1
|
||||||
|
|
||||||
|
> 2026-07-06
|
||||||
|
|
||||||
|
### 改进
|
||||||
|
|
||||||
|
- **重写 README.md** — 精简结构,添加 AI 辅助开发声明(GLM-5.1/5.2、Qwen3.6/3.7 Plus、Doubao Seed 2.1 Pro、MIMO V2.5)
|
||||||
|
- **死代码清理** — 移除 6 项未使用的代码和依赖
|
||||||
|
|
||||||
|
### 移除
|
||||||
|
|
||||||
|
- **WhitelistService.isSuperAdmin** — 未使用的 private 方法,功能已被 isAdminUser 覆盖
|
||||||
|
- **CommentAiAutopilotEndpoint.PersonaResponse** — 未使用的 record 定义
|
||||||
|
- **AiBadgeHeadProcessor** — 空操作的 TemplateHeadProcessor 实现类(整个文件删除)
|
||||||
|
- **ui/src/utils/api.ts** — 未使用的 API 路径常量文件(整个文件删除)
|
||||||
|
- **ui/src/utils/gravatar.ts** — 未使用的 Gravatar 工具文件(整个文件删除)
|
||||||
|
- **canvas-confetti 依赖** — package.json 中声明但从未 import 的前端依赖
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v1.4.0
|
## v1.4.0
|
||||||
|
|
||||||
> 2026-07-06
|
> 2026-07-06
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
version=1.4.0
|
version=1.4.1
|
||||||
|
|
||||||
# Fix Windows Gradle Worker Daemon exit code 268435659 when running pnpm via Exec tasks
|
# Fix Windows Gradle Worker Daemon exit code 268435659 when running pnpm via Exec tasks
|
||||||
org.gradle.daemon=false
|
org.gradle.daemon=false
|
||||||
|
|||||||
@@ -524,12 +524,6 @@ public class CommentAiAutopilotEndpoint implements CustomEndpoint {
|
|||||||
long filteredCount
|
long filteredCount
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record PersonaResponse(
|
|
||||||
String name,
|
|
||||||
String prompt,
|
|
||||||
String avatar
|
|
||||||
) {}
|
|
||||||
|
|
||||||
private Mono<ServerResponse> getConversation(ServerRequest request) {
|
private Mono<ServerResponse> getConversation(ServerRequest request) {
|
||||||
var commentName = request.pathVariable("commentName");
|
var commentName = request.pathVariable("commentName");
|
||||||
return client.fetch(Comment.class, commentName)
|
return client.fetch(Comment.class, commentName)
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package top.nxxy335.commentaiautopilot.processor;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.thymeleaf.context.ITemplateContext;
|
|
||||||
import org.thymeleaf.model.IModel;
|
|
||||||
import org.thymeleaf.processor.element.IElementModelStructureHandler;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import run.halo.app.theme.dialect.TemplateHeadProcessor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Template head processor for comment-ai-autopilot.
|
|
||||||
* Previously used to inject avatar replacement scripts, but now
|
|
||||||
* avatar is handled natively via Gravatar (email-based).
|
|
||||||
* Kept as a no-op to avoid breaking the Spring component scan.
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class AiBadgeHeadProcessor implements TemplateHeadProcessor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Void> process(ITemplateContext context, IModel model,
|
|
||||||
IElementModelStructureHandler structureHandler) {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -137,27 +137,6 @@ public class WhitelistService {
|
|||||||
return Mono.just(matchByDisplayName(owner, config.list()));
|
return Mono.just(matchByDisplayName(owner, config.list()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过 User 资源的角色注解判断是否为超级管理员(super-role)。
|
|
||||||
*/
|
|
||||||
private Mono<Boolean> isSuperAdmin(String username) {
|
|
||||||
return client.fetch(User.class, username)
|
|
||||||
.map(user -> {
|
|
||||||
var annotations = user.getMetadata().getAnnotations();
|
|
||||||
if (annotations == null) return false;
|
|
||||||
String roleNames = annotations.get(User.ROLE_NAMES_ANNO);
|
|
||||||
if (roleNames == null || roleNames.isBlank()) return false;
|
|
||||||
return Arrays.stream(roleNames.split(","))
|
|
||||||
.map(String::trim)
|
|
||||||
.anyMatch(SUPER_ROLE::equals);
|
|
||||||
})
|
|
||||||
.defaultIfEmpty(false)
|
|
||||||
.onErrorResume(e -> {
|
|
||||||
log.debug("[Whitelist] 获取 User {} 失败: {}", username, e.getMessage());
|
|
||||||
return Mono.just(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mono<Boolean> isAdminUser(String username) {
|
private Mono<Boolean> isAdminUser(String username) {
|
||||||
return client.fetch(User.class, username)
|
return client.fetch(User.class, username)
|
||||||
.map(user -> {
|
.map(user -> {
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ spec:
|
|||||||
url: "https://github.com/sunny-335/plugin-comment-ai-autopilot/blob/main/LICENSE"
|
url: "https://github.com/sunny-335/plugin-comment-ai-autopilot/blob/main/LICENSE"
|
||||||
settingName: "comment-ai-autopilot-settings"
|
settingName: "comment-ai-autopilot-settings"
|
||||||
configMapName: "comment-ai-autopilot-configmap"
|
configMapName: "comment-ai-autopilot-configmap"
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
"@halo-dev/ui-plugin-bundler-kit": "^2.23.0",
|
"@halo-dev/ui-plugin-bundler-kit": "^2.23.0",
|
||||||
"@iconify-json/ri": "^1.2.6",
|
"@iconify-json/ri": "^1.2.6",
|
||||||
"@tsconfig/node20": "^20.1.6",
|
"@tsconfig/node20": "^20.1.6",
|
||||||
"@types/canvas-confetti": "^1.9.0",
|
|
||||||
"@types/jsdom": "^21.1.7",
|
"@types/jsdom": "^21.1.7",
|
||||||
"@types/node": "^20.19.1",
|
"@types/node": "^20.19.1",
|
||||||
"@vitest/eslint-plugin": "^1.2.7",
|
"@vitest/eslint-plugin": "^1.2.7",
|
||||||
|
|||||||
Generated
-16
@@ -20,9 +20,6 @@ importers:
|
|||||||
axios:
|
axios:
|
||||||
specifier: ^1.13.5
|
specifier: ^1.13.5
|
||||||
version: 1.17.0
|
version: 1.17.0
|
||||||
canvas-confetti:
|
|
||||||
specifier: ^1.9.3
|
|
||||||
version: 1.9.4
|
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.28
|
specifier: ^3.5.28
|
||||||
version: 3.5.38(typescript@5.8.3)
|
version: 3.5.38(typescript@5.8.3)
|
||||||
@@ -42,9 +39,6 @@ importers:
|
|||||||
'@tsconfig/node20':
|
'@tsconfig/node20':
|
||||||
specifier: ^20.1.6
|
specifier: ^20.1.6
|
||||||
version: 20.1.9
|
version: 20.1.9
|
||||||
'@types/canvas-confetti':
|
|
||||||
specifier: ^1.9.0
|
|
||||||
version: 1.9.0
|
|
||||||
'@types/jsdom':
|
'@types/jsdom':
|
||||||
specifier: ^21.1.7
|
specifier: ^21.1.7
|
||||||
version: 21.1.7
|
version: 21.1.7
|
||||||
@@ -695,9 +689,6 @@ packages:
|
|||||||
'@tybys/wasm-util@0.10.2':
|
'@tybys/wasm-util@0.10.2':
|
||||||
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
|
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
|
||||||
|
|
||||||
'@types/canvas-confetti@1.9.0':
|
|
||||||
resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==}
|
|
||||||
|
|
||||||
'@types/chai@5.2.3':
|
'@types/chai@5.2.3':
|
||||||
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||||
|
|
||||||
@@ -1040,9 +1031,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
canvas-confetti@1.9.4:
|
|
||||||
resolution: {integrity: sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==}
|
|
||||||
|
|
||||||
chai@6.2.2:
|
chai@6.2.2:
|
||||||
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -3051,8 +3039,6 @@ snapshots:
|
|||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@types/canvas-confetti@1.9.0': {}
|
|
||||||
|
|
||||||
'@types/chai@5.2.3':
|
'@types/chai@5.2.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/deep-eql': 4.0.2
|
'@types/deep-eql': 4.0.2
|
||||||
@@ -3466,8 +3452,6 @@ snapshots:
|
|||||||
|
|
||||||
callsites@3.1.0: {}
|
callsites@3.1.0: {}
|
||||||
|
|
||||||
canvas-confetti@1.9.4: {}
|
|
||||||
|
|
||||||
chai@6.2.2: {}
|
chai@6.2.2: {}
|
||||||
|
|
||||||
chalk@4.1.2:
|
chalk@4.1.2:
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
/**
|
|
||||||
* 统一 API 路径常量,避免硬编码散布在多个 Vue 文件中。
|
|
||||||
* 修改 API 路径只需在此处更新。
|
|
||||||
*/
|
|
||||||
export const API_BASE = '/apis/console.api.comment-ai-autopilot.nxxy335.top/v1alpha1'
|
|
||||||
|
|
||||||
// ===== 日志相关 =====
|
|
||||||
export const API_REPLIES = `${API_BASE}/replies`
|
|
||||||
export const apiReply = (name: string) => `${API_REPLIES}/${name}`
|
|
||||||
export const apiReplyAction = (name: string, action: string) => `${API_REPLIES}/${name}/${action}`
|
|
||||||
export const API_BATCH_APPROVE = `${API_REPLIES}/batch-approve`
|
|
||||||
export const API_BATCH_REJECT = `${API_REPLIES}/batch-reject`
|
|
||||||
export const API_BATCH_DELETE = `${API_REPLIES}/batch-delete`
|
|
||||||
export const apiConversation = (commentId: string) => `${API_BASE}/conversation/${commentId}`
|
|
||||||
|
|
||||||
// ===== 概览相关 =====
|
|
||||||
export const API_STATS = `${API_BASE}/stats`
|
|
||||||
export const API_PERSONAS = `${API_BASE}/personas`
|
|
||||||
export const apiPersona = (name: string) => `${API_PERSONAS}/${name}`
|
|
||||||
export const API_HEALTH = `${API_BASE}/health`
|
|
||||||
|
|
||||||
// ===== 设置相关 =====
|
|
||||||
export const API_EXPORT = `${API_BASE}/export`
|
|
||||||
export const API_IMPORT = `${API_BASE}/import`
|
|
||||||
export const API_COMMENTERS = `${API_BASE}/commenters`
|
|
||||||
export const API_CLEANUP = `${API_BASE}/cleanup`
|
|
||||||
|
|
||||||
// ===== 评论触发 =====
|
|
||||||
export const apiCommentTrigger = (commentName: string) => `${API_BASE}/comments/${commentName}/trigger`
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
export async function computeGravatarHash(email: string): Promise<string> {
|
|
||||||
if (!email || email.trim() === '') return ''
|
|
||||||
const normalizedEmail = email.trim().toLowerCase()
|
|
||||||
const encoder = new TextEncoder()
|
|
||||||
const data = encoder.encode(normalizedEmail)
|
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
|
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
|
||||||
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGravatarUrl(hash: string): string {
|
|
||||||
if (!hash) return ''
|
|
||||||
return `https://cn.cravatar.com/avatar/${hash}`
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user