From 55720c10d88af5417a1aa775d8306785978ad5c0 Mon Sep 17 00:00:00 2001 From: Reto Brunner Date: Thu, 28 Dec 2023 14:45:52 +0100 Subject: [PATCH] whowas: return all responses from the server Whowas results are a list of all known RPL_WHOWASUSER replies. Return the most recent event as the top level and add the rest to a `historical` field as an array. Example: ``` :inspircd.server.example 314 val someone ident3 127.0.0.1 * :Realname :inspircd.server.example 312 val someone My.Little.Server :Sun Mar 20 2022 10:59:26 :inspircd.server.example 314 val someone ident2 127.0.0.1 * :Realname :inspircd.server.example 312 val someone My.Little.Server :Sun Mar 20 2022 10:59:16 :inspircd.server.example 314 val someone ident1 127.0.0.1 * :Realname :inspircd.server.example 312 val someone My.Little.Server :Sun Mar 19 2022 9:23:06 :inspircd.server.example 369 val someone :End of WHOWAS whowas { nick: 'someone', ident: 'ident3', hostname: '127.0.0.1', real_name: 'Realname', server: 'My.Little.Server', server_info: 'Sun Mar 20 2022 10:59:26', historical: [ { nick: 'someone', ident: 'ident2', hostname: '127.0.0.1', real_name: 'Realname', server: 'My.Little.Server', server_info: 'Sun Mar 20 2022 10:59:16' }, { nick: 'someone', ident: 'ident1', hostname: '127.0.0.1', real_name: 'Realname', server: 'My.Little.Server', server_info: 'Sun Mar 19 2022 9:23:06' } ] } ``` Fixes: https://github.com/kiwiirc/irc-framework/issues/371 --- docs/events.md | 8 ++++++ src/commands/handlers/user.js | 50 +++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/docs/events.md b/docs/events.md index ceebd271..85f9c41c 100644 --- a/docs/events.md +++ b/docs/events.md @@ -596,6 +596,10 @@ Not all of these options will be available. Some will be missing depending on th **whowas** +The response includes the latest known data. +If more than one RPL_WHOWASUSER is returned by the server, older ones +are stored in the same format in the 'historical' array from newest to oldest. + If the requested user was not found, error will contain 'no_such_nick'. ~~~javascript { @@ -609,6 +613,10 @@ If the requested user was not found, error will contain 'no_such_nick'. server_info: 'Thu Jun 14 09:15:51 2018', account: 'logged on account', error: '' + historical: [ + { ... }, + { ... }, + ] } ~~~ diff --git a/src/commands/handlers/user.js b/src/commands/handlers/user.js index 33eaf50f..a472fd2b 100644 --- a/src/commands/handlers/user.js +++ b/src/commands/handlers/user.js @@ -3,6 +3,7 @@ const _ = { each: require('lodash/each'), map: require('lodash/map'), + cloneDeep: require('lodash/cloneDeep'), }; const Helpers = require('../../helpers'); @@ -325,12 +326,26 @@ const handlers = { RPL_WHOWASUSER: function(command, handler) { const cache_key = command.params[1].toLowerCase(); - const cache = handler.cache('whois.' + cache_key); + const whois_cache = handler.cache('whois.' + cache_key); + + // multiple RPL_WHOWASUSER replies are received prior to the RPL_ENDOFWHOWAS command + // one for each timestamp the server is aware of, from newest to oldest. + // They are optionally interleaved with various other numerics such as RPL_WHOISACTUALLY etc. + // Hence if we already find something we are receiving older data and need to make sure that we + // store anything already in the cache into its own entry + const whowas_cache = handler.cache('whowas.' + cache_key); + if (!whowas_cache.historical) { + // this will get populated by the next RPL_WHOWASUSER or RPL_ENDOFWHOWAS + whowas_cache.historical = []; + } else { + // push the previous event prior to modifying anything + whowas_cache.historical.push(_.cloneDeep(whois_cache)); + } - cache.nick = command.params[1]; - cache.ident = command.params[2]; - cache.hostname = command.params[3]; - cache.real_name = command.params[command.params.length - 1]; + whois_cache.nick = command.params[1]; + whois_cache.ident = command.params[2]; + whois_cache.hostname = command.params[3]; + whois_cache.real_name = command.params[command.params.length - 1]; }, RPL_ENDOFWHOWAS: function(command, handler) { @@ -344,16 +359,29 @@ const handlers = { // server_info, actual_username // More optional fields MAY exist, depending on the type of ircd. const cache_key = command.params[1].toLowerCase(); - const cache = handler.cache('whois.' + cache_key); + const whois_cache = handler.cache('whois.' + cache_key); + const whowas_cache = handler.cache('whowas.' + cache_key); + + // after all prior RPL_WHOWASUSER pushed newer events onto the history stack + // push the last one to complete the set (server returns from newest to oldest) + if (!whowas_cache.historical) { + whowas_cache.historical = []; + } + whowas_cache.historical.push(_.cloneDeep(whois_cache)); + + // now pull the newest response to the top level and add the rest as an array + const event = whowas_cache.historical[0]; + event.historical = whowas_cache.historical.slice(1); // Should, in theory, never happen. - if (!cache.nick) { - cache.nick = command.params[1]; - cache.error = 'no_such_nick'; + if (!event.nick) { + event.nick = command.params[1]; + event.error = 'no_such_nick'; } - handler.emit('whowas', cache); - cache.destroy(); + handler.emit('whowas', event); + whois_cache.destroy(); + whowas_cache.destroy(); }, ERR_WASNOSUCHNICK: function(command, handler) {