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

syncing dev changes

This commit is contained in:
marzavec 2020-11-09 11:55:54 -08:00
parent 85b9ad08bb
commit 7b7a511b8d
11 changed files with 381 additions and 57 deletions

View File

@ -353,7 +353,9 @@ function join(channel) {
var args = JSON.parse(message.data);
var cmd = args.cmd;
var command = COMMANDS[cmd];
command.call(null, args);
if (command) {
command.call(null, args);
}
}
}
@ -368,9 +370,9 @@ var COMMANDS = {
info: function (args) {
args.nick = '*';
pushMessage(args);
},
},
emote: function (args) {
emote: function (args) {
args.nick = '*';
pushMessage(args);
},
@ -410,6 +412,30 @@ var COMMANDS = {
if ($('#joined-left').checked) {
pushMessage({ nick: '*', text: nick + " left" });
}
},
captcha: function (args) {
var messageEl = document.createElement('div');
messageEl.classList.add('info');
var nickSpanEl = document.createElement('span');
nickSpanEl.classList.add('nick');
messageEl.appendChild(nickSpanEl);
var nickLinkEl = document.createElement('a');
nickLinkEl.textContent = '#';
nickSpanEl.appendChild(nickLinkEl);
var textEl = document.createElement('pre');
textEl.style.fontSize = '4px';
textEl.classList.add('text');
textEl.innerHTML = args.text;
messageEl.appendChild(textEl);
$('#messages').appendChild(messageEl);
window.scrollTo(0, document.body.scrollHeight);
}
}
@ -447,7 +473,13 @@ function pushMessage(args) {
if (args.trip) {
var tripEl = document.createElement('span');
tripEl.textContent = args.trip + " ";
if (args.mod) {
tripEl.textContent = String.fromCodePoint(11088) + " " + args.trip + " ";
} else {
tripEl.textContent = args.trip + " ";
}
tripEl.classList.add('trip');
nickSpanEl.appendChild(tripEl);
}
@ -456,6 +488,10 @@ function pushMessage(args) {
var nickLinkEl = document.createElement('a');
nickLinkEl.textContent = args.nick;
if (args.color && /(^[0-9A-F]{6}$)|(^[0-9A-F]{3}$)/i.test(args.color)) {
nickLinkEl.setAttribute('style', 'color:#' + args.color + ' !important');
}
nickLinkEl.onclick = function () {
insertAtCursor("@" + args.nick + " ");
$('#chatinput').focus();

View File

@ -6,6 +6,7 @@ import {
isAdmin,
isModerator,
levels,
getUserDetails,
} from '../utility/_UAC';
// module main
@ -24,6 +25,16 @@ export async function run({
// find targets current connections
const targetMod = server.findSockets({ trip: payload.trip });
if (targetMod.length !== 0) {
// build update notice with new privileges
const updateNotice = {
...getUserDetails(targetMod[0]),
...{
cmd: 'updateUser',
uType: 'user', // @todo use legacyLevelToLabel from _LegacyFunctions.js
level: levels.default,
},
};
for (let i = 0, l = targetMod.length; i < l; i += 1) {
// downgrade privilages
targetMod[i].uType = 'user';
@ -35,6 +46,14 @@ export async function run({
text: 'You are now a user.',
channel: targetMod[i].channel, // @todo Multichannel
}, targetMod[i]);
// notify channel
server.broadcast({
...updateNotice,
...{
channel: targetMod[i].channel,
},
}, { channel: targetMod[i].channel });
}
}

View File

@ -2,12 +2,16 @@
Description: Allows calling client to change their nickname color
*/
import {
getUserDetails,
} from '../utility/_UAC';
// 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,
server, socket, payload,
}) {
const channel = socket.channel;
@ -35,9 +39,9 @@ export async function run({
}
if (newColor === 'RESET') {
socket.color = false;
socket.color = false; // eslint-disable-line no-param-reassign
} else {
socket.color = newColor;
socket.color = newColor; // eslint-disable-line no-param-reassign
}
// build update notice with new color
@ -46,7 +50,7 @@ export async function run({
...{
cmd: 'updateUser',
channel: socket.channel, // @todo Multichannel
}
},
};
// notify channel that the user has changed their name

View File

@ -85,7 +85,7 @@ export async function run({
cmd: 'updateUser',
nick: newNick,
channel, // @todo Multichannel
}
},
};
// build join and leave notices for legacy clients
@ -114,7 +114,7 @@ export async function run({
server.send(joinNotice, peerList[i]);
} else {
// send update info
// @todo this should be sent to every channel the client is in
// @todo this should be sent to every channel the client is in (multichannel)
server.send(updateNotice, peerList[i]);
}
}

View File

@ -76,7 +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,
@ -122,7 +122,7 @@ 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({
...{

View File

@ -3,8 +3,6 @@
@deprecated This module will be removed or replaced
*/
import {
verifyNickname,
getUserPerms,
getUserDetails,
} from '../utility/_UAC';
@ -104,7 +102,7 @@ export async function run({ server, socket, payload }) {
channel: payload.channel,
isme: true,
},
...getUserDetails(socket)
...getUserDetails(socket),
});
// reply with new user list

View File

@ -16,7 +16,6 @@ import {
} from '../utility/_LegacyFunctions';
// module support functions
const parseText = (text) => {
// verifies user input is text
if (typeof text !== 'string') {

View File

@ -9,12 +9,45 @@
import {
isModerator,
} from '../utility/_UAC';
import {
findUser,
} from '../utility/_Channels';
import {
Errors,
} from '../utility/_Constants';
import {
findUser,
} from '../utility/_Channels';
legacyInviteReply,
legacyWhisperReply,
} from '../utility/_LegacyFunctions';
// module support functions
/**
* Returns the channel that should be invited to.
* @param {any} channel
* @return {string}
*/
export function getChannel(channel = undefined) {
if (typeof channel === 'string') {
return channel;
}
return Math.random().toString(36).substr(2, 8);
}
const parseText = (text) => {
// verifies user input is text
if (typeof text !== 'string') {
return false;
}
let sanitizedText = text;
// strip newlines from beginning and end
sanitizedText = sanitizedText.replace(/^\s*\n|^\s+$|\n\s*$/g, '');
// replace 3+ newlines with just 2 newlines
sanitizedText = sanitizedText.replace(/\n{3,}/g, '\n\n');
return sanitizedText;
};
// module constructor
export function init(core) {
@ -101,24 +134,31 @@ export function chatCheck({
if (core.muzzledHashes[socket.hash]) {
// build fake chat payload
const mutedPayload = {
const outgoingPayload = {
cmd: 'chat',
nick: socket.nick,
nick: socket.nick, /* @legacy */
uType: socket.uType, /* @legacy */
userid: socket.userid,
channel: socket.channel,
text: payload.text,
channel: socket.channel, // @todo Multichannel
level: socket.level,
};
if (socket.trip) {
mutedPayload.trip = socket.trip;
outgoingPayload.trip = socket.trip;
}
if (socket.color) {
outgoingPayload.color = socket.color;
}
// broadcast to any duplicate connections in channel
server.broadcast(mutedPayload, { channel: socket.channel, hash: socket.hash });
server.broadcast(outgoingPayload, { channel: socket.channel, hash: socket.hash });
// broadcast to allies, if any
if (core.muzzledHashes[socket.hash].allies) {
server.broadcast(
mutedPayload,
outgoingPayload,
{
channel: socket.channel,
nick: core.muzzledHashes[socket.hash].allies,
@ -140,24 +180,62 @@ export function chatCheck({
}
// shadow-prevent all invites from muzzled users
export function inviteCheck({ core, socket, payload }) {
export function inviteCheck({
core, server, socket, payload,
}) {
if (core.muzzledHashes[socket.hash]) {
// @todo convert to protocol 2
/* const nickValid = Invite.checkNickname(payload.nick);
if (nickValid !== null) {
server.reply({
cmd: 'warn', // @todo Add numeric error code as `id`
text: nickValid,
// check for spam
if (server.police.frisk(socket.address, 2)) {
return server.reply({
cmd: 'warn',
text: 'You are sending invites too fast. Wait a moment before trying again.',
id: Errors.Invite.RATELIMIT,
channel: socket.channel, // @todo Multichannel
}, socket);
}
// verify user input
// if this is a legacy client add missing params to payload
if (socket.hcProtocol === 1) {
if (typeof socket.channel === 'undefined' || typeof payload.nick !== 'string') {
return true;
}
payload.channel = socket.channel; // eslint-disable-line no-param-reassign
} else if (typeof payload.userid !== 'number' || typeof payload.channel !== 'string') {
return true;
}
// @todo Verify this socket is part of payload.channel - multichannel patch
// find target user
const targetUser = findUser(server, payload);
if (!targetUser) {
return server.reply({
cmd: 'warn',
text: 'Could not find user in that channel',
id: Errors.Global.UNKNOWN_USER,
channel: socket.channel, // @todo Multichannel
}, socket);
return false;
}
// generate common channel
const channel = Invite.getChannel();
const channel = getChannel(payload.to);
// send fake reply
server.reply(Invite.createSuccessPayload(payload.nick, channel), socket); */
// build invite
const outgoingPayload = {
cmd: 'invite',
channel: socket.channel, // @todo Multichannel
from: socket.userid,
to: targetUser.userid,
inviteChannel: channel,
};
// send invite notice to this client
if (socket.hcProtocol === 1) {
server.reply(legacyInviteReply(outgoingPayload, targetUser.nick), socket);
} else {
server.reply(outgoingPayload, socket);
}
return false;
}
@ -169,27 +247,56 @@ export function inviteCheck({ core, socket, payload }) {
export function whisperCheck({
core, server, socket, payload,
}) {
if (typeof payload.nick !== 'string') {
return false;
}
if (typeof payload.text !== 'string') {
return false;
}
if (core.muzzledHashes[socket.hash]) {
const targetNick = payload.nick;
// if this is a legacy client add missing params to payload
if (socket.hcProtocol === 1) {
payload.channel = socket.channel; // eslint-disable-line no-param-reassign
}
server.reply({
cmd: 'info',
type: 'whisper',
text: `You whispered to @${targetNick}: ${payload.text}`,
// verify user input
const text = parseText(payload.text);
if (!text) {
// lets not send objects or empty text, yea?
return server.police.frisk(socket.address, 13);
}
// check for spam
const score = text.length / 83 / 4;
if (server.police.frisk(socket.address, score)) {
return server.reply({
cmd: 'warn', // @todo Add numeric error code as `id`
text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.',
channel: socket.channel, // @todo Multichannel
}, socket);
}
const targetUser = findUser(server, payload);
if (!targetUser) {
return server.reply({
cmd: 'warn',
text: 'Could not find user in that channel',
id: Errors.Global.UNKNOWN_USER,
channel: socket.channel, // @todo Multichannel
}, socket);
}
const outgoingPayload = {
cmd: 'whisper',
channel: socket.channel, // @todo Multichannel
}, socket);
from: socket.userid,
to: targetUser.userid,
text,
};
// blanket "spam" protection, may expose the ratelimiting lines from
// `chat` and use that, @todo one day #lazydev
server.police.frisk(socket.address, 9);
// send invite notice to this client
if (socket.hcProtocol === 1) {
server.reply(legacyWhisperReply(outgoingPayload, targetUser.nick), socket);
} else {
server.reply(outgoingPayload, socket);
}
targetUser.whisperReply = socket.nick;
return false;
}

View File

@ -0,0 +1,162 @@
/*
Description: Forces a change on the target socket's nick color
*/
import {
isModerator,
getUserDetails,
} from '../utility/_UAC';
import {
Errors,
} from '../utility/_Constants';
import {
findUser,
} from '../utility/_Channels';
const verifyColor = (color) => /(^[0-9A-F]{6}$)|(^[0-9A-F]{3}$)/i.test(color);
// module main
export async function run({
server, socket, payload,
}) {
// increase rate limit chance and ignore if not admin or mod
if (!isModerator(socket.level)) {
return server.police.frisk(socket.address, 10);
}
const channel = socket.channel;
if (typeof payload.channel === 'undefined') {
payload.channel = channel;
}
// check user input
if (typeof payload.nick !== 'string') {
return true;
}
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);
}
// find target user
const targetUser = findUser(server, payload);
if (!targetUser) {
return server.reply({
cmd: 'warn',
text: 'Could not find user in that channel',
id: Errors.Global.UNKNOWN_USER,
channel: socket.channel, // @todo Multichannel
}, socket);
}
// TODO: Change this uType to use level / uac
// i guess coloring mods or admins isn't the best idea?
if (targetUser.uType !== 'user') {
return true;
}
if (newColor === 'RESET') {
targetUser.color = false;
} else {
targetUser.color = newColor;
}
// build update notice with new color
const updateNotice = {
...getUserDetails(targetUser),
...{
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 });
// mod perks
// TODO: Change this uType to use level / uac
if (socket.uType !== 'user') {
if (typeof server.police.records[socket.address] !== 'undefined') {
server.police.records[socket.address].score = -50;
}
}
return true;
}
// module hook functions
export function initHooks(server) {
server.registerHook('in', 'chat', this.colorCheck.bind(this), 20);
}
// hooks chat commands checking for /whisper
export function colorCheck({
core, server, socket, payload,
}) {
if (typeof payload.text !== 'string') {
return false;
}
if (payload.text.startsWith('/forcecolor ')) {
const input = payload.text.split(' ');
// If there is no nickname target parameter
if (input[1] === undefined) {
server.reply({
cmd: 'warn',
text: 'Refer to `/help forcecolor` for instructions on how to use this command.',
channel: socket.channel, // @todo Multichannel
}, socket);
return false;
}
if (input[2] === undefined) {
server.reply({
cmd: 'warn',
text: 'Refer to `/help forcecolor` for instructions on how to use this command.',
channel: socket.channel, // @todo Multichannel
}, socket);
return false;
}
const target = input[1].replace(/@/g, '');
this.run({
core,
server,
socket,
payload: {
cmd: 'forcecolor',
nick: target,
color: input[2],
},
});
return false;
}
return payload;
}
// module meta
export const requiredData = ['nick', 'color'];
export const info = {
name: 'forcecolor',
description: 'Forces a user nick to become a certain color',
usage: `
API: { cmd: 'forcecolor', nick: '<target nick>', color: '<color as hex>' }
Text: /forcecolor <target nick> <color as hex>`,
};

View File

@ -78,7 +78,6 @@ export async function run({ server, socket, payload }) {
}
}
const newPeerList = server.findSockets({ channel: payload.channel });
const moveAnnouncement = {
...getUserDetails(badClient),
@ -95,7 +94,7 @@ export async function run({ server, socket, payload }) {
nicks.push(newPeerList[i].nick); /* @legacy */
users.push({
...{
channel,
channel: payload.channel,
isme: false,
},
...getUserDetails(newPeerList[i]),
@ -114,7 +113,7 @@ export async function run({ server, socket, payload }) {
cmd: 'onlineSet',
nicks, /* @legacy */
users,
channel, // @todo Multichannel (?)
channel: payload.channel, // @todo Multichannel (?)
}, badClient);
badClient.channel = payload.channel;

View File

@ -61,7 +61,7 @@ export function legacyLevelToLabel(level) {
* @param {string} nick Sender nick
* @return {object}
*/
export function legacyInviteOut(payload, nick) {
export function legacyInviteOut(payload, nick) {
return {
...payload,
...{
@ -99,7 +99,7 @@ export function legacyInviteReply(payload, nick) {
* @param {string} nick Sender nick
* @return {object}
*/
export function legacyWhisperOut(payload, from) {
export function legacyWhisperOut(payload, from) {
return {
...payload,
...{