Add support for WHOX

This allows querying the account of the user.
This commit is contained in:
Simon Ser 2021-09-21 16:58:00 +02:00
parent 0b32d9295a
commit 329f9063d0
4 changed files with 105 additions and 17 deletions

View file

@ -585,8 +585,7 @@ export default class App extends Component {
continue;
}
this.createBuffer(serverID, buf.name);
client.who(buf.name);
client.monitor(buf.name);
this.whoUserBuffer(buf.name, serverID);
}
let lastReceipt = this.latestReceipt(ReceiptType.DELIVERED);
@ -853,6 +852,15 @@ export default class App extends Component {
});
}
whoUserBuffer(target, serverID) {
let client = this.clients.get(serverID);
client.who(target, {
fields: ["flags", "hostname", "nick", "realname", "username", "account"],
});
client.monitor(target);
}
open(target, serverID) {
if (!serverID) {
serverID = State.getActiveServerID(this.state);
@ -865,8 +873,7 @@ export default class App extends Component {
this.switchToChannel = target;
client.send({ command: "JOIN", params: [target] });
} else {
client.who(target);
client.monitor(target);
this.whoUserBuffer(target, serverID);
this.createBuffer(serverID, target);
this.switchBuffer({ server: serverID, name: target });
}

View file

@ -30,7 +30,21 @@ const NORMAL_CLOSURE = 1000;
const GOING_AWAY = 1001;
const UNSUPPORTED_DATA = 1003;
// See https://github.com/quakenet/snircd/blob/master/doc/readme.who
// Sorted by order of appearance in RPL_WHOSPCRPL
const WHOX_FIELDS = {
"channel": "c",
"username": "u",
"hostname": "h",
"server": "s",
"nick": "n",
"flags": "f",
"account": "a",
"realname": "r",
};
let lastLabel = 0;
let lastWhoxToken = 0;
export default class Client extends EventTarget {
static Status = {
@ -65,6 +79,7 @@ export default class Client extends EventTarget {
cm = irc.CaseMapping.RFC1459;
monitored = new irc.CaseMapMap(null, irc.CaseMapping.RFC1459);
whoisDB = new irc.CaseMapMap(null, irc.CaseMapping.RFC1459);
whoxQueries = new Map();
constructor(params) {
super();
@ -332,14 +347,43 @@ export default class Client extends EventTarget {
}
}
who(mask) {
let msg = { command: "WHO", params: [mask] };
who(mask, options) {
let params = [mask];
let fields = "", token = "";
if (options && this.isupport.has("WHOX")) {
let match = ""; // Matches exact channel or nick
fields = "t"; // Always include token in reply
if (options.fields) {
options.fields.forEach((k) => {
if (!WHOX_FIELDS[k]) {
throw new Error(`Unknown WHOX field ${k}`);
}
fields += WHOX_FIELDS[k];
});
}
token = String(lastWhoxToken % 1000);
lastWhoxToken++;
params.push(`${match}%${fields},${token}`);
this.whoxQueries.set(token, fields);
}
let msg = { command: "WHO", params };
let l = [];
return this.roundtrip(msg, (msg) => {
switch (msg.command) {
case irc.RPL_WHOREPLY:
// TODO: match with mask
l.push(msg);
l.push(this.parseWhoReply(msg));
break;
case irc.RPL_WHOSPCRPL:
if (msg.params.length !== fields.length || msg.params[1] !== token) {
break;
}
l.push(this.parseWhoReply(msg));
break;
case irc.RPL_ENDOFWHO:
if (msg.params[1] === mask) {
@ -347,9 +391,45 @@ export default class Client extends EventTarget {
}
break;
}
}).finally(() => {
this.whoxQueries.delete(token);
});
}
parseWhoReply(msg) {
switch (msg.command) {
case irc.RPL_WHOREPLY:
let last = msg.params[msg.params.length - 1];
return {
username: msg.params[2],
hostname: msg.params[3],
server: msg.params[4],
nick: msg.params[5],
flags: msg.params[6],
realname: last.slice(last.indexOf(" ") + 1),
};
case irc.RPL_WHOSPCRPL:
let token = msg.params[1];
let fields = this.whoxQueries.get(token);
if (!fields) {
throw new Error("Unknown WHOX token: " + token);
}
let who = {};
let i = 0;
Object.keys(WHOX_FIELDS).forEach((k) => {
if (fields.indexOf(WHOX_FIELDS[k]) < 0) {
return;
}
who[k] = msg.params[2 + i];
i++;
});
return who;
default:
throw new Error("Not a WHO reply: " + msg.command);
}
}
whois(target) {
let targetCM = this.cm(target);
let msg = { command: "WHOIS", params: [target] };

View file

@ -23,6 +23,7 @@ export const RPL_EXCEPTLIST = "348";
export const RPL_ENDOFEXCEPTLIST = "349";
export const RPL_WHOREPLY = "352";
export const RPL_NAMREPLY = "353";
export const RPL_WHOSPCRPL = "354";
export const RPL_ENDOFNAMES = "366";
export const RPL_BANLIST = "367";
export const RPL_ENDOFBANLIST = "368";

View file

@ -363,16 +363,16 @@ export const State = {
case irc.RPL_ENDOFNAMES:
break;
case irc.RPL_WHOREPLY:
let last = msg.params[msg.params.length - 1];
who = {
username: msg.params[2],
hostname: msg.params[3],
server: msg.params[4],
nick: msg.params[5],
away: msg.params[6] == 'G', // H for here, G for gone
realname: last.slice(last.indexOf(" ") + 1),
offline: false,
};
case irc.RPL_WHOSPCRPL:
let who = client.parseWhoReply(msg);
if (who.flags !== undefined) {
who.away = who.flags.indexOf("G") >= 0; // H for here, G for gone
delete who.flags;
}
who.offline = false;
return updateUser(who.nick, who);
case irc.RPL_ENDOFWHO:
target = msg.params[1];