init: first commit

This commit is contained in:
2025-12-01 19:35:50 +08:00
commit b0aef76685
81 changed files with 2348 additions and 0 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 awfufu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

33
README.md Normal file
View File

@@ -0,0 +1,33 @@
# qbot
使用双向 HTTP 连接与 NapCat 通信。
## example
NapCat 配置:
- 设置 HTTP 服务端,监听 `http://napcat-ip:3000`
- 设置 HTTP 客户端,地址 `http://qbot-ip:3001`
下面是一个 echo 示例。
```go
package main
import (
"log"
"github.com/awfufu/qbot"
)
func main() {
const MasterQQ = 114514 // 主人QQ
bot := qbot.NewBot("qbot-ip:3001")
bot.ConnectNapcat("http://napcat-ip:3000")
bot.GroupMsg(func(b *qbot.Bot, msg *qbot.Message) {
if msg.UserID == MasterQQ {
b.SendGroupMsg(msg.GroupID, msg.Raw)
}
})
log.Fatal(bot.Run())
}
```

17
api/can_send_image.go Normal file
View File

@@ -0,0 +1,17 @@
package api
import "encoding/json"
func CanSendImage(c Client) (bool, error) {
data, err := c.Send("can_send_image", nil)
if err != nil {
return false, err
}
var resp struct {
Yes bool `json:"yes"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return false, err
}
return resp.Yes, nil
}

17
api/can_send_record.go Normal file
View File

@@ -0,0 +1,17 @@
package api
import "encoding/json"
func CanSendRecord(c Client) (bool, error) {
data, err := c.Send("can_send_record", nil)
if err != nil {
return false, err
}
var resp struct {
Yes bool `json:"yes"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return false, err
}
return resp.Yes, nil
}

20
api/check_url_safely.go Normal file
View File

@@ -0,0 +1,20 @@
package api
import "encoding/json"
func CheckUrlSafely(c Client, url string) (int32, error) {
params := map[string]any{
"url": url,
}
data, err := c.Send("check_url_safely", params)
if err != nil {
return 0, err
}
var resp struct {
Level int32 `json:"level"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return 0, err
}
return resp.Level, nil
}

6
api/clean_cache.go Normal file
View File

@@ -0,0 +1,6 @@
package api
func CleanCache(c Client) error {
_, err := c.Send("clean_cache", nil)
return err
}

7
api/client.go Normal file
View File

@@ -0,0 +1,7 @@
package api
import "encoding/json"
type Client interface {
Send(action string, params map[string]any) (json.RawMessage, error)
}

View File

@@ -0,0 +1,11 @@
package api
func CreateGroupFileFolder(c Client, groupID uint64, name, parentID string) error {
params := map[string]any{
"group_id": groupID,
"name": name,
"parent_id": parentID,
}
_, err := c.Send("create_group_file_folder", params)
return err
}

9
api/delete_friend.go Normal file
View File

@@ -0,0 +1,9 @@
package api
func DeleteFriend(c Client, userID uint64) error {
params := map[string]any{
"user_id": userID,
}
_, err := c.Send("delete_friend", params)
return err
}

View File

@@ -0,0 +1,9 @@
package api
func DeleteGroupEssence(c Client, msgID uint64) error {
params := map[string]any{
"message_id": msgID,
}
_, err := c.Send("delete_essence_msg", params)
return err
}

11
api/delete_group_file.go Normal file
View File

@@ -0,0 +1,11 @@
package api
func DeleteGroupFile(c Client, groupID uint64, fileID string, busid int32) error {
params := map[string]any{
"group_id": groupID,
"file_id": fileID,
"busid": busid,
}
_, err := c.Send("delete_group_file", params)
return err
}

View File

@@ -0,0 +1,10 @@
package api
func DeleteGroupFileFolder(c Client, groupID uint64, folderID string) error {
params := map[string]any{
"group_id": groupID,
"folder_id": folderID,
}
_, err := c.Send("delete_group_file_folder", params)
return err
}

9
api/delete_msg.go Normal file
View File

@@ -0,0 +1,9 @@
package api
func DeleteMsg(c Client, msgID uint64) error {
params := map[string]any{
"message_id": msgID,
}
_, err := c.Send("delete_msg", params)
return err
}

View File

@@ -0,0 +1,9 @@
package api
func DeleteUnidirectionalFriend(c Client, userID uint64) error {
params := map[string]any{
"user_id": userID,
}
_, err := c.Send("delete_unidirectional_friend", params)
return err
}

22
api/download_file.go Normal file
View File

@@ -0,0 +1,22 @@
package api
import "encoding/json"
func DownloadFile(c Client, url string, threadCount int, headers string) (string, error) {
params := map[string]any{
"url": url,
"thread_count": threadCount,
"headers": headers,
}
data, err := c.Send("download_file", params)
if err != nil {
return "", err
}
var resp struct {
File string `json:"file"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return "", err
}
return resp.File, nil
}

20
api/get_cookies.go Normal file
View File

@@ -0,0 +1,20 @@
package api
import "encoding/json"
func GetCookies(c Client, domain string) (string, error) {
params := map[string]any{
"domain": domain,
}
data, err := c.Send("get_cookies", params)
if err != nil {
return "", err
}
var resp struct {
Cookies string `json:"cookies"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return "", err
}
return resp.Cookies, nil
}

18
api/get_credentials.go Normal file
View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func GetCredentials(c Client, domain string) (*Credentials, error) {
params := map[string]any{
"domain": domain,
}
data, err := c.Send("get_credentials", params)
if err != nil {
return nil, err
}
var resp Credentials
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

17
api/get_csrf_token.go Normal file
View File

@@ -0,0 +1,17 @@
package api
import "encoding/json"
func GetCsrfToken(c Client) (int32, error) {
data, err := c.Send("get_csrf_token", nil)
if err != nil {
return 0, err
}
var resp struct {
Token int32 `json:"token"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return 0, err
}
return resp.Token, nil
}

View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func GetEssenceMsgList(c Client, groupID uint64) ([]EssenceMsg, error) {
params := map[string]any{
"group_id": groupID,
}
data, err := c.Send("get_essence_msg_list", params)
if err != nil {
return nil, err
}
var resp []EssenceMsg
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp, nil
}

20
api/get_forward_msg.go Normal file
View File

@@ -0,0 +1,20 @@
package api
import "encoding/json"
func GetForwardMsg(c Client, messageID string) ([]ForwardMsg, error) {
params := map[string]any{
"message_id": messageID,
}
data, err := c.Send("get_forward_msg", params)
if err != nil {
return nil, err
}
var resp struct {
Messages []ForwardMsg `json:"messages"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp.Messages, nil
}

15
api/get_friend_list.go Normal file
View File

@@ -0,0 +1,15 @@
package api
import "encoding/json"
func GetFriendList(c Client) ([]FriendInfo, error) {
data, err := c.Send("get_friend_list", nil)
if err != nil {
return nil, err
}
var resp []FriendInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp, nil
}

View File

@@ -0,0 +1,22 @@
package api
import "encoding/json"
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)
if err != nil {
return false, 0, 0, err
}
var resp struct {
CanAtAll bool `json:"can_at_all"`
RemainAtAllCountForGroup int32 `json:"remain_at_all_count_for_group"`
RemainAtAllCountForUin int32 `json:"remain_at_all_count_for_uin"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return false, 0, 0, err
}
return resp.CanAtAll, resp.RemainAtAllCountForGroup, resp.RemainAtAllCountForUin, nil
}

View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func GetGroupFileSystemInfo(c Client, groupID uint64) (*GroupFileSystemInfo, error) {
params := map[string]any{
"group_id": groupID,
}
data, err := c.Send("get_group_file_system_info", params)
if err != nil {
return nil, err
}
var resp GroupFileSystemInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

22
api/get_group_file_url.go Normal file
View File

@@ -0,0 +1,22 @@
package api
import "encoding/json"
func GetGroupFileUrl(c Client, groupID uint64, fileID string, busid int32) (string, error) {
params := map[string]any{
"group_id": groupID,
"file_id": fileID,
"busid": busid,
}
data, err := c.Send("get_group_file_url", params)
if err != nil {
return "", err
}
var resp struct {
Url string `json:"url"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return "", err
}
return resp.Url, nil
}

View File

@@ -0,0 +1,25 @@
package api
import "encoding/json"
func GetGroupFilesByFolder(c Client, groupID uint64, folderID string) (*struct {
Files []GroupFile `json:"files"`
Folders []GroupFolder `json:"folders"`
}, error) {
params := map[string]any{
"group_id": groupID,
"folder_id": folderID,
}
data, err := c.Send("get_group_files_by_folder", params)
if err != nil {
return nil, err
}
var resp struct {
Files []GroupFile `json:"files"`
Folders []GroupFolder `json:"folders"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

View File

@@ -0,0 +1,19 @@
package api
import "encoding/json"
func GetGroupHonorInfo(c Client, groupID uint64, typeStr string) (*GroupHonorInfo, error) {
params := map[string]any{
"group_id": groupID,
"type": typeStr,
}
data, err := c.Send("get_group_honor_info", params)
if err != nil {
return nil, err
}
var resp GroupHonorInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

19
api/get_group_info.go Normal file
View File

@@ -0,0 +1,19 @@
package api
import "encoding/json"
func GetGroupInfo(c Client, groupID uint64, noCache bool) (*GroupInfo, error) {
params := map[string]any{
"group_id": groupID,
"no_cache": noCache,
}
data, err := c.Send("get_group_info", params)
if err != nil {
return nil, err
}
var resp GroupInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

18
api/get_group_list.go Normal file
View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func GetGroupList(c Client, noCache bool) ([]GroupInfo, error) {
params := map[string]any{
"no_cache": noCache,
}
data, err := c.Send("get_group_list", params)
if err != nil {
return nil, err
}
var resp []GroupInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp, nil
}

View File

@@ -0,0 +1,20 @@
package api
import "encoding/json"
func GetGroupMemberInfo(c Client, groupID uint64, userID uint64, noCache bool) (*GroupMemberInfo, error) {
params := map[string]any{
"group_id": groupID,
"user_id": userID,
"no_cache": noCache,
}
data, err := c.Send("get_group_member_info", params)
if err != nil {
return nil, err
}
var resp GroupMemberInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

View File

@@ -0,0 +1,19 @@
package api
import "encoding/json"
func GetGroupMemberList(c Client, groupID uint64, noCache bool) ([]GroupMemberInfo, error) {
params := map[string]any{
"group_id": groupID,
"no_cache": noCache,
}
data, err := c.Send("get_group_member_list", params)
if err != nil {
return nil, err
}
var resp []GroupMemberInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp, nil
}

View File

@@ -0,0 +1,21 @@
package api
import "encoding/json"
func GetGroupMsgHistory(c Client, groupID uint64, messageSeq int32) ([]MessageJson, error) {
params := map[string]any{
"group_id": groupID,
"message_seq": messageSeq,
}
data, err := c.Send("get_group_msg_history", params)
if err != nil {
return nil, err
}
var resp struct {
Messages []MessageJson `json:"messages"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp.Messages, nil
}

18
api/get_group_notice.go Normal file
View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func GetGroupNotice(c Client, groupID uint64) ([]any, error) {
params := map[string]any{
"group_id": groupID,
}
data, err := c.Send("_get_group_notice", params)
if err != nil {
return nil, err
}
var resp []any
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp, nil
}

View File

@@ -0,0 +1,24 @@
package api
import "encoding/json"
func GetGroupRootFiles(c Client, groupID uint64) (*struct {
Files []GroupFile `json:"files"`
Folders []GroupFolder `json:"folders"`
}, error) {
params := map[string]any{
"group_id": groupID,
}
data, err := c.Send("get_group_root_files", params)
if err != nil {
return nil, err
}
var resp struct {
Files []GroupFile `json:"files"`
Folders []GroupFolder `json:"folders"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

View File

@@ -0,0 +1,15 @@
package api
import "encoding/json"
func GetGroupSystemMsg(c Client) (*GroupSystemMsg, error) {
data, err := c.Send("get_group_system_msg", nil)
if err != nil {
return nil, err
}
var resp GroupSystemMsg
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

18
api/get_image.go Normal file
View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func GetImage(c Client, file string) (*ImageInfo, error) {
params := map[string]any{
"file": file,
}
data, err := c.Send("get_image", params)
if err != nil {
return nil, err
}
var resp ImageInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

15
api/get_login_info.go Normal file
View File

@@ -0,0 +1,15 @@
package api
import "encoding/json"
func GetLoginInfo(c Client) (*LoginInfo, error) {
data, err := c.Send("get_login_info", nil)
if err != nil {
return nil, err
}
var resp LoginInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

20
api/get_model_show.go Normal file
View File

@@ -0,0 +1,20 @@
package api
import "encoding/json"
func GetModelShow(c Client, model string) ([]ModelShow, error) {
params := map[string]any{
"model": model,
}
data, err := c.Send("_get_model_show", params)
if err != nil {
return nil, err
}
var resp struct {
Variants []ModelShow `json:"variants"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp.Variants, nil
}

18
api/get_msg.go Normal file
View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func GetMsg(c Client, messageID int32) (*MessageJson, error) {
params := map[string]any{
"message_id": messageID,
}
data, err := c.Send("get_msg", params)
if err != nil {
return nil, err
}
var resp MessageJson
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

20
api/get_online_clients.go Normal file
View File

@@ -0,0 +1,20 @@
package api
import "encoding/json"
func GetOnlineClients(c Client, noCache bool) ([]Device, error) {
params := map[string]any{
"no_cache": noCache,
}
data, err := c.Send("get_online_clients", params)
if err != nil {
return nil, err
}
var resp struct {
Clients []Device `json:"clients"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp.Clients, nil
}

21
api/get_record.go Normal file
View File

@@ -0,0 +1,21 @@
package api
import "encoding/json"
func GetRecord(c Client, file, outFormat string) (string, error) {
params := map[string]any{
"file": file,
"out_format": outFormat,
}
data, err := c.Send("get_record", params)
if err != nil {
return "", err
}
var resp struct {
File string `json:"file"`
}
if err := json.Unmarshal(data, &resp); err != nil {
return "", err
}
return resp.File, nil
}

15
api/get_status.go Normal file
View File

@@ -0,0 +1,15 @@
package api
import "encoding/json"
func GetStatus(c Client) (*StatusInfo, error) {
data, err := c.Send("get_status", nil)
if err != nil {
return nil, err
}
var resp StatusInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

19
api/get_stranger_info.go Normal file
View File

@@ -0,0 +1,19 @@
package api
import "encoding/json"
func GetStrangerInfo(c Client, userID uint64, noCache bool) (*StrangerInfo, error) {
params := map[string]any{
"user_id": userID,
"no_cache": noCache,
}
data, err := c.Send("get_stranger_info", params)
if err != nil {
return nil, err
}
var resp StrangerInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

View File

@@ -0,0 +1,15 @@
package api
import "encoding/json"
func GetUnidirectionalFriendList(c Client) ([]UnidirectionalFriendInfo, error) {
data, err := c.Send("get_unidirectional_friend_list", nil)
if err != nil {
return nil, err
}
var resp []UnidirectionalFriendInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp, nil
}

15
api/get_version_info.go Normal file
View File

@@ -0,0 +1,15 @@
package api
import "encoding/json"
func GetVersionInfo(c Client) (*VersionInfo, error) {
data, err := c.Send("get_version_info", nil)
if err != nil {
return nil, err
}
var resp VersionInfo
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

9
api/mark_msg_as_read.go Normal file
View File

@@ -0,0 +1,9 @@
package api
func MarkMsgAsRead(c Client, messageID int32) error {
params := map[string]any{
"message_id": messageID,
}
_, err := c.Send("mark_msg_as_read", params)
return err
}

18
api/ocr_image.go Normal file
View File

@@ -0,0 +1,18 @@
package api
import "encoding/json"
func OcrImage(c Client, imageID string) (*OcrResult, error) {
params := map[string]any{
"image": imageID,
}
data, err := c.Send("ocr_image", params)
if err != nil {
return nil, err
}
var resp OcrResult
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return &resp, nil
}

View File

@@ -0,0 +1,9 @@
package api
func ReloadEventFilter(c Client, file string) error {
params := map[string]any{
"file": file,
}
_, err := c.Send("reload_event_filter", params)
return err
}

View File

@@ -0,0 +1,21 @@
package api
import "encoding/json"
func SendGroupForwardMsg(c Client, groupID uint64, messages []any) (int32, error) {
params := map[string]any{
"group_id": groupID,
"messages": messages,
}
data, err := c.Send("send_group_forward_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
}

25
api/send_group_msg.go Normal file
View File

@@ -0,0 +1,25 @@
package api
import "encoding/json"
func SendGroupMsg(c Client, groupID uint64, message string, autoEscape bool) (uint64, error) {
if message == "" {
message = " "
}
params := map[string]any{
"group_id": groupID,
"message": message,
"auto_escape": autoEscape,
}
data, err := c.Send("send_group_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 uint64(resp.MessageId), nil
}

11
api/send_group_notice.go Normal file
View File

@@ -0,0 +1,11 @@
package api
func SendGroupNotice(c Client, groupID uint64, content, image string) error {
params := map[string]any{
"group_id": groupID,
"content": content,
"image": image,
}
_, err := c.Send("_send_group_notice", params)
return err
}

9
api/send_group_sign.go Normal file
View File

@@ -0,0 +1,9 @@
package api
func SendGroupSign(c Client, groupID uint64) error {
params := map[string]any{
"group_id": groupID,
}
_, err := c.Send("send_group_sign", params)
return err
}

24
api/send_msg.go Normal file
View File

@@ -0,0 +1,24 @@
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
}

View File

@@ -0,0 +1,21 @@
package api
import "encoding/json"
func SendPrivateForwardMsg(c Client, userID uint64, messages []any) (int32, error) {
params := map[string]any{
"user_id": userID,
"messages": messages,
}
data, err := c.Send("send_private_forward_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
}

25
api/send_private_msg.go Normal file
View File

@@ -0,0 +1,25 @@
package api
import "encoding/json"
func SendPrivateMsg(c Client, userID uint64, message string, autoEscape bool) (uint64, error) {
if message == "" {
message = " "
}
params := map[string]any{
"user_id": userID,
"message": message,
"auto_escape": autoEscape,
}
data, err := c.Send("send_private_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 uint64(resp.MessageId), nil
}

View File

@@ -0,0 +1,11 @@
package api
func SetFriendAddRequest(c Client, flag string, approve bool, remark string) error {
params := map[string]any{
"flag": flag,
"approve": approve,
"remark": remark,
}
_, err := c.Send("set_friend_add_request", params)
return err
}

View File

@@ -0,0 +1,12 @@
package api
func SetGroupAddRequest(c Client, flag, subType string, approve bool, reason string) error {
params := map[string]any{
"flag": flag,
"sub_type": subType,
"approve": approve,
"reason": reason,
}
_, err := c.Send("set_group_add_request", params)
return err
}

11
api/set_group_admin.go Normal file
View File

@@ -0,0 +1,11 @@
package api
func SetGroupAdmin(c Client, groupID uint64, userID uint64, enable bool) error {
params := map[string]any{
"group_id": groupID,
"user_id": userID,
"enable": enable,
}
_, err := c.Send("set_group_admin", params)
return err
}

View File

@@ -0,0 +1,10 @@
package api
func SetGroupAnonymous(c Client, groupID uint64, enable bool) error {
params := map[string]any{
"group_id": groupID,
"enable": enable,
}
_, err := c.Send("set_group_anonymous", params)
return err
}

View File

@@ -0,0 +1,12 @@
package api
func SetGroupAnonymousBan(c Client, groupID uint64, anonymous, anonymousFlag string, duration int) error {
params := map[string]any{
"group_id": groupID,
"anonymous": anonymous,
"anonymous_flag": anonymousFlag,
"duration": duration,
}
_, err := c.Send("set_group_anonymous_ban", params)
return err
}

11
api/set_group_ban.go Normal file
View File

@@ -0,0 +1,11 @@
package api
func SetGroupBan(c Client, groupID uint64, userID uint64, duration int) error {
params := map[string]any{
"group_id": groupID,
"user_id": userID,
"duration": duration,
}
_, err := c.Send("set_group_ban", params)
return err
}

11
api/set_group_card.go Normal file
View File

@@ -0,0 +1,11 @@
package api
func SetGroupCard(c Client, groupID uint64, userID uint64, card string) error {
params := map[string]any{
"group_id": groupID,
"user_id": userID,
"card": card,
}
_, err := c.Send("set_group_card", params)
return err
}

9
api/set_group_essence.go Normal file
View File

@@ -0,0 +1,9 @@
package api
func SetGroupEssence(c Client, msgID uint64) error {
params := map[string]any{
"message_id": msgID,
}
_, err := c.Send("set_essence_msg", params)
return err
}

11
api/set_group_kick.go Normal file
View File

@@ -0,0 +1,11 @@
package api
func SetGroupKick(c Client, groupID uint64, userID uint64, rejectAddRequest bool) error {
params := map[string]any{
"group_id": groupID,
"user_id": userID,
"reject_add_request": rejectAddRequest,
}
_, err := c.Send("set_group_kick", params)
return err
}

10
api/set_group_leave.go Normal file
View File

@@ -0,0 +1,10 @@
package api
func SetGroupLeave(c Client, groupID uint64, isDismiss bool) error {
params := map[string]any{
"group_id": groupID,
"is_dismiss": isDismiss,
}
_, err := c.Send("set_group_leave", params)
return err
}

10
api/set_group_name.go Normal file
View File

@@ -0,0 +1,10 @@
package api
func SetGroupName(c Client, groupID uint64, groupName string) error {
params := map[string]any{
"group_id": groupID,
"group_name": groupName,
}
_, err := c.Send("set_group_name", params)
return err
}

11
api/set_group_portrait.go Normal file
View File

@@ -0,0 +1,11 @@
package api
func SetGroupPortrait(c Client, groupID uint64, file string, cache int) error {
params := map[string]any{
"group_id": groupID,
"file": file,
"cache": cache,
}
_, err := c.Send("set_group_portrait", params)
return err
}

View File

@@ -0,0 +1,11 @@
package api
func SetGroupSpecialTitle(c Client, groupID uint64, userID uint64, specialTitle string) error {
params := map[string]any{
"group_id": groupID,
"user_id": userID,
"special_title": specialTitle,
}
_, err := c.Send("set_group_special_title", params)
return err
}

View File

@@ -0,0 +1,10 @@
package api
func SetGroupWholeBan(c Client, groupID uint64, enable bool) error {
params := map[string]any{
"group_id": groupID,
"enable": enable,
}
_, err := c.Send("set_group_whole_ban", params)
return err
}

10
api/set_model_show.go Normal file
View File

@@ -0,0 +1,10 @@
package api
func SetModelShow(c Client, model, modelShow string) error {
params := map[string]any{
"model": model,
"model_show": modelShow,
}
_, err := c.Send("_set_model_show", params)
return err
}

13
api/set_qq_profile.go Normal file
View File

@@ -0,0 +1,13 @@
package api
func SetQqProfile(c Client, nickname, company, email, college, personalNote string) error {
params := map[string]any{
"nickname": nickname,
"company": company,
"email": email,
"college": college,
"personal_note": personalNote,
}
_, err := c.Send("set_qq_profile", params)
return err
}

258
api/types.go Normal file
View File

@@ -0,0 +1,258 @@
package api
import "encoding/json"
type MessageJson struct {
GroupID uint64 `json:"group_id"`
Time uint64 `json:"time"`
MessageID uint64 `json:"message_id"`
Sender struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Card string `json:"card"`
Role string `json:"role"`
} `json:"sender"`
RawMessage string `json:"raw_message"`
Message []struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
} `json:"message"`
}
type GroupMemberInfo struct {
GroupID uint64 `json:"group_id"`
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Card string `json:"card"`
Sex string `json:"sex"`
Age int32 `json:"age"`
Area string `json:"area"`
JoinTime int32 `json:"join_time"`
LastSentTime int32 `json:"last_sent_time"`
Level string `json:"level"`
Role string `json:"role"`
Unfriendly bool `json:"unfriendly"`
Title string `json:"title"`
TitleExpireTime int64 `json:"title_expire_time"`
CardChangeable bool `json:"card_changeable"`
ShutUpTimestamp int64 `json:"shut_up_timestamp"`
}
type LoginInfo struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
}
type FriendInfo struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Remark string `json:"remark"`
}
type UnidirectionalFriendInfo struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Source string `json:"source"`
}
type StrangerInfo struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Sex string `json:"sex"`
Age int32 `json:"age"`
Qid string `json:"qid"`
Level int32 `json:"level"`
LoginDays int32 `json:"login_days"`
}
type GroupInfo struct {
GroupID uint64 `json:"group_id"`
GroupName string `json:"group_name"`
GroupMemo string `json:"group_memo"`
GroupCreateTime uint32 `json:"group_create_time"`
GroupLevel uint32 `json:"group_level"`
MemberCount int32 `json:"member_count"`
MaxMemberCount int32 `json:"max_member_count"`
}
type GroupHonorInfo struct {
GroupID uint64 `json:"group_id"`
CurrentTalkative struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
DayCount int32 `json:"day_count"`
} `json:"current_talkative"`
TalkativeList []struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Description string `json:"description"`
} `json:"talkative_list"`
PerformerList []struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Description string `json:"description"`
} `json:"performer_list"`
LegendList []struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Description string `json:"description"`
} `json:"legend_list"`
StrongNewbieList []struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Description string `json:"description"`
} `json:"strong_newbie_list"`
EmotionList []struct {
UserID uint64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Description string `json:"description"`
} `json:"emotion_list"`
}
type VersionInfo struct {
AppName string `json:"app_name"`
AppVersion string `json:"app_version"`
ProtocolVersion string `json:"protocol_version"`
}
type StatusInfo struct {
AppInitialized bool `json:"app_initialized"`
AppEnabled bool `json:"app_enabled"`
PluginsGood bool `json:"plugins_good"`
AppGood bool `json:"app_good"`
Online bool `json:"online"`
Good bool `json:"good"`
Stat struct {
PacketReceived uint64 `json:"packet_received"`
PacketSent uint64 `json:"packet_sent"`
PacketLost uint64 `json:"packet_lost"`
MessageReceived uint64 `json:"message_received"`
MessageSent uint64 `json:"message_sent"`
DisconnectTimes uint32 `json:"disconnect_times"`
LostTimes uint32 `json:"lost_times"`
LastMessageTime int64 `json:"last_message_time"`
} `json:"stat"`
}
type FileInfo struct {
FileName string `json:"file_name"`
FileSize int64 `json:"file_size"`
Url string `json:"url"`
}
type GroupFileSystemInfo struct {
FileCount int32 `json:"file_count"`
LimitCount int32 `json:"limit_count"`
UsedSpace int64 `json:"used_space"`
TotalSpace int64 `json:"total_space"`
}
type GroupFile struct {
GroupID uint64 `json:"group_id"`
FileID string `json:"file_id"`
FileName string `json:"file_name"`
BusID int32 `json:"busid"`
FileSize int64 `json:"file_size"`
UploadTime int64 `json:"upload_time"`
DeadTime int64 `json:"dead_time"`
ModifyTime int64 `json:"modify_time"`
DownloadTimes int32 `json:"download_times"`
Uploader uint64 `json:"uploader"`
UploaderName string `json:"uploader_name"`
}
type GroupFolder struct {
GroupID uint64 `json:"group_id"`
FolderID string `json:"folder_id"`
FolderName string `json:"folder_name"`
CreateTime int64 `json:"create_time"`
Creator uint64 `json:"creator"`
CreatorName string `json:"creator_name"`
TotalFileCount int32 `json:"total_file_count"`
}
type EssenceMsg struct {
SenderID uint64 `json:"sender_id"`
SenderNick string `json:"sender_nick"`
SenderTime int64 `json:"sender_time"`
OperatorID uint64 `json:"operator_id"`
OperatorNick string `json:"operator_nick"`
OperatorTime int64 `json:"operator_time"`
MessageID int32 `json:"message_id"`
}
type ModelShow struct {
Model string `json:"model"`
ModelShow string `json:"model_show"`
NeedPay bool `json:"need_pay"`
}
type Device struct {
AppID int64 `json:"app_id"`
DeviceName string `json:"device_name"`
DeviceKind string `json:"device_kind"`
}
type OcrResult struct {
Texts []struct {
Text string `json:"text"`
Confidence int32 `json:"confidence"`
Coordinates any `json:"coordinates"` // simplified
} `json:"texts"`
Language string `json:"language"`
}
type GroupSystemMsg struct {
InvitedRequests []struct {
RequestID int64 `json:"request_id"`
InvitorUin uint64 `json:"invitor_uin"`
InvitorNick string `json:"invitor_nick"`
GroupID uint64 `json:"group_id"`
GroupName string `json:"group_name"`
Checked bool `json:"checked"`
Actor uint64 `json:"actor"`
} `json:"invited_requests"`
JoinRequests []struct {
RequestID int64 `json:"request_id"`
RequesterUin uint64 `json:"requester_uin"`
RequesterNick string `json:"requester_nick"`
Message string `json:"message"`
GroupID uint64 `json:"group_id"`
GroupName string `json:"group_name"`
Checked bool `json:"checked"`
Actor uint64 `json:"actor"`
} `json:"join_requests"`
}
type Credentials struct {
Cookies string `json:"cookies"`
CsrfToken int32 `json:"csrf_token"`
}
type ImageInfo struct {
Size int32 `json:"size"`
Filename string `json:"filename"`
Url string `json:"url"`
}
type ForwardMsg struct {
Content string `json:"content"`
Sender struct {
Nickname string `json:"nickname"`
UserID uint64 `json:"user_id"`
} `json:"sender"`
Time int64 `json:"time"`
}
type QiDianAccountInfo struct {
MasterID uint64 `json:"master_id"`
MasterNick string `json:"master_nick"`
Account uint64 `json:"account"`
Nickname string `json:"nickname"`
}

12
api/upload_group_file.go Normal file
View File

@@ -0,0 +1,12 @@
package api
func UploadGroupFile(c Client, groupID uint64, file, name, folder string) error {
params := map[string]any{
"group_id": groupID,
"file": file,
"name": name,
"folder": folder,
}
_, err := c.Send("upload_group_file", params)
return err
}

View File

@@ -0,0 +1,11 @@
package api
func UploadPrivateFile(c Client, userID uint64, file, name string) error {
params := map[string]any{
"user_id": userID,
"file": file,
"name": name,
}
_, err := c.Send("upload_private_file", params)
return err
}

323
bot_api.go Normal file
View File

@@ -0,0 +1,323 @@
package qbot
import "github.com/awfufu/qbot/api"
// Bot Account APIs
func (b *Bot) SetQqProfile(nickname, company, email, college, personalNote string) error {
return api.SetQqProfile(b, nickname, company, email, college, personalNote)
}
func (b *Bot) GetLoginInfo() (*api.LoginInfo, error) {
return api.GetLoginInfo(b)
}
func (b *Bot) GetModelShow(model string) ([]api.ModelShow, error) {
return api.GetModelShow(b, model)
}
func (b *Bot) SetModelShow(model, modelShow string) error {
return api.SetModelShow(b, model, modelShow)
}
func (b *Bot) GetOnlineClients(noCache bool) ([]api.Device, error) {
return api.GetOnlineClients(b, noCache)
}
// Friend APIs
func (b *Bot) GetStrangerInfo(userID uint64, noCache bool) (*api.StrangerInfo, error) {
return api.GetStrangerInfo(b, userID, noCache)
}
func (b *Bot) GetFriendList() ([]api.FriendInfo, error) {
return api.GetFriendList(b)
}
func (b *Bot) GetUnidirectionalFriendList() ([]api.UnidirectionalFriendInfo, error) {
return api.GetUnidirectionalFriendList(b)
}
func (b *Bot) DeleteFriend(userID uint64) error {
return api.DeleteFriend(b, userID)
}
func (b *Bot) DeleteUnidirectionalFriend(userID uint64) error {
return api.DeleteUnidirectionalFriend(b, userID)
}
// 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)
}
func (b *Bot) DeleteMsg(msgID uint64) error {
return api.DeleteMsg(b, msgID)
}
func (b *Bot) MarkMsgAsRead(messageID int32) error {
return api.MarkMsgAsRead(b, messageID)
}
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)
}
// Image & Voice APIs
func (b *Bot) GetImage(file string) (*api.ImageInfo, error) {
return api.GetImage(b, file)
}
func (b *Bot) CanSendImage() (bool, error) {
return api.CanSendImage(b)
}
func (b *Bot) OcrImage(imageID string) (*api.OcrResult, error) {
return api.OcrImage(b, imageID)
}
func (b *Bot) GetRecord(file, outFormat string) (string, error) {
return api.GetRecord(b, file, outFormat)
}
func (b *Bot) CanSendRecord() (bool, error) {
return api.CanSendRecord(b)
}
// Request APIs
func (b *Bot) SetFriendAddRequest(flag string, approve bool, remark string) error {
return api.SetFriendAddRequest(b, flag, approve, remark)
}
func (b *Bot) SetGroupAddRequest(flag, subType string, approve bool, reason string) error {
return api.SetGroupAddRequest(b, flag, subType, approve, reason)
}
// Group Info APIs
func (b *Bot) GetGroupInfo(groupID uint64, noCache bool) (*api.GroupInfo, error) {
return api.GetGroupInfo(b, groupID, noCache)
}
func (b *Bot) GetGroupList(noCache bool) ([]api.GroupInfo, error) {
return api.GetGroupList(b, noCache)
}
func (b *Bot) GetGroupMemberInfo(groupID uint64, userID uint64, noCache bool) (*api.GroupMemberInfo, error) {
return api.GetGroupMemberInfo(b, groupID, userID, noCache)
}
func (b *Bot) GetGroupMemberList(groupID uint64, noCache bool) ([]api.GroupMemberInfo, error) {
return api.GetGroupMemberList(b, groupID, noCache)
}
func (b *Bot) GetGroupHonorInfo(groupID uint64, typeStr string) (*api.GroupHonorInfo, error) {
return api.GetGroupHonorInfo(b, groupID, typeStr)
}
func (b *Bot) GetGroupSystemMsg() (*api.GroupSystemMsg, error) {
return api.GetGroupSystemMsg(b)
}
func (b *Bot) GetEssenceMsgList(groupID uint64) ([]api.EssenceMsg, error) {
return api.GetEssenceMsgList(b, groupID)
}
func (b *Bot) GetGroupAtAllRemain(groupID uint64) (bool, int32, int32, error) {
return api.GetGroupAtAllRemain(b, groupID)
}
// Group Setting APIs
func (b *Bot) SetGroupSpecialTitle(groupID uint64, userID uint64, specialTitle string) error {
return api.SetGroupSpecialTitle(b, groupID, userID, specialTitle)
}
func (b *Bot) SetGroupName(groupID uint64, groupName string) error {
return api.SetGroupName(b, groupID, groupName)
}
func (b *Bot) SetGroupAdmin(groupID uint64, userID uint64, enable bool) error {
return api.SetGroupAdmin(b, groupID, userID, enable)
}
func (b *Bot) SetGroupBan(groupID uint64, userID uint64, duration int) error {
return api.SetGroupBan(b, groupID, userID, duration)
}
func (b *Bot) SetGroupWholeBan(groupID uint64, enable bool) error {
return api.SetGroupWholeBan(b, groupID, enable)
}
func (b *Bot) SetGroupAnonymousBan(groupID uint64, anonymous, anonymousFlag string, duration int) error {
return api.SetGroupAnonymousBan(b, groupID, anonymous, anonymousFlag, duration)
}
func (b *Bot) SetGroupEssence(msgID uint64) error {
return api.SetGroupEssence(b, msgID)
}
func (b *Bot) DeleteGroupEssence(msgID uint64) error {
return api.DeleteGroupEssence(b, msgID)
}
func (b *Bot) SendGroupSign(groupID uint64) error {
return api.SendGroupSign(b, groupID)
}
func (b *Bot) SetGroupAnonymous(groupID uint64, enable bool) error {
return api.SetGroupAnonymous(b, groupID, enable)
}
func (b *Bot) SendGroupNotice(groupID uint64, content, image string) error {
return api.SendGroupNotice(b, groupID, content, image)
}
func (b *Bot) GetGroupNotice(groupID uint64) ([]any, error) {
return api.GetGroupNotice(b, groupID)
}
func (b *Bot) SetGroupKick(groupID uint64, userID uint64, rejectAddRequest bool) error {
return api.SetGroupKick(b, groupID, userID, rejectAddRequest)
}
func (b *Bot) SetGroupLeave(groupID uint64, isDismiss bool) error {
return api.SetGroupLeave(b, groupID, isDismiss)
}
func (b *Bot) SetGroupPortrait(groupID uint64, file string, cache int) error {
return api.SetGroupPortrait(b, groupID, file, cache)
}
func (b *Bot) SetGroupCard(groupID uint64, userID uint64, card string) error {
return api.SetGroupCard(b, groupID, userID, card)
}
// File APIs
func (b *Bot) UploadGroupFile(groupID uint64, file, name, folder string) error {
return api.UploadGroupFile(b, groupID, file, name, folder)
}
func (b *Bot) DeleteGroupFile(groupID uint64, fileID string, busid int32) error {
return api.DeleteGroupFile(b, groupID, fileID, busid)
}
func (b *Bot) CreateGroupFileFolder(groupID uint64, name, parentID string) error {
return api.CreateGroupFileFolder(b, groupID, name, parentID)
}
func (b *Bot) DeleteGroupFileFolder(groupID uint64, folderID string) error {
return api.DeleteGroupFileFolder(b, groupID, folderID)
}
func (b *Bot) GetGroupFileSystemInfo(groupID uint64) (*api.GroupFileSystemInfo, error) {
return api.GetGroupFileSystemInfo(b, groupID)
}
func (b *Bot) GetGroupRootFiles(groupID uint64) (*struct {
Files []api.GroupFile `json:"files"`
Folders []api.GroupFolder `json:"folders"`
}, error) {
return api.GetGroupRootFiles(b, groupID)
}
func (b *Bot) GetGroupFilesByFolder(groupID uint64, folderID string) (*struct {
Files []api.GroupFile `json:"files"`
Folders []api.GroupFolder `json:"folders"`
}, error) {
return api.GetGroupFilesByFolder(b, groupID, folderID)
}
func (b *Bot) GetGroupFileUrl(groupID uint64, fileID string, busid int32) (string, error) {
return api.GetGroupFileUrl(b, groupID, fileID, busid)
}
func (b *Bot) UploadPrivateFile(userID uint64, file, name string) error {
return api.UploadPrivateFile(b, userID, file, name)
}
// System APIs
func (b *Bot) GetCookies(domain string) (string, error) {
return api.GetCookies(b, domain)
}
func (b *Bot) GetCsrfToken() (int32, error) {
return api.GetCsrfToken(b)
}
func (b *Bot) GetCredentials(domain string) (*api.Credentials, error) {
return api.GetCredentials(b, domain)
}
func (b *Bot) GetVersionInfo() (*api.VersionInfo, error) {
return api.GetVersionInfo(b)
}
func (b *Bot) GetStatus() (*api.StatusInfo, error) {
return api.GetStatus(b)
}
func (b *Bot) ReloadEventFilter(file string) error {
return api.ReloadEventFilter(b, file)
}
func (b *Bot) DownloadFile(url string, threadCount int, headers string) (string, error) {
return api.DownloadFile(b, url, threadCount, headers)
}
func (b *Bot) CheckUrlSafely(url string) (int32, error) {
return api.CheckUrlSafely(b, url)
}
func (b *Bot) CleanCache() error {
return api.CleanCache(b)
}

39
cq.go Normal file
View File

@@ -0,0 +1,39 @@
package qbot
import "fmt"
func CQAt(userId uint64) string {
return fmt.Sprintf("[CQ:at,qq=%d]", userId)
}
func CQReply(msgId uint64) string {
return fmt.Sprintf("[CQ:reply,id=%d]", msgId)
}
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)
}
func CQImage(file string) string {
return fmt.Sprintf("[CQ:image,file=file://data/%s]", file)
}
func CQFile(file string) string {
return fmt.Sprintf("[CQ:file,file=file://data/%s]", file)
}
func CQRecord(file string) string {
return fmt.Sprintf("[CQ:record,file=file://data/%s]", file)
}
func CQRps() string {
return "[CQ:rps]"
}
func CQDice() string {
return "[CQ:dice]"
}

152
event.go Normal file
View File

@@ -0,0 +1,152 @@
package qbot
import (
"encoding/json"
"fmt"
"strconv"
"github.com/awfufu/qbot/api"
)
func (b *Bot) handleEvents(postType *string, msgStr *[]byte, jsonMap *map[string]any) {
switch *postType {
case "meta_event":
// heartbeat, connection state..
case "notice":
// TODO
switch (*jsonMap)["notice_type"] {
case "group_recall":
// TODO
}
case "message":
switch (*jsonMap)["message_type"] {
case "private":
msgJson := &api.MessageJson{}
if json.Unmarshal(*msgStr, msgJson) != nil {
return
}
if msg := parseMsgJson(msgJson); msg != nil {
for _, handler := range b.eventHandlers.privateMsg {
handler(b, msg)
}
}
case "group":
msgJson := &api.MessageJson{}
if json.Unmarshal(*msgStr, msgJson) != nil {
return
}
if msg := parseMsgJson(msgJson); msg != nil {
for _, handler := range b.eventHandlers.groupMsg {
handler(b, msg)
}
}
}
}
}
func parseMsgJson(raw *api.MessageJson) *Message {
if raw == nil {
return nil
}
result := Message{
GroupID: raw.GroupID,
UserID: raw.Sender.UserID,
Nickname: raw.Sender.Nickname,
Card: raw.Sender.Card,
Role: raw.Sender.Role,
Time: raw.Time,
MsgID: raw.MessageID,
Raw: raw.RawMessage,
}
for _, msg := range raw.Message {
var jsonData map[string]any
if err := json.Unmarshal(msg.Data, &jsonData); err != nil {
return nil
}
switch msg.Type {
case "text":
if text, ok := jsonData["text"].(string); ok {
result.Array = append(result.Array, &TextItem{
Content: text,
})
}
case "at":
var qqStr string
if qq, ok := jsonData["qq"].(string); ok {
qqStr = qq
} else if qq, ok := jsonData["qq"].(float64); ok {
qqStr = fmt.Sprintf("%.0f", qq)
}
if qqStr != "" {
qqInt, err := strconv.ParseUint(qqStr, 10, 64)
if err != nil {
continue
}
result.Array = append(result.Array, &AtItem{
TargetID: qqInt,
})
}
case "face":
var idStr string
if id, ok := jsonData["id"].(string); ok {
idStr = id
} else if id, ok := jsonData["id"].(float64); ok {
idStr = fmt.Sprintf("%.0f", id)
}
if idStr != "" {
idInt, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
continue
}
result.Array = append(result.Array, &FaceItem{
ID: idInt,
})
}
case "image":
if url, ok := jsonData["url"].(string); ok {
result.Array = append(result.Array, &ImageItem{
URL: url,
})
}
case "record":
if path, ok := jsonData["path"].(string); ok {
result.Array = append(result.Array, &RecordItem{
Path: path,
})
}
case "reply":
var replyId string
if id, ok := jsonData["id"].(string); ok {
replyId = id
} else if id, ok := jsonData["id"].(float64); ok {
replyId = fmt.Sprintf("%.0f", id)
}
if replyId != "" {
replyIdInt, err := strconv.ParseUint(replyId, 10, 64)
if err != nil {
continue
}
result.Array = append(result.Array, &ReplyItem{
MsgID: replyIdInt,
})
}
case "file":
result.Array = append(result.Array, &FileItem{
Data: string(msg.Data),
})
case "forward":
result.Array = append(result.Array, &ForwardItem{
Data: string(msg.Data),
})
case "json":
result.Array = append(result.Array, &JsonItem{
Data: string(msg.Data),
})
default:
result.Array = append(result.Array, &OtherItem{
Data: string(msg.Data),
})
}
}
return &result
}

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/awfufu/qbot
go 1.25.4

93
message.go Normal file
View File

@@ -0,0 +1,93 @@
package qbot
type MsgType int
const (
Text MsgType = 0
At MsgType = 1
Face MsgType = 2
Image MsgType = 3
Record MsgType = 4
Reply MsgType = 5
File MsgType = 6
Forward MsgType = 7
Json MsgType = 8
Other MsgType = -1
)
type MsgItem interface {
Type() MsgType
}
type TextItem struct {
Content string
}
func (i *TextItem) Type() MsgType { return Text }
type AtItem struct {
TargetID uint64
}
func (i *AtItem) Type() MsgType { return At }
type FaceItem struct {
ID uint64
}
func (i *FaceItem) Type() MsgType { return Face }
type ImageItem struct {
URL string
}
func (i *ImageItem) Type() MsgType { return Image }
type RecordItem struct {
Path string
}
func (i *RecordItem) Type() MsgType { return Record }
type ReplyItem struct {
MsgID uint64
}
func (i *ReplyItem) Type() MsgType { return Reply }
type FileItem struct {
Data string
}
func (i *FileItem) Type() MsgType { return File }
type ForwardItem struct {
Data string
}
func (i *ForwardItem) Type() MsgType { return Forward }
type JsonItem struct {
Data string
}
func (i *JsonItem) Type() MsgType { return Json }
type OtherItem struct {
Data string
}
func (i *OtherItem) Type() MsgType { return Other }
type Message struct {
GroupID uint64
UserID uint64
Nickname string
Card string
Role string
Time uint64
MsgID uint64
Raw string
Array []MsgItem
}

132
qbot.go Normal file
View File

@@ -0,0 +1,132 @@
// qbot/qbot.go
package qbot
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
)
func NewBot(address string) *Bot {
bot := &Bot{
httpClient: &http.Client{
Timeout: 10 * time.Second,
},
}
bot.eventHandlers.groupMsg = make([]func(bot *Bot, msg *Message), 0)
bot.eventHandlers.privateMsg = make([]func(bot *Bot, msg *Message), 0)
bot.httpServer = &http.Server{
Addr: address,
Handler: http.HandlerFunc(bot.handleHttpEvent),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
return bot
}
func (b *Bot) ConnectNapcat(url string) {
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
log.Fatal("Invalid URL")
}
url = strings.TrimRight(url, "/")
b.apiEndpoint = url
}
func (b *Bot) Run() error {
log.Printf("Listening on %s", b.httpServer.Addr)
if err := b.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
return nil
}
func (b *Bot) GroupMsg(handler func(b *Bot, msg *Message)) {
b.eventHandlers.groupMsg = append(b.eventHandlers.groupMsg, handler)
}
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
}
httpReq, err := http.NewRequest(http.MethodPost, b.apiEndpoint+"/"+req.Action, bytes.NewBuffer(jsonBytes))
if err != nil {
return nil, err
}
return b.httpClient.Do(httpReq)
}
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
}

196
qface.go Normal file
View File

@@ -0,0 +1,196 @@
package qbot
import "strconv"
var qfaceMap = map[int]string{
0: "惊讶",
1: "撇嘴",
2: "色",
3: "发呆",
4: "得意",
5: "流泪",
6: "害羞",
7: "闭嘴",
8: "睡",
9: "大哭",
10: "尴尬",
11: "发怒",
12: "调皮",
13: "呲牙",
14: "微笑",
15: "难过",
16: "酷",
18: "抓狂",
19: "吐",
20: "偷笑",
21: "可爱",
22: "白眼",
23: "傲慢",
24: "饥饿",
25: "困",
26: "惊恐",
27: "流汗",
28: "憨笑",
29: "悠闲",
30: "奋斗",
31: "咒骂",
32: "疑问",
33: "嘘",
34: "晕",
35: "折磨",
36: "衰",
37: "骷髅",
38: "敲打",
39: "再见",
41: "发抖",
42: "爱情",
43: "跳跳",
46: "猪头",
49: "拥抱",
53: "蛋糕",
56: "刀",
59: "便便",
60: "咖啡",
63: "玫瑰",
64: "凋谢",
66: "爱心",
67: "心碎",
74: "太阳",
75: "月亮",
76: "赞",
77: "踩",
78: "握手",
79: "胜利",
85: "飞吻",
86: "怄火",
89: "西瓜",
96: "冷汗",
97: "擦汗",
98: "抠鼻",
99: "鼓掌",
100: "糗大了",
101: "坏笑",
102: "左哼哼",
103: "右哼哼",
104: "哈欠",
105: "鄙视",
106: "委屈",
107: "快哭了",
108: "阴险",
109: "左亲亲",
110: "吓",
111: "可怜",
112: "菜刀",
114: "篮球",
116: "示爱",
118: "抱拳",
119: "勾引",
120: "拳头",
121: "差劲",
123: "NO",
124: "OK",
125: "转圈",
129: "挥手",
137: "鞭炮",
144: "喝彩",
146: "爆筋",
147: "棒棒糖",
169: "手枪",
171: "茶",
172: "眨眼睛",
173: "泪奔",
174: "无奈",
175: "卖萌",
176: "小纠结",
177: "喷血",
178: "斜眼笑",
179: "doge",
181: "戳一戳",
182: "笑哭",
183: "我最美",
185: "羊驼",
187: "幽灵",
201: "点赞",
212: "托腮",
262: "脑阔疼",
263: "沧桑",
264: "捂脸",
265: "辣眼睛",
266: "哦哟",
267: "头秃",
268: "问号脸",
269: "暗中观察",
270: "emm",
271: "吃瓜",
272: "呵呵哒",
273: "我酸了",
277: "汪汪",
281: "无眼笑",
282: "敬礼",
283: "狂笑",
284: "面无表情",
285: "摸鱼",
286: "魔鬼笑",
287: "哦",
289: "睁眼",
293: "摸锦鲤",
294: "期待",
295: "拿到红包",
297: "拜谢",
298: "元宝",
299: "牛啊",
300: "胖三斤",
302: "左拜年",
303: "右拜年",
305: "右亲亲",
306: "牛气冲天",
307: "喵喵",
311: "打call",
312: "变形",
314: "仔细分析",
317: "菜汪",
318: "崇拜",
319: "比心",
320: "庆祝",
323: "嫌弃",
324: "吃糖",
325: "惊吓",
326: "生气",
332: "举牌牌",
333: "烟花",
334: "虎虎生威",
336: "豹富",
337: "花朵脸",
338: "我想开了",
339: "舔屏",
341: "打招呼",
342: "酸Q",
343: "我方了",
344: "大怨种",
345: "红包多多",
346: "你真棒棒",
347: "大展宏兔",
349: "坚强",
350: "贴贴",
351: "敲敲",
352: "咦",
353: "拜托",
354: "尊嘟假嘟",
355: "耶",
356: "666",
357: "裂开",
392: "龙年快乐",
393: "新年中龙",
394: "新年大龙",
395: "略略略",
}
func GetQFaceNameByStrID(id string) string {
if idn, err := strconv.Atoi(id); err == nil {
if found, exists := qfaceMap[idn]; exists {
return found
}
return id
}
return ""
}

29
types.go Normal file
View File

@@ -0,0 +1,29 @@
package qbot
import (
"encoding/json"
"net/http"
)
type Bot struct {
httpClient *http.Client
httpServer *http.Server
apiEndpoint string
eventHandlers struct {
groupMsg []func(bot *Bot, msg *Message)
privateMsg []func(bot *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"`
Data json.RawMessage `json:"data"`
Message string `json:"message"`
Wording string `json:"wording"`
}