TikTok Comment Replier
Two-stage pipeline for managing TikTok comments on @codewithtyler:
- Read side: Apify (
clockworks/tiktok-comments-scraper) — clean structured JSON, $0.001/comment - Write side: Playwright with persistent Chromium profile — needed because TikTok has no official reply API
Files
auth.py— one-time login, saves persistent profile todata/profile/apify_fetch.py— pulls comments via Apify, writesdata/unreplied.jsonreply.py— readsdata/replies_queue.json, posts each reply, tracks done indata/posted.jsondata/replies_queue.json— the queue of {cid, author, comment_text, video_url, reply_text}data/posted.json— comment IDs already replied to (prevents double-posting)
The hourly cron + manual draft workflow (PRIMARY pattern)
A cron runs monitor.py every hour at :05. It produces three artifacts in data/:
inbox.json— comments needing your manual reply (with full metadata: cid, author, text, video_url, etc). This is what you ask Claude to help draft.drafts_queue.json— auto-drafted Skool-link replies for keyword CTAs (System / Plan / Skill / Email / Routine / Tools / VFX / schedule / video / workflow). Already ready to post.drafts.md— human-readable running log.
When the user says "any new comments?" / "check my tiktok inbox"
- Read
data/inbox.json(manual-needed) ANDdata/drafts_queue.json(auto-drafts pending). - Show the user the breakdown — count of each plus the actual text of inbox items.
- He picks one or all to draft replies for.
When the user asks "draft a reply for the @username one"
- Read the inbox entry for that comment.
- Compose a reply that fits your tone (casual, helpful, drives to skool.com/the-ai-agency when relevant). Use
/harutfor conversion-sensitive wording. - Show the user the draft, get approval.
- When approved, run:
This appends topython3 ~/.claude/skills/tiktok-replier/queue_draft.py --cid <cid> --reply "<text>"replies_queue.jsonAND removes frominbox.json.
When the user says "post them"
python3 ~/.claude/skills/tiktok-replier/reply.py --post --headless
For auto-drafted Skool-link CTAs:
python3 ~/.claude/skills/tiktok-replier/reply.py --post --headless --queue ~/.claude/skills/tiktok-replier/data/drafts_queue.json
Standard workflow (manual / first-time)
1. Fetch unreplied comments
Use Apify via MCP (recommended — no token maintenance needed):
mcp__apify__call-actor with actor=clockworks/tiktok-comments-scraper
input: {"profiles": ["codewithtyler"], "resultsPerPage": 30, "commentsPerPost": 50, "maxRepliesPerComment": 10, "profileSorting": "latest"}
Then mcp__apify__get-actor-output with the dataset ID, fields=cid,uniqueId,text,diggCount,replyCommentTotal,likedByAuthor,repliesToId,createTimeISO,videoWebUrl.
Filter: top-level (repliesToId is null) AND no reply from @codewithtyler in same thread AND author != codewithtyler.
OR run the local script (needs APIFY_API_TOKEN in ~/.claude/.env):
python3 ~/.claude/skills/tiktok-replier/apify_fetch.py --videos 30 --per 100
2. Build reply queue
Show the user the unreplied list, confirm wording, and write data/replies_queue.json. Each entry needs:
cid— comment ID (used to skip already-posted)author— TikTok username (used to find the row)comment_text— original comment text (used to verify the right row)video_url— full video URLreply_text— what to reply with
For keyword-style CTAs (System / Plan / Skill / Email / Routine / Tools / VFX / schedule), the standard reply is:
https://www.skool.com/the-ai-agency is the link! It is in the Social Media Classroom (free of course!)
3. Post the replies
Login first if data/profile/ is empty: python3 auth.py (interactive, opens browser, auto-detects sessionid cookie).
Always test on 1 first, then batch:
python3 reply.py --post --limit 1 --headless # smoke test
python3 reply.py --post --headless # batch (30s pause between each)
For first run or debugging, drop --headless so the browser is visible. Default to --headless once the flow is confirmed working.
--post is required to actually publish. Without it the script does dry-run (composes but doesn't click submit).
Critical implementation notes (so future sessions don't re-debug TikTok's DOM)
- Auth must use
launch_persistent_context(user_data_dir=...), NOTstorage_state. TikTok's session is bound to fingerprint + localStorage + IndexedDB; cookies alone aren't enough. - Comment-icon click opens the proper comments view:
[data-e2e="comment-icon"]on the video player. The right-side "Comments" tab is a different sidebar preview that doesn't expose proper Reply controls. - Per-comment Reply button is
div[class*="DivReplyTriggerWrapper"]insidediv[class*="DivCommentItemWrapper"]. Click it via real Playwrightpage.mouse.click(x, y)at its center — JS.click()does NOT trigger TikTok's React handler. - Reply composer is a contenteditable Draft.js editor where the placeholder div intercepts pointer events. Focus it via JS (
el.focus()), thenpage.keyboard.type(). Don't use Playwright's.click()on the contenteditable. - Submit button is
[data-e2e="comment-post"](an arrow icon, NOT text "Post"). Same trick: real mouse click at pixel coords. - Verification: composer disappearing OR clearing OR reply text appearing in DOM = success. Don't trust any single signal.
- First-time tutorial: TikTok shows an "Introducing keyboard shortcuts" modal that overlays the comments panel. Strip it surgically — DON'T blanket-remove all
[class*="Modal"]elements (that nukes the legitimate panel).
Safety rules
- Default to
--limit 1on first run of any new queue - Always show drafts to you before bulk posting (matches your memory: "Always confirm before scheduling, even if approved earlier in session")
- 30s spacing between posts to stay under TikTok's spam threshold
posted.jsontracks done IDs so re-runs after a crash skip what already worked- No automatic re-fetch + re-post loops — you initiate each batch
"Keep checking" - the recurring use case
Two paths you can pick:
- Apify scheduler (recommended): set up in their web console, runs in their cloud, emails him the dataset link. No local laptop dependency.
- Local cron via
/scheduleskill: re-runsapify_fetch.pyon a schedule, then prompts to review unreplied list.
Both options drop the post step into manual review — you approve before any reply gets published.