Files
RedditBot/packages/api-contracts/src/chats.ts
T

114 lines
3.7 KiB
TypeScript

import { z } from 'zod';
import { ErrorResponseSchema, RejectedActionResponseSchema } from './common.js';
import { RedditAccountOwnerSchema } from './session.js';
export const ChatMediaSchema = z.strictObject({
originalUrl: z.string(),
localPath: z.string(),
fileName: z.string(),
});
export const ChatMessageSchema = z.strictObject({
eventId: z.string().optional(),
sender: z.string(),
text: z.string(),
timestamp: z.number(),
isSelf: z.boolean(),
isAi: z.boolean().optional(),
media: z.array(ChatMediaSchema).optional(),
});
export const ActiveChatSchema = z.strictObject({
username: z.string(),
roomName: z.string(),
lastMessageTime: z.number(),
lastMessageSnippet: z.string(),
isFromUs: z.boolean(),
unread: z.boolean(),
url: z.string().optional(),
roomId: z.string().optional(),
messages: z.array(ChatMessageSchema).optional(),
originMessage: z.string().optional(),
hasReceivedImages: z.boolean().optional(),
owner: RedditAccountOwnerSchema.optional(),
});
export const ChatsResponseSchema = z.array(ActiveChatSchema);
export const ConversationsResponseSchema = ChatsResponseSchema;
export const SyncKindSchema = z.union([z.literal('full'), z.literal('delta')]);
export const SyncPhaseSchema = z.union([
z.literal('starting'),
z.literal('seeding'),
z.literal('listing-rooms'),
z.literal('resolving-usernames'),
z.literal('loading-details'),
z.literal('finalizing'),
z.literal('done'),
]);
export const SyncProgressSchema = z.strictObject({
kind: SyncKindSchema,
phase: SyncPhaseSchema,
current: z.number(),
total: z.number(),
label: z.string().optional(),
startedAt: z.number(),
});
export const ChatSyncStatusSchema = z.strictObject({
syncing: z.boolean(),
syncingKind: SyncKindSchema.nullable(),
lastFullSyncAt: z.number().nullable(),
lastDeltaSyncAt: z.number().nullable(),
hasEverFullSynced: z.boolean(),
cachedChats: z.number(),
progress: SyncProgressSchema.nullable(),
});
export const ConversationDetailResponseSchema = ActiveChatSchema.extend({
triggeringPost: z.strictObject({
id: z.string(),
subreddit: z.string().optional(),
title: z.string().optional(),
url: z.string(),
imageUrl: z.string().nullable().optional(),
postedAt: z.number().optional(),
sentAt: z.number(),
}).nullable(),
automation: z.strictObject({
turnsUsed: z.number().int().min(0).max(3),
remainingTurns: z.number().int().min(0).max(3),
status: z.enum(['active', 'image-received', 'human-required', 'boundary', 'user-overridden']),
handoffReason: z.string().nullable(),
}),
syncInProgress: z.strictObject({
kind: SyncKindSchema.nullable(),
progress: SyncProgressSchema.nullable(),
staleMessages: z.boolean(),
}).nullable(),
});
export const SuggestedReplyResponseSchema = z.strictObject({
suggestedReply: z.string(),
intent: z.unknown().optional(),
conversationState: z.unknown().optional(),
});
export const SendReplyRequestSchema = z.strictObject({ message: z.string().min(1) });
export const StartConversationRequestSchema = z.strictObject({
accountId: z.string().min(1),
username: z.string().min(1),
message: z.string().min(1),
});
export const StartConversationResponseSchema = z.strictObject({
success: z.literal(true),
chat: ActiveChatSchema,
});
export const ChatSyncTriggerResponseSchema = z.union([
z.strictObject({ success: z.literal(true), kind: SyncKindSchema }),
RejectedActionResponseSchema,
ErrorResponseSchema,
]);
export const FetchAllChatsResponseSchema = z.strictObject({
success: z.literal(true),
count: z.number(),
});
export type ActiveChat = z.infer<typeof ActiveChatSchema>;
export type ChatSyncStatus = z.infer<typeof ChatSyncStatusSchema>;
export type ConversationDetailResponse = z.infer<typeof ConversationDetailResponseSchema>;