diff --git a/protocol/src/protocol.ts b/protocol/src/protocol.ts index 44f0264..b7bfe5a 100644 --- a/protocol/src/protocol.ts +++ b/protocol/src/protocol.ts @@ -2,6 +2,7 @@ export * from './admin.js'; export enum MSAgentProtocolMessageType { // Client-to-server + KeepAlive = "nop", Join = "join", Talk = "talk", Admin = "admin", diff --git a/server/src/client.ts b/server/src/client.ts index 75fd8f0..f2b87de 100644 --- a/server/src/client.ts +++ b/server/src/client.ts @@ -25,6 +25,9 @@ export class Client extends EventEmitter { room: MSAgentChatRoom; socket: WebSocket; + nopTimer: NodeJS.Timeout | undefined; + nopLevel: number; + chatRateLimit: RateLimiter constructor(socket: WebSocket, room: MSAgentChatRoom, ip: string) { @@ -35,6 +38,8 @@ export class Client extends EventEmitter { this.username = null; this.agent = null; this.admin = false; + this.resetNop(); + this.nopLevel = 0; this.chatRateLimit = new RateLimiter(this.room.config.ratelimits.chat); @@ -67,6 +72,20 @@ export class Client extends EventEmitter { }); } + private resetNop() { + clearInterval(this.nopTimer); + this.nopLevel = 0; + this.nopTimer = setInterval(() => { + if (this.nopLevel++ >= 3) { + this.socket.close(); + } else { + this.send({ + op: MSAgentProtocolMessageType.KeepAlive + }); + } + }, 10000) + } + private async parseMessage(data: string) { let msg: MSAgentProtocolMessage; try { @@ -75,6 +94,7 @@ export class Client extends EventEmitter { this.socket.close(); return; } + this.resetNop(); switch (msg.op) { case MSAgentProtocolMessageType.Join: { let joinMsg = msg as MSAgentJoinMessage; diff --git a/webapp/src/ts/client.ts b/webapp/src/ts/client.ts index ddc9a60..c796033 100644 --- a/webapp/src/ts/client.ts +++ b/webapp/src/ts/client.ts @@ -249,6 +249,12 @@ export class MSAgentClient { return; } switch (msg.op) { + case MSAgentProtocolMessageType.KeepAlive: { + this.send({ + op: MSAgentProtocolMessageType.KeepAlive + }); + break; + } case MSAgentProtocolMessageType.Init: { let initMsg = msg as MSAgentInitMessage; this.username = initMsg.data.username;