// STREAMER.BOT SETTINGS const client = new StreamerbotClient({ host: configs.streamerBotSettings.host, port: configs.streamerBotSettings.port, endpoint: configs.streamerBotSettings.endpoint, onConnect: onConnect, onDisconnect: onDisconnect, onError: onError, }); client.on("General.Custom", (data) => onCustom(data)); if (configs.userColorSettings.autoUserColor) { client.on("Twitch.ChatMessage", (data) => onChatMessage(data)); client.on("Kick.ChatMessage", (data) => onChatMessage(data)); } let taskList; let userColors = {}; function getUserColor(sectionId) { return ( userColors[`${sectionId}-color`] ?? localStorage.getItem(`${sectionId}-color`) ); } function taskColorFromSettings(sectionId) { const usernameColor = getUserColor(sectionId); if ( configs.userColorSettings.autoUserColor && usernameColor != undefined && usernameColor != null ) { return usernameColor; } return undefined; } async function onChatMessage(data) { const userColor = data.data.user.color; if (userColor != undefined && userColor != "undefined") { const userId = data.data.user.id; const key = `twitch-${userId}`; localStorage.setItem(`${key}-color`, userColor); userColors[`${key}-color`] = userColor; taskList?.updateUserColor(key, userColor); } } function onDisconnect() { showConnectionError("Connection Failed: Unable to connect to Streamer.bot"); } function onError(err) { showConnectionError( "Connection Failed: " + (err?.message || "Unknown error"), ); } function showConnectionError(message) { const existing = document.getElementById("connection-error"); if (existing) existing.remove(); const popup = document.createElement("div"); popup.id = "connection-error"; popup.textContent = message; Object.assign(popup.style, { position: "fixed", top: "20px", left: "50%", transform: "translateX(-50%)", background: "#e53935", color: "#fff", padding: "12px 24px", borderRadius: "8px", fontFamily: "'Fredoka', sans-serif", fontSize: "1.1rem", fontWeight: "700", zIndex: "9999", boxShadow: "0 4px 12px rgba(0,0,0,0.4)", textAlign: "center", }); document.body.appendChild(popup); setTimeout(() => { popup.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 300, fill: "forwards", }).onfinish = () => popup.remove(); }, 5000); } async function refresh() { const response = await client.getGlobal("rython-task-bot", true); if (response.status !== "ok" || !response.variable?.value) return; const users = JSON.parse(response.variable.value); const sections = transformToSections(users); taskList.load(sections); } async function onConnect() { taskList = new TaskList(".task-panel"); if (configs.emoteSettings.enabled) { const broadcaster = await client.getBroadcaster(); const broadcasterName = broadcaster.platforms.twitch?.broadcastUser ?? broadcaster.platforms.youtube?.broadcastUser ?? broadcaster.platforms.kick?.broadcastUser; const broadcasterId = broadcaster.platforms.twitch?.broadcastUserId ?? broadcaster.platforms.youtube?.broadcastUserId ?? broadcaster.platforms.kick?.broadcastUserId; window.emoteManager = new EmoteManager({ channelName: broadcasterName, channelId: broadcasterId, providers: configs.emoteSettings.providers, size: configs.emoteSettings.size, }); window.emoteManager.init().then(() => { if (taskList) { taskList.load(taskList.getData()); } }); } refresh(); } function transformToSections(users) { return Object.entries(users).map(([userId, userData]) => ({ id: userId, title: userData.Username, tasks: userData.Tasks.map((task) => ({ text: task.Name, done: task.Completed, focused: task.Focused, })), })); } function onCustom(payload) { const data = payload.data; if (data.source !== "rython-task-bot") { return; } if (!taskList) return; const body = data.body; const id = data.id; const username = data.username; switch (body.mode) { case "add": { const taskPayload = { text: body.task, done: body.completed, focused: body.focused, }; const color = taskColorFromSettings(id); if (color) taskPayload.color = color; taskList.addTask(id, taskPayload, username); break; } case "focus": taskList.focusTask(id, body.index); break; case "edit": taskList.editTask(id, body.index, body.task, taskColorFromSettings(id)); break; case "remove": taskList.removeTask(id, body.index); break; case "done": taskList.doneTask(id, body.index); break; case "undone": taskList.undoneTask(id, body.index); break; case "unfocus": taskList.unfocusTask(id); break; case "admindelete": taskList.removeSection(body.id); break; case "clearmydone": taskList.clearmydone(id); break; case "cleardone": taskList.cleardone(); break; case "clearall": case "clearns": case "clearold": refresh(); break; default: break; } }