Reviewers were asked to include or exclude learning trajectories while only seeing metadata — credit class, confidence, status. The decisions and their redacted context turns were already captured and stored, but no endpoint returned them, so the evidence was unreachable from the UI. Add GET /api/learning/trajectories/:id and an expandable row on the learning page that fetches on demand. Also widen SentMessageSchema to accept matrixEventId, imageOutcomeCredit and imageOutcomeAt. The learning loop added these to SentMessageRecord and they flow out of /api/messages, but the strict contract rejected them, breaking the dashboard. Enable generation with shadow mode off so eligible examples reach reply prompts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
340 lines
17 KiB
YAML
340 lines
17 KiB
YAML
reddit:
|
||
# Weights bias how often each sub is picked each poll cycle. Higher weight
|
||
# = more likely to appear earlier in the check order. Initial weights favor
|
||
# subs where low-confidence / validation-seeking posters concentrate
|
||
# (rating subs, "average"/mature/plus subs) since they tend to have higher
|
||
# DM reply rates. Default is 1 if omitted.
|
||
subreddits:
|
||
# Validation-seeking / lower-confidence (highest weight)
|
||
- { name: AverageNudes, weight: 4, tone: 'direct' } # "Average-looking" self-perception
|
||
- { name: gonewild40plus, weight: 4, tone: 'gentle' } # Mature, lower volume, very high reply rate
|
||
- { name: normalnudes, weight: 3 } # Everyday real bodies
|
||
- { name: RateMyNudeBody, weight: 3, tone: 'direct' } # Explicit ask for feedback
|
||
- { name: ratemyboobs, weight: 3, tone: 'direct' } # Rating subs = explicit validation-seeking
|
||
- { name: ratemypussy, weight: 3, tone: 'direct' }
|
||
- { name: cnc_freaks, weight: 4, tone: 'kink-friendly' }
|
||
- { name: traumatizedsluts2, weight: 4, tone: 'kink-friendly' }
|
||
- { name: RateMyRack, weight: 3, tone: 'direct' }
|
||
- { name: GoneWildPlus, weight: 3, tone: 'gentle' } # BBW/plus-size
|
||
- { name: gonewildchubby, weight: 3, tone: 'gentle' } # Soft/chubby bodies
|
||
- { name: gonewild30plus, weight: 2, tone: 'gentle' } # Mature/MILF amateurs
|
||
- { name: GoneMild, weight: 2, tone: 'gentle' } # Softcore teases (shyer posters)
|
||
|
||
# Mid-tier — mixed confidence
|
||
- { name: GoneWildCurvy, weight: 2 }
|
||
- { name: HomemadeNsfw, weight: 2 }
|
||
- { name: realgirlsgonebad, weight: 2, tone: 'dominant' }
|
||
- { name: nsfwselfies, weight: 2 }
|
||
- { name: RealGirls, weight: 2 }
|
||
- { name: Amateur, weight: 2 }
|
||
|
||
# Baseline — generally more confident / curated posters
|
||
- { name: gonewild, weight: 1 } # #1 verified self-posters
|
||
- { name: GoneErotic, weight: 1 }
|
||
- { name: OnOff, weight: 1 }
|
||
- { name: petitegonewild, weight: 1 }
|
||
- { name: tightandtoned, weight: 1 }
|
||
- { name: pawg, weight: 1 }
|
||
- { name: asiansgonewild, weight: 1 }
|
||
- { name: altgonewild, weight: 1 }
|
||
- { name: holdthemoan, weight: 1 }
|
||
|
||
# Chat-only subreddits — no image analysis, gender parsed from post tag
|
||
#
|
||
pollIntervalMs: 30000
|
||
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
||
targetGenders:
|
||
- female # Options: male, female, other, unknown
|
||
filterSellers: true # Skip profiles that appear to be OnlyFans/content sellers
|
||
prioritizeRecentPosts: true # Prefer posts within recentWindowMinutes; older posts only as fallback
|
||
recentWindowMinutes: 30
|
||
maxReadyCandidates: 25 # Pause screening when this many posts are ready to send
|
||
physiqueTargeting:
|
||
enabled: false # When enabled, only scored image posts inside this range continue
|
||
minScore: 1 # 1 = visibly very large-bodied physique
|
||
maxScore: 10 # 10 = exceptionally lean/toned, model-like physique
|
||
# sellerKeywords: [...] # Uncomment to override default keyword list
|
||
|
||
conversations:
|
||
offPlatformKill: true # End the conversation when they push Telegram/Snapchat/payment/etc.
|
||
killOnSellerKeyword: true # Set false to hand seller-keyword hits to an operator instead
|
||
aiReplyScreening:
|
||
enabled: false # Second-stage classifier on inbound replies; fails open
|
||
killConfidence: 0.8
|
||
flagConfidence: 0.6
|
||
|
||
learning:
|
||
captureEnabled: true # Build an auditable dataset without changing messages
|
||
generationEnabled: true # Retrieve eligible examples for reply generation
|
||
shadowMode: false # Inject the retrieved examples into prompts (true = audit only)
|
||
maturityHours: 12
|
||
maxContextExamples: 3
|
||
maxTurnsPerExample: 8
|
||
humanExampleSlots: 2
|
||
autonomousExampleSlots: 1
|
||
minimumAttributionConfidence: exact
|
||
policyVersion: baseline-v1
|
||
|
||
pushNotifications:
|
||
# Phone push via ntfy. The topic is world-readable to anyone who guesses it,
|
||
# so use an unguessable name. Set NTFY_AUTH_TOKEN for a protected topic.
|
||
enabled: false
|
||
serverUrl: "https://ntfy.sh" # Point at your self-hosted instance
|
||
topic: "" # e.g. redditbot-7f3a9c21
|
||
priority: default # min | low | default | high | max
|
||
events:
|
||
imageReceived: true
|
||
replyReceived: false
|
||
messageFailed: false
|
||
botStopped: false
|
||
rateLimited: false
|
||
sessionExpired: true
|
||
syncFailed: false
|
||
|
||
openrouter:
|
||
imageAnalysisModel: "google/gemini-2.5-flash-lite"
|
||
imageAnalysisFallbackModels:
|
||
- "meta-llama/llama-4-maverick"
|
||
- "x-ai/grok-4.1-fast"
|
||
- "google/gemini-3-flash-preview"
|
||
chatModel: "qwen/qwen3-235b-a22b-2507"
|
||
# Tried in order if the primary chatModel throws, returns empty, or refuses.
|
||
# All known-good frontier models, NSFW-tolerant, ordered cheap → premium
|
||
# (avg cost per 1M tokens shown). Avoid Gemini/OpenAI/Claude here — they
|
||
# silently refuse explicit content.
|
||
chatFallbackModels:
|
||
- "deepseek/deepseek-v4-flash" # ~$0.17/M
|
||
- "meta-llama/llama-4-scout" # ~$0.19/M
|
||
- "meta-llama/llama-4-maverick" # ~$0.38/M
|
||
- "deepseek/deepseek-chat-v3.1" # ~$0.50/M
|
||
imageAnalysisPrompt: |
|
||
Analyze this adult image using only directly visible evidence.
|
||
|
||
Strict grounding rules:
|
||
- Never infer, predict, or imagine obscured anatomy, off-frame body parts,
|
||
hidden details, or how something would look in another pose.
|
||
- Do not infer overall body type from a close crop.
|
||
- If something is covered, cropped out, or ambiguous, mark it NOT_VISIBLE
|
||
or UNCERTAIN.
|
||
- Explicit language does not permit inference. Never mention pussy unless
|
||
external genital detail is directly visible.
|
||
|
||
Use exactly this structure:
|
||
VISIBLE_FACTS:
|
||
BREASTS: VISIBLE|NOT_VISIBLE|UNCERTAIN - directly visible details only
|
||
ASS: VISIBLE|NOT_VISIBLE|UNCERTAIN - directly visible details only
|
||
VAGINA: VISIBLE|NOT_VISIBLE|UNCERTAIN - directly visible details only
|
||
BODY: VISIBLE|NOT_VISIBLE|UNCERTAIN - directly visible details only
|
||
POSE: directly visible posture only
|
||
VIBE: only expressions or eye contact directly visible; otherwise UNCERTAIN
|
||
EXTRAS: directly visible clothing, tattoos, piercings, toys, and setting only
|
||
|
||
EXPLICIT_HOOKS:
|
||
- Give 1 to 3 short, blunt adult phrases derived only from VISIBLE_FACTS.
|
||
- Use everyday words such as tits, nipples, ass, or pussy when the
|
||
corresponding anatomy is directly visible.
|
||
- Never mention hidden breasts, nipples, ass, or pussy.
|
||
- Do not add new visual claims or predictions.
|
||
|
||
Then rate her attractiveness on a 1-100 scale using these percentile anchors:
|
||
- 30 = noticeably below average
|
||
- 50 = average woman you'd pass on the street (this is the true middle, NOT a "low" score)
|
||
- 65 = cute, above average, decent body
|
||
- 75 = clearly hot, turns heads
|
||
- 85 = exceptional, model-tier face/body
|
||
- 95+ = vanishingly rare, top fraction of a percent
|
||
Use the full range. Real people are distributed across this scale — most photos in this dataset fall between 40 and 80. Do NOT cluster scores in the 65-75 band as a safe default. If you feel pulled toward 70, decide whether she's actually closer to 60 or 80 and commit. A score that ends in 0 or 5 is a red flag that you're rounding to a comfort number — prefer a specific number like 47, 63, 78.
|
||
|
||
Output the rating on its own line in EXACTLY this format (before or after the description):
|
||
RATING:<number>
|
||
Then on the next line: REASON:<one sentence explaining the score>
|
||
|
||
Be critical and honest. Keep the complete response under 300 words.
|
||
messageGenerationPrompt: |
|
||
Based on this image analysis: {analysis}
|
||
Post title: {title}
|
||
Author: u/{author}
|
||
Post flair: {flair}
|
||
|
||
Attractiveness rating (1-100 scale, 50 = average): {rating}
|
||
If the rating is below 60, lean harder into making her feel sexy and desired — emphasize how hot her pic looks. If the rating is 75+, you can be more confidently flirty without overdoing the flattery.
|
||
|
||
Write a casual Reddit DM (1-3 short sentences) like a horny guy reacting naturally to her post.
|
||
|
||
MUST follow:
|
||
- Start sentences with capital letters, use proper capitalisation
|
||
- Use explicit terms: tits, ass, pussy, boobs (NOT breasts, buttocks, etc)
|
||
- Reference something SPECIFIC from the analysis (her tits, ass, pussy, body, pose)
|
||
- Be direct and confident, not flowery or poetic
|
||
- Follow the selected STYLE exactly, especially its required opening and ending shape
|
||
- Make the message feel materially different from the recent messages supplied below
|
||
|
||
POSSIBLE HOOKS (only when the selected STYLE calls for one):
|
||
- A specific question about the post
|
||
- A playful prediction or challenge
|
||
- A conditional invitation to share something else
|
||
- A statement of desire with no question
|
||
|
||
BAD HOOKS:
|
||
- "How's your day?"
|
||
- "What are you up to?"
|
||
- Generic small talk questions
|
||
- "Tell me about yourself"
|
||
|
||
DO NOT:
|
||
- Sound like an AI or formal writing
|
||
- Use words like "captivating", "alluring", "exquisite", "radiant", "prominent"
|
||
- Use clinical terms like "breasts", "buttocks", "posterior", "physique"
|
||
- Write long elaborate sentences
|
||
- Use all lowercase
|
||
- Default to opening with "Damn", "Wow", "Holy shit", or the same reaction used recently
|
||
- Default to closing with "Got any more pics?", "More please", or a near-copy of a recent closing
|
||
- Use em dashes (—) or en dashes (–). Use commas, full stops, or separate sentences instead. This is a dead giveaway for AI writing.
|
||
- Mention, mirror, or acknowledge the recipient's location
|
||
- Claim or imply you are in the same, nearby, or local area as the recipient, calculate distance, or suggest meeting in person
|
||
|
||
Just write the message, nothing else.
|
||
|
||
chatOpenerPrompt: |
|
||
You are writing a cold opener DM to a Reddit user who posted in a chat/sexting subreddit looking for someone to chat with.
|
||
|
||
Post title: {title}
|
||
Post body: {selftext}
|
||
Author: u/{author}
|
||
Detected gender of poster: {gender}
|
||
Post flair: {flair}
|
||
|
||
YOUR PERSONA:
|
||
- You are male, based in the UK, pick a random age between 30 and 35 for this message
|
||
- Do not mention relationship status unless the selected STYLE explicitly requires it
|
||
- Use British English spelling and phrasing (e.g. "mate", "knackered", "fancy", "cheeky") where it feels natural, not forced
|
||
- You may state that you are in the UK, but never mention or acknowledge the recipient's location
|
||
|
||
Write a casual Reddit DM (1-2 sentences) opening a chat.
|
||
|
||
MUST follow:
|
||
- Include your age/gender/location naturally, but place it where the selected STYLE requires
|
||
- Reference something SPECIFIC from their title/body (their mood, what they're into, vibe, or age), but never their location
|
||
- Be confident and direct, flirty but not creepy
|
||
- Use proper capitalisation, no AI/clinical language
|
||
- End with a hook that invites a reply (ask them what they're into, offer something playful)
|
||
|
||
DO NOT:
|
||
- Use words like "captivating", "alluring", "intriguing"
|
||
- Open with "Hey there" / "How's your day"
|
||
- Write more than 2 sentences
|
||
- Be generic, "wanna chat?" alone is too weak
|
||
- Reuse the opening words, sentence pattern, or closing question from a recent message
|
||
- Claim to be a different age/gender than the persona above
|
||
- Mention or acknowledge the recipient's location
|
||
- Say you are also/nearby/local to the recipient, calculate distance, or suggest meeting in person
|
||
- Use em dashes (—) or en dashes (–) anywhere in the message. Use commas, full stops, or separate sentences instead. This is a dead giveaway for AI writing.
|
||
|
||
Just write the message, nothing else.
|
||
|
||
growthCommentPrompt: |
|
||
Write one short, genuine comment on this r/{subreddit} thread. You are an ordinary UK redditor,
|
||
not a marketer and not an assistant.
|
||
|
||
Title: {title}
|
||
Body: {selftext}
|
||
Flair: {flair}
|
||
|
||
MUST follow:
|
||
- Respond to something SPECIFIC the thread says: a detail, a question it raises, or relevant
|
||
ordinary experience of your own
|
||
- 1-3 sentences, British English, sound like someone typing quickly
|
||
- Plain and useful, or plainly sympathetic; adding nothing is better than padding
|
||
|
||
DO NOT:
|
||
- Include any link, domain, email address, or reference to another platform
|
||
- Mention your profile or posts, or ask anyone to message, DM, or follow you
|
||
- Tag another user
|
||
- Open with a compliment about the post, or restate the title
|
||
- Invent credentials, statistics, or facts you could not know
|
||
- Give medical, legal, or financial advice
|
||
- Use em dashes, or write filler such as "this!", "great post", or "commenting for visibility"
|
||
|
||
strategies:
|
||
explorationRate: 0.2 # 20% of the time try a random strategy instead of the best
|
||
minSamples: 20 # Mature outcomes required before exploiting a strategy
|
||
responseWindowHours: 12 # Age unanswered sends before including them in strategy performance
|
||
variants:
|
||
- id: reaction_statement
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: REACTION STATEMENT. Open with a short physical reaction that is NOT "Damn", "Wow", or "Holy shit".
|
||
Mention one specific visible detail. Finish with a confident statement of desire, not a question and not a request for more.
|
||
- id: question_first
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: QUESTION FIRST. The very first sentence must be a specific, answerable question about her post, pose, outfit, or title.
|
||
Follow with one concise explicit compliment. Do not ask whether she has more pics.
|
||
- id: title_callback
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: TITLE CALLBACK. Open by naturally echoing or answering the post title, without quoting it verbatim.
|
||
Turn that callback into a playful compliment. End with an optional, conditional invitation rather than a generic request.
|
||
- id: teasing_challenge
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: TEASING CHALLENGE. Open with a playful assumption, prediction, or mild challenge based on one visible detail.
|
||
Do not open with a compliment. End with a short dare or question that is specific to the challenge.
|
||
- id: concise_observation
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: CONCISE OBSERVATION. Write exactly one sentence about the most distinctive visible detail in the analysis.
|
||
Do not ask a question, request more content, or begin with an interjection.
|
||
- id: imagined_scenario
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: IMAGINED SCENARIO. Open with a brief, concrete scenario inspired by the pose or setting, then say what it makes you want.
|
||
End on the scenario itself. Do not use a generic compliment opener or ask "got more?".
|
||
|
||
chatStrategies:
|
||
explorationRate: 0.2
|
||
minSamples: 20
|
||
responseWindowHours: 12
|
||
variants:
|
||
- id: chat_identity_first
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: IDENTITY FIRST. Open with your age/gender/location tag, then make one specific reference to their post and ask one answerable question.
|
||
- id: chat_question_first
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: QUESTION FIRST. Open with a specific question prompted by their title or body. Put your age/gender/location naturally in the second sentence.
|
||
- id: chat_post_callback
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: POST CALLBACK. Open by responding to a distinctive phrase, request, or mood from their post. Add your age/gender/location later and close with a playful offer.
|
||
- id: chat_playful_assumption
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: PLAYFUL ASSUMPTION. Start with a light, post-specific assumption about the kind of chat or trouble they want. Include your identity naturally and end with a mild challenge.
|
||
- id: chat_mirror_statement
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: MIRROR STATEMENT. Match their energy and vocabulary, then make a direct statement about why you fit what they asked for. Include your identity, but do not end with a question.
|
||
- id: chat_married_disclosure
|
||
weight: 1
|
||
promptOverride: |
|
||
STYLE: MARRIED DISCLOSURE. Mention naturally in the first sentence that you are married or have a wife, then connect it to one specific detail from their post that caught your attention. Include your age, gender, and UK location naturally, and end with one answerable, post-specific hook. Do not claim an open marriage, your wife's permission or involvement, secrecy, unhappiness, or any other detail about the marriage.
|
||
|
||
rateLimit:
|
||
minDelayBetweenMessagesMs: 15000
|
||
maxMessagesPerHour: 9999
|
||
jitterMs: 60000
|
||
|
||
browser:
|
||
sessionDir: "./data/browser-session"
|
||
headless: true
|
||
dryRun: false # Set to false to actually send messages
|
||
|
||
database:
|
||
path: "./data/reddit-bot.db"
|
||
|
||
web:
|
||
enabled: true
|
||
port: 3000
|