main
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3from datetime import UTC, datetime
4from zoneinfo import ZoneInfo
5
6from glom import glom
7from pyrogram.client import Client
8from pyrogram.types import Message
9
10from config import PROXY, TOKEN, TZ
11from messages.progress import modify_progress
12from messages.sender import send2tg
13from messages.utils import remove_img_tag
14from networking import download_file, download_media, hx_req
15from utils import nowdt
16
17HEADERS = {"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28"}
18if TOKEN.GITHUB:
19 HEADERS["Authorization"] = f"Bearer {TOKEN.GITHUB}"
20
21
22async def preview_github(client: Client, message: Message, url: str, gh_user: str = "", gh_repo: str = "", query: str = "", **kwargs):
23 """Preview github info in the message."""
24 if kwargs.get("show_progress") and "progress" not in kwargs:
25 res = await send2tg(client, message, texts=f"🔗正在解析GitHub链接\n{url}", **kwargs)
26 kwargs["progress"] = res[0]
27 kwargs["send_from_user"] = "" # disable @send_user
28 if not query:
29 resp = await preview_readme(gh_user, gh_repo)
30 elif query.startswith(("issues", "pull")):
31 resp = await preview_issue(gh_user, gh_repo, query)
32 else:
33 return
34
35 if error := resp.get("error"):
36 await modify_progress(text=f"❌GitHub解析失败: {error}", force_update=True, **kwargs)
37 await send2tg(client, message, **resp, **kwargs)
38 await modify_progress(del_status=True, **kwargs)
39
40
41async def preview_readme(gh_user: str, gh_repo: str) -> dict:
42 """Preview github readme.
43
44 https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#get-a-repository
45
46 Returns:
47 {"texts": str, "media": list[dict]}
48 """
49 api = f"https://api.github.com/repos/{gh_user}/{gh_repo}"
50 resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_keys=["full_name"])
51 if error := resp.get("hx_error"):
52 return {"error": error}
53 full_repo = resp["full_name"]
54 gh_user, gh_repo = full_repo.split("/") # correct uppercase / lowercase
55 msg = ""
56 if desc := resp["description"]:
57 msg += f"**{desc}**\n"
58 msg += f"📦[{full_repo}](https://github.com/{full_repo})\n"
59 if upstream := glom(resp, "parent.full_name", default=""):
60 msg += f"⬆️上游: [{upstream}](https://github.com/{upstream})\n"
61
62 msg += f"⭐️Star: {resp['stargazers_count']}\n"
63 msg += f"👁Watch: {resp['subscribers_count']}\n"
64 msg += f"🔀[Fork](https://github.com/{full_repo}/forks): {resp['forks_count']}\n"
65 msg += f"❔[Issues](https://github.com/{full_repo}/issues): {resp['open_issues_count']}\n"
66
67 if lcen := glom(resp, "license.spdx_id", default=""):
68 msg += f"📄License: {lcen}\n"
69 msg += f"🕒创建日期: {convert_dt(resp['created_at']):%Y-%m-%d %H:%M:%S}\n"
70 default_branch = resp.get("default_branch", "main")
71 msg += f"🔄[最新推送](https://github.com/{full_repo}/commits/{default_branch}): {delta_time(resp['pushed_at'])}\n"
72 if tags := glom(resp, "topics", default=[]):
73 msg += "🏷️Tags:"
74 for tag in tags:
75 msg += f" #{tag}"
76 media = []
77 if readme := await download_readme(gh_user, gh_repo):
78 media = [{"document": readme}]
79 return {"texts": msg.strip(), "media": media}
80
81
82async def preview_issue(gh_user: str, gh_repo: str, query: str) -> dict:
83 """Preview github issue.
84
85 https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue
86
87 Args:
88 query:
89 issues/123
90 issues/123#issuecomment-4567
91 pull/123
92 pull/123#issuecomment-4567
93
94 Returns:
95 {"texts": str, "media": list[dict]}
96 """
97 issue_number = query.split("#")[0].split("/")[1]
98 api = f"https://api.github.com/repos/{gh_user}/{gh_repo}/issues/{issue_number}"
99 resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_kv={"number": issue_number})
100 if error := resp.get("hx_error"):
101 return {"error": error}
102 msg = f"📦[{gh_user}/{gh_repo}/{query.split('#')[0]}](https://github.com/{gh_user}/{gh_repo}/{query})\n"
103 emoji = "🟢" if resp["state"] == "open" else "🟣"
104 msg += f"{emoji}**{resp['title']}**\n"
105 if "issuecomment" in query:
106 comment_id = query.split("#issuecomment-")[-1]
107 api = f"https://api.github.com/repos/{gh_user}/{gh_repo}/issues/comments/{comment_id}"
108 resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_kv={"id": comment_id})
109 issue_user = glom(resp, "user.login", default="user")
110 msg += f"👤[{issue_user}](https://github.com/{issue_user})\n"
111 msg += f"🕒{convert_dt(resp['created_at']):%Y-%m-%d %H:%M:%S}创建\n"
112 if resp.get("closed_at"):
113 msg += f"🕒{convert_dt(resp['closed_at']):%Y-%m-%d %H:%M:%S}关闭\n"
114
115 media = []
116 if desc := resp["body"]:
117 cleaned, urls = remove_img_tag(desc)
118 media = [{"photo": download_file(url, proxy=PROXY.GITHUB)} for url in urls]
119 media = await download_media(media)
120 msg += f"{cleaned}\n"
121
122 return {"texts": msg.strip(), "media": media}
123
124
125async def download_readme(user: str, repo: str) -> str:
126 """Returns downloaded README path."""
127 api = f"https://api.github.com/repos/{user}/{repo}/readme"
128 resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_kv={"type": "file"})
129 if not resp.get("download_url"):
130 return ""
131 return await download_file(resp["download_url"], headers=HEADERS, skip_exist=False, proxy=PROXY.GITHUB)
132
133
134def convert_dt(dt: str) -> datetime:
135 """Convert 2017-04-18T22:02:38Z to datetime object."""
136 return datetime.strptime(dt[:-1], "%Y-%m-%dT%H:%M:%S").replace(tzinfo=UTC).astimezone(ZoneInfo(TZ))
137
138
139def delta_time(dt: str) -> str:
140 """Time difference between the current time and the specified time.
141
142 dt format: 2017-04-18T22:02:38Z
143 Returns: 2d12h
144 """
145 time = convert_dt(dt)
146 now = nowdt(TZ)
147 delta = now - time
148 res = ""
149 if delta.days:
150 res += f"{delta.days}天"
151 minutes, _ = divmod(delta.seconds, 60)
152 hours, minutes = divmod(minutes, 60)
153 if hours:
154 res += f"{hours}小时"
155 if minutes:
156 res += f"{minutes}分钟"
157 return res + "前" if res else "刚刚"