diff --git a/defs/chat.py b/defs/chat.py
new file mode 100644
index 0000000..168d2f2
--- /dev/null
+++ b/defs/chat.py
@@ -0,0 +1,151 @@
+from typing import Optional
+
+from mipac import ChatMessage, File
+from mipac.models.lite import LiteUser
+from pyrogram.errors import MediaEmpty
+from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
+
+from glover import misskey_host
+from init import bot, request
+from scheduler import add_delete_file_job, delete_file
+
+
+def get_user_link(user: LiteUser) -> str:
+ if user.host:
+ return f"https://{user.host}/@{user.username}"
+ return f"https://{misskey_host}/@{user.username}"
+
+
+def get_source_link(message: ChatMessage) -> str:
+ return (
+ f"https://{misskey_host}/my/messaging/{message.user.username}?cid={message.user.id}"
+ if not message.group and message.user
+ else f"https://{misskey_host}/my/messaging/group/{message.group.id}"
+ )
+
+
+def gen_button(message: ChatMessage):
+ author = get_user_link(message.user)
+ source = get_source_link(message)
+ first_line = [
+ InlineKeyboardButton(text="Chat", url=source),
+ InlineKeyboardButton(text="Author", url=author),
+ ]
+ return InlineKeyboardMarkup([first_line])
+
+
+def get_content(message: ChatMessage) -> str:
+ content = message.text or ""
+ content = content[:768]
+ user = f"{message.user.nickname}"
+ if message.group:
+ group = f"{message.group.name}"
+ user += f" ( {group} )"
+ return f"""Misskey Message
+
+{user}: {content}
"""
+
+
+async def send_text(cid: int, message: ChatMessage, reply_to_message_id: int):
+ await bot.send_message(
+ cid,
+ get_content(message),
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=gen_button(message),
+ disable_web_page_preview=True,
+ )
+
+
+def deprecated_to_text(func):
+ async def wrapper(*args, **kwargs):
+ try:
+ return await func(*args, **kwargs)
+ except MediaEmpty:
+ return await send_text(args[0], args[2], args[3])
+
+ return wrapper
+
+
+@deprecated_to_text
+async def send_photo(cid: int, url: str, message: ChatMessage, reply_to_message_id: int):
+ if not url:
+ return await send_text(cid, message, reply_to_message_id)
+ await bot.send_photo(
+ cid,
+ url,
+ reply_to_message_id=reply_to_message_id,
+ caption=get_content(message),
+ reply_markup=gen_button(message),
+ )
+
+
+@deprecated_to_text
+async def send_video(cid: int, url: str, message: ChatMessage, reply_to_message_id: int):
+ if not url:
+ return await send_text(cid, message, reply_to_message_id)
+ await bot.send_video(
+ cid,
+ url,
+ reply_to_message_id=reply_to_message_id,
+ caption=get_content(message),
+ reply_markup=gen_button(message),
+ )
+
+
+@deprecated_to_text
+async def send_audio(cid: int, url: str, message: ChatMessage, reply_to_message_id: int):
+ if not url:
+ return await send_text(cid, message, reply_to_message_id)
+ await bot.send_audio(
+ cid,
+ url,
+ reply_to_message_id=reply_to_message_id,
+ caption=get_content(message),
+ reply_markup=gen_button(message),
+ )
+
+
+async def fetch_document(file: File) -> Optional[str]:
+ file_name = f"downloads/{file.name}"
+ file_url = file.url
+ if file.size > 10 * 1024 * 1024:
+ return file_url
+ if not file_url:
+ return file_url
+ req = await request.get(file_url)
+ if req.status_code != 200:
+ return file_url
+ with open(file_name, "wb") as f:
+ f.write(req.content)
+ add_delete_file_job(file_name)
+ return file_name
+
+
+@deprecated_to_text
+async def send_document(cid: int, file: File, message: ChatMessage, reply_to_message_id: int):
+ file = await fetch_document(file)
+ if not file:
+ return await send_text(cid, message, reply_to_message_id)
+ await bot.send_document(
+ cid,
+ file,
+ reply_to_message_id=reply_to_message_id,
+ caption=get_content(message),
+ reply_markup=gen_button(message),
+ )
+ await delete_file(file)
+
+
+async def send_chat_message(cid: int, message: ChatMessage, topic_id: int):
+ if not message.file:
+ return await send_text(cid, message, topic_id)
+ file_url = message.file.url
+ file_type = message.file.type
+ if file_type.startswith("image"):
+ await send_photo(cid, file_url, message, topic_id)
+ elif file_type.startswith("video"):
+ await send_video(cid, file_url, message, topic_id)
+ elif file_type.startswith("audio"):
+ await send_audio(cid, file_url, message, topic_id)
+ else:
+ await send_document(cid, message.file, message, topic_id)
diff --git a/defs/confirm.py b/defs/confirm.py
index be18823..69309b1 100644
--- a/defs/confirm.py
+++ b/defs/confirm.py
@@ -41,7 +41,51 @@ class ReadySend:
except Exception as e:
await msg.edit(f"发送失败:{e}")
else:
- await msg.edit("发送成功")
+ await msg.delete()
+ finally:
+ del ready_send[msg.id]
+
+
+class ReadySendMessage:
+ def __init__(
+ self,
+ text: str,
+ group: bool = False,
+ uid: Optional[str] = None,
+ file_id: Optional[str] = None,
+ ):
+ self.text = text
+ self.user_id = None if group else uid
+ self.group_id = uid if group else None
+ self.file_id = file_id
+
+ async def confirm(self, msg: Message):
+ msg = await msg.reply(
+ "确认发送?",
+ quote=True,
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(text="发送", callback_data="chat_send"),
+ InlineKeyboardButton(text="拒绝", callback_data="delete")
+ ]
+ ]
+ ),
+ )
+ ready_send[msg.id] = self
+
+ async def send(self, msg: Message):
+ try:
+ await misskey_bot.core.api.chat.action.send(
+ text=self.text,
+ user_id=self.user_id,
+ group_id=self.group_id,
+ file_id=self.file_id,
+ )
+ except Exception as e:
+ await msg.edit(f"发送失败:{e}")
+ else:
+ await msg.delete()
finally:
del ready_send[msg.id]
diff --git a/defs/misskey.py b/defs/misskey.py
index 23cde3d..3a614ae 100644
--- a/defs/misskey.py
+++ b/defs/misskey.py
@@ -264,7 +264,7 @@ async def send_group(cid: int, files: list[IDriveFile], note: Note, reply_to_mes
await send_text(cid, note, msg.id if msg else None)
-async def send_update(cid: int, note: Note, topic_id: int = None):
+async def send_update(cid: int, note: Note, topic_id: int):
files = list(note.files)
if note.reply:
files.extend(iter(note.reply.files))
diff --git a/misskey_init.py b/misskey_init.py
index dae1596..f9b6c64 100644
--- a/misskey_init.py
+++ b/misskey_init.py
@@ -1,10 +1,11 @@
from mipa.ext import commands
from mipa.router import Router
-from mipac import Note, NotificationFollow, NotificationFollowRequest
+from mipac import Note, NotificationFollow, NotificationFollowRequest, ChatMessage
+from defs.chat import send_chat_message
from defs.misskey import send_update
from defs.notice import send_user_followed, send_follow_request, send_follow_request_accept
-from glover import admin, topic_group_id, timeline_topic_id
+from glover import admin, topic_group_id, timeline_topic_id, notice_topic_id
class MisskeyBot(commands.Bot):
@@ -29,5 +30,11 @@ class MisskeyBot(commands.Bot):
async def on_follow_request_accept(self, notice: NotificationFollowRequest):
await send_follow_request_accept(notice)
+ async def on_chat(self, message: ChatMessage):
+ await send_chat_message(topic_group_id or admin, message, notice_topic_id)
+
+ async def on_chat_unread_message(self, message: ChatMessage):
+ await message.api.read()
+
misskey_bot = MisskeyBot()
diff --git a/modules/chat.py b/modules/chat.py
new file mode 100644
index 0000000..453a1db
--- /dev/null
+++ b/modules/chat.py
@@ -0,0 +1,69 @@
+import contextlib
+from os import remove
+from typing import Tuple
+
+from pyrogram import Client, filters, ContinuePropagation
+from pyrogram.types import Message, CallbackQuery
+
+from defs.confirm import ready_send, ReadySendMessage
+from glover import admin
+from init import notice_filter
+from misskey_init import misskey_bot
+
+
+def get_uid(message: Message) -> Tuple[bool, str]:
+ group, user, uid = False, None, None
+ if (
+ not message.reply_to_message
+ or not message.reply_to_message.reply_markup
+ ):
+ raise ContinuePropagation
+ with contextlib.suppress(IndexError, AttributeError):
+ url = message.reply_to_message.reply_markup.inline_keyboard[0][0].url
+ user = url.split("/")[-1]
+ if "/my/messaging/group/" in url:
+ group = True
+ uid = user
+ else:
+ uid = user.split("?cid=")[1]
+ if not user:
+ raise ContinuePropagation
+ if not uid:
+ raise ContinuePropagation
+ return group, uid
+
+
+@Client.on_message(filters.incoming & notice_filter & filters.text & filters.user(admin))
+async def chat_command(_: Client, message: Message):
+ group, uid = get_uid(message)
+ text = message.text.strip()
+ if text.startswith("@"):
+ raise ContinuePropagation
+ need_send = ReadySendMessage(text, group, uid)
+ await need_send.confirm(message)
+
+
+@Client.on_message(filters.incoming & notice_filter & filters.photo & filters.user(admin))
+async def chat_photo_command(_: Client, message: Message):
+ group, uid = get_uid(message)
+ text = message.caption.strip() if message.caption else ""
+ photo = await message.download()
+ try:
+ file_ = await misskey_bot.core.api.drive.file.action.upload_file(photo)
+ except Exception as e:
+ return await message.reply(f"上传文件失败:{e}", quote=True)
+ need_send = ReadySendMessage(text, group, uid, file_.id)
+ remove(photo)
+ await need_send.confirm(message)
+
+
+@Client.on_callback_query(filters.regex("^chat_send$"))
+async def chat_send_callback(_: Client, callback_query: CallbackQuery):
+ """
+ 发送
+ """
+ if need_send := ready_send.get(callback_query.message.id, None):
+ await need_send.send(callback_query.message)
+ return await callback_query.answer("发送成功")
+ else:
+ return await callback_query.answer("按钮已过期", show_alert=True)
diff --git a/modules/search_user.py b/modules/search_user.py
index ca54776..2eab2ea 100644
--- a/modules/search_user.py
+++ b/modules/search_user.py
@@ -1,3 +1,4 @@
+import contextlib
from mipac.errors import InternalErrorError, AlreadyFollowingError, FolloweeIsYourselfError
from pyrogram import Client, filters
from pyrogram.types import Message, CallbackQuery
@@ -42,6 +43,8 @@ async def follow_user_callback(_: Client, callback_query: CallbackQuery):
关注/取消关注用户
"""
user_id = callback_query.matches[0].group(1)
+ button = callback_query.message.reply_markup
+ follow = True
try:
await misskey_bot.core.api.follow.action.add(user_id)
await callback_query.answer("关注成功", show_alert=True)
@@ -51,12 +54,16 @@ async def follow_user_callback(_: Client, callback_query: CallbackQuery):
try:
await misskey_bot.core.api.follow.action.remove(user_id)
await callback_query.answer("取消关注成功", show_alert=True)
+ follow = False
except Exception as e:
await callback_query.answer("取消关注失败", show_alert=True)
await callback_query.message.reply(f"取消关注失败:{e}", quote=True)
- return
except FolloweeIsYourselfError:
await callback_query.answer("不能关注自己", show_alert=True)
except Exception as e:
await callback_query.answer("关注失败", show_alert=True)
await callback_query.message.reply(f"关注失败:{e}", quote=True)
+ if button:
+ with contextlib.suppress(Exception):
+ button.inline_keyboard[1][0].text = "➖" if follow else "➕"
+ await callback_query.message.edit_reply_markup(button)
diff --git a/requirements.git.txt b/requirements.git.txt
new file mode 100644
index 0000000..e68d1f5
--- /dev/null
+++ b/requirements.git.txt
@@ -0,0 +1,2 @@
+git+https://github.com/yupix/MiPA.git
+git+https://github.com/yupix/MiPAC.git
diff --git a/requirements.txt b/requirements.txt
index 167b23b..41b1a6c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,3 @@
-git+https://github.com/yupix/MiPA.git
-git+https://github.com/yupix/MiPAC.git
Pyrogram==2.0.97
tgCrypto==1.2.5
httpx==0.23.3