From 4bd1400c892a6cb963f30f5e8857db775a051de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 17 Jan 2026 18:25:41 +0800 Subject: [PATCH] feat(api): add NapCat extended APIs for file operations, group management, and user profile --- README.md | 247 +++++++++++++++++++ api.go | 621 +++++++++++++++++++++++++++++++++++++++++++++++ bot.go | 63 +++-- event_channel.go | 8 +- go.mod | 2 +- go.sum | 9 +- matcher.go | 4 +- matcher_test.go | 2 +- 8 files changed, 915 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 3f7ed26b..316f4752 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,253 @@ func main() { - 通过 `init` 函数实现插件式 - 底层与 Onebot 通信驱动可换,目前支持HTTP、正向/反向WS,且支持基于 `unix socket` 的通信(使用 `ws+unix://`) - 通过添加多个 driver 实现多Q机器人支持 +- 完整的 OneBot 11 标准 API + NapCat / NapNeko 扩展 API 支持 + +## 📡 API 列表 + +所有 API 方法均定义在 `api.go` 中,以下按功能分组列出。带 `ThisGroup` / `This` 前缀的方法是对应方法的便捷版本,自动使用当前事件的群号/消息ID。 + +--- + +### OneBot 11 标准 API + +#### 消息 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SendGroupMessage` | 发送群消息 | `send_group_msg` | +| `SendPrivateMessage` | 发送私聊消息 | `send_private_msg` | +| `DeleteMessage` | 撤回消息 | `delete_msg` | +| `GetMessage` | 获取消息 | `get_msg` | +| `GetForwardMessage` | 获取合并转发消息 | `get_forward_msg` | +| `SendLike` | 发送好友赞 | `send_like` | + +#### 群操作 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SetGroupKick` / `SetThisGroupKick` | 群组踢人 | `set_group_kick` | +| `SetGroupBan` / `SetThisGroupBan` | 群组单人禁言 | `set_group_ban` | +| `SetGroupWholeBan` / `SetThisGroupWholeBan` | 群组全员禁言 | `set_group_whole_ban` | +| `SetGroupAdmin` / `SetThisGroupAdmin` | 群组设置管理员 | `set_group_admin` | +| `SetGroupAnonymous` / `SetThisGroupAnonymous` | 群组匿名 | `set_group_anonymous` | +| `SetGroupCard` / `SetThisGroupCard` | 设置群名片 | `set_group_card` | +| `SetGroupName` / `SetThisGroupName` | 设置群名 | `set_group_name` | +| `SetGroupLeave` / `SetThisGroupLeave` | 退出群组 | `set_group_leave` | +| `SetGroupSpecialTitle` / `SetThisGroupSpecialTitle` | 设置群组专属头衔 | `set_group_special_title` | + +#### 请求处理 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SetFriendAddRequest` | 处理加好友请求 | `set_friend_add_request` | +| `SetGroupAddRequest` | 处理加群请求/邀请 | `set_group_add_request` | + +#### 账号信息 + +| 方法 | 说明 | Action | +|------|------|--------| +| `GetLoginInfo` | 获取登录号信息 | `get_login_info` | +| `GetStrangerInfo` | 获取陌生人信息 | `get_stranger_info` | +| `GetFriendList` | 获取好友列表 | `get_friend_list` | +| `GetGroupInfo` / `GetThisGroupInfo` | 获取群信息 | `get_group_info` | +| `GetGroupList` | 获取群列表 | `get_group_list` | +| `GetGroupMemberInfo` / `GetThisGroupMemberInfo` | 获取群成员信息 | `get_group_member_info` | +| `GetGroupMemberList` / `GetThisGroupMemberList` | 获取群成员列表 | `get_group_member_list` | +| `GetGroupMemberListNoCache` / `GetThisGroupMemberListNoCache` | 无缓存获取群成员列表 | `get_group_member_list` | +| `GetGroupHonorInfo` / `GetThisGroupHonorInfo` | 获取群荣誉信息 | `get_group_honor_info` | +| `GetRecord` | 获取语音 | `get_record` | +| `GetImage` | 获取图片 | `get_image` | +| `GetVersionInfo` | 获取版本信息 | `get_version_info` | + +--- + +### go-cqhttp 扩展 API + +#### 群操作扩展 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SetGroupPortrait` / `SetThisGroupPortrait` | 设置群头像 | `set_group_portrait` | +| `GetGroupSystemMessage` | 获取群系统消息 | `get_group_system_msg` | +| `GetGroupAtAllRemain` / `GetThisGroupAtAllRemain` | 获取群@全体成员剩余次数 | `get_group_at_all_remain` | +| `GetGroupMessageHistory` / `GetThisGroupMessageHistory` | 获取群消息历史记录 | `get_group_msg_history` | +| `GetLatestGroupMessageHistory` / `GetLatestThisGroupMessageHistory` | 获取最新群消息历史记录 | `get_group_msg_history` | +| `GetGroupEssenceMessageList` / `GetThisGroupEssenceMessageList` | 获取群精华消息列表 | `get_essence_msg_list` | +| `SetGroupEssenceMessage` | 设置群精华消息 | `set_essence_msg` | +| `DeleteGroupEssenceMessage` | 移出群精华消息 | `delete_essence_msg` | + +#### 消息扩展 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SendGroupForwardMessage` | 发送合并转发(群) | `send_group_forward_msg` | +| `SendPrivateForwardMessage` | 发送合并转发(私聊) | `send_private_forward_msg` | +| `ForwardFriendSingleMessage` | 转发单条消息到好友 | `forward_friend_single_msg` | +| `ForwardGroupSingleMessage` | 转发单条消息到群 | `forward_group_single_msg` | +| `MarkMessageAsRead` / `MarkThisMessageAsRead` | 标记消息已读 | `mark_msg_as_read` | +| `GetOnlineClients` | 获取当前账号在线客户端列表 | `get_online_clients` | + +#### 文件操作 + +| 方法 | 说明 | Action | +|------|------|--------| +| `GetGroupFilesystemInfo` / `GetThisGroupFilesystemInfo` | 获取群文件系统信息 | `get_group_file_system_info` | +| `GetGroupRootFiles` / `GetThisGroupRootFiles` | 获取群根目录文件列表 | `get_group_root_files` | +| `GetGroupFilesByFolder` / `GetThisGroupFilesByFolder` | 获取群子目录文件列表 | `get_group_files_by_folder` | +| `GetGroupFileURL` / `GetThisGroupFileURL` | 获取群文件资源链接 | `get_group_file_url` | +| `UploadGroupFile` / `UploadThisGroupFile` | 上传群文件 | `upload_group_file` | + +#### 其他 + +| 方法 | 说明 | Action | +|------|------|--------| +| `OCRImage` | 图片 OCR | `ocr_image` | +| `GetWordSlices` | 获取中文分词 | `.get_word_slices` | +| `SendGuildChannelMessage` | 发送频道消息 | `send_guild_channel_msg` | +| `NickName` | 从 args/at 获取昵称 | — | +| `CardOrNickName` | 从 uid 获取群名片或昵称 | — | + +--- + +### NapNeko / LLOneBot 扩展 API + +| 方法 | 说明 | Action | +|------|------|--------| +| `SetMyAvatar` | 设置我的头像 | `set_qq_avatar` | +| `GetFile` | 下载收到的文件 | `get_file` | +| `SetMessageEmojiLike` | 发送表情回应 | `set_msg_emoji_like` | +| `SetGroupSign` | 群签到 | `set_group_sign` | +| `GroupPoke` | 群聊戳一戳 | `group_poke` | +| `FriendPoke` | 私聊戳一戳 | `friend_poke` | +| `SendPoke` | 群聊/私聊戳一戳 | `send_poke` | +| `ArkSharePeer` | 获取推荐好友/群聊卡片 | `ArkSharePeer` | +| `ArkShareGroup` | 获取推荐群聊卡片 | `ArkShareGroup` | +| `GetRobotUinRange` | 获取机器人账号范围 | `get_robot_uin_range` | +| `SetOnlineStatus` | 设置在线状态 | `set_online_status` | +| `GetFriendsWithCategory` | 获取分类的好友列表 | `get_friends_with_category` | +| `TranslateEn2Zh` | 英译中 | `translate_en2zh` | +| `SendForwardMessage` | 发送合并转发 | `send_forward_msg` | +| `MarkPrivateMessageAsRead` | 设置私聊已读 | `mark_private_msg_as_read` | +| `MarkGroupMessageAsRead` | 设置群聊已读 | `mark_group_msg_as_read` | +| `GetFriendMessageHistory` | 获取私聊历史记录 | `get_friend_msg_history` | +| `CreateCollection` | 创建收藏 | `create_collection` | +| `GetCollectionList` | 获取收藏 | `get_collection_list` | +| `SetSelfLongNick` | 设置签名 | `set_self_longnick` | +| `GetRecentContact` | 获取最近联系人 | `get_recent_contact` | +| `MarkAllAsRead` | 标记所有已读 | `_mark_all_as_read` | +| `GetProfileLike` | 获取自身点赞列表 | `get_profile_like` | +| `FetchCustomFace` | 获取自定义表情 | `fetch_custom_face` | +| `GetAIRecord` | AI 文字转语音 | `get_ai_record` | +| `GetAICharacters` | 获取 AI 语音角色列表 | `get_ai_characters` | +| `SendGroupAIRecord` | 群聊发送 AI 语音 | `send_group_ai_record` | + +--- + +### NapCat 扩展 API + +基于 [NapCat API 文档](https://napcat.apifox.cn) 补充的扩展接口: + +#### 文件操作 + +| 方法 | 说明 | Action | +|------|------|--------| +| `UploadPrivateFile` | 上传私聊文件 | `upload_private_file` | +| `DeleteGroupFile` / `DeleteThisGroupFile` | 删除群文件 | `delete_group_file` | +| `CreateGroupFileFolder` / `CreateThisGroupFileFolder` | 创建群文件目录 | `create_group_file_folder` | +| `DeleteGroupFileFolder` / `DeleteThisGroupFileFolder` | 删除群文件目录 | `delete_group_folder` | +| `DownloadFile` | 下载文件到本地 | `download_file` | +| `GetPrivateFileURL` | 获取私聊文件下载链接 | `get_private_file_url` | +| `MoveGroupFile` | 移动群文件 | `move_group_file` | +| `RenameGroupFile` | 重命名群文件 | `rename_group_file` | +| `TransGroupFile` | 传输群文件 | `trans_group_file` | + +#### 群公告 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SendGroupNotice` / `SendThisGroupNotice` | 发送群公告 | `_send_group_notice` | +| `GetGroupNotice` / `GetThisGroupNotice` | 获取群公告列表 | `_get_group_notice` | +| `DeleteGroupNotice` / `DeleteThisGroupNotice` | 删除群公告 | `_del_group_notice` | + +#### 好友管理 + +| 方法 | 说明 | Action | +|------|------|--------| +| `DeleteFriend` | 删除好友 | `delete_friend` | +| `SetFriendRemark` | 设置好友备注 | `set_friend_remark` | +| `GetUnidirectionalFriendList` | 获取单向好友列表 | `get_unidirectional_friend_list` | +| `GetDoubtFriendsAddRequest` | 获取可疑好友申请 | `get_doubt_friends_add_request` | +| `SetDoubtFriendsAddRequest` | 处理可疑好友申请 | `set_doubt_friends_add_request` | + +#### 用户资料 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SetQQProfile` | 设置QQ资料 | `set_qq_profile` | +| `SetInputStatus` | 设置输入状态 | `set_input_status` | +| `GetUserStatus` | 获取用户在线状态 | `nc_get_user_status` | +| `SetCustomOnlineStatus` | 设置自定义在线状态 | `set_custom_online_status` | + +#### 群管理扩展 + +| 方法 | 说明 | Action | +|------|------|--------| +| `GetGroupShutList` / `GetThisGroupShutList` | 获取群禁言列表 | `get_group_shut_list` | +| `GetGroupInfoEx` / `GetThisGroupInfoEx` | 获取群详细信息 | `get_group_info_ex` | +| `SetGroupRemark` / `SetThisGroupRemark` | 设置群备注 | `set_group_remark` | +| `GetGroupIgnoredNotifies` / `GetThisGroupIgnoredNotifies` | 获取群忽略通知 | `get_group_ignored_notifies` | +| `SetGroupAddOption` | 设置群加群选项 | `set_group_add_option` | +| `SetGroupSearchOption` | 设置群搜索选项 | `set_group_search_option` | +| `GroupKickBatch` | 批量踢出群成员 | `set_group_kick` | +| `SetGroupTodo` / `SetThisGroupTodo` | 设置群待办 | `set_group_todo` | + +#### 群相册 + +| 方法 | 说明 | Action | +|------|------|--------| +| `GetGroupAlbumList` | 获取群相册列表 | `get_group_album_list` | +| `GetGroupAlbumMediaList` | 获取群相册媒体列表 | `get_group_album_media_list` | +| `UploadGroupAlbum` | 上传图片到群相册 | `upload_group_album` | +| `DeleteGroupAlbumMedia` | 删除群相册媒体 | `delete_group_album_media` | +| `LikeGroupAlbumMedia` | 点赞群相册媒体 | `like_group_album_media` | + +#### 消息扩展 + +| 方法 | 说明 | Action | +|------|------|--------| +| `SendGroupMusic` | 发送群聊音乐卡片 | `send_group_msg` | +| `SendGroupCustomMusic` | 发送群聊自定义音乐卡片 | `send_group_msg` | +| `GetEmojiLikeList` | 获取消息表情点赞列表 | `get_msg_emoji_like_list` | +| `GetMiniAppArk` | 获取小程序 Ark | `get_mini_app_ark` | + +#### 系统 / 安全 + +| 方法 | 说明 | Action | +|------|------|--------| +| `CheckURLSafely` | 检查URL安全性 | `check_url_safely` | +| `CanSendImage` | 检查是否可以发送图片 | `can_send_image` | +| `CanSendRecord` | 检查是否可以发送语音 | `can_send_record` | +| `GetCSRFToken` | 获取 CSRF Token | `get_csrf_token` | +| `GetCredentials` | 获取登录凭证 | `get_credentials` | +| `GetCookies` | 获取 Cookies | `get_cookies` | +| `GetClientKey` | 获取 ClientKey | `get_clientkey` | +| `GetStatus` | 获取运行状态 | `get_status` | +| `CleanCache` | 清理缓存 | `clean_cache` | +| `Restart` | 重启服务 | `set_restart` | +| `GetPacketStatus` | 获取Packet状态 | `get_packet_status` | +| `Logout` | 退出登录 | `nc_logout` | + +#### 频道 / RKey / 其他 + +| 方法 | 说明 | Action | +|------|------|--------| +| `GetGuildList` | 获取频道列表 | `get_guild_list` | +| `GetGuildServiceProfile` | 获取频道个人信息 | `get_guild_service_profile` | +| `GetRKey` | 获取 RKey | `get_rkey` | +| `NcGetRKey` | 获取扩展 RKey | `nc_get_rkey` | +| `GetRKeyServer` | 获取 RKey 服务器 | `get_rkey_server` | +| `ClickInlineKeyboardButton` | 点击内联键盘按钮 | `click_inline_keyboard_button` | ## 关联项目 diff --git a/api.go b/api.go index cc0f54a6..bbe81a4c 100644 --- a/api.go +++ b/api.go @@ -1001,3 +1001,624 @@ func (ctx *Ctx) SendPoke(groupID, userID int64) { "user_id": userID, }) } + +// ═══════════════════════════════════════════════════════════ +// NapCat 补充 API — 基于 https://napcat.apifox.cn +// ═══════════════════════════════════════════════════════════ + +// ── 文件操作(NapCat 扩展)── + +// UploadPrivateFile 上传私聊文件 +// +// https://napcat.apifox.cn/226658883e0.md +func (ctx *Ctx) UploadPrivateFile(userID int64, file, name string) string { + return ctx.CallAction("upload_private_file", Params{ + "user_id": userID, + "file": file, + "name": name, + }).Data.Get("file_id").Str +} + +// DeleteGroupFile 删除群文件 +// +// https://napcat.apifox.cn/226658755e0.md +func (ctx *Ctx) DeleteGroupFile(groupID int64, fileID string) { + ctx.CallAction("delete_group_file", Params{ + "group_id": groupID, + "file_id": fileID, + }) +} + +// DeleteThisGroupFile 删除本群文件 +func (ctx *Ctx) DeleteThisGroupFile(fileID string) { + ctx.DeleteGroupFile(ctx.Event.GroupID, fileID) +} + +// CreateGroupFileFolder 创建群文件目录 +// +// https://napcat.apifox.cn/226658773e0.md +func (ctx *Ctx) CreateGroupFileFolder(groupID int64, folderName string) gjson.Result { + return ctx.CallAction("create_group_file_folder", Params{ + "group_id": groupID, + "folder_name": folderName, + }).Data +} + +// CreateThisGroupFileFolder 创建本群文件目录 +func (ctx *Ctx) CreateThisGroupFileFolder(folderName string) gjson.Result { + return ctx.CreateGroupFileFolder(ctx.Event.GroupID, folderName) +} + +// DeleteGroupFileFolder 删除群文件目录 +// +// https://napcat.apifox.cn/226658779e0.md +func (ctx *Ctx) DeleteGroupFileFolder(groupID int64, folderID string) { + ctx.CallAction("delete_group_folder", Params{ + "group_id": groupID, + "folder_id": folderID, + }) +} + +// DeleteThisGroupFileFolder 删除本群文件目录 +func (ctx *Ctx) DeleteThisGroupFileFolder(folderID string) { + ctx.DeleteGroupFileFolder(ctx.Event.GroupID, folderID) +} + +// DownloadFile 下载文件到本地临时目录 +// +// https://napcat.apifox.cn/226658887e0.md +func (ctx *Ctx) DownloadFile(url, name, headers string) string { + return ctx.CallAction("download_file", Params{ + "url": url, + "name": name, + "headers": headers, + }).Data.Get("file").Str +} + +// GetPrivateFileURL 获取私聊文件下载链接 +// +// https://napcat.apifox.cn/266151849e0.md +func (ctx *Ctx) GetPrivateFileURL(fileID string) string { + return ctx.CallAction("get_private_file_url", Params{ + "file_id": fileID, + }).Data.Get("url").Str +} + +// MoveGroupFile 移动群文件 +// +// https://napcat.apifox.cn/283136359e0.md +func (ctx *Ctx) MoveGroupFile(groupID int64, fileID, parentFolderID, targetFolderID string) { + ctx.CallAction("move_group_file", Params{ + "group_id": groupID, + "file_id": fileID, + "parent_folder_id": parentFolderID, + "target_folder_id": targetFolderID, + }) +} + +// RenameGroupFile 重命名群文件 +// +// https://napcat.apifox.cn/283136375e0.md +func (ctx *Ctx) RenameGroupFile(groupID int64, fileID, newName string) { + ctx.CallAction("rename_group_file", Params{ + "group_id": groupID, + "file_id": fileID, + "new_name": newName, + }) +} + +// TransGroupFile 传输群文件 +// +// https://napcat.apifox.cn/283136366e0.md +func (ctx *Ctx) TransGroupFile(groupID int64, fileID, targetGroupID string) { + ctx.CallAction("trans_group_file", Params{ + "group_id": groupID, + "file_id": fileID, + "target_group_id": targetGroupID, + }) +} + +// ── 群公告(NapCat 扩展)── + +// SendGroupNotice 发送群公告 +// +// https://napcat.apifox.cn/226658740e0.md +func (ctx *Ctx) SendGroupNotice(groupID int64, content, image string, pinned int) { + ctx.CallAction("_send_group_notice", Params{ + "group_id": groupID, + "content": content, + "image": image, + "pinned": pinned, + }) +} + +// SendThisGroupNotice 发送本群公告 +func (ctx *Ctx) SendThisGroupNotice(content, image string, pinned int) { + ctx.SendGroupNotice(ctx.Event.GroupID, content, image, pinned) +} + +// GetGroupNotice 获取群公告列表 +// +// https://napcat.apifox.cn/226658742e0.md +func (ctx *Ctx) GetGroupNotice(groupID int64) gjson.Result { + return ctx.CallAction("_get_group_notice", Params{ + "group_id": groupID, + }).Data +} + +// GetThisGroupNotice 获取本群公告列表 +func (ctx *Ctx) GetThisGroupNotice() gjson.Result { + return ctx.GetGroupNotice(ctx.Event.GroupID) +} + +// DeleteGroupNotice 删除群公告 +// +// https://napcat.apifox.cn/226659240e0.md +func (ctx *Ctx) DeleteGroupNotice(groupID int64, noticeID string) { + ctx.CallAction("_del_group_notice", Params{ + "group_id": groupID, + "notice_id": noticeID, + }) +} + +// DeleteThisGroupNotice 删除本群公告 +func (ctx *Ctx) DeleteThisGroupNotice(noticeID string) { + ctx.DeleteGroupNotice(ctx.Event.GroupID, noticeID) +} + +// ── 好友管理(NapCat 扩展)── + +// DeleteFriend 删除好友 +// +// https://napcat.apifox.cn/227237873e0.md +func (ctx *Ctx) DeleteFriend(userID int64, tempBlock, tempBothDel bool) { + ctx.CallAction("delete_friend", Params{ + "user_id": userID, + "temp_block": tempBlock, + "temp_both_del": tempBothDel, + }) +} + +// SetFriendRemark 设置好友备注 +// +// https://napcat.apifox.cn/298305173e0.md +func (ctx *Ctx) SetFriendRemark(userID int64, remark string) { + ctx.CallAction("set_friend_remark", Params{ + "user_id": userID, + "remark": remark, + }) +} + +// GetUnidirectionalFriendList 获取单向好友列表 +// +// https://napcat.apifox.cn/266151878e0.md +func (ctx *Ctx) GetUnidirectionalFriendList() gjson.Result { + return ctx.CallAction("get_unidirectional_friend_list", Params{}).Data +} + +// GetDoubtFriendsAddRequest 获取可疑好友申请列表 +// +// https://napcat.apifox.cn/289565516e0.md +func (ctx *Ctx) GetDoubtFriendsAddRequest() gjson.Result { + return ctx.CallAction("get_doubt_friends_add_request", Params{}).Data +} + +// SetDoubtFriendsAddRequest 处理可疑好友申请 +// +// https://napcat.apifox.cn/289565525e0.md +func (ctx *Ctx) SetDoubtFriendsAddRequest(flag string, approve bool, remark string) { + ctx.CallAction("set_doubt_friends_add_request", Params{ + "flag": flag, + "approve": approve, + "remark": remark, + }) +} + +// ── 用户资料(NapCat 扩展)── + +// SetQQProfile 设置QQ资料(昵称、个性签名、性别) +// +// https://napcat.apifox.cn/226657374e0.md +// +// sex: 0=未知, 1=男, 2=女 +func (ctx *Ctx) SetQQProfile(nickname, personalNote string, sex int) { + ctx.CallAction("set_qq_profile", Params{ + "nickname": nickname, + "personal_note": personalNote, + "sex": sex, + }) +} + +// SetInputStatus 设置输入状态 +// +// https://napcat.apifox.cn/226659225e0.md +// +// eventType: 事件类型 +func (ctx *Ctx) SetInputStatus(userID int64, eventType int) { + ctx.CallAction("set_input_status", Params{ + "user_id": userID, + "event_type": eventType, + }) +} + +// GetUserStatus 获取用户在线状态 +// +// https://napcat.apifox.cn/226659292e0.md +// +// 返回 data: status(在线状态), ext_status(扩展状态) +func (ctx *Ctx) GetUserStatus(userID int64) gjson.Result { + return ctx.CallAction("nc_get_user_status", Params{ + "user_id": userID, + }).Data +} + +// SetCustomOnlineStatus 设置自定义在线状态 +// +// https://napcat.apifox.cn/266151905e0.md +func (ctx *Ctx) SetCustomOnlineStatus(faceID int, faceType int, wording string) { + ctx.CallAction("set_custom_online_status", Params{ + "face_id": faceID, + "face_type": faceType, + "wording": wording, + }) +} + +// ── 群管理扩展(NapCat 扩展)── + +// GetGroupShutList 获取群禁言列表 +// +// https://napcat.apifox.cn/226659300e0.md +func (ctx *Ctx) GetGroupShutList(groupID int64) gjson.Result { + return ctx.CallAction("get_group_shut_list", Params{ + "group_id": groupID, + }).Data +} + +// GetThisGroupShutList 获取本群禁言列表 +func (ctx *Ctx) GetThisGroupShutList() gjson.Result { + return ctx.GetGroupShutList(ctx.Event.GroupID) +} + +// GetGroupInfoEx 获取群详细信息(扩展) +// +// https://napcat.apifox.cn/226659229e0.md +func (ctx *Ctx) GetGroupInfoEx(groupID int64) gjson.Result { + return ctx.CallAction("get_group_info_ex", Params{ + "group_id": groupID, + }).Data +} + +// GetThisGroupInfoEx 获取本群详细信息(扩展) +func (ctx *Ctx) GetThisGroupInfoEx() gjson.Result { + return ctx.GetGroupInfoEx(ctx.Event.GroupID) +} + +// SetGroupRemark 设置群备注 +// +// https://napcat.apifox.cn/283136268e0.md +func (ctx *Ctx) SetGroupRemark(groupID int64, remark string) { + ctx.CallAction("set_group_remark", Params{ + "group_id": groupID, + "remark": remark, + }) +} + +// SetThisGroupRemark 设置本群备注 +func (ctx *Ctx) SetThisGroupRemark(remark string) { + ctx.SetGroupRemark(ctx.Event.GroupID, remark) +} + +// GetGroupIgnoredNotifies 获取群忽略通知(被忽略的入群申请和邀请) +// +// https://napcat.apifox.cn/226659323e0.md +func (ctx *Ctx) GetGroupIgnoredNotifies(groupID int64) gjson.Result { + return ctx.CallAction("get_group_ignored_notifies", Params{ + "group_id": groupID, + }).Data +} + +// GetThisGroupIgnoredNotifies 获取本群忽略通知 +func (ctx *Ctx) GetThisGroupIgnoredNotifies() gjson.Result { + return ctx.GetGroupIgnoredNotifies(ctx.Event.GroupID) +} + +// SetGroupAddOption 设置群加群选项 +// +// https://napcat.apifox.cn/301542178e0.md +func (ctx *Ctx) SetGroupAddOption(groupID int64, addOption int) { + ctx.CallAction("set_group_add_option", Params{ + "group_id": groupID, + "add_option": addOption, + }) +} + +// SetGroupSearchOption 设置群搜索选项 +// +// https://napcat.apifox.cn/301542170e0.md +func (ctx *Ctx) SetGroupSearchOption(groupID int64, enabled bool) { + ctx.CallAction("set_group_search_option", Params{ + "group_id": groupID, + "enabled": enabled, + }) +} + +// GroupKickBatch 批量踢出群成员 +// +// https://napcat.apifox.cn/301542209e0.md +func (ctx *Ctx) GroupKickBatch(groupID int64, userIDs []int64, rejectAddRequest bool) { + ctx.CallAction("set_group_kick", Params{ + "group_id": groupID, + "user_ids": userIDs, + "reject_add_request": rejectAddRequest, + }) +} + +// SetGroupTodo 设置群待办 +// +// https://napcat.apifox.cn/395460568e0.md +func (ctx *Ctx) SetGroupTodo(groupID, messageID int64) { + ctx.CallAction("set_group_todo", Params{ + "group_id": groupID, + "message_id": messageID, + }) +} + +// SetThisGroupTodo 设置本群待办 +func (ctx *Ctx) SetThisGroupTodo(messageID int64) { + ctx.SetGroupTodo(ctx.Event.GroupID, messageID) +} + +// ── 群相册(NapCat 扩展)── + +// GetGroupAlbumList 获取群相册列表 +// +// https://napcat.apifox.cn/395460287e0.md +func (ctx *Ctx) GetGroupAlbumList(groupID int64) gjson.Result { + return ctx.CallAction("get_group_album_list", Params{ + "group_id": groupID, + }).Data +} + +// GetGroupAlbumMediaList 获取群相册媒体列表 +// +// https://napcat.apifox.cn/395459066e0.md +func (ctx *Ctx) GetGroupAlbumMediaList(groupID int64, albumID string) gjson.Result { + return ctx.CallAction("get_group_album_media_list", Params{ + "group_id": groupID, + "album_id": albumID, + }).Data +} + +// UploadGroupAlbum 上传图片到群相册 +// +// https://napcat.apifox.cn/395459739e0.md +func (ctx *Ctx) UploadGroupAlbum(groupID int64, albumID, file string) gjson.Result { + return ctx.CallAction("upload_group_album", Params{ + "group_id": groupID, + "album_id": albumID, + "file": file, + }).Data +} + +// DeleteGroupAlbumMedia 删除群相册媒体 +// +// https://napcat.apifox.cn/395455119e0.md +func (ctx *Ctx) DeleteGroupAlbumMedia(groupID int64, albumID, mediaID string) { + ctx.CallAction("delete_group_album_media", Params{ + "group_id": groupID, + "album_id": albumID, + "media_id": mediaID, + }) +} + +// LikeGroupAlbumMedia 点赞群相册媒体 +// +// https://napcat.apifox.cn/395457331e0.md +func (ctx *Ctx) LikeGroupAlbumMedia(groupID int64, albumID, mediaID string) { + ctx.CallAction("like_group_album_media", Params{ + "group_id": groupID, + "album_id": albumID, + "media_id": mediaID, + }) +} + +// ── 消息扩展(NapCat 扩展)── + +// SendGroupMusic 发送群聊音乐卡片 +// +// https://napcat.apifox.cn +// +// musicType: "qq", "163", "custom" +func (ctx *Ctx) SendGroupMusic(groupID int64, musicType string, id int64) int64 { + rsp := ctx.CallAction("send_group_msg", Params{ + "group_id": groupID, + "message": message.Message{message.Music(musicType, id)}, + }).Data.Get("message_id") + if rsp.Exists() { + return rsp.Int() + } + return 0 +} + +// SendGroupCustomMusic 发送群聊自定义音乐卡片 +func (ctx *Ctx) SendGroupCustomMusic(groupID int64, url, audio, title string) int64 { + rsp := ctx.CallAction("send_group_msg", Params{ + "group_id": groupID, + "message": message.Message{message.CustomMusic(url, audio, title)}, + }).Data.Get("message_id") + if rsp.Exists() { + return rsp.Int() + } + return 0 +} + +// GetEmojiLikeList 获取消息表情点赞列表 +// +// https://napcat.apifox.cn/410334663e0.md +func (ctx *Ctx) GetEmojiLikeList(messageID interface{}, emojiID string, count int) gjson.Result { + return ctx.CallAction("get_msg_emoji_like_list", Params{ + "message_id": messageID, + "emoji_id": emojiID, + "count": count, + }).Data +} + +// GetMiniAppArk 获取小程序 Ark +// +// https://napcat.apifox.cn/227738594e0.md +func (ctx *Ctx) GetMiniAppArk(appID, title, desc, iconURL, webURL string) gjson.Result { + return ctx.CallAction("get_mini_app_ark", Params{ + "app_id": appID, + "title": title, + "desc": desc, + "icon_url": iconURL, + "web_url": webURL, + }).Data +} + +// ── 系统 / 安全(NapCat 扩展)── + +// CheckURLSafely 检查URL安全性 +// +// https://napcat.apifox.cn/228534361e0.md +// +// 返回安全等级: 1=安全, 2=未知, 3=危险 +func (ctx *Ctx) CheckURLSafely(url string) int64 { + return ctx.CallAction("check_url_safely", Params{ + "url": url, + }).Data.Get("level").Int() +} + +// CanSendImage 检查是否可以发送图片 +// +// https://napcat.apifox.cn/226657071e0.md +func (ctx *Ctx) CanSendImage() bool { + return ctx.CallAction("can_send_image", Params{}).Data.Get("yes").Bool() +} + +// CanSendRecord 检查是否可以发送语音 +// +// https://napcat.apifox.cn/226657080e0.md +func (ctx *Ctx) CanSendRecord() bool { + return ctx.CallAction("can_send_record", Params{}).Data.Get("yes").Bool() +} + +// GetCSRFToken 获取 CSRF Token +// +// https://napcat.apifox.cn/226657044e0.md +func (ctx *Ctx) GetCSRFToken() int64 { + return ctx.CallAction("get_csrf_token", Params{}).Data.Get("token").Int() +} + +// GetCredentials 获取登录凭证(Cookies + CSRF Token) +// +// https://napcat.apifox.cn/226657054e0.md +func (ctx *Ctx) GetCredentials(domain string) gjson.Result { + return ctx.CallAction("get_credentials", Params{ + "domain": domain, + }).Data +} + +// GetCookies 获取指定域名的 Cookies +// +// https://napcat.apifox.cn/226657041e0.md +func (ctx *Ctx) GetCookies(domain string) string { + return ctx.CallAction("get_cookies", Params{ + "domain": domain, + }).Data.Get("cookies").Str +} + +// GetClientKey 获取当前登录帐号的 ClientKey +// +// https://napcat.apifox.cn/250286915e0.md +func (ctx *Ctx) GetClientKey() string { + return ctx.CallAction("get_clientkey", Params{}).Data.Get("clientkey").Str +} + +// GetStatus 获取运行状态 +// +// https://napcat.apifox.cn/226657083e0.md +func (ctx *Ctx) GetStatus() gjson.Result { + return ctx.CallAction("get_status", Params{}).Data +} + +// CleanCache 清理缓存 +// +// https://napcat.apifox.cn/298305106e0.md +func (ctx *Ctx) CleanCache() { + ctx.CallAction("clean_cache", Params{}) +} + +// Restart 重启服务 +// +// https://napcat.apifox.cn/410334662e0.md +func (ctx *Ctx) Restart() { + ctx.CallAction("set_restart", Params{}) +} + +// GetPacketStatus 获取Packet状态 +// +// https://napcat.apifox.cn/226659280e0.md +func (ctx *Ctx) GetPacketStatus() gjson.Result { + return ctx.CallAction("get_packet_status", Params{}).Data +} + +// Logout 退出登录 +// +// https://napcat.apifox.cn/283136399e0.md +func (ctx *Ctx) Logout() { + ctx.CallAction("nc_logout", Params{}) +} + +// ── 频道(NapCat 扩展)── + +// GetGuildList 获取频道列表 +// +// https://napcat.apifox.cn/226659311e0.md +func (ctx *Ctx) GetGuildList() gjson.Result { + return ctx.CallAction("get_guild_list", Params{}).Data +} + +// GetGuildServiceProfile 获取频道个人信息 +// +// https://napcat.apifox.cn/226659317e0.md +func (ctx *Ctx) GetGuildServiceProfile() gjson.Result { + return ctx.CallAction("get_guild_service_profile", Params{}).Data +} + +// ── RKey(NapCat 扩展)── + +// GetRKey 获取 RKey +// +// https://napcat.apifox.cn/226659297e0.md +func (ctx *Ctx) GetRKey() gjson.Result { + return ctx.CallAction("get_rkey", Params{}).Data +} + +// NcGetRKey 获取扩展RKey +// +// https://napcat.apifox.cn/283136230e0.md +func (ctx *Ctx) NcGetRKey() gjson.Result { + return ctx.CallAction("nc_get_rkey", Params{}).Data +} + +// GetRKeyServer 获取RKey服务器 +// +// https://napcat.apifox.cn/283136236e0.md +func (ctx *Ctx) GetRKeyServer() gjson.Result { + return ctx.CallAction("get_rkey_server", Params{}).Data +} + +// ── 其他(NapCat 扩展)── + +// ClickInlineKeyboardButton 点击内联键盘按钮 +// +// https://napcat.apifox.cn/266151864e0.md +func (ctx *Ctx) ClickInlineKeyboardButton(groupID int64, botAppid string, buttonID, callbackData string) { + ctx.CallAction("click_inline_keyboard_button", Params{ + "group_id": groupID, + "bot_appid": botAppid, + "button_id": buttonID, + "callback_data": callbackData, + }) +} diff --git a/bot.go b/bot.go index d34c16fb..287e7d34 100644 --- a/bot.go +++ b/bot.go @@ -21,6 +21,7 @@ import ( const ( StateKeyPrefixKeep = "__zerobot_keep_" + StateKeyEventIndex = StateKeyPrefixKeep + "_zerobot_ev_idx__" stateKeyNoLogMseeageID = "__zerobot_no_log_mseeage_id__" ) @@ -58,6 +59,7 @@ var BotConfig Config var ( evring eventRing // evring 事件环 isrunning uintptr + recvevcnt uintptr // evcnt 总的接收事件计数器 ) func runinit(op *Config) { @@ -213,6 +215,8 @@ func processEventAsync(response []byte, caller APICaller, maxwait time.Duration) msgid = message.NewMessageIDFromString(event.MessageID.(string)) } + idx := atomic.AddUintptr(&recvevcnt, 1) + switch event.PostType { // process DetailType case "message", "message_sent": event.DetailType = event.MessageType @@ -223,11 +227,11 @@ func processEventAsync(response []byte, caller APICaller, maxwait time.Duration) event.DetailType = event.RequestType } if event.PostType == "message" { - preprocessMessageEvent(&event) + preprocessMessageEvent(&event, idx) } ctx := &Ctx{ Event: &event, - State: State{}, + State: State{StateKeyEventIndex: idx}, caller: &messageLogger{msgid: msgid, caller: caller}, } matcherLock.Lock() @@ -237,13 +241,13 @@ func processEventAsync(response []byte, caller APICaller, maxwait time.Duration) hasMatcherListChanged = false } matcherLock.Unlock() - go match(ctx, matcherListForRanging, maxwait) + go match(ctx, idx, matcherListForRanging, maxwait) } // match 匹配规则,处理事件 -func match(ctx *Ctx, matchers []*Matcher, maxwait time.Duration) { +func match(ctx *Ctx, idx uintptr, matchers []*Matcher, maxwait time.Duration) { if BotConfig.MarkMessage && ctx.Event.MessageID != nil { - ctx.MarkThisMessageAsRead() + go ctx.MarkThisMessageAsRead() } gorule := func(rule Rule) <-chan bool { ch := make(chan bool, 1) @@ -251,7 +255,7 @@ func match(ctx *Ctx, matchers []*Matcher, maxwait time.Duration) { defer func() { close(ch) if pa := recover(); pa != nil { - log.Errorf("[bot] execute rule err: %v\n%v", pa, helper.BytesToString(debug.Stack())) + log.Errorf("[bot] [%d] execute rule err: %v\n%v", idx, pa, helper.BytesToString(debug.Stack())) } }() ch <- rule(ctx) @@ -264,7 +268,7 @@ func match(ctx *Ctx, matchers []*Matcher, maxwait time.Duration) { defer func() { close(ch) if pa := recover(); pa != nil { - log.Errorf("[bot] execute handler err: %v\n%v", pa, helper.BytesToString(debug.Stack())) + log.Errorf("[bot] [%d] execute handler err: %v\n%v", idx, pa, helper.BytesToString(debug.Stack())) } }() h(ctx) @@ -303,9 +307,10 @@ loop: case <-t.C: if m.NoTimeout { // 不设超时限制 t.Reset(maxwait) + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "preHandler 处理达到最大时延, 但用户禁止退出") continue } - log.Warnln("[bot] preHandler 处理达到最大时延, 退出") + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "preHandler 处理达到最大时延, 退出") break loop } break @@ -327,9 +332,10 @@ loop: case <-t.C: if m.NoTimeout { // 不设超时限制 t.Reset(maxwait) + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "rule 处理达到最大时延, 但用户禁止退出") continue } - log.Warnln("[bot] rule 处理达到最大时延, 退出") + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "rule 处理达到最大时延, 退出") break loop } break @@ -352,9 +358,10 @@ loop: case <-t.C: if m.NoTimeout { // 不设超时限制 t.Reset(maxwait) + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "midHandler 处理达到最大时延, 但用户禁止退出") continue } - log.Warnln("[bot] midHandler 处理达到最大时延, 退出") + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "midHandler 处理达到最大时延, 退出") break loop } break @@ -367,19 +374,22 @@ loop: } if m.Handler != nil { - c := gohandler(m.Handler) - for { - select { - case <-c: // 处理事件 - case <-t.C: - if m.NoTimeout { // 不设超时限制 - t.Reset(maxwait) - continue + for _, handler := range m.Handler { + c := gohandler(handler) + for { + select { + case <-c: // 处理事件 + case <-t.C: + if m.NoTimeout { // 不设超时限制 + t.Reset(maxwait) + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "Handler 处理达到最大时延, 但用户禁止退出") + continue + } + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "Handler 处理达到最大时延, 退出") + break loop } - log.Warnln("[bot] Handler 处理达到最大时延, 退出") - break loop + break } - break } } @@ -393,9 +403,10 @@ loop: case <-t.C: if m.NoTimeout { // 不设超时限制 t.Reset(maxwait) + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "postHandler 处理达到最大时延, 但用户禁止退出") continue } - log.Warnln("[bot] postHandler 处理达到最大时延, 退出") + log.Warnln("[bot]", "["+strconv.FormatUint(uint64(idx), 10)+"]", "postHandler 处理达到最大时延, 退出") break loop } break @@ -410,7 +421,7 @@ loop: } // preprocessMessageEvent 返回信息事件 -func preprocessMessageEvent(e *Event) { +func preprocessMessageEvent(e *Event, idx uintptr) { msgs := message.ParseMessage(e.NativeMessage) if len(msgs) > 0 { @@ -461,14 +472,14 @@ func preprocessMessageEvent(e *Event) { switch { case e.DetailType == "group": - log.Infof("[bot] 收到群(%v)消息 %v : %v", e.GroupID, e.Sender.String(), e.RawMessage) + log.Infof("[bot] [%d] 收到群(%v)消息 %v : %v", idx, e.GroupID, e.Sender.String(), e.RawMessage) processAt() case e.DetailType == "guild" && e.SubType == "channel": - log.Infof("[bot] 收到频道(%v)(%v-%v)消息 %v : %v", e.GroupID, e.GuildID, e.ChannelID, e.Sender.String(), e.Message) + log.Infof("[bot] [%d] 收到频道(%v)(%v-%v)消息 %v : %v", idx, e.GroupID, e.GuildID, e.ChannelID, e.Sender.String(), e.Message) processAt() default: e.IsToMe = true // 私聊也判断为at - log.Infof("[bot] 收到私聊消息 %v : %v", e.Sender.String(), e.RawMessage) + log.Infof("[bot] [%d] 收到私聊消息 %v : %v", idx, e.Sender.String(), e.RawMessage) } if len(e.Message) > 0 && e.Message[0].Type == "text" { // Trim Again! e.Message[0].Data["text"] = strings.TrimLeft(e.Message[0].Data["text"], " ") diff --git a/event_channel.go b/event_channel.go index 677ff297..fbe5c47d 100644 --- a/event_channel.go +++ b/event_channel.go @@ -41,14 +41,14 @@ func (n *FutureEvent) Next() <-chan *Ctx { Priority: n.Priority, Rules: n.Rule, Engine: defaultEngine, - Handler: func(ctx *Ctx) { + Handler: []Handler{func(ctx *Ctx) { // 使用 go func 异步发送,确保不阻塞主线程 go func() { defer func() { _ = recover() }() ch <- ctx close(ch) }() - }, + }}, }) return ch } @@ -68,13 +68,13 @@ func (n *FutureEvent) Repeat() (recv <-chan *Ctx, cancel func()) { Priority: n.Priority, Rules: n.Rule, Engine: defaultEngine, - Handler: func(ctx *Ctx) { + Handler: []Handler{func(ctx *Ctx) { // 只要 Consumer 处理不是极度滞后,这种方式就能防止 Bot 核心被阻塞 go func() { defer func() { _ = recover() }() in <- ctx }() - }, + }}, }) for { select { diff --git a/go.mod b/go.mod index 72d2104d..3d24c8b3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438 github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 - github.com/sirupsen/logrus v1.9.3 + github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 github.com/syndtr/goleveldb v1.0.0 github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 diff --git a/go.sum b/go.sum index 48451d85..1b652d53 100644 --- a/go.sum +++ b/go.sum @@ -4,7 +4,6 @@ github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMN github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w= github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438 h1:I0bdwHZ+2DY45b39xPoTD2u+Z8zhvBuu9aZfjMZeiZM= github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438/go.mod h1:GO+9i5UYB4BuZEel6BfGx7O1u3ggwgZWUnGxPATUoTE= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -24,12 +23,10 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= @@ -48,7 +45,6 @@ golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -61,6 +57,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/matcher.go b/matcher.go index 69332caa..9af7328d 100644 --- a/matcher.go +++ b/matcher.go @@ -31,7 +31,7 @@ type Matcher struct { // Rules 匹配规则 Rules []Rule // Handler 处理事件的函数 - Handler Handler + Handler []Handler // Engine 注册 Matcher 的 Engine,Engine可为一系列 Matcher 添加通用 Rule 和 其他钩子 Engine *Engine } @@ -140,7 +140,7 @@ func (m *Matcher) copy() *Matcher { } // Handle 直接处理事件 -func (m *Matcher) Handle(handler Handler) *Matcher { +func (m *Matcher) Handle(handler ...Handler) *Matcher { m.Handler = handler return m } diff --git a/matcher_test.go b/matcher_test.go index 2742cc37..ed30e490 100644 --- a/matcher_test.go +++ b/matcher_test.go @@ -25,7 +25,7 @@ func Test_sortMatcher(t *testing.T) { ctx := &Ctx{} var result []int for _, m := range matcherList { - m.Handler(ctx) + m.Handler[0](ctx) number, err := strconv.Atoi(ctx.message) if err != nil { // should not happen