Browse Source
feat: adding MCP (SSE) to communicate with AI Agent
feat: adding MCP (SSE) to communicate with AI Agent
commitpull/301/head v6.0.09a21f32d73Author: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sun May 25 10:32:50 2025 +0700 feat: add image sending functionality to WhatsApp MCP server - Introduced a new tool for sending images to WhatsApp contacts or groups. - Implemented handler functions to process image sending requests, including parameters for phone number, image URL, caption, and additional options. - Enhanced the MCP server capabilities with image handling features. commit936069e068Author: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 18:47:36 2025 +0700 fix: update Send Poll image URL in README to include versioning for cache busting commit409742dfecAuthor: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 18:45:54 2025 +0700 refactor: reorganize HTTP REST API section in README for clarity commitdf1889ad5bAuthor: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 18:42:09 2025 +0700 docs: enhance README with additional MCP UI details and formatting improvements commit74b97f2dd6Author: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 18:31:44 2025 +0700 feat: update README to clarify download instructions and add command for service commitc24991ddf0Author: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 18:29:01 2025 +0700 feat: enhance README with MCP server details and configuration commit8ef03eb1e4Author: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 18:03:41 2025 +0700 feat: enhance MCP server with new WhatsApp tools - Added new tools for sending messages, contacts, links, and locations via WhatsApp. - Updated the project structure documentation to include the new MCP server directory. - Implemented corresponding handler functions for each new tool to process requests and return results. commitdad99583e9Author: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 16:01:50 2025 +0700 feat: implement WhatsApp MCP server with SSE support - Added a new command to start the WhatsApp MCP server using Server-Sent Events (SSE). - Introduced configuration options for MCP server host and port. - Implemented message sending functionality through the MCP server. - Updated application version to v6.0.0. - Added new dependencies for MCP server functionality in go.mod and go.sum. commitefcdcec087Author: Aldino Kemal <aldinokemal2104@gmail.com> Date: Sat May 24 11:05:58 2025 +0700 feat: restructure folder
35 changed files with 887 additions and 262 deletions
-
122.cursor/rules/project-structure.mdc
-
79readme.md
-
69src/cmd/mcp.go
-
122src/cmd/rest.go
-
168src/cmd/root.go
-
5src/config/settings.go
-
2src/domains/app/app.go
-
2src/domains/group/group.go
-
2src/domains/message/message.go
-
2src/domains/newsletter/newsletter.go
-
2src/domains/send/send.go
-
2src/domains/user/user.go
-
13src/go.mod
-
94src/go.sum
-
2src/infrastructure/whatsapp/init.go
-
0src/infrastructure/whatsapp/utils.go
-
0src/infrastructure/whatsapp/webhook.go
-
338src/ui/mcp/send.go
-
5src/ui/rest/app.go
-
6src/ui/rest/group.go
-
7src/ui/rest/helpers/common.go
-
0src/ui/rest/helpers/flushChatCsv.go
-
6src/ui/rest/message.go
-
0src/ui/rest/middleware/basicauth.go
-
0src/ui/rest/middleware/recovery.go
-
4src/ui/rest/newsletter.go
-
6src/ui/rest/send.go
-
6src/ui/rest/user.go
-
2src/ui/websocket/websocket.go
-
4src/usecase/app.go
-
24src/usecase/group.go
-
6src/usecase/message.go
-
13src/usecase/newsletter.go
-
10src/usecase/send.go
-
26src/usecase/user.go
@ -0,0 +1,122 @@ |
|||||
|
--- |
||||
|
description: |
||||
|
globs: |
||||
|
alwaysApply: false |
||||
|
--- |
||||
|
# Go WhatsApp Web Multidevice API |
||||
|
|
||||
|
This is a Go implementation of a WhatsApp Web Multidevice API that allows you to interact with WhatsApp through HTTP APIs. |
||||
|
|
||||
|
## Project Structure |
||||
|
|
||||
|
### Root Directory |
||||
|
|
||||
|
- [readme.md](mdc:readme.md) - Project documentation and usage instructions |
||||
|
- [docker-compose.yml](mdc:docker-compose.yml) - Docker configuration for running the application |
||||
|
- [LICENCE.txt](mdc:LICENCE.txt) - License information |
||||
|
|
||||
|
### Source Code (`src/`) |
||||
|
|
||||
|
The main source code is organized in the `src` directory with the following structure: |
||||
|
|
||||
|
#### Command Line Interface |
||||
|
|
||||
|
- [src/cmd/root.go](mdc:src/cmd/root.go) - Main entry point using Cobra for CLI commands, handles configuration loading and server initialization |
||||
|
|
||||
|
#### Configuration |
||||
|
|
||||
|
- [src/config/](mdc:src/config) - Application configuration settings and constants |
||||
|
|
||||
|
#### Domain Models |
||||
|
|
||||
|
The application is organized using domain-driven design principles: |
||||
|
|
||||
|
- [src/domains/app/](mdc:src/domains/app) - Core application domain models |
||||
|
- [src/domains/group/](mdc:src/domains/group) - Group-related domain models |
||||
|
- [src/domains/message/](mdc:src/domains/message) - Message-related domain models |
||||
|
- [src/domains/newsletter/](mdc:src/domains/newsletter) - Newsletter-related domain models |
||||
|
- [src/domains/send/](mdc:src/domains/send) - Message sending domain models |
||||
|
- [src/domains/user/](mdc:src/domains/user) - User-related domain models |
||||
|
|
||||
|
#### Infrastructure |
||||
|
|
||||
|
- [src/infrastructure/whatsapp/](mdc:src/infrastructure/whatsapp) - WhatsApp client implementation and related infrastructure |
||||
|
|
||||
|
#### User Interface |
||||
|
|
||||
|
- [src/ui/rest/](mdc:src/ui/rest) - REST API implementation |
||||
|
- [src/ui/rest/helpers/](mdc:src/ui/rest/helpers) - Helper functions for REST handlers |
||||
|
- [src/ui/rest/middleware/](mdc:src/ui/rest/middleware) - Middleware components for request processing |
||||
|
- [src/ui/websocket/](mdc:src/ui/websocket) - WebSocket implementation for real-time communication |
||||
|
- [src/ui/mcp/](mdc:src/ui/mcp) - Model Context Protocol server to communication with AI Agent |
||||
|
|
||||
|
#### Utilities and Shared Components |
||||
|
|
||||
|
- [src/pkg/error/](mdc:src/pkg/error) - Error handling utilities |
||||
|
- [src/pkg/utils/](mdc:src/pkg/utils) - General utility functions |
||||
|
|
||||
|
#### Use Cases |
||||
|
|
||||
|
- [src/usecase/](mdc:src/usecase) - Application services that implement business logic |
||||
|
|
||||
|
#### Static Resources |
||||
|
|
||||
|
- [src/statics/](mdc:src/statics) - Static resources like media files |
||||
|
- [src/statics/media/](mdc:src/statics/media) - Media files |
||||
|
- [src/statics/qrcode/](mdc:src/statics/qrcode) - QR code images for WhatsApp authentication |
||||
|
- [src/statics/senditems/](mdc:src/statics/senditems) - Items to be sent via WhatsApp |
||||
|
|
||||
|
#### Storage |
||||
|
|
||||
|
- [src/storages/](mdc:src/storages) - Storage-related functionality and database connection |
||||
|
|
||||
|
#### Temporary Files |
||||
|
|
||||
|
- [src/tmp/](mdc:src/tmp) - Temporary files and directories |
||||
|
|
||||
|
#### Validation |
||||
|
|
||||
|
- [src/validations/](mdc:src/validations) - Request validation logic |
||||
|
|
||||
|
#### Views |
||||
|
|
||||
|
- [src/views/](mdc:src/views) - Templates and UI components |
||||
|
- [src/views/assets/](mdc:src/views/assets) - Frontend assets (CSS, JS, etc.) |
||||
|
- [src/views/components/](mdc:src/views/components) - Reusable UI components |
||||
|
- [src/views/components/generic/](mdc:src/views/components/generic) - Generic UI components |
||||
|
|
||||
|
### Documentation |
||||
|
|
||||
|
- [docs/](mdc:docs) - Project documentation |
||||
|
- [docs/sdk/](mdc:docs/sdk) - SDK documentation |
||||
|
|
||||
|
### Docker |
||||
|
|
||||
|
- [docker/](mdc:docker) - Docker-related files and configurations |
||||
|
|
||||
|
### GitHub Configuration |
||||
|
|
||||
|
- [.github/](mdc:.github) - GitHub-specific configuration |
||||
|
- [.github/ISSUE_TEMPLATE/](mdc:.github/ISSUE_TEMPLATE) - Templates for GitHub issues |
||||
|
- [.github/workflows/](mdc:.github/workflows) - GitHub Actions workflows |
||||
|
|
||||
|
## Key Application Features |
||||
|
|
||||
|
- WhatsApp login via QR code or pairing code |
||||
|
- Send/receive messages, media, contacts, locations |
||||
|
- Group management features |
||||
|
- Newsletter management |
||||
|
- WebSocket real-time updates |
||||
|
- Webhooks for message events |
||||
|
- Auto-reply functionality |
||||
|
|
||||
|
## Application Flow |
||||
|
|
||||
|
1. The application starts from [src/cmd/root.go](mdc:src/cmd/root.go) |
||||
|
2. Configuration is loaded from environment variables or command line flags |
||||
|
3. The REST server is initialized using Fiber framework |
||||
|
4. WhatsApp client is initialized and services are created |
||||
|
5. REST routes are registered for different domains |
||||
|
6. WebSocket hub is started for real-time communication |
||||
|
7. Background tasks are started (auto-reconnect, chat storage flushing) |
||||
|
8. The server listens for requests on the configured port |
||||
@ -0,0 +1,69 @@ |
|||||
|
package cmd |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"log" |
||||
|
|
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/mcp" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/rest/helpers" |
||||
|
"github.com/mark3labs/mcp-go/server" |
||||
|
"github.com/spf13/cobra" |
||||
|
) |
||||
|
|
||||
|
// rootCmd represents the base command when called without any subcommands
|
||||
|
var mcpCmd = &cobra.Command{ |
||||
|
Use: "mcp", |
||||
|
Short: "Start WhatsApp MCP server using SSE", |
||||
|
Long: `Start a WhatsApp MCP (Model Context Protocol) server using Server-Sent Events (SSE) transport. This allows AI agents to interact with WhatsApp through a standardized protocol.`, |
||||
|
Run: mcpServer, |
||||
|
} |
||||
|
|
||||
|
func init() { |
||||
|
rootCmd.AddCommand(mcpCmd) |
||||
|
mcpCmd.Flags().StringVar(&config.McpPort, "port", "8080", "Port for the SSE MCP server") |
||||
|
mcpCmd.Flags().StringVar(&config.McpHost, "host", "localhost", "Host for the SSE MCP server") |
||||
|
} |
||||
|
|
||||
|
func mcpServer(_ *cobra.Command, _ []string) { |
||||
|
// Prepare folders if not exist
|
||||
|
err := utils.CreateFolder(config.PathQrCode, config.PathSendItems, config.PathStorages, config.PathMedia) |
||||
|
if err != nil { |
||||
|
log.Fatalf("Failed to create folders: %v", err) |
||||
|
} |
||||
|
|
||||
|
// Set auto reconnect to whatsapp server after booting
|
||||
|
go helpers.SetAutoConnectAfterBooting(appUsecase) |
||||
|
// Set auto reconnect checking
|
||||
|
go helpers.SetAutoReconnectChecking(whatsappCli) |
||||
|
|
||||
|
// Create MCP server with capabilities
|
||||
|
mcpServer := server.NewMCPServer( |
||||
|
"WhatsApp Web Multidevice MCP Server", |
||||
|
config.AppVersion, |
||||
|
server.WithToolCapabilities(true), |
||||
|
server.WithResourceCapabilities(true, true), |
||||
|
) |
||||
|
|
||||
|
// Add all WhatsApp tools
|
||||
|
sendHandler := mcp.InitMcpSend(sendUsecase) |
||||
|
sendHandler.AddSendTools(mcpServer) |
||||
|
|
||||
|
// Create SSE server
|
||||
|
sseServer := server.NewSSEServer( |
||||
|
mcpServer, |
||||
|
server.WithBaseURL(fmt.Sprintf("http://%s:%s", config.McpHost, config.McpPort)), |
||||
|
server.WithKeepAlive(true), |
||||
|
) |
||||
|
|
||||
|
// Start the SSE server
|
||||
|
addr := fmt.Sprintf("%s:%s", config.McpHost, config.McpPort) |
||||
|
log.Printf("Starting WhatsApp MCP SSE server on %s", addr) |
||||
|
log.Printf("SSE endpoint: http://%s:%s/sse", config.McpHost, config.McpPort) |
||||
|
log.Printf("Message endpoint: http://%s:%s/message", config.McpHost, config.McpPort) |
||||
|
|
||||
|
if err := sseServer.Start(addr); err != nil { |
||||
|
log.Fatalf("Failed to start SSE server: %v", err) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
package cmd |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"log" |
||||
|
"net/http" |
||||
|
"strings" |
||||
|
|
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/rest" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/rest/helpers" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/rest/middleware" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/websocket" |
||||
|
"github.com/dustin/go-humanize" |
||||
|
"github.com/gofiber/fiber/v2" |
||||
|
"github.com/gofiber/fiber/v2/middleware/basicauth" |
||||
|
"github.com/gofiber/fiber/v2/middleware/cors" |
||||
|
"github.com/gofiber/fiber/v2/middleware/filesystem" |
||||
|
"github.com/gofiber/fiber/v2/middleware/logger" |
||||
|
"github.com/gofiber/template/html/v2" |
||||
|
"github.com/spf13/cobra" |
||||
|
) |
||||
|
|
||||
|
// rootCmd represents the base command when called without any subcommands
|
||||
|
var restCmd = &cobra.Command{ |
||||
|
Use: "rest", |
||||
|
Short: "Send whatsapp API over http", |
||||
|
Long: `This application is from clone https://github.com/aldinokemal/go-whatsapp-web-multidevice`, |
||||
|
Run: restServer, |
||||
|
} |
||||
|
|
||||
|
func init() { |
||||
|
rootCmd.AddCommand(restCmd) |
||||
|
} |
||||
|
func restServer(_ *cobra.Command, _ []string) { |
||||
|
//preparing folder if not exist
|
||||
|
err := utils.CreateFolder(config.PathQrCode, config.PathSendItems, config.PathStorages, config.PathMedia) |
||||
|
if err != nil { |
||||
|
log.Fatalln(err) |
||||
|
} |
||||
|
|
||||
|
engine := html.NewFileSystem(http.FS(EmbedIndex), ".html") |
||||
|
engine.AddFunc("isEnableBasicAuth", func(token any) bool { |
||||
|
return token != nil |
||||
|
}) |
||||
|
app := fiber.New(fiber.Config{ |
||||
|
Views: engine, |
||||
|
BodyLimit: int(config.WhatsappSettingMaxVideoSize), |
||||
|
}) |
||||
|
|
||||
|
app.Static("/statics", "./statics") |
||||
|
app.Use("/components", filesystem.New(filesystem.Config{ |
||||
|
Root: http.FS(EmbedViews), |
||||
|
PathPrefix: "views/components", |
||||
|
Browse: true, |
||||
|
})) |
||||
|
app.Use("/assets", filesystem.New(filesystem.Config{ |
||||
|
Root: http.FS(EmbedViews), |
||||
|
PathPrefix: "views/assets", |
||||
|
Browse: true, |
||||
|
})) |
||||
|
|
||||
|
app.Use(middleware.Recovery()) |
||||
|
app.Use(middleware.BasicAuth()) |
||||
|
if config.AppDebug { |
||||
|
app.Use(logger.New()) |
||||
|
} |
||||
|
app.Use(cors.New(cors.Config{ |
||||
|
AllowOrigins: "*", |
||||
|
AllowHeaders: "Origin, Content-Type, Accept", |
||||
|
})) |
||||
|
|
||||
|
if len(config.AppBasicAuthCredential) > 0 { |
||||
|
account := make(map[string]string) |
||||
|
for _, basicAuth := range config.AppBasicAuthCredential { |
||||
|
ba := strings.Split(basicAuth, ":") |
||||
|
if len(ba) != 2 { |
||||
|
log.Fatalln("Basic auth is not valid, please this following format <user>:<secret>") |
||||
|
} |
||||
|
account[ba[0]] = ba[1] |
||||
|
} |
||||
|
|
||||
|
app.Use(basicauth.New(basicauth.Config{ |
||||
|
Users: account, |
||||
|
})) |
||||
|
} |
||||
|
|
||||
|
// Rest
|
||||
|
rest.InitRestApp(app, appUsecase) |
||||
|
rest.InitRestSend(app, sendUsecase) |
||||
|
rest.InitRestUser(app, userUsecase) |
||||
|
rest.InitRestMessage(app, messageUsecase) |
||||
|
rest.InitRestGroup(app, groupUsecase) |
||||
|
rest.InitRestNewsletter(app, newsletterUsecase) |
||||
|
|
||||
|
app.Get("/", func(c *fiber.Ctx) error { |
||||
|
return c.Render("views/index", fiber.Map{ |
||||
|
"AppHost": fmt.Sprintf("%s://%s", c.Protocol(), c.Hostname()), |
||||
|
"AppVersion": config.AppVersion, |
||||
|
"BasicAuthToken": c.UserContext().Value(middleware.AuthorizationValue("BASIC_AUTH")), |
||||
|
"MaxFileSize": humanize.Bytes(uint64(config.WhatsappSettingMaxFileSize)), |
||||
|
"MaxVideoSize": humanize.Bytes(uint64(config.WhatsappSettingMaxVideoSize)), |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
websocket.RegisterRoutes(app, appUsecase) |
||||
|
go websocket.RunHub() |
||||
|
|
||||
|
// Set auto reconnect to whatsapp server after booting
|
||||
|
go helpers.SetAutoConnectAfterBooting(appUsecase) |
||||
|
// Set auto reconnect checking
|
||||
|
go helpers.SetAutoReconnectChecking(whatsappCli) |
||||
|
// Start auto flush chat csv
|
||||
|
if config.WhatsappChatStorage { |
||||
|
go helpers.StartAutoFlushChatStorage() |
||||
|
} |
||||
|
|
||||
|
if err = app.Listen(":" + config.AppPort); err != nil { |
||||
|
log.Fatalln("Failed to start: ", err.Error()) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,338 @@ |
|||||
|
package mcp |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
|
||||
|
domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send" |
||||
|
"github.com/mark3labs/mcp-go/mcp" |
||||
|
"github.com/mark3labs/mcp-go/server" |
||||
|
) |
||||
|
|
||||
|
type SendHandler struct { |
||||
|
sendService domainSend.ISendUsecase |
||||
|
} |
||||
|
|
||||
|
func InitMcpSend(sendService domainSend.ISendUsecase) *SendHandler { |
||||
|
return &SendHandler{ |
||||
|
sendService: sendService, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) AddSendTools(mcpServer *server.MCPServer) { |
||||
|
mcpServer.AddTool(s.toolSendText(), s.handleSendText) |
||||
|
mcpServer.AddTool(s.toolSendContact(), s.handleSendContact) |
||||
|
mcpServer.AddTool(s.toolSendLink(), s.handleSendLink) |
||||
|
mcpServer.AddTool(s.toolSendLocation(), s.handleSendLocation) |
||||
|
mcpServer.AddTool(s.toolSendImage(), s.handleSendImage) |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) toolSendText() mcp.Tool { |
||||
|
sendTextTool := mcp.NewTool("whatsapp_send_text", |
||||
|
mcp.WithDescription("Send a text message to a WhatsApp contact or group."), |
||||
|
mcp.WithString("phone", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Phone number or group ID to send message to"), |
||||
|
), |
||||
|
mcp.WithString("message", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("The text message to send"), |
||||
|
), |
||||
|
mcp.WithBoolean("is_forwarded", |
||||
|
mcp.Description("Whether this message is being forwarded (default: false)"), |
||||
|
), |
||||
|
mcp.WithString("reply_message_id", |
||||
|
mcp.Description("Message ID to reply to (optional)"), |
||||
|
), |
||||
|
) |
||||
|
|
||||
|
return sendTextTool |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) handleSendText(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { |
||||
|
phone, ok := request.GetArguments()["phone"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("phone must be a string") |
||||
|
} |
||||
|
|
||||
|
message, ok := request.GetArguments()["message"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("message must be a string") |
||||
|
} |
||||
|
|
||||
|
isForwarded, ok := request.GetArguments()["is_forwarded"].(bool) |
||||
|
if !ok { |
||||
|
isForwarded = false |
||||
|
} |
||||
|
|
||||
|
replyMessageId, ok := request.GetArguments()["reply_message_id"].(string) |
||||
|
if !ok { |
||||
|
replyMessageId = "" |
||||
|
} |
||||
|
|
||||
|
res, err := s.sendService.SendText(ctx, domainSend.MessageRequest{ |
||||
|
Phone: phone, |
||||
|
Message: message, |
||||
|
IsForwarded: isForwarded, |
||||
|
ReplyMessageID: &replyMessageId, |
||||
|
}) |
||||
|
|
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return mcp.NewToolResultText(fmt.Sprintf("Message sent successfully with ID %s", res.MessageID)), nil |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) toolSendContact() mcp.Tool { |
||||
|
sendContactTool := mcp.NewTool("whatsapp_send_contact", |
||||
|
mcp.WithDescription("Send a contact card to a WhatsApp contact or group."), |
||||
|
mcp.WithString("phone", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Phone number or group ID to send contact to"), |
||||
|
), |
||||
|
mcp.WithString("contact_name", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Name of the contact to send"), |
||||
|
), |
||||
|
mcp.WithString("contact_phone", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Phone number of the contact to send"), |
||||
|
), |
||||
|
mcp.WithBoolean("is_forwarded", |
||||
|
mcp.Description("Whether this message is being forwarded (default: false)"), |
||||
|
), |
||||
|
) |
||||
|
|
||||
|
return sendContactTool |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) handleSendContact(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { |
||||
|
phone, ok := request.GetArguments()["phone"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("phone must be a string") |
||||
|
} |
||||
|
|
||||
|
contactName, ok := request.GetArguments()["contact_name"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("contact_name must be a string") |
||||
|
} |
||||
|
|
||||
|
contactPhone, ok := request.GetArguments()["contact_phone"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("contact_phone must be a string") |
||||
|
} |
||||
|
|
||||
|
isForwarded, ok := request.GetArguments()["is_forwarded"].(bool) |
||||
|
if !ok { |
||||
|
isForwarded = false |
||||
|
} |
||||
|
|
||||
|
res, err := s.sendService.SendContact(ctx, domainSend.ContactRequest{ |
||||
|
Phone: phone, |
||||
|
ContactName: contactName, |
||||
|
ContactPhone: contactPhone, |
||||
|
IsForwarded: isForwarded, |
||||
|
}) |
||||
|
|
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return mcp.NewToolResultText(fmt.Sprintf("Contact sent successfully with ID %s", res.MessageID)), nil |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) toolSendLink() mcp.Tool { |
||||
|
sendLinkTool := mcp.NewTool("whatsapp_send_link", |
||||
|
mcp.WithDescription("Send a link with caption to a WhatsApp contact or group."), |
||||
|
mcp.WithString("phone", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Phone number or group ID to send link to"), |
||||
|
), |
||||
|
mcp.WithString("link", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("URL link to send"), |
||||
|
), |
||||
|
mcp.WithString("caption", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Caption or description for the link"), |
||||
|
), |
||||
|
mcp.WithBoolean("is_forwarded", |
||||
|
mcp.Description("Whether this message is being forwarded (default: false)"), |
||||
|
), |
||||
|
) |
||||
|
|
||||
|
return sendLinkTool |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) handleSendLink(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { |
||||
|
phone, ok := request.GetArguments()["phone"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("phone must be a string") |
||||
|
} |
||||
|
|
||||
|
link, ok := request.GetArguments()["link"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("link must be a string") |
||||
|
} |
||||
|
|
||||
|
caption, ok := request.GetArguments()["caption"].(string) |
||||
|
if !ok { |
||||
|
caption = "" |
||||
|
} |
||||
|
|
||||
|
isForwarded, ok := request.GetArguments()["is_forwarded"].(bool) |
||||
|
if !ok { |
||||
|
isForwarded = false |
||||
|
} |
||||
|
|
||||
|
res, err := s.sendService.SendLink(ctx, domainSend.LinkRequest{ |
||||
|
Phone: phone, |
||||
|
Link: link, |
||||
|
Caption: caption, |
||||
|
IsForwarded: isForwarded, |
||||
|
}) |
||||
|
|
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return mcp.NewToolResultText(fmt.Sprintf("Link sent successfully with ID %s", res.MessageID)), nil |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) toolSendLocation() mcp.Tool { |
||||
|
sendLocationTool := mcp.NewTool("whatsapp_send_location", |
||||
|
mcp.WithDescription("Send a location coordinates to a WhatsApp contact or group."), |
||||
|
mcp.WithString("phone", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Phone number or group ID to send location to"), |
||||
|
), |
||||
|
mcp.WithString("latitude", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Latitude coordinate (as string)"), |
||||
|
), |
||||
|
mcp.WithString("longitude", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Longitude coordinate (as string)"), |
||||
|
), |
||||
|
mcp.WithBoolean("is_forwarded", |
||||
|
mcp.Description("Whether this message is being forwarded (default: false)"), |
||||
|
), |
||||
|
) |
||||
|
|
||||
|
return sendLocationTool |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) handleSendLocation(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { |
||||
|
phone, ok := request.GetArguments()["phone"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("phone must be a string") |
||||
|
} |
||||
|
|
||||
|
latitude, ok := request.GetArguments()["latitude"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("latitude must be a string") |
||||
|
} |
||||
|
|
||||
|
longitude, ok := request.GetArguments()["longitude"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("longitude must be a string") |
||||
|
} |
||||
|
|
||||
|
isForwarded, ok := request.GetArguments()["is_forwarded"].(bool) |
||||
|
if !ok { |
||||
|
isForwarded = false |
||||
|
} |
||||
|
|
||||
|
res, err := s.sendService.SendLocation(ctx, domainSend.LocationRequest{ |
||||
|
Phone: phone, |
||||
|
Latitude: latitude, |
||||
|
Longitude: longitude, |
||||
|
IsForwarded: isForwarded, |
||||
|
}) |
||||
|
|
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return mcp.NewToolResultText(fmt.Sprintf("Location sent successfully with ID %s", res.MessageID)), nil |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) toolSendImage() mcp.Tool { |
||||
|
sendImageTool := mcp.NewTool("whatsapp_send_image", |
||||
|
mcp.WithDescription("Send an image to a WhatsApp contact or group."), |
||||
|
mcp.WithString("phone", |
||||
|
mcp.Required(), |
||||
|
mcp.Description("Phone number or group ID to send image to"), |
||||
|
), |
||||
|
mcp.WithString("image_url", |
||||
|
mcp.Description("URL of the image to send"), |
||||
|
), |
||||
|
mcp.WithString("caption", |
||||
|
mcp.Description("Caption or description for the image"), |
||||
|
), |
||||
|
mcp.WithBoolean("view_once", |
||||
|
mcp.Description("Whether this image should be viewed only once (default: false)"), |
||||
|
), |
||||
|
mcp.WithBoolean("compress", |
||||
|
mcp.Description("Whether to compress the image (default: true)"), |
||||
|
), |
||||
|
mcp.WithBoolean("is_forwarded", |
||||
|
mcp.Description("Whether this message is being forwarded (default: false)"), |
||||
|
), |
||||
|
) |
||||
|
|
||||
|
return sendImageTool |
||||
|
} |
||||
|
|
||||
|
func (s *SendHandler) handleSendImage(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { |
||||
|
phone, ok := request.GetArguments()["phone"].(string) |
||||
|
if !ok { |
||||
|
return nil, errors.New("phone must be a string") |
||||
|
} |
||||
|
|
||||
|
imageURL, imageURLOk := request.GetArguments()["image_url"].(string) |
||||
|
if !imageURLOk { |
||||
|
return nil, errors.New("image_url must be a string") |
||||
|
} |
||||
|
|
||||
|
caption, ok := request.GetArguments()["caption"].(string) |
||||
|
if !ok { |
||||
|
caption = "" |
||||
|
} |
||||
|
|
||||
|
viewOnce, ok := request.GetArguments()["view_once"].(bool) |
||||
|
if !ok { |
||||
|
viewOnce = false |
||||
|
} |
||||
|
|
||||
|
compress, ok := request.GetArguments()["compress"].(bool) |
||||
|
if !ok { |
||||
|
compress = true |
||||
|
} |
||||
|
|
||||
|
isForwarded, ok := request.GetArguments()["is_forwarded"].(bool) |
||||
|
if !ok { |
||||
|
isForwarded = false |
||||
|
} |
||||
|
|
||||
|
// Create image request
|
||||
|
imageRequest := domainSend.ImageRequest{ |
||||
|
Phone: phone, |
||||
|
Caption: caption, |
||||
|
ViewOnce: viewOnce, |
||||
|
Compress: compress, |
||||
|
IsForwarded: isForwarded, |
||||
|
} |
||||
|
|
||||
|
if imageURLOk && imageURL != "" { |
||||
|
imageRequest.ImageURL = &imageURL |
||||
|
} |
||||
|
res, err := s.sendService.SendImage(ctx, imageRequest) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return mcp.NewToolResultText(fmt.Sprintf("Image sent successfully with ID %s", res.MessageID)), nil |
||||
|
} |
||||
@ -1,24 +1,25 @@ |
|||||
package services |
|
||||
|
package usecase |
||||
|
|
||||
import ( |
import ( |
||||
"context" |
"context" |
||||
|
|
||||
domainNewsletter "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/newsletter" |
domainNewsletter "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/newsletter" |
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp" |
|
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp" |
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations" |
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations" |
||||
"go.mau.fi/whatsmeow" |
"go.mau.fi/whatsmeow" |
||||
) |
) |
||||
|
|
||||
type newsletterService struct { |
|
||||
|
type serviceNewsletter struct { |
||||
WaCli *whatsmeow.Client |
WaCli *whatsmeow.Client |
||||
} |
} |
||||
|
|
||||
func NewNewsletterService(waCli *whatsmeow.Client) domainNewsletter.INewsletterService { |
|
||||
return &newsletterService{ |
|
||||
|
func NewNewsletterService(waCli *whatsmeow.Client) domainNewsletter.INewsletterUsecase { |
||||
|
return &serviceNewsletter{ |
||||
WaCli: waCli, |
WaCli: waCli, |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
func (service newsletterService) Unfollow(ctx context.Context, request domainNewsletter.UnfollowRequest) (err error) { |
|
||||
|
func (service serviceNewsletter) Unfollow(ctx context.Context, request domainNewsletter.UnfollowRequest) (err error) { |
||||
if err = validations.ValidateUnfollowNewsletter(ctx, request); err != nil { |
if err = validations.ValidateUnfollowNewsletter(ctx, request); err != nil { |
||||
return err |
return err |
||||
} |
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue