From 46ba5188b5da08c97a3440aa690003532bdd4fef Mon Sep 17 00:00:00 2001 From: HF Date: Fri, 27 Nov 2020 23:48:59 +0100 Subject: [PATCH] add websocket messages or chat joining and leaving --- src/actions/index.js | 3 +- src/actions/types.js | 2 +- src/client.js | 14 ++- src/core/ChatProvider.js | 205 ++++++++++++++++++++++---------- src/core/utils.js | 10 ++ src/data/models/User.js | 31 +++-- src/reducers/audio.js | 1 - src/reducers/chat.js | 26 +++- src/reducers/gui.js | 4 +- src/reducers/user.js | 10 ++ src/routes/api/block.js | 5 +- src/routes/api/blockdm.js | 16 ++- src/routes/api/leavechan.js | 5 +- src/routes/api/startdm.js | 21 +--- src/socket/ProtocolClient.js | 20 +++- src/socket/SocketServer.js | 109 ++++++++++++----- src/socket/WebSocketEvents.js | 15 +++ src/socket/websockets.js | 44 +++++++ src/store/protocolClientHook.js | 4 + 19 files changed, 403 insertions(+), 142 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index 4b1c727..db75e99 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -369,7 +369,6 @@ export function move([dx, dy]: Cell): ThunkAction { } export function moveDirection([vx, vy]: Cell): ThunkAction { - // TODO check direction is unitary vector return (dispatch, getState) => { 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 { type: 'ADD_CHAT_CHANNEL', channel, diff --git a/src/actions/types.js b/src/actions/types.js index 3769a4c..44acfea 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -70,7 +70,7 @@ export type Action = } | { type: 'RECEIVE_CHAT_HISTORY', cid: number, history: Array } | { 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: 'SET_CHAT_FETCHING', fetching: boolean } | { type: 'SET_CHAT_INPUT_MSG', message: string } diff --git a/src/client.js b/src/client.js index 7f6e5f3..8238e77 100644 --- a/src/client.js +++ b/src/client.js @@ -17,6 +17,8 @@ import { receiveCoolDown, receiveChatMessage, receivePixelReturn, + addChatChannel, + removeChatChannel, setMobile, tryPlacePixel, } from './actions'; @@ -31,7 +33,6 @@ import ProtocolClient from './socket/ProtocolClient'; function init() { initRenderer(store, false); - let nameRegExp = null; ProtocolClient.on('pixelUpdate', ({ i, j, offset, color, }) => { @@ -49,9 +50,6 @@ function init() { ProtocolClient.on('onlineCounter', ({ online }) => { store.dispatch(receiveOnline(online)); }); - ProtocolClient.on('setWsName', (name) => { - nameRegExp = new RegExp(`(^|\\s+)(@${name})(\\s+|$)`, 'g'); - }); ProtocolClient.on('chatMessage', ( name, text, @@ -59,6 +57,8 @@ function init() { channelId, userId, ) => { + const state = store.getState(); + const { nameRegExp } = state.user; const isPing = (nameRegExp && text.match(nameRegExp)); store.dispatch(receiveChatMessage( name, @@ -72,6 +72,12 @@ function init() { ProtocolClient.on('changedMe', () => { store.dispatch(fetchMe()); }); + ProtocolClient.on('remch', (cid) => { + store.dispatch(removeChatChannel(cid)); + }); + ProtocolClient.on('addch', (channel) => { + store.dispatch(addChatChannel(channel)); + }); window.addEventListener('hashchange', () => { store.dispatch(urlChange()); diff --git a/src/core/ChatProvider.js b/src/core/ChatProvider.js index 1f26e98..b7d6bd9 100644 --- a/src/core/ChatProvider.js +++ b/src/core/ChatProvider.js @@ -3,8 +3,9 @@ import logger from './logger'; import redis from '../data/redis'; import User from '../data/models/User'; import webSockets from '../socket/websockets'; -import { Channel, RegUser } from '../data/models'; +import { Channel, RegUser, UserChannel } from '../data/models'; import ChatMessageBuffer from './ChatMessageBuffer'; +import { cheapDetector } from './isProxy'; import { CHAT_CHANNELS, EVENT_USER_NAME, INFO_USER_NAME } from './constants'; @@ -100,6 +101,34 @@ export class ChatProvider { 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) { if (this.defaultChannels[cid]) { if (!write || user.regUser) { @@ -111,18 +140,110 @@ export class ChatProvider { 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) { 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) { const { id } = user; 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) { // eslint-disable-next-line max-len 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)) { 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 :('; } - 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) { return 'Please use int channel'; } @@ -232,6 +310,21 @@ export class ChatProvider { 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( name, 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) { const key = `mute:${user.id}`; const ttl: number = await redis.ttlAsync(key); @@ -296,14 +379,12 @@ export class ChatProvider { if (timeMin) { const ttl = timeMin * 60; await redis.setAsync(key, '', 'EX', ttl); - if (timeMin !== 600 && timeMin !== 60) { - this.broadcastChatMessage( - 'info', - `${name} has been muted for ${timeMin}min`, - channelId, - this.infoUserId, - ); - } + this.broadcastChatMessage( + 'info', + `${name} has been muted for ${timeMin}min`, + channelId, + this.infoUserId, + ); } else { await redis.setAsync(key, ''); this.broadcastChatMessage( diff --git a/src/core/utils.js b/src/core/utils.js index be9d130..3a1c6ca 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -260,3 +260,13 @@ export function setBrightness(hex, dark: boolean = false) { } 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'); +} diff --git a/src/data/models/User.js b/src/data/models/User.js index 8b6ee1b..f38c1c9 100644 --- a/src/data/models/User.js +++ b/src/data/models/User.js @@ -64,17 +64,28 @@ class User { dmu1, dmu2, } = 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.channels[id] = [ - name, - type, - lastTs, - ]; + if (type === 1) { + /* in DMs: + * the name is the name of the other user + * 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) { diff --git a/src/reducers/audio.js b/src/reducers/audio.js index e0f2254..28e5d0e 100644 --- a/src/reducers/audio.js +++ b/src/reducers/audio.js @@ -22,7 +22,6 @@ export default function audio( case 'TOGGLE_MUTE': return { ...state, - // TODO error prone mute: !state.mute, }; diff --git a/src/reducers/chat.js b/src/reducers/chat.js index abb7bb6..5953878 100644 --- a/src/reducers/chat.js +++ b/src/reducers/chat.js @@ -17,6 +17,7 @@ export type ChatState = { * name, * type, * lastTs, + * dmUserId, * ], * ... * } @@ -50,12 +51,29 @@ export default function chat( case 'BLOCK_USER': { 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 { ...state, - blocked: [ - ...state.blocked, - [userId, userName], - ], + blocked, }; } diff --git a/src/reducers/gui.js b/src/reducers/gui.js index 7294793..993d756 100644 --- a/src/reducers/gui.js +++ b/src/reducers/gui.js @@ -117,7 +117,7 @@ export default function gui( chatRead: { ...state.chatRead, cid: Date.now(), - } + }, }; } @@ -148,7 +148,7 @@ export default function gui( case 'RECEIVE_ME': { const { channels } = action; const cids = Object.keys(channels); - const chatRead = {...state.chatRead}; + const chatRead = { ...state.chatRead }; for (let i = 0; i < cids.length; i += 1) { const cid = cids[i]; chatRead[cid] = 0; diff --git a/src/reducers/user.js b/src/reducers/user.js index 64fdf66..cb9ccf1 100644 --- a/src/reducers/user.js +++ b/src/reducers/user.js @@ -2,6 +2,9 @@ import type { Action } from '../actions/types'; +import { createNameRegExp } from '../core/utils'; + + export type UserState = { name: string, @@ -32,6 +35,8 @@ export type UserState = { notification: string, // 1: Admin, 0: ordinary user userlvl: number, + // regExp for detecting ping + nameRegExp: RegExp, }; const initialState: UserState = { @@ -51,6 +56,7 @@ const initialState: UserState = { isOnMobile: false, notification: null, userlvl: 0, + nameRegExp: null, }; export default function user( @@ -145,6 +151,7 @@ export default function user( blockDm, userlvl, } = action; + const nameRegExp = createNameRegExp(name); const messages = (action.messages) ? action.messages : []; return { ...state, @@ -158,6 +165,7 @@ export default function user( minecraftname, blockDm, userlvl, + nameRegExp, }; } @@ -172,9 +180,11 @@ export default function user( case 'SET_NAME': { const { name } = action; + const nameRegExp = createNameRegExp(name); return { ...state, name, + nameRegExp, }; } diff --git a/src/routes/api/block.js b/src/routes/api/block.js index f5f1659..8c4ed87 100644 --- a/src/routes/api/block.js +++ b/src/routes/api/block.js @@ -8,6 +8,7 @@ import type { Request, Response } from 'express'; import logger from '../../core/logger'; +import webSockets from '../../socket/websockets'; import { RegUser, UserBlock, Channel } from '../../data/models'; async function block(req: Request, res: Response) { @@ -108,10 +109,10 @@ async function block(req: Request, res: Response) { if (channel) { const channelId = channel.id; channel.destroy(); + webSockets.broadcastRemoveChatChannel(user.id, channelId, false); + webSockets.broadcastRemoveChatChannel(userId, channelId, true); } - // TODO notify websocket - if (ret) { res.json({ status: 'ok', diff --git a/src/routes/api/blockdm.js b/src/routes/api/blockdm.js index f135eaa..1e2ac0d 100644 --- a/src/routes/api/blockdm.js +++ b/src/routes/api/blockdm.js @@ -8,6 +8,7 @@ import type { Request, Response } from 'express'; import logger from '../../core/logger'; +import webSockets from '../../socket/websockets'; async function blockdm(req: Request, res: Response) { const { block } = req.body; @@ -36,7 +37,20 @@ async function blockdm(req: Request, res: Response) { 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({ status: 'ok', diff --git a/src/routes/api/leavechan.js b/src/routes/api/leavechan.js index 686933e..84fd008 100644 --- a/src/routes/api/leavechan.js +++ b/src/routes/api/leavechan.js @@ -8,6 +8,7 @@ import type { Request, Response } from 'express'; import logger from '../../core/logger'; +import webSockets from '../../socket/websockets'; async function leaveChan(req: Request, res: Response) { const channelId = parseInt(req.body.channelId, 10); @@ -61,9 +62,11 @@ async function leaveChan(req: Request, res: Response) { logger.info( `Removing user ${user.getName()} from channel ${channel.name || channelId}`, ); + user.regUser.removeChannel(channel); - // TODO: inform websocket to remove channelId from user + webSockets.broadcastRemoveChatChannel(user.id, channelId, false); + res.json({ status: 'ok', }); diff --git a/src/routes/api/startdm.js b/src/routes/api/startdm.js index a053b68..bebc9e2 100644 --- a/src/routes/api/startdm.js +++ b/src/routes/api/startdm.js @@ -8,7 +8,8 @@ import type { Request, Response } from 'express'; 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'; async function startDm(req: Request, res: Response) { @@ -107,30 +108,18 @@ async function startDm(req: Request, res: Response) { const ChannelId = channel[0].id; const promises = [ - UserChannel.findOrCreate({ - where: { - UserId: dmu1id, - ChannelId, - }, - raw: true, - }), - UserChannel.findOrCreate({ - where: { - UserId: dmu2id, - ChannelId, - }, - raw: true, - }), + ChatProvider.addUserToChannel(user.id, ChannelId, false), + ChatProvider.addUserToChannel(userId, ChannelId, true), ]; await Promise.all(promises); - // TODO: inform websocket to add channelId to user res.json({ channel: { [ChannelId]: [ userName, 1, Date.now(), + userId, ], }, }); diff --git a/src/socket/ProtocolClient.js b/src/socket/ProtocolClient.js index 3924e2f..27defa5 100644 --- a/src/socket/ProtocolClient.js +++ b/src/socket/ProtocolClient.js @@ -169,15 +169,25 @@ class ProtocolClient extends EventEmitter { const data = JSON.parse(message); if (Array.isArray(data)) { - if (data.length === 5) { - // Ordinary array: Chat message - const [name, text, country, channelId, userId] = data; - this.emit('chatMessage', name, text, country, channelId, userId); + switch (data.length) { + case 5: { + // chat message + 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 { // string = name this.name = data; - this.emit('setWsName', data); } } diff --git a/src/socket/SocketServer.js b/src/socket/SocketServer.js index 57f52da..8b2db88 100644 --- a/src/socket/SocketServer.js +++ b/src/socket/SocketServer.js @@ -19,7 +19,7 @@ import DeRegisterMultipleChunks from './packets/DeRegisterMultipleChunks'; import ChangedMe from './packets/ChangedMe'; import OnlineCounter from './packets/OnlineCounter'; -import chatProvider from '../core/ChatProvider'; +import chatProvider, { ChatProvider } from '../core/ChatProvider'; import authenticateClient from './verifyClient'; import WebSocketEvents from './WebSocketEvents'; 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) { const frame = WebSocket.Sender.frame(data, { readOnly: true, @@ -245,6 +291,10 @@ class SocketServer extends WebSocketEvents { } static async onTextMessage(text, ws) { + /* + * all client -> server text messages are + * chat messages in [message, channelId] format + */ try { let message; let channelId; @@ -263,6 +313,9 @@ class SocketServer extends WebSocketEvents { } message = message.trim(); + /* + * just if logged in + */ if (ws.name && message) { const { user } = ws; const waitLeft = ws.rateLimiter.tick(); @@ -276,44 +329,38 @@ class SocketServer extends WebSocketEvents { ])); return; } - // check proxy - if (!user.isAdmin() && await cheapDetector(user.ip)) { - logger.info( - `${ws.name} / ${user.ip} tried to send chat message with proxy`, - ); - ws.send(JSON.stringify([ - 'info', - 'You can not send chat messages with a proxy', - 'il', - channelId, - ])); - return; + + /* + * if DM channel, make sure that other user has DM open + * (needed because we allow user to leave one-sided + * and auto-join on message) + */ + const dmUserId = chatProvider.checkIfDm(user, channelId); + if (dmUserId) { + const dmWs = this.findWsByUserId(dmUserId); + if (dmWs) { + const { user: dmUser } = dmWs; + if (!dmUser || !dmUser.userHasChannelAccess(channelId)) { + ChatProvider.addUserToChannel( + dmUserId, + channelId, + [ws.name, 1, Date.now(), user.id], + ); + } + } } - // + + /* + * send chat message + */ const errorMsg = await chatProvider.sendMessage( user, message, channelId, ); - 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 { + if (errorMsg) { ws.send(JSON.stringify(['info', errorMsg, 'il', channelId])); } - logger.info( - `Received chat message ${message} from ${ws.name} / ${ws.user.ip}`, - ); } else { logger.info('Got empty message or message from unidentified ws'); } diff --git a/src/socket/WebSocketEvents.js b/src/socket/WebSocketEvents.js index 55a4827..a24f326 100644 --- a/src/socket/WebSocketEvents.js +++ b/src/socket/WebSocketEvents.js @@ -24,6 +24,21 @@ class WebSocketEvents { 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) { } diff --git a/src/socket/websockets.js b/src/socket/websockets.js index fe1eadb..ae9891a 100644 --- a/src/socket/websockets.js +++ b/src/socket/websockets.js @@ -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 * @param name pixelplanetname diff --git a/src/store/protocolClientHook.js b/src/store/protocolClientHook.js index 3827ee7..c8629d5 100644 --- a/src/store/protocolClientHook.js +++ b/src/store/protocolClientHook.js @@ -18,6 +18,10 @@ export default (store) => (next) => (action) => { break; } + /* + * TODO + * make LOGIN / LOGOUT Actions instead of comparing name changes + */ case 'RECEIVE_ME': { const { name } = action; ProtocolClient.setName(name);