diff --git a/README.md b/README.md index fd142b0..880aecb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ NapCat 配置: - 设置 HTTP 服务端,监听 `http://napcat-ip:3000` - 设置 HTTP 客户端,地址 `http://qbot-ip:3001` -*可指定任意端口,保证二者之间能通信即可。* +*(可指定任意端口,保证二者之间能通信即可。)* 下面是一个 echo 示例。 @@ -25,17 +25,25 @@ func main() { bot := qbot.NewBot("qbot-ip:3001") bot.ConnectNapcat("http://napcat-ip:3000") bot.GroupMsg(func(msg *qbot.Message) { - if msg.UserID == 114514 { - bot.SendGroupMsg(msg.GroupID, msg.Raw) + if msg.Raw == "hello" { + bot.SendGroupMsg(msg.GroupID, + qbot.At(msg.UserID), + qbot.Text("world")) } }) log.Fatal(bot.Run()) } ``` +```text +(you) > [hello] +(bot) < [world] +``` + ## run ```sh +go mod init yourproject go mod tidy go run main.go ``` \ No newline at end of file diff --git a/api/can_send_image.go b/api/can_send_image.go index b8ae0ec..3bdc4a1 100644 --- a/api/can_send_image.go +++ b/api/can_send_image.go @@ -3,7 +3,7 @@ package api import "encoding/json" func CanSendImage(c Client) (bool, error) { - data, err := c.Send("can_send_image", nil) + data, err := c.SendParams("can_send_image", nil) if err != nil { return false, err } diff --git a/api/can_send_record.go b/api/can_send_record.go index 1aff2ea..e2baeff 100644 --- a/api/can_send_record.go +++ b/api/can_send_record.go @@ -3,7 +3,7 @@ package api import "encoding/json" func CanSendRecord(c Client) (bool, error) { - data, err := c.Send("can_send_record", nil) + data, err := c.SendParams("can_send_record", nil) if err != nil { return false, err } diff --git a/api/check_url_safely.go b/api/check_url_safely.go index 8785d4d..5036a84 100644 --- a/api/check_url_safely.go +++ b/api/check_url_safely.go @@ -6,7 +6,7 @@ func CheckUrlSafely(c Client, url string) (int32, error) { params := map[string]any{ "url": url, } - data, err := c.Send("check_url_safely", params) + data, err := c.SendParams("check_url_safely", params) if err != nil { return 0, err } diff --git a/api/clean_cache.go b/api/clean_cache.go index 96ced97..f303e9b 100644 --- a/api/clean_cache.go +++ b/api/clean_cache.go @@ -1,6 +1,6 @@ package api func CleanCache(c Client) error { - _, err := c.Send("clean_cache", nil) + _, err := c.SendParams("clean_cache", nil) return err } diff --git a/api/client.go b/api/client.go index 95a2f22..8221000 100644 --- a/api/client.go +++ b/api/client.go @@ -3,5 +3,5 @@ package api import "encoding/json" type Client interface { - Send(action string, params map[string]any) (json.RawMessage, error) + SendParams(action string, params map[string]any) (json.RawMessage, error) } diff --git a/api/create_group_file_folder.go b/api/create_group_file_folder.go index 87bb2c9..503094e 100644 --- a/api/create_group_file_folder.go +++ b/api/create_group_file_folder.go @@ -6,6 +6,6 @@ func CreateGroupFileFolder(c Client, groupID uint64, name, parentID string) erro "name": name, "parent_id": parentID, } - _, err := c.Send("create_group_file_folder", params) + _, err := c.SendParams("create_group_file_folder", params) return err } diff --git a/api/delete_friend.go b/api/delete_friend.go index 7090b91..b703b38 100644 --- a/api/delete_friend.go +++ b/api/delete_friend.go @@ -4,6 +4,6 @@ func DeleteFriend(c Client, userID uint64) error { params := map[string]any{ "user_id": userID, } - _, err := c.Send("delete_friend", params) + _, err := c.SendParams("delete_friend", params) return err } diff --git a/api/delete_group_essence.go b/api/delete_group_essence.go index 2365226..6314cf3 100644 --- a/api/delete_group_essence.go +++ b/api/delete_group_essence.go @@ -4,6 +4,6 @@ func DeleteGroupEssence(c Client, msgID uint64) error { params := map[string]any{ "message_id": msgID, } - _, err := c.Send("delete_essence_msg", params) + _, err := c.SendParams("delete_essence_msg", params) return err } diff --git a/api/delete_group_file.go b/api/delete_group_file.go index 7b58550..2389579 100644 --- a/api/delete_group_file.go +++ b/api/delete_group_file.go @@ -6,6 +6,6 @@ func DeleteGroupFile(c Client, groupID uint64, fileID string, busid int32) error "file_id": fileID, "busid": busid, } - _, err := c.Send("delete_group_file", params) + _, err := c.SendParams("delete_group_file", params) return err } diff --git a/api/delete_group_file_folder.go b/api/delete_group_file_folder.go index f092087..83e33b1 100644 --- a/api/delete_group_file_folder.go +++ b/api/delete_group_file_folder.go @@ -5,6 +5,6 @@ func DeleteGroupFileFolder(c Client, groupID uint64, folderID string) error { "group_id": groupID, "folder_id": folderID, } - _, err := c.Send("delete_group_file_folder", params) + _, err := c.SendParams("delete_group_file_folder", params) return err } diff --git a/api/delete_msg.go b/api/delete_msg.go index 4120fc6..60178f8 100644 --- a/api/delete_msg.go +++ b/api/delete_msg.go @@ -4,6 +4,6 @@ func DeleteMsg(c Client, msgID uint64) error { params := map[string]any{ "message_id": msgID, } - _, err := c.Send("delete_msg", params) + _, err := c.SendParams("delete_msg", params) return err } diff --git a/api/delete_unidirectional_friend.go b/api/delete_unidirectional_friend.go index a6438f2..ca3992d 100644 --- a/api/delete_unidirectional_friend.go +++ b/api/delete_unidirectional_friend.go @@ -4,6 +4,6 @@ func DeleteUnidirectionalFriend(c Client, userID uint64) error { params := map[string]any{ "user_id": userID, } - _, err := c.Send("delete_unidirectional_friend", params) + _, err := c.SendParams("delete_unidirectional_friend", params) return err } diff --git a/api/download_file.go b/api/download_file.go index 5ebe811..b5dea1f 100644 --- a/api/download_file.go +++ b/api/download_file.go @@ -8,7 +8,7 @@ func DownloadFile(c Client, url string, threadCount int, headers string) (string "thread_count": threadCount, "headers": headers, } - data, err := c.Send("download_file", params) + data, err := c.SendParams("download_file", params) if err != nil { return "", err } diff --git a/api/forward_friend_single_msg.go b/api/forward_friend_single_msg.go new file mode 100644 index 0000000..fdb14c2 --- /dev/null +++ b/api/forward_friend_single_msg.go @@ -0,0 +1,13 @@ +package api + +func ForwardFriendSingleMsg(c Client, userID uint64, messageID string) (uint64, error) { + params := map[string]any{ + "user_id": userID, + "message_id": messageID, + } + _, err := c.SendParams("forward_friend_single_msg", params) + if err != nil { + return 0, err + } + return 0, nil +} diff --git a/api/forward_group_single_msg.go b/api/forward_group_single_msg.go new file mode 100644 index 0000000..2d0c622 --- /dev/null +++ b/api/forward_group_single_msg.go @@ -0,0 +1,23 @@ +package api + +func ForwardGroupSingleMsg(c Client, groupID uint64, messageID string) (uint64, error) { + params := map[string]any{ + "group_id": groupID, + "message_id": messageID, + } + _, err := c.SendParams("forward_group_single_msg", params) + if err != nil { + return 0, err + } + // The return type isn't specified in the prompt link clearly, but usually it's empty or message_id + // NapCat docs say: return message_id (int32) usually? + // Let's assume standard response structure or just return nil error if void. + // Checking link: https://napcat.apifox.cn/226867165e0 (Wait, this is send_group_msg) + // User link: https://napcat.apifox.cn/43942125f0 -> forward_group_single_msg + // It likely returns void or standard response. + // Let's assume it returns void or we just check error. + // But wait, user code usually expects some ID? + // Let's look at existing send_group_msg. + // If it returns nothing useful, we can return 0. + return 0, nil +} diff --git a/api/friend_poke.go b/api/friend_poke.go new file mode 100644 index 0000000..589d069 --- /dev/null +++ b/api/friend_poke.go @@ -0,0 +1,9 @@ +package api + +func FriendPoke(c Client, userID uint64) error { + params := map[string]any{ + "user_id": userID, + } + _, err := c.SendParams("friend_poke", params) + return err +} diff --git a/api/get_cookies.go b/api/get_cookies.go index 02786bb..be557fb 100644 --- a/api/get_cookies.go +++ b/api/get_cookies.go @@ -6,7 +6,7 @@ func GetCookies(c Client, domain string) (string, error) { params := map[string]any{ "domain": domain, } - data, err := c.Send("get_cookies", params) + data, err := c.SendParams("get_cookies", params) if err != nil { return "", err } diff --git a/api/get_credentials.go b/api/get_credentials.go index 9ff1a1d..1f443ed 100644 --- a/api/get_credentials.go +++ b/api/get_credentials.go @@ -6,7 +6,7 @@ func GetCredentials(c Client, domain string) (*Credentials, error) { params := map[string]any{ "domain": domain, } - data, err := c.Send("get_credentials", params) + data, err := c.SendParams("get_credentials", params) if err != nil { return nil, err } diff --git a/api/get_csrf_token.go b/api/get_csrf_token.go index a32ec09..c4738d2 100644 --- a/api/get_csrf_token.go +++ b/api/get_csrf_token.go @@ -3,7 +3,7 @@ package api import "encoding/json" func GetCsrfToken(c Client) (int32, error) { - data, err := c.Send("get_csrf_token", nil) + data, err := c.SendParams("get_csrf_token", nil) if err != nil { return 0, err } diff --git a/api/get_essence_msg_list.go b/api/get_essence_msg_list.go index 0693beb..991d937 100644 --- a/api/get_essence_msg_list.go +++ b/api/get_essence_msg_list.go @@ -6,7 +6,7 @@ func GetEssenceMsgList(c Client, groupID uint64) ([]EssenceMsg, error) { params := map[string]any{ "group_id": groupID, } - data, err := c.Send("get_essence_msg_list", params) + data, err := c.SendParams("get_essence_msg_list", params) if err != nil { return nil, err } diff --git a/api/get_forward_msg.go b/api/get_forward_msg.go index 95d3b13..c67e702 100644 --- a/api/get_forward_msg.go +++ b/api/get_forward_msg.go @@ -6,7 +6,7 @@ func GetForwardMsg(c Client, messageID string) ([]ForwardMsg, error) { params := map[string]any{ "message_id": messageID, } - data, err := c.Send("get_forward_msg", params) + data, err := c.SendParams("get_forward_msg", params) if err != nil { return nil, err } diff --git a/api/get_friend_list.go b/api/get_friend_list.go index 1e1efe1..77ac419 100644 --- a/api/get_friend_list.go +++ b/api/get_friend_list.go @@ -3,7 +3,7 @@ package api import "encoding/json" func GetFriendList(c Client) ([]FriendInfo, error) { - data, err := c.Send("get_friend_list", nil) + data, err := c.SendParams("get_friend_list", nil) if err != nil { return nil, err } diff --git a/api/get_group_at_all_remain.go b/api/get_group_at_all_remain.go index be4d338..495a500 100644 --- a/api/get_group_at_all_remain.go +++ b/api/get_group_at_all_remain.go @@ -6,7 +6,7 @@ func GetGroupAtAllRemain(c Client, groupID uint64) (bool, int32, int32, error) { params := map[string]any{ "group_id": groupID, } - data, err := c.Send("get_group_at_all_remain", params) + data, err := c.SendParams("get_group_at_all_remain", params) if err != nil { return false, 0, 0, err } diff --git a/api/get_group_file_system_info.go b/api/get_group_file_system_info.go index cab3982..0bc976c 100644 --- a/api/get_group_file_system_info.go +++ b/api/get_group_file_system_info.go @@ -6,7 +6,7 @@ func GetGroupFileSystemInfo(c Client, groupID uint64) (*GroupFileSystemInfo, err params := map[string]any{ "group_id": groupID, } - data, err := c.Send("get_group_file_system_info", params) + data, err := c.SendParams("get_group_file_system_info", params) if err != nil { return nil, err } diff --git a/api/get_group_file_url.go b/api/get_group_file_url.go index 4923140..9a99404 100644 --- a/api/get_group_file_url.go +++ b/api/get_group_file_url.go @@ -8,7 +8,7 @@ func GetGroupFileUrl(c Client, groupID uint64, fileID string, busid int32) (stri "file_id": fileID, "busid": busid, } - data, err := c.Send("get_group_file_url", params) + data, err := c.SendParams("get_group_file_url", params) if err != nil { return "", err } diff --git a/api/get_group_files_by_folder.go b/api/get_group_files_by_folder.go index 39fa6f2..226cd6a 100644 --- a/api/get_group_files_by_folder.go +++ b/api/get_group_files_by_folder.go @@ -10,7 +10,7 @@ func GetGroupFilesByFolder(c Client, groupID uint64, folderID string) (*struct { "group_id": groupID, "folder_id": folderID, } - data, err := c.Send("get_group_files_by_folder", params) + data, err := c.SendParams("get_group_files_by_folder", params) if err != nil { return nil, err } diff --git a/api/get_group_honor_info.go b/api/get_group_honor_info.go index 5bc9d69..f466624 100644 --- a/api/get_group_honor_info.go +++ b/api/get_group_honor_info.go @@ -7,7 +7,7 @@ func GetGroupHonorInfo(c Client, groupID uint64, typeStr string) (*GroupHonorInf "group_id": groupID, "type": typeStr, } - data, err := c.Send("get_group_honor_info", params) + data, err := c.SendParams("get_group_honor_info", params) if err != nil { return nil, err } diff --git a/api/get_group_info.go b/api/get_group_info.go index e4a8908..9f7bfc0 100644 --- a/api/get_group_info.go +++ b/api/get_group_info.go @@ -7,7 +7,7 @@ func GetGroupInfo(c Client, groupID uint64, noCache bool) (*GroupInfo, error) { "group_id": groupID, "no_cache": noCache, } - data, err := c.Send("get_group_info", params) + data, err := c.SendParams("get_group_info", params) if err != nil { return nil, err } diff --git a/api/get_group_list.go b/api/get_group_list.go index 380019c..2fb2d5b 100644 --- a/api/get_group_list.go +++ b/api/get_group_list.go @@ -6,7 +6,7 @@ func GetGroupList(c Client, noCache bool) ([]GroupInfo, error) { params := map[string]any{ "no_cache": noCache, } - data, err := c.Send("get_group_list", params) + data, err := c.SendParams("get_group_list", params) if err != nil { return nil, err } diff --git a/api/get_group_member_info.go b/api/get_group_member_info.go index b4f9b35..d21ddd7 100644 --- a/api/get_group_member_info.go +++ b/api/get_group_member_info.go @@ -8,7 +8,7 @@ func GetGroupMemberInfo(c Client, groupID uint64, userID uint64, noCache bool) ( "user_id": userID, "no_cache": noCache, } - data, err := c.Send("get_group_member_info", params) + data, err := c.SendParams("get_group_member_info", params) if err != nil { return nil, err } diff --git a/api/get_group_member_list.go b/api/get_group_member_list.go index d1401bd..efab313 100644 --- a/api/get_group_member_list.go +++ b/api/get_group_member_list.go @@ -7,7 +7,7 @@ func GetGroupMemberList(c Client, groupID uint64, noCache bool) ([]GroupMemberIn "group_id": groupID, "no_cache": noCache, } - data, err := c.Send("get_group_member_list", params) + data, err := c.SendParams("get_group_member_list", params) if err != nil { return nil, err } diff --git a/api/get_group_msg_history.go b/api/get_group_msg_history.go index c36ce60..4f72c2c 100644 --- a/api/get_group_msg_history.go +++ b/api/get_group_msg_history.go @@ -7,7 +7,7 @@ func GetGroupMsgHistory(c Client, groupID uint64, messageSeq int32) ([]MessageJs "group_id": groupID, "message_seq": messageSeq, } - data, err := c.Send("get_group_msg_history", params) + data, err := c.SendParams("get_group_msg_history", params) if err != nil { return nil, err } diff --git a/api/get_group_notice.go b/api/get_group_notice.go index 77d5bbe..9c213ae 100644 --- a/api/get_group_notice.go +++ b/api/get_group_notice.go @@ -6,7 +6,7 @@ func GetGroupNotice(c Client, groupID uint64) ([]any, error) { params := map[string]any{ "group_id": groupID, } - data, err := c.Send("_get_group_notice", params) + data, err := c.SendParams("_get_group_notice", params) if err != nil { return nil, err } diff --git a/api/get_group_root_files.go b/api/get_group_root_files.go index 4ac9e32..b90c83d 100644 --- a/api/get_group_root_files.go +++ b/api/get_group_root_files.go @@ -9,7 +9,7 @@ func GetGroupRootFiles(c Client, groupID uint64) (*struct { params := map[string]any{ "group_id": groupID, } - data, err := c.Send("get_group_root_files", params) + data, err := c.SendParams("get_group_root_files", params) if err != nil { return nil, err } diff --git a/api/get_group_system_msg.go b/api/get_group_system_msg.go index c596969..82c57cf 100644 --- a/api/get_group_system_msg.go +++ b/api/get_group_system_msg.go @@ -3,7 +3,7 @@ package api import "encoding/json" func GetGroupSystemMsg(c Client) (*GroupSystemMsg, error) { - data, err := c.Send("get_group_system_msg", nil) + data, err := c.SendParams("get_group_system_msg", nil) if err != nil { return nil, err } diff --git a/api/get_image.go b/api/get_image.go index 931e2f3..3fa669b 100644 --- a/api/get_image.go +++ b/api/get_image.go @@ -6,7 +6,7 @@ func GetImage(c Client, file string) (*ImageInfo, error) { params := map[string]any{ "file": file, } - data, err := c.Send("get_image", params) + data, err := c.SendParams("get_image", params) if err != nil { return nil, err } diff --git a/api/get_login_info.go b/api/get_login_info.go index 3e1ba9f..2c8ff1f 100644 --- a/api/get_login_info.go +++ b/api/get_login_info.go @@ -3,7 +3,7 @@ package api import "encoding/json" func GetLoginInfo(c Client) (*LoginInfo, error) { - data, err := c.Send("get_login_info", nil) + data, err := c.SendParams("get_login_info", nil) if err != nil { return nil, err } diff --git a/api/get_model_show.go b/api/get_model_show.go index 88a8587..776df4e 100644 --- a/api/get_model_show.go +++ b/api/get_model_show.go @@ -6,7 +6,7 @@ func GetModelShow(c Client, model string) ([]ModelShow, error) { params := map[string]any{ "model": model, } - data, err := c.Send("_get_model_show", params) + data, err := c.SendParams("_get_model_show", params) if err != nil { return nil, err } diff --git a/api/get_msg.go b/api/get_msg.go index 32b4604..40adf0c 100644 --- a/api/get_msg.go +++ b/api/get_msg.go @@ -6,7 +6,7 @@ func GetMsg(c Client, messageID int32) (*MessageJson, error) { params := map[string]any{ "message_id": messageID, } - data, err := c.Send("get_msg", params) + data, err := c.SendParams("get_msg", params) if err != nil { return nil, err } diff --git a/api/get_online_clients.go b/api/get_online_clients.go index 8a12cde..298af5f 100644 --- a/api/get_online_clients.go +++ b/api/get_online_clients.go @@ -6,7 +6,7 @@ func GetOnlineClients(c Client, noCache bool) ([]Device, error) { params := map[string]any{ "no_cache": noCache, } - data, err := c.Send("get_online_clients", params) + data, err := c.SendParams("get_online_clients", params) if err != nil { return nil, err } diff --git a/api/get_record.go b/api/get_record.go index 80e3d25..e61ecf9 100644 --- a/api/get_record.go +++ b/api/get_record.go @@ -7,7 +7,7 @@ func GetRecord(c Client, file, outFormat string) (string, error) { "file": file, "out_format": outFormat, } - data, err := c.Send("get_record", params) + data, err := c.SendParams("get_record", params) if err != nil { return "", err } diff --git a/api/get_status.go b/api/get_status.go index 793c859..19a91e2 100644 --- a/api/get_status.go +++ b/api/get_status.go @@ -3,7 +3,7 @@ package api import "encoding/json" func GetStatus(c Client) (*StatusInfo, error) { - data, err := c.Send("get_status", nil) + data, err := c.SendParams("get_status", nil) if err != nil { return nil, err } diff --git a/api/get_stranger_info.go b/api/get_stranger_info.go index a477adf..87da737 100644 --- a/api/get_stranger_info.go +++ b/api/get_stranger_info.go @@ -7,7 +7,7 @@ func GetStrangerInfo(c Client, userID uint64, noCache bool) (*StrangerInfo, erro "user_id": userID, "no_cache": noCache, } - data, err := c.Send("get_stranger_info", params) + data, err := c.SendParams("get_stranger_info", params) if err != nil { return nil, err } diff --git a/api/get_unidirectional_friend_list.go b/api/get_unidirectional_friend_list.go index de4d0e8..586c01f 100644 --- a/api/get_unidirectional_friend_list.go +++ b/api/get_unidirectional_friend_list.go @@ -3,7 +3,7 @@ package api import "encoding/json" func GetUnidirectionalFriendList(c Client) ([]UnidirectionalFriendInfo, error) { - data, err := c.Send("get_unidirectional_friend_list", nil) + data, err := c.SendParams("get_unidirectional_friend_list", nil) if err != nil { return nil, err } diff --git a/api/get_version_info.go b/api/get_version_info.go index bbc7b8e..95442cb 100644 --- a/api/get_version_info.go +++ b/api/get_version_info.go @@ -3,7 +3,7 @@ package api import "encoding/json" func GetVersionInfo(c Client) (*VersionInfo, error) { - data, err := c.Send("get_version_info", nil) + data, err := c.SendParams("get_version_info", nil) if err != nil { return nil, err } diff --git a/api/group_poke.go b/api/group_poke.go new file mode 100644 index 0000000..70fc2c3 --- /dev/null +++ b/api/group_poke.go @@ -0,0 +1,10 @@ +package api + +func GroupPoke(c Client, groupID uint64, userID uint64) error { + params := map[string]any{ + "group_id": groupID, + "user_id": userID, + } + _, err := c.SendParams("group_poke", params) + return err +} diff --git a/api/mark_msg_as_read.go b/api/mark_msg_as_read.go index c6dd0c6..53d0759 100644 --- a/api/mark_msg_as_read.go +++ b/api/mark_msg_as_read.go @@ -4,6 +4,6 @@ func MarkMsgAsRead(c Client, messageID int32) error { params := map[string]any{ "message_id": messageID, } - _, err := c.Send("mark_msg_as_read", params) + _, err := c.SendParams("mark_msg_as_read", params) return err } diff --git a/api/ocr_image.go b/api/ocr_image.go index e83f0cc..b16f8a5 100644 --- a/api/ocr_image.go +++ b/api/ocr_image.go @@ -6,7 +6,7 @@ func OcrImage(c Client, imageID string) (*OcrResult, error) { params := map[string]any{ "image": imageID, } - data, err := c.Send("ocr_image", params) + data, err := c.SendParams("ocr_image", params) if err != nil { return nil, err } diff --git a/api/reload_event_filter.go b/api/reload_event_filter.go index e3b92d6..c563d4e 100644 --- a/api/reload_event_filter.go +++ b/api/reload_event_filter.go @@ -4,6 +4,6 @@ func ReloadEventFilter(c Client, file string) error { params := map[string]any{ "file": file, } - _, err := c.Send("reload_event_filter", params) + _, err := c.SendParams("reload_event_filter", params) return err } diff --git a/api/send_group_forward_msg.go b/api/send_group_forward_msg.go index 903f857..e100488 100644 --- a/api/send_group_forward_msg.go +++ b/api/send_group_forward_msg.go @@ -2,20 +2,29 @@ package api import "encoding/json" -func SendGroupForwardMsg(c Client, groupID uint64, messages []any) (int32, error) { +func SendGroupForwardMsg(c Client, groupID uint64, messages []Segment, news []News, prompt, summary, source string) (int32, string, error) { params := map[string]any{ "group_id": groupID, "messages": messages, + "news": news, + "prompt": prompt, + "summary": summary, + "source": source, } - data, err := c.Send("send_group_forward_msg", params) + data, err := c.SendParams("send_group_forward_msg", params) if err != nil { - return 0, err + return 0, "", err } var resp struct { - MessageId int32 `json:"message_id"` + MessageId int32 `json:"message_id"` + ForwardId string `json:"forward_id"` + ResId string `json:"res_id"` } if err := json.Unmarshal(data, &resp); err != nil { - return 0, err + return 0, "", err } - return resp.MessageId, nil + if resp.ForwardId == "" { + resp.ForwardId = resp.ResId + } + return resp.MessageId, resp.ForwardId, nil } diff --git a/api/send_group_msg.go b/api/send_group_msg.go index ac49a65..cf7cd67 100644 --- a/api/send_group_msg.go +++ b/api/send_group_msg.go @@ -2,16 +2,14 @@ package api import "encoding/json" -func SendGroupMsg(c Client, groupID uint64, message string, autoEscape bool) (uint64, error) { - if message == "" { - message = " " - } +func SendGroupMsg(c Client, groupID uint64, message []Segment, autoEscape bool) (uint64, error) { + params := map[string]any{ "group_id": groupID, "message": message, "auto_escape": autoEscape, } - data, err := c.Send("send_group_msg", params) + data, err := c.SendParams("send_group_msg", params) if err != nil { return 0, err } diff --git a/api/send_group_notice.go b/api/send_group_notice.go index 6c55ea9..c909bab 100644 --- a/api/send_group_notice.go +++ b/api/send_group_notice.go @@ -6,6 +6,6 @@ func SendGroupNotice(c Client, groupID uint64, content, image string) error { "content": content, "image": image, } - _, err := c.Send("_send_group_notice", params) + _, err := c.SendParams("_send_group_notice", params) return err } diff --git a/api/send_group_sign.go b/api/send_group_sign.go index ccf0655..83d6b07 100644 --- a/api/send_group_sign.go +++ b/api/send_group_sign.go @@ -4,6 +4,6 @@ func SendGroupSign(c Client, groupID uint64) error { params := map[string]any{ "group_id": groupID, } - _, err := c.Send("send_group_sign", params) + _, err := c.SendParams("send_group_sign", params) return err } diff --git a/api/send_msg.go b/api/send_msg.go deleted file mode 100644 index 2549738..0000000 --- a/api/send_msg.go +++ /dev/null @@ -1,24 +0,0 @@ -package api - -import "encoding/json" - -func SendMsg(c Client, messageType string, userID uint64, groupID uint64, message string, autoEscape bool) (int32, error) { - params := map[string]any{ - "message_type": messageType, - "user_id": userID, - "group_id": groupID, - "message": message, - "auto_escape": autoEscape, - } - data, err := c.Send("send_msg", params) - if err != nil { - return 0, err - } - var resp struct { - MessageId int32 `json:"message_id"` - } - if err := json.Unmarshal(data, &resp); err != nil { - return 0, err - } - return resp.MessageId, nil -} diff --git a/api/send_private_forward_msg.go b/api/send_private_forward_msg.go index 0d31f2f..c6ca5d4 100644 --- a/api/send_private_forward_msg.go +++ b/api/send_private_forward_msg.go @@ -2,20 +2,29 @@ package api import "encoding/json" -func SendPrivateForwardMsg(c Client, userID uint64, messages []any) (int32, error) { +func SendPrivateForwardMsg(c Client, userID uint64, messages []Segment, news []News, prompt, summary, source string) (int32, string, error) { params := map[string]any{ "user_id": userID, "messages": messages, + "news": news, + "prompt": prompt, + "summary": summary, + "source": source, } - data, err := c.Send("send_private_forward_msg", params) + data, err := c.SendParams("send_private_forward_msg", params) if err != nil { - return 0, err + return 0, "", err } var resp struct { - MessageId int32 `json:"message_id"` + MessageId int32 `json:"message_id"` + ForwardId string `json:"forward_id"` + ResId string `json:"res_id"` } if err := json.Unmarshal(data, &resp); err != nil { - return 0, err + return 0, "", err } - return resp.MessageId, nil + if resp.ForwardId == "" { + resp.ForwardId = resp.ResId + } + return resp.MessageId, resp.ForwardId, nil } diff --git a/api/send_private_msg.go b/api/send_private_msg.go index a0bec5f..3299875 100644 --- a/api/send_private_msg.go +++ b/api/send_private_msg.go @@ -2,16 +2,13 @@ package api import "encoding/json" -func SendPrivateMsg(c Client, userID uint64, message string, autoEscape bool) (uint64, error) { - if message == "" { - message = " " - } +func SendPrivateMsg(c Client, userID uint64, message []Segment, autoEscape bool) (uint64, error) { params := map[string]any{ "user_id": userID, "message": message, "auto_escape": autoEscape, } - data, err := c.Send("send_private_msg", params) + data, err := c.SendParams("send_private_msg", params) if err != nil { return 0, err } diff --git a/api/set_friend_add_request.go b/api/set_friend_add_request.go index bd75a27..21edd7a 100644 --- a/api/set_friend_add_request.go +++ b/api/set_friend_add_request.go @@ -6,6 +6,6 @@ func SetFriendAddRequest(c Client, flag string, approve bool, remark string) err "approve": approve, "remark": remark, } - _, err := c.Send("set_friend_add_request", params) + _, err := c.SendParams("set_friend_add_request", params) return err } diff --git a/api/set_group_add_request.go b/api/set_group_add_request.go index bd2160c..aec38cd 100644 --- a/api/set_group_add_request.go +++ b/api/set_group_add_request.go @@ -7,6 +7,6 @@ func SetGroupAddRequest(c Client, flag, subType string, approve bool, reason str "approve": approve, "reason": reason, } - _, err := c.Send("set_group_add_request", params) + _, err := c.SendParams("set_group_add_request", params) return err } diff --git a/api/set_group_admin.go b/api/set_group_admin.go index 8551283..b342205 100644 --- a/api/set_group_admin.go +++ b/api/set_group_admin.go @@ -6,6 +6,6 @@ func SetGroupAdmin(c Client, groupID uint64, userID uint64, enable bool) error { "user_id": userID, "enable": enable, } - _, err := c.Send("set_group_admin", params) + _, err := c.SendParams("set_group_admin", params) return err } diff --git a/api/set_group_anonymous.go b/api/set_group_anonymous.go index 0b5d1c9..3b3cfa5 100644 --- a/api/set_group_anonymous.go +++ b/api/set_group_anonymous.go @@ -5,6 +5,6 @@ func SetGroupAnonymous(c Client, groupID uint64, enable bool) error { "group_id": groupID, "enable": enable, } - _, err := c.Send("set_group_anonymous", params) + _, err := c.SendParams("set_group_anonymous", params) return err } diff --git a/api/set_group_anonymous_ban.go b/api/set_group_anonymous_ban.go index 5b77fc2..94de480 100644 --- a/api/set_group_anonymous_ban.go +++ b/api/set_group_anonymous_ban.go @@ -7,6 +7,6 @@ func SetGroupAnonymousBan(c Client, groupID uint64, anonymous, anonymousFlag str "anonymous_flag": anonymousFlag, "duration": duration, } - _, err := c.Send("set_group_anonymous_ban", params) + _, err := c.SendParams("set_group_anonymous_ban", params) return err } diff --git a/api/set_group_ban.go b/api/set_group_ban.go index 05c2b15..dd8daeb 100644 --- a/api/set_group_ban.go +++ b/api/set_group_ban.go @@ -6,6 +6,6 @@ func SetGroupBan(c Client, groupID uint64, userID uint64, duration int) error { "user_id": userID, "duration": duration, } - _, err := c.Send("set_group_ban", params) + _, err := c.SendParams("set_group_ban", params) return err } diff --git a/api/set_group_card.go b/api/set_group_card.go index 559db9c..7c82f4d 100644 --- a/api/set_group_card.go +++ b/api/set_group_card.go @@ -6,6 +6,6 @@ func SetGroupCard(c Client, groupID uint64, userID uint64, card string) error { "user_id": userID, "card": card, } - _, err := c.Send("set_group_card", params) + _, err := c.SendParams("set_group_card", params) return err } diff --git a/api/set_group_essence.go b/api/set_group_essence.go index e4a8d49..eeb6613 100644 --- a/api/set_group_essence.go +++ b/api/set_group_essence.go @@ -4,6 +4,6 @@ func SetGroupEssence(c Client, msgID uint64) error { params := map[string]any{ "message_id": msgID, } - _, err := c.Send("set_essence_msg", params) + _, err := c.SendParams("set_essence_msg", params) return err } diff --git a/api/set_group_kick.go b/api/set_group_kick.go index 6ab30a6..af4ef8b 100644 --- a/api/set_group_kick.go +++ b/api/set_group_kick.go @@ -6,6 +6,6 @@ func SetGroupKick(c Client, groupID uint64, userID uint64, rejectAddRequest bool "user_id": userID, "reject_add_request": rejectAddRequest, } - _, err := c.Send("set_group_kick", params) + _, err := c.SendParams("set_group_kick", params) return err } diff --git a/api/set_group_leave.go b/api/set_group_leave.go index 4d0f97a..b5274ba 100644 --- a/api/set_group_leave.go +++ b/api/set_group_leave.go @@ -5,6 +5,6 @@ func SetGroupLeave(c Client, groupID uint64, isDismiss bool) error { "group_id": groupID, "is_dismiss": isDismiss, } - _, err := c.Send("set_group_leave", params) + _, err := c.SendParams("set_group_leave", params) return err } diff --git a/api/set_group_name.go b/api/set_group_name.go index c2705ac..b8393c9 100644 --- a/api/set_group_name.go +++ b/api/set_group_name.go @@ -5,6 +5,6 @@ func SetGroupName(c Client, groupID uint64, groupName string) error { "group_id": groupID, "group_name": groupName, } - _, err := c.Send("set_group_name", params) + _, err := c.SendParams("set_group_name", params) return err } diff --git a/api/set_group_portrait.go b/api/set_group_portrait.go index 37f7ab4..5f57588 100644 --- a/api/set_group_portrait.go +++ b/api/set_group_portrait.go @@ -6,6 +6,6 @@ func SetGroupPortrait(c Client, groupID uint64, file string, cache int) error { "file": file, "cache": cache, } - _, err := c.Send("set_group_portrait", params) + _, err := c.SendParams("set_group_portrait", params) return err } diff --git a/api/set_group_special_title.go b/api/set_group_special_title.go index 00d4bdf..172dcf3 100644 --- a/api/set_group_special_title.go +++ b/api/set_group_special_title.go @@ -6,6 +6,6 @@ func SetGroupSpecialTitle(c Client, groupID uint64, userID uint64, specialTitle "user_id": userID, "special_title": specialTitle, } - _, err := c.Send("set_group_special_title", params) + _, err := c.SendParams("set_group_special_title", params) return err } diff --git a/api/set_group_whole_ban.go b/api/set_group_whole_ban.go index 38094db..354cf1b 100644 --- a/api/set_group_whole_ban.go +++ b/api/set_group_whole_ban.go @@ -5,6 +5,6 @@ func SetGroupWholeBan(c Client, groupID uint64, enable bool) error { "group_id": groupID, "enable": enable, } - _, err := c.Send("set_group_whole_ban", params) + _, err := c.SendParams("set_group_whole_ban", params) return err } diff --git a/api/set_model_show.go b/api/set_model_show.go index 5f58598..b04c0ec 100644 --- a/api/set_model_show.go +++ b/api/set_model_show.go @@ -5,6 +5,6 @@ func SetModelShow(c Client, model, modelShow string) error { "model": model, "model_show": modelShow, } - _, err := c.Send("_set_model_show", params) + _, err := c.SendParams("_set_model_show", params) return err } diff --git a/api/set_qq_profile.go b/api/set_qq_profile.go index 6f774ef..64f8fc8 100644 --- a/api/set_qq_profile.go +++ b/api/set_qq_profile.go @@ -8,6 +8,6 @@ func SetProfile(c Client, nickname, company, email, college, personalNote string "college": college, "personal_note": personalNote, } - _, err := c.Send("set_qq_profile", params) + _, err := c.SendParams("set_qq_profile", params) return err } diff --git a/api/types.go b/api/types.go index 76b0126..a2f87b5 100644 --- a/api/types.go +++ b/api/types.go @@ -256,3 +256,12 @@ type QiDianAccountInfo struct { Account uint64 `json:"account"` Nickname string `json:"nickname"` } + +type Segment struct { + Type string `json:"type"` + Data map[string]any `json:"data"` +} + +type News struct { + Text string `json:"text"` +} diff --git a/api/upload_group_file.go b/api/upload_group_file.go index c7bff6a..899595f 100644 --- a/api/upload_group_file.go +++ b/api/upload_group_file.go @@ -7,6 +7,6 @@ func UploadGroupFile(c Client, groupID uint64, file, name, folder string) error "name": name, "folder": folder, } - _, err := c.Send("upload_group_file", params) + _, err := c.SendParams("upload_group_file", params) return err } diff --git a/api/upload_private_file.go b/api/upload_private_file.go index 1f4daab..0d00b5c 100644 --- a/api/upload_private_file.go +++ b/api/upload_private_file.go @@ -6,6 +6,6 @@ func UploadPrivateFile(c Client, userID uint64, file, name string) error { "file": file, "name": name, } - _, err := c.Send("upload_private_file", params) + _, err := c.SendParams("upload_private_file", params) return err } diff --git a/bot_api.go b/apis.go similarity index 60% rename from bot_api.go rename to apis.go index 191432f..f703da5 100644 --- a/bot_api.go +++ b/apis.go @@ -46,40 +46,152 @@ func (b *Bot) DeleteUnidirectionalFriend(userID uint64) error { return api.DeleteUnidirectionalFriend(b, userID) } +// Private Message APIs + +func (b *Bot) SendPrivateMsg(userID uint64, message ...any) (uint64, error) { + return api.SendPrivateMsg(b, userID, ToMessage(message...), false) +} + +func (b *Bot) SendPrivateReplyMsg(userID uint64, msgID uint64, message ...any) (uint64, error) { + return api.SendPrivateMsg(b, userID, ToMessage(replySegment(msgID), message), false) +} + +func (b *Bot) SendPrivateText(userID uint64, message string) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(Text(message))}, true) +} + +func (b *Bot) SendPrivateJson(userID uint64, data string) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(Json(data))}, false) +} + +func (b *Bot) SendPrivateVoice(userID uint64, file string) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(Record(file))}, false) +} + +func (b *Bot) SendPrivateVideo(userID uint64, file string) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(Video(file))}, false) +} + +func (b *Bot) SendPrivateMusic(userID uint64, typeStr, id string) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(Music(typeStr, id))}, false) +} + +func (b *Bot) SendPrivateCustomMusic(userID uint64, url, audio, title, content, image string) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(CustomMusic(url, audio, title, content, image))}, false) +} + +func (b *Bot) SendPrivateDice(userID uint64) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(Dice())}, false) +} + +func (b *Bot) SendPrivateRps(userID uint64) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(Rps())}, false) +} + +func (b *Bot) SendPrivateFile(userID uint64, file string) (uint64, error) { + return api.SendPrivateMsg(b, userID, []api.Segment{api.Segment(File(file))}, false) +} + +func (b *Bot) SendPrivateForward(userID uint64, block ForwardBlock) (int32, string, error) { + var messages []api.Segment + for _, item := range block.Content { + content := make([]any, len(item.Content)) + for i, s := range item.Content { + content[i] = s + } + messages = append(messages, api.Segment(CustomNode(item.Name, item.UserID, content...))) + } + + var news []api.News + if block.Preview != "" { + news = append(news, api.News{Text: block.Preview}) + } + + return api.SendPrivateForwardMsg(b, userID, messages, news, block.Prompt, block.Summary, block.Title) +} + +func (b *Bot) SendPrivatePoke(userID uint64) error { + return api.FriendPoke(b, userID) +} + +func (b *Bot) ForwardMsgToPrivate(userID uint64, messageID string) (uint64, error) { + return api.ForwardFriendSingleMsg(b, userID, messageID) +} + +// Group Message APIs + +func (b *Bot) SendGroupMsg(groupID uint64, message ...any) (uint64, error) { + return api.SendGroupMsg(b, groupID, ToMessage(message...), false) +} + +func (b *Bot) SendGroupReplyMsg(groupID uint64, msgID uint64, message ...any) (uint64, error) { + return api.SendGroupMsg(b, groupID, ToMessage(replySegment(msgID), message), false) +} + +func (b *Bot) SendGroupText(groupID uint64, message string) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(Text(message))}, true) +} + +func (b *Bot) SendGroupJson(groupID uint64, data string) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(Json(data))}, false) +} + +func (b *Bot) SendGroupVoice(groupID uint64, file string) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(Record(file))}, false) +} + +func (b *Bot) SendGroupVideo(groupID uint64, file string) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(Video(file))}, false) +} + +func (b *Bot) SendGroupMusic(groupID uint64, typeStr, id string) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(Music(typeStr, id))}, false) +} + +func (b *Bot) SendGroupCustomMusic(groupID uint64, url, audio, title, content, image string) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(CustomMusic(url, audio, title, content, image))}, false) +} + +func (b *Bot) SendGroupDice(groupID uint64) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(Dice())}, false) +} + +func (b *Bot) SendGroupRps(groupID uint64) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(Rps())}, false) +} + +func (b *Bot) SendGroupFile(groupID uint64, file string) (uint64, error) { + return api.SendGroupMsg(b, groupID, []api.Segment{api.Segment(File(file))}, false) +} + +func (b *Bot) SendGroupForward(groupID uint64, block ForwardBlock) (int32, string, error) { + var messages []api.Segment + for _, item := range block.Content { + content := make([]any, len(item.Content)) + for i, s := range item.Content { + content[i] = s + } + messages = append(messages, api.Segment(CustomNode(item.Name, item.UserID, content...))) + } + + var news []api.News + if block.Preview != "" { + news = append(news, api.News{Text: block.Preview}) + } + + return api.SendGroupForwardMsg(b, groupID, messages, news, block.Prompt, block.Summary, block.Title) +} + +func (b *Bot) SendGroupPoke(groupID uint64, userID uint64) error { + return api.GroupPoke(b, groupID, userID) +} + +func (b *Bot) ForwardMsgToGroup(messageID string, groupID uint64) (uint64, error) { + return api.ForwardGroupSingleMsg(b, groupID, messageID) +} + // Message APIs -func (b *Bot) SendPrivateMsg(userID uint64, message string) (uint64, error) { - return api.SendPrivateMsg(b, userID, message, false) -} - -func (b *Bot) SendPrivateTextMsg(userID uint64, message string) (uint64, error) { - return api.SendPrivateMsg(b, userID, message, true) -} - -func (b *Bot) SendGroupMsg(groupID uint64, message string) (uint64, error) { - return api.SendGroupMsg(b, groupID, message, false) -} - -func (b *Bot) SendGroupTextMsg(groupID uint64, message string) (uint64, error) { - return api.SendGroupMsg(b, groupID, message, true) -} - -func (b *Bot) SendMsg(groupID uint64, userID uint64, message string) (int32, error) { - if groupID == 0 { - return api.SendMsg(b, "private", userID, groupID, message, false) - } else { - return api.SendMsg(b, "group", userID, groupID, message, false) - } -} - -func (b *Bot) SendTextMsg(groupID uint64, userID uint64, message string) (int32, error) { - if groupID == 0 { - return api.SendMsg(b, "private", userID, groupID, message, true) - } else { - return api.SendMsg(b, "group", userID, groupID, message, true) - } -} - func (b *Bot) GetMsg(messageID int32) (*api.MessageJson, error) { return api.GetMsg(b, messageID) } @@ -96,14 +208,6 @@ func (b *Bot) GetForwardMsg(messageID string) ([]api.ForwardMsg, error) { return api.GetForwardMsg(b, messageID) } -func (b *Bot) SendGroupForwardMsg(groupID uint64, messages []any) (int32, error) { - return api.SendGroupForwardMsg(b, groupID, messages) -} - -func (b *Bot) SendPrivateForwardMsg(userID uint64, messages []any) (int32, error) { - return api.SendPrivateForwardMsg(b, userID, messages) -} - func (b *Bot) GetGroupMsgHistory(groupID uint64, messageSeq int32) ([]api.MessageJson, error) { return api.GetGroupMsgHistory(b, groupID, messageSeq) } diff --git a/cq.go b/cq.go index 34530b4..c6bf241 100644 --- a/cq.go +++ b/cq.go @@ -2,38 +2,42 @@ package qbot import "fmt" +// Deprecated: Use qbot.At instead func CQAt(userId uint64) string { return fmt.Sprintf("[CQ:at,qq=%d]", userId) } +// Deprecated: Use qbot.SendGroupReply or qbot.SendPrivateReply instead func CQReply(msgId uint64) string { return fmt.Sprintf("[CQ:reply,id=%d]", msgId) } +// Deprecated: Use qbot.Poke instead (if implemented) or raw segment func CQPoke(userId uint64) string { return fmt.Sprintf("[CQ:poke,qq=%d]", userId) } -func CQImageFromUrl(url string) string { - return fmt.Sprintf("[CQ:image,sub_type=0,url=%s]", url) -} - +// Deprecated: Use qbot.Image instead func CQImage(file string) string { - return fmt.Sprintf("[CQ:image,file=file://data/%s]", file) + return fmt.Sprintf("[CQ:image,file=%s]", file) } +// Deprecated: Use qbot.File instead (if implemented) or raw segment func CQFile(file string) string { - return fmt.Sprintf("[CQ:file,file=file://data/%s]", file) + return fmt.Sprintf("[CQ:file,file=%s]", file) } +// Deprecated: Use qbot.SendGroupRecord or qbot.SendPrivateRecord instead (if implemented) or raw segment. func CQRecord(file string) string { - return fmt.Sprintf("[CQ:record,file=file://data/%s]", file) + return fmt.Sprintf("[CQ:record,file=%s]", file) } +// Deprecated: Use qbot.SendGroupRps or qbot.SendPrivateRps instead (if implemented) or raw segment. func CQRps() string { return "[CQ:rps]" } +// Deprecated: Use qbot.SendGroupDice or qbot.SendPrivateDice instead (if implemented) or raw segment. func CQDice() string { return "[CQ:dice]" } diff --git a/http.go b/http.go new file mode 100644 index 0000000..a9004c6 --- /dev/null +++ b/http.go @@ -0,0 +1,105 @@ +// qbot/qbot.go +package qbot + +import ( + "bytes" + "encoding/json" + "io" + "log" + "net/http" + "time" +) + +// Send raw parameters to NapCat +func (b *Bot) SendParams(action string, params map[string]any) (json.RawMessage, error) { + if b.enableDebug { + log.Println() + jsonBytes, err := json.Marshal(params) + if err != nil { + return nil, err + } + log.Printf("[Debug] qbot.SendParams: %s\n%s", action, string(jsonBytes)) + } + resp, err := b.sendHttpRequest(action, params) + if err != nil { + return nil, err + } + return resp.Data, nil +} + +func (b *Bot) sendHttpRequest(action string, params map[string]any) (*cqResponse, error) { + jsonBytes, err := json.Marshal(params) + if err != nil { + return nil, err + } + + var httpResp *http.Response + var reqErr error + + // Retry logic from sendRequest + for i := range 3 { + httpReq, err := http.NewRequest(http.MethodPost, b.apiEndpoint+"/"+action, bytes.NewBuffer(jsonBytes)) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", "application/json") + + httpResp, reqErr = b.httpClient.Do(httpReq) + if reqErr == nil { + break // Successfully sent, exit retry loop + } + log.Printf("Request failed: %v. Retrying (%d/3)...", reqErr, i+1) + time.Sleep(1 * time.Second) + } + + if reqErr != nil { // If all retries failed + log.Printf("Request failed: %v", reqErr) + return nil, reqErr + } + + defer httpResp.Body.Close() + + body, err := io.ReadAll(httpResp.Body) + if err != nil { + log.Printf("Request failed: %v", err) + return nil, err + } + + if b.enableDebug { + log.Printf("[Debug] %s: %s\n%s", action, httpResp.Status, string(body)) + } + + var cqResp cqResponse + if err := json.Unmarshal(body, &cqResp); err != nil { + return nil, err + } + + return &cqResp, nil +} + +func (b *Bot) handleHttpEvent(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + defer r.Body.Close() + + jsonMap := make(map[string]any) + if err := json.Unmarshal(body, &jsonMap); err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + if postType, exists := jsonMap["post_type"]; exists { + if str, ok := postType.(string); ok && str != "" { + go b.handleEvents(&str, &body, &jsonMap) + } + } + w.WriteHeader(http.StatusOK) +} diff --git a/message.go b/message.go index badb21a..0084b39 100644 --- a/message.go +++ b/message.go @@ -3,16 +3,16 @@ package qbot type MsgType int const ( - Text MsgType = 0 - At MsgType = 1 - Face MsgType = 2 - Image MsgType = 3 - Record MsgType = 4 - File MsgType = 5 - Forward MsgType = 6 - Json MsgType = 7 + TextType MsgType = 0 + AtType MsgType = 1 + FaceType MsgType = 2 + ImageType MsgType = 3 + RecordType MsgType = 4 + FileType MsgType = 5 + ForwardType MsgType = 6 + JsonType MsgType = 7 - Other MsgType = -1 + OtherType MsgType = -1 ) type MsgItem interface { @@ -23,55 +23,55 @@ type TextItem struct { Content string } -func (i *TextItem) Type() MsgType { return Text } +func (i *TextItem) Type() MsgType { return TextType } type AtItem struct { TargetID uint64 } -func (i *AtItem) Type() MsgType { return At } +func (i *AtItem) Type() MsgType { return AtType } type FaceItem struct { ID uint64 } -func (i *FaceItem) Type() MsgType { return Face } +func (i *FaceItem) Type() MsgType { return FaceType } type ImageItem struct { URL string } -func (i *ImageItem) Type() MsgType { return Image } +func (i *ImageItem) Type() MsgType { return ImageType } type RecordItem struct { Path string } -func (i *RecordItem) Type() MsgType { return Record } +func (i *RecordItem) Type() MsgType { return RecordType } type FileItem struct { Data string } -func (i *FileItem) Type() MsgType { return File } +func (i *FileItem) Type() MsgType { return FileType } type ForwardItem struct { Data string } -func (i *ForwardItem) Type() MsgType { return Forward } +func (i *ForwardItem) Type() MsgType { return ForwardType } type JsonItem struct { Data string } -func (i *JsonItem) Type() MsgType { return Json } +func (i *JsonItem) Type() MsgType { return JsonType } type OtherItem struct { Data string } -func (i *OtherItem) Type() MsgType { return Other } +func (i *OtherItem) Type() MsgType { return OtherType } type Message struct { GroupID uint64 diff --git a/qbot.go b/qbot.go index e4abb42..9203bfe 100644 --- a/qbot.go +++ b/qbot.go @@ -1,10 +1,7 @@ -// qbot/qbot.go package qbot import ( - "bytes" "encoding/json" - "fmt" "io" "log" "net/http" @@ -48,7 +45,7 @@ func (b *Bot) ConnectNapcat(url string) { for { resp, err := b.httpClient.Get(url) if err != nil { - log.Printf("Failed to connect to NapCat: %v. Retrying in 3 seconds...", err) + log.Printf("Connect to NapCat: %v", err) time.Sleep(3 * time.Second) continue } @@ -56,14 +53,14 @@ func (b *Bot) ConnectNapcat(url string) { body, err := io.ReadAll(resp.Body) if err != nil { - log.Printf("Failed to read response from NapCat: %v. Retrying in 3 seconds...", err) + log.Printf("Read response: %v", err) time.Sleep(3 * time.Second) continue } var cqResp cqResponse if err := json.Unmarshal(body, &cqResp); err != nil { - log.Printf("Failed to parse response from NapCat: %v. Retrying in 3 seconds...", err) + log.Printf("Parse response: %v", err) time.Sleep(3 * time.Second) continue } @@ -72,7 +69,7 @@ func (b *Bot) ConnectNapcat(url string) { log.Printf("Connected to NapCat: %s", cqResp.Message) break } else { - log.Printf("Unexpected response from NapCat: %s. Retrying in 3 seconds...", string(body)) + log.Printf("Unexpected response: %s", string(body)) time.Sleep(3 * time.Second) continue } @@ -95,94 +92,6 @@ func (b *Bot) PrivateMsg(handler func(b *Bot, msg *Message)) { b.eventHandlers.privateMsg = append(b.eventHandlers.privateMsg, handler) } -func (b *Bot) handleHttpEvent(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - w.WriteHeader(http.StatusMethodNotAllowed) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - defer r.Body.Close() - - jsonMap := make(map[string]any) - if err := json.Unmarshal(body, &jsonMap); err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - if postType, exists := jsonMap["post_type"]; exists { - if str, ok := postType.(string); ok && str != "" { - go b.handleEvents(&str, &body, &jsonMap) - } - } - w.WriteHeader(http.StatusOK) -} - -func (b *Bot) sendRequest(req *cqRequest) (*http.Response, error) { - jsonBytes, err := json.Marshal(req.Params) - if err != nil { - return nil, err - } - - var resp *http.Response - var reqErr error - - // Retry logic - for i := range 3 { - httpReq, err := http.NewRequest(http.MethodPost, b.apiEndpoint+"/"+req.Action, bytes.NewBuffer(jsonBytes)) - if err != nil { - return nil, err - } - httpReq.Header.Set("Content-Type", "application/json") - - resp, reqErr = b.httpClient.Do(httpReq) - if reqErr == nil { - return resp, nil - } - log.Printf("Request failed: %v. Retrying (%d/3)...", reqErr, i+1) - time.Sleep(1 * time.Second) - } - - return nil, reqErr -} - -func (b *Bot) sendWithResponse(req *cqRequest) (*cqResponse, error) { - resp, err := b.sendRequest(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%d %s", resp.StatusCode, string(body)) - } - - var cqResp cqResponse - if err := json.Unmarshal(body, &cqResp); err != nil { - return nil, fmt.Errorf("%v", err) - } - - return &cqResp, nil -} - -// Send implements api.Client interface -func (b *Bot) Send(action string, params map[string]any) (json.RawMessage, error) { - req := cqRequest{ - Action: action, - Params: params, - } - resp, err := b.sendWithResponse(&req) - if err != nil { - return nil, err - } - return resp.Data, nil +func (b *Bot) Debug(status bool) { + b.enableDebug = status } diff --git a/segment.go b/segment.go new file mode 100644 index 0000000..d257778 --- /dev/null +++ b/segment.go @@ -0,0 +1,213 @@ +package qbot + +import ( + "fmt" + + "github.com/awfufu/qbot/api" +) + +// Segment represents a message segment. +type Segment api.Segment + +// creates a text segment +func Text(text string) Segment { + return Segment{ + Type: "text", + Data: map[string]any{ + "text": text, + }, + } +} + +// creates an at segment +func At(userID uint64) Segment { + return Segment{ + Type: "at", + Data: map[string]any{ + "qq": fmt.Sprintf("%d", userID), + }, + } +} + +// creates a face segment +func Face(id uint64) Segment { + return Segment{ + Type: "face", + Data: map[string]any{ + "id": fmt.Sprintf("%d", id), + }, + } +} + +// creates an image segment +// file can be a local path (file:///path/to/file), a URL (https://example.com/image), or base64; +// summary is optional. +func Image(file string, summary ...string) Segment { + data := map[string]any{ + "file": file, + } + if len(summary) > 0 { + data["summary"] = summary[0] + } + return Segment{ + Type: "image", + Data: data, + } +} + +// Json creates a json segment +func Json(data string) Segment { + return Segment{ + Type: "json", + Data: map[string]any{ + "data": data, + }, + } +} + +// creates a record segment +func Record(file string) Segment { + return Segment{ + Type: "record", + Data: map[string]any{ + "file": file, + }, + } +} + +// creates a video segment +func Video(file string) Segment { + return Segment{ + Type: "video", + Data: map[string]any{ + "file": file, + }, + } +} + +// creates a music segment +func Music(typeStr, id string) Segment { + return Segment{ + Type: "music", + Data: map[string]any{ + "type": typeStr, + "id": id, + }, + } +} + +// creates a custom music segment +func CustomMusic(url, audio, title, content, image string) Segment { + return Segment{ + Type: "music", + Data: map[string]any{ + "type": "custom", + "url": url, + "audio": audio, + "title": title, + "content": content, + "image": image, + }, + } +} + +// creates a dice segment +func Dice() Segment { + return Segment{ + Type: "dice", + Data: map[string]any{}, + } +} + +// creates a rps segment +func Rps() Segment { + return Segment{ + Type: "rps", + Data: map[string]any{}, + } +} + +// creates a file segment +func File(file string) Segment { + return Segment{ + Type: "file", + Data: map[string]any{ + "file": file, + }, + } +} + +// Node creates a forward message node using message ID. +func Node(id string) Segment { + return Segment{ + Type: "node", + Data: map[string]any{ + "id": id, + }, + } +} + +// CustomNode creates a custom forward message node. +func CustomNode(name string, uin uint64, content ...any) Segment { + return Segment{ + Type: "node", + Data: map[string]any{ + "nickname": name, + "user_id": fmt.Sprintf("%d", uin), + "content": ToMessage(content...), + }, + } +} + +// creates a replySegment segment +func replySegment(msgID uint64) Segment { + return Segment{ + Type: "reply", + Data: map[string]any{ + "id": fmt.Sprintf("%d", msgID), + }, + } +} + +// converts variadic arguments to a message ([]api.Segment) +func ToMessage(args ...any) []api.Segment { + if len(args) == 0 { + return []api.Segment{} + } + + var segments []api.Segment + for _, arg := range args { + switch v := arg.(type) { + case Segment: + segments = append(segments, api.Segment(v)) + case api.Segment: + segments = append(segments, v) + case string: + segments = append(segments, api.Segment(Text(v))) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool: + segments = append(segments, api.Segment(Text(fmt.Sprint(v)))) + case fmt.Stringer: + segments = append(segments, api.Segment(Text(v.String()))) + default: + // Try to convert unknown types to string representation + segments = append(segments, api.Segment(Text(fmt.Sprintf("%v", v)))) + } + } + return segments +} + +// ForwardBlockItem represents a single forward message node. +type ForwardBlockItem struct { + Name string `json:"nickname"` + UserID uint64 `json:"user_id"` + Content []Segment `json:"content"` +} + +// ForwardBlock represents a block of forward messages with metadata. +type ForwardBlock struct { + Title string `json:"source"` + Preview string `json:"preview"` + Summary string `json:"summary"` + Prompt string `json:"prompt"` + Content []ForwardBlockItem `json:"messages"` +} diff --git a/types.go b/types.go index f80a7eb..6fd6ce0 100644 --- a/types.go +++ b/types.go @@ -9,17 +9,13 @@ type Bot struct { httpClient *http.Client httpServer *http.Server apiEndpoint string + enableDebug bool eventHandlers struct { groupMsg []func(b *Bot, msg *Message) privateMsg []func(b *Bot, msg *Message) } } -type cqRequest struct { - Action string `json:"action"` - Params map[string]any `json:"params"` -} - type cqResponse struct { Status string `json:"status"` Retcode int `json:"retcode"`