{"id":48622692,"url":"https://github.com/x-pg13/ainews-open","last_synced_at":"2026-06-10T16:00:35.720Z","repository":{"id":349779297,"uuid":"1203841770","full_name":"X-PG13/ainews-open","owner":"X-PG13","description":"Stable open-source workflow for aggregating AI news, generating Chinese daily digests, and publishing them across channels.","archived":false,"fork":false,"pushed_at":"2026-06-10T05:35:02.000Z","size":1454,"stargazers_count":3,"open_issues_count":3,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T06:31:22.205Z","etag":null,"topics":["ai-news","china-tech","daily-digest","fastapi","feishu","llm","news-aggregation","open-source","python","rss","telegram-bot","wechat"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/X-PG13.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":"GOVERNANCE.md","roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":"MAINTAINERS.md","copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-07T12:41:31.000Z","updated_at":"2026-06-10T05:30:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/X-PG13/ainews-open","commit_stats":null,"previous_names":["x-pg13/ainews-open"],"tags_count":64,"template":false,"template_full_name":null,"purl":"pkg:github/X-PG13/ainews-open","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-PG13%2Fainews-open","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-PG13%2Fainews-open/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-PG13%2Fainews-open/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-PG13%2Fainews-open/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/X-PG13","download_url":"https://codeload.github.com/X-PG13/ainews-open/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-PG13%2Fainews-open/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34159251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["ai-news","china-tech","daily-digest","fastapi","feishu","llm","news-aggregation","open-source","python","rss","telegram-bot","wechat"],"created_at":"2026-04-09T04:10:15.656Z","updated_at":"2026-06-10T16:00:35.711Z","avatar_url":"https://github.com/X-PG13.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003eAI News Open\u003c/h1\u003e\n  \u003cp\u003e\u003cstrong\u003eOpen-source workflow for aggregating AI news, translating global coverage into Chinese, and publishing daily digests across channels.\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003e\u003ca href=\"README.md\"\u003eEnglish\u003c/a\u003e · \u003ca href=\"README.zh-CN.md\"\u003e简体中文\u003c/a\u003e\u003c/p\u003e\n  \u003cp\u003e\n    \u003ca href=\"https://github.com/X-PG13/ainews-open/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/X-PG13/ainews-open/actions/workflows/ci.yml/badge.svg\" alt=\"CI\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/X-PG13/ainews-open/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/X-PG13/ainews-open\" alt=\"Release\" /\u003e\u003c/a\u003e\n    \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/X-PG13/ainews-open\" alt=\"License\" /\u003e\u003c/a\u003e\n    \u003ca href=\"pyproject.toml\"\u003e\u003cimg src=\"https://img.shields.io/badge/python-3.9%2B-3776AB\" alt=\"Python\" /\u003e\u003c/a\u003e\n  \u003c/p\u003e\n  \u003cp\u003e\n    \u003ca href=\"https://github.com/X-PG13/ainews-open\"\u003eRepository\u003c/a\u003e ·\n    \u003ca href=\"https://github.com/X-PG13/ainews-open/releases\"\u003eReleases\u003c/a\u003e ·\n    \u003ca href=\"docs/github-launch-kit.md\"\u003eLaunch Kit\u003c/a\u003e ·\n    \u003ca href=\"docs/demo/index.html\"\u003eDemo\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n## Maintainer Status Snapshot\n\n### Current Signals\n\n- **Release status**: read from [release notes index](docs/releases/README.md) (source of truth for latest patch level)\n- **Current planning**: [Roadmap](ROADMAP.md)\n- **Open maintenance milestones**: [open milestones](https://github.com/X-PG13/ainews-open/milestones?q=is%3Aopen+is%3Amilestone)\n- **Deferred engineering**: [Deferred: PyPI](https://github.com/X-PG13/ainews-open/milestone/3)\n\n### Maintainer Rhythm\n\n- Before opening follow-up work, confirm the latest release notes entry and align updates to the active milestone.\n- Before each milestone closeout, verify release assets and release docs using [release checklist](docs/release-checklist.md).\n\n![AI News Open Real Console Screenshot](docs/assets/console-real.png)\n![AI News Open Operations Overview](docs/assets/operations-panel-preview.svg)\n\n## Product Snapshot\n\nAI News Open is an open-source AI news stack built for maintainers, content teams, and operators who need more than a toy feed reader. It turns scattered domestic and international AI sources into one workflow: ingest, clean up, deduplicate, extract article bodies, translate global stories into Chinese, generate daily digests, archive them, and publish them across channels.\n\n## Why It Exists\n\n- Aggregate domestic and international AI news without paying for a commercial news API.\n- Translate global AI coverage into Chinese titles, summaries, and \"why it matters\" notes.\n- Ship one stack that includes CLI, FastAPI, a zero-build admin console, and publishing targets.\n- Meet open-source engineering expectations with tests, CI, lint, Docker, issue templates, and security policy.\n\n## What You Ship\n\n| Layer | Included |\n| --- | --- |\n| Sources | Domestic and international RSS/Atom source registry |\n| Processing | Cleanup, deduplication, extraction, enrichment, digest generation |\n| Interfaces | CLI, FastAPI API, zero-build admin dashboard |\n| Publishing | Telegram, Feishu, static site, WeChat draft/publish |\n| Engineering | Tests, lint, pre-commit, CI, Docker, changelog, security policy |\n\n## Use Cases\n\n- Run your own AI news digest as an individual maintainer.\n- Build a Chinese editorial workflow for international AI news.\n- Support a lightweight AI media product or internal intelligence feed.\n- Push daily digests to Telegram, Feishu, a static site, or a WeChat official account.\n\n## 60-Second Start\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npython -m pip install .\ncp .env.example .env\npython -m ainews run-pipeline --since-hours 48 --limit 30 --max-items 30 --use-llm --persist --export\npython -m ainews serve --port 8000\n```\n\nAfter startup you can:\n\n- Open the admin console at `http://127.0.0.1:8000/`\n- Use the Operations panel to inspect `/health`, recent pipeline runs, source cooldowns, source alerts, and publication failures in one screen\n- Browse the article pool, digest archive, publication history, and WeChat publish status\n- Trigger ingest, extraction, translation, digest generation, and publishing from the dashboard\n- Freeze a preview into a stored digest snapshot, edit ranking/section/title/summary overrides, and publish the confirmed snapshot instead of a fresh recompute\n\n## Public Demo\n\n- Sample demo page: [docs/demo/index.html](docs/demo/index.html)\n- Sample digest markdown (ZH): [docs/demo/sample-digest.md](docs/demo/sample-digest.md)\n- Sample digest markdown (EN): [docs/demo/sample-digest.en.md](docs/demo/sample-digest.en.md)\n- Sample digest JSON: [docs/demo/sample-digest.json](docs/demo/sample-digest.json)\n- Sample health payload: [docs/demo/sample-health.json](docs/demo/sample-health.json)\n- Sample operations payload: [docs/demo/sample-operations.json](docs/demo/sample-operations.json)\n- Sample publication history: [docs/demo/sample-publications.json](docs/demo/sample-publications.json)\n\n## Maintainer Flow\n\n```bash\npython -m pip install -e \".[dev]\"\npre-commit install\nmake check\n```\n\n`make check` is the local maintainer gate. It runs lint, coverage, package build validation, and the `/health` smoke check in one command. Use `make coverage` or `make smoke` separately only when you are iterating on one layer.\n\n## Operator Docs\n\n- [Architecture Overview](docs/architecture.md)\n- [Compatibility Contract](docs/compatibility.md)\n- [Configuration Matrix](docs/configuration.md)\n- [First Deploy Guide](docs/first-deploy.md)\n- [Operator Console Walkthrough](docs/operator-console-walkthrough.md)\n- [Deployment Guide](docs/deployment.md)\n- [Database Migrations](docs/database-migrations.md)\n- [Troubleshooting](docs/troubleshooting.md)\n- [Monitoring](docs/monitoring.md)\n- [Maintainer Bootstrap](docs/maintainer-bootstrap.md)\n- [Roadmap](ROADMAP.md)\n- [Support Lifecycle](docs/support-lifecycle.md)\n- [PR Review Policy](docs/pr-review-policy.md)\n- [Release Notes](docs/releases/README.md)\n- [Release Artifacts](docs/release-artifacts.md)\n- [Release Recovery Notes](docs/release-recovery.md)\n- [Use Cases](docs/use-cases.md)\n- [Community Triage](docs/community-triage.md)\n- [Contributor Playbook](docs/contributor-playbook.md)\n- [Release Checklist](docs/release-checklist.md)\n- [Support Policy](SUPPORT.md)\n\n## What You Get\n\nThe current version includes:\n\n- A domestic and international AI source registry with Chinese sites, global media, and official blogs\n- RSS/Atom ingestion, basic cleanup, deduplicated persistence\n- Cross-source duplicate clustering using canonical URLs, resolved targets, normalized titles, and content fingerprints\n- Article body extraction, source-specific cleanup, and local storage\n- LLM-powered translation and summary enrichment for international stories\n- Chinese daily digest generation and digest history\n- Editorial controls for `pin`, `must_include`, `suppress`, duplicate-primary selection, digest selection preview with explicit inclusion/exclusion reasons, and a frozen digest editor with publish-time overrides\n- A publication layer for Telegram, Feishu, a static site, and WeChat draft publishing\n- Feishu card messages and automatic WeChat cover upload\n- Publication history management and WeChat publish-status refresh\n- A `FastAPI` HTTP API\n- A zero-build admin console\n- CLI commands for ingest, extraction, enrichment, digest generation, publication, and full pipeline execution\n- SQLite storage\n- Unit tests, API smoke tests, Dockerfile, CI workflows\n- `ruff`, coverage, `pre-commit`, issue/PR templates, Security, and Code of Conduct\n\nThe default sources were verified as reachable on `2026-04-07`, including:\n\n- Chinese: `36Kr`, `TMTPost`, `IT之家`, `Google News CN AI`\n- Global: `OpenAI News`, `Google AI Blog`, `Google DeepMind Blog`, `Hugging Face Blog`, `TechCrunch AI`, `The Verge AI`, `VentureBeat AI`, `Google News Global AI`\n\n## Why It Is Designed This Way\n\nThis version optimizes for four practical constraints:\n\n1. No paid news API dependency. Everything starts from public RSS or Atom feeds.\n2. Easy extensibility. All default sources live in `src/ainews/sources.default.json`.\n3. International stories can be extracted first and then translated into Chinese titles, summaries, and \"why it matters\" notes through a configurable LLM.\n4. The same project can run as a service, a CLI task, a dashboard-backed tool, or a scheduled workflow on a server, in Docker, or in GitHub Actions.\n\n## Project Layout\n\n```text\nsrc/ainews/\n  api.py               FastAPI entrypoint\n  cli.py               CLI entrypoint\n  config.py            Environment variables and settings\n  content_extractor.py Article body extraction\n  feed_parser.py       RSS / Atom parsing\n  http.py              HTTP fetching helpers\n  llm.py               OpenAI-compatible LLM client\n  models.py            Data models\n  publisher.py         Digest publishing layer\n  repository.py        SQLite storage\n  service.py           Ingest and aggregation service\n  web/                 Admin console\n  sources.default.json Default source registry\n```\n\n## Quick Start\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npython -m pip install --upgrade pip setuptools wheel\npython -m pip install .\ncp .env.example .env\npython -m ainews ingest\npython -m ainews extract --since-hours 48 --limit 20\npython -m ainews stats\npython -m ainews print-digest --region all --limit 20\npython -m ainews publish --use-llm --persist --export --target static_site\npython -m ainews serve --port 8000\n```\n\nIf you are maintaining or publicly shipping the repository, also run:\n\n```bash\npython -m pip install -e \".[dev]\"\npre-commit install\nmake check\n```\n\nOpen the console at:\n\n```text\nhttp://127.0.0.1:8000/\n```\n\nFor local visual checks, you can also open `src/ainews/web/index.html` directly as a file. The console now uses relative asset paths so style and behavior render correctly in `file:///` mode.\n\nIf you prefer to start the API directly:\n\n```bash\nuvicorn ainews.api:create_app --factory --host 0.0.0.0 --port 8000\n```\n\nIf you prefer to run it in containers:\n\n```bash\ndocker compose up --build\n```\n\nIf you want Prometheus and Grafana alongside the API:\n\n```bash\ndocker compose --profile monitoring up --build\n```\n\nIf your local `pip` is recent enough and you want editable installs:\n\n```bash\npython -m pip install -e \".[dev]\"\n```\n\n## Open-Source Engineering Baseline\n\nThis repository already includes the expected open-source project baseline:\n\n- Community docs: `CONTRIBUTING.md`, `SUPPORT.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `CHANGELOG.md`, `GOVERNANCE.md`, `MAINTAINERS.md`, `CITATION.cff`, `docs/community-triage.md`, `docs/support-lifecycle.md`\n- Collaboration templates: GitHub issue templates, pull request template, `CODEOWNERS`, and review policy\n- Quality gates: `ruff`, unit tests, coverage, package build validation, `pre-commit`\n- Automation: CI, tag-based release workflow, CodeQL, Dependabot\n- Release verification: published artifact checksum and install smoke workflow\n- Packaging and runtime: non-root Docker runtime, `HEALTHCHECK`, `compose.yaml`, `.dockerignore`, `.editorconfig`\n- Supply chain: release checksums, CycloneDX SBOM, build provenance, PyPI trusted publishing workflow\n- Observability: Prometheus-compatible `/metrics`, source runtime history, housekeeping workflow, and ready-to-run monitoring profile\n- Demo assets: sample site content, GitHub Pages workflow, sample digest markdown and JSON output\n\nBefore publishing the repository, still confirm two things:\n\n1. Your private security reporting channel is configured in GitHub Security Advisories.\n2. The organization name, repository URLs, and maintainer metadata in `README.md` and `pyproject.toml` match your real public values.\n\nSecurity reports, maintainer triage, and disclosure expectations are documented\nin [SECURITY.md](SECURITY.md). Support boundaries are documented in\n[SUPPORT.md](SUPPORT.md) and [Support Lifecycle](docs/support-lifecycle.md).\n\nIf you are preparing a GitHub launch, you can reuse:\n\n- `docs/github-launch-kit.md`\n- `docs/project-intro.md`\n\nRecommended community scaffolding:\n\n- `ROADMAP.md`\n- `SUPPORT.md`\n- `docs/community-triage.md`\n- `docs/support-lifecycle.md`\n- `GOVERNANCE.md`\n- `MAINTAINERS.md`\n- `docs/architecture.md`\n- `.github/labels.yml`\n\n## LLM Translation and Digest Generation\n\nIf you want international stories translated into Chinese and daily digests generated by an LLM, configure an OpenAI-compatible endpoint in `.env`:\n\n```env\nAINEWS_LLM_PROVIDER=openai_compatible\nAINEWS_LLM_BASE_URL=your-compatible-endpoint\nAINEWS_LLM_API_KEY=your-key\nAINEWS_LLM_MODEL=your-model\n```\n\nThen run:\n\n```bash\npython -m ainews extract --since-hours 48 --limit 20\npython -m ainews enrich --since-hours 48 --limit 20\npython -m ainews print-digest --region all --limit 20 --use-llm --persist\n```\n\nNotes:\n\n- `extract` fetches article bodies for downstream translation and summarization.\n- `enrich` only targets international stories and fills Chinese title, summary, and importance fields.\n- `print-digest --use-llm` prefers translated content to generate a Chinese daily digest.\n- If no LLM is configured, the system falls back to a rule-based digest template so the workflow remains available.\n\nIf you want the full pipeline in one command:\n\n```bash\npython -m ainews run-pipeline --since-hours 48 --limit 30 --max-items 30 --use-llm --persist --export\n```\n\nThat pipeline executes:\n\n1. Ingest the latest stories\n2. Extract article bodies\n3. Translate international stories\n4. Generate a digest\n5. Export `output/*.md` and `output/*.json`\n\nTo publish as part of the same pipeline:\n\n```bash\npython -m ainews run-pipeline \\\n  --since-hours 48 \\\n  --limit 30 \\\n  --max-items 30 \\\n  --use-llm \\\n  --persist \\\n  --export \\\n  --publish \\\n  --target static_site\n```\n\nNotes:\n\n- `publish` and `run-pipeline --publish` automatically persist the digest so publication status can be refreshed later and idempotency can work.\n- Publishing the same stored `digest` to the same `target` returns `skipped` by default and does not create duplicate publication records.\n- If you intentionally want to publish again, add `--force-republish`.\n\n## Content Extraction and Source-Specific Cleanup\n\nThe content extractor ships with two layers:\n\n- Generic article detection that prefers containers such as `article`, `main`, `entry-content`, and `post-content`\n- Source-specific cleanup rules that currently prioritize the real article body for `36Kr` and `IT之家`, while dropping recommendation panels, share widgets, breadcrumbs, comments, and other site noise\n\nEven if `beautifulsoup4` is not installed, the project falls back to a standard-library parser and still applies source-specific extraction rules for `36Kr` and `IT之家`.\n\n## Publishing Layer\n\nThe current release supports four publication targets:\n\n- `telegram`: send text digests through the Bot API\n- `feishu`: send digests through a custom webhook, with `text` and `interactive` card modes\n- `wechat`: create drafts in a WeChat official account and optionally submit them for publication; supports automatic cover upload to produce `thumb_media_id`\n- `static_site`: generate a zero-dependency static page and `latest.json`\n\nExamples:\n\n```bash\npython -m ainews publish --use-llm --target telegram\npython -m ainews publish --use-llm --target feishu --target static_site\npython -m ainews publish --use-llm --target wechat --wechat-submit\npython -m ainews publish --digest-id 1 --target static_site --force-republish\n```\n\nIf `--target` is omitted, the system reads `AINEWS_PUBLISH_TARGETS`.\n\n### Feishu Cards\n\nIf you want cards instead of plain text by default:\n\n```env\nAINEWS_FEISHU_MESSAGE_TYPE=card\n```\n\nThe implementation tries `interactive` cards first and falls back to `text` automatically if the card send fails.\n\n### Automatic WeChat Cover Upload\n\nIf you do not want to prepare a `thumb_media_id` manually, provide a cover image source instead:\n\n```env\nAINEWS_WECHAT_APP_ID=your-app-id\nAINEWS_WECHAT_APP_SECRET=your-app-secret\nAINEWS_WECHAT_THUMB_IMAGE_PATH=assets/wechat-cover.jpg\n# or\nAINEWS_WECHAT_THUMB_IMAGE_URL=https://example.com/wechat-cover.jpg\nAINEWS_WECHAT_THUMB_UPLOAD_TYPE=thumb\n```\n\nNotes:\n\n- `thumb` mode uses the permanent material upload API with `type=thumb`, which is better suited for cover images. Per the official constraint, the image must be `JPG` and under `64KB`.\n- If you already have a media asset, you can still set `AINEWS_WECHAT_THUMB_MEDIA_ID` directly.\n- The current implementation only uploads the cover asset automatically. It does not yet rewrite external image links inside the article body.\n\n### WeChat Publication Status Refresh\n\nIf you use `--wechat-submit` or `AINEWS_WECHAT_PUBLISH_AFTER_DRAFT=true`, the system stores submitted publication records and lets you refresh the final publication state later.\n\nUse:\n\n```bash\npython -m ainews list-publications --target wechat --limit 20\npython -m ainews refresh-publications --target wechat --limit 20\n```\n\nThe current implementation maps the official `freepublish/get` status into:\n\n- `pending`: still being published\n- `ok`: publication succeeded\n- `error`: originality validation failure, generic failure, review rejection, deletion after publication, or account restriction\n\n## Admin Console\n\nThe root path `/` provides an out-of-the-box admin page that supports:\n\n- News ingest\n- Batch translation for international stories\n- Batch article body extraction\n- Digest generation and review\n- Target selection and one-click publishing\n- Publication history with manual WeChat refresh\n- Digest history\n- Manual curation such as pinning, hiding, and editorial notes\n\nIf you want simple protection for admin routes, set:\n\n```env\nAINEWS_ADMIN_TOKEN=your-secret-token\n```\n\nThe frontend will automatically send `X-Admin-Token` to the admin API.\n\n## CLI\n\n```bash\npython -m ainews ingest\npython -m ainews extract --limit 20\npython -m ainews enrich --limit 20\npython -m ainews print-digest --use-llm --persist\npython -m ainews run-pipeline --use-llm --persist --export\npython -m ainews publish --use-llm --persist --target static_site\npython -m ainews publish --digest-id 1 --target static_site --force-republish\npython -m ainews list-digests --limit 10\npython -m ainews list-publications --limit 20\npython -m ainews refresh-publications --target wechat --limit 20\npython -m ainews stats\npython -m ainews serve --port 8000\n```\n\n## API\n\n### `GET /health`\n\nHealth check. Returns `status`, current service `version`, database checks, and `schema_version`.\n\n### `GET /sources`\n\nList enabled sources.\n\n### `POST /ingest`\n\nTrigger one ingest run.\n\nExample:\n\n```bash\ncurl -X POST \"http://127.0.0.1:8000/ingest?source_id=36kr-ai\u0026source_id=openai-news\"\n```\n\n### `GET /articles`\n\nList stored articles.\n\nExample:\n\n```bash\ncurl \"http://127.0.0.1:8000/articles?region=domestic\u0026since_hours=24\u0026limit=20\"\n```\n\n### `GET /digest/daily`\n\nReturn the aggregated digest view. This route is read-only by default. If `use_llm=true` is added, it will try to generate a digest through the currently configured LLM.\n\nExample:\n\n```bash\ncurl \"http://127.0.0.1:8000/digest/daily?region=all\u0026since_hours=24\u0026limit=30\"\n```\n\n### `GET /admin/stats`\n\nReturn article, enrichment, digest archive, and LLM configuration statistics.\n\n### `POST /admin/enrich`\n\nBatch-translate international stories.\n\n```bash\ncurl -X POST \"http://127.0.0.1:8000/admin/enrich\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Admin-Token: your-secret-token\" \\\n  -d '{\"since_hours\":48,\"limit\":20}'\n```\n\n### `POST /admin/extract`\n\nBatch-extract article bodies.\n\n```bash\ncurl -X POST \"http://127.0.0.1:8000/admin/extract\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Admin-Token: your-secret-token\" \\\n  -d '{\"since_hours\":48,\"limit\":20}'\n```\n\n### `POST /admin/digests/generate`\n\nGenerate and optionally persist a Chinese daily digest.\n\n```bash\ncurl -X POST \"http://127.0.0.1:8000/admin/digests/generate\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Admin-Token: your-secret-token\" \\\n  -d '{\"region\":\"all\",\"since_hours\":48,\"limit\":20,\"use_llm\":true,\"persist\":true}'\n```\n\n### `POST /admin/digests/preview`\n\nBuild a ranked selection preview with explicit inclusion, suppression, duplicate-secondary, and ranked-out decisions.\n\n### `POST /admin/digests/snapshot`\n\nFreeze the current preview into a stored editable digest draft. Optional `editor_items` can override selection, manual rank, section title, publish title, and publish summary.\n\n### `PATCH /admin/digests/{digest_id}/editor`\n\nUpdate a frozen digest draft in place. Publishing with `digest_id` uses this stored snapshot so the outbound digest matches the reviewed editor state instead of a live recompute.\n\n### `PATCH /admin/articles/{id}`\n\nApply manual curation such as hide, pin, or editorial note.\n\n### `POST /admin/pipeline`\n\nRun ingest, extraction, enrichment, digest generation, export, and optionally publication in one call.\n\n### `POST /admin/publish`\n\nBuild or load a digest and publish it to configured targets.\n\n```bash\ncurl -X POST \"http://127.0.0.1:8000/admin/publish\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Admin-Token: your-secret-token\" \\\n  -d '{\"targets\":[\"static_site\",\"telegram\"],\"use_llm\":true,\"persist\":true,\"export\":true,\"force_republish\":false}'\n```\n\n### `GET /admin/publications`\n\nView recent publication records, including target platform, status, external ID, and response summary.\n\nOptional query parameters:\n\n- `digest_id`\n- `target`\n- `status`\n\n### `POST /admin/publications/refresh`\n\nRefresh publication state for platforms that support polling. This is currently used mainly for WeChat `freepublish/get`.\n\n```bash\ncurl -X POST \"http://127.0.0.1:8000/admin/publications/refresh\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Admin-Token: your-secret-token\" \\\n  -d '{\"target\":\"wechat\",\"limit\":20,\"only_pending\":true}'\n```\n\n## Configuration\n\nSee `.env.example` for a concrete sample.\n\n- `AINEWS_DATABASE_URL`: SQLite database location\n- `AINEWS_SOURCES_FILE`: source registry file\n- `AINEWS_HOME`: working directory root, defaults to the current command directory\n- `AINEWS_OUTPUT_DIR`: exported digest directory\n- `AINEWS_STATIC_SITE_DIR`: static site output directory\n- `AINEWS_STATIC_SITE_BASE_URL`: optional external base URL for the static site\n- `AINEWS_REQUEST_TIMEOUT`: fetch timeout in seconds\n- `AINEWS_DEFAULT_LOOKBACK_HOURS`: default lookback window\n- `AINEWS_MAX_ARTICLES_PER_SOURCE`: default per-source ingest cap\n- `AINEWS_ALLOWED_ORIGINS`: API CORS allowlist\n- `AINEWS_ADMIN_TOKEN`: optional admin API token\n- `AINEWS_LOG_LEVEL`: log level, typically `INFO` or `DEBUG`\n- `AINEWS_LOG_FORMAT`: `text` or `json`\n- `AINEWS_EXTRACTION_TEXT_LIMIT`: maximum number of locally stored characters per extracted article\n- `AINEWS_LLM_ARTICLE_CONTEXT_CHARS`: maximum number of article-body characters sent to the LLM\n- `AINEWS_LLM_PROVIDER`: defaults to `openai_compatible`\n- `AINEWS_LLM_BASE_URL`: LLM base URL\n- `AINEWS_LLM_API_KEY`: LLM API key\n- `AINEWS_LLM_MODEL`: LLM model name\n- `AINEWS_LLM_TIMEOUT`: LLM timeout\n- `AINEWS_LLM_TEMPERATURE`: temperature for translation and digest generation\n- `AINEWS_LLM_DIGEST_MAX_ARTICLES`: maximum number of articles used for digest generation\n- `AINEWS_PUBLISH_TARGETS`: default publish targets, comma-separated, for example `telegram,static_site`\n- `AINEWS_TELEGRAM_BOT_TOKEN`: Telegram bot token\n- `AINEWS_TELEGRAM_CHAT_ID`: Telegram chat ID or channel name\n- `AINEWS_TELEGRAM_DISABLE_NOTIFICATION`: Telegram silent delivery toggle\n- `AINEWS_FEISHU_WEBHOOK`: Feishu custom bot webhook\n- `AINEWS_FEISHU_SECRET`: optional Feishu signing secret\n- `AINEWS_FEISHU_MESSAGE_TYPE`: `text` or `card`\n- `AINEWS_WECHAT_ACCESS_TOKEN`: optional fixed WeChat access token\n- `AINEWS_WECHAT_APP_ID`: AppID used for access token retrieval\n- `AINEWS_WECHAT_APP_SECRET`: AppSecret used for access token retrieval\n- `AINEWS_WECHAT_THUMB_MEDIA_ID`: WeChat cover material ID, required for draft creation unless you upload one automatically\n- `AINEWS_WECHAT_THUMB_IMAGE_PATH`: local cover image path, can replace `AINEWS_WECHAT_THUMB_MEDIA_ID`\n- `AINEWS_WECHAT_THUMB_IMAGE_URL`: remote cover image URL, can replace `AINEWS_WECHAT_THUMB_MEDIA_ID`\n- `AINEWS_WECHAT_THUMB_UPLOAD_TYPE`: upload type for the cover, default `thumb`\n- `AINEWS_WECHAT_AUTHOR`: WeChat article author name\n- `AINEWS_WECHAT_CONTENT_SOURCE_URL`: optional \"Read more\" URL in the WeChat article\n- `AINEWS_WECHAT_NEED_OPEN_COMMENT`: whether comments are enabled\n- `AINEWS_WECHAT_ONLY_FANS_CAN_COMMENT`: whether only followers can comment\n- `AINEWS_WECHAT_PUBLISH_AFTER_DRAFT`: whether to submit the draft for publication automatically\n\n## v1.0 Contract Notes\n\n- Exported JSON includes top-level `schema_version`\n- `publish` and `run-pipeline --publish` are idempotent by default on the tuple `(stored digest, target)`\n- Database upgrades follow [Database Migrations](docs/database-migrations.md); the current schema version is `3`\n- Public compatibility guarantees are documented in [Compatibility Contract](docs/compatibility.md)\n\n## How to Keep Improving the Project\n\nThe repository already has a solid open-source skeleton, but if you want to push further toward production use, these are the next four upgrades:\n\n1. Add source health checks, retries, and ingest monitoring.\n2. Add permissions and multi-user editing logs to the admin console.\n3. Add more source-specific extraction rules beyond the current generic DOM strategy.\n4. Add more publishing targets or deeper platform support, such as richer Telegram formatting, automatic inline image upload for WeChat, and more publication polling.\n\nThe repository already includes one scheduled workflow example:\n\n- [daily-digest.yml](.github/workflows/daily-digest.yml)\n\nIt runs `run-pipeline` every day and uploads the generated digest files as workflow artifacts.\n\n## Testing\n\n```bash\npython -m unittest discover -s tests -v\n```\n\n## License\n\nMIT. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx-pg13%2Fainews-open","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fx-pg13%2Fainews-open","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx-pg13%2Fainews-open/lists"}