βββββββ ββββββββββββββββββββββββββββ ββββββββββββ
ββββββββββββββββββββββββββββββββββββ βββββββββββββ
βββ βββββββββ βββ ββββββ βββββββ βββ
βββ βββββββββ βββ ββββββ βββββββ βββ
ββββββββββββββββ βββ βββββββββββ βββ βββ
βββββββ ββββββββ βββ βββββββββββ βββ βββ
TikTok AI & deepfake detection bot. Mention it on a video or slideshow, get a verdict.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TikTok mention βββΊ Worker polls βββΊ Download media βββΊ Sightengine β
β β
β @orkavilabs in comments polled via TikTok notification API β
β Videos downloaded via yt-dlp, uploaded to GCS β
β Slideshows images extracted, uploaded to GCS β
β AI + deepfake scan Sightengine genai + deepfake models β
β Reply Camoufox browser posts result as comment β
β Rate limiting per-user via Upstash Redis β
β Caching Firestore dedup + result cache β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
A TikTok bot that detects AI-generated content and deepfakes. Someone mentions the bot in a comment on any TikTok video or slideshow. The bot downloads the media, runs it through Sightengine's AI generation and deepfake detection models, and replies with a verdict β "AI (92%)", "real (97%)", "deepfake detected", or "not sure".
Supports videos and multi-image slideshows (carousels). Each image in a carousel is scanned individually and results are reported per-slide.
βββββββββββββββββββ βββββββββββββββββββ
β detekt_worker ββββ Temporal workflows βββΊβ detekt_replier β
β (GCE VM) β β (GCE VM) β
β β β β
β Poll mentions β β Receive reply β
β Download media β β task via β
β Upload to GCS β β Temporal β
β Scan via β β β
β Sightengine β β Open TikTok in β
β Dispatch reply β β Camoufox β
β β β Type & post β
ββββββββββ¬ββββββββββ β comment reply β
β βββββββββββββββββββ
β
ββββββ΄βββββββββββββββββββββββββββββββββ
β Firestore GCS bucket Upstash β
β (scan cache (media Redis β
β + dedup) staging) (rate lim)β
βββββββββββββββββββββββββββββββββββββββ
Two services, both Docker containers on GCE VMs running Container-Optimized OS:
detekt_worker β Temporal worker that runs two workflows. PollerWorkflow polls TikTok's notification API on a loop, filters for trigger-word mentions, then spawns a ProcessMentionWorkflow per mention. That workflow downloads the media (yt-dlp for videos, httpx for slideshow images), uploads to GCS, scans via Sightengine (AI generation + deepfake detection), caches results in Firestore, and dispatches a reply task to the replier's Temporal queue.
detekt_replier β Temporal worker that receives reply tasks. Uses Camoufox (anti-fingerprint Firefox) with Playwright to open the TikTok video page, click "Reply" on the original comment, @mention the user, type the result message with human-like typing delays, and post. Handles session rotation (12h TTL), status-8 detection, and automatic reboots.
| Content type | Download | Scan |
|---|---|---|
| Video | yt-dlp β bytes β GCS vids/{id}/video.mp4 |
Sightengine genai + deepfake video sync (frame-by-frame, averaged) |
| Slideshow | httpx per image β GCS pics/{id}/{n}.{ext} |
Sightengine genai + deepfake per image, max score across carousel |
Results are cached in Firestore by comment ID. Duplicate mentions are deduped by mention:{cid} documents.
The reply message is randomized from a pool of templates based on confidence level:
- High confidence AI β "yep, that's AI (92% sure)"
- High confidence deepfake β "real video but the face is swapped (87% sure)"
- Low confidence β "not sure on this one (54% AI generated/manipulated)"
- Real β "looks real to me (96% sure)"
All secrets are managed via Doppler. Both containers run with doppler run -- as the entrypoint, which injects secrets as environment variables. A background thread refreshes the secret cache every 30 seconds with a 120-second TTL.
Terraform manages all GCP resources:
| Resource | What |
|---|---|
google_compute_instance.dtkt_worker |
GCE VM (c2-standard-4), COS image, runs worker container |
google_compute_instance.dtkt_replier |
GCE VM (c2-standard-4, 30GB disk), COS image, runs replier container |
google_storage_bucket.dtkt_media |
GCS bucket for downloaded media, 7-day lifecycle delete |
google_firestore_database.dtkt_default |
Firestore Native database for scan results + dedup |
| Service accounts | Separate SAs for worker (storage admin, firestore, logging) and replier (storage read/write, logging) |
Region defaults to europe-west2 (London).
# set up .env from example
cp .env.example .env
# edit with your GCP project and Doppler tokens
# deploy everything (builds both images, pushes to GCR, runs terraform)
deploy.bat
# or deploy a single service
deploy.bat worker
deploy.bat replierPowerShell alternative:
.\deploy.ps1 -Target all
.\deploy.ps1 -Target worker
.\deploy.ps1 -Target replierThis builds Docker images, pushes to GCR, and runs terraform apply with auto-approve.
Root (.env):
| Var | What |
|---|---|
DTKT_GCP_PROJECT |
GCP project ID |
DTKT_WORKER_DOPPLER_TOKEN |
Doppler service token for the worker |
DTKT_REPLIER_DOPPLER_TOKEN |
Doppler service token for the replier |
Worker secrets (via Doppler):
| Var | What |
|---|---|
DTKT_TEMPORAL_HOST |
Temporal Cloud host |
DTKT_TEMPORAL_NAMESPACE |
Temporal namespace |
DTKT_TEMPORAL_API_KEY |
Temporal API key |
DTKT_TEMPORAL_TASK_QUEUE |
Replier's Temporal task queue name |
DTKT_WORKER_TASK_QUEUE |
Worker's own Temporal task queue name |
DTKT_POLL_INTERVAL_SECONDS |
Polling interval |
DTKT_TRIGGER_WORD |
Word that triggers the bot in comments |
DTKT_USER_BLACKLIST |
Comma-separated usernames to ignore |
DTKT_TT_SESSIONID |
TikTok session cookie |
DTKT_SENTRY_DSN |
Sentry DSN |
DTKT_SENTRY_FLUSH_TIMEOUT |
Sentry flush timeout in seconds |
DTKT_BUCKET_NAME |
GCS bucket name |
DTKT_FIRESTORE_DATABASE |
Firestore database name |
DTKT_FIRESTORE_SCANS_COLLECTION |
Firestore collection name |
DTKT_SIGHTENGINE_API_USER |
Sightengine API user |
DTKT_SIGHTENGINE_API_SECRET |
Sightengine API secret |
DTKT_SIGHTENGINE_REFRESH_INTERVAL |
Sightengine client refresh interval in seconds |
DTKT_SIGHTENGINE_MIN_INTERVAL |
Min seconds between Sightengine API calls (rate limit) |
DTKT_AI_THRESHOLD |
Score threshold for AI/deepfake classification |
DTKT_LOW_CONFIDENCE_MIN |
Lower bound for "unsure" range |
DTKT_LOW_CONFIDENCE_MAX |
Upper bound for "unsure" range |
DTKT_VIDEO_ENA |
Enable video scanning |
DTKT_PHOTO_ENA |
Enable photo/slideshow scanning |
DTKT_MAX_CAROUSEL_PHOTOS |
Max photos per carousel to scan |
DTKT_SUPPORTED_TYPES |
Comma-separated TikTok aweme types to process |
DTKT_UPSTASH_REDIS_URL |
Upstash Redis URL |
DTKT_UPSTASH_REDIS_TOKEN |
Upstash Redis token |
DTKT_RATE_LIMIT_WINDOW |
Rate limit window in seconds |
DTKT_RATE_LIMIT_MAX |
Max scans per user per window |
DTKT_REDIS_REFRESH_INTERVAL |
Redis client refresh interval in seconds |
DTKT_MAX_POLLS_BEFORE_CAN |
Polls before Temporal continue-as-new |
DTKT_MAX_SESSION_RETRIES |
TikTok session creation retry count |
DTKT_SESSION_MAX_AGE_SECONDS |
TikTok API session max age before rotation |
DTKT_SECRETS_CACHE_TTL |
Secret cache TTL in seconds |
DTKT_SECRETS_REFRESH_INTERVAL |
Secret background refresh interval in seconds |
DTKT_WEBSHARE_API_KEY |
Webshare proxy API key |
DTKT_WEBSHARE_COUNTRY |
Proxy country code |
DTKT_WEBSHARE_PROXY_COUNT |
Number of proxy slots |
DTKT_PROXY_ENABLED |
Enable/disable proxy |
Replier secrets (via Doppler):
| Var | What |
|---|---|
DTKT_TEMPORAL_HOST |
Temporal Cloud host |
DTKT_TEMPORAL_NAMESPACE |
Temporal namespace |
DTKT_TEMPORAL_API_KEY |
Temporal API key |
DTKT_TEMPORAL_TASK_QUEUE |
Task queue to listen on |
DTKT_SENTRY_DSN |
Sentry DSN |
DTKT_BUCKET_NAME |
GCS bucket (for cookies + debug screenshots) |
DTKT_GCS_COOKIES_PATH |
GCS path to Netscape cookie file |
DTKT_GCP_SERVICE_ACCOUNT_JSON |
GCP SA JSON for GCS access |
DTKT_DBG_ENA |
Enable debug screenshots |
DTKT_GCS_DBGSC_PATH |
GCS path prefix for debug screenshots |
DTKT_REPLIER_SESSION_TTL |
Camoufox browser session TTL in seconds |
DTKT_REPLIER_BOOT_MAX_RETRIES |
Browser boot retry count |
DTKT_GEO_LATITUDE |
Browser geolocation latitude |
DTKT_GEO_LONGITUDE |
Browser geolocation longitude |
DTKT_GEO_LANGUAGE |
Browser locale language |
DTKT_GEO_REGION |
Browser locale region |
DTKT_GEO_TIMEZONE |
Browser timezone |
DTKT_SECRETS_CACHE_TTL |
Secret cache TTL in seconds |
DTKT_SECRETS_REFRESH_INTERVAL |
Secret background refresh interval in seconds |
| Proxy vars | Same as worker |
See SECURITY.md.
See MAINTAINING.md.
See CONTRIBUTING.md.
MIT β see LICENSE.