18 KiB
Product goals and safety boundaries
Product purpose
RedditBot helps an operator discover suitable adult Reddit users who have explicitly posted in configured communities, start relevant conversations, prioritize existing replies, and learn which outreach approaches lead to genuine engagement. It is an operator-controlled system, not an unconstrained bulk messenger.
The product has five connected goals:
- Find candidates from explicitly configured subreddits.
- Reject unsuitable candidates before expensive AI work or any send.
- Coordinate multiple accounts without contacting the same person repeatedly.
- Continue conversations only within configured, bounded automation policies.
- Make every material decision and outcome visible to the operator.
What good behavior looks like
- Eligibility is completed before selecting a recipient: already-processed posts, recipient history, self-declared age/gender for chat posts, seller signals, content suitability, and AI safety all have explicit outcomes.
- Existing replies take priority over new outreach. An account with actionable inbound messages is not available for a new candidate.
- Candidate inventory follows near-term outbound capacity and the recent-post window; discovery pauses instead of accumulating an ever-older backlog when accounts cannot keep pace.
- Discovery is global while authentication, rate limits, conversations, and content remain account-owned.
- Recipient deduplication is case-insensitive, durable across restarts, and protected by a live reservation during sends.
- Remote side effects are fail-closed: a send is recorded only after the chat service confirms it; interrupted reply claims reconcile against Matrix history before retrying. A queued reply refreshes Matrix immediately before sending and becomes an auditable superseded claim if a newer inbound event or terminal state appeared while it waited.
- Dry-run prepares and records decisions but never sends.
- Operators can see funnel stages, prepared opener and scoring context, blurred source images, account state, rate limits, failures, handoffs, and recent activity rather than inferring success from a process-health check.
- Operators may replace a prepared opener before it is leased for sending. The edited text still passes the deterministic location/meeting safeguard, loses generated strategy/experiment attribution, and is durably labelled
operator-editedrather than credited to the model.
Candidate policy
Configured subreddits have a source type:
image: image-led posts are downloaded and analyzed before an opener is generated.chat: text-led posts require a parseable self-declared gender and adult age, then use the chat-opener prompt.
Image subreddits may separately target either the post author (the default) or a qualifying commenter. Comment-author targeting durably watches newly listed image posts throughout the configured recent window and checks their newest comments in rotation, rather than treating a source post as finished after one pass. A comment qualifies only when it personally expresses a desire to receive or experience the depicted person's role; describing what the commenter would do to the pictured person does not qualify. Confidence must be at least 0.8, and the gender perspective expressed by the comment must be included in reddit.targetGenders when that list is non-empty. unknown therefore fails closed unless it is explicitly targeted. The normal image screen also verifies that the depicted role matches reddit.targetGenders. The comment verdict is an interpretation of public text, not a claim about the author's actual gender or identity. Generic praise, ambiguous reactions, deleted/moderator comments, and the post author's own comments do not qualify. The original image remains scene context, but commenter openers use a separate prompt contract and must ground the message in what the commenter expressed. Drafts fail closed when they attribute the pictured body, pose, post, or image to the commenter, request another image, or omit a clear connection to the comment.
Image analysis may produce a subjective 1–10 visible-physique score. It is not a medical assessment and remains unknown when the image does not show enough of the body for a grounded judgment. The scale is deliberately critical: 5 is broadly average, 6 requires visible above-average leanness or definition, and 8–10 are reserved for rare athletic or model-like physiques. Nudity and photographic presentation do not increase the score. Physique targeting is disabled by default. When an operator enables an inclusive minimum/maximum range, image candidates outside that range—and unscorable image candidates—are rejected before opener generation; chat candidates are unaffected.
Seller filtering combines the candidate post with Reddit profile/about, structured social links when Reddit exposes them, and recent activity evidence. Because Reddit's legacy profile response can omit a social-link button that is visible in its UI, an NSFW bio is also rejected when it combines an explicit link call-to-action with a promise of more or exclusive personal content. The default policy treats Telegram names and links as seller signals alongside paid-content platforms and payment prompts. The configured keyword/domain lists are policy inputs, not a substitute for the surrounding screening flow. A profile lookup failure is not itself proof that a person is a seller; network and rate-limit failures must remain distinguishable from a rejection.
The system must not manufacture personal facts. Generated messages must not claim shared location or proximity, invite an in-person meeting, invent identity/history, ignore a conversation boundary, or suggest chat capabilities the application cannot deliver. Private Reddit chat generation is limited to text and the separately controlled approved saved-picture flow; voice notes, audio messages, and live voice/video/phone calls are rejected deterministically. The final outbound safety check is intentionally separate from prompt wording.
Conversation policy modes
Each subreddit snapshots a policy into a conversation when automation first handles it:
| Mode | Meaning |
|---|---|
manual |
Never auto-reply; surface a human handoff. |
engagement |
Send the normal opener, count the first verified reply as success, then complete without an automated response or human handoff. |
legacy |
At most one optional autonomous media request, counting a request already present in the opener, followed by human handoff. |
r4r |
Bounded rapport-style replies with autonomous safety checks. |
r4r-media |
Rapport first, one bounded media ask, then a handoff/terminal boundary. |
The snapshot matters: changing configuration later must not silently rewrite the rules for an active conversation. Engagement mode marks the conversation and operator triage done as soon as the first post-opener inbound event is durably observed; it does not generate, claim, or send a reply. Other automation stops for seller/upsell evidence, an image received, a detected boundary, unsafe content, an exhausted turn budget, a repeated media request, or a manual policy.
Within those boundaries, R4R replies are expected to participate naturally in consensual adult NSFW conversation instead of moralizing, declining the topic, or diverting ordinary flirting into generic small talk. In-character soft refusals are treated like provider refusals and fall through to the next configured chat model. This does not weaken recipient boundaries, the under-18 handoff, seller/off-platform controls, location and meeting prohibitions, or the separate media-request rules.
Two of those stops are terminal rather than handoffs. A reply that pushes the conversation onto another messaging app, asks for contact details, solicits payment, points at a paid-content funnel, or runs the age/bot "verification" scam sets the automation status to killed, marks the durable seller flag, and resolves the operator's triage state, so the conversation leaves every actionable queue instead of collecting in the manual one. The same deterministic decision runs against cached inbound history during each running-mode reply scan and during the operator-forced seller scan, including conversations that are too old to revive for an automated reply or have already become operator-owned. Detection normalizes the text first — unicode folding, zero-width stripping, leet mapping, separator removal — because these words are routinely obfuscated (0nlyfans, t3l3gram, t e l e g r a m). Ambiguous terms need context: snap on its own is a photo request in this product and never a kill on its own. Off-platform kills are a safety control and stay on independently of reddit.filterSellers; a configured seller keyword or domain also kills unless conversations.killOnSellerKeyword is disabled. Under-18 disclosures and in-person/contact-detail asks remain human handoffs, and now apply to every policy mode rather than the R4R ones only.
Optional second-stage AI screening (conversations.aiReplyScreening) runs only when the deterministic rules come up clean, classifies the inbound side of the conversation as genuine, seller, off-platform, scam, bot, or unclear, and kills at or above killConfidence while handing off at or above flagConfidence. It fails open: a classifier error or outage never blocks a reply, and each inbound event is screened at most once per process. Short refusals are interpreted in sequence: phrases such as “I’m good” become media boundaries only when they answer a media request.
Account growth policy
A new Reddit account cannot start chats until Reddit considers it established. Account growth addresses that by posting a small number of AI-written comments on active threads in an operator-chosen list of communities, per account.
This is inauthentic account activity under Reddit's content policy, and many subreddits prohibit it independently. The realistic failure mode is not a warning but silent filtering, followed by a ban that would also destroy the established accounts alongside it. The controls therefore exist to keep volume low and to detect suppression early, not merely to satisfy a prompt:
- Disabled by default, per account, and never inferred from any other setting.
- Comments post without operator review, so both screening stages fail closed. The deterministic gate rejects links, contact solicitation, user tags, low-effort filler, and any text that reveals the generator or narrates the task. The second-stage classifier must actively approve with sufficient confidence; an outage stalls growth rather than publishing unreviewed text.
- Thread text is attacker-controlled and is passed to the model only as fenced untrusted data. Reddit posts do contain text aimed at automated readers.
- The daily allowance is adaptive by default: it starts at two comments per day and only rises while comments demonstrably survive and earn upvotes, and a removal or a heavily downvoted comment lowers it immediately. An operator may override the figure per account; doing so replaces the adaptive backoff but not the pause below, and the dashboard states that trade-off where the limit is set.
- Two consecutive removals, a comment invisible to signed-out visitors, or a captcha challenge pauses the account until an operator clears it. A failed visibility probe proves nothing and never pauses anything.
- One comment per thread across every owned account, a per-community daily cap, quiet hours, and jittered spacing.
- Due retries run before untouched queued threads, and any pending retry blocks new discovery until it is resolved or ages out. Discovery queues one thread from a fresh weighted-random community pick, avoiding predictable same-community batches.
- Reddit's own refusals are authoritative: a community ban disables that community for that account, a throttle backs off at the scope Reddit named, and a signed-out response invalidates the session rather than being retried.
- Growth deliberately continues while an account is held out of outbound messaging, because building the history is what lifts that hold. It is always the lowest-priority work an account performs, after inbound replies.
- Dry run prepares and records a comment but never posts it. Every posted comment, and every comment refused by either gate, is kept with its reason in the growth audit trail.
Browsing sessions are a separate, disabled-by-default account-growth experiment. They select a conservative number of eligible posts or comments from the same configured communities. The per-account automatic-upvote switch is separately disabled by default; when enabled, a selected item is upvoted through the account's authenticated browser session only in running mode and outside browser dry-run. Owned-account content, outreach recipients, cross-account duplicates, growth-comment threads, promotion experiments, moderator content, closed or removed threads, old or low-context items, and NSFW content unless explicitly allowed are excluded before randomness is applied. Every selection records whether the vote is simulated, pending, submitted, or failed, and browsing never affects the adaptive comment-growth trust level.
Operator exchange media
The exchange library contains only operator-uploaded images. Upload requires confirmation that every depicted adult consented to storage and bot delivery. Received conversation media is account-owned evidence and can never be promoted into this shared library.
The bot may continue to ask another person for an optional photo under the configured conversation policy. During ordinary adult conversation it may also make a low-pressure generic offer to share one of its own approved saved pictures, but it must not promise a particular image or anatomy. A clear acceptance of that immediately preceding offer counts as a generic inbound request; otherwise a library send still requires a direct or indirect inbound image request. Every automated send remains subject to the existing inventory, safety-tier, deduplication, boundary, seller, and send-time checks. Exact anatomy requests match the operator-reviewed tags penis, chest, selfie, topless, or feet; the bot does not substitute a different anatomy when a match is unavailable.
Nonspecific requests broaden progressively within one conversation. The first selects a random unused SFW asset, a later request may include topless/NSFW assets, and further requests may select from the full library. An asset is never selected twice in the same conversation, including after an uncertain send. After Matrix confirms the image event, the bot asks the other person to reciprocate without pressure. Missing inventory, failed delivery, changed conversation state, or an unconfirmed reciprocal message becomes an operator handoff.
Reddit candidate eligibility remains the product's adult-entry assumption; this feature adds no separate verification workflow. Existing deterministic underage disclosures and conversation boundaries still stop automation before a library send.
The conversation dashboard may let an operator choose the specific library asset, but it does not waive these rules. The picker exposes only enabled, unused assets that match the latest inbound request and its current safety tier. A manual choice is revalidated after rate-limit waiting, requires explicit send confirmation, and moves the conversation to operator-owned state after a confirmed delivery.
Success and analytics
Do not collapse all progress into “messages sent.” Useful states include:
- candidates discovered, screened, ready, leased, retrying, rejected, expired, and sent;
- sends awaiting a mature response window versus mature sends;
- replies, declined/boundary/upsell outcomes, and images received;
- model usage, fallback behavior, strategy/sample size, and account ownership.
Learning examples mature only after the conversation policy is no longer active. Full recipient history is reclassified on sync; a later decline or boundary overrides earlier reciprocal activity and removes any previously promoted example.
Fetched listing size is not an eligibility count. Logs and UI should quantify rejection and deferral reasons so a dry funnel can be diagnosed without guessing.
Operational authority
The following require explicit user authorization because they can affect real accounts or production state:
- starting or resuming bot processing;
- sending a message or triggering a send-capable workflow;
- importing/replacing account sessions;
- restarting or deploying the production service;
- changing live configuration;
- deleting/resetting databases, volumes, media, or account state.
Read-only inspection, builds, tests, and documentation work do not grant any of those permissions. If a request is only diagnostic, inspect and explain before editing or operating.
Privacy and secrets
Treat these as sensitive: .env, Reddit cookies, exported session JSON, Matrix access tokens, dashboard/agent tokens, the SQLite database, browser profiles, conversation text, usernames, received private media, operator exchange media, and logs that may contain request metadata.
Never commit or reproduce them in documentation, fixtures, logs, issue text, or tool output. Prefer redacted structural evidence. Production backups contain account and private conversation data and need the same protection as the live volume.
Human-led conversation learning
A received image is an outcome, not proof that the preceding AI action caused it. The learning loop therefore attributes every Matrix event before extracting an example and gives manual interventions their own credit classes: human-created, human-improved, and human-completed AI trajectories. Autonomous and recipient-initiated outcomes remain separate comparison cohorts.
Only privacy-redacted decision context is retained as learning material. Media bytes, private attachment paths, raw prompts, usernames, URLs, email addresses, and phone numbers are not copied into examples. Exact Matrix event IDs are preferred; fingerprint or inferred histories are held for review according to learning.minimumAttributionConfidence.
Examples mature only after the configured delay. Later declines, boundaries, or upsell classifications revoke the trajectory. An operator can explicitly include or exclude a trajectory in Learning review. Generation use is independently gated and defaults to capture-only shadow mode, so collecting evidence cannot silently change live replies.