Files
RedditBot/apps/dashboard/src/features/conversations/conversation-api.ts
T

102 lines
3.5 KiB
TypeScript

import type { ZodType } from 'zod';
import {
ConversationsResponseSchema,
ConversationDetailResponseSchema,
ErrorResponseSchema,
ProcessedPostsResponseSchema,
SendReplyRequestSchema,
StartConversationRequestSchema,
StartConversationResponseSchema,
SuccessResponseSchema,
SuggestedReplyResponseSchema,
type ActiveChat,
type ConversationDetailResponse,
type ProcessedPost,
type SuccessResponse,
} from '../../../../../packages/api-contracts/src/index.js';
import { createDashboardRequest } from '../../api/request.js';
async function parseResponse<T>(response: Response, schema: ZodType<T>): Promise<T> {
const payload: unknown = await response.json();
if (!response.ok) {
const parsedError = ErrorResponseSchema.safeParse(payload);
throw new Error(parsedError.success ? parsedError.data.error : `Request failed (${response.status})`);
}
return schema.parse(payload);
}
function conversationPath(id: string): string {
return `/api/conversations/${encodeURIComponent(id)}`;
}
export interface ConversationApi {
list(): Promise<ActiveChat[]>;
detail(id: string): Promise<ConversationDetailResponse>;
suggestReply(id: string): Promise<string>;
suggestMediaAsk(id: string): Promise<string>;
processedPosts(username: string): Promise<ProcessedPost[]>;
sendReply(id: string, message: string): Promise<SuccessResponse>;
startConversation(username: string, message: string): Promise<ActiveChat>;
}
export function createConversationApi(fetchImplementation?: typeof fetch): ConversationApi {
const request = createDashboardRequest(fetchImplementation);
return {
async list() {
return parseResponse(await request('/api/conversations'), ConversationsResponseSchema);
},
async detail(id) {
return parseResponse(await request(conversationPath(id)), ConversationDetailResponseSchema);
},
async suggestReply(id) {
const response = await parseResponse(
await request(`${conversationPath(id)}/suggest-reply`, { method: 'POST' }),
SuggestedReplyResponseSchema,
);
return response.suggestedReply;
},
async suggestMediaAsk(id) {
const response = await parseResponse(
await request(`${conversationPath(id)}/suggest-media-ask`, { method: 'POST' }),
SuggestedReplyResponseSchema,
);
return response.suggestedReply;
},
async processedPosts(username) {
return parseResponse(
await request(`/api/posts/by-author/${encodeURIComponent(username.trim().replace(/^u\//i, ''))}`),
ProcessedPostsResponseSchema,
);
},
async sendReply(id, message) {
const body = SendReplyRequestSchema.parse({ message: message.trim() });
return parseResponse(
await request(`${conversationPath(id)}/send-reply`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}),
SuccessResponseSchema,
);
},
async startConversation(username, message) {
const body = StartConversationRequestSchema.parse({
username: username.trim().replace(/^u\//i, ''),
message: message.trim(),
});
const response = await parseResponse(
await request('/api/conversations/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}),
StartConversationResponseSchema,
);
return response.chat;
},
};
}
export const conversationApi = createConversationApi();