Collect seeded Qinglong script repositories
This commit is contained in:
181
seed_collect_git.py
Normal file
181
seed_collect_git.py
Normal file
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Clone fixed seed repositories and collect Qinglong scripts without GitHub/Gitee API rate limits."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import urllib.parse
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
|
||||
import collector as c
|
||||
|
||||
SEEDS: List[Tuple[str, str]] = [
|
||||
("zy库", "https://gitee.com/jdqlscript/zy"),
|
||||
("QLScriptPublic", "https://github.com/smallfawn/QLScriptPublic"),
|
||||
("艾默库", "https://github.com/imoki/sign_script"),
|
||||
("i茅台", "https://github.com/AkenClub/ken-iMoutai-Script"),
|
||||
("偷撸库", "https://gitee.com/jdqlscript/toulu"),
|
||||
("滑稽库", "https://github.com/huaji8/huajiScript"),
|
||||
("其他签到库1", "https://github.com/xzxxn777/Surge"),
|
||||
("其他签到库2", "https://gitee.com/jdqlscript/zyqinglong"),
|
||||
("其他签到库3", "https://github.com/NaroisCool/naro-scripts"),
|
||||
("其他签到库4", "https://github.com/Sitoi/dailycheckin"),
|
||||
("其他签到库5", "https://github.com/sudojia/AutoTaskScript"),
|
||||
("其他签到库6", "https://github.com/985Ming/qlk"),
|
||||
("哔哩哔哩", "https://github.com/RayWangQvQ/BiliBiliToolPro"),
|
||||
("自用青龙", "https://github.com/linbailo/zyqinglong"),
|
||||
("zero205", "https://github.com/zero205/JD_tencent_scf"),
|
||||
("yangro", "https://gitee.com/yangro/ql"),
|
||||
("sync", "https://github.com/Aaron-lv/sync"),
|
||||
("JD-Script", "https://github.com/curtinlv/JD-Script"),
|
||||
("什么值得买", "https://gitee.com/jdqlscript/smzdm_script"),
|
||||
("营业厅库", "https://gitee.com/jdqlscript/yyt"),
|
||||
("其他签到库7", "https://github.com/xfmeng970526/ql"),
|
||||
("feverrun", "https://github.com/feverrun/my_scripts"),
|
||||
("WX协议本", "https://github.com/LinYuanovo/AutoTaskScripts"),
|
||||
("WX协议本1", "https://github.com/3ixi/CodeScripts"),
|
||||
]
|
||||
|
||||
CACHE_ROOT = c.META_DIR / "seed_repos"
|
||||
MAX_PER_REPO = int(os.getenv("SEED_MAX_FILES_PER_REPO", "260"))
|
||||
GIT_TIMEOUT = int(os.getenv("SEED_GIT_TIMEOUT", "180"))
|
||||
SOCKS_PROXY = os.getenv("SOCKS_PROXY", "socks5://192.168.0.182:1080")
|
||||
|
||||
|
||||
def safe_dir(url: str) -> str:
|
||||
u = urllib.parse.urlparse(url.rstrip("/"))
|
||||
parts = [p for p in u.path.split("/") if p]
|
||||
return c.safe_segment((u.netloc + "_" + "_".join(parts[:2])).replace(".", "_"), max_len=120)
|
||||
|
||||
|
||||
def repo_full(url: str) -> str:
|
||||
u = urllib.parse.urlparse(url.rstrip("/"))
|
||||
parts = [p for p in u.path.split("/") if p]
|
||||
return "/".join(parts[:2]) if len(parts) >= 2 else url
|
||||
|
||||
|
||||
def platform(url: str) -> str:
|
||||
return "Gitee" if "gitee.com" in urllib.parse.urlparse(url).netloc.lower() else "GitHub"
|
||||
|
||||
|
||||
def run(cmd: List[str], cwd: Path | None = None, use_proxy: bool = False) -> subprocess.CompletedProcess:
|
||||
env = os.environ.copy()
|
||||
if use_proxy:
|
||||
env.setdefault("ALL_PROXY", SOCKS_PROXY)
|
||||
env.setdefault("HTTPS_PROXY", SOCKS_PROXY)
|
||||
env.setdefault("HTTP_PROXY", SOCKS_PROXY)
|
||||
return subprocess.run(cmd, cwd=str(cwd) if cwd else None, env=env, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=GIT_TIMEOUT)
|
||||
|
||||
|
||||
def clone_or_update(label: str, url: str) -> Path | None:
|
||||
CACHE_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
dst = CACHE_ROOT / safe_dir(url)
|
||||
if (dst / ".git").exists():
|
||||
print(f" [GIT] update {dst.name}", flush=True)
|
||||
r = run(["git", "fetch", "--depth", "1", "origin"], cwd=dst)
|
||||
if r.returncode != 0:
|
||||
print(f" [GIT_WARN] fetch failed: {r.stdout[-400:]}", flush=True)
|
||||
r = run(["git", "reset", "--hard", "FETCH_HEAD"], cwd=dst)
|
||||
if r.returncode == 0:
|
||||
return dst
|
||||
if dst.exists():
|
||||
shutil.rmtree(dst, ignore_errors=True)
|
||||
print(f" [GIT] clone {url}", flush=True)
|
||||
r = run(["git", "clone", "--depth", "1", url, str(dst)])
|
||||
if r.returncode != 0 and "github.com" in url:
|
||||
print(" [GIT_WARN] direct clone failed, retry with SOCKS proxy", flush=True)
|
||||
r = run(["git", "clone", "--depth", "1", url, str(dst)], use_proxy=True)
|
||||
if r.returncode != 0:
|
||||
print(f" [GIT_FAIL] {label}: {r.stdout[-800:]}", flush=True)
|
||||
return None
|
||||
return dst
|
||||
|
||||
|
||||
def rel_url(url: str, branch: str, rel: str, raw: bool) -> str:
|
||||
full = repo_full(url)
|
||||
qrel = "/".join(urllib.parse.quote(p) for p in rel.split("/"))
|
||||
if platform(url) == "GitHub":
|
||||
return f"https://raw.githubusercontent.com/{full}/{branch}/{qrel}" if raw else f"https://github.com/{full}/blob/{branch}/{qrel}"
|
||||
return f"https://gitee.com/{full}/raw/{branch}/{qrel}" if raw else f"https://gitee.com/{full}/blob/{branch}/{qrel}"
|
||||
|
||||
|
||||
def default_branch(repo: Path) -> str:
|
||||
for cmd in (["git", "symbolic-ref", "--short", "HEAD"], ["git", "rev-parse", "--abbrev-ref", "HEAD"]):
|
||||
r = run(cmd, cwd=repo)
|
||||
if r.returncode == 0 and r.stdout.strip():
|
||||
return r.stdout.strip()
|
||||
return "main"
|
||||
|
||||
|
||||
def last_commit_date(repo: Path, rel: str) -> str:
|
||||
r = run(["git", "log", "-1", "--format=%cI", "--", rel], cwd=repo)
|
||||
return r.stdout.strip() if r.returncode == 0 else ""
|
||||
|
||||
|
||||
def iter_candidates(repo: Path):
|
||||
files = []
|
||||
for p in repo.rglob("*"):
|
||||
if not p.is_file():
|
||||
continue
|
||||
rel = p.relative_to(repo).as_posix()
|
||||
if rel.startswith(".git/") or not c.is_candidate_path(rel):
|
||||
continue
|
||||
files.append(rel)
|
||||
files.sort(key=lambda path: (
|
||||
0 if re.search(r"(?i)(script|scripts|ql|青龙|wx_|applet|env|bili|smzdm|yyt|moutai|茅台|营业厅|值得买)", path) else 1,
|
||||
1 if re.search(r"(?i)(jd_|jingdong|京东|京喜)", path) else 0,
|
||||
len(path),
|
||||
))
|
||||
return files[:MAX_PER_REPO]
|
||||
|
||||
|
||||
def collect_repo(label: str, url: str, records: List[c.Record], seen: set):
|
||||
print(f"\n[SEED_CLONE] {label} | {url}", flush=True)
|
||||
repo = clone_or_update(label, url)
|
||||
if not repo:
|
||||
return
|
||||
full = repo_full(url)
|
||||
br = default_branch(repo)
|
||||
before = len(records)
|
||||
candidates = iter_candidates(repo)
|
||||
print(f" [SCAN] branch={br} candidates={len(candidates)}", flush=True)
|
||||
for rel in candidates:
|
||||
p = repo / rel
|
||||
try:
|
||||
if p.stat().st_size > 1024 * 1024:
|
||||
continue
|
||||
content = p.read_text(encoding="utf-8", errors="ignore")
|
||||
except Exception:
|
||||
continue
|
||||
uploaded = last_commit_date(repo, rel)
|
||||
c.add_content_record(
|
||||
records, seen, content, platform(url), full, rel,
|
||||
rel_url(url, br, rel, raw=False), rel_url(url, br, rel, raw=True), uploaded,
|
||||
)
|
||||
print(f" [SEED_DONE] added={len(records)-before}", flush=True)
|
||||
|
||||
|
||||
def main():
|
||||
c.OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
c.META_DIR.mkdir(parents=True, exist_ok=True)
|
||||
seen = c.load_seen_hashes()
|
||||
records: List[c.Record] = []
|
||||
for label, url in SEEDS:
|
||||
try:
|
||||
collect_repo(label, url, records, seen)
|
||||
except Exception as e:
|
||||
print(f"[SEED_ERROR] {label}: {type(e).__name__}: {e}", flush=True)
|
||||
if records:
|
||||
c.save_indexes(records)
|
||||
c.update_readme()
|
||||
else:
|
||||
c.rebuild_index_from_files()
|
||||
print(f"\n[DONE] clone_seed_added={len(records)}", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user