2020-08-25 05:42:40 -04:00
|
|
|
// RFC 1459
|
2020-06-14 08:50:59 -04:00
|
|
|
export const RPL_WELCOME = "001";
|
2020-06-26 06:08:14 -04:00
|
|
|
export const RPL_YOURHOST = "002";
|
|
|
|
export const RPL_CREATED = "003";
|
|
|
|
export const RPL_MYINFO = "004";
|
2021-01-22 05:34:04 -05:00
|
|
|
export const RPL_ISUPPORT = "005";
|
2021-05-27 16:02:07 -04:00
|
|
|
export const RPL_WHOISUSER = "311";
|
|
|
|
export const RPL_WHOISSERVER = "312";
|
|
|
|
export const RPL_WHOISOPERATOR = "313";
|
|
|
|
export const RPL_WHOISIDLE = "317";
|
|
|
|
export const RPL_ENDOFWHOIS = "318";
|
|
|
|
export const RPL_WHOISCHANNELS = "319";
|
2020-06-26 06:00:10 -04:00
|
|
|
export const RPL_ENDOFWHO = "315";
|
2020-08-03 12:59:54 -04:00
|
|
|
export const RPL_NOTOPIC = "331";
|
2020-06-14 08:50:59 -04:00
|
|
|
export const RPL_TOPIC = "332";
|
2020-09-03 05:36:08 -04:00
|
|
|
export const RPL_TOPICWHOTIME = "333";
|
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";
|
2021-06-03 04:11:48 -04:00
|
|
|
export const RPL_BANLIST = "367";
|
|
|
|
export const RPL_ENDOFBANLIST = "368";
|
2021-05-28 04:46:32 -04:00
|
|
|
export const RPL_MOTD = "372";
|
|
|
|
export const RPL_MOTDSTART = "375";
|
2021-05-27 09:14:03 -04:00
|
|
|
export const RPL_ENDOFMOTD = "376";
|
2021-05-27 16:02:07 -04:00
|
|
|
export const ERR_NOSUCHNICK = "401";
|
2020-06-29 08:29:31 -04:00
|
|
|
export const ERR_NOMOTD = "422";
|
2020-08-25 05:42:40 -04:00
|
|
|
export const ERR_ERRONEUSNICKNAME = "432";
|
|
|
|
export const ERR_NICKNAMEINUSE = "433";
|
|
|
|
export const ERR_NICKCOLLISION = "436";
|
|
|
|
export const ERR_NOPERMFORHOST = "463";
|
2020-06-14 08:50:59 -04:00
|
|
|
export const ERR_PASSWDMISMATCH = "464";
|
2020-08-25 05:42:40 -04:00
|
|
|
export const ERR_YOUREBANNEDCREEP = "465";
|
|
|
|
// RFC 2812
|
|
|
|
export const ERR_UNAVAILRESOURCE = "437";
|
|
|
|
// IRCv3 SASL: 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
|
|
|
|
2021-06-03 07:18:28 -04:00
|
|
|
export const STD_MEMBERSHIPS = "~&@%+";
|
2021-06-04 13:17:39 -04:00
|
|
|
export const STD_CHANTYPES = "#&+!";
|
2021-05-28 11:45:27 -04:00
|
|
|
export const STD_CHANMODES = "beI,k,l,imnst";
|
2020-06-24 09:52:33 -04:00
|
|
|
|
2020-06-28 09:40:57 -04:00
|
|
|
const tagEscapeMap = {
|
2020-06-12 12:55:57 -04:00
|
|
|
";": "\\:",
|
|
|
|
" ": "\\s",
|
|
|
|
"\\": "\\\\",
|
|
|
|
"\r": "\\r",
|
|
|
|
"\n": "\\n",
|
|
|
|
};
|
|
|
|
|
2020-06-28 09:40:57 -04:00
|
|
|
const tagUnescapeMap = Object.fromEntries(Object.entries(tagEscapeMap).map(([from, to]) => [to, from]));
|
|
|
|
|
|
|
|
function escapeTag(s) {
|
2021-03-09 11:40:57 -05:00
|
|
|
return String(s).replace(/[; \\\r\n]/g, (ch) => tagEscapeMap[ch]);
|
2020-06-28 09:40:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function unescapeTag(s) {
|
2021-03-09 11:40:57 -05:00
|
|
|
return s.replace(/\\[:s\\rn]/g, (seq) => tagUnescapeMap[seq]);
|
2020-06-28 09:40:57 -04:00
|
|
|
}
|
|
|
|
|
2021-01-22 05:43:47 -05:00
|
|
|
export function parseTags(s) {
|
2020-06-12 12:55:57 -04:00
|
|
|
var tags = {};
|
2021-03-09 11:31:12 -05:00
|
|
|
s.split(";").forEach((s) => {
|
2020-06-12 12:55:57 -04:00
|
|
|
if (!s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var parts = s.split("=", 2);
|
|
|
|
var k = parts[0];
|
2021-03-09 11:31:12 -05:00
|
|
|
var v = null;
|
|
|
|
if (parts.length == 2) {
|
|
|
|
v = unescapeTag(parts[1]);
|
|
|
|
if (v.endsWith("\\")) {
|
|
|
|
v = v.slice(0, v.length - 1)
|
|
|
|
}
|
2020-06-18 11:55:47 -04:00
|
|
|
}
|
2020-06-12 12:55:57 -04:00
|
|
|
tags[k] = v;
|
|
|
|
});
|
|
|
|
return tags;
|
|
|
|
}
|
|
|
|
|
2021-01-22 05:43:47 -05:00
|
|
|
export function formatTags(tags) {
|
2020-06-12 12:55:57 -04:00
|
|
|
var l = [];
|
|
|
|
for (var k in tags) {
|
2021-03-09 11:31:12 -05:00
|
|
|
if (tags[k] === undefined || tags[k] === null) {
|
|
|
|
l.push(k);
|
|
|
|
continue;
|
|
|
|
}
|
2020-06-28 09:40:57 -04:00
|
|
|
var v = escapeTag(tags[k]);
|
2020-06-12 12:55:57 -04:00
|
|
|
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 = "";
|
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
|
|
|
|
2021-06-03 07:18:28 -04:00
|
|
|
/** Split a prefix and a name out of a target. */
|
2021-06-03 07:31:43 -04:00
|
|
|
export function parseTargetPrefix(s, allowedPrefixes = STD_MEMBERSHIPS) {
|
2020-06-10 13:51:54 -04:00
|
|
|
var i;
|
|
|
|
for (i = 0; i < s.length; i++) {
|
2021-06-03 07:31:43 -04:00
|
|
|
if (allowedPrefixes.indexOf(s[i]) < 0) {
|
2020-06-10 13:51:54 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
prefix: s.slice(0, i),
|
2021-06-03 07:18:28 -04:00
|
|
|
name: s.slice(i),
|
2020-06-10 13:51:54 -04:00
|
|
|
};
|
|
|
|
}
|
2020-06-29 05:08:47 -04:00
|
|
|
|
|
|
|
const alphaNum = (() => {
|
|
|
|
try {
|
|
|
|
return new RegExp(/^\p{L}$/, "u");
|
|
|
|
} catch (e) {
|
|
|
|
return new RegExp(/^[a-zA-Z0-9]$/, "u");
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
|
|
|
|
function isWordBoundary(ch) {
|
|
|
|
switch (ch) {
|
|
|
|
case "-":
|
|
|
|
case "_":
|
|
|
|
case "|":
|
|
|
|
return false;
|
|
|
|
case "\u00A0":
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return !alphaNum.test(ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-06 09:52:58 -04:00
|
|
|
export function isHighlight(msg, nick, cm) {
|
2020-08-13 05:44:41 -04:00
|
|
|
if (msg.command != "PRIVMSG" && msg.command != "NOTICE") {
|
|
|
|
return false;
|
|
|
|
}
|
2021-06-06 09:52:58 -04:00
|
|
|
|
|
|
|
nick = cm(nick);
|
|
|
|
|
|
|
|
if (msg.prefix && cm(msg.prefix.name) == nick) {
|
2020-08-13 05:44:41 -04:00
|
|
|
return false; // Our own messages aren't highlights
|
|
|
|
}
|
|
|
|
|
2021-06-06 09:52:58 -04:00
|
|
|
var text = cm(msg.params[1]);
|
2020-06-29 05:08:47 -04:00
|
|
|
while (true) {
|
|
|
|
var i = text.indexOf(nick);
|
|
|
|
if (i < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect word boundaries
|
|
|
|
var left = "\x00", right = "\x00";
|
|
|
|
if (i > 0) {
|
|
|
|
left = text[i - 1];
|
|
|
|
}
|
|
|
|
if (i < text.length) {
|
|
|
|
right = text[i + nick.length];
|
|
|
|
}
|
|
|
|
if (isWordBoundary(left) && isWordBoundary(right)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
text = text.slice(i + nick.length);
|
|
|
|
}
|
|
|
|
}
|
2020-06-29 08:29:31 -04:00
|
|
|
|
|
|
|
export function isError(cmd) {
|
|
|
|
if (cmd >= "400" && cmd <= "568") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
switch (cmd) {
|
|
|
|
case ERR_NICKLOCKED:
|
|
|
|
case ERR_SASLFAIL:
|
|
|
|
case ERR_SASLTOOLONG:
|
|
|
|
case ERR_SASLABORTED:
|
|
|
|
case ERR_SASLALREADY:
|
|
|
|
return true;
|
2020-07-15 05:07:28 -04:00
|
|
|
case "FAIL":
|
|
|
|
return true;
|
2020-06-29 08:29:31 -04:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-06-29 03:06:47 -04:00
|
|
|
|
|
|
|
export function formatDate(date) {
|
|
|
|
// ISO 8601
|
|
|
|
var YYYY = date.getUTCFullYear().toString().padStart(4, "0");
|
|
|
|
var MM = (date.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
|
|
var DD = date.getUTCDate().toString().padStart(2, "0");
|
|
|
|
var hh = date.getUTCHours().toString().padStart(2, "0");
|
|
|
|
var mm = date.getUTCMinutes().toString().padStart(2, "0");
|
|
|
|
var ss = date.getUTCSeconds().toString().padStart(2, "0");
|
|
|
|
var sss = date.getUTCMilliseconds().toString().padStart(3, "0");
|
|
|
|
return `${YYYY}-${MM}-${DD}T${hh}:${mm}:${ss}.${sss}Z`;
|
|
|
|
}
|
2020-08-13 10:04:39 -04:00
|
|
|
|
|
|
|
export function parseCTCP(msg) {
|
|
|
|
if (msg.command != "PRIVMSG" && msg.command != "NOTICE") {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
var text = msg.params[1];
|
|
|
|
if (!text.startsWith("\x01")) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
text = text.slice(1);
|
|
|
|
if (text.endsWith("\x01")) {
|
|
|
|
text = text.slice(0, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
var ctcp;
|
|
|
|
var i = text.indexOf(" ");
|
|
|
|
if (i >= 0) {
|
|
|
|
ctcp = { command: text.slice(0, i), param: text.slice(i + 1) };
|
|
|
|
} else {
|
|
|
|
ctcp = { command: text, param: "" };
|
|
|
|
}
|
|
|
|
ctcp.command = ctcp.command.toUpperCase();
|
|
|
|
return ctcp;
|
|
|
|
}
|
2021-01-22 05:34:04 -05:00
|
|
|
|
|
|
|
export function parseISUPPORT(tokens, params) {
|
2021-05-27 09:13:32 -04:00
|
|
|
var changed = [];
|
2021-01-22 05:34:04 -05:00
|
|
|
tokens.forEach((tok) => {
|
|
|
|
if (tok.startsWith("-")) {
|
|
|
|
var k = tok.slice(1);
|
|
|
|
params.delete(k.toUpperCase());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var i = tok.indexOf("=");
|
|
|
|
var k = tok, v = "";
|
|
|
|
if (i >= 0) {
|
|
|
|
k = tok.slice(0, i);
|
|
|
|
v = tok.slice(i + 1);
|
|
|
|
}
|
2021-05-27 09:13:32 -04:00
|
|
|
|
|
|
|
k = k.toUpperCase();
|
|
|
|
|
|
|
|
params.set(k, v);
|
|
|
|
changed.push(k);
|
2021-01-22 05:34:04 -05:00
|
|
|
});
|
2021-05-27 09:13:32 -04:00
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const CaseMapping = {
|
|
|
|
ASCII(str) {
|
|
|
|
var out = "";
|
|
|
|
for (var i = 0; i < str.length; i++) {
|
|
|
|
var ch = str[i];
|
|
|
|
if ("A" <= ch && ch <= "Z") {
|
|
|
|
ch = ch.toLowerCase();
|
|
|
|
}
|
|
|
|
out += ch;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
},
|
|
|
|
|
|
|
|
RFC1459(str) {
|
|
|
|
var out = "";
|
|
|
|
for (var i = 0; i < str.length; i++) {
|
|
|
|
var ch = str[i];
|
|
|
|
if ("A" <= ch && ch <= "Z") {
|
|
|
|
ch = ch.toLowerCase();
|
|
|
|
} else if (ch == "{") {
|
|
|
|
ch = "[";
|
|
|
|
} else if (ch == "}") {
|
|
|
|
ch = "]";
|
|
|
|
} else if (ch == "\\") {
|
|
|
|
ch = "|";
|
|
|
|
} else if (ch == "~") {
|
|
|
|
ch = "^";
|
|
|
|
}
|
|
|
|
out += ch;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
},
|
|
|
|
|
|
|
|
RFC1459Strict(str) {
|
|
|
|
var out = "";
|
|
|
|
for (var i = 0; i < str.length; i++) {
|
|
|
|
var ch = str[i];
|
|
|
|
if ("A" <= ch && ch <= "Z") {
|
|
|
|
ch = ch.toLowerCase();
|
|
|
|
} else if (ch == "{") {
|
|
|
|
ch = "[";
|
|
|
|
} else if (ch == "}") {
|
|
|
|
ch = "]";
|
|
|
|
} else if (ch == "\\") {
|
|
|
|
ch = "|";
|
|
|
|
}
|
|
|
|
out += ch;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
},
|
|
|
|
|
|
|
|
byName(name) {
|
|
|
|
switch (name) {
|
|
|
|
case "ascii":
|
|
|
|
return CaseMapping.ASCII;
|
|
|
|
case "rfc1459":
|
|
|
|
return CaseMapping.RFC1459;
|
|
|
|
case "rfc1459-strict":
|
|
|
|
return CaseMapping.RFC1459Strict;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
function createIterator(next) {
|
|
|
|
var it = { next };
|
|
|
|
// Not defining this can lead to surprises when feeding the iterator
|
|
|
|
// to e.g. Array.from
|
|
|
|
it[Symbol.iterator] = () => it;
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
function mapIterator(it, f) {
|
|
|
|
return createIterator(() => {
|
|
|
|
var { value, done } = it.next();
|
|
|
|
if (done) {
|
|
|
|
return { done: true };
|
|
|
|
}
|
|
|
|
return { value: f(value), done: false };
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export class CaseMapMap {
|
|
|
|
caseMap = null;
|
|
|
|
map = null;
|
|
|
|
|
|
|
|
constructor(iterable, cm) {
|
|
|
|
if ((iterable instanceof CaseMapMap) && (iterable.caseMap === cm || !cm)) {
|
|
|
|
// Fast-path if we're just cloning another CaseMapMap
|
|
|
|
this.caseMap = iterable.caseMap;
|
|
|
|
this.map = new Map(iterable.map);
|
|
|
|
} else {
|
|
|
|
if (!cm) {
|
|
|
|
throw new Error("Missing case-mapping when creating CaseMapMap");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.caseMap = cm;
|
|
|
|
this.map = new Map();
|
|
|
|
|
|
|
|
if (iterable) {
|
|
|
|
for (var [key, value] of iterable) {
|
|
|
|
this.set(key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get size() {
|
|
|
|
return this.map.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
has(key) {
|
|
|
|
return this.map.has(this.caseMap(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
get(key) {
|
|
|
|
var kv = this.map.get(this.caseMap(key));
|
|
|
|
if (kv) {
|
|
|
|
return kv.value;
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
set(key, value) {
|
|
|
|
this.map.set(this.caseMap(key), { key, value });
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(key) {
|
|
|
|
this.map.delete(this.caseMap(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
entries() {
|
|
|
|
var it = this.map.values();
|
|
|
|
return mapIterator(it, (kv) => {
|
|
|
|
return [kv.key, kv.value];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
keys() {
|
|
|
|
var it = this.map.values();
|
|
|
|
return mapIterator(it, (kv) => {
|
|
|
|
return kv.key;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
values() {
|
|
|
|
var it = this.map.values();
|
|
|
|
return mapIterator(it, (kv) => {
|
|
|
|
return kv.value;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Symbol.iterator]() {
|
|
|
|
return this.entries();
|
|
|
|
}
|
2021-01-22 05:34:04 -05:00
|
|
|
}
|
2021-05-28 11:45:27 -04:00
|
|
|
|
2021-06-03 07:18:28 -04:00
|
|
|
/** Parse the ISUPPORT PREFIX token */
|
|
|
|
export function parseMembershipModes(str) {
|
2021-05-28 11:45:27 -04:00
|
|
|
if (str[0] !== "(") {
|
|
|
|
throw new Error("malformed ISUPPORT PREFIX value: expected opening parenthesis");
|
|
|
|
}
|
|
|
|
|
|
|
|
var sep = str.indexOf(")");
|
|
|
|
if (sep < 0) {
|
|
|
|
throw new Error("malformed ISUPPORT PREFIX value: expected closing parenthesis");
|
|
|
|
}
|
|
|
|
|
|
|
|
var n = str.length - sep - 1;
|
|
|
|
var memberships = [];
|
|
|
|
for (var i = 0; i < n; i++) {
|
|
|
|
var mode = str[i + 1];
|
|
|
|
var prefix = str[sep + i + 1];
|
|
|
|
memberships.push({ mode, prefix });
|
|
|
|
}
|
|
|
|
return memberships;
|
|
|
|
}
|
2021-06-04 13:45:51 -04:00
|
|
|
|
|
|
|
export function findBatchByType(msg, type) {
|
|
|
|
var batch = msg.batch;
|
|
|
|
while (batch) {
|
|
|
|
if (batch.type === type) {
|
|
|
|
return batch;
|
|
|
|
}
|
|
|
|
batch = batch.parent;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2021-06-04 14:38:01 -04:00
|
|
|
|
|
|
|
export function getMessageLabel(msg) {
|
|
|
|
if (msg.tags.label) {
|
|
|
|
return msg.tags.label;
|
|
|
|
}
|
|
|
|
|
|
|
|
var batch = msg.batch;
|
|
|
|
while (batch) {
|
|
|
|
if (batch.tags.label) {
|
|
|
|
return batch.tags.label;
|
|
|
|
}
|
|
|
|
batch = batch.parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|