#!/usr/bin/env python3 """Fetch open Google Tasks count for the daily briefing (fallback helper).""" import json, urllib.request, urllib.parse from pathlib import Path HERMES = Path.home() / ".hermes" TOKEN_PATH = HERMES / "google_token.json" CLIENT_PATH = HERMES / "google_client_secret.json" SCOPES = ["https://www.googleapis.com/auth/tasks.readonly"] def get_access_token(): # Try a refresh that explicitly requests the tasks scope tok = json.loads(TOKEN_PATH.read_text()) client = json.loads(CLIENT_PATH.read_text()) c = client.get("installed", client.get("web", {})) data = urllib.parse.urlencode({ "client_id": c["client_id"], "client_secret": c["client_secret"], "refresh_token": tok["refresh_token"], "grant_type": "refresh_token"}).encode() resp = urllib.request.urlopen(urllib.request.Request( "https://oauth2.googleapis.com/token", data=data), timeout=20) payload = json.loads(resp.read()) import sys print("GRANTED_SCOPES: " + payload.get("scope", "(none reported)"), file=sys.stderr) return payload["access_token"] def main(): if not TOKEN_PATH.exists(): print(json.dumps({"error": "no_token_file"})); return tok = json.loads(TOKEN_PATH.read_text()) headers = {"Authorization": "Bearer " + get_access_token()} try: lists = json.loads(urllib.request.urlopen(urllib.request.Request( "https://tasks.googleapis.com/tasks/v1/users/@me/lists", headers=headers), timeout=20 ).read()).get("items", []) except urllib.error.HTTPError as e: print(json.dumps({"error": "http_%s" % e.code, "body": e.read().decode(errors="replace")[:500], "saved_scope": tok.get("scope", "")}, ensure_ascii=False)) return total, details = 0, [] for lst in lists: u = ("https://tasks.googleapis.com/tasks/v1/lists/%s/tasks" "?maxResults=50&showCompleted=false&showHidden=false" % lst["id"]) try: items = json.loads(urllib.request.urlopen( urllib.request.Request(u, headers=headers), timeout=20 ).read()).get("items", []) except Exception: items = [] total += len(items) for t in items[:10]: details.append({"list": lst.get("title", "?"), "title": t.get("title", ""), "due": t.get("due", "")}) print(json.dumps({"tasks": total, "details": details}, ensure_ascii=False)) if __name__ == "__main__": main()