misskey2telegram/defs/misskey.py

387 lines
11 KiB
Python
Raw Normal View History

2023-07-21 05:52:01 +00:00
import contextlib
2023-07-22 02:44:28 +00:00
import re
2023-08-07 05:55:43 +00:00
from asyncio import sleep
2022-12-22 14:18:43 +00:00
from datetime import datetime, timedelta, timezone
2023-07-21 14:00:49 +00:00
from typing import Optional, List
2022-12-22 14:18:43 +00:00
2023-07-21 05:52:01 +00:00
import aiofiles as aiofiles
2023-08-07 05:55:43 +00:00
from httpx import AsyncClient
2023-07-21 14:00:49 +00:00
from mipac import Note, File
2022-12-22 14:18:43 +00:00
from mipac.models.lite import LiteUser
from pyrogram.enums import ParseMode
2023-08-07 05:55:43 +00:00
from pyrogram.errors import MediaEmpty, FloodWait
2023-07-03 14:39:52 +00:00
from pyrogram.types import (
InlineKeyboardMarkup,
InlineKeyboardButton,
InputMediaPhoto,
InputMediaVideo,
InputMediaDocument,
InputMediaAudio,
2023-07-21 14:00:49 +00:00
Message,
2023-07-03 14:39:52 +00:00
)
2022-12-22 14:18:43 +00:00
2023-07-21 06:40:07 +00:00
from defs.image import webp_to_png
2023-08-07 05:55:43 +00:00
from init import bot, logs, headers
2023-01-27 12:36:41 +00:00
from models.services.scheduler import add_delete_file_job, delete_file
2022-12-22 14:18:43 +00:00
2023-07-22 02:44:28 +00:00
at_parse = re.compile(r"(?<!\S)@(\S+)\s")
2022-12-22 14:18:43 +00:00
2023-07-20 14:21:37 +00:00
def get_note_url(host: str, note: Note) -> str:
return f"https://{host}/notes/{note.id}"
2022-12-22 14:18:43 +00:00
2023-07-20 15:35:10 +00:00
def gen_button(host: str, note: Note, author: str, show_second: bool):
2023-07-20 14:21:37 +00:00
source = get_note_url(host, note)
reply_source = get_note_url(host, note.reply) if note.reply else None
2023-07-21 10:26:12 +00:00
renote_id = note.renote_id or note.id
2022-12-22 14:18:43 +00:00
if reply_source:
2022-12-24 08:31:16 +00:00
first_line = [
2022-12-22 14:18:43 +00:00
InlineKeyboardButton(text="Source", url=source),
InlineKeyboardButton(text="RSource", url=reply_source),
2022-12-24 08:31:16 +00:00
InlineKeyboardButton(text="Author", url=author),
]
2022-12-22 14:18:43 +00:00
else:
2022-12-24 08:31:16 +00:00
first_line = [
2022-12-22 14:18:43 +00:00
InlineKeyboardButton(text="Source", url=source),
2022-12-24 08:31:16 +00:00
InlineKeyboardButton(text="Author", url=author),
]
second_line = [
2023-01-27 15:20:25 +00:00
InlineKeyboardButton(text="🔁", callback_data=f"renote:{renote_id}"),
2023-07-05 12:02:42 +00:00
InlineKeyboardButton(text="❤️", callback_data=f"react:{renote_id}:love"),
InlineKeyboardButton(text="🌐", callback_data=f"translate:{renote_id}"),
2022-12-24 08:31:16 +00:00
]
2023-07-20 15:35:10 +00:00
return (
InlineKeyboardMarkup([first_line, second_line])
if show_second
else InlineKeyboardMarkup([first_line])
)
2022-12-22 14:18:43 +00:00
2023-07-20 14:21:37 +00:00
def get_user_link(host: str, user: LiteUser) -> str:
2022-12-22 14:18:43 +00:00
if user.host:
2023-07-20 14:21:37 +00:00
return f"https://{host}/@{user.username}@{user.host}"
return f"https://{host}/@{user.username}"
2022-12-22 14:18:43 +00:00
2023-07-20 14:21:37 +00:00
def get_user_alink(host: str, user: LiteUser) -> str:
return '<a href="{}">{}</a>'.format(
get_user_link(host, user), user.nickname or f"@{user.username}"
)
2023-07-05 11:58:39 +00:00
2022-12-22 14:18:43 +00:00
def get_post_time(date: datetime) -> str:
try:
date = date + timedelta(hours=8)
return date.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
2023-07-22 02:44:28 +00:00
def format_at(host: str, content: str) -> str:
replaced = rf'<a href="https://{host}/@\1">@\1</a> '
return at_parse.sub(replaced, content)
2023-07-20 14:21:37 +00:00
def get_content(host: str, note: Note) -> str:
2022-12-22 15:01:53 +00:00
content = note.content or ""
action = "发表"
origin = ""
show_note = note
if note.renote:
show_note = note.renote
action = "转推"
2023-07-26 12:51:12 +00:00
if content:
action = "引用"
2023-07-29 05:08:09 +00:00
content = (
f"> {note.renote.content or ''}\n\n=====================\n\n{content}"
)
2023-07-26 12:51:12 +00:00
else:
content = note.renote.content or ""
2023-07-03 14:39:52 +00:00
origin = (
2023-07-20 14:21:37 +00:00
f"\n{get_user_alink(host, note.renote.author)} "
2023-07-03 14:39:52 +00:00
f"发表于 {get_post_time(note.renote.created_at)}"
)
2023-07-21 10:26:12 +00:00
if note.reply:
show_note = note.reply
action = "回复"
if note.reply.content:
2023-07-23 02:59:13 +00:00
content = f"> {note.reply.content}\n\n=====================\n\n{content}"
2023-07-21 10:26:12 +00:00
origin = (
f"\n{get_user_alink(host, note.reply.author)} "
f"发表于 {get_post_time(note.reply.created_at)}"
)
2023-07-23 02:59:13 +00:00
content = format_at(host, content[:768] + " ").strip()
2022-12-22 14:18:43 +00:00
return f"""<b>Misskey Timeline Update</b>
2023-07-21 10:26:12 +00:00
{content}
2022-12-22 14:18:43 +00:00
2023-07-20 14:21:37 +00:00
{get_user_alink(host, note.author)} {action} {get_post_time(note.created_at)}{origin}
2023-01-17 08:36:41 +00:00
点赞: {sum(show_note.reactions.values())} | 回复: {show_note.replies_count} | 转发: {show_note.renote_count}"""
2022-12-22 14:18:43 +00:00
2023-08-07 05:55:43 +00:00
def retry(func):
async def wrapper(*args, **kwargs):
try:
return await func(*args, **kwargs)
except FloodWait as e:
await sleep(e.value + 1)
return await func(*args, **kwargs)
return wrapper
@retry
2023-07-20 15:35:10 +00:00
async def send_text(
host: str, cid: int, note: Note, reply_to_message_id: int, show_second: bool
2023-07-21 14:00:49 +00:00
) -> Message:
return await bot.send_message(
2022-12-22 14:18:43 +00:00
cid,
2023-07-20 14:21:37 +00:00
get_content(host, note),
2022-12-24 08:31:16 +00:00
reply_to_message_id=reply_to_message_id,
2023-07-20 15:35:10 +00:00
reply_markup=gen_button(
host, note, get_user_link(host, note.author), show_second
),
2022-12-22 14:18:43 +00:00
disable_web_page_preview=True,
)
def deprecated_to_text(func):
async def wrapper(*args, **kwargs):
try:
return await func(*args, **kwargs)
except MediaEmpty:
2023-07-20 15:35:10 +00:00
return await send_text(args[0], args[1], args[3], args[4], args[5])
2022-12-22 14:18:43 +00:00
return wrapper
2023-07-21 14:00:49 +00:00
async def fetch_document(file: File) -> Optional[str]:
file_name = "downloads/" + file.name
file_url = file.url
if file.size > 10 * 1024 * 1024:
2023-07-21 05:52:01 +00:00
return file_url
if not file_url:
return file_url
2023-08-07 05:55:43 +00:00
logs.info(f"下载远程文件:{file_url}")
async with AsyncClient(timeout=60.0, headers=headers) as request:
req = await request.get(file_url)
2023-07-21 05:52:01 +00:00
if req.status_code != 200:
return file_url
2023-07-21 06:40:07 +00:00
if file_name.lower().endswith(".webp"):
file_name = file_name[:-5] + ".jpg"
io = webp_to_png(req.content).getvalue()
else:
io = req.content
2023-07-21 05:52:01 +00:00
async with aiofiles.open(file_name, "wb") as f:
2023-07-21 06:40:07 +00:00
await f.write(io)
2023-07-21 05:52:01 +00:00
add_delete_file_job(file_name)
return file_name
2023-08-07 05:55:43 +00:00
@retry
2022-12-22 14:18:43 +00:00
@deprecated_to_text
2023-07-20 14:21:37 +00:00
async def send_photo(
2023-07-20 15:35:10 +00:00
host: str,
cid: int,
url: str,
note: Note,
reply_to_message_id: int,
show_second: bool,
2023-07-21 14:00:49 +00:00
) -> Message:
2022-12-22 14:18:43 +00:00
if not url:
2023-07-20 15:35:10 +00:00
return await send_text(host, cid, note, reply_to_message_id, show_second)
2023-07-21 14:00:49 +00:00
return await bot.send_photo(
2022-12-22 14:18:43 +00:00
cid,
url,
2023-01-17 03:26:10 +00:00
reply_to_message_id=reply_to_message_id,
2023-07-20 14:21:37 +00:00
caption=get_content(host, note),
2023-07-20 15:35:10 +00:00
reply_markup=gen_button(
host, note, get_user_link(host, note.author), show_second
),
2022-12-22 14:18:43 +00:00
)
2023-08-07 05:55:43 +00:00
@retry
2022-12-22 14:18:43 +00:00
@deprecated_to_text
2023-07-20 14:21:37 +00:00
async def send_video(
2023-07-20 15:35:10 +00:00
host: str,
cid: int,
url: str,
note: Note,
reply_to_message_id: int,
show_second: bool,
2023-07-21 14:00:49 +00:00
) -> Message:
2022-12-22 14:18:43 +00:00
if not url:
2023-07-20 15:35:10 +00:00
return await send_text(host, cid, note, reply_to_message_id, show_second)
2023-07-21 14:00:49 +00:00
return await bot.send_video(
2022-12-22 14:18:43 +00:00
cid,
url,
2023-01-17 03:26:10 +00:00
reply_to_message_id=reply_to_message_id,
2023-07-20 14:21:37 +00:00
caption=get_content(host, note),
2023-07-20 15:35:10 +00:00
reply_markup=gen_button(
host, note, get_user_link(host, note.author), show_second
),
2022-12-22 14:18:43 +00:00
)
2023-08-07 05:55:43 +00:00
@retry
2022-12-22 14:18:43 +00:00
@deprecated_to_text
2023-07-20 14:21:37 +00:00
async def send_audio(
2023-07-20 15:35:10 +00:00
host: str,
cid: int,
url: str,
note: Note,
reply_to_message_id: int,
show_second: bool,
2023-07-21 14:00:49 +00:00
) -> Message:
2022-12-22 14:18:43 +00:00
if not url:
2023-07-20 15:35:10 +00:00
return await send_text(host, cid, note, reply_to_message_id, show_second)
2023-07-21 14:00:49 +00:00
return await bot.send_audio(
2022-12-22 14:18:43 +00:00
cid,
url,
2023-01-17 03:26:10 +00:00
reply_to_message_id=reply_to_message_id,
2023-07-20 14:21:37 +00:00
caption=get_content(host, note),
2023-07-20 15:35:10 +00:00
reply_markup=gen_button(
host, note, get_user_link(host, note.author), show_second
),
2022-12-22 14:18:43 +00:00
)
2023-08-07 05:55:43 +00:00
@retry
2022-12-22 14:18:43 +00:00
@deprecated_to_text
2023-07-03 14:39:52 +00:00
async def send_document(
2023-07-20 15:35:10 +00:00
host: str,
cid: int,
2023-07-21 05:52:01 +00:00
url: str,
2023-07-20 15:35:10 +00:00
note: Note,
reply_to_message_id: int,
show_second: bool,
2023-07-21 14:00:49 +00:00
) -> Message:
2023-07-21 05:52:01 +00:00
if not url:
2023-07-20 15:35:10 +00:00
return await send_text(host, cid, note, reply_to_message_id, show_second)
2023-07-21 14:00:49 +00:00
msg = await bot.send_document(
2022-12-22 14:18:43 +00:00
cid,
2023-07-21 05:52:01 +00:00
url,
2023-01-17 03:26:10 +00:00
reply_to_message_id=reply_to_message_id,
2023-07-20 14:21:37 +00:00
caption=get_content(host, note),
2023-07-20 15:35:10 +00:00
reply_markup=gen_button(
host, note, get_user_link(host, note.author), show_second
),
2022-12-22 14:18:43 +00:00
)
2023-07-21 05:52:01 +00:00
with contextlib.suppress(Exception):
await delete_file(url)
2023-07-21 14:00:49 +00:00
return msg
2022-12-22 14:18:43 +00:00
2023-07-21 14:00:49 +00:00
async def get_media_group(files: list[File]) -> list:
2022-12-22 14:18:43 +00:00
media_lists = []
2022-12-24 08:31:16 +00:00
for file_ in files:
2023-08-07 05:55:43 +00:00
file_url = await fetch_document(file_)
2022-12-22 14:18:43 +00:00
if not file_url:
continue
2023-07-21 14:00:49 +00:00
file_type = file_.type
2022-12-22 14:18:43 +00:00
if file_type.startswith("image"):
media_lists.append(
InputMediaPhoto(
file_url,
parse_mode=ParseMode.HTML,
)
)
elif file_type.startswith("video"):
media_lists.append(
InputMediaVideo(
file_url,
parse_mode=ParseMode.HTML,
)
)
elif file_type.startswith("audio"):
media_lists.append(
InputMediaAudio(
file_url,
parse_mode=ParseMode.HTML,
)
)
2022-12-24 08:31:16 +00:00
elif file := await fetch_document(file_):
2022-12-22 14:18:43 +00:00
media_lists.append(
InputMediaDocument(
file,
parse_mode=ParseMode.HTML,
)
)
return media_lists
2023-08-07 05:55:43 +00:00
@retry
async def send_media_group(cid: int, groups: list):
return await bot.send_media_group(cid, groups)
async def send_group_msg(cid: int, groups: list):
msgs = []
for i in range(0, len(groups), 10):
msg = await send_media_group(cid, groups[i : i + 10])
msgs.extend(msg)
return msgs
@deprecated_to_text
2023-07-03 14:39:52 +00:00
async def send_group(
2023-07-20 15:35:10 +00:00
host: str,
cid: int,
2023-07-21 14:00:49 +00:00
files: list[File],
2023-07-20 15:35:10 +00:00
note: Note,
reply_to_message_id: int,
show_second: bool,
2023-07-21 14:00:49 +00:00
) -> List[Message]:
2022-12-24 08:31:16 +00:00
groups = await get_media_group(files)
2022-12-22 14:18:43 +00:00
if len(groups) == 0:
2023-07-21 14:00:49 +00:00
return [await send_text(host, cid, note, reply_to_message_id, show_second)]
2023-08-07 05:55:43 +00:00
photo, video, audio, document, msg_ids = [], [], [], [], []
2022-12-22 14:18:43 +00:00
for i in groups:
if isinstance(i, InputMediaPhoto):
photo.append(i)
elif isinstance(i, InputMediaVideo):
video.append(i)
elif isinstance(i, InputMediaAudio):
audio.append(i)
elif isinstance(i, InputMediaDocument):
document.append(i)
2023-08-07 05:55:43 +00:00
for i in (photo, video, audio, document):
msg_ids.extend(await send_group_msg(cid, i))
2023-07-21 14:00:49 +00:00
tmsg = await send_text(
host, cid, note, msg_ids[0].id if msg_ids else None, show_second
)
if tmsg:
msg_ids.append(tmsg)
return msg_ids
2022-12-22 14:18:43 +00:00
2023-07-20 15:35:10 +00:00
async def send_update(
host: str, cid: int, note: Note, topic_id: Optional[int], show_second: bool
2023-07-21 14:00:49 +00:00
) -> List[Message]:
2022-12-22 14:18:43 +00:00
files = list(note.files)
if note.reply:
files.extend(iter(note.reply.files))
2022-12-22 15:01:53 +00:00
if note.renote:
files.extend(iter(note.renote.files))
2022-12-22 14:18:43 +00:00
match len(files):
case 0:
2023-07-21 14:00:49 +00:00
return [await send_text(host, cid, note, topic_id, show_second)]
2022-12-22 14:18:43 +00:00
case 1:
file = files[0]
2023-07-21 14:00:49 +00:00
file_type = file.type
2023-07-21 05:52:01 +00:00
url = await fetch_document(file)
2022-12-22 14:18:43 +00:00
if file_type.startswith("image"):
2023-07-21 14:09:18 +00:00
return [await send_photo(host, cid, url, note, topic_id, show_second)]
2022-12-22 14:18:43 +00:00
elif file_type.startswith("video"):
2023-07-21 14:09:18 +00:00
return [await send_video(host, cid, url, note, topic_id, show_second)]
2022-12-22 14:18:43 +00:00
elif file_type.startswith("audio"):
2023-07-21 14:09:18 +00:00
return [await send_audio(host, cid, url, note, topic_id, show_second)]
2022-12-22 14:18:43 +00:00
else:
2023-07-29 05:08:09 +00:00
return [
await send_document(host, cid, url, note, topic_id, show_second)
]
2022-12-22 14:18:43 +00:00
case _:
2023-07-21 14:00:49 +00:00
return await send_group(host, cid, files, note, topic_id, show_second)