Browse Source

feat: adding MCP (SSE) to communicate with AI Agent

commit 9a21f32d73
Author: 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.

commit 936069e068
Author: 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

commit 409742dfec
Author: Aldino Kemal <aldinokemal2104@gmail.com>
Date:   Sat May 24 18:45:54 2025 +0700

    refactor: reorganize HTTP REST API section in README for clarity

commit df1889ad5b
Author: 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

commit 74b97f2dd6
Author: 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

commit c24991ddf0
Author: Aldino Kemal <aldinokemal2104@gmail.com>
Date:   Sat May 24 18:29:01 2025 +0700

    feat: enhance README with MCP server details and configuration

commit 8ef03eb1e4
Author: 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.

commit dad99583e9
Author: 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.

commit efcdcec087
Author: Aldino Kemal <aldinokemal2104@gmail.com>
Date:   Sat May 24 11:05:58 2025 +0700

    feat: restructure folder
pull/301/head v6.0.0
Aldino Kemal 10 months ago
parent
commit
84805f0dad
  1. 122
      .cursor/rules/project-structure.mdc
  2. 79
      readme.md
  3. 69
      src/cmd/mcp.go
  4. 122
      src/cmd/rest.go
  5. 168
      src/cmd/root.go
  6. 5
      src/config/settings.go
  7. 2
      src/domains/app/app.go
  8. 2
      src/domains/group/group.go
  9. 2
      src/domains/message/message.go
  10. 2
      src/domains/newsletter/newsletter.go
  11. 2
      src/domains/send/send.go
  12. 2
      src/domains/user/user.go
  13. 13
      src/go.mod
  14. 94
      src/go.sum
  15. 2
      src/infrastructure/whatsapp/init.go
  16. 0
      src/infrastructure/whatsapp/utils.go
  17. 0
      src/infrastructure/whatsapp/webhook.go
  18. 338
      src/ui/mcp/send.go
  19. 5
      src/ui/rest/app.go
  20. 6
      src/ui/rest/group.go
  21. 7
      src/ui/rest/helpers/common.go
  22. 0
      src/ui/rest/helpers/flushChatCsv.go
  23. 6
      src/ui/rest/message.go
  24. 0
      src/ui/rest/middleware/basicauth.go
  25. 0
      src/ui/rest/middleware/recovery.go
  26. 4
      src/ui/rest/newsletter.go
  27. 6
      src/ui/rest/send.go
  28. 6
      src/ui/rest/user.go
  29. 2
      src/ui/websocket/websocket.go
  30. 4
      src/usecase/app.go
  31. 24
      src/usecase/group.go
  32. 6
      src/usecase/message.go
  33. 13
      src/usecase/newsletter.go
  34. 10
      src/usecase/send.go
  35. 26
      src/usecase/user.go

122
.cursor/rules/project-structure.mdc

@ -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

79
readme.md

@ -13,16 +13,17 @@ ___
![release linux](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/release-linux.yml/badge.svg) ![release linux](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/release-linux.yml/badge.svg)
![release macos](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/release-mac.yml/badge.svg) ![release macos](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/release-mac.yml/badge.svg)
## Support `ARM` Architecture
## Support for `ARM` & `AMD` Architecture along with `MCP` Support
Now that we support ARM64 for Linux:
Download:
- [Release](https://github.com/aldinokemal/go-whatsapp-web-multidevice/releases/latest) for ARM64
- [Docker Image](https://hub.docker.com/r/aldinokemal2104/go-whatsapp-web-multidevice/tags) for ARM64.
- [Release](https://github.com/aldinokemal/go-whatsapp-web-multidevice/releases/latest)
- [Docker Image](https://hub.docker.com/r/aldinokemal2104/go-whatsapp-web-multidevice/tags)
## Feature ## Feature
- Send WhatsApp message via http API, [docs/openapi.yml](./docs/openapi.yaml) for more details - Send WhatsApp message via http API, [docs/openapi.yml](./docs/openapi.yaml) for more details
- **MCP (Model Context Protocol) Server Support** - Integrate with AI agents and tools using standardized protocol
- Mention someone - Mention someone
- `@phoneNumber` - `@phoneNumber`
- example: `Hello @628974812XXXX, @628974812XXXX` - example: `Hello @628974812XXXX, @628974812XXXX`
@ -115,10 +116,50 @@ Note: Command-line flags will override any values set in environment variables o
1. run `.\whatsapp.exe --help` for more detail flags 1. run `.\whatsapp.exe --help` for more detail flags
6. open `http://localhost:3000` in browser 6. open `http://localhost:3000` in browser
### MCP Server (Model Context Protocol)
This application can also run as an MCP server, allowing AI agents and tools to interact with WhatsApp through a standardized protocol.
1. Clone this repo `git clone https://github.com/aldinokemal/go-whatsapp-web-multidevice`
2. Open the folder that was cloned via cmd/terminal.
3. run `cd src`
4. run `go run main.go mcp` or build the binary and run `./whatsapp mcp`
5. The MCP server will start on `http://localhost:8080` by default
#### MCP Server Options
- `--host localhost` - Set the host for MCP server (default: localhost)
- `--port 8080` - Set the port for MCP server (default: 8080)
#### Available MCP Tools
- `whatsapp_send_text` - Send text messages
- `whatsapp_send_contact` - Send contact cards
- `whatsapp_send_link` - Send links with captions
- `whatsapp_send_location` - Send location coordinates
#### MCP Endpoints
- SSE endpoint: `http://localhost:8080/sse`
- Message endpoint: `http://localhost:8080/message`
### MCP Configuration
make sure you have running MCP server, `./whatsapp mcp`
```json
{
"mcpServers": {
"whatsapp": {
"url": "http://localhost:8080/sse"
}
}
```
### Production Mode (docker) ### Production Mode (docker)
```bash ```bash
docker run --detach --publish=3000:3000 --name=whatsapp --restart=always --volume=$(docker volume create --name=whatsapp):/app/storages aldinokemal2104/go-whatsapp-web-multidevice --autoreply="Dont't reply this message please"
docker run --detach --publish=3000:3000 --name=whatsapp --restart=always --volume=$(docker volume create --name=whatsapp):/app/storages aldinokemal2104/go-whatsapp-web-multidevice rest --autoreply="Dont't reply this message please"
``` ```
### Production Mode (docker compose) ### Production Mode (docker compose)
@ -136,6 +177,7 @@ services:
volumes: volumes:
- whatsapp:/app/storages - whatsapp:/app/storages
command: command:
- rest
- --basic-auth=admin:admin - --basic-auth=admin:admin
- --port=3000 - --port=3000
- --debug=true - --debug=true
@ -158,6 +200,8 @@ services:
- "3000:3000" - "3000:3000"
volumes: volumes:
- whatsapp:/app/storages - whatsapp:/app/storages
command:
- rest
environment: environment:
- APP_BASIC_AUTH=admin:admin - APP_BASIC_AUTH=admin:admin
- APP_PORT=3000 - APP_PORT=3000
@ -177,6 +221,15 @@ You can fork or edit this source code !
## Current API ## Current API
### MCP (Model Context Protocol) API
- MCP server provides standardized tools for AI agents to interact with WhatsApp
- Supports Server-Sent Events (SSE) transport
- Available tools: `whatsapp_send_text`, `whatsapp_send_contact`, `whatsapp_send_link`, `whatsapp_send_location`
- Compatible with MCP-enabled AI tools and agents
### HTTP REST API
- [API Specification Document](https://bump.sh/aldinokemal/doc/go-whatsapp-web-multidevice). - [API Specification Document](https://bump.sh/aldinokemal/doc/go-whatsapp-web-multidevice).
- Check [docs/openapi.yml](./docs/openapi.yaml) for detailed API specifications. - Check [docs/openapi.yml](./docs/openapi.yaml) for detailed API specifications.
- Use [SwaggerEditor](https://editor.swagger.io) to visualize the API. - Use [SwaggerEditor](https://editor.swagger.io) to visualize the API.
@ -230,7 +283,18 @@ You can fork or edit this source code !
❌ = Not Available Yet ❌ = Not Available Yet
``` ```
### User Interface
## User Interface
### MCP UI
- Setup MCP (tested in cursor)
![Setup MCP](https://i.ibb.co/vCg4zNWt/mcpsetup.png)
- Test MCP
![Test MCP](https://i.ibb.co/B2LX38DW/mcptest.png)
- Successfully setup MCP
![Success MCP](https://i.ibb.co/1fCx0Myc/mcpsuccess.png)
### HTTP REST API UI
| Description | Image | | Description | Image |
|----------------------|------------------------------------------------------------------------------------------| |----------------------|------------------------------------------------------------------------------------------|
@ -244,7 +308,7 @@ You can fork or edit this source code !
| Send Contact | ![Send Contact](https://i.ibb.co.com/NsFfQBv/send-Contact.png) | | Send Contact | ![Send Contact](https://i.ibb.co.com/NsFfQBv/send-Contact.png) |
| Send Location | ![Send Location](https://i.ibb.co.com/vDGmFvk/send-Location.png) | | Send Location | ![Send Location](https://i.ibb.co.com/vDGmFvk/send-Location.png) |
| Send Audio | ![Send Audio](https://i.ibb.co.com/XJdQLP8/send-Audio.png) | | Send Audio | ![Send Audio](https://i.ibb.co.com/XJdQLP8/send-Audio.png) |
| Send Poll | ![Send Poll](https://i.ibb.co.com/4TswfT3/sendPoll.png) |
| Send Poll | ![Send Poll](https://i.ibb.co.com/4TswfT3/sendPoll.png?v=1) |
| Send Presence | ![Send Presence](https://i.ibb.co.com/NSTC3QX/send-Presence.png) | | Send Presence | ![Send Presence](https://i.ibb.co.com/NSTC3QX/send-Presence.png) |
| Revoke Message | ![Revoke Message](https://i.ibb.co.com/r4nDc57/revoke-Message.png) | | Revoke Message | ![Revoke Message](https://i.ibb.co.com/r4nDc57/revoke-Message.png) |
| Delete Message | ![Delete Message](https://i.ibb.co.com/dtrTJ1M/delete-Message.png) | | Delete Message | ![Delete Message](https://i.ibb.co.com/dtrTJ1M/delete-Message.png) |
@ -270,3 +334,4 @@ You can fork or edit this source code !
- This project is unofficial and not affiliated with WhatsApp. - This project is unofficial and not affiliated with WhatsApp.
- Please use official WhatsApp API to avoid any issues. - Please use official WhatsApp API to avoid any issues.
- We only able to run MCP or REST API, this is limitation from whatsmeow library. independent MCP will be available in the future.

69
src/cmd/mcp.go

@ -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)
}
}

122
src/cmd/rest.go

@ -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())
}
}

168
src/cmd/root.go

@ -3,36 +3,44 @@ package cmd
import ( import (
"context" "context"
"embed" "embed"
"fmt"
"log"
"net/http"
"os" "os"
"strings" "strings"
"time"
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" "github.com/aldinokemal/go-whatsapp-web-multidevice/config"
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/rest"
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/rest/helpers"
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/rest/middleware"
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/websocket"
domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group"
domainMessage "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/message"
domainNewsletter "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/newsletter"
domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send"
domainUser "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/user"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/services"
"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/lib/pq"
_ "github.com/mattn/go-sqlite3"
"github.com/aldinokemal/go-whatsapp-web-multidevice/usecase"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.mau.fi/whatsmeow"
"go.mau.fi/whatsmeow/store/sqlstore"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
) )
var ( var (
EmbedIndex embed.FS EmbedIndex embed.FS
EmbedViews embed.FS EmbedViews embed.FS
// Whatsapp
whatsappCli *whatsmeow.Client
whatsappDB *sqlstore.Container
// Usecase
appUsecase domainApp.IAppUsecase
sendUsecase domainSend.ISendUsecase
userUsecase domainUser.IUserUsecase
messageUsecase domainMessage.IMessageUsecase
groupUsecase domainGroup.IGroupUsecase
newsletterUsecase domainNewsletter.INewsletterUsecase
) )
// rootCmd represents the base command when called without any subcommands // rootCmd represents the base command when called without any subcommands
@ -40,16 +48,21 @@ var rootCmd = &cobra.Command{
Short: "Send free whatsapp API", Short: "Send free whatsapp API",
Long: `This application is from clone https://github.com/aldinokemal/go-whatsapp-web-multidevice, Long: `This application is from clone https://github.com/aldinokemal/go-whatsapp-web-multidevice,
you can send whatsapp over http api but your whatsapp account have to be multi device version`, you can send whatsapp over http api but your whatsapp account have to be multi device version`,
Run: runRest,
} }
func init() { func init() {
// Load environment variables first // Load environment variables first
utils.LoadConfig(".") utils.LoadConfig(".")
// Initialize configurations, flag is higher priority than env
initEnvConfig()
time.Local = time.UTC
rootCmd.CompletionOptions.DisableDefaultCmd = true
// Initialize flags first, before any subcommands are added
initFlags() initFlags()
// Then initialize other components
cobra.OnInitialize(initEnvConfig, initApp)
} }
// initEnvConfig loads configuration from environment variables // initEnvConfig loads configuration from environment variables
@ -96,10 +109,7 @@ func initEnvConfig() {
} }
} }
// initFlags sets up command line flags that override environment variables
func initFlags() { func initFlags() {
rootCmd.CompletionOptions.DisableDefaultCmd = true
// Application flags // Application flags
rootCmd.PersistentFlags().StringVarP( rootCmd.PersistentFlags().StringVarP(
&config.AppPort, &config.AppPort,
@ -107,6 +117,7 @@ func initFlags() {
config.AppPort, config.AppPort,
"change port number with --port <number> | example: --port=8080", "change port number with --port <number> | example: --port=8080",
) )
rootCmd.PersistentFlags().BoolVarP( rootCmd.PersistentFlags().BoolVarP(
&config.AppDebug, &config.AppDebug,
"debug", "d", "debug", "d",
@ -173,109 +184,22 @@ func initFlags() {
) )
} }
func runRest(_ *cobra.Command, _ []string) {
func initApp() {
if config.AppDebug { if config.AppDebug {
config.WhatsappLogLevel = "DEBUG" config.WhatsappLogLevel = "DEBUG"
} }
// TODO: Init Rest App
//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,
}))
}
ctx := context.Background() ctx := context.Background()
db := whatsapp.InitWaDB(ctx)
cli := whatsapp.InitWaCLI(ctx, db)
// Service
appService := services.NewAppService(cli, db)
sendService := services.NewSendService(cli, appService)
userService := services.NewUserService(cli)
messageService := services.NewMessageService(cli)
groupService := services.NewGroupService(cli)
newsletterService := services.NewNewsletterService(cli)
// Rest
rest.InitRestApp(app, appService)
rest.InitRestSend(app, sendService)
rest.InitRestUser(app, userService)
rest.InitRestMessage(app, messageService)
rest.InitRestGroup(app, groupService)
rest.InitRestNewsletter(app, newsletterService)
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, appService)
go websocket.RunHub()
// Set auto reconnect to whatsapp server after booting
go helpers.SetAutoConnectAfterBooting(appService)
// Set auto reconnect checking
go helpers.SetAutoReconnectChecking(cli)
// 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())
}
whatsappDB = whatsapp.InitWaDB(ctx)
whatsappCli = whatsapp.InitWaCLI(ctx, whatsappDB)
// Usecase
appUsecase = usecase.NewAppService(whatsappCli, whatsappDB)
sendUsecase = usecase.NewSendService(whatsappCli, appUsecase)
userUsecase = usecase.NewUserService(whatsappCli)
messageUsecase = usecase.NewMessageService(whatsappCli)
groupUsecase = usecase.NewGroupService(whatsappCli)
newsletterUsecase = usecase.NewNewsletterService(whatsappCli)
} }
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.

5
src/config/settings.go

@ -5,7 +5,7 @@ import (
) )
var ( var (
AppVersion = "v5.6.2"
AppVersion = "v6.0.0"
AppPort = "3000" AppPort = "3000"
AppDebug = false AppDebug = false
AppOs = "AldinoKemal" AppOs = "AldinoKemal"
@ -13,6 +13,9 @@ var (
AppBasicAuthCredential []string AppBasicAuthCredential []string
AppChatFlushIntervalDays = 7 // Number of days before flushing chat.csv AppChatFlushIntervalDays = 7 // Number of days before flushing chat.csv
McpPort = "8080"
McpHost = "localhost"
PathQrCode = "statics/qrcode" PathQrCode = "statics/qrcode"
PathSendItems = "statics/senditems" PathSendItems = "statics/senditems"
PathMedia = "statics/media" PathMedia = "statics/media"

2
src/domains/app/app.go

@ -5,7 +5,7 @@ import (
"time" "time"
) )
type IAppService interface {
type IAppUsecase interface {
Login(ctx context.Context) (response LoginResponse, err error) Login(ctx context.Context) (response LoginResponse, err error)
LoginWithCode(ctx context.Context, phoneNumber string) (loginCode string, err error) LoginWithCode(ctx context.Context, phoneNumber string) (loginCode string, err error)
Logout(ctx context.Context) (err error) Logout(ctx context.Context) (err error)

2
src/domains/group/group.go

@ -7,7 +7,7 @@ import (
"go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow"
) )
type IGroupService interface {
type IGroupUsecase interface {
JoinGroupWithLink(ctx context.Context, request JoinGroupWithLinkRequest) (groupID string, err error) JoinGroupWithLink(ctx context.Context, request JoinGroupWithLinkRequest) (groupID string, err error)
LeaveGroup(ctx context.Context, request LeaveGroupRequest) (err error) LeaveGroup(ctx context.Context, request LeaveGroupRequest) (err error)
CreateGroup(ctx context.Context, request CreateGroupRequest) (groupID string, err error) CreateGroup(ctx context.Context, request CreateGroupRequest) (groupID string, err error)

2
src/domains/message/message.go

@ -2,7 +2,7 @@ package message
import "context" import "context"
type IMessageService interface {
type IMessageUsecase interface {
MarkAsRead(ctx context.Context, request MarkAsReadRequest) (response GenericResponse, err error) MarkAsRead(ctx context.Context, request MarkAsReadRequest) (response GenericResponse, err error)
ReactMessage(ctx context.Context, request ReactionRequest) (response GenericResponse, err error) ReactMessage(ctx context.Context, request ReactionRequest) (response GenericResponse, err error)
RevokeMessage(ctx context.Context, request RevokeRequest) (response GenericResponse, err error) RevokeMessage(ctx context.Context, request RevokeRequest) (response GenericResponse, err error)

2
src/domains/newsletter/newsletter.go

@ -2,7 +2,7 @@ package newsletter
import "context" import "context"
type INewsletterService interface {
type INewsletterUsecase interface {
Unfollow(ctx context.Context, request UnfollowRequest) (err error) Unfollow(ctx context.Context, request UnfollowRequest) (err error)
} }

2
src/domains/send/send.go

@ -4,7 +4,7 @@ import (
"context" "context"
) )
type ISendService interface {
type ISendUsecase interface {
SendText(ctx context.Context, request MessageRequest) (response GenericResponse, err error) SendText(ctx context.Context, request MessageRequest) (response GenericResponse, err error)
SendImage(ctx context.Context, request ImageRequest) (response GenericResponse, err error) SendImage(ctx context.Context, request ImageRequest) (response GenericResponse, err error)
SendFile(ctx context.Context, request FileRequest) (response GenericResponse, err error) SendFile(ctx context.Context, request FileRequest) (response GenericResponse, err error)

2
src/domains/user/user.go

@ -4,7 +4,7 @@ import (
"context" "context"
) )
type IUserService interface {
type IUserUsecase interface {
Info(ctx context.Context, request InfoRequest) (response InfoResponse, err error) Info(ctx context.Context, request InfoRequest) (response InfoResponse, err error)
Avatar(ctx context.Context, request AvatarRequest) (response AvatarResponse, err error) Avatar(ctx context.Context, request AvatarRequest) (response AvatarResponse, err error)
ChangeAvatar(ctx context.Context, request ChangeAvatarRequest) (err error) ChangeAvatar(ctx context.Context, request ChangeAvatarRequest) (err error)

13
src/go.mod

@ -12,6 +12,7 @@ require (
github.com/gofiber/websocket/v2 v2.2.1 github.com/gofiber/websocket/v2 v2.2.1
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
github.com/mark3labs/mcp-go v0.29.0
github.com/mattn/go-sqlite3 v1.14.28 github.com/mattn/go-sqlite3 v1.14.28
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
@ -30,6 +31,8 @@ require (
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fasthttp/websocket v1.5.12 // indirect github.com/fasthttp/websocket v1.5.12 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
@ -38,12 +41,16 @@ require (
github.com/gofiber/utils v1.1.0 // indirect github.com/gofiber/utils v1.1.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/jsonschema v0.12.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/compress v1.18.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/metoro-io/mcp-golang v0.13.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb // indirect github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/zerolog v1.34.0 // indirect github.com/rs/zerolog v1.34.0 // indirect
@ -54,7 +61,13 @@ require (
github.com/spf13/cast v1.8.0 // indirect github.com/spf13/cast v1.8.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.mau.fi/util v0.8.7 // indirect go.mau.fi/util v0.8.7 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.38.0 // indirect golang.org/x/crypto v0.38.0 // indirect

94
src/go.sum

@ -1,7 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8=
github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
@ -11,6 +11,10 @@ github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmg
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -25,8 +29,6 @@ github.com/fasthttp/websocket v1.5.12 h1:e4RGPpWW2HTbL3zV0Y/t7g0ub294LkiuXXUuTOU
github.com/fasthttp/websocket v1.5.12/go.mod h1:I+liyL7/4moHojiOgUOIKEWm9EIxHqxZChS+aMFltyg= github.com/fasthttp/websocket v1.5.12/go.mod h1:I+liyL7/4moHojiOgUOIKEWm9EIxHqxZChS+aMFltyg=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
@ -34,8 +36,6 @@ github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRi
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/fiber/v2 v2.52.8 h1:xl4jJQ0BV5EJTA2aWiKw/VddRpHrKeZLF0QPUxqn0x4= github.com/gofiber/fiber/v2 v2.52.8 h1:xl4jJQ0BV5EJTA2aWiKw/VddRpHrKeZLF0QPUxqn0x4=
github.com/gofiber/fiber/v2 v2.52.8/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/gofiber/fiber/v2 v2.52.8/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
@ -54,6 +54,9 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -62,6 +65,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mark3labs/mcp-go v0.29.0 h1:sH1NBcumKskhxqYzhXfGc201D7P76TVXiT0fGVhabeI=
github.com/mark3labs/mcp-go v0.29.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
@ -71,20 +78,15 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/metoro-io/mcp-golang v0.13.0 h1:54TFBJIW76VRB55CJovQQje9x4GnXg0BQQwGRtXrbCE=
github.com/metoro-io/mcp-golang v0.13.0/go.mod h1:ifLP9ZzKpN1UqFWNTpAHOqSvNkMK6b7d1FSZ5Lu0lN0=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a h1:S+AGcmAESQ0pXCUNnRH7V+bOUIgkSX5qVt2cNKCrm0Q=
github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb h1:3PrKuO92dUTMrQ9dx0YNejC6U/Si6jqKmyQ9vWjwqR4= github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb h1:3PrKuO92dUTMrQ9dx0YNejC6U/Si6jqKmyQ9vWjwqR4=
github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
@ -98,12 +100,8 @@ github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.8.0 h1:mXaMVw7IqxNBxfv3LdWt9MDmcWDQ1fagDH918lOdVaQ=
github.com/sagikazarmark/locafero v0.8.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287 h1:qIQ0tWF9vxGtkJa24bR+2i53WBCz1nW/Pc47oVYauC4= github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287 h1:qIQ0tWF9vxGtkJa24bR+2i53WBCz1nW/Pc47oVYauC4=
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg= github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
@ -114,16 +112,12 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -133,33 +127,31 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw=
github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc=
github.com/valyala/fasthttp v1.62.0 h1:8dKRBX/y2rCzyc6903Zu1+3qN0H/d2MsxPPmVNamiH0= github.com/valyala/fasthttp v1.62.0 h1:8dKRBX/y2rCzyc6903Zu1+3qN0H/d2MsxPPmVNamiH0=
github.com/valyala/fasthttp v1.62.0/go.mod h1:FCINgr4GKdKqV8Q0xv8b+UxPV+H/O5nNFo3D+r54Htg= github.com/valyala/fasthttp v1.62.0/go.mod h1:FCINgr4GKdKqV8Q0xv8b+UxPV+H/O5nNFo3D+r54Htg=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mau.fi/libsignal v0.1.2 h1:Vs16DXWxSKyzVtI+EEXLCSy5pVWzzCzp/2eqFGvLyP0=
go.mau.fi/libsignal v0.1.2/go.mod h1:JpnLSSJptn/s1sv7I56uEMywvz8x4YzxeF5OzdPb6PE=
go.mau.fi/libsignal v0.2.0 h1:oRXj3OHhEJq51BFEM8/50UZblmWiTYH93hsNTPcbk90= go.mau.fi/libsignal v0.2.0 h1:oRXj3OHhEJq51BFEM8/50UZblmWiTYH93hsNTPcbk90=
go.mau.fi/libsignal v0.2.0/go.mod h1:tvjoDsMejgT38CXTXwqaYu8itBiY8O2Mb6biWvZBb9k= go.mau.fi/libsignal v0.2.0/go.mod h1:tvjoDsMejgT38CXTXwqaYu8itBiY8O2Mb6biWvZBb9k=
go.mau.fi/util v0.8.6 h1:AEK13rfgtiZJL2YsNK+W4ihhYCuukcRom8WPP/w/L54=
go.mau.fi/util v0.8.6/go.mod h1:uNB3UTXFbkpp7xL1M/WvQks90B/L4gvbLpbS0603KOE=
go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c= go.mau.fi/util v0.8.7 h1:ywKarPxouJQEEijTs4mPlxC7F4AWEKokEpWc+2TYy6c=
go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY= go.mau.fi/util v0.8.7/go.mod h1:j6R3cENakc1f8HpQeFl0N15UiSTcNmIfDBNJUbL71RY=
go.mau.fi/whatsmeow v0.0.0-20250318233852-06705625cf82 h1:AZlDkXHgoQNW4gd2hnTCvPH7hYznmwc3gPaYqGZ5w8A=
go.mau.fi/whatsmeow v0.0.0-20250318233852-06705625cf82/go.mod h1:WNhj4JeQ6YR6dUOEiCXKqmE4LavSFkwRoKmu4atRrRs=
go.mau.fi/whatsmeow v0.0.0-20250402091807-b0caa1b76088 h1:ns6nk2NjqdaQnCKrp+Qqwpf+3OI7+nnH56D71+7XzOM=
go.mau.fi/whatsmeow v0.0.0-20250402091807-b0caa1b76088/go.mod h1:WNhj4JeQ6YR6dUOEiCXKqmE4LavSFkwRoKmu4atRrRs=
go.mau.fi/whatsmeow v0.0.0-20250417131650-164ddf482526 h1:i9w16FdM3zmOWdF5nh1l2MlmE/wK7ulL6rbT02WBBJs=
go.mau.fi/whatsmeow v0.0.0-20250417131650-164ddf482526/go.mod h1:NlPtoLdpX3RnltqCTCZQ6kIUfprqLirtSK1gHvwoNx0=
go.mau.fi/whatsmeow v0.0.0-20250501130609-4c93ee4e6efa h1:+bQKfMtnhX2jVoCSaneH4Ctk51IVT1K2gvjyqfFjVW0=
go.mau.fi/whatsmeow v0.0.0-20250501130609-4c93ee4e6efa/go.mod h1:NlPtoLdpX3RnltqCTCZQ6kIUfprqLirtSK1gHvwoNx0=
go.mau.fi/whatsmeow v0.0.0-20250521125706-91ac75c2f61a h1:PYtzz5wdma64I47CiquGicyubzg3HIPkH/jMzpmHu8g= go.mau.fi/whatsmeow v0.0.0-20250521125706-91ac75c2f61a h1:PYtzz5wdma64I47CiquGicyubzg3HIPkH/jMzpmHu8g=
go.mau.fi/whatsmeow v0.0.0-20250521125706-91ac75c2f61a/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4= go.mau.fi/whatsmeow v0.0.0-20250521125706-91ac75c2f61a/go.mod h1:Qy3L3BNBcnxfrAQ09lmFMa0ItZfg8zl9DzxKrptzfU4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -170,21 +162,11 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@ -201,12 +183,6 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -230,10 +206,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
@ -254,10 +226,6 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -267,8 +235,6 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

2
src/pkg/whatsapp/init.go → src/infrastructure/whatsapp/init.go

@ -10,9 +10,9 @@ import (
"time" "time"
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" "github.com/aldinokemal/go-whatsapp-web-multidevice/config"
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/websocket"
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/websocket"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow"
"go.mau.fi/whatsmeow/appstate" "go.mau.fi/whatsmeow/appstate"

0
src/pkg/whatsapp/utils.go → src/infrastructure/whatsapp/utils.go

0
src/pkg/whatsapp/webhook.go → src/infrastructure/whatsapp/webhook.go

338
src/ui/mcp/send.go

@ -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
}

5
src/internal/rest/app.go → src/ui/rest/app.go

@ -2,16 +2,17 @@ package rest
import ( import (
"fmt" "fmt"
domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app" domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type App struct { type App struct {
Service domainApp.IAppService
Service domainApp.IAppUsecase
} }
func InitRestApp(app *fiber.App, service domainApp.IAppService) App {
func InitRestApp(app *fiber.App, service domainApp.IAppUsecase) App {
rest := App{Service: service} rest := App{Service: service}
app.Get("/app/login", rest.Login) app.Get("/app/login", rest.Login)
app.Get("/app/login-with-code", rest.LoginWithCode) app.Get("/app/login-with-code", rest.LoginWithCode)

6
src/internal/rest/group.go → src/ui/rest/group.go

@ -4,17 +4,17 @@ import (
"fmt" "fmt"
domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group" domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow"
) )
type Group struct { type Group struct {
Service domainGroup.IGroupService
Service domainGroup.IGroupUsecase
} }
func InitRestGroup(app *fiber.App, service domainGroup.IGroupService) Group {
func InitRestGroup(app *fiber.App, service domainGroup.IGroupUsecase) Group {
rest := Group{Service: service} rest := Group{Service: service}
app.Post("/group", rest.CreateGroup) app.Post("/group", rest.CreateGroup)
app.Post("/group/join-with-link", rest.JoinGroupWithLink) app.Post("/group/join-with-link", rest.JoinGroupWithLink)

7
src/internal/rest/helpers/common.go → src/ui/rest/helpers/common.go

@ -2,13 +2,14 @@ package helpers
import ( import (
"context" "context"
domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
"go.mau.fi/whatsmeow"
"mime/multipart" "mime/multipart"
"time" "time"
domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
"go.mau.fi/whatsmeow"
) )
func SetAutoConnectAfterBooting(service domainApp.IAppService) {
func SetAutoConnectAfterBooting(service domainApp.IAppUsecase) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
_ = service.Reconnect(context.Background()) _ = service.Reconnect(context.Background())
} }

0
src/internal/rest/helpers/flushChatCsv.go → src/ui/rest/helpers/flushChatCsv.go

6
src/internal/rest/message.go → src/ui/rest/message.go

@ -2,16 +2,16 @@ package rest
import ( import (
domainMessage "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/message" domainMessage "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/message"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type Message struct { type Message struct {
Service domainMessage.IMessageService
Service domainMessage.IMessageUsecase
} }
func InitRestMessage(app *fiber.App, service domainMessage.IMessageService) Message {
func InitRestMessage(app *fiber.App, service domainMessage.IMessageUsecase) Message {
rest := Message{Service: service} rest := Message{Service: service}
app.Post("/message/:message_id/reaction", rest.ReactMessage) app.Post("/message/:message_id/reaction", rest.ReactMessage)
app.Post("/message/:message_id/revoke", rest.RevokeMessage) app.Post("/message/:message_id/revoke", rest.RevokeMessage)

0
src/internal/rest/middleware/basicauth.go → src/ui/rest/middleware/basicauth.go

0
src/internal/rest/middleware/recovery.go → src/ui/rest/middleware/recovery.go

4
src/internal/rest/newsletter.go → src/ui/rest/newsletter.go

@ -7,10 +7,10 @@ import (
) )
type Newsletter struct { type Newsletter struct {
Service domainNewsletter.INewsletterService
Service domainNewsletter.INewsletterUsecase
} }
func InitRestNewsletter(app *fiber.App, service domainNewsletter.INewsletterService) Newsletter {
func InitRestNewsletter(app *fiber.App, service domainNewsletter.INewsletterUsecase) Newsletter {
rest := Newsletter{Service: service} rest := Newsletter{Service: service}
app.Post("/newsletter/unfollow", rest.Unfollow) app.Post("/newsletter/unfollow", rest.Unfollow)
return rest return rest

6
src/internal/rest/send.go → src/ui/rest/send.go

@ -2,16 +2,16 @@ package rest
import ( import (
domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send" domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type Send struct { type Send struct {
Service domainSend.ISendService
Service domainSend.ISendUsecase
} }
func InitRestSend(app *fiber.App, service domainSend.ISendService) Send {
func InitRestSend(app *fiber.App, service domainSend.ISendUsecase) Send {
rest := Send{Service: service} rest := Send{Service: service}
app.Post("/send/message", rest.SendText) app.Post("/send/message", rest.SendText)
app.Post("/send/image", rest.SendImage) app.Post("/send/image", rest.SendImage)

6
src/internal/rest/user.go → src/ui/rest/user.go

@ -2,16 +2,16 @@ package rest
import ( import (
domainUser "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/user" domainUser "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/user"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type User struct { type User struct {
Service domainUser.IUserService
Service domainUser.IUserUsecase
} }
func InitRestUser(app *fiber.App, service domainUser.IUserService) User {
func InitRestUser(app *fiber.App, service domainUser.IUserUsecase) User {
rest := User{Service: service} rest := User{Service: service}
app.Get("/user/info", rest.UserInfo) app.Get("/user/info", rest.UserInfo)
app.Get("/user/avatar", rest.UserAvatar) app.Get("/user/avatar", rest.UserAvatar)

2
src/internal/websocket/websocket.go → src/ui/websocket/websocket.go

@ -76,7 +76,7 @@ func RunHub() {
} }
} }
func RegisterRoutes(app *fiber.App, service domainApp.IAppService) {
func RegisterRoutes(app *fiber.App, service domainApp.IAppUsecase) {
app.Use("/ws", func(c *fiber.Ctx) error { app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) { if websocket.IsWebSocketUpgrade(c) {
return c.Next() return c.Next()

4
src/services/app.go → src/usecase/app.go

@ -1,4 +1,4 @@
package services
package usecase
import ( import (
"context" "context"
@ -26,7 +26,7 @@ type serviceApp struct {
db *sqlstore.Container db *sqlstore.Container
} }
func NewAppService(waCli *whatsmeow.Client, db *sqlstore.Container) domainApp.IAppService {
func NewAppService(waCli *whatsmeow.Client, db *sqlstore.Container) domainApp.IAppUsecase {
return &serviceApp{ return &serviceApp{
WaCli: waCli, WaCli: waCli,
db: db, db: db,

24
src/services/group.go → src/usecase/group.go

@ -1,4 +1,4 @@
package services
package usecase
import ( import (
"context" "context"
@ -6,24 +6,24 @@ import (
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" "github.com/aldinokemal/go-whatsapp-web-multidevice/config"
domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group" domainGroup "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/group"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/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"
"go.mau.fi/whatsmeow/types" "go.mau.fi/whatsmeow/types"
) )
type groupService struct {
type serviceGroup struct {
WaCli *whatsmeow.Client WaCli *whatsmeow.Client
} }
func NewGroupService(waCli *whatsmeow.Client) domainGroup.IGroupService {
return &groupService{
func NewGroupService(waCli *whatsmeow.Client) domainGroup.IGroupUsecase {
return &serviceGroup{
WaCli: waCli, WaCli: waCli,
} }
} }
func (service groupService) JoinGroupWithLink(ctx context.Context, request domainGroup.JoinGroupWithLinkRequest) (groupID string, err error) {
func (service serviceGroup) JoinGroupWithLink(ctx context.Context, request domainGroup.JoinGroupWithLinkRequest) (groupID string, err error) {
if err = validations.ValidateJoinGroupWithLink(ctx, request); err != nil { if err = validations.ValidateJoinGroupWithLink(ctx, request); err != nil {
return groupID, err return groupID, err
} }
@ -36,7 +36,7 @@ func (service groupService) JoinGroupWithLink(ctx context.Context, request domai
return jid.String(), nil return jid.String(), nil
} }
func (service groupService) LeaveGroup(ctx context.Context, request domainGroup.LeaveGroupRequest) (err error) {
func (service serviceGroup) LeaveGroup(ctx context.Context, request domainGroup.LeaveGroupRequest) (err error) {
if err = validations.ValidateLeaveGroup(ctx, request); err != nil { if err = validations.ValidateLeaveGroup(ctx, request); err != nil {
return err return err
} }
@ -49,7 +49,7 @@ func (service groupService) LeaveGroup(ctx context.Context, request domainGroup.
return service.WaCli.LeaveGroup(JID) return service.WaCli.LeaveGroup(JID)
} }
func (service groupService) CreateGroup(ctx context.Context, request domainGroup.CreateGroupRequest) (groupID string, err error) {
func (service serviceGroup) CreateGroup(ctx context.Context, request domainGroup.CreateGroupRequest) (groupID string, err error) {
if err = validations.ValidateCreateGroup(ctx, request); err != nil { if err = validations.ValidateCreateGroup(ctx, request); err != nil {
return groupID, err return groupID, err
} }
@ -75,7 +75,7 @@ func (service groupService) CreateGroup(ctx context.Context, request domainGroup
return groupInfo.JID.String(), nil return groupInfo.JID.String(), nil
} }
func (service groupService) ManageParticipant(ctx context.Context, request domainGroup.ParticipantRequest) (result []domainGroup.ParticipantStatus, err error) {
func (service serviceGroup) ManageParticipant(ctx context.Context, request domainGroup.ParticipantRequest) (result []domainGroup.ParticipantStatus, err error) {
if err = validations.ValidateParticipant(ctx, request); err != nil { if err = validations.ValidateParticipant(ctx, request); err != nil {
return result, err return result, err
} }
@ -115,7 +115,7 @@ func (service groupService) ManageParticipant(ctx context.Context, request domai
return result, nil return result, nil
} }
func (service groupService) GetGroupRequestParticipants(ctx context.Context, request domainGroup.GetGroupRequestParticipantsRequest) (result []domainGroup.GetGroupRequestParticipantsResponse, err error) {
func (service serviceGroup) GetGroupRequestParticipants(ctx context.Context, request domainGroup.GetGroupRequestParticipantsRequest) (result []domainGroup.GetGroupRequestParticipantsResponse, err error) {
if err = validations.ValidateGetGroupRequestParticipants(ctx, request); err != nil { if err = validations.ValidateGetGroupRequestParticipants(ctx, request); err != nil {
return result, err return result, err
} }
@ -140,7 +140,7 @@ func (service groupService) GetGroupRequestParticipants(ctx context.Context, req
return result, nil return result, nil
} }
func (service groupService) ManageGroupRequestParticipants(ctx context.Context, request domainGroup.GroupRequestParticipantsRequest) (result []domainGroup.ParticipantStatus, err error) {
func (service serviceGroup) ManageGroupRequestParticipants(ctx context.Context, request domainGroup.GroupRequestParticipantsRequest) (result []domainGroup.ParticipantStatus, err error) {
if err = validations.ValidateManageGroupRequestParticipants(ctx, request); err != nil { if err = validations.ValidateManageGroupRequestParticipants(ctx, request); err != nil {
return result, err return result, err
} }
@ -179,7 +179,7 @@ func (service groupService) ManageGroupRequestParticipants(ctx context.Context,
return result, nil return result, nil
} }
func (service groupService) participantToJID(participants []string) ([]types.JID, error) {
func (service serviceGroup) participantToJID(participants []string) ([]types.JID, error) {
var participantsJID []types.JID var participantsJID []types.JID
for _, participant := range participants { for _, participant := range participants {
formattedParticipant := participant + config.WhatsappTypeUser formattedParticipant := participant + config.WhatsappTypeUser

6
src/services/message.go → src/usecase/message.go

@ -1,4 +1,4 @@
package services
package usecase
import ( import (
"context" "context"
@ -6,7 +6,7 @@ import (
"time" "time"
domainMessage "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/message" domainMessage "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/message"
"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"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow"
@ -22,7 +22,7 @@ type serviceMessage struct {
WaCli *whatsmeow.Client WaCli *whatsmeow.Client
} }
func NewMessageService(waCli *whatsmeow.Client) domainMessage.IMessageService {
func NewMessageService(waCli *whatsmeow.Client) domainMessage.IMessageUsecase {
return &serviceMessage{ return &serviceMessage{
WaCli: waCli, WaCli: waCli,
} }

13
src/services/newsletter.go → src/usecase/newsletter.go

@ -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
} }

10
src/services/send.go → src/usecase/send.go

@ -1,4 +1,4 @@
package services
package usecase
import ( import (
"context" "context"
@ -10,10 +10,10 @@ import (
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" "github.com/aldinokemal/go-whatsapp-web-multidevice/config"
"github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app" "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send" domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send"
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/rest/helpers"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils" "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/ui/rest/helpers"
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations" "github.com/aldinokemal/go-whatsapp-web-multidevice/validations"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
fiberUtils "github.com/gofiber/fiber/v2/utils" fiberUtils "github.com/gofiber/fiber/v2/utils"
@ -27,10 +27,10 @@ import (
type serviceSend struct { type serviceSend struct {
WaCli *whatsmeow.Client WaCli *whatsmeow.Client
appService app.IAppService
appService app.IAppUsecase
} }
func NewSendService(waCli *whatsmeow.Client, appService app.IAppService) domainSend.ISendService {
func NewSendService(waCli *whatsmeow.Client, appService app.IAppUsecase) domainSend.ISendUsecase {
return &serviceSend{ return &serviceSend{
WaCli: waCli, WaCli: waCli,
appService: appService, appService: appService,

26
src/services/user.go → src/usecase/user.go

@ -1,4 +1,4 @@
package services
package usecase
import ( import (
"bytes" "bytes"
@ -9,8 +9,8 @@ import (
"time" "time"
domainUser "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/user" domainUser "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/user"
"github.com/aldinokemal/go-whatsapp-web-multidevice/infrastructure/whatsapp"
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations" "github.com/aldinokemal/go-whatsapp-web-multidevice/validations"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"go.mau.fi/whatsmeow" "go.mau.fi/whatsmeow"
@ -18,17 +18,17 @@ import (
"go.mau.fi/whatsmeow/types" "go.mau.fi/whatsmeow/types"
) )
type userService struct {
type serviceUser struct {
WaCli *whatsmeow.Client WaCli *whatsmeow.Client
} }
func NewUserService(waCli *whatsmeow.Client) domainUser.IUserService {
return &userService{
func NewUserService(waCli *whatsmeow.Client) domainUser.IUserUsecase {
return &serviceUser{
WaCli: waCli, WaCli: waCli,
} }
} }
func (service userService) Info(ctx context.Context, request domainUser.InfoRequest) (response domainUser.InfoResponse, err error) {
func (service serviceUser) Info(ctx context.Context, request domainUser.InfoRequest) (response domainUser.InfoResponse, err error) {
err = validations.ValidateUserInfo(ctx, request) err = validations.ValidateUserInfo(ctx, request)
if err != nil { if err != nil {
return response, err return response, err
@ -71,7 +71,7 @@ func (service userService) Info(ctx context.Context, request domainUser.InfoRequ
return response, nil return response, nil
} }
func (service userService) Avatar(ctx context.Context, request domainUser.AvatarRequest) (response domainUser.AvatarResponse, err error) {
func (service serviceUser) Avatar(ctx context.Context, request domainUser.AvatarRequest) (response domainUser.AvatarResponse, err error) {
chanResp := make(chan domainUser.AvatarResponse) chanResp := make(chan domainUser.AvatarResponse)
chanErr := make(chan error) chanErr := make(chan error)
@ -118,7 +118,7 @@ func (service userService) Avatar(ctx context.Context, request domainUser.Avatar
} }
func (service userService) MyListGroups(_ context.Context) (response domainUser.MyListGroupsResponse, err error) {
func (service serviceUser) MyListGroups(_ context.Context) (response domainUser.MyListGroupsResponse, err error) {
whatsapp.MustLogin(service.WaCli) whatsapp.MustLogin(service.WaCli)
groups, err := service.WaCli.GetJoinedGroups() groups, err := service.WaCli.GetJoinedGroups()
@ -132,7 +132,7 @@ func (service userService) MyListGroups(_ context.Context) (response domainUser.
return response, nil return response, nil
} }
func (service userService) MyListNewsletter(_ context.Context) (response domainUser.MyListNewsletterResponse, err error) {
func (service serviceUser) MyListNewsletter(_ context.Context) (response domainUser.MyListNewsletterResponse, err error) {
whatsapp.MustLogin(service.WaCli) whatsapp.MustLogin(service.WaCli)
datas, err := service.WaCli.GetSubscribedNewsletters() datas, err := service.WaCli.GetSubscribedNewsletters()
@ -146,7 +146,7 @@ func (service userService) MyListNewsletter(_ context.Context) (response domainU
return response, nil return response, nil
} }
func (service userService) MyPrivacySetting(ctx context.Context) (response domainUser.MyPrivacySettingResponse, err error) {
func (service serviceUser) MyPrivacySetting(ctx context.Context) (response domainUser.MyPrivacySettingResponse, err error) {
whatsapp.MustLogin(service.WaCli) whatsapp.MustLogin(service.WaCli)
resp, err := service.WaCli.TryFetchPrivacySettings(ctx, true) resp, err := service.WaCli.TryFetchPrivacySettings(ctx, true)
@ -161,7 +161,7 @@ func (service userService) MyPrivacySetting(ctx context.Context) (response domai
return response, nil return response, nil
} }
func (service userService) MyListContacts(ctx context.Context) (response domainUser.MyListContactsResponse, err error) {
func (service serviceUser) MyListContacts(ctx context.Context) (response domainUser.MyListContactsResponse, err error) {
whatsapp.MustLogin(service.WaCli) whatsapp.MustLogin(service.WaCli)
contacts, err := service.WaCli.Store.Contacts.GetAllContacts(ctx) contacts, err := service.WaCli.Store.Contacts.GetAllContacts(ctx)
@ -179,7 +179,7 @@ func (service userService) MyListContacts(ctx context.Context) (response domainU
return response, nil return response, nil
} }
func (service userService) ChangeAvatar(ctx context.Context, request domainUser.ChangeAvatarRequest) (err error) {
func (service serviceUser) ChangeAvatar(ctx context.Context, request domainUser.ChangeAvatarRequest) (err error) {
whatsapp.MustLogin(service.WaCli) whatsapp.MustLogin(service.WaCli)
file, err := request.Avatar.Open() file, err := request.Avatar.Open()
@ -233,7 +233,7 @@ func (service userService) ChangeAvatar(ctx context.Context, request domainUser.
return nil return nil
} }
func (service userService) ChangePushName(ctx context.Context, request domainUser.ChangePushNameRequest) (err error) {
func (service serviceUser) ChangePushName(ctx context.Context, request domainUser.ChangePushNameRequest) (err error) {
whatsapp.MustLogin(service.WaCli) whatsapp.MustLogin(service.WaCli)
err = service.WaCli.SendAppState(ctx, appstate.BuildSettingPushName(request.PushName)) err = service.WaCli.SendAppState(ctx, appstate.BuildSettingPushName(request.PushName))
Loading…
Cancel
Save