2020-06-14 08:50:59 -04:00
|
|
|
export const RPL_WELCOME = "001";
|
2020-06-26 06:00:10 -04:00
|
|
|
export const RPL_ENDOFWHO = "315";
|
2020-06-14 08:50:59 -04:00
|
|
|
export const RPL_TOPIC = "332";
|
2020-06-26 06:00:10 -04:00
|
|
|
export const RPL_WHOREPLY = "352";
|
2020-06-14 08:50:59 -04:00
|
|
|
export const RPL_NAMREPLY = "353";
|
|
|
|
export const RPL_ENDOFNAMES = "366";
|
|
|
|
export const ERR_PASSWDMISMATCH = "464";
|
2020-06-12 12:17:49 -04:00
|
|
|
// https://ircv3.net/specs/extensions/sasl-3.1
|
2020-06-14 08:50:59 -04:00
|
|
|
export const RPL_LOGGEDIN = "900";
|
|
|
|
export const RPL_LOGGEDOUT = "901";
|
|
|
|
export const ERR_NICKLOCKED = "902";
|
|
|
|
export const RPL_SASLSUCCESS = "903";
|
|
|
|
export const ERR_SASLFAIL = "904";
|
|
|
|
export const ERR_SASLTOOLONG = "905";
|
|
|
|
export const ERR_SASLABORTED = "906";
|
|
|
|
export const ERR_SASLALREADY = "907";
|
2020-06-06 04:06:07 -04:00
|
|
|
|
2020-06-24 09:52:33 -04:00
|
|
|
export const STD_CHANNEL_TYPES = "#&+!";
|
|
|
|
|
2020-06-12 12:55:57 -04:00
|
|
|
var tagsEscape = {
|
|
|
|
";": "\\:",
|
|
|
|
" ": "\\s",
|
|
|
|
"\\": "\\\\",
|
|
|
|
"\r": "\\r",
|
|
|
|
"\n": "\\n",
|
|
|
|
};
|
|
|
|
|
|
|
|
function parseTags(s) {
|
|
|
|
var tags = {};
|
|
|
|
s.split(";").forEach(function(s) {
|
|
|
|
if (!s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var parts = s.split("=", 2);
|
|
|
|
if (parts.length != 2) {
|
|
|
|
throw new Error("expected an equal sign in tag");
|
|
|
|
}
|
|
|
|
var k = parts[0];
|
|
|
|
var v = parts[1];
|
|
|
|
for (var ch in tagsEscape) {
|
|
|
|
v = v.replaceAll(tagsEscape[ch], ch);
|
|
|
|
}
|
2020-06-18 11:55:47 -04:00
|
|
|
if (v.endsWith("\\")) {
|
|
|
|
v = v.slice(0, v.length - 1)
|
|
|
|
}
|
2020-06-12 12:55:57 -04:00
|
|
|
tags[k] = v;
|
|
|
|
});
|
|
|
|
return tags;
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatTags(tags) {
|
|
|
|
var l = [];
|
|
|
|
for (var k in tags) {
|
|
|
|
var v = tags[k];
|
|
|
|
for (var ch in tagsEscape) {
|
|
|
|
v = v.replaceAll(ch, tagsEscape[ch]);
|
|
|
|
}
|
|
|
|
l.push(k + "=" + v);
|
|
|
|
}
|
|
|
|
return l.join(";");
|
|
|
|
}
|
|
|
|
|
2020-04-24 13:01:02 -04:00
|
|
|
function parsePrefix(s) {
|
|
|
|
var prefix = {
|
|
|
|
name: null,
|
|
|
|
user: null,
|
|
|
|
host: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
var i = s.indexOf("@");
|
|
|
|
if (i < 0) {
|
|
|
|
prefix.name = s;
|
|
|
|
return prefix;
|
|
|
|
}
|
|
|
|
prefix.host = s.slice(i + 1);
|
|
|
|
s = s.slice(0, i);
|
|
|
|
|
|
|
|
var i = s.indexOf("!");
|
|
|
|
if (i < 0) {
|
|
|
|
prefix.name = s;
|
|
|
|
return prefix;
|
|
|
|
}
|
|
|
|
prefix.name = s.slice(0, i);
|
|
|
|
prefix.user = s.slice(i + 1);
|
|
|
|
return prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatPrefix(prefix) {
|
|
|
|
if (!prefix.host) {
|
|
|
|
return prefix.name;
|
|
|
|
}
|
|
|
|
if (!prefix.user) {
|
|
|
|
return prefix.name + "@" + prefix.host;
|
|
|
|
}
|
|
|
|
return prefix.name + "!" + prefix.user + "@" + prefix.host;
|
|
|
|
}
|
|
|
|
|
2020-06-14 08:50:59 -04:00
|
|
|
export function parseMessage(s) {
|
2020-04-24 13:01:02 -04:00
|
|
|
if (s.endsWith("\r\n")) {
|
|
|
|
s = s.slice(0, s.length - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
var msg = {
|
2020-06-12 12:55:57 -04:00
|
|
|
tags: {},
|
2020-04-24 13:01:02 -04:00
|
|
|
prefix: null,
|
|
|
|
command: null,
|
|
|
|
params: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
if (s.startsWith("@")) {
|
2020-06-12 12:55:57 -04:00
|
|
|
var i = s.indexOf(" ");
|
|
|
|
if (i < 0) {
|
|
|
|
throw new Error("expected a space after tags");
|
|
|
|
}
|
|
|
|
msg.tags = parseTags(s.slice(1, i));
|
|
|
|
s = s.slice(i + 1);
|
2020-04-24 13:01:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (s.startsWith(":")) {
|
|
|
|
var i = s.indexOf(" ");
|
|
|
|
if (i < 0) {
|
|
|
|
throw new Error("expected a space after prefix");
|
|
|
|
}
|
|
|
|
msg.prefix = parsePrefix(s.slice(1, i));
|
|
|
|
s = s.slice(i + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
var i = s.indexOf(" ");
|
|
|
|
if (i < 0) {
|
|
|
|
msg.command = s;
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
msg.command = s.slice(0, i);
|
|
|
|
s = s.slice(i + 1);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (s.startsWith(":")) {
|
|
|
|
msg.params.push(s.slice(1));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = s.indexOf(" ");
|
|
|
|
if (i < 0) {
|
|
|
|
msg.params.push(s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.params.push(s.slice(0, i));
|
|
|
|
s = s.slice(i + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2020-06-14 08:50:59 -04:00
|
|
|
export function formatMessage(msg) {
|
2020-04-24 13:01:02 -04:00
|
|
|
var s = "";
|
|
|
|
// TODO: format tags
|
2020-06-12 12:55:57 -04:00
|
|
|
if (msg.tags && Object.keys(msg.tags).length > 0) {
|
|
|
|
s += "@" + formatTags(msg.tags) + " ";
|
|
|
|
}
|
2020-04-24 13:01:02 -04:00
|
|
|
if (msg.prefix) {
|
|
|
|
s += ":" + formatPrefix(msg.prefix) + " ";
|
|
|
|
}
|
|
|
|
s += msg.command;
|
|
|
|
if (msg.params && msg.params.length > 0) {
|
|
|
|
var last = msg.params[msg.params.length - 1];
|
|
|
|
if (msg.params.length > 1) {
|
|
|
|
s += " " + msg.params.slice(0, -1).join(" ");
|
|
|
|
}
|
|
|
|
s += " :" + last;
|
|
|
|
}
|
|
|
|
s += "\r\n";
|
|
|
|
return s;
|
|
|
|
}
|
2020-06-10 13:51:54 -04:00
|
|
|
|
2020-06-14 08:50:59 -04:00
|
|
|
export function parseMembership(s) {
|
2020-06-10 13:51:54 -04:00
|
|
|
// TODO: use the PREFIX token from RPL_ISUPPORT
|
|
|
|
const STD_MEMBERSHIPS = "~&@%+";
|
|
|
|
|
|
|
|
var i;
|
|
|
|
for (i = 0; i < s.length; i++) {
|
|
|
|
if (STD_MEMBERSHIPS.indexOf(s[i]) < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
prefix: s.slice(0, i),
|
|
|
|
nick: s.slice(i),
|
|
|
|
};
|
|
|
|
}
|