'fixes'
This commit is contained in:
@@ -40,6 +40,7 @@ class WTVIRC {
|
|||||||
this.channels = new Map();
|
this.channels = new Map();
|
||||||
this.channeltimestamps = new Map(); // channel -> timestamp of creation
|
this.channeltimestamps = new Map(); // channel -> timestamp of creation
|
||||||
this.channelops = new Map(); // channel -> Set of operators
|
this.channelops = new Map(); // channel -> Set of operators
|
||||||
|
this.channelhalfops = new Map(); // channel -> Set of half-operators
|
||||||
this.channelvoices = new Map(); // channel -> Set of voiced users
|
this.channelvoices = new Map(); // channel -> Set of voiced users
|
||||||
this.channeltopics = new Map(); // channel -> topic
|
this.channeltopics = new Map(); // channel -> topic
|
||||||
this.channelinvites = new Map(); // channel -> Set of invited users
|
this.channelinvites = new Map(); // channel -> Set of invited users
|
||||||
@@ -86,7 +87,7 @@ class WTVIRC {
|
|||||||
this.clientpeak = 0;
|
this.clientpeak = 0;
|
||||||
this.globalpeak = 0;
|
this.globalpeak = 0;
|
||||||
this.caps = [
|
this.caps = [
|
||||||
`AWAYLEN=${this.awaylen} CASEMAPPING=rfc1459 CHANMODES=beI,k,l,itmnpcTVZRrNQO CHANNELLEN=${this.channellen} CHANTYPES=${this.channelprefixes.join('')} PREFIX=(ov)@+ USERMODES=oxizZws MAXLIST=b:${this.maxbans},e:${this.maxexcept},i:${this.maxinvite},k:${this.maxkeylen},l:${this.maxlimit}`,
|
`AWAYLEN=${this.awaylen} CASEMAPPING=rfc1459 CHANMODES=beI,k,l,itmnpcTVZRrNQO CHANNELLEN=${this.channellen} CHANTYPES=${this.channelprefixes.join('')} PREFIX=(ohv)@%+ USERMODES=oxirzZws MAXLIST=b:${this.maxbans},e:${this.maxexcept},i:${this.maxinvite},k:${this.maxkeylen},l:${this.maxlimit}`,
|
||||||
`CHARSET=ascii MODES=3 EXCEPTS=e INVEX=I CHANLIMIT=${this.channelprefixes.join('')}:${this.channellimit} NICKLEN=${this.nicklen} TOPICLEN=${this.topiclen} KICKLEN=${this.kicklen}`
|
`CHARSET=ascii MODES=3 EXCEPTS=e INVEX=I CHANLIMIT=${this.channelprefixes.join('')}:${this.channellimit} NICKLEN=${this.nicklen} TOPICLEN=${this.topiclen} KICKLEN=${this.kicklen}`
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -393,13 +394,11 @@ class WTVIRC {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var oldNick = this.findUserByUniqueId(parts[1]);
|
var oldNick = this.findUserByUniqueId(parts[1]);
|
||||||
var oldTs = parseInt(parts[2]);
|
|
||||||
var newNick = parts[3];
|
var newNick = parts[3];
|
||||||
var newTs = parseInt(parts[4]) || Math.floor(Date.now() / 1000);
|
|
||||||
var targetSocket = this.findSocketByUniqueId(parts[1]);
|
var targetSocket = this.findSocketByUniqueId(parts[1]);
|
||||||
targetSocket.write(`:${targetSocket.nickname}!${targetSocket.username}@${targetSocket.host} NICK :${newNick}\r\n`);
|
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, oldNick, newNick);
|
this.processNickChange(targetSocket, newNick);
|
||||||
this.broadcastToAllServers(line, socket);
|
this.broadcastToAllServers(line, socket);
|
||||||
break;
|
break;
|
||||||
case 'SQUIT':
|
case 'SQUIT':
|
||||||
@@ -527,38 +526,66 @@ class WTVIRC {
|
|||||||
this.usernames.delete(nick_name);
|
this.usernames.delete(nick_name);
|
||||||
this.uniqueids.delete(nick_name);
|
this.uniqueids.delete(nick_name);
|
||||||
break;
|
break;
|
||||||
}
|
} else if (srvCommand === "GLOBOPS") {
|
||||||
if (srvCommand === "GLOBOPS") {
|
|
||||||
var message = parts.slice(3).join(' ');
|
var message = parts.slice(3).join(' ');
|
||||||
this.broadcastToAllServers(`:${sourceUniqueId} GLOBOPS :${message}`, socket);
|
this.broadcastToAllServers(`:${sourceUniqueId} GLOBOPS :${message}`, socket);
|
||||||
}
|
break;
|
||||||
var targetUniqueId = parts[2];
|
} else if (srvCommand === 'PRIVMSG' || srvCommand === 'NOTICE') {
|
||||||
var message = parts.slice(3).join(' ');
|
var targetUniqueId = parts[2];
|
||||||
|
var message = parts.slice(3).join(' ');
|
||||||
|
|
||||||
var sourceSocket = this.findSocketByUniqueId(sourceUniqueId);
|
var sourceSocket = this.findSocketByUniqueId(sourceUniqueId);
|
||||||
var targetSocket = this.findSocketByUniqueId(targetUniqueId);
|
var targetSocket = this.findSocketByUniqueId(targetUniqueId);
|
||||||
if (!sourceSocket) {
|
if (!sourceSocket) {
|
||||||
console.warn(`No socket found for source unique ID ${sourceUniqueId}`);
|
console.warn(`No socket found for source unique ID ${sourceUniqueId}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!targetSocket) {
|
if (!targetSocket) {
|
||||||
console.warn(`No socket found for target unique ID ${targetUniqueId}`);
|
console.warn(`No socket found for target unique ID ${targetUniqueId}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var sourceNickname = this.getUsernameFromUniqueId(sourceUniqueId);
|
var sourceNickname = this.getUsernameFromUniqueId(sourceUniqueId);
|
||||||
var sourceUsername = this.usernames.get(sourceNickname) || sourceNickname;
|
var sourceUsername = this.usernames.get(sourceNickname) || sourceNickname;
|
||||||
var targetNickname = this.getUsernameFromUniqueId(targetUniqueId);
|
var targetNickname = this.getUsernameFromUniqueId(targetUniqueId);
|
||||||
// Process the command based on the source and target unique IDs
|
|
||||||
if (srvCommand === 'PRIVMSG' || srvCommand === 'NOTICE') {
|
|
||||||
if (message.startsWith(':')) {
|
if (message.startsWith(':')) {
|
||||||
message = message.slice(1); // Remove leading ':'
|
message = message.slice(1); // Remove leading ':'
|
||||||
}
|
}
|
||||||
targetSocket.write(`:${sourceNickname}!${sourceUsername}@${sourceSocket.serverinfo.name} ${srvCommand} ${targetNickname} :${message}\r\n`);
|
targetSocket.write(`:${sourceNickname}!${sourceUsername}@${sourceSocket.serverinfo.name} ${srvCommand} ${targetNickname} :${message}\r\n`);
|
||||||
break;
|
break;
|
||||||
|
} else if (srvCommand === "SVSMODE") {
|
||||||
|
if (parts.length < 4) {
|
||||||
|
console.warn('Invalid SVSMODE command from server');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var targetUniqueId = parts[2];
|
||||||
|
var targetSocket = this.findSocketByUniqueId(targetUniqueId);
|
||||||
|
var modes = parts[4].split('');
|
||||||
|
let adding = true;
|
||||||
|
for (const char of modes.join('')) {
|
||||||
|
if (char === '+') {
|
||||||
|
adding = true;
|
||||||
|
} else if (char === '-') {
|
||||||
|
adding = false;
|
||||||
|
} else {
|
||||||
|
let usermodes = this.usermodes.get(targetNickname) || [];
|
||||||
|
if (usermodes === true) usermodes = [];
|
||||||
|
if (adding) {
|
||||||
|
if (!usermodes.includes(char)) {
|
||||||
|
usermodes.push(char);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
usermodes = usermodes.filter(m => m !== char);
|
||||||
|
}
|
||||||
|
this.usermodes.set(targetNickname, usermodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetSocket.write(`:${socket.servername} MODE ${targetSocket.nickname} ${modes.join('')}\r\n`);
|
||||||
|
this.broadcastToAllServers(`:${sourceUniqueId} SVSMODE ${targetUniqueId} ${modes.join('')}\r\n`, socket);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
console.log(`Unhandled server command from ${sourceUniqueId} to ${targetUniqueId}: ${message}`);
|
console.log(`Unhandled server command from ${sourceUniqueId} to ${targetUniqueId}: ${srvCommand} ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.warn(`Unknown server command: ${command}`);
|
console.warn(`Unknown server command: ${command}`);
|
||||||
@@ -667,22 +694,28 @@ 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;
|
||||||
}
|
}
|
||||||
if (!this.channelops.has(channel) || this.channelops.get(channel) === true) {
|
var channel = params[0];
|
||||||
socket.write(`:${this.servername} 482 ${socket.nickname} ${channel} :You're not channel operator\r\n`);
|
var targetNick = params[1];
|
||||||
break;
|
// Check if the user is a channel operator
|
||||||
} else {
|
if (this.channelops.has(channel) && this.channelops.get(channel) instanceof Set && this.channelops.get(channel).has(socket.nickname)) {
|
||||||
if (!this.channelops.get(channel).has(socket.nickname)) {
|
// Allow kick
|
||||||
socket.write(`:${this.servername} 482 ${socket.nickname} ${channel} :You're not channel operator\r\n`);
|
} else if (this.channelhalfops.has(channel) && this.channelhalfops.get(channel) instanceof Set && this.channelhalfops.get(channel).has(socket.nickname)) {
|
||||||
|
// Only allow kick if the target is NOT a channel operator
|
||||||
|
if (this.channelops.has(channel) && this.channelops.get(channel) instanceof Set && this.channelops.get(channel).has(targetNick)) {
|
||||||
|
socket.write(`:${this.servername} 482 ${socket.nickname} ${channel} :You cannot kick a channel operator\r\n`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Allow kick
|
||||||
|
} else {
|
||||||
|
socket.write(`:${this.servername} 482 ${socket.nickname} ${channel} :You're not channel operator\r\n`);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (params.length < 2) {
|
if (params.length < 2) {
|
||||||
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, Date.now());
|
||||||
var channel = params[0];
|
|
||||||
const targetNick = params[1];
|
|
||||||
|
|
||||||
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`);
|
||||||
@@ -712,7 +745,7 @@ class WTVIRC {
|
|||||||
} else {
|
} else {
|
||||||
targetSocket.write(`:${socket.nickname}!${socket.username}@${socket.host} KICK ${channel} ${targetNick}\r\n`);
|
targetSocket.write(`:${socket.nickname}!${socket.username}@${socket.host} KICK ${channel} ${targetNick}\r\n`);
|
||||||
this.broadcastChannel(channel, `:${socket.nickname}!${socket.username}@${socket.host} KICK ${channel} ${targetNick}\r\n`);
|
this.broadcastChannel(channel, `:${socket.nickname}!${socket.username}@${socket.host} KICK ${channel} ${targetNick}\r\n`);
|
||||||
this.broadcastToAllServers(`:${socket.uniqueId} KICK ${channel} ${targetSocket.uniqueId} :${reason}\r\n`);
|
this.broadcastToAllServers(`:${socket.uniqueId} KICK ${channel} ${targetSocket.uniqueId}\r\n`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'TOPIC':
|
case 'TOPIC':
|
||||||
@@ -944,7 +977,8 @@ class WTVIRC {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (this.nicknames.size > 0) {
|
if (this.nicknames.size > 0) {
|
||||||
var result = Array.from(this.nicknames.values()).find(nick => nick.toLowerCase() === new_nickname.toLowerCase());
|
|
||||||
|
var result = Array.from(this.nicknames.keys()).find(s => this.nicknames.get(s).toLowerCase() === new_nickname.toLowerCase());
|
||||||
if (result) {
|
if (result) {
|
||||||
socket.write(`:${this.servername} 433 * ${new_nickname} :Nickname is already in use\r\n`);
|
socket.write(`:${this.servername} 433 * ${new_nickname} :Nickname is already in use\r\n`);
|
||||||
break;
|
break;
|
||||||
@@ -1776,6 +1810,7 @@ class WTVIRC {
|
|||||||
if (!this.isReservedChannel(channel)) {
|
if (!this.isReservedChannel(channel)) {
|
||||||
this.channels.delete(channel);
|
this.channels.delete(channel);
|
||||||
this.channelops.delete(channel);
|
this.channelops.delete(channel);
|
||||||
|
this.channelhalfops.delete(channel);
|
||||||
this.channelvoices.delete(channel);
|
this.channelvoices.delete(channel);
|
||||||
this.channeltopics.delete(channel);
|
this.channeltopics.delete(channel);
|
||||||
this.channelbans.delete(channel);
|
this.channelbans.delete(channel);
|
||||||
@@ -1806,6 +1841,11 @@ class WTVIRC {
|
|||||||
ops.delete(nickname);
|
ops.delete(nickname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const [ch, ops] of this.channelhalfops.entries()) {
|
||||||
|
if (ops && ops !== true && ops.has(nickname)) {
|
||||||
|
ops.delete(nickname);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const [ch, voices] of this.channelvoices.entries()) {
|
for (const [ch, voices] of this.channelvoices.entries()) {
|
||||||
if (voices && voices !== true && voices.has(nickname)) {
|
if (voices && voices !== true && voices.has(nickname)) {
|
||||||
voices.delete(nickname);
|
voices.delete(nickname);
|
||||||
@@ -1869,11 +1909,14 @@ class WTVIRC {
|
|||||||
getUsersInChannel(channel) {
|
getUsersInChannel(channel) {
|
||||||
if (this.channels.has(channel)) {
|
if (this.channels.has(channel)) {
|
||||||
const ops = this.channelops.get(channel) || new Set();
|
const ops = this.channelops.get(channel) || new Set();
|
||||||
|
const halfops = this.channelhalfops.get(channel) || new Set();
|
||||||
const voices = this.channelvoices.get(channel) || new Set();
|
const voices = this.channelvoices.get(channel) || new Set();
|
||||||
return Array.from(this.channels.get(channel)).map(user => {
|
return Array.from(this.channels.get(channel)).map(user => {
|
||||||
if (ops === true) return user;
|
if (ops === true) return user;
|
||||||
if (voices === true) return user;
|
if (voices === true) return user;
|
||||||
|
if (halfops === true) return user;
|
||||||
if (ops.has(user)) return '@' + user;
|
if (ops.has(user)) return '@' + user;
|
||||||
|
if (halfops.has(user)) return '%' + user;
|
||||||
if (voices.has(user)) return '+' + user;
|
if (voices.has(user)) return '+' + user;
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
@@ -1949,6 +1992,7 @@ class WTVIRC {
|
|||||||
if (!this.channels.has(channel)) {
|
if (!this.channels.has(channel)) {
|
||||||
this.channels.set(channel, new Set());
|
this.channels.set(channel, new Set());
|
||||||
this.channelops.set(channel, new Set([creator]));
|
this.channelops.set(channel, new Set([creator]));
|
||||||
|
this.channelhalfops.set(channel, new Set());
|
||||||
this.channelvoices.set(channel, new Set());
|
this.channelvoices.set(channel, new Set());
|
||||||
this.channeltopics.set(channel, 'No topic set');
|
this.channeltopics.set(channel, 'No topic set');
|
||||||
this.channelbans.set(channel, new Set());
|
this.channelbans.set(channel, new Set());
|
||||||
@@ -2144,6 +2188,7 @@ class WTVIRC {
|
|||||||
'b': true, // ban
|
'b': true, // ban
|
||||||
'e': true, // exception
|
'e': true, // exception
|
||||||
'I': true, // invite-exception
|
'I': true, // invite-exception
|
||||||
|
'h': true, // halfop
|
||||||
'o': true, // op
|
'o': true, // op
|
||||||
'v': true // voice
|
'v': true // voice
|
||||||
};
|
};
|
||||||
@@ -2176,6 +2221,7 @@ class WTVIRC {
|
|||||||
(
|
(
|
||||||
!this.channelops.has(channel) ||
|
!this.channelops.has(channel) ||
|
||||||
this.channelops.get(channel) === true ||
|
this.channelops.get(channel) === true ||
|
||||||
|
this.channelhalfops.get(channel) === true ||
|
||||||
!this.channelops.get(channel).has(nickname)
|
!this.channelops.get(channel).has(nickname)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -2344,6 +2390,24 @@ class WTVIRC {
|
|||||||
this.channelops.set(channel, (this.channelops.get(channel) || new Set()).delete(target_nickname));
|
this.channelops.set(channel, (this.channelops.get(channel) || new Set()).delete(target_nickname));
|
||||||
this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -o ${target_nickname}\r\n`);
|
this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -o ${target_nickname}\r\n`);
|
||||||
return;
|
return;
|
||||||
|
} else if (mode.startsWith('+h')) {
|
||||||
|
if (params.length < 3) {
|
||||||
|
socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target_nickname = params[2];
|
||||||
|
this.channelhalfops.set(channel, (this.channelhalfops.get(channel) || new Set()).add(target_nickname));
|
||||||
|
this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +h ${target_nickname}\r\n`);
|
||||||
|
return;
|
||||||
|
} else if (mode.startsWith('-h')) {
|
||||||
|
if (params.length < 3) {
|
||||||
|
socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target_nickname = params[2];
|
||||||
|
this.channelhalfops.set(channel, (this.channelhalfops.get(channel) || new Set()).delete(target_nickname));
|
||||||
|
this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -h ${target_nickname}\r\n`);
|
||||||
|
return;
|
||||||
} else if (mode.startsWith('+v')) {
|
} else if (mode.startsWith('+v')) {
|
||||||
if (params.length < 3) {
|
if (params.length < 3) {
|
||||||
socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`);
|
socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`);
|
||||||
@@ -2577,7 +2641,7 @@ class WTVIRC {
|
|||||||
chan_modes = [];
|
chan_modes = [];
|
||||||
}
|
}
|
||||||
if (!this.isIRCOp(nickname)) {
|
if (!this.isIRCOp(nickname)) {
|
||||||
socket.write(`:${this.servername} 482 ${nickname} ${channel} :You're not an IRC operator\r\n`);
|
socket.write(`:${this.servername} ${nickname} ${channel} :You're not an IRC operator\r\n`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.channelmodes.set(channel, [...chan_modes, 'O']);
|
this.channelmodes.set(channel, [...chan_modes, 'O']);
|
||||||
@@ -2809,7 +2873,7 @@ class WTVIRC {
|
|||||||
socket.write(`:${this.servername} 001 ${nickname} :Welcome to the IRC server, ${nickname}\r\n`);
|
socket.write(`:${this.servername} 001 ${nickname} :Welcome to the IRC server, ${nickname}\r\n`);
|
||||||
socket.write(`:${this.servername} 002 ${nickname} :Your host is ${this.servername}, running version minisrv ${this.minisrv_config.version}\r\n`);
|
socket.write(`:${this.servername} 002 ${nickname} :Your host is ${this.servername}, running version minisrv ${this.minisrv_config.version}\r\n`);
|
||||||
socket.write(`:${this.servername} 003 ${nickname} :This server is ready to accept commands\r\n`);
|
socket.write(`:${this.servername} 003 ${nickname} :This server is ready to accept commands\r\n`);
|
||||||
socket.write(`:${this.servername} 004 ${nickname} ${this.servername} minisrv ${this.minisrv_config.version} oxizZws obtkmeZIlvTVROQrnc beIklov\r\n`);
|
socket.write(`:${this.servername} 004 ${nickname} ${this.servername} minisrv ${this.minisrv_config.version} oxizrZws obtkmeZIlhvTVROQrnc beIklohv\r\n`);
|
||||||
for (const caps of this.caps) {
|
for (const caps of this.caps) {
|
||||||
socket.write(`:${this.servername} 005 ${caps}\r\n`);
|
socket.write(`:${this.servername} 005 ${caps}\r\n`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user