添加 OpenWebUI 社区统计功能

- 新增统计脚本 scripts/openwebui_stats.py
- 新增 GitHub Actions 每日自动更新统计
- README 中英文版添加统计徽章和热门插件 Top 5
- 统计数据输出到 docs/community-stats.md 和 JSON
This commit is contained in:
fujie
2026-01-06 01:32:38 +08:00
parent 4b9790df00
commit 53f04debaf
6 changed files with 706 additions and 0 deletions

54
.github/workflows/community-stats.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
# OpenWebUI 社区统计报告自动生成
# 每天自动获取并更新社区统计数据
name: Community Stats
on:
# 每天 UTC 8:00 (北京时间 16:00) 自动运行
schedule:
- cron: '0 8 * * *'
# 手动触发
workflow_dispatch:
permissions:
contents: write
jobs:
update-stats:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install requests python-dotenv
- name: Generate stats report
env:
OPENWEBUI_API_KEY: ${{ secrets.OPENWEBUI_API_KEY }}
OPENWEBUI_USER_ID: ${{ secrets.OPENWEBUI_USER_ID }}
run: |
python scripts/openwebui_stats.py
- name: Check for changes
id: check_changes
run: |
git diff --quiet docs/community-stats.md README.md README_CN.md || echo "changed=true" >> $GITHUB_OUTPUT
- name: Commit and push changes
if: steps.check_changes.outputs.changed == 'true'
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add docs/community-stats.md docs/community-stats.json README.md README_CN.md
git commit -m "📊 更新社区统计数据 $(date +'%Y-%m-%d')"
git push

View File

@@ -1,5 +1,28 @@
# OpenWebUI Extras
<!-- STATS_START -->
## 📊 社区统计
> 🕐 自动更新于 2026-01-06
| 📝 发布 | ⬇️ 下载 | 👁️ 浏览 | 👍 点赞 | 💾 收藏 |
|:---:|:---:|:---:|:---:|:---:|
| **11** | **729** | **7627** | **54** | **43** |
### 🔥 热门插件 Top 5
| 排名 | 插件 | 下载 | 浏览 |
|:---:|------|:---:|:---:|
| 🥇 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/f/turn_any_text_into_beautiful_mind_maps_3094c59a) | 212 | 1892 |
| 🥈 | [Export to Excel](https://openwebui.com/f/export_mulit_table_to_excel_244b8f9d) | 167 | 433 |
| 🥉 | [Async Context Compression](https://openwebui.com/f/async_context_compression_b1655bc8) | 110 | 1185 |
| 4⃣ | [Flash Card ](https://openwebui.com/f/flash_card_65a2ea8f) | 70 | 1332 |
| 5⃣ | [Smart Infographic](https://openwebui.com/f/smart_infographic_ad6f0c7f) | 62 | 850 |
*完整统计请查看 [社区统计报告](./docs/community-stats.md)*
<!-- STATS_END -->
English | [中文](./README_CN.md)
A collection of enhancements, plugins, and prompts for [OpenWebUI](https://github.com/open-webui/open-webui), developed and curated for personal use to extend functionality and improve experience.

View File

@@ -1,5 +1,28 @@
# OpenWebUI Extras
<!-- STATS_START -->
## 📊 社区统计
> 🕐 自动更新于 2026-01-06
| 📝 发布 | ⬇️ 下载 | 👁️ 浏览 | 👍 点赞 | 💾 收藏 |
|:---:|:---:|:---:|:---:|:---:|
| **11** | **729** | **7627** | **54** | **43** |
### 🔥 热门插件 Top 5
| 排名 | 插件 | 下载 | 浏览 |
|:---:|------|:---:|:---:|
| 🥇 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/f/turn_any_text_into_beautiful_mind_maps_3094c59a) | 212 | 1892 |
| 🥈 | [Export to Excel](https://openwebui.com/f/export_mulit_table_to_excel_244b8f9d) | 167 | 433 |
| 🥉 | [Async Context Compression](https://openwebui.com/f/async_context_compression_b1655bc8) | 110 | 1185 |
| 4⃣ | [Flash Card ](https://openwebui.com/f/flash_card_65a2ea8f) | 70 | 1332 |
| 5⃣ | [Smart Infographic](https://openwebui.com/f/smart_infographic_ad6f0c7f) | 62 | 850 |
*完整统计请查看 [社区统计报告](./docs/community-stats.md)*
<!-- STATS_END -->
[English](./README.md) | 中文
OpenWebUI 增强功能集合。包含个人开发与收集的### 🧩 插件 (Plugins)

168
docs/community-stats.json Normal file
View File

@@ -0,0 +1,168 @@
{
"total_posts": 11,
"total_downloads": 729,
"total_views": 7627,
"total_upvotes": 54,
"total_downvotes": 0,
"total_saves": 43,
"total_comments": 13,
"by_type": {
"unknown": 11
},
"posts": [
{
"title": "Turn Any Text into Beautiful Mind Maps",
"slug": "turn_any_text_into_beautiful_mind_maps_3094c59a",
"type": "unknown",
"version": "",
"downloads": 212,
"views": 1892,
"upvotes": 10,
"saves": 15,
"comments": 8,
"created_at": "2025-12-31",
"updated_at": "2026-01-04",
"url": "https://openwebui.com/f/turn_any_text_into_beautiful_mind_maps_3094c59a"
},
{
"title": "Export to Excel",
"slug": "export_mulit_table_to_excel_244b8f9d",
"type": "unknown",
"version": "",
"downloads": 167,
"views": 433,
"upvotes": 3,
"saves": 2,
"comments": 0,
"created_at": "2025-05-30",
"updated_at": "2026-01-03",
"url": "https://openwebui.com/f/export_mulit_table_to_excel_244b8f9d"
},
{
"title": "Async Context Compression",
"slug": "async_context_compression_b1655bc8",
"type": "unknown",
"version": "",
"downloads": 110,
"views": 1185,
"upvotes": 5,
"saves": 9,
"comments": 0,
"created_at": "2025-11-08",
"updated_at": "2025-12-31",
"url": "https://openwebui.com/f/async_context_compression_b1655bc8"
},
{
"title": "Flash Card ",
"slug": "flash_card_65a2ea8f",
"type": "unknown",
"version": "",
"downloads": 70,
"views": 1332,
"upvotes": 8,
"saves": 5,
"comments": 2,
"created_at": "2025-12-30",
"updated_at": "2026-01-04",
"url": "https://openwebui.com/f/flash_card_65a2ea8f"
},
{
"title": "Smart Infographic",
"slug": "smart_infographic_ad6f0c7f",
"type": "unknown",
"version": "",
"downloads": 62,
"views": 850,
"upvotes": 6,
"saves": 7,
"comments": 2,
"created_at": "2025-12-28",
"updated_at": "2026-01-04",
"url": "https://openwebui.com/f/smart_infographic_ad6f0c7f"
},
{
"title": "Export to Word (Enhanced Formatting)",
"slug": "export_to_word_enhanced_formatting_fca6a315",
"type": "unknown",
"version": "",
"downloads": 37,
"views": 419,
"upvotes": 5,
"saves": 2,
"comments": 0,
"created_at": "2026-01-03",
"updated_at": "2026-01-05",
"url": "https://openwebui.com/f/export_to_word_enhanced_formatting_fca6a315"
},
{
"title": "智能信息图",
"slug": "智能信息图_e04a48ff",
"type": "unknown",
"version": "",
"downloads": 31,
"views": 372,
"upvotes": 3,
"saves": 0,
"comments": 0,
"created_at": "2025-12-28",
"updated_at": "2025-12-29",
"url": "https://openwebui.com/f/智能信息图_e04a48ff"
},
{
"title": "导出为 Word-支持公式、流程图、表格和代码块",
"slug": "导出为_word_支持公式流程图表格和代码块_8a6306c0",
"type": "unknown",
"version": "",
"downloads": 13,
"views": 502,
"upvotes": 7,
"saves": 1,
"comments": 1,
"created_at": "2026-01-04",
"updated_at": "2026-01-05",
"url": "https://openwebui.com/f/导出为_word_支持公式流程图表格和代码块_8a6306c0"
},
{
"title": "闪记卡生成插件",
"slug": "闪记卡生成插件_4a31eac3",
"type": "unknown",
"version": "",
"downloads": 12,
"views": 305,
"upvotes": 3,
"saves": 1,
"comments": 0,
"created_at": "2025-12-30",
"updated_at": "2026-01-01",
"url": "https://openwebui.com/f/闪记卡生成插件_4a31eac3"
},
{
"title": "智能生成交互式思维导图,帮助用户可视化知识",
"slug": "智能生成交互式思维导图帮助用户可视化知识_8d4b097b",
"type": "unknown",
"version": "",
"downloads": 10,
"views": 233,
"upvotes": 2,
"saves": 0,
"comments": 0,
"created_at": "2025-12-31",
"updated_at": "2025-12-31",
"url": "https://openwebui.com/f/智能生成交互式思维导图帮助用户可视化知识_8d4b097b"
},
{
"title": "异步上下文压缩",
"slug": "异步上下文压缩_5c0617cb",
"type": "unknown",
"version": "",
"downloads": 5,
"views": 104,
"upvotes": 2,
"saves": 1,
"comments": 0,
"created_at": "2025-11-08",
"updated_at": "2025-12-31",
"url": "https://openwebui.com/f/异步上下文压缩_5c0617cb"
}
]
}

34
docs/community-stats.md Normal file
View File

@@ -0,0 +1,34 @@
# 📊 OpenWebUI 社区统计报告
> 📅 更新时间: 2026-01-06 01:31:12
## 📈 总览
| 指标 | 数值 |
|------|------|
| 📝 发布数量 | 11 |
| ⬇️ 总下载量 | 729 |
| 👁️ 总浏览量 | 7627 |
| 👍 总点赞数 | 54 |
| 💾 总收藏数 | 43 |
| 💬 总评论数 | 13 |
## 📂 按类型分类
- **unknown**: 11
## 📋 发布列表
| 排名 | 标题 | 类型 | 版本 | 下载 | 浏览 | 点赞 | 收藏 | 更新日期 |
|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| 1 | [Turn Any Text into Beautiful Mind Maps](https://openwebui.com/f/turn_any_text_into_beautiful_mind_maps_3094c59a) | unknown | | 212 | 1892 | 10 | 15 | 2026-01-04 |
| 2 | [Export to Excel](https://openwebui.com/f/export_mulit_table_to_excel_244b8f9d) | unknown | | 167 | 433 | 3 | 2 | 2026-01-03 |
| 3 | [Async Context Compression](https://openwebui.com/f/async_context_compression_b1655bc8) | unknown | | 110 | 1185 | 5 | 9 | 2025-12-31 |
| 4 | [Flash Card ](https://openwebui.com/f/flash_card_65a2ea8f) | unknown | | 70 | 1332 | 8 | 5 | 2026-01-04 |
| 5 | [Smart Infographic](https://openwebui.com/f/smart_infographic_ad6f0c7f) | unknown | | 62 | 850 | 6 | 7 | 2026-01-04 |
| 6 | [Export to Word (Enhanced Formatting)](https://openwebui.com/f/export_to_word_enhanced_formatting_fca6a315) | unknown | | 37 | 419 | 5 | 2 | 2026-01-05 |
| 7 | [智能信息图](https://openwebui.com/f/智能信息图_e04a48ff) | unknown | | 31 | 372 | 3 | 0 | 2025-12-29 |
| 8 | [导出为 Word-支持公式、流程图、表格和代码块](https://openwebui.com/f/导出为_word_支持公式流程图表格和代码块_8a6306c0) | unknown | | 13 | 502 | 7 | 1 | 2026-01-05 |
| 9 | [闪记卡生成插件](https://openwebui.com/f/闪记卡生成插件_4a31eac3) | unknown | | 12 | 305 | 3 | 1 | 2026-01-01 |
| 10 | [智能生成交互式思维导图,帮助用户可视化知识](https://openwebui.com/f/智能生成交互式思维导图帮助用户可视化知识_8d4b097b) | unknown | | 10 | 233 | 2 | 0 | 2025-12-31 |
| 11 | [异步上下文压缩](https://openwebui.com/f/异步上下文压缩_5c0617cb) | unknown | | 5 | 104 | 2 | 1 | 2025-12-31 |

404
scripts/openwebui_stats.py Normal file
View File

@@ -0,0 +1,404 @@
#!/usr/bin/env python3
"""
OpenWebUI 社区统计工具
获取并统计你在 openwebui.com 上发布的插件/帖子数据。
使用方法:
1. 设置环境变量:
- OPENWEBUI_API_KEY: 你的 API Key
- OPENWEBUI_USER_ID: 你的用户 ID
2. 运行: python scripts/openwebui_stats.py
获取 API Key
访问 https://openwebui.com/settings/api 创建 API Key (sk-开头)
获取 User ID
从个人主页的 API 请求中获取,格式如: b15d1348-4347-42b4-b815-e053342d6cb0
"""
import os
import json
import requests
from datetime import datetime
from typing import Optional
from pathlib import Path
# 尝试加载 .env 文件
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
class OpenWebUIStats:
"""OpenWebUI 社区统计工具"""
BASE_URL = "https://api.openwebui.com/api/v1"
def __init__(self, api_key: str, user_id: Optional[str] = None):
"""
初始化统计工具
Args:
api_key: OpenWebUI API Key (JWT Token)
user_id: 用户 ID如果为 None 则从 token 中解析
"""
self.api_key = api_key
self.user_id = user_id or self._parse_user_id_from_token(api_key)
self.session = requests.Session()
self.session.headers.update(
{
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
"Content-Type": "application/json",
}
)
def _parse_user_id_from_token(self, token: str) -> str:
"""从 JWT Token 中解析用户 ID"""
import base64
try:
# JWT 格式: header.payload.signature
payload = token.split(".")[1]
# 添加 padding
padding = 4 - len(payload) % 4
if padding != 4:
payload += "=" * padding
decoded = base64.urlsafe_b64decode(payload)
data = json.loads(decoded)
return data.get("id", "")
except Exception as e:
print(f"⚠️ 无法从 Token 解析用户 ID: {e}")
return ""
def get_user_posts(self, sort: str = "new", page: int = 1) -> list:
"""
获取用户发布的帖子列表
Args:
sort: 排序方式 (new/top/hot)
page: 页码
Returns:
帖子列表
"""
url = f"{self.BASE_URL}/posts/users/{self.user_id}"
params = {"sort": sort, "page": page}
response = self.session.get(url, params=params)
response.raise_for_status()
return response.json()
def get_all_posts(self, sort: str = "new") -> list:
"""获取所有帖子(自动分页)"""
all_posts = []
page = 1
while True:
posts = self.get_user_posts(sort=sort, page=page)
if not posts:
break
all_posts.extend(posts)
page += 1
return all_posts
def generate_stats(self, posts: list) -> dict:
"""生成统计数据"""
stats = {
"total_posts": len(posts),
"total_downloads": 0,
"total_views": 0,
"total_upvotes": 0,
"total_downvotes": 0,
"total_saves": 0,
"total_comments": 0,
"by_type": {},
"posts": [],
}
for post in posts:
# 累计统计
stats["total_downloads"] += post.get("downloads", 0)
stats["total_views"] += post.get("views", 0)
stats["total_upvotes"] += post.get("upvotes", 0)
stats["total_downvotes"] += post.get("downvotes", 0)
stats["total_saves"] += post.get("saveCount", 0)
stats["total_comments"] += post.get("commentCount", 0)
# 按类型分类
post_type = post.get("data", {}).get("meta", {}).get("type", "unknown")
if post_type not in stats["by_type"]:
stats["by_type"][post_type] = 0
stats["by_type"][post_type] += 1
# 单个帖子信息
manifest = post.get("data", {}).get("meta", {}).get("manifest", {})
created_at = datetime.fromtimestamp(post.get("createdAt", 0))
updated_at = datetime.fromtimestamp(post.get("updatedAt", 0))
stats["posts"].append(
{
"title": post.get("title", ""),
"slug": post.get("slug", ""),
"type": post_type,
"version": manifest.get("version", ""),
"downloads": post.get("downloads", 0),
"views": post.get("views", 0),
"upvotes": post.get("upvotes", 0),
"saves": post.get("saveCount", 0),
"comments": post.get("commentCount", 0),
"created_at": created_at.strftime("%Y-%m-%d"),
"updated_at": updated_at.strftime("%Y-%m-%d"),
"url": f"https://openwebui.com/f/{post.get('slug', '')}",
}
)
# 按下载量排序
stats["posts"].sort(key=lambda x: x["downloads"], reverse=True)
return stats
def print_stats(self, stats: dict):
"""打印统计报告到终端"""
print("\n" + "=" * 60)
print("📊 OpenWebUI 社区统计报告")
print("=" * 60)
print(f"📅 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# 总览
print("📈 总览")
print("-" * 40)
print(f" 📝 发布数量: {stats['total_posts']}")
print(f" ⬇️ 总下载量: {stats['total_downloads']}")
print(f" 👁️ 总浏览量: {stats['total_views']}")
print(f" 👍 总点赞数: {stats['total_upvotes']}")
print(f" 💾 总收藏数: {stats['total_saves']}")
print(f" 💬 总评论数: {stats['total_comments']}")
print()
# 按类型分类
print("📂 按类型分类")
print("-" * 40)
for post_type, count in stats["by_type"].items():
print(f"{post_type}: {count}")
print()
# 详细列表
print("📋 发布列表 (按下载量排序)")
print("-" * 60)
# 表头
print(f"{'排名':<4} {'标题':<30} {'下载':<8} {'浏览':<8} {'点赞':<6}")
print("-" * 60)
for i, post in enumerate(stats["posts"], 1):
title = (
post["title"][:28] + ".." if len(post["title"]) > 30 else post["title"]
)
print(
f"{i:<4} {title:<30} {post['downloads']:<8} {post['views']:<8} {post['upvotes']:<6}"
)
print("=" * 60)
def generate_markdown(self, stats: dict) -> str:
"""生成 Markdown 格式报告"""
md = []
md.append("# 📊 OpenWebUI 社区统计报告")
md.append("")
md.append(f"> 📅 更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
md.append("")
# 总览
md.append("## 📈 总览")
md.append("")
md.append("| 指标 | 数值 |")
md.append("|------|------|")
md.append(f"| 📝 发布数量 | {stats['total_posts']} |")
md.append(f"| ⬇️ 总下载量 | {stats['total_downloads']} |")
md.append(f"| 👁️ 总浏览量 | {stats['total_views']} |")
md.append(f"| 👍 总点赞数 | {stats['total_upvotes']} |")
md.append(f"| 💾 总收藏数 | {stats['total_saves']} |")
md.append(f"| 💬 总评论数 | {stats['total_comments']} |")
md.append("")
# 按类型分类
md.append("## 📂 按类型分类")
md.append("")
for post_type, count in stats["by_type"].items():
md.append(f"- **{post_type}**: {count}")
md.append("")
# 详细列表
md.append("## 📋 发布列表")
md.append("")
md.append(
"| 排名 | 标题 | 类型 | 版本 | 下载 | 浏览 | 点赞 | 收藏 | 更新日期 |"
)
md.append("|:---:|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|")
for i, post in enumerate(stats["posts"], 1):
title_link = f"[{post['title']}]({post['url']})"
md.append(
f"| {i} | {title_link} | {post['type']} | {post['version']} | "
f"{post['downloads']} | {post['views']} | {post['upvotes']} | "
f"{post['saves']} | {post['updated_at']} |"
)
md.append("")
return "\n".join(md)
def save_json(self, stats: dict, filepath: str):
"""保存 JSON 格式数据"""
with open(filepath, "w", encoding="utf-8") as f:
json.dump(stats, f, ensure_ascii=False, indent=2)
print(f"✅ JSON 数据已保存到: {filepath}")
def generate_readme_stats(self, stats: dict) -> str:
"""生成 README 统计徽章区域"""
# 获取 Top 5 插件
top_plugins = stats["posts"][:5]
lines = []
lines.append("<!-- STATS_START -->")
lines.append("## 📊 社区统计")
lines.append("")
lines.append(f"> 🕐 自动更新于 {datetime.now().strftime('%Y-%m-%d')}")
lines.append("")
# 统计徽章 - 使用 shields.io 风格的表格
lines.append("| 📝 发布 | ⬇️ 下载 | 👁️ 浏览 | 👍 点赞 | 💾 收藏 |")
lines.append("|:---:|:---:|:---:|:---:|:---:|")
lines.append(
f"| **{stats['total_posts']}** | **{stats['total_downloads']}** | "
f"**{stats['total_views']}** | **{stats['total_upvotes']}** | **{stats['total_saves']}** |"
)
lines.append("")
# Top 5 热门插件
lines.append("### 🔥 热门插件 Top 5")
lines.append("")
lines.append("| 排名 | 插件 | 下载 | 浏览 |")
lines.append("|:---:|------|:---:|:---:|")
medals = ["🥇", "🥈", "🥉", "4", "5"]
for i, post in enumerate(top_plugins):
medal = medals[i] if i < len(medals) else str(i + 1)
lines.append(
f"| {medal} | [{post['title']}]({post['url']}) | {post['downloads']} | {post['views']} |"
)
lines.append("")
lines.append("*完整统计请查看 [社区统计报告](./docs/community-stats.md)*")
lines.append("<!-- STATS_END -->")
return "\n".join(lines)
def update_readme(self, stats: dict, readme_path: str):
"""更新 README 文件中的统计区域"""
import re
# 读取现有内容
with open(readme_path, "r", encoding="utf-8") as f:
content = f.read()
# 生成新的统计区域
new_stats = self.generate_readme_stats(stats)
# 检查是否已有统计区域
pattern = r"<!-- STATS_START -->.*?<!-- STATS_END -->"
if re.search(pattern, content, re.DOTALL):
# 替换现有区域
new_content = re.sub(pattern, new_stats, content, flags=re.DOTALL)
else:
# 在文件开头(标题之后)插入统计区域
# 找到第一个 ## 标题或在第一个空行后插入
lines = content.split("\n")
insert_pos = 0
for i, line in enumerate(lines):
if line.startswith("# "):
# 找到主标题后继续
continue
if line.startswith("[") or line.strip() == "":
insert_pos = i + 1
if line.strip() == "":
break
# 在适当位置插入
lines.insert(insert_pos, "")
lines.insert(insert_pos + 1, new_stats)
lines.insert(insert_pos + 2, "")
new_content = "\n".join(lines)
# 写回文件
with open(readme_path, "w", encoding="utf-8") as f:
f.write(new_content)
print(f"✅ README 已更新: {readme_path}")
def main():
"""主函数"""
# 获取配置
api_key = os.getenv("OPENWEBUI_API_KEY")
user_id = os.getenv("OPENWEBUI_USER_ID")
if not api_key:
print("❌ 错误: 未设置 OPENWEBUI_API_KEY 环境变量")
print("请设置环境变量:")
print(" export OPENWEBUI_API_KEY='your_api_key_here'")
return 1
if not user_id:
print("❌ 错误: 未设置 OPENWEBUI_USER_ID 环境变量")
print("请设置环境变量:")
print(" export OPENWEBUI_USER_ID='your_user_id_here'")
print("\n提示: 用户 ID 可以从之前的 curl 请求中获取")
print(" 例如: b15d1348-4347-42b4-b815-e053342d6cb0")
return 1
# 初始化
stats_client = OpenWebUIStats(api_key, user_id)
print(f"🔍 用户 ID: {stats_client.user_id}")
# 获取所有帖子
print("📥 正在获取帖子数据...")
posts = stats_client.get_all_posts()
print(f"✅ 获取到 {len(posts)} 个帖子")
# 生成统计
stats = stats_client.generate_stats(posts)
# 打印到终端
stats_client.print_stats(stats)
# 保存 Markdown 报告
script_dir = Path(__file__).parent.parent
md_path = script_dir / "docs" / "community-stats.md"
md_content = stats_client.generate_markdown(stats)
with open(md_path, "w", encoding="utf-8") as f:
f.write(md_content)
print(f"\n✅ Markdown 报告已保存到: {md_path}")
# 保存 JSON 数据
json_path = script_dir / "docs" / "community-stats.json"
stats_client.save_json(stats, str(json_path))
# 更新 README 文件
readme_path = script_dir / "README.md"
readme_cn_path = script_dir / "README_CN.md"
stats_client.update_readme(stats, str(readme_path))
stats_client.update_readme(stats, str(readme_cn_path))
return 0
if __name__ == "__main__":
exit(main())