Files
NeoBot/core/bot.py
2026-01-01 20:38:53 +08:00

406 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Bot 抽象模块
定义了 Bot 类,封装了 OneBot API 的调用逻辑,提供了便捷的消息发送方法。
"""
from typing import TYPE_CHECKING, Union, List
if TYPE_CHECKING:
from .ws import WS
from models import OneBotEvent, MessageSegment
class Bot:
"""
Bot 抽象类,封装 API 调用和常用操作
"""
def __init__(self, ws_client: "WS"):
"""
初始化 Bot 实例
:param ws_client: WebSocket 客户端实例,用于底层通信
"""
self.ws = ws_client
async def call_api(self, action: str, params: dict = None) -> dict:
"""
调用 OneBot API
:param action: API 动作名称
:param params: API 参数
:return: API 响应结果
"""
return await self.ws.call_api(action, params)
async def send_group_msg(self, group_id: int, message: Union[str, "MessageSegment", List["MessageSegment"]], auto_escape: bool = False) -> dict:
"""
发送群消息
:param group_id: 群号
:param message: 消息内容可以是字符串、MessageSegment 对象或 MessageSegment 列表
:param auto_escape: 是否自动转义(仅当 message 为字符串时有效)
:return: API 响应结果
"""
return await self.call_api(
"send_group_msg", {"group_id": group_id, "message": self._process_message(message), "auto_escape": auto_escape}
)
async def send_private_msg(self, user_id: int, message: Union[str, "MessageSegment", List["MessageSegment"]], auto_escape: bool = False) -> dict:
"""
发送私聊消息
:param user_id: 用户 QQ 号
:param message: 消息内容可以是字符串、MessageSegment 对象或 MessageSegment 列表
:param auto_escape: 是否自动转义(仅当 message 为字符串时有效)
:return: API 响应结果
"""
return await self.call_api(
"send_private_msg", {"user_id": user_id, "message": self._process_message(message), "auto_escape": auto_escape}
)
async def send(self, event: "OneBotEvent", message: Union[str, "MessageSegment", List["MessageSegment"]], auto_escape: bool = False) -> dict:
"""
智能发送消息,根据事件类型自动选择发送方式
:param event: 触发事件对象
:param message: 消息内容
:param auto_escape: 是否自动转义
:return: API 响应结果
"""
# 如果是消息事件,直接调用 reply
if hasattr(event, "reply"):
await event.reply(message, auto_escape)
return {"status": "ok", "msg": "Replied via event.reply()"}
# 尝试从事件中获取 user_id 或 group_id
user_id = getattr(event, "user_id", None)
group_id = getattr(event, "group_id", None)
if group_id:
return await self.send_group_msg(group_id, message, auto_escape)
elif user_id:
return await self.send_private_msg(user_id, message, auto_escape)
return {"status": "failed", "msg": "Unknown message target"}
async def delete_msg(self, message_id: int) -> dict:
"""
撤回消息
:param message_id: 消息 ID
:return: API 响应结果
"""
return await self.call_api("delete_msg", {"message_id": message_id})
async def get_msg(self, message_id: int) -> dict:
"""
获取消息
:param message_id: 消息 ID
:return: API 响应结果
"""
return await self.call_api("get_msg", {"message_id": message_id})
async def get_forward_msg(self, id: str) -> dict:
"""
获取合并转发消息
:param id: 合并转发 ID
:return: API 响应结果
"""
return await self.call_api("get_forward_msg", {"id": id})
async def send_like(self, user_id: int, times: int = 1) -> dict:
"""
发送点赞
:param user_id: 对方 QQ 号
:param times: 点赞次数
:return: API 响应结果
"""
return await self.call_api("send_like", {"user_id": user_id, "times": times})
async def set_group_kick(self, group_id: int, user_id: int, reject_add_request: bool = False) -> dict:
"""
群组踢人
:param group_id: 群号
:param user_id: 要踢的 QQ 号
:param reject_add_request: 拒绝此人的加群请求
:return: API 响应结果
"""
return await self.call_api("set_group_kick", {"group_id": group_id, "user_id": user_id, "reject_add_request": reject_add_request})
async def set_group_ban(self, group_id: int, user_id: int, duration: int = 30 * 60) -> dict:
"""
群组单人禁言
:param group_id: 群号
:param user_id: 要禁言的 QQ 号
:param duration: 禁言时长0 表示解除禁言
:return: API 响应结果
"""
return await self.call_api("set_group_ban", {"group_id": group_id, "user_id": user_id, "duration": duration})
async def set_group_anonymous_ban(self, group_id: int, anonymous: dict = None, duration: int = 30 * 60, flag: str = None) -> dict:
"""
群组匿名禁言
:param group_id: 群号
:param anonymous: 可选,要禁言的匿名用户对象(群消息事件的 anonymous 字段)
:param duration: 禁言时长(秒)
:param flag: 可选,要禁言的匿名用户的 flag需从群消息事件的 anonymous 字段中获取)
:return: API 响应结果
"""
params = {"group_id": group_id, "duration": duration}
if anonymous:
params["anonymous"] = anonymous
if flag:
params["flag"] = flag
return await self.call_api("set_group_anonymous_ban", params)
async def set_group_whole_ban(self, group_id: int, enable: bool = True) -> dict:
"""
群组全员禁言
:param group_id: 群号
:param enable: 是否开启
:return: API 响应结果
"""
return await self.call_api("set_group_whole_ban", {"group_id": group_id, "enable": enable})
async def set_group_admin(self, group_id: int, user_id: int, enable: bool = True) -> dict:
"""
群组设置管理员
:param group_id: 群号
:param user_id: 要设置的 QQ 号
:param enable: True 为设置False 为取消
:return: API 响应结果
"""
return await self.call_api("set_group_admin", {"group_id": group_id, "user_id": user_id, "enable": enable})
async def set_group_anonymous(self, group_id: int, enable: bool = True) -> dict:
"""
群组匿名
:param group_id: 群号
:param enable: 是否开启
:return: API 响应结果
"""
return await self.call_api("set_group_anonymous", {"group_id": group_id, "enable": enable})
async def set_group_card(self, group_id: int, user_id: int, card: str = "") -> dict:
"""
设置群名片(群备注)
:param group_id: 群号
:param user_id: 要设置的 QQ 号
:param card: 群名片内容,不填或空字符串表示删除群名片
:return: API 响应结果
"""
return await self.call_api("set_group_card", {"group_id": group_id, "user_id": user_id, "card": card})
async def set_group_name(self, group_id: int, group_name: str) -> dict:
"""
设置群名
:param group_id: 群号
:param group_name: 新群名
:return: API 响应结果
"""
return await self.call_api("set_group_name", {"group_id": group_id, "group_name": group_name})
async def set_group_leave(self, group_id: int, is_dismiss: bool = False) -> dict:
"""
退出群组
:param group_id: 群号
:param is_dismiss: 是否解散,如果登录号是群主,则仅在此项为 True 时能够解散
:return: API 响应结果
"""
return await self.call_api("set_group_leave", {"group_id": group_id, "is_dismiss": is_dismiss})
async def set_group_special_title(self, group_id: int, user_id: int, special_title: str = "", duration: int = -1) -> dict:
"""
设置群组专属头衔
:param group_id: 群号
:param user_id: 要设置的 QQ 号
:param special_title: 专属头衔,不填或空字符串表示删除
:param duration: 有效期(秒),-1 表示永久
:return: API 响应结果
"""
return await self.call_api("set_group_special_title", {"group_id": group_id, "user_id": user_id, "special_title": special_title, "duration": duration})
async def get_group_info(self, group_id: int, no_cache: bool = False) -> dict:
"""
获取群信息
:param group_id: 群号
:param no_cache: 是否不使用缓存
:return: API 响应结果
"""
return await self.call_api("get_group_info", {"group_id": group_id, "no_cache": no_cache})
async def get_group_list(self) -> dict:
"""
获取群列表
:return: API 响应结果
"""
return await self.call_api("get_group_list")
async def get_group_member_info(self, group_id: int, user_id: int, no_cache: bool = False) -> dict:
"""
获取群成员信息
:param group_id: 群号
:param user_id: QQ 号
:param no_cache: 是否不使用缓存
:return: API 响应结果
"""
return await self.call_api("get_group_member_info", {"group_id": group_id, "user_id": user_id, "no_cache": no_cache})
async def get_group_member_list(self, group_id: int) -> dict:
"""
获取群成员列表
:param group_id: 群号
:return: API 响应结果
"""
return await self.call_api("get_group_member_list", {"group_id": group_id})
async def get_group_honor_info(self, group_id: int, type: str) -> dict:
"""
获取群荣誉信息
:param group_id: 群号
:param type: 要获取的群荣誉类型,可传入 talkative, performer, legend, strong_newbie, emotion 等
:return: API 响应结果
"""
return await self.call_api("get_group_honor_info", {"group_id": group_id, "type": type})
async def get_login_info(self) -> dict:
"""
获取登录号信息
:return: API 响应结果
"""
return await self.call_api("get_login_info")
async def get_stranger_info(self, user_id: int, no_cache: bool = False) -> dict:
"""
获取陌生人信息
:param user_id: QQ 号
:param no_cache: 是否不使用缓存
:return: API 响应结果
"""
return await self.call_api("get_stranger_info", {"user_id": user_id, "no_cache": no_cache})
async def get_friend_list(self) -> dict:
"""
获取好友列表
:return: API 响应结果
"""
return await self.call_api("get_friend_list")
async def set_friend_add_request(self, flag: str, approve: bool = True, remark: str = "") -> dict:
"""
处理加好友请求
:param flag: 加好友请求的 flag需从上报的数据中获取
:param approve: 是否同意请求
:param remark: 添加后的好友备注(仅在同意时有效)
:return: API 响应结果
"""
return await self.call_api("set_friend_add_request", {"flag": flag, "approve": approve, "remark": remark})
async def set_group_add_request(self, flag: str, sub_type: str, approve: bool = True, reason: str = "") -> dict:
"""
处理加群请求/邀请
:param flag: 加群请求的 flag需从上报的数据中获取
:param sub_type: add 或 invite请求类型需要与上报消息中的 sub_type 字段相符)
:param approve: 是否同意请求/邀请
:param reason: 拒绝理由(仅在拒绝时有效)
:return: API 响应结果
"""
return await self.call_api("set_group_add_request", {"flag": flag, "sub_type": sub_type, "approve": approve, "reason": reason})
async def get_version_info(self) -> dict:
"""
获取版本信息
:return: API 响应结果
"""
return await self.call_api("get_version_info")
async def get_status(self) -> dict:
"""
获取状态
:return: API 响应结果
"""
return await self.call_api("get_status")
async def can_send_image(self) -> dict:
"""
检查是否可以发送图片
:return: API 响应结果
"""
return await self.call_api("can_send_image")
async def can_send_record(self) -> dict:
"""
检查是否可以发送语音
:return: API 响应结果
"""
return await self.call_api("can_send_record")
async def clean_cache(self) -> dict:
"""
清理缓存
:return: API 响应结果
"""
return await self.call_api("clean_cache")
def _process_message(self, message: Union[str, "MessageSegment", List["MessageSegment"]]) -> Union[str, List[dict]]:
"""
处理消息内容,将其转换为 API 可接受的格式
:param message: 原始消息内容
:return: 处理后的消息内容
"""
if isinstance(message, str):
return message
# 避免循环导入,在运行时导入
from models import MessageSegment
if isinstance(message, MessageSegment):
return [self._segment_to_dict(message)]
if isinstance(message, list):
return [self._segment_to_dict(m) for m in message if isinstance(m, MessageSegment)]
return str(message)
def _segment_to_dict(self, segment: "MessageSegment") -> dict:
"""
将 MessageSegment 对象转换为字典
:param segment: MessageSegment 对象
:return: 字典格式的消息段
"""
return {
"type": segment.type,
"data": segment.data
}