Show per-account delivery status

This commit is contained in:
jjbrunton committed 2026-08-09 16:12:05 +01:00
1 parent 9b86cee9be
commit 22eb32e5bf
14 files changed
+402 -13

No files matched your search

@@ -84,11 +84,15 @@ export function createConcreteRedditAccountRuntimeFactory(
}
async function buildRuntime(
{ config, logger, db, isBotRunning = () => true }: ConcreteRuntimeFactoryOptions,
{ config, logger: rootLogger, db, isBotRunning = () => true }: ConcreteRuntimeFactoryOptions,
session: VerifiedAccountSession,
recipientDeduplicator: RecipientDeduplicator,
redditRequestCoordinator: RedditRequestCoordinator,
): Promise<RedditAccountRuntime> {
const logger = rootLogger.child({
accountId: session.accountId,
redditUsername: session.redditUsername,
});
const browserProfileDir = accountBrowserProfileDir(
path.resolve(process.cwd(), config.browser.sessionDir),
session.accountId,
+57 -2
View File
@@ -26,18 +26,64 @@ export function createStatsRouter(ctx: WebContext): Router {
router.get('/stats', (_req, res) => {
const runtimes = runningRuntimes(ctx);
const runtimeSnapshots = ctx.services.accountRuntimes?.list() ?? [];
const messages = runtimes.length > 0
? runtimes.flatMap((runtime) => runtime.resources.messagesRepo.getRecent(1000))
: ctx.repos.messages.getRecent(1000);
const totalSent = messages.length;
const totalResponses = messages.filter((m) => m.gotResponse).length;
const cooldown = runtimes.map((runtime) => runtime.resources.chatService.getInvitationCooldown()).find(Boolean)
const cooldownRuntime = runtimes.find((runtime) => runtime.resources.chatService.getInvitationCooldown());
const cooldown = cooldownRuntime?.resources.chatService.getInvitationCooldown()
?? ctx.services.chat?.getInvitationCooldown()
?? null;
const messagesThisHour = runtimes.length > 0
? runtimes.reduce((total, runtime) => total + runtime.resources.rateLimiter.getStats().messagesThisHour, 0)
: ctx.getMessagesThisHour();
const accounts: BotStats['accounts'] = (ctx.services.redditAccounts?.list() ?? []).map((account) => {
const snapshot = runtimeSnapshots.find((item) => item.accountId === account.accountId);
const runtime = snapshot ? ctx.services.accountRuntimes?.get(snapshot.accountId) : null;
const authenticated = runtime?.resources.accountAuth.isFullyAuthenticated() ?? false;
const accountCooldown = runtime?.resources.chatService.getInvitationCooldown() ?? null;
const timers = runtime?.resources.rateLimiter.getTimers() ?? {
nextMessageAvailableAt: null,
hourlyResetAt: Date.now() + 60 * 60_000,
messagesRemaining: ctx.config.rateLimit.maxMessagesPerHour,
};
const messageStats = runtime?.resources.rateLimiter.getStats();
const priority = runtime?.resources.conversationAutomation.getPriorityStatus();
const runtimeState = snapshot?.state ?? 'stopped';
const sendingState: BotStats['accounts'][number]['sendingState'] = runtimeState === 'failed'
? 'runtime-failed'
: account.status === 'login-required' || (runtimeState === 'running' && !authenticated)
? 'login-required'
: runtimeState !== 'running'
? 'runtime-stopped'
: accountCooldown
? 'matrix-cooldown'
: !ctx.getBotRunning()
? 'bot-stopped'
: (priority?.processing || (priority?.actionableBacklog ?? 0) > 0)
? 'replies-prioritized'
: timers.nextMessageAvailableAt !== null && timers.nextMessageAvailableAt > Date.now()
? 'pacing'
: 'ready';
return {
accountId: account.accountId,
redditUsername: account.redditUsername,
runtimeState,
authenticated,
sendingState,
messagesThisHour: messageStats?.messagesThisHour ?? 0,
maxMessagesPerHour: ctx.config.rateLimit.maxMessagesPerHour,
nextMessageAvailableAt: timers.nextMessageAvailableAt,
hourlyResetAt: timers.hourlyResetAt,
messagesRemaining: timers.messagesRemaining,
actionableReplies: priority?.actionableBacklog ?? 0,
invitationCooldown: accountCooldown,
failure: snapshot?.failure ?? null,
};
});
const stats: BotStats = {
isRunning: ctx.getBotRunning(),
@@ -53,8 +99,17 @@ export function createStatsRouter(ctx: WebContext): Router {
subreddits: ctx.config.reddit.subreddits.map((s) => ({ name: s.name, type: s.type })),
targetGenders: ctx.config.reddit.targetGenders,
recentActivity: recentActivity(10),
accounts,
invitationCooldown: cooldown
? { until: cooldown.until, reason: cooldown.reason, redditCode: cooldown.redditCode }
? {
until: cooldown.until,
reason: cooldown.reason,
redditCode: cooldown.redditCode,
...(cooldownRuntime ? {
accountId: cooldownRuntime.session.accountId,
redditUsername: cooldownRuntime.session.redditUsername,
} : {}),
}
: null,
};
+17
View File
@@ -10,11 +10,28 @@ export interface BotStats {
subreddits: { name: string; type: 'image' | 'chat' }[];
targetGenders: string[];
recentActivity: ActivityItem[];
accounts: Array<{
accountId: string;
redditUsername: string;
runtimeState: 'created' | 'starting' | 'running' | 'stopping' | 'stopped' | 'failed';
authenticated: boolean;
sendingState: 'ready' | 'bot-stopped' | 'runtime-stopped' | 'runtime-failed' | 'login-required' | 'matrix-cooldown' | 'replies-prioritized' | 'pacing';
messagesThisHour: number;
maxMessagesPerHour: number;
nextMessageAvailableAt: number | null;
hourlyResetAt: number;
messagesRemaining: number;
actionableReplies: number;
invitationCooldown: { until: number; reason: string; redditCode: string | null } | null;
failure: string | null;
}>;
/** Non-null when Reddit has throttled new DM invitations. */
invitationCooldown: {
until: number;
reason: string;
redditCode: string | null;
accountId?: string;
redditUsername?: string;
} | null;
}