diff --git a/__tests__/index.html b/__tests__/index.html
index ff40c8b..2fd2b24 100644
--- a/__tests__/index.html
+++ b/__tests__/index.html
@@ -6,7 +6,7 @@
diff --git a/db/chats.js b/db/chats.js
index bb95abe..ce2368e 100644
--- a/db/chats.js
+++ b/db/chats.js
@@ -3,15 +3,12 @@ import moment from 'moment-timezone'; // Import moment-timezone for timezones
// Set the default timezone to Beijing
moment.tz.setDefault("Asia/Shanghai");
-
-
// 数据库文件名
const dbFile = 'chats.db';
-
// 创建数据库连接
let db = new sqlite3.Database(dbFile, (err) => {
if (err) {
- console.error(err.message);
+ // console.error(err.message);
} else {
console.log('连接聊天记录数据库');
db.run(`
@@ -29,37 +26,41 @@ let db = new sqlite3.Database(dbFile, (err) => {
// 日志记录函数
export function chat(name, message) {
-
- // Limit message length to 200 characters
- // if(!message) return
- // const truncatedMessage = message.substring(0, 200);
-
const timestamp = moment().format("YYYY-MM-DD HH:mm"); // Format timestamp with Beijing time
db.run(`INSERT INTO chats (timestamp, name, message) VALUES (?, ?, ?)`, [timestamp, name, message], function (err) {
if (err) {
- console.error(err.message);
+ // console.error(err.message);
}
});
-
-
// Keep only the latest 200 log entries
db.run(`DELETE FROM chats WHERE id NOT IN (SELECT id FROM logs ORDER BY id DESC LIMIT 200)`);
-
-
}
-
// 查询日志
export function getChats() {
return new Promise((resolve, reject) => {
db.all(`SELECT * FROM chats ORDER BY timestamp DESC`, [], (err, rows) => {
if (err) {
- console.error(err.message);
+ // console.error(err.message);
reject(err);
} else {
resolve(rows);
}
});
});
+}
+
+// 删除所有聊天记录
+export function deleteChats() {
+ return new Promise((resolve, reject) => {
+ db.run(`DELETE FROM chats`, [], function(err) {
+ if (err) {
+ // console.error(err.message);
+ reject(err);
+ } else {
+ resolve({message: '所有日志已删除'}); // Resolve with a success message
+ }
+ });
+ });
}
\ No newline at end of file
diff --git a/db/config.js b/db/config.js
new file mode 100644
index 0000000..3e3415b
--- /dev/null
+++ b/db/config.js
@@ -0,0 +1,126 @@
+import sqlite3 from 'sqlite3';
+
+// 数据库文件名
+const dbFile = 'config.db'; // Use a separate DB file for config
+
+// Create the database connection
+const db = new sqlite3.Database(dbFile, (err) => {
+ if (err) {
+ console.error(err.message);
+ } else {
+ console.log('配置文件数据库连接成功');
+ // Create the config table if it doesn't exist
+ db.run(`
+ CREATE TABLE IF NOT EXISTS config (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ difyApiKey TEXT,
+ geminiApiKey TEXT,
+ greeting TEXT,
+ pushTime TEXT,
+ groups TEXT,
+ isEnable INTEGER,
+ isPushEnable INTEGER
+ )
+ `, (err) => {
+ if (err) {
+ console.error("Error creating config table:", err.message);
+ } else {
+ // Insert initial config if the table is empty
+ db.get("SELECT COUNT(*) AS count FROM config", [], (err, row) => {
+ if (err) {
+ console.error("Error checking config table:", err.message);
+ } else if (row.count === 0) {
+ const defaultConfig = {
+ difyApiKey: '',
+ geminiApiKey: '',
+ greeting: 'Hello!',
+ pushTime: '09:00', // Example default time
+ groups: '[]', // Example default groups
+ isEnable: 1, // 1 for true, 0 for false
+ isPushEnable: 1, // 1 for true, 0 for false
+
+ };
+ insertConfig(defaultConfig) // Use the insertConfig function
+ .then(() => console.log("写入默认配置"))
+ .catch(err => console.error("写入数据错误:", err));
+ }
+ });
+ }
+ });
+
+ }
+});
+
+
+
+// Function to insert or update config (upsert)
+export function saveConfig(config) {
+ return new Promise((resolve, reject) => {
+ db.get("SELECT COUNT(*) AS count FROM config", [], (err, row) => {
+ if (err) {
+ reject(err);
+ } else if (row.count === 0) {
+ // Insert if no config exists
+ insertConfig(config).then(resolve).catch(reject);
+ } else {
+ // Update if config already exists (assuming only one config row)
+ updateConfig(config).then(resolve).catch(reject);
+ }
+ });
+ });
+}
+
+
+
+// Helper function to insert config
+function insertConfig(config) {
+ const { difyApiKey, geminiApiKey,greeting, groups, pushTime, isEnable,isPushEnable } = config;
+ return new Promise((resolve, reject) => {
+ db.run(`INSERT INTO config (difyApiKey,geminiApiKey, greeting, groups,pushTime, isEnable, isPushEnable) VALUES (?, ?, ?, ?, ?, ?, ?)`,
+ [difyApiKey, geminiApiKey,greeting, groups,pushTime, isEnable ? 1 : 0, isPushEnable ? 1 : 0],
+ function (err) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(this.lastID);
+ }
+ });
+ });
+}
+
+// Helper function to update config
+function updateConfig(config) {
+ const { difyApiKey, geminiApiKey,greeting, groups, pushTime, isEnable,isPushEnable } = config;
+ return new Promise((resolve, reject) => {
+ db.run(`UPDATE config SET difyApiKey = ?,geminiApiKey= ?, greeting = ?, groups = ?, pushTime = ?, isEnable = ? ,isPushEnable = ? WHERE id = 1`, // 更新第一列
+ [difyApiKey, geminiApiKey,greeting, groups, pushTime, isEnable ? 1 : 0,isPushEnable ? 1 : 0],
+ function (err) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(this.changes);
+ }
+ });
+ });
+}
+
+
+
+// Get config
+export function getConfig() {
+ return new Promise((resolve, reject) => {
+ db.get(`SELECT * FROM config WHERE id = 1`, [], (err, row) => { // Select the first row
+ if (err) {
+ reject(err);
+ } else if (!row) {
+ resolve(null); // No config found.
+ } else {
+
+ // Convert isEnable back to boolean
+ row.isEnable = !!row.isEnable;
+ row.isPushEnable = !!row.isPushEnable;
+ resolve(row);
+ }
+ });
+ });
+}
\ No newline at end of file
diff --git a/db/logs.js b/db/logs.js
index eb902de..de79974 100644
--- a/db/logs.js
+++ b/db/logs.js
@@ -1,61 +1,51 @@
import sqlite3 from 'sqlite3';
-import moment from 'moment-timezone'; // Import moment-timezone for timezones
+import moment from 'moment-timezone';
-// Set the default timezone to Beijing
moment.tz.setDefault("Asia/Shanghai");
-
-// 数据库文件名
const dbFile = 'logs.db';
-// 创建数据库连接
-let db = new sqlite3.Database(dbFile, (err) => {
+const db = new sqlite3.Database(dbFile, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
if (err) {
- console.error(err.message);
+ console.error('Error opening database:', err.message);
} else {
- console.log('连接日志数据库');
- // 创建日志表(如果不存在)
+ console.log('连接日志数据库成功');
+ // Create the logs table
db.run(`
- CREATE TABLE IF NOT EXISTS logs (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- timestamp DATETIME DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), -- Use STRFTIME for timestamps
- level TEXT,
- message TEXT
- )
- `);
+ CREATE TABLE IF NOT EXISTS logs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ timestamp TEXT, -- Store timestamp as TEXT for simplicity
+ level TEXT,
+ message TEXT
+ )
+ `, (err) => { // Handle table creation errors
+ if (err) {
+ console.error("Error creating logs table:", err.message);
+ }
+ });
}
});
-
-// 日志记录函数
+// 记录日志
export function log(level, message) {
-
- // Limit message length to 200 characters
- // if(!message) return
- // const truncatedMessage = message.substring(0, 200);
-
- const timestamp = moment().format("YYYY-MM-DD HH:mm"); // Format timestamp with Beijing time
- db.run(`INSERT INTO logs (timestamp, level, message) VALUES (?, ?, ?)`, [timestamp, level, message], function (err) {
- if (err) {
- console.error(err.message);
- }
+ const timestamp = moment().format("YYYY-MM-DD HH:mm:ss"); // Add seconds
+ db.serialize(() => { // Ensure operations are executed in order
+ db.run(`INSERT INTO logs (timestamp, level, message) VALUES (?, ?, ?)`, [timestamp, level, message], (err) => {
+ if (err) {
+ console.error('Error inserting log:', err.message); // Log insertion errors
+ }
+ });
});
-
-
- // Keep only the latest 200 log entries
- db.run(`DELETE FROM logs WHERE id NOT IN (SELECT id FROM logs ORDER BY id DESC LIMIT 200)`);
-
-
}
-
// 查询日志
export function getLogs() {
return new Promise((resolve, reject) => {
db.all(`SELECT * FROM logs ORDER BY timestamp DESC`, [], (err, rows) => { // Order by timestamp descending
if (err) {
+ console.error("触发错误");
console.error(err.message);
reject(err);
} else {
@@ -63,4 +53,18 @@ export function getLogs() {
}
});
});
+}
+
+// 删除所有日志
+export function deleteLogs() {
+ return new Promise((resolve, reject) => {
+ db.run(`DELETE FROM logs`, [], function(err) {
+ if (err) {
+ console.error(err.message);
+ reject(err);
+ } else {
+ resolve({message: '所有日志已删除'}); // Resolve with a success message
+ }
+ });
+ });
}
\ No newline at end of file
diff --git a/db/users.js b/db/users.js
new file mode 100644
index 0000000..74a2b3d
--- /dev/null
+++ b/db/users.js
@@ -0,0 +1,160 @@
+import sqlite3 from 'sqlite3';
+import bcrypt from 'bcrypt'; // 引入 bcrypt 用于密码加密
+
+// 数据库文件名
+const dbFile = 'users.db'; // 使用 users.db 存储用户信息
+
+
+// 创建数据库连接
+const db = new sqlite3.Database(dbFile, (err) => {
+ if (err) {
+ // console.error(err.message);
+ } else {
+ console.log('连接用户数据库');
+ // 创建用户表(如果不存在)
+ db.run(`
+ CREATE TABLE IF NOT EXISTS users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ username TEXT UNIQUE NOT NULL,
+ password TEXT NOT NULL,
+ level TEXT NOT NULL
+ )
+ `, (err) => {
+ if (err) {
+ // console.error("创建用户表失败:", err.message);
+ } else {
+ // 检查初始用户是否存在,如果不存在则创建
+ db.get(`SELECT * FROM users WHERE username = 'admin'`, [], (err, row) => {
+ if (err) {
+ // console.error("查询初始用户失败:", err.message);
+ } else if (!row) {
+ // 创建初始用户 admin,密码加密
+ bcrypt.hash('123456', 10, (err, hash) => { // 使用 bcrypt 加密密码
+ if (err) {
+ // console.error("密码加密失败:", err.message);
+ } else {
+ db.run(`INSERT INTO users (username, password, level) VALUES (?, ?, ?)`, ['admin', hash, 'admin'], (err) => {
+ if (err) {
+ // console.error("创建初始用户失败:", err.message);
+ } else {
+ console.log("初始用户 admin 创建成功");
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
+ });
+ }
+});
+
+
+
+// 添加用户
+export async function addUser(username, password, level) {
+ return new Promise((resolve, reject) => {
+ (async () => {
+ try {
+ const hashedPassword = await bcrypt.hash(password, 10); // 加密密码
+ db.run('INSERT INTO users (username, password, level) VALUES (?, ?, ?)', [username, hashedPassword, level], function(err) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(this.lastID); // 返回新用户的 ID
+ }
+ });
+ } catch (error) {
+ reject(error);
+ }
+ })();
+ });
+ }
+
+
+
+// 获取所有用户
+export function getUsers() {
+ return new Promise((resolve, reject) => {
+ db.all('SELECT * FROM users', [], (err, rows) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(rows);
+ }
+ });
+ });
+ }
+
+
+
+// 更新用户信息
+export async function updateUser(id, username, password, level) {
+ return new Promise((resolve, reject) => {
+ (async () => {
+ try {
+ // Only hash the password if it's provided
+ const data = [username, level, id];
+ let sql = 'UPDATE users SET username = ?, level = ? WHERE id = ?';
+
+ if (password) {
+ const hashedPassword = await bcrypt.hash(password, 10);
+ data.splice(1, 0, hashedPassword); // Insert hashed password at the correct position
+ sql = 'UPDATE users SET username = ?, password = ?, level = ? WHERE id = ?';
+ }
+
+ db.run(sql, data, function(err) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(this.changes); // Return the number of changed rows
+ }
+ });
+ } catch (error) {
+ reject(error);
+ }
+ })();
+ });
+ }
+
+
+// 删除用户
+export function deleteUser(id) {
+ return new Promise((resolve, reject) => {
+ db.run('DELETE FROM users WHERE id = ?', id, function(err) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(this.changes); // Return the number of changed rows
+ }
+ });
+ });
+ }
+
+
+
+// 验证用户登录
+export async function verifyUser(username, password) {
+ return new Promise((resolve, reject) => {
+ db.get('SELECT * FROM users WHERE username = ?', [username], async function(err, row) {
+ if (err) {
+ reject(err);
+ } else if (!row) {
+ resolve(null); // 用户不存在
+ } else {
+ try {
+ const match = await bcrypt.compare(password, row.password);
+ if (match) {
+ resolve(row); // 密码匹配,返回用户信息
+ } else {
+ resolve(null); // 密码不匹配
+ }
+ } catch (error) {
+ reject(error); // bcrypt 比较出错
+ }
+ }
+ });
+ });
+
+}
\ No newline at end of file
diff --git a/index.js b/index.js
index e1f5e35..374ecb0 100644
--- a/index.js
+++ b/index.js
@@ -13,15 +13,22 @@ import { WebSocketServer } from "ws"
import express from 'express';
import path from 'path';
import open from 'open';
-import {log,getLogs} from './db/logs.js'
-import {chat,getChats} from './db/chats.js'
+import {log,getLogs,deleteLogs} from './db/logs.js'
+import {chat,getChats,deleteChats} from './db/chats.js'
+import {getConfig,saveConfig} from './db/config.js'
import { fileURLToPath } from 'url'; // Import fileURLToPath
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
-const wss = new WebSocketServer({ port: 1982 })
+const wss = new WebSocketServer({ port: 1983 })
+// 限制 WebSocketServer 的最大连接数
+wss.setMaxListeners(20); // 或者设置为更合适的数值
+
const app = express();
+app.use(express.json()); // 解析 JSON 格式的请求体
+app.use(express.urlencoded({ extended: true })); // 解析 URL 编码的请求体
+
const port = 3000;
config();
@@ -281,6 +288,13 @@ export async function prepareBot() {
})
})
+ // wss.on('connection', function connection(ws) {
+ // bot.on('scan', (qrcode) => { // 将 scan 事件处理程序放在 connection 内部
+ // ws.send(qrcode);
+ // });
+ // });
+
+
bot.on("login", (user) => {
let {payload} = user;
let {name} = payload;
@@ -294,6 +308,7 @@ export async function prepareBot() {
})
bot.on("error", (e) => {
+ console.log(e)
log('error', e);
})
// 启动的时间会比较久
@@ -337,16 +352,54 @@ async function startBot() {
res.json(data);
});
});
+ // 删除全部日志
+ app.delete('/api/logs', (req, res) => {
+ deleteLogs().then((data) => {
+ res.json(data);
+ });
+ });
+ // 删除全部聊天记录
+ app.delete('/api/chats', (req, res) => {
+ deleteChats().then((data) => {
+ res.json(data);
+ });
+ });
+ // 获取全部聊天记录
app.get('/api/chats', (req, res) => {
getChats().then((data) => {
res.json(data);
});
});
+ // 获取配置信息
+ app.get('/api/settings', (req, res) => {
+ getConfig().then((data) => {
+ res.json(data);
+ });
+ });
+ // 更新配置信息
+ app.post('/api/settings', (req, res) => {
+ const config = req.body;
+ saveConfig(config).then((data) => {
+ res.json(data);
+ });
+ });
app.get('/settings', (req, res) => {
res.sendFile(path.join(__dirname, './public/settings.html'));
});
- app.get('/', (req, res) => {
- res.sendFile(path.join(__dirname, './public/index.html'));
+ app.get('/docs', (req, res) => {
+ res.sendFile(path.join(__dirname, './public/docs.html'));
+ });
+ app.get('/poster', (req, res) => {
+ res.sendFile(path.join(__dirname, './public/poster.html'));
+ });
+ app.get('/chats', (req, res) => {
+ res.sendFile(path.join(__dirname, './public/chats.html'));
+ });
+ app.get('/logs', (req, res) => {
+ res.sendFile(path.join(__dirname, './public/logs.html'));
+ });
+ app.get('/knowledge', (req, res) => {
+ res.sendFile(path.join(__dirname, './public/knowledge.html'));
});
app.listen(port, async () => {
console.log(`Web server listening at http://localhost:${port}`);
@@ -364,6 +417,7 @@ process.on('exit', async(code) => {
process.on('SIGINT', async () => {
log('warning', "程序退出");
+ wss.close(); // 关闭 WebSocket 服务器
await bot.logout();
process.exit();
});
diff --git a/package.json b/package.json
index 62c9434..a7da485 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"openai": "^4.28.0",
"pdf-parse": "^1.1.1",
"qrcode-terminal": "^0.12.0",
+ "qrcode.react": "^4.2.0",
"replicate": "^0.34.0",
"sensitive-words-js": "^1.0.4",
"sqlite3": "^5.1.7",
diff --git a/public/chats.html b/public/chats.html
new file mode 100644
index 0000000..640e4d1
--- /dev/null
+++ b/public/chats.html
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+ TubeX微信机器人
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/docs.html b/public/docs.html
new file mode 100644
index 0000000..5c725a8
--- /dev/null
+++ b/public/docs.html
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+ TubeX微信机器人
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 7b38363..6f7ea81 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,190 +1,219 @@
-
+
+
- TubeX微信机器人
-
-
-
-
- TubeX微信机器人
-
- 聊天记录
-
-
-
- ID |
- 时间戳 |
- 用户名/群名 |
- 消息 |
-
-
-
-
- 日志记录
-
-
-
- ID |
- 时间戳 |
- 等级 |
- 消息 |
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- async function getLogs() {
- try {
- const response = await fetch('/api/logs'); // Make sure this route is correct
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- const logs = await response.json();
- displayLogs(logs);
- } catch (error) {
- console.error("Error retrieving or displaying logs:", error);
- // You could also display an error message on the page here.
- }
- }
- async function getChats() {
- try {
- const response = await fetch('/api/chats'); // Make sure this route is correct
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- const chats = await response.json();
- displayChats(chats);
- } catch (error) {
- console.error("Error retrieving or displaying logs:", error);
- // You could also display an error message on the page here.
- }
- }
- getLogs();
- getChats();
-
-
+
+
+
+