Commit 02e3495
Changed files (4)
src
src/messages/utils.py
@@ -233,3 +233,11 @@ async def delete_message(message: Message | None):
return
with contextlib.suppress(Exception):
await message.delete()
+
+
+def remove_img_tag(markdown: str) -> tuple[str, list[str]]:
+ """Removes all image tags from a markdown string."""
+ image_pattern = r"!\[.*?\]\((.*?)\)" # Matches both with and without alt text
+ clean = re.sub(image_pattern, "", markdown)
+ urls = re.findall(image_pattern, markdown)
+ return clean, urls
src/podcast/utils.py
@@ -1,7 +1,6 @@
#!/venv/bin/python
# -*- coding: utf-8 -*-
import base64
-import re
import string
from datetime import UTC, datetime
from zoneinfo import ZoneInfo
@@ -18,12 +17,6 @@ HEADERS = {
}
-def remove_img_tag(markdown: str):
- """Removes all image tags from a markdown string."""
- image_pattern = r"!\[.*?\]\((.*?)\)" # Matches both with and without alt text
- return re.sub(image_pattern, "", markdown)
-
-
def clean_feed_url(url: str) -> str:
if not url:
return ""
src/preview/github.py
@@ -10,25 +10,47 @@ from pyrogram.types import Message
from config import PROXY, TOKEN, TZ
from messages.progress import modify_progress
from messages.sender import send2tg
-from networking import download_file, hx_req
+from messages.utils import remove_img_tag
+from networking import download_file, download_media, hx_req
from utils import nowdt
+HEADERS = {"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28"}
+if TOKEN.GITHUB:
+ HEADERS["Authorization"] = f"Bearer {TOKEN.GITHUB}"
-async def preview_github(client: Client, message: Message, url: str, gh_user: str = "", gh_repo: str = "", **kwargs):
+
+async def preview_github(client: Client, message: Message, url: str, gh_user: str = "", gh_repo: str = "", query: str = "", **kwargs):
"""Preview github info in the message."""
if kwargs.get("show_progress") and "progress" not in kwargs:
res = await send2tg(client, message, texts=f"🔗正在解析GitHub链接\n{url}", **kwargs)
kwargs["progress"] = res[0]
- headers = {"Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28"}
- if TOKEN.GITHUB:
- headers["Authorization"] = f"Bearer {TOKEN.GITHUB}"
+ kwargs["send_from_user"] = "" # disable @send_user
+ if not query:
+ resp = await preview_readme(gh_user, gh_repo)
+ elif query.startswith(("issues", "pull")):
+ resp = await preview_issue(gh_user, gh_repo, query)
+ else:
+ resp = {"error": "未知的GitHub查询类型"}
+
+ if error := resp.get("error"):
+ await modify_progress(text=f"❌GitHub解析失败: {error}", force_update=True, **kwargs)
+ await send2tg(client, message, **resp, **kwargs)
+ await modify_progress(del_status=True, **kwargs)
+
+
+async def preview_readme(gh_user: str, gh_repo: str) -> dict:
+ """Preview github readme.
+
+ https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#get-a-repository
+
+ Returns:
+ {"texts": str, "media": list[dict]}
+ """
api = f"https://api.github.com/repos/{gh_user}/{gh_repo}"
- resp = await hx_req(api, headers=headers, proxy=PROXY.GITHUB, check_keys=["full_name"])
+ resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_keys=["full_name"])
if error := resp.get("hx_error"):
- await modify_progress(text=f"❌GitHub解析失败: {error}", force_update=True, **kwargs)
- return
+ return {"error": error}
full_repo = resp["full_name"]
- url = f"https://github.com/{full_repo}"
gh_user, gh_repo = full_repo.split("/") # correct uppercase / lowercase
msg = ""
if desc := resp["description"]:
@@ -52,20 +74,61 @@ async def preview_github(client: Client, message: Message, url: str, gh_user: st
for tag in tags:
msg += f" #{tag}"
media = []
- if readme := await github_readme(gh_user, gh_repo, headers=headers):
+ if readme := await download_readme(gh_user, gh_repo):
media = [{"document": readme}]
- kwargs["send_from_user"] = "" # disable @send_user
- await send2tg(client, message, texts=msg.strip(), media=media, **kwargs)
- await modify_progress(del_status=True, **kwargs)
+ return {"texts": msg.strip(), "media": media}
+
+
+async def preview_issue(gh_user: str, gh_repo: str, query: str) -> dict:
+ """Preview github issue.
+
+ https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue
+
+ Args:
+ query:
+ issues/123
+ issues/123#issuecomment-4567
+ pull/123
+ pull/123#issuecomment-4567
+
+ Returns:
+ {"texts": str, "media": list[dict]}
+ """
+ issue_number = query.split("#")[0].split("/")[1]
+ api = f"https://api.github.com/repos/{gh_user}/{gh_repo}/issues/{issue_number}"
+ resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_kv={"number": issue_number})
+ if error := resp.get("hx_error"):
+ return {"error": error}
+ msg = f"📦[{gh_user}/{gh_repo}/{query.split('#')[0]}](https://github.com/{gh_user}/{gh_repo}/{query})\n"
+ emoji = "🟢" if resp["state"] == "open" else "🟣"
+ msg += f"{emoji}**{resp['title']}**\n"
+ if "issuecomment" in query:
+ comment_id = query.split("#issuecomment-")[-1]
+ api = f"https://api.github.com/repos/{gh_user}/{gh_repo}/issues/comments/{comment_id}"
+ resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_kv={"id": comment_id})
+ issue_user = glom(resp, "user.login", default="user")
+ msg += f"👤[{issue_user}](https://github.com/{issue_user})\n"
+ msg += f"🕒{convert_dt(resp['created_at']):%Y-%m-%d %H:%M:%S}创建\n"
+ if resp.get("closed_at"):
+ msg += f"🕒{convert_dt(resp['closed_at']):%Y-%m-%d %H:%M:%S}关闭\n"
+
+ media = []
+ if desc := resp["body"]:
+ cleaned, urls = remove_img_tag(desc)
+ media = [{"photo": download_file(url, proxy=PROXY.GITHUB)} for url in urls]
+ media = await download_media(media)
+ msg += f"{cleaned}\n"
+
+ return {"texts": msg.strip(), "media": media}
-async def github_readme(user: str, repo: str, headers: dict) -> str:
+async def download_readme(user: str, repo: str) -> str:
"""Returns downloaded README path."""
api = f"https://api.github.com/repos/{user}/{repo}/readme"
- resp = await hx_req(api, headers=headers, proxy=PROXY.GITHUB, check_kv={"type": "file"})
+ resp = await hx_req(api, headers=HEADERS, proxy=PROXY.GITHUB, check_kv={"type": "file"})
if not resp.get("download_url"):
return ""
- return await download_file(resp["download_url"], headers=headers, skip_exist=False, proxy=PROXY.GITHUB)
+ return await download_file(resp["download_url"], headers=HEADERS, skip_exist=False, proxy=PROXY.GITHUB)
def convert_dt(dt: str) -> datetime:
@@ -85,7 +148,7 @@ def delta_time(dt: str) -> str:
res = ""
if delta.days:
res += f"{delta.days}天"
- minutes, seconds = divmod(delta.seconds, 60)
+ minutes, _ = divmod(delta.seconds, 60)
hours, minutes = divmod(minutes, 60)
if hours:
res += f"{hours}小时"
src/networking.py
@@ -217,6 +217,8 @@ async def download_first_success_urls(links: list[str], **kwargs) -> str:
async def download_media(media: list[dict], **kwargs) -> list[dict]:
+ if not media:
+ return []
tasks = []
for item in media:
if task := item.get("photo"): # async function
@@ -362,11 +364,16 @@ async def match_social_media_link(text: str, *, flatten_first: bool = True) -> d
return {"url": url, "db_key": url, "post_id": post_id, "platform": "bilibili-opus"}
# https://github.com/user-name/repo
- if matched := re.search(r"(https?://)?github\.com/([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)/([a-zA-Z0-9_-]+)", text):
+ # https://github.com/user-name/repo/issues/123
+ # https://github.com/user-name/repo/issues/123#issuecomment-45678
+ # https://github.com/user-name/repo/pull/123
+ # https://github.com/user-name/repo/pull/123#issuecomment-45678
+ if matched := re.search(r"(https?://)?github\.com/([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)/([a-zA-Z0-9_-]+)/?([#/a-zA-Z0-9_-]+)?", text):
gh_user = matched.group(2)
gh_repo = matched.group(4)
- url = f"https://github.com/{gh_user}/{gh_repo}"
- return {"url": url, "db_key": bare_url(url), "gh_user": gh_user, "gh_repo": gh_repo, "platform": "github"}
+ query = matched.group(5) or ""
+ url = matched.group(0)
+ return {"url": https_url(url), "db_key": bare_url(url), "gh_user": gh_user, "gh_repo": gh_repo, "query": query, "platform": "github"}
# https://www.v2ex.com/t/1153086
if matched := re.search(r"(https?://)?(www\.)?v2ex\.com/t/(\d+)", text):
@@ -522,8 +529,15 @@ if __name__ == "__main__":
# asyncio.run(flatten_rediercts("https://v.douyin.com/CeiJfJMQG/"))
# asyncio.run(flatten_rediercts("https://www.tiktok.com/t/ZT2mcMA7f/"))
# asyncio.run(flatten_rediercts("https://t.co/Wwo3x69CQz"))
- print(asyncio.run(match_social_media_link("https://www.bilibili.com/video/BV1TC411J7PK")))
- print(asyncio.run(match_social_media_link("https://www.bilibili.com/BV1TC411J7PK")))
+ print(asyncio.run(match_social_media_link("https://github.com/yt-dlp/yt-dlp/issues/14463")))
+ print(asyncio.run(match_social_media_link("https://github.com/yt-dlp/yt-dlp/")))
+ print(asyncio.run(match_social_media_link("https://github.com/yt-dlp/yt-dlp")))
+ print(asyncio.run(match_social_media_link("https://github.com/yt-dlp/yt-dlp/issues/14404#issuecomment-3323873708")))
+ print(asyncio.run(match_social_media_link("https://github.com/yt-dlp/yt-dlp/pull/14467")))
+ print(asyncio.run(match_social_media_link("https://github.com/yt-dlp/yt-dlp/pull/14417#issuecomment-3327344721")))
+
+ # print(asyncio.run(match_social_media_link("https://www.bilibili.com/video/BV1TC411J7PK")))
+ # print(asyncio.run(match_social_media_link("https://www.bilibili.com/BV1TC411J7PK")))
# print(asyncio.run(match_social_media_link("https://www.instagram.com/miyoshi.aa/p/DN5hFcUE8rS/")))
# print(asyncio.run(match_social_media_link("https://www.youtube.com/watch?v=D6aE2E0RHTc")))
# print(asyncio.run(match_social_media_link("https://youtube.com/shorts/lFKHbluAlJw")))