Minor improvements to socket.io chat, including banning and unbanning per room, and spam detection and server bans

This commit is contained in:
knotteye
2019-12-07 21:23:50 -06:00
parent a0be256a64
commit 61deb1afa7
5 changed files with 120 additions and 14 deletions

View File

@ -5,6 +5,7 @@ import * as socketio from "socket.io";
import * as http from "http";
import * as cookies from "cookie-parser";
import * as dirty from "dirty";
import * as socketSpam from "socket-anti-spam";
import * as api from "./api";
import * as db from "./database";
import * as irc from "./irc";
@ -18,6 +19,8 @@ const app = express();
const server = http.createServer(app);
const io = socketio(server);
const store = dirty();
var banlist;
var ircconf;
var jwkey;
try{
jwkey = JWK.asKey(readFileSync('./config/jwt.pem'));
@ -28,7 +31,8 @@ try{
}
var njkconf;
async function init(satyr: any, http: object, ircconf: any){
async function init(satyr: any, http: object, irc: any){
ircconf = irc;
njk.configure('templates', {
autoescape : true,
express : app,
@ -65,19 +69,22 @@ async function init(satyr: any, http: object, ircconf: any){
else res.status(404).render('404.njk', njkconf);
//res.status(404).render('404.njk', njkconf);
});
await initChat(ircconf);
banlist = new dirty('./config/bans.db').on('load', () => {initChat()});
server.listen(http['port']);
}
async function newNick(socket, skip?: boolean) {
async function newNick(socket, skip?: boolean, i?: number) {
if(socket.handshake.headers['cookie'] && !skip){
let c = await parseCookie(socket.handshake.headers['cookie']);
let t = await validToken(c['Authorization']);
if(t) return t['username'];
if(t) {
store.set(t, socket.id);
return t['username'];
}
}
//i just realized how shitty of an idea this is
let n: string = 'Guest'+Math.floor(Math.random() * Math.floor(1000));
if(store.get(n)) return newNick(socket, true);
if(!i) i = 10;
let n: string = 'Guest'+Math.floor(Math.random() * Math.floor(i));
if(store.get(n)) return newNick(socket, true, Math.floor(i * 10));
else {
store.set(n, socket.id);
return n;
@ -90,7 +97,7 @@ async function chgNick(socket, nick, f?: boolean) {
io.to(rooms[i]).emit('ALERT', socket.nick+' is now known as '+nick);
}
if(store.get(socket.nick)) store.rm(socket.nick);
if (!f) store.set(nick, socket.id);
store.set(nick, socket.id);
socket.nick = nick;
}
@ -328,7 +335,7 @@ async function initSite(openReg) {
});
}
async function initChat(ircconf: any) {
async function initChat() {
//irc peering
if(ircconf.enable){
await irc.connect({
@ -349,6 +356,15 @@ async function initChat(ircconf: any) {
socket.on('JOINROOM', async (data) => {
let t: any = await db.query('select username from users where username='+db.raw.escape(data));
if(t[0]){
if(banlist.get(data) && banlist.get(data)[socket.ip]){
if(Math.floor(banlist.get(data)[socket.ip]['time'] + (banlist.get(data)[socket.ip]['length'] * 60)) < Math.floor(Date.now() / 1000)){
banlist.set('data', Object.assign(banlist['data'], {[socket.ip]: null}));
}
else {
socket.emit('ALERT', 'You are banned from that room');
return;
}
}
socket.join(data);
io.to(data).emit('JOINED', {nick: socket.nick});
if(ircconf.enable) irc.join(socket.nick, data);
@ -386,10 +402,6 @@ async function initChat(ircconf: any) {
});
socket.on('NICK', async (data) => {
data.nick = data.nick.replace(' ','');
if(store.get(data.nick)){
socket.emit('ALERT', 'Nickname is already in use');
return false;
}
let user = await db.query('select username from users where username='+db.raw.escape(data.nick));
if(user[0]){
if(!data.password){
@ -402,6 +414,10 @@ async function initChat(ircconf: any) {
else socket.emit('ALERT','Incorrect username or password');
}
else {
if(store.get(data.nick)){
socket.emit('ALERT', 'Nickname is already in use');
return false;
}
chgNick(socket, data.nick);
}
});
@ -423,6 +439,63 @@ async function initChat(ircconf: any) {
}
else socket.emit('ALERT', 'Not authorized to do that.');
});
socket.on('BAN', (data: Object) => {
if(socket.nick === data['room']){
let id: string = store.get(data['nick']);
if(id){
let target = io.sockets.connected[id];
if(typeof(data['time']) === 'number' && (data['time'] !== 0 || data['time'] !== NaN)) banlist.set(data['room'], Object.assign({[target.ip]: {time: Math.floor(Date.now() / 1000), length: data['time']}}, banlist.get(data['room'])));
else banlist.set(data['room'], Object.assign({[target.ip]: {time: Math.floor(Date.now() / 1000), length: 30}}, banlist.get(data['room'])));
target.disconnect(true);
io.to(data['room']).emit('ALERT', target.nick+' was banned.');
}
else socket.emit('ALERT', 'No such user found.');
}
else socket.emit('ALERT', 'Not authorized to do that.');
});
socket.on('UNBAN', (data: Object) => {
if(socket.nick === data['room']){
if(banlist.get(data['room']) && banlist.get(data['room'])[data['ip']]){
banlist.set(data['room'], Object.assign(banlist.get(data['room']), {[data['ip']]: null}));
socket.emit('ALERT', data['ip']+' was unbanned.');
}
else
socket.emit('ALERT', 'That IP is not banned.');
}
else socket.emit('ALERT', 'Not authorized to do that.');
});
socket.on('LISTBAN', (data: Object) => {
if(socket.nick === data['room']){
if(banlist.get(data['room'])) {
let bans = Object.keys(banlist.get(data['room']));
let str = '';
for(let i=0;i<bans.length;i++){
str += bans[i]+', ';
}
socket.emit('ALERT', 'Banned IP adresses: '+str.substring(0, str.length - 2));
return;
}
socket.emit('ALERT', 'No one is banned from this room');
}
else socket.emit('ALERT', 'Not authorized to do that.');
});
});
//socketio spam
const socketAS = new socketSpam({
banTime: 20,
kickThreshold: 10,
kickTimesBeforeBan: 3,
banning: true,
io: io
});
socketAS.event.on('ban', (socket) => {
let rooms = Object.keys(socket.rooms);
for(let i=1;i<rooms.length;i++){
if(ircconf.enable) irc.part(socket.nick, rooms[i]);
io.to(rooms[i]).emit('ALERT', socket.nick+' was banned.');
}
if(ircconf.enable) irc.unregisterUser(socket.nick);
store.rm(socket.nick);
});
}