This commit is contained in:
jjbrunton committed 2026-01-04 19:16:31 +00:00
commit c552956516
23 files changed
+5193

No files matched your search

+101
View File
@@ -0,0 +1,101 @@
import type { RedditApiClient, RedditPost } from '../reddit/api.js';
import type { OpenRouterClient, DetectedGender } from '../ai/openrouter.js';
import type { Logger } from '../utils/logger.js';
import type { Config } from '../config/index.js';
import { downloadImage, bufferToBase64, isValidImageMimeType } from '../utils/image.js';
export interface ProcessedPost {
post: RedditPost;
imageAnalysis: string;
detectedGender: DetectedGender;
generatedMessage: string;
}
export class ProcessorService {
private successfulExamples: string[] = [];
constructor(
private redditApi: RedditApiClient,
private aiClient: OpenRouterClient,
private config: Config,
private logger: Logger
) {}
setSuccessfulExamples(examples: string[]): void {
this.successfulExamples = examples;
if (examples.length > 0) {
this.logger.info(`Loaded ${examples.length} successful message examples for learning`);
}
}
async processPost(post: RedditPost): Promise<ProcessedPost | null> {
const imageUrl = this.redditApi.getImageUrl(post);
if (!imageUrl) {
this.logger.warn(`No image URL found for post ${post.id}`);
return null;
}
this.logger.info(`Processing post: "${post.title.slice(0, 50)}..." by u/${post.author}`);
// Download image
let imageData;
try {
imageData = await downloadImage(imageUrl, this.config.reddit.userAgent);
} catch (error) {
this.logger.error(`Failed to download image from ${imageUrl}:`, error);
return null;
}
// Validate image type
if (!isValidImageMimeType(imageData.mimeType)) {
this.logger.warn(`Invalid image type ${imageData.mimeType} for post ${post.id}`);
return null;
}
// Check image size (skip very small images like thumbnails)
if (imageData.buffer.length < 5000) {
this.logger.warn(`Image too small (${imageData.buffer.length} bytes) for post ${post.id}`);
return null;
}
// Convert to base64
const base64 = bufferToBase64(imageData.buffer);
// Quick gender check first (saves API costs on non-matching posts)
const targetGenders = this.config.reddit.targetGenders;
if (targetGenders.length > 0) {
const detectedGender = await this.aiClient.detectGenderOnly(base64, imageData.mimeType);
if (!targetGenders.includes(detectedGender)) {
this.logger.info(
`Skipping post ${post.id} - detected gender "${detectedGender}" not in target list [${targetGenders.join(', ')}]`
);
return null;
}
this.logger.debug(`Gender check passed: ${detectedGender}`);
}
// Full analysis and message generation (only for matching posts)
const { analysis, message } = await this.aiClient.analyzeAndGenerateMessage(
base64,
imageData.mimeType,
post.title,
post.author,
this.successfulExamples
);
if (!message || message.length < 10) {
this.logger.warn(`Generated message too short or empty for post ${post.id}`);
return null;
}
return {
post,
imageAnalysis: analysis.description,
detectedGender: analysis.detectedGender,
generatedMessage: message,
};
}
}