{"id":43803803,"url":"https://github.com/alliraine/legialerts","last_synced_at":"2026-02-05T22:16:32.189Z","repository":{"id":163547812,"uuid":"585303164","full_name":"alliraine/legialerts","owner":"alliraine","description":"A Twitter and Google Sheets Bot for tracking anti-lgbtq legislation","archived":false,"fork":false,"pushed_at":"2025-12-26T16:48:49.000Z","size":834,"stargazers_count":26,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-12T14:12:42.513Z","etag":null,"topics":["automation","legislation","lgbtqia"],"latest_commit_sha":null,"homepage":"https://legialerts.com","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alliraine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-04T20:39:33.000Z","updated_at":"2025-12-26T16:48:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"84d26b3a-faea-4d31-915b-5cdb0dc689b9","html_url":"https://github.com/alliraine/legialerts","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alliraine/legialerts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alliraine%2Flegialerts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alliraine%2Flegialerts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alliraine%2Flegialerts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alliraine%2Flegialerts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alliraine","download_url":"https://codeload.github.com/alliraine/legialerts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alliraine%2Flegialerts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29136406,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T21:59:57.939Z","status":"ssl_error","status_checked_at":"2026-02-05T21:59:57.628Z","response_time":65,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","legislation","lgbtqia"],"created_at":"2026-02-05T22:16:31.463Z","updated_at":"2026-02-05T22:16:32.175Z","avatar_url":"https://github.com/alliraine.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![.github/workflows/trigger-legialerts.yml](https://github.com/alliraine/legialerts/actions/workflows/trigger-legialerts.yml/badge.svg)](https://github.com/alliraine/legialerts/actions/workflows/trigger-legialerts.yml)\n[![Better Stack Badge](https://uptime.betterstack.com/status-badges/v1/monitor/2c1nr.svg)](https://uptime.betterstack.com/?utm_source=status_badge)\n[![Better Stack Badge](https://uptime.betterstack.com/status-badges/v1/monitor/2c1ns.svg)](https://uptime.betterstack.com/?utm_source=status_badge)\n[![Better Stack Badge](https://uptime.betterstack.com/status-badges/v1/monitor/2c1np.svg)](https://uptime.betterstack.com/?utm_source=status_badge)\n\n# LegiAlerts\nA Twitter/Bsky bot and automation platform for tracking anti-LGBTQ legislation. The bot updates the tracker at https://legialerts.org and posts alerts when new bills appear or when statuses change.\n\n## How it works\n- Pulls session master lists and bill details from the LegiScan API.\n- Updates Google Sheets worksheets for anti/pro and rollover bills.\n- Posts alerts to X (Twitter) and Bluesky, and sends HTML email reports.\n- Caches session lists and sheet snapshots in `cache/` (or `/var/data` when `PRODUCTION=true`) to limit API calls.\n\n## Requirements\n- Python 3\n- A LegiScan API key\n- A Google service account for Sheets access\n- Social accounts and SMTP credentials for notifications\n\nInstall dependencies:\n```bash\npip install -r requirements.txt\n```\n\n## Configuration (environment variables)\nThe bot reads all secrets from environment variables (see `.env.example` for a template):\n- `legiscan_key`: LegiScan API key.\n- `gsuite_service_account` **or** `GSUITE_SERVICE_ACCOUNT_FILE`: Google service account credentials (JSON string or secure file path). Keep the JSON out of version control.\n- `TRACKER_YEARS`: Comma-separated list of tracker years (defaults to `2026`). Each year needs a matching `gsheet_key_\u003cyear\u003e` value.\n- `gsheet_key_\u003cyear\u003e`: Google Sheet key for each tracker year (e.g., `gsheet_key_2026`).\n- `twitter_consumer_key`, `twitter_consumer_secret`, `twitter_access_token`, `twitter_access_token_secret`: X/Twitter API credentials.\n- `bsky_user`, `bsky_pass`: Bluesky credentials.\n- `GOOGLE_TOKEN`: Gmail SMTP app password used to send email reports.\n- `API_AUTH_TOKEN`: Bearer token required for the Flask endpoints (set `API_ALLOW_ANONYMOUS=true` to intentionally disable auth).\n- `LOG_LEVEL`: Logging verbosity (DEBUG, INFO, WARNING, ERROR, CRITICAL). Default is INFO.\n- `LOG_FILE`: Optional log file path (defaults to `cache/legialerts.log`, or `/var/data/legialerts.log` in production).\n- `LEGISCAN_MIN_INTERVAL`: Optional delay between LegiScan API calls in seconds.\n- `REQUEST_TIMEOUT`: Request timeout in seconds for LegiScan calls (defaults to 30).\n- `SEARCH_CACHE_TTL`: Optional cache TTL for search results in seconds (defaults to 3600).\n- `SOCIAL_ENABLED`: Set to `false` to disable posting to X/Twitter and Bluesky.\n- `PRODUCTION`: Set to `true` to store cache files under `/var/data` instead of `cache/`.\n\nSecurity note: Do not commit real tokens, service accounts, or SMTP secrets. Use environment variables or a mounted secret file instead of tracking credentials in Git.\n\n## Running\nThe bot runs continuously and sleeps ~15 minutes between cycles:\n```bash\npython main.py\n```\nIt can also be run under a process manager (systemd, supervisor) or a cronjob that invokes the script repeatedly.\n\n## Web endpoint (Flask)\nYou can trigger a single run via a lightweight Flask app:\n```bash\nflask --app app run --host 0.0.0.0 --port 8080\n```\nEndpoints:\n- `GET /run`: triggers one update cycle; returns 409 if a run is already in progress.\n- `GET /health`: basic health check.\n- `GET /stats`: run metrics and worksheet summary stats from cached sheets.\n\n### Authentication\nAuthentication is required by default; set `API_AUTH_TOKEN` and provide the same token in requests. To deliberately allow anonymous access, set `API_ALLOW_ANONYMOUS=true`. Example:\n```bash\ncurl -H \"Authorization: Bearer $API_AUTH_TOKEN\" http://localhost:8080/health\n```\n\n## Sheets and cache expectations\n- Worksheets expected: `Anti-LGBTQ Bills`, `Pro-LGBTQ Bills`, `Rollover Anti-LGBTQ Bills`, `Rollover Pro-LGBTQ Bills`.\n- The header row is used as the schema; the bot fills in fields like `Status`, `Date`, `Change Hash`, `Sponsors`, `History`, and `PDF`.\n- Cache files are written to `cache/` (or `/var/data` in production), including `sessions.csv`, per-state session lists, and `gsheet-\u003cworksheet\u003e-\u003cyear\u003e.csv`.\n\n## Notes\n- The `years` list in `main.py` controls which tracker years are updated.\n- Email templates live in `utils/email.html`.\n\n## Attribution\nLegiAlerts uses data from LegiScan. All data is licensed under Creative Commons Attribution 4.0; attribution to LegiScan is required.\n\n\n# Credits\nCreated by Allison Chapman\n\nCode Base Maintained By: Allison Chapman @AlliRaine22, Kim MC42, and others\n\nSpreadsheet Maintained by Allison Chapman @AlliRaine22, Alejandra Caraballo @esqueer_, and Erin Reed @erininthemorn\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falliraine%2Flegialerts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falliraine%2Flegialerts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falliraine%2Flegialerts/lists"}