mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 05:37:29 +08:00
227 lines
6.0 KiB
Go
227 lines
6.0 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"clawgo/pkg/bus"
|
|
)
|
|
|
|
type SendCallback func(channel, chatID, action, content, media, messageID, emoji string, buttons [][]bus.Button) error
|
|
|
|
type MessageTool struct {
|
|
mu sync.RWMutex
|
|
sendCallback SendCallback
|
|
defaultChannel string
|
|
defaultChatID string
|
|
}
|
|
|
|
var buttonRowPool = sync.Pool{New: func() interface{} { return make([]bus.Button, 0, 8) }}
|
|
|
|
func NewMessageTool() *MessageTool {
|
|
return &MessageTool{}
|
|
}
|
|
|
|
func (t *MessageTool) Name() string {
|
|
return "message"
|
|
}
|
|
|
|
func (t *MessageTool) Description() string {
|
|
return "Channel actions tool. Supports action=send (default). OpenClaw-compatible fields: action, message/content, to/chat_id, channel."
|
|
}
|
|
|
|
func (t *MessageTool) Parameters() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"action": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Action type: send|edit|delete|react",
|
|
},
|
|
"message": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "The message content to send (OpenClaw-compatible)",
|
|
},
|
|
"content": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Alias of message",
|
|
},
|
|
"to": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Optional target id (alias of chat_id)",
|
|
},
|
|
"channel": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Optional: target channel (telegram, whatsapp, etc.)",
|
|
},
|
|
"chat_id": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Optional: target chat/user ID",
|
|
},
|
|
"media": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Optional media path or URL for action=send",
|
|
},
|
|
"path": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Alias of media",
|
|
},
|
|
"file_path": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Alias of media",
|
|
},
|
|
"filePath": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Alias of media",
|
|
},
|
|
"message_id": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Target message id for edit/delete/react",
|
|
},
|
|
"emoji": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Emoji for react action",
|
|
},
|
|
"buttons": map[string]interface{}{
|
|
"type": "array",
|
|
"description": "Optional: buttons to include in the message (2D array: rows of buttons)",
|
|
"items": map[string]interface{}{
|
|
"type": "array",
|
|
"items": map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"text": map[string]interface{}{"type": "string", "description": "Button text"},
|
|
"data": map[string]interface{}{"type": "string", "description": "Callback data"},
|
|
},
|
|
"required": []string{"text", "data"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"required": []string{},
|
|
}
|
|
}
|
|
|
|
func (t *MessageTool) SetContext(channel, chatID string) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
t.defaultChannel = channel
|
|
t.defaultChatID = chatID
|
|
}
|
|
|
|
func (t *MessageTool) SetSendCallback(callback SendCallback) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
t.sendCallback = callback
|
|
}
|
|
|
|
func (t *MessageTool) Execute(ctx context.Context, args map[string]interface{}) (string, error) {
|
|
action, _ := args["action"].(string)
|
|
action = strings.ToLower(strings.TrimSpace(action))
|
|
if action == "" {
|
|
action = "send"
|
|
}
|
|
content, _ := args["content"].(string)
|
|
if msg, _ := args["message"].(string); msg != "" {
|
|
content = msg
|
|
}
|
|
media, _ := args["media"].(string)
|
|
if media == "" {
|
|
if p, _ := args["path"].(string); p != "" {
|
|
media = p
|
|
}
|
|
}
|
|
if media == "" {
|
|
if p, _ := args["file_path"].(string); p != "" {
|
|
media = p
|
|
}
|
|
}
|
|
if media == "" {
|
|
if p, _ := args["filePath"].(string); p != "" {
|
|
media = p
|
|
}
|
|
}
|
|
messageID, _ := args["message_id"].(string)
|
|
emoji, _ := args["emoji"].(string)
|
|
|
|
switch action {
|
|
case "send":
|
|
if content == "" && media == "" {
|
|
return "", fmt.Errorf("%w: message/content or media for action=send", ErrMissingField)
|
|
}
|
|
case "edit":
|
|
if messageID == "" || content == "" {
|
|
return "", fmt.Errorf("%w: message_id and message/content for action=edit", ErrMissingField)
|
|
}
|
|
case "delete":
|
|
if messageID == "" {
|
|
return "", fmt.Errorf("%w: message_id for action=delete", ErrMissingField)
|
|
}
|
|
case "react":
|
|
if messageID == "" || emoji == "" {
|
|
return "", fmt.Errorf("%w: message_id and emoji for action=react", ErrMissingField)
|
|
}
|
|
default:
|
|
return "", fmt.Errorf("%w: %s", ErrUnsupportedAction, action)
|
|
}
|
|
|
|
channel, _ := args["channel"].(string)
|
|
chatID, _ := args["chat_id"].(string)
|
|
if to, _ := args["to"].(string); to != "" {
|
|
chatID = to
|
|
}
|
|
|
|
t.mu.RLock()
|
|
defaultChannel := t.defaultChannel
|
|
defaultChatID := t.defaultChatID
|
|
sendCallback := t.sendCallback
|
|
t.mu.RUnlock()
|
|
|
|
if channel == "" {
|
|
channel = defaultChannel
|
|
}
|
|
if chatID == "" {
|
|
chatID = defaultChatID
|
|
}
|
|
|
|
if channel == "" || chatID == "" {
|
|
return "Error: No target channel/chat specified", nil
|
|
}
|
|
|
|
if sendCallback == nil {
|
|
return "Error: Message sending not configured", nil
|
|
}
|
|
|
|
var buttons [][]bus.Button
|
|
if btns, ok := args["buttons"].([]interface{}); ok {
|
|
for _, row := range btns {
|
|
if rowArr, ok := row.([]interface{}); ok {
|
|
pooled := buttonRowPool.Get().([]bus.Button)
|
|
buttonRow := pooled[:0]
|
|
for _, b := range rowArr {
|
|
if bMap, ok := b.(map[string]interface{}); ok {
|
|
text, _ := bMap["text"].(string)
|
|
data, _ := bMap["data"].(string)
|
|
if text != "" && data != "" {
|
|
buttonRow = append(buttonRow, bus.Button{Text: text, Data: data})
|
|
}
|
|
}
|
|
}
|
|
if len(buttonRow) > 0 {
|
|
copied := append([]bus.Button(nil), buttonRow...)
|
|
buttons = append(buttons, copied)
|
|
}
|
|
buttonRowPool.Put(buttonRow[:0])
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := sendCallback(channel, chatID, action, content, media, messageID, emoji, buttons); err != nil {
|
|
return fmt.Sprintf("Error sending message: %v", err), nil
|
|
}
|
|
|
|
return fmt.Sprintf("Message action=%s sent to %s:%s", action, channel, chatID), nil
|
|
}
|