如何在Express.js中触发事件并在独立Discord.js应用中回传任务结果
我有一个简易Express.js后端,负责处理Discord发来的GET请求,通过Bull+Throng后台运行耗时任务。目前流程正常:Discord发GET请求后,后端把请求加入任务队列,任务完成后能在Queue.on()监听器里拿到返回值。
现在需要解决的是:怎么把监听器里的结果发回Discord,同时让Discord监听这个事件并展示结果? 注意:Discord和Express是两个独立应用,不能整合到同一个项目里。
现有代码
Index.js
const express = require('express') const scrapTikTok = require('./tiktokScraping') const Queue = require('bull') const app = express() // Initialize BULL const REDIS_URL = process.env.REDIS_URL || 'redis://127.0.0.1:6379' const workQueue = new Queue('work', REDIS_URL, { defaultJobOptions: { removeOnComplete: true, removeOnFail: true } }) app.get('/scrap', async (req, res) => { res.send('Working on it...') await scrapTikTok() }) app.get('/url', async (req, res) => { const url = 'https://www.tiktok.com/@tristanvincentt' let job = await workQueue.add({url: url}) res.json({id: job.id}) }) workQueue.on('global:completed', (jobId, result) => { console.log(`Job completed with result ${result}`) // SEND RESULT BACK TO DISCORD }) app.listen(process.env.PORT || 3000, () => { console.log('App is running...') })
Worker.js
const throng = require('throng') const Queue = require("bull") const REDIS_URL = process.env.REDIS_URL || "redis://127.0.0.1:6379" const workers = process.env.WEB_CONCURRENCY || 1 const maxJobsPerWorker = 50 const sleep = ms => new Promise(r => setTimeout(r, ms)); function start() { const workQueue = new Queue('work', REDIS_URL) workQueue.process(maxJobsPerWorker, async job => { console.log(job.data.url) await sleep(10000) return {value: job.id} }) } throng({workers, start})
因为两个应用独立,只能通过跨应用异步通信实现,下面是三种可行方案:
方案1:Discord主动轮询任务状态
最简单的实现方式,不需要后端主动推送,适合轻量场景。
后端修改:新增任务查询接口
在Index.js里添加一个接口,让Discord能根据jobId查询任务状态和结果:
// 新增查询任务结果的接口 app.get('/job/:id', async (req, res) => { const job = await workQueue.getJob(req.params.id); if (!job) { return res.status(404).json({ status: 'not_found' }); } const state = await job.getState(); if (state === 'completed') { const result = await job.returnvalue(); res.json({ status: 'completed', result }); } else if (state === 'failed') { res.json({ status: 'failed' }); } else { res.json({ status: 'pending' }); } })
Discord端操作
Discord调用/url接口拿到jobId后,每隔固定时间(比如5秒)调用/job/:id接口查询状态,直到返回completed状态,然后把结果展示给用户。
方案2:后端通过Discord Webhook主动推送
实时性更好,后端任务完成后直接把结果推到Discord频道,不需要Discord轮询。
准备工作
在Discord服务器里创建Webhook:进入服务器设置→集成→Webhooks→新建Webhook,复制生成的Webhook URL。
后端修改:在监听器里调用Webhook
先安装axios依赖:npm install axios,然后修改Index.js的监听器:
const axios = require('axios'); // 替换成你的Discord Webhook地址 const DISCORD_WEBHOOK_URL = '你的Discord Webhook URL'; // 先修改/url接口,让Discord传入需要推送的目标标识(比如用户ID) app.get('/url', async (req, res) => { const { url, targetUserId } = req.query; // Discord请求时带上用户ID let job = await workQueue.add({ url, targetUserId }); res.json({ id: job.id }); }) workQueue.on('global:completed', async (jobId, result) => { console.log(`Job completed with result ${result}`); // 获取任务关联的用户ID const job = await workQueue.getJob(jobId); const { targetUserId } = job.data; try { await axios.post(DISCORD_WEBHOOK_URL, { content: `<@${targetUserId}> 你的任务完成啦!结果:${JSON.stringify(result)}` }); } catch (err) { console.error('推送结果到Discord失败:', err); } })
Discord端操作
不需要额外监听,Webhook会直接把消息发送到指定频道,@对应的用户即可。
方案3:WebSocket双向通信
适合需要高实时性的场景,建立Express和Discord之间的持久连接,任务完成后即时推送。
后端修改:添加WebSocket服务
安装ws依赖:npm install ws,然后在Index.js里添加WebSocket服务:
const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); // 存储jobId和对应Discord客户端的连接 const clientMap = new Map(); wss.on('connection', (ws) => { // Discord连接后发送jobId绑定 ws.on('message', (data) => { const { jobId } = JSON.parse(data); clientMap.set(jobId, ws); }); // 清理断开的连接 ws.on('close', () => { for (const [id, clientWs] of clientMap.entries()) { if (clientWs === ws) { clientMap.delete(id); break; } } }); }); // 任务完成时推送结果 workQueue.on('global:completed', async (jobId, result) => { const client = clientMap.get(jobId); if (client && client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ status: 'completed', result })); clientMap.delete(jobId); // 推送后移除绑定 } })
Discord端操作
在Discord应用里使用WebSocket客户端连接到ws://你的Express地址:8080,发送{ "jobId": "xxx" }绑定任务,然后监听消息,收到结果后展示给用户。
方案选择建议
- 简单场景选方案1,实现成本极低;
- 追求实时性选方案2,实现简单且体验好,推荐大多数情况使用;
- 高实时交互场景选方案3,但需要处理连接断开、重连等异常情况。
内容的提问来源于stack exchange,提问作者Alaa

