import { z } from 'zod'; import { ActivityItemSchema } from './activity.js'; import { BotModeSchema } from './common.js'; export const AccountSendingStateSchema = z.enum([ 'ready', 'bot-stopped', 'scan-only', 'runtime-stopped', 'runtime-failed', 'login-required', 'matrix-cooldown', 'replies-prioritized', 'pacing', 'unestablished', ]); export const AccountStatusSchema = z.strictObject({ accountId: z.string(), redditUsername: z.string(), runtimeState: z.enum(['created', 'starting', 'running', 'stopping', 'stopped', 'failed']), authenticated: z.boolean(), sendingState: AccountSendingStateSchema, messagesThisHour: z.number(), maxMessagesPerHour: z.number(), nextMessageAvailableAt: z.number().nullable(), hourlyResetAt: z.number(), messagesRemaining: z.number(), actionableReplies: z.number(), invitationCooldown: z.strictObject({ until: z.number(), reason: z.string(), redditCode: z.string().nullable(), }).nullable(), failure: z.string().nullable(), }); const PerformanceFields = { totalSent: z.number(), totalResponses: z.number(), responseRate: z.number(), totalImages: z.number(), imageRate: z.number(), pendingSent: z.number().optional(), responseRateLow: z.number().optional(), responseRateHigh: z.number().optional(), }; export const StatsResponseSchema = z.strictObject({ isRunning: z.boolean(), mode: BotModeSchema, dryRunMode: z.boolean(), totalProcessed: z.number(), totalMessagesSent: z.number(), totalResponses: z.number(), responseRate: z.number(), messagesThisHour: z.number(), maxMessagesPerHour: z.number(), subreddits: z.array(z.strictObject({ name: z.string(), type: z.union([z.literal('image'), z.literal('chat')]), })), targetGenders: z.array(z.string()), recentActivity: z.array(ActivityItemSchema), accounts: z.array(AccountStatusSchema), invitationCooldown: z.strictObject({ until: z.number(), reason: z.string(), redditCode: z.string().nullable(), accountId: z.string().optional(), redditUsername: z.string().optional(), }).nullable(), }); export const TimersResponseSchema = z.strictObject({ nextMessageAvailableAt: z.number().nullable(), hourlyResetAt: z.number(), isHourlyLimitReached: z.boolean(), messagesRemaining: z.number(), }); export const SubredditPerformanceSchema = z.strictObject({ subreddit: z.string(), ...PerformanceFields, }); export const SubredditPerformanceResponseSchema = z.array(SubredditPerformanceSchema); export const StrategyPerformanceSchema = z.strictObject({ strategyId: z.string(), ...PerformanceFields, configured: z.boolean().optional(), }); export const StrategyPerformanceResponseSchema = z.array(StrategyPerformanceSchema); export const DailyTrendSchema = z.strictObject({ date: z.string(), ...PerformanceFields, }); export const DailyTrendResponseSchema = z.array(DailyTrendSchema); export const FunnelResponseSchema = z.strictObject({ responseWindowHours: z.number(), processed: z.number(), sent: z.number(), pending: z.number(), mature: z.number(), responded: z.number(), engaged: z.number(), declined: z.number(), boundary: z.number(), upsell: z.number(), images: z.number(), }); const AiUsageFields = { requests: z.number().int().nonnegative(), successes: z.number().int().nonnegative(), refusals: z.number().int().nonnegative(), errors: z.number().int().nonnegative(), promptTokens: z.number().int().nonnegative(), completionTokens: z.number().int().nonnegative(), totalTokens: z.number().int().nonnegative(), costUsd: z.number().nonnegative(), averageLatencyMs: z.number().nonnegative(), }; export const AiUsageSummaryResponseSchema = z.strictObject({ days: z.number().int().positive(), ...AiUsageFields, models: z.array(z.strictObject({ model: z.string(), ...AiUsageFields })), }); export type StatsResponse = z.infer; export type AccountStatus = z.infer; export type AccountSendingState = z.infer; export type TimersResponse = z.infer; export type AiUsageSummaryResponse = z.infer;