main
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3import contextlib
4import re
5from datetime import UTC, datetime
6from zoneinfo import ZoneInfo
7
8from glom import glom
9from loguru import logger
10from pyrogram.client import Client
11from pyrogram.types import Message
12
13from config import PROXY, TZ
14from database.r2 import get_cf_r2
15from messages.database import copy_messages_from_db, save_messages
16from messages.progress import modify_progress
17from messages.sender import send2tg
18from messages.utils import blockquote, summay_media
19from networking import download_file, download_media, hx_req
20from preview.utils import has_markdown_img
21from utils import nowstr
22
23
24async def preview_reddit(client: Client, message: Message, url: str = "", db_key: str = "", **kwargs):
25 """Preview reddit link in the message.
26
27 Args:
28 client (Client): The Pyrogram client.
29 message (Message): The trigger message object.
30 url (str, optional): Reddit link
31 db_key (str, optional): The cache key.
32 """
33 if kv := await get_cf_r2(db_key):
34 logger.debug(f"Reddit preview cache hit for key={db_key}")
35 if await copy_messages_from_db(client, message, key=db_key, kv=kv, **kwargs):
36 return
37 logger.warning("❌从缓存中转发失败, 尝试重新解析...")
38 if kwargs.get("show_progress") and "progress" not in kwargs:
39 res = await send2tg(client, message, texts=f"🔗正在解析Reddit链接\n{url}", **kwargs)
40 kwargs["progress"] = res[0]
41
42 post_info = await get_reddit_info(url)
43 if error := post_info.get("error"):
44 await modify_progress(text=f"❌Reddit链接解析失败{url}\n{error}", force_update=True, **kwargs)
45 return
46 sent_messages = await send2tg(client, message, **post_info, **kwargs)
47 await modify_progress(del_status=True, **kwargs)
48 await save_messages(messages=sent_messages, key=db_key)
49
50
51async def get_reddit_info(url: str, **kwargs) -> dict:
52 """Get Reddit post info."""
53 api_url = url + ".json"
54 resp = await hx_req(api_url, proxy=PROXY.REDDIT, check_kv={"0.data.dist": 1, "1.data.children.0.kind": "t1"}, check_keys=["0.data.children.0.data.selftext"], **kwargs)
55 if isinstance(resp, dict) and resp.get("hx_error"):
56 return {"error": resp["hx_error"]}
57 try:
58 data = glom(resp, "0.data.children.0.data")
59 title = data.get("title", "Title")
60 author = data.get("author", "author")
61 author_url = f"https://www.reddit.com/user/{author}"
62 dt = nowstr()
63 with contextlib.suppress(Exception):
64 dt = datetime.fromtimestamp(data["created_utc"], tz=UTC).astimezone(ZoneInfo(TZ))
65 dt = dt.strftime("%Y-%m-%d %H:%M:%S")
66 desc = remove_preview_links(data.get("selftext", "")).strip()
67 texts = f"🎈[{author}]({author_url})\n🕒{dt}\n**📝[{title}]({url})**\n{desc}"
68 media = []
69 if gallery := glom(data, "media_metadata.*", default=[]): # multiple images
70 for img in gallery:
71 ext = img.get("m", "").split("/")[-1] # image/png -> ping
72 img_url = f"https://i.redd.it/{img['id']}.{ext}"
73 media.append({"photo": download_file(img_url, proxy=PROXY.REDDIT, **kwargs)})
74 elif data.get("url", "").startswith("https://i.redd.it/"): # single image
75 media.append({"photo": download_file(data["url"], proxy=PROXY.REDDIT, **kwargs)})
76 if video_url := glom(data, "secure_media.reddit_video.fallback_url", default=""):
77 media.append({"video": download_file(video_url, proxy=PROXY.REDDIT, **kwargs)})
78 comments = ""
79 for reply in glom(resp, "1.data.children.*.data"):
80 author = reply.get("author", "author")
81 author_url = f"https://www.reddit.com/user/{author}"
82 comment = reply.get("body", "")
83 if author == "[deleted]":
84 continue
85 if comment == "[removed]" or has_markdown_img(comment):
86 continue
87 comments += f"\n💬**[{author}]({author_url})**: {comment}"
88 if comments:
89 comments = blockquote(f"💬**点击展开评论**:{comments}")
90 await modify_progress(text=f"⏬正在下载:\n{summay_media(media)}", force_update=True, **kwargs)
91 media = await download_media(media, **kwargs)
92 except Exception as e:
93 logger.error(e)
94 return {"error": str(e)}
95 return {"texts": texts + comments, "media": media}
96
97
98def remove_preview_links(text: str) -> str:
99 """Remove the preview.redd.it links in the post contents."""
100 pattern = r"https?://preview\.redd\.it/\S+\s"
101 return re.sub(pattern, "", text)