You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 06:01:26