| name | tiktok-replier |
| description | Scrape TikTok comments on @codewithtyler videos via Apify, then post replies via Playwright using a saved logged-in session. Trigger phrases - "tiktok comments", "tiktok replies", "reply to tiktok", "check tiktok comments", "tiktok unreplied", "respond on tiktok". |
| argument-hint | optional - "fetch", "post", "all" (defaults to fetch + show unreplied) |
| allowed-tools | ["Bash","Read","Write","Edit"] |
| user-invocable | true |
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 to data/profile/
apify_fetch.py — pulls comments via Apify, writes data/unreplied.json
reply.py — reads data/replies_queue.json, posts each reply, tracks done in data/posted.json
data/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) AND data/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
/harut for conversion-sensitive wording.
- Show the user the draft, get approval.
- When approved, run:
python3 ~/.claude/skills/tiktok-replier/queue_draft.py --cid <cid> --reply "<text>"
This appends to replies_queue.json AND removes from inbox.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 URL
reply_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=...), NOT storage_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"] inside div[class*="DivCommentItemWrapper"]. Click it via real Playwright page.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()), then page.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 1 on 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.json tracks 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
/schedule skill: re-runs apify_fetch.py on a schedule, then prompts to review unreplied list.
Both options drop the post step into manual review — you approve before any reply gets published.