Add support for MONITOR

This commit is contained in:
Simon Ser 2021-08-24 12:53:46 +02:00
parent dd67e0789e
commit e283d9c7ab
4 changed files with 82 additions and 2 deletions

View file

@ -577,6 +577,7 @@ export default class App extends Component {
} }
this.createBuffer(serverID, buf.name); this.createBuffer(serverID, buf.name);
client.who(buf.name); client.who(buf.name);
client.monitor(buf.name);
} }
let lastReceipt = this.latestReceipt(ReceiptType.DELIVERED); let lastReceipt = this.latestReceipt(ReceiptType.DELIVERED);
@ -785,6 +786,8 @@ export default class App extends Component {
case irc.RPL_TOPICWHOTIME: case irc.RPL_TOPICWHOTIME:
case irc.RPL_NAMREPLY: case irc.RPL_NAMREPLY:
case irc.RPL_ENDOFNAMES: case irc.RPL_ENDOFNAMES:
case irc.RPL_MONONLINE:
case irc.RPL_MONOFFLINE:
case "AWAY": case "AWAY":
case "SETNAME": case "SETNAME":
case "CAP": case "CAP":
@ -853,6 +856,7 @@ export default class App extends Component {
client.send({ command: "JOIN", params: [target] }); client.send({ command: "JOIN", params: [target] });
} else { } else {
client.who(target); client.who(target);
client.monitor(target);
this.createBuffer(serverID, target); this.createBuffer(serverID, target);
this.switchBuffer({ server: serverID, name: target }); this.switchBuffer({ server: serverID, name: target });
} }
@ -928,6 +932,8 @@ export default class App extends Component {
return { buffers }; return { buffers };
}); });
client.unmonitor(buf.name);
this.receipts.delete(buf.name); this.receipts.delete(buf.name);
this.saveReceipts(); this.saveReceipts();

View file

@ -54,6 +54,7 @@ export default class Client extends EventTarget {
pingIntervalID = null; pingIntervalID = null;
pendingHistory = Promise.resolve(null); pendingHistory = Promise.resolve(null);
cm = irc.CaseMapping.RFC1459; cm = irc.CaseMapping.RFC1459;
monitored = new irc.CaseMapMap(null, irc.CaseMapping.RFC1459);
whoisDB = new irc.CaseMapMap(null, irc.CaseMapping.RFC1459); whoisDB = new irc.CaseMapMap(null, irc.CaseMapping.RFC1459);
constructor(params) { constructor(params) {
@ -97,6 +98,7 @@ export default class Client extends EventTarget {
this.batches = new Map(); this.batches = new Map();
this.pendingHistory = Promise.resolve(null); this.pendingHistory = Promise.resolve(null);
this.isupport = new Map(); this.isupport = new Map();
this.monitored = new irc.CaseMapMap(null, irc.CaseMapping.RFC1459);
if (this.autoReconnect) { if (this.autoReconnect) {
if (!navigator.onLine) { if (!navigator.onLine) {
@ -199,6 +201,10 @@ export default class Client extends EventTarget {
if (changed.indexOf("CASEMAPPING") >= 0) { if (changed.indexOf("CASEMAPPING") >= 0) {
this.setCaseMapping(this.isupport.get("CASEMAPPING")); this.setCaseMapping(this.isupport.get("CASEMAPPING"));
} }
if (changed.indexOf("MONITOR") >= 0 && this.isupport.has("MONITOR")) {
let targets = Array.from(this.monitored.keys()).slice(0, this.maxMonitorTargets());
this.send({ command: "MONITOR", params: ["+", targets.join(",")] });
}
break; break;
case irc.RPL_ENDOFMOTD: case irc.RPL_ENDOFMOTD:
case irc.ERR_NOMOTD: case irc.ERR_NOMOTD:
@ -478,6 +484,7 @@ export default class Client extends EventTarget {
} }
this.whoisDB = new irc.CaseMapMap(this.whoisDB, this.cm); this.whoisDB = new irc.CaseMapMap(this.whoisDB, this.cm);
this.monitored = new irc.CaseMapMap(this.monitored, this.cm);
} }
isServer(name) { isServer(name) {
@ -683,4 +690,40 @@ export default class Client extends EventTarget {
return networks; return networks;
}); });
} }
maxMonitorTargets() {
if (!this.isupport.has("MONITOR")) {
return 0;
}
return parseInt(this.isupport.get("MONITOR"), 10);
}
monitor(target) {
if (this.monitored.has(target)) {
return;
}
this.monitored.set(target, true);
// TODO: add poll-based fallback when MONITOR is not supported
if (this.monitored.size + 1 > this.maxMonitorTargets()) {
return;
}
this.send({ command: "MONITOR", params: ["+", target] });
}
unmonitor(target) {
if (!this.monitored.has(target)) {
return;
}
this.monitored.delete(target);
if (!this.isupport.has("MONITOR")) {
return;
}
this.send({ command: "MONITOR", params: ["-", target] });
}
} }

View file

@ -42,6 +42,12 @@ export const ERR_UNAVAILRESOURCE = "437";
// Other // Other
export const RPL_QUIETLIST = "728"; export const RPL_QUIETLIST = "728";
export const RPL_ENDOFQUIETLIST = "729"; export const RPL_ENDOFQUIETLIST = "729";
// IRCv3 MONITOR: https://ircv3.net/specs/extensions/monitor
export const RPL_MONONLINE = "730";
export const RPL_MONOFFLINE = "731";
export const RPL_MONLIST = "732";
export const RPL_ENDOFMONLIST = "733";
export const ERR_MONLISTFULL = "734";
// IRCv3 SASL: https://ircv3.net/specs/extensions/sasl-3.1 // IRCv3 SASL: https://ircv3.net/specs/extensions/sasl-3.1
export const RPL_LOGGEDIN = "900"; export const RPL_LOGGEDIN = "900";
export const RPL_LOGGEDOUT = "901"; export const RPL_LOGGEDOUT = "901";
@ -107,7 +113,7 @@ export function formatTags(tags) {
return l.join(";"); return l.join(";");
} }
function parsePrefix(s) { export function parsePrefix(s) {
let prefix = { let prefix = {
name: null, name: null,
user: null, user: null,
@ -306,6 +312,7 @@ export function isError(cmd) {
case ERR_SASLTOOLONG: case ERR_SASLTOOLONG:
case ERR_SASLABORTED: case ERR_SASLABORTED:
case ERR_SASLALREADY: case ERR_SASLALREADY:
case ERR_MONLISTFULL:
return true; return true;
case "FAIL": case "FAIL":
return true; return true;

View file

@ -293,7 +293,7 @@ export const State = {
return; return;
} }
let target, channel, topic; let target, channel, topic, targets;
switch (msg.command) { switch (msg.command) {
case irc.RPL_MYINFO: case irc.RPL_MYINFO:
// TODO: parse available modes // TODO: parse available modes
@ -437,6 +437,30 @@ export const State = {
return { members }; return { members };
}); });
case irc.RPL_MONONLINE:
targets = msg.params[1].split(",");
for (let target of targets) {
let prefix = irc.parsePrefix(target);
let update = updateBuffer(prefix.name, (buf) => {
return { offline: false };
});
state = { ...state, ...update };
}
return state;
case irc.RPL_MONOFFLINE:
targets = msg.params[1].split(",");
for (let target of targets) {
let prefix = irc.parsePrefix(target);
let update = updateBuffer(prefix.name, (buf) => {
return { offline: true };
});
state = { ...state, ...update };
}
return state;
} }
}, },
addMessage(state, msg, bufID) { addMessage(state, msg, bufID) {