Skill: alexbloch-ia/reddit-account-operations (version 1.2.0)

--- name: reddit-account-operations description: Operating doctrine for Reddit account automation — careful, helpful, non-spam contributor pattern with role separation, post qualification, quotas, anti-spam triggers and recovery. Use this for any scheduled Reddit activity (cron, agent, recurring task) where account safety, sub-mod tolerance and long-term reputation matter more than raw output. --- # Reddit Account Operations This skill is the operating doctrine for every Reddit automation run on a brand, professional or personal account. **The goal is not to comment fast. The goal is to operate Reddit like a careful, helpful human contributor: stable browser, correct context, useful action, no spam, no reputational risk.** Drop-in for any niche (legal, medical, software, finance, creator, ecommerce). Replace the placeholders in section 0 with your own values. --- ## 0. Configure for your brand Before running anything, fill these placeholders in your local copy or your agent's memory: | Placeholder | Example | Your value | |---|---|---| | `<BRAND_NAME>` | "Acme Studio" | — | | `<BRAND_DOMAIN>` | "acme.studio" | — | | `<REDDIT_USERNAME>` | "u/AcmeAlex" | — | | `<BROWSER_PROFILE>` | "reddit-live" | — | | `<BROWSER_PORT>` | "9223" | — | | `<NICHE_KEYWORDS>` | "retrait permis OR contester amende" | — | | `<TARGET_SUBS>` | "r/conseiljuridique, r/AskFrance" | — | | `<WORKSPACE_DIR>` | "~/.openclaw/workspace/reddit-<brand>" | — | All shell snippets below assume an [OpenClaw](https://openclaw.ai) browser CLI bound by CDP, but the doctrine works with any browser-automation stack (Playwright, Puppeteer, Chrome MCP). Swap the CLI calls for your own. ### Quick config (copy-paste YAML) If your agent reads config from YAML, drop this in `<WORKSPACE_DIR>/config.yaml`: ```yaml brand: name: <BRAND_NAME> domain: <BRAND_DOMAIN> reddit: username: <REDDIT_USERNAME> # e.g. "u/AcmeAlex" browser_profile: <BROWSER_PROFILE> # e.g. "reddit-live" browser_port: <BROWSER_PORT> # e.g. 9223 discovery: niche_keywords: <NICHE_KEYWORDS> # e.g. "retrait permis OR contester amende" target_subs: - r/<sub-1> - r/<sub-2> workspace: dir: <WORKSPACE_DIR> # e.g. "~/.openclaw/workspace/reddit-acme" alerts: channel: telegram | slack | discord webhook: <YOUR_WEBHOOK_URL> ``` ### Compatibility The skill is markdown — it works wherever an agent reads `SKILL.md`: | Stack | Skill install path | |---|---| | [Claude Code](https://claude.ai/code) | `~/.claude/skills/reddit-account-operations/` | | [OpenClaw](https://openclaw.ai) | `~/.openclaw/skills/reddit-account-operations/` | | ClawHub-published | one-click install via [clawhub.ai](https://clawhub.ai/alexbloch-ia/reddit-account-operations) | | Cursor / Copilot CLI | drop `SKILL.md` into your project's `.cursorrules` or `AGENTS.md` | | Any LLM agent reading markdown rules | concatenate `SKILL.md` into your system prompt | --- ## 1. Browser architecture ### Physical profile A single browser profile is used per account: - `<BROWSER_PROFILE>` (CDP direct, attached to `http://127.0.0.1:<BROWSER_PORT>`) User data dir: `~/.openclaw-browser-profiles/<BROWSER_PROFILE>`. Use it through the OpenClaw browser CLI: ```bash openclaw browser --browser-profile <BROWSER_PROFILE> status openclaw browser --browser-profile <BROWSER_PROFILE> tabs openclaw browser --browser-profile <BROWSER_PROFILE> navigate https://www.reddit.com/ openclaw browser --browser-profile <BROWSER_PROFILE> snapshot --limit 160 openclaw browser --browser-profile <BROWSER_PROFILE> click <ref> openclaw browser --browser-profile <BROWSER_PROFILE> type <ref> "text" openclaw browser --browser-profile <BROWSER_PROFILE> press Enter openclaw browser --browser-profile <BROWSER_PROFILE> evaluate --fn "<async function>" ``` `evaluate --fn` is the primary API tool: it runs JS in the active tab and returns the function's value as JSON. Use it for `/api/me.json`, `/api/comment`, `/api/submit`, `/api/selectflair`, `/r/<sub>/rising.json`, `/search.json`. ### Roles (mental separation, single physical profile) #### Role: `red-post` Account cockpit. Use for direct account action. Use for: - original informative posts (text/self) - replies on threads - inbox/notifications check - profile checks - karma snapshots - flair application Default page: `https://www.reddit.com/user/<REDDIT_USERNAME without u/>` Mental model: act as the account. Protect it. Do not clutter it. #### Role: `red-engage` Search and discovery radar. Use to find what deserves action. Use for: - keyword searches across target subs - monitoring rising/new posts in target subreddits - finding qualified candidates for replies - competitor observation - trend discovery Default page: `https://www.reddit.com/search/?q=<NICHE_KEYWORDS_URL_ENCODED>&type=link&t=day&sort=new` Mental model: discover, qualify, decide. Do not post impulsively from discovery. #### Role: `red-stealth` Quiet maintenance bay. Use for low-noise maintenance. Use for: - subscribing to relevant subreddits - saving posts for later study - reading without interacting - sub-state observation (flair requirements, karma requirements, mod messages) Default page: `https://www.reddit.com/` Mental model: maintain quietly. No noisy engagement. ### Operational law - `red-post` = act as the account - `red-engage` = discover what to react to - `red-stealth` = maintain quietly - stability matters more than speed - after every run, navigate back to the role's default page (resting state) Never collapse all workflows into chaotic browsing. --- ## 2. Login verification (run first on every cron) ```bash openclaw browser --browser-profile <BROWSER_PROFILE> status openclaw browser --browser-profile <BROWSER_PROFILE> evaluate --fn "async () => { const d = await (await fetch('/api/me.json',{credentials:'include'})).json(); return {name: d.data?.name || null, link_karma: d.data?.link_karma ?? null, comment_karma: d.data?.comment_karma ?? null, total_karma: d.data?.total_karma ?? null, modhash: !!d.data?.modhash}; }" ``` - If `status` is `stopped`: report the blockage in your recap channel and stop. Do not attempt to relaunch Chrome from inside a cron — that's a user-side action. - If `name` is `null`: login expired. Stop and report. --- ## 3. Phase gating (karma) Reddit's spam filters and most sub auto-mods judge new accounts harshly. Run in two phases: - **Phase A** (karma < 100): only karma-builder + ops crons run. Any brand-mentioning cron must check karma at start and abort with `"phase A, karma=X, skip"` if karma < 100. - **Phase B** (karma ≥ 100): all crons authorized. Always read `<WORKSPACE_DIR>/memory/reddit-karma-log.md` at start (last line = current state) AND verify via `/api/me.json`. A Daily Metrics Recap cron appends a snapshot every night and pings your alert channel explicitly when karma crosses 100 for the first time. ### Manual override (advanced) You can force Phase B before karma ≥ 100 by appending a line `YYYY-MM-DD - karma=X - phase=B (manual override)` to `reddit-karma-log.md`. The override line takes priority over the threshold. Use when: - You have an established brand and want immediate brand-aware posting despite low karma. - You accept the risk of more automod removals on early posts (auto-freeze on 2 removals still applies — see §10). Document the decision in `reddit-learnings.md` so future runs know why the threshold was bypassed. --- ## 4. Qualification of a post for reply A post is repliable only if **all** of: - Age ≤ 24h (use `created_utc`) - Comment count < 200 - Score positive (≥ 1, accept controversial-but-genuine) - Not flagged `removed` or `locked` - Question is a real question in your domain (not a vent, not a meme, not a request for free work) - No more than 1 expert-like reply already in top 10 comments (check for domain markers in flairs/body) - Subreddit is not in freeze state (see `reddit-subs-state.md`) - Your account has not interacted with this thread before (`reddit-reply-log.md`) - Phase A: no domain-expert reply at all — only general helpful Phase A comments in Phase A subs. If any check fails: skip. Document why in the recap. --- ## 5. Reply templates ### Phase A (karma builder, NO brand mention) - 1-3 sentences, conversational, genuinely helpful - Match the sub's tone - Never expert-grade advice (medical, legal, financial) - Never link to anything - Never sign with anything that hints at a profession Example structure: ``` [Direct answer or empathy hook]. [One concrete tip from common sense]. [Soft close optional]. ``` ### Phase B (brand-aware, expert) Default mode = **pedagogical**, not promotional. Structure: ``` [Acknowledge the situation in 1 sentence, neutral tone.] [General rule / framework in 2-3 sentences. Cite a source only if essential. No promise.] [Concrete next step the OP can take themselves: deadline, document to gather, official portal, command to run.] [Optional: if the case is complex, mention that a specialist can review the OP's situation. No brand name in the comment body.] ``` Brand link policy — **ZERO outgoing links to `<BRAND_DOMAIN>` (or any owned domain) in any reply or post.** Indirect signaling only (mention a profession, never a brand name or URL). Reddit's automod and most subs flag self-promo aggressively — a single brand URL is enough to get a thread removed or trigger a sub ban. - The username profile is your only allowed brand signal (curious readers click through; no need to push). - No link in the comment body, not even in a trailing line. - No link in a footer / signature block. - No mention of the brand by name. Forbidden in any reply: - Any URL on `<BRAND_DOMAIN>` (and all variants: `www.`, subdomains, shorteners). - "Contact me", "DM me", "check our site", "more info here", phone numbers, email addresses. - Brand name in the comment body. - Promises ("you will win", "it's certain", "guaranteed results"). - Quoting specific past client/customer cases. - Asking the OP to DM you. --- ## 6. Informative post structure (regular cadence, e.g. Tue + Fri) Target sub: pick one from `<TARGET_SUBS>` based on: 1. Sub karma requirement satisfied 2. Sub freeze state (skip if frozen) 3. Topic relevance (consult `reddit-ideas.md` and rotate) 4. Last 14 days have NOT seen you post in that sub (cross-check `reddit-post-log.md`) Format: ``` Title: [Question-form or "Guide:" prefix, ≤ 100 chars] Body (markdown): **Context** : [1 paragraph framing the problem people face] **The rule / framework** : [2-3 paragraphs of plain-language explanation: sources, deadlines, mechanics] **What to do concretely**: 1. [Action 1] 2. [Action 2] 3. [Action 3] **Takeaway** : [1 paragraph summary] --- *[Generic role tag, e.g. "Specialist in <field>"]. I don't take case-specific questions in public comments — for a precise situation, a 1:1 review with a qualified professional works better.* ``` **ZERO brand link** in the body, in the title, or in the closing block. The closing line is an indirect role mention only — no name, no URL, no contact info. Reddit treats brand domains as self-promo; a single URL is usually enough for automod removal in legal/medical/financial subs. Apply flair if the sub requires one. If no relevant flair exists, skip the sub. --- ## 7. Quotas (hard limits) | Action | Limit | |--------|-------| | Replies / 24h (Phase B) | 3 max | | Replies / Reply Pass run | 1-2 max | | Informative posts / week | 2 max | | Informative posts / day | 1 max | | Subscribes / week | 5 max | | Actions in same sub | min 6h apart | | Actions globally | min 30 min apart | | Karma builder comments / run | 1-2 max | | Karma builder comments / day | 3 max | Quota tracking: read `reddit-reply-log.md` and `reddit-post-log.md` at start of every run, count entries with timestamps in the last 24h / 7d, abort early if quotas already met. --- ## 8. Anti-spam triggers (words and patterns to avoid) ### Comment/post content | Avoid | Use instead | |-------|-------------| | "DM me", "contact me", "check our site", "more info here" | (just don't ask, don't link) | | `<BRAND_NAME>` in the comment body | (no brand name in body) | | "free consultation", "free first call" | (no marketing) | | Phone number, email | (never) | | Emojis | (most professional/legal/medical subs are sober) | | All caps for emphasis | (use italics/bold sparingly) | | **Any URL on `<BRAND_DOMAIN>`** (and `www.`, subdomains, shorteners) | **never**, in any reply or post | | Multiple external links of any kind | max 1 *external* link total per post (never in replies) | | bit.ly / tinyurl / shorteners | banned — Reddit flags these as spam directly | ### Behavioral triggers - Same content cross-posted < 7 days apart → spam detection - ≥ 2 links in a single comment → auto-removed - New account (karma < 100) posting in large subs → auto-removed - Replying within 60s of post creation → looks bot-like, wait ≥ 5 min - Same opening phrase across multiple replies → flagged - > 3 actions in 30 min → temporary rate limit --- ## 9. Human-like posting flow (anti-CAPTCHA) Reddit's `/api/submit` endpoint is gated behind **reCAPTCHA Enterprise Invisible** (sitekey [REDACTED_SECRET] at the time of writing). Posting purely via API will fail silently or get blocked. The reliable path is a **human-like UI flow** through the browser CLI: dwells, slow typing, reading pauses — enough behavioral signal to score above reCAPTCHA's threshold and let the post through. ### Flow 1. **Pre-flight session check** — `/api/me.json` returns a non-null `name`. 2. **Navigate to the sub feed** — let it load (≥ 1.5 s dwell). 3. **Human scroll** — scroll the sub feed for 4-7 s to simulate reading. 4. **Navigate to `/r/<sub>/submit`** — wait for composer ready. 5. **Find the title field via UI ref** (the localized label, e.g. "Title" / "Titre"), **type slowly** (50-120 ms/key with jitter), then 0.8-1.5 s pause. 6. **Find the body field via UI ref** ("Body" / "Champ de texte du corps de la publication"), type slowly, 0.8-1.5 s pause. 7. **Review pause** — 3-7 s simulating proof-reading. 8. **Re-snapshot the UI** — the Publish button transitions `disabled → enabled` once the fields are filled, so its ref **changes**. Always re-snapshot before clicking. 9. **Click Publish once**, then wait for redirect to `/comments/<id>/` (≤ 30 s). Total flow: **~6 minutes per post**. Slower is the point — speed is what trips the captcha. ### Exit codes (recommended convention for your script) | Code | Meaning | Recap action | |------|---------|--------------| | 0 | Published, URL on stdout | `status: ok`, log post URL | | 1 | Fatal error (UI ref missing, empty body) | `status: error`, attach screenshot | | 2 | CAPTCHA visible mid-flow | `status: blocked`, fall back to "draft to alert channel for manual publish" | | 3 | Session / login KO | `status: blocked`, alert user to re-login | ### Gotchas (real ones, costly to discover) - **`LC_NUMERIC` trap on macOS**: locales like `fr_FR.UTF-8` format decimals as `0,123` instead of `0.123`. `sleep` and `awk` reject the comma → all dwells fall back to 0 → typing becomes instant → captcha fires immediately. **Force `LC_ALL=C` in any helper that does fractional-second math.** First time this bites you, you'll think your script is correct because no error is raised; the post just gets removed. - **Re-snapshot before clicking Publish**: the Publish button's UI ref changes when it transitions from disabled to enabled. Stale refs = wrong click target. - **Localize the field labels**: the labels `Title` / `Body` / `Publish` are localized server-side based on the account language. Either pin the account UI to English or maintain a label-map (FR/EN/etc.) and try both in `find_ref_named`. - **Cron timeout**: bump the per-job timeout to **at least 1200 s** for posting crons — a 60-120 s default will kill a successful flow. - **Save debug artifacts**: screenshots + page snapshots at each step in a scratch dir (e.g. `/tmp/<your-namespace>/reddit-post/`). They are gold when a post silently fails. - **Reply Pass doesn't need this** — replies use a different code path and do not currently trigger reCAPTCHA Enterprise. Only top-level *post* creation is gated. ### When the UI changes Reddit ships UI changes regularly. When `find_ref_named` starts returning null on labels that used to work: 1. Take a manual snapshot via your browser CLI. 2. Inspect the new label string for Title / Body / Publish. 3. Update the label map. 4. Re-test on `r/test` (safe, no consequence) before re-enabling the live posting cron. --- ## 10. Sub state management File: `<WORKSPACE_DIR>/memory/reddit-subs-state.md` For each sub, track: - Last action timestamp (any reply or post) - Last 5 actions and their outcome (live / removed / locked) - Karma requirement observed - Flair required (yes/no/list) - Freeze state (yes if 2 last actions were removed → unfreeze date = removal date + 7 days) - Mod messages received (any DM from /r/[sub] mods → flag for human review) Update at the end of every run that touched the sub. --- ## 11. Recovery & blockers | Issue | Action | |-------|--------| | `status: stopped` | Report in recap: "Chrome <BROWSER_PROFILE> stopped, user action required (relaunch Chrome on port <BROWSER_PORT>)". Stop. | | `name: null` from /api/me.json | Report login expired. Stop. | | HTTP 429 | Stop the run, report "rate limited", wait next scheduled run. | | HTTP 403 on POST | Likely modhash expired — re-fetch from /api/me.json once, retry once. If still 403: report and stop. | | Captcha / challenge page | Stop. Never attempt to solve. Report for user action. | | `result.json.errors` contains `RATELIMIT`, `DOMAIN_BANNED`, `BAD_CAPTCHA`, `SUBREDDIT_NOEXIST`, `USER_BLOCKED` | Translate the error in the recap, do not retry, freeze the sub if it's a sub-level error. | | Comment posted but not visible in user profile after 5 min | Possible shadowban. Flag for human verification (open profile in incognito). | | Post removed by automod < 30 min after publish | Auto-freeze sub for 7 days. Append to `reddit-learnings.md`. | | `evaluate` returns null with no error | Page is not on reddit.com → navigate first, retry once. | --- ## 12. Mandatory recap (alert channel + memory) At the end of each cron: **Alert channel (Telegram / Slack / Discord) — final run message**: ``` [Job name] — [status: ok|partial|blocked|skipped] Public actions: [list short OR "no post/reply"] Links: [URLs of new comments/posts] Karma: link_karma=X comment_karma=Y total=Z Blockers: [text OR "—"] Next action: [1 line] ``` **Memory** — append to `<WORKSPACE_DIR>/memory/reddit-recaps.md`: ``` ## YYYY-MM-DD HH:MM TZ — <job-id> — status: <status> - Job: <description> - Phase: A|B - Karma: link=X comment=Y total=Z - Actions: <list or "no action — reason"> - Subs touched: <list> - Links: <URLs> - Blockers: <text or "—"> - Next useful action: <1 line> ``` If no action was taken: say so clearly (`no post/reply/follow/subscribe performed, reason`). --- ## 13. Memory files inventory Located at: `<WORKSPACE_DIR>/memory/` | File | Purpose | Update cadence | |------|---------|----------------| | `reddit-recaps.md` | Per-run logs (mandatory append) | Every cron run | | `reddit-post-log.md` | Informative posts (sub, URL, date, score) | Each informative post run | | `reddit-reply-log.md` | Replies sent (thread URL, comment URL, sub, date, removed?) | Each reply pass | | `reddit-karma-log.md` | Daily karma snapshot + phase | Daily Metrics Recap | | `reddit-subs-state.md` | Per-sub freeze, flair, karma req | After any sub-touching action | | `reddit-ideas.md` | Content backlog for informative posts | Weekly Planning + ad hoc | | `reddit-learnings.md` | Patterns observed (what got upvoted / removed) | Weekly Planning + ad hoc | --- ## 14. Account identity guardrails This account exists to be **useful in public** with the brand as a *background* signal. - The username should be intentionally neutral — do not advertise the brand in the handle - Profile bio: a short generic line, no firm name, no contact info - Never reply to a DM that asks for expert-grade advice — direct the person to the brand contact page via a standard polite reply - Never share private case details, names, or numbers - Never reuse strict templates from your CRM / case management as Reddit content - Never promise an outcome - Never charge or solicit payment via Reddit If anyone DMs about hiring you: reply once with a neutral line (`"the studio takes inquiries via <BRAND_DOMAIN>/contact"`) and stop. --- ## 15. Phase A → Phase B transition When the Daily Metrics Recap detects `total_karma ≥ 100`: 1. Append to `reddit-karma-log.md`: `YYYY-MM-DD - karma=X - PHASE_B_THRESHOLD_REACHED` 2. Send a distinct alert: `🎉 Reddit karma ≥ 100 — enable Phase B (jobs.json: keyword-monitor-* + reply-pass-* + informative-post-* + subscribe-growth — flip enabled=true)` 3. Do NOT auto-flip the crons (manual user action — reduces risk of premature B-phase posting) Once Phase B is enabled by the user: - First week: 1 reply/day max (not the full 3) — soft ramp - Keep karma builder running for 2 more weeks in parallel - Once total_karma ≥ 250: disable karma builder, keep only Phase B crons --- ## 16. Stability discipline - Read the UI before clicking - One click → verify with a snapshot - Do not stack clicks - Close stale tabs at the end of every run - Return to the role's default page when done - Never silently fake a successful action — always verify via API or UI confirmation **Better silence than spam. Better a blockage report than a fake success.** --- ## 17. First-run checklist Before enabling any cron, run through this checklist: - [ ] Section 0 placeholders filled in your local copy / agent memory. - [ ] Reddit account created with **neutral username** (no brand in the handle). - [ ] Profile bio is one short generic line — no firm name, no contact info. - [ ] Browser profile launched at `http://127.0.0.1:<BROWSER_PORT>` and logged in. - [ ] `/api/me.json` returns a non-null `name` (login verified). - [ ] `<WORKSPACE_DIR>/memory/` directory exists with the 7 memory files (see section 13) — empty is fine, scripts append to them. - [ ] Alert channel (Telegram / Slack / Discord) webhook tested with a "hello" message. - [ ] Phase A confirmed: `total_karma < 100` → only karma-builder + ops crons enabled. - [ ] At least 2 weeks of organic activity (subscribes, low-stakes comments) **before** enabling brand-mentioning crons, even if karma ≥ 100. A bash one-liner to init the memory files: ```bash mkdir -p "<WORKSPACE_DIR>/memory" && cd "$_" && touch reddit-recaps.md reddit-post-log.md reddit-reply-log.md reddit-karma-log.md reddit-subs-state.md reddit-ideas.md reddit-learnings.md ``` (The GitHub repo ships an `init-memory.sh` script that does the same interactively.) --- ## 18. FAQ **Q: Do I need OpenClaw to use this skill?** A: No. OpenClaw browser CLI is the example stack — the doctrine works with Playwright, Puppeteer, Chrome MCP, or any CDP-capable tool. Swap the CLI calls. **Q: Can I use this skill for multiple Reddit accounts?** A: Yes — clone the workspace dir per account. Each account gets its own `memory/` and its own browser profile. Do not cross-link accounts in any reply. **Q: What if my niche has only one relevant sub and it's strict?** A: Run Phase A on adjacent subs (low-stakes, high-volume) to build karma, then start Phase B on the niche sub with the longest possible runway. Some niche subs also require 90+ days of account age regardless of karma. **Q: Is shadowban detection reliable?** A: Partially. The "comment posted but invisible in incognito after 5 min" check catches most shadowbans, but Reddit also does **community-specific shadowbans** that don't fire that signal. Track per-sub upvote ratios over time as a backup signal. **Q: Can I run multiple crons in parallel?** A: Yes, but respect the global "30 min apart" rule across all crons. A lock file at `<WORKSPACE_DIR>/.lock` is the cleanest way to enforce it. **Q: What if my account is suspended?** A: Stop everything. Do not appeal automatically. Manual review only. Document the suspension in `reddit-learnings.md` with the exact last action before suspension.


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)