diff --git a/src/config.ts b/src/config.ts index 6c7a88b..a67d000 100644 --- a/src/config.ts +++ b/src/config.ts @@ -8,8 +8,7 @@ export const timezone = 'Europe/Athens'; export const speed = { minDelay: 5, maxDelay: 15, - speedMethod: 'divide', - speedFactor: 60, + baseWpm: 200, }; export const statuses = ['online', 'idle', 'dnd', 'offline']; diff --git a/src/events/message-create/index.ts b/src/events/message-create/index.ts index 451e980..0b0a328 100644 --- a/src/events/message-create/index.ts +++ b/src/events/message-create/index.ts @@ -14,6 +14,7 @@ import { reply as staggeredReply } from '@/utils/delay'; import { getTrigger } from '@/utils/triggers'; import { logTrigger, logIncoming, logReply } from '@/utils/log'; import logger from '@/lib/logger'; +import { updateUserTyping } from '@/utils/global-state'; export const name = Events.MessageCreate; export const once = false; @@ -54,6 +55,7 @@ export async function execute(message: Message) { const ctxId = isDM ? `dm:${author.id}` : guild.id; logIncoming(ctxId, author.username, content); + updateUserTyping(author.id, content); if (!(await canReply(ctxId))) return; diff --git a/src/utils/delay.ts b/src/utils/delay.ts index 36b71a9..6fd3c5f 100644 --- a/src/utils/delay.ts +++ b/src/utils/delay.ts @@ -2,35 +2,17 @@ import { speed as speedConfig } from '@/config'; import { sentences, normalize } from './tokenize-messages'; import { DMChannel, Message, TextChannel, ThreadChannel } from 'discord.js'; import logger from '@/lib/logger'; +import state, { getUserWpm } from '@/utils/global-state'; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); -function calculateDelay(text: string): number { - const { speedMethod, speedFactor } = speedConfig; - - const length = text.length; - const baseSeconds = (() => { - switch (speedMethod) { - case 'multiply': - return length * speedFactor; - case 'add': - return length + speedFactor; - case 'divide': - return length / speedFactor; - case 'subtract': - return length - speedFactor; - default: - return length; - } - })(); - - const punctuationCount = text - .split(' ') - .filter((w) => /[.!?]$/.test(w)).length; - const extraMs = punctuationCount * 500; - - const totalMs = baseSeconds * 1000 + extraMs; - return Math.max(totalMs, 100); +function calculateDelay(text: string, userId?: string): number { + const baseWpm = state.speed.baseWpm; + const userWpm = userId ? getUserWpm(userId) : baseWpm; + const wpm = (baseWpm + userWpm) / 2; + const words = text.split(/\s+/).filter(Boolean).length; + const ms = (words / wpm) * 60 * 1000; + return Math.min(ms, 14000); } export async function reply(message: Message, reply: string): Promise { @@ -48,6 +30,11 @@ export async function reply(message: Message, reply: string): Promise { const segments = normalize(sentences(reply)); let isFirst = true; + while (state.isTyping) { + await sleep(500); + } + state.isTyping = true; + for (const raw of segments) { const text = raw.toLowerCase().trim().replace(/\.$/, ''); if (!text) continue; @@ -58,7 +45,7 @@ export async function reply(message: Message, reply: string): Promise { try { await channel.sendTyping(); - await sleep(calculateDelay(text)); + await sleep(calculateDelay(text, message.author.id)); if (isFirst && Math.random() < 0.5) { await message.reply(text); @@ -71,4 +58,5 @@ export async function reply(message: Message, reply: string): Promise { break; } } + state.isTyping = false; } diff --git a/src/utils/global-state.ts b/src/utils/global-state.ts new file mode 100644 index 0000000..316785b --- /dev/null +++ b/src/utils/global-state.ts @@ -0,0 +1,48 @@ +import { speed as speedConfig } from '@/config'; + +export interface UserTypingStats { + lastTimestamp: number; + wpm: number; +} + +export interface SpeedState { + baseWpm: number; +} + +export interface GlobalState { + speed: SpeedState; + userTyping: Map; + isTyping: boolean; +} + +const state: GlobalState = { + speed: { + baseWpm: speedConfig.baseWpm, + }, + userTyping: new Map(), + isTyping: false, +}; + +export default state; + +export function updateUserTyping(userId: string, message: string) { + const now = Date.now(); + const words = message.trim().split(/\s+/).filter(Boolean).length; + const prev = state.userTyping.get(userId); + if (prev) { + const deltaMin = (now - prev.lastTimestamp) / 60000; + if (deltaMin > 0) { + const wpm = words / deltaMin; + prev.wpm = wpm; + } + prev.lastTimestamp = now; + } else { + state.userTyping.set(userId, { lastTimestamp: now, wpm: state.speed.baseWpm }); + } +} + +export function getUserWpm(userId: string): number { + const stats = state.userTyping.get(userId); + return stats ? stats.wpm : state.speed.baseWpm; +} +