Initial
This commit is contained in:
commit
c552956516
23 files changed
+5193
No files matched your search
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user