✨ 添加 OpenWebUI 社区统计功能
- 新增统计脚本 scripts/openwebui_stats.py - 新增 GitHub Actions 每日自动更新统计 - README 中英文版添加统计徽章和热门插件 Top 5 - 统计数据输出到 docs/community-stats.md 和 JSON
This commit is contained in:
54
.github/workflows/community-stats.yml
vendored
Normal file
54
.github/workflows/community-stats.yml
vendored
Normal 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
|
||||
23
README.md
23
README.md
@@ -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.
|
||||
|
||||
23
README_CN.md
23
README_CN.md
@@ -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
168
docs/community-stats.json
Normal 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
34
docs/community-stats.md
Normal 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
404
scripts/openwebui_stats.py
Normal 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())
|
||||
Reference in New Issue
Block a user