Last updated: 2025-09-12 22:23 EDT
This document summarizes what we’re building for SnapBot, how it’s structured, environment and deployment details, and the current implementation status with next steps.
We follow the project preferences: prefer simple solutions, avoid duplicate logic, consider dev/test/prod environments, and avoid introducing new tech when fixing issues unless necessary. Also, do not perform media conversion inside the container; require pre-converted Y4M video and WAV audio; use Postgres; use HMAC-signed webhooks; and base Docker image on the Puppeteer-maintained image.
- Automate Snapchat Web workflows with a robust, headless-capable bot (SnapBot) powered by Puppeteer/Chromium.
- Expose an HTTP API to schedule and run jobs (send snap, send video), list recipients, and check user status.
- Persist jobs, runs, recipients, webhook deliveries, and logs in Postgres with DB migrations.
- Provide structured logging with pino to console, file, and DB (correlated by jobId).
- Package in a Docker image based on the Puppeteer-maintained base, with clear volume mounts and healthcheck.
- Support video sending with pre-converted media only (Y4M video + WAV audio), no in-container conversion.
Non-goals (for now):
- Media conversion or editing inside the container.
- Complex job orchestration beyond a single-run worker model.
- Multi-tenancy or advanced auth.
- API Server:
api/server.js- Express routes to submit jobs, trigger login, list recipients, and fetch user status.
- Validates inputs and enqueues jobs that are executed asynchronously.
- Job Runner:
services/jobRunner.js- Consumes jobs immediately after creation (fire-and-forget scheduling), runs SnapBot flows, sends callbacks, and logs results.
- Bot Automation:
snapbot.js- Puppeteer automation of the Snapchat Web UI.
- Key methods:
launchSnapchat(),ensureLoggedIn(),captureSnap(),recordVideo(),send(),listRecipients(),userStatus(),closeBrowser().
- Database: Postgres (
pg)- Pooled connections in
db/pool.jsusingDATABASE_URL. - Repositories in
db/repositories.js(jobs, runs, recipients, webhook deliveries, logs). - Migrations in
migrations/.
- Pooled connections in
- Logging:
pinoviautils/logger.js- JSON logs to console and file (LOG_FILE), and structured events saved to DB (
logstable).
- JSON logs to console and file (LOG_FILE), and structured events saved to DB (
- Webhooks
- HMAC SHA-256 signed callbacks with retry/backoff; persistence of delivery attempts in DB.
migrations/001_init.sqljobs (id, type, payload, callback_url, status, created_at, updated_at)runs (id, job_id, started_at, finished_at, status, error)media (id, path, kind, metadata, created_at)[foundation for potential media management]recipients (id, name)sessions (id, account, cookies_path, created_at, updated_at)webhook_deliveries (id, job_id, attempt, status, response_code, error, sent_at)jobs.updated_attrigger
migrations/002_logs.sqllogs (id, job_id, level, message, meta, created_at)
Run with: npm run migrate (calls scripts/migrate.js).
Base URL: http://localhost:3000 (default PORT)
- GET
/health- Returns DB connectivity status.
- POST
/login- Body:
{ username?, password?, headless?, userDataDir? } - Ensures a browser context launches and logs in (using env creds by default).
- Body:
- POST
/sendSnap- Body:
{ category: "BestFriends" | "Groups" | "Friends", caption?, headless?, userDataDir?, callbackUrl? } - Creates a job to capture a photo and send to a category, then returns
202withid.
- Body:
- POST
/sendVideo- Body:
{ category, videoPathY4M, audioPathWAV, caption?, durationMs?, headless?, userDataDir?, callbackUrl? } - Pre-converted inputs required: Y4M video, WAV audio. Validates presence and wires Chromium fake device flags.
- Body:
- GET
/listRecipients- Query:
?headless=true&userDataDir=... - Logs in, scrapes recipients, upserts into DB, and returns the list.
- Query:
- GET
/userStatus- Query:
?headless=true&userDataDir=... - Logs in and returns per-recipient status info (type/time/streak shape).
- Query:
- GET
/jobs/:id- Returns job and all runs for that job.
Example: Send a video job
curl -X POST http://localhost:3000/sendVideo \
-H "Content-Type: application/json" \
-d '{
"category": "BestFriends",
"videoPathY4M": "/app/media/input.y4m",
"audioPathWAV": "/app/media/input.wav",
"caption": "Hey!",
"durationMs": 5000,
"headless": true,
"userDataDir": "/app/data/cookies/profile",
"callbackUrl": "https://example.com/snapbot/callback"
}'Callback Payload (HMAC signed if CALLBACK_HMAC_SECRET is set):
{
"id": "<job-id>",
"type": "sendVideo",
"status": "succeeded"
}Headers include X-Job-Id and X-Signature (hex sha256 HMAC).
- API validates request and creates a
jobsrecord. - Immediately invokes
runJob()(async) to:- Create a
runsrecord - Launch Chromium with required flags (fake devices for video)
ensureLoggedIn()to Snapchat- Execute
captureSnap()orrecordVideo()thensend(category) - Update run and job status
- Save structured logs to DB
- Send callback with retries/backoff; record webhook deliveries
- Create a
- Container does not perform any transcoding. Inputs must be:
- Video: Y4M
- Audio: WAV
- Chromium flags used for video jobs:
--use-fake-device-for-media-stream--use-file-for-fake-video-capture=<videoPathY4M>--use-file-for-fake-audio-capture=<audioPathWAV>
- Ensure files exist on the mounted volume (
/app/media).
.env (see .env.example, do not overwrite your existing .env)
USER_NAME,USER_PASSWORD— Snapchat credentials.DATABASE_URL— Postgres connection string.PORT— API port; default 3000.CALLBACK_HMAC_SECRET— optional; enables HMAC-signed callbacks.LOG_LEVEL— pino level: trace | debug | info | warn | error | fatal.LOG_FILE— path to JSON log file; defaults tologs/app.log(or/app/logs/app.login Docker).
- Console and file logging via
pinoinutils/logger.js. - Correlation:
- API logs annotate routes and job IDs.
- Job Runner attaches
jobIdto all messages.
- DB logs:
saveLog({ jobId, level, message, meta })persists structured events tologstable.
- Webhook deliveries:
- Each attempt recorded with status and response codes.
- Base image:
ghcr.io/puppeteer/puppeteer:latest(all Chromium deps preinstalled). - Non-root user:
pptruser. - Healthcheck: GET
/health. - Volumes:
/app/media— mount media for sending/app/logs— persisted JSON logs/app/data/cookies— prepared for persistent cookies (wiring into code is a small follow-up)
- CMD runs migrations then API:
node scripts/migrate.js && node api/server.js
Example run:
docker build -t snapbot:dev .
docker run --rm -p 3000:3000 \
-e DATABASE_URL="postgres://user:pass@host:5432/snapbot" \
-e USER_NAME="..." \
-e USER_PASSWORD="..." \
-e CALLBACK_HMAC_SECRET="..." \
-e LOG_LEVEL="info" \
-e LOG_FILE="/app/logs/app.log" \
-v C:\\full\\path\\to\\media:/app/media \
-v C:\\full\\path\\to\\logs:/app/logs \
--name snapbot snapbot:dev- HMAC-signed webhooks with shared secret to prevent spoofing.
- Credentials provided via env; not stored beyond runtime.
- Chromium permissions automatically granted for camera/microphone.
- If CAPTCHA occurs, pre-login to Snapchat Web and pass a
userDataDir(Chrome profile) to reuse cookies.
Completed
- Ensure login helper:
ensureLoggedIn()insnapbot.js. - Video capture support with fake device flags and presence checks.
- REST API endpoints:
/health,/login,/sendSnap,/sendVideo,/listRecipients,/userStatus,/jobs/:id. - Postgres integration and migrations.
- Webhook callbacks with retries/backoff and persistence of delivery attempts.
- Structured logging: pino to console/file and logs persisted in DB.
- Dockerfile on Puppeteer base image, healthcheck, volumes, non-root.
In Progress
- Volume-mountable cookie path wiring: redirect cookie files to
/app/data/cookiesinsnapbot.js. - Config/docs polish: document runtime flags and volume usage in
README.md.
Pending / Next
- Convenience wrapper:
sendVideoTo(category, videoPathY4M, audioPathWAV, caption?). - Documentation:
- Docker usage examples
- API reference
- DB schema notes
- ffmpeg conversion examples (outside container)
- Scheduling patterns
- Install dependencies:
npm ci
- Configure
.env(based on.env.example) - Migrate DB:
npm run migrate
- Start API:
npm run api
- Health:
GET http://localhost:3000/health
- Snapchat Web selectors may change; we added some resilience but occasional updates may be needed.
- CAPTCHA can block automation; use
userDataDirto reuse a pre-logged-in Chrome profile. - Cookie persistence path follow-up: wire to
/app/data/cookiesfor Docker volume persistence. - Long videos are limited by how long the capture button is held (
durationMs).