function parseIRCMessage(line) { line = line.replace(/\r?\n$/, ''); let tags = {}; let prefix = null; let nick = null; let command = null; const params = []; // tags if (line[0] === '@') { const end = line.indexOf(' '); const rawTags = line.slice(1, end); for (const tag of rawTags.split(';')) { const [k, v = true] = tag.split('='); tags[k] = v === true ? true : v; } line = line.slice(end + 1); } // prefix if (line[0] === ':') { const end = line.indexOf(' '); prefix = line.slice(1, end); const bang = prefix.indexOf('!'); if (bang !== -1) nick = prefix.slice(0, bang); line = line.slice(end + 1); } // command and params const parts = line.split(' '); command = parts.shift(); for (let i = 0; i < parts.length; i++) { if (parts[i][0] === ':') { params.push(parts.slice(i).join(' ').slice(1)); break; } if (parts[i].length) params.push(parts[i]); } return { tags, prefix, nick, command, params }; } function addMessage(message) { const messages = document.getElementById("messages"); if (!messages || message.command != "PRIVMSG") { return; } // msg base const elem = document.createElement("p"); elem.classList.add("message"); // username const usernameElem = document.createElement("span"); elem.append(usernameElem); if ("color" in message.tags) { usernameElem.style.color = message.tags["color"]; } usernameElem.textContent = `${message.nick}:`; // message text elem.innerHTML += ' ' + message.params[1]; messages.append(elem); window.scrollTo(0, document.body.scrollHeight); } function addSystemMessage(message) { addMessage({ "params": ["#", message], "nick": "System", "prefix": "system", "tags": {}, "command": "PRIVMSG", }); } function connectToChat(host, nick, password, room) { const socket = new WebSocket(host); socket.addEventListener("open", () => { socket.send(`NICK ${nick}`); socket.send(`PASS ${password}`); socket.send("CAP REQ :twitch.tv/tags"); socket.send(`JOIN #${room}`); addSystemMessage("Connected to the chat!"); }); socket.addEventListener("message", (e) => { const lines = e.data.split("\r\n"); for (const line of lines) { const l = line.trim(); if (l.length == 0) { continue; } addMessage(parseIRCMessage(l)); } }); socket.addEventListener("close", (e) => { addMessage("Chat", `Disconnected from the chat: ${e.reason}`); addMessage("Chat", "Reconnecting to the chat in 5 seconds..."); setTimeout(() => connectToChat(host, nick, password, room), 5000); }); }