88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
import type { BoxBrainAvailability } from '../core/types';
|
|
|
|
export type RandomSource = () => number;
|
|
|
|
export interface TypingDelayOptions {
|
|
rng?: RandomSource | undefined;
|
|
minSecondsPerCharacter?: number | undefined;
|
|
maxSecondsPerCharacter?: number | undefined;
|
|
}
|
|
|
|
export interface ReplyDelayOptions {
|
|
isFirstReplyInExchange: boolean;
|
|
rng?: RandomSource | undefined;
|
|
onlineMinSeconds?: number | undefined;
|
|
onlineMaxSeconds?: number | undefined;
|
|
dndReplyProbability?: number | undefined;
|
|
dndMinSeconds?: number | undefined;
|
|
dndMaxSeconds?: number | undefined;
|
|
}
|
|
|
|
export const ONLINE_AVAILABILITY: BoxBrainAvailability = { mode: 'online' };
|
|
export const DND_AVAILABILITY: BoxBrainAvailability = { mode: 'do_not_disturb' };
|
|
export const OFFLINE_AVAILABILITY: BoxBrainAvailability = { mode: 'offline' };
|
|
|
|
export class TimingProfile {
|
|
createTypingDelay(message: string, options: TypingDelayOptions = {}): number {
|
|
if (message.length === 0) {
|
|
return 0;
|
|
}
|
|
|
|
const rng = options.rng ?? Math.random;
|
|
const min = options.minSecondsPerCharacter ?? 0.05;
|
|
const max = options.maxSecondsPerCharacter ?? 0.08;
|
|
const secondsPerCharacter = interpolate(min, max, clampUnit(rng()));
|
|
|
|
return roundSeconds(message.length * secondsPerCharacter);
|
|
}
|
|
|
|
createReplyDelay(availability: BoxBrainAvailability, options: ReplyDelayOptions): number | null {
|
|
if (availability.mode === 'offline') {
|
|
return null;
|
|
}
|
|
|
|
if (!options.isFirstReplyInExchange) {
|
|
return 0;
|
|
}
|
|
|
|
const rng = options.rng ?? Math.random;
|
|
|
|
if (availability.mode === 'do_not_disturb') {
|
|
const probability = options.dndReplyProbability ?? 0.2;
|
|
if (clampUnit(rng()) > probability) {
|
|
return null;
|
|
}
|
|
|
|
return roundSeconds(interpolate(options.dndMinSeconds ?? 60, options.dndMaxSeconds ?? 600, clampUnit(rng())));
|
|
}
|
|
|
|
return roundSeconds(interpolate(options.onlineMinSeconds ?? 1, options.onlineMaxSeconds ?? 12, clampUnit(rng())));
|
|
}
|
|
}
|
|
|
|
const DEFAULT_TIMING_PROFILE = new TimingProfile();
|
|
|
|
export function createTypingDelay(message: string, options: TypingDelayOptions = {}): number {
|
|
return DEFAULT_TIMING_PROFILE.createTypingDelay(message, options);
|
|
}
|
|
|
|
export function createReplyDelay(availability: BoxBrainAvailability, options: ReplyDelayOptions): number | null {
|
|
return DEFAULT_TIMING_PROFILE.createReplyDelay(availability, options);
|
|
}
|
|
|
|
function interpolate(min: number, max: number, ratio: number): number {
|
|
return min + (max - min) * ratio;
|
|
}
|
|
|
|
function clampUnit(value: number): number {
|
|
if (Number.isNaN(value)) {
|
|
return 0;
|
|
}
|
|
|
|
return Math.min(1, Math.max(0, value));
|
|
}
|
|
|
|
function roundSeconds(value: number): number {
|
|
return Math.round(value * 1_000_000) / 1_000_000;
|
|
}
|