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; continue;
} }
this.createBuffer(serverID, buf.name); this.createBuffer(serverID, buf.name);
client.who(buf.name); this.whoUserBuffer(buf.name, serverID);
client.monitor(buf.name);
} }
let lastReceipt = this.latestReceipt(ReceiptType.DELIVERED); 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) { open(target, serverID) {
if (!serverID) { if (!serverID) {
serverID = State.getActiveServerID(this.state); serverID = State.getActiveServerID(this.state);
@ -865,8 +873,7 @@ export default class App extends Component {
this.switchToChannel = target; this.switchToChannel = target;
client.send({ command: "JOIN", params: [target] }); client.send({ command: "JOIN", params: [target] });
} else { } else {
client.who(target); this.whoUserBuffer(target, serverID);
client.monitor(target);
this.createBuffer(serverID, target); this.createBuffer(serverID, target);
this.switchBuffer({ server: serverID, name: target }); this.switchBuffer({ server: serverID, name: target });
} }

View File

@ -30,7 +30,21 @@ const NORMAL_CLOSURE = 1000;
const GOING_AWAY = 1001; const GOING_AWAY = 1001;
const UNSUPPORTED_DATA = 1003; 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 lastLabel = 0;
let lastWhoxToken = 0;
export default class Client extends EventTarget { export default class Client extends EventTarget {
static Status = { static Status = {
@ -65,6 +79,7 @@ export default class Client extends EventTarget {
cm = irc.CaseMapping.RFC1459; cm = irc.CaseMapping.RFC1459;
monitored = new irc.CaseMapMap(null, 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);
whoxQueries = new Map();
constructor(params) { constructor(params) {
super(); super();
@ -332,14 +347,43 @@ export default class Client extends EventTarget {
} }
} }
who(mask) { who(mask, options) {
let msg = { command: "WHO", params: [mask] }; 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 = []; let l = [];
return this.roundtrip(msg, (msg) => { return this.roundtrip(msg, (msg) => {
switch (msg.command) { switch (msg.command) {
case irc.RPL_WHOREPLY: case irc.RPL_WHOREPLY:
// TODO: match with mask // 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; break;
case irc.RPL_ENDOFWHO: case irc.RPL_ENDOFWHO:
if (msg.params[1] === mask) { if (msg.params[1] === mask) {
@ -347,9 +391,45 @@ export default class Client extends EventTarget {
} }
break; 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) { whois(target) {
let targetCM = this.cm(target); let targetCM = this.cm(target);
let msg = { command: "WHOIS", params: [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_ENDOFEXCEPTLIST = "349";
export const RPL_WHOREPLY = "352"; export const RPL_WHOREPLY = "352";
export const RPL_NAMREPLY = "353"; export const RPL_NAMREPLY = "353";
export const RPL_WHOSPCRPL = "354";
export const RPL_ENDOFNAMES = "366"; export const RPL_ENDOFNAMES = "366";
export const RPL_BANLIST = "367"; export const RPL_BANLIST = "367";
export const RPL_ENDOFBANLIST = "368"; export const RPL_ENDOFBANLIST = "368";

View File

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