「群内AA制记账混乱?这款Electron机器人自动监听WhatsApp消息,支持复杂数学计算、操作撤回、定时清理,源码即插即用!」
本文完整解析基于 Electron + Node.js + TypeScript 开发的WhatsApp群聊记账机器人,包含以下核心功能:

  • ✅ 消息监听与数学表达式实时计算(支持 + - * / 和复杂公式)

  • ✅ 操作历史记录一键撤回(防止误操作)

  • ✅ 定时自动清理旧数据(可配置周期)

  • ✅ 管理员权限系统(Web界面动态管理)

  • ✅ 本地加密存储 + 多平台兼容(Windows/macOS/Linux)
    👉 付费解锁完整企业级源码(已用于海外财务系统)+ 防封号指南 + 二次开发教程

  • 目录(部分隐藏,付费后解锁)

    一、项目架构解析(免费试读)
  • 1、技术栈:Electron 主进程 vs 渲染进程设计

  • 2、数据流:消息监听 → 本金计算 → 日志记录 → 界面展示

  • 3、安全方案:SQLite 本地加密 + 防多开检测

 主要功能代码

const { Client, LocalAuth } = require('whatsapp-web.js');
const qrcode = require('qrcode-terminal');
const math = require('mathjs');
const fs = require('fs');
const path = require('path');
const { EventEmitter } = require('events');

console.log('正在初始化 WhatsApp 客户端...');

// ========== 动态路径配置(通过外部初始化) ==========
let DATA_DIR;
let CAPITAL_DATA_PATH;
let LOG_DIR;
let mainWindow = null; // 主窗口引用(由主进程设置)
let client; // 将 client 提升到模块作用域

// ==================== 初始化函数 ====================
// ==================== 初始化函数 ====================
function init(config) {
    DATA_DIR = config.dataDir;
    CAPITAL_DATA_PATH = path.join(DATA_DIR, 'capital.json');
    LOG_DIR = path.join(DATA_DIR, 'logs');
  
    // 创建数据目录
    if (!fs.existsSync(DATA_DIR)) {
      fs.mkdirSync(DATA_DIR, { recursive: true });
    }
  
    // 初始化 WhatsApp 客户端
    client = new Client({
      authStrategy: new LocalAuth({ dataPath: DATA_DIR }),
      puppeteer: {
        headless: false,
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage'
        ]
      }
    });
  
    // 绑定事件监听器
    bindClientEvents();
  }
// ==================== 本金管理系统 ====================
//const CAPITAL_DATA_PATH = path.join(DATA_DIR, 'capital.json');
// 添加管理员ID列表
// const ADMIN_IDS = [
//     '2348144171939',  // 管理员ID
//     // 可以添加更多管理员ID
//     '2347016857486'
// ];

class CapitalManager {
    static getData() {
        try {
            return JSON.parse(fs.readFileSync(CAPITAL_DATA_PATH, 'utf8')) || {};
        } catch {
            return {};
        }
    }

    static saveData(data) {
        fs.writeFileSync(CAPITAL_DATA_PATH, JSON.stringify(data, null, 2));
    }
    //获取群组资金信息
    static getCapital(groupId) {
        const data = this.getData();
        //初始化新群组
        if (!data[groupId]) {
            data[groupId] = { 
                capital: 0,
                history: []  // 新增历史记录
            };
            this.saveData(data);
        }
        return data[groupId];
    }
    //更新资金信息
    static updateCapital(groupId, newAmount, operation) {
        const data = this.getData();
        const groupData = data[groupId] || { capital: 0, history: [] };
        
        // 添加历史记录
        groupData.history.push({
            timestamp: new Date().toISOString(),
            oldValue: groupData.capital,
            newValue: newAmount,
            operation: operation
        });
        
        groupData.capital = parseFloat(newAmount.toFixed(4));
        data[groupId] = groupData;
        this.saveData(data);
        return true;
    }
    //撤回
    static undoLastOperation(groupId) {
        const data = this.getData();
        const groupData = data[groupId];
        
        if (!groupData || groupData.history.length === 0) {
            return null;
        }
        
        // 获取最后一条记录
        const lastOperation = groupData.history.pop();
        
        // 恢复上一状态
        if (groupData.history.length > 0) {
            groupData.capital = groupData.history[groupData.history.length - 1].newValue;
        } else {
            groupData.capital = 0;
        }
        
        this.saveData(data);
        return lastOperation;
    }
    //查账
    static getHistory(groupId) {
        const data = this.getData();
        if (!data[groupId]) {
            // 如果群组不存在,初始化一个新的群组数据
            data[groupId] = {
                capital: 0,
                history: []
            };
            this.saveData(data);
        }
        return data[groupId].history;
    }
    //清账
    static clearCapital(groupId) {
        const data = this.getData();
        if (!data[groupId]) {
            // 如果群组不存在,初始化一个新的群组
            data[groupId] = {
                capital: 0,
                history: []
            };
        } else {
            // 只重置本金和清空历史记录,保留群组记录
            data[groupId].capital = 0;
            data[groupId].history = [];
        }
        
        this.saveData(data);
        return true;
    }
}
// ==================== 日志系统 ====================
const getLogDate = () => new Date().toISOString().split('T')[0];

class Logger {
  static write(logData) {
    if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
    const logPath = path.join(LOG_DIR, `${getLogDate()}.log`);

    // 写入文件
    const logEntry = JSON.stringify({
      timestamp: new Date().toISOString(),
      ...logData
    }) + '\n';
    fs.appendFileSync(logPath, logEntry);

    // 发送到界面(通过主进程转发)
    if (mainWindow) {
      const logText = `${new Date().toISOString()} [${logData.type}] ${logData.event || logData.action}`;
      mainWindow.webContents.send('log-update', logText);
    }
  }

  static system(event, details) {
    this.write({ type: 'SYSTEM', event, details });
  }

  static operation(groupId, action, user, capitalChange) {
    this.write({ type: 'OPERATION', groupId, action, user, ...capitalChange });
  }
}
// ==================== WhatsApp 客户端核心逻辑 ====================
function bindClientEvents() {
    // ====== 验证 client 是否为 EventEmitter 实例 ======
    if (!(client instanceof EventEmitter)) {
        throw new Error('client 未正确初始化为 EventEmitter');
    }
    // ==================== 事件监听器 ====================
    client.on('loading_screen', (percent, message) => {
        console.log('加载进度:', percent, '%', message);
    });

    client.on('qr', qr => {
        const qr_svg = require('qr-image').svg(qr, { size: 10 });
        fs.writeFileSync('whatsapp-qr.svg', qr_svg);
        console.log('二维码已保存为 whatsapp-qr.svg');
        qrcode.generate(qr, { small: true });
    });

    client.on('authenticated', () => {
        console.log('WhatsApp 认证成功!');
    });

    client.on('ready', async () => {
        Logger.system('CLIENT_READY', { status: 'online' });
        console.log('WhatsApp 客户端已准备就绪!');
        if (!fs.existsSync('data')) fs.mkdirSync('data');
    });

    // ==================== 核心消息处理 ====================
    client.on('message', async (message) => {
        try {
            // 过滤条件:自己发送的消息、非群组消息、非文本消息
            if (message.fromMe || 
                !(await message.getChat()).id._serialized.endsWith('@g.us') ||
                message.hasMedia || 
                message.type !== 'chat'
            ) return;
            const jsonData = JSON.parse(fs.readFileSync(CAPITAL_DATA_PATH, 'utf8')) || {};
            const ADMIN_USERNAME = jsonData._adminIds
            
            const chat = await message.getChat();
            await chat.sendStateTyping(); // 可选:显示正在输入状态
            
            // 获取用户信息
            const contact = await message.getContact();
            
            const userInfo = {
                id: contact.id.user,
                name: contact.pushname,
            };
            // console.log(jsonData);
            // console.log(ADMIN_USERNAME);
            // console.log(userInfo);
            
            // 检查是否为群组消息
            const isGroup = chat.id._serialized.endsWith('@g.us');
            if (!isGroup) return;

            const groupId = chat.id._serialized;
        
            const text = message.body.trim();
            if (!ADMIN_USERNAME.includes(userInfo.name)) {
                return message.reply('❌ 仅指定管理员可执行操作');
            }
            // 撤回命令
            if (text === '/撤回') {
                const lastOperation = CapitalManager.undoLastOperation(groupId);
                if (!lastOperation) {
                    return chat.sendMessage('❌ 没有可撤回的操作记录');
                }
                
                // 记录撤回操作日志
                Logger.operation(groupId, 'UNDO', userInfo, {
                    before: lastOperation.oldValue,
                    after: lastOperation.newValue,
                    operation: lastOperation.operation
                });
                
                return chat.sendMessage(
                    `⏪ 已撤回最后一次操作\n` +
                    `操作时间: ${new Date(lastOperation.timestamp).toLocaleString()}\n` +
                    `原操作: ${lastOperation.operation}\n` +
                    `恢复金额: ${lastOperation.oldValue}`
                );
            }
            // 查账命令
            if (text === '/查账') {
                console.log('收到查账命令');
                try {
                    const history = CapitalManager.getHistory(groupId);
                    //console.log('获取到历史记录:', history);
                    
                    // 获取当前余额
                    const currentBalance = CapitalManager.getCapital(groupId).capital;
                    
                    if (history.length === 0) {
                        //console.log('无历史记录');
                        return chat.sendMessage(`📜 暂无操作记录\n当前余额: ${currentBalance}`);
                    }
                    
                    // 记录查账操作日志
                    Logger.operation(groupId, 'QUERY', userInfo, {
                        recordCount: history.length,
                        currentCapital: currentBalance
                    });
                    
                    // 格式化历史记录
                    const historyText = history.map((record, index) => {
                        const date = new Date(record.timestamp).toLocaleString();
                        return `[${index + 1}] ${date}\n` +
                            `操作: ${record.operation}\n` +
                            `变化: ${record.oldValue} → ${record.newValue}`;
                    }).join('\n\n');

                    const message = `📜 操作历史记录(最近${history.length}条):\n` +
                                `当前余额: ${currentBalance}\n\n${historyText}`;
                    
                    console.log('准备发送消息:', message);
                    return chat.sendMessage(message);
                    
                } catch (error) {
                    console.error('查账出错:', error);
                    return chat.sendMessage('❌ 查询失败: ' + error.message);
                }
            }
            // 清账命令处理
            if (text === '/清账') {
                // 验证管理员权限
                // console.log("消息用户id:",contact.id.user);
                // console.log("管理员id:",ADMIN_IDS);
                // console.log("是否有权限:",ADMIN_IDS.includes(contact.id.user));
                
                // if (!ADMIN_IDS.includes(contact.id.user)) {
                //     return message.reply('❌ 仅指定管理员可执行此操作');
                // }

                // 检查是否有历史记录
                const history = CapitalManager.getHistory(groupId);
                if (history.length === 0) {
                    return message.reply(
                        '❌ 无需清账\n' +
                        '当前本金: 0\n' +
                        '历史记录为空'
                    );
                }

                const success = CapitalManager.clearCapital(groupId);
                // 记录清账操作日志
                Logger.operation(groupId, 'CLEAR', userInfo, {
                    before: CapitalManager.getCapital(groupId).capital,
                    after: 0
                });
                return message.reply(
                    '🔄 清账成功\n' +
                    '当前本金: 0\n' +
                    '历史记录已全部清除'
                );
            }
            // 匹配数学表达式(支持复杂运算)
            const mathExpression = text.match(/^([+\-*/])([\d().+\-*/]+)/);
            if (!mathExpression) return;

            const [_, operator, expression] = mathExpression;
            const groupdata = CapitalManager.getCapital(groupId);
            if(!groupdata){
                chat.sendMessage("无资金信息")
                return
            }
            //console.log("更新前:",groupdata);
            try {
                // 构造完整表达式
                const currentValue = groupdata.capital
                const fullExpression = `(${currentValue})${operator}(${expression})`;
                
                // 安全计算
                const result = math.evaluate(fullExpression);
                const formattedResult = parseFloat(result.toFixed(4));

                // 更新本金
                CapitalManager.updateCapital(groupId, formattedResult, fullExpression);
                const groupDataUpdate = CapitalManager.getCapital(groupId);
                if (!groupDataUpdate) return;
                //console.log("更新后:",groupDataUpdate);
                // 发送格式化响应
                chat.sendMessage(
                    `🔢 计算成功\n` +
                    `当前本金:${groupDataUpdate.capital}\n`+
                    `原值: ${currentValue}\n` +
                    `算式: ${fullExpression}\n` +
                    `新值: ${formattedResult}`
                );
                // 记录计账操作日志
                Logger.operation(groupId, 'CALCULATION', userInfo, {
                    before: currentValue,
                    after: formattedResult,
                    expression: fullExpression
                });
            } catch (error) {
                chat.sendMessage(`❌ 计算错误: ${error.message}`);
                console.log(`❌ 计算错误: ${error.message}`);
                
            }
        } catch (error) {
            console.error('处理消息时出错:', error);
        }
    });

    // ==================== 其他系统事件 ====================
    let reconnectAttempts = 0;
    const maxReconnectAttempts = 5;

    client.on('disconnected', async (reason) => {
        Logger.system('CLIENT_DISCONNECTED', { reason });
        console.log('断开连接,原因:', reason);
        if (reconnectAttempts < maxReconnectAttempts) {
            reconnectAttempts++;
            console.log(`尝试重新连接 (${reconnectAttempts}/${maxReconnectAttempts})...`);
            await client.initialize().catch(console.error);
        }
    });

    process.on('unhandledRejection', (reason) => {
        console.error('未处理的 Promise 拒绝:', reason);
    });

}

function startBot() {
    if (!client) {
      throw new Error('Client 未初始化,请先调用 init()');
    }
    client.initialize().catch(err => {
      console.error('初始化失败:', err);
    });
}
// 暴露方法供主进程设置窗口引用
module.exports = {
    setMainWindow: (window) => {
      mainWindow = window;
    }
};
// ==================== 模块导出 ====================
module.exports = {
    init,
    startBot,
    setMainWindow: (window) => {
      mainWindow = window;
    }
  };

付费用户专享权益

  1. 获取完整源码包

    • 包含Electron打包配置(electron-builder

    • 预编译版本(开箱即用)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐