修复代码格式和文件名大小写问题

This commit is contained in:
Windpicker-owo
2025-08-31 20:50:17 +08:00
parent df29014e41
commit 8149731925
218 changed files with 6913 additions and 8257 deletions

View File

@@ -1 +1 @@
PLUGIN_NAME = "napcat_adapter"
PLUGIN_NAME = "napcat_adapter"

View File

@@ -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)

View File

@@ -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): ...

View File

@@ -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

View File

@@ -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")

View File

@@ -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"

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -53,8 +53,6 @@ class MaiBotServerConfig(ConfigBase):
"""MaiMCore的端口号"""
@dataclass
class VoiceConfig(ConfigBase):
use_tts: bool = False

View File

@@ -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}")

View File

@@ -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("简化消息缓冲器已关闭")

View File

@@ -35,7 +35,7 @@ class NoticeType: # 通知事件
class Notify:
poke = "poke" # 戳一戳
input_status = "input_status" # 正在输入
input_status = "input_status" # 正在输入
class GroupBan:
ban = "ban" # 禁言

View File

@@ -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)

View File

@@ -1,4 +1,5 @@
from src.common.logger import get_logger
logger = get_logger("napcat_adapter")
from maim_message import MessageBase, Router

View File

@@ -1,4 +1,5 @@
from src.common.logger import get_logger
logger = get_logger("napcat_adapter")
from ..config import global_config
import time

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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()