commit
f3bcdb2986
2
main.py
2
main.py
|
|
@ -52,7 +52,7 @@ async def main():
|
||||||
|
|
||||||
async def napcat_server():
|
async def napcat_server():
|
||||||
logger.info("正在启动adapter...")
|
logger.info("正在启动adapter...")
|
||||||
async with Server.serve(message_recv, global_config.napcat_server.host, global_config.napcat_server.port) as server:
|
async with Server.serve(message_recv, global_config.napcat_server.host, global_config.napcat_server.port, max_size=2**26) as server:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Adapter已启动,监听地址: ws://{global_config.napcat_server.host}:{global_config.napcat_server.port}"
|
f"Adapter已启动,监听地址: ws://{global_config.napcat_server.host}:{global_config.napcat_server.port}"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "MaiBotNapcatAdapter"
|
name = "MaiBotNapcatAdapter"
|
||||||
version = "0.4.4"
|
version = "0.4.7"
|
||||||
description = "A MaiBot adapter for Napcat"
|
description = "A MaiBot adapter for Napcat"
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
|
|
||||||
|
|
@ -123,14 +123,26 @@ class DatabaseManager:
|
||||||
其同时还是简化版的更新方式。
|
其同时还是简化版的更新方式。
|
||||||
"""
|
"""
|
||||||
with Session(self.engine) as session:
|
with Session(self.engine) as session:
|
||||||
db_record = DB_BanUser(
|
# 检查记录是否已存在
|
||||||
user_id=ban_record.user_id, group_id=ban_record.group_id, lift_time=ban_record.lift_time
|
statement = select(DB_BanUser).where(
|
||||||
|
DB_BanUser.user_id == ban_record.user_id, DB_BanUser.group_id == ban_record.group_id
|
||||||
)
|
)
|
||||||
session.add(db_record)
|
existing_record = session.exec(statement).first()
|
||||||
|
if existing_record:
|
||||||
|
# 如果记录已存在,更新 lift_time
|
||||||
|
existing_record.lift_time = ban_record.lift_time
|
||||||
|
session.add(existing_record)
|
||||||
|
logger.debug(f"更新禁言记录: {ban_record}")
|
||||||
|
else:
|
||||||
|
# 如果记录不存在,创建新记录
|
||||||
|
db_record = DB_BanUser(
|
||||||
|
user_id=ban_record.user_id, group_id=ban_record.group_id, lift_time=ban_record.lift_time
|
||||||
|
)
|
||||||
|
session.add(db_record)
|
||||||
|
logger.debug(f"创建新禁言记录: {ban_record}")
|
||||||
session.commit()
|
session.commit()
|
||||||
logger.debug(f"创建/更新禁言记录: {ban_record}")
|
|
||||||
|
|
||||||
def delete_ban_record(self, ban_record: BanUser) -> bool:
|
def delete_ban_record(self, ban_record: BanUser):
|
||||||
"""
|
"""
|
||||||
删除特定用户在特定群组中的禁言记录。
|
删除特定用户在特定群组中的禁言记录。
|
||||||
一个简化版本的删除方式,防止 update_ban_record 方法的复杂性。
|
一个简化版本的删除方式,防止 update_ban_record 方法的复杂性。
|
||||||
|
|
|
||||||
|
|
@ -84,4 +84,4 @@ class CommandType(Enum):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
ACCEPT_FORMAT = ["text", "image", "emoji", "reply", "voice", "command"]
|
ACCEPT_FORMAT = ["text", "image", "emoji", "reply", "voice", "command", "voiceurl", "music", "videourl", "file"]
|
||||||
|
|
|
||||||
|
|
@ -60,20 +60,6 @@ class MessageHandler:
|
||||||
bool: 是否允许聊天
|
bool: 是否允许聊天
|
||||||
"""
|
"""
|
||||||
logger.debug(f"群聊id: {group_id}, 用户id: {user_id}")
|
logger.debug(f"群聊id: {group_id}, 用户id: {user_id}")
|
||||||
if global_config.chat.ban_qq_bot and group_id and not ignore_bot:
|
|
||||||
logger.debug("开始判断是否为机器人")
|
|
||||||
member_info = await get_member_info(self.server_connection, group_id, user_id)
|
|
||||||
if member_info:
|
|
||||||
is_bot = member_info.get("is_robot")
|
|
||||||
if is_bot is None:
|
|
||||||
logger.warning("无法获取用户是否为机器人,默认为不是但是不进行更新")
|
|
||||||
else:
|
|
||||||
if is_bot:
|
|
||||||
logger.warning("QQ官方机器人消息拦截已启用,消息被丢弃,新机器人加入拦截名单")
|
|
||||||
self.bot_id_list[user_id] = True
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
self.bot_id_list[user_id] = False
|
|
||||||
logger.debug("开始检查聊天白名单/黑名单")
|
logger.debug("开始检查聊天白名单/黑名单")
|
||||||
if group_id:
|
if group_id:
|
||||||
if global_config.chat.group_list_type == "whitelist" and group_id not in global_config.chat.group_list:
|
if global_config.chat.group_list_type == "whitelist" and group_id not in global_config.chat.group_list:
|
||||||
|
|
@ -92,6 +78,22 @@ class MessageHandler:
|
||||||
if user_id in global_config.chat.ban_user_id and not ignore_global_list:
|
if user_id in global_config.chat.ban_user_id and not ignore_global_list:
|
||||||
logger.warning("用户在全局黑名单中,消息被丢弃")
|
logger.warning("用户在全局黑名单中,消息被丢弃")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if global_config.chat.ban_qq_bot and group_id and not ignore_bot:
|
||||||
|
logger.debug("开始判断是否为机器人")
|
||||||
|
member_info = await get_member_info(self.server_connection, group_id, user_id)
|
||||||
|
if member_info:
|
||||||
|
is_bot = member_info.get("is_robot")
|
||||||
|
if is_bot is None:
|
||||||
|
logger.warning("无法获取用户是否为机器人,默认为不是但是不进行更新")
|
||||||
|
else:
|
||||||
|
if is_bot:
|
||||||
|
logger.warning("QQ官方机器人消息拦截已启用,消息被丢弃,新机器人加入拦截名单")
|
||||||
|
self.bot_id_list[user_id] = True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.bot_id_list[user_id] = False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def handle_raw_message(self, raw_message: dict) -> None:
|
async def handle_raw_message(self, raw_message: dict) -> None:
|
||||||
|
|
@ -422,8 +424,14 @@ class MessageHandler:
|
||||||
"""
|
"""
|
||||||
message_data: dict = raw_message.get("data")
|
message_data: dict = raw_message.get("data")
|
||||||
file: str = message_data.get("file")
|
file: str = message_data.get("file")
|
||||||
|
if not file:
|
||||||
|
logger.warning("语音消息缺少文件信息")
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
record_detail = await get_record_detail(self.server_connection, file)
|
record_detail = await get_record_detail(self.server_connection, file)
|
||||||
|
if not record_detail:
|
||||||
|
logger.warning("获取语音消息详情失败")
|
||||||
|
return None
|
||||||
audio_base64: str = record_detail.get("base64")
|
audio_base64: str = record_detail.get("base64")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"语音消息处理失败: {str(e)}")
|
logger.error(f"语音消息处理失败: {str(e)}")
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,19 @@ response_dict: Dict = {}
|
||||||
response_time_dict: Dict = {}
|
response_time_dict: Dict = {}
|
||||||
|
|
||||||
|
|
||||||
async def get_response(request_id: str) -> dict:
|
async def get_response(request_id: str, timeout: int = 10) -> dict:
|
||||||
retry_count = 0
|
response = await asyncio.wait_for(_get_response(request_id), timeout)
|
||||||
max_retries = 50 # 10秒超时
|
|
||||||
while request_id not in response_dict:
|
|
||||||
retry_count += 1
|
|
||||||
if retry_count >= max_retries:
|
|
||||||
raise TimeoutError(f"请求超时,未收到响应,request_id: {request_id}")
|
|
||||||
await asyncio.sleep(0.2)
|
|
||||||
response = response_dict.pop(request_id)
|
|
||||||
_ = response_time_dict.pop(request_id)
|
_ = response_time_dict.pop(request_id)
|
||||||
logger.trace(f"响应信息id: {request_id} 已从响应字典中取出")
|
logger.trace(f"响应信息id: {request_id} 已从响应字典中取出")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
async def _get_response(request_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
内部使用的获取响应函数,主要用于在需要时获取响应
|
||||||
|
"""
|
||||||
|
while request_id not in response_dict:
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
return response_dict.pop(request_id)
|
||||||
|
|
||||||
async def put_response(response: dict):
|
async def put_response(response: dict):
|
||||||
echo_id = response.get("echo")
|
echo_id = response.get("echo")
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,9 @@ class SendHandler:
|
||||||
elif seg.type == "videourl":
|
elif seg.type == "videourl":
|
||||||
video_url = seg.data
|
video_url = seg.data
|
||||||
new_payload = self.build_payload(payload, self.handle_videourl_message(video_url), False)
|
new_payload = self.build_payload(payload, self.handle_videourl_message(video_url), False)
|
||||||
|
elif seg.type == "file":
|
||||||
|
file_path = seg.data
|
||||||
|
new_payload = self.build_payload(payload, self.handle_file_message(file_path), False)
|
||||||
return new_payload
|
return new_payload
|
||||||
|
|
||||||
def build_payload(self, payload: list, addon: dict, is_reply: bool = False) -> list:
|
def build_payload(self, payload: list, addon: dict, is_reply: bool = False) -> list:
|
||||||
|
|
@ -261,6 +264,13 @@ class SendHandler:
|
||||||
"data": {"file": video_url},
|
"data": {"file": video_url},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def handle_file_message(self, file_path: str) -> dict:
|
||||||
|
"""处理文件消息"""
|
||||||
|
return {
|
||||||
|
"type": "file",
|
||||||
|
"data": {"file": f"file://{file_path}"},
|
||||||
|
}
|
||||||
|
|
||||||
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]]:
|
||||||
"""处理封禁命令
|
"""处理封禁命令
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ async def get_message_detail(websocket: Server.ServerConnection, message_id: Uni
|
||||||
payload = json.dumps({"action": "get_msg", "params": {"message_id": message_id}, "echo": request_uuid})
|
payload = json.dumps({"action": "get_msg", "params": {"message_id": message_id}, "echo": request_uuid})
|
||||||
try:
|
try:
|
||||||
await websocket.send(payload)
|
await websocket.send(payload)
|
||||||
response: dict = await get_response(request_uuid)
|
response: dict = await get_response(request_uuid, 30) # 增加超时时间到30秒
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
logger.error(f"获取消息详情超时,消息ID: {message_id}")
|
logger.error(f"获取消息详情超时,消息ID: {message_id}")
|
||||||
return None
|
return None
|
||||||
|
|
@ -242,7 +242,7 @@ async def get_record_detail(
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await websocket.send(payload)
|
await websocket.send(payload)
|
||||||
response: dict = await get_response(request_uuid)
|
response: dict = await get_response(request_uuid, 30) # 增加超时时间到30秒
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
logger.error(f"获取语音消息详情超时,文件: {file}, 文件ID: {file_id}")
|
logger.error(f"获取语音消息详情超时,文件: {file}, 文件ID: {file_id}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue