import { Inject, Injectable } from "@nestjs/common"; import { RedisService } from "../redis/redis.service.js"; @Injectable() export class ChatRateLimiter { constructor(@Inject(RedisService) private readonly redis: RedisService) {} async consume(accountId: string, sessionId: string): Promise { const prefix = process.env.REDIS_KEY_PREFIX ?? "drift:auth:"; const max = Number(process.env.CHAT_MESSAGE_RATE_LIMIT ?? 30); const ttl = Number(process.env.CHAT_MESSAGE_RATE_WINDOW_SECONDS ?? 60); const keys = [ `${prefix}chat:account:${accountId}`, `${prefix}chat:session:${sessionId}`, ]; const result = await this.redis.executeWithDeadline((client) => client.eval( `for _,k in ipairs(KEYS) do if tonumber(redis.call('GET',k) or '0')+1>tonumber(ARGV[1]) then return 0 end end; for _,k in ipairs(KEYS) do local n=redis.call('INCR',k); if n==1 then redis.call('EXPIRE',k,ARGV[2]) end end; return 1`, { keys, arguments: [String(max), String(ttl)] }, ), ); return Number(result) === 1; } }