使用whatsapp-web.js时导出的Client对象导入后未更新求助
问题
使用whatsapp-web.js开发WhatsApp机器人时,想要在多个文件中复用Client实例,但导出后导入的文件只能拿到初始的空对象{}。此前未使用RemoteAuth时,全局对象更新的方式能正常工作,但改用RemoteAuth(在mongoose连接的异步回调里实例化Client)后就失效了。
导出代码
const { Client, RemoteAuth } = require('whatsapp-web.js'); var client = {} // Require database mongoose.connect(DATABASE_URL).then(async () => { const store = new MongoStore({ mongoose: mongoose }); client = new Client({ authStrategy: new RemoteAuth({ store: store, backupSyncIntervalMs: 300000 }), headless: false, puppeteer: { args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--disable-gpu' ], }, }); client.on('authenticated', async () => { console.log('Client is authenticated!'); }); client.on('qr', async qr => { qrcode.generate(qr, { small: true }); }); client.on('ready', async () => { console.log('Client is ready!'); }); client.on('remote_session_saved', () => { console.log('Client is saved!'); }); client.initialize(); }); exports.client = client
导入及使用代码
var client = require('../app').client; //Funcion para que se envie correo de minimo de productos async function correoAgotados(agotados) { console.log('inicia proceso de envio de correo') var mensaje = "Saludos desde " + agotados[0].planta + ", <br>"; for (var i = 0; i < agotados.length; i++) mensaje = mensaje + "<p>El producto: " + agotados[i].producto + " ha llegado a su minimo.</p>" + "<p>Cantidad en Existencia: " + agotados[i].cantidad + " " + agotados[i].unidadCosteo + "</p><p>Minimo Registrado: " + agotados[i].min + " " + agotados[i].unidadCosteo + "</p> <br>" var mailOptions = { from: '--', to: '--', subject: 'Falta de Materiales', html: mensaje }; try { var contacts = ['--'] if (agotados[0].planta == 'Hadrok') contacts.push('--') mensaje = mensaje for (var i = 0; i < contacts.length; i++) { await client.sendMessage('521' + contacts[i] + '@c.us', mensaje.replace(/<br>|<p>/g, '\n').replace(/<\/p>/g, '')) } } catch { console.log('error de bot') console.log(client) } }
解决方案
问题核心是变量引用的赋值逻辑:
你一开始把client赋值为空对象,exports.client = client导出的是这个空对象的引用。但在mongoose连接的异步回调里,client = new Client(...)是把变量指向了一个新对象,导出的引用不会自动更新到新对象上。之前不用RemoteAuth时是同步实例化,所以导出时已经是正确实例,异步逻辑打破了这个流程。
修复方法
方法一:导出Promise,等待Client实例化完成
把Client的创建过程包装成Promise,导入方通过等待Promise拿到就绪的实例:
const { Client, RemoteAuth } = require('whatsapp-web.js'); // 用Promise包裹Client实例化流程 const clientPromise = mongoose.connect(DATABASE_URL).then(async () => { const store = new MongoStore({ mongoose: mongoose }); const client = new Client({ authStrategy: new RemoteAuth({ store: store, backupSyncIntervalMs: 300000 }), headless: false, puppeteer: { args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--disable-gpu' ], }, }); // 绑定事件 client.on('authenticated', async () => { console.log('Client is authenticated!'); }); client.on('qr', async qr => { qrcode.generate(qr, { small: true }); }); client.on('ready', async () => { console.log('Client is ready!'); }); client.on('remote_session_saved', () => { console.log('Client is saved!'); }); await client.initialize(); return client; }); exports.clientPromise = clientPromise;
导入代码修改:
const clientPromise = require('../app').clientPromise; async function correoAgotados(agotados) { console.log('inicia proceso de envio de correo') var mensaje = "Saludos desde " + agotados[0].planta + ", <br>"; for (var i = 0; i < agotados.length; i++) mensaje = mensaje + "<p>El producto: " + agotados[i].producto + " ha llegado a su minimo.</p>" + "<p>Cantidad en Existencia: " + agotados[i].cantidad + " " + agotados[i].unidadCosteo + "</p><p>Minimo Registrado: " + agotados[i].min + " " + agotados[i].unidadCosteo + "</p> <br>" var mailOptions = { from: '--', to: '--', subject: 'Falta de Materiales', html: mensaje }; try { // 等待Client实例化完成 const client = await clientPromise; var contacts = ['--'] if (agotados[0].planta == 'Hadrok') contacts.push('--') for (var i = 0; i < contacts.length; i++) { await client.sendMessage('521' + contacts[i] + '@c.us', mensaje.replace(/<br>|<p>/g, '\n').replace(/<\/p>/g, '')) } } catch (err) { console.log('error de bot', err) console.log(err) } }
方法二:用对象包裹Client,保持引用不变
把Client放在一个对象里,修改对象属性而非替换整个变量,保证导出的引用始终指向同一个对象:
const { Client, RemoteAuth } = require('whatsapp-web.js'); // 用对象包裹client,保持引用不变 const clientWrapper = { client: null }; mongoose.connect(DATABASE_URL).then(async () => { const store = new MongoStore({ mongoose: mongoose }); clientWrapper.client = new Client({ authStrategy: new RemoteAuth({ store: store, backupSyncIntervalMs: 300000 }), headless: false, puppeteer: { args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--disable-gpu' ], }, }); // 绑定事件 clientWrapper.client.on('authenticated', async () => { console.log('Client is authenticated!'); }); clientWrapper.client.on('qr', async qr => { qrcode.generate(qr, { small: true }); }); clientWrapper.client.on('ready', async () => { console.log('Client is ready!'); }); clientWrapper.client.on('remote_session_saved', () => { console.log('Client is saved!'); }); clientWrapper.client.initialize(); }); exports.clientWrapper = clientWrapper;
导入代码修改:
const clientWrapper = require('../app').clientWrapper; async function correoAgotados(agotados) { console.log('inicia proceso de envio de correo') var mensaje = "Saludos desde " + agotados[0].planta + ", <br>"; for (var i = 0; i < agotados.length; i++) mensaje = mensaje + "<p>El producto: " + agotados[i].producto + " ha llegado a su minimo.</p>" + "<p>Cantidad en Existencia: " + agotados[i].cantidad + " " + agotados[i].unidadCosteo + "</p><p>Minimo Registrado: " + agotados[i].min + " " + agotados[i].unidadCosteo + "</p> <br>" var mailOptions = { from: '--', to: '--', subject: 'Falta de Materiales', html: mensaje }; try { // 先判断Client是否初始化完成 if (!clientWrapper.client) { console.log('Client还未初始化完成'); return; } var contacts = ['--'] if (agotados[0].planta == 'Hadrok') contacts.push('--') for (var i = 0; i < contacts.length; i++) { await clientWrapper.client.sendMessage('521' + contacts[i] + '@c.us', mensaje.replace(/<br>|<p>/g, '\n').replace(/<\/p>/g, '')) } } catch (err) { console.log('error de bot', err) console.log(err) } }
注意事项
- 方法一的Promise方案更可靠,能确保拿到的是已经初始化完成的Client实例;
- 方法二需要自行处理Client未就绪的情况,比如监听
ready事件后再执行业务逻辑,避免调用API时实例未初始化。
内容的提问来源于stack exchange,提问作者Adrian Gonzalez
相关产品推荐
相关产品推荐

