# EpointMsgPlugins 插件项目介绍

> 本文档供后续 Agent 快速了解项目全貌、插件分类、关键技术路径及维护要点。

---

## 一、项目概述

### 1.1 是什么

EpointMsgPlugins 是**国泰新点即时通讯（EpointMsg）** 的插件系统。插件以 JavaScript 脚本形式注入到 Electron 渲染进程中，通过操纵 `window.RongIM` / `window.RongIMLib` 融云 SDK 来扩展聊天功能。

| 属性 | 值 |
|------|-----|
| 宿主程序 | `E:\EpointMsg2025\新点即时通讯.exe` |
| 环境 | Electron 22.3.26 渲染进程（Chromium） |
| 支持 API | `require('fs')`、`require('path')`、`require('electron')`、`require('@electron/remote')` |
| 融云 SDK | `window.RongIMLib.RongIMClient`、`window.RongIM.dataModel` |
| 存储目录 | `%APPDATA%\EpointMsgPlugins\` |
| 安装方式 | `irm 'https://rongassist.xiaofengzi.cc:8443/scripts/install.ps1' | iex` |

### 1.2 目录结构

```
%APPDATA%\EpointMsgPlugins\
├── core/                       # 插件管理器核心（由远程维护）
│   ├── plugin-manager.js      # 插件加载、初始化、生命周期控制
│   ├── updater.js             # 核心组件/插件同步（远程→本地）
│   ├── heartbeat.js           # 心跳上报
│   └── config-panel.js        # 配置面板
├── plugins/                   # 所有插件（每个子目录一个）
│   ├── <plugin-id>/
│   │   ├── manifest.json      # 元信息：id, name, version, entry
│   │   ├── main.js            # 插件入口（module.exports）
│   │   ├── styles.css         # 样式（可选）
│   │   └── cache/             # 本地数据缓存（可选，不上传）
├── docs/
│   ├── local-modifications.md # 离线化/隐私化定制改动
│   └── PROJECT.md             # 本文档
├── plugin-config.json         # 运行时配置（启停、服务器地址）
└── .version.json              # 核心组件版本追踪
```

### 1.3 插件生命周期

```
init(config, pluginManager)   → 插件加载、DOM 注入、事件绑定
destroy(instance)             → 清理 DOM、解绑事件、断开观察器
```

所有插件在 `plugin-manager.js` 的 `init()` 中注册，DOM 就绪后逐个 `init()`。

---

## 二、插件分类与功能矩阵

### 🔵 A 类：官方插件（远程同步）

| 插件 ID | 功能 | 状态 | 说明 |
|---------|------|------|------|
| `ai-memo` | 消息标记 | 禁用 | 标记待办、截止时间，依赖远程 AI |
| `auto-lock-screen` | 自动锁屏 | 禁用 | 无操作自动锁屏 |
| `dark-mode-toggle` | 日夜间模式 | 禁用 | 白天/夜间/跟随系统 |
| `group-ai-summary` | 聊天记录总结（AI版） | 禁用 | 调用远程 AI 接口，读取 OA Token |
| `now-playing` | 正在听歌 | 禁用 | 读取系统播放器状态 |
| `recent-contacts` | 最近联系人 | **启用** | 搜索框聚焦时展示最近联系人 |
| `shortcut-entry` | 快捷入口 | 禁用 | 腾讯文档/会议等快捷键 |
| `sticker-import` | 表情包导入 | 禁用 | 导入 QQ 表情包 |
| `water-reminder` | 小憩提醒 | 禁用 | 整点喝水/走动提醒 |
| `work-log-summary` | 工作日志总结（AI版） | 禁用 | 调用远程 AI 生成日志 |

### 🟢 B 类：本地隐私插件（定制开发，零上传）

| 插件 ID | 功能 | 状态 | 关键差异 |
|---------|------|------|---------|
| `group-summary-local` | 群聊本地总结 | **启用** | 不调远程 AI、不读 OA Token、本地固定 prompt→HTML |
| `work-log-local` | 工作日志本地总结 | **启用** | 消息收集逻辑与原版一致，但不上传、本地生成 HTML |
| `chat-data-exporter` | 聊天数据全量导出器 | **启用** | 分页全量拉取，支持日期筛选，导出 JSON |

---

## 三、关键技术路径

### 3.1 融云 SDK 核心方法

```javascript
const RongIMClient = window.RongIMLib.RongIMClient;

// 获取会话列表
RongIMClient.getInstance().getConversationList(
  { onSuccess, onError }, 
  types,    // [1]=私聊, [3]=群聊, [1,3]=全部
  count     // 最多 100 条
);

// 获取历史消息（先查本地缓存）
RongIMClient.getInstance().getHistoryMessages(
  conversationType,  // 1=私聊, 3=群聊
  targetId,          // 会话目标 ID
  oldestMessageId,   // 分页起点（0=最新）
  count,             // 每页条数（上限 200）
  { onSuccess, onError }
);

// 获取用户信息（用于解析 userId 为姓名）
window.RongIM.dataModel.User.get(userId, (err, user) => {
  // user.name, user.alias
});
```

### 3.2 消息类型识别

消息的 `objectName` 字段标识类型：

| objectName | 类型 | 内容字段 |
|------------|------|---------|
| `RC:TxtMsg` | 文本消息 | `content.content` |
| `RC:ImgMsg` | 图片 | `[图片]` 标记 |
| `RC:FileMsg` | 文件 | `content.name` |
| `RC:VcMsg` | 语音 | `[语音消息]` |
| `RC:SightMsg` | 短视频 | `[短视频]` |
| `RC:ReferenceMessage` | 引用消息 | `content.content` |
| `RC:CombineMsg` | 合并转发 | `[合并转发]` |

### 3.3 数据存储路径

| 用途 | 路径 | 格式 |
|------|------|------|
| 融云消息数据库（加密） | `E:\EpointMsg2025\RongCloud\{appKey}\{userId}\storage.db` | SQLCipher |
| 工作日志缓存 | `plugins/work-log-local/cache/work-log-YYYY-MM-DD.json` | JSON |
| 群聊总结报告 | `%APPDATA%\EpointMsgPluginsLocal\reports\群聊总结_*.html` | HTML |
| 工作日志报告 | `%APPDATA%\EpointMsgPluginsLocal\reports\工作日志_*.html` | HTML |
| 聊天全量导出 | `plugins/chat-data-exporter/cache/export_*.json` | JSON |
| 最近联系人 | `plugins/recent-contacts/cache/recent-contacts.json` | JSON |

---

## 四、插件详细说明（B 类）

### 4.1 `group-summary-local` — 群聊本地总结

| 属性 | 值 |
|------|-----|
| 入口 | `plugins/group-summary-local/main.js` |
| 触发 | 工具栏按钮（打开群聊后出现） |
| 数据源 | `RongIMLib.getHistoryMessages(3, targetId, 0, 50)` → 最近 50 条 |
| 输出 | HTML 报告 → `%APPDATA%\EpointMsgPluginsLocal\reports\` |
| 用户解析 | `RongIM.dataModel.User.get()` 批量解析 userId 为姓名 |
| 隐私 | 不调远程 AI、不读 OA Token、不上传任何数据 |

### 4.2 `work-log-local` — 工作日志本地总结

| 属性 | 值 |
|------|-----|
| 入口 | `plugins/work-log-local/main.js` |
| 触发 | 导航栏图标 → 面板 → 选择日期 → 收集 → 生成 |
| 数据源 | 遍历所有会话 → `getHistoryMessages()` 各 200 条 |
| 收集规则 | 私聊：所有文本消息；群聊：仅自己发送 + @我的 |
| 输出 | HTML 报告 → `%APPDATA%\EpointMsgPluginsLocal\reports\` |
| 缓存 | `cache/work-log-YYYY-MM-DD.json`（保留 30 天，自动清理） |
| 交互流程 | 1.选择日期 → 2.收集消息 → 3.查看概览 → 4.生成报告 |

### 4.3 `chat-data-exporter` — 聊天数据全量导出器

| 属性 | 值 |
|------|-----|
| 入口 | 工具栏按钮（打开聊天后出现） |
| 选择方式 | 两种：①跟随当前会话 ②从列表选择 |
| 数据源 | 分页 `getHistoryMessages()` |
| 拉取上限 | 500 页 × 每页 100~200 条 ≈ 5~10 万条 |
| 日期筛选 | 支持开始/结束日期过滤 |
| 用户解析 | `RongIM.dataModel.User.get()` 批量解析 userId |
| 输出 | JSON → `cache/export_<会话名>_<时间戳>.json` |
| 统计信息 | totalMessages, myMessages, participants, timeRange, totalFetched, totalKept |

#### JSON 导出结构

```json
{
  "exportTime": "ISO 时间",
  "conversation": { "type": "群聊|私聊", "targetId": "...", "targetName": "..." },
  "stats": {
    "totalMessages": 1234,
    "myMessages": 567,
    "participants": 23,
    "totalFetched": 1500,
    "totalKept": 1234,
    "timeRange": { "start": "...", "end": "..." },
    "dateFilter": { "startTime": 1717000000000, "endTime": 1717086400000 }
  },
  "messages": [
    {
      "id": "消息唯一ID",
      "time": 1779409943176,
      "timeStr": "2026-05-22 08:32:23",
      "sender": "发送者姓名",
      "senderId": "发送者ID",
      "content": "消息文本内容",
      "type": "RC:TxtMsg",
      "isMine": false,
      "isAtMe": true
    }
  ]
}
```

---

## 五、维护指南

### 5.1 插件启停

编辑 `plugin-config.json`，将目标插件的 `enabled` 改为 `true`/`false`，然后重启新点即时通讯。

### 5.2 添加新插件

1. 在 `plugins/` 下创建 `<new-plugin-id>/` 目录
2. 创建 `manifest.json`（id、name、version、entry）
3. 创建 `main.js`（`module.exports = { init, destroy }`）
4. 在 `plugin-config.json` 末尾追加注册条目
5. 在 `plugin-manager.js` 的 `[LOCAL]` 块中注册（如需本地保留）
6. 重启 EpointMsg

### 5.3 更新核心组件后重新应用补丁

当 `plugin-manager.js` / `updater.js` 被远程更新覆盖后，需要重新执行 `docs/local-modifications.md` 中记录的补丁步骤。

### 5.4 常见问题

| 问题 | 原因 | 解决 |
|------|------|------|
| 插件不显示 | 未注册或 `enabled: false` | 检查 `plugin-config.json` |
| 新插件不生效 | 未重启 | 重启新点即时通讯 |
| 本地插件被删 | 远程同步覆盖 | 确认 `updater.js` 的 `-local` 豁免补丁已应用 |
| 消息不全 | work-log-local 200 条限制/群聊过滤 | 改用 chat-data-exporter 全量导出 |
| 导出无数据 | 融云客户端未就绪/未登录 | 确认已在聊天软件中登录 |
| 工具栏按钮换行 | `display:flex` 导致块级元素 | 使用 `background-image` + `inline-block` |
| 图标白色看不清 | `PluginTheme` 返回白色 | 硬编码 `#808CA4` 灰色 |

---

## 六、安装与注入机制

### 6.1 安装脚本

```powershell
irm 'https://rongassist.xiaofengzi.cc:8443/scripts/install.ps1' | iex
```

安装流程：
1. 查找 `app.asar` 路径（进程/注册表/常见目录）
2. 终止应用进程
3. 解包 ASAR → 注入引导代码到 `preload.js` 末尾
4. 重新打包 ASAR（使用自定义 Pickle 序列化器）
5. 拉取组件清单 manifest.json
6. 下载核心组件 + 插件
7. 写入 plugin-config.json
8. 启动应用

### 6.2 注入代码

注入到 `preload.js` 末尾的引导代码：
```javascript
// >>> EPOINT_PLUGIN_INJECT_START <<<
(function() {
    const { app } = require("@electron/remote");
    const pluginDir = require("path").join(app.getPath("appData"), "EpointMsgPlugins");
    const entryFile = require("path").join(pluginDir, "core", "plugin-manager.js");
    if (require("fs").existsSync(entryFile)) {
        try { require(entryFile); } catch(e) { console.error("[Bootstrap] Failed:", e); }
    }
})();
// >>> EPOINT_PLUGIN_INJECT_END <<<
```

### 6.3 本地补丁

- `plugin-manager.js`：[LOCAL] 本地插件注册 + 非阻塞同步
- `updater.js`：`-local` 版本免移除保护
- `plugin-config.json`：`serverUrl` 可控开关

### 6.4 ⚠️ 自动更新风险

新点即时通讯自动更新会覆盖 `app.asar`，导致注入丢失。需重新运行安装脚本。本地插件（`-local` 后缀）由 `updater.js` 的豁免机制保护不被远程同步删除。
