mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 12:05:54 +00:00
Compare commits
3 Commits
copilot/fi
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5029d89630 | ||
|
|
b081ef89d5 | ||
|
|
75d449f21a |
@@ -166,6 +166,11 @@ class QQOfficialClient:
|
||||
else:
|
||||
message_data['image_attachments'] = None
|
||||
|
||||
# Extract message_reference if present
|
||||
message_reference = msg.get('d', {}).get('message_reference', {})
|
||||
if message_reference:
|
||||
message_data['message_reference'] = message_reference
|
||||
|
||||
return message_data
|
||||
|
||||
async def is_image(self, attachment: dict) -> bool:
|
||||
@@ -272,6 +277,57 @@ class QQOfficialClient:
|
||||
return True
|
||||
return time.time() > self.access_token_expiry_time
|
||||
|
||||
async def get_message_by_id(self, message_id: str, channel_id: str = None, group_openid: str = None, user_openid: str = None) -> Dict[str, Any]:
|
||||
"""根据消息ID获取消息内容
|
||||
|
||||
Args:
|
||||
message_id: 消息ID
|
||||
channel_id: 频道ID(频道消息需要)
|
||||
group_openid: 群组openid(群消息需要)
|
||||
user_openid: 用户openid(私聊消息需要)
|
||||
|
||||
Returns:
|
||||
消息内容字典
|
||||
"""
|
||||
if not await self.check_access_token():
|
||||
await self.get_access_token()
|
||||
|
||||
# Validate that exactly one context parameter is provided
|
||||
provided_contexts = sum([bool(channel_id), bool(group_openid), bool(user_openid)])
|
||||
if provided_contexts == 0:
|
||||
await self.logger.warning(f'Cannot fetch message {message_id}: no context provided')
|
||||
return {}
|
||||
if provided_contexts > 1:
|
||||
await self.logger.warning(f'Cannot fetch message {message_id}: multiple contexts provided')
|
||||
return {}
|
||||
|
||||
# Determine which API endpoint to use based on provided parameters
|
||||
if channel_id:
|
||||
# Channel message
|
||||
url = f'{self.base_url}/channels/{channel_id}/messages/{message_id}'
|
||||
elif group_openid:
|
||||
# Group message
|
||||
url = f'{self.base_url}/v2/groups/{group_openid}/messages/{message_id}'
|
||||
elif user_openid:
|
||||
# Private message
|
||||
url = f'{self.base_url}/v2/users/{user_openid}/messages/{message_id}'
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
headers = {
|
||||
'Authorization': f'QQBot {self.access_token}',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
try:
|
||||
response = await client.get(url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
await self.logger.warning(f'Failed to fetch message {message_id}: {response.status_code}')
|
||||
return {}
|
||||
except Exception as e:
|
||||
await self.logger.warning(f'Error fetching message {message_id}: {e}')
|
||||
return {}
|
||||
|
||||
async def repeat_seed(self, bot_secret: str, target_size: int = 32) -> bytes:
|
||||
seed = bot_secret
|
||||
while len(seed) < target_size:
|
||||
|
||||
@@ -110,3 +110,10 @@ class QQOfficialEvent(dict):
|
||||
文件类型
|
||||
"""
|
||||
return self.get('content_type', '')
|
||||
|
||||
@property
|
||||
def message_reference(self) -> dict:
|
||||
"""
|
||||
引用消息
|
||||
"""
|
||||
return self.get('message_reference', {})
|
||||
|
||||
@@ -100,6 +100,7 @@ class PreProcessor(stage.PipelineStage):
|
||||
|
||||
plain_text = ''
|
||||
quote_msg = query.pipeline_config['trigger'].get('misc', '').get('combine-quote-message')
|
||||
quoted_text = '' # Store quoted message text
|
||||
|
||||
for me in query.message_chain:
|
||||
if isinstance(me, platform_message.Plain):
|
||||
@@ -117,6 +118,7 @@ class PreProcessor(stage.PipelineStage):
|
||||
elif isinstance(me, platform_message.Quote) and quote_msg:
|
||||
for msg in me.origin:
|
||||
if isinstance(msg, platform_message.Plain):
|
||||
quoted_text += msg.text
|
||||
content_list.append(provider_message.ContentElement.from_text(msg.text))
|
||||
elif isinstance(msg, platform_message.Image):
|
||||
if selected_runner != 'local-agent' or (
|
||||
@@ -126,6 +128,7 @@ class PreProcessor(stage.PipelineStage):
|
||||
content_list.append(provider_message.ContentElement.from_image_base64(msg.base64))
|
||||
|
||||
query.variables['user_message_text'] = plain_text
|
||||
query.variables['quoted_message_text'] = quoted_text # Add quoted text as variable
|
||||
|
||||
query.user_message = provider_message.Message(role='user', content=content_list)
|
||||
# =========== 触发事件 PromptPreProcessing
|
||||
|
||||
@@ -16,6 +16,9 @@ from ..logger import EventLogger
|
||||
|
||||
|
||||
class QQOfficialMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
|
||||
def __init__(self, bot: QQOfficialClient = None):
|
||||
self.bot = bot
|
||||
|
||||
@staticmethod
|
||||
async def yiri2target(message_chain: platform_message.MessageChain):
|
||||
content_list = []
|
||||
@@ -31,10 +34,63 @@ class QQOfficialMessageConverter(abstract_platform_adapter.AbstractMessageConver
|
||||
|
||||
return content_list
|
||||
|
||||
@staticmethod
|
||||
async def target2yiri(message: str, message_id: str, pic_url: str, content_type):
|
||||
async def target2yiri(self, message: str, message_id: str, pic_url: str, content_type, message_reference: dict = None, event_type: str = None, channel_id: str = None, group_openid: str = None, user_openid: str = None):
|
||||
yiri_msg_list = []
|
||||
yiri_msg_list.append(platform_message.Source(id=message_id, time=datetime.datetime.now()))
|
||||
|
||||
# Handle quoted message if message_reference exists
|
||||
if message_reference and message_reference.get('message_id') and self.bot:
|
||||
referenced_msg_id = message_reference.get('message_id')
|
||||
try:
|
||||
# Fetch the referenced message
|
||||
referenced_msg = await self.bot.get_message_by_id(
|
||||
referenced_msg_id,
|
||||
channel_id=channel_id,
|
||||
group_openid=group_openid,
|
||||
user_openid=user_openid
|
||||
)
|
||||
|
||||
if referenced_msg:
|
||||
# Create message chain for the quoted content
|
||||
quoted_content = referenced_msg.get('content', '')
|
||||
quoted_chain = platform_message.MessageChain()
|
||||
|
||||
if quoted_content:
|
||||
quoted_chain.append(platform_message.Plain(text=quoted_content))
|
||||
|
||||
# Add images if present in quoted message
|
||||
quoted_attachments = referenced_msg.get('attachments', [])
|
||||
for attachment in quoted_attachments:
|
||||
if attachment.get('content_type', '').startswith('image/'):
|
||||
img_url = attachment.get('url', '')
|
||||
if img_url:
|
||||
try:
|
||||
img_base64 = await image.get_qq_official_image_base64(
|
||||
pic_url=img_url if img_url.startswith('https://') else 'https://' + img_url,
|
||||
content_type=attachment.get('content_type', '')
|
||||
)
|
||||
quoted_chain.append(platform_message.Image(base64=img_base64))
|
||||
except Exception:
|
||||
# If image fetch fails, just skip it
|
||||
pass
|
||||
|
||||
# Get sender info from referenced message
|
||||
quoted_sender_id = referenced_msg.get('author', {}).get('id', '') or \
|
||||
referenced_msg.get('author', {}).get('user_openid', '') or \
|
||||
referenced_msg.get('author', {}).get('member_openid', '')
|
||||
|
||||
# Add Quote component
|
||||
yiri_msg_list.append(
|
||||
platform_message.Quote(
|
||||
id=referenced_msg_id,
|
||||
sender_id=quoted_sender_id,
|
||||
origin=quoted_chain,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
# If fetching quoted message fails, log and continue
|
||||
await self.bot.logger.warning(f'Failed to fetch quoted message {referenced_msg_id}: {e}')
|
||||
|
||||
if pic_url is not None:
|
||||
base64_url = await image.get_qq_official_image_base64(pic_url=pic_url, content_type=content_type)
|
||||
yiri_msg_list.append(platform_message.Image(base64=base64_url))
|
||||
@@ -45,20 +101,35 @@ class QQOfficialMessageConverter(abstract_platform_adapter.AbstractMessageConver
|
||||
|
||||
|
||||
class QQOfficialEventConverter(abstract_platform_adapter.AbstractEventConverter):
|
||||
def __init__(self, message_converter: QQOfficialMessageConverter):
|
||||
self.message_converter = message_converter
|
||||
|
||||
@staticmethod
|
||||
async def yiri2target(event: platform_events.MessageEvent) -> QQOfficialEvent:
|
||||
return event.source_platform_object
|
||||
|
||||
@staticmethod
|
||||
async def target2yiri(event: QQOfficialEvent):
|
||||
async def target2yiri(self, event: QQOfficialEvent):
|
||||
"""
|
||||
QQ官方消息转换为LB对象
|
||||
"""
|
||||
yiri_chain = await QQOfficialMessageConverter.target2yiri(
|
||||
# Get message reference if present
|
||||
message_reference = event.message_reference
|
||||
|
||||
# Determine context based on event type
|
||||
channel_id = event.channel_id if event.t in ['AT_MESSAGE_CREATE', 'DIRECT_MESSAGE_CREATE'] else None
|
||||
group_openid = event.group_openid if event.t == 'GROUP_AT_MESSAGE_CREATE' else None
|
||||
user_openid = event.user_openid if event.t == 'C2C_MESSAGE_CREATE' else None
|
||||
|
||||
yiri_chain = await self.message_converter.target2yiri(
|
||||
message=event.content,
|
||||
message_id=event.d_id,
|
||||
pic_url=event.attachments,
|
||||
content_type=event.content_type,
|
||||
message_reference=message_reference,
|
||||
event_type=event.t,
|
||||
channel_id=channel_id,
|
||||
group_openid=group_openid,
|
||||
user_openid=user_openid,
|
||||
)
|
||||
|
||||
if event.t == 'C2C_MESSAGE_CREATE':
|
||||
@@ -135,20 +206,27 @@ class QQOfficialAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter
|
||||
config: dict
|
||||
bot_account_id: str
|
||||
bot_uuid: str = None
|
||||
message_converter: QQOfficialMessageConverter = QQOfficialMessageConverter()
|
||||
event_converter: QQOfficialEventConverter = QQOfficialEventConverter()
|
||||
message_converter: QQOfficialMessageConverter
|
||||
event_converter: QQOfficialEventConverter
|
||||
|
||||
def __init__(self, config: dict, logger: EventLogger):
|
||||
bot = QQOfficialClient(
|
||||
app_id=config['appid'], secret=config['secret'], token=config['token'], logger=logger, unified_mode=True
|
||||
)
|
||||
|
||||
# Initialize converters with bot reference
|
||||
message_converter = QQOfficialMessageConverter(bot=bot)
|
||||
event_converter = QQOfficialEventConverter(message_converter=message_converter)
|
||||
|
||||
super().__init__(
|
||||
config=config,
|
||||
logger=logger,
|
||||
bot=bot,
|
||||
bot_account_id=config['appid'],
|
||||
)
|
||||
|
||||
self.message_converter = message_converter
|
||||
self.event_converter = event_converter
|
||||
|
||||
async def reply_message(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user