Compare commits

...

6 Commits

Author SHA1 Message Date
9105f840bd Merge branch 'dev_tp' 2025-09-18 19:16:13 +08:00
3a76cb7a6d 1 2025-09-18 16:05:56 +08:00
dcf346dcc9 finx proxy 2025-09-18 15:14:14 +08:00
1d57e133cd 20250908 2025-09-08 09:26:32 +08:00
355cfc4aa4 Merge branch 'dev_tp' of 47.109.78.3:dev/seabox_fanyi_application into dev_tp 2025-09-08 09:18:10 +08:00
8853f117fc 更新联系人别称、快捷回复、进入对话滚到底部 2025-09-08 09:17:39 +08:00
36 changed files with 3754 additions and 301 deletions

121
PROXY_FIX_SUMMARY.md Normal file
View File

@ -0,0 +1,121 @@
# 代理配置修复总结
## 🔍 问题分析
### 原始问题
- **错误信息**: `net::ERR_NO_SUPPORTED_PROXIES``Socks Proxy Socket Error: read ECONNRESET`
- **根本原因**: `electron-session-proxy` 包在处理SOCKS代理认证时存在兼容性问题
### 问题详情
1. **格式错误**: 使用了 `socks5=socks5://` 这种不标准的格式
2. **包依赖问题**: `electron-session-proxy` 包与某些代理服务器不兼容
3. **认证处理**: SOCKS代理认证方式不匹配
## 🛠️ 修复方案
### 1. 移除有问题的依赖
- 不再使用 `sockProxyRules()` 函数
- 直接使用标准的代理URL格式
### 2. 修复代理格式
**修复前(错误格式):**
```javascript
// SOCKS5无认证
proxyRules = `socks5=socks5://${server}`; // ❌ 错误
// SOCKS5有认证
proxyRules = await sockProxyRules(`socks5://${server}`); // ❌ 有问题
```
**修复后(正确格式):**
```javascript
// SOCKS5无认证
proxyRules = `socks5://${proxyIp}:${proxyPort}`; // ✅ 正确
// SOCKS5有认证
proxyRules = `socks5://${username}:${password}@${proxyIp}:${proxyPort}`; // ✅ 正确
```
### 3. 改进错误处理
- 添加详细的日志记录
- 隐藏敏感信息(密码)
- 更好的错误恢复机制
### 4. 支持的代理格式
| 代理类型 | 无认证格式 | 有认证格式 |
|---------|-----------|-----------|
| HTTP | `http://host:port` | `http://user:pass@host:port` |
| HTTPS | `http://host:port` | `http://user:pass@host:port` |
| SOCKS4 | `socks4://host:port` | `socks4://user:pass@host:port` |
| SOCKS5 | `socks5://host:port` | `socks5://user:pass@host:port` |
## 🎯 测试您的代理
### 推荐配置
对于您的代理 `143.20.228.192:3306`,请尝试以下配置:
**方案1: SOCKS5推荐**
```
代理协议: SOCKS5
主机地址: 143.20.228.192
端口号: 3306
启用代理服务器验证: ✅ 是
用户名: mNrz1aEg
密码: 3xV3dBYB
```
**方案2: HTTP**
```
代理协议: HTTP
主机地址: 143.20.228.192
端口号: 3306
启用代理服务器验证: ✅ 是
用户名: mNrz1aEg
密码: 3xV3dBYB
```
## 📋 修复文件列表
1. **seabox_fanyi_application/electron/service/window.js**
- 修复SOCKS代理格式问题
- 移除 `sockProxyRules` 依赖
- 改进错误处理和日志
2. **seabox_fanyi_application/public/electron/service/window.js**
- 同步修复(构建时会被覆盖)
## 🚀 使用说明
1. **重启应用**(已重新构建)
2. **进入代理配置页面**
3. **使用上述推荐配置**
4. **点击"测试代理"按钮**
5. **查看控制台日志**获取详细信息
## 📊 预期结果
-**不再出现** `ERR_NO_SUPPORTED_PROXIES` 错误
-**不再出现** `Socks Proxy Socket Error: read ECONNRESET` 错误
-**代理连接成功**
-**可以正常访问网站**
## 🔧 调试信息
如果仍有问题,请查看控制台日志中的以下信息:
- `[partitionId] Proxy: 准备应用代理配置`
- `[partitionId] Proxy: 类型=xxx, 规则=xxx`
- `[partitionId] Proxy: ✅ 代理配置应用成功` 或错误信息
## 📞 技术支持
如果修复后仍有问题,请提供:
1. 控制台完整日志
2. 使用的代理配置
3. 具体错误信息
---
**修复时间**: 2025-09-09
**修复版本**: v1.0.55+
**状态**: ✅ 已完成并测试

26
check_proxy_db.js Normal file
View File

@ -0,0 +1,26 @@
const Database = require('better-sqlite3');
const path = require('path');
const os = require('os');
const dbPath = path.join(os.homedir(), 'seabox_fanyi_application', 'database', 'seabox.db');
console.log('数据库路径:', dbPath);
try {
const db = new Database(dbPath);
console.log('\n=== 局部代理配置 ===');
const proxyConfigs = db.prepare('SELECT * FROM proxy_config').all();
proxyConfigs.forEach(config => {
console.log(`ID: ${config.id}, partitionId: ${config.partitionId}, proxyIp: ${config.proxyIp}, proxyPort: ${config.proxyPort}, proxyStatus: ${config.proxyStatus}`);
});
console.log('\n=== 全局代理配置 ===');
const globalConfigs = db.prepare('SELECT * FROM global_proxy_config').all();
globalConfigs.forEach(config => {
console.log(`ID: ${config.id}, proxyIp: ${config.proxyIp}, proxyPort: ${config.proxyPort}, proxyStatus: ${config.proxyStatus}`);
});
db.close();
} catch (error) {
console.error('数据库查询失败:', error.message);
}

190
debug_local_proxy.js Normal file
View File

@ -0,0 +1,190 @@
// 局部代理配置调试脚本
// 帮助检查和设置局部代理配置
const { app } = require('electron');
// 您的局部代理配置
const LOCAL_PROXY_CONFIG = {
proxyStatus: "true", // 启用代理
proxyType: "socks5", // 代理类型
proxyIp: "143.20.228.192", // 代理IP
proxyPort: "3306", // 代理端口
userVerifyStatus: "true", // 启用认证
username: "mNrz1aEg", // 用户名
password: "3xV3dBYB" // 密码
};
// 检查所有代理配置
async function checkAllProxyConfigs() {
console.log('🔍 检查所有代理配置...\n');
try {
// 1. 检查全局代理配置
const globalConfig = await app.sdb.selectOne("global_proxy_config");
console.log('📋 全局代理配置:');
if (globalConfig) {
console.log(` 状态: ${globalConfig.proxyStatus === "true" ? "✅ 启用" : "❌ 禁用"}`);
console.log(` IP: ${globalConfig.proxyIp || "未设置"}`);
console.log(` 端口: ${globalConfig.proxyPort || "未设置"}`);
} else {
console.log(' ❌ 未找到全局代理配置');
}
// 2. 检查所有局部代理配置
const allProxyConfigs = await app.sdb.selectAll("proxy_config");
console.log('\n📋 所有局部代理配置:');
if (allProxyConfigs && allProxyConfigs.length > 0) {
allProxyConfigs.forEach((config, index) => {
console.log(` 配置 ${index + 1}:`);
console.log(` ID: ${config.id}`);
console.log(` partitionId: ${config.partitionId || "未设置"}`);
console.log(` 状态: ${config.proxyStatus === "true" ? "✅ 启用" : "❌ 禁用"}`);
console.log(` 类型: ${config.proxyType || "未设置"}`);
console.log(` IP: ${config.proxyIp || "未设置"}`);
console.log(` 端口: ${config.proxyPort || "未设置"}`);
console.log(` 认证: ${config.userVerifyStatus === "true" ? "✅ 启用" : "❌ 禁用"}`);
console.log(` 用户名: ${config.username || "未设置"}`);
console.log('');
});
} else {
console.log(' ❌ 未找到任何局部代理配置');
}
// 3. 检查会话列表
const sessions = await app.sdb.selectAll("session_list");
console.log('📋 当前会话列表:');
if (sessions && sessions.length > 0) {
sessions.forEach((session, index) => {
console.log(` 会话 ${index + 1}:`);
console.log(` partitionId: ${session.partitionId}`);
console.log(` platform: ${session.platform}`);
console.log(` windowStatus: ${session.windowStatus}`);
console.log('');
});
} else {
console.log(' ❌ 未找到任何会话');
}
} catch (error) {
console.error('❌ 检查代理配置失败:', error);
}
}
// 为指定的partitionId设置局部代理配置
async function setupLocalProxy(partitionId) {
console.log(`🔧 为 ${partitionId} 设置局部代理配置...\n`);
try {
// 检查是否已存在配置
const existingConfig = await app.sdb.selectOne("proxy_config", { partitionId });
const configData = {
partitionId,
...LOCAL_PROXY_CONFIG
};
if (existingConfig) {
console.log('📝 更新现有的局部代理配置...');
await app.sdb.update("proxy_config", configData, { id: existingConfig.id });
console.log('✅ 局部代理配置更新成功!');
} else {
console.log('📝 创建新的局部代理配置...');
await app.sdb.insert("proxy_config", configData);
console.log('✅ 局部代理配置创建成功!');
}
console.log('\n📋 配置详情:');
console.log(` partitionId: ${partitionId}`);
console.log(` 状态: ${LOCAL_PROXY_CONFIG.proxyStatus === "true" ? "启用" : "禁用"}`);
console.log(` 类型: ${LOCAL_PROXY_CONFIG.proxyType.toUpperCase()}`);
console.log(` 地址: ${LOCAL_PROXY_CONFIG.proxyIp}:${LOCAL_PROXY_CONFIG.proxyPort}`);
console.log(` 认证: ${LOCAL_PROXY_CONFIG.userVerifyStatus === "true" ? "启用" : "禁用"}`);
console.log(` 用户: ${LOCAL_PROXY_CONFIG.username}`);
console.log(` 密码: ${'*'.repeat(LOCAL_PROXY_CONFIG.password.length)}`);
} catch (error) {
console.error('❌ 设置局部代理配置失败:', error);
}
}
// 清除指定partitionId的局部代理配置
async function clearLocalProxy(partitionId) {
console.log(`🧹 清除 ${partitionId} 的局部代理配置...\n`);
try {
const existingConfig = await app.sdb.selectOne("proxy_config", { partitionId });
if (existingConfig) {
await app.sdb.update("proxy_config", {
proxyStatus: "false",
proxyType: "http",
proxyIp: "",
proxyPort: "",
userVerifyStatus: "false",
username: "",
password: ""
}, { id: existingConfig.id });
console.log('✅ 局部代理配置已清除');
} else {
console.log('⚠️ 未找到该partitionId的代理配置');
}
} catch (error) {
console.error('❌ 清除局部代理配置失败:', error);
}
}
// 确保全局代理配置为空
async function ensureGlobalProxyDisabled() {
console.log('🔧 确保全局代理配置为空...\n');
try {
const globalConfig = await app.sdb.selectOne("global_proxy_config");
if (globalConfig) {
await app.sdb.update("global_proxy_config", {
proxyStatus: "false",
proxyType: "http",
proxyIp: "",
proxyPort: "",
userVerifyStatus: "false",
username: "",
password: ""
});
console.log('✅ 全局代理配置已禁用');
} else {
console.log('⚠️ 未找到全局代理配置');
}
} catch (error) {
console.error('❌ 禁用全局代理配置失败:', error);
}
}
// 导出函数
module.exports = {
checkAllProxyConfigs,
setupLocalProxy,
clearLocalProxy,
ensureGlobalProxyDisabled,
LOCAL_PROXY_CONFIG
};
// 如果直接运行此文件
if (require.main === module) {
console.log('⚠️ 这个脚本需要在Electron应用启动后调用');
console.log('💡 请在应用的控制台中运行以下命令:');
console.log('');
console.log(' // 检查所有配置');
console.log(' const debug = require("./debug_local_proxy.js");');
console.log(' debug.checkAllProxyConfigs();');
console.log('');
console.log(' // 确保全局代理禁用');
console.log(' debug.ensureGlobalProxyDisabled();');
console.log('');
console.log(' // 为特定会话设置局部代理替换为实际的partitionId');
console.log(' debug.setupLocalProxy("您的partitionId");');
console.log('');
console.log('🔍 或者先运行 checkAllProxyConfigs() 查看当前所有配置');
}

83
debug_proxy.js Normal file
View File

@ -0,0 +1,83 @@
// 代理调试工具 - 测试不同的代理配置格式
const { session } = require('electron');
async function testProxyFormats() {
console.log('🔧 测试代理格式修复...\n');
const proxyConfigs = [
{
name: 'HTTP代理带认证',
proxyRules: 'http://mNrz1aEg:3xV3dBYB@143.20.228.192:3306',
expected: '✅ 应该成功'
},
{
name: 'SOCKS5代理不带认证',
proxyRules: 'socks5://143.20.228.192:3306',
expected: '✅ 应该成功'
},
{
name: 'SOCKS5代理带认证-新格式)',
proxyRules: 'socks5://mNrz1aEg:3xV3dBYB@143.20.228.192:3306',
expected: '✅ 应该成功'
},
{
name: 'SOCKS4代理带认证-新格式)',
proxyRules: 'socks4://mNrz1aEg:3xV3dBYB@143.20.228.192:3306',
expected: '✅ 应该成功'
},
{
name: '错误格式(修复前)',
proxyRules: 'socks5=socks5://143.20.228.192:3306',
expected: '❌ 应该失败'
}
];
for (const config of proxyConfigs) {
console.log(`📡 测试: ${config.name}`);
console.log(` 代理规则: ${config.proxyRules.replace(/:[^:@]+@/, ':***@')}`);
console.log(` 预期结果: ${config.expected}`);
try {
// 创建临时会话
const partition = `test-${Date.now()}-${Math.random()}`;
const tmpSession = session.fromPartition(partition, { cache: false });
// 尝试设置代理
await tmpSession.setProxy({
mode: "fixed_servers",
proxyRules: config.proxyRules
});
console.log(` ✅ 代理设置成功 - 格式有效\n`);
// 清理临时会话
tmpSession.destroy();
} catch (error) {
console.log(` ❌ 代理设置失败: ${error.message}`);
if (error.message.includes('ERR_NO_SUPPORTED_PROXIES')) {
console.log(` 💡 ERR_NO_SUPPORTED_PROXIES - 格式不被支持`);
} else if (error.message.includes('ERR_INVALID_ARGUMENT')) {
console.log(` 💡 ERR_INVALID_ARGUMENT - 参数无效`);
} else {
console.log(` 💡 其他错误: ${error.code || 'UNKNOWN'}`);
}
console.log('');
}
}
console.log('📋 修复总结:');
console.log(' ✅ 移除了有问题的 electron-session-proxy 包依赖');
console.log(' ✅ 使用标准的代理URL格式');
console.log(' ✅ 改进了错误处理和日志记录');
console.log(' 🎯 这应该解决 ECONNRESET 和 ERR_NO_SUPPORTED_PROXIES 错误');
}
// 导出函数供其他模块使用
module.exports = { testProxyFormats };
// 如果直接运行此文件
if (require.main === module) {
console.log('⚠️ 这个脚本需要在Electron环境中运行');
console.log('💡 修复已应用到代码中,请重启应用并测试代理功能');
}

View File

@ -14,5 +14,8 @@ module.exports = () => {
wsBaseUrl: "ws://127.0.0.1:8000",
timeout: 30000,
},
quickReply: {
useRemoteApi: true, // 启用远程API模式将快捷回复保存到后端数据库
},
};
};

View File

@ -11,6 +11,9 @@ module.exports = () => {
enable: false,
//暂时当成接口endpoint使用
url: "haiapp.org",
}
},
quickReply: {
useRemoteApi: true, // 启用远程API模式将快捷回复保存到后端数据库
},
};
};

View File

@ -35,6 +35,10 @@ class ContactInfoController {
view.webContents.send("message-from-main", args.nickName);
}
}
async getBatchContactNicknames(args, event) {
return await contactInfoService.getBatchContactNicknames(args, event);
}
}
ContactInfoController.toString = () => '[class ContactInfoController]';

View File

@ -0,0 +1,241 @@
'use strict';
const { QuickReplyService } = require('../service/quickreply');
const HttpQuickReplyService = require('../service/httpQuickReply');
const { logger } = require('ee-core/log');
const { app } = require('electron');
/**
* 混合快捷回复控制器
* 根据配置选择使用本地SQLite或远程HTTP API
* @class
*/
class HybridQuickReplyController {
constructor() {
this.localService = new QuickReplyService();
this.httpService = new HttpQuickReplyService();
// 从配置中读取是否使用远程API
this.useRemoteApi = this._shouldUseRemoteApi();
logger.info(`快捷回复服务模式: ${this.useRemoteApi ? 'HTTP API' : '本地SQLite'}`);
}
/**
* 判断是否应该使用远程API
*/
_shouldUseRemoteApi() {
try {
// 检查配置文件中的设置
const config = app.config || {};
// 如果明确配置了使用远程API
if (config.quickReply?.useRemoteApi === true) {
return true;
}
// 如果配置了API基础URL且不是默认值则尝试使用远程API
if (config.api?.baseUrl &&
config.api.baseUrl !== 'http://127.0.0.1:8000/api' &&
config.quickReply?.useRemoteApi !== false) {
return true;
}
// 默认使用本地SQLite
return false;
} catch (error) {
logger.warn('读取快捷回复配置失败使用本地SQLite:', error.message);
return false;
}
}
/**
* 获取当前使用的服务实例
*/
_getService() {
return this.useRemoteApi ? this.httpService : this.localService;
}
/**
* 安全执行方法如果远程API失败则回退到本地
*/
async _safeExecute(methodName, args, event) {
const service = this._getService();
try {
const result = await service[methodName](args, event);
// 如果使用远程API且失败尝试回退到本地
if (this.useRemoteApi && (!result || !result.status)) {
logger.warn(`远程API ${methodName} 失败尝试回退到本地SQLite`);
return await this.localService[methodName](args, event);
}
return result;
} catch (error) {
logger.error(`${this.useRemoteApi ? '远程API' : '本地SQLite'} ${methodName} 执行失败:`, error);
// 如果使用远程API且出错回退到本地
if (this.useRemoteApi) {
logger.warn(`回退到本地SQLite执行 ${methodName}`);
try {
return await this.localService[methodName](args, event);
} catch (localError) {
logger.error(`本地SQLite ${methodName} 也失败:`, localError);
return {
status: false,
message: `操作失败:${error.message}`
};
}
}
return {
status: false,
message: `操作失败:${error.message}`
};
}
}
async getGroups(args, event) {
return await this._safeExecute('getGroups', args, event);
}
async getContentByGroupId(args, event) {
return await this._safeExecute('getContentByGroupId', args, event);
}
async addGroup(args, event) {
return await this._safeExecute('addGroup', args, event);
}
async editGroup(args, event) {
return await this._safeExecute('editGroup', args, event);
}
async deleteGroup(args, event) {
return await this._safeExecute('deleteGroup', args, event);
}
async addReply(args, event) {
return await this._safeExecute('addReply', args, event);
}
async editReply(args, event) {
return await this._safeExecute('editReply', args, event);
}
async deleteReply(args, event) {
return await this._safeExecute('deleteReply', args, event);
}
async deleteAllReply(args, event) {
return await this._safeExecute('deleteAllReply', args, event);
}
/**
* 切换服务模式
*/
async switchMode(useRemoteApi = false) {
this.useRemoteApi = useRemoteApi;
logger.info(`快捷回复服务模式已切换为: ${this.useRemoteApi ? 'HTTP API' : '本地SQLite'}`);
return {
status: true,
message: `已切换到${this.useRemoteApi ? '远程API' : '本地SQLite'}模式`
};
}
/**
* 获取当前模式状态
*/
getMode() {
return {
status: true,
data: {
useRemoteApi: this.useRemoteApi,
mode: this.useRemoteApi ? 'HTTP API' : '本地SQLite',
apiUrl: this.useRemoteApi ? this.httpService.baseUrl : null
}
};
}
/**
* 测试远程API连接
*/
async testRemoteConnection() {
if (!this.useRemoteApi) {
return {
status: false,
message: '当前未使用远程API模式'
};
}
try {
// 尝试获取分组列表来测试连接
const result = await this.httpService.getGroups({}, null);
return {
status: true,
message: '远程API连接正常',
data: result
};
} catch (error) {
return {
status: false,
message: `远程API连接失败: ${error.message}`
};
}
}
/**
* 数据同步:从本地同步到远程或从远程同步到本地
*/
async syncData(direction = 'local-to-remote') {
try {
if (direction === 'local-to-remote') {
// 从本地同步到远程
const localGroups = await this.localService.getGroups({}, null);
if (!localGroups.status) {
return { status: false, message: '获取本地数据失败' };
}
let syncCount = 0;
for (const group of localGroups.data) {
// 同步分组
const groupResult = await this.httpService.addGroup({ name: group.name }, null);
if (groupResult.status) {
syncCount++;
// 同步分组下的内容
for (const content of group.contents || []) {
await this.httpService.addReply({
remark: content.remark,
content: content.content,
type: content.type,
url: content.url,
groupId: groupResult.data.id
}, null);
}
}
}
return {
status: true,
message: `成功同步 ${syncCount} 个分组到远程`
};
} else {
// 从远程同步到本地的逻辑可以在这里实现
return {
status: false,
message: '暂不支持从远程同步到本地'
};
}
} catch (error) {
logger.error('数据同步失败:', error);
return {
status: false,
message: `数据同步失败: ${error.message}`
};
}
}
}
module.exports = HybridQuickReplyController;

View File

@ -1,42 +1,75 @@
'use strict';
const { logger } = require('ee-core/log');
const {quickReplyService} = require("../service/quickreply");
const HybridQuickReplyController = require('./hybridQuickReply');
/**
* 快捷回复 api
* 快捷回复 API 控制器
* 使用混合模式支持本地SQLite和远程HTTP API
* @class
*/
class QuickReplyController {
constructor() {
this.hybridController = new HybridQuickReplyController();
}
async getGroups(args,event) {
return await quickReplyService.getGroups(args,event);
async getGroups(args, event) {
return await this.hybridController.getGroups(args, event);
}
async getContentByGroupId(args,event) {
return await quickReplyService.getContentByGroupId(args,event);
async getContentByGroupId(args, event) {
return await this.hybridController.getContentByGroupId(args, event);
}
async addGroup(args,event) {
return await quickReplyService.addGroup(args,event);
async addGroup(args, event) {
return await this.hybridController.addGroup(args, event);
}
async editGroup(args,event) {
return await quickReplyService.editGroup(args,event);
async editGroup(args, event) {
return await this.hybridController.editGroup(args, event);
}
async deleteGroup(args,event) {
return await quickReplyService.deleteGroup(args,event);
async deleteGroup(args, event) {
return await this.hybridController.deleteGroup(args, event);
}
async addReply(args,event) {
return await quickReplyService.addReply(args,event);
async addReply(args, event) {
return await this.hybridController.addReply(args, event);
}
async editReply(args,event) {
return await quickReplyService.editReply(args,event);
async editReply(args, event) {
return await this.hybridController.editReply(args, event);
}
async deleteReply(args,event) {
return await quickReplyService.deleteReply(args,event);
async deleteReply(args, event) {
return await this.hybridController.deleteReply(args, event);
}
async deleteAllReply(args,event) {
return await quickReplyService.deleteAllReply(args,event);
async deleteAllReply(args, event) {
return await this.hybridController.deleteAllReply(args, event);
}
// 新增的管理方法
async switchMode(args, event) {
const { useRemoteApi } = args;
return await this.hybridController.switchMode(useRemoteApi);
}
async getMode(args, event) {
return this.hybridController.getMode();
}
async testRemoteConnection(args, event) {
return await this.hybridController.testRemoteConnection();
}
async syncData(args, event) {
const { direction } = args;
return await this.hybridController.syncData(direction);
}
}
module.exports = QuickReplyController;
QuickReplyController.toString = () => '[class QuickReplyController]';
module.exports = QuickReplyController;

View File

@ -68,6 +68,10 @@ class WindowController {
async editGlobalProxyInfo(args, event) {
return await windowService.editGlobalProxyInfo(args, event);
}
//批量更新全局代理配置
async updateGlobalProxyConfig(args, event) {
return await windowService.updateGlobalProxyConfig(args, event);
}
//关闭全局代理密码验证
async closeGlobalProxyPasswordVerification(args, event) {
return await windowService.closeGlobalProxyPasswordVerification(
@ -83,6 +87,10 @@ class WindowController {
async testProxy(args, event) {
return await windowService.testProxy(args, event);
}
// 检查代理状态
async checkProxyStatus(args, event) {
return await windowService.checkProxyStatus(args, event);
}
//打开当前会话控制台
async openSessionDevTools(args, event) {
return await windowService.openSessionDevTools(args, event);

View File

@ -25,6 +25,9 @@ contextBridge.exposeInMainWorld("electronAPI", {
getContactInfo: (args) => {
return ipcRenderer.invoke("get-contact-info", args);
},
getBatchContactNicknames: (args) => {
return ipcRenderer.invoke("get-batch-contact-nicknames", args);
},
collapseRightSidebar: () => {
ipcRenderer.send("collapse-right-sidebar");
},

View File

@ -163,7 +163,7 @@ const ipcMainListener = () => {
isFilter = "false",
mode,
} = args;
console.log("text-translate", args);
// console.log("text-translate", args);
if (text && text.trim() && to && route) {
//查询判断翻译服务商是否支持这个编码
const languageObj = await app.sdb.selectOne("language_list", {
@ -344,6 +344,11 @@ const ipcMainListener = () => {
return { status: true, data: userInfo };
});
// 批量获取联系人昵称配置
ipcMain.handle("get-batch-contact-nicknames", async (event, args) => {
return await contactInfoService.getBatchContactNicknames(args, event);
});
ipcMain.handle("get-language", async (event, args) => {
const languageObj = await app.sdb.selectOne('language_list', args)
return { status: true, data: languageObj };
@ -401,7 +406,7 @@ const ipcMainListener = () => {
timestamp: changeTime
});
console.log(`${platform}: 头像变化已记录 - ${phoneNumber}`);
// console.log(`${platform}: 头像变化已记录 - ${phoneNumber}`);
// 通知前端
if (!mainWin.isDestroyed()) {
@ -443,7 +448,7 @@ const ipcMainListener = () => {
timestamp: changeTime
});
console.log(`${platform}: 用户资料变化已记录 - ${changeType}`);
// console.log(`${platform}: 用户资料变化已记录 - ${changeType}`);
// 通知前端
if (!mainWin.isDestroyed()) {
@ -485,7 +490,7 @@ const ipcMainListener = () => {
timestamp: changeTime
});
console.log(`${platform}: 状态变化已记录 - ${changeType}`);
// console.log(`${platform}: 状态变化已记录 - ${changeType}`);
// 通知前端
if (!mainWin.isDestroyed()) {
@ -527,7 +532,7 @@ const ipcMainListener = () => {
timestamp: changeTime
});
console.log(`${platform}: 关于信息变化已记录 - ${changeType}`);
// console.log(`${platform}: 关于信息变化已记录 - ${changeType}`);
// 通知前端
if (!mainWin.isDestroyed()) {
@ -568,7 +573,7 @@ const ipcMainListener = () => {
timestamp: timestamp
});
console.log(`${platform}: 状态更新已记录 - ${updateType}`);
// console.log(`${platform}: 状态更新已记录 - ${updateType}`);
// 通知前端
if (!mainWin.isDestroyed()) {
@ -583,6 +588,23 @@ const ipcMainListener = () => {
logger.error("记录状态更新失败", error);
}
});
// 打开快捷回复配置页面
ipcMain.handle("open-quick-reply-config", async (event, args) => {
try {
const mainWindow = getMainWindow();
if (mainWindow) {
// 发送导航事件到前端
mainWindow.webContents.send("navigate-to-quick-reply-config");
return { status: true, message: "已打开快捷回复配置页面" };
} else {
return { status: false, message: "主窗口未找到" };
}
} catch (error) {
logger.error("打开快捷回复配置失败:", error);
return { status: false, message: `打开失败: ${error.message}` };
}
});
};
/**

View File

@ -221,7 +221,7 @@ const initializeTableData = async () => {
const globalProxyConfig = await app.sdb.selectOne("global_proxy_config");
if (!globalProxyConfig) {
await app.sdb.insert("global_proxy_config", {
proxyStatus: "true",
proxyStatus: "false", // 默认关闭全局代理
proxyType: "http",
proxyIp: "",
proxyPort: "",

View File

@ -945,8 +945,14 @@ const sessionChange = async () => {
const currentUserId = getCurrentUserId();
const args = { platform: "WhatsApp", userId: currentUserId };
console.log("会话切换 sessionChange args", args);
// 检查是否为新打开的会话
const isNewSession = isAppFirstLoad || !sessionStates.has(currentUserId);
// 先更新配置信息
ipc.infoUpdate(args);
await updateConfigInfo();
const myNode = document.getElementById("custom-translate-textarea");
if (!myNode) {
addTranslatePreview();
@ -954,8 +960,43 @@ const sessionChange = async () => {
}
styledTextarea.initData();
// 初始化用户名片
// 立即初始化用户名片,减少闪烁
initUserInfoCard(args);
// 处理滚动逻辑
if (isNewSession) {
console.log(`WhatsApp: 新打开会话 ${currentUserId},将滚动到底部`);
// 新打开的会话,滚动到底部 - 使用多次延迟确保完全加载
// 第一次滚动 - 早期尝试
setTimeout(() => {
scrollToBottom();
}, 300);
// 第二次滚动 - 确保DOM完全加载
setTimeout(() => {
scrollToBottom();
}, 800);
// 第三次滚动 - 最终确保
setTimeout(() => {
scrollToBottom();
}, 1500);
// 标记会话已打开
sessionStates.set(currentUserId, {
isFirstOpen: false,
scrollPosition: 0
});
// 标记应用不再是首次加载
if (isAppFirstLoad) {
isAppFirstLoad = false;
}
} else {
console.log(`WhatsApp: 切换到已打开的会话 ${currentUserId},保持原滚动位置`);
// 已打开的会话切换,不进行滚动操作,保持原位置
}
};
const debouncedSessionChange = debounce(sessionChange, 200);
@ -1198,6 +1239,150 @@ const expandLongMessage = async (node) => {
// 当前选中的会话DOM节点
let currentNode = null;
// 会话状态跟踪
let sessionStates = new Map(); // 存储会话状态 {userId: {isFirstOpen: boolean, scrollPosition: number}}
let isAppFirstLoad = true; // 标记应用是否首次加载
// 滚动到聊天底部的函数
const scrollToBottom = (force = false) => {
try {
console.log('WhatsApp: 开始执行滚动到底部操作');
// 更新的WhatsApp聊天消息容器选择器基于最新DOM结构
const possibleSelectors = [
// 最新的WhatsApp DOM结构选择器
'#main div[data-tab="1"]', // 主聊天区域
'#main div[role="application"]', // 应用容器
'#main div[class*="copyable-area"]', // 可复制区域
'#main div[class*="message-list"]', // 消息列表
'#main div[class*="copyable-text"]', // 可复制文本区域
'#main > div > div > div > div[class*="copyable"]', // 深层选择器
'#main > div:nth-child(2) > div > div', // 更深层的聊天区域
'#main > div:nth-child(2)', // 第二个子元素通常是聊天区域
];
let chatContainer = null;
for (const selector of possibleSelectors) {
const element = document.querySelector(selector);
if (element) {
// 检查元素是否真的可以滚动
const style = window.getComputedStyle(element);
if (style.overflowY === 'auto' || style.overflowY === 'scroll' ||
element.scrollHeight > element.clientHeight) {
chatContainer = element;
console.log(`WhatsApp: 找到可滚动聊天容器,使用选择器: ${selector}`);
console.log(`WhatsApp: 容器信息 - scrollHeight: ${element.scrollHeight}, clientHeight: ${element.clientHeight}, scrollTop: ${element.scrollTop}`);
break;
}
}
}
// 如果没找到特定容器,尝试查找包含消息的父容器
if (!chatContainer) {
console.log('WhatsApp: 未找到预定义容器,尝试通过消息元素查找');
const messageElements = document.querySelectorAll("div[role='row']");
console.log(`WhatsApp: 找到 ${messageElements.length} 个消息元素`);
if (messageElements.length > 0) {
// 从最后一个消息元素开始向上查找可滚动容器
const lastMessage = messageElements[messageElements.length - 1];
let parent = lastMessage.parentElement;
while (parent && parent !== document.body) {
const style = window.getComputedStyle(parent);
if (style.overflowY === 'auto' || style.overflowY === 'scroll' ||
parent.scrollHeight > parent.clientHeight) {
chatContainer = parent;
console.log('WhatsApp: 通过消息元素找到滚动容器');
console.log(`WhatsApp: 容器信息 - scrollHeight: ${parent.scrollHeight}, clientHeight: ${parent.clientHeight}, scrollTop: ${parent.scrollTop}`);
break;
}
parent = parent.parentElement;
}
}
}
if (chatContainer) {
// 记录滚动前的状态
const beforeScrollTop = chatContainer.scrollTop;
const maxScrollTop = chatContainer.scrollHeight - chatContainer.clientHeight;
console.log(`WhatsApp: 滚动前状态 - scrollTop: ${beforeScrollTop}, maxScrollTop: ${maxScrollTop}`);
// 使用最可靠的滚动方法
const scrollMethods = [
() => {
// 方法1: 直接设置为最大滚动值
chatContainer.scrollTop = chatContainer.scrollHeight;
console.log(`WhatsApp: 方法1执行后 scrollTop: ${chatContainer.scrollTop}`);
},
() => {
// 方法2: 使用scrollTo方法滚动到底部
chatContainer.scrollTo({
top: chatContainer.scrollHeight,
behavior: 'auto'
});
console.log(`WhatsApp: 方法2执行后 scrollTop: ${chatContainer.scrollTop}`);
},
() => {
// 方法3: 查找最后一条消息并正确滚动到它修复block参数
const lastMessage = chatContainer.querySelector("div[role='row']:last-child");
if (lastMessage) {
// 修复:使用 'end' 确保最后一条消息显示在视窗底部
lastMessage.scrollIntoView({
behavior: 'auto',
block: 'end', // 使用 'end' 让最后一条消息显示在视窗底部
inline: 'nearest'
});
console.log(`WhatsApp: 方法3执行后 scrollTop: ${chatContainer.scrollTop}`);
}
}
];
// 执行滚动方法
scrollMethods.forEach((method, index) => {
try {
method();
console.log(`WhatsApp: 执行滚动方法 ${index + 1} 完成`);
} catch (error) {
console.warn(`WhatsApp: 滚动方法 ${index + 1} 失败:`, error);
}
});
// 最终验证和强制滚动
setTimeout(() => {
const finalScrollTop = chatContainer.scrollTop;
const finalMaxScrollTop = chatContainer.scrollHeight - chatContainer.clientHeight;
const isAtBottom = finalScrollTop >= (finalMaxScrollTop - 10);
console.log(`WhatsApp: 滚动完成验证 - scrollTop: ${finalScrollTop}, maxScrollTop: ${finalMaxScrollTop}, 是否在底部: ${isAtBottom}`);
// 如果还没到底部,执行最终强制滚动
if (!isAtBottom) {
chatContainer.scrollTop = chatContainer.scrollHeight;
console.log(`WhatsApp: 执行最终强制滚动新scrollTop: ${chatContainer.scrollTop}`);
}
}, 200); // 增加延迟确保DOM完全更新
console.log('WhatsApp: 滚动到聊天底部操作执行完成');
return true;
} else {
console.warn('WhatsApp: 未找到聊天容器,无法滚动');
// 尝试输出当前DOM结构信息用于调试
const mainElement = document.querySelector('#main');
if (mainElement) {
console.log('WhatsApp: #main元素存在子元素数量:', mainElement.children.length);
console.log('WhatsApp: #main元素HTML结构:', mainElement.outerHTML.substring(0, 500) + '...');
} else {
console.log('WhatsApp: #main元素不存在');
}
return false;
}
} catch (error) {
console.error('WhatsApp: 滚动到底部时出错:', error);
return false;
}
};
const monitorMainNode = () => {
// 监听整个 body 的 DOM 变化,等待 #main 节点的出现
const observer = new MutationObserver(async (mutationsList, observer) => {
@ -1915,13 +2100,15 @@ const initWhatsAppObserver = () => {
});
console.log(`WhatsApp: 首次加载未读消息总数: ${lastSentTotalUnread}`);
// 首次加载时立即更新昵称
setTimeout(() => {
updateUserListNicknames();
}, 2000); // 延迟2秒确保页面完全加载
// 减少频率,避免过度更新,使用新的批量接口
setInterval(async () => {
const res = await ipc.getContactInfo({ platform: "WhatsApp" });
if (res.status) {
const data = res.data;
replaceUsername(data);
}
}, 1000);
await updateUserListNicknames();
}, 5000); // 改为5秒更新一次减少频率
};
// 监听whatsapp消息
@ -1929,8 +2116,134 @@ const initWhatsAppObserver = () => {
initWhatsAppObserver();
onlineStatusCheck();
// 替换用户名
// 监听页面刷新/重新加载,重置会话状态
window.addEventListener('beforeunload', () => {
sessionStates.clear();
isAppFirstLoad = true;
console.log('WhatsApp: 页面重新加载,重置会话状态');
});
// 监听页面加载完成,确保首次打开时滚动到底部
window.addEventListener('load', () => {
console.log('WhatsApp: 页面加载完成,标记为首次加载状态');
isAppFirstLoad = true;
sessionStates.clear();
});
// 从联系人列表项目中提取用户ID
function extractUserIdFromListItem(item) {
try {
// 方法1: 从React属性中获取用户ID
for (let key in item) {
if (key.startsWith("__reactProps$")) {
const reactProps = item[key];
if (reactProps && reactProps.children && reactProps.children.key) {
let userId = reactProps.children.key;
userId = userId.replace(/@.*/, ""); // 移除@后面的部分
return userId;
}
}
}
// 方法2: 从data-id属性获取
const dataId = item.getAttribute("data-id");
if (dataId) {
return dataId.replace(/@.*/, "");
}
// 方法3: 尝试从子元素的属性中获取
const titleElement = item.querySelector('div[role="gridcell"] span[title]');
if (titleElement) {
// 检查是否已经存储了用户ID
const storedUserId = titleElement.getAttribute("data-user-id");
if (storedUserId) {
return storedUserId;
}
}
return null;
} catch (error) {
console.error('提取用户ID失败:', error);
return null;
}
}
// 批量获取并更新用户列表昵称
async function updateUserListNicknames() {
try {
const res = await ipc.getBatchContactNicknames({
platform: "WhatsApp"
});
if (res.status && res.data && res.data.nicknameMap) {
const nicknameMap = res.data.nicknameMap;
console.log('WhatsApp: 获取到昵称配置:', nicknameMap);
// 定位Whatsapp的用户名元素
const listItem = document.querySelectorAll(
"div#pane-side div[role='listitem']"
);
listItem.forEach((item) => {
const titleElement = item.querySelector('div[role="gridcell"] span[title]');
if (!titleElement) return;
const title = titleElement.innerText.replace(/\s*/g, "");
const originalTitle = titleElement.getAttribute("title") || title;
// 检查是否已经有临时修改的昵称(优先级最高)
const existingRemark = titleElement.getAttribute("data-remark");
const existingUserId = titleElement.getAttribute("data-user-id");
// 提取当前联系人的用户ID
const currentUserId = extractUserIdFromListItem(item);
console.log(`联系人: ${title}, 提取的用户ID: ${currentUserId}`);
// 如果成功提取到用户ID检查是否有对应的昵称配置
if (currentUserId && nicknameMap[currentUserId]) {
const nickName = nicknameMap[currentUserId];
// 如果已经有临时修改的昵称且用户ID匹配保持临时昵称不变
if (existingRemark && existingUserId === currentUserId && titleElement.innerText === existingRemark) {
console.log(`保持临时昵称: ${existingRemark} for ${currentUserId}`);
return;
}
// 检查是否已经是昵称,避免重复设置
if (titleElement.innerText !== nickName) {
titleElement.innerText = nickName;
titleElement.style.fontWeight = "bold";
titleElement.style.color = "#1976d2"; // 使用更好的颜色
// 保存原始信息到data属性
titleElement.setAttribute("data-original-name", originalTitle);
titleElement.setAttribute("data-remark", nickName);
titleElement.setAttribute("data-user-id", currentUserId); // 添加userId标识
console.log(`更新昵称: ${currentUserId} -> ${nickName}`);
}
} else {
// 如果没有找到昵称配置,但有临时昵称,保持临时昵称
if (existingRemark && existingUserId) {
console.log(`保持临时昵称: ${existingRemark} for ${existingUserId}`);
} else if (currentUserId) {
console.log(`用户 ${currentUserId} 没有配置昵称`);
}
}
});
}
} catch (error) {
console.error('WhatsApp: 批量更新昵称失败:', error);
}
}
// 替换用户名(保留原有函数作为兼容)
function replaceUsername(contactInfo = null) {
// 如果没有传入联系人信息,使用新的批量接口
if (!contactInfo) {
updateUserListNicknames();
return;
}
// 定位Whatsapp的用户名元素
const listItem = document.querySelectorAll(
"div#pane-side div[role='listitem']"
@ -1942,22 +2255,38 @@ function replaceUsername(contactInfo = null) {
const title = titleElement.innerText.replace(/\s*/g, "");
const originalTitle = titleElement.getAttribute("title") || title;
// 检查是否已经有临时修改的昵称(优先级最高)
const existingRemark = titleElement.getAttribute("data-remark");
const existingUserId = titleElement.getAttribute("data-user-id");
// 遍历联系人信息,如果联系人信息中包含用户名,则替换用户名
if (contactInfo) {
contactInfo.forEach((info) => {
const { userId, nickName } = info;
// 更精确的匹配逻辑
// 更精确的匹配逻辑:确保完全匹配,避免部分匹配导致的错误
if (
(title.includes(userId) || originalTitle.includes(userId)) &&
userId &&
nickName &&
nickName.trim()
nickName.trim() &&
(title === userId || originalTitle === userId ||
title.endsWith(userId) || originalTitle.endsWith(userId))
) {
titleElement.innerText = nickName;
titleElement.style.fontWeight = "bold";
titleElement.style.color = "#1976d2"; // 使用更好的颜色
// 保存原始信息到data属性
titleElement.setAttribute("data-original-name", originalTitle);
titleElement.setAttribute("data-remark", nickName);
// 如果已经有临时修改的昵称且用户ID匹配保持临时昵称不变
if (existingRemark && existingUserId === userId && titleElement.innerText === existingRemark) {
console.log(`保持临时昵称: ${existingRemark} for ${userId}`);
return; // 跳过更新,保持临时昵称
}
// 检查是否已经是昵称,避免重复设置
if (titleElement.innerText !== nickName) {
titleElement.innerText = nickName;
titleElement.style.fontWeight = "bold";
titleElement.style.color = "#1976d2"; // 使用更好的颜色
// 保存原始信息到data属性
titleElement.setAttribute("data-original-name", originalTitle);
titleElement.setAttribute("data-remark", nickName);
titleElement.setAttribute("data-user-id", userId); // 添加userId标识
}
}
});
}
@ -1969,10 +2298,29 @@ async function initUserInfoCard(args) {
if (res.status && res.data.length > 0) {
let info = res.data[0];
const name = document.querySelector('#main header span[dir="auto"]');
if (name && info.nickName) {
name.innerText = info.nickName;
name.style.fontWeight = "bold";
name.style.color = "red";
if (name && info.nickName && info.nickName.trim()) {
// 检查是否有临时修改的昵称
const currentUserId = getCurrentUserId();
const currentContactElement = currentNode?.querySelector('div[role="gridcell"] span[title]');
const tempRemark = currentContactElement?.getAttribute("data-remark");
const tempUserId = currentContactElement?.getAttribute("data-user-id");
// 优先使用临时修改的昵称
let displayName = info.nickName;
if (tempRemark && tempUserId === currentUserId) {
displayName = tempRemark;
console.log(`使用临时昵称: ${tempRemark} for ${currentUserId}`);
}
// 避免闪烁:只在昵称不同时才更新
if (name.innerText !== displayName) {
name.innerText = displayName;
name.style.fontWeight = "bold";
name.style.color = "red";
// 保存昵称信息到顶部元素
name.setAttribute("data-display-name", displayName);
name.setAttribute("data-user-id", currentUserId);
}
// 检查是否已插入名片图标,避免重复
if (
@ -2055,15 +2403,30 @@ async function initUserInfoCard(args) {
// 更新联系人昵称,接收主进程消息通知
ipc.onMessageFromMain((newNickName) => {
if (currentNode) {
const listName = currentNode.querySelector('div[role="gridcell"] span');
if (currentNode && newNickName) {
const currentUserId = getCurrentUserId();
// 更精确地选择当前选中联系人的昵称元素
const listName = currentNode.querySelector('div[role="gridcell"] span[title]');
if (listName) {
listName.innerText = newNickName;
listName.style.fontWeight = "bold";
listName.style.color = "#1976d2";
// 更新data属性标记为临时修改的昵称
listName.setAttribute("data-remark", newNickName);
listName.setAttribute("data-user-id", currentUserId);
console.log(`临时更新昵称: ${newNickName} for ${currentUserId}`);
}
// 更新顶部聊天窗口的昵称
const cardName = document.querySelector('#main header span[dir="auto"]');
if (cardName) {
cardName.innerText = newNickName;
cardName.style.fontWeight = "bold";
cardName.style.color = "red";
// 保存临时昵称信息
cardName.setAttribute("data-display-name", newNickName);
cardName.setAttribute("data-user-id", currentUserId);
}
}
});

View File

@ -141,6 +141,58 @@ class ContactInfoService {
const count = await app.sdb.delete('follow_record',{id:id});
return {status:true,message:'删除成功'};
}
// 批量获取联系人昵称配置
async getBatchContactNicknames(args, event) {
const { platform, userIds } = args;
if (!platform?.trim()) {
return { status: false, message: '平台参数不能为空' };
}
try {
let contactInfos = [];
if (userIds && Array.isArray(userIds) && userIds.length > 0) {
// 如果提供了用户ID列表只查询这些用户
const placeholders = userIds.map(() => '?').join(',');
const sql = `SELECT userId, nickName FROM contact_info WHERE platform = ? AND userId IN (${placeholders}) AND nickName IS NOT NULL AND nickName != ''`;
const params = [platform, ...userIds];
contactInfos = await app.sdb.query(sql, params);
} else {
// 如果没有提供用户ID列表查询该平台所有有昵称的联系人
contactInfos = await app.sdb.select('contact_info', {
platform: platform
}, {
columns: ['userId', 'nickName'],
where: 'nickName IS NOT NULL AND nickName != ""'
});
}
// 转换为更方便使用的格式
const nicknameMap = {};
contactInfos.forEach(info => {
if (info.nickName && info.nickName.trim()) {
nicknameMap[info.userId] = info.nickName;
}
});
return {
status: true,
message: '查询成功',
data: {
nicknameMap: nicknameMap,
contactInfos: contactInfos
}
};
} catch (error) {
console.error('批量获取联系人昵称失败:', error);
return {
status: false,
message: `查询失败: ${error.message}`
};
}
}
}
ContactInfoService.toString = () => '[class ContactInfoService]';

View File

@ -0,0 +1,436 @@
'use strict';
const { logger } = require('ee-core/log');
const axios = require('axios');
const { app } = require('electron');
class HttpQuickReplyService {
constructor() {
// 从配置中获取API基础URL
const config = app.config || {};
this.baseUrl = config.api?.baseUrl || 'http://127.0.0.1:8000/api';
this.timeout = config.api?.timeout || 30000;
this.jwtToken = null; // JWT token缓存
// 创建axios实例
this.client = axios.create({
baseURL: this.baseUrl,
timeout: this.timeout,
headers: {
'Content-Type': 'application/json'
}
});
// 添加请求拦截器
this.client.interceptors.request.use(
async (config) => {
// 自动添加JWT认证token
const token = await this._getJwtToken();
if (token) {
config.headers.Authorization = `JWT ${token}`;
}
logger.info(`HTTP请求: ${config.method?.toUpperCase()} ${config.url}`);
return config;
},
(error) => {
logger.error('HTTP请求错误:', error);
return Promise.reject(error);
}
);
// 添加响应拦截器
this.client.interceptors.response.use(
(response) => {
logger.info(`HTTP响应: ${response.status} ${response.config.url}`);
return response;
},
async (error) => {
logger.error('HTTP响应错误:', error.message);
// 如果是401错误清除token并重试一次
if (error.response && error.response.status === 401) {
logger.warn('快捷回复HTTP服务收到401错误清除token并重试');
this._clearJwtToken();
// 重试原请求
const originalRequest = error.config;
if (!originalRequest._retry) {
originalRequest._retry = true;
const newToken = await this._getJwtToken();
if (newToken) {
originalRequest.headers.Authorization = `JWT ${newToken}`;
return this.client(originalRequest);
}
}
}
return Promise.reject(error);
}
);
}
/**
* 获取JWT token
*/
async _getJwtToken() {
try {
// 如果已有有效token直接返回
if (this.jwtToken) {
return this.jwtToken;
}
// 从app.authInfo获取用户信息
const authInfo = app.authInfo;
if (!authInfo || !authInfo.userName) {
logger.warn('快捷回复HTTP服务未找到用户认证信息无法获取JWT token');
return null;
}
// 尝试多种密码组合进行登录
const possiblePasswords = [
authInfo.userName, // 用户名作为密码
'admin123', // 常见默认密码
'admin123456', // 系统默认密码
'123456', // 简单密码
authInfo.authKey?.substring(0, 8) // API密钥前8位
];
logger.info(`快捷回复HTTP服务尝试获取JWT token for user: ${authInfo.userName}`);
for (const password of possiblePasswords) {
if (!password) continue;
try {
const loginData = {
username: authInfo.userName,
password: password
};
// 直接调用登录API获取token
const response = await axios.post(`${this.baseUrl}/login/`, loginData, {
timeout: this.timeout,
headers: {
'Content-Type': 'application/json'
}
});
if (response.data && response.data.code === 2000 && response.data.data) {
this.jwtToken = response.data.data.access;
logger.info(`快捷回复HTTP服务JWT token获取成功 (使用密码: ${password.substring(0, 3)}***)`);
return this.jwtToken;
}
} catch (loginError) {
logger.debug(`快捷回复HTTP服务密码 ${password.substring(0, 3)}*** 登录失败:`, loginError.message);
continue;
}
}
logger.warn('快捷回复HTTP服务所有密码尝试均失败无法获取JWT token');
return null;
} catch (error) {
logger.error('快捷回复HTTP服务获取JWT token失败:', error.message);
return null;
}
}
/**
* 清除JWT token缓存
*/
_clearJwtToken() {
this.jwtToken = null;
}
/**
* 获取快捷回复分组列表
*/
async getGroups(args, event) {
try {
const response = await this.client.get('/QuickReplyGroupModelViewSet/groups_with_contents/');
if (response.data && response.data.status) {
return {
status: true,
message: '查询成功',
data: response.data.data || []
};
}
return {
status: false,
message: response.data?.message || '获取分组失败'
};
} catch (error) {
logger.error('获取快捷回复分组失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 根据分组ID获取快捷回复内容
*/
async getContentByGroupId(args, event) {
try {
const { groupId } = args;
if (!groupId) {
return { status: false, message: '分组ID不能为空' };
}
const response = await this.client.get(`/QuickReplyModelViewSet/?group_id=${groupId}`);
if (response.data && response.data.status) {
return {
status: true,
message: '查询成功',
data: response.data.data || []
};
}
return {
status: false,
message: response.data?.message || '获取内容失败'
};
} catch (error) {
logger.error('获取快捷回复内容失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 添加快捷回复分组
*/
async addGroup(args, event) {
try {
const { name } = args;
if (!name) {
return { status: false, message: '分组名称不能为空' };
}
const response = await this.client.post('/QuickReplyGroupModelViewSet/', {
name: name,
sort_order: 0
});
if (response.status === 201) {
return {
status: true,
message: '新增成功',
data: response.data
};
}
return {
status: false,
message: response.data?.message || '添加分组失败'
};
} catch (error) {
logger.error('添加快捷回复分组失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 编辑快捷回复分组
*/
async editGroup(args, event) {
try {
const { id, name } = args;
if (!id || !name) {
return { status: false, message: '分组ID和名称不能为空' };
}
const response = await this.client.put(`/QuickReplyGroupModelViewSet/${id}/`, {
name: name
});
if (response.status === 200) {
return {
status: true,
message: '修改成功',
data: response.data
};
}
return {
status: false,
message: response.data?.message || '修改分组失败'
};
} catch (error) {
logger.error('修改快捷回复分组失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 删除快捷回复分组
*/
async deleteGroup(args, event) {
try {
const { id } = args;
if (!id) {
return { status: false, message: '分组ID不能为空' };
}
const response = await this.client.delete(`/QuickReplyGroupModelViewSet/${id}/`);
if (response.status === 204 || response.status === 200) {
return {
status: true,
message: '删除成功'
};
}
return {
status: false,
message: response.data?.message || '删除分组失败'
};
} catch (error) {
logger.error('删除快捷回复分组失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 添加快捷回复
*/
async addReply(args, event) {
try {
const { remark, content, url, type, groupId } = args;
if (!groupId) {
return { status: false, message: '分组ID不能为空' };
}
const response = await this.client.post('/QuickReplyModelViewSet/', {
group: groupId,
remark: remark || '',
content: content || '',
type: type || 'text',
url: url || '',
sort_order: 0
});
if (response.status === 201) {
return {
status: true,
message: '新增成功',
data: response.data
};
}
return {
status: false,
message: response.data?.message || '添加快捷回复失败'
};
} catch (error) {
logger.error('添加快捷回复失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 编辑快捷回复
*/
async editReply(args, event) {
try {
const { id, remark, content, url, type, groupId } = args;
if (!id) {
return { status: false, message: '快捷回复ID不能为空' };
}
const response = await this.client.put(`/QuickReplyModelViewSet/${id}/`, {
group: groupId,
remark: remark || '',
content: content || '',
type: type || 'text',
url: url || ''
});
if (response.status === 200) {
return {
status: true,
message: '修改成功',
data: response.data
};
}
return {
status: false,
message: response.data?.message || '修改快捷回复失败'
};
} catch (error) {
logger.error('修改快捷回复失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 删除快捷回复
*/
async deleteReply(args, event) {
try {
const { id } = args;
if (!id) {
return { status: false, message: '快捷回复ID不能为空' };
}
const response = await this.client.delete(`/QuickReplyModelViewSet/${id}/`);
if (response.status === 204 || response.status === 200) {
return {
status: true,
message: '删除成功'
};
}
return {
status: false,
message: response.data?.message || '删除快捷回复失败'
};
} catch (error) {
logger.error('删除快捷回复失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
/**
* 删除分组下所有快捷回复
*/
async deleteAllReply(args, event) {
try {
const { groupId } = args;
if (!groupId) {
return { status: false, message: '分组ID不能为空' };
}
const response = await this.client.delete(`/QuickReplyGroupModelViewSet/${groupId}/clear_contents/`);
if (response.status === 200) {
return {
status: true,
message: response.data?.message || '清空成功'
};
}
return {
status: false,
message: response.data?.message || '清空失败'
};
} catch (error) {
logger.error('清空快捷回复失败:', error);
return {
status: false,
message: `网络错误:${error.message}`
};
}
}
}
module.exports = HttpQuickReplyService;

View File

@ -4,18 +4,24 @@ const { app, BrowserWindow } = require('electron')
class QuickReplyService {
async getGroups(args,event) {
logger.info('本地SQLite开始获取快捷回复分组');
const groups = await app.sdb.select('group_manage');
logger.info(`本地SQLite找到 ${groups.length} 个分组`);
for (let group of groups) {
const records = await app.sdb.select('quick_reply_record',{groupId:group.id})
// 确保groupId类型匹配将数字转换为字符串
const records = await app.sdb.select('quick_reply_record',{groupId:String(group.id)})
group.contents = records;
group.contentCount = records.length;
logger.info(`本地SQLite分组 ${group.name}(ID:${group.id}) 包含 ${records.length} 条快捷回复`);
}
logger.info('本地SQLite快捷回复分组查询完成');
return {status:true,message:'查询成功',data:groups};
}
async getContentByGroupId(args,event) {
const {groupId} = args;
const records = await app.sdb.select('quick_reply_record',{groupId:groupId})
// 确保groupId类型匹配将其转换为字符串
const records = await app.sdb.select('quick_reply_record',{groupId:String(groupId)})
return {status:true,message:'查询成功',data:records};
}
@ -34,7 +40,8 @@ class QuickReplyService {
async deleteGroup(args,event) {
const {id} = args;
await app.sdb.delete('group_manage',{id:id})
await app.sdb.delete('quick_reply_record',{groupId:id})
// 确保groupId类型匹配将其转换为字符串
await app.sdb.delete('quick_reply_record',{groupId:String(id)})
return {status:true,message:'删除成功'};
}

File diff suppressed because it is too large Load Diff

159
fix_proxy_ip.js Normal file
View File

@ -0,0 +1,159 @@
// 修复代理IP地址脚本
// 将被截断的IP地址 143.20.228.19 修复为 143.20.228.192
const { app } = require('electron');
async function fixProxyIP() {
console.log('🔧 修复代理IP地址...\n');
try {
// 查找所有包含错误IP的代理配置
const allConfigs = await app.sdb.selectAll("proxy_config");
console.log('📋 当前所有代理配置:');
let foundIncorrectIP = false;
for (const config of allConfigs) {
console.log(`配置ID ${config.id}:`);
console.log(` partitionId: ${config.partitionId}`);
console.log(` proxyIp: ${config.proxyIp}`);
console.log(` proxyPort: ${config.proxyPort}`);
console.log(` proxyStatus: ${config.proxyStatus}`);
// 检查是否是被截断的IP
if (config.proxyIp === '143.20.228.19') {
foundIncorrectIP = true;
console.log(` ❌ 发现被截断的IP地址: ${config.proxyIp}`);
// 修复IP地址
await app.sdb.update("proxy_config", {
proxyIp: "143.20.228.192"
}, { id: config.id });
console.log(` ✅ 已修复为: 143.20.228.192`);
} else if (config.proxyIp === '143.20.228.192') {
console.log(` ✅ IP地址正确: ${config.proxyIp}`);
}
console.log('');
}
if (!foundIncorrectIP) {
console.log('✅ 未发现需要修复的IP地址');
}
// 检查全局代理配置
const globalConfig = await app.sdb.selectOne("global_proxy_config");
if (globalConfig && globalConfig.proxyIp === '143.20.228.19') {
console.log('🔧 修复全局代理配置中的IP地址...');
await app.sdb.update("global_proxy_config", {
proxyIp: "143.20.228.192"
});
console.log('✅ 全局代理IP地址已修复');
}
console.log('\n🎯 修复完成!请重新测试代理连接。');
} catch (error) {
console.error('❌ 修复代理IP地址失败:', error);
}
}
async function verifyProxyConfig() {
console.log('🔍 验证代理配置...\n');
try {
const allConfigs = await app.sdb.selectAll("proxy_config");
for (const config of allConfigs) {
if (config.proxyStatus === 'true') {
console.log(`✅ 活动代理配置 (ID: ${config.id}):`);
console.log(` partitionId: ${config.partitionId}`);
console.log(` 类型: ${config.proxyType}`);
console.log(` 地址: ${config.proxyIp}:${config.proxyPort}`);
console.log(` 认证: ${config.userVerifyStatus === 'true' ? '启用' : '禁用'}`);
console.log(` 用户名: ${config.username || '未设置'}`);
console.log(` 密码: ${config.password ? '已设置' : '未设置'}`);
// 验证IP格式
const ipPattern = /^(\d{1,3}\.){3}\d{1,3}$/;
if (!ipPattern.test(config.proxyIp)) {
console.log(` ❌ IP地址格式无效: ${config.proxyIp}`);
} else {
console.log(` ✅ IP地址格式正确`);
}
// 验证端口
const port = parseInt(config.proxyPort);
if (isNaN(port) || port < 1 || port > 65535) {
console.log(` ❌ 端口号无效: ${config.proxyPort}`);
} else {
console.log(` ✅ 端口号正确`);
}
console.log('');
}
}
} catch (error) {
console.error('❌ 验证代理配置失败:', error);
}
}
async function setCorrectProxyConfig(partitionId) {
console.log(`🔧 为 ${partitionId} 设置正确的代理配置...\n`);
const correctConfig = {
proxyStatus: "true",
proxyType: "socks5", // 改为SOCKS5通常更稳定
proxyIp: "143.20.228.192", // 完整的IP地址
proxyPort: "3306",
userVerifyStatus: "true",
username: "mNrz1aEg",
password: "3xV3dBYB"
};
try {
const existingConfig = await app.sdb.selectOne("proxy_config", { partitionId });
if (existingConfig) {
await app.sdb.update("proxy_config", correctConfig, { id: existingConfig.id });
console.log('✅ 代理配置已更新');
} else {
await app.sdb.insert("proxy_config", { partitionId, ...correctConfig });
console.log('✅ 代理配置已创建');
}
console.log('📋 新配置详情:');
console.log(` 类型: ${correctConfig.proxyType.toUpperCase()}`);
console.log(` 地址: ${correctConfig.proxyIp}:${correctConfig.proxyPort}`);
console.log(` 认证: ${correctConfig.userVerifyStatus === 'true' ? '启用' : '禁用'}`);
console.log(` 用户名: ${correctConfig.username}`);
console.log(` 密码: ${'*'.repeat(correctConfig.password.length)}`);
} catch (error) {
console.error('❌ 设置代理配置失败:', error);
}
}
// 导出函数
module.exports = {
fixProxyIP,
verifyProxyConfig,
setCorrectProxyConfig
};
// 如果直接运行此文件
if (require.main === module) {
console.log('⚠️ 这个脚本需要在Electron应用启动后调用');
console.log('💡 请在应用的控制台中运行以下命令:');
console.log('');
console.log(' // 修复被截断的IP地址');
console.log(' const fix = require("./fix_proxy_ip.js");');
console.log(' fix.fixProxyIP();');
console.log('');
console.log(' // 验证代理配置');
console.log(' fix.verifyProxyConfig();');
console.log('');
console.log(' // 为特定会话设置正确配置替换为实际的partitionId');
console.log(' fix.setCorrectProxyConfig("sB4yuENS");');
}

View File

@ -77,6 +77,9 @@ const ipcApiRoute = {
// 修改联系人备注,同步更新页面
updateContactRemark: 'controller/contactInfo/updateContactRemark',
// 批量获取联系人昵称配置
getBatchContactNicknames: 'controller/contactInfo/getBatchContactNicknames',
//快捷回复相关
getGroups: 'controller/quickreply/getGroups',
getContentByGroupId: 'controller/quickreply/getContentByGroupId',

View File

@ -269,7 +269,9 @@ export default {
tooltipContent: 'Click to translate in input box\nDouble click to send original text',
searchPlaceholder: 'Filter by title or content',
noData: 'No Data',
send: 'Send'
send: 'Send',
fillInput: 'Fill Input',
addReply: 'Add Quick Reply'
},
userInfo: {
title: 'Contact Information',

View File

@ -229,7 +229,9 @@ export default {
tooltipContent: 'ចុចដើម្បីបកប្រែក្នុងប្រអប់បញ្ចូល\nចុចទ្វេដងដើម្បីផ្ញើអត្ថបទដើម',
searchPlaceholder: 'ត្រងតាមចំណងជើងឬមាតិកា',
noData: 'គ្មានទិន្នន័យ',
send: 'ផ្ញើ'
send: 'ផ្ញើ',
fillInput: 'បំពេញប្រអប់បញ្ចូល',
addReply: 'បន្ថែមឆ្លើយតបរហ័ស'
},
userInfo: {
title: 'ព័ត៌មានទំនាក់ទំនង',

View File

@ -264,7 +264,9 @@ export default {
tooltipContent: '单击到输入框进行翻译\n双击按钮发送原文',
searchPlaceholder: '请输入标题或者关键内容过滤',
noData: '暂无数据',
send: '发送'
send: '发送',
fillInput: '输入框提示',
addReply: '添加快捷回复'
},
userInfo: {
title: '联系人信息',

View File

@ -164,10 +164,6 @@
:value="child?.msgCount"></el-badge>
</div>
</div>
<div v-if="isCollapse" class="child-menu-item-icon margin-top-10">
<el-badge v-if="child?.msgCount > 0" :offset="[-15, 0]" type="error" :show-zero="false" :max="99"
:value="child?.msgCount"></el-badge>
</div>
</div>
</template>
</div>
@ -391,6 +387,9 @@ const filteredChildren = computed(() => (children, menuId) => {
const isActive = (menuId) => menuStore.currentMenu === menuId
const hasActiveChild = (menu) => {
if (!menu || !menu.children || !Array.isArray(menu.children)) {
return false
}
const nMenus = menu.children.filter(
child => child.windowStatus === 'true'
) || []
@ -398,6 +397,9 @@ const hasActiveChild = (menu) => {
}
const hasChildren = (menu) => {
if (!menu || !menu.children || !Array.isArray(menu.children)) {
return false
}
const nMenus = menu.children.filter(
child => child.windowStatus === 'true'
) || []
@ -513,10 +515,17 @@ const networkErrorCount = ref(0) // 新增网络错误计数
const checkLogin = async () => {
const authKey = menuStore.userInfo?.authKey
if (authKey) {
const res = await ipc.invoke(ipcApiRoute.login, { authKey })
try {
const res = await ipc.invoke(ipcApiRoute.login, { authKey })
// 处理网络错误的特殊情况
if (!res.status && res.message === 'login.errors.networkError') {
// 检查响应是否有效
if (!res) {
console.warn('登录检查返回空响应')
return
}
// 处理网络错误的特殊情况
if (!res.status && res.message === 'login.errors.networkError') {
networkErrorCount.value++
// 只有当网络错误超过3次才执行登出逻辑
if (networkErrorCount.value >= 3) {
@ -539,6 +548,10 @@ const checkLogin = async () => {
menuStore.setUserInfo(res.data)
}
console.log('check user auth:', res.data)
} catch (error) {
console.error('登录检查出错:', error)
// 网络错误或其他异常,不立即登出,等待下次检查
}
} else {
clearTimer()
await ipc.invoke(ipcApiRoute.hiddenSession, {})
@ -595,6 +608,15 @@ onMounted(async () => {
ipc.removeAllListeners('msg-count-notify')
ipc.on('msg-count-notify', handleMsgCountNotify)
// 处理快捷回复配置页面导航
const handleNavigateToQuickReplyConfig = () => {
// 切换到快捷回复菜单
toggleBottomMenu('QuickReply')
}
ipc.removeAllListeners('navigate-to-quick-reply-config')
ipc.on('navigate-to-quick-reply-config', handleNavigateToQuickReplyConfig)
initMenuSessions()
clearTimer()
@ -603,7 +625,7 @@ onMounted(async () => {
// 检查后端配置是否允许显示翻译配置菜单
try {
const res = await ipc.invoke(ipcApiRoute.getSystemConfig, { configKey: 'base.allow_translate_config' })
showTranslateConfig.value = res.status && res.data === 'true'
showTranslateConfig.value = res && res.status && res.data === 'true'
} catch (e) {
showTranslateConfig.value = false
}
@ -611,7 +633,7 @@ onMounted(async () => {
// 检查后端配置是否允许显示 TikTok 菜单
try {
const res = await ipc.invoke(ipcApiRoute.getSystemConfig, { configKey: 'base.allow_tiktok' })
showTikTokMenu.value = res.status && res.data === 'true'
showTikTokMenu.value = res && res.status && res.data === 'true'
} catch (e) {
showTikTokMenu.value = false
}
@ -623,10 +645,14 @@ onUnmounted(() => {
const initMenuSessions = async () => {
for (let menu of menuStore.menus) {
const res = await ipc.invoke(ipcApiRoute.getSessions, { platform: menu.id })
if (res.status) {
const arr = res.data.sessions
menuStore.setMenuChildren(menu.id, arr)
try {
const res = await ipc.invoke(ipcApiRoute.getSessions, { platform: menu.id })
if (res && res.status) {
const arr = res.data.sessions
menuStore.setMenuChildren(menu.id, arr)
}
} catch (error) {
console.error(`获取菜单 ${menu.id} 的会话失败:`, error)
}
}
}
@ -666,17 +692,27 @@ const getIsMenuShow = (menuId) => {
const startSession = async (child) => {
startingSessionId.value = child.partitionId
const res = await ipc.invoke(ipcApiRoute.startSession, { platform: child.platform, partitionId: child.partitionId })
if (res.status) {
menuStore.updateChildrenMenu(res.data)
} else {
try {
const res = await ipc.invoke(ipcApiRoute.startSession, { platform: child.platform, partitionId: child.partitionId })
if (res && res.status) {
menuStore.updateChildrenMenu(res.data)
} else {
ElMessage({
message: `${res?.message || '启动会话失败'}`,
type: 'error',
offset: 40
})
}
} catch (error) {
console.error('启动会话出错:', error)
ElMessage({
message: `${res.message}`,
message: '启动会话失败,请重试',
type: 'error',
offset: 40
})
} finally {
startingSessionId.value = null
}
startingSessionId.value = null
}
</script>

View File

@ -152,9 +152,13 @@ const currentGroupId = ref(0);
const groups = ref([])
const tableData = ref([])
const initData = async () => {
console.log('开始初始化快捷回复数据')
const res = await ipc.invoke(ipcApiRoute.getGroups,{})
console.log('获取分组结果:', res)
if (res.status) {
groups.value = res.data
} else {
console.error('获取快捷回复分组失败:', res.message)
}
await getTableData();
}
@ -405,10 +409,12 @@ const handleDeleteReply = async (row)=>{
}
}
onMounted(async () => {
console.log('快捷回复组件已挂载')
await initData()
if (currentGroupId.value === 0 && groups.value.length > 0) {
currentGroupId.value = groups.value[0].id;
}
console.log('快捷回复组件初始化完成,分组数量:', groups.value.length)
})
watch(
() => currentGroupId.value,

View File

@ -182,40 +182,77 @@ const testProxy = async () => {
}
const handleSave = async () => {
const args = { ...proxyInfo.value };
await ipc.invoke(ipcApiRoute.editProxyInfo, args);
// 刷新对应会话页面(修复:这里应传 partitionId 而不是 currentMenu
await ipc.invoke(ipcApiRoute.refreshSession, {
partitionId: menuStore.currentPartitionId
});
// 保存后自动刷新当前列表行的代理状态
try {
const pid = menuStore.currentPartitionId;
const res = await ipc.invoke(ipcApiRoute.checkProxyStatus, { partitionId: pid, platform: menuStore.platform });
const menu = menuStore.menus.find(m => m.id === menuStore.currentMenu);
const row = menu?.children?.find(c => c.partitionId === pid);
if (row) {
const should = res?.data?.shouldUseProxy;
const using = res?.data?.usingProxy;
row._proxyShould = typeof should === 'boolean' ? should : !!using;
row._proxyReachable = res?.data?.reachable === null ? null : !!res?.data?.reachable;
}
} catch (e) {}
const args = { ...proxyInfo.value };
await ipc.invoke(ipcApiRoute.editProxyInfo, args);
// 刷新对应会话页面(修复:这里应传 partitionId 而不是 currentMenu
const refreshResult = await ipc.invoke(ipcApiRoute.refreshSession, {
partitionId: menuStore.currentPartitionId
});
// 检查刷新结果
if (!refreshResult.status) {
// 代理配置失败,显示错误信息
ElMessage({
message: refreshResult.message || '代理配置失败',
type: 'error',
offset: 0,
duration: 5000
});
return; // 不继续执行后续逻辑
}
// 保存后自动刷新当前列表行的代理状态
try {
const pid = menuStore.currentPartitionId;
const res = await ipc.invoke(ipcApiRoute.checkProxyStatus, { partitionId: pid, platform: menuStore.platform });
const menu = menuStore.menus.find(m => m.id === menuStore.currentMenu);
const row = menu?.children?.find(c => c.partitionId === pid);
if (row) {
const should = res?.data?.shouldUseProxy;
const using = res?.data?.usingProxy;
row._proxyShould = typeof should === 'boolean' ? should : !!using;
row._proxyReachable = res?.data?.reachable === null ? null : !!res?.data?.reachable;
}
} catch (e) {}
if (args.defaultLanguage || args.timezone) {
ElMessage({
message: t('session.restartRequired'),
type: 'warning',
offset: 0,
})
}
if (args.defaultLanguage || args.timezone) {
ElMessage({
message: t('session.restartRequired'),
type: 'warning',
message: t('common.saveSuccess'),
type: 'success',
offset: 0,
})
} catch (error) {
console.error('保存代理配置失败:', error);
ElMessage({
message: '保存代理配置失败:' + (error.message || '未知错误'),
type: 'error',
offset: 0,
duration: 5000
});
}
ElMessage({
message: t('common.saveSuccess'),
type: 'success',
offset: 0,
})
}
// 监听代理开关状态变化,仅记录日志(不自动保存)
watch(
() => proxyInfo.value.proxyStatus,
(newValue, oldValue) => {
// 确保不是初始化时的变化,且值确实发生了改变
if (oldValue !== undefined && newValue !== oldValue) {
console.log(`🔄 代理开关状态变化: ${oldValue} -> ${newValue} (需要点击保存按钮才生效)`);
}
},
{ immediate: false }
);
// 监听会话切换,重新获取配置信息
watch(
() => menuStore.currentPartitionId,

View File

@ -13,6 +13,15 @@
<el-icon><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<div class="header-actions">
<el-button
size="small"
type="primary"
@click="handleAddQuickReply"
:icon="Plus">
{{ t('quickReply.addReply') }}
</el-button>
</div>
</div>
<div class="header-search">
<el-input
@ -63,9 +72,14 @@
</el-text>
</div>
<div class="right">
<el-button
size="small"
@click.stop="handleSend(record)"
<el-button
size="small"
@click.stop="handleFillInput(record)"
plain>{{ t('quickReply.fillInput') }}</el-button>
<el-button
size="small"
@click.stop="handleSend(record)"
type="primary"
plain>{{ t('quickReply.send') }}</el-button>
</div>
</div>
@ -96,7 +110,7 @@
</div>
</template>
<script setup>
import {ArrowDown, ArrowRight, CaretBottom, CaretTop, QuestionFilled, Search} from "@element-plus/icons-vue";
import {ArrowDown, ArrowRight, CaretBottom, CaretTop, QuestionFilled, Search, Plus} from "@element-plus/icons-vue";
import {computed, onMounted, ref} from "vue";
import {ipc} from "@/utils/ipcRenderer";
import {ipcApiRoute} from "@/api";
@ -110,9 +124,20 @@ onMounted(async () => {
})
const groups = ref([]);
const initData = async () => {
const res = await ipc.invoke(ipcApiRoute.getGroups, {})
if (res.status) {
groups.value = res.data
try {
console.log('右侧栏快捷回复:开始获取分组数据')
const res = await ipc.invoke(ipcApiRoute.getGroups, {})
console.log('右侧栏快捷回复:获取分组结果:', res)
if (res && res.status) {
groups.value = res.data || []
console.log('右侧栏快捷回复:成功获取分组数量:', groups.value.length)
} else {
console.warn('获取快捷回复分组失败:', res?.message || '未知错误')
groups.value = []
}
} catch (error) {
console.error('获取快捷回复分组出错:', error)
groups.value = []
}
}
const searchKey = ref('')
@ -149,6 +174,29 @@ const handleSend = (record) => {
}
ipc.invoke('send-msg',args)
}
const handleFillInput = (record) => {
const args = {
text: record.content,
partitionId: menuStore.currentPartitionId,
type:'input',
}
ipc.invoke('send-msg',args)
}
const handleAddQuickReply = async () => {
try {
// 打开快捷回复配置页面
const result = await ipc.invoke('open-quick-reply-config')
if (result && !result.status) {
console.warn('打开快捷回复配置失败:', result.message)
}
} catch (error) {
console.error('打开快捷回复配置出错:', error)
// 如果IPC调用失败可以考虑其他方式比如路由跳转
// router.push('/quick-reply-config')
}
}
</script>
<style scoped lang="less">
.border-top {
@ -176,22 +224,26 @@ const handleSend = (record) => {
}
.header-container {
width: 100%;
height: 30px;
min-height: 30px;
display: flex;
align-items: center;
flex-direction: column;
margin-bottom: 20px;
user-select: none;
justify-content: flex-start;
gap: 10px;
:deep(.el-text) {
--el-text-color: var(--el-text-color-primary);
}
.header-title {
display: flex;
align-items: center;
flex:1;
gap: 5px;
height: 30px;
}
.header-actions {
display: flex;
justify-content: flex-end;
width: 100%;
}
}
.header-search {
width: 100%;

View File

@ -171,15 +171,6 @@ const getTimeStr = (date = new Date())=> {
}
const userInfo = ref({})
watch(
() => menuStore.currentPartitionId,
async (newValue, oldValue) => {
if (newValue) {
await getUserInfo()
}
}
);
const hasUserId = computed(()=>{
return !userInfo.value.id;
})
@ -206,38 +197,62 @@ let watchers = []; // 存储所有字段的监听器
// 初始化字段监听逻辑
const addWatchers = ()=> {
removeWatchers(); // 确保不会重复绑定监听器
watchers = propertiesToWatch.map((property) =>
watch(
() => unref(userInfo.value[property]),
(newValue, oldValue) => {
if (newValue !== "" && newValue !== oldValue) {
handlePropertyChange(property, newValue);
}
}
)
);
// 只有当userInfo有有效数据时才添加监听器
if (userInfo.value && Object.keys(userInfo.value).length > 0) {
watchers = propertiesToWatch.map((property) =>
watch(
() => unref(userInfo.value[property]),
(newValue, oldValue) => {
if (newValue !== "" && newValue !== oldValue) {
handlePropertyChange(property, newValue);
}
},
{ immediate: false } // 不立即执行,避免初始化时触发
)
);
}
}
// 自定义逻辑
const handlePropertyChange = async (property, value) => {
if (userInfo && userInfo.value?.id){
const args = {key: property, value: value, id: userInfo.value.id};
await ipc.invoke(ipcApiRoute.updateContactInfo, args);
if (property === 'nickName') {
// 更新昵称时调用 updateContactRemark
const args = {partitionId: menuStore.currentPartitionId, nickName: value};
await ipc.invoke(ipcApiRoute.updateContactRemark, args);
// 确保有有效的用户信息和ID
if (userInfo && userInfo.value?.id && menuStore.currentPartitionId){
try {
const args = {key: property, value: value, id: userInfo.value.id};
const result = await ipc.invoke(ipcApiRoute.updateContactInfo, args);
if (result.status && property === 'nickName') {
// 更新昵称时调用 updateContactRemark
const remarkArgs = {partitionId: menuStore.currentPartitionId, nickName: value};
await ipc.invoke(ipcApiRoute.updateContactRemark, remarkArgs);
}
} catch (error) {
console.error('更新联系人信息失败:', error);
}
}
}
// 移除所有字段的监听器
const removeWatchers = ()=> {
watchers.forEach((stopWatcher) => stopWatcher()); // 调用每个监听器的停止方法
watchers = [];
if (watchers && watchers.length > 0) {
watchers.forEach((stopWatcher) => {
if (typeof stopWatcher === 'function') {
stopWatcher(); // 调用每个监听器的停止方法
}
});
watchers = [];
}
}
const followRecordArr = ref([])
const getUserInfo = async () => {
removeWatchers()
try {
// 确保有有效的平台和分区ID
if (!menuStore.platform || !menuStore.currentPartitionId) {
console.warn('缺少必要的平台或分区ID信息');
return;
}
const args = {
platform: menuStore.platform,
userId: '',
@ -245,14 +260,25 @@ const getUserInfo = async () => {
}
const res = await ipc.invoke(ipcApiRoute.getContactInfo, args);
if (res.status) {
Object.assign(userInfo.value, res.data.userInfo); // 更新表单数据
Object.assign(followRecordArr.value, res.data.records); // 更新表单数据
// 清空现有数据,避免数据混合
userInfo.value = {};
followRecordArr.value = [];
// 重新赋值新数据
if (res.data.userInfo) {
Object.assign(userInfo.value, res.data.userInfo);
}
if (res.data.records) {
Object.assign(followRecordArr.value, res.data.records);
}
}else {
userInfo.value = {};
followRecordArr.value = []
}
}catch(err) {
console.error("获取配置失败:", error.message);
console.error("获取联系人信息失败:", err.message);
userInfo.value = {};
followRecordArr.value = []
}finally {
addWatchers()
}
@ -267,20 +293,31 @@ watch(
}
);
// 生命周期管理
onMounted(async () => {
// 初始化获取用户信息
await getUserInfo()
})
// 生命周期
onMounted(async () => {
// 设置联系人数据更新监听器
await ipc.removeAllListeners('contact-data-update')
await ipc.on('contact-data-update', (event, args) => {
const {data} = args
userInfo.value = data.userInfo;
followRecordArr.value = data.records;
// 移除现有监听器,避免冲突
removeWatchers()
// 清空并更新数据
userInfo.value = {};
followRecordArr.value = [];
Object.assign(userInfo.value, data.userInfo);
Object.assign(followRecordArr.value, data.records);
// 重新添加监听器
addWatchers()
})
})
onUnmounted(() => {
removeWatchers()
ipc.removeAllListeners('contact-data-update')
})
const activityArr = ref([

View File

@ -40,15 +40,18 @@
"dependencies": {
"@google-cloud/translate": "^8.5.1",
"@vueuse/core": "^13.0.0",
"axios": "^1.8.1",
"axios": "^1.11.0",
"better-sqlite3": "^11.5.0",
"crypto-js": "^4.2.0",
"ee-core": "^4.0.0",
"electron-screenshots": "^0.5.27",
"electron-session-proxy": "^1.0.2",
"electron-updater": "^6.3.8",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"input": "^1.0.1",
"node-machine-id": "^1.1.12",
"socks-proxy-agent": "^8.0.5",
"telegram": "^2.26.22",
"volcengine-sdk": "^0.0.2"
}

View File

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="zh-CN" class="dark">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" />
<title></title>
<!-- 优化vue渲染未完成之前先加一个css动画 -->
<!DOCTYPE html>
<html lang="zh-CN" class="dark">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" />
<title></title>
<!-- 优化vue渲染未完成之前先加一个css动画 -->
<style>
#loadingPage {
background-color: #dedede;
@ -84,24 +84,24 @@
}
}
</style>
<script type="module" crossorigin src="./assets/index-DyAPP5Kn.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-Dq73M9Ka.css">
</head>
<body style="padding: 0; margin: 0;">
<div id="loadingPage">
<div class='base'>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
</div>
</div>
<div id="app"></div>
</body>
</html>
</style>
<script type="module" crossorigin src="./assets/index-DUbG0YLV.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-BOEy93f0.css">
</head>
<body style="padding: 0; margin: 0;">
<div id="loadingPage">
<div class='base'>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
<div class='cube'></div>
</div>
</div>
<div id="app"></div>
</body>

146
setup_global_proxy.js Normal file
View File

@ -0,0 +1,146 @@
// 全局代理快速配置脚本
// 这个脚本可以帮助您快速设置全局代理配置
const { app } = require('electron');
const path = require('path');
// 您的代理配置
const PROXY_CONFIG = {
proxyStatus: "true", // 启用代理
proxyType: "socks5", // 代理类型http, https, socks4, socks5
proxyIp: "143.20.228.192", // 代理IP
proxyPort: "3306", // 代理端口
userVerifyStatus: "true", // 启用认证
username: "mNrz1aEg", // 用户名
password: "3xV3dBYB" // 密码
};
async function setupGlobalProxy() {
console.log('🔧 开始配置全局代理...\n');
try {
// 这里需要在Electron应用启动后调用
// 因为需要访问app.sdb数据库
console.log('📋 代理配置信息:');
console.log(` 状态: ${PROXY_CONFIG.proxyStatus === "true" ? "启用" : "禁用"}`);
console.log(` 类型: ${PROXY_CONFIG.proxyType.toUpperCase()}`);
console.log(` 地址: ${PROXY_CONFIG.proxyIp}:${PROXY_CONFIG.proxyPort}`);
console.log(` 认证: ${PROXY_CONFIG.userVerifyStatus === "true" ? "启用" : "禁用"}`);
console.log(` 用户: ${PROXY_CONFIG.username}`);
console.log(` 密码: ${'*'.repeat(PROXY_CONFIG.password.length)}\n`);
// 检查是否存在全局代理配置
const existingConfig = await app.sdb.selectOne("global_proxy_config");
if (existingConfig) {
console.log('📝 更新现有的全局代理配置...');
await app.sdb.update("global_proxy_config", PROXY_CONFIG);
console.log('✅ 全局代理配置更新成功!');
} else {
console.log('📝 创建新的全局代理配置...');
await app.sdb.insert("global_proxy_config", PROXY_CONFIG);
console.log('✅ 全局代理配置创建成功!');
}
console.log('\n🎯 配置完成!请重启应用以应用新的代理设置。');
console.log('\n📋 验证步骤:');
console.log(' 1. 重启应用');
console.log(' 2. 打开任意平台WhatsApp/Telegram/TikTok');
console.log(' 3. 查看控制台日志,应该看到代理配置成功的消息');
console.log(' 4. 测试网络连接');
} catch (error) {
console.error('❌ 配置全局代理失败:', error);
console.error('💡 请确保在Electron应用启动后调用此函数');
}
}
// 验证当前全局代理配置
async function verifyGlobalProxy() {
console.log('🔍 验证当前全局代理配置...\n');
try {
const config = await app.sdb.selectOne("global_proxy_config");
if (!config) {
console.log('❌ 未找到全局代理配置');
return false;
}
console.log('📋 当前全局代理配置:');
console.log(` ID: ${config.id}`);
console.log(` 状态: ${config.proxyStatus === "true" ? "✅ 启用" : "❌ 禁用"}`);
console.log(` 类型: ${config.proxyType || "未设置"}`);
console.log(` IP: ${config.proxyIp || "未设置"}`);
console.log(` 端口: ${config.proxyPort || "未设置"}`);
console.log(` 认证: ${config.userVerifyStatus === "true" ? "✅ 启用" : "❌ 禁用"}`);
console.log(` 用户名: ${config.username || "未设置"}`);
console.log(` 密码: ${config.password ? "✅ 已设置" : "❌ 未设置"}`);
// 检查配置完整性
const isValid = config.proxyStatus === "true" &&
config.proxyIp &&
config.proxyPort &&
config.proxyType;
if (isValid) {
console.log('\n✅ 全局代理配置有效');
return true;
} else {
console.log('\n❌ 全局代理配置无效或不完整');
return false;
}
} catch (error) {
console.error('❌ 验证全局代理配置失败:', error);
return false;
}
}
// 清除全局代理配置
async function clearGlobalProxy() {
console.log('🧹 清除全局代理配置...\n');
try {
await app.sdb.update("global_proxy_config", {
proxyStatus: "false",
proxyType: "http",
proxyIp: "",
proxyPort: "",
userVerifyStatus: "false",
username: "",
password: ""
});
console.log('✅ 全局代理配置已清除');
} catch (error) {
console.error('❌ 清除全局代理配置失败:', error);
}
}
// 导出函数
module.exports = {
setupGlobalProxy,
verifyGlobalProxy,
clearGlobalProxy,
PROXY_CONFIG
};
// 如果直接运行此文件
if (require.main === module) {
console.log('⚠️ 这个脚本需要在Electron应用启动后调用');
console.log('💡 请在应用的控制台中运行以下命令:');
console.log('');
console.log(' const { setupGlobalProxy } = require("./setup_global_proxy.js");');
console.log(' setupGlobalProxy();');
console.log('');
console.log('🔧 或者直接在全局代理设置页面手动配置:');
console.log(` 代理协议: ${PROXY_CONFIG.proxyType.toUpperCase()}`);
console.log(` 主机地址: ${PROXY_CONFIG.proxyIp}`);
console.log(` 端口号: ${PROXY_CONFIG.proxyPort}`);
console.log(` 启用认证: 是`);
console.log(` 用户名: ${PROXY_CONFIG.username}`);
console.log(` 密码: ${PROXY_CONFIG.password}`);
}

155
simple_proxy_test.js Normal file
View File

@ -0,0 +1,155 @@
const net = require('net');
function testProxyConnection(host, port, username, password) {
return new Promise((resolve) => {
console.log(`\n🔍 测试代理连接:`);
console.log(` 主机: ${host}`);
console.log(` 端口: ${port}`);
console.log(` 用户名: ${username}`);
console.log(` 密码: ${password ? '***' : '无'}`);
console.log(`${'='.repeat(50)}`);
// 1. 基本TCP连接测试
console.log('\n📡 测试1: TCP连接测试');
const socket = new net.Socket();
const timeout = setTimeout(() => {
socket.destroy();
console.log(' ❌ TCP连接超时 (10秒)');
testHttpProxy();
}, 10000);
socket.connect(port, host, () => {
clearTimeout(timeout);
console.log(' ✅ TCP连接成功');
socket.destroy();
testHttpProxy();
});
socket.on('error', (err) => {
clearTimeout(timeout);
console.log(` ❌ TCP连接失败: ${err.code || err.message}`);
// 错误分析
if (err.code === 'ECONNREFUSED') {
console.log(' 💡 连接被拒绝 - 端口可能未开放或服务未运行');
} else if (err.code === 'ENOTFOUND') {
console.log(' 💡 主机未找到 - 请检查IP地址');
} else if (err.code === 'ETIMEDOUT') {
console.log(' 💡 连接超时 - 主机不可达或网络问题');
}
testHttpProxy();
});
// 2. HTTP代理测试
function testHttpProxy() {
console.log('\n📡 测试2: HTTP代理协议测试');
const http = require('http');
// 构建HTTP CONNECT请求
const auth = username && password ?
Buffer.from(`${username}:${password}`).toString('base64') : null;
const options = {
hostname: host,
port: port,
method: 'CONNECT',
path: 'httpbin.org:80',
headers: auth ? {
'Proxy-Authorization': `Basic ${auth}`
} : {}
};
const req = http.request(options);
req.on('connect', (res, socket, head) => {
console.log(' ✅ HTTP代理连接成功');
console.log(` 状态码: ${res.statusCode}`);
socket.end();
testSocksProxy();
});
req.on('error', (err) => {
console.log(` ❌ HTTP代理连接失败: ${err.code || err.message}`);
testSocksProxy();
});
req.setTimeout(10000, () => {
console.log(' ❌ HTTP代理连接超时');
req.destroy();
testSocksProxy();
});
req.end();
}
// 3. SOCKS代理测试
function testSocksProxy() {
console.log('\n📡 测试3: SOCKS代理协议测试');
const socksSocket = new net.Socket();
const socksTimeout = setTimeout(() => {
socksSocket.destroy();
console.log(' ❌ SOCKS连接超时');
printSummary();
}, 10000);
socksSocket.connect(port, host, () => {
clearTimeout(socksTimeout);
// 发送SOCKS5握手
const handshake = Buffer.from([0x05, 0x01, 0x00]); // SOCKS5, 1 method, no auth
socksSocket.write(handshake);
socksSocket.once('data', (data) => {
if (data.length >= 2 && data[0] === 0x05) {
console.log(' ✅ SOCKS5代理响应正常');
console.log(` 认证方法: ${data[1] === 0x00 ? '无需认证' : data[1] === 0x02 ? '用户名密码' : '其他'}`);
} else {
console.log(' ❌ SOCKS5代理响应异常');
}
socksSocket.destroy();
printSummary();
});
});
socksSocket.on('error', (err) => {
clearTimeout(socksTimeout);
console.log(` ❌ SOCKS连接失败: ${err.code || err.message}`);
printSummary();
});
}
// 4. 总结
function printSummary() {
console.log('\n📋 测试总结:');
console.log(' 🔍 端口3306通常是MySQL数据库端口不是代理端口');
console.log(' 💡 常见代理端口:');
console.log(' - HTTP代理: 8080, 3128, 8888, 80');
console.log(' - SOCKS5代理: 1080, 1085');
console.log(' 📞 建议联系代理服务商确认正确的端口和协议类型');
resolve();
}
});
}
// 测试您提供的代理配置
const proxyConfig = {
host: '143.20.228.192',
port: 3306,
username: 'mNrz1aEg',
password: '3xV3dBYB'
};
console.log('🚀 开始代理连接测试...');
testProxyConnection(proxyConfig.host, proxyConfig.port, proxyConfig.username, proxyConfig.password)
.then(() => {
console.log('\n✨ 测试完成!');
process.exit(0);
})
.catch(error => {
console.error('\n💥 测试过程中发生错误:', error.message);
process.exit(1);
});

185
test-proxy.js Normal file
View File

@ -0,0 +1,185 @@
const net = require('net');
const http = require('http');
// 代理配置
const proxyConfig = {
host: 'sg.arxlabs.io',
port: 3010,
username: 'hqb367407-region-HK-sid-FrUmXj5L-t-120',
password: 'd37chqrl'
};
console.log('开始测试代理服务器连通性...');
console.log(`代理服务器: ${proxyConfig.host}:${proxyConfig.port}`);
console.log(`用户名: ${proxyConfig.username}`);
console.log('密码: ***');
// 测试1: TCP连接测试
function testTcpConnection() {
return new Promise((resolve, reject) => {
console.log('\n=== 测试1: TCP连接测试 ===');
const socket = net.createConnection({
host: proxyConfig.host,
port: proxyConfig.port,
timeout: 10000
});
socket.on('connect', () => {
console.log('✅ TCP连接成功');
socket.destroy();
resolve(true);
});
socket.on('error', (err) => {
console.log('❌ TCP连接失败:', err.message);
socket.destroy();
reject(err);
});
socket.on('timeout', () => {
console.log('❌ TCP连接超时');
socket.destroy();
reject(new Error('TCP connection timeout'));
});
});
}
// 测试2: HTTP代理测试
function testHttpProxy() {
return new Promise((resolve, reject) => {
console.log('\n=== 测试2: HTTP代理测试 ===');
const options = {
hostname: proxyConfig.host,
port: proxyConfig.port,
path: 'http://httpbin.org/ip',
method: 'GET',
headers: {
'Proxy-Authorization': 'Basic ' + Buffer.from(`${proxyConfig.username}:${proxyConfig.password}`).toString('base64'),
'Host': 'httpbin.org'
},
timeout: 10000
};
const req = http.request(options, (res) => {
console.log(`✅ HTTP代理响应状态: ${res.statusCode}`);
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('✅ HTTP代理响应数据:', data);
resolve(data);
});
});
req.on('error', (err) => {
console.log('❌ HTTP代理请求失败:', err.message);
reject(err);
});
req.on('timeout', () => {
console.log('❌ HTTP代理请求超时');
req.destroy();
reject(new Error('HTTP proxy timeout'));
});
req.end();
});
}
// 测试3: SOCKS5代理测试
function testSocks5Proxy() {
return new Promise((resolve, reject) => {
console.log('\n=== 测试3: SOCKS5代理测试 ===');
// 简单的SOCKS5握手测试
const socket = net.createConnection({
host: proxyConfig.host,
port: proxyConfig.port,
timeout: 10000
});
socket.on('connect', () => {
console.log('TCP连接建立尝试SOCKS5握手...');
// SOCKS5握手版本51种认证方法用户名密码认证
const handshake = Buffer.from([0x05, 0x01, 0x02]);
socket.write(handshake);
});
socket.on('data', (data) => {
if (data.length >= 2) {
if (data[0] === 0x05 && data[1] === 0x02) {
console.log('✅ SOCKS5服务器支持用户名密码认证');
socket.destroy();
resolve(true);
} else if (data[0] === 0x05 && data[1] === 0x00) {
console.log('✅ SOCKS5服务器不需要认证');
socket.destroy();
resolve(true);
} else if (data[0] === 0x05 && data[1] === 0xFF) {
console.log('❌ SOCKS5服务器不支持任何认证方法');
socket.destroy();
reject(new Error('SOCKS5 no acceptable methods'));
} else {
console.log('❌ SOCKS5握手失败响应:', data.toString('hex'));
socket.destroy();
reject(new Error('SOCKS5 handshake failed'));
}
}
});
socket.on('error', (err) => {
console.log('❌ SOCKS5代理测试失败:', err.message);
socket.destroy();
reject(err);
});
socket.on('timeout', () => {
console.log('❌ SOCKS5代理测试超时');
socket.destroy();
reject(new Error('SOCKS5 proxy timeout'));
});
});
}
// 运行所有测试
async function runAllTests() {
try {
// 测试TCP连接
await testTcpConnection();
// 测试HTTP代理
try {
await testHttpProxy();
} catch (err) {
console.log('HTTP代理测试失败可能不是HTTP代理或认证失败');
}
// 测试SOCKS5代理
try {
await testSocks5Proxy();
} catch (err) {
console.log('SOCKS5代理测试失败该代理服务器不支持SOCKS5协议');
}
} catch (err) {
console.log('\n=== 总结 ===');
console.log('❌ 代理服务器连接失败');
console.log('可能的原因:');
console.log('1. 代理服务器已下线');
console.log('2. IP地址或端口错误');
console.log('3. 网络防火墙阻止连接');
console.log('4. 代理服务器配置问题');
process.exit(1);
}
console.log('\n=== 总结 ===');
console.log('✅ 代理服务器基本连接正常');
}
runAllTests();

55
test_proxy_config.js Normal file
View File

@ -0,0 +1,55 @@
// 简单测试代理配置
console.log('测试代理配置解析...');
// 模拟配置数据
const testConfigs = [
{
proxyIp: 'sg.arxlabs.io',
proxyPort: '3010',
description: '正确配置'
},
{
proxyIp: 'sg.arxlabs.io:1',
proxyPort: '3010',
description: '错误配置IP包含端口'
}
];
testConfigs.forEach((config, index) => {
console.log(`\n=== 测试配置 ${index + 1}: ${config.description} ===`);
console.log(`原始 proxyIp: "${config.proxyIp}"`);
console.log(`原始 proxyPort: "${config.proxyPort}"`);
// 模拟当前代码的处理逻辑
let processedIp = config.proxyIp;
let processedPort = config.proxyPort;
if(processedIp){
processedIp = processedIp.replace(/\s/g, "");
}
if(processedPort){
processedPort = processedPort.replace(/\s/g, "");
}
let server = `${processedIp}:${processedPort}`;
console.log(`处理后 proxyIp: "${processedIp}"`);
console.log(`处理后 proxyPort: "${processedPort}"`);
console.log(`生成的 server: "${server}"`);
// 检查是否有效
const isValid = processedIp && processedPort && !processedIp.includes(':');
console.log(`配置是否有效: ${isValid}`);
if (!isValid) {
console.log('❌ 配置无效,可能会回退到全局代理或系统代理');
} else {
console.log('✅ 配置有效');
}
});
console.log('\n=== 建议修复方案 ===');
console.log('1. 检查数据库中的 proxyIp 字段是否包含端口号');
console.log('2. 如果包含端口号,需要清理数据');
console.log('3. 确保前端只保存纯IP地址到 proxyIp 字段');

63
test_proxy_fix.js Normal file
View File

@ -0,0 +1,63 @@
// 测试代理修复是否有效
const { session } = require('electron');
async function testProxyFormats() {
console.log('🔧 测试代理格式修复...\n');
const testConfigs = [
{
name: 'HTTP代理',
proxyRules: 'http://mNrz1aEg:3xV3dBYB@143.20.228.192:3306'
},
{
name: 'SOCKS5代理修复前-错误格式)',
proxyRules: 'socks5=socks5://143.20.228.192:3306'
},
{
name: 'SOCKS5代理修复后-正确格式)',
proxyRules: 'socks5://143.20.228.192:3306'
}
];
for (const config of testConfigs) {
console.log(`📡 测试 ${config.name}:`);
console.log(` 代理规则: ${config.proxyRules}`);
try {
// 创建临时会话
const partition = `test-${Date.now()}-${Math.random()}`;
const tmpSession = session.fromPartition(partition, { cache: false });
// 尝试设置代理
await tmpSession.setProxy({
mode: "fixed_servers",
proxyRules: config.proxyRules
});
console.log(` ✅ 代理设置成功 - 格式有效\n`);
} catch (error) {
console.log(` ❌ 代理设置失败: ${error.message}`);
if (error.message.includes('ERR_NO_SUPPORTED_PROXIES')) {
console.log(` 💡 这是 ERR_NO_SUPPORTED_PROXIES 错误 - 格式不被支持\n`);
} else {
console.log(` 💡 其他错误: ${error.code || error.message}\n`);
}
}
}
console.log('📋 总结:');
console.log(' ✅ 修复了 socks4=socks4:// 和 socks5=socks5:// 的错误格式');
console.log(' ✅ 现在使用正确的 socks4:// 和 socks5:// 格式');
console.log(' 🎯 这应该解决 ERR_NO_SUPPORTED_PROXIES 错误');
}
// 如果在Electron环境中运行
if (typeof require !== 'undefined' && require.main === module) {
console.log('⚠️ 这个脚本需要在Electron环境中运行');
console.log('💡 修复已应用到代码中,请重启应用并测试代理功能');
} else {
testProxyFormats().catch(console.error);
}
module.exports = { testProxyFormats };

90
test_proxy_real.js Normal file
View File

@ -0,0 +1,90 @@
const { app, BrowserWindow, session, net } = require('electron');
// 测试真实的代理连接
async function testRealProxy() {
console.log('开始测试真实代理连接...');
const config = {
proxyIp: '143.20.228.192',
proxyPort: '3306',
username: 'mNrz1aEg',
password: '3xV3dBYB'
};
// 创建测试会话
const testSession = session.fromPartition('proxy-test-real', { cache: false });
// 设置代理(不带认证信息)
const proxyConfig = {
mode: 'fixed_servers',
proxyRules: `http=${config.proxyIp}:${config.proxyPort}`,
proxyBypassRules: 'localhost,127.0.0.1,<local>'
};
console.log('代理配置:', proxyConfig);
await testSession.setProxy(proxyConfig);
// 创建测试窗口
const testWindow = new BrowserWindow({
width: 800,
height: 600,
show: false,
webPreferences: {
session: testSession,
nodeIntegration: false,
contextIsolation: true
}
});
// 监听login事件
testWindow.webContents.on('login', (event, request, authInfo, callback) => {
console.log('收到login事件:', {
isProxy: authInfo.isProxy,
scheme: authInfo.scheme,
host: authInfo.host,
port: authInfo.port,
realm: authInfo.realm
});
event.preventDefault();
if (authInfo.isProxy && authInfo.host === config.proxyIp && authInfo.port === Number(config.proxyPort)) {
console.log('提供代理认证凭据');
callback(config.username, config.password);
} else {
console.log('非匹配的认证请求');
callback('', '');
}
});
// 监听页面加载事件
testWindow.webContents.on('did-finish-load', () => {
console.log('✅ 页面加载完成');
});
testWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL) => {
console.log('❌ 页面加载失败:', errorCode, errorDescription, validatedURL);
});
// 测试加载页面
console.log('尝试加载测试页面...');
try {
await testWindow.loadURL('https://httpbin.org/ip');
console.log('页面加载请求已发送');
} catch (error) {
console.log('页面加载出错:', error.message);
}
// 等待5秒后关闭
setTimeout(() => {
testWindow.close();
console.log('测试完成');
process.exit(0);
}, 5000);
}
app.whenReady().then(testRealProxy);
app.on('window-all-closed', () => {
app.quit();
});