Skip to content

piusalfred/whatsapp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

whatsapp

GoDoc Go Report Card Status

A highly configurable golang client for Whatsapp Cloud API

Important

This is the third-party library and not the official one. Not affiliated nor maintained by Meta.

Supported API

setup

  • Go to apps you have registered click on the one you want to develop API for and go to API setup if not create one.
  • Get these details access token,phone number id,business account id and make sure you have authorized one or more whatsapp phone numbers to receive these messages
  • While on the root directory of the project run task build-examples to build all the examples that will be in examples/bin directory
  • Then run cp examples/api.env examples/bin/api.env
  • And populate the values you have got to this api.env file, The given values here are not valid
WHATSAPP_CLOUD_API_BASE_URL=https://graph.facebook.com
WHATSAPP_CLOUD_API_API_VERSION=v20.0
WHATSAPP_CLOUD_API_ACCESS_TOKEN=EAALLrT0ok6UBJZB0ZB3gvzk9hJaEjGM8ISZAxPR5e3ZAFn4RmBIThoeK0XOdbKv8y2zB3YQ7uaijShZBjVIcZD
WHATSAPP_CLOUD_API_PHONE_NUMBER_ID=111112271552333
WHATSAPP_CLOUD_API_BUSINESS_ACCOUNT_ID=222222508300000
WHATSAPP_CLOUD_API_TEST_RECIPIENT=+XXXX7374665453
  • now you can navigate to the examples/bin directory and run the examples. But before that make sure you have sent the hello world template message to the recipient and replied and this should be within 24 hrs window

Note

You will find BaseClient and Client. Client provides a stateful approach, reusing the same configuration across multiple requests until manually refreshed, making it ideal for long-running services where consistency and thread safety are required. BaseClient is stateless, reloading the configuration on each request, making it more flexible for dynamic environments like multi-tenancy, where different configurations may be needed for each request.

usage

Install the library by running

go get github.com/piusalfred/whatsapp

Note

The webhooks and messaging clients are separated to allow for different configurations and use cases. The webhooks client is designed to handle incoming notifications, while the messaging client is focused on sending messages.

package main

import (
	"context"
	"fmt"
	whttp "github.com/piusalfred/whatsapp/pkg/http"
	"log"
	"net/http"

	"github.com/piusalfred/whatsapp/config"
	"github.com/piusalfred/whatsapp/message"
)

func main() {
	// Create the HTTP client
	httpClient := &http.Client{}

	// Define a configuration reader function
	configReader := config.ReaderFunc(func(ctx context.Context) (*config.Config, error) {
		return &config.Config{
			BaseURL:           "https://your-whatsapp-api-url",   // Replace with your API URL
			AccessToken:       "your-access-token",              // Replace with your access token
			APIVersion:        "v14.0",                          // WhatsApp API version
			BusinessAccountID: "your-business-account-id",       // Replace with your business account ID
		}, nil
	})

	clientOptions := []whttp.CoreClientOption[message.Message]{
		whttp.WithCoreClientHTTPClient[message.Message](httpClient),
		whttp.WithCoreClientRequestInterceptor[message.Message](
			func(ctx context.Context, req *http.Request) error {
				fmt.Println("Request Intercepted")
				return nil
			},
		),
		whttp.WithCoreClientResponseInterceptor[message.Message](
			func(ctx context.Context, resp *http.Response) error {
				fmt.Println("Response Intercepted")
				return nil
			},
		),
	}

	coreClient := whttp.NewSender[message.Message](clientOptions...)
	client, err := message.NewBaseClient(coreClient, configReader)
	if err != nil {
		log.Fatalf("failed to create client: %v", err)
	}

	// Define the recipient's WhatsApp phone number (including country code)
	recipient := "1234567890"

	// Create a new text message request
	textMessage := message.NewRequest(recipient, &message.Text{
		Body: "Hello, this is a test message!",
	}, "")

	// Send the text message
	ctx := context.Background()
	response, err := client.SendText(ctx, textMessage)
	if err != nil {
		log.Fatalf("failed to send message: %v", err)
	}

	// Print the response from the API
	fmt.Printf("Message sent successfully! Response: %+v\n", response)
}

webhooks

package main

import (
    "context"
    "fmt"
    "log"
    "log/slog"
    "net/http"
    
    "github.com/piusalfred/whatsapp/message"
    "github.com/piusalfred/whatsapp/webhooks"
)

// LoggingMiddleware is an example middleware that logs
// before and after handling a single incoming webhook request.
func LoggingMiddleware(next webhooks.NotificationHandlerFunc) webhooks.NotificationHandlerFunc {
  return func(ctx context.Context, notification *webhooks.Notification) *webhooks.Response {
	  fmt.Println("[LoggingMiddleware] --> Before handling notification")
	  response := next(ctx, notification)
	  fmt.Println("[LoggingMiddleware] <-- After handling notification")
	  return response
  }
}

// ReactionHandler is an example of a more advanced handler type
// that implements logic for dealing with Reaction messages.
type ReactionHandler struct {
    Logger *slog.Logger
    Store  map[string]any
}

// Ensure that ReactionHandler satisfies the interface for reaction messages.
// This is optional, but helpful if you want static type checking.
var _ webhooks.ReactionHandler = (*ReactionHandler)(nil)

// Handle processes reaction messages (type: reaction).
// It logs the incoming reaction and stores the emoji in a map.
func (r *ReactionHandler) Handle(
        ctx context.Context,
        nctx *webhooks.MessageNotificationContext,
        mctx *webhooks.MessageInfo,
        reaction *message.Reaction,
) error {
    r.Logger.Info("Received reaction message",
      "context", nctx,
      "message_info", mctx,
      "reaction", reaction,
    )
    
    r.Logger.Info("Reaction emoji", "emoji", reaction.Emoji)
    
    // For example, you can store the emoji in an in-memory map keyed by the message context ID.
    if r.Store == nil {
      r.Store = make(map[string]any)
    }
    if mctx.Context != nil {
      r.Store[mctx.Context.ID] = reaction.Emoji
    }
    
    return nil
}

func main() {
    // Create a Handler that can process various types of webhook notifications.
    // This initializes no-op handlers for all message types. The no-op handlers
    // does nothing and returns nil.
    handler := webhooks.NewHandler()
    
    // There are two ways to register handlers for specific message types:
    // 1. A simple callback function for a specific message type.
    // 2. Implementing a specific handler interface (e.g., webhooks.ReactionHandler) for more complex logic.
    
    // Register a simple callback for text messages using OnTextMessage. This will replace the
    // no-op handler that was initialized by called webhooks.NewHandler().
    // This callback will be invoked whenever we receive a "type": "text" message.
    handler.OnTextMessage(func(ctx context.Context,
            nctx *webhooks.MessageNotificationContext,
            mctx *webhooks.MessageInfo,
            text *webhooks.Text,
    ) error {
      fmt.Printf("[OnTextMessage] New text message received:\n")
      fmt.Printf("  Notification context: %+v\n", nctx)
      fmt.Printf("  Message info:         %+v\n", mctx)
      fmt.Printf("  Text content:         %+v\n", text)
      return nil
    })
    
    // In case of complex handlers you can implement a certain notification type handler interface
    // for example for reaction messages you can implement the webhooks.ReactionHandler interface
    // which is an alias for MessageHandler[message.Reaction]
    reactionHandler := &ReactionHandler{
      Logger: slog.Default(),
      Store:  make(map[string]any),
    }
    handler.SetReactionMessageHandler(reactionHandler)
    messageListener := webhooks.NewListener(
      handler.HandleNotification, // The core Handler’s method
      func(ctx context.Context) (string, error) { // VerifyTokenReader: returns your verify token
        return "TOKEN", nil
      },
      &webhooks.ValidateOptions{
        Validate:  false, // Skip signature validation for simplicity
        AppSecret: "SUPERSECRET",
      },
      LoggingMiddleware, // Example middleware
    )
    
    http.HandleFunc("POST /webhooks/messages", messageListener.HandleNotification)
    http.HandleFunc("POST /webhooks/verify", messageListener.HandleSubscriptionVerification)
    
    // Start an HTTP server on port :8080 to listen for incoming requests.
    fmt.Println("[main] Starting server on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

block users from sending messages to your business account

        reader, recipient := LoadConfigFromFile("api.env")
	coreClient1 := whttp.NewSender[user.BlockBaseRequest]()
	blocker := user.NewBlockClient(reader, coreClient1)
	resp, err := blocker.Block(ctx, []string{"1234567890"})
	if err != nil {
		return 
	}
	fmt.Println(resp)

See more in examples

Testing

There is provision of mocks that may come handy in testing.

Links