mirror of
https://github.com/thelounge/thelounge.git
synced 2026-07-31 15:15:52 -04:00
Updated version of #4550. Closes #337, closes #872 (#337 can't be fully addressed — tracking nick changes isn't really possible atm). <img width="253" height="149" alt="image" src="https://github.com/user-attachments/assets/8a961868-0aa9-406b-92e2-f9d4858e2d0d" /> Away support: <img width="214" alt="image" src="https://user-images.githubusercontent.com/8675906/165882385-6f928c8e-5a0b-4502-8bd3-3cd0bd70e0e0.png"> ## Summary Query windows show whether the person behind them is online, and whether they are away, using [MONITOR](https://ircv3.net/specs/extensions/monitor) rather than polling. `Network` keeps the monitor list: queries are registered in batched `MONITOR +` commands once RPL_ISUPPORT tells us the server supports it, and one at a time as new query windows open. The limit advertised in `MONITOR=<n>` is respected, with anything over it waiting for a slot; `MONITOR=0` means no monitoring at all, a bare `MONITOR` means no limit. A nick change on a monitored query swaps the target in place so the query keeps its slot. [extended-monitor](https://ircv3.net/specs/extensions/extended-monitor) is requested too, so away, nick, account and chghost updates arrive for monitored nicks we share no channel with. Away state for people we do share a channel with comes from a WHO on join. The indicator is a dot on the query in the sidebar and next to the nick in the message bubble, with a tooltip and an `img` role so it isn't colour-only, and it can be turned off under Appearance. Known limitation: a server that enforces a monitor limit it doesn't advertise will reject part of a batch with `ERR_MONLISTFULL`; the user gets a message in the lobby, but the client's own list isn't reconciled until reconnect. ## Test Plan - Unit tests for the monitor list: add, case-insensitive dedupe, queueing at the limit, removal draining the queue, batching, and the rename path. - Ran the server against a local IRCd and watched the wire: opening queries sends `MONITOR + <nick>` each, a reconnect with four open queries sends a single `MONITOR + bob,carol,dave,mallory`, 730/731 drive the online/offline state, a rename sends `MONITOR - bob` then `MONITOR + bob2`, `MONITOR=0` sends nothing at all, and a bare `MONITOR` behaves as unlimited.
79 lines
2.1 KiB
TypeScript
79 lines
2.1 KiB
TypeScript
import type {IrcEventHandler} from "../../client";
|
|
import Msg from "../../models/msg";
|
|
import {ChanType} from "../../../shared/types/chan";
|
|
import {MessageType} from "../../../shared/types/msg";
|
|
|
|
// https://ircv3.net/specs/extensions/monitor
|
|
// https://ircv3.net/specs/extensions/extended-monitor
|
|
export default <IrcEventHandler>function (irc, network) {
|
|
const client = this;
|
|
|
|
function getMonitoredQuery(nick: string) {
|
|
const channel = network.getChannel(nick);
|
|
return channel?.type === ChanType.QUERY ? channel : undefined;
|
|
}
|
|
|
|
// RPL_MONONLINE (730) / RPL_MONOFFLINE (731)
|
|
function handleMonitorStatus(nicks: string[], online: boolean) {
|
|
const changedChannels: string[] = [];
|
|
|
|
for (const nick of nicks) {
|
|
const channel = getMonitoredQuery(nick);
|
|
|
|
if (channel && channel.isOnline !== online) {
|
|
channel.isOnline = online;
|
|
changedChannels.push(channel.name);
|
|
}
|
|
}
|
|
|
|
if (changedChannels.length > 0) {
|
|
client.emit(online ? "users:online" : "users:offline", {
|
|
changedChannels,
|
|
networkId: network.uuid,
|
|
});
|
|
}
|
|
}
|
|
|
|
irc.on("users online", (data) => handleMonitorStatus(data.nicks, true));
|
|
irc.on("users offline", (data) => handleMonitorStatus(data.nicks, false));
|
|
|
|
// ERR_MONLISTFULL (734). Surface as a status message — the user didn't
|
|
// trigger MONITOR directly, so this is informational, not an error.
|
|
irc.on("irc error", function (data) {
|
|
if (data.error !== "monitor_list_full") {
|
|
return;
|
|
}
|
|
|
|
network.getLobby().pushMessage(
|
|
client,
|
|
new Msg({
|
|
text: "Your monitor list is full on this network; some queries may not show online/away status.",
|
|
}),
|
|
true
|
|
);
|
|
});
|
|
|
|
// account-notify for a monitored nick we don't share a channel with
|
|
irc.on("account", function (data) {
|
|
if (network.serverOptions.MONITOR === null) {
|
|
return;
|
|
}
|
|
|
|
const channel = getMonitoredQuery(data.nick);
|
|
|
|
if (!channel || channel.findUser(data.nick)) {
|
|
return;
|
|
}
|
|
|
|
channel.pushMessage(
|
|
client,
|
|
new Msg({
|
|
time: data.time,
|
|
type: data.account ? MessageType.LOGIN : MessageType.LOGOUT,
|
|
from: channel.getUser(data.nick),
|
|
text: data.account || "",
|
|
})
|
|
);
|
|
});
|
|
};
|