1
0
mirror of https://github.com/hack-chat/main.git synced 2024-03-22 13:20:33 +08:00
hack-chat-main/commands/core/join.js

309 lines
8.0 KiB
JavaScript
Raw Permalink Normal View History

2020-09-17 13:44:32 +08:00
/* eslint no-param-reassign: 0 */
2022-06-23 00:32:51 +08:00
/* eslint import/no-cycle: [0, { ignoreExternal: true }] */
2020-09-17 13:44:32 +08:00
2022-06-23 00:32:51 +08:00
/**
* @author Marzavec ( https://github.com/marzavec )
* @summary Join target channel
* @version 1.0.0
* @description Join the target channel using the supplied nick and password
* @module join
*/
2018-03-10 15:47:00 +08:00
2022-06-23 00:32:51 +08:00
import {
getSession,
} from './session.js';
import {
canJoinChannel,
2022-06-23 00:32:51 +08:00
socketInChannel,
getChannelSettings,
2022-06-23 00:32:51 +08:00
} from '../utility/_Channels.js';
import {
Errors,
SystemMOTDs,
2022-06-23 00:32:51 +08:00
} from '../utility/_Constants.js';
import {
upgradeLegacyJoin,
legacyLevelToLabel,
2022-06-23 00:32:51 +08:00
} from '../utility/_LegacyFunctions.js';
import {
verifyNickname,
getUserPerms,
2020-11-07 05:16:43 +08:00
getUserDetails,
2023-12-27 16:51:52 +08:00
isModerator,
getAppearance,
2022-06-23 00:32:51 +08:00
} from '../utility/_UAC.js';
2022-06-23 00:32:51 +08:00
/**
* Executes when invoked by a remote client
* @param {Object} env - Environment object with references to core, server, socket & payload
2022-06-23 00:32:51 +08:00
* @public
* @return {void}
*/
2020-09-17 13:44:32 +08:00
export async function run({
core, server, socket, payload,
2022-06-23 00:32:51 +08:00
}) {
// check for spam
2023-12-27 16:26:49 +08:00
if (server.police.frisk(socket, 3)) {
return server.reply({
cmd: 'warn',
2019-11-07 15:35:23 +08:00
text: 'You are joining channels too fast. Wait a moment and try again.',
id: Errors.Join.RATELIMIT,
2020-10-10 13:34:59 +08:00
channel: false, // @todo Multichannel, false for global event
}, socket);
}
// `join` is the legacy entry point, check if it needs to be upgraded
2023-12-22 15:14:03 +08:00
if (typeof socket.hcProtocol === 'undefined' || socket.hcProtocol === 1) {
payload = upgradeLegacyJoin(server, socket, payload);
}
// store payload values
const { channel, nick, pass } = payload;
// check if a client is able to join target channel
const mayJoin = canJoinChannel(channel, socket);
if (mayJoin !== true) {
return server.reply({
cmd: 'warn',
text: 'You may not join that channel.',
id: mayJoin,
2020-10-10 13:34:59 +08:00
channel: false, // @todo Multichannel, false for global event
2018-03-10 15:47:00 +08:00
}, socket);
}
2018-06-04 15:07:24 +08:00
// calling socket already in a channel
// @todo multichannel update, will remove
2018-03-10 15:47:00 +08:00
if (typeof socket.channel !== 'undefined') {
2020-09-08 12:51:47 +08:00
return server.reply({
2024-01-09 03:09:59 +08:00
cmd: 'warn',
2020-09-08 12:51:47 +08:00
text: 'Joining more than one channel is not currently supported',
id: Errors.Join.ALREADY_JOINED,
2020-10-10 13:34:59 +08:00
channel: false, // @todo Multichannel, false for global event
2020-09-08 12:51:47 +08:00
}, socket);
2018-03-10 15:47:00 +08:00
}
// end todo
2018-03-10 15:47:00 +08:00
// validates the user input for `nick`
2020-10-17 12:36:28 +08:00
if (verifyNickname(nick, socket) !== true) {
return server.reply({
cmd: 'warn',
text: 'Nickname must consist of up to 24 letters, numbers, and underscores',
id: Errors.Join.INVALID_NICK,
2020-10-10 13:34:59 +08:00
channel: false, // @todo Multichannel, false for global event
}, socket);
}
// get trip and level
2022-06-23 00:32:51 +08:00
const { trip, level } = getUserPerms(pass, core.saltKey, core.appConfig.data, channel);
2020-11-10 03:55:54 +08:00
const channelSettings = getChannelSettings(core.appConfig.data, channel);
if (level < channelSettings.lockLevel) {
return server.reply({
cmd: 'warn',
text: 'You may not join that channel.',
2024-01-09 03:09:59 +08:00
id: Errors.Join.CHANNEL_LOCKED,
channel: false, // @todo Multichannel, false for global event
}, socket);
}
const { color, flair } = getAppearance(level);
socket.color = color;
socket.flair = flair;
// store the user values
const userInfo = {
nick,
trip,
uType: legacyLevelToLabel(level),
hash: socket.hash,
level,
userid: socket.userid,
2020-11-07 05:16:43 +08:00
isBot: socket.isBot,
color,
flair,
channel,
};
2018-03-10 15:47:00 +08:00
2018-06-04 15:07:24 +08:00
// check if the nickname already exists in the channel
2019-11-07 15:35:23 +08:00
const userExists = server.findSockets({
channel,
2019-11-07 15:35:23 +08:00
nick: (targetNick) => targetNick.toLowerCase() === userInfo.nick.toLowerCase(),
2018-04-29 13:29:38 +08:00
});
2018-03-10 15:47:00 +08:00
2018-04-29 13:29:38 +08:00
if (userExists.length > 0) {
2018-06-04 15:07:24 +08:00
// that nickname is already in that channel
return server.reply({
cmd: 'warn',
2019-11-07 15:35:23 +08:00
text: 'Nickname taken',
id: Errors.Join.NAME_TAKEN,
2020-10-10 13:34:59 +08:00
channel: false, // @todo Multichannel, false for global event
2018-04-29 13:29:38 +08:00
}, socket);
2018-03-10 15:47:00 +08:00
}
2018-06-04 15:07:24 +08:00
// prepare to notify channel peers
const newPeerList = server.findSockets({ channel });
2020-03-13 02:28:20 +08:00
const nicks = []; /* @legacy */
const users = [];
const joinAnnouncement = { ...{ cmd: 'onlineAdd' }, ...userInfo };
2018-06-04 15:07:24 +08:00
// send join announcement and prep online set reply
2019-11-07 15:35:23 +08:00
for (let i = 0, l = newPeerList.length; i < l; i += 1) {
2018-04-29 13:29:38 +08:00
server.reply(joinAnnouncement, newPeerList[i]);
2020-11-10 03:55:54 +08:00
2020-03-13 02:28:20 +08:00
nicks.push(newPeerList[i].nick); /* @legacy */
users.push({
2020-11-07 05:16:43 +08:00
...{
channel,
isme: false,
},
...getUserDetails(newPeerList[i]),
2020-03-13 02:28:20 +08:00
});
2018-04-29 13:29:38 +08:00
}
2018-03-10 15:47:00 +08:00
2018-06-04 15:07:24 +08:00
// store user info
socket.nick = userInfo.nick;
2020-03-13 02:28:20 +08:00
socket.trip = userInfo.trip;
socket.level = userInfo.level;
socket.uType = userInfo.uType; /* @legacy */
socket.channel = channel; /* @legacy */
// @todo multi-channel patch
// socket.channels.push(channel);
2022-06-23 00:32:51 +08:00
socket.channels = [channel];
2020-03-13 02:28:20 +08:00
2023-12-27 16:38:18 +08:00
// global mod perks
if (isModerator(socket.level)) {
socket.ratelimitImmune = true;
}
nicks.push(userInfo.nick); /* @legacy */
2020-10-15 12:43:38 +08:00
users.push({ ...{ isme: true, isBot: socket.isBot }, ...userInfo });
2018-03-10 15:47:00 +08:00
2018-06-04 15:07:24 +08:00
// reply with channel peer list
2018-03-10 15:47:00 +08:00
server.reply({
cmd: 'onlineSet',
2020-03-13 02:28:20 +08:00
nicks, /* @legacy */
users,
2020-10-10 13:34:59 +08:00
channel, // @todo Multichannel (?)
2018-03-10 15:47:00 +08:00
}, socket);
2024-01-09 03:09:59 +08:00
let { motd } = channelSettings;
if (motd === '') {
motd = SystemMOTDs[Math.floor(Math.random() * SystemMOTDs.length)];
}
server.reply({
2024-01-09 03:09:59 +08:00
cmd: 'info', // @todo Add numeric info code as `id`
text: motd,
channel,
}, socket);
2022-06-23 00:32:51 +08:00
// update client with new session info
server.reply({
cmd: 'session',
restored: false,
token: getSession(socket, core),
channels: socket.channels,
}, socket);
2018-06-04 15:07:24 +08:00
// stats are fun
2019-03-19 14:36:21 +08:00
core.stats.increment('users-joined');
2018-03-10 15:47:00 +08:00
2019-11-07 15:35:23 +08:00
return true;
}
2022-06-23 00:32:51 +08:00
export function restoreJoin({
server, socket, channel,
}) {
// check if a client is able to join target channel
const mayJoin = canJoinChannel(channel, socket);
if (mayJoin !== true) {
return server.reply({
cmd: 'warn',
text: 'You may not join that channel.',
id: mayJoin,
channel: false, // @todo Multichannel, false for global event
}, socket);
}
// store the user values
const userInfo = {
nick: socket.nick,
trip: socket.trip,
uType: legacyLevelToLabel(socket.level),
hash: socket.hash,
level: socket.level,
userid: socket.userid,
isBot: socket.isBot,
color: socket.color,
channel,
};
// prepare to notify channel peers
const newPeerList = server.findSockets({ channel });
const nicks = []; /* @legacy */
const users = [];
const joinAnnouncement = { ...{ cmd: 'onlineAdd' }, ...userInfo };
// build update notice with new privileges
const updateAnnouncement = {
...getUserDetails(socket),
...{
cmd: 'updateUser',
online: true,
},
};
const isDuplicate = socketInChannel(server, channel, socket);
// send join announcement and prep online set reply
for (let i = 0, l = newPeerList.length; i < l; i += 1) {
if (isDuplicate) {
server.reply(updateAnnouncement, newPeerList[i]);
} else {
server.reply(joinAnnouncement, newPeerList[i]);
}
nicks.push(newPeerList[i].nick); /* @legacy */
users.push({
...{
channel,
isme: false,
},
...getUserDetails(newPeerList[i]),
});
}
nicks.push(userInfo.nick); /* @legacy */
users.push({ ...{ isme: true, isBot: socket.isBot }, ...userInfo });
// reply with channel peer list
server.reply({
cmd: 'onlineSet',
nicks, /* @legacy */
users,
channel, // @todo Multichannel (?)
}, socket);
socket.channel = channel; /* @legacy */
socket.channels.push(channel);
return true;
}
/**
* Module meta information
* @public
* @typedef {Object} join/info
* @property {string} name - Module command name
* @property {string} category - Module category name
* @property {string} description - Information about module
* @property {string} usage - Information about module usage
*/
2019-11-07 15:35:23 +08:00
export const info = {
2018-05-13 18:33:22 +08:00
name: 'join',
2022-06-23 00:32:51 +08:00
category: 'core',
description: 'Join the target channel using the supplied nick and password',
usage: `
2020-09-08 12:51:47 +08:00
API: { cmd: 'join', nick: '<your nickname>', pass: '<optional password>', channel: '<target channel>' }`,
};