修复代码格式和文件名大小写问题
This commit is contained in:
@@ -17,51 +17,51 @@ logger = get_logger("bilibili_tool")
|
||||
|
||||
class BilibiliVideoAnalyzer:
|
||||
"""哔哩哔哩视频分析器,集成视频下载和AI分析功能"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.video_analyzer = get_video_analyzer()
|
||||
self.headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Referer': 'https://www.bilibili.com/',
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"Referer": "https://www.bilibili.com/",
|
||||
}
|
||||
|
||||
|
||||
def extract_bilibili_url(self, text: str) -> Optional[str]:
|
||||
"""从文本中提取哔哩哔哩视频链接"""
|
||||
# 哔哩哔哩短链接模式
|
||||
short_pattern = re.compile(r'https?://b23\.tv/[\w]+', re.IGNORECASE)
|
||||
short_pattern = re.compile(r"https?://b23\.tv/[\w]+", re.IGNORECASE)
|
||||
# 哔哩哔哩完整链接模式
|
||||
full_pattern = re.compile(r'https?://(?:www\.)?bilibili\.com/video/(?:BV[\w]+|av\d+)', re.IGNORECASE)
|
||||
|
||||
full_pattern = re.compile(r"https?://(?:www\.)?bilibili\.com/video/(?:BV[\w]+|av\d+)", re.IGNORECASE)
|
||||
|
||||
# 先匹配短链接
|
||||
short_match = short_pattern.search(text)
|
||||
if short_match:
|
||||
return short_match.group(0)
|
||||
|
||||
|
||||
# 再匹配完整链接
|
||||
full_match = full_pattern.search(text)
|
||||
if full_match:
|
||||
return full_match.group(0)
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def get_video_info(self, url: str) -> Optional[Dict[str, Any]]:
|
||||
"""获取哔哩哔哩视频基本信息"""
|
||||
try:
|
||||
logger.info(f"🔍 解析视频URL: {url}")
|
||||
|
||||
|
||||
# 如果是短链接,先解析为完整链接
|
||||
if 'b23.tv' in url:
|
||||
if "b23.tv" in url:
|
||||
logger.info("🔗 检测到短链接,正在解析...")
|
||||
timeout = aiohttp.ClientTimeout(total=30)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.get(url, headers=self.headers, allow_redirects=True) as response:
|
||||
url = str(response.url)
|
||||
logger.info(f"✅ 短链接解析完成: {url}")
|
||||
|
||||
|
||||
# 提取BV号或AV号
|
||||
bv_match = re.search(r'BV([\w]+)', url)
|
||||
av_match = re.search(r'av(\d+)', url)
|
||||
|
||||
bv_match = re.search(r"BV([\w]+)", url)
|
||||
av_match = re.search(r"av(\d+)", url)
|
||||
|
||||
if bv_match:
|
||||
bvid = f"BV{bv_match.group(1)}"
|
||||
api_url = f"https://api.bilibili.com/x/web-interface/view?bvid={bvid}"
|
||||
@@ -73,7 +73,7 @@ class BilibiliVideoAnalyzer:
|
||||
else:
|
||||
logger.error("❌ 无法从URL中提取视频ID")
|
||||
return None
|
||||
|
||||
|
||||
# 获取视频信息
|
||||
logger.info("📡 正在获取视频信息...")
|
||||
timeout = aiohttp.ClientTimeout(total=30)
|
||||
@@ -83,38 +83,39 @@ class BilibiliVideoAnalyzer:
|
||||
logger.error(f"❌ API请求失败,状态码: {response.status}")
|
||||
return None
|
||||
data = await response.json()
|
||||
|
||||
if data.get('code') != 0:
|
||||
error_msg = data.get('message', '未知错误')
|
||||
|
||||
if data.get("code") != 0:
|
||||
error_msg = data.get("message", "未知错误")
|
||||
logger.error(f"❌ B站API返回错误: {error_msg} (code: {data.get('code')})")
|
||||
return None
|
||||
|
||||
video_data = data['data']
|
||||
|
||||
|
||||
video_data = data["data"]
|
||||
|
||||
# 验证必要字段
|
||||
if not video_data.get('title'):
|
||||
if not video_data.get("title"):
|
||||
logger.error("❌ 视频数据不完整,缺少标题")
|
||||
return None
|
||||
|
||||
|
||||
result = {
|
||||
'title': video_data.get('title', ''),
|
||||
'desc': video_data.get('desc', ''),
|
||||
'duration': video_data.get('duration', 0),
|
||||
'view': video_data.get('stat', {}).get('view', 0),
|
||||
'like': video_data.get('stat', {}).get('like', 0),
|
||||
'coin': video_data.get('stat', {}).get('coin', 0),
|
||||
'favorite': video_data.get('stat', {}).get('favorite', 0),
|
||||
'share': video_data.get('stat', {}).get('share', 0),
|
||||
'owner': video_data.get('owner', {}).get('name', ''),
|
||||
'pubdate': video_data.get('pubdate', 0),
|
||||
'aid': video_data.get('aid'),
|
||||
'bvid': video_data.get('bvid'),
|
||||
'cid': video_data.get('cid') or (video_data.get('pages', [{}])[0].get('cid') if video_data.get('pages') else None)
|
||||
"title": video_data.get("title", ""),
|
||||
"desc": video_data.get("desc", ""),
|
||||
"duration": video_data.get("duration", 0),
|
||||
"view": video_data.get("stat", {}).get("view", 0),
|
||||
"like": video_data.get("stat", {}).get("like", 0),
|
||||
"coin": video_data.get("stat", {}).get("coin", 0),
|
||||
"favorite": video_data.get("stat", {}).get("favorite", 0),
|
||||
"share": video_data.get("stat", {}).get("share", 0),
|
||||
"owner": video_data.get("owner", {}).get("name", ""),
|
||||
"pubdate": video_data.get("pubdate", 0),
|
||||
"aid": video_data.get("aid"),
|
||||
"bvid": video_data.get("bvid"),
|
||||
"cid": video_data.get("cid")
|
||||
or (video_data.get("pages", [{}])[0].get("cid") if video_data.get("pages") else None),
|
||||
}
|
||||
|
||||
|
||||
logger.info(f"✅ 视频信息获取成功: {result['title']}")
|
||||
return result
|
||||
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
logger.error("❌ 获取视频信息超时")
|
||||
return None
|
||||
@@ -125,15 +126,15 @@ class BilibiliVideoAnalyzer:
|
||||
logger.error(f"❌ 获取哔哩哔哩视频信息时发生未知错误: {e}")
|
||||
logger.exception("详细错误信息:")
|
||||
return None
|
||||
|
||||
|
||||
async def get_video_stream_url(self, aid: int, cid: int) -> Optional[str]:
|
||||
"""获取视频流URL"""
|
||||
try:
|
||||
logger.info(f"🎥 获取视频流URL: aid={aid}, cid={cid}")
|
||||
|
||||
|
||||
# 构建播放信息API请求
|
||||
api_url = f"https://api.bilibili.com/x/player/playurl?avid={aid}&cid={cid}&qn=80&type=&otype=json&fourk=1&fnver=0&fnval=4048&session="
|
||||
|
||||
|
||||
timeout = aiohttp.ClientTimeout(total=30)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.get(api_url, headers=self.headers) as response:
|
||||
@@ -141,38 +142,38 @@ class BilibiliVideoAnalyzer:
|
||||
logger.error(f"❌ 播放信息API请求失败,状态码: {response.status}")
|
||||
return None
|
||||
data = await response.json()
|
||||
|
||||
if data.get('code') != 0:
|
||||
error_msg = data.get('message', '未知错误')
|
||||
|
||||
if data.get("code") != 0:
|
||||
error_msg = data.get("message", "未知错误")
|
||||
logger.error(f"❌ 获取播放信息失败: {error_msg} (code: {data.get('code')})")
|
||||
return None
|
||||
|
||||
play_data = data['data']
|
||||
|
||||
|
||||
play_data = data["data"]
|
||||
|
||||
# 尝试获取DASH格式的视频流
|
||||
if 'dash' in play_data and play_data['dash'].get('video'):
|
||||
videos = play_data['dash']['video']
|
||||
if "dash" in play_data and play_data["dash"].get("video"):
|
||||
videos = play_data["dash"]["video"]
|
||||
logger.info(f"🎬 找到 {len(videos)} 个DASH视频流")
|
||||
|
||||
|
||||
# 选择最高质量的视频流
|
||||
video_stream = max(videos, key=lambda x: x.get('bandwidth', 0))
|
||||
stream_url = video_stream.get('baseUrl') or video_stream.get('base_url')
|
||||
|
||||
video_stream = max(videos, key=lambda x: x.get("bandwidth", 0))
|
||||
stream_url = video_stream.get("baseUrl") or video_stream.get("base_url")
|
||||
|
||||
if stream_url:
|
||||
logger.info(f"✅ 获取到DASH视频流URL (带宽: {video_stream.get('bandwidth', 0)})")
|
||||
return stream_url
|
||||
|
||||
|
||||
# 降级到FLV格式
|
||||
if 'durl' in play_data and play_data['durl']:
|
||||
if "durl" in play_data and play_data["durl"]:
|
||||
logger.info("📹 使用FLV格式视频流")
|
||||
stream_url = play_data['durl'][0].get('url')
|
||||
stream_url = play_data["durl"][0].get("url")
|
||||
if stream_url:
|
||||
logger.info("✅ 获取到FLV视频流URL")
|
||||
return stream_url
|
||||
|
||||
|
||||
logger.error("❌ 未找到可用的视频流")
|
||||
return None
|
||||
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
logger.error("❌ 获取视频流URL超时")
|
||||
return None
|
||||
@@ -183,55 +184,55 @@ class BilibiliVideoAnalyzer:
|
||||
logger.error(f"❌ 获取视频流URL时发生未知错误: {e}")
|
||||
logger.exception("详细错误信息:")
|
||||
return None
|
||||
|
||||
|
||||
async def download_video_bytes(self, stream_url: str, max_size_mb: int = 100) -> Optional[bytes]:
|
||||
"""下载视频字节数据
|
||||
|
||||
|
||||
Args:
|
||||
stream_url: 视频流URL
|
||||
max_size_mb: 最大下载大小限制(MB),默认100MB
|
||||
|
||||
|
||||
Returns:
|
||||
视频字节数据或None
|
||||
"""
|
||||
try:
|
||||
logger.info(f"📥 开始下载视频: {stream_url[:50]}...")
|
||||
|
||||
|
||||
# 设置超时和大小限制
|
||||
timeout = aiohttp.ClientTimeout(total=300, connect=30) # 5分钟总超时,30秒连接超时
|
||||
|
||||
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.get(stream_url, headers=self.headers) as response:
|
||||
if response.status != 200:
|
||||
logger.error(f"❌ 下载失败,HTTP状态码: {response.status}")
|
||||
return None
|
||||
|
||||
|
||||
# 检查内容长度
|
||||
content_length = response.headers.get('content-length')
|
||||
content_length = response.headers.get("content-length")
|
||||
if content_length:
|
||||
size_mb = int(content_length) / 1024 / 1024
|
||||
if size_mb > max_size_mb:
|
||||
logger.error(f"❌ 视频文件过大: {size_mb:.1f}MB > {max_size_mb}MB")
|
||||
return None
|
||||
logger.info(f"📊 预计下载大小: {size_mb:.1f}MB")
|
||||
|
||||
|
||||
# 分块下载并监控大小
|
||||
video_bytes = bytearray()
|
||||
downloaded_mb = 0
|
||||
|
||||
|
||||
async for chunk in response.content.iter_chunked(8192): # 8KB块
|
||||
video_bytes.extend(chunk)
|
||||
downloaded_mb = len(video_bytes) / 1024 / 1024
|
||||
|
||||
|
||||
# 检查大小限制
|
||||
if downloaded_mb > max_size_mb:
|
||||
logger.error(f"❌ 下载中止,文件过大: {downloaded_mb:.1f}MB > {max_size_mb}MB")
|
||||
return None
|
||||
|
||||
|
||||
final_size_mb = len(video_bytes) / 1024 / 1024
|
||||
logger.info(f"✅ 视频下载完成,实际大小: {final_size_mb:.2f}MB")
|
||||
return bytes(video_bytes)
|
||||
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
logger.error("❌ 下载超时")
|
||||
return None
|
||||
@@ -242,93 +243,84 @@ class BilibiliVideoAnalyzer:
|
||||
logger.error(f"❌ 下载视频时发生未知错误: {e}")
|
||||
logger.exception("详细错误信息:")
|
||||
return None
|
||||
|
||||
|
||||
async def analyze_bilibili_video(self, url: str, prompt: str = None) -> Dict[str, Any]:
|
||||
"""分析哔哩哔哩视频并返回详细信息和AI分析结果"""
|
||||
try:
|
||||
logger.info(f"🎬 开始分析哔哩哔哩视频: {url}")
|
||||
|
||||
|
||||
# 1. 获取视频基本信息
|
||||
video_info = await self.get_video_info(url)
|
||||
if not video_info:
|
||||
logger.error("❌ 无法获取视频基本信息")
|
||||
return {"error": "无法获取视频信息"}
|
||||
|
||||
|
||||
logger.info(f"📺 视频标题: {video_info['title']}")
|
||||
logger.info(f"👤 UP主: {video_info['owner']}")
|
||||
logger.info(f"⏱️ 时长: {video_info['duration']}秒")
|
||||
|
||||
|
||||
# 2. 获取视频流URL
|
||||
stream_url = await self.get_video_stream_url(video_info['aid'], video_info['cid'])
|
||||
stream_url = await self.get_video_stream_url(video_info["aid"], video_info["cid"])
|
||||
if not stream_url:
|
||||
logger.warning("⚠️ 无法获取视频流,仅返回基本信息")
|
||||
return {
|
||||
"video_info": video_info,
|
||||
"error": "无法获取视频流,仅返回基本信息"
|
||||
}
|
||||
|
||||
return {"video_info": video_info, "error": "无法获取视频流,仅返回基本信息"}
|
||||
|
||||
# 3. 下载视频
|
||||
video_bytes = await self.download_video_bytes(stream_url)
|
||||
if not video_bytes:
|
||||
logger.warning("⚠️ 视频下载失败,仅返回基本信息")
|
||||
return {
|
||||
"video_info": video_info,
|
||||
"error": "视频下载失败,仅返回基本信息"
|
||||
}
|
||||
|
||||
return {"video_info": video_info, "error": "视频下载失败,仅返回基本信息"}
|
||||
|
||||
# 4. 构建增强的元数据信息
|
||||
enhanced_metadata = {
|
||||
"title": video_info['title'],
|
||||
"uploader": video_info['owner'],
|
||||
"duration": video_info['duration'],
|
||||
"view_count": video_info['view'],
|
||||
"like_count": video_info['like'],
|
||||
"description": video_info['desc'],
|
||||
"bvid": video_info['bvid'],
|
||||
"aid": video_info['aid'],
|
||||
"title": video_info["title"],
|
||||
"uploader": video_info["owner"],
|
||||
"duration": video_info["duration"],
|
||||
"view_count": video_info["view"],
|
||||
"like_count": video_info["like"],
|
||||
"description": video_info["desc"],
|
||||
"bvid": video_info["bvid"],
|
||||
"aid": video_info["aid"],
|
||||
"file_size": len(video_bytes),
|
||||
"source": "bilibili"
|
||||
"source": "bilibili",
|
||||
}
|
||||
|
||||
|
||||
# 5. 使用新的视频分析API,传递完整的元数据
|
||||
logger.info("🤖 开始AI视频分析...")
|
||||
analysis_result = await self.video_analyzer.analyze_video_from_bytes(
|
||||
video_bytes=video_bytes,
|
||||
filename=f"{video_info['title']}.mp4",
|
||||
prompt=prompt # 使用新API的prompt参数而不是user_question
|
||||
prompt=prompt, # 使用新API的prompt参数而不是user_question
|
||||
)
|
||||
|
||||
|
||||
# 6. 检查分析结果
|
||||
if not analysis_result or not analysis_result.get('summary'):
|
||||
if not analysis_result or not analysis_result.get("summary"):
|
||||
logger.error("❌ 视频分析失败或返回空结果")
|
||||
return {
|
||||
"video_info": video_info,
|
||||
"error": "视频分析失败,仅返回基本信息"
|
||||
}
|
||||
|
||||
return {"video_info": video_info, "error": "视频分析失败,仅返回基本信息"}
|
||||
|
||||
# 7. 格式化返回结果
|
||||
duration_str = f"{video_info['duration'] // 60}分{video_info['duration'] % 60}秒"
|
||||
|
||||
|
||||
result = {
|
||||
"video_info": {
|
||||
"标题": video_info['title'],
|
||||
"UP主": video_info['owner'],
|
||||
"标题": video_info["title"],
|
||||
"UP主": video_info["owner"],
|
||||
"时长": duration_str,
|
||||
"播放量": f"{video_info['view']:,}",
|
||||
"点赞": f"{video_info['like']:,}",
|
||||
"投币": f"{video_info['coin']:,}",
|
||||
"收藏": f"{video_info['favorite']:,}",
|
||||
"转发": f"{video_info['share']:,}",
|
||||
"简介": video_info['desc'][:200] + "..." if len(video_info['desc']) > 200 else video_info['desc']
|
||||
"简介": video_info["desc"][:200] + "..." if len(video_info["desc"]) > 200 else video_info["desc"],
|
||||
},
|
||||
"ai_analysis": analysis_result.get('summary', ''),
|
||||
"ai_analysis": analysis_result.get("summary", ""),
|
||||
"success": True,
|
||||
"metadata": enhanced_metadata # 添加元数据信息
|
||||
"metadata": enhanced_metadata, # 添加元数据信息
|
||||
}
|
||||
|
||||
|
||||
logger.info("✅ 哔哩哔哩视频分析完成")
|
||||
return result
|
||||
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"分析哔哩哔哩视频时发生异常: {str(e)}"
|
||||
logger.error(f"❌ {error_msg}")
|
||||
@@ -339,9 +331,10 @@ class BilibiliVideoAnalyzer:
|
||||
# 全局实例
|
||||
_bilibili_analyzer = None
|
||||
|
||||
|
||||
def get_bilibili_analyzer() -> BilibiliVideoAnalyzer:
|
||||
"""获取哔哩哔哩视频分析器实例(单例模式)"""
|
||||
global _bilibili_analyzer
|
||||
if _bilibili_analyzer is None:
|
||||
_bilibili_analyzer = BilibiliVideoAnalyzer()
|
||||
return _bilibili_analyzer
|
||||
return _bilibili_analyzer
|
||||
|
||||
@@ -15,75 +15,78 @@ logger = get_logger("bilibili_tool")
|
||||
|
||||
class BilibiliTool(BaseTool):
|
||||
"""哔哩哔哩视频观看体验工具 - 像真实用户一样观看和评价用户分享的哔哩哔哩视频"""
|
||||
|
||||
|
||||
name = "bilibili_video_watcher"
|
||||
description = "观看用户分享的哔哩哔哩视频,以真实用户视角给出观看感受和评价"
|
||||
available_for_llm = True
|
||||
|
||||
|
||||
parameters = [
|
||||
("url", ToolParamType.STRING, "用户分享给我的哔哩哔哩视频链接,我会认真观看这个视频并给出真实的观看感受", True, None),
|
||||
("interest_focus", ToolParamType.STRING, "你特别感兴趣的方面(如:搞笑内容、学习资料、美食、游戏、音乐等),我会重点关注这些内容", False, None)
|
||||
(
|
||||
"url",
|
||||
ToolParamType.STRING,
|
||||
"用户分享给我的哔哩哔哩视频链接,我会认真观看这个视频并给出真实的观看感受",
|
||||
True,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"interest_focus",
|
||||
ToolParamType.STRING,
|
||||
"你特别感兴趣的方面(如:搞笑内容、学习资料、美食、游戏、音乐等),我会重点关注这些内容",
|
||||
False,
|
||||
None,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, plugin_config: dict = None):
|
||||
super().__init__(plugin_config)
|
||||
self.analyzer = get_bilibili_analyzer()
|
||||
|
||||
|
||||
async def execute(self, function_args: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""执行哔哩哔哩视频观看体验"""
|
||||
try:
|
||||
url = function_args.get("url", "").strip()
|
||||
interest_focus = function_args.get("interest_focus", "").strip() or None
|
||||
|
||||
|
||||
if not url:
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": "🤔 你想让我看哪个视频呢?给我个链接吧!"
|
||||
}
|
||||
|
||||
return {"name": self.name, "content": "🤔 你想让我看哪个视频呢?给我个链接吧!"}
|
||||
|
||||
logger.info(f"开始'观看'哔哩哔哩视频: {url}")
|
||||
|
||||
|
||||
# 验证是否为哔哩哔哩链接
|
||||
extracted_url = self.analyzer.extract_bilibili_url(url)
|
||||
if not extracted_url:
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": "🤨 这好像不是哔哩哔哩的链接诶,我只会看哔哩哔哩的视频哦~ 给我一个bilibili.com或b23.tv的链接吧!"
|
||||
"content": "🤨 这好像不是哔哩哔哩的链接诶,我只会看哔哩哔哩的视频哦~ 给我一个bilibili.com或b23.tv的链接吧!",
|
||||
}
|
||||
|
||||
|
||||
# 构建个性化的观看提示词
|
||||
watch_prompt = self._build_watch_prompt(interest_focus)
|
||||
|
||||
|
||||
# 执行视频分析
|
||||
result = await self.analyzer.analyze_bilibili_video(extracted_url, watch_prompt)
|
||||
|
||||
|
||||
if result.get("error"):
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": f"😔 唉,这个视频我看不了... {result['error']}\n可能是网络问题或者视频有限制"
|
||||
"content": f"😔 唉,这个视频我看不了... {result['error']}\n可能是网络问题或者视频有限制",
|
||||
}
|
||||
|
||||
|
||||
# 格式化输出结果
|
||||
video_info = result.get("video_info", {})
|
||||
ai_analysis = result.get("ai_analysis", "")
|
||||
|
||||
|
||||
# 构建个性化的观看体验报告
|
||||
content = self._format_watch_experience(video_info, ai_analysis, interest_focus)
|
||||
|
||||
|
||||
logger.info("✅ 哔哩哔哩视频观看体验完成")
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": content.strip()
|
||||
}
|
||||
|
||||
return {"name": self.name, "content": content.strip()}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"😅 看视频的时候出了点问题: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
return {
|
||||
"name": self.name,
|
||||
"content": error_msg
|
||||
}
|
||||
|
||||
return {"name": self.name, "content": error_msg}
|
||||
|
||||
def _build_watch_prompt(self, interest_focus: str = None) -> str:
|
||||
"""构建个性化的观看提示词"""
|
||||
base_prompt = """请以一个真实哔哩哔哩用户的视角来观看用户分享给我的这个视频。用户特意分享了这个视频给我,我需要认真观看并给出真实的反馈。
|
||||
@@ -95,17 +98,17 @@ class BilibiliTool(BaseTool):
|
||||
4. 用轻松、自然的语气表达,就像在和分享视频的朋友聊天
|
||||
5. 可以表达个人偏好,比如"我比较喜欢..."、"这种类型不太符合我的口味"等
|
||||
7. 对用户的分享表示感谢,体现出这是用户主动分享给我的内容"""
|
||||
|
||||
|
||||
if interest_focus:
|
||||
base_prompt += f"\n\n特别关注点:我对 {interest_focus} 相关的内容比较感兴趣,请重点评价这方面的内容。"
|
||||
|
||||
|
||||
return base_prompt
|
||||
|
||||
|
||||
def _format_watch_experience(self, video_info: Dict, ai_analysis: str, interest_focus: str = None) -> str:
|
||||
"""格式化观看体验报告"""
|
||||
|
||||
|
||||
# 根据播放量生成热度评价
|
||||
view_count = video_info.get('播放量', '0').replace(',', '')
|
||||
view_count = video_info.get("播放量", "0").replace(",", "")
|
||||
if view_count.isdigit():
|
||||
views = int(view_count)
|
||||
if views > 1000000:
|
||||
@@ -118,40 +121,42 @@ class BilibiliTool(BaseTool):
|
||||
popularity = "🆕 比较新"
|
||||
else:
|
||||
popularity = "🤷♀️ 数据不明"
|
||||
|
||||
|
||||
# 生成时长评价
|
||||
duration = video_info.get('时长', '')
|
||||
if '分' in duration:
|
||||
duration = video_info.get("时长", "")
|
||||
if "分" in duration:
|
||||
time_comment = self._get_duration_comment(duration)
|
||||
else:
|
||||
time_comment = ""
|
||||
|
||||
|
||||
content = f"""🎬 **谢谢你分享的这个哔哩哔哩视频!我认真看了一下~**
|
||||
|
||||
📺 **视频速览**
|
||||
• 标题:{video_info.get('标题', '未知')}
|
||||
• UP主:{video_info.get('UP主', '未知')}
|
||||
• 标题:{video_info.get("标题", "未知")}
|
||||
• UP主:{video_info.get("UP主", "未知")}
|
||||
• 时长:{duration} {time_comment}
|
||||
• 热度:{popularity} ({video_info.get('播放量', '0')}播放)
|
||||
• 互动:👍{video_info.get('点赞', '0')} 🪙{video_info.get('投币', '0')} ⭐{video_info.get('收藏', '0')}
|
||||
• 热度:{popularity} ({video_info.get("播放量", "0")}播放)
|
||||
• 互动:👍{video_info.get("点赞", "0")} 🪙{video_info.get("投币", "0")} ⭐{video_info.get("收藏", "0")}
|
||||
|
||||
📝 **UP主说了什么**
|
||||
{video_info.get('简介', '这个UP主很懒,什么都没写...')[:150]}{'...' if len(video_info.get('简介', '')) > 150 else ''}
|
||||
{video_info.get("简介", "这个UP主很懒,什么都没写...")[:150]}{"..." if len(video_info.get("简介", "")) > 150 else ""}
|
||||
|
||||
🤔 **我的观看感受**
|
||||
{ai_analysis}
|
||||
"""
|
||||
|
||||
|
||||
if interest_focus:
|
||||
content += f"\n💭 **关于你感兴趣的'{interest_focus}'**\n我特别注意了这方面的内容,感觉{self._get_focus_comment()}~"
|
||||
|
||||
content += (
|
||||
f"\n💭 **关于你感兴趣的'{interest_focus}'**\n我特别注意了这方面的内容,感觉{self._get_focus_comment()}~"
|
||||
)
|
||||
|
||||
return content
|
||||
|
||||
|
||||
def _get_duration_comment(self, duration: str) -> str:
|
||||
"""根据时长生成评价"""
|
||||
if '分' in duration:
|
||||
if "分" in duration:
|
||||
try:
|
||||
minutes = int(duration.split('分')[0])
|
||||
minutes = int(duration.split("分")[0])
|
||||
if minutes < 3:
|
||||
return "(短小精悍)"
|
||||
elif minutes < 10:
|
||||
@@ -163,17 +168,18 @@ class BilibiliTool(BaseTool):
|
||||
except:
|
||||
return ""
|
||||
return ""
|
||||
|
||||
|
||||
def _get_focus_comment(self) -> str:
|
||||
"""生成关注点评价"""
|
||||
import random
|
||||
|
||||
comments = [
|
||||
"挺符合你的兴趣的",
|
||||
"内容还算不错",
|
||||
"可能会让你感兴趣",
|
||||
"值得一看",
|
||||
"可能不太符合你的口味",
|
||||
"内容比较一般"
|
||||
"内容比较一般",
|
||||
]
|
||||
return random.choice(comments)
|
||||
|
||||
@@ -190,11 +196,7 @@ class BilibiliPlugin(BasePlugin):
|
||||
config_file_name: str = "config.toml"
|
||||
|
||||
# 配置节描述
|
||||
config_section_descriptions = {
|
||||
"plugin": "插件基本信息",
|
||||
"bilibili": "哔哩哔哩视频观看配置",
|
||||
"tool": "工具配置"
|
||||
}
|
||||
config_section_descriptions = {"plugin": "插件基本信息", "bilibili": "哔哩哔哩视频观看配置", "tool": "工具配置"}
|
||||
|
||||
# 配置Schema定义
|
||||
config_schema: dict = {
|
||||
@@ -212,12 +214,12 @@ class BilibiliPlugin(BasePlugin):
|
||||
"tool": {
|
||||
"available_for_llm": ConfigField(type=bool, default=True, description="是否对LLM可用"),
|
||||
"name": ConfigField(type=str, default="bilibili_video_watcher", description="工具名称"),
|
||||
"description": ConfigField(type=str, default="观看用户分享的哔哩哔哩视频并给出真实观看体验", description="工具描述"),
|
||||
}
|
||||
"description": ConfigField(
|
||||
type=str, default="观看用户分享的哔哩哔哩视频并给出真实观看体验", description="工具描述"
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""返回插件包含的工具组件"""
|
||||
return [
|
||||
(BilibiliTool.get_tool_info(), BilibiliTool)
|
||||
]
|
||||
return [(BilibiliTool.get_tool_info(), BilibiliTool)]
|
||||
|
||||
@@ -19,7 +19,7 @@ from src.plugin_system.base.component_types import PythonDependency
|
||||
|
||||
class EchoCommand(PlusCommand):
|
||||
"""Echo命令示例"""
|
||||
|
||||
|
||||
command_name = "echo"
|
||||
command_description = "回显命令"
|
||||
command_aliases = ["say", "repeat"]
|
||||
@@ -32,23 +32,23 @@ class EchoCommand(PlusCommand):
|
||||
if args.is_empty():
|
||||
await self.send_text("❓ 请提供要回显的内容\n用法: /echo <内容>")
|
||||
return True, "参数不足", True
|
||||
|
||||
|
||||
content = args.get_raw()
|
||||
|
||||
|
||||
# 检查内容长度限制
|
||||
max_length = self.get_config("commands.max_content_length", 500)
|
||||
if len(content) > max_length:
|
||||
await self.send_text(f"❌ 内容过长,最大允许 {max_length} 字符")
|
||||
return True, "内容过长", True
|
||||
|
||||
|
||||
await self.send_text(f"🔊 {content}")
|
||||
|
||||
|
||||
return True, "Echo命令执行成功", True
|
||||
|
||||
|
||||
class HelloCommand(PlusCommand):
|
||||
"""Hello命令示例"""
|
||||
|
||||
|
||||
command_name = "hello"
|
||||
command_description = "问候命令"
|
||||
command_aliases = ["hi", "greet"]
|
||||
@@ -63,13 +63,13 @@ class HelloCommand(PlusCommand):
|
||||
else:
|
||||
name = args.get_first()
|
||||
await self.send_text(f"👋 Hello, {name}! 很高兴见到你!")
|
||||
|
||||
|
||||
return True, "Hello命令执行成功", True
|
||||
|
||||
|
||||
class InfoCommand(PlusCommand):
|
||||
"""信息命令示例"""
|
||||
|
||||
|
||||
command_name = "info"
|
||||
command_description = "显示插件信息"
|
||||
command_aliases = ["about"]
|
||||
@@ -91,13 +91,13 @@ class InfoCommand(PlusCommand):
|
||||
"• /test <子命令> [参数] - 测试各种功能"
|
||||
)
|
||||
await self.send_text(info_text)
|
||||
|
||||
|
||||
return True, "Info命令执行成功", True
|
||||
|
||||
|
||||
class TestCommand(PlusCommand):
|
||||
"""测试命令示例,展示参数解析功能"""
|
||||
|
||||
|
||||
command_name = "test"
|
||||
command_description = "测试命令,展示参数解析功能"
|
||||
command_aliases = ["t"]
|
||||
@@ -119,9 +119,9 @@ class TestCommand(PlusCommand):
|
||||
)
|
||||
await self.send_text(help_text)
|
||||
return True, "显示帮助", True
|
||||
|
||||
|
||||
subcommand = args.get_first().lower()
|
||||
|
||||
|
||||
if subcommand == "args":
|
||||
result = (
|
||||
f"🔍 参数解析结果:\n"
|
||||
@@ -132,7 +132,7 @@ class TestCommand(PlusCommand):
|
||||
f"剩余参数: '{args.get_remaining()}'"
|
||||
)
|
||||
await self.send_text(result)
|
||||
|
||||
|
||||
elif subcommand == "flags":
|
||||
result = (
|
||||
f"🏴 标志测试结果:\n"
|
||||
@@ -142,34 +142,34 @@ class TestCommand(PlusCommand):
|
||||
f"--name 的值: '{args.get_flag_value('--name', '未设置')}'"
|
||||
)
|
||||
await self.send_text(result)
|
||||
|
||||
|
||||
elif subcommand == "count":
|
||||
count = args.count() - 1 # 减去子命令本身
|
||||
await self.send_text(f"📊 除子命令外的参数数量: {count}")
|
||||
|
||||
|
||||
elif subcommand == "join":
|
||||
remaining = args.get_remaining()
|
||||
if remaining:
|
||||
await self.send_text(f"🔗 连接结果: {remaining}")
|
||||
else:
|
||||
await self.send_text("❌ 没有可连接的参数")
|
||||
|
||||
|
||||
else:
|
||||
await self.send_text(f"❓ 未知的子命令: {subcommand}")
|
||||
|
||||
|
||||
return True, "Test命令执行成功", True
|
||||
|
||||
|
||||
@register_plugin
|
||||
class EchoExamplePlugin(BasePlugin):
|
||||
"""Echo 示例插件"""
|
||||
|
||||
|
||||
plugin_name: str = "echo_example_plugin"
|
||||
enable_plugin: bool = True
|
||||
dependencies: List[str] = []
|
||||
python_dependencies: List[Union[str, "PythonDependency"]] = []
|
||||
config_file_name: str = "config.toml"
|
||||
|
||||
|
||||
config_schema = {
|
||||
"plugin": {
|
||||
"enabled": ConfigField(bool, default=True, description="是否启用插件"),
|
||||
@@ -181,7 +181,7 @@ class EchoExamplePlugin(BasePlugin):
|
||||
"max_content_length": ConfigField(int, default=500, description="最大回显内容长度"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
config_section_descriptions = {
|
||||
"plugin": "插件基本配置",
|
||||
"commands": "命令相关配置",
|
||||
@@ -190,14 +190,14 @@ class EchoExamplePlugin(BasePlugin):
|
||||
def get_plugin_components(self) -> List[Tuple[PlusCommandInfo, Type]]:
|
||||
"""获取插件组件"""
|
||||
components = []
|
||||
|
||||
|
||||
if self.get_config("plugin.enabled", True):
|
||||
# 添加所有命令,直接使用PlusCommand类
|
||||
if self.get_config("commands.echo_enabled", True):
|
||||
components.append((EchoCommand.get_plus_command_info(), EchoCommand))
|
||||
|
||||
|
||||
components.append((HelloCommand.get_plus_command_info(), HelloCommand))
|
||||
components.append((InfoCommand.get_plus_command_info(), InfoCommand))
|
||||
components.append((TestCommand.get_plus_command_info(), TestCommand))
|
||||
|
||||
|
||||
return components
|
||||
|
||||
@@ -83,34 +83,20 @@ class HelloWorldPlugin(BasePlugin):
|
||||
python_dependencies = []
|
||||
config_file_name = "config.toml"
|
||||
enable_plugin = False
|
||||
|
||||
|
||||
config_schema = {
|
||||
"meta": {
|
||||
"config_version": ConfigField(
|
||||
type=int,
|
||||
default=1,
|
||||
description="配置文件版本,请勿手动修改。"
|
||||
),
|
||||
"config_version": ConfigField(type=int, default=1, description="配置文件版本,请勿手动修改。"),
|
||||
},
|
||||
"greeting": {
|
||||
"message": ConfigField(
|
||||
type=str,
|
||||
default="这是来自配置文件的问候!👋",
|
||||
description="HelloCommand 使用的问候语。"
|
||||
type=str, default="这是来自配置文件的问候!👋", description="HelloCommand 使用的问候语。"
|
||||
),
|
||||
},
|
||||
"components": {
|
||||
"hello_command_enabled": ConfigField(
|
||||
type=bool,
|
||||
default=True,
|
||||
description="是否启用 /hello 命令。"
|
||||
),
|
||||
"random_emoji_action_enabled": ConfigField(
|
||||
type=bool,
|
||||
default=True,
|
||||
description="是否启用随机表情动作。"
|
||||
),
|
||||
}
|
||||
"hello_command_enabled": ConfigField(type=bool, default=True, description="是否启用 /hello 命令。"),
|
||||
"random_emoji_action_enabled": ConfigField(type=bool, default=True, description="是否启用随机表情动作。"),
|
||||
},
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
@@ -122,8 +108,8 @@ class HelloWorldPlugin(BasePlugin):
|
||||
|
||||
if self.get_config("components.hello_command_enabled", True):
|
||||
components.append((HelloCommand.get_command_info(), HelloCommand))
|
||||
|
||||
|
||||
if self.get_config("components.random_emoji_action_enabled", True):
|
||||
components.append((RandomEmojiAction.get_action_info(), RandomEmojiAction))
|
||||
|
||||
return components
|
||||
return components
|
||||
|
||||
@@ -1 +1 @@
|
||||
PLUGIN_NAME = "napcat_adapter"
|
||||
PLUGIN_NAME = "napcat_adapter"
|
||||
|
||||
@@ -5,6 +5,7 @@ from .src.send_handler import send_handler
|
||||
from .event_types import NapcatEvent
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
|
||||
@@ -15,36 +16,32 @@ class SetProfileHandler(BaseEventHandler):
|
||||
intercept_message: bool = False
|
||||
init_subscribe = [NapcatEvent.ACCOUNT.SET_PROFILE]
|
||||
|
||||
async def execute(self,params:dict):
|
||||
raw = params.get("raw",{})
|
||||
nickname = params.get("nickname","")
|
||||
personal_note = params.get("personal_note","")
|
||||
sex = params.get("sex","")
|
||||
async def execute(self, params: dict):
|
||||
raw = params.get("raw", {})
|
||||
nickname = params.get("nickname", "")
|
||||
personal_note = params.get("personal_note", "")
|
||||
sex = params.get("sex", "")
|
||||
|
||||
if params.get("raw", ""):
|
||||
nickname = raw.get("nickname", "")
|
||||
personal_note = raw.get("personal_note", "")
|
||||
sex = raw.get("sex", "")
|
||||
|
||||
if params.get("raw",""):
|
||||
nickname = raw.get("nickname","")
|
||||
personal_note = raw.get("personal_note","")
|
||||
sex = raw.get("sex","")
|
||||
|
||||
if not nickname:
|
||||
logger.error("事件 napcat_set_qq_profile 缺少必要参数: nickname ")
|
||||
return HandlerResult(False,False,{"status":"error"})
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"nickname": nickname,
|
||||
"personal_note": personal_note,
|
||||
"sex": sex
|
||||
}
|
||||
response = await send_handler.send_message_to_napcat(action="set_qq_profile",params=payload)
|
||||
if response.get("status","") == "ok":
|
||||
if response.get("data","").get("result","") == 0:
|
||||
return HandlerResult(True,True,response)
|
||||
payload = {"nickname": nickname, "personal_note": personal_note, "sex": sex}
|
||||
response = await send_handler.send_message_to_napcat(action="set_qq_profile", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
if response.get("data", "").get("result", "") == 0:
|
||||
return HandlerResult(True, True, response)
|
||||
else:
|
||||
logger.error(f"事件 napcat_set_qq_profile 请求失败!err={response.get("data","").get("errMsg","")}")
|
||||
return HandlerResult(False,False,response)
|
||||
logger.error(f"事件 napcat_set_qq_profile 请求失败!err={response.get('data', '').get('errMsg', '')}")
|
||||
return HandlerResult(False, False, response)
|
||||
else:
|
||||
logger.error("事件 napcat_set_qq_profile 请求失败!")
|
||||
return HandlerResult(False,False,{"status":"error"})
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetOnlineClientsHandler(BaseEventHandler):
|
||||
@@ -61,9 +58,7 @@ class GetOnlineClientsHandler(BaseEventHandler):
|
||||
if params.get("raw", ""):
|
||||
no_cache = raw.get("no_cache", False)
|
||||
|
||||
payload = {
|
||||
"no_cache": no_cache
|
||||
}
|
||||
payload = {"no_cache": no_cache}
|
||||
response = await send_handler.send_message_to_napcat(action="get_online_clients", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -94,11 +89,7 @@ class SetOnlineStatusHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_online_status 缺少必要参数: status")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"status": status,
|
||||
"ext_status": ext_status,
|
||||
"battery_status": battery_status
|
||||
}
|
||||
payload = {"status": status, "ext_status": ext_status, "battery_status": battery_status}
|
||||
response = await send_handler.send_message_to_napcat(action="set_online_status", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -142,9 +133,7 @@ class SetAvatarHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_qq_avatar 缺少必要参数: file")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"file": file
|
||||
}
|
||||
payload = {"file": file}
|
||||
response = await send_handler.send_message_to_napcat(action="set_qq_avatar", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -173,10 +162,7 @@ class SendLikeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_like 缺少必要参数: user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"user_id": str(user_id),
|
||||
"times": times
|
||||
}
|
||||
payload = {"user_id": str(user_id), "times": times}
|
||||
response = await send_handler.send_message_to_napcat(action="send_like", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -207,11 +193,7 @@ class SetFriendAddRequestHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_friend_add_request 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"flag": flag,
|
||||
"approve": approve,
|
||||
"remark": remark
|
||||
}
|
||||
payload = {"flag": flag, "approve": approve, "remark": remark}
|
||||
response = await send_handler.send_message_to_napcat(action="set_friend_add_request", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -238,15 +220,15 @@ class SetSelfLongnickHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_self_longnick 缺少必要参数: longNick")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"longNick": longNick
|
||||
}
|
||||
payload = {"longNick": longNick}
|
||||
response = await send_handler.send_message_to_napcat(action="set_self_longnick", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
if response.get("data", {}).get("result", "") == 0:
|
||||
return HandlerResult(True, True, response)
|
||||
else:
|
||||
logger.error(f"事件 napcat_set_self_longnick 请求失败!err={response.get('data', {}).get('errMsg', '')}")
|
||||
logger.error(
|
||||
f"事件 napcat_set_self_longnick 请求失败!err={response.get('data', {}).get('errMsg', '')}"
|
||||
)
|
||||
return HandlerResult(False, False, response)
|
||||
else:
|
||||
logger.error("事件 napcat_set_self_longnick 请求失败!")
|
||||
@@ -284,9 +266,7 @@ class GetRecentContactHandler(BaseEventHandler):
|
||||
if params.get("raw", ""):
|
||||
count = raw.get("count", 20)
|
||||
|
||||
payload = {
|
||||
"count": count
|
||||
}
|
||||
payload = {"count": count}
|
||||
response = await send_handler.send_message_to_napcat(action="get_recent_contact", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -313,9 +293,7 @@ class GetStrangerInfoHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_stranger_info 缺少必要参数: user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"user_id": str(user_id)
|
||||
}
|
||||
payload = {"user_id": str(user_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_stranger_info", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -338,9 +316,7 @@ class GetFriendListHandler(BaseEventHandler):
|
||||
if params.get("raw", ""):
|
||||
no_cache = raw.get("no_cache", False)
|
||||
|
||||
payload = {
|
||||
"no_cache": no_cache
|
||||
}
|
||||
payload = {"no_cache": no_cache}
|
||||
response = await send_handler.send_message_to_napcat(action="get_friend_list", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -367,10 +343,7 @@ class GetProfileLikeHandler(BaseEventHandler):
|
||||
start = raw.get("start", 0)
|
||||
count = raw.get("count", 10)
|
||||
|
||||
payload = {
|
||||
"start": start,
|
||||
"count": count
|
||||
}
|
||||
payload = {"start": start, "count": count}
|
||||
if user_id:
|
||||
payload["user_id"] = str(user_id)
|
||||
|
||||
@@ -404,11 +377,7 @@ class DeleteFriendHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_delete_friend 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"user_id": str(user_id),
|
||||
"temp_block": temp_block,
|
||||
"temp_both_del": temp_both_del
|
||||
}
|
||||
payload = {"user_id": str(user_id), "temp_block": temp_block, "temp_both_del": temp_both_del}
|
||||
response = await send_handler.send_message_to_napcat(action="delete_friend", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
if response.get("data", {}).get("result", "") == 0:
|
||||
@@ -439,9 +408,7 @@ class GetUserStatusHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_user_status 缺少必要参数: user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"user_id": str(user_id)
|
||||
}
|
||||
payload = {"user_id": str(user_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_user_status", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -504,7 +471,7 @@ class GetMiniAppArkHandler(BaseEventHandler):
|
||||
"picUrl": picUrl,
|
||||
"jumpUrl": jumpUrl,
|
||||
"webUrl": webUrl,
|
||||
"rawArkData": rawArkData
|
||||
"rawArkData": rawArkData,
|
||||
}
|
||||
response = await send_handler.send_message_to_napcat(action="get_mini_app_ark", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
@@ -536,11 +503,7 @@ class SetDiyOnlineStatusHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_diy_online_status 缺少必要参数: face_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"face_id": str(face_id),
|
||||
"face_type": str(face_type),
|
||||
"wording": wording
|
||||
}
|
||||
payload = {"face_id": str(face_id), "face_type": str(face_type), "wording": wording}
|
||||
response = await send_handler.send_message_to_napcat(action="set_diy_online_status", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -570,10 +533,7 @@ class SendPrivateMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_private_msg 缺少必要参数: user_id 或 message")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"user_id": str(user_id),
|
||||
"message": message
|
||||
}
|
||||
payload = {"user_id": str(user_id), "message": message}
|
||||
response = await send_handler.send_message_to_napcat(action="send_private_msg", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -602,9 +562,7 @@ class SendPokeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_poke 缺少必要参数: user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"user_id": str(user_id)
|
||||
}
|
||||
payload = {"user_id": str(user_id)}
|
||||
if group_id is not None:
|
||||
payload["group_id"] = str(group_id)
|
||||
|
||||
@@ -634,9 +592,7 @@ class DeleteMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_delete_msg 缺少必要参数: message_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"message_id": str(message_id)
|
||||
}
|
||||
payload = {"message_id": str(message_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="delete_msg", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -673,7 +629,7 @@ class GetGroupMsgHistoryHandler(BaseEventHandler):
|
||||
"group_id": str(group_id),
|
||||
"message_seq": int(message_seq),
|
||||
"count": int(count),
|
||||
"reverseOrder": bool(reverseOrder)
|
||||
"reverseOrder": bool(reverseOrder),
|
||||
}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_msg_history", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
@@ -701,9 +657,7 @@ class GetMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_msg 缺少必要参数: message_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"message_id": str(message_id)
|
||||
}
|
||||
payload = {"message_id": str(message_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_msg", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -730,9 +684,7 @@ class GetForwardMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_forward_msg 缺少必要参数: message_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"message_id": str(message_id)
|
||||
}
|
||||
payload = {"message_id": str(message_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_forward_msg", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -763,11 +715,7 @@ class SetMsgEmojiLikeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_msg_emoji_like 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"message_id": str(message_id),
|
||||
"emoji_id": int(emoji_id),
|
||||
"set": bool(set_flag)
|
||||
}
|
||||
payload = {"message_id": str(message_id), "emoji_id": int(emoji_id), "set": bool(set_flag)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_msg_emoji_like", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -804,7 +752,7 @@ class GetFriendMsgHistoryHandler(BaseEventHandler):
|
||||
"user_id": str(user_id),
|
||||
"message_seq": int(message_seq),
|
||||
"count": int(count),
|
||||
"reverseOrder": bool(reverseOrder)
|
||||
"reverseOrder": bool(reverseOrder),
|
||||
}
|
||||
response = await send_handler.send_message_to_napcat(action="get_friend_msg_history", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
@@ -842,7 +790,7 @@ class FetchEmojiLikeHandler(BaseEventHandler):
|
||||
"message_id": str(message_id),
|
||||
"emojiId": str(emoji_id),
|
||||
"emojiType": str(emoji_type),
|
||||
"count": int(count)
|
||||
"count": int(count),
|
||||
}
|
||||
response = await send_handler.send_message_to_napcat(action="fetch_emoji_like", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
@@ -882,13 +830,7 @@ class SendForwardMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_forward_msg 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"messages": messages,
|
||||
"news": news,
|
||||
"prompt": prompt,
|
||||
"summary": summary,
|
||||
"source": source
|
||||
}
|
||||
payload = {"messages": messages, "news": news, "prompt": prompt, "summary": summary, "source": source}
|
||||
if group_id is not None:
|
||||
payload["group_id"] = str(group_id)
|
||||
if user_id is not None:
|
||||
@@ -924,11 +866,7 @@ class SendGroupAiRecordHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_group_ai_record 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"character": character,
|
||||
"text": text
|
||||
}
|
||||
payload = {"group_id": str(group_id), "character": character, "text": text}
|
||||
response = await send_handler.send_message_to_napcat(action="send_group_ai_record", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -936,6 +874,7 @@ class SendGroupAiRecordHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_group_ai_record 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
# ===GROUP===
|
||||
class GetGroupInfoHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_info_handler"
|
||||
@@ -955,9 +894,7 @@ class GetGroupInfoHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_info 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_info", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -965,6 +902,7 @@ class GetGroupInfoHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_info 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupAddOptionHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_add_option_handler"
|
||||
handler_description: str = "设置群添加选项"
|
||||
@@ -989,10 +927,7 @@ class SetGroupAddOptionHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_add_option 缺少必要参数: group_id 或 add_type")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"add_type": str(add_type)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "add_type": str(add_type)}
|
||||
if group_question:
|
||||
payload["group_question"] = group_question
|
||||
if group_answer:
|
||||
@@ -1005,6 +940,7 @@ class SetGroupAddOptionHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_add_option 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupKickMembersHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_kick_members_handler"
|
||||
handler_description: str = "批量踢出群成员"
|
||||
@@ -1027,11 +963,7 @@ class SetGroupKickMembersHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_kick_members 缺少必要参数: group_id 或 user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"user_id": user_id,
|
||||
"reject_add_request": bool(reject_add_request)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "user_id": user_id, "reject_add_request": bool(reject_add_request)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_kick_members", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1039,6 +971,7 @@ class SetGroupKickMembersHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_kick_members 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupRemarkHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_remark_handler"
|
||||
handler_description: str = "设置群备注"
|
||||
@@ -1059,10 +992,7 @@ class SetGroupRemarkHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_remark 缺少必要参数: group_id 或 remark")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"remark": remark
|
||||
}
|
||||
payload = {"group_id": str(group_id), "remark": remark}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_remark", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1070,6 +1000,7 @@ class SetGroupRemarkHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_remark 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupKickHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_kick_handler"
|
||||
handler_description: str = "群踢人"
|
||||
@@ -1092,11 +1023,7 @@ class SetGroupKickHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_kick 缺少必要参数: group_id 或 user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"user_id": str(user_id),
|
||||
"reject_add_request": bool(reject_add_request)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "user_id": str(user_id), "reject_add_request": bool(reject_add_request)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_kick", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1104,6 +1031,7 @@ class SetGroupKickHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_kick 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupSystemMsgHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_system_msg_handler"
|
||||
handler_description: str = "获取群系统消息"
|
||||
@@ -1122,9 +1050,7 @@ class GetGroupSystemMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_system_msg 缺少必要参数: count")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"count": int(count)
|
||||
}
|
||||
payload = {"count": int(count)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_system_msg", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1132,6 +1058,7 @@ class GetGroupSystemMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_system_msg 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupBanHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_ban_handler"
|
||||
handler_description: str = "群禁言"
|
||||
@@ -1154,11 +1081,7 @@ class SetGroupBanHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_ban 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"user_id": str(user_id),
|
||||
"duration": int(duration)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "user_id": str(user_id), "duration": int(duration)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_ban", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1166,6 +1089,7 @@ class SetGroupBanHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_ban 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetEssenceMsgListHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_essence_msg_list_handler"
|
||||
handler_description: str = "获取群精华消息"
|
||||
@@ -1184,9 +1108,7 @@ class GetEssenceMsgListHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_essence_msg_list 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_essence_msg_list", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1194,6 +1116,7 @@ class GetEssenceMsgListHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_essence_msg_list 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupWholeBanHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_whole_ban_handler"
|
||||
handler_description: str = "全体禁言"
|
||||
@@ -1214,10 +1137,7 @@ class SetGroupWholeBanHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_whole_ban 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"enable": bool(enable)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "enable": bool(enable)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_whole_ban", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1225,6 +1145,7 @@ class SetGroupWholeBanHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_whole_ban 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupPortraitHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_portrait_handler"
|
||||
handler_description: str = "设置群头像"
|
||||
@@ -1245,10 +1166,7 @@ class SetGroupPortraitHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_portrait 缺少必要参数: group_id 或 file")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"file": file_path
|
||||
}
|
||||
payload = {"group_id": str(group_id), "file": file_path}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_portrait", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1256,6 +1174,7 @@ class SetGroupPortraitHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_portrait 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupAdminHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_admin_handler"
|
||||
handler_description: str = "设置群管理"
|
||||
@@ -1278,11 +1197,7 @@ class SetGroupAdminHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_admin 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"user_id": str(user_id),
|
||||
"enable": bool(enable)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "user_id": str(user_id), "enable": bool(enable)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_admin", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1290,6 +1205,7 @@ class SetGroupAdminHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_admin 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupCardHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_card_handler"
|
||||
handler_description: str = "设置群成员名片"
|
||||
@@ -1312,10 +1228,7 @@ class SetGroupCardHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_card 缺少必要参数: group_id 或 user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"user_id": str(user_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "user_id": str(user_id)}
|
||||
if card:
|
||||
payload["card"] = card
|
||||
|
||||
@@ -1326,6 +1239,7 @@ class SetGroupCardHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_card 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetEssenceMsgHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_essence_msg_handler"
|
||||
handler_description: str = "设置群精华消息"
|
||||
@@ -1344,9 +1258,7 @@ class SetEssenceMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_essence_msg 缺少必要参数: message_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"message_id": str(message_id)
|
||||
}
|
||||
payload = {"message_id": str(message_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_essence_msg", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1354,6 +1266,7 @@ class SetEssenceMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_essence_msg 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupNameHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_name_handler"
|
||||
handler_description: str = "设置群名"
|
||||
@@ -1374,10 +1287,7 @@ class SetGroupNameHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_name 缺少必要参数: group_id 或 group_name")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"group_name": group_name
|
||||
}
|
||||
payload = {"group_id": str(group_id), "group_name": group_name}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_name", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1385,6 +1295,7 @@ class SetGroupNameHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_name 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class DeleteEssenceMsgHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_delete_essence_msg_handler"
|
||||
handler_description: str = "删除群精华消息"
|
||||
@@ -1403,9 +1314,7 @@ class DeleteEssenceMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_delete_essence_msg 缺少必要参数: message_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"message_id": str(message_id)
|
||||
}
|
||||
payload = {"message_id": str(message_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="delete_essence_msg", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1413,6 +1322,7 @@ class DeleteEssenceMsgHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_delete_essence_msg 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupLeaveHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_leave_handler"
|
||||
handler_description: str = "退群"
|
||||
@@ -1431,9 +1341,7 @@ class SetGroupLeaveHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_leave 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_leave", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1441,6 +1349,7 @@ class SetGroupLeaveHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_leave 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SendGroupNoticeHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_send_group_notice_handler"
|
||||
handler_description: str = "发送群公告"
|
||||
@@ -1463,10 +1372,7 @@ class SendGroupNoticeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_group_notice 缺少必要参数: group_id 或 content")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"content": content
|
||||
}
|
||||
payload = {"group_id": str(group_id), "content": content}
|
||||
if image:
|
||||
payload["image"] = image
|
||||
|
||||
@@ -1477,6 +1383,7 @@ class SendGroupNoticeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_send_group_notice 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupSpecialTitleHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_special_title_handler"
|
||||
handler_description: str = "设置群头衔"
|
||||
@@ -1499,10 +1406,7 @@ class SetGroupSpecialTitleHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_special_title 缺少必要参数: group_id 或 user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"user_id": str(user_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "user_id": str(user_id)}
|
||||
if special_title:
|
||||
payload["special_title"] = special_title
|
||||
|
||||
@@ -1513,6 +1417,7 @@ class SetGroupSpecialTitleHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_special_title 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupNoticeHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_notice_handler"
|
||||
handler_description: str = "获取群公告"
|
||||
@@ -1531,9 +1436,7 @@ class GetGroupNoticeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_notice 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="_get_group_notice", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1541,6 +1444,7 @@ class GetGroupNoticeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_notice 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupAddRequestHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_add_request_handler"
|
||||
handler_description: str = "处理加群请求"
|
||||
@@ -1563,10 +1467,7 @@ class SetGroupAddRequestHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_add_request 缺少必要参数")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"flag": flag,
|
||||
"approve": bool(approve)
|
||||
}
|
||||
payload = {"flag": flag, "approve": bool(approve)}
|
||||
if reason:
|
||||
payload["reason"] = reason
|
||||
|
||||
@@ -1577,6 +1478,7 @@ class SetGroupAddRequestHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_add_request 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupListHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_list_handler"
|
||||
handler_description: str = "获取群列表"
|
||||
@@ -1591,9 +1493,7 @@ class GetGroupListHandler(BaseEventHandler):
|
||||
if params.get("raw", ""):
|
||||
no_cache = raw.get("no_cache", False)
|
||||
|
||||
payload = {
|
||||
"no_cache": bool(no_cache)
|
||||
}
|
||||
payload = {"no_cache": bool(no_cache)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_list", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1601,6 +1501,7 @@ class GetGroupListHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_list 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class DeleteGroupNoticeHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_del_group_notice_handler"
|
||||
handler_description: str = "删除群公告"
|
||||
@@ -1621,10 +1522,7 @@ class DeleteGroupNoticeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_del_group_notice 缺少必要参数: group_id 或 notice_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"notice_id": notice_id
|
||||
}
|
||||
payload = {"group_id": str(group_id), "notice_id": notice_id}
|
||||
response = await send_handler.send_message_to_napcat(action="_del_group_notice", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1632,6 +1530,7 @@ class DeleteGroupNoticeHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_del_group_notice 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupMemberInfoHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_member_info_handler"
|
||||
handler_description: str = "获取群成员信息"
|
||||
@@ -1654,11 +1553,7 @@ class GetGroupMemberInfoHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_member_info 缺少必要参数: group_id 或 user_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"user_id": str(user_id),
|
||||
"no_cache": bool(no_cache)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "user_id": str(user_id), "no_cache": bool(no_cache)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_member_info", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1666,6 +1561,7 @@ class GetGroupMemberInfoHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_member_info 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupMemberListHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_member_list_handler"
|
||||
handler_description: str = "获取群成员列表"
|
||||
@@ -1686,10 +1582,7 @@ class GetGroupMemberListHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_member_list 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id),
|
||||
"no_cache": bool(no_cache)
|
||||
}
|
||||
payload = {"group_id": str(group_id), "no_cache": bool(no_cache)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_member_list", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1697,6 +1590,7 @@ class GetGroupMemberListHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_member_list 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupHonorInfoHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_honor_info_handler"
|
||||
handler_description: str = "获取群荣誉"
|
||||
@@ -1717,9 +1611,7 @@ class GetGroupHonorInfoHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_honor_info 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
if type:
|
||||
payload["type"] = type
|
||||
|
||||
@@ -1730,6 +1622,7 @@ class GetGroupHonorInfoHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_honor_info 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupInfoExHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_info_ex_handler"
|
||||
handler_description: str = "获取群信息ex"
|
||||
@@ -1748,9 +1641,7 @@ class GetGroupInfoExHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_info_ex 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_info_ex", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1758,6 +1649,7 @@ class GetGroupInfoExHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_info_ex 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupAtAllRemainHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_at_all_remain_handler"
|
||||
handler_description: str = "获取群 @全体成员 剩余次数"
|
||||
@@ -1776,9 +1668,7 @@ class GetGroupAtAllRemainHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_at_all_remain 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_at_all_remain", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1786,6 +1676,7 @@ class GetGroupAtAllRemainHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_at_all_remain 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupShutListHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_shut_list_handler"
|
||||
handler_description: str = "获取群禁言列表"
|
||||
@@ -1804,9 +1695,7 @@ class GetGroupShutListHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_shut_list 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="get_group_shut_list", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
@@ -1814,6 +1703,7 @@ class GetGroupShutListHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_shut_list 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class GetGroupIgnoredNotifiesHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_get_group_ignored_notifies_handler"
|
||||
handler_description: str = "获取群过滤系统消息"
|
||||
@@ -1830,6 +1720,7 @@ class GetGroupIgnoredNotifiesHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_get_group_ignored_notifies 请求失败!")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
|
||||
class SetGroupSignHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_set_group_sign_handler"
|
||||
handler_description: str = "群打卡"
|
||||
@@ -1848,9 +1739,7 @@ class SetGroupSignHandler(BaseEventHandler):
|
||||
logger.error("事件 napcat_set_group_sign 缺少必要参数: group_id")
|
||||
return HandlerResult(False, False, {"status": "error"})
|
||||
|
||||
payload = {
|
||||
"group_id": str(group_id)
|
||||
}
|
||||
payload = {"group_id": str(group_id)}
|
||||
response = await send_handler.send_message_to_napcat(action="set_group_sign", params=payload)
|
||||
if response.get("status", "") == "ok":
|
||||
return HandlerResult(True, True, response)
|
||||
|
||||
@@ -1,44 +1,48 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class NapcatEvent:
|
||||
"""
|
||||
napcat插件事件枚举类
|
||||
"""
|
||||
class ON_RECEIVED(Enum):
|
||||
|
||||
class ON_RECEIVED(Enum):
|
||||
"""
|
||||
该分类下均为消息接受事件,只能由napcat_plugin触发
|
||||
"""
|
||||
TEXT = "napcat_on_received_text"
|
||||
'''接收到文本消息'''
|
||||
FACE = "napcat_on_received_face"
|
||||
'''接收到表情消息'''
|
||||
REPLY = "napcat_on_received_reply"
|
||||
'''接收到回复消息'''
|
||||
IMAGE = "napcat_on_received_image"
|
||||
'''接收到图像消息'''
|
||||
RECORD = "napcat_on_received_record"
|
||||
'''接收到语音消息'''
|
||||
VIDEO = "napcat_on_received_video"
|
||||
'''接收到视频消息'''
|
||||
AT = "napcat_on_received_at"
|
||||
'''接收到at消息'''
|
||||
DICE = "napcat_on_received_dice"
|
||||
'''接收到骰子消息'''
|
||||
SHAKE = "napcat_on_received_shake"
|
||||
'''接收到屏幕抖动消息'''
|
||||
JSON = "napcat_on_received_json"
|
||||
'''接收到JSON消息'''
|
||||
RPS = "napcat_on_received_rps"
|
||||
'''接收到魔法猜拳消息'''
|
||||
FRIEND_INPUT = "napcat_on_friend_input"
|
||||
'''好友正在输入'''
|
||||
|
||||
|
||||
TEXT = "napcat_on_received_text"
|
||||
"""接收到文本消息"""
|
||||
FACE = "napcat_on_received_face"
|
||||
"""接收到表情消息"""
|
||||
REPLY = "napcat_on_received_reply"
|
||||
"""接收到回复消息"""
|
||||
IMAGE = "napcat_on_received_image"
|
||||
"""接收到图像消息"""
|
||||
RECORD = "napcat_on_received_record"
|
||||
"""接收到语音消息"""
|
||||
VIDEO = "napcat_on_received_video"
|
||||
"""接收到视频消息"""
|
||||
AT = "napcat_on_received_at"
|
||||
"""接收到at消息"""
|
||||
DICE = "napcat_on_received_dice"
|
||||
"""接收到骰子消息"""
|
||||
SHAKE = "napcat_on_received_shake"
|
||||
"""接收到屏幕抖动消息"""
|
||||
JSON = "napcat_on_received_json"
|
||||
"""接收到JSON消息"""
|
||||
RPS = "napcat_on_received_rps"
|
||||
"""接收到魔法猜拳消息"""
|
||||
FRIEND_INPUT = "napcat_on_friend_input"
|
||||
"""好友正在输入"""
|
||||
|
||||
class ACCOUNT(Enum):
|
||||
"""
|
||||
该分类是对账户相关的操作,只能由外部触发,napcat_plugin负责处理
|
||||
"""
|
||||
SET_PROFILE = "napcat_set_qq_profile"
|
||||
'''设置账号信息
|
||||
|
||||
SET_PROFILE = "napcat_set_qq_profile"
|
||||
"""设置账号信息
|
||||
|
||||
Args:
|
||||
nickname (Optional[str]): 名称(必须)
|
||||
@@ -59,9 +63,9 @@ class NapcatEvent:
|
||||
"echo": "string"
|
||||
}
|
||||
|
||||
'''
|
||||
GET_ONLINE_CLIENTS = "napcat_get_online_clients"
|
||||
'''获取当前账号在线客户端列表
|
||||
"""
|
||||
GET_ONLINE_CLIENTS = "napcat_get_online_clients"
|
||||
"""获取当前账号在线客户端列表
|
||||
|
||||
Args:
|
||||
no_cache (Optional[bool]): 是否不使用缓存
|
||||
@@ -78,9 +82,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_ONLINE_STATUS = "napcat_set_online_status"
|
||||
'''设置在线状态
|
||||
"""
|
||||
SET_ONLINE_STATUS = "napcat_set_online_status"
|
||||
"""设置在线状态
|
||||
|
||||
Args:
|
||||
status (Optional[str]): 状态代码(必须)
|
||||
@@ -97,9 +101,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_FRIENDS_WITH_CATEGORY = "napcat_get_friends_with_category"
|
||||
'''获取好友分组列表
|
||||
"""
|
||||
GET_FRIENDS_WITH_CATEGORY = "napcat_get_friends_with_category"
|
||||
"""获取好友分组列表
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
@@ -134,9 +138,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_AVATAR = "napcat_set_qq_avatar"
|
||||
'''设置头像
|
||||
"""
|
||||
SET_AVATAR = "napcat_set_qq_avatar"
|
||||
"""设置头像
|
||||
|
||||
Args:
|
||||
file (Optional[str]): 文件路径或base64(必需)
|
||||
@@ -151,9 +155,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SEND_LIKE = "napcat_send_like"
|
||||
'''点赞
|
||||
"""
|
||||
SEND_LIKE = "napcat_send_like"
|
||||
"""点赞
|
||||
|
||||
Args:
|
||||
user_id (Optional[str|int]): 用户id(必需)
|
||||
@@ -169,9 +173,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_FRIEND_ADD_REQUEST = "napcat_set_friend_add_request"
|
||||
'''处理好友请求
|
||||
"""
|
||||
SET_FRIEND_ADD_REQUEST = "napcat_set_friend_add_request"
|
||||
"""处理好友请求
|
||||
|
||||
Args:
|
||||
flag (Optional[str]): 请求id(必需)
|
||||
@@ -188,9 +192,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_SELF_LONGNICK = "napcat_set_self_longnick"
|
||||
'''设置个性签名
|
||||
"""
|
||||
SET_SELF_LONGNICK = "napcat_set_self_longnick"
|
||||
"""设置个性签名
|
||||
|
||||
Args:
|
||||
longNick (Optional[str]): 内容(必需)
|
||||
@@ -208,9 +212,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_LOGIN_INFO = "napcat_get_login_info"
|
||||
'''获取登录号信息
|
||||
"""
|
||||
GET_LOGIN_INFO = "napcat_get_login_info"
|
||||
"""获取登录号信息
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
@@ -224,9 +228,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_RECENT_CONTACT = "napcat_get_recent_contact"
|
||||
'''最近消息列表
|
||||
"""
|
||||
GET_RECENT_CONTACT = "napcat_get_recent_contact"
|
||||
"""最近消息列表
|
||||
|
||||
Args:
|
||||
count (Optional[int]): 会话数量
|
||||
@@ -281,9 +285,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_STRANGER_INFO = "napcat_get_stranger_info"
|
||||
'''获取(指定)账号信息
|
||||
"""
|
||||
GET_STRANGER_INFO = "napcat_get_stranger_info"
|
||||
"""获取(指定)账号信息
|
||||
|
||||
Args:
|
||||
user_id (Optional[str|int]): 用户id(必需)
|
||||
@@ -315,9 +319,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_FRIEND_LIST = "napcat_get_friend_list"
|
||||
'''获取好友列表
|
||||
"""
|
||||
GET_FRIEND_LIST = "napcat_get_friend_list"
|
||||
"""获取好友列表
|
||||
|
||||
Args:
|
||||
no_cache (Optional[bool]): 是否不使用缓存
|
||||
@@ -347,9 +351,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_PROFILE_LIKE = "napcat_get_profile_like"
|
||||
'''获取点赞列表
|
||||
"""
|
||||
GET_PROFILE_LIKE = "napcat_get_profile_like"
|
||||
"""获取点赞列表
|
||||
|
||||
Args:
|
||||
user_id (Optional[str|int]): 用户id,指定用户,不填为获取所有
|
||||
@@ -420,9 +424,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
DELETE_FRIEND = "napcat_delete_friend"
|
||||
'''删除好友
|
||||
"""
|
||||
DELETE_FRIEND = "napcat_delete_friend"
|
||||
"""删除好友
|
||||
|
||||
Args:
|
||||
user_id (Optional[str|int]): 用户id(必需)
|
||||
@@ -442,9 +446,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_USER_STATUS = "napcat_get_user_status"
|
||||
'''获取(指定)用户状态
|
||||
"""
|
||||
GET_USER_STATUS = "napcat_get_user_status"
|
||||
"""获取(指定)用户状态
|
||||
|
||||
Args:
|
||||
user_id (Optional[str|int]): 用户id(必需)
|
||||
@@ -462,9 +466,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_STATUS = "napcat_get_status"
|
||||
'''获取状态
|
||||
"""
|
||||
GET_STATUS = "napcat_get_status"
|
||||
"""获取状态
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
@@ -479,9 +483,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_MINI_APP_ARK = "napcat_get_mini_app_ark"
|
||||
'''获取小程序卡片
|
||||
"""
|
||||
GET_MINI_APP_ARK = "napcat_get_mini_app_ark"
|
||||
"""获取小程序卡片
|
||||
|
||||
Args:
|
||||
type (Optional[str]): 类型(如bili、weibo,必需)
|
||||
@@ -539,9 +543,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_DIY_ONLINE_STATUS = "napcat_set_diy_online_status"
|
||||
'''设置自定义在线状态
|
||||
"""
|
||||
SET_DIY_ONLINE_STATUS = "napcat_set_diy_online_status"
|
||||
"""设置自定义在线状态
|
||||
|
||||
Args:
|
||||
face_id (Optional[str|int]): 表情ID(必需)
|
||||
@@ -558,14 +562,15 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
|
||||
"""
|
||||
|
||||
class MESSAGE(Enum):
|
||||
"""
|
||||
该分类是对信息相关的操作,只能由外部触发,napcat_plugin负责处理
|
||||
"""
|
||||
SEND_PRIVATE_MSG = "napcat_send_private_msg"
|
||||
'''发送私聊消息
|
||||
|
||||
SEND_PRIVATE_MSG = "napcat_send_private_msg"
|
||||
"""发送私聊消息
|
||||
|
||||
Args:
|
||||
user_id (Optional[str|int]): 用户id(必需)
|
||||
@@ -583,9 +588,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SEND_POKE = "napcat_send_poke"
|
||||
'''发送戳一戳
|
||||
"""
|
||||
SEND_POKE = "napcat_send_poke"
|
||||
"""发送戳一戳
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号
|
||||
@@ -601,9 +606,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
DELETE_MSG = "napcat_delete_msg"
|
||||
'''撤回消息
|
||||
"""
|
||||
DELETE_MSG = "napcat_delete_msg"
|
||||
"""撤回消息
|
||||
|
||||
Args:
|
||||
message_id (Optional[str|int]): 消息id(必需)
|
||||
@@ -618,9 +623,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_GROUP_MSG_HISTORY = "napcat_get_group_msg_history"
|
||||
'''获取群历史消息
|
||||
"""
|
||||
GET_GROUP_MSG_HISTORY = "napcat_get_group_msg_history"
|
||||
"""获取群历史消息
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -673,9 +678,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_MSG = "napcat_get_msg"
|
||||
'''获取消息详情
|
||||
"""
|
||||
GET_MSG = "napcat_get_msg"
|
||||
"""获取消息详情
|
||||
|
||||
Args:
|
||||
message_id (Optional[str|int]): 消息id(必需)
|
||||
@@ -721,9 +726,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_FORWARD_MSG = "napcat_get_forward_msg"
|
||||
'''获取合并转发消息
|
||||
"""
|
||||
GET_FORWARD_MSG = "napcat_get_forward_msg"
|
||||
"""获取合并转发消息
|
||||
|
||||
Args:
|
||||
message_id (Optional[str|int]): 消息id(必需)
|
||||
@@ -773,9 +778,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_MSG_EMOJI_LIKE = "napcat_set_msg_emoji_like"
|
||||
'''贴表情
|
||||
"""
|
||||
SET_MSG_EMOJI_LIKE = "napcat_set_msg_emoji_like"
|
||||
"""贴表情
|
||||
|
||||
Args:
|
||||
message_id (Optional[str|int]): 消息id(必需)
|
||||
@@ -795,9 +800,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_FRIEND_MSG_HISTORY = "napcat_get_friend_msg_history"
|
||||
'''获取好友历史消息
|
||||
"""
|
||||
GET_FRIEND_MSG_HISTORY = "napcat_get_friend_msg_history"
|
||||
"""获取好友历史消息
|
||||
|
||||
Args:
|
||||
user_id (Optional[str|int]): 用户id(必需)
|
||||
@@ -850,9 +855,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
FETCH_EMOJI_LIKE = "napcat_fetch_emoji_like"
|
||||
'''获取贴表情详情
|
||||
"""
|
||||
FETCH_EMOJI_LIKE = "napcat_fetch_emoji_like"
|
||||
"""获取贴表情详情
|
||||
|
||||
Args:
|
||||
message_id (Optional[str|int]): 消息id(必需)
|
||||
@@ -883,9 +888,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SEND_FORWARD_MSG = "napcat_send_forward_msg"
|
||||
'''发送合并转发消息
|
||||
"""
|
||||
SEND_FORWARD_MSG = "napcat_send_forward_msg"
|
||||
"""发送合并转发消息
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号
|
||||
@@ -906,9 +911,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SEND_GROUP_AI_RECORD = "napcat_send_group_ai_record"
|
||||
'''发送群AI语音
|
||||
"""
|
||||
SEND_GROUP_AI_RECORD = "napcat_send_group_ai_record"
|
||||
"""发送群AI语音
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -927,15 +932,15 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
|
||||
"""
|
||||
|
||||
class GROUP(Enum):
|
||||
"""
|
||||
该分类是对群聊相关的操作,只能由外部触发,napcat_plugin负责处理
|
||||
"""
|
||||
GET_GROUP_INFO = "napcat_get_group_info"
|
||||
'''获取群信息
|
||||
|
||||
GET_GROUP_INFO = "napcat_get_group_info"
|
||||
"""获取群信息
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -957,9 +962,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_ADD_OPTION = "napcat_set_group_add_option"
|
||||
'''设置群添加选项
|
||||
"""
|
||||
SET_GROUP_ADD_OPTION = "napcat_set_group_add_option"
|
||||
"""设置群添加选项
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -977,9 +982,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_KICK_MEMBERS = "napcat_set_group_kick_members"
|
||||
'''批量踢出群成员
|
||||
"""
|
||||
SET_GROUP_KICK_MEMBERS = "napcat_set_group_kick_members"
|
||||
"""批量踢出群成员
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -996,9 +1001,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
SET_GROUP_REMARK = "napcat_set_group_remark"
|
||||
'''设置群备注
|
||||
"""设置群备注
|
||||
|
||||
Args:
|
||||
group_id (Optional[str]): 群号(必需)
|
||||
@@ -1014,9 +1019,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_KICK = "napcat_set_group_kick"
|
||||
'''群踢人
|
||||
"""
|
||||
SET_GROUP_KICK = "napcat_set_group_kick"
|
||||
"""群踢人
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1033,9 +1038,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_GROUP_SYSTEM_MSG = "napcat_get_group_system_msg"
|
||||
'''获取群系统消息
|
||||
"""
|
||||
GET_GROUP_SYSTEM_MSG = "napcat_get_group_system_msg"
|
||||
"""获取群系统消息
|
||||
|
||||
Args:
|
||||
count (Optional[int]): 获取数量(必需)
|
||||
@@ -1077,9 +1082,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_BAN = "napcat_set_group_ban"
|
||||
'''群禁言
|
||||
"""
|
||||
SET_GROUP_BAN = "napcat_set_group_ban"
|
||||
"""群禁言
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1096,9 +1101,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_ESSENCE_MSG_LIST = "napcat_get_essence_msg_list"
|
||||
'''获取群精华消息
|
||||
"""
|
||||
GET_ESSENCE_MSG_LIST = "napcat_get_essence_msg_list"
|
||||
"""获取群精华消息
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1132,9 +1137,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_WHOLE_BAN = "napcat_set_group_whole_ban"
|
||||
'''全体禁言
|
||||
"""
|
||||
SET_GROUP_WHOLE_BAN = "napcat_set_group_whole_ban"
|
||||
"""全体禁言
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1150,9 +1155,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_PORTRAINT = "napcat_set_group_portrait"
|
||||
'''设置群头像
|
||||
"""
|
||||
SET_GROUP_PORTRAINT = "napcat_set_group_portrait"
|
||||
"""设置群头像
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1171,9 +1176,9 @@ class NapcatEvent:
|
||||
"wording": "",
|
||||
"echo": null
|
||||
}
|
||||
'''
|
||||
SET_GROUP_ADMIN = "napcat_set_group_admin"
|
||||
'''设置群管理
|
||||
"""
|
||||
SET_GROUP_ADMIN = "napcat_set_group_admin"
|
||||
"""设置群管理
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1190,9 +1195,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_CARD = "napcat_group_card"
|
||||
'''设置群成员名片
|
||||
"""
|
||||
SET_GROUP_CARD = "napcat_group_card"
|
||||
"""设置群成员名片
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1209,9 +1214,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_ESSENCE_MSG = "napcat_set_essence_msg"
|
||||
'''设置群精华消息
|
||||
"""
|
||||
SET_ESSENCE_MSG = "napcat_set_essence_msg"
|
||||
"""设置群精华消息
|
||||
|
||||
Args:
|
||||
message_id (Optional[str|int]): 消息id(必需)
|
||||
@@ -1251,9 +1256,9 @@ class NapcatEvent:
|
||||
"wording": "",
|
||||
"echo": null
|
||||
}
|
||||
'''
|
||||
SET_GROUP_NAME = "napcat_set_group_name"
|
||||
'''设置群名
|
||||
"""
|
||||
SET_GROUP_NAME = "napcat_set_group_name"
|
||||
"""设置群名
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1269,9 +1274,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
DELETE_ESSENCE_MSG = "napcat_delete_essence_msg"
|
||||
'''删除群精华消息
|
||||
"""
|
||||
DELETE_ESSENCE_MSG = "napcat_delete_essence_msg"
|
||||
"""删除群精华消息
|
||||
|
||||
Args:
|
||||
message_id (Optional[str|int]): 消息id(必需)
|
||||
@@ -1311,9 +1316,9 @@ class NapcatEvent:
|
||||
"wording": "",
|
||||
"echo": null
|
||||
}
|
||||
'''
|
||||
SET_GROUP_LEAVE = "napcat_set_group_leave"
|
||||
'''退群
|
||||
"""
|
||||
SET_GROUP_LEAVE = "napcat_set_group_leave"
|
||||
"""退群
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1328,9 +1333,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SEND_GROUP_NOTICE = "napcat_group_notice"
|
||||
'''发送群公告
|
||||
"""
|
||||
SEND_GROUP_NOTICE = "napcat_group_notice"
|
||||
"""发送群公告
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1347,9 +1352,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
SET_GROUP_SPECIAL_TITLE = "napcat_set_group_special_title"
|
||||
'''设置群头衔
|
||||
"""
|
||||
SET_GROUP_SPECIAL_TITLE = "napcat_set_group_special_title"
|
||||
"""设置群头衔
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1366,9 +1371,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_GROUP_NOTICE = "napcat_get_group_notice"
|
||||
'''获取群公告
|
||||
"""
|
||||
GET_GROUP_NOTICE = "napcat_get_group_notice"
|
||||
"""获取群公告
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1399,9 +1404,9 @@ class NapcatEvent:
|
||||
"wording": "",
|
||||
"echo": null
|
||||
}
|
||||
'''
|
||||
SET_GROUP_ADD_REQUEST = "napcat_set_group_add_request"
|
||||
'''处理加群请求
|
||||
"""
|
||||
SET_GROUP_ADD_REQUEST = "napcat_set_group_add_request"
|
||||
"""处理加群请求
|
||||
|
||||
Args:
|
||||
flag (Optional[str]): 请求id(必需)
|
||||
@@ -1418,9 +1423,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
GET_GROUP_LIST = "napcat_get_group_list"
|
||||
'''获取群列表
|
||||
"""
|
||||
GET_GROUP_LIST = "napcat_get_group_list"
|
||||
"""获取群列表
|
||||
|
||||
Args:
|
||||
no_cache (Optional[bool]): 是否不缓存
|
||||
@@ -1444,9 +1449,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
DELETE_GROUP_NOTICE = "napcat_del_group_notice"
|
||||
'''删除群公告
|
||||
"""删除群公告
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1465,9 +1470,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
GET_GROUP_MEMBER_INFO = "napcat_get_group_member_info"
|
||||
'''获取群成员信息
|
||||
"""获取群成员信息
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1504,9 +1509,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
GET_GROUP_MEMBER_LIST = "napcat_get_group_member_list"
|
||||
'''获取群成员列表
|
||||
"""获取群成员列表
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1544,9 +1549,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
GET_GROUP_HONOR_INFO = "napcat_get_group_honor_info"
|
||||
'''获取群荣誉
|
||||
"""获取群荣誉
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1610,9 +1615,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
GET_GROUP_INFO_EX = "napcat_get_group_info_ex"
|
||||
'''获取群信息ex
|
||||
"""获取群信息ex
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1679,9 +1684,9 @@ class NapcatEvent:
|
||||
"wording": "",
|
||||
"echo": null
|
||||
}
|
||||
'''
|
||||
"""
|
||||
GET_GROUP_AT_ALL_REMAIN = "napcat_get_group_at_all_remain"
|
||||
'''获取群 @全体成员 剩余次数
|
||||
"""获取群 @全体成员 剩余次数
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1700,9 +1705,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
GET_GROUP_SHUT_LIST = "napcat_get_group_shut_list"
|
||||
'''获取群禁言列表
|
||||
"""获取群禁言列表
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1758,9 +1763,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
GET_GROUP_IGNORED_NOTIFIES = "napcat_get_group_ignored_notifies"
|
||||
'''获取群过滤系统消息
|
||||
"""获取群过滤系统消息
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
@@ -1798,9 +1803,9 @@ class NapcatEvent:
|
||||
"wording": "string",
|
||||
"echo": "string"
|
||||
}
|
||||
'''
|
||||
"""
|
||||
SET_GROUP_SIGN = "napcat_set_group_sign"
|
||||
'''群打卡
|
||||
"""群打卡
|
||||
|
||||
Args:
|
||||
group_id (Optional[str|int]): 群号(必需)
|
||||
@@ -1808,7 +1813,6 @@ class NapcatEvent:
|
||||
|
||||
Returns:
|
||||
dict: {}
|
||||
'''
|
||||
"""
|
||||
|
||||
class FILE(Enum):
|
||||
...
|
||||
class FILE(Enum): ...
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import sys
|
||||
import asyncio
|
||||
import json
|
||||
import inspect
|
||||
import websockets as Server
|
||||
from . import event_types,CONSTS,event_handlers
|
||||
from . import event_types, CONSTS, event_handlers
|
||||
|
||||
from typing import List
|
||||
|
||||
@@ -29,6 +28,7 @@ logger = get_logger("napcat_adapter")
|
||||
|
||||
message_queue = asyncio.Queue()
|
||||
|
||||
|
||||
def get_classes_in_module(module):
|
||||
classes = []
|
||||
for name, member in inspect.getmembers(module):
|
||||
@@ -36,6 +36,7 @@ def get_classes_in_module(module):
|
||||
classes.append(member)
|
||||
return classes
|
||||
|
||||
|
||||
class LauchNapcatAdapterHandler(BaseEventHandler):
|
||||
"""自动启动Adapter"""
|
||||
|
||||
@@ -77,24 +78,24 @@ class LauchNapcatAdapterHandler(BaseEventHandler):
|
||||
"""启动 Napcat WebSocket 连接(支持正向和反向连接)"""
|
||||
mode = global_config.napcat_server.mode
|
||||
logger.info(f"正在启动 adapter,连接模式: {mode}")
|
||||
|
||||
|
||||
try:
|
||||
await websocket_manager.start_connection(self.message_recv)
|
||||
except Exception as e:
|
||||
logger.error(f"启动 WebSocket 连接失败: {e}")
|
||||
raise
|
||||
|
||||
async def execute(self, kwargs):
|
||||
async def execute(self, kwargs):
|
||||
# 执行功能配置迁移(如果需要)
|
||||
logger.info("检查功能配置迁移...")
|
||||
auto_migrate_features()
|
||||
|
||||
|
||||
# 初始化功能管理器
|
||||
logger.info("正在初始化功能管理器...")
|
||||
features_manager.load_config()
|
||||
await features_manager.start_file_watcher(check_interval=2.0)
|
||||
logger.info("功能管理器初始化完成")
|
||||
logger.info("开始启动Napcat Adapter")
|
||||
logger.info("开始启动Napcat Adapter")
|
||||
message_send_instance.maibot_router = router
|
||||
# 创建单独的异步任务,防止阻塞主线程
|
||||
asyncio.create_task(self.napcat_server())
|
||||
@@ -102,6 +103,7 @@ class LauchNapcatAdapterHandler(BaseEventHandler):
|
||||
asyncio.create_task(self.message_process())
|
||||
asyncio.create_task(check_timeout_response())
|
||||
|
||||
|
||||
class APITestHandler(BaseEventHandler):
|
||||
handler_name: str = "napcat_api_test_handler"
|
||||
handler_description: str = "接口测试"
|
||||
@@ -109,10 +111,10 @@ class APITestHandler(BaseEventHandler):
|
||||
intercept_message: bool = False
|
||||
init_subscribe = [EventType.ON_MESSAGE]
|
||||
|
||||
async def execute(self,_):
|
||||
async def execute(self, _):
|
||||
logger.info("5s后开始测试napcat接口...")
|
||||
await asyncio.sleep(5)
|
||||
'''
|
||||
"""
|
||||
# 测试获取登录信息
|
||||
logger.info("测试获取登录信息...")
|
||||
res = await event_manager.trigger_event(
|
||||
@@ -196,9 +198,10 @@ class APITestHandler(BaseEventHandler):
|
||||
logger.info(f"GET_PROFILE_LIKE: {res.get_message_result()}")
|
||||
|
||||
logger.info("所有ACCOUNT接口测试完成!")
|
||||
'''
|
||||
return HandlerResult(True,True,"所有接口测试完成")
|
||||
|
||||
"""
|
||||
return HandlerResult(True, True, "所有接口测试完成")
|
||||
|
||||
|
||||
@register_plugin
|
||||
class NapcatAdapterPlugin(BasePlugin):
|
||||
plugin_name = CONSTS.PLUGIN_NAME
|
||||
@@ -219,26 +222,25 @@ class NapcatAdapterPlugin(BasePlugin):
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for e in event_types.NapcatEvent.ON_RECEIVED:
|
||||
event_manager.register_event(e ,allowed_triggers=[self.plugin_name])
|
||||
|
||||
event_manager.register_event(e, allowed_triggers=[self.plugin_name])
|
||||
|
||||
for e in event_types.NapcatEvent.ACCOUNT:
|
||||
event_manager.register_event(e,allowed_subscribers=[f"{e.value}_handler"])
|
||||
event_manager.register_event(e, allowed_subscribers=[f"{e.value}_handler"])
|
||||
|
||||
for e in event_types.NapcatEvent.GROUP:
|
||||
event_manager.register_event(e,allowed_subscribers=[f"{e.value}_handler"])
|
||||
event_manager.register_event(e, allowed_subscribers=[f"{e.value}_handler"])
|
||||
|
||||
for e in event_types.NapcatEvent.MESSAGE:
|
||||
event_manager.register_event(e,allowed_subscribers=[f"{e.value}_handler"])
|
||||
event_manager.register_event(e, allowed_subscribers=[f"{e.value}_handler"])
|
||||
|
||||
def get_plugin_components(self):
|
||||
components = []
|
||||
components.append((LauchNapcatAdapterHandler.get_handler_info(), LauchNapcatAdapterHandler))
|
||||
components.append((APITestHandler.get_handler_info(), APITestHandler))
|
||||
for handler in get_classes_in_module(event_handlers):
|
||||
if issubclass(handler,BaseEventHandler):
|
||||
if issubclass(handler, BaseEventHandler):
|
||||
components.append((handler.get_handler_info(), handler))
|
||||
return components
|
||||
|
||||
@@ -2,6 +2,7 @@ from enum import Enum
|
||||
import tomlkit
|
||||
import os
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
|
||||
@@ -22,9 +23,7 @@ class CommandType(Enum):
|
||||
return self.value
|
||||
|
||||
|
||||
pyproject_path = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)), "pyproject.toml"
|
||||
)
|
||||
pyproject_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pyproject.toml")
|
||||
toml_data = tomlkit.parse(open(pyproject_path, "r", encoding="utf-8").read())
|
||||
project_data = toml_data.get("project", {})
|
||||
version = project_data.get("version", "unknown")
|
||||
|
||||
@@ -8,6 +8,7 @@ import shutil
|
||||
from tomlkit import TOMLDocument
|
||||
from tomlkit.items import Table
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
from rich.traceback import install
|
||||
|
||||
@@ -37,7 +38,7 @@ def update_config():
|
||||
"""更新配置文件,统一使用 config/old 目录进行备份"""
|
||||
# 确保目录存在
|
||||
ensure_config_directories()
|
||||
|
||||
|
||||
# 定义文件路径
|
||||
template_path = f"{TEMPLATE_DIR}/template_config.toml"
|
||||
config_path = f"{CONFIG_DIR}/config.toml"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
配置文件工具模块
|
||||
提供统一的配置文件生成和管理功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
@@ -9,6 +10,7 @@ from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
|
||||
@@ -19,36 +21,33 @@ def ensure_config_directories():
|
||||
|
||||
|
||||
def create_config_from_template(
|
||||
config_path: str,
|
||||
template_path: str,
|
||||
config_name: str = "配置文件",
|
||||
should_exit: bool = True
|
||||
config_path: str, template_path: str, config_name: str = "配置文件", should_exit: bool = True
|
||||
) -> bool:
|
||||
"""
|
||||
从模板创建配置文件的统一函数
|
||||
|
||||
|
||||
Args:
|
||||
config_path: 配置文件路径
|
||||
template_path: 模板文件路径
|
||||
config_name: 配置文件名称(用于日志显示)
|
||||
should_exit: 创建后是否退出程序
|
||||
|
||||
|
||||
Returns:
|
||||
bool: 是否成功创建配置文件
|
||||
"""
|
||||
try:
|
||||
# 确保配置目录存在
|
||||
ensure_config_directories()
|
||||
|
||||
|
||||
config_path_obj = Path(config_path)
|
||||
template_path_obj = Path(template_path)
|
||||
|
||||
|
||||
# 检查配置文件是否存在
|
||||
if config_path_obj.exists():
|
||||
return False # 配置文件已存在,无需创建
|
||||
|
||||
|
||||
logger.info(f"{config_name}不存在,从模板创建新配置")
|
||||
|
||||
|
||||
# 检查模板文件是否存在
|
||||
if not template_path_obj.exists():
|
||||
logger.error(f"模板文件不存在: {template_path}")
|
||||
@@ -56,20 +55,20 @@ def create_config_from_template(
|
||||
logger.critical("无法创建配置文件,程序退出")
|
||||
quit(1)
|
||||
return False
|
||||
|
||||
|
||||
# 确保配置文件目录存在
|
||||
config_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
# 复制模板文件到配置目录
|
||||
shutil.copy2(template_path_obj, config_path_obj)
|
||||
logger.info(f"已创建新{config_name}: {config_path}")
|
||||
|
||||
|
||||
if should_exit:
|
||||
logger.info("程序将退出,请检查配置文件后重启")
|
||||
quit(0)
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建{config_name}失败: {e}")
|
||||
if should_exit:
|
||||
@@ -81,30 +80,30 @@ def create_config_from_template(
|
||||
def create_default_config_dict(default_values: dict, config_path: str, config_name: str = "配置文件") -> bool:
|
||||
"""
|
||||
创建默认配置文件(使用字典数据)
|
||||
|
||||
|
||||
Args:
|
||||
default_values: 默认配置值字典
|
||||
config_path: 配置文件路径
|
||||
config_name: 配置文件名称(用于日志显示)
|
||||
|
||||
|
||||
Returns:
|
||||
bool: 是否成功创建配置文件
|
||||
"""
|
||||
try:
|
||||
import tomlkit
|
||||
|
||||
|
||||
config_path_obj = Path(config_path)
|
||||
|
||||
|
||||
# 确保配置文件目录存在
|
||||
config_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
# 写入默认配置
|
||||
with open(config_path_obj, "w", encoding="utf-8") as f:
|
||||
tomlkit.dump(default_values, f)
|
||||
|
||||
|
||||
logger.info(f"已创建默认{config_name}: {config_path}")
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建默认{config_name}失败: {e}")
|
||||
return False
|
||||
@@ -113,11 +112,11 @@ def create_default_config_dict(default_values: dict, config_path: str, config_na
|
||||
def backup_config_file(config_path: str, backup_dir: str = "config/old") -> Optional[str]:
|
||||
"""
|
||||
备份配置文件
|
||||
|
||||
|
||||
Args:
|
||||
config_path: 要备份的配置文件路径
|
||||
backup_dir: 备份目录
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[str]: 备份文件路径,失败时返回None
|
||||
"""
|
||||
@@ -125,22 +124,22 @@ def backup_config_file(config_path: str, backup_dir: str = "config/old") -> Opti
|
||||
config_path_obj = Path(config_path)
|
||||
if not config_path_obj.exists():
|
||||
return None
|
||||
|
||||
|
||||
# 确保备份目录存在
|
||||
backup_dir_obj = Path(backup_dir)
|
||||
backup_dir_obj.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
# 创建备份文件名
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_filename = f"{config_path_obj.stem}.toml.bak.{timestamp}"
|
||||
backup_path = backup_dir_obj / backup_filename
|
||||
|
||||
|
||||
# 备份文件
|
||||
shutil.copy2(config_path_obj, backup_path)
|
||||
logger.info(f"已备份配置文件到: {backup_path}")
|
||||
|
||||
|
||||
return str(backup_path)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"备份配置文件失败: {e}")
|
||||
return None
|
||||
return None
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Literal, Optional
|
||||
from pathlib import Path
|
||||
import tomlkit
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
from .config_base import ConfigBase
|
||||
from .config_utils import create_config_from_template, create_default_config_dict
|
||||
@@ -12,31 +13,31 @@ from .config_utils import create_config_from_template, create_default_config_dic
|
||||
@dataclass
|
||||
class FeaturesConfig(ConfigBase):
|
||||
"""功能配置类"""
|
||||
|
||||
|
||||
group_list_type: Literal["whitelist", "blacklist"] = "whitelist"
|
||||
"""群聊列表类型 白名单/黑名单"""
|
||||
|
||||
|
||||
group_list: list[int] = field(default_factory=list)
|
||||
"""群聊列表"""
|
||||
|
||||
|
||||
private_list_type: Literal["whitelist", "blacklist"] = "whitelist"
|
||||
"""私聊列表类型 白名单/黑名单"""
|
||||
|
||||
|
||||
private_list: list[int] = field(default_factory=list)
|
||||
"""私聊列表"""
|
||||
|
||||
|
||||
ban_user_id: list[int] = field(default_factory=list)
|
||||
"""被封禁的用户ID列表,封禁后将无法与其进行交互"""
|
||||
|
||||
|
||||
ban_qq_bot: bool = False
|
||||
"""是否屏蔽QQ官方机器人,若为True,则所有QQ官方机器人将无法与MaiMCore进行交互"""
|
||||
|
||||
|
||||
enable_poke: bool = True
|
||||
"""是否启用戳一戳功能"""
|
||||
|
||||
|
||||
ignore_non_self_poke: bool = False
|
||||
"""是否无视不是针对自己的戳一戳"""
|
||||
|
||||
|
||||
poke_debounce_seconds: int = 3
|
||||
"""戳一戳防抖时间(秒),在指定时间内第二次针对机器人的戳一戳将被忽略"""
|
||||
|
||||
@@ -45,61 +46,61 @@ class FeaturesConfig(ConfigBase):
|
||||
|
||||
reply_at_rate: float = 0.5
|
||||
"""引用回复时艾特用户的几率 (0.0 ~ 1.0)"""
|
||||
|
||||
|
||||
enable_video_analysis: bool = True
|
||||
"""是否启用视频识别功能"""
|
||||
|
||||
|
||||
max_video_size_mb: int = 100
|
||||
"""视频文件最大大小限制(MB)"""
|
||||
|
||||
|
||||
download_timeout: int = 60
|
||||
"""视频下载超时时间(秒)"""
|
||||
|
||||
|
||||
supported_formats: list[str] = field(default_factory=lambda: ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"])
|
||||
"""支持的视频格式"""
|
||||
|
||||
|
||||
# 消息缓冲配置
|
||||
enable_message_buffer: bool = True
|
||||
"""是否启用消息缓冲合并功能"""
|
||||
|
||||
|
||||
message_buffer_enable_group: bool = True
|
||||
"""是否启用群消息缓冲合并"""
|
||||
|
||||
|
||||
message_buffer_enable_private: bool = True
|
||||
"""是否启用私聊消息缓冲合并"""
|
||||
|
||||
|
||||
message_buffer_interval: float = 3.0
|
||||
"""消息合并间隔时间(秒),在此时间内的连续消息将被合并"""
|
||||
|
||||
|
||||
message_buffer_initial_delay: float = 0.5
|
||||
"""消息缓冲初始延迟(秒),收到第一条消息后等待此时间开始合并"""
|
||||
|
||||
|
||||
message_buffer_max_components: int = 50
|
||||
"""单个会话最大缓冲消息组件数量,超过此数量将强制合并"""
|
||||
|
||||
|
||||
message_buffer_block_prefixes: list[str] = field(default_factory=lambda: ["/", "!", "!", ".", "。", "#", "%"])
|
||||
"""消息缓冲屏蔽前缀,以这些前缀开头的消息不会被缓冲"""
|
||||
|
||||
|
||||
class FeaturesManager:
|
||||
"""功能管理器,支持热重载"""
|
||||
|
||||
|
||||
def __init__(self, config_path: str = "plugins/napcat_adapter_plugin/config/features.toml"):
|
||||
self.config_path = Path(config_path)
|
||||
self.config: Optional[FeaturesConfig] = None
|
||||
self._file_watcher_task: Optional[asyncio.Task] = None
|
||||
self._last_modified: Optional[float] = None
|
||||
self._callbacks: list = []
|
||||
|
||||
|
||||
def add_reload_callback(self, callback):
|
||||
"""添加配置重载回调函数"""
|
||||
self._callbacks.append(callback)
|
||||
|
||||
|
||||
def remove_reload_callback(self, callback):
|
||||
"""移除配置重载回调函数"""
|
||||
if callback in self._callbacks:
|
||||
self._callbacks.remove(callback)
|
||||
|
||||
|
||||
async def _notify_callbacks(self):
|
||||
"""通知所有回调函数配置已重载"""
|
||||
for callback in self._callbacks:
|
||||
@@ -110,7 +111,7 @@ class FeaturesManager:
|
||||
callback(self.config)
|
||||
except Exception as e:
|
||||
logger.error(f"配置重载回调执行失败: {e}")
|
||||
|
||||
|
||||
def load_config(self) -> FeaturesConfig:
|
||||
"""加载功能配置文件"""
|
||||
try:
|
||||
@@ -121,33 +122,33 @@ class FeaturesManager:
|
||||
# 配置文件创建后程序应该退出,让用户检查配置
|
||||
logger.info("程序将退出,请检查功能配置文件后重启")
|
||||
quit(0)
|
||||
|
||||
|
||||
with open(self.config_path, "r", encoding="utf-8") as f:
|
||||
config_data = tomlkit.load(f)
|
||||
|
||||
|
||||
self.config = FeaturesConfig.from_dict(config_data)
|
||||
self._last_modified = self.config_path.stat().st_mtime
|
||||
logger.info(f"功能配置加载成功: {self.config_path}")
|
||||
return self.config
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"功能配置加载失败: {e}")
|
||||
logger.critical("无法加载功能配置文件,程序退出")
|
||||
quit(1)
|
||||
|
||||
|
||||
def _create_default_config(self):
|
||||
"""创建默认功能配置文件"""
|
||||
template_path = "template/features_template.toml"
|
||||
|
||||
|
||||
# 尝试从模板创建配置文件
|
||||
if create_config_from_template(
|
||||
str(self.config_path),
|
||||
template_path,
|
||||
"功能配置文件",
|
||||
should_exit=False # 不在这里退出,由调用方决定
|
||||
should_exit=False, # 不在这里退出,由调用方决定
|
||||
):
|
||||
return
|
||||
|
||||
|
||||
# 如果模板文件不存在,创建基本配置
|
||||
logger.info("模板文件不存在,创建基本功能配置")
|
||||
default_config = {
|
||||
@@ -173,78 +174,77 @@ class FeaturesManager:
|
||||
"message_buffer_interval": 3.0,
|
||||
"message_buffer_initial_delay": 0.5,
|
||||
"message_buffer_max_components": 50,
|
||||
"message_buffer_block_prefixes": ["/", "!", "!", ".", "。", "#", "%"]
|
||||
"message_buffer_block_prefixes": ["/", "!", "!", ".", "。", "#", "%"],
|
||||
}
|
||||
|
||||
|
||||
if not create_default_config_dict(default_config, str(self.config_path), "功能配置文件"):
|
||||
logger.critical("无法创建功能配置文件")
|
||||
quit(1)
|
||||
|
||||
|
||||
async def reload_config(self) -> bool:
|
||||
"""重新加载配置文件"""
|
||||
try:
|
||||
if not self.config_path.exists():
|
||||
logger.warning(f"功能配置文件不存在,无法重载: {self.config_path}")
|
||||
return False
|
||||
|
||||
|
||||
current_modified = self.config_path.stat().st_mtime
|
||||
if self._last_modified and current_modified <= self._last_modified:
|
||||
return False # 文件未修改
|
||||
|
||||
|
||||
old_config = self.config
|
||||
new_config = self.load_config()
|
||||
|
||||
|
||||
# 检查配置是否真的发生了变化
|
||||
if old_config and self._configs_equal(old_config, new_config):
|
||||
return False
|
||||
|
||||
|
||||
logger.info("功能配置已重载")
|
||||
await self._notify_callbacks()
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"功能配置重载失败: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _configs_equal(self, config1: FeaturesConfig, config2: FeaturesConfig) -> bool:
|
||||
"""比较两个配置是否相等"""
|
||||
return (
|
||||
config1.group_list_type == config2.group_list_type and
|
||||
set(config1.group_list) == set(config2.group_list) and
|
||||
config1.private_list_type == config2.private_list_type and
|
||||
set(config1.private_list) == set(config2.private_list) and
|
||||
set(config1.ban_user_id) == set(config2.ban_user_id) and
|
||||
config1.ban_qq_bot == config2.ban_qq_bot and
|
||||
config1.enable_poke == config2.enable_poke and
|
||||
config1.ignore_non_self_poke == config2.ignore_non_self_poke and
|
||||
config1.poke_debounce_seconds == config2.poke_debounce_seconds and
|
||||
config1.enable_reply_at == config2.enable_reply_at and
|
||||
config1.reply_at_rate == config2.reply_at_rate and
|
||||
config1.enable_video_analysis == config2.enable_video_analysis and
|
||||
config1.max_video_size_mb == config2.max_video_size_mb and
|
||||
config1.download_timeout == config2.download_timeout and
|
||||
set(config1.supported_formats) == set(config2.supported_formats) and
|
||||
config1.group_list_type == config2.group_list_type
|
||||
and set(config1.group_list) == set(config2.group_list)
|
||||
and config1.private_list_type == config2.private_list_type
|
||||
and set(config1.private_list) == set(config2.private_list)
|
||||
and set(config1.ban_user_id) == set(config2.ban_user_id)
|
||||
and config1.ban_qq_bot == config2.ban_qq_bot
|
||||
and config1.enable_poke == config2.enable_poke
|
||||
and config1.ignore_non_self_poke == config2.ignore_non_self_poke
|
||||
and config1.poke_debounce_seconds == config2.poke_debounce_seconds
|
||||
and config1.enable_reply_at == config2.enable_reply_at
|
||||
and config1.reply_at_rate == config2.reply_at_rate
|
||||
and config1.enable_video_analysis == config2.enable_video_analysis
|
||||
and config1.max_video_size_mb == config2.max_video_size_mb
|
||||
and config1.download_timeout == config2.download_timeout
|
||||
and set(config1.supported_formats) == set(config2.supported_formats)
|
||||
and
|
||||
# 消息缓冲配置比较
|
||||
config1.enable_message_buffer == config2.enable_message_buffer and
|
||||
config1.message_buffer_enable_group == config2.message_buffer_enable_group and
|
||||
config1.message_buffer_enable_private == config2.message_buffer_enable_private and
|
||||
config1.message_buffer_interval == config2.message_buffer_interval and
|
||||
config1.message_buffer_initial_delay == config2.message_buffer_initial_delay and
|
||||
config1.message_buffer_max_components == config2.message_buffer_max_components and
|
||||
set(config1.message_buffer_block_prefixes) == set(config2.message_buffer_block_prefixes)
|
||||
config1.enable_message_buffer == config2.enable_message_buffer
|
||||
and config1.message_buffer_enable_group == config2.message_buffer_enable_group
|
||||
and config1.message_buffer_enable_private == config2.message_buffer_enable_private
|
||||
and config1.message_buffer_interval == config2.message_buffer_interval
|
||||
and config1.message_buffer_initial_delay == config2.message_buffer_initial_delay
|
||||
and config1.message_buffer_max_components == config2.message_buffer_max_components
|
||||
and set(config1.message_buffer_block_prefixes) == set(config2.message_buffer_block_prefixes)
|
||||
)
|
||||
|
||||
|
||||
async def start_file_watcher(self, check_interval: float = 1.0):
|
||||
"""启动文件监控,定期检查配置文件变化"""
|
||||
if self._file_watcher_task and not self._file_watcher_task.done():
|
||||
logger.warning("文件监控已在运行")
|
||||
return
|
||||
|
||||
self._file_watcher_task = asyncio.create_task(
|
||||
self._file_watcher_loop(check_interval)
|
||||
)
|
||||
|
||||
self._file_watcher_task = asyncio.create_task(self._file_watcher_loop(check_interval))
|
||||
logger.info(f"功能配置文件监控已启动,检查间隔: {check_interval}秒")
|
||||
|
||||
|
||||
async def stop_file_watcher(self):
|
||||
"""停止文件监控"""
|
||||
if self._file_watcher_task and not self._file_watcher_task.done():
|
||||
@@ -254,7 +254,7 @@ class FeaturesManager:
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
logger.info("功能配置文件监控已停止")
|
||||
|
||||
|
||||
async def _file_watcher_loop(self, check_interval: float):
|
||||
"""文件监控循环"""
|
||||
while True:
|
||||
@@ -266,13 +266,13 @@ class FeaturesManager:
|
||||
except Exception as e:
|
||||
logger.error(f"文件监控循环出错: {e}")
|
||||
await asyncio.sleep(check_interval)
|
||||
|
||||
|
||||
def get_config(self) -> FeaturesConfig:
|
||||
"""获取当前功能配置"""
|
||||
if self.config is None:
|
||||
return self.load_config()
|
||||
return self.config
|
||||
|
||||
|
||||
def is_group_allowed(self, group_id: int) -> bool:
|
||||
"""检查群聊是否被允许"""
|
||||
config = self.get_config()
|
||||
@@ -280,7 +280,7 @@ class FeaturesManager:
|
||||
return group_id in config.group_list
|
||||
else: # blacklist
|
||||
return group_id not in config.group_list
|
||||
|
||||
|
||||
def is_private_allowed(self, user_id: int) -> bool:
|
||||
"""检查私聊是否被允许"""
|
||||
config = self.get_config()
|
||||
@@ -288,67 +288,67 @@ class FeaturesManager:
|
||||
return user_id in config.private_list
|
||||
else: # blacklist
|
||||
return user_id not in config.private_list
|
||||
|
||||
|
||||
def is_user_banned(self, user_id: int) -> bool:
|
||||
"""检查用户是否被全局禁止"""
|
||||
config = self.get_config()
|
||||
return user_id in config.ban_user_id
|
||||
|
||||
|
||||
def is_qq_bot_banned(self) -> bool:
|
||||
"""检查是否禁止QQ官方机器人"""
|
||||
config = self.get_config()
|
||||
return config.ban_qq_bot
|
||||
|
||||
|
||||
def is_poke_enabled(self) -> bool:
|
||||
"""检查戳一戳功能是否启用"""
|
||||
config = self.get_config()
|
||||
return config.enable_poke
|
||||
|
||||
|
||||
def is_non_self_poke_ignored(self) -> bool:
|
||||
"""检查是否忽略非自己戳一戳"""
|
||||
config = self.get_config()
|
||||
return config.ignore_non_self_poke
|
||||
|
||||
|
||||
def is_message_buffer_enabled(self) -> bool:
|
||||
"""检查消息缓冲功能是否启用"""
|
||||
config = self.get_config()
|
||||
return config.enable_message_buffer
|
||||
|
||||
|
||||
def is_message_buffer_group_enabled(self) -> bool:
|
||||
"""检查群消息缓冲是否启用"""
|
||||
config = self.get_config()
|
||||
return config.message_buffer_enable_group
|
||||
|
||||
|
||||
def is_message_buffer_private_enabled(self) -> bool:
|
||||
"""检查私聊消息缓冲是否启用"""
|
||||
config = self.get_config()
|
||||
return config.message_buffer_enable_private
|
||||
|
||||
|
||||
def get_message_buffer_interval(self) -> float:
|
||||
"""获取消息缓冲间隔时间"""
|
||||
config = self.get_config()
|
||||
return config.message_buffer_interval
|
||||
|
||||
|
||||
def get_message_buffer_initial_delay(self) -> float:
|
||||
"""获取消息缓冲初始延迟"""
|
||||
config = self.get_config()
|
||||
return config.message_buffer_initial_delay
|
||||
|
||||
|
||||
def get_message_buffer_max_components(self) -> int:
|
||||
"""获取消息缓冲最大组件数量"""
|
||||
config = self.get_config()
|
||||
return config.message_buffer_max_components
|
||||
|
||||
|
||||
def is_message_buffer_group_enabled(self) -> bool:
|
||||
"""检查是否启用群聊消息缓冲"""
|
||||
config = self.get_config()
|
||||
return config.message_buffer_enable_group
|
||||
|
||||
|
||||
def is_message_buffer_private_enabled(self) -> bool:
|
||||
"""检查是否启用私聊消息缓冲"""
|
||||
config = self.get_config()
|
||||
return config.message_buffer_enable_private
|
||||
|
||||
|
||||
def get_message_buffer_block_prefixes(self) -> list[str]:
|
||||
"""获取消息缓冲屏蔽前缀列表"""
|
||||
config = self.get_config()
|
||||
@@ -356,4 +356,4 @@ class FeaturesManager:
|
||||
|
||||
|
||||
# 全局功能管理器实例
|
||||
features_manager = FeaturesManager()
|
||||
features_manager = FeaturesManager()
|
||||
|
||||
@@ -8,15 +8,18 @@ import shutil
|
||||
from pathlib import Path
|
||||
import tomlkit
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
|
||||
def migrate_features_from_config(old_config_path: str = "plugins/napcat_adapter_plugin/config/config.toml",
|
||||
new_features_path: str = "plugins/napcat_adapter_plugin/config/features.toml",
|
||||
template_path: str = "plugins/napcat_adapter_plugin/template/features_template.toml"):
|
||||
def migrate_features_from_config(
|
||||
old_config_path: str = "plugins/napcat_adapter_plugin/config/config.toml",
|
||||
new_features_path: str = "plugins/napcat_adapter_plugin/config/features.toml",
|
||||
template_path: str = "plugins/napcat_adapter_plugin/template/features_template.toml",
|
||||
):
|
||||
"""
|
||||
从旧配置文件迁移功能设置到新的功能配置文件
|
||||
|
||||
|
||||
Args:
|
||||
old_config_path: 旧配置文件路径
|
||||
new_features_path: 新功能配置文件路径
|
||||
@@ -27,38 +30,46 @@ def migrate_features_from_config(old_config_path: str = "plugins/napcat_adapter_
|
||||
if not os.path.exists(old_config_path):
|
||||
logger.warning(f"旧配置文件不存在: {old_config_path}")
|
||||
return False
|
||||
|
||||
|
||||
# 读取旧配置文件
|
||||
with open(old_config_path, "r", encoding="utf-8") as f:
|
||||
old_config = tomlkit.load(f)
|
||||
|
||||
|
||||
# 检查是否有chat配置段和video配置段
|
||||
chat_config = old_config.get("chat", {})
|
||||
video_config = old_config.get("video", {})
|
||||
|
||||
|
||||
# 检查是否有权限相关配置
|
||||
permission_keys = ["group_list_type", "group_list", "private_list_type",
|
||||
"private_list", "ban_user_id", "ban_qq_bot",
|
||||
"enable_poke", "ignore_non_self_poke", "poke_debounce_seconds"]
|
||||
permission_keys = [
|
||||
"group_list_type",
|
||||
"group_list",
|
||||
"private_list_type",
|
||||
"private_list",
|
||||
"ban_user_id",
|
||||
"ban_qq_bot",
|
||||
"enable_poke",
|
||||
"ignore_non_self_poke",
|
||||
"poke_debounce_seconds",
|
||||
]
|
||||
video_keys = ["enable_video_analysis", "max_video_size_mb", "download_timeout", "supported_formats"]
|
||||
|
||||
|
||||
has_permission_config = any(key in chat_config for key in permission_keys)
|
||||
has_video_config = any(key in video_config for key in video_keys)
|
||||
|
||||
|
||||
if not has_permission_config and not has_video_config:
|
||||
logger.info("旧配置文件中没有找到功能相关配置,无需迁移")
|
||||
return False
|
||||
|
||||
|
||||
# 确保新功能配置目录存在
|
||||
new_features_dir = Path(new_features_path).parent
|
||||
new_features_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
# 如果新功能配置文件已存在,先备份
|
||||
if os.path.exists(new_features_path):
|
||||
backup_path = f"{new_features_path}.backup"
|
||||
shutil.copy2(new_features_path, backup_path)
|
||||
logger.info(f"已备份现有功能配置文件到: {backup_path}")
|
||||
|
||||
|
||||
# 创建新的功能配置
|
||||
new_features_config = {
|
||||
"group_list_type": chat_config.get("group_list_type", "whitelist"),
|
||||
@@ -73,22 +84,24 @@ def migrate_features_from_config(old_config_path: str = "plugins/napcat_adapter_
|
||||
"enable_video_analysis": video_config.get("enable_video_analysis", True),
|
||||
"max_video_size_mb": video_config.get("max_video_size_mb", 100),
|
||||
"download_timeout": video_config.get("download_timeout", 60),
|
||||
"supported_formats": video_config.get("supported_formats", ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"])
|
||||
"supported_formats": video_config.get(
|
||||
"supported_formats", ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"]
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
# 写入新的功能配置文件
|
||||
with open(new_features_path, "w", encoding="utf-8") as f:
|
||||
tomlkit.dump(new_features_config, f)
|
||||
|
||||
|
||||
logger.info(f"功能配置已成功迁移到: {new_features_path}")
|
||||
|
||||
|
||||
# 显示迁移的配置内容
|
||||
logger.info("迁移的配置内容:")
|
||||
for key, value in new_features_config.items():
|
||||
logger.info(f" {key}: {value}")
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"功能配置迁移失败: {e}")
|
||||
return False
|
||||
@@ -97,7 +110,7 @@ def migrate_features_from_config(old_config_path: str = "plugins/napcat_adapter_
|
||||
def remove_features_from_old_config(config_path: str = "plugins/napcat_adapter_plugin/config/config.toml"):
|
||||
"""
|
||||
从旧配置文件中移除功能相关配置,并将旧配置移动到 config/old/ 目录
|
||||
|
||||
|
||||
Args:
|
||||
config_path: 配置文件路径
|
||||
"""
|
||||
@@ -105,66 +118,74 @@ def remove_features_from_old_config(config_path: str = "plugins/napcat_adapter_p
|
||||
if not os.path.exists(config_path):
|
||||
logger.warning(f"配置文件不存在: {config_path}")
|
||||
return False
|
||||
|
||||
|
||||
# 确保 config/old 目录存在
|
||||
old_config_dir = "plugins/napcat_adapter_plugin/config/old"
|
||||
os.makedirs(old_config_dir, exist_ok=True)
|
||||
|
||||
|
||||
# 备份原配置文件到 config/old 目录
|
||||
old_config_path = os.path.join(old_config_dir, "config_with_features.toml")
|
||||
shutil.copy2(config_path, old_config_path)
|
||||
logger.info(f"已备份包含功能配置的原文件到: {old_config_path}")
|
||||
|
||||
|
||||
# 读取配置文件
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = tomlkit.load(f)
|
||||
|
||||
|
||||
# 移除chat段中的功能相关配置
|
||||
removed_keys = []
|
||||
if "chat" in config:
|
||||
chat_config = config["chat"]
|
||||
permission_keys = ["group_list_type", "group_list", "private_list_type",
|
||||
"private_list", "ban_user_id", "ban_qq_bot",
|
||||
"enable_poke", "ignore_non_self_poke", "poke_debounce_seconds"]
|
||||
|
||||
permission_keys = [
|
||||
"group_list_type",
|
||||
"group_list",
|
||||
"private_list_type",
|
||||
"private_list",
|
||||
"ban_user_id",
|
||||
"ban_qq_bot",
|
||||
"enable_poke",
|
||||
"ignore_non_self_poke",
|
||||
"poke_debounce_seconds",
|
||||
]
|
||||
|
||||
for key in permission_keys:
|
||||
if key in chat_config:
|
||||
del chat_config[key]
|
||||
removed_keys.append(key)
|
||||
|
||||
|
||||
if removed_keys:
|
||||
logger.info(f"已从chat配置段中移除功能相关配置: {removed_keys}")
|
||||
|
||||
|
||||
# 移除video段中的配置
|
||||
if "video" in config:
|
||||
video_config = config["video"]
|
||||
video_keys = ["enable_video_analysis", "max_video_size_mb", "download_timeout", "supported_formats"]
|
||||
|
||||
|
||||
video_removed_keys = []
|
||||
for key in video_keys:
|
||||
if key in video_config:
|
||||
del video_config[key]
|
||||
video_removed_keys.append(key)
|
||||
|
||||
|
||||
if video_removed_keys:
|
||||
logger.info(f"已从video配置段中移除配置: {video_removed_keys}")
|
||||
removed_keys.extend(video_removed_keys)
|
||||
|
||||
|
||||
# 如果video段为空,则删除整个段
|
||||
if not video_config:
|
||||
del config["video"]
|
||||
logger.info("已删除空的video配置段")
|
||||
|
||||
|
||||
if removed_keys:
|
||||
logger.info(f"总共移除的配置项: {removed_keys}")
|
||||
|
||||
|
||||
# 写回配置文件
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
f.write(tomlkit.dumps(config))
|
||||
|
||||
|
||||
logger.info(f"已更新配置文件: {config_path}")
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"移除功能配置失败: {e}")
|
||||
return False
|
||||
@@ -175,20 +196,20 @@ def auto_migrate_features():
|
||||
自动执行功能配置迁移
|
||||
"""
|
||||
logger.info("开始自动功能配置迁移...")
|
||||
|
||||
|
||||
# 执行迁移
|
||||
if migrate_features_from_config():
|
||||
logger.info("功能配置迁移成功")
|
||||
|
||||
|
||||
# 询问是否要从旧配置文件中移除功能配置
|
||||
logger.info("功能配置已迁移到独立文件,建议从主配置文件中移除相关配置")
|
||||
# 在实际使用中,这里可以添加用户确认逻辑
|
||||
# 为了自动化,这里直接执行移除
|
||||
remove_features_from_old_config()
|
||||
|
||||
|
||||
else:
|
||||
logger.info("功能配置迁移跳过或失败")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
auto_migrate_features()
|
||||
auto_migrate_features()
|
||||
|
||||
@@ -53,8 +53,6 @@ class MaiBotServerConfig(ConfigBase):
|
||||
"""MaiMCore的端口号"""
|
||||
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class VoiceConfig(ConfigBase):
|
||||
use_tts: bool = False
|
||||
|
||||
@@ -4,6 +4,7 @@ from dataclasses import dataclass
|
||||
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
"""
|
||||
@@ -100,12 +101,11 @@ class DatabaseManager:
|
||||
)
|
||||
if ban_record := session.exec(statement).first():
|
||||
session.delete(ban_record)
|
||||
|
||||
|
||||
logger.debug(f"删除禁言记录: {ban_record}")
|
||||
else:
|
||||
logger.info(f"未找到禁言记录: {ban_record}")
|
||||
|
||||
|
||||
logger.info("禁言记录已更新")
|
||||
|
||||
def get_ban_records(self) -> List[BanUser]:
|
||||
@@ -141,7 +141,6 @@ class DatabaseManager:
|
||||
)
|
||||
session.add(db_record)
|
||||
logger.debug(f"创建新禁言记录: {ban_record}")
|
||||
|
||||
|
||||
def delete_ban_record(self, ban_record: BanUser):
|
||||
"""
|
||||
@@ -154,7 +153,7 @@ class DatabaseManager:
|
||||
statement = select(DB_BanUser).where(DB_BanUser.user_id == user_id, DB_BanUser.group_id == group_id)
|
||||
if ban_record := session.exec(statement).first():
|
||||
session.delete(ban_record)
|
||||
|
||||
|
||||
logger.debug(f"删除禁言记录: {ban_record}")
|
||||
else:
|
||||
logger.info(f"未找到禁言记录: user_id: {user_id}, group_id: {group_id}")
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Dict, List, Any, Optional
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
from .config.features_config import features_manager
|
||||
@@ -13,6 +14,7 @@ from .recv_handler import RealMessageType
|
||||
@dataclass
|
||||
class TextMessage:
|
||||
"""文本消息"""
|
||||
|
||||
text: str
|
||||
timestamp: float = field(default_factory=time.time)
|
||||
|
||||
@@ -20,6 +22,7 @@ class TextMessage:
|
||||
@dataclass
|
||||
class BufferedSession:
|
||||
"""缓冲会话数据"""
|
||||
|
||||
session_id: str
|
||||
messages: List[TextMessage] = field(default_factory=list)
|
||||
timer_task: Optional[asyncio.Task] = None
|
||||
@@ -29,11 +32,10 @@ class BufferedSession:
|
||||
|
||||
|
||||
class SimpleMessageBuffer:
|
||||
|
||||
def __init__(self, merge_callback=None):
|
||||
"""
|
||||
初始化消息缓冲器
|
||||
|
||||
|
||||
Args:
|
||||
merge_callback: 消息合并后的回调函数,接收(session_id, merged_text, original_event)参数
|
||||
"""
|
||||
@@ -41,12 +43,12 @@ class SimpleMessageBuffer:
|
||||
self.lock = asyncio.Lock()
|
||||
self.merge_callback = merge_callback
|
||||
self._shutdown = False
|
||||
|
||||
|
||||
def get_session_id(self, event_data: Dict[str, Any]) -> str:
|
||||
"""根据事件数据生成会话ID"""
|
||||
message_type = event_data.get("message_type", "unknown")
|
||||
user_id = event_data.get("user_id", "unknown")
|
||||
|
||||
|
||||
if message_type == "private":
|
||||
return f"private_{user_id}"
|
||||
elif message_type == "group":
|
||||
@@ -54,18 +56,18 @@ class SimpleMessageBuffer:
|
||||
return f"group_{group_id}_{user_id}"
|
||||
else:
|
||||
return f"{message_type}_{user_id}"
|
||||
|
||||
|
||||
def extract_text_from_message(self, message: List[Dict[str, Any]]) -> Optional[str]:
|
||||
"""从OneBot消息中提取纯文本,如果包含非文本内容则返回None"""
|
||||
text_parts = []
|
||||
has_non_text = False
|
||||
|
||||
|
||||
logger.debug(f"正在提取消息文本,消息段数量: {len(message)}")
|
||||
|
||||
|
||||
for msg_seg in message:
|
||||
msg_type = msg_seg.get("type", "")
|
||||
logger.debug(f"处理消息段类型: {msg_type}")
|
||||
|
||||
|
||||
if msg_type == RealMessageType.text:
|
||||
text = msg_seg.get("data", {}).get("text", "").strip()
|
||||
if text:
|
||||
@@ -75,112 +77,105 @@ class SimpleMessageBuffer:
|
||||
# 发现非文本消息段,标记为包含非文本内容
|
||||
has_non_text = True
|
||||
logger.debug(f"发现非文本消息段: {msg_type},跳过缓冲")
|
||||
|
||||
|
||||
# 如果包含非文本内容,则不进行缓冲
|
||||
if has_non_text:
|
||||
logger.debug("消息包含非文本内容,不进行缓冲")
|
||||
return None
|
||||
|
||||
|
||||
if text_parts:
|
||||
combined_text = " ".join(text_parts).strip()
|
||||
logger.debug(f"成功提取纯文本: {combined_text[:50]}...")
|
||||
return combined_text
|
||||
|
||||
|
||||
logger.debug("没有找到有效的文本内容")
|
||||
return None
|
||||
|
||||
|
||||
def should_skip_message(self, text: str) -> bool:
|
||||
"""判断消息是否应该跳过缓冲"""
|
||||
if not text or not text.strip():
|
||||
return True
|
||||
|
||||
|
||||
# 检查屏蔽前缀
|
||||
config = features_manager.get_config()
|
||||
block_prefixes = tuple(config.message_buffer_block_prefixes)
|
||||
|
||||
|
||||
text = text.strip()
|
||||
if text.startswith(block_prefixes):
|
||||
logger.debug(f"消息以屏蔽前缀开头,跳过缓冲: {text[:20]}...")
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
async def add_text_message(self, event_data: Dict[str, Any], message: List[Dict[str, Any]],
|
||||
original_event: Any = None) -> bool:
|
||||
|
||||
async def add_text_message(
|
||||
self, event_data: Dict[str, Any], message: List[Dict[str, Any]], original_event: Any = None
|
||||
) -> bool:
|
||||
"""
|
||||
添加文本消息到缓冲区
|
||||
|
||||
|
||||
Args:
|
||||
event_data: 事件数据
|
||||
message: OneBot消息数组
|
||||
original_event: 原始事件对象
|
||||
|
||||
|
||||
Returns:
|
||||
是否成功添加到缓冲区
|
||||
"""
|
||||
if self._shutdown:
|
||||
return False
|
||||
|
||||
|
||||
config = features_manager.get_config()
|
||||
if not config.enable_message_buffer:
|
||||
return False
|
||||
|
||||
|
||||
# 检查是否启用对应类型的缓冲
|
||||
message_type = event_data.get("message_type", "")
|
||||
if message_type == "group" and not config.message_buffer_enable_group:
|
||||
return False
|
||||
elif message_type == "private" and not config.message_buffer_enable_private:
|
||||
return False
|
||||
|
||||
|
||||
# 提取文本
|
||||
text = self.extract_text_from_message(message)
|
||||
if not text:
|
||||
return False
|
||||
|
||||
|
||||
# 检查是否应该跳过
|
||||
if self.should_skip_message(text):
|
||||
return False
|
||||
|
||||
|
||||
session_id = self.get_session_id(event_data)
|
||||
|
||||
|
||||
async with self.lock:
|
||||
# 获取或创建会话
|
||||
if session_id not in self.buffer_pool:
|
||||
self.buffer_pool[session_id] = BufferedSession(
|
||||
session_id=session_id,
|
||||
original_event=original_event
|
||||
)
|
||||
|
||||
self.buffer_pool[session_id] = BufferedSession(session_id=session_id, original_event=original_event)
|
||||
|
||||
session = self.buffer_pool[session_id]
|
||||
|
||||
|
||||
# 检查是否超过最大组件数量
|
||||
if len(session.messages) >= config.message_buffer_max_components:
|
||||
logger.info(f"会话 {session_id} 消息数量达到上限,强制合并")
|
||||
asyncio.create_task(self._force_merge_session(session_id))
|
||||
self.buffer_pool[session_id] = BufferedSession(
|
||||
session_id=session_id,
|
||||
original_event=original_event
|
||||
)
|
||||
self.buffer_pool[session_id] = BufferedSession(session_id=session_id, original_event=original_event)
|
||||
session = self.buffer_pool[session_id]
|
||||
|
||||
|
||||
# 添加文本消息
|
||||
session.messages.append(TextMessage(text=text))
|
||||
session.original_event = original_event # 更新事件
|
||||
|
||||
|
||||
# 取消之前的定时器
|
||||
await self._cancel_session_timers(session)
|
||||
|
||||
|
||||
# 设置新的延迟任务
|
||||
session.delay_task = asyncio.create_task(
|
||||
self._wait_and_start_merge(session_id)
|
||||
)
|
||||
|
||||
session.delay_task = asyncio.create_task(self._wait_and_start_merge(session_id))
|
||||
|
||||
logger.debug(f"文本消息已添加到缓冲器 {session_id}: {text[:50]}...")
|
||||
return True
|
||||
|
||||
|
||||
async def _cancel_session_timers(self, session: BufferedSession):
|
||||
"""取消会话的所有定时器"""
|
||||
for task_name in ['timer_task', 'delay_task']:
|
||||
for task_name in ["timer_task", "delay_task"]:
|
||||
task = getattr(session, task_name)
|
||||
if task and not task.done():
|
||||
task.cancel()
|
||||
@@ -189,12 +184,12 @@ class SimpleMessageBuffer:
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
setattr(session, task_name, None)
|
||||
|
||||
|
||||
async def _wait_and_start_merge(self, session_id: str):
|
||||
"""等待初始延迟后开始合并定时器"""
|
||||
config = features_manager.get_config()
|
||||
await asyncio.sleep(config.message_buffer_initial_delay)
|
||||
|
||||
|
||||
async with self.lock:
|
||||
session = self.buffer_pool.get(session_id)
|
||||
if session and session.messages:
|
||||
@@ -205,22 +200,20 @@ class SimpleMessageBuffer:
|
||||
await session.timer_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
# 设置合并定时器
|
||||
session.timer_task = asyncio.create_task(
|
||||
self._wait_and_merge(session_id)
|
||||
)
|
||||
|
||||
session.timer_task = asyncio.create_task(self._wait_and_merge(session_id))
|
||||
|
||||
async def _wait_and_merge(self, session_id: str):
|
||||
"""等待合并间隔后执行合并"""
|
||||
config = features_manager.get_config()
|
||||
await asyncio.sleep(config.message_buffer_interval)
|
||||
await self._merge_session(session_id)
|
||||
|
||||
|
||||
async def _force_merge_session(self, session_id: str):
|
||||
"""强制合并会话(不等待定时器)"""
|
||||
await self._merge_session(session_id, force=True)
|
||||
|
||||
|
||||
async def _merge_session(self, session_id: str, force: bool = False):
|
||||
"""合并会话中的消息"""
|
||||
async with self.lock:
|
||||
@@ -228,23 +221,23 @@ class SimpleMessageBuffer:
|
||||
if not session or not session.messages:
|
||||
self.buffer_pool.pop(session_id, None)
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
# 合并文本消息
|
||||
text_parts = []
|
||||
for msg in session.messages:
|
||||
if msg.text.strip():
|
||||
text_parts.append(msg.text.strip())
|
||||
|
||||
|
||||
if not text_parts:
|
||||
self.buffer_pool.pop(session_id, None)
|
||||
return
|
||||
|
||||
|
||||
merged_text = ",".join(text_parts) # 使用中文逗号连接
|
||||
message_count = len(session.messages)
|
||||
|
||||
|
||||
logger.info(f"合并会话 {session_id} 的 {message_count} 条文本消息: {merged_text[:100]}...")
|
||||
|
||||
|
||||
# 调用回调函数
|
||||
if self.merge_callback:
|
||||
try:
|
||||
@@ -254,67 +247,64 @@ class SimpleMessageBuffer:
|
||||
self.merge_callback(session_id, merged_text, session.original_event)
|
||||
except Exception as e:
|
||||
logger.error(f"消息合并回调执行失败: {e}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"合并会话 {session_id} 时出错: {e}")
|
||||
finally:
|
||||
# 清理会话
|
||||
await self._cancel_session_timers(session)
|
||||
self.buffer_pool.pop(session_id, None)
|
||||
|
||||
|
||||
async def flush_session(self, session_id: str):
|
||||
"""强制刷新指定会话的缓冲区"""
|
||||
await self._force_merge_session(session_id)
|
||||
|
||||
|
||||
async def flush_all(self):
|
||||
"""强制刷新所有会话的缓冲区"""
|
||||
session_ids = list(self.buffer_pool.keys())
|
||||
for session_id in session_ids:
|
||||
await self._force_merge_session(session_id)
|
||||
|
||||
|
||||
async def get_buffer_stats(self) -> Dict[str, Any]:
|
||||
"""获取缓冲区统计信息"""
|
||||
async with self.lock:
|
||||
stats = {
|
||||
"total_sessions": len(self.buffer_pool),
|
||||
"sessions": {}
|
||||
}
|
||||
|
||||
stats = {"total_sessions": len(self.buffer_pool), "sessions": {}}
|
||||
|
||||
for session_id, session in self.buffer_pool.items():
|
||||
stats["sessions"][session_id] = {
|
||||
"message_count": len(session.messages),
|
||||
"created_at": session.created_at,
|
||||
"age": time.time() - session.created_at
|
||||
"age": time.time() - session.created_at,
|
||||
}
|
||||
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
async def clear_expired_sessions(self, max_age: float = 300.0):
|
||||
"""清理过期的会话"""
|
||||
current_time = time.time()
|
||||
expired_sessions = []
|
||||
|
||||
|
||||
async with self.lock:
|
||||
for session_id, session in self.buffer_pool.items():
|
||||
if current_time - session.created_at > max_age:
|
||||
expired_sessions.append(session_id)
|
||||
|
||||
|
||||
for session_id in expired_sessions:
|
||||
logger.info(f"清理过期会话: {session_id}")
|
||||
await self._force_merge_session(session_id)
|
||||
|
||||
|
||||
async def shutdown(self):
|
||||
"""关闭消息缓冲器"""
|
||||
self._shutdown = True
|
||||
logger.info("正在关闭简化消息缓冲器...")
|
||||
|
||||
|
||||
# 刷新所有缓冲区
|
||||
await self.flush_all()
|
||||
|
||||
|
||||
# 确保所有任务都被取消
|
||||
async with self.lock:
|
||||
for session in list(self.buffer_pool.values()):
|
||||
await self._cancel_session_timers(session)
|
||||
self.buffer_pool.clear()
|
||||
|
||||
|
||||
logger.info("简化消息缓冲器已关闭")
|
||||
|
||||
@@ -35,7 +35,7 @@ class NoticeType: # 通知事件
|
||||
|
||||
class Notify:
|
||||
poke = "poke" # 戳一戳
|
||||
input_status = "input_status" # 正在输入
|
||||
input_status = "input_status" # 正在输入
|
||||
|
||||
class GroupBan:
|
||||
ban = "ban" # 禁言
|
||||
|
||||
@@ -87,7 +87,7 @@ class MessageHandler:
|
||||
"""
|
||||
logger.debug(f"群聊id: {group_id}, 用户id: {user_id}")
|
||||
logger.debug("开始检查聊天白名单/黑名单")
|
||||
|
||||
|
||||
# 使用新的权限管理器检查权限
|
||||
if group_id:
|
||||
if not features_manager.is_group_allowed(group_id):
|
||||
@@ -97,7 +97,7 @@ class MessageHandler:
|
||||
if not features_manager.is_private_allowed(user_id):
|
||||
logger.warning("私聊不在聊天权限范围内,消息被丢弃")
|
||||
return False
|
||||
|
||||
|
||||
# 检查全局禁止名单
|
||||
if not ignore_global_list and features_manager.is_user_banned(user_id):
|
||||
logger.warning("用户在全局黑名单中,消息被丢弃")
|
||||
@@ -184,7 +184,9 @@ class MessageHandler:
|
||||
# -------------------这里需要群信息吗?-------------------
|
||||
|
||||
# 获取群聊相关信息,在此单独处理group_name,因为默认发送的消息中没有
|
||||
fetched_group_info: dict = await get_group_info(self.get_server_connection(), raw_message.get("group_id"))
|
||||
fetched_group_info: dict = await get_group_info(
|
||||
self.get_server_connection(), raw_message.get("group_id")
|
||||
)
|
||||
group_name = ""
|
||||
if fetched_group_info.get("group_name"):
|
||||
group_name = fetched_group_info.get("group_name")
|
||||
@@ -262,16 +264,16 @@ class MessageHandler:
|
||||
# 检查消息类型是否启用缓冲
|
||||
message_type = raw_message.get("message_type")
|
||||
should_use_buffer = False
|
||||
|
||||
|
||||
if message_type == "group" and features_manager.is_message_buffer_group_enabled():
|
||||
should_use_buffer = True
|
||||
elif message_type == "private" and features_manager.is_message_buffer_private_enabled():
|
||||
should_use_buffer = True
|
||||
|
||||
|
||||
if should_use_buffer:
|
||||
logger.debug(f"尝试缓冲消息,消息类型: {message_type}, 用户: {user_info.user_id}")
|
||||
logger.debug(f"原始消息段: {raw_message.get('message', [])}")
|
||||
|
||||
|
||||
# 尝试添加到缓冲器
|
||||
buffered = await self.message_buffer.add_text_message(
|
||||
event_data={
|
||||
@@ -280,12 +282,9 @@ class MessageHandler:
|
||||
"group_id": group_info.group_id if group_info else None,
|
||||
},
|
||||
message=raw_message.get("message", []),
|
||||
original_event={
|
||||
"message_info": message_info,
|
||||
"raw_message": raw_message
|
||||
}
|
||||
original_event={"message_info": message_info, "raw_message": raw_message},
|
||||
)
|
||||
|
||||
|
||||
if buffered:
|
||||
logger.info(f"✅ 文本消息已成功缓冲: {user_info.user_id}")
|
||||
return None # 缓冲成功,不立即发送
|
||||
@@ -331,14 +330,18 @@ class MessageHandler:
|
||||
case RealMessageType.text:
|
||||
ret_seg = await self.handle_text_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.TEXT,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.TEXT, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("text处理失败")
|
||||
case RealMessageType.face:
|
||||
ret_seg = await self.handle_face_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.FACE,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.FACE, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("face处理失败或不支持")
|
||||
@@ -346,7 +349,9 @@ class MessageHandler:
|
||||
if not in_reply:
|
||||
ret_seg = await self.handle_reply_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.REPLY,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.REPLY, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message += ret_seg
|
||||
else:
|
||||
logger.warning("reply处理失败")
|
||||
@@ -354,7 +359,9 @@ class MessageHandler:
|
||||
logger.debug("开始处理图片消息段")
|
||||
ret_seg = await self.handle_image_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.IMAGE,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.IMAGE, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
logger.debug("图片处理成功,添加到消息段")
|
||||
else:
|
||||
@@ -363,7 +370,9 @@ class MessageHandler:
|
||||
case RealMessageType.record:
|
||||
ret_seg = await self.handle_record_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.RECORD,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.RECORD, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.clear()
|
||||
seg_message.append(ret_seg)
|
||||
break # 使得消息只有record消息
|
||||
@@ -372,7 +381,9 @@ class MessageHandler:
|
||||
case RealMessageType.video:
|
||||
ret_seg = await self.handle_video_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.VIDEO,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.VIDEO, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("video处理失败")
|
||||
@@ -383,33 +394,43 @@ class MessageHandler:
|
||||
raw_message.get("group_id"),
|
||||
)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.AT,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.AT, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("at处理失败")
|
||||
case RealMessageType.rps:
|
||||
ret_seg = await self.handle_rps_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.RPS,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.RPS, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("rps处理失败")
|
||||
case RealMessageType.dice:
|
||||
ret_seg = await self.handle_dice_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.DICE,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.DICE, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("dice处理失败")
|
||||
case RealMessageType.shake:
|
||||
ret_seg = await self.handle_shake_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.SHAKE,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.SHAKE, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("shake处理失败")
|
||||
case RealMessageType.share:
|
||||
print("\n\n哦哦哦噢噢噢哦哦你收到了一个超级无敌SHARE消息,快速速把你刚刚收到的消息截图发到MoFox-Bot群里!!!!\n\n")
|
||||
print(
|
||||
"\n\n哦哦哦噢噢噢哦哦你收到了一个超级无敌SHARE消息,快速速把你刚刚收到的消息截图发到MoFox-Bot群里!!!!\n\n"
|
||||
)
|
||||
logger.warning("暂时不支持链接解析")
|
||||
case RealMessageType.forward:
|
||||
messages = await self._get_forward_message(sub_message)
|
||||
@@ -422,18 +443,22 @@ class MessageHandler:
|
||||
else:
|
||||
logger.warning("转发消息处理失败")
|
||||
case RealMessageType.node:
|
||||
print("\n\n哦哦哦噢噢噢哦哦你收到了一个超级无敌NODE消息,快速速把你刚刚收到的消息截图发到MoFox-Bot群里!!!!\n\n")
|
||||
print(
|
||||
"\n\n哦哦哦噢噢噢哦哦你收到了一个超级无敌NODE消息,快速速把你刚刚收到的消息截图发到MoFox-Bot群里!!!!\n\n"
|
||||
)
|
||||
logger.warning("不支持转发消息节点解析")
|
||||
case RealMessageType.json:
|
||||
ret_seg = await self.handle_json_message(sub_message)
|
||||
if ret_seg:
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.JSON,plugin_name=PLUGIN_NAME,message_seg=ret_seg)
|
||||
await event_manager.trigger_event(
|
||||
NapcatEvent.ON_RECEIVED.JSON, plugin_name=PLUGIN_NAME, message_seg=ret_seg
|
||||
)
|
||||
seg_message.append(ret_seg)
|
||||
else:
|
||||
logger.warning("json处理失败")
|
||||
case _:
|
||||
logger.warning(f"未知消息类型: {sub_message_type}")
|
||||
|
||||
|
||||
logger.debug(f"handle_real_message完成,处理了{len(real_message)}个消息段,生成了{len(seg_message)}个seg")
|
||||
return seg_message
|
||||
|
||||
@@ -515,7 +540,9 @@ class MessageHandler:
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
member_info: dict = await get_member_info(self.get_server_connection(), group_id=group_id, user_id=qq_id)
|
||||
member_info: dict = await get_member_info(
|
||||
self.get_server_connection(), group_id=group_id, user_id=qq_id
|
||||
)
|
||||
if member_info:
|
||||
return Seg(type="text", data=f"@<{member_info.get('nickname')}:{member_info.get('user_id')}>")
|
||||
else:
|
||||
@@ -557,26 +584,26 @@ class MessageHandler:
|
||||
seg_data: Seg: 处理后的消息段
|
||||
"""
|
||||
message_data: dict = raw_message.get("data")
|
||||
|
||||
|
||||
# 添加详细的调试信息
|
||||
logger.debug(f"视频消息原始数据: {raw_message}")
|
||||
logger.debug(f"视频消息数据: {message_data}")
|
||||
|
||||
|
||||
# QQ视频消息可能包含url或filePath字段
|
||||
video_url = message_data.get("url")
|
||||
file_path = message_data.get("filePath") or message_data.get("file_path")
|
||||
|
||||
|
||||
logger.info(f"视频URL: {video_url}")
|
||||
logger.info(f"视频文件路径: {file_path}")
|
||||
|
||||
|
||||
# 优先使用本地文件路径,其次使用URL
|
||||
video_source = file_path if file_path else video_url
|
||||
|
||||
|
||||
if not video_source:
|
||||
logger.warning("视频消息缺少URL或文件路径信息")
|
||||
logger.warning(f"完整消息数据: {message_data}")
|
||||
return None
|
||||
|
||||
|
||||
try:
|
||||
# 检查是否为本地文件路径
|
||||
if file_path and Path(file_path).exists():
|
||||
@@ -584,45 +611,51 @@ class MessageHandler:
|
||||
# 直接读取本地文件
|
||||
with open(file_path, "rb") as f:
|
||||
video_data = f.read()
|
||||
|
||||
|
||||
# 将视频数据编码为base64用于传输
|
||||
video_base64 = base64.b64encode(video_data).decode('utf-8')
|
||||
video_base64 = base64.b64encode(video_data).decode("utf-8")
|
||||
logger.info(f"视频文件大小: {len(video_data) / (1024 * 1024):.2f} MB")
|
||||
|
||||
|
||||
# 返回包含详细信息的字典格式
|
||||
return Seg(type="video", data={
|
||||
"base64": video_base64,
|
||||
"filename": Path(file_path).name,
|
||||
"size_mb": len(video_data) / (1024 * 1024)
|
||||
})
|
||||
|
||||
return Seg(
|
||||
type="video",
|
||||
data={
|
||||
"base64": video_base64,
|
||||
"filename": Path(file_path).name,
|
||||
"size_mb": len(video_data) / (1024 * 1024),
|
||||
},
|
||||
)
|
||||
|
||||
elif video_url:
|
||||
logger.info(f"使用视频URL下载: {video_url}")
|
||||
# 使用video_handler下载视频
|
||||
video_downloader = get_video_downloader()
|
||||
download_result = await video_downloader.download_video(video_url)
|
||||
|
||||
|
||||
if not download_result["success"]:
|
||||
logger.warning(f"视频下载失败: {download_result.get('error', '未知错误')}")
|
||||
logger.warning(f"失败的URL: {video_url}")
|
||||
return None
|
||||
|
||||
|
||||
# 将视频数据编码为base64用于传输
|
||||
video_base64 = base64.b64encode(download_result["data"]).decode('utf-8')
|
||||
video_base64 = base64.b64encode(download_result["data"]).decode("utf-8")
|
||||
logger.info(f"视频下载成功,大小: {len(download_result['data']) / (1024 * 1024):.2f} MB")
|
||||
|
||||
|
||||
# 返回包含详细信息的字典格式
|
||||
return Seg(type="video", data={
|
||||
"base64": video_base64,
|
||||
"filename": download_result.get("filename", "video.mp4"),
|
||||
"size_mb": len(download_result["data"]) / (1024 * 1024),
|
||||
"url": video_url
|
||||
})
|
||||
|
||||
return Seg(
|
||||
type="video",
|
||||
data={
|
||||
"base64": video_base64,
|
||||
"filename": download_result.get("filename", "video.mp4"),
|
||||
"size_mb": len(download_result["data"]) / (1024 * 1024),
|
||||
"url": video_url,
|
||||
},
|
||||
)
|
||||
|
||||
else:
|
||||
logger.warning("既没有有效的本地文件路径,也没有有效的视频URL")
|
||||
return None
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"视频消息处理失败: {str(e)}")
|
||||
logger.error(f"视频源: {video_source}")
|
||||
@@ -666,9 +699,7 @@ class MessageHandler:
|
||||
Parameters:
|
||||
message_list: list: 转发消息列表
|
||||
"""
|
||||
handled_message, image_count = await self._handle_forward_message(
|
||||
message_list, 0
|
||||
)
|
||||
handled_message, image_count = await self._handle_forward_message(message_list, 0)
|
||||
handled_message: Seg
|
||||
image_count: int
|
||||
if not handled_message:
|
||||
@@ -678,15 +709,11 @@ class MessageHandler:
|
||||
if image_count < 5 and image_count > 0:
|
||||
# 处理图片数量小于5的情况,此时解析图片为base64
|
||||
logger.info("图片数量小于5,开始解析图片为base64")
|
||||
processed_message = await self._recursive_parse_image_seg(
|
||||
handled_message, True
|
||||
)
|
||||
processed_message = await self._recursive_parse_image_seg(handled_message, True)
|
||||
elif image_count > 0:
|
||||
logger.info("图片数量大于等于5,开始解析图片为占位符")
|
||||
# 处理图片数量大于等于5的情况,此时解析图片为占位符
|
||||
processed_message = await self._recursive_parse_image_seg(
|
||||
handled_message, False
|
||||
)
|
||||
processed_message = await self._recursive_parse_image_seg(handled_message, False)
|
||||
else:
|
||||
# 处理没有图片的情况,此时直接返回
|
||||
logger.info("没有图片,直接返回")
|
||||
@@ -697,21 +724,21 @@ class MessageHandler:
|
||||
return Seg(type="seglist", data=[forward_hint, processed_message])
|
||||
|
||||
async def handle_dice_message(self, raw_message: dict) -> Seg:
|
||||
message_data: dict = raw_message.get("data",{})
|
||||
res = message_data.get("result","")
|
||||
message_data: dict = raw_message.get("data", {})
|
||||
res = message_data.get("result", "")
|
||||
return Seg(type="text", data=f"[扔了一个骰子,点数是{res}]")
|
||||
|
||||
async def handle_shake_message(self, raw_message: dict) -> Seg:
|
||||
return Seg(type="text", data="[向你发送了窗口抖动,现在你的屏幕猛烈地震了一下!]")
|
||||
|
||||
|
||||
async def handle_json_message(self, raw_message: dict) -> Seg:
|
||||
message_data: str = raw_message.get("data","").get("data","")
|
||||
message_data: str = raw_message.get("data", "").get("data", "")
|
||||
res = json.loads(message_data)
|
||||
return Seg(type="json", data=res)
|
||||
|
||||
async def handle_rps_message(self, raw_message: dict) -> Seg:
|
||||
message_data: dict = raw_message.get("data",{})
|
||||
res = message_data.get("result","")
|
||||
message_data: dict = raw_message.get("data", {})
|
||||
res = message_data.get("result", "")
|
||||
if res == "1":
|
||||
shape = "布"
|
||||
elif res == "2":
|
||||
@@ -719,7 +746,7 @@ class MessageHandler:
|
||||
else:
|
||||
shape = "石头"
|
||||
return Seg(type="text", data=f"[发送了一个魔法猜拳表情,结果是:{shape}]")
|
||||
|
||||
|
||||
async def _recursive_parse_image_seg(self, seg_data: Seg, to_image: bool) -> Seg:
|
||||
# sourcery skip: merge-else-if-into-elif
|
||||
if to_image:
|
||||
@@ -898,22 +925,25 @@ class MessageHandler:
|
||||
# 从原始事件数据中提取信息
|
||||
message_info = original_event.get("message_info")
|
||||
raw_message = original_event.get("raw_message")
|
||||
|
||||
|
||||
if not message_info or not raw_message:
|
||||
logger.error("缓冲消息缺少必要信息")
|
||||
return
|
||||
|
||||
|
||||
# 创建合并后的消息段 - 将合并的文本转换为Seg格式
|
||||
from maim_message import Seg
|
||||
|
||||
merged_seg = Seg(type="text", data=merged_text)
|
||||
submit_seg = Seg(type="seglist", data=[merged_seg])
|
||||
|
||||
|
||||
# 创建新的消息ID
|
||||
import time
|
||||
|
||||
new_message_id = f"buffered-{message_info.message_id}-{int(time.time() * 1000)}"
|
||||
|
||||
|
||||
# 更新消息信息
|
||||
from maim_message import BaseMessageInfo, MessageBase
|
||||
|
||||
buffered_message_info = BaseMessageInfo(
|
||||
platform=message_info.platform,
|
||||
message_id=new_message_id,
|
||||
@@ -924,17 +954,17 @@ class MessageHandler:
|
||||
format_info=message_info.format_info,
|
||||
additional_config=message_info.additional_config,
|
||||
)
|
||||
|
||||
|
||||
# 创建MessageBase
|
||||
message_base = MessageBase(
|
||||
message_info=buffered_message_info,
|
||||
message_segment=submit_seg,
|
||||
raw_message=raw_message.get("raw_message", ""),
|
||||
)
|
||||
|
||||
|
||||
logger.info(f"发送缓冲合并消息到Maibot处理: {session_id}")
|
||||
await message_send_instance.message_send(message_base)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"发送缓冲消息失败: {e}", exc_info=True)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
from maim_message import MessageBase, Router
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
from ..config import global_config
|
||||
import time
|
||||
|
||||
@@ -5,6 +5,7 @@ import websockets as Server
|
||||
from typing import Tuple, Optional
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
from ..config import global_config
|
||||
@@ -121,7 +122,8 @@ class NoticeHandler:
|
||||
case NoticeType.Notify.input_status:
|
||||
from src.plugin_system.core.event_manager import event_manager
|
||||
from ...event_types import NapcatEvent
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.FRIEND_INPUT,plugin_name=PLUGIN_NAME)
|
||||
|
||||
await event_manager.trigger_event(NapcatEvent.ON_RECEIVED.FRIEND_INPUT, plugin_name=PLUGIN_NAME)
|
||||
case _:
|
||||
logger.warning(f"不支持的notify类型: {notice_type}.{sub_type}")
|
||||
case NoticeType.group_ban:
|
||||
@@ -200,7 +202,7 @@ class NoticeHandler:
|
||||
|
||||
self_id = raw_message.get("self_id")
|
||||
target_id = raw_message.get("target_id")
|
||||
|
||||
|
||||
# 防抖检查:如果是针对机器人的戳一戳,检查防抖时间
|
||||
if self_id == target_id:
|
||||
current_time = time.time()
|
||||
@@ -211,10 +213,10 @@ class NoticeHandler:
|
||||
if time_diff < debounce_seconds:
|
||||
logger.info(f"戳一戳防抖:用户 {user_id} 的戳一戳被忽略(距离上次戳一戳 {time_diff:.2f} 秒)")
|
||||
return None, None
|
||||
|
||||
|
||||
# 记录这次戳一戳的时间
|
||||
self.last_poke_time = current_time
|
||||
|
||||
|
||||
target_name: str = None
|
||||
raw_info: list = raw_message.get("raw_info")
|
||||
|
||||
@@ -244,7 +246,7 @@ class NoticeHandler:
|
||||
if features_manager.is_non_self_poke_ignored():
|
||||
logger.info("忽略不是针对自己的戳一戳消息")
|
||||
return None, None
|
||||
|
||||
|
||||
# 老实说这一步判定没啥意义,毕竟私聊是没有其他人之间的戳一戳,但是感觉可以有这个判定来强限制群聊环境
|
||||
if group_id:
|
||||
fetched_member_info: dict = await get_member_info(self.get_server_connection(), group_id, target_id)
|
||||
@@ -551,6 +553,4 @@ class NoticeHandler:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
|
||||
|
||||
notice_handler = NoticeHandler()
|
||||
|
||||
@@ -3,6 +3,7 @@ import time
|
||||
from typing import Dict
|
||||
from .config import global_config
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
|
||||
response_dict: Dict = {}
|
||||
@@ -15,6 +16,7 @@ async def get_response(request_id: str, timeout: int = 10) -> dict:
|
||||
logger.info(f"响应信息id: {request_id} 已从响应字典中取出")
|
||||
return response
|
||||
|
||||
|
||||
async def _get_response(request_id: str) -> dict:
|
||||
"""
|
||||
内部使用的获取响应函数,主要用于在需要时获取响应
|
||||
@@ -23,6 +25,7 @@ async def _get_response(request_id: str) -> dict:
|
||||
await asyncio.sleep(0.2)
|
||||
return response_dict.pop(request_id)
|
||||
|
||||
|
||||
async def put_response(response: dict):
|
||||
echo_id = response.get("echo")
|
||||
now_time = time.time()
|
||||
|
||||
@@ -17,6 +17,7 @@ from . import CommandType
|
||||
from .config import global_config
|
||||
from .response_pool import get_response
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
from .utils import get_image_format, convert_image_to_gif
|
||||
from .recv_handler.message_sending import message_send_instance
|
||||
@@ -68,9 +69,7 @@ class SendHandler:
|
||||
processed_message: list = []
|
||||
try:
|
||||
if user_info:
|
||||
processed_message = await self.handle_seg_recursive(
|
||||
message_segment, user_info
|
||||
)
|
||||
processed_message = await self.handle_seg_recursive(message_segment, user_info)
|
||||
except Exception as e:
|
||||
logger.error(f"处理消息时发生错误: {e}")
|
||||
return
|
||||
@@ -115,11 +114,7 @@ class SendHandler:
|
||||
message_info: BaseMessageInfo = raw_message_base.message_info
|
||||
message_segment: Seg = raw_message_base.message_segment
|
||||
group_info: Optional[GroupInfo] = message_info.group_info
|
||||
seg_data: Dict[str, Any] = (
|
||||
message_segment.data
|
||||
if isinstance(message_segment.data, dict)
|
||||
else {}
|
||||
)
|
||||
seg_data: Dict[str, Any] = message_segment.data if isinstance(message_segment.data, dict) else {}
|
||||
command_name: Optional[str] = seg_data.get("name")
|
||||
try:
|
||||
args = seg_data.get("args", {})
|
||||
@@ -130,9 +125,7 @@ class SendHandler:
|
||||
case CommandType.GROUP_BAN.name:
|
||||
command, args_dict = self.handle_ban_command(args, group_info)
|
||||
case CommandType.GROUP_WHOLE_BAN.name:
|
||||
command, args_dict = self.handle_whole_ban_command(
|
||||
args, group_info
|
||||
)
|
||||
command, args_dict = self.handle_whole_ban_command(args, group_info)
|
||||
case CommandType.GROUP_KICK.name:
|
||||
command, args_dict = self.handle_kick_command(args, group_info)
|
||||
case CommandType.SEND_POKE.name:
|
||||
@@ -140,15 +133,11 @@ class SendHandler:
|
||||
case CommandType.DELETE_MSG.name:
|
||||
command, args_dict = self.delete_msg_command(args)
|
||||
case CommandType.AI_VOICE_SEND.name:
|
||||
command, args_dict = self.handle_ai_voice_send_command(
|
||||
args, group_info
|
||||
)
|
||||
command, args_dict = self.handle_ai_voice_send_command(args, group_info)
|
||||
case CommandType.SET_EMOJI_LIKE.name:
|
||||
command, args_dict = self.handle_set_emoji_like_command(args)
|
||||
case CommandType.SEND_AT_MESSAGE.name:
|
||||
command, args_dict = self.handle_at_message_command(
|
||||
args, group_info
|
||||
)
|
||||
command, args_dict = self.handle_at_message_command(args, group_info)
|
||||
case CommandType.SEND_LIKE.name:
|
||||
command, args_dict = self.handle_send_like_command(args)
|
||||
case _:
|
||||
@@ -175,48 +164,38 @@ class SendHandler:
|
||||
logger.info("处理适配器命令中")
|
||||
message_info: BaseMessageInfo = raw_message_base.message_info
|
||||
message_segment: Seg = raw_message_base.message_segment
|
||||
seg_data: Dict[str, Any] = (
|
||||
message_segment.data
|
||||
if isinstance(message_segment.data, dict)
|
||||
else {}
|
||||
)
|
||||
|
||||
seg_data: Dict[str, Any] = message_segment.data if isinstance(message_segment.data, dict) else {}
|
||||
|
||||
try:
|
||||
action = seg_data.get("action")
|
||||
params = seg_data.get("params", {})
|
||||
request_id = seg_data.get("request_id")
|
||||
|
||||
|
||||
if not action:
|
||||
logger.error("适配器命令缺少action参数")
|
||||
await self.send_adapter_command_response(
|
||||
raw_message_base,
|
||||
{"status": "error", "message": "缺少action参数"},
|
||||
request_id
|
||||
raw_message_base, {"status": "error", "message": "缺少action参数"}, request_id
|
||||
)
|
||||
return
|
||||
|
||||
logger.info(f"执行适配器命令: {action}")
|
||||
|
||||
|
||||
# 直接向Napcat发送命令并获取响应
|
||||
response_task = asyncio.create_task(self.send_message_to_napcat(action, params))
|
||||
response = await response_task
|
||||
|
||||
# 发送响应回MaiBot
|
||||
await self.send_adapter_command_response(raw_message_base, response, request_id)
|
||||
|
||||
|
||||
if response.get("status") == "ok":
|
||||
logger.info(f"适配器命令 {action} 执行成功")
|
||||
else:
|
||||
logger.warning(f"适配器命令 {action} 执行失败,napcat返回:{str(response)}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理适配器命令时发生错误: {e}")
|
||||
error_response = {"status": "error", "message": str(e)}
|
||||
await self.send_adapter_command_response(
|
||||
raw_message_base,
|
||||
error_response,
|
||||
seg_data.get("request_id")
|
||||
)
|
||||
await self.send_adapter_command_response(raw_message_base, error_response, seg_data.get("request_id"))
|
||||
|
||||
def get_level(self, seg_data: Seg) -> int:
|
||||
if seg_data.type == "seglist":
|
||||
@@ -236,9 +215,7 @@ class SendHandler:
|
||||
payload = await self.process_message_by_type(seg_data, payload, user_info)
|
||||
return payload
|
||||
|
||||
async def process_message_by_type(
|
||||
self, seg: Seg, payload: list, user_info: UserInfo
|
||||
) -> list:
|
||||
async def process_message_by_type(self, seg: Seg, payload: list, user_info: UserInfo) -> list:
|
||||
# sourcery skip: reintroduce-else, swap-if-else-branches, use-named-expression
|
||||
new_payload = payload
|
||||
if seg.type == "reply":
|
||||
@@ -247,9 +224,7 @@ class SendHandler:
|
||||
return payload
|
||||
new_payload = self.build_payload(
|
||||
payload,
|
||||
await self.handle_reply_message(
|
||||
target_id if isinstance(target_id, str) else "", user_info
|
||||
),
|
||||
await self.handle_reply_message(target_id if isinstance(target_id, str) else "", user_info),
|
||||
True,
|
||||
)
|
||||
elif seg.type == "text":
|
||||
@@ -286,9 +261,7 @@ class SendHandler:
|
||||
new_payload = self.build_payload(payload, self.handle_file_message(file_path), False)
|
||||
return new_payload
|
||||
|
||||
def build_payload(
|
||||
self, payload: list, addon: dict | list, is_reply: bool = False
|
||||
) -> list:
|
||||
def build_payload(self, payload: list, addon: dict | list, is_reply: bool = False) -> list:
|
||||
# sourcery skip: for-append-to-extend, merge-list-append, simplify-generator
|
||||
"""构建发送的消息体"""
|
||||
if is_reply:
|
||||
@@ -324,13 +297,13 @@ class SendHandler:
|
||||
try:
|
||||
# 尝试通过 message_id 获取消息详情
|
||||
msg_info_response = await self.send_message_to_napcat("get_msg", {"message_id": int(id)})
|
||||
|
||||
|
||||
replied_user_id = None
|
||||
if msg_info_response and msg_info_response.get("status") == "ok":
|
||||
sender_info = msg_info_response.get("data", {}).get("sender")
|
||||
if sender_info:
|
||||
replied_user_id = sender_info.get("user_id")
|
||||
|
||||
|
||||
# 如果没有获取到被回复者的ID,则直接返回,不进行@
|
||||
if not replied_user_id:
|
||||
logger.warning(f"无法获取消息 {id} 的发送者信息,跳过 @")
|
||||
@@ -342,7 +315,7 @@ class SendHandler:
|
||||
# 在艾特后面添加一个空格
|
||||
text_seg = {"type": "text", "data": {"text": " "}}
|
||||
return [reply_seg, at_seg, text_seg]
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理引用回复并尝试@时出错: {e}")
|
||||
# 出现异常时,只发送普通的回复,避免程序崩溃
|
||||
@@ -404,6 +377,7 @@ class SendHandler:
|
||||
"type": "music",
|
||||
"data": {"type": "163", "id": song_id},
|
||||
}
|
||||
|
||||
def handle_videourl_message(self, video_url: str) -> dict:
|
||||
"""处理视频链接消息"""
|
||||
return {
|
||||
@@ -422,9 +396,7 @@ class SendHandler:
|
||||
"""处理删除消息命令"""
|
||||
return "delete_msg", {"message_id": args["message_id"]}
|
||||
|
||||
def handle_ban_command(
|
||||
self, args: Dict[str, Any], group_info: GroupInfo
|
||||
) -> Tuple[str, Dict[str, Any]]:
|
||||
def handle_ban_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]:
|
||||
"""处理封禁命令
|
||||
|
||||
Args:
|
||||
@@ -546,11 +518,7 @@ class SendHandler:
|
||||
|
||||
return (
|
||||
CommandType.SET_EMOJI_LIKE.value,
|
||||
{
|
||||
"message_id": message_id,
|
||||
"emoji_id": emoji_id,
|
||||
"set": set_like
|
||||
},
|
||||
{"message_id": message_id, "emoji_id": emoji_id, "set": set_like},
|
||||
)
|
||||
|
||||
def handle_send_like_command(self, args: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]:
|
||||
@@ -571,10 +539,7 @@ class SendHandler:
|
||||
|
||||
return (
|
||||
CommandType.SEND_LIKE.value,
|
||||
{
|
||||
"user_id": user_id,
|
||||
"times": times
|
||||
},
|
||||
{"user_id": user_id, "times": times},
|
||||
)
|
||||
|
||||
def handle_ai_voice_send_command(self, args: Dict[str, Any], group_info: GroupInfo) -> Tuple[str, Dict[str, Any]]:
|
||||
@@ -606,13 +571,13 @@ class SendHandler:
|
||||
async def send_message_to_napcat(self, action: str, params: dict) -> dict:
|
||||
request_uuid = str(uuid.uuid4())
|
||||
payload = json.dumps({"action": action, "params": params, "echo": request_uuid})
|
||||
|
||||
|
||||
# 获取当前连接
|
||||
connection = self.get_server_connection()
|
||||
if not connection:
|
||||
logger.error("没有可用的 Napcat 连接")
|
||||
return {"status": "error", "message": "no connection"}
|
||||
|
||||
|
||||
try:
|
||||
await connection.send(payload)
|
||||
response = await get_response(request_uuid)
|
||||
@@ -647,7 +612,7 @@ class SendHandler:
|
||||
) -> None:
|
||||
"""
|
||||
发送适配器命令响应回MaiBot
|
||||
|
||||
|
||||
Args:
|
||||
original_message: 原始消息
|
||||
response_data: 响应数据
|
||||
@@ -662,17 +627,13 @@ class SendHandler:
|
||||
|
||||
# 修改 message_segment 为 adapter_response 类型
|
||||
original_message.message_segment = Seg(
|
||||
type="adapter_response",
|
||||
data={
|
||||
"request_id": request_id,
|
||||
"response": response_data,
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
type="adapter_response",
|
||||
data={"request_id": request_id, "response": response_data, "timestamp": int(time.time() * 1000)},
|
||||
)
|
||||
|
||||
|
||||
await message_send_instance.message_send(original_message)
|
||||
logger.debug(f"已发送适配器命令响应,request_id: {request_id}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"发送适配器命令响应时出错: {e}")
|
||||
|
||||
@@ -708,4 +669,5 @@ class SendHandler:
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
send_handler = SendHandler()
|
||||
|
||||
@@ -8,6 +8,7 @@ import io
|
||||
|
||||
from .database import BanUser, db_manager
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
from .response_pool import get_response
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("video_handler")
|
||||
|
||||
|
||||
@@ -17,73 +18,69 @@ class VideoDownloader:
|
||||
def __init__(self, max_size_mb: int = 100, download_timeout: int = 60):
|
||||
self.max_size_mb = max_size_mb
|
||||
self.download_timeout = download_timeout
|
||||
self.supported_formats = {'.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv', '.webm', '.m4v'}
|
||||
|
||||
self.supported_formats = {".mp4", ".avi", ".mov", ".mkv", ".flv", ".wmv", ".webm", ".m4v"}
|
||||
|
||||
def is_video_url(self, url: str) -> bool:
|
||||
"""检查URL是否为视频文件"""
|
||||
try:
|
||||
# QQ视频URL可能没有扩展名,所以先检查Content-Type
|
||||
# 对于QQ视频,我们先假设是视频,稍后通过Content-Type验证
|
||||
|
||||
|
||||
# 检查URL中是否包含视频相关的关键字
|
||||
video_keywords = ['video', 'mp4', 'avi', 'mov', 'mkv', 'flv', 'wmv', 'webm', 'm4v']
|
||||
video_keywords = ["video", "mp4", "avi", "mov", "mkv", "flv", "wmv", "webm", "m4v"]
|
||||
url_lower = url.lower()
|
||||
|
||||
|
||||
# 如果URL包含视频关键字,认为是视频
|
||||
if any(keyword in url_lower for keyword in video_keywords):
|
||||
return True
|
||||
|
||||
|
||||
# 检查文件扩展名(传统方法)
|
||||
path = Path(url.split('?')[0]) # 移除查询参数
|
||||
path = Path(url.split("?")[0]) # 移除查询参数
|
||||
if path.suffix.lower() in self.supported_formats:
|
||||
return True
|
||||
|
||||
|
||||
# 对于QQ等特殊平台,URL可能没有扩展名
|
||||
# 我们允许这些URL通过,稍后通过HTTP头Content-Type验证
|
||||
qq_domains = ['qpic.cn', 'gtimg.cn', 'qq.com', 'tencent.com']
|
||||
qq_domains = ["qpic.cn", "gtimg.cn", "qq.com", "tencent.com"]
|
||||
if any(domain in url_lower for domain in qq_domains):
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
except:
|
||||
# 如果解析失败,默认允许尝试下载(稍后验证)
|
||||
return True
|
||||
|
||||
|
||||
def check_file_size(self, content_length: Optional[str]) -> bool:
|
||||
"""检查文件大小是否在允许范围内"""
|
||||
if content_length is None:
|
||||
return True # 无法获取大小时允许下载
|
||||
|
||||
|
||||
try:
|
||||
size_bytes = int(content_length)
|
||||
size_mb = size_bytes / (1024 * 1024)
|
||||
return size_mb <= self.max_size_mb
|
||||
except:
|
||||
return True
|
||||
|
||||
|
||||
async def download_video(self, url: str, filename: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
下载视频文件
|
||||
|
||||
|
||||
Args:
|
||||
url: 视频URL
|
||||
filename: 可选的文件名
|
||||
|
||||
|
||||
Returns:
|
||||
dict: 下载结果,包含success、data、filename、error等字段
|
||||
"""
|
||||
try:
|
||||
logger.info(f"开始下载视频: {url}")
|
||||
|
||||
|
||||
# 检查URL格式
|
||||
if not self.is_video_url(url):
|
||||
logger.warning(f"URL格式检查失败: {url}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": "不支持的视频格式",
|
||||
"url": url
|
||||
}
|
||||
|
||||
return {"success": False, "error": "不支持的视频格式", "url": url}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
# 先发送HEAD请求检查文件大小
|
||||
try:
|
||||
@@ -91,99 +88,87 @@ class VideoDownloader:
|
||||
if response.status != 200:
|
||||
logger.warning(f"HEAD请求失败,状态码: {response.status}")
|
||||
else:
|
||||
content_length = response.headers.get('Content-Length')
|
||||
content_length = response.headers.get("Content-Length")
|
||||
if not self.check_file_size(content_length):
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"视频文件过大,超过{self.max_size_mb}MB限制",
|
||||
"url": url
|
||||
"url": url,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"HEAD请求失败: {e},继续尝试下载")
|
||||
|
||||
|
||||
# 下载文件
|
||||
async with session.get(url, timeout=aiohttp.ClientTimeout(total=self.download_timeout)) as response:
|
||||
if response.status != 200:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"下载失败,HTTP状态码: {response.status}",
|
||||
"url": url
|
||||
}
|
||||
|
||||
return {"success": False, "error": f"下载失败,HTTP状态码: {response.status}", "url": url}
|
||||
|
||||
# 检查Content-Type是否为视频
|
||||
content_type = response.headers.get('Content-Type', '').lower()
|
||||
content_type = response.headers.get("Content-Type", "").lower()
|
||||
if content_type:
|
||||
# 检查是否为视频类型
|
||||
video_mime_types = [
|
||||
'video/', 'application/octet-stream',
|
||||
'application/x-msvideo', 'video/x-msvideo'
|
||||
"video/",
|
||||
"application/octet-stream",
|
||||
"application/x-msvideo",
|
||||
"video/x-msvideo",
|
||||
]
|
||||
is_video_content = any(mime in content_type for mime in video_mime_types)
|
||||
|
||||
|
||||
if not is_video_content:
|
||||
logger.warning(f"Content-Type不是视频格式: {content_type}")
|
||||
# 如果不是明确的视频类型,但可能是QQ的特殊格式,继续尝试
|
||||
if 'text/' in content_type or 'application/json' in content_type:
|
||||
if "text/" in content_type or "application/json" in content_type:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"URL返回的不是视频内容,Content-Type: {content_type}",
|
||||
"url": url
|
||||
"url": url,
|
||||
}
|
||||
|
||||
|
||||
# 再次检查Content-Length
|
||||
content_length = response.headers.get('Content-Length')
|
||||
content_length = response.headers.get("Content-Length")
|
||||
if not self.check_file_size(content_length):
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"视频文件过大,超过{self.max_size_mb}MB限制",
|
||||
"url": url
|
||||
}
|
||||
|
||||
return {"success": False, "error": f"视频文件过大,超过{self.max_size_mb}MB限制", "url": url}
|
||||
|
||||
# 读取文件内容
|
||||
video_data = await response.read()
|
||||
|
||||
|
||||
# 检查实际文件大小
|
||||
actual_size_mb = len(video_data) / (1024 * 1024)
|
||||
if actual_size_mb > self.max_size_mb:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"视频文件过大,实际大小: {actual_size_mb:.2f}MB",
|
||||
"url": url
|
||||
"url": url,
|
||||
}
|
||||
|
||||
|
||||
# 确定文件名
|
||||
if filename is None:
|
||||
filename = Path(url.split('?')[0]).name
|
||||
if not filename or '.' not in filename:
|
||||
filename = Path(url.split("?")[0]).name
|
||||
if not filename or "." not in filename:
|
||||
filename = "video.mp4"
|
||||
|
||||
|
||||
logger.info(f"视频下载成功: {filename}, 大小: {actual_size_mb:.2f}MB")
|
||||
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": video_data,
|
||||
"filename": filename,
|
||||
"size_mb": actual_size_mb,
|
||||
"url": url
|
||||
"url": url,
|
||||
}
|
||||
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "下载超时",
|
||||
"url": url
|
||||
}
|
||||
return {"success": False, "error": "下载超时", "url": url}
|
||||
except Exception as e:
|
||||
logger.error(f"下载视频时出错: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"url": url
|
||||
}
|
||||
return {"success": False, "error": str(e), "url": url}
|
||||
|
||||
|
||||
# 全局实例
|
||||
_video_downloader = None
|
||||
|
||||
|
||||
def get_video_downloader(max_size_mb: int = 100, download_timeout: int = 60) -> VideoDownloader:
|
||||
"""获取视频下载器实例"""
|
||||
global _video_downloader
|
||||
|
||||
@@ -2,38 +2,39 @@ import asyncio
|
||||
import websockets as Server
|
||||
from typing import Optional, Callable, Any
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("napcat_adapter")
|
||||
from .config import global_config
|
||||
|
||||
|
||||
class WebSocketManager:
|
||||
"""WebSocket 连接管理器,支持正向和反向连接"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.connection: Optional[Server.ServerConnection] = None
|
||||
self.server: Optional[Server.WebSocketServer] = None
|
||||
self.is_running = False
|
||||
self.reconnect_interval = 5 # 重连间隔(秒)
|
||||
self.max_reconnect_attempts = 10 # 最大重连次数
|
||||
|
||||
|
||||
async def start_connection(self, message_handler: Callable[[Server.ServerConnection], Any]) -> None:
|
||||
"""根据配置启动 WebSocket 连接"""
|
||||
mode = global_config.napcat_server.mode
|
||||
|
||||
|
||||
if mode == "reverse":
|
||||
await self._start_reverse_connection(message_handler)
|
||||
elif mode == "forward":
|
||||
await self._start_forward_connection(message_handler)
|
||||
else:
|
||||
raise ValueError(f"不支持的连接模式: {mode}")
|
||||
|
||||
|
||||
async def _start_reverse_connection(self, message_handler: Callable[[Server.ServerConnection], Any]) -> None:
|
||||
"""启动反向连接(作为服务器)"""
|
||||
host = global_config.napcat_server.host
|
||||
port = global_config.napcat_server.port
|
||||
|
||||
|
||||
logger.info(f"正在启动反向连接模式,监听地址: ws://{host}:{port}")
|
||||
|
||||
|
||||
async def handle_client(websocket, path=None):
|
||||
self.connection = websocket
|
||||
logger.info(f"Napcat 客户端已连接: {websocket.remote_address}")
|
||||
@@ -44,47 +45,42 @@ class WebSocketManager:
|
||||
finally:
|
||||
self.connection = None
|
||||
logger.info("Napcat 客户端已断开连接")
|
||||
|
||||
self.server = await Server.serve(
|
||||
handle_client,
|
||||
host,
|
||||
port,
|
||||
max_size=2**26
|
||||
)
|
||||
|
||||
self.server = await Server.serve(handle_client, host, port, max_size=2**26)
|
||||
self.is_running = True
|
||||
logger.info(f"反向连接服务器已启动,监听地址: ws://{host}:{port}")
|
||||
|
||||
|
||||
# 保持服务器运行
|
||||
await self.server.serve_forever()
|
||||
|
||||
|
||||
async def _start_forward_connection(self, message_handler: Callable[[Server.ServerConnection], Any]) -> None:
|
||||
"""启动正向连接(作为客户端)"""
|
||||
url = self._get_forward_url()
|
||||
logger.info(f"正在启动正向连接模式,目标地址: {url}")
|
||||
|
||||
|
||||
reconnect_count = 0
|
||||
|
||||
|
||||
while reconnect_count < self.max_reconnect_attempts:
|
||||
try:
|
||||
logger.info(f"尝试连接到 Napcat 服务器: {url}")
|
||||
|
||||
|
||||
# 准备连接参数
|
||||
connect_kwargs = {"max_size": 2**26}
|
||||
|
||||
|
||||
# 如果配置了访问令牌,添加到请求头
|
||||
if global_config.napcat_server.access_token:
|
||||
connect_kwargs["additional_headers"] = {
|
||||
"Authorization": f"Bearer {global_config.napcat_server.access_token}"
|
||||
}
|
||||
logger.info("已添加访问令牌到连接请求头")
|
||||
|
||||
|
||||
async with Server.connect(url, **connect_kwargs) as websocket:
|
||||
self.connection = websocket
|
||||
self.is_running = True
|
||||
reconnect_count = 0 # 重置重连计数
|
||||
|
||||
|
||||
logger.info(f"成功连接到 Napcat 服务器: {url}")
|
||||
|
||||
|
||||
try:
|
||||
await message_handler(websocket)
|
||||
except Server.exceptions.ConnectionClosed:
|
||||
@@ -94,11 +90,16 @@ class WebSocketManager:
|
||||
finally:
|
||||
self.connection = None
|
||||
self.is_running = False
|
||||
|
||||
except (Server.exceptions.ConnectionClosed, Server.exceptions.InvalidMessage, OSError, ConnectionRefusedError) as e:
|
||||
|
||||
except (
|
||||
Server.exceptions.ConnectionClosed,
|
||||
Server.exceptions.InvalidMessage,
|
||||
OSError,
|
||||
ConnectionRefusedError,
|
||||
) as e:
|
||||
reconnect_count += 1
|
||||
logger.warning(f"连接失败 ({reconnect_count}/{self.max_reconnect_attempts}): {e}")
|
||||
|
||||
|
||||
if reconnect_count < self.max_reconnect_attempts:
|
||||
logger.info(f"将在 {self.reconnect_interval} 秒后重试连接...")
|
||||
await asyncio.sleep(self.reconnect_interval)
|
||||
@@ -108,24 +109,24 @@ class WebSocketManager:
|
||||
except Exception as e:
|
||||
logger.error(f"正向连接时发生未知错误: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def _get_forward_url(self) -> str:
|
||||
"""获取正向连接的 URL"""
|
||||
config = global_config.napcat_server
|
||||
|
||||
|
||||
# 如果配置了完整的 URL,直接使用
|
||||
if config.url:
|
||||
return config.url
|
||||
|
||||
|
||||
# 否则根据 host 和 port 构建 URL
|
||||
host = config.host
|
||||
port = config.port
|
||||
return f"ws://{host}:{port}"
|
||||
|
||||
|
||||
async def stop_connection(self) -> None:
|
||||
"""停止 WebSocket 连接"""
|
||||
self.is_running = False
|
||||
|
||||
|
||||
if self.connection:
|
||||
try:
|
||||
await self.connection.close()
|
||||
@@ -134,7 +135,7 @@ class WebSocketManager:
|
||||
logger.error(f"关闭 WebSocket 连接时出错: {e}")
|
||||
finally:
|
||||
self.connection = None
|
||||
|
||||
|
||||
if self.server:
|
||||
try:
|
||||
self.server.close()
|
||||
@@ -144,15 +145,15 @@ class WebSocketManager:
|
||||
logger.error(f"关闭 WebSocket 服务器时出错: {e}")
|
||||
finally:
|
||||
self.server = None
|
||||
|
||||
|
||||
def get_connection(self) -> Optional[Server.ServerConnection]:
|
||||
"""获取当前的 WebSocket 连接"""
|
||||
return self.connection
|
||||
|
||||
|
||||
def is_connected(self) -> bool:
|
||||
"""检查是否已连接"""
|
||||
return self.connection is not None and self.is_running
|
||||
|
||||
|
||||
# 全局 WebSocket 管理器实例
|
||||
websocket_manager = WebSocketManager()
|
||||
websocket_manager = WebSocketManager()
|
||||
|
||||
@@ -23,7 +23,7 @@ def get_emoji_id(emoji_input: str) -> str | None:
|
||||
if emoji_input.isdigit() or (isinstance(emoji_input, str) and emoji_input.startswith("😊")):
|
||||
if emoji_input in qq_face:
|
||||
return emoji_input
|
||||
|
||||
|
||||
# 尝试从 "[表情:xxx]" 格式中提取
|
||||
match = re.search(r"\[表情:(.+?)\]", emoji_input)
|
||||
if match:
|
||||
@@ -36,7 +36,7 @@ def get_emoji_id(emoji_input: str) -> str | None:
|
||||
# value 的格式是 "[表情:xxx]"
|
||||
if f"[表情:{emoji_name}]" == value:
|
||||
return key
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -58,12 +58,17 @@ class SetEmojiLikeAction(BaseAction):
|
||||
match = re.search(r"\[表情:(.+?)\]", name)
|
||||
if match:
|
||||
emoji_options.append(match.group(1))
|
||||
|
||||
|
||||
action_parameters = {
|
||||
"emoji": f"要回应的表情,必须从以下表情中选择: {', '.join(emoji_options)}",
|
||||
"set": "是否设置回应 (True/False)",
|
||||
}
|
||||
action_require = ["当需要对消息贴表情时使用","当你想回应某条消息但又不想发文字时使用","不要连续发送,如果你已经贴表情包,就不要选择此动作","当你想用贴表情回应某条消息时使用"]
|
||||
action_require = [
|
||||
"当需要对消息贴表情时使用",
|
||||
"当你想回应某条消息但又不想发文字时使用",
|
||||
"不要连续发送,如果你已经贴表情包,就不要选择此动作",
|
||||
"当你想用贴表情回应某条消息时使用",
|
||||
]
|
||||
llm_judge_prompt = """
|
||||
判定是否需要使用贴表情动作的条件:
|
||||
1. 用户明确要求使用贴表情包
|
||||
@@ -87,10 +92,10 @@ class SetEmojiLikeAction(BaseAction):
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"执行了set_emoji_like动作:{self.action_name},失败: 未提供消息ID",
|
||||
action_done=False
|
||||
action_done=False,
|
||||
)
|
||||
return False, "未提供消息ID"
|
||||
|
||||
|
||||
emoji_input = self.action_data.get("emoji")
|
||||
set_like = self.action_data.get("set", True)
|
||||
|
||||
@@ -105,7 +110,7 @@ class SetEmojiLikeAction(BaseAction):
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"执行了set_emoji_like动作:{self.action_name},失败: 找不到表情: '{emoji_input}'",
|
||||
action_done=False
|
||||
action_done=False,
|
||||
)
|
||||
return False, f"找不到表情: '{emoji_input}'。请从可用列表中选择。"
|
||||
|
||||
@@ -115,7 +120,7 @@ class SetEmojiLikeAction(BaseAction):
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"执行了set_emoji_like动作:{self.action_name},失败: 未提供消息ID",
|
||||
action_done=False
|
||||
action_done=False,
|
||||
)
|
||||
return False, "未提供消息ID"
|
||||
|
||||
@@ -123,40 +128,36 @@ class SetEmojiLikeAction(BaseAction):
|
||||
# 使用适配器API发送贴表情命令
|
||||
response = await send_api.adapter_command_to_stream(
|
||||
action="set_msg_emoji_like",
|
||||
params={
|
||||
"message_id": message_id,
|
||||
"emoji_id": emoji_id,
|
||||
"set": set_like
|
||||
},
|
||||
params={"message_id": message_id, "emoji_id": emoji_id, "set": set_like},
|
||||
stream_id=self.chat_stream.stream_id if self.chat_stream else None,
|
||||
timeout=30.0,
|
||||
storage_message=False
|
||||
storage_message=False,
|
||||
)
|
||||
|
||||
|
||||
if response["status"] == "ok":
|
||||
logger.info(f"设置表情回应成功: {response}")
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"执行了set_emoji_like动作,{emoji_input},设置表情回应: {emoji_id}, 是否设置: {set_like}",
|
||||
action_done=True
|
||||
action_done=True,
|
||||
)
|
||||
return True, f"成功设置表情回应: {response.get('message', '成功')}"
|
||||
else:
|
||||
error_msg = response.get('message', '未知错误')
|
||||
error_msg = response.get("message", "未知错误")
|
||||
logger.error(f"设置表情回应失败: {error_msg}")
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"执行了set_emoji_like动作:{self.action_name},失败: {error_msg}",
|
||||
action_done=False
|
||||
action_done=False,
|
||||
)
|
||||
return False, f"设置表情回应失败: {error_msg}"
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"设置表情回应失败: {e}")
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"执行了set_emoji_like动作:{self.action_name},失败: {e}",
|
||||
action_done=False
|
||||
action_done=False,
|
||||
)
|
||||
return False, f"设置表情回应失败: {e}"
|
||||
|
||||
@@ -174,10 +175,7 @@ class SetEmojiLikePlugin(BasePlugin):
|
||||
config_file_name: str = "config.toml" # 配置文件名
|
||||
|
||||
# 配置节描述
|
||||
config_section_descriptions = {
|
||||
"plugin": "插件基本信息",
|
||||
"components": "插件组件"
|
||||
}
|
||||
config_section_descriptions = {"plugin": "插件基本信息", "components": "插件组件"}
|
||||
|
||||
# 配置Schema定义
|
||||
config_schema: dict = {
|
||||
@@ -189,7 +187,7 @@ class SetEmojiLikePlugin(BasePlugin):
|
||||
},
|
||||
"components": {
|
||||
"action_set_emoji_like": ConfigField(type=bool, default=True, description="是否启用设置表情回应功能"),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
|
||||
@@ -180,7 +180,7 @@ qq_face: dict = {
|
||||
"394": "[表情:新年大龙]",
|
||||
"395": "[表情:略略略]",
|
||||
"396": "[表情:龙年快乐]",
|
||||
"424":" [表情:按钮]",
|
||||
"424": " [表情:按钮]",
|
||||
"😊": "[表情:嘿嘿]",
|
||||
"😌": "[表情:羞涩]",
|
||||
"😚": "[ 表情:亲亲]",
|
||||
|
||||
Reference in New Issue
Block a user