95 lines
3.1 KiB
Python
95 lines
3.1 KiB
Python
"""
|
||
腾讯元宝网页版驱动引擎(yuanbao.tencent.com)。
|
||
元宝暂无公开 API,仅支持网页模式。
|
||
|
||
选择器说明(如页面改版需更新):
|
||
- 输入框:[class*="input-area"] textarea 或 [contenteditable="true"]
|
||
- 发送按钮:[class*="send-btn"] 或 [aria-label="发送"]
|
||
- 停止生成:[class*="stop"] 相关按钮
|
||
- 复制按钮:最后一条回复下的复制按钮
|
||
"""
|
||
import asyncio
|
||
from .base import BaseEngine
|
||
|
||
|
||
class YuanbaoEngine(BaseEngine):
|
||
async def generate(self, prompt: str) -> str:
|
||
await self.page.goto("https://yuanbao.tencent.com/chat")
|
||
await self.page.wait_for_load_state("networkidle")
|
||
|
||
# 登录检测
|
||
input_selectors = [
|
||
"textarea[class*='input']",
|
||
"div[class*='input-area'] textarea",
|
||
"[contenteditable='true']",
|
||
"textarea",
|
||
]
|
||
editor = None
|
||
for sel in input_selectors:
|
||
try:
|
||
loc = self.page.locator(sel).first
|
||
await loc.wait_for(state="visible", timeout=4000)
|
||
editor = loc
|
||
break
|
||
except Exception:
|
||
continue
|
||
if editor is None:
|
||
return "ERROR:REQUIRE_LOGIN"
|
||
|
||
# 输入提示词
|
||
await editor.click()
|
||
await self.page.keyboard.insert_text(prompt)
|
||
await asyncio.sleep(0.5)
|
||
|
||
# 发送
|
||
sent = False
|
||
for sel in (
|
||
'[class*="send-btn"]',
|
||
'[aria-label="发送"]',
|
||
'button[class*="send"]',
|
||
'button[type="submit"]',
|
||
):
|
||
try:
|
||
btn = self.page.locator(sel).first
|
||
if await btn.is_visible(timeout=1000):
|
||
await btn.click()
|
||
sent = True
|
||
break
|
||
except Exception:
|
||
continue
|
||
if not sent:
|
||
await self.page.keyboard.press("Enter")
|
||
|
||
print("💡 [llm-manager/yuanbao] 已发送提示词,等待腾讯元宝生成响应...")
|
||
await asyncio.sleep(2)
|
||
|
||
# 等待生成完毕
|
||
stop_selectors = ['[class*="stop"]', '[aria-label="停止"]', '[aria-label="停止生成"]']
|
||
deadline = asyncio.get_event_loop().time() + 150
|
||
while asyncio.get_event_loop().time() < deadline:
|
||
stop_visible = False
|
||
for sel in stop_selectors:
|
||
try:
|
||
if await self.page.locator(sel).first.is_visible(timeout=300):
|
||
stop_visible = True
|
||
break
|
||
except Exception:
|
||
pass
|
||
if not stop_visible:
|
||
break
|
||
await asyncio.sleep(2)
|
||
|
||
await asyncio.sleep(2)
|
||
|
||
# 取结果
|
||
try:
|
||
copy_btn = self.page.locator(
|
||
'[aria-label="复制"], [class*="copy"], button:has(svg[class*="copy"])'
|
||
).last
|
||
await copy_btn.click()
|
||
await asyncio.sleep(0.5)
|
||
result = await self.page.evaluate("navigator.clipboard.readText()")
|
||
return result.strip()
|
||
except Exception as e:
|
||
return f"ERROR:抓取腾讯元宝内容时异常:{e}"
|