From 5cc626b8a05f28e2472768c789df7089914ff17b Mon Sep 17 00:00:00 2001
From: HF
Date: Mon, 27 Apr 2020 18:24:57 +0200
Subject: [PATCH] add /mute chat command fix eslint no-import error for jsons
that don't get packed by webpack (i am sure there must be a better way to do
that, but i am too lazy right now)
---
src/backup.js | 1 +
src/components/HelpModal.jsx | 2 +-
src/components/UserMessages.jsx | 2 +-
src/core/ChatProvider.js | 114 ++++++++++++++++++++++++++++++++
src/core/Image.js | 1 +
src/core/draw.js | 1 +
src/core/me.js | 1 +
src/core/passport.js | 1 +
src/core/tileserver.js | 1 +
src/data/models/RedisCanvas.js | 1 +
src/data/models/User.js | 37 +++++++++--
src/routes/admintools.js | 1 +
src/routes/api/mctp.js | 1 +
src/socket/APISocketServer.js | 9 +--
src/socket/ChatHistory.js | 26 --------
src/socket/SocketServer.js | 16 ++---
src/socket/websockets.js | 2 -
src/utils/proxiedFetch.js | 1 +
18 files changed, 169 insertions(+), 49 deletions(-)
create mode 100644 src/core/ChatProvider.js
delete mode 100644 src/socket/ChatHistory.js
diff --git a/src/backup.js b/src/backup.js
index 80c7f26..b37f7f1 100644
--- a/src/backup.js
+++ b/src/backup.js
@@ -24,6 +24,7 @@ import {
createPngBackup,
incrementialBackupRedis,
} from './core/tilesBackup';
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
/*
diff --git a/src/components/HelpModal.jsx b/src/components/HelpModal.jsx
index 44c8693..8f409f6 100644
--- a/src/components/HelpModal.jsx
+++ b/src/components/HelpModal.jsx
@@ -48,7 +48,7 @@ const HelpModal = () => (
Some canvases have a different cooldown for replacing a user-set pixels than placing on a unset pixel. i.e. 4s/7s means 4s on fresh
pixels and 7s on already set pixels.
Higher zoomlevels take some time to update, the 3D globe gets updated at least once per day.
- Have fun!
+ Have fun!
Discord: pixelplanet.fun/discord
Source on github
Reddit: r/PixelPlanetFun
diff --git a/src/components/UserMessages.jsx b/src/components/UserMessages.jsx
index 4e272a3..a4a5bbb 100644
--- a/src/components/UserMessages.jsx
+++ b/src/components/UserMessages.jsx
@@ -79,7 +79,7 @@ class UserMessages extends React.Component {
{(messages.includes('not_verified') && messages.splice(messages.indexOf('not_verified'), 1))
? (
- Please verify your mail address or your account could get deleted after a few days.
+ Please verify your mail address or your account could get deleted after a few days.
{(this.state.verify_answer)
? {this.state.verify_answer}
: Click here to request a new verification mail.}
diff --git a/src/core/ChatProvider.js b/src/core/ChatProvider.js
new file mode 100644
index 0000000..d91c381
--- /dev/null
+++ b/src/core/ChatProvider.js
@@ -0,0 +1,114 @@
+/* @flow */
+
+
+import logger from '../core/logger';
+import redis from '../data/redis';
+import User from '../data/models/User';
+import webSockets from '../socket/websockets';
+
+
+class ChatProvider {
+ /*
+ * TODO:
+ * history really be saved in redis
+ */
+ history: Array;
+
+ constructor() {
+ this.history = [];
+ }
+
+ addMessage(name, message) {
+ if (this.history.length > 20) {
+ this.history.shift();
+ }
+ this.history.push([name, message]);
+ }
+
+ async sendMessage(user, message) {
+ if (message.length > 300) {
+ // eslint-disable-next-line max-len
+ return 'You can\'t send a message this long :(';
+ }
+ const name = (user.regUser) ? user.regUser.name : null;
+ if (!name) {
+ // 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) === '/') {
+ // admin commands
+ const cmd = message.split(' ', 3);
+ if (cmd[0] === '/mute') {
+ return ChatProvider.mute(cmd[1], cmd[2]);
+ } if (cmd[0] === '/unmute') {
+ return ChatProvider.unmute(cmd[1]);
+ }
+ }
+
+ const muted = await ChatProvider.checkIfMuted(user);
+ if (muted === -1) {
+ return 'You are permanently muted, join our discord to apppeal the mute';
+ }
+ if (muted > 0) {
+ if (muted > 120) {
+ const timeMin = Math.round(muted / 60);
+ return `You are muted for another ${timeMin} minutes`;
+ }
+ return `You are muted for another ${muted} seconds`;
+ }
+ this.addMessage(name, message);
+ webSockets.broadcastChatMessage(name, message);
+ return null;
+ }
+
+ broadcastChatMessage(name, message, sendapi: boolean = true) {
+ this.addMessage(name, message);
+ webSockets.broadcastChatMessage(name, message, sendapi);
+ }
+
+ static async checkIfMuted(user) {
+ const key = `mute:${user.id}`;
+ const ttl: number = await redis.ttlAsync(key);
+ return ttl;
+ }
+
+ static async mute(name, timeMin) {
+ const id = await User.name2Id(name);
+ if (!id) {
+ return `Couldn't find user ${name}`;
+ }
+ const key = `mute:${id}`;
+ if (timeMin) {
+ const ttl = timeMin * 60;
+ await redis.setAsync(key, '', 'EX', ttl);
+ webSockets.broadcastChatMessage('info',
+ `${name} has been muted for ${timeMin}min`);
+ } else {
+ await redis.setAsync(key, '');
+ webSockets.broadcastChatMessage('info',
+ `${name} has been muted forever`);
+ }
+ logger.info(`Muted user ${id}`);
+ return null;
+ }
+
+ static async unmute(name) {
+ const id = await User.name2Id(name);
+ if (!id) {
+ return `Couldn't find user ${name}`;
+ }
+ const key = `mute:${id}`;
+ const delKeys = await redis.delAsync(key);
+ if (delKeys !== 1) {
+ return `User ${name} is not muted`;
+ }
+ webSockets.broadcastChatMessage('info',
+ `${name} has been unmuted`);
+ logger.info(`Unmuted user ${id}`);
+ return null;
+ }
+}
+
+const chatProvider = new ChatProvider();
+export default chatProvider;
diff --git a/src/core/Image.js b/src/core/Image.js
index e9c72cd..3eed446 100644
--- a/src/core/Image.js
+++ b/src/core/Image.js
@@ -8,6 +8,7 @@ import RedisCanvas from '../data/models/RedisCanvas';
import logger from './logger';
import { getChunkOfPixel } from './utils';
import { TILE_SIZE } from './constants';
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import Palette from './Palette';
diff --git a/src/core/draw.js b/src/core/draw.js
index f2ea784..8626f65 100644
--- a/src/core/draw.js
+++ b/src/core/draw.js
@@ -8,6 +8,7 @@ import { getChunkOfPixel, getOffsetOfPixel } from './utils';
import webSockets from '../socket/websockets';
import logger from './logger';
import RedisCanvas from '../data/models/RedisCanvas';
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import { THREE_CANVAS_HEIGHT } from './constants';
diff --git a/src/core/me.js b/src/core/me.js
index 519c0c1..94a6717 100644
--- a/src/core/me.js
+++ b/src/core/me.js
@@ -5,6 +5,7 @@
*
* @flow
*/
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
diff --git a/src/core/passport.js b/src/core/passport.js
index 633404b..18a6f98 100644
--- a/src/core/passport.js
+++ b/src/core/passport.js
@@ -33,6 +33,7 @@ passport.deserializeUser((req, id, done) => {
RegUser.findOne({ where: { id } }).then((reguser) => {
if (reguser) {
user.regUser = reguser;
+ user.id = id;
} else {
user.id = null;
}
diff --git a/src/core/tileserver.js b/src/core/tileserver.js
index 3f160ba..3a1ddfa 100644
--- a/src/core/tileserver.js
+++ b/src/core/tileserver.js
@@ -8,6 +8,7 @@ import fs from 'fs';
import type { Cell } from './Cell';
import logger from './logger';
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import Palette from './Palette';
import RedisCanvas from '../data/models/RedisCanvas';
diff --git a/src/data/models/RedisCanvas.js b/src/data/models/RedisCanvas.js
index ef5fed8..f9fc591 100644
--- a/src/data/models/RedisCanvas.js
+++ b/src/data/models/RedisCanvas.js
@@ -6,6 +6,7 @@ import {
THREE_TILE_SIZE,
THREE_CANVAS_HEIGHT,
} from '../../core/constants';
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import logger from '../../core/logger';
diff --git a/src/data/models/User.js b/src/data/models/User.js
index d06636b..2954c11 100644
--- a/src/data/models/User.js
+++ b/src/data/models/User.js
@@ -9,7 +9,6 @@
import Sequelize from 'sequelize';
import redis from '../redis';
-import { randomDice } from '../../utils/random';
import logger from '../../core/logger';
import Model from '../sequelize';
@@ -32,8 +31,23 @@ class User {
this.regUser = null;
}
+ static async name2Id(name: string) {
+ try {
+ const userq = await Model.query('SELECT id FROM Users WHERE name = $1',
+ {
+ bind: [name],
+ type: Sequelize.QueryTypes.SELECT,
+ raw: true,
+ plain: true,
+ });
+ return userq.id;
+ } catch {
+ return null;
+ }
+ }
+
async setWait(coolDown: number, canvasId: number): Promise {
- if (coolDown == 0) return false;
+ if (!coolDown) return false;
this.wait = Date.now() + coolDown;
// PX is milliseconds expire
await redis.setAsync(`cd:${canvasId}:ip:${this.ip}`, '', 'PX', coolDown);
@@ -46,7 +60,9 @@ class User {
async getWait(canvasId: number): Promise {
let ttl: number = await redis.pttlAsync(`cd:${canvasId}:ip:${this.ip}`);
if (this.id != null && ttl < 0) {
- const ttlid: number = await redis.pttlAsync(`cd:${canvasId}:id:${this.id}`);
+ const ttlid: number = await redis.pttlAsync(
+ `cd:${canvasId}:id:${this.id}`,
+ );
ttl = Math.max(ttl, ttlid);
}
logger.debug('ererer', ttl, typeof ttl);
@@ -82,10 +98,15 @@ class User {
return this.regUser.totalPixels;
}
try {
- const userq = await Model.query('SELECT totalPixels FROM Users WHERE id = $1',
+ const userq = await Model.query(
+ 'SELECT totalPixels FROM Users WHERE id = $1',
{
- bind: [id], type: Sequelize.QueryTypes.SELECT, raw: true, plain: true,
- });
+ bind: [id],
+ type: Sequelize.QueryTypes.SELECT,
+ raw: true,
+ plain: true,
+ },
+ );
return userq.totalPixels;
} catch (err) {
return 0;
@@ -95,7 +116,9 @@ class User {
async updateLogInTimestamp(): Promise {
if (!this.regUser) return false;
try {
- await this.regUser.update({ lastLogIn: Sequelize.literal('CURRENT_TIMESTAMP') });
+ await this.regUser.update({
+ lastLogIn: Sequelize.literal('CURRENT_TIMESTAMP'),
+ });
} catch (err) {
return false;
}
diff --git a/src/routes/admintools.js b/src/routes/admintools.js
index cee4087..621378d 100644
--- a/src/routes/admintools.js
+++ b/src/routes/admintools.js
@@ -20,6 +20,7 @@ import logger from '../core/logger';
import { Blacklist, Whitelist } from '../data/models';
import { MINUTE } from '../core/constants';
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import { imageABGR2Canvas } from '../core/Image';
diff --git a/src/routes/api/mctp.js b/src/routes/api/mctp.js
index c2b0709..6347134 100644
--- a/src/routes/api/mctp.js
+++ b/src/routes/api/mctp.js
@@ -9,6 +9,7 @@
import type { Request, Response } from 'express';
+// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import webSockets from '../../socket/websockets';
diff --git a/src/socket/APISocketServer.js b/src/socket/APISocketServer.js
index fb956d3..c328fd7 100644
--- a/src/socket/APISocketServer.js
+++ b/src/socket/APISocketServer.js
@@ -18,6 +18,7 @@ import Minecraft from '../core/minecraft';
import { drawUnsafe, setPixel } from '../core/draw';
import logger from '../core/logger';
import { APISOCKET_KEY } from '../core/config';
+import chatProvider from '../core/ChatProvider';
function heartbeat() {
this.isAlive = true;
@@ -182,7 +183,7 @@ class APISocketServer extends WebSocketEvents {
if (clr < 0 || clr > 32) return;
// be aware that user null has no cd
if (!minecraftid && !ip) {
- setPixel("0", clr, x, y);
+ setPixel('0', clr, x, y);
ws.send(JSON.stringify(['retpxl', null, null, true, 0, 0]));
return;
}
@@ -190,7 +191,7 @@ class APISocketServer extends WebSocketEvents {
user.ip = ip;
const {
error, success, waitSeconds, coolDownSeconds,
- } = await drawUnsafe(user, "0", clr, x, y, null);
+ } = await drawUnsafe(user, '0', clr, x, y, null);
ws.send(JSON.stringify([
'retpxl',
(minecraftid) || ip,
@@ -237,13 +238,13 @@ class APISocketServer extends WebSocketEvents {
const chatname = (user.id)
? `[MC] ${user.regUser.name}`
: `[MC] ${minecraftname}`;
- webSockets.broadcastChatMessage(chatname, msg, false);
+ chatProvider.broadcastChatMessage(chatname, msg, false);
this.broadcastChatMessage(chatname, msg, true, ws);
return;
}
if (command == 'chat') {
const [name, msg] = packet;
- webSockets.broadcastChatMessage(name, msg, false);
+ chatProvider.broadcastChatMessage(name, msg, false);
this.broadcastChatMessage(name, msg, true, ws);
return;
}
diff --git a/src/socket/ChatHistory.js b/src/socket/ChatHistory.js
deleted file mode 100644
index 4156859..0000000
--- a/src/socket/ChatHistory.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * save the chat history
- * TODO:
- * This should really be saved in redis
- *
- * @flow
- */
-
-class ChatHistory {
- OP_CODE = 0xA5;
- history: Array;
-
- constructor() {
- this.history = [];
- }
-
- addMessage(name, message) {
- if (this.history.length > 20) {
- this.history.shift();
- }
- this.history.push([name, message]);
- }
-}
-
-const chatHistory = new ChatHistory();
-export default chatHistory;
diff --git a/src/socket/SocketServer.js b/src/socket/SocketServer.js
index 42d6440..228fe2c 100644
--- a/src/socket/SocketServer.js
+++ b/src/socket/SocketServer.js
@@ -17,7 +17,7 @@ import RequestChatHistory from './packets/RequestChatHistory';
import CoolDownPacket from './packets/CoolDownPacket';
import ChangedMe from './packets/ChangedMe';
-import ChatHistory from './ChatHistory';
+import chatProvider from '../core/ChatProvider';
import authenticateClient from './verifyClient';
import WebSocketEvents from './WebSocketEvents';
import webSockets from './websockets';
@@ -217,17 +217,17 @@ class SocketServer extends WebSocketEvents {
webSockets.broadcastOnlineCounter(online);
}
- static onTextMessage(message, ws) {
+ static async onTextMessage(message, ws) {
if (ws.name && message) {
const waitLeft = ws.rateLimiter.tick();
if (waitLeft) {
// eslint-disable-next-line max-len
ws.send(JSON.stringify(['info', `You are sending messages too fast, you have to wait ${Math.floor(waitLeft / 1000)}s :(`]));
- } else if (message.length > 300) {
- // eslint-disable-next-line max-len
- ws.send(JSON.stringify(['info', 'You can\'t send a message this long :(']));
- } else {
- webSockets.broadcastChatMessage(ws.name, message);
+ return;
+ }
+ const errorMsg = await chatProvider.sendMessage(ws.user, message);
+ if (errorMsg) {
+ ws.send(JSON.stringify(['info', errorMsg]));
}
} else {
logger.info('Got empty message or message from unidentified ws');
@@ -278,7 +278,7 @@ class SocketServer extends WebSocketEvents {
break;
}
case RequestChatHistory.OP_CODE: {
- const history = JSON.stringify(ChatHistory.history);
+ const history = JSON.stringify(chatProvider.history);
ws.send(history);
break;
}
diff --git a/src/socket/websockets.js b/src/socket/websockets.js
index 1dba69a..3ed27d8 100644
--- a/src/socket/websockets.js
+++ b/src/socket/websockets.js
@@ -6,7 +6,6 @@
*/
import logger from '../core/logger';
-import ChatHistory from './ChatHistory';
import OnlineCounter from './packets/OnlineCounter';
import PixelUpdate from './packets/PixelUpdate';
@@ -67,7 +66,6 @@ class WebSockets {
sendapi: boolean = true,
) {
logger.info(`Received chat message ${message} from ${name}`);
- ChatHistory.addMessage(name, message);
this.listeners.forEach(
(listener) => listener.broadcastChatMessage(name, message, sendapi),
);
diff --git a/src/utils/proxiedFetch.js b/src/utils/proxiedFetch.js
index 1110bdb..5764479 100644
--- a/src/utils/proxiedFetch.js
+++ b/src/utils/proxiedFetch.js
@@ -7,6 +7,7 @@
import isoFetch from 'isomorphic-fetch';
import HttpProxyAgent from 'http-proxy-agent';
+// eslint-disable-next-line import/no-unresolved
import proxylist from './proxies.json';
import logger from '../core/logger';