Forum Marketing — Grassroots Traffic, Trust & Style Matching
Complete forum marketing orchestration with advanced linguistic style matching. Integrates open-source tools with existing Hermes skills for an end-to-end pipeline: corpus extraction → style analysis → persona calibration → content generation in forum-native voice → multi-account deployment → anti-detection.
Philosophy
Effective forum marketing doesn't look like marketing. It looks like a genuine community member sharing something they found valuable. The key insight: you must write exactly like the community writes. Not "good writing" — THEIR writing. Same sentence rhythms, same jargon, same humor register, same formatting quirks. When your post is indistinguishable from a top-voted community post, you win.
Three principles:
- Style match before you post. Scrape the forum's top content. Extract linguistic patterns. Generate only after you understand how they talk.
- Give before you take. 80% of posts should be genuine contributions with zero promotion. Build the account's reputation first.
- The thread is the product. A well-crafted discussion with stylistically matched comments is worth more than any link drop.
When to Use
- User wants to promote a product, SaaS, tool, website, or content via forums
- User asks about "forum marketing," "Reddit promotion," "grassroots campaigns"
- User wants posts that match a specific forum's writing style
- User says "write like they do" or "match the style of r/..."
- User wants to orchestrate multi-account engagement on a thread
- User needs to build trust/credibility in a forum before promoting
Don't use for:
- Single-platform social media (use x-posting-agent)
- Pure content marketing without forum engagement
- One-off link drops without style analysis (waste of effort)
Tool Ecosystem: What We Have vs. What's Open Source
This skill bridges existing Hermes skills with external open-source tools:
| Pipeline Stage | Hermes Skill | External Open-Source Tool |
|---|---|---|
| Forum Discovery | agent-reach (Exa search, Jina Reader) |
PRAW subreddit search, subredditstats.com |
| Corpus Extraction | agent-reach (page reading) |
PRAW, asyncpraw, arctic-shift (historical data) |
| Linguistic Style Analysis | voice-humanizer (voice fingerprint) |
textstat, NLTK, spaCy, stylo (R), jstylometry |
| AI Tell Removal | humanizer (28-pattern detection) |
brandonwise/humanizer CLI, GPTZero API |
| Narrative Alignment | narrative-mimicry (community narratives) |
PRAW top-posts corpus analysis |
| Content Generation | DeepSeek V4 Pro (LLM drafting) | LLaMA/Mistral local models for batch generation |
| Browser Automation | browser-automation (Playwright, stealth) |
puppeteer-extra-plugin-stealth, playwright-stealth, undetected-chromedriver |
| Proxy Management | browser-automation (ProxyEmpire) |
proxybroker, proxy-pool, BrightData, IPRoyal |
| Multi-Account Mgmt | - | Playwright contexts, Multilogin (reference), session-storage isolation |
| API Posting | - | PRAW (Python), snoowrap (JS), asyncpraw (async) |
| Scheduling | x-posting-agent patterns |
cronjob, custom Python scheduler |
| Analytics | - | PRAW post metrics, SocialGrep, reddit-trends, custom analytics |
| Anti-Detection | browser-automation (full coverage) |
puppeteer-extra-stealth, fingerprint-suite, camo-proxy |
Architecture
~/.hermes/forum-marketing/
├── campaigns.json — active and archived campaigns
├── accounts.json — forum account registry (personas)
├── forums.json — discovered forums with metadata
├── proxies.json — proxy pool assignments
├── <campaign-id>/
│ ├── campaign.json — campaign parameters + status
│ ├── forum_corpus/ — PHASE 0: scraped forum content
│ │ ├── <forum>-top-posts.json — top 50-100 posts
│ │ ├── <forum>-top-comments.json — top comments
│ │ └── <forum>-corpus.txt — plaintext for NLP
│ ├── style_analysis/ — PHASE 0: linguistic analysis
│ │ ├── <forum>-fingerprint.json — stylometric fingerprint
│ │ ├── <forum>-vocab-frequency.csv
│ │ └── <forum>-style-rules.md — human-readable style guide
│ ├── persona_calibration/ — PHASE 1: persona tuning
│ │ └── <persona>-voice.json — calibrated voice per persona
│ ├── op_drafts/ — PHASE 2: OP drafts
│ │ └── <date>-<version>.md
│ ├── comment_drafts/ — PHASE 3: staged comments
│ │ └── <persona>-<sequence>.md
│ ├── schedule.json — timing plan
│ ├── published.json — published post/comment IDs + URLs
│ └── metrics.json — engagement + traffic data
└── templates/
├── persona_template.json
├── style_extraction.py — corpus extraction script
└── style_analyzer.py — stylometric analysis script
Phase 0: Linguistic Style Analysis (RUN FIRST — NEVER SKIP)
This is the most important phase. Posts that don't match the forum's writing style get downvoted, flagged, or banned. Posts that match become top content.
0.1 — Corpus Extraction
Extract 50-100 top posts + their top comments from the target forum:
Reddit via PRAW (Python Reddit API Wrapper):
# Install: pip install praw
# Setup: Reddit API credentials at https://www.reddit.com/prefs/apps
import praw
import json
from datetime import datetime
reddit = praw.Reddit(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
user_agent="forum-style-analyzer/1.0"
)
subreddit = reddit.subreddit("TARGET_SUBREDDIT")
corpus = []
# Top posts from past year
for post in subreddit.top(time_filter="year", limit=100):
entry = {
"id": post.id,
"title": post.title,
"selftext": post.selftext,
"score": post.score,
"num_comments": post.num_comments,
"upvote_ratio": post.upvote_ratio,
"created_utc": post.created_utc,
"author": str(post.author) if post.author else "[deleted]",
"flair": post.link_flair_text,
"url": post.url,
"top_comments": []
}
# Extract top 5 comments per post
post.comments.replace_more(limit=0)
for comment in post.comments[:5]:
entry["top_comments"].append({
"id": comment.id,
"body": comment.body,
"score": comment.score,
"author": str(comment.author) if comment.author else "[deleted]",
})
corpus.append(entry)
# Save
with open("forum_corpus/<forum>-top-posts.json", "w") as f:
json.dump(corpus, f, indent=2)
Alternative: asyncpraw for bulk extraction (faster):
pip install asyncpraw
import asyncio
import asyncpraw
async def extract_corpus(subreddit_name, limit=100):
reddit = asyncpraw.Reddit(
client_id="...",
client_secret="...",
user_agent="forum-style-analyzer/1.0"
)
subreddit = await reddit.subreddit(subreddit_name)
posts = []
async for post in subreddit.top(time_filter="year", limit=limit):
posts.append(post)
return posts
Non-Reddit forums:
# Use agent-reach's Jina Reader for forum scraping
curl -s "https://r.jina.ai/FORUM_URL" > forum_page.md
# For Discourse forums, use their JSON API
curl -s "https://FORUM_URL/latest.json" | python3 -m json.tool
# For XenForo/phpBB, use agent-reach + manual page extraction
Historical data via arctic-shift (Reddit):
# arctic-shift is the Pushshift successor — free Reddit search API
curl -s "https://arctic-shift.photon-reddit.com/api/posts/search?subreddit=SUBREDDIT&sort=top&limit=50"
0.2 — Linguistic Fingerprint Extraction
Extract the forum's writing style fingerprint using textstat + NLTK/spaCy:
# Install: pip install textstat nltk spacy
# python -m spacy download en_core_web_sm
import textstat
import nltk
import json
from collections import Counter
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk.corpus import stopwords
nltk.download('punkt')
nltk.download('stopwords')
nltk.download('averaged_perceptron_tagger')
def extract_forum_fingerprint(corpus_file):
"""Extract comprehensive linguistic fingerprint from forum corpus."""
with open(corpus_file) as f:
posts = json.load(f)
all_text = []
all_titles = []
all_comments = []
for post in posts:
all_titles.append(post["title"])
if post["selftext"]:
all_text.append(post["selftext"])
for comment in post.get("top_comments", []):
all_comments.append(comment["body"])
full_corpus = " ".join(all_text + all_comments)
sentences = sent_tokenize(full_corpus)
words = word_tokenize(full_corpus.lower())
words_no_stop = [w for w in words if w not in stopwords.words('english') and w.isalpha()]
fingerprint = {
"forum_id": "<forum-slug>",
"analyzed_at": datetime.utcnow().isoformat(),
"corpus_stats": {
"total_posts": len(posts),
"total_comments": len(all_comments),
"total_words": len(words),
"total_sentences": len(sentences),
"avg_post_length_chars": sum(len(t) for t in all_text) / max(len(all_text), 1),
"avg_comment_length_chars": sum(len(c) for c in all_comments) / max(len(all_comments), 1),
},
"readability": {
"flesch_reading_ease": textstat.flesch_reading_ease(full_corpus),
"flesch_kincaid_grade": textstat.flesch_kincaid_grade(full_corpus),
"gunning_fog": textstat.gunning_fog(full_corpus),
"smog_index": textstat.smog_index(full_corpus),
"automated_readability_index": textstat.automated_readability_index(full_corpus),
},
"sentence_structure": {
"avg_sentence_length_words": len(words) / max(len(sentences), 1),
"sentence_length_distribution": _get_length_distribution(sentences),
"avg_sentences_per_paragraph": _avg_sentences_per_paragraph(all_text),
},
"vocabulary": {
"top_words": [w for w, c in Counter(words_no_stop).most_common(100)],
"top_bigrams": [f"{a}_{b}" for a, b, c in Counter(
zip(words_no_stop, words_no_stop[1:])
).most_common(50)],
"unique_word_ratio": len(set(words_no_stop)) / max(len(words_no_stop), 1),
},
"parts_of_speech": _pos_distribution(full_corpus),
"punctuation": {
"exclamation_per_sentence": full_corpus.count('!') / max(len(sentences), 1),
"question_per_sentence": full_corpus.count('?') / max(len(sentences), 1),
"parentheses_per_sentence": (full_corpus.count('(') + full_corpus.count(')')) / max(len(sentences), 1),
"em_dash_count": full_corpus.count('—') + full_corpus.count('–'),
},
"formatting_conventions": {
"bold_usage": _count_markdown_pattern(full_corpus, r'\*\*.*?\*\*'),
"italic_usage": _count_markdown_pattern(full_corpus, r'\*.*?\*'),
"code_block_usage": _count_markdown_pattern(full_corpus, r'`.*?`'),
"bullet_list_usage": sum(1 for line in full_corpus.split('\n') if line.strip().startswith(('- ', '* ', '1. '))),
"quote_block_usage": sum(1 for line in full_corpus.split('\n') if line.startswith('>')),
},
"tone_indicators": {
"swear_word_ratio": _count_swear_words(words) / max(len(words), 1),
"hedging_ratio": _count_hedging(words) / max(len(words), 1),
"first_person_ratio": words.count('i') / max(len(words), 1),
"second_person_ratio": words.count('you') / max(len(words), 1),
"contraction_ratio": _count_contractions(full_corpus) / max(len(words), 1),
},
"formatting_markers": _extract_formatting_markers(all_text + all_comments),
}
return fingerprint
0.3 — Style Rules Generation
From the fingerprint, generate a human-readable style guide for the forum:
## r/SUBREDDIT Style Rules (auto-generated)
### Readability Target
- Flesch-Kincaid Grade: 7.2 → Write at ~7th grade level
- Average sentence: 16 words
- Average post: 340 characters
- Average comment: 85 characters
### Sentence Rhythm
- Mix of short (4-8 words) and medium (12-20 words)
- Avoid long sentences (>30 words) — they don't exist here
- Fragments are common and accepted
### Vocabulary
- Top words: [extracted]
- Jargon specific to this community: [extracted]
- Avoid: formal/academic vocabulary, corporate speak
### Formatting
- Bold: rare, for emphasis only
- Bullet lists: common in top posts
- Code blocks: frequent (technical community)
- No emoji (or minimal)
### Tone
- Direct, slightly irreverent
- First-person is standard
- Swearing: occasional, matches community
- Humor: deadpan/self-deprecating
- Hedging: low — this community states opinions directly
### Openings & Closings
- Common openings: "Honestly...", "Hot take:", "PSA:"
- Common closings: questions, TL;DRs, "Edit:" follow-ups
0.4 — Advanced Stylometric Analysis (for maximum match)
For deeper matching, use the voice-humanizer skill's fingerprinting system:
Load: voice-humanizer
Run the stylometric extraction prompt on the forum corpus as if it were
a "voice sample." This gives you the 12-dimension voice fingerprint
(sentence length, rhythm, openings, closings, function words, punctuation,
register, contractions, hedging, idioms, paragraph rhythm, do/don't rules).
Save this as ~/.hermes/forum-marketing/<campaign>/style_analysis/<forum>-voice-fingerprint.json
Then use the narrative-mimicry skill to detect community narratives:
Load: narrative-mimicry
Run Phase 1 (Narrative Detection) on the forum corpus to identify:
- Dominant narratives (>15% frequency)
- Established narratives (5-15%)
- Counter-narratives
- Sacred concepts and forbidden moves
Save to ~/.hermes/forum-marketing/<campaign>/style_analysis/<forum>-narratives.json
Combined output: You now have:
- Linguistic fingerprint — HOW they write (syntax, vocabulary, rhythm)
- Voice fingerprint — finer-grained voice traits (from voice-humanizer)
- Narrative map — WHAT they believe and argue about (from narrative-mimicry)
This triad is the foundation. Every post you generate passes through all three filters.
0.5 — Style Verification (Pre-Post Check)
Before posting anything, run the draft through the style matcher:
def verify_style_match(draft_text, forum_fingerprint):
"""Score how well a draft matches the forum's style."""
draft_sentences = sent_tokenize(draft_text)
draft_words = word_tokenize(draft_text.lower())
forum_sent_len = forum_fingerprint["sentence_structure"]["avg_sentence_length_words"]
forum_grade = forum_fingerprint["readability"]["flesch_kincaid_grade"]
scores = {
"readability_diff": abs(textstat.flesch_kincaid_grade(draft_text) - forum_grade),
"sentence_length_diff": abs(
len(draft_words) / max(len(draft_sentences), 1) - forum_sent_len
),
"vocab_overlap": len(
set(draft_words) & set(forum_fingerprint["vocabulary"]["top_words"])
) / max(len(set(draft_words)), 1),
}
# Thresholds
issues = []
if scores["readability_diff"] > 2:
issues.append(f"Readability off by {scores['readability_diff']:.1f} grades")
if scores["sentence_length_diff"] > 8:
issues.append("Sentence length doesn't match forum rhythm")
return {
"pass": len(issues) == 0,
"scores": scores,
"issues": issues,
}
Gate rule: If style verification returns pass: false, rewrite the draft
before posting. Never post style-mismatched content.
Phase 1: Forum Discovery
1.1 — Identify Target Forums
Find 5-15 candidate forums using multiple discovery methods:
Method A: PRAW subreddit discovery
import praw
reddit = praw.Reddit(...)
# Search for subreddits by keyword
for sub in reddit.subreddits.search("NICHE_KEYWORD", limit=20):
print(f"r/{sub.display_name}: {sub.subscribers:,} subscribers — {sub.public_description[:100]}")
# Find related subreddits
target = reddit.subreddit("KNOWN_SUBREDDIT")
# Check sidebar/wiki for related communities
# Check crossposted content for source subreddits
Method B: arctic-shift community search
# Search for subreddits that discuss your topic
curl -s "https://arctic-shift.photon-reddit.com/api/posts/search?query=KEYWORD&sort=top&limit=20" \
| python3 -c "import json,sys; data=json.load(sys.stdin); [print(p['subreddit']) for p in data['data']]" \
| sort | uniq -c | sort -rn
Method C: Exa semantic search (agent-reach)
export PATH="$HOME/.hermes/node/bin:$PATH"
mcporter call 'exa.web_search_exa(query: "site:reddit.com best subreddits for [NICHE] discussion community", numResults: 10)'
Method D: Niche forum discovery
mcporter call 'exa.web_search_exa(query: "[NICHE] forum community discussion site:discourse OR site:xenforo OR site:phpbb", numResults: 10)'
1.2 — Score and Select
Score each forum on:
| Factor | Weight | How to Measure |
|---|---|---|
| Audience relevance | 5 | Manual assessment |
| Traffic potential | 4 | PRAW .subscribers, monthly active users |
| Link tolerance | 4 | Read subreddit rules; check if link posts allowed |
| Moderation climate | 3 | Check moderator activity, ban patterns |
| Account age gate | 3 | Test post with warm account; note removals |
| Style match difficulty | 3 | Phase 0 analysis — how homogeneous is the style? |
| SEO value | 2 | Google "site:reddit.com/r/SUBREDDIT KEYWORD" |
| Competitor presence | 2 | PRAW search for competitor mentions |
Select top 3-5 forums. More than 5 is unmanageable.
1.3 — Forum Deep Dive
For each selected forum, run the full Phase 0 analysis. Document:
- Self-promotion policy
- Link posting rules
- Account age/karma minimums
- Content format rules
- Banned topics or domains
- Moderator pet peeves
- Style fingerprint (from Phase 0.2)
- Style rules (from Phase 0.3)
- Narrative map (from Phase 0.4)
Phase 2: Persona Creation & Voice Calibration
2.1 — Persona Design with Forum-Native Voice
Each persona must write like a natural member of the target forum. Use the forum's style fingerprint as the baseline, then add individual variation:
Process:
- Load the forum's linguistic fingerprint (Phase 0.2)
- Load the forum's voice fingerprint (Phase 0.4 via voice-humanizer)
- Generate 3-5 persona variations that all match the forum fingerprint but differ subtly from each other
- Calibrate each persona's voice using the voice-humanizer's do/don't rules
Persona variation dimensions (keep these distinct per persona):
- Sentence length: ±3 words from forum average
- Technical depth: beginner / intermediate / expert
- Humor: deadpan / enthusiastic / none
- Hedging: low (confident) / medium (nuanced)
- Contrarianism: mainstream / slightly contrarian / skeptical
{
"persona_id": "rust-dev-73",
"platform": "reddit",
"username": "rust_dev_73",
"forum_profile": "r/rust",
"forum_fingerprint_ref": "r-rust-fingerprint.json",
"persona_description": "Mid-career systems programmer, writes C++ at work but Rust for side projects. Knows the language deeply but not an influencer. Answers newbie questions patiently, gets technical in advanced threads. Occasionally frustrated with async Rust.",
"voice_calibration": {
"baseline": "r/rust forum fingerprint",
"variations": {
"sentence_length": "-2 words from forum avg",
"technical_depth": "expert (uses jargon naturally)",
"humor": "deadpan, occasional compiler joke",
"hedging": "low — states preferences directly",
"contrarianism": "mainstream Rust views, slightly pro-unsafe-when-necessary"
},
"writing_quirks": [
"Prefers 'yeah' to 'yes'",
"Uses code-formatted inline references (`Rc<RefCell<T>>`)",
"Sometimes starts replies with 'FWIW'",
"Ends long replies with a one-line summary"
]
},
"karma": {"post": 342, "comment": 1280},
"proxy_assigned": "proxy-03 (DE residential)",
"status": "warming_up"
}
2.2 — Voice Distinctness Verification
Before deploying personas, verify they're stylistically distinct:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
def verify_voice_distinctness(persona_samples):
"""Check that personas write differently from each other."""
vectorizer = TfidfVectorizer(ngram_range=(1, 3), max_features=1000)
tfidf = vectorizer.fit_transform(persona_samples)
similarity = cosine_similarity(tfidf)
issues = []
for i in range(len(persona_samples)):
for j in range(i+1, len(persona_samples)):
if similarity[i][j] > 0.85:
issues.append(f"Persona {i} and {j} too similar ({similarity[i][j]:.2f})")
return {"distinct": len(issues) == 0, "issues": issues}
Regenerate any persona that scores >0.85 similarity with another.
2.3 — Warm-Up Protocol (Style-Matched)
All warm-up activity must match the forum's style. Use the forum fingerprint to guide comment generation during warm-up:
Week 1:
- 2-3 comments/day, spread across 3-4 threads
- Each comment must pass the style verification gate (Phase 0.5)
- Comments must be genuine contributions — no promotion at all
- Build 10-50 karma
Week 2:
- 1-2 posts/day in non-target communities (genuine, style-matched)
- Continue commenting in target community
- Post type: question, observation, or resource
Week 3+:
- Account ready for campaign
- Continue non-promotional activity at 3:1 ratio
2.4 — IP & Browser Isolation
Different forum accounts need clean separation:
Tool: browser-automation skill (Playwright with separate contexts)
Tool: ProxyEmpire (residential proxies, one IP per persona)
Tool: playwright-stealth (pip install playwright-stealth)
Reference: puppeteer-extra-plugin-stealth (JS ecosystem alternative)
Playwright context per persona:
from playwright.sync_api import sync_playwright
from playwright_stealth import Stealth
# One context per persona — completely isolated
context_configs = {
"persona_alpha": {
"proxy": "http://user-alpha:pass@proxy1.empire.io:5000",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
"viewport": {"width": 1920, "height": 1080},
"timezone_id": "America/New_York",
"locale": "en-US",
"storage_state": "profiles/alpha/cookies.json",
},
"persona_beta": {
"proxy": "http://user-beta:pass@proxy2.empire.io:5000",
# ... different config
},
}
# Launch with stealth
for persona_id, config in context_configs.items():
context = browser.new_context(**config)
Stealth().apply_stealth_sync(context) # or async
See browser-automation skill for full anti-detection setup (fingerprint
overrides, human-like behavior, account warming, SMS verification).
Phase 3: Content Generation (Style-Matched)
3.1 — Post Type Selection
Match the post type to the forum's top-performing formats:
| Post Type | Best For | Style Match Required |
|---|---|---|
| Problem → Solution | SaaS, tools, services | Technical depth matching |
| "I built X" (transparent) | Indie products, OSS | Hacker authenticity |
| Resource share | Content sites, tools | Enthusiasm matching |
| Case study / results | B2B, productivity | Data-density matching |
| Hot take / contrarian | Thought leadership | Argument style matching |
| Question → Answer (alt) | Soft promotion | Curiosity matching |
| News + commentary | Content, traffic | Timeliness |
3.2 — Post Structure (Forum-Native Format)
Generate using the forum's fingerprint as constraint. The DeepSeek V4 Pro model handles this with the style rules as a system prompt:
SYSTEM: You are writing a post for r/SUBREDDIT. Match these style rules exactly:
READABILITY: Flesch-Kincaid grade 7.2
SENTENCE RHYTHM: Average 16 words. Mix of 4-8 word and 12-20 word sentences.
NO sentences over 30 words.
FORMATTING: Bold is rare. Bullet lists are common. Code blocks are frequent.
NO emoji. NO markdown headers.
TONE: Direct, slightly irreverent. First-person. Deadpan humor.
NO corporate speak. NO formal introductions.
VOCABULARY: Use community jargon naturally. NO Tier 1 AI vocabulary (delve,
tapestry, crucial, comprehensive, meticulous, embark, robust, seamless).
OPENINGS: Start with a hook, not an introduction.
CLOSINGS: End with a question or TL;DR. Add "Edit:" for follow-ups.
CONTENT RULES: 80% genuine value, 20% mention of the thing. One link,
naturally placed, never in the first paragraph. No UTM parameters.
3.3 — AI Tell Removal
After drafting, run the humanizer skill's 28-pattern detection:
Load: humanizer
Run on the draft:
- Strip Tier 1/2 vocabulary
- Kill banned phrases
- Remove chatbot artifacts
- Fix hedging and filler
- Verify zero em dashes
Then run the agentic-humanizer 5-pass rewrite if deeper polishing is needed.
3.4 — Title Optimization (Style-Aware)
Generate titles that match the forum's title conventions:
# Extract title patterns from corpus
def extract_title_patterns(corpus):
"""Analyze what title formats perform best in this forum."""
patterns = {
"question_titles": [],
"how_i_titles": [],
"psa_titles": [],
"hot_take_titles": [],
"resource_titles": [],
}
for post in corpus:
title = post["title"]
if "?" in title:
patterns["question_titles"].append((title, post["score"]))
if title.lower().startswith(("how i", "how we")):
patterns["how_i_titles"].append((title, post["score"]))
# ... more pattern detection
# Return best-performing patterns
return {k: sorted(v, key=lambda x: x[1], reverse=True)[:5] for k, v in patterns.items()}
Present top 3 title candidates to the user, scored by pattern match + predicted performance.
Phase 4: Multi-Account Comment Orchestration
4.1 — Three-Wave Strategy (Style-Calibrated)
Each persona's comments must pass style verification. Comments should be indistinguishable from real forum members.
Wave 1: Validation (30-60 min after posting)
Persona B (enthusiast):
- Style: matches forum fingerprint, slightly more enthusiastic than average
- Content: "This is exactly what I needed. Been struggling with [pain point] for [timeframe]. Quick question — [edge case]?"
Persona C (comparison shopper):
- Style: matches forum fingerprint, analytical
- Content: "Interesting. I've been using [alternative] but [specific pain point] is driving me crazy. How does this handle [technical detail]?"
Wave 2: Discussion (2-6 hours)
Persona D (skeptical expert):
- Style: matches forum fingerprint, slightly contrarian
- Content: "How does this compare to [well-known tool] on [specific metric]? I ran benchmarks on [alternative] last month and got [specific number]."
OP response: Answer substantively. Share specific numbers, limitations, trade-offs.
Persona B follow-up: "Thanks for the detailed answer. One more thing — [deeper question]."
Wave 3: Social Proof (6-24 hours)
Persona E (late adopter):
- "Bookmarked. Been looking for something like this since [old tool] died."
Persona F (recent convert):
- "Just tried it — [specific feature] alone is worth it. The [detail] is better than I expected."
4.2 — Comment Generation Pipeline
1. Load forum linguistic fingerprint
2. Load persona voice calibration
3. Generate comment with DeepSeek V4 Pro (style-constrained prompt)
4. Run style verification (Phase 0.5)
5. Run humanizer (strip AI tells)
6. Run voice distinctness check against other persona comments
7. Final human review
4.3 — API Posting (PRAW Integration)
For Reddit, use PRAW instead of browser automation when possible (browser is for account creation/warming; PRAW is for posting):
import praw
def post_comment(persona_config, submission_id, comment_text):
"""Post a comment as a specific persona via PRAW."""
reddit = praw.Reddit(
client_id=persona_config["client_id"],
client_secret=persona_config["client_secret"],
user_agent=persona_config["user_agent"],
username=persona_config["username"],
password=persona_config["password"],
)
submission = reddit.submission(id=submission_id)
comment = submission.reply(comment_text)
return {"id": comment.id, "permalink": comment.permalink}
Snoowrap (JS alternative):
const snoowrap = require('snoowrap');
const r = new snoowrap({
userAgent: 'forum-bot/1.0',
clientId: '...',
clientSecret: '...',
username: 'persona_alpha',
password: '...'
});
r.getSubmission('post_id').reply('comment text');
Phase 5: Traffic & Trust Conversion
5.1 — Trust Funnel
Organic Discovery → Style Recognition ("this sounds legit") → Interest →
Trust (OP sounds like us) → Click (natural curiosity) → Conversion
5.2 — Link Strategy
- Primary link: In OP body, once, naturally. Never first paragraph.
- Secondary links: In OP replies, only when genuinely answering a question.
- NO links in persona comments.
- Use clean URLs (no UTMs, no affiliate tags).
5.3 — Analytics
Track via PRAW:
def get_post_metrics(reddit, post_id):
submission = reddit.submission(id=post_id)
return {
"score": submission.score,
"upvote_ratio": submission.upvote_ratio,
"num_comments": submission.num_comments,
"view_count": submission.view_count, # if available
"created_utc": submission.created_utc,
}
Additional analytics tools:
- SocialGrep (socialgrep.com) — Reddit search and analytics
- reddit-trends (various GitHub repos) — subreddit growth tracking
- Custom URL shortener with click tracking (your own domain + analytics)
Phase 6: Anti-Detection
6.1 — Detection Vectors & Mitigations
| Vector | Tool/Mitigation |
|---|---|
| IP clustering | ProxyEmpire per persona (browser-automation skill) |
| Browser fingerprinting | playwright-stealth, puppeteer-extra-plugin-stealth |
| Stylometric similarity | Voice distinctness verification (Phase 2.2) |
| Temporal clustering | Stagger account creation + activity |
| Link velocity | 5:1 no-link to link post ratio |
| Vote rings | Max 2-3 upvotes from personas, spread over hours |
| Karma farming | Quality warm-up, not rapid low-effort |
| Cross-referencing | Limit persona overlap to 1-2 campaigns |
6.2 — Reddit Shadowban Detection
def check_shadowban(username):
"""Check if a Reddit account is shadowbanned."""
import requests
r = requests.get(
f"https://www.reddit.com/user/{username}/about.json",
headers={"User-Agent": "shadowban-checker/1.0"}
)
if r.status_code == 404:
return {"status": "shadowbanned", "note": "Profile not found — likely shadowbanned"}
elif r.status_code == 200:
data = r.json()
return {"status": "active", "karma": data.get("data", {}).get("total_karma", 0)}
return {"status": "unknown", "http_status": r.status_code}
6.3 — Pre-Flight Checklist
- Phase 0 completed: corpus extracted, fingerprint generated, style rules defined
- All persona voice calibrations verified distinct (similarity <0.85)
- OP account has >2 weeks of style-matched post/comment history
- OP account has posted in target community before (3+ times)
- Persona accounts have distinct IPs (proxies assigned in browser-automation)
- Persona accounts have distinct browser contexts (Playwright)
- Persona accounts have >1 week of style-matched warm-up activity
- No persona has interacted with any other persona recently
- OP link-to-no-link post ratio is >1:5
- OP draft passes style verification (Phase 0.5)
- All comments pass style verification
- All content passes humanizer (zero AI tells)
- Post complies with forum's self-promotion rules
- Link is clean (no UTMs, no affiliate tags)
- Timing plan accounts for forum's peak hours
- Shadowban check passed for all accounts
Tool Installation Summary
# Python tools
pip install praw asyncpraw textstat nltk spacy
python -m spacy download en_core_web_sm
# JS tools (optional, for snoowrap + stealth)
npm install snoowrap puppeteer-extra puppeteer-extra-plugin-stealth
# Playwright stealth (Python)
pip install playwright-stealth
# NLP
pip install scikit-learn # for TF-IDF voice distinctness
# Reddit API setup: https://www.reddit.com/prefs/apps
# Create "script" type app for each persona
# Note: client_id, client_secret, username, password per persona
Model Routing
- MiMo V2.5: corpus extraction, PRAW data collection, readability calculation, voice distinctness scoring, style verification checks, timing calculations, schema validation, shadowban checks, proxy health checks
- DeepSeek V4 Pro: linguistic fingerprint interpretation, style rule generation, persona design, post/comment drafting with style constraints, narrative analysis, title optimization, campaign strategy, anti-detection reasoning
Common Pitfalls
Skipping Phase 0. The #1 reason forum posts fail. If you don't extract the forum's style fingerprint, your posts will sound foreign. Style analysis is not optional.
Over-matching the style. Posts that hit EVERY style marker look like parodies. Deliberately skip 1-2 markers per post. Real community members don't recite a checklist.
Identical writing voices. The voice distinctness check (Phase 2.2) is mandatory. If two personas score >0.85 similarity, redesign one.
Rushing the warm-up. 2-week minimum. No exceptions for Reddit.
Neglecting real replies. Staged comments are scaffolding. Reply to every real commenter within 12 hours. Be genuinely helpful, not promotional.
Cross-contamination. Same proxy + same browser = same fingerprint. Full isolation per persona.
Campaign density. Don't run the same product across 5 subreddits in one week. Stagger by 1-2 weeks.
Using browser for API calls. PRAW is faster, cheaper, and doesn't trigger bot detection for posting. Browser is for account creation and warming. API is for content deployment.
Ignoring the style guide during warm-up. Every warm-up comment must also match the forum's style. Inconsistent style history is a detection vector.
Aging the wrong way. Comments during warm-up that are obviously AI-generated build a trail of evidence against the account. Style-match everything.
Verification Checklist
- Phase 0 completed: 50-100 posts extracted, linguistic fingerprint generated
- Style rules document created and reviewed
- Forum fingerprint passed through voice-humanizer for deeper analysis
- Narrative map generated via narrative-mimicry skill
- 3-5 target forums identified, scored, and documented with rules
- OP persona created with forum-calibrated voice
- 2-4 commenter personas created with verified distinct voices (<0.85 similarity)
- Proxies assigned and tested for each persona (browser-automation)
- Browser isolation confirmed (separate Playwright contexts)
- OP draft written with style-matched 5-part structure
- OP draft passes style verification (Phase 0.5)
- OP draft passes humanizer (zero AI tells)
- 5-10 title variants generated, top 2-3 selected by forum pattern match
- Three-wave comment plan drafted with style-matched, voice-distinct comments
- All comments pass style verification
- Timing plan set for forum's peak activity hours
- Link is clean (no UTMs, no affiliate tags)
- Pre-flight checklist completed for all accounts
- Shadowban check passed for all accounts
- API credentials ready (PRAW/snoowrap per persona)
- Campaign tracking set up (PRAW metrics + URL monitoring)