From 28cef978aa3dbb046229f239d23267f5dfa53ed8 Mon Sep 17 00:00:00 2001 From: zefie Date: Fri, 13 Jun 2025 12:52:08 -0400 Subject: [PATCH] invites, +m, +i --- zefie_wtvp_minisrv/includes/classes/WTVIRC.js | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/zefie_wtvp_minisrv/includes/classes/WTVIRC.js b/zefie_wtvp_minisrv/includes/classes/WTVIRC.js index c33a9a51..3df89241 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTVIRC.js +++ b/zefie_wtvp_minisrv/includes/classes/WTVIRC.js @@ -21,6 +21,7 @@ class WTVIRC { this.channelops = new Map(); // channel -> Set of operators this.channelvoices = new Map(); // channel -> Set of voiced users this.channeltopics = new Map(); // channel -> topic + this.channelinvites = new Map(); // channel -> Set of invited users this.channelbans = new Map(); // channel -> Set of banned users this.channelmodes = new Map(); // channel -> modes this.nicknames = new Map(); // socket -> nickname @@ -165,6 +166,66 @@ class WTVIRC { const modes = this.channelmodes.get(channel) || ''; socket.write(`:${this.servername} 324 ${nickname} ${channel} ${modes}\r\n`); break; + } else if (mode.startsWith('+m')) { + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '') + 'm'); + socket.write(`:${this.servername} 324 ${nickname} ${channel} +m\r\n`); + break; + } else if (mode.startsWith('-m')) { + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '').replace('m', '')); + socket.write(`:${this.servername} 324 ${nickname} ${channel} -m\r\n`); + break; + } else if (mode.startsWith('+l')) { + if (params.length < 3) { + socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`); + break; + } + const limit = parseInt(params[2], 10); + if (isNaN(limit) || limit < 0) { + socket.write(`:${this.servername} 501 ${nickname} :Invalid channel limit\r\n`); + break; + } + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '') + `l${limit}`); + socket.write(`:${this.servername} 324 ${nickname} ${channel} +l ${limit}\r\n`); + break; + } else if (mode.startsWith('-l')) { + if (params.length < 3) { + socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`); + break; + } + const limit = parseInt(params[2], 10); + if (isNaN(limit) || limit < 0) { + socket.write(`:${this.servername} 501 ${nickname} :Invalid channel limit\r\n`); + break; + } + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '').replace(`l${limit}`, '')); + socket.write(`:${this.servername} 324 ${nickname} ${channel} -l ${limit}\r\n`); + break; + } else if (mode.startsWith('+k')) { + if (params.length < 3) { + socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`); + break; + } + const key = params[2]; + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '') + `k${key}`); + socket.write(`:${this.servername} 324 ${nickname} ${channel} +k ${key}\r\n`); + break; + } else if (mode.startsWith('-k')) { + if (params.length < 3) { + socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`); + break; + } + const key = params[2]; + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '').replace(`k${key}`, '')); + socket.write(`:${this.servername} 324 ${nickname} ${channel} -k ${key}\r\n`); + break; + } else if (mode.startsWith('+i')) { + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '') + 'i'); + socket.write(`:${this.servername} 324 ${nickname} ${channel} +i\r\n`); + break; + } else if (mode.startsWith('-i')) { + this.channelmodes.set(channel, (this.channelmodes.get(channel) || '').replace('i', '')); + socket.write(`:${this.servername} 324 ${nickname} ${channel} -i\r\n`); + break; } else if (mode.startsWith('+o')) { if (!this.channelops.has(channel) || this.channelops.get(channel) === true) { socket.write(`:${this.servername} 482 ${nickname} ${channel} :You're not channel operator\r\n`); @@ -372,6 +433,34 @@ class WTVIRC { continue; // Skip joining this channel } } + if (this.channelmodes.has(ch)) { + const modes = this.channelmodes.get(ch); + const keyMatch = modes.match(/k([^\s]+)/); + if (keyMatch) { + const channelKey = keyMatch[1]; + // The key must be provided as the second parameter in the JOIN command + // params[1] is the key for the first channel, params[2] for the second, etc. + // For simplicity, assume only one channel per JOIN or the key is always params[1] + const providedKey = params[1]; + if (!providedKey || providedKey !== channelKey) { + socket.write(`:${this.servername} 475 ${nickname} ${ch} :Cannot join channel (+k)\r\n`); + continue; // Skip joining this channel + } + } + if (this.channelmodes.has(ch) && this.channelmodes.get(ch).includes('i')) { + // Channel is invite-only (+i) + // For simplicity, let's assume you have an invited list per channel (not implemented yet) + // We'll use a Map: this.channelinvites = new Map(); // channel -> Set of invited nicks + if (!this.channelinvites) this.channelinvites = new Map(); + const invited = this.channelinvites.get(ch) || new Set(); + if (!invited.has(nickname)) { + socket.write(`:${this.servername} 473 ${nickname} ${ch} :Cannot join channel (+i)\r\n`); + continue; // Skip joining this channel + } + invited.delete(nickname); + this.channelinvites.set(ch, invited); + } + } // Recursively process each channel join const joinLine = `JOIN ${ch}`; // Simulate a JOIN command for each channel @@ -456,6 +545,51 @@ class WTVIRC { } } break; + case 'INVITE': + if (!registered) { + socket.write(`:${this.servername} 451 ${nickname} :You have not registered\r\n`); + break; + } + if (params.length < 2) { + socket.write(`:${this.servername} 461 ${nickname} INVITE :Not enough parameters\r\n`); + break; + } + const invitee = params[0]; + channel = params[1]; + if (!this.channels.has(channel)) { + socket.write(`:${this.servername} 403 ${nickname} ${channel} :No such channel\r\n`); + break; + } + if (!this.channelops.has(channel) || this.channelops.get(channel) === true) { + socket.write(`:${this.servername} 482 ${nickname} ${channel} :You're not channel operator\r\n`); + break; + } else { + if (!this.channelops.get(channel).has(nickname)) { + socket.write(`:${this.servername} 482 ${nickname} ${channel} :You're not channel operator\r\n`); + break; + } + } + if (!this.nicknames.has(socket)) { + socket.write(`:${this.servername} 401 ${nickname} ${invitee} :No such nick/channel\r\n`); + break; + } + const inviteeSocket = Array.from(this.nicknames.keys()).find(s => this.nicknames.get(s) === invitee); + if (!inviteeSocket) { + socket.write(`:${this.servername} 401 ${nickname} ${invitee} :No such nick/channel\r\n`); + break; + } + if (!this.channels.has(channel) || !this.channels.get(channel).has(invitee)) { + if (!this.channelinvites) this.channelinvites = new Map(); + const invited = this.channelinvites.get(channel) || new Set(); + invited.add(invitee); + this.channelinvites.set(channel, invited); + socket.write(`:${this.servername} 341 ${nickname} ${invitee} ${channel} :Invited to channel\r\n`); + inviteeSocket.write(`:${this.servername} 341 ${nickname} ${invitee} ${channel} :You have been invited to join ${channel}\r\n`); + break; + } else { + socket.write(`:${this.servername} 443 ${nickname} ${invitee} ${channel} :${invitee} is already on that channel\r\n`); + break; + } case 'LIST': if (!registered) { socket.write(`:${this.servername} 451 ${nickname} :You have not registered\r\n`); @@ -519,6 +653,19 @@ class WTVIRC { break; } if (params[0]) { + const target = params[0]; + if (target.startsWith('#')) { + // Channel message + if (this.channelmodes.has(target) && this.channelmodes.get(target).includes('m')) { + // Channel is moderated (+m) + const voices = this.channelvoices.get(target) || new Set(); + const ops = this.channelops.get(target) || new Set(); + if (!(voices.has(nickname) || ops.has(nickname))) { + socket.write(`:${this.servername} 404 ${nickname} ${target} :Cannot send to channel (+m)\r\n`); + return; + } + } + } const msg = line.slice(line.indexOf(':', 1) + 1); this.broadcast(`:${nickname}!${username}@${host} PRIVMSG ${params[0]} :${msg}\r\n`, socket); }