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

Discord.js Twitch直播embed报错TypeError 无法读取channel属性

Discord Twitch直播通知机器人报错排查与修复方案

问题现象

开发Discord平台Twitch直播通知功能时先后出现两类异常:

  1. 初始版本触发TypeError: Cannot read properties of undefined (reading 'channel')报错,嵌入消息仅展示静态封面图,无法正常加载直播流跳转链接
  2. 调整代码后消息可正常发送,但机器人仍会随机报错崩溃

报错根因

两个版本的异常均由空值访问、逻辑疏漏导致,具体问题点如下:

  • 初始版本的核心错误是多余属性访问:channels.cache.find()返回值本身就是频道对象,额外访问不存在的.channel属性直接触发第一类报错
  • 两个版本共同问题是缺少空值校验:如果频道未加载到缓存、机器人无频道访问权限、事件触发时服务器缓存未就绪,查询到的频道对象为undefined,直接调用send方法就会导致进程崩溃
  • 初始版本消息结构错误:将普通@提醒文本和Embed对象一同传入embeds数组,会导致消息解析异常,直播链接无法正常渲染
  • 缺少开播去重逻辑:直播过程中用户在线状态会多次更新(比如切换游戏、修改个人状态),没有去重判断会导致重复发通知,高频调用接口也容易触发限流失效
  • 未过滤非Twitch直播:Discord的STREAMING活动类型支持YouTube等多个平台,直接按Twitch规则拼接预览图会出现资源加载失败

修复方案

修复后完整可运行代码

// 提前定义通知频道ID,避免硬编码散落在业务逻辑中
const ANNOUNCE_CHANNEL_ID = "993751231799500871";
// 记录已发送开播通知的用户ID,避免重复推送
const liveUserMap = new Map();

bot.on("presenceUpdate", async (oldPresence, newPresence) => {
    // 前置空值校验,跳过缓存不完整的状态更新事件
    if (!newPresence?.guild || !newPresence?.user || !newPresence?.activities) return;
    
    // 筛选新状态中Twitch平台的直播活动
    const activeTwitchStream = newPresence.activities.find(act => {
        return act.type === "STREAMING" && act.url?.includes("twitch.tv/");
    });
    // 校验旧状态,判断是否为刚开播场景
    const existedStream = oldPresence?.activities?.find(act => {
        return act.type === "STREAMING" && act.url?.includes("twitch.tv/");
    });

    // 非新开播场景直接跳过
    if (!activeTwitchStream || existedStream) return;

    try {
        // 优先从缓存取频道,缓存未命中时主动调用接口拉取,解决缓存缺失导致的undefined问题
        const announceChannel = newPresence.guild.channels.cache.get(ANNOUNCE_CHANNEL_ID) 
            || await newPresence.guild.channels.fetch(ANNOUNCE_CHANNEL_ID);
        
        // 校验频道有效性和机器人发送权限
        if (!announceChannel?.isText() || !announceChannel.permissionsFor(bot.user).has("SEND_MESSAGES", "EMBED_LINKS")) {
            console.error("通知频道不存在或缺少发送/嵌入链接权限");
            return;
        }

        // 提取纯净Twitch用户名,处理带参数的直播链接
        const twitchUsername = activeTwitchStream.url.split("twitch.tv/").pop().split("?")[0];
        // 构造消息Embed,使用Twitch官方品牌色
        const streamEmbed = new Discord.MessageEmbed()
            .setColor("#9146FF")
            .setTitle(`${newPresence.user.tag} 正在Twitch直播!`)
            .setURL(activeTwitchStream.url)
            .setDescription(`**直播已开始**\n${activeTwitchStream.details || "快来围观吧~"}`)
            // 封面地址加时间戳避免CDN缓存旧图
            .setImage(`https://static-cdn.jtvnw.net/previews-ttv/live_user_${twitchUsername}-1920x1080.jpg?t=${Date.now()}`)
            .setTimestamp();

        // 正确拆分消息结构:普通文本传content字段,Embed对象传embeds字段
        await announceChannel.send({
            content: `@everyone ${newPresence.user.tag} 正在Twitch开播啦,快来看!\n${activeTwitchStream.url}`,
            embeds: [streamEmbed]
        });

        // 标记用户已发送开播通知
        liveUserMap.set(newPresence.user.id, true);

        // 监听下播事件,清除标记,下次开播可正常推送
        const offlineCheck = (oldState, newState) => {
            if (newState.user.id !== newPresence.user.id) return;
            const isStillStreaming = newState.activities?.some(act => act.type === "STREAMING" && act.url?.includes("twitch.tv/"));
            if (!isStillStreaming) {
                liveUserMap.delete(newPresence.user.id);
                bot.off("presenceUpdate", offlineCheck);
            }
        };
        bot.on("presenceUpdate", offlineCheck);

    } catch (err) {
        console.error("发送直播通知失败:", err);
    }
});

关键修复点说明

  • 所有可能为空的取值都增加可选链?.做前置校验,避免空值访问直接导致进程崩溃
  • 频道查询增加缓存未命中的主动拉取逻辑,同时校验频道类型和机器人操作权限
  • 修正消息传参结构,解决链接无法渲染、消息发送失败问题
  • 增加开播状态去重逻辑,避免直播过程中反复发送通知
  • 过滤非Twitch平台的直播活动,避免预览图拼接错误
  • 所有异步逻辑增加try/catch错误捕获,即使发送失败也不会导致机器人退出
  • 封面地址增加时间戳参数,避免CDN缓存返回历史静态图
  • 移除初始版本中多余的.channel属性访问,直接在频道对象上调用发送方法

内容的提问来源于stack exchange,提问作者Joe_42o

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 11:12:06