-
Notifications
You must be signed in to change notification settings - Fork 0
Social Cards
Signal automatically generates three infographic cards each morning and posts them to Bluesky at scheduled times. Each card is a 1200×630px PNG rendered from an HTML template and posted with a short teaser that links back to the full report.
Command runbook: This page is the full how-to (every shell command). The same text lives in the repo as
docs/social-cards-setup.md. Design spec:docs/signal_social_feature.md.
All commands from the repo root:
cd /Users/garotconklin/garotm/fleXRPL/signalAlways use .venv/bin/pip and .venv/bin/python — not system pip (macOS blocks it).
.venv/bin/pip install playwright atproto python-dotenv
.venv/bin/playwright install chromium- bsky.app → your account → Settings → Privacy and Security → App Passwords
-
Add App Password (e.g. name:
signal-pipeline) — copy thexxxx-xxxx-xxxx-xxxxvalue once -
Do not put your normal login password in
.env
cp .env.example .envEdit .env:
BLUESKY_HANDLE=yourhandle.bsky.social
BLUESKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
Confirm .env never appears in git status.
Cards are created during the 5:00 AM daily run, not at post time.
.venv/bin/python main.pyOr after the scheduled run, verify today's files (UTC date):
DATE=$(date -u +%Y%m%d)
ls -la reports/cards/am_${DATE}.png reports/cards/noon_${DATE}.png reports/cards/pm_${DATE}.png
ls -la reports/posts/am_${DATE}.json reports/posts/noon_${DATE}.json reports/posts/pm_${DATE}.jsonIf missing: grep -i social logs/cron.log | tail -20
.venv/bin/python post_scheduled.py --slot am --dry-run
.venv/bin/python post_scheduled.py --slot noon --dry-run
.venv/bin/python post_scheduled.py --slot pm --dry-runSpecific date:
.venv/bin/python post_scheduled.py --slot am --date 20260601 --dry-run.venv/bin/python post_scheduled.py --slot am
.venv/bin/python post_scheduled.py --slot noon
.venv/bin/python post_scheduled.py --slot pmRe-post: set "posted": false in reports/posts/{slot}_YYYYMMDD.json, then run the command again.
cp scripts/com.flexrpl.signal.social.am.plist ~/Library/LaunchAgents/
cp scripts/com.flexrpl.signal.social.noon.plist ~/Library/LaunchAgents/
cp scripts/com.flexrpl.signal.social.pm.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.social.am.plist
launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.social.noon.plist
launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.social.pm.plistVerify all Signal jobs:
launchctl list | grep flexrplYou should see daily, weekly, and three social.* entries. See also Scheduling for daily/weekly setup.
launchctl start com.flexrpl.signal.social.am
launchctl start com.flexrpl.signal.social.noon
launchctl start com.flexrpl.signal.social.pmtail -f logs/social_am.log
tail -f logs/social_noon.log
tail -f logs/social_pm.log
tail -f logs/cron.log # card generation during daily pipelineAfter editing scripts/com.flexrpl.signal.social.*.plist:
launchctl unload ~/Library/LaunchAgents/com.flexrpl.signal.social.am.plist
cp scripts/com.flexrpl.signal.social.am.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.social.am.plistDisable automated social posting:
launchctl unload ~/Library/LaunchAgents/com.flexrpl.signal.social.am.plist
launchctl unload ~/Library/LaunchAgents/com.flexrpl.signal.social.noon.plist
launchctl unload ~/Library/LaunchAgents/com.flexrpl.signal.social.pm.plist| Slot | Time | Card | Content source |
|---|---|---|---|
| AM | 7:00 AM | Watch List |
watch_items from brief_data.json
|
| Noon | 12:00 PM | Spectrum Breakdown |
top_cluster framing from brief_data.json
|
| PM | 6:00 PM | Blindspot Analysis |
left_only / right_only from brief_data.json
|
All three cards are generated during the 5:00 AM pipeline run. The posting jobs do no model or rendering work — they simply load pre-generated files and post.
5:00 AM — main.py
→ Passes 1–5: analysis pipeline
→ reporter.py writes:
reports/brief_YYYYMMDD_HHMM.html (full report)
reports/brief_YYYYMMDD_HHMM.json (structured card data)
→ infographic.py renders:
reports/cards/am_YYYYMMDD.png
reports/cards/noon_YYYYMMDD.png
reports/cards/pm_YYYYMMDD.png
→ social.py writes:
reports/posts/am_YYYYMMDD.json
reports/posts/noon_YYYYMMDD.json
reports/posts/pm_YYYYMMDD.json
7:00 AM — post_scheduled.py --slot am
→ loads reports/posts/am_YYYYMMDD.json
→ posts image + text to Bluesky
→ marks package as posted
12:00 PM — post_scheduled.py --slot noon
→ loads reports/posts/noon_YYYYMMDD.json → posts
6:00 PM — post_scheduled.py --slot pm
→ loads reports/posts/pm_YYYYMMDD.json → posts
If the morning pipeline run fails, all three JSON packages will not exist and the posting jobs exit cleanly with code 1 (logged, no crash, no retry).
All cards use the Signal dark theme (IBM Plex Mono / IBM Plex Sans, #0f1117 background) at 1200×630px.
- Header: amber dot · "SIGNAL // Watch List" · date
- Subheader: item count + time horizon summary
- Grid: up to 6 watch items, each with a color-coded time window badge
- 24hr → red, 48hr → orange, 72hr → amber, 5-day → green
- Footer:
flexrpl.github.io/signal
- Header: cyan dot · "SIGNAL // Political Intelligence" · article/source counts
- Story block: top cluster headline with left red border accent
- Spectrum bar: proportional distribution across 7 outlet categories
- Coverage columns (3-up): Left frame / Center frame / Right frame with source tags
- Omissions row: what each side is not saying
- Footer: cluster count
- Header: purple dot · "SIGNAL // Blindspot Analysis" · "What each side isn't saying"
- Subheader: most significant suppressed story
- 2-column grid: Left-Only stories (blue) vs Right-Only stories (red)
- Footer: source count
| File | Purpose |
|---|---|
pipeline/infographic.py |
Playwright HTML→PNG renderer; render_all_cards() entry point |
pipeline/social.py |
Bluesky auth, image upload, post composition; post_slot() dispatcher |
pipeline/templates/card_watch.html |
Jinja2 template for Watch List card |
pipeline/templates/card_spectrum.html |
Jinja2 template for Spectrum Breakdown card |
pipeline/templates/card_blindspot.html |
Jinja2 template for Blindspot Analysis card |
post_scheduled.py |
CLI: --slot [am|noon|pm], --dry-run, --date YYYYMMDD
|
scripts/com.flexrpl.signal.social.am.plist |
launchd job — 7:00 AM |
scripts/com.flexrpl.signal.social.noon.plist |
launchd job — 12:00 PM |
scripts/com.flexrpl.signal.social.pm.plist |
launchd job — 6:00 PM |
.env.example |
Credential template |
reports/cards/ |
Generated PNG card images (committed, served via Pages) |
reports/posts/ |
Pre-generated JSON post packages |
Cards not generated after morning run:
Check logs/cron.log for Social card generation failed. The most common cause is playwright not installed — run .venv/bin/playwright install chromium.
Post failed: "BLUESKY_HANDLE and BLUESKY_APP_PASSWORD must be set":
The .env file is missing or not being read. Verify it exists at the repo root and contains both variables. The launchd plist passes SIGNAL_ENV_FILE pointing to it — python-dotenv loads it automatically via load_dotenv().
Post skipped: "already posted":
Each post package tracks a posted: true flag. If you need to re-post, edit the JSON in reports/posts/ and set "posted": false.
Slot not found (exit code 1):
The morning pipeline run did not complete successfully. Check logs/cron.log. The JSON package for that date does not exist, so the post job exits cleanly.
Signal · Repository · fleXRPL · Daily political intelligence — powered by local AI