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)
This commit is contained in:
HF 2020-04-27 18:24:57 +02:00
parent cd7c373277
commit 5cc626b8a0
18 changed files with 169 additions and 49 deletions

View File

@ -24,6 +24,7 @@ import {
createPngBackup,
incrementialBackupRedis,
} from './core/tilesBackup';
// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
/*

View File

@ -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!</p>
Have fun!</p>
<p>Discord: <a href="./discord" target="_blank">pixelplanet.fun/discord</a></p>
<p>Source on <a href="https://github.com/pixelplanetdev/pixelplanet" target="_blank">github</a></p>
<p>Reddit: <a href="https://www.reddit.com/r/PixelPlanetFun/" target="_blank">r/PixelPlanetFun</a></p>

View File

@ -79,7 +79,7 @@ class UserMessages extends React.Component {
{(messages.includes('not_verified') && messages.splice(messages.indexOf('not_verified'), 1))
? (
<p className="usermessages">
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)
? <span className="modallink">{this.state.verify_answer}</span>
: <span className="modallink" onClick={this.submit_resend_verify}>Click here to request a new verification mail.</span>}

114
src/core/ChatProvider.js Normal file
View File

@ -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;

View File

@ -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';

View File

@ -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';

View File

@ -5,6 +5,7 @@
*
* @flow
*/
// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';

View File

@ -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;
}

View File

@ -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';

View File

@ -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';

View File

@ -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<boolean> {
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<?number> {
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<boolean> {
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;
}

View File

@ -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';

View File

@ -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';

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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),
);

View File

@ -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';