feat: 支持获取剪贴板

This commit is contained in:
余泓铮
2025-07-24 17:26:25 +08:00
parent 9add5df1b2
commit 8b20fceb63
7 changed files with 81 additions and 17 deletions
+6 -10
View File
@@ -574,30 +574,26 @@ func (ud *UIA2Driver) SetPasteboard(contentType types.PasteboardType, content st
return return
} }
func (ud *UIA2Driver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { func (ud *UIA2Driver) GetPasteboard() (content string, err error) {
if len(contentType) == 0 {
contentType = types.PasteboardTypePlaintext
}
// register(postHandler, new GetClipboard("/wd/hub/session/:sessionId/appium/device/get_clipboard")) // register(postHandler, new GetClipboard("/wd/hub/session/:sessionId/appium/device/get_clipboard"))
data := map[string]interface{}{ data := map[string]interface{}{
"contentType": contentType[0], "contentType": types.PasteboardTypePlaintext,
} }
var rawResp DriverRawResponse var rawResp DriverRawResponse
urlStr := fmt.Sprintf("/session/%s/appium/device/get_clipboard", ud.Session.ID) urlStr := fmt.Sprintf("/session/%s/appium/device/get_clipboard", ud.Session.ID)
if rawResp, err = ud.Session.POST(data, urlStr); err != nil { if rawResp, err = ud.Session.POST(data, urlStr); err != nil {
return return "", err
} }
reply := new(struct{ Value string }) reply := new(struct{ Value string })
if err = json.Unmarshal(rawResp, reply); err != nil { if err = json.Unmarshal(rawResp, reply); err != nil {
return return "", err
} }
if data, err := base64.StdEncoding.DecodeString(reply.Value); err != nil { if data, err := base64.StdEncoding.DecodeString(reply.Value); err != nil {
raw.Write([]byte(reply.Value)) return reply.Value, nil
} else { } else {
raw.Write(data) return string(data), nil
} }
return
} }
// SendKeys Android input does not support setting frequency. // SendKeys Android input does not support setting frequency.
+4 -1
View File
@@ -365,7 +365,6 @@ func (wd *BrowserDriver) Input(text string, option ...option.ActionOption) (err
// Source Return application elements tree // Source Return application elements tree
func (wd *BrowserDriver) Source(srcOpt ...option.SourceOption) (string, error) { func (wd *BrowserDriver) Source(srcOpt ...option.SourceOption) (string, error) {
result, err := wd.CustomeGet(wd.concatURL(wd.Session.ID, "stub/source")) result, err := wd.CustomeGet(wd.concatURL(wd.Session.ID, "stub/source"))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -751,3 +750,7 @@ func (wd *BrowserDriver) CustomeGet(urlStr string) (response *WebAgentResponse,
return &webResp, err return &webResp, err
} }
func (wd *BrowserDriver) GetPasteboard() (content string, err error) {
return "", errors.New("not implemented")
}
+3
View File
@@ -86,4 +86,7 @@ type IDriver interface {
// triggers the log capture and returns the log entries // triggers the log capture and returns the log entries
StartCaptureLog(identifier ...string) error StartCaptureLog(identifier ...string) error
StopCaptureLog() (result interface{}, err error) StopCaptureLog() (result interface{}, err error)
// clipboard operations
GetPasteboard() (string, error)
} }
+4
View File
@@ -348,3 +348,7 @@ func (hd *HDCDriver) SecondaryClick(x, y float64) (err error) {
func (hd *HDCDriver) SecondaryClickBySelector(selector string, options ...option.ActionOption) (err error) { func (hd *HDCDriver) SecondaryClickBySelector(selector string, options ...option.ActionOption) (err error) {
return err return err
} }
func (hd *HDCDriver) GetPasteboard() (content string, err error) {
return "", errors.New("not implemented")
}
+6 -5
View File
@@ -630,21 +630,22 @@ func (wd *WDADriver) SetPasteboard(contentType types.PasteboardType, content str
return return
} }
func (wd *WDADriver) GetPasteboard() (raw *bytes.Buffer, err error) { func (wd *WDADriver) GetPasteboard() (content string, err error) {
// [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)] // [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)]
err = wd.AppLaunch("com.gtf.wda.runner.xctrunner") err = wd.AppLaunch("com.gtf.wda.runner.xctrunner")
if err != nil { if err != nil {
return nil, errors.Wrap(err, "GetPasteboard failed. WDA app not launched") return "", errors.Wrap(err, "GetPasteboard failed. WDA app not launched")
} }
data := map[string]interface{}{} data := map[string]interface{}{}
var rawResp DriverRawResponse var rawResp DriverRawResponse
if rawResp, err = wd.Session.POST(data, "/wda/getPasteboard"); err != nil { if rawResp, err = wd.Session.POST(data, "/wda/getPasteboard"); err != nil {
return nil, err return "", err
} }
var raw *bytes.Buffer
if raw, err = rawResp.ValueDecodeAsBase64(); err != nil { if raw, err = rawResp.ValueDecodeAsBase64(); err != nil {
return nil, err return "", err
} }
return return string(raw.Bytes()), nil
} }
func (wd *WDADriver) SetIme(ime string) error { func (wd *WDADriver) SetIme(ime string) error {
+56
View File
@@ -0,0 +1,56 @@
package uixt
import (
"context"
"fmt"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/httprunner/httprunner/v5/uixt/option"
)
// ToolGetPasteboard implements the get_pasteboard tool call.
type ToolGetPasteboard struct {
// Return data fields - these define the structure of data returned by this tool
Content string `json:"content" desc:"Clipboard content that was retrieved"`
}
func (t *ToolGetPasteboard) Name() option.ActionName {
return option.ACTION_GetPasteboard
}
func (t *ToolGetPasteboard) Description() string {
return "Get the clipboard content from the device"
}
func (t *ToolGetPasteboard) Options() []mcp.ToolOption {
unifiedReq := &option.ActionOptions{}
return unifiedReq.GetMCPOptions(option.ACTION_GetPasteboard)
}
func (t *ToolGetPasteboard) Implement() server.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
arguments := request.GetArguments()
driverExt, err := setupXTDriver(ctx, arguments)
if err != nil {
return nil, fmt.Errorf("setup driver failed: %w", err)
}
// Directly call the GetPasteboard method on the driver
content, err := driverExt.IDriver.GetPasteboard()
if err != nil {
return NewMCPErrorResponse(fmt.Sprintf("Get pasteboard failed: %s", err.Error())), err
}
message := "Successfully retrieved clipboard content"
returnData := ToolGetPasteboard{Content: content}
return NewMCPSuccessResponse(message, &returnData), nil
}
}
func (t *ToolGetPasteboard) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
arguments := map[string]any{}
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
}
+1
View File
@@ -55,6 +55,7 @@ const (
ACTION_SetIme ActionName = "set_ime" ACTION_SetIme ActionName = "set_ime"
ACTION_GetSource ActionName = "get_source" ACTION_GetSource ActionName = "get_source"
ACTION_GetForegroundApp ActionName = "get_foreground_app" ACTION_GetForegroundApp ActionName = "get_foreground_app"
ACTION_GetPasteboard ActionName = "get_pasteboard" // get clipboard content
ACTION_AppInfo ActionName = "app_info" // get app info action ACTION_AppInfo ActionName = "app_info" // get app info action
// UI handling // UI handling