mirror of
https://github.com/thelounge/thelounge.git
synced 2026-08-02 16:16:08 -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.
99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
import {IrcEventHandler} from "../../client";
|
|
|
|
import Msg from "../../models/msg";
|
|
import Config from "../../config";
|
|
import {MessageType} from "../../../shared/types/msg";
|
|
|
|
export default <IrcEventHandler>function (irc, network) {
|
|
const client = this;
|
|
|
|
irc.on("irc error", function (data) {
|
|
if (data.error === "monitor_list_full") {
|
|
return;
|
|
}
|
|
|
|
const msg = new Msg({
|
|
type: MessageType.ERROR,
|
|
error: data.error,
|
|
showInActive: true,
|
|
nick: data.nick,
|
|
channel: data.channel,
|
|
reason: data.reason,
|
|
command: data.command,
|
|
});
|
|
|
|
let target = network.getLobby();
|
|
|
|
// If this error is channel specific and a channel
|
|
// with this name exists, put this error in that channel
|
|
if (data.channel) {
|
|
const channel = network.getChannel(data.channel);
|
|
|
|
if (typeof channel !== "undefined") {
|
|
target = channel;
|
|
msg.showInActive = false;
|
|
}
|
|
}
|
|
|
|
target.pushMessage(client, msg, true);
|
|
});
|
|
|
|
irc.on("nick in use", function (data) {
|
|
let message = data.nick + ": " + (data.reason || "Nickname is already in use.");
|
|
|
|
if (irc.connection.registered === false && !Config.values.public) {
|
|
message += " An attempt to use it will be made when this nick quits.";
|
|
|
|
// Clients usually get nick in use on connect when reconnecting to a network
|
|
// after a network failure (like ping timeout), and as a result of that,
|
|
// TL will append a random number to the nick.
|
|
// keepNick will try to set the original nick name back if it sees a QUIT for that nick.
|
|
network.keepNick = irc.user.nick;
|
|
}
|
|
|
|
const lobby = network.getLobby();
|
|
const msg = new Msg({
|
|
type: MessageType.ERROR,
|
|
text: message,
|
|
showInActive: true,
|
|
});
|
|
lobby.pushMessage(client, msg, true);
|
|
|
|
if (irc.connection.registered === false) {
|
|
const nickLen = parseInt(network.irc.network.options.NICKLEN, 10) || 16;
|
|
|
|
const random = (data.nick || irc.user.nick) + Math.floor(Math.random() * 10);
|
|
|
|
// Safeguard nick changes up to allowed length
|
|
// Some servers may send "nick in use" error even for randomly generated nicks
|
|
if (random.length <= nickLen) {
|
|
irc.changeNick(random);
|
|
}
|
|
}
|
|
|
|
client.emit("nick", {
|
|
network: network.uuid,
|
|
nick: irc.user.nick,
|
|
});
|
|
});
|
|
|
|
irc.on("nick invalid", function (data) {
|
|
const lobby = network.getLobby();
|
|
const msg = new Msg({
|
|
type: MessageType.ERROR,
|
|
text: data.nick + ": " + (data.reason || "Nickname is invalid."),
|
|
showInActive: true,
|
|
});
|
|
lobby.pushMessage(client, msg, true);
|
|
|
|
if (irc.connection.registered === false) {
|
|
irc.changeNick(Config.getDefaultNick());
|
|
}
|
|
|
|
client.emit("nick", {
|
|
network: network.uuid,
|
|
nick: irc.user.nick,
|
|
});
|
|
});
|
|
};
|