refactor: replace callback-based bot with channel-driven receiver and sender architecture

This commit is contained in:
2025-12-16 22:03:57 +08:00
parent 355360dc09
commit 6223c729b7
4 changed files with 172 additions and 174 deletions

186
apis.go
View File

@@ -6,96 +6,96 @@ import (
// Bot Account APIs
func (b *Bot) SetProfile(nickname, company, email, college, personalNote string) error {
func (b *Sender) SetProfile(nickname, company, email, college, personalNote string) error {
return api.SetProfile(b, nickname, company, email, college, personalNote)
}
func (b *Bot) GetLoginInfo() (*api.LoginInfo, error) {
func (b *Sender) GetLoginInfo() (*api.LoginInfo, error) {
return api.GetLoginInfo(b)
}
func (b *Bot) GetModelShow(model string) ([]api.ModelShow, error) {
func (b *Sender) GetModelShow(model string) ([]api.ModelShow, error) {
return api.GetModelShow(b, model)
}
func (b *Bot) SetModelShow(model, modelShow string) error {
func (b *Sender) SetModelShow(model, modelShow string) error {
return api.SetModelShow(b, model, modelShow)
}
func (b *Bot) GetOnlineClients(noCache bool) ([]api.Device, error) {
func (b *Sender) GetOnlineClients(noCache bool) ([]api.Device, error) {
return api.GetOnlineClients(b, noCache)
}
// Friend APIs
func (b *Bot) GetStrangerInfo(userID uint64, noCache bool) (*api.StrangerInfo, error) {
func (b *Sender) GetStrangerInfo(userID uint64, noCache bool) (*api.StrangerInfo, error) {
return api.GetStrangerInfo(b, userID, noCache)
}
func (b *Bot) GetFriendList() ([]api.FriendInfo, error) {
func (b *Sender) GetFriendList() ([]api.FriendInfo, error) {
return api.GetFriendList(b)
}
func (b *Bot) GetUnidirectionalFriendList() ([]api.UnidirectionalFriendInfo, error) {
func (b *Sender) GetUnidirectionalFriendList() ([]api.UnidirectionalFriendInfo, error) {
return api.GetUnidirectionalFriendList(b)
}
func (b *Bot) DeleteFriend(userID uint64) error {
func (b *Sender) DeleteFriend(userID uint64) error {
return api.DeleteFriend(b, userID)
}
func (b *Bot) DeleteUnidirectionalFriend(userID uint64) error {
func (b *Sender) DeleteUnidirectionalFriend(userID uint64) error {
return api.DeleteUnidirectionalFriend(b, userID)
}
// Private Message APIs
func (b *Bot) SendPrivateMsg(userID uint64, message ...any) (uint64, error) {
func (b *Sender) SendPrivateMsg(userID uint64, message ...any) (uint64, error) {
return api.SendPrivateMsg(b, userID, toSegments(message...), false)
}
func (b *Bot) SendPrivateReplyMsg(userID uint64, msgID uint64, message ...any) (uint64, error) {
func (b *Sender) SendPrivateReplyMsg(userID uint64, msgID uint64, message ...any) (uint64, error) {
fullMessage := append([]any{replySegment(msgID)}, message...)
return api.SendPrivateMsg(b, userID, toSegments(fullMessage...), false)
}
func (b *Bot) SendPrivateText(userID uint64, message string) (uint64, error) {
func (b *Sender) SendPrivateText(userID uint64, message string) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{textSegment(message)}, true)
}
func (b *Bot) SendPrivateJson(userID uint64, data string) (uint64, error) {
func (b *Sender) SendPrivateJson(userID uint64, data string) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{jsonSegment(data)}, false)
}
func (b *Bot) SendPrivateVoice(userID uint64, file string) (uint64, error) {
func (b *Sender) SendPrivateVoice(userID uint64, file string) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{recordSegment(file)}, false)
}
func (b *Bot) SendPrivateVideo(userID uint64, file string) (uint64, error) {
func (b *Sender) SendPrivateVideo(userID uint64, file string) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{videoSegment(file)}, false)
}
func (b *Bot) SendPrivateMusic(userID uint64, typeStr, id string) (uint64, error) {
func (b *Sender) SendPrivateMusic(userID uint64, typeStr, id string) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{musicSegment(typeStr, id)}, false)
}
func (b *Bot) SendPrivateCustomMusic(userID uint64, url, audio, title, content, image string) (uint64, error) {
func (b *Sender) SendPrivateCustomMusic(userID uint64, url, audio, title, content, image string) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{customMusicSegment(url, audio, title, content, image)}, false)
}
func (b *Bot) SendPrivateDice(userID uint64) (uint64, error) {
func (b *Sender) SendPrivateDice(userID uint64) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{diceSegment()}, false)
}
func (b *Bot) SendPrivateRps(userID uint64) (uint64, error) {
func (b *Sender) SendPrivateRps(userID uint64) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{rpsSegment()}, false)
}
func (b *Bot) SendPrivateFile(userID uint64, file string) (uint64, error) {
func (b *Sender) SendPrivateFile(userID uint64, file string) (uint64, error) {
return api.SendPrivateMsg(b, userID, []Segment{fileSegment(file)}, false)
}
func (b *Bot) SendPrivateForward(userID uint64, block ForwardBlock) (int32, string, error) {
func (b *Sender) SendPrivateForward(userID uint64, block ForwardBlock) (int32, string, error) {
var messages []Segment
for _, item := range block.Content {
content := make([]any, len(item.Content))
@@ -113,62 +113,62 @@ func (b *Bot) SendPrivateForward(userID uint64, block ForwardBlock) (int32, stri
return api.SendPrivateForwardMsg(b, userID, messages, news, block.Prompt, block.Summary, block.Title)
}
func (b *Bot) SendPrivatePoke(userID uint64) error {
func (b *Sender) SendPrivatePoke(userID uint64) error {
return api.FriendPoke(b, userID)
}
func (b *Bot) ForwardMsgToPrivate(userID uint64, messageID string) (uint64, error) {
func (b *Sender) 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) {
func (b *Sender) SendGroupMsg(groupID uint64, message ...any) (uint64, error) {
return api.SendGroupMsg(b, groupID, toSegments(message...), false)
}
func (b *Bot) SendGroupReplyMsg(groupID uint64, msgID uint64, message ...any) (uint64, error) {
func (b *Sender) SendGroupReplyMsg(groupID uint64, msgID uint64, message ...any) (uint64, error) {
fullMessage := append([]any{replySegment(msgID)}, message...)
return api.SendGroupMsg(b, groupID, toSegments(fullMessage...), false)
}
func (b *Bot) SendGroupText(groupID uint64, message string) (uint64, error) {
func (b *Sender) SendGroupText(groupID uint64, message string) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{textSegment(message)}, true)
}
func (b *Bot) SendGroupJson(groupID uint64, data string) (uint64, error) {
func (b *Sender) SendGroupJson(groupID uint64, data string) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{jsonSegment(data)}, false)
}
func (b *Bot) SendGroupVoice(groupID uint64, file string) (uint64, error) {
func (b *Sender) SendGroupVoice(groupID uint64, file string) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{recordSegment(file)}, false)
}
func (b *Bot) SendGroupVideo(groupID uint64, file string) (uint64, error) {
func (b *Sender) SendGroupVideo(groupID uint64, file string) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{videoSegment(file)}, false)
}
func (b *Bot) SendGroupMusic(groupID uint64, typeStr, id string) (uint64, error) {
func (b *Sender) SendGroupMusic(groupID uint64, typeStr, id string) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{musicSegment(typeStr, id)}, false)
}
func (b *Bot) SendGroupCustomMusic(groupID uint64, url, audio, title, content, image string) (uint64, error) {
func (b *Sender) SendGroupCustomMusic(groupID uint64, url, audio, title, content, image string) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{customMusicSegment(url, audio, title, content, image)}, false)
}
func (b *Bot) SendGroupDice(groupID uint64) (uint64, error) {
func (b *Sender) SendGroupDice(groupID uint64) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{diceSegment()}, false)
}
func (b *Bot) SendGroupRps(groupID uint64) (uint64, error) {
func (b *Sender) SendGroupRps(groupID uint64) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{rpsSegment()}, false)
}
func (b *Bot) SendGroupFile(groupID uint64, file string) (uint64, error) {
func (b *Sender) SendGroupFile(groupID uint64, file string) (uint64, error) {
return api.SendGroupMsg(b, groupID, []Segment{fileSegment(file)}, false)
}
func (b *Bot) SendGroupForward(groupID uint64, block ForwardBlock) (int32, string, error) {
func (b *Sender) SendGroupForward(groupID uint64, block ForwardBlock) (int32, string, error) {
var messages []Segment
for _, item := range block.Content {
content := make([]any, len(item.Content))
@@ -186,250 +186,250 @@ func (b *Bot) SendGroupForward(groupID uint64, block ForwardBlock) (int32, strin
return api.SendGroupForwardMsg(b, groupID, messages, news, block.Prompt, block.Summary, block.Title)
}
func (b *Bot) SendGroupPoke(groupID uint64, userID uint64) error {
func (b *Sender) SendGroupPoke(groupID uint64, userID uint64) error {
return api.GroupPoke(b, groupID, userID)
}
func (b *Bot) ForwardMsgToGroup(messageID string, groupID uint64) (uint64, error) {
func (b *Sender) ForwardMsgToGroup(messageID string, groupID uint64) (uint64, error) {
return api.ForwardGroupSingleMsg(b, groupID, messageID)
}
// Message APIs
func (b *Bot) GetMsg(messageID int32) (*api.MessageJson, error) {
func (b *Sender) GetMsg(messageID int32) (*api.MessageJson, error) {
return api.GetMsg(b, messageID)
}
func (b *Bot) DeleteMsg(msgID uint64) error {
func (b *Sender) DeleteMsg(msgID uint64) error {
return api.DeleteMsg(b, msgID)
}
func (b *Bot) MarkMsgAsRead(messageID int32) error {
func (b *Sender) MarkMsgAsRead(messageID int32) error {
return api.MarkMsgAsRead(b, messageID)
}
func (b *Bot) GetForwardMsg(messageID string) ([]api.ForwardMsg, error) {
func (b *Sender) GetForwardMsg(messageID string) ([]api.ForwardMsg, error) {
return api.GetForwardMsg(b, messageID)
}
func (b *Bot) GetGroupMsgHistory(groupID uint64, messageSeq int32) ([]api.MessageJson, error) {
func (b *Sender) 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) {
func (b *Sender) GetImage(file string) (*api.ImageInfo, error) {
return api.GetImage(b, file)
}
func (b *Bot) CanSendImage() (bool, error) {
func (b *Sender) CanSendImage() (bool, error) {
return api.CanSendImage(b)
}
func (b *Bot) OcrImage(imageID string) (*api.OcrResult, error) {
func (b *Sender) OcrImage(imageID string) (*api.OcrResult, error) {
return api.OcrImage(b, imageID)
}
func (b *Bot) GetRecord(file, outFormat string) (string, error) {
func (b *Sender) GetRecord(file, outFormat string) (string, error) {
return api.GetRecord(b, file, outFormat)
}
func (b *Bot) CanSendRecord() (bool, error) {
func (b *Sender) CanSendRecord() (bool, error) {
return api.CanSendRecord(b)
}
func (b *Bot) SendEmojiReaction(messageID uint64, emojiID uint64, set bool) error {
func (b *Sender) SendEmojiReaction(messageID uint64, emojiID uint64, set bool) error {
return api.SetMsgEmojiLike(b, messageID, emojiID, set)
}
// Request APIs
func (b *Bot) SetFriendAddRequest(flag string, approve bool, remark string) error {
func (b *Sender) 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 {
func (b *Sender) 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) {
func (b *Sender) GetGroupInfo(groupID uint64, noCache bool) (*api.GroupInfo, error) {
return api.GetGroupInfo(b, groupID, noCache)
}
func (b *Bot) GetGroupList(noCache bool) ([]api.GroupInfo, error) {
func (b *Sender) GetGroupList(noCache bool) ([]api.GroupInfo, error) {
return api.GetGroupList(b, noCache)
}
func (b *Bot) GetGroupMemberInfo(groupID uint64, userID uint64, noCache bool) (*api.GroupMemberInfo, error) {
func (b *Sender) 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) {
func (b *Sender) 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) {
func (b *Sender) GetGroupHonorInfo(groupID uint64, typeStr string) (*api.GroupHonorInfo, error) {
return api.GetGroupHonorInfo(b, groupID, typeStr)
}
func (b *Bot) GetGroupSystemMsg() (*api.GroupSystemMsg, error) {
func (b *Sender) GetGroupSystemMsg() (*api.GroupSystemMsg, error) {
return api.GetGroupSystemMsg(b)
}
func (b *Bot) GetEssenceMsgList(groupID uint64) ([]api.EssenceMsg, error) {
func (b *Sender) GetEssenceMsgList(groupID uint64) ([]api.EssenceMsg, error) {
return api.GetEssenceMsgList(b, groupID)
}
func (b *Bot) GetGroupAtAllRemain(groupID uint64) (bool, int32, int32, error) {
func (b *Sender) 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 {
func (b *Sender) SetGroupSpecialTitle(groupID uint64, userID uint64, specialTitle string) error {
return api.SetGroupSpecialTitle(b, groupID, userID, specialTitle)
}
func (b *Bot) SetGroupName(groupID uint64, groupName string) error {
func (b *Sender) SetGroupName(groupID uint64, groupName string) error {
return api.SetGroupName(b, groupID, groupName)
}
func (b *Bot) SetGroupAdmin(groupID uint64, userID uint64, enable bool) error {
func (b *Sender) 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 {
func (b *Sender) SetGroupBan(groupID uint64, userID uint64, duration int) error {
return api.SetGroupBan(b, groupID, userID, duration)
}
func (b *Bot) SetGroupWholeBan(groupID uint64, enable bool) error {
func (b *Sender) SetGroupWholeBan(groupID uint64, enable bool) error {
return api.SetGroupWholeBan(b, groupID, enable)
}
func (b *Bot) SetGroupAnonymousBan(groupID uint64, anonymous, anonymousFlag string, duration int) error {
func (b *Sender) SetGroupAnonymousBan(groupID uint64, anonymous, anonymousFlag string, duration int) error {
return api.SetGroupAnonymousBan(b, groupID, anonymous, anonymousFlag, duration)
}
func (b *Bot) SetGroupEssence(msgID uint64) error {
func (b *Sender) SetGroupEssence(msgID uint64) error {
return api.SetGroupEssence(b, msgID)
}
func (b *Bot) DeleteGroupEssence(msgID uint64) error {
func (b *Sender) DeleteGroupEssence(msgID uint64) error {
return api.DeleteGroupEssence(b, msgID)
}
func (b *Bot) SendGroupSign(groupID uint64) error {
func (b *Sender) SendGroupSign(groupID uint64) error {
return api.SendGroupSign(b, groupID)
}
func (b *Bot) SetGroupAnonymous(groupID uint64, enable bool) error {
func (b *Sender) SetGroupAnonymous(groupID uint64, enable bool) error {
return api.SetGroupAnonymous(b, groupID, enable)
}
func (b *Bot) SendGroupNotice(groupID uint64, content, image string) error {
func (b *Sender) SendGroupNotice(groupID uint64, content, image string) error {
return api.SendGroupNotice(b, groupID, content, image)
}
func (b *Bot) GetGroupNotice(groupID uint64) ([]any, error) {
func (b *Sender) GetGroupNotice(groupID uint64) ([]any, error) {
return api.GetGroupNotice(b, groupID)
}
func (b *Bot) SetGroupKick(groupID uint64, userID uint64, rejectAddRequest bool) error {
func (b *Sender) SetGroupKick(groupID uint64, userID uint64, rejectAddRequest bool) error {
return api.SetGroupKick(b, groupID, userID, rejectAddRequest)
}
func (b *Bot) SetGroupLeave(groupID uint64, isDismiss bool) error {
func (b *Sender) SetGroupLeave(groupID uint64, isDismiss bool) error {
return api.SetGroupLeave(b, groupID, isDismiss)
}
func (b *Bot) SetGroupPortrait(groupID uint64, file string, cache int) error {
func (b *Sender) 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 {
func (b *Sender) 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 {
func (b *Sender) 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 {
func (b *Sender) 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 {
func (b *Sender) CreateGroupFileFolder(groupID uint64, name, parentID string) error {
return api.CreateGroupFileFolder(b, groupID, name, parentID)
}
func (b *Bot) DeleteGroupFileFolder(groupID uint64, folderID string) error {
func (b *Sender) DeleteGroupFileFolder(groupID uint64, folderID string) error {
return api.DeleteGroupFileFolder(b, groupID, folderID)
}
func (b *Bot) GetGroupFileSystemInfo(groupID uint64) (*api.GroupFileSystemInfo, error) {
func (b *Sender) GetGroupFileSystemInfo(groupID uint64) (*api.GroupFileSystemInfo, error) {
return api.GetGroupFileSystemInfo(b, groupID)
}
func (b *Bot) GetGroupRootFiles(groupID uint64) (*struct {
func (b *Sender) 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 {
func (b *Sender) 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) {
func (b *Sender) 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 {
func (b *Sender) UploadPrivateFile(userID uint64, file, name string) error {
return api.UploadPrivateFile(b, userID, file, name)
}
// System APIs
func (b *Bot) GetCookies(domain string) (string, error) {
func (b *Sender) GetCookies(domain string) (string, error) {
return api.GetCookies(b, domain)
}
func (b *Bot) GetCsrfToken() (int32, error) {
func (b *Sender) GetCsrfToken() (int32, error) {
return api.GetCsrfToken(b)
}
func (b *Bot) GetCredentials(domain string) (*api.Credentials, error) {
func (b *Sender) GetCredentials(domain string) (*api.Credentials, error) {
return api.GetCredentials(b, domain)
}
func (b *Bot) GetVersionInfo() (*api.VersionInfo, error) {
func (b *Sender) GetVersionInfo() (*api.VersionInfo, error) {
return api.GetVersionInfo(b)
}
func (b *Bot) GetStatus() (*api.StatusInfo, error) {
func (b *Sender) GetStatus() (*api.StatusInfo, error) {
return api.GetStatus(b)
}
func (b *Bot) ReloadEventFilter(file string) error {
func (b *Sender) ReloadEventFilter(file string) error {
return api.ReloadEventFilter(b, file)
}
func (b *Bot) DownloadFile(url string, threadCount int, headers string) (string, error) {
func (b *Sender) DownloadFile(url string, threadCount int, headers string) (string, error) {
return api.DownloadFile(b, url, threadCount, headers)
}
func (b *Bot) CheckUrlSafely(url string) (int32, error) {
func (b *Sender) CheckUrlSafely(url string) (int32, error) {
return api.CheckUrlSafely(b, url)
}
func (b *Bot) CleanCache() error {
func (b *Sender) CleanCache() error {
return api.CleanCache(b)
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/awfufu/qbot/api"
)
func (b *Bot) handleEvents(header *eventHeader, msgStr *[]byte) {
func (r *Receiver) handleEvents(header *eventHeader, msgStr *[]byte) {
switch header.PostType {
case "notice":
switch header.NoticeType {
@@ -15,8 +15,9 @@ func (b *Bot) handleEvents(header *eventHeader, msgStr *[]byte) {
notice := &api.EmojiLikeNotice{}
if json.Unmarshal(*msgStr, notice) == nil {
if n := parseEmojiLikeNotice(notice); n != nil {
for _, handler := range b.eventHandlers.emojiLike {
handler(b, n)
select {
case r.emojiLike <- n:
default:
}
}
}
@@ -26,8 +27,9 @@ func (b *Bot) handleEvents(header *eventHeader, msgStr *[]byte) {
notice := &api.RecallNotice{}
if json.Unmarshal(*msgStr, notice) == nil {
if n := parseRecallNotice(notice); n != nil {
for _, handler := range b.eventHandlers.recall {
handler(b, n)
select {
case r.recall <- n:
default:
}
}
}
@@ -36,8 +38,9 @@ func (b *Bot) handleEvents(header *eventHeader, msgStr *[]byte) {
notice := &api.PokeNotify{}
if json.Unmarshal(*msgStr, notice) == nil {
if n := parsePokeNotify(notice); n != nil {
for _, handler := range b.eventHandlers.poke {
handler(b, n)
select {
case r.poke <- n:
default:
}
}
}
@@ -49,8 +52,9 @@ func (b *Bot) handleEvents(header *eventHeader, msgStr *[]byte) {
return
}
if msg := parseMsgJson(msgJson); msg != nil {
for _, handler := range b.eventHandlers.message {
handler(b, msg)
select {
case r.message <- msg:
default:
}
}
}

20
http.go
View File

@@ -24,27 +24,27 @@ type httpResponse struct {
}
// Send raw parameters to NapCat
func (b *Bot) SendParams(action string, params map[string]any) (json.RawMessage, error) {
resp, err := b.sendHttpRequest(action, params)
func (s *Sender) SendParams(action string, params map[string]any) (json.RawMessage, error) {
resp, err := s.sendHttpRequest(action, params)
if err != nil {
return nil, err
}
return resp.Data, nil
}
func (b *Bot) sendHttpRequest(action string, params map[string]any) (*httpResponse, error) {
func (s *Sender) sendHttpRequest(action string, params map[string]any) (*httpResponse, error) {
jsonBytes, err := json.Marshal(params)
if err != nil {
return nil, fmt.Errorf("marshal params: %v", err)
}
httpReq, err := http.NewRequest(http.MethodPost, b.apiEndpoint+"/"+action, bytes.NewBuffer(jsonBytes))
httpReq, err := http.NewRequest(http.MethodPost, s.apiEndpoint+"/"+action, bytes.NewBuffer(jsonBytes))
if err != nil {
return nil, fmt.Errorf("create request: %v", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpResp, err := b.httpClient.Do(httpReq)
httpResp, err := s.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("request failed: %v", err)
}
@@ -63,18 +63,18 @@ func (b *Bot) sendHttpRequest(action string, params map[string]any) (*httpRespon
return &result, nil
}
func (b *Bot) handleHttpEvent(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
func (r *Receiver) handleHttpEvent(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
body, err := io.ReadAll(req.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
defer r.Body.Close()
defer req.Body.Close()
var header eventHeader
if err := json.Unmarshal(body, &header); err != nil {
@@ -83,7 +83,7 @@ func (b *Bot) handleHttpEvent(w http.ResponseWriter, r *http.Request) {
}
if header.PostType != "" {
go b.handleEvents(&header, &body)
go r.handleEvents(&header, &body)
}
w.WriteHeader(http.StatusOK)
}

118
qbot.go
View File

@@ -1,28 +1,30 @@
package qbot
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
type Bot struct {
httpClient *http.Client
httpServer *http.Server
apiEndpoint string
eventHandlers struct {
message []func(b *Bot, msg *Message)
emojiLike []func(b *Bot, msg *EmojiReaction)
recall []func(b *Bot, msg *RecallNotice)
poke []func(b *Bot, msg *PokeNotify)
}
type Sender struct {
httpClient *http.Client
apiEndpoint string
}
func NewBot(address string) *Bot {
bot := &Bot{
type Receiver struct {
httpServer *http.Server
// Channels for events
message chan *Message
emojiLike chan *EmojiReaction
recall chan *RecallNotice
poke chan *PokeNotify
err chan error // Channel for server errors
}
func HttpClient(url string) *Sender {
url = strings.TrimRight(url, "/")
return &Sender{
httpClient: &http.Client{
Transport: &http.Transport{
MaxIdleConns: 10,
@@ -32,71 +34,63 @@ func NewBot(address string) *Bot {
},
Timeout: 10 * time.Second,
},
apiEndpoint: url,
}
bot.eventHandlers.message = make([]func(b *Bot, msg *Message), 0)
bot.eventHandlers.emojiLike = make([]func(b *Bot, msg *EmojiReaction), 0)
bot.eventHandlers.recall = make([]func(b *Bot, msg *RecallNotice), 0)
bot.eventHandlers.poke = make([]func(b *Bot, msg *PokeNotify), 0)
}
bot.httpServer = &http.Server{
func HttpServer(address string) *Receiver {
rx := &Receiver{
message: make(chan *Message, 100),
emojiLike: make(chan *EmojiReaction, 100),
recall: make(chan *RecallNotice, 100),
poke: make(chan *PokeNotify, 100),
err: make(chan error, 1),
}
rx.httpServer = &http.Server{
Addr: address,
Handler: http.HandlerFunc(bot.handleHttpEvent),
Handler: http.HandlerFunc(rx.handleHttpEvent),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
return bot
// Start server in goroutine
go func() {
if err := rx.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
rx.err <- err
}
close(rx.err)
close(rx.message)
close(rx.emojiLike)
close(rx.recall)
close(rx.poke)
}()
return rx
}
func (b *Bot) ConnectNapcat(url string) error {
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
return fmt.Errorf("invalid URL: %s", url)
}
url = strings.TrimRight(url, "/")
b.apiEndpoint = url
// Event Channels
resp, err := b.httpClient.Get(url)
if err != nil {
return fmt.Errorf("connect to NapCat: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("read response: %v", err)
}
var cqResp httpResponse
if err := json.Unmarshal(body, &cqResp); err != nil {
return fmt.Errorf("parse response: %v", err)
}
if cqResp.Status == "ok" && strings.Contains(cqResp.Message, "NapCat") {
return nil
}
return fmt.Errorf("unexpected response: %s", string(body))
func (r *Receiver) OnMessage() <-chan *Message {
return r.message
}
func (b *Bot) Run() error {
if err := b.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
return nil
func (r *Receiver) OnEmojiReaction() <-chan *EmojiReaction {
return r.emojiLike
}
func (b *Bot) OnMessage(handler func(b *Bot, msg *Message)) {
b.eventHandlers.message = append(b.eventHandlers.message, handler)
func (r *Receiver) OnRecall() <-chan *RecallNotice {
return r.recall
}
func (b *Bot) OnEmojiReaction(handler func(b *Bot, emoji *EmojiReaction)) {
b.eventHandlers.emojiLike = append(b.eventHandlers.emojiLike, handler)
func (r *Receiver) OnPoke() <-chan *PokeNotify {
return r.poke
}
func (b *Bot) OnRecall(handler func(b *Bot, recall *RecallNotice)) {
b.eventHandlers.recall = append(b.eventHandlers.recall, handler)
func (r *Receiver) Error() <-chan error {
return r.err
}
func (b *Bot) OnPoke(handler func(b *Bot, poke *PokeNotify)) {
b.eventHandlers.poke = append(b.eventHandlers.poke, handler)
func (r *Receiver) Close() error {
return r.httpServer.Close()
}