fix: reconcile physique targeting for queued posts

This commit is contained in:
jjbrunton committed 2026-08-10 18:23:23 +01:00
1 parent d0a8bd0e2f
commit d8d635e1ee
10 files changed
+250 -12

No files matched your search

+56 -3
View File
@@ -106,6 +106,52 @@ function joinText(values: unknown[]): string {
return values.filter((value): value is string => typeof value === 'string').join(' ');
}
function recordValue(value: unknown, key: string): unknown {
return value && typeof value === 'object' ? (value as Record<string, unknown>)[key] : undefined;
}
/** Flatten Reddit's structured profile-link payloads across legacy/current field naming. */
function socialLinkText(userData: unknown): string {
const subreddit = recordValue(userData, 'subreddit');
const candidates = [
recordValue(userData, 'social_links'),
recordValue(userData, 'socialLinks'),
recordValue(subreddit, 'social_links'),
recordValue(subreddit, 'socialLinks'),
];
const values: unknown[] = [];
for (const candidate of candidates) {
if (!Array.isArray(candidate)) continue;
for (const link of candidate) {
values.push(
recordValue(link, 'title'),
recordValue(link, 'handle'),
recordValue(link, 'type'),
recordValue(link, 'url'),
recordValue(link, 'outbound_url'),
recordValue(link, 'outboundUrl'),
);
}
}
return joinText(values);
}
/**
* Catch a narrow paid-content funnel that deliberately keeps the platform name
* out of the bio. Requiring an NSFW profile, an explicit link CTA, and a
* promise of more/exclusive personal content avoids treating ordinary links as
* seller evidence.
*/
function findProfileFunnel(text: string, isNsfw: boolean): string | null {
if (!isNsfw) return null;
const normalized = text.toLocaleLowerCase().replace(/[\u2018\u2019]/g, "'");
const linkCallToAction = /\b(?:hit|check|tap|click|visit|follow)\s+(?:the|my)\s+link\b/;
const contentPromise = /\b(?:see|get|show(?:ing)?|watch)\s+(?:a\s+)?(?:lot\s+)?more(?:\s+of\s+me)?\b|\bexclusive\s+(?:content|pics?|photos?|videos?)\b/;
return linkCallToAction.test(normalized) && contentPromise.test(normalized)
? 'NSFW profile links to more or exclusive personal content'
: null;
}
/** Match configured seller signals in user-controlled text. */
export function findSellerSignal(
text: string,
@@ -422,10 +468,12 @@ export class RedditApiClient {
const userData = response.data?.data;
if (userData) {
const subreddit = userData.subreddit;
const profileText = joinText([
userData.subreddit?.public_description || '',
userData.subreddit?.title || '',
userData.subreddit?.display_name_prefixed || '',
subreddit?.public_description || '',
subreddit?.title || '',
subreddit?.display_name_prefixed || '',
socialLinkText(userData),
]);
const profileSignal = findSellerSignal(profileText, sellerKeywords, sellerDomains);
if (profileSignal) {
@@ -434,6 +482,11 @@ export class RedditApiClient {
);
return true;
}
const funnel = findProfileFunnel(profileText, subreddit?.over_18 === true);
if (funnel) {
this.logger.info(`u/${username} flagged as seller profile (matched funnel: "${funnel}")`);
return true;
}
}
} catch (error) {
const rateLimitError = this.toRateLimitError(error);