fix: reconcile physique targeting for queued posts
This commit is contained in:
1 parent
d0a8bd0e2f
commit
d8d635e1ee
10 files changed
+250
-12
No files matched your search
+56
-3
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user