DEV Community

孫昊
孫昊

Posted on

The 30-Minute Daily Briefing Script That Replaced My Morning Routine

TL;DR: A 60-line Python script that emails me a daily indie hacker briefing at 7am. Aggregates: dev.to traffic, Gumroad sales, Apple ASC build state, Substack subs, GitHub stars, scheduled tasks for today. Replaces 30 minutes of "checking state" each morning.


What the briefing looks like

=== AutoApp Daily Briefing — 2026-05-07 ===

CONTENT
- dev.to: 50 articles, 0 published yesterday (target: 5/week)
- Substack: 12 issues, 0 published yesterday (target: 1/week)

REVENUE
- Gumroad sales (24h): 0 / total 0
- iOS IAP (24h): 0 / total 0
- B2B consulting (week): 0 calls booked
- Affiliate commissions (week): $0

iOS APPS
- AutoChoice: build VALID, NOT in App Store
- AltitudeNow: build VALID, NOT in App Store
- DaysUntil: build VALID, NOT in App Store
- PromptVault: build VALID, NOT in App Store

AUDIENCE
- dev.to followers: 12 (+0)
- Substack subscribers: 0 (+0)
- GitHub stars (autoapp-toolkit): 0 (+0)
- Twitter followers: 89 (+0)

LIVE ASSETS
- Site pages: 31 LIVE
- All HTTP 200: ✓ (33/33)

TODAY'S TODOS
- [ ] Reddit r/SideProject post (paste-ready /reports/reddit-toolkit-launch)
- [ ] Substack #24 publish (paste-ready /reports/substack-issue-24)
- [ ] dev.to #51-#56 batch publish

ACTIONS YOU CAN DO IN < 5 MIN
- Open https://dev.to/dashboard → check stats
- Open https://gumroad.com/dashboard → check sales
- Open https://appstoreconnect.apple.com → check Apple Review
Enter fullscreen mode Exit fullscreen mode

The script (~60 lines)

#!/usr/bin/env python3
"""Daily indie hacker briefing — designed to run at 7am via cron."""
import json, requests
from datetime import datetime, timedelta
from pathlib import Path

ROOT = Path(__file__).parent.parent
CONFIG = json.loads((ROOT / "config" / "briefing.json").read_text())

def section(title):
    return f"\n{title}\n" + "-" * len(title) + "\n"

def fetch_devto():
    api_key = CONFIG["devto_api_key"]
    r = requests.get(
        "https://dev.to/api/articles/me",
        headers={"api-key": api_key},
        params={"per_page": 100},
    )
    if r.ok:
        articles = r.json()
        published = [a for a in articles if a.get("published")]
        return f"- dev.to: {len(published)} articles total"
    return "- dev.to: API error"

def fetch_gumroad():
    api_key = CONFIG["gumroad_api_key"]
    r = requests.get(
        "https://api.gumroad.com/v2/sales",
        params={"access_token": api_key},
    )
    if r.ok:
        sales = r.json().get("sales", [])
        cutoff = datetime.now() - timedelta(hours=24)
        recent = [s for s in sales if datetime.fromisoformat(s["created_at"].rstrip("Z")) > cutoff]
        return f"- Gumroad sales (24h): {len(recent)} / total {len(sales)}"
    return "- Gumroad: API error"

def fetch_asc():
    state_path = ROOT / "data" / "asc_status.json"
    if state_path.exists():
        s = json.loads(state_path.read_text())
        lines = []
        for app in s.get("apps", []):
            status = "VALID" if app.get("build_state") == "VALID" else "BUILDING"
            in_store = "in App Store" if app.get("in_app_store") else "NOT in App Store"
            lines.append(f"- {app['name']}: build {status}, {in_store}")
        return "\n".join(lines)
    return "- ASC: status file missing"

def fetch_audit():
    state_path = ROOT / "data" / "audit.json"
    if state_path.exists():
        a = json.loads(state_path.read_text())
        return f"- All HTTP 200: {'' if a['fail'] == 0 else ''} ({a['ok']}/{a['total']})"
    return "- Audit: not yet run today"

def main():
    today = datetime.now().strftime("%Y-%m-%d")
    out = []
    out.append(f"=== AutoApp Daily Briefing — {today} ===")
    out.append(section("CONTENT"))
    out.append(fetch_devto())
    out.append(section("REVENUE"))
    out.append(fetch_gumroad())
    out.append(section("iOS APPS"))
    out.append(fetch_asc())
    out.append(section("LIVE ASSETS"))
    out.append(fetch_audit())
    out.append(section("TODAY'S TODOS"))
    out.append(check_todos())
    print("\n".join(out))

def check_todos():
    todo_path = ROOT / "INBOX" / "WEEKLY-CONTENT-PLAN-current.md"
    if todo_path.exists():
        # Find lines starting with - [ ]
        lines = todo_path.read_text().split("\n")
        unchecked = [l for l in lines if l.strip().startswith("- [ ]")]
        return "\n".join(unchecked[:5])  # top 5
    return "- (no plan file found)"

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Cron setup (Windows)

Use Windows Task Scheduler:

  1. Action → Start Program → python.exe
  2. Args: C:\path\to\dashboard\daily_briefing.py
  3. Trigger: Daily at 07:00
  4. Output → save to C:\path\to\briefing-{date}.txt

Or PowerShell scheduled task:

$action = New-ScheduledTaskAction -Execute "python.exe" -Argument "C:\path\to\daily_briefing.py"
$trigger = New-ScheduledTaskTrigger -Daily -At 07:00
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "DailyBriefing"
Enter fullscreen mode Exit fullscreen mode

Cron setup (Linux/macOS)

crontab -e
# Add:
0 7 * * * cd /path/to/project && python dashboard/daily_briefing.py | mail -s "Daily Briefing" you@example.com
Enter fullscreen mode Exit fullscreen mode

What replaces 30 minutes

Pre-script morning routine:

  • Open dev.to dashboard, check article stats: 5 min
  • Open Gumroad, check sales: 3 min
  • Open ASC, check build states: 4 min
  • Open Substack, check subs: 2 min
  • Open GitHub, check stars: 1 min
  • Look at today's TODO list: 5 min
  • Mentally aggregate: "what should I do today?": 10 min

Total: 30 min, every morning.

Post-script: 90 sec to read the briefing email + decide.

Time saved: 28.5 min/day × 365 = 173 hours/year.

What I'd add next

  • Twitter follower delta (Twitter API V2)
  • Stripe subscriptions (when I add paid tier)
  • ConvertKit subscriber delta (if I switch from Substack)
  • Apple Search Ads spend + installs
  • Google Analytics page views (when I add GA)

All cron-friendly, all stack on the same script.

Source

Full daily briefing script + config + Windows Task Scheduler XML:

AutoApp Dashboard ($39) includes:

  • daily_briefing.py (this article)
  • weekly_summary_gen.py (Sunday weekly report)
  • monthly_review.py (last day of month)
  • cron-setup.md (Windows + Linux + macOS)

If you spend more than 5 minutes/day "checking state," you have a briefing-shaped problem. 60 lines of Python solves it.

Top comments (0)