"use strict"; const { configuredChannels } = require("../../../src/services/twitch-eventsub"); const { getClient: getYouTubeClient } = require("../../../src/services/youtube"); const { getClient: getDiscordClient } = require("../../../src/services/discord"); const { twitchEventSubManager } = require("../../../src/services/twitch-eventsub"); const { listEventTypes } = require("../../../src/services/lumi-events"); const { getPlatformLiveState } = require("../../../src/services/platform-live-state"); const { getSetting } = require("../../../src/services/settings"); function listSources() { const sources = configuredChannels().map((channel) => ({ id: `twitch:${channel}`, platform: "twitch", label: `Twitch · ${channel}`, channel_id: channel, live: getPlatformLiveState("twitch", channel)?.live ?? null, available: true, status: getPlatformLiveState("twitch", channel)?.live ? "live" : "configured" })); const youtube = getYouTubeClient(); const youtubeChannelId = youtube?.channelId || getSetting("youtube_bot_channel_id", ""); if (youtubeChannelId) sources.push({ id: `youtube:${youtubeChannelId}`, platform: "youtube", label: `YouTube · ${youtube?.channelName || youtubeChannelId}`, channel_id: youtubeChannelId, live: youtube ? Boolean(youtube.liveChatId) : null, available: Boolean(youtube), status: youtube ? (youtube.liveChatId ? "live" : "offline") : "not connected" }); const discord = getDiscordClient(); for (const guild of discord?.guilds?.cache?.values?.() || []) { for (const channel of guild.channels?.cache?.values?.() || []) { if (!isReadableTextChannel(channel, discord)) continue; sources.push({ id: `discord:${guild.id}:${channel.id}`, platform: "discord", label: `Discord · ${guild.name} / #${channel.name || channel.id}`, channel_id: channel.id, guild_id: guild.id, live: null, available: true, status: "connected" }); } } return sources.sort((left, right) => left.label.localeCompare(right.label)); } function listStatuses() { const youtube = getYouTubeClient(); const discord = getDiscordClient(); const twitch = twitchEventSubManager.getStatus(); const missing = Object.values(twitch.capabilities || {}).filter((value) => String(value).startsWith("missing ")); return [ { platform: "twitch", ...twitch, detail: `${twitch.detail}${missing.length ? ` Limited events: ${missing.join(", ")}.` : ""}` }, { platform: "youtube", state: youtube ? "connected" : "unavailable", detail: youtube ? (youtube.liveChatId ? "Receiving live chat." : "Connected; no live chat is active.") : "YouTube is not configured." }, { platform: "discord", state: discord?.isReady?.() ? "connected" : "unavailable", detail: discord?.isReady?.() ? "Discord is connected." : "Discord is not connected." } ]; } function sourceIdForChat(message) { if (message.platform === "twitch") return `twitch:${normalize(message.channel?.name || message.channel?.key || message.channel?.id)}`; if (message.platform === "youtube") { const youtube = getYouTubeClient(); return `youtube:${youtube?.channelId || message.channel?.id || normalize(message.channel?.key || message.channel?.name)}`; } if (message.platform === "discord") { const [guildId, channelId] = String(message.channel?.key || "").split("/"); return `discord:${guildId || "unknown"}:${channelId || message.channel?.id || "unknown"}`; } return `${normalize(message.platform)}:${normalize(message.channel?.id || message.channel?.key)}`; } function sourceIdForEvent(event) { const payload = event.payload || {}; if (event.type.startsWith("twitch.")) return `twitch:${normalize(payload.broadcaster_name || payload.broadcaster_id)}`; if (event.type.startsWith("youtube.")) return `youtube:${payload.channel_id || payload.live_chat_id || "current"}`; if (event.type.startsWith("discord.")) return `discord:${payload.guild_id || "unknown"}:${payload.channel_id || "*"}`; return ""; } function isReadableTextChannel(channel, discord) { const text = typeof channel?.isTextBased === "function" ? channel.isTextBased() : ["GUILD_TEXT", 0].includes(channel?.type); if (!text || !channel?.guild) return false; try { const permissions = channel.permissionsFor?.(discord.user); if (!permissions) return true; for (const flag of ["ViewChannel", "VIEW_CHANNEL", 1024n]) { try { if (permissions.has?.(flag)) return true; } catch {} } return false; } catch { return false; } } function normalize(value) { return String(value || "").trim().toLowerCase().replace(/^#/, ""); } module.exports = { listSources, listStatuses, sourceIdForChat, sourceIdForEvent, listEventTypes, isReadableTextChannel };