fix timestamps and fix SVSNICK double nick

This commit is contained in:
zefie
2025-06-16 17:17:27 -04:00
parent 8035d12fa1
commit 3b4a3e29d4

View File

@@ -65,7 +65,7 @@ class WTVIRC {
this.channelprefixes = ['#','&']; this.channelprefixes = ['#','&'];
this.default_channel_modes = ['n','t']; this.default_channel_modes = ['n','t'];
this.default_user_modes = ['x']; this.default_user_modes = ['x'];
this.server_start_time = Date.now(); this.server_start_time = this.getDate();
this.allowed_characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_[]{}\\|^-'; this.allowed_characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_[]{}\\|^-';
this.irc_config = minisrv_config.config.irc || {}; this.irc_config = minisrv_config.config.irc || {};
this.servername = this.irc_config.server_hostname || 'irc.local'; this.servername = this.irc_config.server_hostname || 'irc.local';
@@ -110,7 +110,7 @@ class WTVIRC {
} }
} }
} }
this.server_start_time = Date.now(); this.server_start_time = this.getDate();
this.server = net.createServer((socket) => { this.server = net.createServer((socket) => {
// Detect SSL handshake and wrap socket if needed // Detect SSL handshake and wrap socket if needed
socket.once('data', (firstChunk) => { socket.once('data', (firstChunk) => {
@@ -183,7 +183,7 @@ class WTVIRC {
secureSocket.host = this.filterHostname(secureSocket, hostname); secureSocket.host = this.filterHostname(secureSocket, hostname);
}); });
secureSocket.timestamp = Date.now(); secureSocket.timestamp = this.getDate();
secureSocket.secure = true; secureSocket.secure = true;
secureSocket.uniqueId = `${this.serverId}${this.generateUniqueId(secureSocket)}`; secureSocket.uniqueId = `${this.serverId}${this.generateUniqueId(secureSocket)}`;
// Push the secure socket to clients // Push the secure socket to clients
@@ -225,7 +225,7 @@ class WTVIRC {
socket.realhost = hostname; socket.realhost = hostname;
socket.host = this.filterHostname(socket, hostname); socket.host = this.filterHostname(socket, hostname);
}); });
socket.timestamp = Date.now(); socket.timestamp = this.getDate();
socket.secure = false; socket.secure = false;
socket.uniqueId = `${this.serverId}${this.generateUniqueId(socket)}`; socket.uniqueId = `${this.serverId}${this.generateUniqueId(socket)}`;
@@ -323,7 +323,7 @@ class WTVIRC {
for (const [sock, nickname] of this.nicknames.entries()) { for (const [sock, nickname] of this.nicknames.entries()) {
if (!sock || !nickname) continue; if (!sock || !nickname) continue;
const uniqueId = sock.uniqueId; const uniqueId = sock.uniqueId;
const signonTime = Math.floor(this.usersignontimestamps.get(nickname) || Date.now() / 1000); const signonTime = Math.floor(this.usersignontimestamps.get(nickname) || this.getDate());
const userModes = (this.usermodes.get(nickname) || []).join(''); const userModes = (this.usermodes.get(nickname) || []).join('');
const username = this.usernames.get(nickname) || ''; const username = this.usernames.get(nickname) || '';
socket.write(`:${this.serverId} UID ${nickname} 1 ${signonTime} +${userModes} ${username} ${sock.host} ${sock.realhost} ${sock.remoteAddress} ${uniqueId} * :${sock.userinfo}\r\n`); socket.write(`:${this.serverId} UID ${nickname} 1 ${signonTime} +${userModes} ${username} ${sock.host} ${sock.realhost} ${sock.remoteAddress} ${uniqueId} * :${sock.userinfo}\r\n`);
@@ -342,7 +342,7 @@ class WTVIRC {
} }
const userUniqueId = this.uniqueids.get(user); const userUniqueId = this.uniqueids.get(user);
if (userUniqueId) { if (userUniqueId) {
socket.write(`:${this.serverId} SJOIN ${Math.floor(Date.now() / 1000)} ${channel} +${modes.join('')} :${userPrefix}${userUniqueId}\r\n`); socket.write(`:${this.serverId} SJOIN ${this.getDate()} ${channel} +${modes.join('')} :${userPrefix}${userUniqueId}\r\n`);
} }
} }
} }
@@ -355,7 +355,7 @@ class WTVIRC {
console.warn('Invalid SVINFO command from server'); console.warn('Invalid SVINFO command from server');
break; break;
} }
const serverInfoMessage = `SVINFO 6 6 0 :${parseInt(Date.now() / 1000)}\r\n`; const serverInfoMessage = `SVINFO 6 6 0 :${this.getDate()}\r\n`;
socket.write(serverInfoMessage); socket.write(serverInfoMessage);
case 'PING': case 'PING':
// Respond to PING with PONG // Respond to PING with PONG
@@ -432,7 +432,6 @@ class WTVIRC {
var oldNick = this.findUserByUniqueId(parts[1]); var oldNick = this.findUserByUniqueId(parts[1]);
var newNick = parts[3]; var newNick = parts[3];
var targetSocket = this.findSocketByUniqueId(parts[1]); var targetSocket = this.findSocketByUniqueId(parts[1]);
targetSocket.write(`:${targetSocket.nickname}!${targetSocket.username}@${targetSocket.host} NICK :${newNick}\r\n`);
this.broadcastUser(oldNick, `:${targetSocket.nickname}!${targetSocket.username}@${targetSocket.host} NICK :${newNick}\r\n`); this.broadcastUser(oldNick, `:${targetSocket.nickname}!${targetSocket.username}@${targetSocket.host} NICK :${newNick}\r\n`);
this.processNickChange(targetSocket, newNick); this.processNickChange(targetSocket, newNick);
this.broadcastToAllServers(line, socket); this.broadcastToAllServers(line, socket);
@@ -908,7 +907,7 @@ class WTVIRC {
if (modeString.length > 0) { if (modeString.length > 0) {
targetSocket.write(`:${this.servername} 324 ${nickname} ${channelName} +${modeString}${modeParams.length ? ' ' + modeParams.join(' ') : ''}\r\n`); targetSocket.write(`:${this.servername} 324 ${nickname} ${channelName} +${modeString}${modeParams.length ? ' ' + modeParams.join(' ') : ''}\r\n`);
} }
this.broadcastToAllServers(`:${this.serverId} SJOIN ${Math.floor(Date.now() / 1000)} ${channelName} +${modeString}${modeParams.length ? ' ' + modeParams.join(' ') : ''} ${targetUniqueId}\r\n`); this.broadcastToAllServers(`:${this.serverId} SJOIN ${this.getDate()} ${channelName} +${modeString}${modeParams.length ? ' ' + modeParams.join(' ') : ''} ${targetUniqueId}\r\n`);
break; break;
case "SVSMODE": case "SVSMODE":
if (parts.length < 4) { if (parts.length < 4) {
@@ -1040,7 +1039,7 @@ class WTVIRC {
socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`); socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`);
break; break;
} }
const uptime = Math.floor((Date.now() - this.server_start_time) / 1000); const uptime = this.getDate() - this.server_start_time;
const days = Math.floor(uptime / 86400); const days = Math.floor(uptime / 86400);
const hours = Math.floor((uptime % 86400) / 3600); const hours = Math.floor((uptime % 86400) / 3600);
const minutes = Math.floor((uptime % 3600) / 60); const minutes = Math.floor((uptime % 3600) / 60);
@@ -1072,7 +1071,7 @@ class WTVIRC {
socket.write(`:${this.servername} 461 ${socket.nickname} KICK :Not enough parameters\r\n`); socket.write(`:${this.servername} 461 ${socket.nickname} KICK :Not enough parameters\r\n`);
break; break;
} }
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
if (!this.channels.has(channel)) { if (!this.channels.has(channel)) {
@@ -1123,7 +1122,7 @@ class WTVIRC {
break; break;
} }
} }
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
var channel = params[0]; var channel = params[0];
if (!this.channels.has(channel)) { if (!this.channels.has(channel)) {
socket.write(`:${this.servername} 403 ${socket.nickname} ${channel} :No such channel\r\n`); socket.write(`:${this.servername} 403 ${socket.nickname} ${channel} :No such channel\r\n`);
@@ -1148,7 +1147,7 @@ class WTVIRC {
socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`); socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`);
break; break;
} }
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
if (params.length > 0) { if (params.length > 0) {
socket.write(`:${this.servername} 306 ${socket.nickname} :You are now marked as away\r\n`); socket.write(`:${this.servername} 306 ${socket.nickname} :You are now marked as away\r\n`);
let awayMsg = params.join(' '); let awayMsg = params.join(' ');
@@ -1328,7 +1327,7 @@ class WTVIRC {
} else { } else {
socket.write(`:${this.servername} 324 ${socket.nickname} ${channel}\r\n`); socket.write(`:${this.servername} 324 ${socket.nickname} ${channel}\r\n`);
} }
socket.write(`:${this.servername} 329 ${socket.nickname} ${channel} ${this.channeltimestamps.get(channel) || Date.now()}\r\n`); socket.write(`:${this.servername} 329 ${socket.nickname} ${channel} ${this.channeltimestamps.get(channel) || M}\r\n`);
break; break;
} else { } else {
this.processChannelModeBatch(socket.nickname, channel, mode, params.slice(2)); this.processChannelModeBatch(socket.nickname, channel, mode, params.slice(2));
@@ -1403,11 +1402,11 @@ class WTVIRC {
} }
this.broadcastUser(socket.nickname, `:${socket.nickname}!${socket.username}@${socket.host} NICK :${new_nickname}\r\n`, socket); this.broadcastUser(socket.nickname, `:${socket.nickname}!${socket.username}@${socket.host} NICK :${new_nickname}\r\n`, socket);
this.processNickChange(socket, new_nickname); this.processNickChange(socket, new_nickname);
this.broadcastToAllServers(`:${socket.uniqueId} NICK ${new_nickname} :${Math.floor(Date.now() / 1000)}\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} NICK ${new_nickname} :${this.getDate()}\r\n`);
} }
if (!socket.registered && socket.nickname && socket.username) { if (!socket.registered && socket.nickname && socket.username) {
socket.registered = true; socket.registered = true;
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
this.usersignontimestamps.set(new_nickname, socket.timestamp); this.usersignontimestamps.set(new_nickname, socket.timestamp);
this.doLogin(socket.nickname, socket); this.doLogin(socket.nickname, socket);
} }
@@ -1425,7 +1424,7 @@ class WTVIRC {
if (!socket.registered && socket.nickname && socket.username) { if (!socket.registered && socket.nickname && socket.username) {
socket.registered = true; socket.registered = true;
this.usernames.set(socket.nickname, socket.username); this.usernames.set(socket.nickname, socket.username);
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
this.usersignontimestamps.set(socket.nickname, socket.timestamp); this.usersignontimestamps.set(socket.nickname, socket.timestamp);
this.doLogin(socket.nickname, socket); this.doLogin(socket.nickname, socket);
} }
@@ -1573,7 +1572,7 @@ class WTVIRC {
// Only run the code after $PLACEHOLDER$ for each channel // Only run the code after $PLACEHOLDER$ for each channel
// (excluding the code before $PLACEHOLDER$ to avoid duplicate checks) // (excluding the code before $PLACEHOLDER$ to avoid duplicate checks)
// You can refactor this logic into a helper if needed // You can refactor this logic into a helper if needed
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
socket.write(`:${socket.nickname}!${socket.username}@${socket.host} JOIN ${ch}\r\n`); socket.write(`:${socket.nickname}!${socket.username}@${socket.host} JOIN ${ch}\r\n`);
if (!this.channels.has(ch)) { if (!this.channels.has(ch)) {
this.channels.set(ch, new Set()); this.channels.set(ch, new Set());
@@ -1593,7 +1592,7 @@ class WTVIRC {
} else if ((this.channelvoices.get(ch) || new Set()).has(socket.nickname)) { } else if ((this.channelvoices.get(ch) || new Set()).has(socket.nickname)) {
prefix = '+'; prefix = '+';
} }
this.broadcastToAllServers(`:${this.serverId} SJOIN ${Math.floor(Date.now() / 1000)} ${ch} +${modes.join('')} :${prefix}${socket.uniqueId}\r\n`); this.broadcastToAllServers(`:${this.serverId} SJOIN ${this.getDate()} ${ch} +${modes.join('')} :${prefix}${socket.uniqueId}\r\n`);
if (this.channeltopics.has(ch)) { if (this.channeltopics.has(ch)) {
const topic = this.channeltopics.get(ch); const topic = this.channeltopics.get(ch);
socket.write(`:${this.servername} 332 ${socket.nickname} ${ch} :${topic}\r\n`); socket.write(`:${this.servername} 332 ${socket.nickname} ${ch} :${topic}\r\n`);
@@ -1649,7 +1648,7 @@ class WTVIRC {
socket.write(`:${this.servername} 442 ${socket.nickname} ${channel} :You're not on that channel\r\n`); socket.write(`:${this.servername} 442 ${socket.nickname} ${channel} :You're not on that channel\r\n`);
break; break;
} }
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
if (params.length == 2) { if (params.length == 2) {
let reason = params.join(' '); let reason = params.join(' ');
if (reason.startsWith(':')) { if (reason.startsWith(':')) {
@@ -1820,7 +1819,7 @@ class WTVIRC {
socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`); socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`);
break; break;
} }
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
if (params[0]) { if (params[0]) {
const target = params[0]; const target = params[0];
let targets = target.includes(',') ? target.split(',') : [target]; let targets = target.includes(',') ? target.split(',') : [target];
@@ -1936,7 +1935,7 @@ class WTVIRC {
socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`); socket.write(`:${this.servername} 451 ${socket.nickname} :You have not registered\r\n`);
break; break;
} }
this.usertimestamps.set(socket.nickname, Date.now()); this.usertimestamps.set(socket.nickname, this.getDate());
if (params[0]) { if (params[0]) {
const target = params[0]; const target = params[0];
let targets = target.includes(',') ? target.split(',') : [target]; let targets = target.includes(',') ? target.split(',') : [target];
@@ -2084,9 +2083,9 @@ class WTVIRC {
if (usermodes && usermodes.includes('r')) { if (usermodes && usermodes.includes('r')) {
socket.write(`:${this.servername} 307 ${socket.nickname} ${whoisNick} :is a registered nick\r\n`); socket.write(`:${this.servername} 307 ${socket.nickname} ${whoisNick} :is a registered nick\r\n`);
} }
var now = Date.now(); var now = this.getDate();
var userTimestamp = this.usertimestamps.get(whoisNick) || now; var userTimestamp = this.usertimestamps.get(whoisNick) || now;
var idleTime = Math.floor((now - userTimestamp) / 1000); var idleTime = now - userTimestamp;
socket.write(`:${this.servername} 317 ${socket.nickname} ${whoisNick} ${idleTime} :seconds idle\r\n`); socket.write(`:${this.servername} 317 ${socket.nickname} ${whoisNick} ${idleTime} :seconds idle\r\n`);
if (userChannels.length > 0) { if (userChannels.length > 0) {
socket.write(`:${this.servername} 319 ${socket.nickname} ${whoisNick} :${userChannels.join(' ')}\r\n`); socket.write(`:${this.servername} 319 ${socket.nickname} ${whoisNick} :${userChannels.join(' ')}\r\n`);
@@ -2435,7 +2434,7 @@ class WTVIRC {
this.channelexemptions.set(channel, new Set()); this.channelexemptions.set(channel, new Set());
this.channelinvites.set(channel, new Set()); this.channelinvites.set(channel, new Set());
this.channelmodes.set(channel, this.default_channel_modes.slice()); this.channelmodes.set(channel, this.default_channel_modes.slice());
this.channeltimestamps.set(channel, Math.floor(Date.now() / 1000)); this.channeltimestamps.set(channel, this.getDate());
} }
} }
@@ -3333,19 +3332,19 @@ class WTVIRC {
this.nicknames.set(socket, newNick); this.nicknames.set(socket, newNick);
this.uniqueids.delete(socket.nickname); this.uniqueids.delete(socket.nickname);
this.addUserUniqueId(newNick, socket.uniqueId); this.addUserUniqueId(newNick, socket.uniqueId);
this.usertimestamps.set(newNick, Date.now()); this.usertimestamps.set(newNick, this.getDate());
this.usertimestamps.delete(socket.nickname); this.usertimestamps.delete(socket.nickname);
this.usermodes.set(newNick, this.usermodes.get(socket.nickname) || []); this.usermodes.set(newNick, this.usermodes.get(socket.nickname) || []);
this.usermodes.delete(socket.nickname); this.usermodes.delete(socket.nickname);
this.awaymsgs.set(newNick, this.awaymsgs.get(socket.nickname) || ''); this.awaymsgs.set(newNick, this.awaymsgs.get(socket.nickname) || '');
this.awaymsgs.delete(socket.nickname); this.awaymsgs.delete(socket.nickname);
this.usersignontimestamps.set(newNick, this.usersignontimestamps.get(socket.nickname) || Math.floor(Date.now() / 1000)); this.usersignontimestamps.set(newNick, this.usersignontimestamps.get(socket.nickname) || this.getDate());
this.usersignontimestamps.delete(socket.nickname); this.usersignontimestamps.delete(socket.nickname);
socket.nickname = newNick; socket.nickname = newNick;
} }
generateUniqueId(socket) { generateUniqueId(socket) {
const timestamp = Date.now(); const timestamp = this.getDate();
const randomPart = Math.floor(Math.random() * 1000000); const randomPart = Math.floor(Math.random() * 1000000);
const uniqueId = `${socket.remoteAddr}-${socket.port}-${timestamp}-${randomPart}`; const uniqueId = `${socket.remoteAddr}-${socket.port}-${timestamp}-${randomPart}`;
const hash = crypto.createHash('sha256').update(uniqueId).digest('hex').slice(0, 6).toUpperCase(); const hash = crypto.createHash('sha256').update(uniqueId).digest('hex').slice(0, 6).toUpperCase();
@@ -3359,6 +3358,10 @@ class WTVIRC {
} }
} }
getDate() {
return Math.floor(Date.now() / 1000)
}
doLogin(nickname, socket) { doLogin(nickname, socket) {
for (const [srvSocket, serverName] of this.servers.entries()) { for (const [srvSocket, serverName] of this.servers.entries()) {
if (srvSocket) { if (srvSocket) {
@@ -3366,7 +3369,7 @@ class WTVIRC {
const nickname = socket.nickname; const nickname = socket.nickname;
const username = socket.username || this.usernames.get(socket.nickname) || socket.nickname; const username = socket.username || this.usernames.get(socket.nickname) || socket.nickname;
const uniqueId = socket.uniqueId; const uniqueId = socket.uniqueId;
const signonTime = Math.floor(socket.timestamp / 1000); const signonTime = socket.timestamp || this.getDate();
const userModes = (this.usermodes.get(nickname) || []).join(''); const userModes = (this.usermodes.get(nickname) || []).join('');
const userinfo = socket.userinfo || ''; const userinfo = socket.userinfo || '';
srvSocket.write(`:${this.serverId} UID ${nickname} 1 ${signonTime} +${userModes} ${username} ${socket.host} ${socket.realhost} ${socket.remoteAddress} ${uniqueId} * ${nickname} :${userinfo}\r\n`); srvSocket.write(`:${this.serverId} UID ${nickname} 1 ${signonTime} +${userModes} ${username} ${socket.host} ${socket.realhost} ${socket.remoteAddress} ${uniqueId} * ${nickname} :${userinfo}\r\n`);