1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
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.classList.add("author");
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);
});
}
|