Compare commits

...

12 Commits
0.6.0 ... main

Author SHA1 Message Date
UnCLAS-Prommer 4f928b42c1
版本号更新 2026-01-19 10:08:37 +08:00
UnCLAS-Prommer e58c13c3b1
Merge pull request #78 from tcmofashi/dev
feat: 支持maim_message API mode(未验证)
2026-01-18 21:42:44 +08:00
UnCLAS-Prommer 09173eb135
Merge pull request #77 from HyperSharkawa/dev
fix: 修复不正确的贴表情action
2026-01-16 23:37:21 +08:00
tcmofashi b165eff6b9 req: 修复依赖 2026-01-16 15:31:04 +00:00
tcmofashi 866637b658 feat: 增加maim_message自定义logger 2026-01-16 09:31:09 +00:00
tcmofashi bbff5702a5 Merge branch 'dev' of github.com:Mai-with-u/MaiBot-Napcat-Adapter into dev 2026-01-16 07:44:37 +00:00
tcmofashi ce8f05361d Merge remote-tracking branch 'remote/main'
- 添加配置文件监控和配置变更时自动重启WebSocket服务器功能
- 修复main.py中router引用问题

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 07:41:36 +00:00
tcmofashi 58d7be7f08 feat: 支持maim_message API mode 2026-01-16 07:21:15 +00:00
UnCLAS-Prommer e0d4b6ee55
删除uvlock 2026-01-16 00:27:20 +08:00
UnCLAS-Prommer 0af3559506
删除uvlock 2026-01-16 00:26:20 +08:00
UnCLAS-Prommer c6f892def0
移除uvlock 2026-01-15 19:25:51 +08:00
sharkie 29e852dcd0 fix: 修复不正确的贴表情action 2026-01-10 07:07:10 +08:00
10 changed files with 180 additions and 1115 deletions

4
.gitignore vendored
View File

@ -277,4 +277,6 @@ config.toml.back
test
data/NapcatAdapter.db
data/NapcatAdapter.db-shm
data/NapcatAdapter.db-wal
data/NapcatAdapter.db-wal
uv.lock

View File

@ -101,7 +101,7 @@ Seg.data: Dict[str, Any] = {
### 给消息贴表情
```python
Seg.data: Dict[str, Any] = {
"name": "MESSAGE_LIKE",
"name": "SET_MSG_EMOJI_LIKE",
"args": {
"message_id": "消息ID",
"emoji_id": "表情ID"

View File

@ -116,8 +116,7 @@ async def main():
logger.info("正在重启WebSocket服务器...")
await asyncio.sleep(1) # 等待1秒后重启
message_send_instance.maibot_router = router
_ = await asyncio.gather(napcat_with_restart(), mmc_start_com(), message_process(), check_timeout_response())
def check_napcat_server_token(conn, request):

View File

@ -1,12 +1,13 @@
[project]
name = "MaiBotNapcatAdapter"
version = "0.5.5"
version = "0.7.0"
description = "A MaiBot adapter for Napcat"
requires-python = ">=3.10"
dependencies = [
"aiohttp>=3.13.2",
"asyncio>=4.0.0",
"loguru>=0.7.3",
"maim-message>=0.5.7",
"maim-message>=0.6.2",
"pillow>=12.0.0",
"requests>=2.32.5",
"rich>=14.2.0",
@ -34,7 +35,7 @@ select = [
"B", # flake8-bugbear
]
ignore = ["E711","E501"]
ignore = ["E711", "E501"]
[tool.ruff.format]
docstring-code-format = true

View File

@ -16,7 +16,7 @@ class CommandType(Enum):
SEND_POKE = "send_poke" # 戳一戳
DELETE_MSG = "delete_msg" # 撤回消息
AI_VOICE_SEND = "send_group_ai_record" # 发送群AI语音
MESSAGE_LIKE = "message_like" # 给消息贴表情
SET_MSG_EMOJI_LIKE = "set_msg_emoji_like" # 给消息贴表情
SET_QQ_PROFILE = "set_qq_profile" # 设置账号信息
# 查询类命令

View File

@ -46,6 +46,15 @@ class MaiBotServerConfig(ConfigBase):
port: int = 8000
"""MaiMCore的端口号"""
enable_api_server: bool = False
"""是否启用API-Server模式连接"""
base_url: str = ""
"""API-Server连接地址 (ws://ipp:port/path)"""
api_key: str = ""
"""API Key (仅在enable_api_server为True时使用)"""
@dataclass
class ChatConfig(ConfigBase):

View File

@ -1,24 +1,164 @@
from maim_message import Router, RouteConfig, TargetConfig
from maim_message import Router, RouteConfig, TargetConfig, MessageBase
from .config import global_config
from .logger import logger, custom_logger
from .send_handler.main_send_handler import send_handler
from .recv_handler.message_sending import message_send_instance
from maim_message.client import create_client_config, WebSocketClient
from maim_message.message import APIMessageBase
from typing import Dict, Any
import importlib.metadata
route_config = RouteConfig(
route_config={
global_config.maibot_server.platform_name: TargetConfig(
url=f"ws://{global_config.maibot_server.host}:{global_config.maibot_server.port}/ws",
token=None,
# 检查 maim_message 版本是否支持 MessageConverter (>= 0.6.2)
try:
maim_message_version = importlib.metadata.version("maim_message")
version_int = [int(x) for x in maim_message_version.split(".")]
HAS_MESSAGE_CONVERTER = version_int >= [0, 6, 2]
except (importlib.metadata.PackageNotFoundError, ValueError):
HAS_MESSAGE_CONVERTER = False
# router = Router(route_config, custom_logger)
# router will be initialized in mmc_start_com
router = None
class APIServerWrapper:
"""
Wrapper to make WebSocketClient compatible with legacy Router interface
"""
def __init__(self, client: WebSocketClient):
self.client = client
self.platform = global_config.maibot_server.platform_name
def register_class_handler(self, handler):
# In API Server mode, we register the on_message callback in config,
# but here we might need to bridge it if the handler structure is different.
# However, WebSocketClient config handles on_message.
# The legacy Router.register_class_handler registers a handler for received messages.
# We need to adapt the callback style.
pass
async def send_message(self, message: MessageBase) -> bool:
# 使用 MessageConverter 转换 Legacy MessageBase 到 APIMessageBase
# 接收场景Adapter 收到来自 Napcat 的消息,发送给 MaiMBot
# group_info/user_info 是消息发送者信息,放入 sender_info
from maim_message import MessageConverter
api_message = MessageConverter.to_api_receive(
message=message,
api_key=global_config.maibot_server.api_key,
platform=message.message_info.platform or self.platform,
)
}
)
router = Router(route_config, custom_logger)
return await self.client.send_message(api_message)
async def send_custom_message(self, platform: str, message_type_name: str, message: Dict) -> bool:
return await self.client.send_custom_message(message_type_name, message)
async def run(self):
await self.client.start()
await self.client.connect()
async def stop(self):
await self.client.stop()
# Global variable to hold the communication object (Router or Wrapper)
router = None
async def _legacy_message_handler_adapter(message: APIMessageBase, metadata: dict):
# Adapter to call the legacy handler with dict as expected by main_send_handler
# send_handler.handle_message expects a dict.
# We need to convert APIMessageBase back to dict legacy format if possible.
# Or check what handle_message expects.
# main_send_handler.py: handle_message takes raw_message_base_dict: dict
# and does MessageBase.from_dict(raw_message_base_dict).
# So we need to serialize APIMessageBase to a dict that looks like legacy MessageBase dict.
# This might be tricky if structures diverged.
# Let's try `to_dict()` if available, otherwise construct it.
# Inspecting APIMessageBase structure from docs:
# APIMessageBase has message_info, message_segment, message_dim.
# Legacy MessageBase has message_info, message_segment.
# We can try to construct the dict.
data = {
"message_info": {
"id": message.message_info.message_id,
"timestamp": message.message_info.time,
"group_info": {}, # Fill if available
"user_info": {}, # Fill if available
},
"message_segment": {
"type": message.message_segment.type,
"data": message.message_segment.data
}
}
# Note: This is an approximation. Ideally we should check strict compatibility.
# However, for the adapter -> bot direction (sending to napcat),
# the bot sends messages to adapter? No, Adapter sends to Bot?
# mmc_com_layer seems to be for Adapter talking to MaiBot Core.
# recv_handler/message_sending.py uses this router to send TO MaiBot.
# The `register_class_handler` in `mmc_start_com` suggests MaiBot sends messages TO Adapter?
# Wait, `send_handler.handle_message` seems to be handling messages RECEIVED FROM MaiBot.
# So `router` is bidirectional.
# If explicit to_dict is needed:
await send_handler.handle_message(data)
async def mmc_start_com():
logger.info("正在连接MaiBot")
router.register_class_handler(send_handler.handle_message)
await router.run()
global router
config = global_config.maibot_server
if config.enable_api_server and HAS_MESSAGE_CONVERTER:
logger.info("使用 API-Server 模式连接 MaiBot")
# Create legacy adapter handler
# We need to define the on_message callback here to bridge to send_handler
async def on_message_bridge(message: APIMessageBase, metadata: Dict[str, Any]):
# 使用 MessageConverter 转换 APIMessageBase 到 Legacy MessageBase
# 发送场景:收到来自 MaiMBot 的回复消息,需要发送给 Napcat
# receiver_info 包含消息接收者信息,需要提取到 group_info/user_info
try:
from maim_message import MessageConverter
legacy_message = MessageConverter.from_api_send(message)
msg_dict = legacy_message.to_dict()
await send_handler.handle_message(msg_dict)
except Exception as e:
logger.error(f"消息桥接转换失败: {e}")
import traceback
logger.error(traceback.format_exc())
client_config = create_client_config(
url=config.base_url,
api_key=config.api_key,
platform=config.platform_name,
on_message=on_message_bridge,
custom_logger=custom_logger # 传入自定义logger
)
client = WebSocketClient(client_config)
router = APIServerWrapper(client)
message_send_instance.maibot_router = router
await router.run()
else:
logger.info("使用 Legacy WebSocket 模式连接 MaiBot")
route_config = RouteConfig(
route_config={
config.platform_name: TargetConfig(
url=f"ws://{config.host}:{config.port}/ws",
token=None,
)
}
)
router = Router(route_config, custom_logger)
router.register_class_handler(send_handler.handle_message)
message_send_instance.maibot_router = router
await router.run()
async def mmc_stop_com():
await router.stop()
if router:
await router.stop()

View File

@ -386,8 +386,8 @@ class SendCommandHandleClass:
)
@staticmethod
@register_command(CommandType.MESSAGE_LIKE, require_group=False)
def handle_message_like_command(args: Dict[str, Any], group_info: Optional[GroupInfo]) -> Tuple[str, Dict[str, Any]]:
@register_command(CommandType.SET_MSG_EMOJI_LIKE, require_group=False)
def handle_set_msg_emoji_like_command(args: Dict[str, Any], group_info: Optional[GroupInfo]) -> Tuple[str, Dict[str, Any]]:
"""处理给消息贴表情命令
Args:
@ -415,7 +415,7 @@ class SendCommandHandleClass:
raise ValueError("表情ID无效")
return (
CommandType.MESSAGE_LIKE.value,
CommandType.SET_MSG_EMOJI_LIKE.value,
{
"message_id": message_id,
"emoji_id": emoji_id,

View File

@ -12,8 +12,11 @@ token = "" # Napcat设定的访问令牌若无则留空
heartbeat_interval = 30 # 与Napcat设置的心跳相同按秒计
[maibot_server] # 连接麦麦的ws服务设置
host = "localhost" # 麦麦在.env文件中设置的主机地址即HOST字段
port = 8000 # 麦麦在.env文件中设置的端口即PORT字段
host = "localhost" # 麦麦在.env文件中设置的主机地址即HOST字段
port = 8000 # 麦麦在.env文件中设置的端口即PORT字段
enable_api_server = false # 是否启用API-Server模式连接
base_url = "ws://127.0.0.1:18095/ws" # API-Server连接地址 (ws://ip:port/path)仅在enable_api_server为true时使用
api_key = "maibot" # API Key (仅在enable_api_server为true时使用)
[chat] # 黑白名单功能
group_list_type = "whitelist" # 群组名单类型可选为whitelist, blacklist

1089
uv.lock

File diff suppressed because it is too large Load Diff