feat: expand outreach targeting and messaging

This commit is contained in:
jjbrunton committed 2026-08-10 17:31:05 +01:00
1 parent 7defff422d
commit d0a8bd0e2f
40 files changed
+890 -82

No files matched your search

+16 -2
View File
@@ -30,6 +30,8 @@ export interface ImageAnalysisResult {
description: string;
detectedGender: DetectedGender;
attractivenessScore: number | null;
/** Subjective 1-10 visible-physique score, or null when the image is insufficient. */
physiqueScore: number | null;
}
interface ProtectedTextPrompt {
@@ -370,6 +372,8 @@ Use every applicable tag; tags may overlap. Set safety to "nsfw" when nudity, ex
const startedAt = Date.now();
const genderPrompt = `First, identify the gender of the person in this image. Respond with EXACTLY one of these on the first line: GENDER:male, GENDER:female, GENDER:other, or GENDER:unknown
On the second line, assess only the directly visible physique. Output exactly PHYSIQUE_SCORE:<number> using an integer from 1 to 10, or PHYSIQUE_SCORE:UNKNOWN when the torso and enough of the body are not visible for a grounded assessment. Do not infer physique from a face, a close crop, clothing that obscures body shape, or off-frame anatomy. This is not a medical diagnosis: 1 means a visibly very large-bodied physique, 5 means a broadly average physique, and 10 means an exceptionally lean, toned, model-like physique. Ignore facial attractiveness, photography quality, pose, and setting.
Then provide your analysis:
${this.config.openrouter.imageAnalysisPrompt}`;
@@ -411,6 +415,12 @@ ${this.config.openrouter.imageAnalysisPrompt}`;
detectedGender = genderMatch[1].toLowerCase() as DetectedGender;
}
let physiqueScore: number | null = null;
const physiqueMatch = fullResponse.match(/PHYSIQUE_SCORE[:\s*_]*[*_\s]*(UNKNOWN|10|[1-9])\b/i);
if (physiqueMatch && physiqueMatch[1].toUpperCase() !== 'UNKNOWN') {
physiqueScore = Number(physiqueMatch[1]);
}
// Parse attractiveness rating (1-100 scale) from response.
// Tolerate markdown wrapping like **RATING:** 63 or RATING: **63**.
let attractivenessScore: number | null = null;
@@ -425,6 +435,7 @@ ${this.config.openrouter.imageAnalysisPrompt}`;
// Remove gender and rating lines from description
const description = fullResponse
.replace(/GENDER:\s*(male|female|other|unknown)\n?/i, '')
.replace(/\**\s*PHYSIQUE_SCORE[:\s*_]*[*_\s]*(?:UNKNOWN|10|[1-9])\**\n?/i, '')
.replace(/\**\s*RATING[:\s*]*[*_\s]*\d{1,3}\**\n?/i, '')
.trim();
@@ -436,10 +447,13 @@ ${this.config.openrouter.imageAnalysisPrompt}`;
this.recordUsage('analyzeImage', model, startedAt, response, description ? 'success' : 'empty');
this.logger.debug(`Image analysis complete (${model}) - Gender: ${detectedGender}, Rating: ${attractivenessScore ?? 'n/a'}`);
this.logger.debug(
`Image analysis complete (${model}) - Gender: ${detectedGender}, `
+ `Physique: ${physiqueScore ?? 'n/a'}, Rating: ${attractivenessScore ?? 'n/a'}`,
);
this.logger.debug(`Description: ${description}`);
return { description, detectedGender, attractivenessScore };
return { description, detectedGender, attractivenessScore, physiqueScore };
}
private isRefusal(text: string): boolean {