diff --git a/server/package-lock.json b/server/package-lock.json index 8f42463..8049c6c 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -18,6 +18,11 @@ "color-convert": "^2.0.1" } }, + "ascii-captcha": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/ascii-captcha/-/ascii-captcha-0.0.3.tgz", + "integrity": "sha1-NAtO1oVYOHEHsJVzBC/kc4v0mPk=" + }, "async": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", diff --git a/server/package.json b/server/package.json index f6a9a90..a6459d7 100644 --- a/server/package.json +++ b/server/package.json @@ -18,6 +18,7 @@ "author": "Marzavec", "license": "WTFPL", "dependencies": { + "ascii-captcha": "0.0.3", "chalk": "^3.0.0", "common-tags": "^1.8.0", "dateformat": "^3.0.3", diff --git a/server/src/commands/admin/addmod.js b/server/src/commands/admin/addmod.js index 0f443eb..3a6b140 100644 --- a/server/src/commands/admin/addmod.js +++ b/server/src/commands/admin/addmod.js @@ -2,14 +2,19 @@ Description: Adds the target trip to the mod list then elevates the uType */ -import * as UAC from '../utility/UAC/_info'; +import { + isAdmin, + isModerator, + levels, + getUserDetails, +} from '../utility/_UAC'; // module main export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin - if (!UAC.isAdmin(socket.level)) { + if (!isAdmin(socket.level)) { return server.police.frisk(socket.address, 20); } @@ -19,10 +24,20 @@ export async function run({ // find targets current connections const newMod = server.findSockets({ trip: payload.trip }); if (newMod.length !== 0) { + // build update notice with new privileges + const updateNotice = { + ...getUserDetails(newMod[0]), + ...{ + cmd: 'updateUser', + uType: 'mod', // @todo use legacyLevelToLabel from _LegacyFunctions.js + level: levels.moderator, + }, + }; + for (let i = 0, l = newMod.length; i < l; i += 1) { // upgrade privilages - newMod[i].uType = 'mod'; - newMod[i].level = UAC.levels.moderator; + newMod[i].uType = 'mod'; // @todo use legacyLevelToLabel from _LegacyFunctions.js + newMod[i].level = levels.moderator; // inform new mod server.send({ @@ -30,6 +45,14 @@ export async function run({ text: 'You are now a mod.', channel: newMod[i].channel, // @todo Multichannel }, newMod[i]); + + // notify channel + server.broadcast({ + ...updateNotice, + ...{ + channel: newMod[i].channel, + }, + }, { channel: newMod[i].channel }); } } @@ -45,7 +68,7 @@ export async function run({ cmd: 'info', text: `Added mod: ${payload.trip}`, channel: false, // @todo Multichannel, false for global info - }, { level: UAC.isModerator }); + }, { level: isModerator }); return true; } diff --git a/server/src/commands/admin/listusers.js b/server/src/commands/admin/listusers.js index ba5908a..850ae75 100644 --- a/server/src/commands/admin/listusers.js +++ b/server/src/commands/admin/listusers.js @@ -6,12 +6,14 @@ Description: Outputs all current channels and their user nicks */ -import * as UAC from '../utility/UAC/_info'; +import { + isAdmin, +} from '../utility/_UAC'; // module main export async function run({ server, socket }) { // increase rate limit chance and ignore if not admin - if (!UAC.isAdmin(socket.level)) { + if (!isAdmin(socket.level)) { return server.police.frisk(socket.address, 20); } diff --git a/server/src/commands/admin/reload.js b/server/src/commands/admin/reload.js index 83488e5..32ca04f 100644 --- a/server/src/commands/admin/reload.js +++ b/server/src/commands/admin/reload.js @@ -2,14 +2,17 @@ Description: Clears and resets the command modules, outputting any errors */ -import * as UAC from '../utility/UAC/_info'; +import { + isAdmin, + isModerator, +} from '../utility/_UAC'; // module main export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin - if (!UAC.isAdmin(socket.level)) { + if (!isAdmin(socket.level)) { return server.police.frisk(socket.address, 20); } @@ -37,7 +40,7 @@ export async function run({ cmd: 'info', text: loadResult, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); return true; } diff --git a/server/src/commands/admin/removemod.js b/server/src/commands/admin/removemod.js index 96ae439..85d5fd6 100644 --- a/server/src/commands/admin/removemod.js +++ b/server/src/commands/admin/removemod.js @@ -2,14 +2,18 @@ Description: Removes target trip from the config as a mod and downgrades the socket type */ -import * as UAC from '../utility/UAC/_info'; +import { + isAdmin, + isModerator, + levels, +} from '../utility/_UAC'; // module main export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin - if (!UAC.isAdmin(socket.level)) { + if (!isAdmin(socket.level)) { return server.police.frisk(socket.address, 20); } @@ -23,7 +27,7 @@ export async function run({ for (let i = 0, l = targetMod.length; i < l; i += 1) { // downgrade privilages targetMod[i].uType = 'user'; - targetMod[i].level = UAC.levels.default; + targetMod[i].level = levels.default; // inform ex-mod server.send({ @@ -48,7 +52,7 @@ export async function run({ cmd: 'info', text: `Removed mod: ${payload.trip}`, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); return true; } diff --git a/server/src/commands/admin/saveconfig.js b/server/src/commands/admin/saveconfig.js index 2ad8a6e..a24f422 100644 --- a/server/src/commands/admin/saveconfig.js +++ b/server/src/commands/admin/saveconfig.js @@ -2,12 +2,15 @@ Description: Writes the current config to disk */ -import * as UAC from '../utility/UAC/_info'; +import { + isAdmin, + isModerator, +} from '../utility/_UAC'; // module main export async function run({ core, server, socket }) { // increase rate limit chance and ignore if not admin - if (!UAC.isAdmin(socket.level)) { + if (!isAdmin(socket.level)) { return server.police.frisk(socket.address, 20); } @@ -25,7 +28,7 @@ export async function run({ core, server, socket }) { cmd: 'info', text: 'Config saved!', channel: false, // @todo Multichannel - }, { level: UAC.isModerator }); + }, { level: isModerator }); return true; } diff --git a/server/src/commands/admin/shout.js b/server/src/commands/admin/shout.js index bd3b10d..fa75058 100644 --- a/server/src/commands/admin/shout.js +++ b/server/src/commands/admin/shout.js @@ -2,12 +2,14 @@ Description: Emmits a server-wide message as `info` */ -import * as UAC from '../utility/UAC/_info'; +import { + isAdmin, +} from '../utility/_UAC'; // module main export async function run({ server, socket, payload }) { // increase rate limit chance and ignore if not admin - if (!UAC.isAdmin(socket.level)) { + if (!isAdmin(socket.level)) { return server.police.frisk(socket.address, 20); } diff --git a/server/src/commands/core/changecolor.js b/server/src/commands/core/changecolor.js new file mode 100644 index 0000000..afbb8f7 --- /dev/null +++ b/server/src/commands/core/changecolor.js @@ -0,0 +1,111 @@ +/* + Description: Allows calling client to change their nickname color +*/ + +// module support functions +const verifyColor = (color) => /(^[0-9A-F]{6}$)|(^[0-9A-F]{3}$)/i.test(color); + +// module main +export async function run({ + core, server, socket, payload, +}) { + const channel = socket.channel; + + if (server.police.frisk(socket.address, 6)) { + return server.reply({ + cmd: 'warn', // @todo Add numeric error code as `id` + text: 'You are changing colors too fast. Wait a moment before trying again.', + channel, // @todo Multichannel + }, socket); + } + + // verify user data is string + if (typeof payload.color !== 'string') { + return true; + } + + // make sure requested nickname meets standards + const newColor = payload.color.trim().toUpperCase().replace(/#/g, ''); + if (newColor !== 'RESET' && !verifyColor(newColor)) { + return server.reply({ + cmd: 'warn', + text: 'Invalid color! Color must be in hex value', + channel, // @todo Multichannel + }, socket); + } + + if (newColor === 'RESET') { + socket.color = false; + } else { + socket.color = newColor; + } + + // build update notice with new color + const updateNotice = { + ...getUserDetails(socket), + ...{ + cmd: 'updateUser', + channel: socket.channel, // @todo Multichannel + } + }; + + // notify channel that the user has changed their name + // @todo this should be sent to every channel the user is in (multichannel) + server.broadcast(updateNotice, { channel: socket.channel }); + + return true; +} + +// module hook functions +export function initHooks(server) { + server.registerHook('in', 'chat', this.colorCheck.bind(this), 29); +} + +// hooks chat commands checking for /color +export function colorCheck({ + core, server, socket, payload, +}) { + if (typeof payload.text !== 'string') { + return false; + } + + if (payload.text.startsWith('/color ')) { + const input = payload.text.split(' '); + + // If there is no color target parameter + if (input[1] === undefined) { + server.reply({ + cmd: 'warn', + text: 'Refer to `/help changecolor` for instructions on how to use this command.', + channel: socket.channel, // @todo Multichannel + }, socket); + + return false; + } + + this.run({ + core, + server, + socket, + payload: { + cmd: 'changecolor', + color: input[1], + }, + }); + + return false; + } + + return payload; +} + +// module meta +export const requiredData = ['color']; +export const info = { + name: 'changecolor', + description: 'This will change your nickname color', + usage: ` + API: { cmd: 'changecolor', color: '' } + Text: /color + Removal: /color reset`, +}; diff --git a/server/src/commands/core/changenick.js b/server/src/commands/core/changenick.js index 2dc55bd..558a1b8 100644 --- a/server/src/commands/core/changenick.js +++ b/server/src/commands/core/changenick.js @@ -4,17 +4,22 @@ Description: Allows calling client to change their current nickname */ -import * as UAC from '../utility/UAC/_info'; +import { + verifyNickname, + getUserDetails, +} from '../utility/_UAC'; // module main export async function run({ core, server, socket, payload, }) { + const channel = socket.channel; + if (server.police.frisk(socket.address, 6)) { return server.reply({ cmd: 'warn', // @todo Add numeric error code as `id` text: 'You are changing nicknames too fast. Wait a moment before trying again.', - channel: socket.channel, // @todo Multichannel + channel, // @todo Multichannel }, socket); } @@ -27,11 +32,11 @@ export async function run({ // make sure requested nickname meets standards const newNick = payload.nick.trim(); - if (!UAC.verifyNickname(newNick)) { + if (!verifyNickname(newNick)) { return server.reply({ cmd: 'warn', // @todo Add numeric error code as `id` text: 'Nickname must consist of up to 24 letters, numbers, and underscores', - channel: socket.channel, // @todo Multichannel + channel, // @todo Multichannel }, socket); } @@ -43,7 +48,7 @@ export async function run({ return server.reply({ cmd: 'warn', // @todo Add numeric error code as `id` text: 'You are not the admin, liar!', - channel: socket.channel, // @todo Multichannel + channel, // @todo Multichannel }, socket); } @@ -51,13 +56,13 @@ export async function run({ return server.reply({ cmd: 'warn', // @todo Add numeric error code as `id` text: 'You already have that name', - channel: socket.channel, // @todo Multichannel + channel, // @todo Multichannel }, socket); } // find any sockets that have the same nickname const userExists = server.findSockets({ - channel: socket.channel, + channel, nick: (targetNick) => targetNick.toLowerCase() === newNick.toLowerCase() // Allow them to rename themselves to a different case && targetNick != previousNick, @@ -69,37 +74,57 @@ export async function run({ return server.reply({ cmd: 'warn', // @todo Add numeric error code as `id` text: 'Nickname taken', - channel: socket.channel, // @todo Multichannel + channel, // @todo Multichannel }, socket); } - // build join and leave notices - // @todo this is a legacy client holdover, name changes in the future will - // have thieir own event + // build update notice with new nickname + const updateNotice = { + ...getUserDetails(socket), + ...{ + cmd: 'updateUser', + nick: newNick, + channel, // @todo Multichannel + } + }; + + // build join and leave notices for legacy clients const leaveNotice = { cmd: 'onlineRemove', + userid: socket.userid, nick: socket.nick, - channel: socket.channel, // @todo Multichannel + channel, // @todo Multichannel }; const joinNotice = { - cmd: 'onlineAdd', - nick: newNick, - trip: socket.trip || 'null', - hash: socket.hash, - channel: socket.channel, // @todo Multichannel + ...getUserDetails(socket), + ...{ + cmd: 'onlineAdd', + nick: newNick, + channel, // @todo Multichannel + }, }; - // broadcast remove event and join event with new name, this is to support legacy clients and bots - server.broadcast(leaveNotice, { channel: socket.channel }); - server.broadcast(joinNotice, { channel: socket.channel }); + // gather channel peers + const peerList = server.findSockets({ channel }); + for (let i = 0, l = peerList.length; i < l; i += 1) { + if (peerList[i].hcProtocol === 1) { + // send join/leave to legacy clients + server.send(leaveNotice, peerList[i]); + server.send(joinNotice, peerList[i]); + } else { + // send update info + // @todo this should be sent to every channel the client is in + server.send(updateNotice, peerList[i]); + } + } // notify channel that the user has changed their name server.broadcast({ cmd: 'info', text: `${socket.nick} is now ${newNick}`, - channel: socket.channel, // @todo Multichannel - }, { channel: socket.channel }); + channel, // @todo Multichannel + }, { channel }); // commit change to nickname socket.nick = newNick; // eslint-disable-line no-param-reassign diff --git a/server/src/commands/core/chat.js b/server/src/commands/core/chat.js index 579d7d5..65e26c4 100644 --- a/server/src/commands/core/chat.js +++ b/server/src/commands/core/chat.js @@ -2,7 +2,10 @@ Description: Rebroadcasts any `text` to all clients in a `channel` */ -import * as UAC from '../utility/UAC/_info'; +import { + isAdmin, + isModerator, +} from '../utility/_UAC'; // module support functions const parseText = (text) => { @@ -54,9 +57,9 @@ export async function run({ level: socket.level, }; - if (UAC.isAdmin(socket.level)) { + if (isAdmin(socket.level)) { outgoingPayload.admin = true; - } else if (UAC.isModerator(socket.level)) { + } else if (isModerator(socket.level)) { outgoingPayload.mod = true; } @@ -64,6 +67,10 @@ export async function run({ outgoingPayload.trip = socket.trip; /* @legacy */ } + if (socket.color) { + outgoingPayload.color = socket.color; + } + // broadcast to channel peers server.broadcast(outgoingPayload, { channel: socket.channel }); diff --git a/server/src/commands/core/join.js b/server/src/commands/core/join.js index 60250d0..9332c8c 100644 --- a/server/src/commands/core/join.js +++ b/server/src/commands/core/join.js @@ -4,7 +4,6 @@ Description: Adds requested channel into the calling clients "subscribed channels" */ -// import * as UAC from '../utility/UAC/_info'; import { canJoinChannel, } from '../utility/_Channels'; @@ -18,6 +17,7 @@ import { import { verifyNickname, getUserPerms, + getUserDetails, } from '../utility/_UAC'; // module main @@ -76,6 +76,7 @@ export async function run({ // get trip and level const { trip, level } = getUserPerms(pass, core.config, channel); + // store the user values const userInfo = { nick, @@ -84,6 +85,8 @@ export async function run({ hash: socket.hash, level, userid: socket.userid, + isBot: socket.isBot, + color: socket.color, channel, }; @@ -119,18 +122,14 @@ export async function run({ // send join announcement and prep online set reply for (let i = 0, l = newPeerList.length; i < l; i += 1) { server.reply(joinAnnouncement, newPeerList[i]); + nicks.push(newPeerList[i].nick); /* @legacy */ - users.push({ - nick: newPeerList[i].nick, - trip: newPeerList[i].trip, - uType: newPeerList[i].uType, /* @legacy */ - hash: newPeerList[i].hash, - level: newPeerList[i].level, - userid: newPeerList[i].userid, - channel, - isme: false, - isBot: newPeerList[i].isBot, + ...{ + channel, + isme: false, + }, + ...getUserDetails(newPeerList[i]), }); } diff --git a/server/src/commands/core/move.js b/server/src/commands/core/move.js index 8de0a71..d6537c5 100644 --- a/server/src/commands/core/move.js +++ b/server/src/commands/core/move.js @@ -2,6 +2,11 @@ Description: Changes the current channel of the calling socket @deprecated This module will be removed or replaced */ +import { + verifyNickname, + getUserPerms, + getUserDetails, +} from '../utility/_UAC'; // module main export async function run({ server, socket, payload }) { @@ -27,7 +32,7 @@ export async function run({ server, socket, payload }) { }, socket); } - if (payload.channel === socket.channel) { + if (payload.channel === socket.channel) { // @todo Multichannel update // they are trying to rejoin the channel return true; } @@ -52,41 +57,61 @@ export async function run({ server, socket, payload }) { server.reply({ cmd: 'onlineRemove', nick: peerList[i].nick, + userid: peerList[i].userid, channel: socket.channel, // @todo Multichannel }, socket); - if (socket.nick !== peerList[i].nick) { + if (socket.userid !== peerList[i].userid) { server.reply({ cmd: 'onlineRemove', nick: socket.nick, + userid: socket.userid, channel: socket.channel, // @todo Multichannel }, peerList[i]); } } } - // @todo import function from join module // broadcast join notice to new peers const newPeerList = server.findSockets({ channel: payload.channel }); const moveAnnouncement = { - cmd: 'onlineAdd', - nick: socket.nick, - trip: socket.trip || 'null', - hash: socket.hash, + ...{ + cmd: 'onlineAdd', + channel: payload.channel, // @todo Multichannel + }, + ...getUserDetails(socket), }; + const nicks = []; + const users = []; for (let i = 0, l = newPeerList.length; i < l; i += 1) { server.reply(moveAnnouncement, newPeerList[i]); - nicks.push(newPeerList[i].nick); + + nicks.push(newPeerList[i].nick); /* @legacy */ + users.push({ + ...{ + channel: payload.channel, + isme: false, + }, + ...getUserDetails(newPeerList[i]), + }); } - nicks.push(socket.nick); + nicks.push(socket.nick); /* @legacy */ + users.push({ + ...{ + channel: payload.channel, + isme: true, + }, + ...getUserDetails(socket) + }); // reply with new user list server.reply({ cmd: 'onlineSet', nicks, + users, channel: socket.channel, // @todo Multichannel (!) }, socket); diff --git a/server/src/commands/core/session.js b/server/src/commands/core/session.js index 2d02810..1e4249b 100644 --- a/server/src/commands/core/session.js +++ b/server/src/commands/core/session.js @@ -55,6 +55,7 @@ export async function run({ server, socket, payload }) { socket.userid = Math.floor(Math.random() * 9999999999999); socket.hash = server.getSocketHash(socket); socket.isBot = payload.isBot || false; + socket.color = false; // dispatch info server.reply({ diff --git a/server/src/commands/core/whisper.js b/server/src/commands/core/whisper.js index a2d85dd..6705cb7 100644 --- a/server/src/commands/core/whisper.js +++ b/server/src/commands/core/whisper.js @@ -90,6 +90,8 @@ export async function run({ server, socket, payload }) { server.reply(outgoingPayload, socket); } + targetUser.whisperReply = socket.nick; + return true; } @@ -161,6 +163,7 @@ export function whisperCheck({ payload: { cmd: 'whisper', nick: socket.whisperReply, + channel: socket.channel, // @todo Multichannel text: whisperText, }, }); diff --git a/server/src/commands/mod/ban.js b/server/src/commands/mod/ban.js index 2db87cd..2529633 100644 --- a/server/src/commands/mod/ban.js +++ b/server/src/commands/mod/ban.js @@ -3,7 +3,11 @@ Description: Adds the target socket's ip to the ratelimiter */ -import * as UAC from '../utility/UAC/_info'; +import { + isModerator, + getUserDetails, + levels, +} from '../utility/_UAC'; import { Errors, } from '../utility/_Constants'; @@ -16,7 +20,7 @@ export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin or mod - if (!UAC.isModerator(socket.level)) { + if (!isModerator(socket.level)) { return server.police.frisk(socket.address, 10); } @@ -62,9 +66,9 @@ export async function run({ server.broadcast({ cmd: 'info', text: `Banned ${targetNick}`, - user: UAC.getUserDetails(targetUser), + user: getUserDetails(targetUser), channel: socket.channel, // @todo Multichannel - }, { channel: socket.channel, level: (level) => level < UAC.levels.moderator }); + }, { channel: socket.channel, level: (level) => level < levels.moderator }); // notify mods server.broadcast({ @@ -72,9 +76,9 @@ export async function run({ text: `${socket.nick}#${socket.trip} banned ${targetNick} in ${payload.channel}, userhash: ${targetUser.hash}`, channel: socket.channel, // @todo Multichannel inChannel: payload.channel, - user: UAC.getUserDetails(targetUser), - banner: UAC.getUserDetails(socket), - }, { level: UAC.isModerator }); + user: getUserDetails(targetUser), + banner: getUserDetails(socket), + }, { level: isModerator }); // force connection closed targetUser.terminate(); diff --git a/server/src/commands/mod/dumb.js b/server/src/commands/mod/dumb.js index 558f02e..5b66684 100644 --- a/server/src/commands/mod/dumb.js +++ b/server/src/commands/mod/dumb.js @@ -6,7 +6,9 @@ * Author: simple */ -import * as UAC from '../utility/UAC/_info'; +import { + isModerator, +} from '../utility/_UAC'; import { Errors, } from '../utility/_Constants'; @@ -26,7 +28,7 @@ export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin or mod - if (!UAC.isModerator(socket.level)) { + if (!isModerator(socket.level)) { return server.police.frisk(socket.address, 10); } @@ -77,7 +79,7 @@ export async function run({ cmd: 'info', text: `${socket.nick}#${socket.trip} muzzled ${targetUser.nick} in ${payload.channel}, userhash: ${targetUser.hash}`, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); return true; } diff --git a/server/src/commands/mod/kick.js b/server/src/commands/mod/kick.js index 6dc58ea..c476e57 100644 --- a/server/src/commands/mod/kick.js +++ b/server/src/commands/mod/kick.js @@ -4,7 +4,11 @@ Description: Forces a change on the target(s) socket's channel, then broadcasts event */ -import * as UAC from '../utility/UAC/_info'; +import { + isModerator, + levels, + getUserDetails, +} from '../utility/_UAC'; import { Errors, } from '../utility/_Constants'; @@ -17,7 +21,7 @@ export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin or mod - if (!UAC.isModerator(socket.level)) { + if (!isModerator(socket.level)) { return server.police.frisk(socket.address, 10); } @@ -76,17 +80,15 @@ export async function run({ // Announce the kicked clients arrival in destChannel and that they were kicked // Before they arrive, so they don't see they got moved - for (let i = 0; i < kicked.length; i += 1) { - server.broadcast({ + const moveAnnouncement = { + ...getUserDetails(socket), + ...{ cmd: 'onlineAdd', - nick: kicked[i].nick, - trip: kicked[i].trip || 'null', - uType: 'user', - hash: kicked[i].hash, - level: UAC.levels.default, - userid: kicked[i].userid, - channel: destChannel, - }, { channel: destChannel }); + channel: destChannel, // @todo Multichannel + }, + }; + for (let i = 0; i < kicked.length; i += 1) { + server.broadcast(moveAnnouncement, { channel: destChannel }); } // Move all kicked clients to the new channel @@ -98,7 +100,7 @@ export async function run({ cmd: 'info', text: `${kicked[i].nick} was banished to ?${destChannel}`, channel: socket.channel, // @todo Multichannel - }, { channel: socket.channel, level: UAC.isModerator }); + }, { channel: socket.channel, level: isModerator }); console.log(`${socket.nick} [${socket.trip}] kicked ${kicked[i].nick} in ${socket.channel} to ${destChannel} `); } @@ -118,7 +120,7 @@ export async function run({ cmd: 'info', text: `Kicked ${kicked.map((k) => k.nick).join(', ')}`, channel: socket.channel, // @todo Multichannel - }, { channel: socket.channel, level: (level) => level < UAC.levels.moderator }); + }, { channel: socket.channel, level: (level) => level < levels.moderator }); // stats are fun core.stats.increment('users-kicked', kicked.length); diff --git a/server/src/commands/mod/moveuser.js b/server/src/commands/mod/moveuser.js index eb17cf8..a616620 100644 --- a/server/src/commands/mod/moveuser.js +++ b/server/src/commands/mod/moveuser.js @@ -3,12 +3,15 @@ @deprecated This module will be removed or replaced */ -import * as UAC from '../utility/UAC/_info'; +import { + isModerator, + getUserDetails, +} from '../utility/_UAC'; // module main export async function run({ server, socket, payload }) { // increase rate limit chance and ignore if not admin or mod - if (!UAC.isModerator(socket.level)) { + if (!isModerator(socket.level)) { return server.police.frisk(socket.address, 10); } @@ -59,6 +62,7 @@ export async function run({ server, socket, payload }) { for (let i = 0, l = peerList.length; i < l; i += 1) { server.reply({ cmd: 'onlineRemove', + userid: peerList[i].userid, nick: peerList[i].nick, channel: socket.channel, // @todo Multichannel }, badClient); @@ -66,6 +70,7 @@ export async function run({ server, socket, payload }) { if (badClient.nick !== peerList[i].nick) { server.reply({ cmd: 'onlineRemove', + userid: badClient.userid, nick: badClient.nick, channel: socket.channel, // @todo Multichannel }, peerList[i]); @@ -73,27 +78,43 @@ export async function run({ server, socket, payload }) { } } - // @todo import from join module + const newPeerList = server.findSockets({ channel: payload.channel }); const moveAnnouncement = { - cmd: 'onlineAdd', - nick: badClient.nick, - trip: badClient.trip || 'null', - hash: server.getSocketHash(badClient), + ...getUserDetails(badClient), + ...{ + cmd: 'onlineAdd', + channel: payload.channel, // @todo Multichannel + }, }; const nicks = []; - + const users = []; for (let i = 0, l = newPeerList.length; i < l; i += 1) { server.reply(moveAnnouncement, newPeerList[i]); - nicks.push(newPeerList[i].nick); + + nicks.push(newPeerList[i].nick); /* @legacy */ + users.push({ + ...{ + channel, + isme: false, + }, + ...getUserDetails(newPeerList[i]), + }); } - nicks.push(badClient.nick); + nicks.push(badClient.nick); /* @legacy */ + users.push({ + ...{ + isme: true, + }, + ...getUserDetails(badClient), + }); server.reply({ cmd: 'onlineSet', - nicks, - channel: socket.channel, // @todo Multichannel (!) + nicks, /* @legacy */ + users, + channel, // @todo Multichannel (?) }, badClient); badClient.channel = payload.channel; @@ -101,7 +122,7 @@ export async function run({ server, socket, payload }) { server.broadcast({ cmd: 'info', text: `${badClient.nick} was moved into ?${payload.channel}`, - channel: socket.channel, // @todo Multichannel + channel: payload.channel, // @todo Multichannel }, { channel: payload.channel }); return true; diff --git a/server/src/commands/mod/speak.js b/server/src/commands/mod/speak.js index a197d01..15b3c10 100644 --- a/server/src/commands/mod/speak.js +++ b/server/src/commands/mod/speak.js @@ -5,7 +5,9 @@ * Author: simple */ -import * as UAC from '../utility/UAC/_info'; +import { + isModerator, +} from '../utility/_UAC'; // module constructor export function init(core) { @@ -19,7 +21,7 @@ export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin or mod - if (!UAC.isModerator(socket.level)) { + if (!isModerator(socket.level)) { return server.police.frisk(socket.address, 10); } @@ -40,7 +42,7 @@ export async function run({ cmd: 'info', text: `${socket.nick} unmuzzled all users`, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); } } else if (payload.hash === '*') { core.muzzledHashes = {}; @@ -49,7 +51,7 @@ export async function run({ cmd: 'info', text: `${socket.nick} unmuzzled all users`, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); } // find target & remove mute status @@ -67,7 +69,7 @@ export async function run({ cmd: 'info', text: `${socket.nick}#${socket.trip} unmuzzled : ${target}`, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); return true; } diff --git a/server/src/commands/mod/unban.js b/server/src/commands/mod/unban.js index a13dea3..5554585 100644 --- a/server/src/commands/mod/unban.js +++ b/server/src/commands/mod/unban.js @@ -4,14 +4,16 @@ Description: Removes a target ip from the ratelimiter */ -import * as UAC from '../utility/UAC/_info'; +import { + isModerator, +} from '../utility/_UAC'; // module main export async function run({ core, server, socket, payload, }) { // increase rate limit chance and ignore if not admin or mod - if (!UAC.isModerator(socket.level)) { + if (!isModerator(socket.level)) { return server.police.frisk(socket.address, 10); } @@ -56,7 +58,7 @@ export async function run({ cmd: 'info', text: `${socket.nick}#${socket.trip} unbanned: ${target}`, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); // stats are fun core.stats.decrement('users-banned'); diff --git a/server/src/commands/mod/unbanall.js b/server/src/commands/mod/unbanall.js index 3d37efd..5705704 100644 --- a/server/src/commands/mod/unbanall.js +++ b/server/src/commands/mod/unbanall.js @@ -4,12 +4,14 @@ Description: Clears all bans and ratelimits */ -import * as UAC from '../utility/UAC/_info'; +import { + isModerator, +} from '../utility/_UAC'; // module main export async function run({ core, server, socket }) { // increase rate limit chance and ignore if not admin or mod - if (!UAC.isModerator(socket.level)) { + if (!isModerator(socket.level)) { return server.police.frisk(socket.address, 10); } @@ -32,7 +34,7 @@ export async function run({ core, server, socket }) { cmd: 'info', text: `${socket.nick}#${socket.trip} unbanned all ip addresses`, channel: false, // @todo Multichannel, false for global - }, { level: UAC.isModerator }); + }, { level: isModerator }); return true; } diff --git a/server/src/commands/utility/UAC/_info.js b/server/src/commands/utility/UAC/_info.js deleted file mode 100644 index a43f0e6..0000000 --- a/server/src/commands/utility/UAC/_info.js +++ /dev/null @@ -1,119 +0,0 @@ -// NOTE: this has been moved into /utility/ as _UAC.js -/** - * User Account Control information containing level constants - * and simple helper functions related to users - * @property {Object} levels - Defines labels for default permission ranges - * @author MinusGix ( https://github.com/MinusGix ) - * @version v1.0.0 - * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) - */ - -/** - * Object defining labels for default permission ranges - * @typedef {Object} levels - * @property {number} admin Global administrator range - * @property {number} moderator Global moderator range - * @property {number} channelOwner Local administrator range - * @property {number} channelModerator Local moderator range - * @property {number} channelTrusted Local (non-public) channel trusted - * @property {number} trustedUser Public channel trusted - * @property {number} default Default user level - */ -export const levels = { - admin: 9999999, - moderator: 999999, - - channelOwner: 99999, - channelModerator: 9999, - channelTrusted: 8999, - - trustedUser: 500, - default: 100, -}; - -/** - * Returns true if target level is equal or greater than the global admin level - * @public - * @param {number} level Level to verify - * @return {boolean} - */ -export function isAdmin(level) { - return level >= levels.admin; -} - -/** - * Returns true if target level is equal or greater than the global moderator level - * @public - * @param {number} level Level to verify - * @return {boolean} - */ -export function isModerator(level) { - return level >= levels.moderator; -} - -/** - * Returns true if target level is equal or greater than the channel owner level - * @public - * @param {number} level Level to verify - * @return {boolean} - */ -export function isChannelOwner(level) { - return level >= levels.channelOwner; -} - -/** - * Returns true if target level is equal or greater than the channel moderator level - * @public - * @param {number} level Level to verify - * @return {boolean} - */ -export function isChannelModerator(level) { - return level >= levels.channelModerator; -} - -/** - * Returns true if target level is equal or greater than the channel trust level - * @public - * @param {number} level Level to verify - * @return {boolean} - */ -export function isChannelTrusted(level) { - return level >= levels.channelTrusted; -} - -/** - * Returns true if target level is equal or greater than a trusted user - * @public - * @param {number} level Level to verify - * @return {boolean} - */ -export function isTrustedUser(level) { - return level >= levels.trustedUser; -} - -/** - * Return an object containing public information about the socket - * @public - * @param {WebSocket} socket Target client - * @return {Object} - */ -export function getUserDetails(socket) { - return { - uType: socket.uType, - nick: socket.nick, - trip: socket.trip || 'null', - hash: socket.hash, - level: socket.level, - userid: socket.userid, - }; -} - -/** - * Returns true if the nickname is valid - * @public - * @param {string} nick Nickname to verify - * @return {boolean} - */ -export function verifyNickname(nick) { - return /^[a-zA-Z0-9_]{1,24}$/.test(nick); -} diff --git a/server/src/commands/utility/_LegacyFunctions.js b/server/src/commands/utility/_LegacyFunctions.js index 6662267..7c27b7b 100644 --- a/server/src/commands/utility/_LegacyFunctions.js +++ b/server/src/commands/utility/_LegacyFunctions.js @@ -22,6 +22,7 @@ export function upgradeLegacyJoin(server, socket, payload) { // these would have been applied in the `session` module, apply it now socket.hash = server.getSocketHash(socket); socket.isBot = false; + socket.color = false; // pull the password from the nick const nickArray = payload.nick.split('#', 2); diff --git a/server/src/commands/utility/_UAC.js b/server/src/commands/utility/_UAC.js index aee98d2..52e5e7a 100644 --- a/server/src/commands/utility/_UAC.js +++ b/server/src/commands/utility/_UAC.js @@ -104,12 +104,14 @@ export function isTrustedUser(level) { */ export function getUserDetails(socket) { return { - uType: socket.uType, nick: socket.nick, - trip: socket.trip || 'null', + trip: socket.trip || '', + uType: socket.uType, hash: socket.hash, level: socket.level, userid: socket.userid, + isBot: socket.isBot, + color: socket.color, }; } @@ -154,13 +156,12 @@ export function getUserPerms(pass, config, channel) { }; } + let level = levels.default; + // check if user is global mod config.mods.forEach((mod) => { // eslint-disable-line consistent-return if (trip === mod.trip) { - return { - trip, - level: levels.moderator, - }; + level = levels.moderator; } }); @@ -181,6 +182,6 @@ export function getUserPerms(pass, config, channel) { return { trip, - level: levels.default, + level, }; }