main
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3import re
4
5from loguru import logger
6from pyrogram.client import Client
7from pyrogram.types import Message, ReplyParameters
8
9from config import DB, FAVORITE
10from database.r2 import get_cf_r2, set_cf_r2
11from messages.parser import parse_msg
12from messages.sender import send2tg
13from messages.utils import delete_message, equal_prefix, set_reaction, startswith_prefix
14from utils import slim_cid, strings_list, to_int
15
16HELP = f"""⭐️**收藏消息**
17- `{FAVORITE.SAVE_PREFIX} keyword` : 保存消息为"keyword"到收藏夹
18- `{FAVORITE.SEND_PREFIX} keyword` : 从收藏夹发送"keyword"的消息
19- `{FAVORITE.SEND_PREFIX} fwd keyword` : 转发原始消息
20
21保存同一条消息为多个关键词时, 需使用逗号分隔
22"""
23PRESERVED_KEYS = {"fwd"} # these keys are preserved
24
25
26async def save_favorite(client: Client, message: Message, **kwargs):
27 """Save message to Favorites."""
28 # send docs if message == "/save"
29 if equal_prefix(message.text, prefix=FAVORITE.SAVE_PREFIX):
30 await send2tg(client, message, texts=HELP, **kwargs)
31 return
32 if not message.reply_to_message:
33 return
34 info = parse_msg(message, silent=True)
35 if not startswith_prefix(info["text"], prefix=FAVORITE.SAVE_PREFIX):
36 return
37 # check if user can use this command
38 if str(info["uid"]) not in strings_list(FAVORITE.TIDS_ALLOW_SAVE):
39 await message.reply(f"👤{info['full_name']}无权使用【保存收藏】功能\n🆔{info['uid']}", quote=True)
40 return
41 if not all([DB.CF_R2_ENABLED, FAVORITE.BACKUP_CHAT]):
42 await message.reply("【保存收藏】功能缺失必要参数设置", quote=True)
43 return
44
45 await set_reaction(client, message, reaction="👌")
46 keyword = info["text"].removeprefix(FAVORITE.SAVE_PREFIX).strip()
47 is_force_save = keyword.startswith(("!", "!")) # noqa: RUF001
48 keyword = keyword.removeprefix("!").removeprefix("!") # noqa: RUF001
49 msg_to_save = message.reply_to_message
50
51 # custom chat_id and message_id
52 if "cid=" in keyword and "mid=" in keyword:
53 cid = msg_to_save.chat.id
54 mid = msg_to_save.id
55 if matched := re.search(r"cid=(-?\w+)", keyword, re.IGNORECASE):
56 cid = to_int(matched.group(1))
57 keyword = re.sub(r"cid=(-?\w+)", "", keyword).strip()
58 if matched := re.search(r"mid=(\d+)", keyword, re.IGNORECASE):
59 mid = to_int(matched.group(1))
60 keyword = re.sub(r"mid=(\d+)", "", keyword).strip()
61 msg: Message = await client.get_messages(cid, mid) # type: ignore
62 if isinstance(msg, Message):
63 msg_to_save = msg
64 else:
65 await send2tg(client, message, texts=f"❌获取此消息失败, 可能是消息已被删除或者无访问权限.\nChatID={cid}, MessageID={mid}", **kwargs)
66 return
67
68 if not keyword:
69 return
70 keywords = strings_list(keyword.replace(",", ",")) # noqa: RUF001
71 # check if key is existed
72 for key in keywords:
73 if not is_force_save and await get_cf_r2(FAVORITE.R2_PREFIX + key):
74 await send2tg(client, message, texts=f"⚠️【{key}】已存在\n🔄请使用以下命令覆盖收藏:\n`{FAVORITE.SAVE_PREFIX}! {keyword}`", **kwargs)
75 return
76 if key.lower() in PRESERVED_KEYS:
77 await message.reply(f"【{key}】为保留关键字, 请使用其他关键词", quote=True)
78 return
79
80 # forward message to backup chat
81 if msg_to_save.media_group_id:
82 messages = await client.get_media_group(msg_to_save.chat.id, msg_to_save.id)
83 save_msgs: list[Message] = await client.forward_messages(
84 chat_id=to_int(FAVORITE.BACKUP_CHAT),
85 from_chat_id=msg_to_save.chat.id,
86 message_ids=[m.id for m in messages],
87 ) # type: ignore
88 save_msg = save_msgs[0]
89 else:
90 save_msg: Message = await msg_to_save.forward(to_int(FAVORITE.BACKUP_CHAT)) # type: ignore
91 metainfo = " ".join([f"#{key}" for key in keywords]) + f"\nhttps://t.me/c/{slim_cid(msg_to_save.chat.id)}/{msg_to_save.id}"
92 await save_msg.reply(metainfo, quote=True)
93 save_info = parse_msg(save_msg, silent=True, use_cache=False)
94 # only allow standard type
95 keys_to_remove = []
96 for k, v in save_info.items():
97 if not isinstance(v, (str, int, float, list, dict)):
98 keys_to_remove.append(k)
99 [save_info.pop(k) for k in keys_to_remove]
100 for key in keywords:
101 if not await set_cf_r2(FAVORITE.R2_PREFIX + key, save_info, silent=True):
102 logger.error(f"保存【{key}】到收藏失败")
103 await set_reaction(client, message, reaction="💔")
104 return
105 logger.success(f"【{key}】已保存到收藏")
106 await set_reaction(client, message, reaction="✍️")
107 await delete_message(message)
108
109
110async def send_favorite(client: Client, message: Message, **kwargs):
111 """Send message from Favorites."""
112 # send docs if message == "/fav"
113 if equal_prefix(message.text, prefix=FAVORITE.SEND_PREFIX):
114 await send2tg(client, message, texts=HELP, **kwargs)
115 return
116 info = parse_msg(message, silent=True)
117 if not startswith_prefix(info["text"], prefix=FAVORITE.SEND_PREFIX):
118 return
119 # check if user can use this command
120 if FAVORITE.TIDS_ALLOW_SEND != "all" and str(info["uid"]) not in strings_list(FAVORITE.TIDS_ALLOW_SEND):
121 await message.reply(f"👤{info['full_name']}无权使用【发送收藏】功能\n🆔{info['uid']}", quote=True)
122 return
123 if not all([DB.CF_R2_ENABLED]):
124 await message.reply("【发送收藏】功能缺失必要参数设置", quote=True)
125 return
126 keyword: str = info["text"].removeprefix(FAVORITE.SEND_PREFIX).strip()
127 use_forward = keyword.lower().startswith("fwd ")
128 if use_forward:
129 keyword = keyword[4:]
130 if not keyword:
131 return
132
133 await set_reaction(client, message, reaction="👌")
134 save_key = f"{FAVORITE.R2_PREFIX}{keyword}"
135 # check if key is existed
136 save_info = await get_cf_r2(save_key)
137 if not save_info:
138 await send2tg(client, message, texts=f"【{keyword}】不存在", **kwargs)
139 await set_reaction(client, message, reaction="💔")
140 return
141 reply_message_id = message.reply_to_message.id if message.reply_to_message else None
142 reply = ReplyParameters(message_id=reply_message_id) # type: ignore
143 if use_forward:
144 if save_info.get("media_group_id"):
145 messages = await client.get_media_group(save_info["cid"], save_info["mid"])
146 succ = await client.forward_messages(chat_id=info["cid"], from_chat_id=save_info["cid"], message_ids=[m.id for m in messages])
147 else:
148 succ = await client.forward_messages(chat_id=info["cid"], from_chat_id=save_info["cid"], message_ids=save_info["mid"])
149 elif save_info.get("media_group_id"):
150 succ = await client.copy_media_group(chat_id=info["cid"], from_chat_id=save_info["cid"], message_id=save_info["mid"], reply_parameters=reply)
151 else:
152 succ = await client.copy_message(chat_id=info["cid"], from_chat_id=save_info["cid"], message_id=save_info["mid"], reply_parameters=reply)
153 if isinstance(succ, Message) or (isinstance(succ, list) and all(isinstance(s, Message) for s in succ)):
154 await set_reaction(client, message)
155 else:
156 await set_reaction(client, message, reaction="💔")
157 await delete_message(message)