add websocket messages or chat joining and leaving
This commit is contained in:
parent
8f24a34a1d
commit
46ba5188b5
|
@ -369,7 +369,6 @@ export function move([dx, dy]: Cell): ThunkAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function moveDirection([vx, vy]: Cell): ThunkAction {
|
export function moveDirection([vx, vy]: Cell): ThunkAction {
|
||||||
// TODO check direction is unitary vector
|
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const { viewscale } = getState().canvas;
|
const { viewscale } = getState().canvas;
|
||||||
|
|
||||||
|
@ -768,7 +767,7 @@ export function setChatChannel(cid: number): Action {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addChatChannel(channel: Array): Action {
|
export function addChatChannel(channel: Object): Action {
|
||||||
return {
|
return {
|
||||||
type: 'ADD_CHAT_CHANNEL',
|
type: 'ADD_CHAT_CHANNEL',
|
||||||
channel,
|
channel,
|
||||||
|
|
|
@ -70,7 +70,7 @@ export type Action =
|
||||||
}
|
}
|
||||||
| { type: 'RECEIVE_CHAT_HISTORY', cid: number, history: Array }
|
| { type: 'RECEIVE_CHAT_HISTORY', cid: number, history: Array }
|
||||||
| { type: 'SET_CHAT_CHANNEL', cid: number }
|
| { type: 'SET_CHAT_CHANNEL', cid: number }
|
||||||
| { type: 'ADD_CHAT_CHANNEL', channel: Array }
|
| { type: 'ADD_CHAT_CHANNEL', channel: Object }
|
||||||
| { type: 'REMOVE_CHAT_CHANNEL', cid: number }
|
| { type: 'REMOVE_CHAT_CHANNEL', cid: number }
|
||||||
| { type: 'SET_CHAT_FETCHING', fetching: boolean }
|
| { type: 'SET_CHAT_FETCHING', fetching: boolean }
|
||||||
| { type: 'SET_CHAT_INPUT_MSG', message: string }
|
| { type: 'SET_CHAT_INPUT_MSG', message: string }
|
||||||
|
|
|
@ -17,6 +17,8 @@ import {
|
||||||
receiveCoolDown,
|
receiveCoolDown,
|
||||||
receiveChatMessage,
|
receiveChatMessage,
|
||||||
receivePixelReturn,
|
receivePixelReturn,
|
||||||
|
addChatChannel,
|
||||||
|
removeChatChannel,
|
||||||
setMobile,
|
setMobile,
|
||||||
tryPlacePixel,
|
tryPlacePixel,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
@ -31,7 +33,6 @@ import ProtocolClient from './socket/ProtocolClient';
|
||||||
function init() {
|
function init() {
|
||||||
initRenderer(store, false);
|
initRenderer(store, false);
|
||||||
|
|
||||||
let nameRegExp = null;
|
|
||||||
ProtocolClient.on('pixelUpdate', ({
|
ProtocolClient.on('pixelUpdate', ({
|
||||||
i, j, offset, color,
|
i, j, offset, color,
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -49,9 +50,6 @@ function init() {
|
||||||
ProtocolClient.on('onlineCounter', ({ online }) => {
|
ProtocolClient.on('onlineCounter', ({ online }) => {
|
||||||
store.dispatch(receiveOnline(online));
|
store.dispatch(receiveOnline(online));
|
||||||
});
|
});
|
||||||
ProtocolClient.on('setWsName', (name) => {
|
|
||||||
nameRegExp = new RegExp(`(^|\\s+)(@${name})(\\s+|$)`, 'g');
|
|
||||||
});
|
|
||||||
ProtocolClient.on('chatMessage', (
|
ProtocolClient.on('chatMessage', (
|
||||||
name,
|
name,
|
||||||
text,
|
text,
|
||||||
|
@ -59,6 +57,8 @@ function init() {
|
||||||
channelId,
|
channelId,
|
||||||
userId,
|
userId,
|
||||||
) => {
|
) => {
|
||||||
|
const state = store.getState();
|
||||||
|
const { nameRegExp } = state.user;
|
||||||
const isPing = (nameRegExp && text.match(nameRegExp));
|
const isPing = (nameRegExp && text.match(nameRegExp));
|
||||||
store.dispatch(receiveChatMessage(
|
store.dispatch(receiveChatMessage(
|
||||||
name,
|
name,
|
||||||
|
@ -72,6 +72,12 @@ function init() {
|
||||||
ProtocolClient.on('changedMe', () => {
|
ProtocolClient.on('changedMe', () => {
|
||||||
store.dispatch(fetchMe());
|
store.dispatch(fetchMe());
|
||||||
});
|
});
|
||||||
|
ProtocolClient.on('remch', (cid) => {
|
||||||
|
store.dispatch(removeChatChannel(cid));
|
||||||
|
});
|
||||||
|
ProtocolClient.on('addch', (channel) => {
|
||||||
|
store.dispatch(addChatChannel(channel));
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener('hashchange', () => {
|
window.addEventListener('hashchange', () => {
|
||||||
store.dispatch(urlChange());
|
store.dispatch(urlChange());
|
||||||
|
|
|
@ -3,8 +3,9 @@ import logger from './logger';
|
||||||
import redis from '../data/redis';
|
import redis from '../data/redis';
|
||||||
import User from '../data/models/User';
|
import User from '../data/models/User';
|
||||||
import webSockets from '../socket/websockets';
|
import webSockets from '../socket/websockets';
|
||||||
import { Channel, RegUser } from '../data/models';
|
import { Channel, RegUser, UserChannel } from '../data/models';
|
||||||
import ChatMessageBuffer from './ChatMessageBuffer';
|
import ChatMessageBuffer from './ChatMessageBuffer';
|
||||||
|
import { cheapDetector } from './isProxy';
|
||||||
|
|
||||||
import { CHAT_CHANNELS, EVENT_USER_NAME, INFO_USER_NAME } from './constants';
|
import { CHAT_CHANNELS, EVENT_USER_NAME, INFO_USER_NAME } from './constants';
|
||||||
|
|
||||||
|
@ -100,6 +101,34 @@ export class ChatProvider {
|
||||||
this.eventUserId = eventUser[0].id;
|
this.eventUserId = eventUser[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async addUserToChannel(
|
||||||
|
userId,
|
||||||
|
channelId,
|
||||||
|
channelArray,
|
||||||
|
notify = true,
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
* since UserId and ChannelId are primary keys,
|
||||||
|
* this will throw if already exists
|
||||||
|
*/
|
||||||
|
const relation = await UserChannel.create({
|
||||||
|
UserId: userId,
|
||||||
|
ChannelId: channelId,
|
||||||
|
}, {
|
||||||
|
raw: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('HEREEEEE HHEEERRREEE');
|
||||||
|
console.log(relation);
|
||||||
|
|
||||||
|
webSockets.broadcastAddChatChannel(
|
||||||
|
userId,
|
||||||
|
channelId,
|
||||||
|
channelArray,
|
||||||
|
notify,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
userHasChannelAccess(user, cid, write = false) {
|
userHasChannelAccess(user, cid, write = false) {
|
||||||
if (this.defaultChannels[cid]) {
|
if (this.defaultChannels[cid]) {
|
||||||
if (!write || user.regUser) {
|
if (!write || user.regUser) {
|
||||||
|
@ -111,18 +140,110 @@ export class ChatProvider {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkIfDm(user, cid) {
|
||||||
|
if (this.defaultChannels[cid]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const channelArray = user.channels[cid];
|
||||||
|
if (channelArray && channelArray.length === 4) {
|
||||||
|
return user.channels[cid][4];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getHistory(cid, limit = 30) {
|
getHistory(cid, limit = 30) {
|
||||||
return this.chatMessageBuffer.getMessages(cid, limit);
|
return this.chatMessageBuffer.getMessages(cid, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adminCommands(message: string, channelId: number) {
|
||||||
|
// admin commands
|
||||||
|
const cmdArr = message.split(' ');
|
||||||
|
const cmd = cmdArr[0].substr(1);
|
||||||
|
const args = cmdArr.slice(1);
|
||||||
|
switch (cmd) {
|
||||||
|
case 'mute': {
|
||||||
|
const timeMin = Number(args.slice(-1));
|
||||||
|
if (Number.isNaN(timeMin)) {
|
||||||
|
return this.mute(args.join(' '), channelId);
|
||||||
|
}
|
||||||
|
return this.mute(
|
||||||
|
args.slice(0, -1).join(' '),
|
||||||
|
channelId,
|
||||||
|
timeMin,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'unmute':
|
||||||
|
return this.unmute(args.join(' '), channelId);
|
||||||
|
|
||||||
|
case 'mutec': {
|
||||||
|
if (args[0]) {
|
||||||
|
const cc = args[0].toLowerCase();
|
||||||
|
this.mutedCountries.push(cc);
|
||||||
|
this.broadcastChatMessage(
|
||||||
|
'info',
|
||||||
|
`Country ${cc} has been muted`,
|
||||||
|
channelId,
|
||||||
|
this.infoUserId,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return 'No country defined for mutec';
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'unmutec': {
|
||||||
|
if (args[0]) {
|
||||||
|
const cc = args[0].toLowerCase();
|
||||||
|
if (!this.mutedCountries.includes(cc)) {
|
||||||
|
return `Country ${cc} is not muted`;
|
||||||
|
}
|
||||||
|
this.mutedCountries = this.mutedCountries.filter((c) => c !== cc);
|
||||||
|
this.broadcastChatMessage(
|
||||||
|
'info',
|
||||||
|
`Country ${cc} has been unmuted`,
|
||||||
|
channelId,
|
||||||
|
this.infoUserId,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (this.mutedCountries.length) {
|
||||||
|
this.broadcastChatMessage(
|
||||||
|
'info',
|
||||||
|
`Countries ${this.mutedCountries} have been unmuted`,
|
||||||
|
channelId,
|
||||||
|
this.infoUserId,
|
||||||
|
);
|
||||||
|
this.mutedCountries = [];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return 'No country is currently muted';
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return `Couln't parse command ${cmd}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async sendMessage(user, message, channelId: number = 0) {
|
async sendMessage(user, message, channelId: number = 0) {
|
||||||
const { id } = user;
|
const { id } = user;
|
||||||
const name = user.getName();
|
const name = user.getName();
|
||||||
|
|
||||||
|
if (!user.isAdmin() && await cheapDetector(user.ip)) {
|
||||||
|
logger.info(
|
||||||
|
`${name} / ${user.ip} tried to send chat message with proxy`,
|
||||||
|
);
|
||||||
|
return 'You can not send chat messages with proxy';
|
||||||
|
}
|
||||||
|
|
||||||
if (!name || !id) {
|
if (!name || !id) {
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
return 'Couldn\'t send your message, pls log out and back in again.';
|
return 'Couldn\'t send your message, pls log out and back in again.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.isAdmin() && message.charAt(0) === '/') {
|
||||||
|
return this.adminCommands(message, channelId);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.userHasChannelAccess(user, channelId)) {
|
if (!this.userHasChannelAccess(user, channelId)) {
|
||||||
return 'You don\'t have access to this channel';
|
return 'You don\'t have access to this channel';
|
||||||
}
|
}
|
||||||
|
@ -181,49 +302,6 @@ export class ChatProvider {
|
||||||
return 'You can\'t send a message this long :(';
|
return 'You can\'t send a message this long :(';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.isAdmin() && message.charAt(0) === '/') {
|
|
||||||
// admin commands
|
|
||||||
const cmdArr = message.split(' ');
|
|
||||||
const cmd = cmdArr[0].substr(1);
|
|
||||||
const args = cmdArr.slice(1);
|
|
||||||
if (cmd === 'mute') {
|
|
||||||
const timeMin = Number(args.slice(-1));
|
|
||||||
if (Number.isNaN(timeMin)) {
|
|
||||||
return this.mute(args.join(' '), channelId);
|
|
||||||
}
|
|
||||||
return this.mute(
|
|
||||||
args.slice(0, -1).join(' '),
|
|
||||||
channelId,
|
|
||||||
timeMin,
|
|
||||||
);
|
|
||||||
} if (cmd === 'unmute') {
|
|
||||||
return this.unmute(args.join(' '), channelId);
|
|
||||||
} if (cmd === 'mutec' && args[0]) {
|
|
||||||
const cc = args[0].toLowerCase();
|
|
||||||
this.mutedCountries.push(cc);
|
|
||||||
this.broadcastChatMessage(
|
|
||||||
'info',
|
|
||||||
`Country ${cc} has been muted`,
|
|
||||||
channelId,
|
|
||||||
this.infoUserId,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
} if (cmd === 'unmutec' && args[0]) {
|
|
||||||
const cc = args[0].toLowerCase();
|
|
||||||
if (!this.mutedCountries.includes(cc)) {
|
|
||||||
return `Country ${cc} is not muted`;
|
|
||||||
}
|
|
||||||
this.mutedCountries = this.mutedCountries.filter((c) => c !== cc);
|
|
||||||
this.broadcastChatMessage(
|
|
||||||
'info',
|
|
||||||
`Country ${cc} has been unmuted`,
|
|
||||||
channelId,
|
|
||||||
this.infoUserId,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.match(this.cyrillic) && channelId === this.enChannelId) {
|
if (message.match(this.cyrillic) && channelId === this.enChannelId) {
|
||||||
return 'Please use int channel';
|
return 'Please use int channel';
|
||||||
}
|
}
|
||||||
|
@ -232,6 +310,21 @@ export class ChatProvider {
|
||||||
return 'Your country is temporary muted from chat';
|
return 'Your country is temporary muted from chat';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.last_message && user.last_message === message) {
|
||||||
|
user.message_repeat += 1;
|
||||||
|
if (user.message_repeat >= 4) {
|
||||||
|
this.mute(name, channelId, 60);
|
||||||
|
user.message_repeat = 0;
|
||||||
|
return 'Stop flooding.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user.message_repeat = 0;
|
||||||
|
user.last_message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Received chat message ${message} from ${name} / ${user.ip}`,
|
||||||
|
);
|
||||||
this.broadcastChatMessage(
|
this.broadcastChatMessage(
|
||||||
name,
|
name,
|
||||||
message,
|
message,
|
||||||
|
@ -270,16 +363,6 @@ export class ChatProvider {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
automute(name, channelId) {
|
|
||||||
this.mute(name, channelId, 60);
|
|
||||||
this.broadcastChatMessage(
|
|
||||||
'info',
|
|
||||||
`${name} has been muted for spam for 60min`,
|
|
||||||
channelId,
|
|
||||||
this.infoUserId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async checkIfMuted(user) {
|
static async checkIfMuted(user) {
|
||||||
const key = `mute:${user.id}`;
|
const key = `mute:${user.id}`;
|
||||||
const ttl: number = await redis.ttlAsync(key);
|
const ttl: number = await redis.ttlAsync(key);
|
||||||
|
@ -296,14 +379,12 @@ export class ChatProvider {
|
||||||
if (timeMin) {
|
if (timeMin) {
|
||||||
const ttl = timeMin * 60;
|
const ttl = timeMin * 60;
|
||||||
await redis.setAsync(key, '', 'EX', ttl);
|
await redis.setAsync(key, '', 'EX', ttl);
|
||||||
if (timeMin !== 600 && timeMin !== 60) {
|
this.broadcastChatMessage(
|
||||||
this.broadcastChatMessage(
|
'info',
|
||||||
'info',
|
`${name} has been muted for ${timeMin}min`,
|
||||||
`${name} has been muted for ${timeMin}min`,
|
channelId,
|
||||||
channelId,
|
this.infoUserId,
|
||||||
this.infoUserId,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
await redis.setAsync(key, '');
|
await redis.setAsync(key, '');
|
||||||
this.broadcastChatMessage(
|
this.broadcastChatMessage(
|
||||||
|
|
|
@ -260,3 +260,13 @@ export function setBrightness(hex, dark: boolean = false) {
|
||||||
}
|
}
|
||||||
return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`;
|
return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create RegExp to search for ping in chat messages
|
||||||
|
* @param name name
|
||||||
|
* @return regular expression to search for name in message
|
||||||
|
*/
|
||||||
|
export function createNameRegExp(name: string) {
|
||||||
|
if (!name) return null;
|
||||||
|
return new RegExp(`(^|\\s+)(@${name})(\\s+|$)`, 'g');
|
||||||
|
}
|
||||||
|
|
|
@ -64,17 +64,28 @@ class User {
|
||||||
dmu1,
|
dmu1,
|
||||||
dmu2,
|
dmu2,
|
||||||
} = reguser.channel[i];
|
} = reguser.channel[i];
|
||||||
// in DMs the name is the name of the other user
|
|
||||||
let { name } = reguser.channel[i];
|
|
||||||
if (type === 1) {
|
|
||||||
name = (dmu1.id === this.id) ? dmu2.name : dmu1.name;
|
|
||||||
}
|
|
||||||
this.channelIds.push(id);
|
this.channelIds.push(id);
|
||||||
this.channels[id] = [
|
if (type === 1) {
|
||||||
name,
|
/* in DMs:
|
||||||
type,
|
* the name is the name of the other user
|
||||||
lastTs,
|
* id also gets grabbed
|
||||||
];
|
*/
|
||||||
|
const name = (dmu1.id === this.id) ? dmu2.name : dmu1.name;
|
||||||
|
const dmu = (dmu1.id === this.id) ? dmu2.id : dmu1.id;
|
||||||
|
this.channels[id] = [
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
lastTs,
|
||||||
|
dmu,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
const { name } = reguser.channel[i];
|
||||||
|
this.channels[id] = [
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
lastTs,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (reguser.blocked) {
|
if (reguser.blocked) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ export default function audio(
|
||||||
case 'TOGGLE_MUTE':
|
case 'TOGGLE_MUTE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
// TODO error prone
|
|
||||||
mute: !state.mute,
|
mute: !state.mute,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ export type ChatState = {
|
||||||
* name,
|
* name,
|
||||||
* type,
|
* type,
|
||||||
* lastTs,
|
* lastTs,
|
||||||
|
* dmUserId,
|
||||||
* ],
|
* ],
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
|
@ -50,12 +51,29 @@ export default function chat(
|
||||||
|
|
||||||
case 'BLOCK_USER': {
|
case 'BLOCK_USER': {
|
||||||
const { userId, userName } = action;
|
const { userId, userName } = action;
|
||||||
|
const blocked = [
|
||||||
|
...state.blocked,
|
||||||
|
[userId, userName],
|
||||||
|
];
|
||||||
|
/*
|
||||||
|
* remove DM channel if exists
|
||||||
|
*/
|
||||||
|
const channels = { ...state.channels };
|
||||||
|
const chanKeys = Object.keys(channels);
|
||||||
|
for (let i = 0; i < chanKeys; i += 1) {
|
||||||
|
const cid = chanKeys[i];
|
||||||
|
if (channels[cid][1] === 1 && channels[cid][3] === userId) {
|
||||||
|
delete channels[cid];
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
channels,
|
||||||
|
blocked,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
blocked: [
|
blocked,
|
||||||
...state.blocked,
|
|
||||||
[userId, userName],
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ export default function gui(
|
||||||
chatRead: {
|
chatRead: {
|
||||||
...state.chatRead,
|
...state.chatRead,
|
||||||
cid: Date.now(),
|
cid: Date.now(),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ export default function gui(
|
||||||
case 'RECEIVE_ME': {
|
case 'RECEIVE_ME': {
|
||||||
const { channels } = action;
|
const { channels } = action;
|
||||||
const cids = Object.keys(channels);
|
const cids = Object.keys(channels);
|
||||||
const chatRead = {...state.chatRead};
|
const chatRead = { ...state.chatRead };
|
||||||
for (let i = 0; i < cids.length; i += 1) {
|
for (let i = 0; i < cids.length; i += 1) {
|
||||||
const cid = cids[i];
|
const cid = cids[i];
|
||||||
chatRead[cid] = 0;
|
chatRead[cid] = 0;
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
import type { Action } from '../actions/types';
|
import type { Action } from '../actions/types';
|
||||||
|
|
||||||
|
import { createNameRegExp } from '../core/utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type UserState = {
|
export type UserState = {
|
||||||
name: string,
|
name: string,
|
||||||
|
@ -32,6 +35,8 @@ export type UserState = {
|
||||||
notification: string,
|
notification: string,
|
||||||
// 1: Admin, 0: ordinary user
|
// 1: Admin, 0: ordinary user
|
||||||
userlvl: number,
|
userlvl: number,
|
||||||
|
// regExp for detecting ping
|
||||||
|
nameRegExp: RegExp,
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: UserState = {
|
const initialState: UserState = {
|
||||||
|
@ -51,6 +56,7 @@ const initialState: UserState = {
|
||||||
isOnMobile: false,
|
isOnMobile: false,
|
||||||
notification: null,
|
notification: null,
|
||||||
userlvl: 0,
|
userlvl: 0,
|
||||||
|
nameRegExp: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function user(
|
export default function user(
|
||||||
|
@ -145,6 +151,7 @@ export default function user(
|
||||||
blockDm,
|
blockDm,
|
||||||
userlvl,
|
userlvl,
|
||||||
} = action;
|
} = action;
|
||||||
|
const nameRegExp = createNameRegExp(name);
|
||||||
const messages = (action.messages) ? action.messages : [];
|
const messages = (action.messages) ? action.messages : [];
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -158,6 +165,7 @@ export default function user(
|
||||||
minecraftname,
|
minecraftname,
|
||||||
blockDm,
|
blockDm,
|
||||||
userlvl,
|
userlvl,
|
||||||
|
nameRegExp,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,9 +180,11 @@ export default function user(
|
||||||
|
|
||||||
case 'SET_NAME': {
|
case 'SET_NAME': {
|
||||||
const { name } = action;
|
const { name } = action;
|
||||||
|
const nameRegExp = createNameRegExp(name);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
name,
|
name,
|
||||||
|
nameRegExp,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
|
import webSockets from '../../socket/websockets';
|
||||||
import { RegUser, UserBlock, Channel } from '../../data/models';
|
import { RegUser, UserBlock, Channel } from '../../data/models';
|
||||||
|
|
||||||
async function block(req: Request, res: Response) {
|
async function block(req: Request, res: Response) {
|
||||||
|
@ -108,10 +109,10 @@ async function block(req: Request, res: Response) {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
const channelId = channel.id;
|
const channelId = channel.id;
|
||||||
channel.destroy();
|
channel.destroy();
|
||||||
|
webSockets.broadcastRemoveChatChannel(user.id, channelId, false);
|
||||||
|
webSockets.broadcastRemoveChatChannel(userId, channelId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO notify websocket
|
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
|
import webSockets from '../../socket/websockets';
|
||||||
|
|
||||||
async function blockdm(req: Request, res: Response) {
|
async function blockdm(req: Request, res: Response) {
|
||||||
const { block } = req.body;
|
const { block } = req.body;
|
||||||
|
@ -36,7 +37,20 @@ async function blockdm(req: Request, res: Response) {
|
||||||
blockDm: block,
|
blockDm: block,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO notify websocket
|
/*
|
||||||
|
* remove all dm channels
|
||||||
|
*/
|
||||||
|
const channels = user.regUser.channel;
|
||||||
|
for (let i = 0; i < channels.length; i += 1) {
|
||||||
|
const channel = channels[i];
|
||||||
|
if (channel.type === 1) {
|
||||||
|
const channelId = channel.id;
|
||||||
|
channel.destroy();
|
||||||
|
const { dmu1id, dmu2id } = channel;
|
||||||
|
webSockets.broadcastRemoveChatChannel(dmu1id, channelId, true);
|
||||||
|
webSockets.broadcastRemoveChatChannel(dmu2id, channelId, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
|
import webSockets from '../../socket/websockets';
|
||||||
|
|
||||||
async function leaveChan(req: Request, res: Response) {
|
async function leaveChan(req: Request, res: Response) {
|
||||||
const channelId = parseInt(req.body.channelId, 10);
|
const channelId = parseInt(req.body.channelId, 10);
|
||||||
|
@ -61,9 +62,11 @@ async function leaveChan(req: Request, res: Response) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Removing user ${user.getName()} from channel ${channel.name || channelId}`,
|
`Removing user ${user.getName()} from channel ${channel.name || channelId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
user.regUser.removeChannel(channel);
|
user.regUser.removeChannel(channel);
|
||||||
|
|
||||||
// TODO: inform websocket to remove channelId from user
|
webSockets.broadcastRemoveChatChannel(user.id, channelId, false);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
import { Channel, UserChannel, RegUser } from '../../data/models';
|
import { ChatProvider } from '../../core/ChatProvider';
|
||||||
|
import { Channel, RegUser } from '../../data/models';
|
||||||
import { isUserBlockedBy } from '../../data/models/UserBlock';
|
import { isUserBlockedBy } from '../../data/models/UserBlock';
|
||||||
|
|
||||||
async function startDm(req: Request, res: Response) {
|
async function startDm(req: Request, res: Response) {
|
||||||
|
@ -107,30 +108,18 @@ async function startDm(req: Request, res: Response) {
|
||||||
const ChannelId = channel[0].id;
|
const ChannelId = channel[0].id;
|
||||||
|
|
||||||
const promises = [
|
const promises = [
|
||||||
UserChannel.findOrCreate({
|
ChatProvider.addUserToChannel(user.id, ChannelId, false),
|
||||||
where: {
|
ChatProvider.addUserToChannel(userId, ChannelId, true),
|
||||||
UserId: dmu1id,
|
|
||||||
ChannelId,
|
|
||||||
},
|
|
||||||
raw: true,
|
|
||||||
}),
|
|
||||||
UserChannel.findOrCreate({
|
|
||||||
where: {
|
|
||||||
UserId: dmu2id,
|
|
||||||
ChannelId,
|
|
||||||
},
|
|
||||||
raw: true,
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
// TODO: inform websocket to add channelId to user
|
|
||||||
res.json({
|
res.json({
|
||||||
channel: {
|
channel: {
|
||||||
[ChannelId]: [
|
[ChannelId]: [
|
||||||
userName,
|
userName,
|
||||||
1,
|
1,
|
||||||
Date.now(),
|
Date.now(),
|
||||||
|
userId,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -169,15 +169,25 @@ class ProtocolClient extends EventEmitter {
|
||||||
const data = JSON.parse(message);
|
const data = JSON.parse(message);
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
if (data.length === 5) {
|
switch (data.length) {
|
||||||
// Ordinary array: Chat message
|
case 5: {
|
||||||
const [name, text, country, channelId, userId] = data;
|
// chat message
|
||||||
this.emit('chatMessage', name, text, country, channelId, userId);
|
const [name, text, country, channelId, userId] = data;
|
||||||
|
this.emit('chatMessage', name, text, country, channelId, userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: {
|
||||||
|
// signal
|
||||||
|
const [signal, args] = data;
|
||||||
|
this.emit(signal, args);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// string = name
|
// string = name
|
||||||
this.name = data;
|
this.name = data;
|
||||||
this.emit('setWsName', data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import DeRegisterMultipleChunks from './packets/DeRegisterMultipleChunks';
|
||||||
import ChangedMe from './packets/ChangedMe';
|
import ChangedMe from './packets/ChangedMe';
|
||||||
import OnlineCounter from './packets/OnlineCounter';
|
import OnlineCounter from './packets/OnlineCounter';
|
||||||
|
|
||||||
import chatProvider from '../core/ChatProvider';
|
import chatProvider, { ChatProvider } from '../core/ChatProvider';
|
||||||
import authenticateClient from './verifyClient';
|
import authenticateClient from './verifyClient';
|
||||||
import WebSocketEvents from './WebSocketEvents';
|
import WebSocketEvents from './WebSocketEvents';
|
||||||
import webSockets from './websockets';
|
import webSockets from './websockets';
|
||||||
|
@ -179,6 +179,52 @@ class SocketServer extends WebSocketEvents {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findWsByUserId(userId) {
|
||||||
|
const { clients } = this.wss;
|
||||||
|
for (let i = 0; i < clients.length; i += 1) {
|
||||||
|
const ws = clients[i];
|
||||||
|
if (ws.user.id === userId && ws.readyState === WebSocket.OPEN) {
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastAddChatChannel(
|
||||||
|
userId: number,
|
||||||
|
channelId: number,
|
||||||
|
channelArray: Array,
|
||||||
|
notify: boolean,
|
||||||
|
) {
|
||||||
|
const ws = this.findWsByUserId(userId);
|
||||||
|
if (ws) {
|
||||||
|
ws.user.channels[channelId] = channelArray;
|
||||||
|
const text = JSON.stringify([
|
||||||
|
'addch', {
|
||||||
|
[channelId]: channelArray,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
if (notify) {
|
||||||
|
ws.send(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastRemoveChatChannel(
|
||||||
|
userId: number,
|
||||||
|
channelId: number,
|
||||||
|
notify: boolean,
|
||||||
|
) {
|
||||||
|
const ws = this.findWsByUserId(userId);
|
||||||
|
if (ws) {
|
||||||
|
delete ws.user.channels[channelId];
|
||||||
|
const text = JSON.stringify('remch', channelId);
|
||||||
|
if (notify) {
|
||||||
|
ws.send(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
broadcastPixelBuffer(canvasId: number, chunkid, data: Buffer) {
|
broadcastPixelBuffer(canvasId: number, chunkid, data: Buffer) {
|
||||||
const frame = WebSocket.Sender.frame(data, {
|
const frame = WebSocket.Sender.frame(data, {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
|
@ -245,6 +291,10 @@ class SocketServer extends WebSocketEvents {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async onTextMessage(text, ws) {
|
static async onTextMessage(text, ws) {
|
||||||
|
/*
|
||||||
|
* all client -> server text messages are
|
||||||
|
* chat messages in [message, channelId] format
|
||||||
|
*/
|
||||||
try {
|
try {
|
||||||
let message;
|
let message;
|
||||||
let channelId;
|
let channelId;
|
||||||
|
@ -263,6 +313,9 @@ class SocketServer extends WebSocketEvents {
|
||||||
}
|
}
|
||||||
message = message.trim();
|
message = message.trim();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* just if logged in
|
||||||
|
*/
|
||||||
if (ws.name && message) {
|
if (ws.name && message) {
|
||||||
const { user } = ws;
|
const { user } = ws;
|
||||||
const waitLeft = ws.rateLimiter.tick();
|
const waitLeft = ws.rateLimiter.tick();
|
||||||
|
@ -276,44 +329,38 @@ class SocketServer extends WebSocketEvents {
|
||||||
]));
|
]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check proxy
|
|
||||||
if (!user.isAdmin() && await cheapDetector(user.ip)) {
|
/*
|
||||||
logger.info(
|
* if DM channel, make sure that other user has DM open
|
||||||
`${ws.name} / ${user.ip} tried to send chat message with proxy`,
|
* (needed because we allow user to leave one-sided
|
||||||
);
|
* and auto-join on message)
|
||||||
ws.send(JSON.stringify([
|
*/
|
||||||
'info',
|
const dmUserId = chatProvider.checkIfDm(user, channelId);
|
||||||
'You can not send chat messages with a proxy',
|
if (dmUserId) {
|
||||||
'il',
|
const dmWs = this.findWsByUserId(dmUserId);
|
||||||
channelId,
|
if (dmWs) {
|
||||||
]));
|
const { user: dmUser } = dmWs;
|
||||||
return;
|
if (!dmUser || !dmUser.userHasChannelAccess(channelId)) {
|
||||||
|
ChatProvider.addUserToChannel(
|
||||||
|
dmUserId,
|
||||||
|
channelId,
|
||||||
|
[ws.name, 1, Date.now(), user.id],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//
|
|
||||||
|
/*
|
||||||
|
* send chat message
|
||||||
|
*/
|
||||||
const errorMsg = await chatProvider.sendMessage(
|
const errorMsg = await chatProvider.sendMessage(
|
||||||
user,
|
user,
|
||||||
message,
|
message,
|
||||||
channelId,
|
channelId,
|
||||||
);
|
);
|
||||||
if (!errorMsg) {
|
if (errorMsg) {
|
||||||
// automute on repeated message spam
|
|
||||||
if (ws.last_message && ws.last_message === message) {
|
|
||||||
ws.message_repeat += 1;
|
|
||||||
if (ws.message_repeat >= 4) {
|
|
||||||
logger.info(`User ${ws.name} got automuted`);
|
|
||||||
chatProvider.automute(ws.name, channelId);
|
|
||||||
ws.message_repeat = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ws.message_repeat = 0;
|
|
||||||
ws.last_message = message;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ws.send(JSON.stringify(['info', errorMsg, 'il', channelId]));
|
ws.send(JSON.stringify(['info', errorMsg, 'il', channelId]));
|
||||||
}
|
}
|
||||||
logger.info(
|
|
||||||
`Received chat message ${message} from ${ws.name} / ${ws.user.ip}`,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
logger.info('Got empty message or message from unidentified ws');
|
logger.info('Got empty message or message from unidentified ws');
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,21 @@ class WebSocketEvents {
|
||||||
broadcastMinecraftLink(name: string, minecraftid: string, accepted: boolean) {
|
broadcastMinecraftLink(name: string, minecraftid: string, accepted: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
broadcastAddChatChannel(
|
||||||
|
userId: number,
|
||||||
|
channelId: number,
|
||||||
|
channelArray: Array,
|
||||||
|
notify: boolean,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastRemoveChatChannel(
|
||||||
|
userId: number,
|
||||||
|
channelId: number,
|
||||||
|
notify: boolean,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
notifyChangedMe(name: string) {
|
notifyChangedMe(name: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,50 @@ class WebSockets {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* broadcast Assigning chat channel to user
|
||||||
|
* @param userId numerical id of user
|
||||||
|
* @param channelId numerical id of chat channel
|
||||||
|
* @param channelArray array with channel info [name, type, lastTs]
|
||||||
|
* @param notify if user should get notified over websocket
|
||||||
|
* (i.e. false if the user already gets it via api response)
|
||||||
|
*/
|
||||||
|
broadcastAddChatChannel(
|
||||||
|
userId: number,
|
||||||
|
channelId: number,
|
||||||
|
channelArray: Array,
|
||||||
|
notify: boolean = true,
|
||||||
|
) {
|
||||||
|
this.listeners.forEach(
|
||||||
|
(listener) => listener.broadcastAddChatChannel(
|
||||||
|
userId,
|
||||||
|
channelArray,
|
||||||
|
notify,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* broadcast Removing chat channel from user
|
||||||
|
* @param userId numerical id of user
|
||||||
|
* @param channelId numerical id of chat channel
|
||||||
|
* @param notify if user should get notified over websocket
|
||||||
|
* (i.e. false if the user already gets it via api response)
|
||||||
|
*/
|
||||||
|
broadcastRemoveChatChannel(
|
||||||
|
userId: number,
|
||||||
|
channelId: number,
|
||||||
|
notify: boolean = true,
|
||||||
|
) {
|
||||||
|
this.listeners.forEach(
|
||||||
|
(listener) => listener.broadcastRemoveChatChannel(
|
||||||
|
userId,
|
||||||
|
channelId,
|
||||||
|
notify,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* broadcast minecraft linking to API
|
* broadcast minecraft linking to API
|
||||||
* @param name pixelplanetname
|
* @param name pixelplanetname
|
||||||
|
|
|
@ -18,6 +18,10 @@ export default (store) => (next) => (action) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO
|
||||||
|
* make LOGIN / LOGOUT Actions instead of comparing name changes
|
||||||
|
*/
|
||||||
case 'RECEIVE_ME': {
|
case 'RECEIVE_ME': {
|
||||||
const { name } = action;
|
const { name } = action;
|
||||||
ProtocolClient.setName(name);
|
ProtocolClient.setName(name);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user