Collect authenticated Qinglong scripts batch

This commit is contained in:
Hermes Agent
2026-05-24 03:53:42 +00:00
parent 0d80b33d25
commit 28d62fef03
86 changed files with 18420 additions and 5 deletions

View File

@@ -0,0 +1,246 @@
# Source: https://github.com/CN-Grace/QinglongScripts/blob/main/HelloGithub.py
# Raw: https://raw.githubusercontent.com/CN-Grace/QinglongScripts/main/HelloGithub.py
# Repo: CN-Grace/QinglongScripts
# Path: HelloGithub.py
# UploadedAt: 2026-05-23T15:48:24Z
# SHA256: 041c84250a0164d2dd96a531bc3bb7a90a4a49ee508c39952a74641b319915f2
# Category: APP版/抓包
# Evidence: cookie/token/authorization/header
#!/usr/bin/env python3
"""
cron: 0 8 1 * *
new Env("HelloGitHub月刊")
HelloGitHub 月刊更新提醒
每月运行一次,检查并发送最新月刊内容。
支持分多条消息发送完整内容,基于字符长度分段。
"""
import json
import os
import re
import time
import requests
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from utils import log_info, log_success, log_warning, log_error, beijing_time_str
from notifier import send as notify_send
# ==================== 用户配置 ====================
STATE_FILE = Path.home() / ".hellogithub_bot_state.json"
PERIODICAL_API = "https://abroad.hellogithub.com/v1/periodical/"
PERIODICAL_PAGE_URL = "https://hellogithub.com/periodical"
MAX_MESSAGE_LENGTH = 3900
def load_last_sent_volume() -> int:
try:
if STATE_FILE.exists():
with open(STATE_FILE, "r", encoding="utf-8") as f:
return json.load(f).get("last_sent_volume", 0)
return 0
except Exception as e:
log_error(f"读取状态文件失败: {e}")
return 0
def save_last_sent_volume(volume_num: int) -> None:
try:
state = {"last_sent_volume": volume_num, "updated_at": datetime.now().isoformat(), "last_run": beijing_time_str()}
with open(STATE_FILE, "w", encoding="utf-8") as f:
json.dump(state, f, ensure_ascii=False, indent=2)
log_success(f"已更新状态文件,最新期数: {volume_num}")
except Exception as e:
log_error(f"保存状态文件失败: {e}")
def get_latest_volume_info() -> Tuple[Optional[int], Optional[str]]:
try:
resp = requests.get(PERIODICAL_API, timeout=10)
resp.raise_for_status()
data = resp.json()
if data.get("success"):
latest = data["volumes"][0]
return latest["num"], latest["lastmod"]
log_error("API 返回 success 为 false")
return None, None
except Exception as e:
log_error(f"请求期数 API 失败: {e}")
return None, None
def extract_build_id() -> Optional[str]:
try:
resp = requests.get(PERIODICAL_PAGE_URL, timeout=10)
resp.raise_for_status()
match = re.search(r'<script id="__NEXT_DATA__" type="application/json">(.*?)</script>', resp.text, re.DOTALL)
if match:
next_data = json.loads(match.group(1))
build_id = next_data.get("buildId")
if build_id:
log_info(f"成功获取 buildId: {build_id}")
return build_id
log_error("未找到 buildId")
return None
except Exception as e:
log_error(f"提取 buildId 失败: {e}")
return None
def get_volume_content(volume_num: int, build_id: str) -> Optional[Dict]:
content_api = f"https://hellogithub.com/_next/data/{build_id}/zh/periodical/volume/{volume_num}.json"
try:
data = requests.get(content_api, timeout=10).json()
page_props = data.get("pageProps", {})
volume_data = page_props.get("volumeData") or page_props.get("volume", {})
if not volume_data:
return None
title = volume_data.get("title", f"HelloGitHub 第 {volume_num}")
desc = volume_data.get("desc", "")
categories_data = []
for category_item in volume_data.get("data", []):
formatted_items = []
for item in category_item.get("items", []):
formatted_items.append({
"full_name": item.get("full_name", "未知项目"),
"description": item.get("description", "暂无描述"),
"github_url": item.get("github_url", ""),
"stars": item.get("stars", 0),
"forks": item.get("forks", 0),
"watch": item.get("watch", 0),
"language": item.get("lang", ""),
"homepage": item.get("homepage", ""),
})
if formatted_items:
categories_data.append({"category_name": category_item.get("category_name", "未分类"), "items": formatted_items})
return {"title": title, "description": desc, "categories": categories_data, "total_items": sum(len(c["items"]) for c in categories_data), "volume_num": volume_num}
except Exception as e:
log_error(f"请求内容 API 失败: {e}")
return None
def format_project_info(project: Dict, project_num: int, total_projects: int) -> str:
name = project["full_name"]
desc = project["description"]
stars, forks, watch = project["stars"], project["forks"], project["watch"]
lang = project["language"]
github_url = project["github_url"]
lines = [f" {project_num}/{total_projects}. 【{name}"]
if lang:
lines.append(f" 语言: {lang}")
lines.append(f" 描述: {desc}")
stats = []
if stars > 0: stats.append(f"{stars}")
if forks > 0: stats.append(f"🔀 {forks}")
if watch > 0: stats.append(f"👀 {watch}")
if stats:
lines.append(f" 数据: {' | '.join(stats)}")
if github_url:
lines.append(f" 链接: {github_url}")
lines.append("")
return "\n".join(lines)
def format_category_header(category: Dict, category_num: int, total_categories: int) -> str:
category_name = category["category_name"]
items = category["items"]
return f"\n{'' * 40}\n\n🎯 {category_num}/{total_categories}. {category_name} ({len(items)} 个项目)\n\n"
def create_report_pages(content: Dict) -> List[str]:
if not content:
return []
title = content["title"]
desc = content["description"]
categories = content.get("categories", [])
total_items = content.get("total_items", 0)
volume_num = content.get("volume_num", 0)
total_categories = len(categories)
latest_num, lastmod = get_latest_volume_info()
pub_date = lastmod[:10] if lastmod else beijing_time_str("%Y-%m-%d")
# 标题页
header = f"🚀 {title}\n\n"
if desc:
header += f"📝 {desc}\n\n"
header += f"📅 发布时间: {pub_date}\n🔢 期号: 第 {volume_num}\n📚 项目总数: {total_items}\n🏷️ 分类数量: {total_categories}\n\n{'' * 40}\n"
pages = [header]
all_parts = []
cat_num = 1
for cat in categories:
all_parts.append(format_category_header(cat, cat_num, total_categories))
for i, item in enumerate(cat["items"]):
proj_num = sum(len(c["items"]) for c in categories[:cat_num - 1]) + i + 1
all_parts.append(format_project_info(item, proj_num, total_items))
cat_num += 1
current = ""
for part in all_parts:
if len(current) + len(part) <= MAX_MESSAGE_LENGTH:
current += part
else:
if current:
pages.append(f"📋 第 {volume_num} 期 - 项目详情\n\n{current}")
current = part
if len(part) > MAX_MESSAGE_LENGTH:
pages.append(part)
current = ""
if current:
pages.append(f"📋 第 {volume_num} 期 - 项目详情\n\n{current}")
read_more = f"https://hellogithub.com/zh/periodical/volume/{volume_num}/"
footer = f"\n{'' * 40}\n\n🎉 {title} 完整内容已发送完毕!\n\n📊 本期共 {total_items} 个项目,{total_categories} 个分类\n🔗 在线阅读: {read_more}\n🌟 GitHub: https://github.com/521xueweihan/HelloGitHub"
pages.append(footer)
return pages
def main():
log_info("===== HelloGitHub 月刊检查开始 =====")
last_sent = load_last_sent_volume()
log_info(f"上次发送的期数: {last_sent}")
latest_num, lastmod = get_latest_volume_info()
if not latest_num:
log_error("无法获取最新期数信息,任务终止")
return
log_info(f"最新期数: {latest_num}, 更新时间: {lastmod}")
if latest_num <= last_sent:
log_info(f"没有新刊发布 (最新: {latest_num}, 已发送: {last_sent})")
return
log_info(f"发现新刊! 第 {latest_num}")
build_id = extract_build_id()
if not build_id:
log_error("无法获取 buildId任务终止")
return
content = get_volume_content(latest_num, build_id)
if not content:
log_error(f"无法获取第 {latest_num} 期内容")
return
log_success(f"成功获取第 {latest_num} 期内容,包含 {content.get('total_items', 0)} 个项目")
pages = create_report_pages(content)
log_info(f"共生成 {len(pages)} 条消息")
for i, page_text in enumerate(pages, 1):
log_info(f"正在发送第 {i}/{len(pages)} 条消息 ({len(page_text)} 字符)...")
notify_send(f"HelloGitHub 第 {latest_num} 期 ({i}/{len(pages)})", page_text)
if i < len(pages):
time.sleep(2)
save_last_sent_volume(latest_num)
log_info("===== 任务完成 =====")
if __name__ == "__main__":
main()