Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
import botpy.message
import botpy.types
import botpy.types.message
from astrbot.api import logger
from astrbot.api.event import AstrMessageEvent, MessageChain
from astrbot.api.message_components import At, File, Image, Plain, Record, Video
from astrbot.api.platform import AstrBotMessage, Group, PlatformMetadata
from astrbot.core.platform.sources.qqofficial.qqofficial_chunked_upload import (
QQOFFICIAL_CHUNKED_UPLOAD_THRESHOLD,
QQOfficialChunkedUploader,
)
from astrbot.core.utils.media_utils import MediaResolver, file_uri_to_path, is_file_uri
from botpy import Client
from botpy.http import Route
from botpy.types import message
Expand All @@ -25,16 +34,6 @@
wait_exponential,
)

from astrbot.api import logger
from astrbot.api.event import AstrMessageEvent, MessageChain
from astrbot.api.message_components import File, Image, Plain, Record, Video
from astrbot.api.platform import AstrBotMessage, Group, PlatformMetadata
from astrbot.core.platform.sources.qqofficial.qqofficial_chunked_upload import (
QQOFFICIAL_CHUNKED_UPLOAD_THRESHOLD,
QQOfficialChunkedUploader,
)
from astrbot.core.utils.media_utils import MediaResolver, file_uri_to_path, is_file_uri


class APIReturnNoneError(Exception):
pass
Expand Down Expand Up @@ -233,6 +232,28 @@ def _extract_response_message_id(ret) -> str | None:
ret_id = getattr(ret, "id", None)
return str(ret_id) if ret_id is not None else None

@staticmethod
def _get_mention_id(component: At) -> str | None:
qq = getattr(component, "qq", None)
if not qq:
return None
qq_id = str(qq)
return qq_id if qq_id != "all" else None

@classmethod
def _has_mention(cls, message: MessageChain) -> bool:
return any(
isinstance(component, At) and cls._get_mention_id(component) is not None
for component in message.chain
)

@staticmethod
def _set_media_payload(payload: dict, media: Media, plain_text: str) -> None:
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None

@staticmethod
def _split_message_chain_by_media(message: MessageChain) -> list[MessageChain]:
chunks: list[MessageChain] = []
Expand Down Expand Up @@ -339,9 +360,10 @@ async def _post_send_one(
):
plain_text = plain_text + "\n"

# 根据消息链的 use_markdown_ 标记决定发送模式
# QQ only resolves <@openid> mentions in Markdown messages.
has_mention = self._has_mention(message_to_send)
use_md = getattr(self.send_buffer, "use_markdown_", None)
if use_md is False:
if use_md is False and not has_mention:
payload: dict = {
"content": plain_text,
"msg_type": 0,
Expand Down Expand Up @@ -371,32 +393,23 @@ async def _post_send_one(
self.IMAGE_FILE_TYPE,
group_openid=source.group_openid,
)
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
if record_file_path: # group record msg
media = await self.upload_group_and_c2c_media(
record_file_path,
self.VOICE_FILE_TYPE,
group_openid=source.group_openid,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
if video_file_source:
media = await self.upload_group_and_c2c_media(
video_file_source,
self.VIDEO_FILE_TYPE,
group_openid=source.group_openid,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
if file_source:
media = await self.upload_group_and_c2c_media(
file_source,
Expand All @@ -405,10 +418,7 @@ async def _post_send_one(
group_openid=source.group_openid,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
ret = await self._send_with_markdown_fallback(
send_func=lambda retry_payload: self.bot.api.post_group_message(
group_openid=source.group_openid or "",
Expand All @@ -426,32 +436,23 @@ async def _post_send_one(
self.IMAGE_FILE_TYPE,
openid=source.author.user_openid,
)
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
if record_file_path: # c2c record
media = await self.upload_group_and_c2c_media(
record_file_path,
self.VOICE_FILE_TYPE,
openid=source.author.user_openid,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
if video_file_source:
media = await self.upload_group_and_c2c_media(
video_file_source,
self.VIDEO_FILE_TYPE,
openid=source.author.user_openid,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
if file_source:
media = await self.upload_group_and_c2c_media(
file_source,
Expand All @@ -460,10 +461,7 @@ async def _post_send_one(
openid=source.author.user_openid,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
payload.pop("markdown", None)
payload["content"] = plain_text or None
self._set_media_payload(payload, media, plain_text)
if stream:
ret = await self._send_with_markdown_fallback(
send_func=lambda retry_payload: self.post_c2c_message(
Expand Down Expand Up @@ -806,6 +804,10 @@ async def _parse_to_qqofficial(message: MessageChain):
for i in message.chain:
if isinstance(i, Plain):
plain_text += i.text
elif isinstance(i, At):
qq_id = QQOfficialMessageEvent._get_mention_id(i)
if qq_id:
plain_text += f"<@{qq_id}>"
elif isinstance(i, Image) and not image_base64:
if not i.file:
raise ValueError("Unsupported image file format")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@

import botpy
import botpy.message
from botpy import Client
from botpy.connection import ConnectionState

from astrbot import logger
from astrbot.api.event import MessageChain
from astrbot.api.message_components import At, File, Image, Plain, Record, Reply, Video
Expand All @@ -39,6 +36,8 @@
from astrbot.core.platform.register import register_platform_adapter
from astrbot.core.utils.astrbot_path import get_astrbot_temp_path
from astrbot.core.utils.io import download_file
from botpy import Client
from botpy.connection import ConnectionState

from .qqofficial_message_event import QQOfficialMessageEvent

Expand Down Expand Up @@ -332,7 +331,17 @@ async def _send_by_session_common(
)
return

payload: dict[str, Any] = {"content": plain_text}
has_mention = QQOfficialMessageEvent._has_mention(message_chain)
use_markdown = (
has_mention or getattr(message_chain, "use_markdown_", None) is True
)
if use_markdown and plain_text:
payload: dict[str, Any] = {
"markdown": botpy.types.message.MarkdownPayload(content=plain_text),
"msg_type": 2,
}
else:
payload = {"content": plain_text, "msg_type": 0}
if msg_id and not group_proactive_send:
payload["msg_id"] = msg_id
ret: Any | None = None
Expand Down Expand Up @@ -361,26 +370,29 @@ async def _send_by_session_common(
QQOfficialMessageEvent.IMAGE_FILE_TYPE,
group_openid=session.session_id,
)
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(
payload, media, plain_text
)
if record_file_path:
media = await helper_event.upload_group_and_c2c_media(
record_file_path,
QQOfficialMessageEvent.VOICE_FILE_TYPE,
group_openid=session.session_id,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(
payload, media, plain_text
)
if video_file_source:
media = await helper_event.upload_group_and_c2c_media(
video_file_source,
QQOfficialMessageEvent.VIDEO_FILE_TYPE,
group_openid=session.session_id,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(
payload, media, plain_text
)
payload.pop("msg_id", None)
if file_source:
media = await helper_event.upload_group_and_c2c_media(
Expand All @@ -390,8 +402,9 @@ async def _send_by_session_common(
group_openid=session.session_id,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(
payload, media, plain_text
)
payload.pop("msg_id", None)
ret = await self.client.api.post_group_message(
group_openid=session.session_id or "",
Expand All @@ -401,6 +414,7 @@ async def _send_by_session_common(
# channel (guild) message path
if image_path:
payload["file_image"] = image_path
payload.pop("msg_type", None)
ret = await self.client.api.post_message(
channel_id=session.session_id or "",
**payload,
Expand All @@ -415,26 +429,27 @@ async def _send_by_session_common(
QQOfficialMessageEvent.IMAGE_FILE_TYPE,
openid=session.session_id,
)
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(payload, media, plain_text)
if record_file_path:
media = await helper_event.upload_group_and_c2c_media(
record_file_path,
QQOfficialMessageEvent.VOICE_FILE_TYPE,
openid=session.session_id,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(
payload, media, plain_text
)
if video_file_source:
media = await helper_event.upload_group_and_c2c_media(
video_file_source,
QQOfficialMessageEvent.VIDEO_FILE_TYPE,
openid=session.session_id,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(
payload, media, plain_text
)
if file_source:
media = await helper_event.upload_group_and_c2c_media(
file_source,
Expand All @@ -443,8 +458,9 @@ async def _send_by_session_common(
openid=session.session_id,
)
if media:
payload["media"] = media
payload["msg_type"] = 7
QQOfficialMessageEvent._set_media_payload(
payload, media, plain_text
)
ret = await helper_event.post_c2c_message(
openid=session.session_id,
**payload,
Expand Down Expand Up @@ -620,6 +636,17 @@ def replace_face(match: re.Match[str]) -> str:

return re.sub(r"<faceType=\d+[^>]*>", replace_face, content)

@staticmethod
def _strip_bot_mention_markup(content: str | None, mention_id: str) -> str:
normalized = content or ""
for markup in (
f'<qqbot-at-user id="{mention_id}" />',
f"<@{mention_id}>",
f"<@!{mention_id}>",
):
normalized = normalized.replace(markup, "")
return normalized

@staticmethod
async def _parse_from_qqofficial(
message: botpy.message.Message
Expand Down Expand Up @@ -708,12 +735,8 @@ async def _parse_from_qqofficial(
group_mentioned = bool(bot_mention_ids) or force_group_mention
plain_content = str(getattr(message, "content", "") or "")
for mention_id in bot_mention_ids:
plain_content = plain_content.replace(
f"<@{mention_id}>",
"",
).replace(
f"<@!{mention_id}>",
"",
plain_content = QQOfficialPlatformAdapter._strip_bot_mention_markup(
plain_content, mention_id
)
abm.message_str = QQOfficialPlatformAdapter._parse_face_message(
plain_content.strip(),
Expand Down Expand Up @@ -759,7 +782,9 @@ async def _parse_from_qqofficial(
abm.self_id = ""
content_raw = getattr(message, "content", "") or ""
plain_content = QQOfficialPlatformAdapter._parse_face_message(
content_raw.replace(f"<@!{abm.self_id}>", "").strip(),
QQOfficialPlatformAdapter._strip_bot_mention_markup(
content_raw, str(abm.self_id)
).strip()
)
await QQOfficialPlatformAdapter._append_attachments(
msg,
Expand Down
Loading
Loading