Initial commit

This commit is contained in:
2026-07-05 17:59:02 +08:00
commit 11ea2ddb9a
18 changed files with 5549 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
.git
.DS_Store
.env
.env.*
!.env.example
__pycache__/
*.pyc
tmp/
logs/
newspaper/*.html
newspaper/*.md
newspaper/*.json
+20
View File
@@ -0,0 +1,20 @@
# Copy this file to .env on the Debian server.
# Do not put real credentials in committed files.
# DeepSeek API settings.
DEEPSEEK_API_KEY=
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-v4-pro
# SMTP delivery settings.
NEWSPAPER_SMTP_HOST=
NEWSPAPER_SMTP_PORT=
NEWSPAPER_SMTP_USERNAME=
NEWSPAPER_SMTP_PASSWORD=
NEWSPAPER_FROM_EMAIL=
NEWSPAPER_FROM_NAME=Hongyu Daily
NEWSPAPER_SMTP_SECURITY=ssl
# Scheduler settings for docker compose.
NEWSPAPER_SCHEDULE_TIME=08:00
TZ=Asia/Shanghai
+9
View File
@@ -0,0 +1,9 @@
.DS_Store
.env
.env.*
!.env.example
__pycache__/
*.pyc
tmp/
logs/
+157
View File
@@ -0,0 +1,157 @@
# Daily News for Markyan04 Editorial Constraints
本仓库用于生成和发送 **Daily News for Markyan04**,一份每天早上的中文个人报纸。
它不是通用科技 newsletter,也不是申请提醒。它应该像一份压缩后的早报:先说明当天真正发生了什么,再补充必要背景,最后把技术、金融和安全信息整理到可长期阅读的结构里。
## 输出契约
每天生成一封邮件。
标题格式:
```text
Daily News for Markyan04 | YYYY - MM - DD
```
正文必须使用中文。常见英文技术词保留英文,例如 `RAG``agent``vibe coding``LLMOps``MCP``eBPF``KYC / AML`
邮件栏目顺序固定为:
1. 今日汇总
2. 传统新闻
3. AI领域
4. 传统软工
5. 安全与性能工程
6. GitHub / 开源项目跟踪
7. 金融领域
目标权重:
- 今日汇总:5%
- 传统新闻:15%
- AI领域:35%
- 传统软工:15%
- 安全与性能工程:12%
- GitHub / 开源项目跟踪:10%
- 金融领域:8%
最终邮件应当信息密度高、选择克制,适合 8-12 分钟读完。
## 写作原则
- 每次生成前必须重新采集来源,不得把模型记忆当成新闻来源。
- 新闻型条目必须有来源 URL,并带有发布时间、更新时间或采集时间。
- 不要重复过去几期已经写过的同一事件;除非出现重大新进展,才可以再次报道,并且要说明新增事实。
- 不要使用“为何重要”“学习启示”“对你来说”“建议关注”这类模板化分段词。
- 标准信息放在标题下方的 metadata 行,例如:`来源:BBC World | 时间:2026-07-05 | 链接:https://...`
- 正文以自然文段写作,优先概括事实本身:谁做了什么、何时发生、涉及哪些地区或机构、当前进展是什么、还有哪些不确定性。
- 可以解释影响,但解释必须服务于理解事件,不要只说“这很重要”而不交代事件内容。
- 不要直接对读者进行个人化说教,不要写“你应该学习”“对你而言”。
- 不要加入每日行动清单、实习就业板块、英语写作练习或申请动态板块。
- 重要论文、技术报告、benchmark 放进 `AI领域`,不单独设研究动态。
- 不追踪用户自己的 GitHub 项目,除非用户明确要求。
## 栏目要求
### 1. 今日汇总
最多 5 条,覆盖当天最值得读的内容。可以使用短 bullet,但要总结具体事件,不要只写意义。
示例:
```text
- 传统新闻:...
- AI...
- 安全:...
- GitHub...
- 金融:...
```
### 2. 传统新闻
覆盖国际政治、地缘冲突、中国与香港政策、金融监管、宏观经济和亚太地区重要事件。
优先主题包括:
- 中美关系、特朗普政府相关政策、访华或外交动态。
- 俄乌战争及其外交、军事、能源、制裁进展。
- 中国内地与香港金融监管,例如证监会、港交所、香港证监会、金管局的重要动作。
- 影响科技、金融和全球供应链的政策新闻。
使用成熟新闻机构和官方监管机构来源。二手媒体报道可以用于发现,但重大政策和监管内容应尽量回到官方来源核验。
### 3. AI领域
这是主栏目,覆盖前沿新闻和工程理解。
扫描范围包括:
- 大模型发布、能力更新和产品变化。
- AI coding tools、coding agents、vibe coding 工作流。
- agent 框架、工具调用、MCP、多 agent 系统、工作流编排。
- AI engineering / LLMOps:评测、可观测性、路由、guardrails、推理、缓存、部署、成本、可靠性。
- RAG、GraphRAG、长上下文、memory、retrieval evaluation。
- 多模态 AI、UI agents、computer-use agents。
- 开源模型与推理基础设施。
- AI securityprompt injection、数据泄露、沙箱、agent 风险。
- 进入工程实践的新词和新概念。
- 高价值论文、benchmark 或技术报告。
每天选择约 5-8 条。优先最近 24-72 小时内的实质更新;可以保留少量背景解释,但必须由近期事件触发。
### 4. 传统软工
关注耐用的软件工程知识和工业级实践:
- BackendSpring Boot、FastAPI、API 设计、auth、服务边界。
- FrontendVue、TypeScript、构建系统、状态管理、测试。
- DatabasePostgreSQL、MySQL、索引、事务、查询计划、缓存。
- DevOpsDocker、CI/CD、Nginx、Linux service、Sentry、observability。
- TestingE2E、contract test、load test、回归策略。
- 事故复盘、生产故障分析、可维护性和技术债。
避免初级教程,除非它解释了常被误解的重要概念。
### 5. 安全与性能工程
覆盖安全漏洞、GFW / 网络测量和系统性能。
安全条目应包括漏洞编号或名称、受影响系统、利用状态、当前处置或缓解方向。不得提供攻击步骤、绕过审查教程或可操作规避指南。
性能条目优先 Linux performance、`perf``eBPF`、FlameGraph、scheduler、memory、IO、networking、Android Perfetto、数据库和后端延迟分析。
### 6. GitHub / 开源项目跟踪
只跟踪外部高价值项目。
优先类别:
- AI agents 和 coding agents。
- LLM infrastructure。
- RAG 与 evaluation tools。
- Developer tools。
- Security tools。
- Performance engineering tools。
- Fintech 相关基础设施。
项目摘要要说明它在做什么、为什么最近被关注、技术栈或架构特征、成熟度。避免低质量 prompt collection、壳包装、营销 demo 和长期无人维护项目。
### 7. 金融领域
这是金融系统和 fintech 素养栏目,不提供个性化投资建议。
优先主题:
- 利率、债券、收益率曲线、ETF、流动性、做市、清算与结算。
- 支付、清算、风控、反欺诈、`KYC / AML`、open banking。
- 稳定币、tokenization、CBDC 和金融基础设施。
- AI in finance:风控模型、欺诈检测、研究助手、文档处理、合规。
- HKMA、MAS、BIS、SEC、Fed、交易所等机构动态。
## 来源和归档
来源规则在 `config/sources.yaml`。投递和归档规则在 `config/delivery.yaml`
发送前必须写入 `newspaper/` 下的 Markdown、HTML 和 metadata JSON。不要把 SMTP 授权码、DeepSeek API key、cookie 或 session token 写入仓库。
+33
View File
@@ -0,0 +1,33 @@
FROM python:3.12-slim
ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG ALL_PROXY
ARG NO_PROXY
ARG http_proxy
ARG https_proxy
ARG all_proxy
ARG no_proxy
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
TZ=Asia/Shanghai \
NEWSPAPER_ROOT=/app
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates tzdata \
&& rm -rf /var/lib/apt/lists/*
COPY pyproject.toml ./
COPY AGENTS.md ./
COPY config ./config
COPY src ./src
COPY newspaper ./newspaper
RUN pip install --no-cache-dir .
VOLUME ["/app/newspaper"]
CMD ["hongyu-daily", "run"]
+130
View File
@@ -0,0 +1,130 @@
# Daily News for Markyan04
这是一个中文个人日报系统。它会每天早上生成一份报纸,归档到 `newspaper/` 目录,并通过邮件发送到:
```text
markyan04@163.com
```
生产环境目标是:**个人 Debian 服务器 + Docker Compose + DeepSeek API + SMTP 邮件发送**。
默认执行时间:
```text
每天 08:00Asia/Shanghai 时区
```
## 目录说明
- `AGENTS.md`:日报编辑约束,主要给 agent / 生成器读取。
- `config/newspaper.yaml`:日报结构、栏目顺序、栏目权重。
- `config/sources.yaml`:新闻来源、来源分层、新闻质量规则。
- `config/delivery.yaml`:收件人、归档、SMTP、定时任务配置。
- `.env.example`:服务器上的 `.env` 配置模板。
- `docker-compose.yml`:一次性运行和每日定时运行的 Docker 服务。
- `docs/debian-docker-deployment.md`:完整 Debian 部署说明。
- `newspaper/`:每天生成并发送的报纸归档目录。
## Debian 快速部署
把仓库复制到服务器,例如:
```bash
sudo mkdir -p /srv/apps/newspaper
sudo chown -R "$USER":"$USER" /srv/apps/newspaper
rsync -av --exclude .env ./ /srv/apps/newspaper/
cd /srv/apps/newspaper
```
创建 `.env`
```bash
cp .env.example .env
nano .env
```
填写必要配置:
```text
DEEPSEEK_API_KEY=
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-v4-pro
GITHUB_TOKEN=
NEWSPAPER_SMTP_HOST=smtp.163.com
NEWSPAPER_SMTP_PORT=465
NEWSPAPER_SMTP_USERNAME=
NEWSPAPER_SMTP_PASSWORD=
NEWSPAPER_FROM_EMAIL=
NEWSPAPER_FROM_NAME=Daily News for Markyan04
NEWSPAPER_SMTP_SECURITY=ssl
NEWSPAPER_SCHEDULE_TIME=08:00
TZ=Asia/Shanghai
```
说明:
- `DEEPSEEK_API_KEY` 填你的 DeepSeek API Key。
- `GITHUB_TOKEN` 可不填;填一个只读 token 可以提高 GitHub Search API 限额,让开源项目跟踪更稳定。
- 如果用 163 邮箱发件,`NEWSPAPER_SMTP_PASSWORD` 应该填邮箱授权码,不是网页登录密码。
- `.env` 里有密钥,不能提交到仓库。
## 构建和检查
```bash
docker compose build
docker compose run --rm newspaper hongyu-daily check-config
```
## 只生成,不发送
```bash
docker compose run --rm newspaper hongyu-daily generate
```
生成后检查:
```bash
ls -lh newspaper/
```
应该看到类似:
```text
YYYY-MM-DD-daily-news-for-markyan04.md
YYYY-MM-DD-daily-news-for-markyan04.html
YYYY-MM-DD-daily-news-for-markyan04.meta.json
```
## 生成并立即发送一次
```bash
docker compose run --rm newspaper hongyu-daily run
```
## 启动每日定时任务
```bash
docker compose up -d scheduler
docker compose logs -f scheduler
```
启动后,`scheduler` 服务会常驻运行,并在每天 08:00 生成和发送日报。
## 停止定时任务
```bash
docker compose down
```
## 密钥注意事项
不要提交这些内容:
- `.env`
- SMTP 授权码
- DeepSeek API Key
- Cookie
- Session token
- 任何邮箱密码或平台密钥
+100
View File
@@ -0,0 +1,100 @@
version: 1
name: Daily News for Markyan04 Delivery Configuration
timezone: Asia/Shanghai
recipient:
email: markyan04@163.com
display_name: Hongyu Yan
archive:
directory: newspaper
write_before_send: true
mark_sent_after_success: true
formats:
- markdown
- html
- metadata_json
naming:
markdown: "{YYYY}-{MM}-{DD}-daily-news-for-markyan04.md"
html: "{YYYY}-{MM}-{DD}-daily-news-for-markyan04.html"
metadata_json: "{YYYY}-{MM}-{DD}-daily-news-for-markyan04.meta.json"
metadata_fields:
- date
- subject
- recipient
- generated_at
- sent_at
- delivery_status
- source_item_count
- filtered_recent_source_item_count
- source_error_count
- source_errors
- source_items
- content_hash
email:
transport: smtp
subject_format_ref: config/newspaper.yaml:title_format
body:
primary_format: html
include_plain_text_fallback: true
smtp:
host_env: NEWSPAPER_SMTP_HOST
port_env: NEWSPAPER_SMTP_PORT
username_env: NEWSPAPER_SMTP_USERNAME
password_env: NEWSPAPER_SMTP_PASSWORD
from_email_env: NEWSPAPER_FROM_EMAIL
from_name_env: NEWSPAPER_FROM_NAME
security_env: NEWSPAPER_SMTP_SECURITY
supported_security:
- ssl
- starttls
timeout_seconds: 30
secrets_policy:
never_commit_credentials: true
require_env_vars: true
allow_dotenv_for_local_dev: true
dotenv_file: .env
dotenv_should_be_gitignored: true
delivery_policy:
send_only_after_successful_generation: true
do_not_send_if_required_sections_missing: true
do_not_send_if_news_items_lack_sources: true
max_send_attempts: 3
retry_backoff_seconds:
- 60
- 300
failure_behavior:
- keep_archived_draft
- write_failure_metadata
- do_not_mark_sent
automation:
preferred_time_local: "08:00"
preferred_runner: docker_compose_scheduler
timezone: Asia/Shanghai
docker_compose_scheduler:
schedule_local: "08:00"
note: "Run the scheduler service on the Debian server with docker compose. It loops and runs once per local day."
required_secrets:
- DEEPSEEK_API_KEY
- DEEPSEEK_BASE_URL
- DEEPSEEK_MODEL
- NEWSPAPER_SMTP_HOST
- NEWSPAPER_SMTP_PORT
- NEWSPAPER_SMTP_USERNAME
- NEWSPAPER_SMTP_PASSWORD
- NEWSPAPER_FROM_EMAIL
- NEWSPAPER_FROM_NAME
- NEWSPAPER_SMTP_SECURITY
debian_host_cron:
cron_local: "0 8 * * *"
command: "cd /srv/apps/newspaper && docker compose run --rm newspaper hongyu-daily run"
note: "Use this instead of the scheduler service if you prefer host-level cron."
sender_notes:
- "The recipient is fixed as markyan04@163.com."
- "The sender account is intentionally not fixed here. Use any SMTP-capable mailbox or transactional email provider."
- "If a 163 mailbox is used as the sender, enable SMTP/IMAP service in the mailbox settings and use an authorization code rather than the normal login password."
- "Keep provider-specific SMTP host and port in environment variables, not in committed code."
+211
View File
@@ -0,0 +1,211 @@
title_format: "Daily News for Markyan04 | YYYY - MM - DD"
language: zh-CN
timezone: Asia/Shanghai
target_reading_minutes: 8-12
sources_config: config/sources.yaml
delivery_config: config/delivery.yaml
archive_dir: newspaper
output_language_policy:
newspaper_body: Chinese
preserve_common_english_terms: true
examples:
- RAG
- agent
- vibe coding
- LLMOps
- MCP
- eBPF
- KYC / AML
freshness_policy:
daily_collection_required: true
do_not_use_model_memory_as_news_source: true
news_items_require_source_url: true
news_items_require_date_or_collected_at: true
default_recent_window_hours: 72
prefer_recent_window_hours: 24
allow_evergreen_background_when_labeled: true
no_fresh_item_behavior: state_no_high_signal_update
sections:
summary:
weight: 0.05
title: 今日汇总
general_news:
weight: 0.15
title: 传统新闻
ai:
weight: 0.35
title: AI领域
software_engineering:
weight: 0.15
title: 传统软工
security_performance:
weight: 0.12
title: 安全与性能工程
github:
weight: 0.10
title: GitHub / 开源项目跟踪
finance:
weight: 0.08
title: 金融领域
general_news:
target_items_per_issue:
min: 3
max: 5
priority_topics:
- international_politics
- us_china_relations
- china_hong_kong_policy
- russia_ukraine_war
- asia_pacific
- macroeconomy
- market_regulation
- financial_regulation
style:
- summarize_events_before_interpretation
- prefer_primary_or_established_newsroom_sources
- include_source_metadata_under_title
ai:
discovery_map_not_daily_quota: true
target_items_per_issue:
min: 5
max: 8
freshness_rules:
min_recent_items_when_available: 4
recent_window_hours: 72
max_evergreen_or_background_items: 2
include_fresh_discovery_slot: true
do_not_repackage_old_concepts_as_today_news: true
priority_topics:
- large_language_models
- ai_coding_tools
- coding_agents
- vibe_coding
- agent_frameworks
- model_context_protocol
- multi_agent_systems
- llmops
- evaluation
- evaluation_harness
- agent_harness
- context_engineering
- observability
- guardrails
- inference_optimization
- rag
- graphrag
- long_context
- memory_systems
- multimodal_ai
- open_source_models
- ai_security
- new_ai_terms
- high_value_papers_and_technical_reports
item_template:
- what_happened
- context
- concrete_update
- possible_impact
- source
- published_or_collected_at
software_engineering:
priority_topics:
- backend_architecture
- spring_boot
- fastapi
- api_design
- frontend_engineering
- vue
- typescript
- databases
- postgresql
- mysql
- caching
- docker
- ci_cd
- observability
- testing
- incident_reviews
- maintainability
security_performance:
priority_topics:
- cve
- nvd
- cisa_kev
- vendor_advisories
- exploited_in_the_wild
- supply_chain_security
- gfw_measurement
- network_censorship_research
- linux_performance
- ebpf
- perf
- flamegraph
- android_performance
- systrace
- perfetto
- database_performance
safety_constraints:
- do_not_provide_censorship_evasion_tutorials
- do_not_provide_operational_bypass_guidance
github:
track_user_projects: false
priority_topics:
- ai_agents
- coding_agents
- llm_infrastructure
- rag_tools
- evaluation_tools
- developer_tools
- security_tools
- performance_tools
- fintech_infrastructure
selection_criteria:
- recent_star_growth
- active_commits_or_releases
- practical_use_case
- technical_learning_value
exclude:
- low_quality_prompt_collections
- thin_wrappers
- marketing_demos
- abandoned_repositories
finance:
not_investment_advice: true
priority_topics:
- interest_rates
- bonds
- yield_curve
- etf
- liquidity
- market_makers
- clearing
- settlement
- payments
- risk_control
- anti_fraud
- kyc_aml
- open_banking
- stablecoins
- tokenization
- ai_in_finance
- financial_regulation
negative_constraints:
- no_daily_action_checklist
- no_internship_or_job_section
- no_english_writing_section
- no_separate_research_section
- no_phd_or_mphil_content
- no_supervisor_matching
- no_cold_email_content
- no_user_project_github_tracking
- no_personalized_investment_advice
+875
View File
@@ -0,0 +1,875 @@
version: 1
name: Daily News for Markyan04 Source Configuration
language: zh-CN
timezone: Asia/Shanghai
purpose:
- Provide stable source rules for Daily News for Markyan04.
- Separate timely news from evergreen explanation.
- Make every newspaper item answer the basic journalistic questions when applicable.
- Prefer primary sources and fresh collection over model memory.
journalism_contract:
news_questions:
who: "谁发布、谁受影响、谁参与"
what: "发生了什么具体变化"
when: "发布日期、更新时间、事件发生时间或采集时间"
where: "发生在哪个机构、产品、地区、生态或仓库"
why: "事件的背景、影响范围和后续观察点"
how: "证据、机制、技术路径、影响方式或复现条件"
required_fields_for_news_like_items:
- title
- content_class
- section
- source_name
- source_url
- source_tier
- published_at_or_updated_at
- collected_at
- who
- what
- when
- where
- context_and_impact
- how_or_evidence
required_fields_for_background_items:
- title
- content_class
- section
- trigger_reason
- source_name
- source_url
- explanation
- relation_to_section_topic
background_items:
allowed: true
max_per_issue: 3
rule: "Background explanations are allowed only when they help understand a fresh item or a recurring important concept."
label: "背景理解"
content_classes:
breaking_news:
time_sensitive: true
freshness_window_hours: 24
requires_news_questions: true
official_release:
time_sensitive: true
freshness_window_hours: 72
requires_news_questions: true
technical_report:
time_sensitive: true
freshness_window_hours: 168
requires_news_questions: true
paper:
time_sensitive: true
freshness_window_hours: 168
requires_news_questions: true
belongs_under_section: ai
vulnerability:
time_sensitive: true
freshness_window_hours: 72
requires_news_questions: true
github_project_signal:
time_sensitive: true
freshness_window_hours: 72
requires_growth_signal: true
requires_news_questions: true
explainer:
time_sensitive: false
requires_trigger_reason: true
requires_news_questions: false
concept_note:
time_sensitive: false
requires_trigger_reason: true
requires_news_questions: false
source_tiers:
official_primary:
description: "Official pages, official blogs, official docs, official APIs, university pages, regulator pages."
default_weight: 1.0
technical_primary:
description: "Project repos, release notes, engineering blogs, model cards, arXiv papers, benchmark pages."
default_weight: 0.9
reputable_secondary:
description: "High-quality technical newsletters, established security research, respected independent experts."
default_weight: 0.75
community_signal:
description: "GitHub Trending, Hacker News, social discovery pages. Use for discovery, then verify with primary source."
default_weight: 0.55
background_reference:
description: "Evergreen docs and explainers. Use only as background, not as today's news."
default_weight: 0.4
collection_policy:
daily_collection_required: true
prefer_recent_window_hours: 24
default_recent_window_hours: 72
collect_before_generate: true
no_model_memory_as_news_source: true
store_raw_items: true
store_normalized_items: true
store_snapshots_for_change_only_sources: true
deduplicate_by:
- canonical_url
- normalized_title
- source_item_id
if_source_unavailable:
- retry_once
- use_fallback_url_if_configured
- mark_source_error
- do_not_invent_item
if_no_fresh_high_signal_item:
behavior: "state_no_high_signal_update"
text: "今日未发现高信号更新。"
ranking_policy:
score_fields:
source_tier: 0.25
freshness: 0.20
relevance_to_sections: 0.25
novelty: 0.15
technical_depth: 0.10
practical_value: 0.05
demote:
- no_source_url
- no_publication_or_collection_time
- repost_without_primary_source
- generic_commentary
- marketing_without_technical_detail
- low_quality_ai_listicle
promote:
- primary_source
- official_release_with_technical_detail
- useful_engineering_lessons
- affects_ai_engineering_workflow
- affects_general_news_or_financial_regulation
- active_exploitation_or_urgent_patch
- fast_github_growth_with_real_utility
sections:
summary:
source_strategy: "Use selected high-score items from all sections."
max_items: 5
general_news:
source_strategy: "Use established newsrooms plus official regulators for international politics, China/Hong Kong policy, war, and market regulation."
recent_window_hours: 72
target_items_per_issue:
min: 3
max: 5
allowed_item_classes:
- breaking_news
- official_release
- technical_report
priority_topics:
- us_china_relations
- russia_ukraine_war
- china_hong_kong_policy
- financial_regulation
- macroeconomy
- asia_pacific
sources:
- id: bbc_world
name: "BBC World"
kind: rss
source_tier: reputable_secondary
url: "https://www.bbc.com/news/world"
feed_url: "https://feeds.bbci.co.uk/news/world/rss.xml"
access_status: seed
tags: [general_news, world, politics]
- id: bbc_business
name: "BBC Business"
kind: rss
source_tier: reputable_secondary
url: "https://www.bbc.com/news/business"
feed_url: "https://feeds.bbci.co.uk/news/business/rss.xml"
access_status: seed
tags: [general_news, business, economy]
- id: guardian_world
name: "The Guardian World"
kind: rss
source_tier: reputable_secondary
url: "https://www.theguardian.com/world"
feed_url: "https://www.theguardian.com/world/rss"
access_status: seed
tags: [general_news, world, politics]
- id: guardian_us_news
name: "The Guardian US News"
kind: rss
source_tier: reputable_secondary
url: "https://www.theguardian.com/us-news"
feed_url: "https://www.theguardian.com/us-news/rss"
access_status: seed
tags: [general_news, US, politics, Trump]
- id: aljazeera_all
name: "Al Jazeera"
kind: rss
source_tier: reputable_secondary
url: "https://www.aljazeera.com/"
feed_url: "https://www.aljazeera.com/xml/rss/all.xml"
access_status: seed
tags: [general_news, world, war, middle_east]
- id: nytimes_world
name: "The New York Times World"
kind: rss
source_tier: reputable_secondary
url: "https://www.nytimes.com/section/world"
feed_url: "https://rss.nytimes.com/services/xml/rss/nyt/World.xml"
access_status: seed
tags: [general_news, world, politics]
- id: nytimes_asia_pacific
name: "The New York Times Asia Pacific"
kind: rss
source_tier: reputable_secondary
url: "https://www.nytimes.com/section/world/asia"
feed_url: "https://rss.nytimes.com/services/xml/rss/nyt/AsiaPacific.xml"
access_status: seed
tags: [general_news, Asia, China, Hong_Kong]
- id: npr_world
name: "NPR World"
kind: rss
source_tier: reputable_secondary
url: "https://www.npr.org/sections/world/"
feed_url: "https://feeds.npr.org/1004/rss.xml"
access_status: seed
tags: [general_news, world, politics]
- id: un_news
name: "UN News"
kind: rss
source_tier: official_primary
url: "https://news.un.org/en/"
feed_url: "https://news.un.org/feed/subscribe/en/news/all/rss.xml"
access_status: seed
tags: [general_news, UN, war, humanitarian]
- id: google_news_trump_china_visit
name: "Google News Search - Trump China Visit"
kind: rss
source_tier: community_signal
url: "https://news.google.com/search?q=Trump%20China%20visit"
feed_url: "https://news.google.com/rss/search?q=Trump%20China%20visit&hl=zh-CN&gl=US&ceid=US:zh-Hans"
access_status: seed
tags: [general_news, US_China, Trump, discovery]
- id: google_news_russia_ukraine
name: "Google News Search - Russia Ukraine War"
kind: rss
source_tier: community_signal
url: "https://news.google.com/search?q=Russia%20Ukraine%20war"
feed_url: "https://news.google.com/rss/search?q=Russia%20Ukraine%20war&hl=zh-CN&gl=US&ceid=US:zh-Hans"
access_status: seed
tags: [general_news, Russia, Ukraine, war, discovery]
- id: google_news_csrc_hk_brokers
name: "Google News Search - CSRC Hong Kong Brokers"
kind: rss
source_tier: community_signal
url: "https://news.google.com/search?q=CSRC%20Hong%20Kong%20brokers"
feed_url: "https://news.google.com/rss/search?q=CSRC%20Hong%20Kong%20brokers%20OR%20%E4%B8%AD%E5%9B%BD%E8%AF%81%E7%9B%91%E4%BC%9A%20%E9%A6%99%E6%B8%AF%E5%88%B8%E5%95%86&hl=zh-CN&gl=HK&ceid=HK:zh-Hans"
access_status: seed
tags: [general_news, China, Hong_Kong, securities, regulation, discovery]
- id: china_csrc_news
name: "China Securities Regulatory Commission"
kind: official_html
source_tier: official_primary
url: "http://www.csrc.gov.cn/csrc/c100028/common_list.shtml"
access_status: seed
tags: [general_news, China, securities, regulation]
- id: hong_kong_sfc_news
name: "Hong Kong SFC News"
kind: official_html
source_tier: official_primary
url: "https://www.sfc.hk/en/News-and-announcements/News"
access_status: seed
tags: [general_news, Hong_Kong, securities, regulation]
- id: china_mfa_news
name: "China MFA News"
kind: official_html
source_tier: official_primary
url: "https://www.mfa.gov.cn/eng/xw/"
access_status: seed
tags: [general_news, China, diplomacy, US_China]
ai:
source_strategy: "Broad fresh scan, then selective reporting. Topic list is a discovery map, not a quota."
target_items_per_issue:
min: 5
max: 8
min_recent_items_when_available: 4
recent_window_hours: 72
max_background_items: 2
include_fresh_discovery_slot: true
allowed_item_classes:
- breaking_news
- official_release
- technical_report
- paper
- github_project_signal
- explainer
- concept_note
sources:
- id: openai_news
name: "OpenAI News"
kind: rss
source_tier: official_primary
url: "https://openai.com/news/"
feed_url: "https://openai.com/news/rss.xml"
access_status: verified_2026_07_04
tags: [AI, LLM, agents, models, product]
- id: anthropic_news
name: "Anthropic News"
kind: official_html
source_tier: official_primary
url: "https://www.anthropic.com/news"
access_status: verified_2026_07_04
tags: [AI, LLM, Claude, agents, safety]
- id: google_deepmind_blog
name: "Google DeepMind Blog"
kind: official_html
source_tier: official_primary
url: "https://deepmind.google/discover/blog/"
access_status: seed
tags: [AI, LLM, multimodal, research, agents]
- id: google_ai_blog
name: "Google AI Blog"
kind: official_html
source_tier: official_primary
url: "https://blog.google/technology/ai/"
access_status: seed
tags: [AI, LLM, Gemini, product, engineering]
- id: meta_ai_blog
name: "Meta AI Blog"
kind: official_html
source_tier: official_primary
url: "https://ai.meta.com/blog/"
access_status: seed
tags: [AI, Llama, open_source_models, multimodal]
- id: microsoft_ai_blog
name: "Microsoft AI Blog"
kind: official_html
source_tier: official_primary
url: "https://blogs.microsoft.com/ai/"
access_status: seed
tags: [AI, Copilot, agents, enterprise_ai]
- id: microsoft_research_blog
name: "Microsoft Research Blog"
kind: rss
source_tier: technical_primary
url: "https://www.microsoft.com/en-us/research/blog/"
feed_url: "https://www.microsoft.com/en-us/research/feed/"
access_status: seed
tags: [AI, research, agents, systems, engineering]
- id: mistral_news
name: "Mistral AI News"
kind: official_html
source_tier: official_primary
url: "https://mistral.ai/news/"
access_status: seed
tags: [AI, LLM, open_models, agents]
- id: cohere_blog
name: "Cohere Blog"
kind: official_html
source_tier: official_primary
url: "https://cohere.com/blog"
access_status: seed
tags: [AI, LLM, enterprise_ai, RAG]
- id: qwen_blog
name: "Qwen Blog"
kind: official_html
source_tier: official_primary
url: "https://qwenlm.github.io/blog/"
access_status: seed
tags: [AI, LLM, open_source_models, multimodal]
- id: huggingface_blog
name: "Hugging Face Blog"
kind: rss_or_html
source_tier: technical_primary
url: "https://huggingface.co/blog"
feed_url: "https://huggingface.co/blog/feed.xml"
access_status: slow_or_unverified_2026_07_04
tags: [AI, open_source_models, datasets, inference, tooling]
- id: aws_ml_blog
name: "AWS Machine Learning Blog"
kind: rss
source_tier: technical_primary
url: "https://aws.amazon.com/blogs/machine-learning/"
feed_url: "https://aws.amazon.com/blogs/machine-learning/feed/"
access_status: seed
tags: [AI, LLMOps, deployment, inference, cloud]
- id: nvidia_ai_blog
name: "NVIDIA Technical Blog - Generative AI"
kind: rss
source_tier: technical_primary
url: "https://developer.nvidia.com/blog/category/generative-ai/"
feed_url: "https://developer.nvidia.com/blog/category/generative-ai/feed/"
access_status: seed
tags: [AI, inference, GPU, deployment, multimodal]
- id: langchain_blog
name: "LangChain Blog"
kind: rss_or_html
source_tier: technical_primary
url: "https://blog.langchain.com/"
feed_url: "https://blog.langchain.com/rss/"
access_status: seed
tags: [AI, agents, RAG, LLMOps, evaluation]
- id: llamaindex_blog
name: "LlamaIndex Blog"
kind: official_html
source_tier: technical_primary
url: "https://www.llamaindex.ai/blog"
access_status: seed
tags: [AI, RAG, agents, data_frameworks]
- id: vllm_blog
name: "vLLM Blog"
kind: official_html
source_tier: technical_primary
url: "https://blog.vllm.ai/"
access_status: seed
tags: [AI, inference, serving, open_source_models]
- id: ollama_blog
name: "Ollama Blog"
kind: official_html
source_tier: technical_primary
url: "https://ollama.com/blog"
access_status: seed
tags: [AI, local_models, developer_tools, open_source_models]
- id: vercel_ai_blog
name: "Vercel AI Blog"
kind: official_html
source_tier: technical_primary
url: "https://vercel.com/blog/category/ai"
access_status: seed
tags: [AI, frontend_ai, ai_sdk, agents, engineering]
- id: cursor_changelog
name: "Cursor Changelog"
kind: official_html
source_tier: technical_primary
url: "https://cursor.com/changelog"
access_status: seed
tags: [AI, coding_agent, vibe_coding, developer_tools]
- id: arxiv_cs_cl
name: "arXiv cs.CL"
kind: rss
source_tier: technical_primary
url: "https://arxiv.org/list/cs.CL/recent"
feed_url: "https://export.arxiv.org/rss/cs.CL"
access_status: seed
tags: [AI, NLP, LLM, papers]
- id: arxiv_cs_ai
name: "arXiv cs.AI"
kind: rss
source_tier: technical_primary
url: "https://arxiv.org/list/cs.AI/recent"
feed_url: "https://export.arxiv.org/rss/cs.AI"
access_status: seed
tags: [AI, agents, reasoning, papers]
- id: arxiv_cs_lg
name: "arXiv cs.LG"
kind: rss
source_tier: technical_primary
url: "https://arxiv.org/list/cs.LG/recent"
feed_url: "https://export.arxiv.org/rss/cs.LG"
access_status: seed
tags: [AI, machine_learning, papers]
- id: simon_willison
name: "Simon Willison"
kind: atom
source_tier: reputable_secondary
url: "https://simonwillison.net/"
feed_url: "https://simonwillison.net/atom/everything/"
access_status: verified_2026_07_04
tags: [AI, LLM, tools, agents, engineering]
- id: latent_space
name: "Latent Space"
kind: rss
source_tier: reputable_secondary
url: "https://www.latent.space/"
feed_url: "https://www.latent.space/feed"
access_status: seed
tags: [AI, LLM, agents, industry_analysis]
- id: deeplearning_ai_the_batch
name: "The Batch by DeepLearning.AI"
kind: rss_or_html
source_tier: reputable_secondary
url: "https://www.deeplearning.ai/the-batch/"
access_status: seed
tags: [AI, weekly_news, explainers]
software_engineering:
source_strategy: "Prefer engineering blogs, release notes, incident reviews, and deep technical writeups."
recent_window_hours: 168
allowed_item_classes:
- official_release
- technical_report
- explainer
- concept_note
sources:
- id: github_engineering
name: "GitHub Engineering"
kind: rss
source_tier: technical_primary
url: "https://github.blog/engineering/"
feed_url: "https://github.blog/engineering/feed/"
access_status: seed
tags: [software_engineering, developer_tools, scaling, devops]
- id: cloudflare_blog
name: "Cloudflare Blog"
kind: rss
source_tier: technical_primary
url: "https://blog.cloudflare.com/"
feed_url: "https://blog.cloudflare.com/rss/"
access_status: verified_2026_07_04
tags: [software_engineering, networking, security, performance]
- id: netflix_techblog
name: "Netflix TechBlog"
kind: rss
source_tier: technical_primary
url: "https://netflixtechblog.com/"
feed_url: "https://netflixtechblog.com/feed"
access_status: seed
tags: [software_engineering, distributed_systems, reliability]
- id: uber_engineering
name: "Uber Engineering"
kind: rss_or_html
source_tier: technical_primary
url: "https://www.uber.com/blog/engineering/"
access_status: seed
tags: [software_engineering, backend, data, platform]
- id: stripe_engineering
name: "Stripe Engineering"
kind: rss_or_html
source_tier: technical_primary
url: "https://stripe.com/blog/engineering"
access_status: seed
tags: [software_engineering, fintech, infrastructure, reliability]
- id: martin_fowler
name: "Martin Fowler"
kind: atom
source_tier: reputable_secondary
url: "https://martinfowler.com/"
feed_url: "https://martinfowler.com/feed.atom"
access_status: seed
tags: [software_design, architecture, refactoring]
- id: acm_queue
name: "ACM Queue"
kind: rss
source_tier: reputable_secondary
url: "https://queue.acm.org/"
feed_url: "https://queue.acm.org/rss/feeds/queuecontent.xml"
access_status: seed
tags: [software_engineering, systems, architecture]
- id: spring_blog
name: "Spring Blog"
kind: atom
source_tier: technical_primary
url: "https://spring.io/blog"
feed_url: "https://spring.io/blog.atom"
access_status: seed
tags: [software_engineering, Java, Spring_Boot, backend]
- id: vue_blog
name: "Vue Blog"
kind: rss_or_html
source_tier: technical_primary
url: "https://blog.vuejs.org/"
feed_url: "https://blog.vuejs.org/feed.rss"
access_status: seed
tags: [software_engineering, Vue, frontend, TypeScript]
- id: postgresql_news
name: "PostgreSQL News"
kind: rss
source_tier: official_primary
url: "https://www.postgresql.org/about/news/"
feed_url: "https://www.postgresql.org/about/news/rss/"
access_status: seed
tags: [software_engineering, database, PostgreSQL]
security_performance:
source_strategy: "Prioritize official advisories for urgent risk and deep technical sources for learning."
recent_window_hours: 72
allowed_item_classes:
- vulnerability
- breaking_news
- official_release
- technical_report
- explainer
- concept_note
safety_constraints:
- do_not_provide_censorship_evasion_tutorials
- do_not_provide_operational_bypass_guidance
sources:
- id: cisa_kev
name: "CISA Known Exploited Vulnerabilities Catalog"
kind: json
source_tier: official_primary
url: "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
access_status: verified_2026_07_04
tags: [security, CVE, exploited_in_the_wild, urgent_patch]
- id: cisa_advisories
name: "CISA Cybersecurity Advisories"
kind: rss_or_html
source_tier: official_primary
url: "https://www.cisa.gov/news-events/cybersecurity-advisories"
feed_url: "https://www.cisa.gov/cybersecurity-advisories/all.xml"
access_status: seed
tags: [security, advisories, vulnerability]
- id: nvd_cve_api
name: "NVD CVE API"
kind: api
source_tier: official_primary
url: "https://services.nvd.nist.gov/rest/json/cves/2.0"
access_status: seed
tags: [security, CVE, vulnerability]
- id: github_advisory_api
name: "GitHub Security Advisory API"
kind: api
source_tier: official_primary
url: "https://api.github.com/advisories"
access_status: seed
tags: [security, supply_chain, dependencies]
- id: google_project_zero
name: "Google Project Zero"
kind: rss
source_tier: technical_primary
url: "https://googleprojectzero.blogspot.com/"
feed_url: "https://googleprojectzero.blogspot.com/feeds/posts/default"
access_status: seed
tags: [security, vulnerability_research, browser, OS]
- id: trail_of_bits_blog
name: "Trail of Bits Blog"
kind: rss
source_tier: technical_primary
url: "https://blog.trailofbits.com/"
feed_url: "https://blog.trailofbits.com/feed/"
access_status: seed
tags: [security, supply_chain, code_audit, vulnerability_research]
- id: portswigger_research
name: "PortSwigger Research"
kind: rss_or_html
source_tier: technical_primary
url: "https://portswigger.net/research"
feed_url: "https://portswigger.net/research/rss"
access_status: seed
tags: [security, web_security, vulnerability_research]
- id: ooni_blog
name: "OONI Blog"
kind: rss_or_html
source_tier: technical_primary
url: "https://ooni.org/blog/"
feed_url: "https://ooni.org/index.xml"
access_status: seed
tags: [GFW, network_measurement, censorship_research]
- id: citizen_lab
name: "Citizen Lab"
kind: rss
source_tier: reputable_secondary
url: "https://citizenlab.ca/"
feed_url: "https://citizenlab.ca/feed/"
access_status: seed
tags: [GFW, censorship_research, surveillance, security]
- id: censored_planet
name: "Censored Planet Publications"
kind: official_html
source_tier: technical_primary
url: "https://censoredplanet.org/publications"
access_status: seed
tags: [GFW, network_measurement, censorship_research]
- id: lwn_headlines
name: "LWN Headlines"
kind: rss
source_tier: reputable_secondary
url: "https://lwn.net/"
feed_url: "https://lwn.net/headlines/rss"
access_status: verified_2026_07_04
tags: [Linux, kernel, performance, systems]
- id: brendan_gregg
name: "Brendan Gregg"
kind: rss_or_html
source_tier: technical_primary
url: "https://www.brendangregg.com/blog/"
feed_url: "https://www.brendangregg.com/blog/rss.xml"
access_status: seed
tags: [Linux, performance, eBPF, FlameGraph, observability]
- id: ebpf_blog
name: "eBPF.io Blog"
kind: rss_or_html
source_tier: technical_primary
url: "https://ebpf.io/blog/"
access_status: seed
tags: [Linux, eBPF, observability, performance]
github:
source_strategy: "Use community signals for discovery, then verify project value on GitHub repo, releases, commits, and issues."
recent_window_hours: 72
track_user_projects: false
allowed_item_classes:
- github_project_signal
- technical_report
source_rules:
require_repo_url: true
require_growth_or_activity_signal: true
require_project_summary: true
do_not_select_prompt_collections_without_engineering_value: true
sources:
- id: github_trending_daily
name: "GitHub Trending Daily"
kind: html
source_tier: community_signal
url: "https://github.com/trending?since=daily"
access_status: seed
tags: [GitHub, open_source, discovery]
- id: github_trending_weekly
name: "GitHub Trending Weekly"
kind: html
source_tier: community_signal
url: "https://github.com/trending?since=weekly"
access_status: seed
tags: [GitHub, open_source, discovery]
- id: github_trending_python
name: "GitHub Trending Python"
kind: html
source_tier: community_signal
url: "https://github.com/trending/python?since=weekly"
access_status: seed
tags: [GitHub, Python, AI, tools]
- id: github_trending_typescript
name: "GitHub Trending TypeScript"
kind: html
source_tier: community_signal
url: "https://github.com/trending/typescript?since=weekly"
access_status: seed
tags: [GitHub, TypeScript, AI, developer_tools]
- id: github_search_api_ai_agents
name: "GitHub Search API - AI Agents"
kind: api
source_tier: community_signal
url: "https://api.github.com/search/repositories"
query_template: "agents ai stars:>200 pushed:>{date_minus_30d}"
access_status: seed
tags: [GitHub, AI, agents, discovery]
- id: github_search_api_llm_infra
name: "GitHub Search API - LLM Infrastructure"
kind: api
source_tier: community_signal
url: "https://api.github.com/search/repositories"
query_template: "llm inference stars:>200 pushed:>{date_minus_30d}"
access_status: seed
tags: [GitHub, LLM, inference, infrastructure]
- id: github_search_api_security_tools
name: "GitHub Search API - Security Tools"
kind: api
source_tier: community_signal
url: "https://api.github.com/search/repositories"
query_template: "security tool stars:>500 pushed:>{date_minus_30d}"
access_status: seed
tags: [GitHub, security, tools]
finance:
source_strategy: "Use official regulators and central banks first. Use fintech media only as discovery, not as investment advice."
recent_window_hours: 168
allowed_item_classes:
- breaking_news
- official_release
- technical_report
- explainer
- concept_note
not_investment_advice: true
sources:
- id: hkma_press_releases
name: "Hong Kong Monetary Authority Press Releases"
kind: official_html
source_tier: official_primary
url: "https://www.hkma.gov.hk/eng/news-and-media/press-releases/"
access_status: verified_2026_07_04
tags: [finance, fintech, Hong_Kong, banking, regulation]
- id: mas_news
name: "Monetary Authority of Singapore News"
kind: official_html
source_tier: official_primary
url: "https://www.mas.gov.sg/news"
access_status: verified_2026_07_04
tags: [finance, fintech, Singapore, banking, regulation]
- id: federal_reserve_press
name: "Federal Reserve Press Releases"
kind: rss
source_tier: official_primary
url: "https://www.federalreserve.gov/newsevents/pressreleases.htm"
feed_url: "https://www.federalreserve.gov/feeds/press_all.xml"
access_status: verified_2026_07_04
tags: [finance, rates, banking, regulation, macro]
- id: bis_press
name: "Bank for International Settlements Press Releases"
kind: official_html
source_tier: official_primary
url: "https://www.bis.org/press/pressrels.htm"
access_status: verified_2026_07_04
tags: [finance, central_banks, payments, regulation]
- id: bis_innovation_hub
name: "BIS Innovation Hub"
kind: official_html
source_tier: official_primary
url: "https://www.bis.org/about/bisih/"
access_status: seed
tags: [finance, fintech, CBDC, tokenization, payments]
- id: sec_news
name: "SEC Newsroom"
kind: official_html
source_tier: official_primary
url: "https://www.sec.gov/newsroom"
access_status: protected_or_dynamic_2026_07_04
tags: [finance, securities, regulation, fintech]
- id: finextra
name: "Finextra"
kind: rss_or_html
source_tier: reputable_secondary
url: "https://www.finextra.com/"
access_status: seed
tags: [finance, fintech, payments, banking]
- id: chainalysis_blog
name: "Chainalysis Blog"
kind: rss_or_html
source_tier: reputable_secondary
url: "https://www.chainalysis.com/blog/"
access_status: seed
tags: [finance, crypto, compliance, AML, stablecoins]
global_search_queries:
general_news:
- "\"Trump\" \"China visit\" OR \"特朗普\" \"访华\""
- "\"Russia Ukraine war\" \"latest\" OR \"俄乌战争\""
- "\"CSRC\" \"Hong Kong brokers\" OR \"中国证监会\" \"香港券商\""
- "\"US China\" \"tariff\" OR \"中美关系\" \"关税\""
- "\"Hong Kong\" \"securities regulator\" OR \"香港\" \"证券监管\""
ai:
- "\"AI agent\" OR \"coding agent\" OR \"vibe coding\""
- "\"agent harness\" OR \"evaluation harness\" OR \"context engineering\""
- "\"LLMOps\" OR \"model routing\" OR \"prompt caching\" OR \"agent evaluation\""
- "\"MCP\" \"agent\" \"release\""
- "\"RAG\" \"evaluation\" OR \"GraphRAG\""
- "\"computer use\" \"agent\" \"AI\""
software_engineering:
- "\"incident review\" \"engineering\""
- "\"PostgreSQL\" \"performance\" \"release\""
- "\"Spring Boot\" \"release\""
- "\"Vue\" \"TypeScript\" \"release\""
security_performance:
- "\"CVE\" \"exploited in the wild\""
- "\"CISA KEV\" \"added\""
- "\"eBPF\" \"performance\""
- "\"Linux perf\" \"FlameGraph\""
- "\"network censorship measurement\" OR \"GFW measurement\""
github:
- "\"GitHub\" \"AI agent\" \"stars\""
- "\"open source\" \"LLM infrastructure\""
- "\"RAG\" \"GitHub\" \"release\""
finance:
- "\"fintech\" \"HKMA\""
- "\"fintech\" \"MAS\""
- "\"stablecoin\" \"regulation\""
- "\"tokenization\" \"BIS\""
output_rules:
cite_every_news_item: true
cite_background_items: true
show_dates_for_news_items: true
source_label_format: "{source_name}, {published_at_or_updated_at}"
stale_item_policy: "Do not include stale news unless explicitly labeled as background."
primary_source_verification_for_secondary_items: "Required when item makes factual claims about releases, vulnerabilities, market regulation, or official policy."
metadata_placement: "Put source, date, and link directly under each item title."
prose_style: "Write paragraphs. Do not use formulaic labels such as 为何重要, 学习启示, 对你来说, 来源, 时间 as sectioned bullets inside the body."
+49
View File
@@ -0,0 +1,49 @@
services:
newspaper:
network_mode: host
build:
context: .
network: host
args:
HTTP_PROXY: ${HTTP_PROXY:-}
HTTPS_PROXY: ${HTTPS_PROXY:-}
ALL_PROXY: ${ALL_PROXY:-}
NO_PROXY: ${NO_PROXY:-localhost,127.0.0.1}
http_proxy: ${HTTP_PROXY:-}
https_proxy: ${HTTPS_PROXY:-}
all_proxy: ${ALL_PROXY:-}
no_proxy: ${NO_PROXY:-localhost,127.0.0.1}
image: hongyu-daily-newspaper:latest
env_file:
- .env
environment:
TZ: ${TZ:-Asia/Shanghai}
volumes:
- ./newspaper:/app/newspaper
command: ["hongyu-daily", "run"]
restart: "no"
scheduler:
network_mode: host
build:
context: .
network: host
args:
HTTP_PROXY: ${HTTP_PROXY:-}
HTTPS_PROXY: ${HTTPS_PROXY:-}
ALL_PROXY: ${ALL_PROXY:-}
NO_PROXY: ${NO_PROXY:-localhost,127.0.0.1}
http_proxy: ${HTTP_PROXY:-}
https_proxy: ${HTTPS_PROXY:-}
all_proxy: ${ALL_PROXY:-}
no_proxy: ${NO_PROXY:-localhost,127.0.0.1}
image: hongyu-daily-newspaper:latest
env_file:
- .env
environment:
TZ: ${TZ:-Asia/Shanghai}
NEWSPAPER_SCHEDULE_TIME: ${NEWSPAPER_SCHEDULE_TIME:-08:00}
volumes:
- ./newspaper:/app/newspaper
command: ["hongyu-daily", "schedule"]
restart: unless-stopped
+256
View File
@@ -0,0 +1,256 @@
# Debian Docker 部署说明
这份文档说明如何把 Daily News for Markyan04 部署到你的个人 Debian 服务器上,通过 Docker Compose 每天早上自动生成并发送日报。
## 部署目标
每天早上 08:00,按 `Asia/Shanghai` 时区执行:
```text
采集新闻 -> 生成中文日报 -> 归档到 newspaper/ -> 发送到 markyan04@163.com
```
收件人已经在 `config/delivery.yaml` 中配置为:
```text
markyan04@163.com
```
## 服务器前置条件
服务器需要安装 Docker 和 Docker Compose plugin。
Debian 常见安装方式:
```bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
如果希望当前用户直接运行 Docker,可以执行:
```bash
sudo usermod -aG docker "$USER"
```
执行后需要退出 SSH 并重新登录,用户组才会生效。
## 复制仓库到服务器
建议放到 `/srv/apps/newspaper`
```bash
sudo mkdir -p /srv/apps/newspaper
sudo chown -R "$USER":"$USER" /srv/apps/newspaper
rsync -av --exclude .env /path/to/Newspaper/ /srv/apps/newspaper/
cd /srv/apps/newspaper
```
如果你用 `scp``git clone` 或 Synology 同步目录,也可以,只要最终目录结构完整即可。
## 创建 `.env`
复制模板:
```bash
cp .env.example .env
nano .env
```
填写:
```text
DEEPSEEK_API_KEY=你的 DeepSeek API Key
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-v4-pro
GITHUB_TOKEN=可选,GitHub 只读 token,用于提高开源项目搜索限额
NEWSPAPER_SMTP_HOST=smtp.163.com
NEWSPAPER_SMTP_PORT=465
NEWSPAPER_SMTP_USERNAME=你的发件邮箱
NEWSPAPER_SMTP_PASSWORD=你的邮箱授权码
NEWSPAPER_FROM_EMAIL=你的发件邮箱
NEWSPAPER_FROM_NAME=Daily News for Markyan04
NEWSPAPER_SMTP_SECURITY=ssl
NEWSPAPER_SCHEDULE_TIME=08:00
TZ=Asia/Shanghai
```
注意:
- 如果使用 163 邮箱发件,需要在邮箱设置中开启 SMTP/IMAP 服务。
- `GITHUB_TOKEN` 可以留空;如果 GitHub / 开源项目栏目经常空白,建议填一个只读 token。
- `NEWSPAPER_SMTP_PASSWORD` 应填写邮箱授权码,不是网页登录密码。
- 如果使用其他邮箱或邮件服务商,请按服务商文档替换 SMTP host、port 和 security。
- 不要把 `.env` 提交到仓库,也不要把授权码发到聊天里。
## 构建镜像
```bash
docker compose build
```
## 检查配置
```bash
docker compose run --rm newspaper hongyu-daily check-config
```
期望输出:
```text
config/newspaper.yaml: OK
config/sources.yaml: OK
config/delivery.yaml: OK
newspaper archive: newspaper/
```
## 只生成日报,不发送
```bash
docker compose run --rm newspaper hongyu-daily generate
```
检查归档目录:
```bash
ls -lh newspaper/
```
应该看到:
```text
YYYY-MM-DD-daily-news-for-markyan04.md
YYYY-MM-DD-daily-news-for-markyan04.html
YYYY-MM-DD-daily-news-for-markyan04.meta.json
```
建议第一次先打开 `.md``.html` 检查:
- 正文是否为中文。
- 每条新闻是否有来源链接。
- 新闻是否带有发布时间、更新时间或采集时间。
- AI 栏目是否是新鲜内容,而不是旧概念拼贴。
- 传统新闻是否覆盖了真实的新进展,而不是泛泛复述旧背景。
## 发送已生成的日报
如果你已经生成了某一天的日报,可以单独发送:
```bash
docker compose run --rm newspaper hongyu-daily send --date YYYY-MM-DD
```
例如:
```bash
docker compose run --rm newspaper hongyu-daily send --date 2026-07-04
```
## 生成并发送一次
```bash
docker compose run --rm newspaper hongyu-daily run
```
这个命令会完成:
```text
采集 -> 生成 -> 归档 -> 发送
```
## 启动每日定时任务
```bash
docker compose up -d scheduler
```
查看日志:
```bash
docker compose logs -f scheduler
```
`scheduler` 服务会常驻运行,每天本地时间 08:00 执行一次。
## 停止每日定时任务
```bash
docker compose down
```
## 可选方案:使用 Debian 主机 cron
如果你不想让 `scheduler` 容器常驻,也可以用 Debian 主机的 cron。
先创建日志目录:
```bash
mkdir -p /srv/apps/newspaper/logs
```
编辑 crontab
```bash
crontab -e
```
加入:
```text
0 8 * * * cd /srv/apps/newspaper && docker compose run --rm newspaper hongyu-daily run >> logs/cron.log 2>&1
```
注意:不要同时启用主机 cron 和 `scheduler` 容器,否则每天可能会重复发送。
## 更新部署
当你把新代码复制到服务器后:
```bash
cd /srv/apps/newspaper
docker compose build
docker compose up -d scheduler
```
如果只想手动跑一次:
```bash
docker compose run --rm newspaper hongyu-daily run
```
## 常见问题
### Docker 构建失败
先确认服务器能访问 Python 包源和 Debian 软件源。
```bash
docker compose build --no-cache
```
### 邮件发送失败
优先检查:
- SMTP host 和 port 是否正确。
- `NEWSPAPER_SMTP_SECURITY` 是否应为 `ssl``starttls`
- 邮箱是否开启 SMTP/IMAP。
- `NEWSPAPER_SMTP_PASSWORD` 是否是授权码。
- 服务器是否允许连接 SMTP 端口。
### 生成日报失败
优先检查:
- `DEEPSEEK_API_KEY` 是否正确。
- DeepSeek API 账户是否有额度。
- 服务器是否能访问 `https://api.deepseek.com`
- `config/sources.yaml` 中的来源是否大面积无法访问。
@@ -0,0 +1,119 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Daily News for Markyan04 | 2026 - 07 - 05</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; line-height: 1.65; color: #111; max-width: 860px; margin: 0 auto; padding: 24px; }
a { color: #1756a9; }
h1, h2, h3 { line-height: 1.25; }
blockquote { border-left: 4px solid #ddd; padding-left: 12px; color: #555; }
code { background: #f4f4f4; padding: 2px 4px; border-radius: 4px; }
</style>
</head>
<body>
<h1>Daily News for Markyan04 | 2026 - 07 - 05</h1>
<h2>1. 今日汇总</h2>
<ul>
<li><strong>传统新闻</strong>:特朗普与普京通长电话,在乌克兰问题上重新将自己定位为和平缔造者;德黑兰为在美以空袭中丧生的哈梅内伊举行国葬,特朗普承诺葬礼期间保持克制。</li>
<li><strong>AI</strong>Anthropic 于 6 月 30 日发布 Claude Sonnet 5,在编程、agent 和专业工作中达到前沿性能;同时重新部署 Fable 5,并联合 Amazon 提出 jailbreak 严重性评分行业框架。</li>
<li><strong>传统软工</strong>Cloudflare 发布 Monetization Gateway,允许开发者对任何 Web 资源收费并以稳定币通过 x402 协议结算,同时发布 AI 时代网络经济报告。</li>
<li><strong>安全</strong>:Linux 稳定内核批量更新修复关键 IPv6 漏洞 CVE-2026-53362GNU Guix 披露四个严重漏洞,包括远程提权和本地敏感文件泄露。</li>
<li><strong>GitHub</strong><code>usestrix/strix</code> 开源 AI 渗透测试工具获 1,904 星;<code>DeusData/codebase-memory-mcp</code> 代码智能 MCP 服务器本周获 9,517 星。</li>
</ul>
<h2>2. 传统新闻</h2>
<h3>特朗普与普京长通话,重新定位为乌克兰和平缔造者</h3>
<p>来源:The Guardian | 时间:2026-07-05 | 链接:https://www.theguardian.com/us-news/2026/jul/04/trump-speech-dc-evacuation-thunderstorm</p>
<p>根据 Google News 汇总报道,特朗普与普京进行了长时间通话,在乌克兰战争问题上重新调整立场,将自己定位为和平缔造者角色。通话发生在俄乌战争持续且国际社会对停火路径尚无共识的背景下,特朗普的具体建议或谈判框架尚未公开。目前尚不清楚此次通话是否会对美国对乌政策产生实质性转向。</p>
<h3>伊朗为哈梅内伊举行国葬,特朗普承诺葬礼期间保持克制</h3>
<p>来源:Al Jazeera | 时间:2026-07-05 | 链接:https://www.aljazeera.com/news/liveblog/2026/7/5/iran-war-live-huge-crowds-mourn-khamenei-trump-vows-calm-during-funeral</p>
<p>伊朗最高领袖哈梅内伊及其家人于今年二月在美以联合空袭中丧生,大规模哀悼仪式于德黑兰启动,数万名伊朗民众走上街头。特朗普在葬礼期间公开承诺将“保持克制”。Al Jazeera 现场博客持续跟踪最新动态,地区安全局势在葬礼期间的稳定性仍存在高度不确定性。</p>
<h3>菲律宾副总统弹劾审判即将开始</h3>
<p>来源:The New York Times | 时间:2026-07-05 | 链接:https://www.nytimes.com/2026/07/05/world/asia/impeachment-trial-philippines-sara-duterte.html</p>
<p>菲律宾副总统莎拉·杜特尔特面临腐败指控及对总统马科斯的死亡威胁指控,弹劾审判即将启动。若罪名成立,她可能被禁止从政,而她此前已表示将竞选总统。审判将对菲律宾国内政治格局产生深远影响,目前尚无明确时间表披露庭审具体进程。</p>
<h3>习近平准备延长任期,蔡奇成为关键助手</h3>
<p>来源:The New York Times | 时间:2026-07-05 | 链接:https://www.nytimes.com/2026/07/05/world/asia/china-xi-leadership-cai-qi.html</p>
<p>习近平正在准备延长其任期,并计划向党内精英层注入新鲜血液。蔡奇被视为承担这一任务的核心人物。相关决策预计将在下一次重大党内会议上明朗化。</p>
<h3>美国 250 周年独立日遭受恶劣天气冲击</h3>
<p>来源:Al Jazeera | 时间:2026-07-05 | 链接:https://www.aljazeera.com/news/2026/7/5/severe-weather-disrupts-uss-250th-celebrations?traffic_source=rss</p>
<p>美国 250 周年独立日庆祝活动遭恶劣天气严重干扰,华盛顿特区部分区域因雷暴紧急疏散。特朗普在深夜以竞选式演讲发表讲话,称这是美国历史上“最欢乐、最辉煌的时刻之一”。</p>
<h2>3. AI领域</h2>
<h3>Anthropic 发布 Claude Sonnet 5</h3>
<p>来源:Anthropic News | 时间:2026-06-30 | 链接:https://www.anthropic.com/news/claude-sonnet-5</p>
<p>Anthropic 于 6 月 30 日正式发布 Claude Sonnet 5。根据官方公告,Sonnet 5 在编程、agent 及大规模专业工作场景中实现了前沿性能,意味着其定位已不仅限于中端模型,而是开始在核心能力上逼近或匹配前代旗舰水平。目前尚未公开详细 benchmark 数据或架构调整说明,但发布指出这是面向规模化应用的重要迭代。</p>
<h3>Anthropic 重新部署 Fable 5 并提出 Jailbreak 严重性框架</h3>
<p>来源:Anthropic News | 时间:2026-06-30 | 链接:https://www.anthropic.com/news/redeploying-fable-5</p>
<p>在 Claude Sonnet 5 发布同日,Anthropic 宣布 Fable 5 于 7 月 1 日全球重新上线,并与 Amazon 合作提出了一套行业范围的 jailbreak 严重性评分框架。这表明 Anthropic 正在试图将安全评估标准化为可跨模型和跨组织比较的公共度量,类似 CVSS 之于传统软件安全的思路。Fable 5 此前曾被部署后被短暂撤回,此次重新发布可能涉及安全机制的调整。</p>
<h3>Meta 发布 Muse Spark:向个人超级智能扩展</h3>
<p>来源:Meta AI Blog | 时间:2026-07-05 | 链接:https://ai.meta.com/blog/introducing-muse-spark-msl/</p>
<p>Meta 发布 Muse Spark(MSL),方向定位为“向个人超级智能扩展”。目前公开信息有限,但从路线图来看,该产品线可能涉及个性化模型微调、本地推理能力提升和 agent 长期记忆,试图将超大规模模型的能力压缩到个人可用的尺度。这与 Google Gemini 系列的个人化方向构成直接竞争。</p>
<h3>Meta 发布 Brain2Qwerty:非侵入式脑电波文本通信</h3>
<p>来源:Meta AI Blog | 时间:2026-07-05 | 链接:https://ai.meta.com/blog/brain2qwerty-brain-ai-human-communication/</p>
<p>Meta 发布了 Brain2Qwerty 研究,展示了一条不依赖手术植入的脑电波到文字的通信路径。该技术属于脑机接口领域中非侵入式分支,相较于 Neuralink 的手术方案,安全性更高但信号精度和速度存在固有瓶颈,目前大概率仍处于实验室验证阶段。</p>
<h3>主流模型系统提示大规模泄露</h3>
<p>来源:GitHub - asgeirtj/system_prompts_leaks | 时间:2026-07-05 | 链接:https://github.com/asgeirtj/system_prompts_leaks</p>
<p>一个名为 <code>system_prompts_leaks</code> 的仓库公开了当前主流模型的系统提示,涵盖 Anthropic Claude Fable 5、Opus 4.8、Claude Code、OpenAI ChatGPT 5.5 Thinking、GPT 5.5 Instant、Codex、Google Gemini 3.5 Flash、3.1 Pro、xAI Grok、Cursor、Copilot、VS Code 和 Perplexity 等。这些提示的提取方法未公开,但其广泛的范围说明多数商业模型的系统提示仍缺乏有效的防提取保护。对于依赖系统提示进行安全约束的部署场景,这是一个直接影响。</p>
<h3>编程 Agent 工具生态持续快速扩张</h3>
<p>近期多个工具试图解决编程 agent 的实际问题。OpenAI 发布 <code>codex-plugin-cc</code>(718 星),让 Codex 可直接在 Claude Code 中被调用,说明不同 AI 编程工具之间的互操作性和竞争关系正在加速融合。<code>JuliusBrussee/caveman</code>1,089 星)是一个 Claude Code 技能,通过将 prompt 压缩成“穴居人语”声称可节省 65% token,反映出企业在 token 成本上的压力。<code>alibaba/page-agent</code>(742 星)则是一个页面内 GUI agent,用自然语言控制网页界面,属于 computer-use agent 的轻量化实现路径。</p>
<h3>AI 记忆与知识图谱基础设施出现多头竞争</h3>
<p><code>topoteretes/cognee</code>(3,388 星本周)定位为开源 AI 记忆平台,向 agent 提供跨会话的持久长期记忆,底層使用自托管的图引擎。<code>Ontos-AI/knowhere</code>(1,854 星)则专注于将非结构化内容提取为结构化块以供 RAG 和 agent 使用。两者从不同方向切入同一个核心问题:如何让 LLM 在多次交互中保持对上下文的理解,而不依赖无限增长的长上下文窗口或重复嵌入检索。</p>
<h3>多 Agent 系统和并行编排工具</h3>
<p><code>stablyai/orca</code>(3,790 星本周)是一个支持多 agent 并行运行的编排环境,可让用户用自己的订阅运行任何编程 agent,支持桌面和移动端。<code>ohdearquant/lionagi</code>(399 星)自称“智能乐团”,暗示其多 agent 调度可能在模型路由和工作流编排方面进行了抽象。<code>kunchenguid/gnhf</code>443 星本周)的 slogan 是“睡觉前告诉 agent:晚安,玩得开心”,反映了夜间自主 agent 运行场景正在从梗变成工程实践。</p>
<h3>Harvard 发布机器学习系统教材</h3>
<p>来源:GitHub - harvard-edge/cs249r_book | 时间:2026-07-05 | 链接:https://github.com/harvard-edge/cs249r_book</p>
<p>Harvard Edge 计算实验室公开了 CS249r 课程教材《Machine Learning Systems》,涵盖生产级 ML 系统的完整生命周期,面向 AI engineering 方向的系统和工程化教育正在从散落博文走向结构化课程。</p>
<h2>4. 传统软工</h2>
<h3>Cloudflare 发布 Monetization Gateway:用稳定币计费任何 Web 资源</h3>
<p>来源:Cloudflare Blog | 时间:2026-07-01 | 链接:https://blog.cloudflare.com/monetization-gateway/</p>
<p>Cloudflare 开放 Monetization Gateway 等待列表,该产品允许开发者对 Cloudflare 后面的任何网页、数据集、API 或 MCP 工具进行收费。结算采用 x402 开放协议以稳定币完成,无需自建支付系统。x402 协议在此前主要用于 AI 内容付费和微支付场景,此次与 Cloudflare 的边缘基础设施结合后,实际降低了内容创作者直接通过缓存层收费的门槛。</p>
<h3>Cloudflare 发布“Agentic Internet”一周年报告</h3>
<p>来源:Cloudflare Blog | 时间:2026-07-01 | 链接:https://blog.cloudflare.com/agentic-internet-bot-report/</p>
<p>在宣布“内容独立日”一周年之际,Cloudflare 发布报告分析自主 AI agent 如何颠覆传统搜索引擎推荐流量,并研究了支撑可持续网络经济所需的新基础设施堆栈。报告的核心论点是:随着 agent 成为网络内容的主要消费者,传统以广告和搜索导流为核心的网站盈利模式正在失效,需要新的计费和发现机制。</p>
<p>配合这一判断,Cloudflare 同步推出了三项功能:让所有客户精细区分并管理 Search、Agent 和 Training bot 的流量选项;Attribution Business Insights 仪表盘用于理解爬虫行为和内容价值;以及“让 AI 搜索更聪明”的双项计划,旨在帮助创作者在 agent 时代保持可发现性和内容变现能力。</p>
<h3>Netflix 发布 GenPage:端到端生成式首页构建</h3>
<p>来源:Netflix TechBlog | 时间:2026-06-29 | 链接:https://netflixtechblog.com/genpage-towards-end-to-end-generative-homepage-construction-at-netflix-77146fba8a08?source=rss----2615bd06b42e---4</p>
<p>Netflix 技术博客介绍了 GenPage,一种自回归首页生成方法。它将首页构建分解为逐行动态生成,每一步基于已生成内容和用户上下文决定下一个实体或行。这不是传统推荐系统上的 LLM 外衣,而是从页面结构层面用大模型直接生成组合决策,可能影响推荐系统的延迟、可解释性和冷启动策略。</p>
<h3>Spring AI 与 Spring Framework 最新进展</h3>
<p>来源:Spring Blog | 时间:2026-07-02 | 链接:https://spring.io/blog/2026/07/02/a-bootiful-podcast-sebastien-deleuze</p>
<p>Spring 框架核心贡献者 Sébastien Deleuze 在播客中详述了 Spring AI 的最新进展以及 Spring Framework 在 Kotlin 支持上的持续投入。Spring AI 作为 Java 生态对接 LLM 的主要桥梁,其框架级设计(如向量存储抽象、工具调用和 agent 编排)对于已经在 Spring 基础设施上投入的团队具有直接的工程参考价值。</p>
<h2>5. 安全与性能工程</h2>
<h3>Linux 稳定内核批量更新修复关键 IPv6 漏洞</h3>
<p>来源:LWN | 时间:2026-07-04 | 链接:https://lwn.net/Articles/1081230/</p>
<p>Greg Kroah-Hartman 发布了七个 Linux 稳定内核版本(7.1.3、6.18.38、6.12.95、6.6.144、6.1.177、5.15.211、5.10.260)。其中多个版本修复了 CVE-2026-53362——一个在 6.0 内核引入的 IPv6 漏洞,可能允许攻击者逃逸沙箱或实现权限提升。该漏洞影响范围广,横跨最新主线到多个 LTS 分支,建议尽快评估并部署对应内核更新。</p>
<h3>GNU Guix 披露四个严重漏洞</h3>
<p>来源:LWN | 时间:2026-07-03 | 链接:https://lwn.net/Articles/1081199/</p>
<p>GNU Guix 项目公布四个漏洞:三个存在于 <code>guix substitute</code> 工具中,另一个影响 <code>guix pull</code><code>guix time-machine</code> 命令。漏洞严重度从远程提权到本地敏感文件泄露。由于 Guix 是用于可复现构建和科学计算环境的基础设施,这些漏洞可能影响 CI 管道和开发环境的安全性,远程可利用性尤为值得警惕。</p>
<h3>Linux 负向 Dentry 限制的存储峰会讨论</h3>
<p>来源:LWN | 时间:2026-07-03 | 链接:https://lwn.net/Articles/1079407/</p>
<p>在 2026 年 Linux 存储、文件系统、内存管理和 BPF 峰会上,负向目录条目(negative dentries)引发专题讨论。负向 dentry 用于标记“某名称的文件在目录中不存在”,在频繁查询不存在文件的场景下会不受限制地积累,导致内存占用激增。讨论聚焦于内核是否应引入全局限制机制,但方案共识尚未形成,这可能影响高并发 Web 服务和容器化环境下的内存预估。</p>
<h3>CalyxOS 隐私导向 Android 发行版正式回归</h3>
<p>来源:LWN | 时间:2026-07-02 | 链接:https://lwn.net/Articles/1081038/</p>
<p>CalyxOS 在去年 8 月因核心开发者离开暂停发布后,宣布“正式回归”。项目已完成发布流程重做、安全协议升级和签名密钥更换。对于使用该系统的用户,升级路径和密钥信任链的迁移方案将是接下来需要关注的实际问题。</p>
<h2>6. GitHub / 开源项目跟踪</h2>
<h3>usestrix/strix:开源 AI 渗透测试工具</h3>
<p>来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/usestrix/strix</p>
<p>一个 Python 项目的 AI 渗透测试工具,定位为自动发现和修复应用漏洞,本周获 1,904 星。技术栈未完全披露,但从功能描述来看,该工具可能结合了 agent 驱动的安全扫描和自动漏洞利用验证。作为开源安全测试工具,其成熟度尚需通过实际安全审计来验证。</p>
<h3>DeusData/codebase-memory-mcp:高性能代码智能 MCP 服务器</h3>
<p>来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/DeusData/codebase-memory-mcp</p>
<p>用 C 编写的 MCP 服务器,将代码库索引到持久化知识图中,声称“平均代码库毫秒级索引”、支持 158 种语言、亚毫秒级查询并节省 99% token。以单一静态二进制分发,零依赖。本周 9,517 星,反映开发者在 LLM 编程场景中对低成本代码上下文检索的迫切需求。</p>
<h3>ChromeDevTools/chrome-devtools-mcp:面向编程 Agent 的浏览器控制协议</h3>
<p>来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/ChromeDevTools/chrome-devtools-mcp</p>
<p>Chrome DevTools 团队官方发布的 MCP 服务器,让编程 agent 能够以结构化方式与浏览器交互,而不是通过原始 DOM 或截图驱动。TypeScript 实现,日增 304 星。此前第三方 browser-use 工具流行度爆炸,此官方方案的出现可能推动 browser-agent 从实验进入标准化。</p>
<h3>ddalcu/mlx-serveApple Silicon 原生 LLM 推理服务器</h3>
<p>来源:GitHub Search API | 时间:2026-07-05 | 链接:https://github.com/ddalcu/mlx-serve</p>
<p>专为 Apple Silicon 设计的 LLM 推理服务器,与 OpenAI 和 Anthropic API 兼容,使用 Zig 编写,不依赖 Python。包含 macOS 原生应用,支持聊天、agent 模式和工具调用。232 星,代表了在本地推理效率上绕开 Python 解释器开销的工程尝试。</p>
<h3>topoteretes/cognee:开源 AI Agent 长期记忆平台</h3>
<p>来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/topoteretes/cognee</p>
<p>Python 编写的 AI 记忆平台,为 agent 提供跨会话持久长期记忆,使用自托管知识图引擎。本周 3,388 星。在多个 agent 复用上下文、避免重复推理和降低 token 成本的场景中有直接工程价值。</p>
<h3>calesthio/OpenMontage:开源智能视频制作系统</h3>
<p>来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/calesthio/OpenMontage</p>
<p>号称“世界首个开源智能视频制作系统”,包含 12 条流水线、52 个工具和 500+ 项 agent 技能。本周 8,447 星,是目前 agent 在创意工具领域最高关注度的项目之一。Python 实现,具体生产线流程和输出质量尚无第三方评估。</p>
<h2>7. 金融领域</h2>
<h3>美联储对 Small Business Bank 采取执法行动,终止对法巴的执法</h3>
<p>来源:Federal Reserve | 时间:2026-07-02 | 链接:https://www.federalreserve.gov/newsevents/pressreleases/enforcement20260702a.htm</p>
<p>美联储宣布对 Small Business Bank 启动新的执法行动,同时终止对法国巴黎银行及其美国子公司、法巴证券公司和 Community Bankshares 的执法行动。终止执法通常意味着相关银行已满足整改要求,法巴这类全球系统重要性银行的合规问题一度涉及反洗钱和制裁合规领域。具体执法原因和被终止的整改措施细节尚未一并公开。</p>
<h3>支付领域:稳定币结算进入基础设施层</h3>
<p>Cloudflare 发布的 Monetization Gateway(详见传统软工栏目)允许用稳定币结算网络资源费用,这意味着稳定币正在从投资和跨境转账场景渗透到微支付和 API 计费等基础设施层面。x402 协议的作用类似于 HTTP 状态码 402“Payment Required”的工程实现,配合 Cloudflare 的边缘网络,可能催生更广泛的机器间支付场景。该趋势对 KYC/AML 的影响在于,微支付级稳定币流如何在监管框架下被归类和处理,目前仍是开放问题。</p>
</body>
</html>
@@ -0,0 +1,195 @@
# Daily News for Markyan04 | 2026 - 07 - 05
## 1. 今日汇总
- **传统新闻**:特朗普与普京通长电话,在乌克兰问题上重新将自己定位为和平缔造者;德黑兰为在美以空袭中丧生的哈梅内伊举行国葬,特朗普承诺葬礼期间保持克制。
- **AI**Anthropic 于 6 月 30 日发布 Claude Sonnet 5,在编程、agent 和专业工作中达到前沿性能;同时重新部署 Fable 5,并联合 Amazon 提出 jailbreak 严重性评分行业框架。
- **传统软工**Cloudflare 发布 Monetization Gateway,允许开发者对任何 Web 资源收费并以稳定币通过 x402 协议结算,同时发布 AI 时代网络经济报告。
- **安全**:Linux 稳定内核批量更新修复关键 IPv6 漏洞 CVE-2026-53362GNU Guix 披露四个严重漏洞,包括远程提权和本地敏感文件泄露。
- **GitHub**`usestrix/strix` 开源 AI 渗透测试工具获 1,904 星;`DeusData/codebase-memory-mcp` 代码智能 MCP 服务器本周获 9,517 星。
## 2. 传统新闻
### 特朗普与普京长通话,重新定位为乌克兰和平缔造者
来源:The Guardian | 时间:2026-07-05 | 链接:https://www.theguardian.com/us-news/2026/jul/04/trump-speech-dc-evacuation-thunderstorm
根据 Google News 汇总报道,特朗普与普京进行了长时间通话,在乌克兰战争问题上重新调整立场,将自己定位为和平缔造者角色。通话发生在俄乌战争持续且国际社会对停火路径尚无共识的背景下,特朗普的具体建议或谈判框架尚未公开。目前尚不清楚此次通话是否会对美国对乌政策产生实质性转向。
### 伊朗为哈梅内伊举行国葬,特朗普承诺葬礼期间保持克制
来源:Al Jazeera | 时间:2026-07-05 | 链接:https://www.aljazeera.com/news/liveblog/2026/7/5/iran-war-live-huge-crowds-mourn-khamenei-trump-vows-calm-during-funeral
伊朗最高领袖哈梅内伊及其家人于今年二月在美以联合空袭中丧生,大规模哀悼仪式于德黑兰启动,数万名伊朗民众走上街头。特朗普在葬礼期间公开承诺将“保持克制”。Al Jazeera 现场博客持续跟踪最新动态,地区安全局势在葬礼期间的稳定性仍存在高度不确定性。
### 菲律宾副总统弹劾审判即将开始
来源:The New York Times | 时间:2026-07-05 | 链接:https://www.nytimes.com/2026/07/05/world/asia/impeachment-trial-philippines-sara-duterte.html
菲律宾副总统莎拉·杜特尔特面临腐败指控及对总统马科斯的死亡威胁指控,弹劾审判即将启动。若罪名成立,她可能被禁止从政,而她此前已表示将竞选总统。审判将对菲律宾国内政治格局产生深远影响,目前尚无明确时间表披露庭审具体进程。
### 习近平准备延长任期,蔡奇成为关键助手
来源:The New York Times | 时间:2026-07-05 | 链接:https://www.nytimes.com/2026/07/05/world/asia/china-xi-leadership-cai-qi.html
习近平正在准备延长其任期,并计划向党内精英层注入新鲜血液。蔡奇被视为承担这一任务的核心人物。相关决策预计将在下一次重大党内会议上明朗化。
### 美国 250 周年独立日遭受恶劣天气冲击
来源:Al Jazeera | 时间:2026-07-05 | 链接:https://www.aljazeera.com/news/2026/7/5/severe-weather-disrupts-uss-250th-celebrations?traffic_source=rss
美国 250 周年独立日庆祝活动遭恶劣天气严重干扰,华盛顿特区部分区域因雷暴紧急疏散。特朗普在深夜以竞选式演讲发表讲话,称这是美国历史上“最欢乐、最辉煌的时刻之一”。
## 3. AI领域
### Anthropic 发布 Claude Sonnet 5
来源:Anthropic News | 时间:2026-06-30 | 链接:https://www.anthropic.com/news/claude-sonnet-5
Anthropic 于 6 月 30 日正式发布 Claude Sonnet 5。根据官方公告,Sonnet 5 在编程、agent 及大规模专业工作场景中实现了前沿性能,意味着其定位已不仅限于中端模型,而是开始在核心能力上逼近或匹配前代旗舰水平。目前尚未公开详细 benchmark 数据或架构调整说明,但发布指出这是面向规模化应用的重要迭代。
### Anthropic 重新部署 Fable 5 并提出 Jailbreak 严重性框架
来源:Anthropic News | 时间:2026-06-30 | 链接:https://www.anthropic.com/news/redeploying-fable-5
在 Claude Sonnet 5 发布同日,Anthropic 宣布 Fable 5 于 7 月 1 日全球重新上线,并与 Amazon 合作提出了一套行业范围的 jailbreak 严重性评分框架。这表明 Anthropic 正在试图将安全评估标准化为可跨模型和跨组织比较的公共度量,类似 CVSS 之于传统软件安全的思路。Fable 5 此前曾被部署后被短暂撤回,此次重新发布可能涉及安全机制的调整。
### Meta 发布 Muse Spark:向个人超级智能扩展
来源:Meta AI Blog | 时间:2026-07-05 | 链接:https://ai.meta.com/blog/introducing-muse-spark-msl/
Meta 发布 Muse Spark(MSL),方向定位为“向个人超级智能扩展”。目前公开信息有限,但从路线图来看,该产品线可能涉及个性化模型微调、本地推理能力提升和 agent 长期记忆,试图将超大规模模型的能力压缩到个人可用的尺度。这与 Google Gemini 系列的个人化方向构成直接竞争。
### Meta 发布 Brain2Qwerty:非侵入式脑电波文本通信
来源:Meta AI Blog | 时间:2026-07-05 | 链接:https://ai.meta.com/blog/brain2qwerty-brain-ai-human-communication/
Meta 发布了 Brain2Qwerty 研究,展示了一条不依赖手术植入的脑电波到文字的通信路径。该技术属于脑机接口领域中非侵入式分支,相较于 Neuralink 的手术方案,安全性更高但信号精度和速度存在固有瓶颈,目前大概率仍处于实验室验证阶段。
### 主流模型系统提示大规模泄露
来源:GitHub - asgeirtj/system_prompts_leaks | 时间:2026-07-05 | 链接:https://github.com/asgeirtj/system_prompts_leaks
一个名为 `system_prompts_leaks` 的仓库公开了当前主流模型的系统提示,涵盖 Anthropic Claude Fable 5、Opus 4.8、Claude Code、OpenAI ChatGPT 5.5 Thinking、GPT 5.5 Instant、Codex、Google Gemini 3.5 Flash、3.1 Pro、xAI Grok、Cursor、Copilot、VS Code 和 Perplexity 等。这些提示的提取方法未公开,但其广泛的范围说明多数商业模型的系统提示仍缺乏有效的防提取保护。对于依赖系统提示进行安全约束的部署场景,这是一个直接影响。
### 编程 Agent 工具生态持续快速扩张
近期多个工具试图解决编程 agent 的实际问题。OpenAI 发布 `codex-plugin-cc`(718 星),让 Codex 可直接在 Claude Code 中被调用,说明不同 AI 编程工具之间的互操作性和竞争关系正在加速融合。`JuliusBrussee/caveman`1,089 星)是一个 Claude Code 技能,通过将 prompt 压缩成“穴居人语”声称可节省 65% token,反映出企业在 token 成本上的压力。`alibaba/page-agent`(742 星)则是一个页面内 GUI agent,用自然语言控制网页界面,属于 computer-use agent 的轻量化实现路径。
### AI 记忆与知识图谱基础设施出现多头竞争
`topoteretes/cognee`(3,388 星本周)定位为开源 AI 记忆平台,向 agent 提供跨会话的持久长期记忆,底層使用自托管的图引擎。`Ontos-AI/knowhere`(1,854 星)则专注于将非结构化内容提取为结构化块以供 RAG 和 agent 使用。两者从不同方向切入同一个核心问题:如何让 LLM 在多次交互中保持对上下文的理解,而不依赖无限增长的长上下文窗口或重复嵌入检索。
### 多 Agent 系统和并行编排工具
`stablyai/orca`(3,790 星本周)是一个支持多 agent 并行运行的编排环境,可让用户用自己的订阅运行任何编程 agent,支持桌面和移动端。`ohdearquant/lionagi`(399 星)自称“智能乐团”,暗示其多 agent 调度可能在模型路由和工作流编排方面进行了抽象。`kunchenguid/gnhf`443 星本周)的 slogan 是“睡觉前告诉 agent:晚安,玩得开心”,反映了夜间自主 agent 运行场景正在从梗变成工程实践。
### Harvard 发布机器学习系统教材
来源:GitHub - harvard-edge/cs249r_book | 时间:2026-07-05 | 链接:https://github.com/harvard-edge/cs249r_book
Harvard Edge 计算实验室公开了 CS249r 课程教材《Machine Learning Systems》,涵盖生产级 ML 系统的完整生命周期,面向 AI engineering 方向的系统和工程化教育正在从散落博文走向结构化课程。
## 4. 传统软工
### Cloudflare 发布 Monetization Gateway:用稳定币计费任何 Web 资源
来源:Cloudflare Blog | 时间:2026-07-01 | 链接:https://blog.cloudflare.com/monetization-gateway/
Cloudflare 开放 Monetization Gateway 等待列表,该产品允许开发者对 Cloudflare 后面的任何网页、数据集、API 或 MCP 工具进行收费。结算采用 x402 开放协议以稳定币完成,无需自建支付系统。x402 协议在此前主要用于 AI 内容付费和微支付场景,此次与 Cloudflare 的边缘基础设施结合后,实际降低了内容创作者直接通过缓存层收费的门槛。
### Cloudflare 发布“Agentic Internet”一周年报告
来源:Cloudflare Blog | 时间:2026-07-01 | 链接:https://blog.cloudflare.com/agentic-internet-bot-report/
在宣布“内容独立日”一周年之际,Cloudflare 发布报告分析自主 AI agent 如何颠覆传统搜索引擎推荐流量,并研究了支撑可持续网络经济所需的新基础设施堆栈。报告的核心论点是:随着 agent 成为网络内容的主要消费者,传统以广告和搜索导流为核心的网站盈利模式正在失效,需要新的计费和发现机制。
配合这一判断,Cloudflare 同步推出了三项功能:让所有客户精细区分并管理 Search、Agent 和 Training bot 的流量选项;Attribution Business Insights 仪表盘用于理解爬虫行为和内容价值;以及“让 AI 搜索更聪明”的双项计划,旨在帮助创作者在 agent 时代保持可发现性和内容变现能力。
### Netflix 发布 GenPage:端到端生成式首页构建
来源:Netflix TechBlog | 时间:2026-06-29 | 链接:https://netflixtechblog.com/genpage-towards-end-to-end-generative-homepage-construction-at-netflix-77146fba8a08?source=rss----2615bd06b42e---4
Netflix 技术博客介绍了 GenPage,一种自回归首页生成方法。它将首页构建分解为逐行动态生成,每一步基于已生成内容和用户上下文决定下一个实体或行。这不是传统推荐系统上的 LLM 外衣,而是从页面结构层面用大模型直接生成组合决策,可能影响推荐系统的延迟、可解释性和冷启动策略。
### Spring AI 与 Spring Framework 最新进展
来源:Spring Blog | 时间:2026-07-02 | 链接:https://spring.io/blog/2026/07/02/a-bootiful-podcast-sebastien-deleuze
Spring 框架核心贡献者 Sébastien Deleuze 在播客中详述了 Spring AI 的最新进展以及 Spring Framework 在 Kotlin 支持上的持续投入。Spring AI 作为 Java 生态对接 LLM 的主要桥梁,其框架级设计(如向量存储抽象、工具调用和 agent 编排)对于已经在 Spring 基础设施上投入的团队具有直接的工程参考价值。
## 5. 安全与性能工程
### Linux 稳定内核批量更新修复关键 IPv6 漏洞
来源:LWN | 时间:2026-07-04 | 链接:https://lwn.net/Articles/1081230/
Greg Kroah-Hartman 发布了七个 Linux 稳定内核版本(7.1.3、6.18.38、6.12.95、6.6.144、6.1.177、5.15.211、5.10.260)。其中多个版本修复了 CVE-2026-53362——一个在 6.0 内核引入的 IPv6 漏洞,可能允许攻击者逃逸沙箱或实现权限提升。该漏洞影响范围广,横跨最新主线到多个 LTS 分支,建议尽快评估并部署对应内核更新。
### GNU Guix 披露四个严重漏洞
来源:LWN | 时间:2026-07-03 | 链接:https://lwn.net/Articles/1081199/
GNU Guix 项目公布四个漏洞:三个存在于 `guix substitute` 工具中,另一个影响 `guix pull``guix time-machine` 命令。漏洞严重度从远程提权到本地敏感文件泄露。由于 Guix 是用于可复现构建和科学计算环境的基础设施,这些漏洞可能影响 CI 管道和开发环境的安全性,远程可利用性尤为值得警惕。
### Linux 负向 Dentry 限制的存储峰会讨论
来源:LWN | 时间:2026-07-03 | 链接:https://lwn.net/Articles/1079407/
在 2026 年 Linux 存储、文件系统、内存管理和 BPF 峰会上,负向目录条目(negative dentries)引发专题讨论。负向 dentry 用于标记“某名称的文件在目录中不存在”,在频繁查询不存在文件的场景下会不受限制地积累,导致内存占用激增。讨论聚焦于内核是否应引入全局限制机制,但方案共识尚未形成,这可能影响高并发 Web 服务和容器化环境下的内存预估。
### CalyxOS 隐私导向 Android 发行版正式回归
来源:LWN | 时间:2026-07-02 | 链接:https://lwn.net/Articles/1081038/
CalyxOS 在去年 8 月因核心开发者离开暂停发布后,宣布“正式回归”。项目已完成发布流程重做、安全协议升级和签名密钥更换。对于使用该系统的用户,升级路径和密钥信任链的迁移方案将是接下来需要关注的实际问题。
## 6. GitHub / 开源项目跟踪
### usestrix/strix:开源 AI 渗透测试工具
来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/usestrix/strix
一个 Python 项目的 AI 渗透测试工具,定位为自动发现和修复应用漏洞,本周获 1,904 星。技术栈未完全披露,但从功能描述来看,该工具可能结合了 agent 驱动的安全扫描和自动漏洞利用验证。作为开源安全测试工具,其成熟度尚需通过实际安全审计来验证。
### DeusData/codebase-memory-mcp:高性能代码智能 MCP 服务器
来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/DeusData/codebase-memory-mcp
用 C 编写的 MCP 服务器,将代码库索引到持久化知识图中,声称“平均代码库毫秒级索引”、支持 158 种语言、亚毫秒级查询并节省 99% token。以单一静态二进制分发,零依赖。本周 9,517 星,反映开发者在 LLM 编程场景中对低成本代码上下文检索的迫切需求。
### ChromeDevTools/chrome-devtools-mcp:面向编程 Agent 的浏览器控制协议
来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/ChromeDevTools/chrome-devtools-mcp
Chrome DevTools 团队官方发布的 MCP 服务器,让编程 agent 能够以结构化方式与浏览器交互,而不是通过原始 DOM 或截图驱动。TypeScript 实现,日增 304 星。此前第三方 browser-use 工具流行度爆炸,此官方方案的出现可能推动 browser-agent 从实验进入标准化。
### ddalcu/mlx-serveApple Silicon 原生 LLM 推理服务器
来源:GitHub Search API | 时间:2026-07-05 | 链接:https://github.com/ddalcu/mlx-serve
专为 Apple Silicon 设计的 LLM 推理服务器,与 OpenAI 和 Anthropic API 兼容,使用 Zig 编写,不依赖 Python。包含 macOS 原生应用,支持聊天、agent 模式和工具调用。232 星,代表了在本地推理效率上绕开 Python 解释器开销的工程尝试。
### topoteretes/cognee:开源 AI Agent 长期记忆平台
来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/topoteretes/cognee
Python 编写的 AI 记忆平台,为 agent 提供跨会话持久长期记忆,使用自托管知识图引擎。本周 3,388 星。在多个 agent 复用上下文、避免重复推理和降低 token 成本的场景中有直接工程价值。
### calesthio/OpenMontage:开源智能视频制作系统
来源:GitHub Trending | 时间:2026-07-05 | 链接:https://github.com/calesthio/OpenMontage
号称“世界首个开源智能视频制作系统”,包含 12 条流水线、52 个工具和 500+ 项 agent 技能。本周 8,447 星,是目前 agent 在创意工具领域最高关注度的项目之一。Python 实现,具体生产线流程和输出质量尚无第三方评估。
## 7. 金融领域
### 美联储对 Small Business Bank 采取执法行动,终止对法巴的执法
来源:Federal Reserve | 时间:2026-07-02 | 链接:https://www.federalreserve.gov/newsevents/pressreleases/enforcement20260702a.htm
美联储宣布对 Small Business Bank 启动新的执法行动,同时终止对法国巴黎银行及其美国子公司、法巴证券公司和 Community Bankshares 的执法行动。终止执法通常意味着相关银行已满足整改要求,法巴这类全球系统重要性银行的合规问题一度涉及反洗钱和制裁合规领域。具体执法原因和被终止的整改措施细节尚未一并公开。
### 支付领域:稳定币结算进入基础设施层
Cloudflare 发布的 Monetization Gateway(详见传统软工栏目)允许用稳定币结算网络资源费用,这意味着稳定币正在从投资和跨境转账场景渗透到微支付和 API 计费等基础设施层面。x402 协议的作用类似于 HTTP 状态码 402“Payment Required”的工程实现,配合 Cloudflare 的边缘网络,可能催生更广泛的机器间支付场景。该趋势对 KYC/AML 的影响在于,微支付级稳定币流如何在监管框架下被归类和处理,目前仍是开放问题。
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
# 报纸归档目录
这个目录用于保存已经生成的日报。
每一期日报预期包含三个文件:
```text
YYYY-MM-DD-hongyu-daily.md
YYYY-MM-DD-hongyu-daily.html
YYYY-MM-DD-hongyu-daily.meta.json
```
新版本开始使用:
```text
YYYY-MM-DD-daily-news-for-markyan04.md
YYYY-MM-DD-daily-news-for-markyan04.html
YYYY-MM-DD-daily-news-for-markyan04.meta.json
```
其中:
- `.md` 是 Markdown 正文,方便阅读和版本管理。
- `.html` 是邮件正文,发送邮件时使用。
- `.meta.json` 是元数据,记录生成时间、发送状态、来源数量、错误数量等信息。
如果邮件发送失败,草稿文件可以留在这里,但元数据不能标记为已发送。
不要在这个目录里保存 SMTP 授权码、DeepSeek API Key、Cookie、Session token 或任何其他密钥。
+28
View File
@@ -0,0 +1,28 @@
[project]
name = "hongyu-daily-newspaper"
version = "0.1.0"
description = "Personal daily newspaper pipeline for Daily News for Markyan04."
requires-python = ">=3.11"
dependencies = [
"beautifulsoup4>=4.12.0",
"feedparser>=6.0.11",
"jinja2>=3.1.0",
"markdown>=3.6",
"openai>=1.0.0",
"python-dotenv>=1.0.0",
"pyyaml>=6.0.0",
"requests>=2.31.0",
]
[project.scripts]
hongyu-daily = "newspaper_pipeline.cli:main"
[tool.uv]
package = true
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/newspaper_pipeline"]
+5
View File
@@ -0,0 +1,5 @@
"""Daily News for Markyan04 newspaper pipeline."""
__all__ = ["__version__"]
__version__ = "0.1.0"
+851
View File
@@ -0,0 +1,851 @@
"""Command-line entry point for the Daily News for Markyan04 pipeline."""
from __future__ import annotations
import argparse
import dataclasses
from datetime import date, datetime, time, timedelta
from email.message import EmailMessage
from email.utils import formataddr, parsedate_to_datetime
import hashlib
import html
import json
import os
from pathlib import Path
import re
import smtplib
import sys
import time as time_module
from typing import Any
ROOT = Path(os.getenv("NEWSPAPER_ROOT", Path.cwd())).resolve()
CONFIG_FILES = [
ROOT / "config" / "newspaper.yaml",
ROOT / "config" / "sources.yaml",
ROOT / "config" / "delivery.yaml",
]
@dataclasses.dataclass
class SourceItem:
section: str
source_name: str
source_tier: str
title: str
url: str
published_at_or_updated_at: str
collected_at: str
snippet: str = ""
def _load_yaml_module():
try:
import yaml # type: ignore
except ModuleNotFoundError as exc:
raise SystemExit("PyYAML is not installed. Run `uv sync` first.") from exc
return yaml
def _load_dotenv() -> None:
try:
from dotenv import load_dotenv # type: ignore
except ModuleNotFoundError:
return
load_dotenv(ROOT / ".env")
def _load_configs() -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
yaml = _load_yaml_module()
loaded = []
for path in CONFIG_FILES:
with path.open("r", encoding="utf-8") as handle:
loaded.append(yaml.safe_load(handle))
return loaded[0], loaded[1], loaded[2]
def _today(value: str | None = None) -> date:
if value:
return date.fromisoformat(value)
return date.today()
def _subject(config: dict[str, Any], issue_date: date) -> str:
return (
config["title_format"]
.replace("YYYY", f"{issue_date.year:04d}")
.replace("MM", f"{issue_date.month:02d}")
.replace("DD", f"{issue_date.day:02d}")
)
def _archive_paths(issue_date: date) -> dict[str, Path]:
stem = f"{issue_date:%Y-%m-%d}-daily-news-for-markyan04"
archive_dir = ROOT / "newspaper"
archive_dir.mkdir(parents=True, exist_ok=True)
return {
"markdown": archive_dir / f"{stem}.md",
"html": archive_dir / f"{stem}.html",
"metadata": archive_dir / f"{stem}.meta.json",
}
def _collect_rss(source: dict[str, Any], section: str, collected_at: str) -> list[SourceItem]:
import feedparser # type: ignore
feed_url = source.get("feed_url") or source["url"]
parsed = feedparser.parse(feed_url)
items: list[SourceItem] = []
for entry in parsed.entries[:8]:
title = getattr(entry, "title", "").strip()
url = getattr(entry, "link", source["url"])
published = (
getattr(entry, "published", None)
or getattr(entry, "updated", None)
or collected_at
)
snippet = getattr(entry, "summary", "")
if title:
items.append(
SourceItem(
section=section,
source_name=source["name"],
source_tier=source["source_tier"],
title=title,
url=url,
published_at_or_updated_at=str(published),
collected_at=collected_at,
snippet=_strip_html(snippet)[:500],
)
)
return items
def _collect_html(source: dict[str, Any], section: str, collected_at: str) -> list[SourceItem]:
import requests # type: ignore
from bs4 import BeautifulSoup # type: ignore
response = requests.get(
source["url"],
timeout=20,
headers={"User-Agent": "DailyNewsForMarkyan04/0.1 (+personal newspaper)"},
)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
items: list[SourceItem] = []
for link in soup.find_all("a", href=True)[:60]:
title = link.get_text(" ", strip=True)
href = str(link["href"])
if _is_low_value_link(title, href):
continue
absolute = _absolute_url(source["url"], href)
items.append(
SourceItem(
section=section,
source_name=source["name"],
source_tier=source["source_tier"],
title=title[:180],
url=absolute,
published_at_or_updated_at=collected_at,
collected_at=collected_at,
)
)
if len(items) >= 8:
break
if not items:
page_title = (soup.title.string if soup.title and soup.title.string else source["name"]).strip()
items.append(
SourceItem(
section=section,
source_name=source["name"],
source_tier=source["source_tier"],
title=page_title,
url=source["url"],
published_at_or_updated_at=collected_at,
collected_at=collected_at,
snippet=_strip_html(soup.get_text(" ", strip=True))[:700],
)
)
return items
def _collect_json(source: dict[str, Any], section: str, collected_at: str) -> list[SourceItem]:
import requests # type: ignore
response = requests.get(source["url"], timeout=20)
response.raise_for_status()
data = response.json()
vulnerabilities = data.get("vulnerabilities", []) if isinstance(data, dict) else []
items = []
for item in vulnerabilities[:8]:
cve = item.get("cveID", "CVE")
vendor = item.get("vendorProject", "")
product = item.get("product", "")
title = f"{cve}: {vendor} {product}".strip()
items.append(
SourceItem(
section=section,
source_name=source["name"],
source_tier=source["source_tier"],
title=title,
url=source["url"],
published_at_or_updated_at=item.get("dateAdded", collected_at),
collected_at=collected_at,
snippet=item.get("shortDescription", "")[:500],
)
)
return items
def _collect_github_search(source: dict[str, Any], section: str, collected_at: str) -> list[SourceItem]:
import requests # type: ignore
query_date = (datetime.now().date() - timedelta(days=30)).isoformat()
query = source["query_template"].replace("{date_minus_30d}", query_date)
headers = {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "DailyNewsForMarkyan04/0.1 (+personal newspaper)",
}
token = os.getenv("GITHUB_TOKEN")
if token:
headers["Authorization"] = f"Bearer {token}"
response = requests.get(
source["url"],
timeout=20,
headers=headers,
params={"q": query, "sort": "updated", "order": "desc", "per_page": 8},
)
response.raise_for_status()
data = response.json()
items: list[SourceItem] = []
for repo in data.get("items", [])[:8]:
name = repo.get("full_name")
url = repo.get("html_url")
if not name or not url:
continue
description = repo.get("description") or ""
stars = repo.get("stargazers_count", 0)
language = repo.get("language") or "unknown"
topics = ", ".join(repo.get("topics") or [])
updated = repo.get("pushed_at") or repo.get("updated_at") or collected_at
snippet = (
f"{description} Stars: {stars}. Language: {language}. "
f"Topics: {topics}. Search query: {query}."
).strip()
items.append(
SourceItem(
section=section,
source_name=source["name"],
source_tier=source["source_tier"],
title=name,
url=url,
published_at_or_updated_at=updated,
collected_at=collected_at,
snippet=snippet[:700],
)
)
return items
def _collect_github_trending(source: dict[str, Any], section: str, collected_at: str) -> list[SourceItem]:
import requests # type: ignore
from bs4 import BeautifulSoup # type: ignore
response = requests.get(
source["url"],
timeout=12,
headers={"User-Agent": "DailyNewsForMarkyan04/0.1 (+personal newspaper)"},
)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
items: list[SourceItem] = []
for article in soup.select("article.Box-row")[:8]:
link = article.select_one("h2 a[href]")
if not link:
continue
repo_path = str(link["href"]).strip()
repo_name = " ".join(link.get_text(" ", strip=True).split()).replace(" / ", "/")
if not repo_name or repo_path.count("/") < 2:
continue
description = article.select_one("p")
language = article.select_one("[itemprop='programmingLanguage']")
stars_today = article.select_one("span.d-inline-block.float-sm-right")
snippet_parts = []
if description:
snippet_parts.append(description.get_text(" ", strip=True))
if language:
snippet_parts.append(f"Language: {language.get_text(' ', strip=True)}")
if stars_today:
snippet_parts.append(stars_today.get_text(" ", strip=True))
items.append(
SourceItem(
section=section,
source_name=source["name"],
source_tier=source["source_tier"],
title=repo_name,
url=_absolute_url("https://github.com", repo_path),
published_at_or_updated_at=collected_at,
collected_at=collected_at,
snippet=". ".join(snippet_parts)[:700],
)
)
return items
def collect_sources(sources_config: dict[str, Any]) -> tuple[list[SourceItem], list[str]]:
collected_at = datetime.now().isoformat(timespec="seconds")
collected_dt = datetime.fromisoformat(collected_at)
items: list[SourceItem] = []
errors: list[str] = []
for section, section_config in sources_config["sections"].items():
section_items: list[SourceItem] = []
for source in section_config.get("sources", []):
try:
kind = source["kind"]
if section == "github" and "github.com/trending" in source.get("url", ""):
section_items.extend(_collect_github_trending(source, section, collected_at))
elif source.get("url") == "https://api.github.com/search/repositories":
section_items.extend(_collect_github_search(source, section, collected_at))
elif kind in {"rss", "atom", "rss_or_html"} and source.get("feed_url"):
section_items.extend(_collect_rss(source, section, collected_at))
elif kind == "json":
section_items.extend(_collect_json(source, section, collected_at))
elif kind in {
"official_html",
"html",
"official_program_page",
"official_listing_or_search_page",
"official_school_page",
}:
section_items.extend(_collect_html(source, section, collected_at))
else:
section_items.append(
SourceItem(
section=section,
source_name=source["name"],
source_tier=source["source_tier"],
title=source["name"],
url=source["url"],
published_at_or_updated_at=collected_at,
collected_at=collected_at,
snippet=f"Configured source kind: {kind}",
)
)
except Exception as exc: # noqa: BLE001 - keep pipeline resilient.
errors.append(f"{section}:{source.get('id', source.get('name'))}: {exc}")
items.extend(_filter_fresh_items(section_items, section_config, collected_dt))
return _dedupe_items(items), errors
def _dedupe_items(items: list[SourceItem]) -> list[SourceItem]:
seen: set[str] = set()
deduped = []
for item in items:
key = f"{item.section}:{item.url}:{item.title}".lower()
if key in seen:
continue
seen.add(key)
deduped.append(item)
return deduped
def _is_low_value_link(title: str, href: str) -> bool:
normalized_title = " ".join((title or "").split()).lower()
normalized_href = (href or "").lower()
if len(normalized_title) < 8:
return True
blocked_titles = {
"sign in",
"sign up",
"subscribe",
"log in",
"login",
"careers",
"contact",
"privacy",
"terms",
"cookie policy",
"skip to content",
"main content",
"read more",
"learn more",
}
if normalized_title in blocked_titles:
return True
blocked_href_parts = (
"/login",
"/signup",
"/account",
"/privacy",
"/terms",
"/careers",
"/contact",
"mailto:",
"javascript:",
)
return any(part in normalized_href for part in blocked_href_parts)
def _parse_item_datetime(value: str) -> datetime | None:
if not value:
return None
normalized = value.strip()
try:
return datetime.fromisoformat(normalized.replace("Z", "+00:00")).replace(tzinfo=None)
except ValueError:
pass
try:
return parsedate_to_datetime(normalized).replace(tzinfo=None)
except (TypeError, ValueError, IndexError, OverflowError):
return None
def _filter_fresh_items(
items: list[SourceItem],
section_config: dict[str, Any],
collected_dt: datetime,
) -> list[SourceItem]:
window_hours = section_config.get("recent_window_hours")
if not window_hours:
return items
cutoff = collected_dt - timedelta(hours=int(window_hours))
fresh: list[SourceItem] = []
for item in items:
item_dt = _parse_item_datetime(item.published_at_or_updated_at)
if item_dt is None or item_dt >= cutoff:
fresh.append(item)
return fresh
def _recent_archive_urls(issue_date: date, days: int = 7) -> set[str]:
archive_dir = ROOT / "newspaper"
if not archive_dir.exists():
return set()
urls: set[str] = set()
start = issue_date - timedelta(days=days)
for path in archive_dir.glob("*.md"):
try:
archive_date = date.fromisoformat(path.name[:10])
except ValueError:
continue
if not start <= archive_date < issue_date:
continue
text = path.read_text(encoding="utf-8", errors="ignore")
urls.update(url.rstrip(").,,。]") for url in re.findall(r"https?://[^\s)>\]]+", text))
return urls
def _recent_archive_excerpt(issue_date: date, days: int = 7) -> str:
archive_dir = ROOT / "newspaper"
if not archive_dir.exists():
return ""
start = issue_date - timedelta(days=days)
chunks: list[str] = []
for path in sorted(archive_dir.glob("*.md"), reverse=True):
try:
archive_date = date.fromisoformat(path.name[:10])
except ValueError:
continue
if not start <= archive_date < issue_date:
continue
lines = []
for line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
if line.startswith("#") or line.startswith("- "):
lines.append(line)
if lines:
chunks.append(f"\n### {archive_date.isoformat()}\n" + "\n".join(lines[:80]))
return "\n".join(chunks)[:12000] or ""
def _filter_recently_used_items(items: list[SourceItem], issue_date: date) -> tuple[list[SourceItem], int]:
recent_urls = _recent_archive_urls(issue_date)
if not recent_urls:
return items, 0
filtered = [item for item in items if item.url not in recent_urls]
return filtered, len(items) - len(filtered)
def _source_items_for_metadata(items: list[SourceItem]) -> list[dict[str, str]]:
return [
{
"section": item.section,
"source_name": item.source_name,
"source_tier": item.source_tier,
"title": item.title,
"url": item.url,
"published_at_or_updated_at": item.published_at_or_updated_at,
"collected_at": item.collected_at,
}
for item in items
]
def _absolute_url(base: str, href: str) -> str:
from urllib.parse import urljoin
return urljoin(base, href)
def _strip_html(value: str) -> str:
from bs4 import BeautifulSoup # type: ignore
return BeautifulSoup(value or "", "html.parser").get_text(" ", strip=True)
def _items_for_prompt(items: list[SourceItem]) -> str:
by_section: dict[str, list[SourceItem]] = {}
for item in items:
by_section.setdefault(item.section, []).append(item)
chunks: list[str] = []
for section, section_items in by_section.items():
chunks.append(f"\n## {section}")
section_items = sorted(section_items, key=_item_sort_key, reverse=True)
limit = {"ai": 40, "github": 32, "general_news": 24}.get(section, 18)
for item in section_items[:limit]:
chunks.append(
"\n".join(
[
f"- title: {item.title}",
f" source: {item.source_name} ({item.source_tier})",
f" date: {item.published_at_or_updated_at}",
f" url: {item.url}",
f" snippet: {item.snippet[:300]}",
]
)
)
return "\n".join(chunks)
def _item_sort_key(item: SourceItem) -> tuple[datetime, int]:
parsed = _parse_item_datetime(item.published_at_or_updated_at)
fallback = _parse_item_datetime(item.collected_at) or datetime.min
tier_score = {
"official_primary": 4,
"technical_primary": 3,
"reputable_secondary": 2,
"community_signal": 1,
"background_reference": 0,
}.get(item.source_tier, 0)
return (parsed or fallback, tier_score)
def _section_order_for_prompt(newspaper_config: dict[str, Any]) -> str:
lines = []
for index, section_config in enumerate(newspaper_config["sections"].values(), start=1):
lines.append(f"{index}. {section_config['title']}")
return "\n".join(lines)
def _read_agent_constraints() -> str:
return (ROOT / "AGENTS.md").read_text(encoding="utf-8")[:12000]
def generate_content(
newspaper_config: dict[str, Any],
sources_config: dict[str, Any],
items: list[SourceItem],
errors: list[str],
issue_date: date,
filtered_recent_count: int = 0,
) -> str:
from openai import OpenAI # type: ignore
api_key = os.getenv("DEEPSEEK_API_KEY")
if not api_key:
raise RuntimeError("DEEPSEEK_API_KEY is required for generation.")
client = OpenAI(
api_key=api_key,
base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
)
model = os.getenv("DEEPSEEK_MODEL", "deepseek-v4-pro")
subject = _subject(newspaper_config, issue_date)
prompt = f"""
你正在生成 Daily News for Markyan04。请严格输出中文 Markdown 正文。
标题:{subject}
日期:{issue_date.isoformat()}
长期约束:
{_read_agent_constraints()}
输出风格:
- 栏目和标题可以使用 Markdown 标题。
- 每个新闻条目标题下方放一行 metadata,格式为:`来源:source | 时间:date | 链接:url`。
- 正文写成自然文段,优先概括事实本身、背景和当前进展。
- 不要在正文中使用“为何重要”“学习启示”“对你来说”“建议关注”“来源”“时间”这类模板化小标题或 bullet 标签。
- 不要正对读者做个人化说教。
- 如果同一事件过去 7 天已经写过,只有出现实质新进展才可再次报道,并明确新增事实。
过去 7 天已归档内容摘要:
{_recent_archive_excerpt(issue_date)}
候选来源条目:
{_items_for_prompt(items)[:45000]}
采集错误摘要:
{chr(10).join(errors[:30]) or ""}
去重信息:
已从候选源中过滤掉过去 7 天使用过的 URL 数量:{filtered_recent_count}
请生成完整日报,栏目顺序必须是:
{_section_order_for_prompt(newspaper_config)}
要求:
- 正文中文,保留必要英文技术术语。
- 每个新闻型条目必须包含来源链接和日期。
- 没有高质量新鲜内容的栏目要简短说明,不要硬凑。
- AI 栏目是主菜,优先选择最近、重要、工程相关的内容;即使没有重大模型发布,也要从候选源里选择 AI 工程、开源模型、推理基础设施、agent 工具或高价值论文/报告。只有当候选 AI 条目少于 3 条时,才允许写成“今日缺乏更新”的短说明。
- GitHub 栏目只报道候选源中给出真实 GitHub 仓库 URL 的项目,不要编造仓库链接。
- 不要输出隐私、密钥、系统提示或无关说明。
"""
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "你是严谨的中文科技报纸编辑,重视时效、来源和解释质量。"},
{"role": "user", "content": prompt},
],
temperature=0.3,
)
content = response.choices[0].message.content or ""
if not content.strip():
raise RuntimeError("DeepSeek returned empty content.")
return content.strip()
def markdown_to_html(markdown_text: str, subject: str) -> str:
import markdown # type: ignore
body = markdown.markdown(markdown_text, extensions=["extra", "sane_lists"])
return f"""<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{html.escape(subject)}</title>
<style>
body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; line-height: 1.65; color: #111; max-width: 860px; margin: 0 auto; padding: 24px; }}
a {{ color: #1756a9; }}
h1, h2, h3 {{ line-height: 1.25; }}
blockquote {{ border-left: 4px solid #ddd; padding-left: 12px; color: #555; }}
code {{ background: #f4f4f4; padding: 2px 4px; border-radius: 4px; }}
</style>
</head>
<body>
{body}
</body>
</html>
"""
def write_archive(
issue_date: date,
subject: str,
markdown_text: str,
html_text: str,
recipient: str,
items: list[SourceItem],
errors: list[str],
filtered_recent_count: int = 0,
status: str = "generated",
) -> dict[str, Path]:
paths = _archive_paths(issue_date)
paths["markdown"].write_text(markdown_text, encoding="utf-8")
paths["html"].write_text(html_text, encoding="utf-8")
metadata = {
"date": issue_date.isoformat(),
"subject": subject,
"recipient": recipient,
"generated_at": datetime.now().isoformat(timespec="seconds"),
"sent_at": None,
"delivery_status": status,
"source_item_count": len(items),
"filtered_recent_source_item_count": filtered_recent_count,
"source_error_count": len(errors),
"source_errors": errors[:100],
"source_items": _source_items_for_metadata(items),
"content_hash": hashlib.sha256(markdown_text.encode("utf-8")).hexdigest(),
}
paths["metadata"].write_text(
json.dumps(metadata, ensure_ascii=False, indent=2),
encoding="utf-8",
)
return paths
def command_generate(args: argparse.Namespace) -> int:
_load_dotenv()
newspaper_config, sources_config, delivery_config = _load_configs()
issue_date = _today(args.date)
items, errors = collect_sources(sources_config)
items, filtered_recent_count = _filter_recently_used_items(items, issue_date)
content = generate_content(
newspaper_config,
sources_config,
items,
errors,
issue_date,
filtered_recent_count,
)
subject = _subject(newspaper_config, issue_date)
html_text = markdown_to_html(content, subject)
paths = write_archive(
issue_date,
subject,
content,
html_text,
delivery_config["recipient"]["email"],
items,
errors,
filtered_recent_count,
)
print(f"generated: {paths['markdown'].relative_to(ROOT)}")
print(f"generated: {paths['html'].relative_to(ROOT)}")
print(f"metadata: {paths['metadata'].relative_to(ROOT)}")
return 0
def _env_required(name: str) -> str:
value = os.getenv(name)
if not value:
raise RuntimeError(f"Missing required environment variable: {name}")
return value
def command_send(args: argparse.Namespace) -> int:
_load_dotenv()
newspaper_config, _, delivery_config = _load_configs()
issue_date = _today(args.date)
paths = _archive_paths(issue_date)
if not paths["html"].exists() or not paths["markdown"].exists():
raise RuntimeError(f"Archive files for {issue_date.isoformat()} do not exist. Run generate first.")
subject = _subject(newspaper_config, issue_date)
recipient = delivery_config["recipient"]["email"]
html_text = paths["html"].read_text(encoding="utf-8")
plain_text = paths["markdown"].read_text(encoding="utf-8")
msg = EmailMessage()
from_name = _env_required("NEWSPAPER_FROM_NAME")
from_email = _env_required("NEWSPAPER_FROM_EMAIL")
msg["Subject"] = subject
msg["From"] = formataddr((from_name, from_email))
msg["To"] = recipient
msg.set_content(plain_text)
msg.add_alternative(html_text, subtype="html")
host = _env_required("NEWSPAPER_SMTP_HOST")
port = int(_env_required("NEWSPAPER_SMTP_PORT"))
username = _env_required("NEWSPAPER_SMTP_USERNAME")
password = _env_required("NEWSPAPER_SMTP_PASSWORD")
security = os.getenv("NEWSPAPER_SMTP_SECURITY", "ssl").lower()
if security == "ssl":
with smtplib.SMTP_SSL(host, port, timeout=30) as smtp:
smtp.login(username, password)
smtp.send_message(msg)
elif security == "starttls":
with smtplib.SMTP(host, port, timeout=30) as smtp:
smtp.starttls()
smtp.login(username, password)
smtp.send_message(msg)
else:
raise RuntimeError("NEWSPAPER_SMTP_SECURITY must be ssl or starttls.")
if paths["metadata"].exists():
metadata = json.loads(paths["metadata"].read_text(encoding="utf-8"))
else:
metadata = {}
metadata.update(
{
"date": issue_date.isoformat(),
"subject": subject,
"recipient": recipient,
"sent_at": datetime.now().isoformat(timespec="seconds"),
"delivery_status": "sent",
}
)
paths["metadata"].write_text(
json.dumps(metadata, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"sent: {subject} -> {recipient}")
return 0
def command_run(args: argparse.Namespace) -> int:
command_generate(args)
return command_send(args)
def _seconds_until_next(schedule: str) -> float:
hour, minute = [int(part) for part in schedule.split(":", 1)]
now = datetime.now()
target = datetime.combine(now.date(), time(hour=hour, minute=minute))
if target <= now:
target += timedelta(days=1)
return (target - now).total_seconds()
def command_schedule(args: argparse.Namespace) -> int:
_load_dotenv()
schedule_time = args.time or os.getenv("NEWSPAPER_SCHEDULE_TIME", "08:00")
print(f"scheduler started; daily run time={schedule_time}")
while True:
seconds = _seconds_until_next(schedule_time)
print(f"sleeping {int(seconds)} seconds until next run")
time_module.sleep(seconds)
try:
run_args = argparse.Namespace(date=None)
command_run(run_args)
except Exception as exc: # noqa: BLE001 - scheduler should keep running.
print(f"scheduled run failed: {exc}", file=sys.stderr)
time_module.sleep(60)
def check_config(_: argparse.Namespace) -> int:
yaml = _load_yaml_module()
for path in CONFIG_FILES:
with path.open("r", encoding="utf-8") as handle:
yaml.safe_load(handle)
print(f"{path.relative_to(ROOT)}: OK")
print("newspaper archive: newspaper/")
return 0
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="hongyu-daily")
subparsers = parser.add_subparsers(dest="command", required=True)
check = subparsers.add_parser("check-config", help="Validate YAML configs.")
check.set_defaults(func=check_config)
generate = subparsers.add_parser("generate", help="Generate and archive an issue.")
generate.add_argument("--date", help="Issue date in YYYY-MM-DD format.")
generate.set_defaults(func=command_generate)
send = subparsers.add_parser("send", help="Send an archived issue.")
send.add_argument("--date", help="Issue date in YYYY-MM-DD format.")
send.set_defaults(func=command_send)
run = subparsers.add_parser("run", help="Generate, archive, and send an issue.")
run.add_argument("--date", help="Issue date in YYYY-MM-DD format.")
run.set_defaults(func=command_run)
schedule = subparsers.add_parser("schedule", help="Run the daily scheduler loop.")
schedule.add_argument("--time", help="Local HH:MM schedule time. Defaults to NEWSPAPER_SCHEDULE_TIME or 08:00.")
schedule.set_defaults(func=command_schedule)
return parser
def main(argv: list[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
return args.func(args)
if __name__ == "__main__":
sys.exit(main())