Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
35 changes: 35 additions & 0 deletions astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@
"port": 6185,
},
"platform": [],
"platform_specific": {
# 平台特异配置:按平台分类,平台下按功能分组
"lark": {
"pre_ack_emoji": {"enable": False, "emojis": ["Typing"]},
},
"telegram": {
"pre_ack_emoji": {"enable": False, "emojis": ["✍️"]},
},
},
"wake_prefix": ["/"],
"log_level": "INFO",
"pip_install_arg": "",
Expand Down Expand Up @@ -2295,6 +2304,32 @@
"description": "用户权限不足时是否回复",
"type": "bool",
},
"platform_specific.lark.pre_ack_emoji.enable": {
"description": "[飞书] 启用预回应表情",
"type": "bool",
},
"platform_specific.lark.pre_ack_emoji.emojis": {
"description": "表情列表(飞书表情枚举名)",
"type": "list",
"items": {"type": "string"},
"hint": "表情枚举名参考:https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce",
"condition": {
"platform_specific.lark.pre_ack_emoji.enable": True,
},
},
"platform_specific.telegram.pre_ack_emoji.enable": {
"description": "[Telegram] 启用预回应表情",
"type": "bool",
},
"platform_specific.telegram.pre_ack_emoji.emojis": {
"description": "表情列表(Unicode)",
"type": "list",
"items": {"type": "string"},
"hint": "Telegram 仅支持固定反应集合,参考:https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9",
"condition": {
"platform_specific.telegram.pre_ack_emoji.enable": True,
},
},
},
},
},
Expand Down
21 changes: 21 additions & 0 deletions astrbot/core/pipeline/preprocess_stage/stage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import traceback
import asyncio
import random
from typing import Union, AsyncGenerator
from ..stage import Stage, register_stage
from ..context import PipelineContext
Expand All @@ -22,6 +23,26 @@ async def process(
self, event: AstrMessageEvent
) -> Union[None, AsyncGenerator[None, None]]:
"""在处理事件之前的预处理"""
# 平台特异配置:platform_specific.<platform>.pre_ack_emoji
supported = {"telegram", "lark"}
platform = event.get_platform_name()
cfg = (
self.config.get("platform_specific", {})
.get(platform, {})
.get("pre_ack_emoji", {})
) or {}
emojis = cfg.get("emojis") or []
if (
cfg.get("enable", False)
and platform in supported
and emojis
and event.is_at_or_wake_command
):
try:
await event.react(random.choice(emojis))
except Exception as e:
logger.warning(f"{platform} 预回应表情发送失败: {e}")

# 路径映射
if mappings := self.platform_settings.get("path_mapping", []):
# 支持 Record,Image 消息段的路径映射。
Expand Down
10 changes: 10 additions & 0 deletions astrbot/core/platform/astr_message_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,16 @@ async def send(self, message: MessageChain):
)
self._has_send_oper = True

async def react(self, emoji: str):
"""
对消息添加表情回应。

默认实现为发送一条包含该表情的消息。
注意:此实现并不一定符合所有平台的原生“表情回应”行为。
如需支持平台原生的消息反应功能,请在对应平台的子类中重写本方法。
"""
await self.send(MessageChain([Plain(emoji)]))

async def get_group(self, group_id: str = None, **kwargs) -> Optional[Group]:
"""获取一个群聊的数据, 如果不填写 group_id: 如果是私聊消息,返回 None。如果是群聊消息,返回当前群聊的数据。

Expand Down
16 changes: 16 additions & 0 deletions astrbot/core/platform/sources/lark/lark_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,22 @@ async def send(self, message: MessageChain):

await super().send(message)

async def react(self, emoji: str):
request = (
CreateMessageReactionRequest.builder()
.message_id(self.message_obj.message_id)
.request_body(
CreateMessageReactionRequestBody.builder()
.reaction_type(Emoji.builder().emoji_type(emoji).build())
.build()
)
.build()
)
response = await self.bot.im.v1.message_reaction.acreate(request)
if not response.success():
logger.error(f"发送飞书表情回应失败({response.code}): {response.msg}")
return None

async def send_streaming(self, generator, use_fallback: bool = False):
buffer = None
async for chain in generator:
Expand Down
34 changes: 34 additions & 0 deletions astrbot/core/platform/sources/telegram/tg_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from astrbot.core.utils.io import download_file
from astrbot import logger
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
from telegram import ReactionTypeEmoji, ReactionTypeCustomEmoji


class TelegramPlatformEvent(AstrMessageEvent):
Expand Down Expand Up @@ -135,6 +136,39 @@ async def send(self, message: MessageChain):
await self.send_with_client(self.client, message, self.get_sender_id())
await super().send(message)

async def react(self, emoji: str | None, big: bool = False):
"""
给原消息添加 Telegram 反应:
- 普通 emoji:传入 '👍'、'😂' 等
- 自定义表情:传入其 custom_emoji_id(纯数字字符串)
- 取消本机器人的反应:传入 None 或空字符串
"""
try:
# 解析 chat_id(去掉超级群的 "#<thread_id>" 片段)
if self.get_message_type() == MessageType.GROUP_MESSAGE:
chat_id = (self.message_obj.group_id or "").split("#")[0]
else:
chat_id = self.get_sender_id()

message_id = int(self.message_obj.message_id)

# 组装 reaction 参数(必须是 ReactionType 的列表)
if not emoji: # 清空本 bot 的反应
reaction_param = [] # 空列表表示移除本 bot 的反应
elif emoji.isdigit(): # 自定义表情:传 custom_emoji_id
reaction_param = [ReactionTypeCustomEmoji(emoji)]
else: # 普通 emoji
reaction_param = [ReactionTypeEmoji(emoji)]

await self.client.set_message_reaction(
chat_id=chat_id,
message_id=message_id,
reaction=reaction_param, # 注意是列表
is_big=big, # 可选:大动画
)
except Exception as e:
logger.error(f"[Telegram] 添加反应失败: {e}")

async def send_streaming(self, generator, use_fallback: bool = False):
message_thread_id = None

Expand Down