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:
parent
cd7c373277
commit
5cc626b8a0
|
@ -24,6 +24,7 @@ import {
|
||||||
createPngBackup,
|
createPngBackup,
|
||||||
incrementialBackupRedis,
|
incrementialBackupRedis,
|
||||||
} from './core/tilesBackup';
|
} from './core/tilesBackup';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
114
src/core/ChatProvider.js
Normal file
114
src/core/ChatProvider.js
Normal 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;
|
|
@ -8,6 +8,7 @@ import RedisCanvas from '../data/models/RedisCanvas';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import { getChunkOfPixel } from './utils';
|
import { getChunkOfPixel } from './utils';
|
||||||
import { TILE_SIZE } from './constants';
|
import { TILE_SIZE } from './constants';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
import Palette from './Palette';
|
import Palette from './Palette';
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { getChunkOfPixel, getOffsetOfPixel } from './utils';
|
||||||
import webSockets from '../socket/websockets';
|
import webSockets from '../socket/websockets';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import RedisCanvas from '../data/models/RedisCanvas';
|
import RedisCanvas from '../data/models/RedisCanvas';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
|
|
||||||
import { THREE_CANVAS_HEIGHT } from './constants';
|
import { THREE_CANVAS_HEIGHT } from './constants';
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*
|
*
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ passport.deserializeUser((req, id, done) => {
|
||||||
RegUser.findOne({ where: { id } }).then((reguser) => {
|
RegUser.findOne({ where: { id } }).then((reguser) => {
|
||||||
if (reguser) {
|
if (reguser) {
|
||||||
user.regUser = reguser;
|
user.regUser = reguser;
|
||||||
|
user.id = id;
|
||||||
} else {
|
} else {
|
||||||
user.id = null;
|
user.id = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import fs from 'fs';
|
||||||
|
|
||||||
import type { Cell } from './Cell';
|
import type { Cell } from './Cell';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
import Palette from './Palette';
|
import Palette from './Palette';
|
||||||
import RedisCanvas from '../data/models/RedisCanvas';
|
import RedisCanvas from '../data/models/RedisCanvas';
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
THREE_TILE_SIZE,
|
THREE_TILE_SIZE,
|
||||||
THREE_CANVAS_HEIGHT,
|
THREE_CANVAS_HEIGHT,
|
||||||
} from '../../core/constants';
|
} from '../../core/constants';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
import Sequelize from 'sequelize';
|
import Sequelize from 'sequelize';
|
||||||
import redis from '../redis';
|
import redis from '../redis';
|
||||||
import { randomDice } from '../../utils/random';
|
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
|
|
||||||
import Model from '../sequelize';
|
import Model from '../sequelize';
|
||||||
|
@ -32,8 +31,23 @@ class User {
|
||||||
this.regUser = null;
|
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> {
|
async setWait(coolDown: number, canvasId: number): Promise<boolean> {
|
||||||
if (coolDown == 0) return false;
|
if (!coolDown) return false;
|
||||||
this.wait = Date.now() + coolDown;
|
this.wait = Date.now() + coolDown;
|
||||||
// PX is milliseconds expire
|
// PX is milliseconds expire
|
||||||
await redis.setAsync(`cd:${canvasId}:ip:${this.ip}`, '', 'PX', coolDown);
|
await redis.setAsync(`cd:${canvasId}:ip:${this.ip}`, '', 'PX', coolDown);
|
||||||
|
@ -46,7 +60,9 @@ class User {
|
||||||
async getWait(canvasId: number): Promise<?number> {
|
async getWait(canvasId: number): Promise<?number> {
|
||||||
let ttl: number = await redis.pttlAsync(`cd:${canvasId}:ip:${this.ip}`);
|
let ttl: number = await redis.pttlAsync(`cd:${canvasId}:ip:${this.ip}`);
|
||||||
if (this.id != null && ttl < 0) {
|
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);
|
ttl = Math.max(ttl, ttlid);
|
||||||
}
|
}
|
||||||
logger.debug('ererer', ttl, typeof ttl);
|
logger.debug('ererer', ttl, typeof ttl);
|
||||||
|
@ -82,10 +98,15 @@ class User {
|
||||||
return this.regUser.totalPixels;
|
return this.regUser.totalPixels;
|
||||||
}
|
}
|
||||||
try {
|
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;
|
return userq.totalPixels;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -95,7 +116,9 @@ class User {
|
||||||
async updateLogInTimestamp(): Promise<boolean> {
|
async updateLogInTimestamp(): Promise<boolean> {
|
||||||
if (!this.regUser) return false;
|
if (!this.regUser) return false;
|
||||||
try {
|
try {
|
||||||
await this.regUser.update({ lastLogIn: Sequelize.literal('CURRENT_TIMESTAMP') });
|
await this.regUser.update({
|
||||||
|
lastLogIn: Sequelize.literal('CURRENT_TIMESTAMP'),
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import logger from '../core/logger';
|
||||||
import { Blacklist, Whitelist } from '../data/models';
|
import { Blacklist, Whitelist } from '../data/models';
|
||||||
|
|
||||||
import { MINUTE } from '../core/constants';
|
import { MINUTE } from '../core/constants';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
import { imageABGR2Canvas } from '../core/Image';
|
import { imageABGR2Canvas } from '../core/Image';
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import canvases from './canvases.json';
|
import canvases from './canvases.json';
|
||||||
import webSockets from '../../socket/websockets';
|
import webSockets from '../../socket/websockets';
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import Minecraft from '../core/minecraft';
|
||||||
import { drawUnsafe, setPixel } from '../core/draw';
|
import { drawUnsafe, setPixel } from '../core/draw';
|
||||||
import logger from '../core/logger';
|
import logger from '../core/logger';
|
||||||
import { APISOCKET_KEY } from '../core/config';
|
import { APISOCKET_KEY } from '../core/config';
|
||||||
|
import chatProvider from '../core/ChatProvider';
|
||||||
|
|
||||||
function heartbeat() {
|
function heartbeat() {
|
||||||
this.isAlive = true;
|
this.isAlive = true;
|
||||||
|
@ -182,7 +183,7 @@ class APISocketServer extends WebSocketEvents {
|
||||||
if (clr < 0 || clr > 32) return;
|
if (clr < 0 || clr > 32) return;
|
||||||
// be aware that user null has no cd
|
// be aware that user null has no cd
|
||||||
if (!minecraftid && !ip) {
|
if (!minecraftid && !ip) {
|
||||||
setPixel("0", clr, x, y);
|
setPixel('0', clr, x, y);
|
||||||
ws.send(JSON.stringify(['retpxl', null, null, true, 0, 0]));
|
ws.send(JSON.stringify(['retpxl', null, null, true, 0, 0]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -190,7 +191,7 @@ class APISocketServer extends WebSocketEvents {
|
||||||
user.ip = ip;
|
user.ip = ip;
|
||||||
const {
|
const {
|
||||||
error, success, waitSeconds, coolDownSeconds,
|
error, success, waitSeconds, coolDownSeconds,
|
||||||
} = await drawUnsafe(user, "0", clr, x, y, null);
|
} = await drawUnsafe(user, '0', clr, x, y, null);
|
||||||
ws.send(JSON.stringify([
|
ws.send(JSON.stringify([
|
||||||
'retpxl',
|
'retpxl',
|
||||||
(minecraftid) || ip,
|
(minecraftid) || ip,
|
||||||
|
@ -237,13 +238,13 @@ class APISocketServer extends WebSocketEvents {
|
||||||
const chatname = (user.id)
|
const chatname = (user.id)
|
||||||
? `[MC] ${user.regUser.name}`
|
? `[MC] ${user.regUser.name}`
|
||||||
: `[MC] ${minecraftname}`;
|
: `[MC] ${minecraftname}`;
|
||||||
webSockets.broadcastChatMessage(chatname, msg, false);
|
chatProvider.broadcastChatMessage(chatname, msg, false);
|
||||||
this.broadcastChatMessage(chatname, msg, true, ws);
|
this.broadcastChatMessage(chatname, msg, true, ws);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (command == 'chat') {
|
if (command == 'chat') {
|
||||||
const [name, msg] = packet;
|
const [name, msg] = packet;
|
||||||
webSockets.broadcastChatMessage(name, msg, false);
|
chatProvider.broadcastChatMessage(name, msg, false);
|
||||||
this.broadcastChatMessage(name, msg, true, ws);
|
this.broadcastChatMessage(name, msg, true, ws);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
|
@ -17,7 +17,7 @@ import RequestChatHistory from './packets/RequestChatHistory';
|
||||||
import CoolDownPacket from './packets/CoolDownPacket';
|
import CoolDownPacket from './packets/CoolDownPacket';
|
||||||
import ChangedMe from './packets/ChangedMe';
|
import ChangedMe from './packets/ChangedMe';
|
||||||
|
|
||||||
import ChatHistory from './ChatHistory';
|
import 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';
|
||||||
|
@ -217,17 +217,17 @@ class SocketServer extends WebSocketEvents {
|
||||||
webSockets.broadcastOnlineCounter(online);
|
webSockets.broadcastOnlineCounter(online);
|
||||||
}
|
}
|
||||||
|
|
||||||
static onTextMessage(message, ws) {
|
static async onTextMessage(message, ws) {
|
||||||
if (ws.name && message) {
|
if (ws.name && message) {
|
||||||
const waitLeft = ws.rateLimiter.tick();
|
const waitLeft = ws.rateLimiter.tick();
|
||||||
if (waitLeft) {
|
if (waitLeft) {
|
||||||
// eslint-disable-next-line max-len
|
// 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 :(`]));
|
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) {
|
return;
|
||||||
// eslint-disable-next-line max-len
|
}
|
||||||
ws.send(JSON.stringify(['info', 'You can\'t send a message this long :(']));
|
const errorMsg = await chatProvider.sendMessage(ws.user, message);
|
||||||
} else {
|
if (errorMsg) {
|
||||||
webSockets.broadcastChatMessage(ws.name, message);
|
ws.send(JSON.stringify(['info', errorMsg]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info('Got empty message or message from unidentified ws');
|
logger.info('Got empty message or message from unidentified ws');
|
||||||
|
@ -278,7 +278,7 @@ class SocketServer extends WebSocketEvents {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestChatHistory.OP_CODE: {
|
case RequestChatHistory.OP_CODE: {
|
||||||
const history = JSON.stringify(ChatHistory.history);
|
const history = JSON.stringify(chatProvider.history);
|
||||||
ws.send(history);
|
ws.send(history);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../core/logger';
|
import logger from '../core/logger';
|
||||||
import ChatHistory from './ChatHistory';
|
|
||||||
import OnlineCounter from './packets/OnlineCounter';
|
import OnlineCounter from './packets/OnlineCounter';
|
||||||
import PixelUpdate from './packets/PixelUpdate';
|
import PixelUpdate from './packets/PixelUpdate';
|
||||||
|
|
||||||
|
@ -67,7 +66,6 @@ class WebSockets {
|
||||||
sendapi: boolean = true,
|
sendapi: boolean = true,
|
||||||
) {
|
) {
|
||||||
logger.info(`Received chat message ${message} from ${name}`);
|
logger.info(`Received chat message ${message} from ${name}`);
|
||||||
ChatHistory.addMessage(name, message);
|
|
||||||
this.listeners.forEach(
|
this.listeners.forEach(
|
||||||
(listener) => listener.broadcastChatMessage(name, message, sendapi),
|
(listener) => listener.broadcastChatMessage(name, message, sendapi),
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import isoFetch from 'isomorphic-fetch';
|
import isoFetch from 'isomorphic-fetch';
|
||||||
import HttpProxyAgent from 'http-proxy-agent';
|
import HttpProxyAgent from 'http-proxy-agent';
|
||||||
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import proxylist from './proxies.json';
|
import proxylist from './proxies.json';
|
||||||
|
|
||||||
import logger from '../core/logger';
|
import logger from '../core/logger';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user