227 lines
8.2 KiB
Python
227 lines
8.2 KiB
Python
"""
|
|
随机视频插件
|
|
|
|
用户发送关键词获取随机小姐姐视频
|
|
"""
|
|
|
|
import tomllib
|
|
import httpx
|
|
import uuid
|
|
from pathlib import Path
|
|
from loguru import logger
|
|
from typing import List
|
|
from utils.plugin_base import PluginBase
|
|
from utils.decorators import on_text_message
|
|
|
|
|
|
class RandomVideo(PluginBase):
|
|
"""随机视频插件"""
|
|
|
|
description = "随机小姐姐视频"
|
|
author = "ShiHao"
|
|
version = "1.0.0"
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.config = None
|
|
|
|
async def async_init(self):
|
|
"""异步初始化"""
|
|
config_path = Path(__file__).parent / "config.toml"
|
|
with open(config_path, "rb") as f:
|
|
self.config = tomllib.load(f)
|
|
logger.success("随机视频插件已加载")
|
|
|
|
@on_text_message(priority=65)
|
|
async def handle_message(self, bot, message: dict):
|
|
"""处理文本消息"""
|
|
content = message.get("Content", "").strip()
|
|
from_wxid = message.get("FromWxid", "")
|
|
is_group = message.get("IsGroup", False)
|
|
|
|
# 精确匹配关键词
|
|
if content not in self.config["behavior"]["keywords"]:
|
|
return True
|
|
|
|
if not self.config["behavior"]["enabled"]:
|
|
return True
|
|
|
|
# 检查群聊过滤
|
|
if is_group:
|
|
enabled_groups = self.config["behavior"]["enabled_groups"]
|
|
disabled_groups = self.config["behavior"]["disabled_groups"]
|
|
|
|
if from_wxid in disabled_groups:
|
|
return True
|
|
if enabled_groups and from_wxid not in enabled_groups:
|
|
return True
|
|
|
|
logger.info(f"收到随机视频请求: {from_wxid}")
|
|
|
|
try:
|
|
# 获取视频URL
|
|
video_url = await self._fetch_video_url()
|
|
if not video_url:
|
|
await bot.send_text(from_wxid, self.config["messages"]["api_error"])
|
|
return False
|
|
|
|
# 下载视频
|
|
video_path = await self._download_video(video_url)
|
|
if not video_path:
|
|
await bot.send_text(from_wxid, self.config["messages"]["download_error"])
|
|
return False
|
|
|
|
# 发送视频
|
|
success = await bot.send_file(from_wxid, video_path)
|
|
if success:
|
|
logger.success(f"随机视频发送成功: {from_wxid}")
|
|
# 延迟删除,等待微信上传完成
|
|
import asyncio
|
|
asyncio.create_task(self._delayed_cleanup(video_path, 120))
|
|
else:
|
|
await bot.send_text(from_wxid, self.config["messages"]["send_error"])
|
|
logger.error(f"随机视频发送失败: {from_wxid}")
|
|
# 立即删除失败的文件
|
|
try:
|
|
Path(video_path).unlink()
|
|
except:
|
|
pass
|
|
|
|
except Exception as e:
|
|
logger.error(f"随机视频处理失败: {e}")
|
|
await bot.send_text(from_wxid, self.config["messages"]["api_error"])
|
|
|
|
return False
|
|
|
|
async def _delayed_cleanup(self, file_path: str, delay: int):
|
|
"""延迟删除文件"""
|
|
import asyncio
|
|
await asyncio.sleep(delay)
|
|
try:
|
|
Path(file_path).unlink()
|
|
logger.info(f"已清理临时文件: {file_path}")
|
|
except Exception as e:
|
|
logger.warning(f"清理临时文件失败: {e}")
|
|
|
|
async def _fetch_video_url(self) -> str:
|
|
"""获取视频URL"""
|
|
try:
|
|
timeout = httpx.Timeout(self.config["api"]["timeout"])
|
|
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
response = await client.get(self.config["api"]["url"])
|
|
|
|
if response.status_code != 200:
|
|
logger.error(f"API返回错误: {response.status_code}")
|
|
return ""
|
|
|
|
result = response.json()
|
|
|
|
if result.get("code") != 200:
|
|
logger.error(f"API错误: {result.get('msg')}")
|
|
return ""
|
|
|
|
video_url = result.get("data", "")
|
|
logger.info(f"获取到视频URL: {video_url}")
|
|
return video_url
|
|
|
|
except Exception as e:
|
|
logger.error(f"获取视频URL失败: {e}")
|
|
return ""
|
|
|
|
async def _download_video(self, video_url: str) -> str:
|
|
"""下载视频到本地"""
|
|
try:
|
|
videos_dir = Path(__file__).parent / "videos"
|
|
videos_dir.mkdir(exist_ok=True)
|
|
|
|
filename = f"random_{uuid.uuid4().hex[:8]}.mp4"
|
|
file_path = videos_dir / filename
|
|
|
|
timeout = httpx.Timeout(connect=10.0, read=60.0, write=10.0, pool=10.0)
|
|
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
response = await client.get(video_url)
|
|
response.raise_for_status()
|
|
|
|
with open(file_path, "wb") as f:
|
|
f.write(response.content)
|
|
|
|
logger.info(f"视频下载成功: {file_path}")
|
|
return str(file_path.resolve())
|
|
|
|
except Exception as e:
|
|
logger.error(f"下载视频失败: {e}")
|
|
return ""
|
|
|
|
def get_llm_tools(self) -> List[dict]:
|
|
"""返回LLM工具定义"""
|
|
return [
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "get_random_video",
|
|
"description": "获取随机小姐姐视频。当用户想看随机视频、小姐姐视频、擦边视频时调用",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {},
|
|
"required": []
|
|
}
|
|
}
|
|
}
|
|
]
|
|
|
|
async def execute_llm_tool(self, tool_name: str, arguments: dict, bot, from_wxid: str) -> dict:
|
|
"""执行LLM工具调用"""
|
|
try:
|
|
if not self.config["behavior"]["enabled"]:
|
|
return {"success": False, "message": "随机视频插件未启用"}
|
|
|
|
# 检查群聊过滤
|
|
is_group = from_wxid.endswith("@chatroom")
|
|
if is_group:
|
|
enabled_groups = self.config["behavior"]["enabled_groups"]
|
|
disabled_groups = self.config["behavior"]["disabled_groups"]
|
|
|
|
if from_wxid in disabled_groups:
|
|
return {"success": False, "message": "此群聊未启用随机视频功能"}
|
|
if enabled_groups and from_wxid not in enabled_groups:
|
|
return {"success": False, "message": "此群聊未启用随机视频功能"}
|
|
|
|
if tool_name == "get_random_video":
|
|
logger.info(f"LLM工具调用获取随机视频: {from_wxid}")
|
|
|
|
# 获取视频URL
|
|
video_url = await self._fetch_video_url()
|
|
if not video_url:
|
|
await bot.send_text(from_wxid, self.config["messages"]["api_error"])
|
|
return {"success": False, "message": "获取视频URL失败"}
|
|
|
|
# 下载视频
|
|
video_path = await self._download_video(video_url)
|
|
if not video_path:
|
|
await bot.send_text(from_wxid, self.config["messages"]["download_error"])
|
|
return {"success": False, "message": "视频下载失败"}
|
|
|
|
# 发送视频
|
|
success = await bot.send_file(from_wxid, video_path)
|
|
|
|
if success:
|
|
logger.success(f"随机视频发送成功: {from_wxid}")
|
|
# 延迟删除,等待微信上传完成
|
|
import asyncio
|
|
asyncio.create_task(self._delayed_cleanup(video_path, 120))
|
|
return {"success": True, "message": "随机视频发送成功"}
|
|
else:
|
|
await bot.send_text(from_wxid, self.config["messages"]["send_error"])
|
|
# 立即删除失败的文件
|
|
try:
|
|
Path(video_path).unlink()
|
|
except:
|
|
pass
|
|
return {"success": False, "message": "视频发送失败"}
|
|
else:
|
|
return None
|
|
|
|
except Exception as e:
|
|
logger.error(f"LLM工具执行失败: {e}")
|
|
return {"success": False, "message": f"执行失败: {str(e)}"}
|