diff --git a/en/docs/connectors/catalog/communication/google-chat/actions.md b/en/docs/connectors/catalog/communication/google-chat/actions.md new file mode 100644 index 0000000000..8b701145cb --- /dev/null +++ b/en/docs/connectors/catalog/communication/google-chat/actions.md @@ -0,0 +1,738 @@ +--- +title: Actions +description: "Full reference for the Google Chat connector client operations: managing spaces, messages, members, and attachments." +keywords: [google chat, google workspace, chat api, ballerina connector, actions] +connector: true +connector_name: "googleapis.chat" +toc_max_heading_level: 4 +--- + +# Actions + +The `ballerinax/googleapis.chat` package exposes the following client: + +| Client | Purpose | +|--------|---------| +| [`Client`](#client) | Space, message, member, and attachment management on the Google Chat REST API. | + +## Client + +### Configuration + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `auth` | ServiceAccountAuthConfig|OAuth2Config|http:BearerTokenConfig | Required | Authentication configuration: service account, OAuth2, or bearer token. | +| `httpVersion` | http:HttpVersion | http:HTTP_2_0 | The HTTP version understood by the client. | +| `timeout` | decimal | 30 | The maximum time to wait (in seconds) for a response. | +| `forwarded` | string | "disable" | The choice of setting `forwarded`/`x-forwarded` header. | +| `retryConfig` | http:RetryConfig | () | Retry configuration for failed requests. | +| `cache` | http:CacheConfig | {} | HTTP caching-related configurations. | + +### Initializing the client + +```ballerina +import ballerinax/googleapis.chat; + +configurable chat:OAuth2Config oauthAuth = ?; + +final chat:Client chatClient = check new ({auth: oauthAuth}); +``` + +### Operations + +Most `Client` operations are resource functions, invoked with the HTTP method and resource path shown in each signature. + +#### Spaces + +
+get spaces + +`GET /spaces` + +Lists spaces the app has access to. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `pageSize` | `int?` | No | The maximum number of spaces to return. | +| `filter` | `string?` | No | A filter query, for example by space type. | + +Returns: `ListSpacesResponse|error` + +Sample code: + +```ballerina +chat:ListSpacesResponse spaces = check chatClient->/spaces(); +``` + +Sample response: + +```json +{ + "spaces": [{"name": "spaces/AAAA", "displayName": "Engineering", "spaceType": "SPACE"}] +} +``` + +
+ +
+get spaces/[spaceId] + +`GET /spaces/[spaceId]` + +Gets details of a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name, for example `spaces/AAAA`. | + +Returns: `Space|error` + +Sample code: + +```ballerina +chat:Space space = check chatClient->/spaces/[spaceId](); +``` + +
+ +
+post spaces + +`POST /spaces` + +Creates a named space (requires user authentication). + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `payload` | `Space` | Yes | The space to create. | + +Returns: `Space|error` + +Sample code: + +```ballerina +chat:Space created = check chatClient->/spaces.post({displayName: "Engineering", spaceType: "SPACE"}); +``` + +
+ +
+patch spaces/[spaceId] + +`PATCH /spaces/[spaceId]` + +Updates a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The ID of the space to update. | +| `payload` | `Space` | Yes | The updated space fields. | +| `updateMask` | `string?` | No | The field paths to update (comma-separated). | + +Returns: `Space|error` + +Sample code: + +```ballerina +chat:Space updated = check chatClient->/spaces/[spaceId].patch({displayName: "Engineering Team"}, + updateMask = "displayName"); +``` + +
+ +
+delete spaces/[spaceId] + +`DELETE /spaces/[spaceId]` + +Deletes a named space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The ID of the space to delete. | + +Returns: `error?` + +Sample code: + +```ballerina +check chatClient->/spaces/[spaceId].delete(); +``` + +
+ +
+get spaces/findDirectMessage + +`GET /spaces:findDirectMessage` + +Finds an existing direct message space with a specified user. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `name` | `string?` | No | Resource name of the user to find a DM with, for example `users/user@example.com`. | + +Returns: `Space|error` + +Sample code: + +```ballerina +chat:Space dm = check chatClient->/spaces/findDirectMessage(name = "users/user@example.com"); +``` + +
+ +
+get spaces/search + +`GET /spaces:search` + +Searches for spaces in a Google Workspace organization. Requires the caller to be a Workspace administrator with the manage chat and spaces conversations privilege, and the `chat.admin.spaces` or `chat.admin.spaces.readonly` OAuth scope. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `query` | `string` | Yes | A search query, for example `customer = "customers/my_customer" AND spaceType = "SPACE"`. `customer` and `spaceType` are required fields in the query. | +| `useAdminAccess` | `boolean` | Yes | Must be `true`. Runs the method using the caller's admin privileges. | +| `pageSize` | `int?` | No | Maximum number of spaces to return (default 100, max 1000). | +| `pageToken` | `string?` | No | Page token from a previous search request. | +| `orderBy` | `string?` | No | How to order results, for example `lastActiveTime DESC`. | + +Returns: `SearchSpacesResponse|error` + +Sample code: + +```ballerina +chat:SearchSpacesResponse results = check chatClient->/spaces/search( + query = "customer = \"customers/my_customer\" AND spaceType = \"SPACE\"", useAdminAccess = true); +``` + +
+ +
+post spaces/setup + +`POST /spaces:setup` + +Creates a space and adds specified users or Google Groups to it in one call. The calling user is added automatically and shouldn't be listed in `memberships`. Requires user authentication with the `chat.spaces` or `chat.spaces.create` OAuth scope. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `payload` | `SetUpSpaceRequest` | Yes | The space to create (`space.spaceType` is required: `SPACE`, `GROUP_CHAT`, or `DIRECT_MESSAGE`), plus optional `requestId` and up to 49 initial `memberships`. | + +Returns: `Space|error` + +Sample code: + +```ballerina +chat:Space created = check chatClient->/spaces/setup.post({ + space: {displayName: "Engineering", spaceType: "SPACE"}, + memberships: [{member: {name: "users/user@example.com", 'type: "HUMAN"}}] +}); +``` + +
+ +#### Messages + +
+post spaces/[spaceId]/messages + +`POST /spaces/[spaceId]/messages` + +Sends a message to a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `payload` | `Message` | Yes | The message content. | + +Returns: `Message|error` + +Sample code: + +```ballerina +chat:Message sent = check chatClient->/spaces/[spaceId]/messages.post({ + text: "Hello from Ballerina!" +}); +``` + +Sample response: + +```json +{ + "name": "spaces/AAAA/messages/BBBB", + "text": "Hello from Ballerina!" +} +``` + +
+ +
+get spaces/[spaceId]/messages + +`GET /spaces/[spaceId]/messages` + +Lists messages in a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `pageSize` | `int?` | No | Maximum number of messages to return (max 1000). | +| `pageToken` | `string?` | No | Page token from a previous list request. | +| `filter` | `string?` | No | A query filter. | +| `orderBy` | `string?` | No | Ordering of results, for example `createTime desc`. | +| `showDeleted` | `boolean?` | No | Whether to include deleted messages in the response. | + +Returns: `ListMessagesResponse|error` + +Sample code: + +```ballerina +chat:ListMessagesResponse messages = check chatClient->/spaces/[spaceId]/messages(); +``` + +
+ +
+get spaces/[spaceId]/messages/[messageId] + +`GET /spaces/[spaceId]/messages/[messageId]` + +Returns details about a message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `messageId` | `string` | Yes | The message resource name. | + +Returns: `Message|error` + +Sample code: + +```ballerina +chat:Message message = check chatClient->/spaces/[spaceId]/messages/[messageId](); +``` + +
+ +
+patch spaces/[spaceId]/messages/[messageId] + +`PATCH /spaces/[spaceId]/messages/[messageId]` + +Updates an existing message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `messageId` | `string` | Yes | The message resource name. | +| `payload` | `Message` | Yes | The updated message content. | + +Returns: `Message|error` + +Sample code: + +```ballerina +chat:Message updated = check chatClient->/spaces/[spaceId]/messages/[messageId].patch({ + text: "Updated text" +}); +``` + +
+ +
+delete spaces/[spaceId]/messages/[messageId] + +`DELETE /spaces/[spaceId]/messages/[messageId]` + +Deletes a message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `messageId` | `string` | Yes | The message resource name. | + +Returns: `error?` + +Sample code: + +```ballerina +check chatClient->/spaces/[spaceId]/messages/[messageId].delete(); +``` + +
+ +#### Members + +
+post spaces/[spaceId]/members + +`POST /spaces/[spaceId]/members` + +Adds a member to a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `payload` | `Membership` | Yes | The member to add. | + +Returns: `Membership|error` + +Sample code: + +```ballerina +chat:Membership membership = check chatClient->/spaces/[spaceId]/members.post({ + member: {name: "users/user@example.com", 'type: "HUMAN"} +}); +``` + +
+ +
+get spaces/[spaceId]/members + +`GET /spaces/[spaceId]/members` + +Lists memberships in a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `pageSize` | `int?` | No | Maximum number of memberships to return (max 1000). | +| `pageToken` | `string?` | No | Page token from a previous list request. | +| `filter` | `string?` | No | A query filter, for example `role = "ROLE_MANAGER"`. | +| `showGroups` | `boolean?` | No | Whether to include Google Group memberships. | +| `showInvited` | `boolean?` | No | Whether to include invited memberships. | +| `useAdminAccess` | `boolean?` | No | Whether to use the caller's Workspace admin privileges for the request. | + +Returns: `ListMembershipsResponse|error` + +Sample code: + +```ballerina +chat:ListMembershipsResponse members = check chatClient->/spaces/[spaceId]/members(); +``` + +
+ +
+get spaces/[spaceId]/members/[memberId] + +`GET /spaces/[spaceId]/members/[memberId]` + +Returns details about a membership. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `memberId` | `string` | Yes | The member resource name. | +| `useAdminAccess` | `boolean?` | No | Runs the method using the caller's Workspace admin privileges. Not supported for app memberships. | + +Returns: `Membership|error` + +Sample code: + +```ballerina +chat:Membership member = check chatClient->/spaces/[spaceId]/members/[memberId](); +``` + +
+ +
+patch spaces/[spaceId]/members/[memberId] + +`PATCH /spaces/[spaceId]/members/[memberId]` + +Updates a membership, for example to change a member's role in a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `memberId` | `string` | Yes | The member resource name. | +| `payload` | `Membership` | Yes | The membership with updated fields. | +| `updateMask` | `string` | Yes | The field paths to update. Currently only `role` is supported. | +| `useAdminAccess` | `boolean?` | No | Runs the method using the caller's Workspace admin privileges. | + +Returns: `Membership|error` + +Sample code: + +```ballerina +chat:Membership updated = check chatClient->/spaces/[spaceId]/members/[memberId].patch( + {role: "ROLE_MANAGER"}, updateMask = "role"); +``` + +
+ +
+delete spaces/[spaceId]/members/[memberId] + +`DELETE /spaces/[spaceId]/members/[memberId]` + +Removes a user or Chat app from a space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `memberId` | `string` | Yes | The member resource name. | + +Returns: `error?` + +Sample code: + +```ballerina +check chatClient->/spaces/[spaceId]/members/[memberId].delete(); +``` + +
+ +#### Reactions + +
+post spaces/[spaceId]/messages/[messageId]/reactions + +`POST /spaces/[spaceId]/messages/[messageId]/reactions` + +Creates a reaction on a message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `messageId` | `string` | Yes | The message resource name. | +| `payload` | `Reaction` | Yes | The reaction to create, for example `{emoji: {unicode: "👍"}}`. | + +Returns: `Reaction|error` + +Sample code: + +```ballerina +chat:Reaction reaction = check chatClient->/spaces/[spaceId]/messages/[messageId]/reactions.post({ + emoji: {unicode: "👍"} +}); +``` + +
+ +
+get spaces/[spaceId]/messages/[messageId]/reactions + +`GET /spaces/[spaceId]/messages/[messageId]/reactions` + +Lists reactions on a message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `messageId` | `string` | Yes | The message resource name. | +| `pageSize` | `int?` | No | Maximum number of reactions to return (max 25). | +| `pageToken` | `string?` | No | Page token from a previous list request. | +| `filter` | `string?` | No | A query filter, for example by emoji. | + +Returns: `ListReactionsResponse|error` + +Sample code: + +```ballerina +chat:ListReactionsResponse reactions = check chatClient->/spaces/[spaceId]/messages/[messageId]/reactions(); +``` + +
+ +
+delete spaces/[spaceId]/messages/[messageId]/reactions/[reactionId] + +`DELETE /spaces/[spaceId]/messages/[messageId]/reactions/[reactionId]` + +Deletes a reaction from a message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `messageId` | `string` | Yes | The message resource name. | +| `reactionId` | `string` | Yes | The reaction resource name. | + +Returns: `error?` + +Sample code: + +```ballerina +check chatClient->/spaces/[spaceId]/messages/[messageId]/reactions/[reactionId].delete(); +``` + +
+ +#### Attachments + +
+post spaces/[spaceId]/attachments/upload + +`POST /spaces/[spaceId]/attachments:upload` + +Uploads an attachment to a Google Chat space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The ID of the space that will own the uploaded attachment. | +| `payload` | `UploadAttachmentRequest` | Yes | `filename` and the raw `mediaBytes` to upload. | + +Returns: `UploadAttachmentResponse|error` + +Sample code: + +```ballerina +chat:UploadAttachmentResponse uploaded = check chatClient->/spaces/[spaceId]/attachments/upload.post({ + filename: "report.pdf", + mediaBytes: fileBytes +}); +``` + +
+ +
+get spaces/[spaceId]/messages/[messageId]/attachments/[attachmentId] + +`GET /spaces/[spaceId]/messages/[messageId]/attachments/[attachmentId]` + +Gets the metadata of a message attachment. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `messageId` | `string` | Yes | The message resource name. | +| `attachmentId` | `string` | Yes | The attachment resource name. | + +Returns: `Attachment|error` + +Sample code: + +```ballerina +chat:Attachment attachment = check chatClient->/spaces/[spaceId]/messages/[messageId]/attachments/[attachmentId](); +``` + +
+ +
+downloadMedia + +Downloads attachment media bytes given its resource name. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `resourceName` | `string` | Yes | The attachment media resource name from a message's `attachment.downloadUri` or `attachment.name`. | + +Returns: `byte[]|error` + +Sample code: + +```ballerina +byte[] mediaBytes = check chatClient->downloadMedia(resourceName); +``` + +
+ +#### Space events + +
+get spaces/[spaceId]/spaceEvents/[spaceEventId] + +`GET /spaces/[spaceId]/spaceEvents/[spaceEventId]` + +Returns an event from a Google Chat space, from the Workspace Events API. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `spaceEventId` | `string` | Yes | The space event resource name. | + +Returns: `SpaceEvent|error` + +Sample code: + +```ballerina +chat:SpaceEvent event = check chatClient->/spaces/[spaceId]/spaceEvents/[spaceEventId](); +``` + +
+ +
+get spaces/[spaceId]/spaceEvents + +`GET /spaces/[spaceId]/spaceEvents` + +Lists events from a Google Chat space. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `spaceId` | `string` | Yes | The space resource name. | +| `filter` | `string` | Yes | An event type filter, for example `eventTypes:"google.workspace.chat.message.v1.created"`. | +| `pageSize` | `int?` | No | Maximum number of events to return. | +| `pageToken` | `string?` | No | Page token from a previous list request. | + +Returns: `ListSpaceEventsResponse|error` + +Sample code: + +```ballerina +chat:ListSpaceEventsResponse events = check chatClient->/spaces/[spaceId]/spaceEvents( + filter = string `eventTypes:"google.workspace.chat.message.v1.created"`); +``` + +
+ +## What's next + +- [Trigger Reference](triggers.md): react to interaction events using the webhook listener. +- [Example](example.md): complete example integrations for the Google Chat connector and trigger. +- [Setup Guide](setup-guide.md): create a GCP project and configure the Chat app. diff --git a/en/docs/connectors/catalog/communication/google-chat/example.md b/en/docs/connectors/catalog/communication/google-chat/example.md new file mode 100644 index 0000000000..d6b32375a1 --- /dev/null +++ b/en/docs/connectors/catalog/communication/google-chat/example.md @@ -0,0 +1,79 @@ +--- +title: Examples +description: Step-by-step example for using the Google Chat connector to build an echo bot using a webhook trigger. +keywords: [google chat, google workspace, connector example, webhook, trigger, ballerina] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Example + +## Google Chat Trigger Example + +### What you'll build + +A minimal Google Chat app that replies to every message with the same text. Google Chat delivers interaction events over HTTP; the `chat:Listener` verifies the Google-signed bearer token on each request before dispatching to `onMessage`, which replies via the injected `chat:MessageCaller`. + +### Architecture + +```mermaid +flowchart LR + A((Chat User)) --> B[Google Chat] + B --> C[[chat:Listener]] + C --> D[Handler: onMessage] + D --> E[caller->respond] +``` + +### Prerequisites + +- A GCP project with the Google Chat API enabled and a Chat app configured. See the [Setup Guide](setup-guide.md). +- A public HTTPS URL for local development, for example using [ngrok](https://ngrok.com) + +### Setting up the Google Chat integration + +> **New to WSO2 Integrator?** Follow the [Create a New Integration](../../../../develop/create-integrations/create-a-new-integration.md) guide to set up your integration first, then return here to add the trigger. + +### Adding the Google Chat trigger + +Add a Google Chat event integration and implement the listener and service as described in the [Google Chat event integration guide](../../../../develop/integration-artifacts/event/google-chat.md). WSO2 Integrator renders the listener and service on the design canvas, with all available handlers listed under **Event Handlers**. + + + +Select **chat:ChatService** in the design canvas to open the Service Designer, which lists every pre-registered event handler bound to the `chatListener`. + + + +### Handling Google Chat events + +Select the **onMessage** row to open its flow canvas, then declare a variable for the message text and respond via the injected caller: + +```ballerina +remote function onMessage(chat:MessageEvent event, chat:MessageCaller caller) returns error? { + string text = event.message.text ?: ""; + log:printInfo("Google Chat message received", text = text); + check caller->respond({text: "Echo: " + text}); +} +``` + +### Running the integration + +Run the integration from WSO2 Integrator, then message your Chat app from Google Chat. Google Chat delivers the event to your listener, which invokes `onMessage`, logs the message, and replies with the echoed text. Verify both the log entry and the reply in the chat. + +## What's next + +- [Action Reference](actions.md): manage spaces, messages, and members using the `Client`. +- [Trigger Reference](triggers.md): full listener configuration and service callback reference. +- [Setup Guide](setup-guide.md): create a GCP project and configure the Chat app. diff --git a/en/docs/connectors/catalog/communication/google-chat/overview.md b/en/docs/connectors/catalog/communication/google-chat/overview.md new file mode 100644 index 0000000000..07de39d11d --- /dev/null +++ b/en/docs/connectors/catalog/communication/google-chat/overview.md @@ -0,0 +1,66 @@ +--- +title: "Google Chat Connector Overview" +description: "Overview of the Ballerina Google Chat connector: send and manage messages, and handle Google Chat interaction events." +keywords: [google chat, google workspace, webhook, ballerina connector] +connector: true +connector_name: "googleapis.chat" +--- + +# Overview + +[Google Chat](https://workspace.google.com/products/chat/) is a communication platform from Google, designed for teams and businesses as part of Google Workspace. The Ballerina `ballerinax/googleapis.chat` connector provides both a REST client for the Google Chat API and a webhook listener that receives Google Chat interaction events directly over HTTP. + +The listener runs as a plain HTTPS endpoint that Google Chat posts events to directly. There's no separate webhook relay. It supports three authentication mechanisms: service account (recommended for bots), OAuth 2.0 (for user-scoped actions), and short-lived bearer tokens (for quick tests). + +## Key features + +- Create spaces, send messages, manage memberships, and upload attachments via the REST client +- Receive Chat interaction events (messages, slash commands, card clicks, dialog submissions, app-home opens) over a webhook listener +- Reply synchronously within the event window using event-specific callers +- Google-signed bearer token verification, validated against your HTTP endpoint URL or GCP project number + +## Actions + +Actions are operations you invoke on the Google Chat API from your integration: listing spaces, sending messages, and downloading media. The connector exposes actions through a single client: + +| Client | Actions | +|--------|---------| +| `Client` | Space and message management, media download | + +See the **[Action Reference](actions.md)** for the full list of operations, parameters, and sample code. Most send/respond/update/delete operations used inside event handlers go through the per-event callers documented in the [Trigger Reference](triggers.md) instead. + +## Triggers + +Triggers allow your integration to react to Google Chat interaction events in real time. The connector's listener receives events directly from Google Chat and invokes your service callbacks automatically. + +Supported trigger events (`chat:ChatService` callbacks): + +| Callback | Description | +|----------|-------------| +| `onMessage` | A user sends a message, @mentions the app, or invokes a slash command. | +| `onAddedToSpace` | The app is added to a space. | +| `onRemovedFromSpace` | The app is removed from a space. | +| `onCardClicked` | A user clicks a button or interactive element on a card. | +| `onWidgetUpdated` | A widget requests an autocomplete or similar update. | +| `onAppCommand` | A user invokes a Chat app command. | +| `onAppHome` | A user opens the app's home page. | +| `onSubmitForm` | A user submits a dialog or form. | + +See the **[Trigger Reference](triggers.md)** for listener configuration, service callbacks, and the event payload structure. + +## Documentation + +* **[Setup Guide](setup-guide.md)**: Create a GCP project, enable the Google Chat API, and configure your Chat app. +* **[Action Reference](actions.md)**: Full reference for the client: operations, parameters, return types, and sample code. +* **[Trigger Reference](triggers.md)**: Reference for event-driven integration using the webhook listener and service model. +* **[Example](example.md)**: Learn how to build and configure an integration using the **Google Chat** connector, including event-driven trigger setup. + +## How to contribute + +As an open source project, WSO2 welcomes contributions from the community. + +To contribute to the code for this connector, please create a pull request in the following repository. + +* [Google Chat Connector GitHub repository](https://github.com/ballerina-platform/module-ballerinax-googleapis.chat) + +Check the issue tracker for open issues that interest you. We look forward to receiving your contributions. diff --git a/en/docs/connectors/catalog/communication/google-chat/setup-guide.md b/en/docs/connectors/catalog/communication/google-chat/setup-guide.md new file mode 100644 index 0000000000..7777546860 --- /dev/null +++ b/en/docs/connectors/catalog/communication/google-chat/setup-guide.md @@ -0,0 +1,95 @@ +--- +title: Setup Guide +description: "How to create a GCP project, enable the Google Chat API, and configure a Chat app for the Google Chat connector." +keywords: [google chat, google cloud platform, chat api, service account, oauth] +connector: true +connector_name: "googleapis.chat" +--- + +# Setup Guide + +This guide walks you through creating a Google Cloud Platform (GCP) project and configuring a Chat app for the Google Chat connector. + +## Prerequisites + +- A [Google Cloud Platform](https://console.cloud.google.com/) account with a project, or [sign up for one](https://cloud.google.com/) + +## Create a Google Cloud Platform project + +Open the [Google Cloud Platform Console](https://console.cloud.google.com/), click the project drop-down menu, and select an existing project or create a new one for your Chat app. + +## Enable the Google Chat API + +Navigate to **APIs & Services → Library** and enable the **Google Chat API**. + +## Expose your local listener + +Google Chat must reach the listener over a public HTTPS URL. For local development, the easiest option is [ngrok](https://ngrok.com/): + +```bash +ngrok http 8000 +``` + +Copy the `https://.ngrok-free.app` URL it prints. You'll use this both in the next step and as the listener's `endpointUrl`. + +For production, deploy the listener behind any HTTPS-terminating load balancer or reverse proxy. + +## Configure the Chat app + +1. In the Google Cloud Console, open the **Google Chat API** page and select the **Configuration** tab. +2. Provide the **App name**, **Avatar URL**, and **Description**. +3. Make sure **"Build this Chat app as a Workspace add-on"** is **unchecked**. This connector handles interaction events directly over HTTP, not as a Workspace add-on. +4. Under **Interactive features**, enable the features your app needs (receive 1:1 messages, join spaces, slash commands, and so on). +5. Under **Connection settings**, choose **HTTP endpoint URL** and paste the ngrok (or production) HTTPS URL. +6. Set **Authentication audience** to either: + - the same **HTTP endpoint URL** (use `HttpEndpointUrlConfig`/`endpointUrl` in the `@chat:ServiceConfig` annotation), or + - your **Project number** (use `ProjectNumberConfig`/`projectNumber` instead). + + The value you choose here must match your service annotation. The listener uses it to validate the `aud` claim of the Google-signed bearer token on every incoming request. +7. Under **Visibility**, add the email addresses of users or Google Workspace domains that can install your app. + +## Choose an authentication method + +The connector supports three authentication modes for the internal Chat API client used by event callers. + +### Option A: Service account (recommended for bots) + +A service account lets your app act as itself. It's ideal for bots that post messages or run continuously. + +1. Navigate to **APIs & Services → Credentials**, open **+ Create credentials**, and select **Service account**. +2. Give it a name, click **Done**, then open the created service account and go to the **Keys** tab. +3. Click **Add key → Create new key → JSON** and save the downloaded JSON file securely. You'll reference its path from `Config.toml`. + +### Option B: OAuth 2.0 (for user-scoped actions) + +OAuth 2.0 lets your app act on behalf of a signed-in user. It's required for operations like attachment uploads that need user scopes. + +1. Open **APIs & Services → OAuth consent screen** and configure your consent screen (app name and support email). +2. Open **APIs & Services → Credentials → Create credentials → OAuth client ID**. +3. Fill in the form: + + | Field | Value | + |---|---| + | Application type | Web Application | + | Name | ChatConnector | + | Authorized Redirect URIs | `https://developers.google.com/oauthplayground` | + +4. Save the **Client ID** and **Client secret**. +5. Use the [OAuth 2.0 Playground](https://developers.google.com/oauthplayground) to obtain a refresh token: open the gear icon, select "Use your own OAuth credentials", enter the client ID and secret, authorize the Chat scopes you need, then exchange the authorization code for tokens. + +### Option C: Bearer token (for quick tests) + +For short-lived experiments, use a Google access token directly: + +```bash +gcloud auth print-access-token +``` + +:::note +Google access tokens expire in roughly one hour. Bearer-token auth is best for short-lived processes. For long-running services, use service account or OAuth 2.0. Both auto-refresh tokens. +::: + +## Next steps + +- [Action Reference](actions.md): call the Chat REST API using the `Client`. +- [Trigger Reference](triggers.md): handle interaction events using the `Listener` and `ChatService`. diff --git a/en/docs/connectors/catalog/communication/google-chat/triggers.md b/en/docs/connectors/catalog/communication/google-chat/triggers.md new file mode 100644 index 0000000000..5822c9c6e7 --- /dev/null +++ b/en/docs/connectors/catalog/communication/google-chat/triggers.md @@ -0,0 +1,142 @@ +--- +title: Triggers +description: "Reference for the Google Chat webhook listener and service callbacks: configure message, card click, and other interaction event handlers in Ballerina integrations." +keywords: [google chat, webhook listener, chat api, event handler, ballerina] +connector: true +connector_name: "googleapis.chat" +--- + +# Triggers + +The `ballerinax/googleapis.chat` package supports event-driven integration through direct HTTP delivery of Google Chat interaction events. There's no separate webhook relay. When a user messages, adds, or interacts with your Chat app, the listener receives the event and dispatches it to the matching service callback automatically. + +Components that work together: + +| Component | Role | +|-----------|------| +| `chat:Listener` | Exposes the HTTP endpoint, verifies the Google-signed bearer token, and dispatches incoming events to an attached `ChatService`. | +| `chat:ChatService` | Defines the event-type callbacks, such as `onMessage`, `onCardClicked`, and `onAppHome`. Requires a `@chat:ServiceConfig` annotation. | +| `chat:MessageEvent` | The event payload passed to `onMessage`, with `message` guaranteed non-optional. | +| `chat:MessageCaller`, `chat:CardClickedCaller`, `chat:AppHomeCaller`, `chat:SubmitFormCaller` | Event-specific callers injected into handlers, pre-configured with the event's space context, used to respond or call the Chat API asynchronously. | + +For action-based operations, see the [Action Reference](actions.md). + +## Error handling + +Each service callback returns `error?`. If a callback returns an error, the listener logs the failure. + +## Listener + +The `chat:Listener` receives interaction events directly from Google Chat over HTTP and routes them to the attached service. It also builds an internal Chat API client, used by the injected callers, from the `auth` configuration. + +### Configuration + +| Config Type | Description | +|-------------|-------------| +| `int\|http:Listener` | The port or HTTP listener to listen on. Defaults to port `8000`. | +| `ListenerConfig` | Auth credentials for the internal Chat API client, plus optional inbound HTTP listener settings. | + +`chat:ListenerConfig` fields: + +| Field | Type | Default | Description | +|---|---|---|---| +| `auth` | ServiceAccountAuthConfig|OAuth2Config|http:BearerTokenConfig | Required | Authentication for the internal Chat API client. | +| `httpListenerConfig` | `http:ListenerConfiguration` | `{}` | Optional inbound HTTP listener settings. | + +### Initializing the listener + +```ballerina +import ballerinax/googleapis.chat; + +configurable chat:ServiceAccountFileConfig serviceAccountAuth = ?; + +listener chat:Listener chatListener = new (8000, { + auth: serviceAccountAuth +}); +``` + +## Service + +A Google Chat trigger service is a Ballerina service attached to a `chat:Listener`, implementing `chat:ChatService`, with a required `@chat:ServiceConfig` annotation that declares the bearer-token audience. + +```ballerina +@chat:ServiceConfig { + endpointUrl: "https://my-app.example.com" +} +service chat:ChatService on chatListener { + // ... +} +``` + +Use `projectNumber` instead of `endpointUrl` if your Chat app's **Authentication audience** is set to **Project Number**. The value must match your Chat app's configuration exactly. The listener validates the `aud` claim of every incoming bearer token against it, and attaching a service without this annotation fails. + +### Callback signatures + +| Callback | Signature | Description | +|----------|-----------|-------------| +| `onMessage` | `remote function onMessage(chat:MessageEvent event, chat:MessageCaller caller) returns error?` | Invoked when a user sends a message, @mentions the app, or invokes a slash command. | +| `onAddedToSpace` | `remote function onAddedToSpace(chat:ChatEvent event, chat:MessageCaller caller) returns error?` | Invoked when the app is added to a space. | +| `onRemovedFromSpace` | `remote function onRemovedFromSpace(chat:ChatEvent event) returns error?` | Invoked when the app is removed from a space. No caller, since the app can no longer respond. | +| `onCardClicked` | `remote function onCardClicked(chat:ChatEvent event, chat:CardClickedCaller caller) returns error?` | Invoked when a user clicks a button or interactive element on a card. | +| `onWidgetUpdated` | `remote function onWidgetUpdated(chat:ChatEvent event, chat:WidgetUpdatedCaller caller) returns error?` | Invoked when a widget requests an autocomplete or similar update. | +| `onAppCommand` | `remote function onAppCommand(chat:ChatEvent event, chat:MessageCaller caller) returns error?` | Invoked when a user invokes a Chat app command. | +| `onAppHome` | `remote function onAppHome(chat:ChatEvent event, chat:AppHomeCaller caller) returns error?` | Invoked when a user opens the app's home page. | +| `onSubmitForm` | `remote function onSubmitForm(chat:ChatEvent event, chat:SubmitFormCaller caller) returns error?` | Invoked when a user submits a dialog or form. | + +:::note +The native dispatcher inspects each declared remote function's signature at runtime to determine which event-specific caller to inject alongside the event. +::: + +### Full usage example + +```ballerina +import ballerina/log; +import ballerinax/googleapis.chat; + +configurable chat:ServiceAccountFileConfig serviceAccountAuth = ?; +configurable string endpointUrl = ?; + +listener chat:Listener chatListener = new (8000, {auth: serviceAccountAuth}); + +@chat:ServiceConfig { + endpointUrl: endpointUrl +} +service chat:ChatService on chatListener { + + remote function onMessage(chat:MessageEvent event, chat:MessageCaller caller) returns error? { + string text = event.message.text ?: ""; + log:printInfo("Google Chat message received", text = text); + check caller->respond({text: "Echo: " + text}); + } +} +``` + +## Event payload types + +### `ChatEvent` + +The base Google Chat app interaction event. + +| Field | Type | Description | +|-------|------|-------------| +| `type` | `EventType` | The type of interaction event. | +| `eventTime` | `string?` | When the event occurred (RFC 3339 timestamp). | +| `message` | `Message?` | The message that triggered the event, for `MESSAGE`, `ADDED_TO_SPACE`, and `CARD_CLICKED` events. | +| `user` | `User?` | The user that triggered the interaction. | +| `space` | `Space?` | The space where the interaction occurred. | +| `action` | `FormAction?` | The form action data, for `CARD_CLICKED` and `SUBMIT_FORM` events. | +| `common` | `CommonEventObject?` | Information about the user's client (locale, platform, form inputs). | + +### `MessageEvent` + +A specialization of `ChatEvent` with `message` guaranteed non-optional. Used as the parameter type for `onMessage` to avoid nil-check operators. + +| Field | Type | Description | +|-------|------|-------------| +| `message` | `Message` | The message that triggered the event (always present for `MESSAGE` events). | + +## What's next + +- [Action Reference](actions.md): manage spaces, messages, and members using the `Client`. +- [Example](example.md): complete example integrations for the Google Chat connector and trigger. +- [Setup Guide](setup-guide.md): create a GCP project and configure the Chat app. diff --git a/en/docs/connectors/catalog/communication/telegram/actions.md b/en/docs/connectors/catalog/communication/telegram/actions.md new file mode 100644 index 0000000000..9fdf8baace --- /dev/null +++ b/en/docs/connectors/catalog/communication/telegram/actions.md @@ -0,0 +1,784 @@ +--- +title: Actions +description: "Full reference for the Telegram connector client operations: messaging, chat management, query answers, and webhook management." +keywords: [telegram, telegram bot api, sendMessage, answerCallbackQuery, setWebhook, ballerina connector] +connector: true +connector_name: "telegram" +toc_max_heading_level: 4 +--- + +# Actions + +The `ballerinax/telegram` package exposes the following client: + +| Client | Purpose | +|--------|---------| +| [`Client`](#client) | Messaging, chat management, callback/inline query answers, file metadata/download, and webhook management on the Telegram Bot API. | + +## Client + +### Configuration + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `accessToken` | string | Required | The bot access token issued by @BotFather; embedded in every request's resource path. | +| `httpVersion` | http:HttpVersion | http:HTTP_2_0 | The HTTP version understood by the client. | +| `timeout` | decimal | 30 | The maximum time to wait (in seconds) for a response. | +| `forwarded` | string | "disable" | The choice of setting `forwarded`/`x-forwarded` header. | +| `poolConfig` | http:PoolConfiguration | () | Configurations associated with request pooling. | +| `cache` | http:CacheConfig | {} | HTTP caching-related configurations. | + +### Initializing the client + +```ballerina +import ballerinax/telegram; + +configurable string accessToken = ?; + +telegram:Client telegramClient = check new ({accessToken}); +``` + +### Operations + +#### Messaging + +
+sendMessage + +Sends a text message to a chat. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `text` | `string` | Yes | The message text. | +| `options` | `SendMessageOptions` | No | Optional fields such as `parse_mode`, `reply_markup`, and `disable_notification`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendMessage(chatId, "Hello from Ballerina!"); +``` + +Sample response: + +```json +{ + "message_id": 42, + "date": 1735689600, + "chat": {"id": 123456789, "type": "private"}, + "text": "Hello from Ballerina!" +} +``` + +
+ +
+sendPhoto + +Sends a photo by URL or as uploaded bytes. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `photo` | string|byte[] | Yes | A photo URL, `file_id`, or raw bytes to upload. | +| `options` | `SendPhotoOptions` | No | Optional fields such as `caption`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message photo = check telegramClient->sendPhoto(chatId, "https://example.com/photo.jpg"); +``` + +
+ +
+sendVideo + +Sends a video by URL or as uploaded bytes. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `video` | string|byte[] | Yes | A video URL, `file_id`, or raw bytes to upload. | +| `options` | `SendVideoOptions` | No | Optional fields such as `duration`, `width`, `height`, and `caption`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendVideo(chatId, "https://example.com/video.mp4"); +``` + +
+ +
+sendAudio + +Sends an audio file. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `audio` | string|byte[] | Yes | An audio URL, `file_id`, or raw bytes to upload. | +| `options` | `SendAudioOptions` | No | Optional fields such as `duration`, `performer`, and `title`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendAudio(chatId, "https://example.com/audio.mp3"); +``` + +
+ +
+sendDocument + +Sends a general file. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `document` | string|byte[] | Yes | A document URL, `file_id`, or raw bytes to upload. | +| `options` | `SendDocumentOptions` | No | Optional fields such as `caption` and `disable_content_type_detection`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendDocument(chatId, "https://example.com/report.pdf"); +``` + +
+ +
+sendAnimation + +Sends an animation (GIF or soundless MP4). + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `animation` | string|byte[] | Yes | An animation URL, `file_id`, or raw bytes to upload. | +| `options` | `SendAnimationOptions` | No | Optional fields such as `duration`, `width`, `height`, and `caption`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendAnimation(chatId, "https://example.com/clip.gif"); +``` + +
+ +
+sendSticker + +Sends a sticker. Telegram stickers have no caption. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `sticker` | string|byte[] | Yes | A sticker URL, `file_id`, or raw bytes to upload. | +| `options` | `SendStickerOptions` | No | Optional fields such as `emoji` (uploads only). | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendSticker(chatId, stickerFileId); +``` + +
+ +
+sendLocation + +Sends a point on the map. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `latitude` | `decimal` | Yes | The location's latitude. | +| `longitude` | `decimal` | Yes | The location's longitude. | +| `options` | `SendLocationOptions` | No | Optional fields such as `live_period` and `heading`, for live locations. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendLocation(chatId, 37.7749, -122.4194); +``` + +
+ +
+sendMediaGroup + +Sends a group of photos, videos, documents, or audio files as an album. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `media` | `InputMedia[]` | Yes | The media items to send, 2-10 items; `file_id`/URL strings only (raw-byte uploads via Telegram's `attach://` convention are not yet supported). | +| `options` | `SendMediaGroupOptions` | No | Optional fields such as `disable_notification`. | + +Returns: `Message[]|Error` + +Sample code: + +```ballerina +telegram:Message[] sent = check telegramClient->sendMediaGroup(chatId, [ + {'type: "photo", media: "https://example.com/one.jpg"}, + {'type: "photo", media: "https://example.com/two.jpg"} +]); +``` + +
+ +
+sendChatAction + +Shows a short-lived chat action indicator (for example, "typing...") to chat members. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `action` | `ChatAction` | Yes | The action to show; expires after ~5 seconds, or on the next sent message. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->sendChatAction(chatId, "typing"); +``` + +
+ +
+sendMessageDraft + +Streams a partial message to the same message bubble, identified by `draftId`. Successive calls with the same `draftId` update the same message bubble. Introduced in Bot API 9.3. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `draftId` | `int` | Yes | A non-zero ID; successive calls with the same ID update the same message bubble. | +| `options` | `SendMessageDraftOptions` | No | Optional fields such as `text` and `parse_mode`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message draft = check telegramClient->sendMessageDraft(chatId, draftId, text = "Thinking..."); +``` + +
+ +
+sendRichMessage + +Sends a message with structured rich-text formatting, authored in Markdown or HTML. Introduced in Bot API 10.1. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `richMessage` | `RichMessage` | Yes | The rich message content: `{markdown: string}` or `{html: string}`. | +| `options` | `SendRichMessageOptions` | No | Optional fields such as `disable_notification` and `reply_markup`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendRichMessage(chatId, {markdown: "**Bold** and _italic_ text"}); +``` + +
+ +
+sendRichMessageDraft + +Streams a partial rich message to the same message bubble, identified by `draftId`. Introduced in Bot API 10.1. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `draftId` | `int` | Yes | A non-zero ID; successive calls with the same ID update the same message bubble. | +| `richMessage` | `RichMessage` | Yes | The rich message content: `{markdown: string}` or `{html: string}`. | +| `messageThreadId` | `int?` | No | The forum topic to post the draft in, if any. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message draft = check telegramClient->sendRichMessageDraft(chatId, draftId, {markdown: "Thinking..."}); +``` + +
+ +
+sendApprovalMessage + +Sends a prompt with approve/decline inline-keyboard buttons. A client-side convenience wrapper around `sendMessage`; observe which button was pressed via the `Listener`'s `onCallbackQuery` handler, matching on `approve.callback_data`/`decline.callback_data`. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `text` | `string` | Yes | The prompt text. | +| `approve` | `ApprovalButton` | Yes | The approve button's label and `callback_data`. | +| `decline` | `ApprovalButton?` | No | The decline button's label and `callback_data`, if a decline option is wanted. | +| `options` | `ApprovalMessageOptions` | No | Optional fields such as `disable_notification`. | + +Returns: `Message|Error` + +Sample code: + +```ballerina +telegram:Message sent = check telegramClient->sendApprovalMessage(chatId, "Approve this request?", + {text: "Approve", callback_data: "approve:123"}, {text: "Decline", callback_data: "decline:123"}); +``` + +
+ +#### Message management + +
+deleteMessage + +Deletes a message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `messageId` | `int` | Yes | The message's ID. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->deleteMessage(chatId, messageId); +``` + +
+ +
+editMessageText + +Edits a text message the bot previously sent, or the text of an inline message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `text` | `string` | Yes | The new message text. | +| `chatId` | int|string? | No | The target chat's ID or `@username`; required together with `messageId`. | +| `messageId` | `int?` | No | The message's ID; required together with `chatId`. | +| `inlineMessageId` | `string?` | No | An inline message's ID; mutually exclusive with `chatId`/`messageId`. | +| `options` | `EditMessageTextOptions` | No | Optional fields such as `parse_mode` and `reply_markup`. | + +Returns: `Message|Error?` + +Sample code: + +```ballerina +telegram:Message|() edited = check telegramClient->editMessageText("Updated text", chatId, messageId); +``` + +
+ +
+pinChatMessage + +Pins a message in a chat. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `messageId` | `int` | Yes | The message's ID. | +| `disableNotification` | `boolean` | No | Whether to pin silently, without notifying chat members. Defaults to `false`. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->pinChatMessage(chatId, messageId); +``` + +
+ +
+unpinChatMessage + +Unpins a message in a chat. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `messageId` | `int?` | No | The pinned message's ID; unpins the most recent pinned message if omitted. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->unpinChatMessage(chatId); +``` + +
+ +#### Chat management + +
+getChat + +Gets up-to-date information about a chat. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | + +Returns: `ChatFullInfo|Error` + +Sample code: + +```ballerina +telegram:ChatFullInfo chat = check telegramClient->getChat(chatId); +``` + +Sample response: + +```json +{ + "id": 123456789, + "type": "private", + "first_name": "Jane" +} +``` + +
+ +
+getChatAdministrators + +Gets the list of administrators of a chat. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | + +Returns: `ChatMember[]|Error` + +Sample code: + +```ballerina +telegram:ChatMember[] admins = check telegramClient->getChatAdministrators(chatId); +``` + +
+ +
+getChatMember + +Gets information about one member of a chat. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `userId` | `int` | Yes | The target user's ID. | + +Returns: `ChatMember|Error` + +Sample code: + +```ballerina +telegram:ChatMember member = check telegramClient->getChatMember(chatId, userId); +``` + +
+ +
+leaveChat + +Makes the bot leave a chat. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->leaveChat(chatId); +``` + +
+ +
+setChatDescription + +Sets a group, supergroup, or channel's description. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `description` | `string` | Yes | The new description, 0-255 characters. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->setChatDescription(chatId, "Support channel for Acme customers"); +``` + +
+ +
+setChatTitle + +Sets a chat's title. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `chatId` | int|string | Yes | The numeric chat ID, or an `@username` for channels. | +| `title` | `string` | Yes | The new title, 1-255 characters. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->setChatTitle(chatId, "Acme Support"); +``` + +
+ +#### Query answers + +
+answerCallbackQuery + +Answers a callback query raised by pressing an inline keyboard button. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `callbackQueryId` | `string` | Yes | The `id` of the `CallbackQuery` to answer. | +| `options` | `AnswerCallbackQueryOptions` | No | Optional fields such as `text` and `show_alert`. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->answerCallbackQuery(callbackQuery.id, text = "Got it!"); +``` + +
+ +
+answerInlineQuery + +Answers an inline query with a list of results. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `inlineQueryId` | `string` | Yes | The `id` of the `InlineQuery` to answer. | +| `results` | `InlineQueryResult[]` | Yes | The results to display to the user. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->answerInlineQuery(inlineQuery.id, results); +``` + +
+ +#### Files + +
+getFile + +Gets metadata, including the download path, for a file. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `fileId` | `string` | Yes | The file identifier from a message attachment. | + +Returns: `File|Error` + +Sample code: + +```ballerina +telegram:File file = check telegramClient->getFile(fileId); +``` + +
+ +
+downloadFile + +Downloads a file's bytes given its file ID. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `fileId` | `string` | Yes | The file identifier from a message attachment. | + +Returns: `byte[]|Error` + +Sample code: + +```ballerina +byte[] fileBytes = check telegramClient->downloadFile(fileId); +``` + +
+ +#### Webhook management + +
+setWebhook + +Registers a webhook URL to receive updates. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `url` | `string` | Yes | The public HTTPS URL to deliver updates to. | +| `options` | `SetWebhookOptions` | No | Optional fields such as `secret_token`. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->setWebhook("https://my-app.example.com/", secret_token = secretToken); +``` + +
+ +
+deleteWebhook + +Removes the currently registered webhook. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `dropPendingUpdates` | `boolean` | No | Whether to discard updates that were queued while the webhook was set. Defaults to `false`. | + +Returns: `Error?` + +Sample code: + +```ballerina +_ = check telegramClient->deleteWebhook(); +``` + +
+ +
+getWebhookInfo + +Gets information about the currently registered webhook, useful for debugging. + +Returns: `WebhookInfo|Error` + +Sample code: + +```ballerina +telegram:WebhookInfo info = check telegramClient->getWebhookInfo(); +``` + +Sample response: + +```json +{ + "url": "https://my-app.example.com/", + "has_custom_certificate": false, + "pending_update_count": 0 +} +``` + +
+ +## What's next + +- [Trigger Reference](triggers.md): react to messages and queries using the webhook listener. +- [Example](example.md): complete example integrations for the Telegram connector and trigger. +- [Setup Guide](setup-guide.md): create a bot and obtain a token. diff --git a/en/docs/connectors/catalog/communication/telegram/example.md b/en/docs/connectors/catalog/communication/telegram/example.md new file mode 100644 index 0000000000..418ffe8018 --- /dev/null +++ b/en/docs/connectors/catalog/communication/telegram/example.md @@ -0,0 +1,77 @@ +--- +title: Examples +description: Step-by-step example for using the Telegram connector to react to incoming messages using a webhook trigger. +keywords: [telegram, telegram bot, connector example, webhook, trigger, ballerina] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Example + +## Telegram Trigger Example + +### What you'll build + +This integration reacts to incoming Telegram messages. When a user sends a message to your bot, the `telegram:Listener` receives the webhook update and routes it to the `onMessage` handler in your `telegram:TelegramService`. The handler logs the chat ID and message text. + +### Architecture + +```mermaid +flowchart LR + A((Telegram User)) --> B[Telegram Bot API] + B --> C[[telegram:Listener]] + C --> D[Handler: onMessage] + D --> E[log:printInfo] +``` + +### Prerequisites + +- A Telegram bot created with [@BotFather](https://t.me/BotFather). See the [Setup Guide](setup-guide.md). +- A public HTTPS URL for local development, for example using [ngrok](https://ngrok.com) + +### Setting up the Telegram integration + +> **New to WSO2 Integrator?** Follow the [Create a New Integration](../../../../develop/create-integrations/create-a-new-integration.md) guide to set up your integration first, then return here to add the trigger. + +### Adding the Telegram trigger + +Add a Telegram event integration and implement the listener and service as described in the [Telegram event integration guide](../../../../develop/integration-artifacts/event/telegram.md). WSO2 Integrator renders the listener and service on the design canvas, with all available handlers listed under **Event Handlers**. + + + +Select **telegram:TelegramService** in the design canvas to open the Service Designer, which lists every pre-registered event handler bound to the `telegramListener`. + + + +### Handling Telegram events + +Select the **onMessage** row to open its flow canvas, then add a **Log Info** step with the message text: + +```ballerina +remote function onMessage(telegram:Message message) returns error? { + log:printInfo("Telegram message received", chatId = message.chat.id, text = message.text); +} +``` + +### Running the integration + +Run the integration from WSO2 Integrator, then send a message to your bot from Telegram. Telegram delivers the webhook to your listener, which invokes `onMessage` and logs the message. Verify the log entry in the WSO2 Integrator console output. + +## What's next + +- [Action Reference](actions.md): send messages and manage chats using the `Client`. +- [Trigger Reference](triggers.md): full listener configuration and service callback reference. +- [Setup Guide](setup-guide.md): create a bot and configure the webhook. diff --git a/en/docs/connectors/catalog/communication/telegram/overview.md b/en/docs/connectors/catalog/communication/telegram/overview.md new file mode 100644 index 0000000000..1ea5fbc19a --- /dev/null +++ b/en/docs/connectors/catalog/communication/telegram/overview.md @@ -0,0 +1,68 @@ +--- +title: "Telegram Connector Overview" +description: "Overview of the Ballerina Telegram connector: send messages, manage chats and files, and handle Telegram Bot API webhook updates." +keywords: [telegram, telegram bot, webhook, ballerina connector] +connector: true +connector_name: "telegram" +--- + +# Overview + +The [Telegram Bot API](https://core.telegram.org/bots/api) lets bots send and receive messages, manage chats, and answer inline or callback queries over a simple HTTPS REST API. The Ballerina `ballerinax/telegram` connector provides both a client covering chat management, messaging, and file operations, and a webhook listener for the update types Telegram delivers. + +## Key features + +- Send text messages, photos, videos, documents, audio, animations, stickers, locations, and media groups +- Manage chats: get chat info, get or list administrators, leave a chat, update the chat title or description +- Answer callback and inline queries +- Retrieve file metadata and download files +- Register, remove, and inspect webhooks (`setWebhook`, `deleteWebhook`, `getWebhookInfo`) +- Receive the nine most common Telegram update types over a webhook listener +- Secret-token authentication of inbound webhook requests via `X-Telegram-Bot-Api-Secret-Token` + +## Actions + +Actions are operations you invoke on the Telegram Bot API from your integration: sending messages, managing chats, and answering queries. The connector exposes actions through a single client: + +| Client | Actions | +|--------|---------| +| `Client` | Messaging, chat management, callback/inline query answers, file metadata/download, webhook management | + +See the **[Action Reference](actions.md)** for the full list of operations, parameters, and sample code. + +## Triggers + +Triggers allow your integration to react to Telegram Bot API updates in real time. The connector provides a webhook listener that receives Telegram's updates and invokes your service callbacks automatically. + +Supported trigger events (`telegram:TelegramService` callbacks): + +| Callback | Description | +|----------|-------------| +| `onMessage` | A new incoming message arrives. | +| `onEditedMessage` | An existing message is edited. | +| `onChannelPost` | A new channel post is published. | +| `onEditedChannelPost` | An existing channel post is edited. | +| `onCallbackQuery` | A user presses an inline keyboard button. | +| `onInlineQuery` | A user sends a new inline query. | +| `onPoll` | A poll's state changes. | +| `onPreCheckoutQuery` | A user confirms a payment, just before it's charged. | +| `onShippingQuery` | A user provides a shipping address for an invoice with flexible pricing. | + +See the **[Trigger Reference](triggers.md)** for listener configuration, service callbacks, and the event payload structure. + +## Documentation + +* **[Setup Guide](setup-guide.md)**: Create a bot with BotFather and obtain a bot token. +* **[Action Reference](actions.md)**: Full reference for the client: operations, parameters, return types, and sample code. +* **[Trigger Reference](triggers.md)**: Reference for event-driven integration using the webhook listener and service model. +* **[Example](example.md)**: Learn how to build and configure an integration using the **Telegram** connector, including connection setup and event-driven trigger setup. + +## How to contribute + +As an open source project, WSO2 welcomes contributions from the community. + +To contribute to the code for this connector, please create a pull request in the following repository. + +* [Telegram Connector GitHub repository](https://github.com/ballerina-platform/module-ballerinax-telegram) + +Check the issue tracker for open issues that interest you. We look forward to receiving your contributions. diff --git a/en/docs/connectors/catalog/communication/telegram/setup-guide.md b/en/docs/connectors/catalog/communication/telegram/setup-guide.md new file mode 100644 index 0000000000..46f1436c27 --- /dev/null +++ b/en/docs/connectors/catalog/communication/telegram/setup-guide.md @@ -0,0 +1,66 @@ +--- +title: Setup Guide +description: "How to create a Telegram bot with BotFather and configure a webhook for the Telegram connector." +keywords: [telegram, botfather, webhook, bot token, secret token] +connector: true +connector_name: "telegram" +--- + +# Setup Guide + +This guide walks you through creating a Telegram bot and obtaining the credentials required to use the Telegram connector. + +## Prerequisites + +- A Telegram account + +## Create a bot and get a token + +1. Open a chat with [@BotFather](https://t.me/BotFather) on Telegram. +2. Send `/newbot` and follow the prompts to choose a display name and a unique `@username` (must end in `bot`). +3. BotFather replies with a bot token (for example, `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`). Use this as `accessToken` in `telegram:ConnectionConfig` or as the listener's `accessToken`. + +:::warning +Treat the token like a password: anyone who has it can control the bot. Regenerate it with BotFather's `/revoke` command if it leaks. +::: + +Unlike most APIs, Telegram doesn't use a header or query parameter for authentication. The token is embedded directly in every request's URL path. The client and listener handle this automatically. + +## Get a chat ID + +Most `Client` operations need a `chatId`, the numeric ID of the chat, group, or channel to act on: + +- **Private chat**: message your bot from your own account, then call `getUpdates` (`https://api.telegram.org/bot/getUpdates`) and read `message.chat.id` from the response. +- **Group or supergroup**: add the bot to the group, send any message, then use the same `getUpdates` approach. Group chat IDs are negative numbers. +- **Channel**: add the bot as an administrator, then use the channel's `@username` (for example, `"@my_channel"`) directly as `chatId` instead of a numeric ID. + +## Configure a webhook + +The `telegram:Listener` needs updates pushed to it. Telegram supports only one webhook URL per bot, and it must be reachable over HTTPS. + +1. Expose the listener's port publicly (a tunneling tool such as `ngrok http 8090` is the usual approach during development). +2. Start the listener with the bot's access token and the callback URL. It registers its own webhook automatically: + + ```ballerina + listener telegram:Listener telegramListener = new (8090, accessToken = "my-bot-access-token", callbackUrl = "https://my-app.example.com/"); + ``` + +By default, `allowed_updates` is set to exactly the nine update types the listener supports, so Telegram filters out anything else before it reaches your webhook. + +If you'd rather register the webhook yourself, omit `callbackUrl` and call `Client->setWebhook` explicitly instead: + +```ballerina +listener telegram:Listener telegramListener = new (8090, accessToken = "my-bot-access-token"); + +telegram:Client telegramClient = check new ({accessToken: "my-bot-access-token"}); +_ = check telegramClient->setWebhook("https://my-app.example.com/"); +``` + +Both sides independently derive the same secret token from `accessToken` via `deriveSecretToken`, so they agree automatically as long as both use the same access token. The webhook secret is no longer something you set directly on the listener. To override the secret token used for `setWebhook` only, pass `secret_token` explicitly. Use any string matching `[A-Za-z0-9_-]{1,256}`. If it doesn't match what the listener derives from its own `accessToken`, every update is rejected with `401`. + +To stop receiving updates, call `Client->deleteWebhook()`. To check what's currently registered, call `Client->getWebhookInfo()`. + +## Next steps + +- [Action Reference](actions.md): send messages and manage chats using the `Client`. +- [Trigger Reference](triggers.md): handle webhook updates using the `Listener` and `TelegramService`. diff --git a/en/docs/connectors/catalog/communication/telegram/triggers.md b/en/docs/connectors/catalog/communication/telegram/triggers.md new file mode 100644 index 0000000000..35648b31d6 --- /dev/null +++ b/en/docs/connectors/catalog/communication/telegram/triggers.md @@ -0,0 +1,197 @@ +--- +title: Triggers +description: "Reference for the Telegram webhook listener and service callbacks: configure message, callback query, and other update handlers in Ballerina integrations." +keywords: [telegram, webhook listener, telegram bot api, event handler, ballerina] +connector: true +connector_name: "telegram" +--- + +# Triggers + +The `ballerinax/telegram` package supports event-driven integration through Telegram Bot API webhooks. When a message, callback query, or other supported update arrives, the listener receives the webhook request and dispatches it to the matching service callback automatically. + +Three components work together: + +| Component | Role | +|-----------|------| +| `telegram:Listener` | Wraps an `http:Listener`, authenticates each update against a secret token (`X-Telegram-Bot-Api-Secret-Token`) derived internally from `accessToken`, and dispatches updates to an attached `TelegramService`. | +| `telegram:TelegramService` | Defines the update-type callbacks, such as `onMessage`, `onCallbackQuery`, and `onInlineQuery`. | +| `telegram:Message` | The most commonly used event payload, passed to `onMessage`, `onEditedMessage`, `onChannelPost`, and `onEditedChannelPost`. | +| `telegram:Caller` | Provided to a handler's optional second parameter for manual acknowledgement when `autoAck` is `false`. | + +For action-based operations, see the [Action Reference](actions.md). + +## Error handling + +Each service callback returns `error?`. If a callback returns an error, the listener logs the failure and continues processing subsequent updates. + +## Listener + +The `telegram:Listener` receives webhook requests from Telegram and routes updates to the attached service. Passing `callbackUrl` alongside `accessToken` registers this listener's webhook automatically when it starts. No separate `Client->setWebhook` call is needed. + +### Configuration + +| Config Type | Description | +|-------------|-------------| +| `int\|http:Listener` | A port number to bind a new `http:Listener` to, or an existing `http:Listener`. | +| `ListenerConfig` | Requires `accessToken`. | + +`telegram:ListenerConfig` fields: + +| Field | Type | Default | Description | +|---|---|---|---| +| `accessToken` | `string` | Required | The bot access token issued by @BotFather. Used both to derive the webhook secret token and, together with `callbackUrl`, to auto-register the webhook. | +| `callbackUrl` | `string?` | `()` | This listener's public HTTPS URL, used to auto-register the webhook when set. | +| `serviceUrl` | `string` | `https://api.telegram.org` | The Telegram Bot API base URL; override only for a self-hosted Bot API server, tests, or a proxy. | + +### Initializing the listener + +**Listener that auto-registers its webhook:** + +```ballerina +import ballerinax/telegram; + +configurable string botAccessToken = ?; +configurable string callbackUrl = ?; + +listener telegram:Listener telegramListener = new (8090, accessToken = botAccessToken, callbackUrl = callbackUrl); +``` + +## Service + +A Telegram trigger service is a Ballerina service attached to a `telegram:Listener`, implementing `telegram:TelegramService`. All nine handlers are optional. Declare only the ones you need. + +By default, the listener acknowledges (`200 OK`) each update automatically, before any handler runs. Telegram requires a fast `2xx` and retries otherwise, so this is the safe default for slow handlers. Annotate the service `@telegram:ServiceConfig {autoAck: false}` to take control of this yourself: declare a handler's optional second parameter as a `telegram:Caller` and call `caller->complete()` when ready. See [Manual acknowledgement](#manual-acknowledgement). + +### Callback signatures + +| Callback | Signature | Description | +|----------|-----------|-------------| +| `onMessage` | `remote function onMessage(telegram:Message message) returns error?` | Invoked on a new incoming message. | +| `onEditedMessage` | `remote function onEditedMessage(telegram:Message editedMessage) returns error?` | Invoked when an existing message is edited. | +| `onChannelPost` | `remote function onChannelPost(telegram:Message channelPost) returns error?` | Invoked on a new channel post. | +| `onEditedChannelPost` | `remote function onEditedChannelPost(telegram:Message editedChannelPost) returns error?` | Invoked when an existing channel post is edited. | +| `onCallbackQuery` | `remote function onCallbackQuery(telegram:CallbackQuery callbackQuery) returns error?` | Invoked when a user presses an inline keyboard button. | +| `onInlineQuery` | `remote function onInlineQuery(telegram:InlineQuery inlineQuery) returns error?` | Invoked when a user sends a new inline query. | +| `onPoll` | `remote function onPoll(telegram:Poll poll) returns error?` | Invoked when a poll's state changes. | +| `onPreCheckoutQuery` | `remote function onPreCheckoutQuery(telegram:PreCheckoutQuery preCheckoutQuery) returns error?` | Invoked when a user confirms a payment, just before it's charged. | +| `onShippingQuery` | `remote function onShippingQuery(telegram:ShippingQuery shippingQuery) returns error?` | Invoked when a user provides a shipping address for an invoice with flexible pricing. | + +:::note +Declaring a handler under any other name, with the wrong parameter type, or without the `remote` qualifier is a compile error, caught by this connector's compiler plugin. Every handler may optionally declare a second parameter typed `telegram:Caller`, for manual acknowledgement. +::: + +### Full usage example + +```ballerina +import ballerina/log; +import ballerinax/telegram; + +configurable string botAccessToken = ?; +configurable string callbackUrl = ?; + +listener telegram:Listener telegramListener = new (8090, accessToken = botAccessToken, callbackUrl = callbackUrl); + +service telegram:TelegramService on telegramListener { + + remote function onMessage(telegram:Message message) returns error? { + log:printInfo("Message received", chatId = message.chat.id, text = message.text); + } + + remote function onCallbackQuery(telegram:CallbackQuery callbackQuery) returns error? { + log:printInfo("Callback query received", queryId = callbackQuery.id, data = callbackQuery.data); + } +} +``` + +### Manual acknowledgement + +```ballerina +import ballerina/log; +import ballerinax/telegram; + +configurable string botAccessToken = ?; + +listener telegram:Listener telegramListener = new (8090, accessToken = botAccessToken); + +@telegram:ServiceConfig { + autoAck: false +} +service telegram:TelegramService on telegramListener { + + remote function onMessage(telegram:Message message, telegram:Caller caller) returns error? { + check persistMessage(message); + check caller->complete(); + } +} +``` + +:::note +When `autoAck` is `true` (the default), the listener acknowledges the update automatically before dispatching it. Set `autoAck: false` and declare a `telegram:Caller` to acknowledge only after your own processing has durably succeeded. If a handler declared with a `Caller` never calls `caller->complete()`, the listener never sends its own `200 OK`; the underlying HTTP service falls back to a default `500`, a non-`2xx` that Telegram's own retry behavior treats the same as any other failed delivery. + +Telegram may redeliver an update if the acknowledgement is slow, dropped, or never sent, under either `autoAck` setting, not just `false`. Make handler processing idempotent, or deduplicate using `update_id`, rather than assuming a webhook update is delivered exactly once. +::: + +## Event payload types + +### `Message` + +The most common event payload, passed to `onMessage`, `onEditedMessage`, `onChannelPost`, and `onEditedChannelPost`. + +| Field | Type | Description | +|-------|------|-------------| +| `message_id` | `int` | The unique message identifier inside the chat. | +| `date` | `int` | The Unix timestamp the message was sent. | +| `chat` | `Chat` | The chat the message belongs to. | +| `from` | `User?` | The message sender, if present. | +| `text` | `string?` | The message text, for text messages. | +| `reply_to_message` | `Message?` | The message this one replies to, if any. | +| `photo` | `PhotoSize[]?` | Available photo sizes, if the message contains a photo. | +| `document` | `Document?` | The attached document, if present. | +| `location` | `Location?` | The attached location, if present. | + +### `Chat` + +| Field | Type | Description | +|-------|------|-------------| +| `id` | `int` | The unique chat identifier. | +| `type` | `string` | The chat type: `private`, `group`, `supergroup`, or `channel`. | +| `title` | `string?` | The title, for groups, supergroups, and channels. | +| `username` | `string?` | The username, if available. | + +### `CallbackQuery` + +Passed to `onCallbackQuery` when a user presses an inline keyboard button. + +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The unique query identifier, passed to `answerCallbackQuery`. | +| `from` | `User` | The user who pressed the button. | +| `message` | `Message?` | The message the button is attached to, if available. | +| `chat_instance` | `string` | An identifier for the chat, used for tracking purposes. | +| `data` | `string?` | The callback data associated with the button, if any. | + +### `InlineQuery` + +Passed to `onInlineQuery` when a user types `@yourbot` in a chat. + +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | The unique query identifier, passed to `answerInlineQuery`. | +| `from` | `User` | The user who sent the query. | +| `query` | `string` | The query text. | +| `offset` | `string` | The pagination offset for the results. | + +### `Caller` + +Passed to a handler's optional second parameter for manual acknowledgement when the service is annotated `@telegram:ServiceConfig {autoAck: false}`. + +| Method | Signature | Description | +|--------|-----------|-------------| +| `complete` | `remote isolated function complete() returns telegram:Error?` | Acknowledges (`200 OK`) the update. Idempotent: a no-op if already called. | + +## What's next + +- [Action Reference](actions.md): send messages and manage chats using the `Client`. +- [Example](example.md): complete example integrations for the Telegram connector and trigger. +- [Setup Guide](setup-guide.md): create a bot and configure the webhook. diff --git a/en/docs/connectors/catalog/communication/whatsapp-business/actions.md b/en/docs/connectors/catalog/communication/whatsapp-business/actions.md new file mode 100644 index 0000000000..4a006c6c20 --- /dev/null +++ b/en/docs/connectors/catalog/communication/whatsapp-business/actions.md @@ -0,0 +1,266 @@ +--- +title: Actions +description: "Full reference for the WhatsApp Business connector client operations: sending messages and templates, and managing media." +keywords: [whatsapp, meta cloud api, sendMessage, sendTemplateMessage, uploadMedia, ballerina connector] +connector: true +connector_name: "whatsapp.business" +toc_max_heading_level: 4 +--- + +# Actions + +The `ballerinax/whatsapp.business` package exposes the following client: + +| Client | Purpose | +|--------|---------| +| [`Client`](#client) | Send text, media, location, contact, and template messages, and manage media on WhatsApp Business Cloud. | + +## Client + +### Configuration + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `auth` | http:BearerTokenConfig | Required | Bearer-token authentication using a system-user or temporary access token. | +| `apiVersion` | string | "v23.0" | The Meta Graph API version path segment used for every request. | +| `httpVersion` | http:HttpVersion | http:HTTP_2_0 | The HTTP version understood by the client. | +| `timeout` | decimal | 30 | The maximum time to wait (in seconds) for a response. | +| `forwarded` | string | "disable" | The choice of setting `forwarded`/`x-forwarded` header. | +| `poolConfig` | http:PoolConfiguration | () | Configurations associated with request pooling. | +| `cache` | http:CacheConfig | {} | HTTP caching-related configurations. | +| `compression` | http:Compression | http:COMPRESSION_AUTO | The way of handling the `accept-encoding` header. | +| `circuitBreaker` | http:CircuitBreakerConfig | () | Configurations associated with the behavior of the circuit breaker. | + +### Initializing the client + +```ballerina +import ballerinax/whatsapp.business as whatsapp; + +configurable string accessToken = ?; +configurable string phoneNumberId = ?; + +whatsapp:Client whatsappClient = check new ({auth: {token: accessToken}}); +``` + +### Operations + +#### Messaging + +
+sendMessage + +Sends a text, image, audio, video, document, location, or contact message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `phoneNumberId` | `string` | Yes | The business phone number ID to send from. | +| `payload` | `Message` | Yes | The message to send. A union of `TextMessage`, `ImageMessage`, `AudioMessage`, `VideoMessage`, `DocumentMessage`, `LocationMessage`, and `ContactMessage`. | + +Returns: `MessageResponsePayload|Error` + +Sample code: + +```ballerina +whatsapp:TextMessage message = { + to: "1XXXXXXXXXX", + text: {body: "Hello from Ballerina!"} +}; + +whatsapp:MessageResponsePayload response = check whatsappClient->sendMessage(phoneNumberId, message); +``` + +Sample response: + +```json +{ + "messaging_product": "whatsapp", + "contacts": [{"input": "1XXXXXXXXXX", "wa_id": "1XXXXXXXXXX"}], + "messages": [{"id": "wamid.HBgLMTX..."}] +} +``` + +
+ +
+sendTemplateMessage + +Sends a pre-approved message template. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `phoneNumberId` | `string` | Yes | The business phone number ID to send from. | +| `payload` | `TemplateMessage` | Yes | The template name, language, and component parameters. | + +Returns: `MessageResponsePayload|Error` + +Sample code: + +```ballerina +whatsapp:MessageResponsePayload response = check whatsappClient->sendTemplateMessage(phoneNumberId, { + to: "1XXXXXXXXXX", + template: {name: "hello_world", language: {code: "en_US"}} +}); +``` + +Sample response: + +```json +{ + "messaging_product": "whatsapp", + "contacts": [{"input": "1XXXXXXXXXX", "wa_id": "1XXXXXXXXXX"}], + "messages": [{"id": "wamid.HBgLMTX..."}] +} +``` + +
+ +#### Media + +
+uploadMedia + +Uploads media to be referenced by ID in a subsequent message. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `phoneNumberId` | `string` | Yes | The business phone number ID to upload against. | +| `payload` | `MediaUploadRequest` | Yes | The file content, file name, and MIME type. | + +Returns: `MediaUploadResponse|Error` + +Sample code: + +```ballerina +whatsapp:MediaUploadResponse uploaded = check whatsappClient->uploadMedia(phoneNumberId, { + fileContent: check io:fileReadBytes("image.jpg"), + fileName: "image.jpg", + mimeType: "image/jpeg" +}); +``` + +Sample response: + +```json +{ + "id": "1234567890" +} +``` + +
+ +
+retrieveMediaUrl + +Retrieves the temporary download URL for a media object. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `mediaId` | `string` | Yes | The media object ID. | + +Returns: `MediaUrlResponse|Error` + +Sample code: + +```ballerina +whatsapp:MediaUrlResponse mediaInfo = check whatsappClient->retrieveMediaUrl(mediaId); +``` + +Sample response: + +```json +{ + "url": "https://lookaside.fbsbx.com/whatsapp_business/attachments/...", + "id": "1234567890", + "mime_type": "image/jpeg", + "sha256": "...", + "file_size": 12345 +} +``` + +
+ +
+downloadMedia + +Downloads a media object's bytes given its media ID. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `mediaId` | `string` | Yes | The media object ID. | + +Returns: `byte[]|Error` + +Sample code: + +```ballerina +byte[] mediaBytes = check whatsappClient->downloadMedia(mediaId); +``` + +
+ +
+downloadMediaFromUrl + +Downloads a media object's bytes from its signed download URL (a `MediaUrlResponse.url`, as returned by `retrieveMediaUrl`), without re-fetching the media's metadata. Use this when you already have the URL (for example, after calling `retrieveMediaUrl` yourself to inspect the media's MIME type or size before deciding whether to download it), to avoid the redundant `retrieveMediaUrl` call that `downloadMedia` makes internally. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `url` | `string` | Yes | The signed download URL from a `MediaUrlResponse.url`. | + +Returns: `byte[]|Error` + +Sample code: + +```ballerina +whatsapp:MediaUrlResponse mediaInfo = check whatsappClient->retrieveMediaUrl(mediaId); +byte[] mediaBytes = check whatsappClient->downloadMediaFromUrl(mediaInfo.url); +``` + +
+ +
+deleteMedia + +Deletes a previously uploaded media object. + +Parameters: + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `mediaId` | `string` | Yes | The media object ID. | + +Returns: `MediaDeleteResponse|Error` + +Sample code: + +```ballerina +whatsapp:MediaDeleteResponse deleted = check whatsappClient->deleteMedia(mediaId); +``` + +Sample response: + +```json +{ + "success": true +} +``` + +
+ +## What's next + +- [Trigger Reference](triggers.md): react to inbound messages and status updates using the webhook listener. +- [Example](example.md): complete example integrations for the WhatsApp Business connector and trigger. +- [Setup Guide](setup-guide.md): create a Meta app and obtain credentials. diff --git a/en/docs/connectors/catalog/communication/whatsapp-business/example.md b/en/docs/connectors/catalog/communication/whatsapp-business/example.md new file mode 100644 index 0000000000..c0f46e0b9f --- /dev/null +++ b/en/docs/connectors/catalog/communication/whatsapp-business/example.md @@ -0,0 +1,84 @@ +--- +title: Examples +description: Step-by-step example for using the WhatsApp Business connector to react to inbound messages and account updates using a webhook trigger. +keywords: [whatsapp, meta cloud api, connector example, webhook, trigger, ballerina] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +# Example + +## WhatsApp Business Trigger Example + +### What you'll build + +This integration reacts to WhatsApp Business Cloud webhook events. When a customer sends a WhatsApp message, the `whatsapp:Listener` receives the webhook and routes it to the `onMessages` handler in your `whatsapp:WhatsAppService`. The handler logs each inbound message or status update. + +### Architecture + +```mermaid +flowchart LR + A((WhatsApp User)) --> B[WhatsApp Business Cloud] + B --> C[[whatsapp:Listener]] + C --> D[Handler: onMessages] + D --> E[log:printInfo] +``` + +### Prerequisites + +- A Meta app with the WhatsApp product added, and a webhook configured to point at your listener's public URL. See the [Setup Guide](setup-guide.md). + +### Setting up the WhatsApp Business integration + +> **New to WSO2 Integrator?** Follow the [Create a New Integration](../../../../develop/create-integrations/create-a-new-integration.md) guide to set up your integration first, then return here to add the trigger. + +### Adding the WhatsApp Business trigger + +Add a WhatsApp Business event integration and implement the listener and service as described in the [WhatsApp Business event integration guide](../../../../develop/integration-artifacts/event/whatsapp-business.md). WSO2 Integrator renders the listener and service on the design canvas, with all available handlers listed under **Event Handlers**. + + + +Select **whatsapp:WhatsAppService** in the design canvas to open the Service Designer, which lists every pre-registered event handler bound to the `whatsappListener`. + + + +### Handling WhatsApp Business events + +Select the **onMessages** row to open its flow canvas, then narrow the notification with an **If** step (`notification is whatsapp:Messages`) and add a **Foreach** step over the inbound messages or status updates to log each one: + +```ballerina +remote function onMessages(whatsapp:MessagesNotification notification) returns error? { + if notification is whatsapp:Messages { + foreach whatsapp:InboundMessage message in notification.messages { + log:printInfo("Inbound WhatsApp message received", sender = message.'from, messageId = message.messageId); + } + } else { + foreach whatsapp:MessageStatusUpdate statusUpdate in notification.statuses { + log:printInfo("WhatsApp message status update", messageId = statusUpdate.messageId, status = statusUpdate.status); + } + } +} +``` + +### Running the integration + +Run the integration from WSO2 Integrator, then send a test message to your WhatsApp Business number. Meta delivers the webhook to your listener, which invokes `onMessages` and logs the inbound message. Verify the log entry in the WSO2 Integrator console output. + +## What's next + +- [Action Reference](actions.md): send messages, templates, and manage media using the `Client`. +- [Trigger Reference](triggers.md): full listener configuration and service callback reference. +- [Setup Guide](setup-guide.md): create a Meta app and configure the webhook. diff --git a/en/docs/connectors/catalog/communication/whatsapp-business/overview.md b/en/docs/connectors/catalog/communication/whatsapp-business/overview.md new file mode 100644 index 0000000000..565f41d5fe --- /dev/null +++ b/en/docs/connectors/catalog/communication/whatsapp-business/overview.md @@ -0,0 +1,69 @@ +--- +title: "WhatsApp Business Connector Overview" +description: "Overview of the Ballerina WhatsApp Business connector: send messages and templates, manage media, and handle WhatsApp Business Cloud webhook events." +keywords: [whatsapp, whatsapp business, meta cloud api, webhook, ballerina connector] +connector: true +connector_name: "whatsapp.business" +--- + +# Overview + +[WhatsApp Business Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api) is Meta's hosted API for sending and receiving WhatsApp messages, managing business phone numbers, and handling message templates. The Ballerina `ballerinax/whatsapp.business` connector provides both a client for calling the Cloud API and a webhook listener for all ten WhatsApp Business Cloud webhook event types. + +## Key features + +- Send text, media, location, and contact messages +- Send message templates +- Upload, retrieve, and delete media +- Receive inbound messages and outbound message status updates over a webhook listener +- React to account, template, and phone number lifecycle changes +- Built-in `X-Hub-Signature-256` (HMAC-SHA256) webhook signature verification + +## Actions + +Actions are operations you invoke on WhatsApp Business Cloud from your integration: sending messages and templates, and managing media. The connector exposes actions through a single client: + +| Client | Actions | +|--------|---------| +| `Client` | Send text/template messages, upload media, download media, delete media | + +See the **[Action Reference](actions.md)** for the full list of operations, parameters, and sample code. + +## Triggers + +Triggers allow your integration to react to WhatsApp Business Cloud webhook events in real time. The connector provides a webhook listener that receives Meta's callbacks, including the subscription handshake, and invokes your service callbacks automatically. + +Supported trigger events (`whatsapp:WhatsAppService` callbacks): + +| Callback | Description | +|----------|-------------| +| `onMessages` | Inbound messages or outbound message status updates arrive. | +| `onAccountReviewUpdate` | A WhatsApp Business Account (WABA) review decision changes. | +| `onAccountUpdate` | An account-lifecycle or compliance change occurs. | +| `onBusinessCapabilityUpdate` | A WABA's messaging or phone-number capability limits change. | +| `onMessageTemplateQualityUpdate` | A message template's quality score changes. | +| `onMessageTemplateStatusUpdate` | A message template's status changes. | +| `onPhoneNumberNameUpdate` | A phone number's display-name review outcome is available. | +| `onPhoneNumberQualityUpdate` | A phone number's messaging throughput or quality tier changes. | +| `onSecurity` | A two-step-verification PIN change or reset request occurs. | +| `onTemplateCategoryUpdate` | A message template's category changes. | +| `onError` | Any handler above returned an error while being dispatched. | + +See the **[Trigger Reference](triggers.md)** for listener configuration, service callbacks, and the event payload structure. + +## Documentation + +* **[Setup Guide](setup-guide.md)**: Create a Meta app, get messaging credentials, and configure webhooks. +* **[Action Reference](actions.md)**: Full reference for the client: operations, parameters, return types, and sample code. +* **[Trigger Reference](triggers.md)**: Reference for event-driven integration using the webhook listener and service model. +* **[Example](example.md)**: Learn how to build and configure an integration using the **WhatsApp Business** connector, including connection setup and event-driven trigger setup. + +## How to contribute + +As an open source project, WSO2 welcomes contributions from the community. + +To contribute to the code for this connector, please create a pull request in the following repository. + +* [WhatsApp Business Connector GitHub repository](https://github.com/ballerina-platform/module-ballerinax-whatsapp.business) + +Check the issue tracker for open issues that interest you. We look forward to receiving your contributions. diff --git a/en/docs/connectors/catalog/communication/whatsapp-business/setup-guide.md b/en/docs/connectors/catalog/communication/whatsapp-business/setup-guide.md new file mode 100644 index 0000000000..15b53f2391 --- /dev/null +++ b/en/docs/connectors/catalog/communication/whatsapp-business/setup-guide.md @@ -0,0 +1,64 @@ +--- +title: Setup Guide +description: "How to create a Meta app, obtain WhatsApp Business Cloud API credentials, and configure webhooks for the WhatsApp Business connector." +keywords: [whatsapp, meta for developers, webhook, verify token, app secret] +connector: true +connector_name: "whatsapp.business" +--- + +# Setup Guide + +This guide walks you through creating a Meta app and obtaining the credentials required to use the WhatsApp Business connector. + +## Prerequisites + +- A [Meta for Developers](https://developers.facebook.com/) account +- A WhatsApp Business Account (WABA) + +## Create a Meta app + +1. Go to [Meta for Developers](https://developers.facebook.com/) and create a new app of type **Business**. +2. Add the **WhatsApp** product to the app. + +## Get the messaging credentials + +From **WhatsApp → API Setup**: + +- **Phone number ID**: the test or production business phone number ID. +- **Access token**: a temporary token is shown for testing. For production, create a **System User** in Meta Business Settings and generate a permanent token with the `whatsapp_business_messaging` and `whatsapp_business_management` permissions. + +Use the access token as the `token` in `whatsapp:ConnectionConfig.auth`. The client attaches it to every request automatically. + +## Configure webhooks + +From **WhatsApp → Configuration → Webhooks**: + +1. Set the **Callback URL** to your listener's public URL (for example, via a tunneling tool during development). +2. Set a **Verify token**. This must match the `verifyToken` you pass to `whatsapp:Listener`. +3. Subscribe to the fields you want notifications for. Each field maps 1:1 to one `WhatsAppService` handler: + + | Webhook field | Handler | + |---|---| + | `messages` | `onMessages` | + | `account_review_update` | `onAccountReviewUpdate` | + | `account_update` | `onAccountUpdate` | + | `business_capability_update` | `onBusinessCapabilityUpdate` | + | `message_template_quality_update` | `onMessageTemplateQualityUpdate` | + | `message_template_status_update` | `onMessageTemplateStatusUpdate` | + | `phone_number_name_update` | `onPhoneNumberNameUpdate` | + | `phone_number_quality_update` | `onPhoneNumberQualityUpdate` | + | `security` | `onSecurity` | + | `template_category_update` | `onTemplateCategoryUpdate` | + +4. Copy the app's **App secret** (**App Settings → Basic**) and use it as the listener's `appSecret` so inbound notifications are authenticated via `X-Hub-Signature-256`. + +:::note +Meta calls your callback URL with a `GET` handshake (echoing `hub.challenge`) when you save the webhook configuration, then delivers notifications via `POST`. The listener handles the handshake automatically. +::: + +Both `verifyToken` and `appSecret` are required. The listener can't start without them. + +## Next steps + +- [Action Reference](actions.md): send messages and manage media using the `Client`. +- [Trigger Reference](triggers.md): handle webhook events using the `Listener` and `WhatsAppService`. diff --git a/en/docs/connectors/catalog/communication/whatsapp-business/triggers.md b/en/docs/connectors/catalog/communication/whatsapp-business/triggers.md new file mode 100644 index 0000000000..308536d037 --- /dev/null +++ b/en/docs/connectors/catalog/communication/whatsapp-business/triggers.md @@ -0,0 +1,215 @@ +--- +title: Triggers +description: "Reference for the WhatsApp Business webhook listener and service callbacks: configure inbound message, status, and lifecycle event handlers in Ballerina integrations." +keywords: [whatsapp, webhook listener, meta cloud api, event handler, ballerina] +connector: true +connector_name: "whatsapp.business" +--- + +# Triggers + +The `ballerinax/whatsapp.business` package supports event-driven integration through WhatsApp Business Cloud webhooks. When a message arrives or an account, template, or phone number state changes, the listener receives the webhook request and dispatches it to the matching service callback automatically. + +Three components work together: + +| Component | Role | +|-----------|------| +| `whatsapp:Listener` | Wraps an `http:Listener`, handles the Meta subscription handshake and `X-Hub-Signature-256` verification, and dispatches events to an attached `WhatsAppService`. | +| `whatsapp:WhatsAppService` | Defines the webhook-field callbacks, such as `onMessages`, `onAccountUpdate`, and `onSecurity`. | +| `whatsapp:MessagesNotification` | The payload passed to `onMessages`: either a batch of inbound messages (`Messages`) or a batch of status updates (`MessageStatuses`). | +| `whatsapp:Caller` | Provided to a handler's optional second parameter for manual acknowledgement when `autoAck` is `false`. | + +For action-based operations, see the [Action Reference](actions.md). + +## Error handling + +Each service callback returns `error?`. If a callback returns an error, the listener logs it and continues processing subsequent events. Declare the eleventh, optional `onError` handler to react to a handler failure beyond logging. It receives the originating error and the webhook field being dispatched at the time. + +## Listener + +The `whatsapp:Listener` receives webhook requests from Meta, handles the `GET` subscription handshake automatically, and routes `POST` notifications to the attached service. + +### Configuration + +| Config Type | Description | +|-------------|-------------| +| `int\|http:Listener` | A port number to bind a new `http:Listener` to, or an existing `http:Listener`. | +| `ListenerConfig` | The verify token and app secret; both required. | + +`whatsapp:ListenerConfig` fields: + +| Field | Type | Default | Description | +|---|---|---|---| +| `verifyToken` | `string` | Required | The verification token configured in the Meta App dashboard. | +| `appSecret` | `string` | Required | The Meta app secret, used to verify the `X-Hub-Signature-256` header on inbound webhook notifications. | + +### Initializing the listener + +```ballerina +import ballerinax/whatsapp.business as whatsapp; + +configurable string verifyToken = ?; +configurable string appSecret = ?; + +listener whatsapp:Listener whatsappListener = new (8090, verifyToken = verifyToken, appSecret = appSecret); +``` + +## Service + +A WhatsApp Business trigger service is a Ballerina service attached to a `whatsapp:Listener`, implementing `whatsapp:WhatsAppService`. None of its handlers are required. Declare only the ones you care about. A field whose handler you didn't declare (or a field outside this closed set) is logged and dropped rather than delivered anywhere. + +By default, the listener acknowledges (`200 OK`) each notification automatically, before any handler runs. Meta requires a fast `2xx` and retries otherwise, so this is the safe default for slow handlers. Annotate the service `@business:ServiceConfig {autoAck: false}` to take control of this yourself: declare a handler's optional second parameter as a `whatsapp:Caller` and call `caller->complete()` when ready. See [Manual acknowledgement](#manual-acknowledgement). + +### Callback signatures + +| Callback | Signature | Description | +|----------|-----------|-------------| +| `onMessages` | `remote function onMessages(whatsapp:MessagesNotification notification) returns error?` | Invoked for inbound messages and outbound message status updates. | +| `onAccountReviewUpdate` | `remote function onAccountReviewUpdate(whatsapp:AccountReviewUpdate update) returns error?` | Invoked when a WABA review decision changes. | +| `onAccountUpdate` | `remote function onAccountUpdate(whatsapp:AccountUpdate update) returns error?` | Invoked on an account-lifecycle or compliance change. | +| `onBusinessCapabilityUpdate` | `remote function onBusinessCapabilityUpdate(whatsapp:BusinessCapabilityUpdate update) returns error?` | Invoked when a WABA's messaging or phone-number capability limits change. | +| `onMessageTemplateQualityUpdate` | `remote function onMessageTemplateQualityUpdate(whatsapp:MessageTemplateQualityUpdate update) returns error?` | Invoked when a message template's quality score changes. | +| `onMessageTemplateStatusUpdate` | `remote function onMessageTemplateStatusUpdate(whatsapp:MessageTemplateStatusUpdate update) returns error?` | Invoked when a message template's status changes. | +| `onPhoneNumberNameUpdate` | `remote function onPhoneNumberNameUpdate(whatsapp:PhoneNumberNameUpdate update) returns error?` | Invoked when a phone number's display-name review outcome is available. | +| `onPhoneNumberQualityUpdate` | `remote function onPhoneNumberQualityUpdate(whatsapp:PhoneNumberQualityUpdate update) returns error?` | Invoked when a phone number's messaging throughput or quality tier changes. | +| `onSecurity` | `remote function onSecurity(whatsapp:Security security) returns error?` | Invoked on a two-step-verification PIN change or reset request. | +| `onTemplateCategoryUpdate` | `remote function onTemplateCategoryUpdate(whatsapp:TemplateCategoryUpdate update) returns error?` | Invoked when a message template's category changes. | +| `onError` | `remote function onError(whatsapp:HandlerError handlerError) returns error?` | Invoked when any handler above returned an error while being dispatched. | + +:::note +A compiler plugin validates every handler you declare: its name must be one of the eleven above, its parameter must match the documented event type, and it must return `error?`. Every handler may optionally declare a second parameter typed `whatsapp:Caller`, for manual acknowledgement. +::: + +### Full usage example + +```ballerina +import ballerina/log; +import ballerinax/whatsapp.business as whatsapp; + +configurable string verifyToken = ?; +configurable string appSecret = ?; + +listener whatsapp:Listener whatsappListener = new (8090, verifyToken = verifyToken, appSecret = appSecret); + +service whatsapp:WhatsAppService on whatsappListener { + + remote function onMessages(whatsapp:MessagesNotification notification) returns error? { + if notification is whatsapp:Messages { + // handle inbound messages: notification.messages + } else { + // handle status updates: notification.statuses + } + } + + remote function onSecurity(whatsapp:Security security) returns error? { + // handle a PIN change/reset event + } +} +``` + +### Manual acknowledgement + +```ballerina +import ballerina/log; +import ballerinax/whatsapp.business as whatsapp; + +configurable string verifyToken = ?; +configurable string appSecret = ?; + +listener whatsapp:Listener whatsappListener = new (8090, verifyToken = verifyToken, appSecret = appSecret); + +@whatsapp:ServiceConfig { + autoAck: false +} +service whatsapp:WhatsAppService on whatsappListener { + + remote function onMessages(whatsapp:MessagesNotification notification, whatsapp:Caller caller) returns error? { + check persistNotification(notification); + check caller->complete(); + } +} +``` + +:::note +When `autoAck` is `true` (the default), the listener acknowledges the notification automatically before dispatching it. Set `autoAck: false` and declare a `whatsapp:Caller` to acknowledge only after your own processing has durably succeeded. If a handler declared with a `Caller` never calls `caller->complete()`, the listener never sends its own `200 OK`; the underlying HTTP service falls back to a default `500`, a non-`2xx` that Meta's own retry behavior treats the same as any other failed delivery. + +Meta may redeliver a notification if the acknowledgement is slow, dropped, or never sent, under either `autoAck` setting, not just `false`. Make handler processing idempotent, or deduplicate using a scoped key: `InboundMessage.messageId` (`wamid`) alone for messages, but `MessageStatusUpdate.messageId` needs `status` alongside it, since one message's several status updates share the same `messageId`. +::: + +## Event payload types + +### `InboundMessage` + +One inbound message, part of a `Messages` batch. + +| Field | Type | Description | +|-------|------|-------------| +| `from` | `string` | The WhatsApp ID (phone number) of the sender. | +| `messageId` | `string` | The unique WhatsApp message ID (`wamid...`). | +| `messageType` | `string` | The message type (for example, `text`, `image`, `interactive`). | +| `text` | `string?` | The message body for text messages; `()` for non-text messages. | +| `timestamp` | `string?` | The provider-reported timestamp of the message, if present. | +| `contactName` | `string?` | The sender's WhatsApp profile name, if present. | +| `raw` | `json` | The raw message object as received, for accessing type-specific fields. | + +### `MessageStatusUpdate` + +One outbound message status update, part of a `MessageStatuses` batch. + +| Field | Type | Description | +|-------|------|-------------| +| `messageId` | `string` | The message ID whose status changed. | +| `status` | `string` | The new status (`sent`, `delivered`, `read`, or `failed`). | +| `recipientId` | `string` | The WhatsApp ID of the recipient. | +| `timestamp` | `string?` | The provider-reported timestamp of the status change, if present. | +| `errors` | `MessageErrorDetail[]?` | Delivery failure details; present only when `status` is `failed`. | +| `raw` | `json` | The raw status object as received. | + +### `AccountUpdate` + +A WABA account-lifecycle or compliance change. Only the sub-record matching `event` is populated. + +| Field | Type | Description | +|-------|------|-------------| +| `wabaId` | `string` | The WhatsApp Business Account ID the change relates to. | +| `event` | `string` | The event type (for example, `ACCOUNT_DELETED`, `ACCOUNT_VIOLATION`, `DISABLED_UPDATE`). | +| `country` | `string?` | The ISO country code; present only for location-related events. | +| `timestamp` | `int?` | The entry-level webhook trigger timestamp (Unix epoch seconds), if present. | +| `raw` | `json` | The raw `value` object as received, for accessing undocumented sub-fields. | + +### `Security` + +A two-step-verification PIN change, reset request, or reset success for a business phone number. + +| Field | Type | Description | +|-------|------|-------------| +| `wabaId` | `string` | The WhatsApp Business Account ID the phone number belongs to. | +| `displayPhoneNumber` | `string` | The business phone number the security event relates to. | +| `event` | `string` | The security action (`PIN_CHANGED`, `PIN_RESET_REQUEST`, or `PIN_REQUEST_SUCCESS`). | +| `requester` | `string?` | The Meta Business Suite user ID that initiated the action; present only for reset requests. | +| `timestamp` | `int?` | The entry-level webhook trigger timestamp (Unix epoch seconds), if present. | +| `raw` | `json` | The raw `value` object as received. | + +### `HandlerError` + +Reports an error returned by another `WhatsAppService` handler while it was being invoked. Delivered to `onError`, if declared. + +| Field | Type | Description | +|-------|------|-------------| +| `error` | `error` | The error the handler returned. | +| `field` | `string` | The webhook field being dispatched when the handler failed (for example, `messages`, `account_update`). | +| `payload` | `json` | The raw `value` object that was being dispatched, for diagnostics. | + +### `Caller` + +Passed to a handler's optional second parameter for manual acknowledgement when the service is annotated `@business:ServiceConfig {autoAck: false}`. + +| Method | Signature | Description | +|--------|-----------|-------------| +| `complete` | `remote isolated function complete() returns whatsapp:Error?` | Acknowledges (`200 OK`) the notification. Idempotent: a no-op if already called. | + +## What's next + +- [Action Reference](actions.md): send messages and templates, and manage media. +- [Example](example.md): complete example integrations for the WhatsApp Business connector and trigger. +- [Setup Guide](setup-guide.md): create a Meta app and configure the webhook. diff --git a/en/docs/develop/integration-artifacts/event/google-chat.md b/en/docs/develop/integration-artifacts/event/google-chat.md new file mode 100644 index 0000000000..57a130a468 --- /dev/null +++ b/en/docs/develop/integration-artifacts/event/google-chat.md @@ -0,0 +1,228 @@ +--- +title: Google Chat +description: React to Google Chat interaction events, such as messages, card clicks, dialog submissions, and app-home opens, using pre-built event handlers. +keywords: [wso2 integrator, google chat, google workspace, event integration, webhook listener, chat app] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Google Chat + +Google Chat event integrations receive interaction events directly from Google Chat over HTTP and trigger handler functions as users message, add, or interact with your Chat app. Use them to build Chat apps, bots, and interactive cards without polling any API. + +:::note +The Google Chat listener must be reachable over a public HTTPS URL. For local development, use a tunneling tool such as [ngrok](https://ngrok.com) to create a public URL for your local port. + +After starting the integration, configure your Chat app in the **Google Cloud Console** under **Google Chat API → Configuration**: set the **HTTP endpoint URL** to your listener's public URL and choose an **Authentication audience** that matches your service's configuration. See the [setup guide](../../../connectors/catalog/communication/google-chat/setup-guide.md) for details. +::: + +## Creating a Google Chat listener + + + + +1. Click **+ Add Artifact** in the canvas or click **+** next to **Entry Points** in the sidebar. +2. In the **Artifacts** panel, select **Google Chat** under **Event Integration**. +3. In the creation form, fill in the following fields: + + + + | Field | Description | Default | + |---|---|---| + | **Listener Name** | Identifier for the listener created with this service. | `chatListener` | + | **Port** | The port or HTTP listener to listen on. | `8000` | + | **Auth Config** | Authentication for the Chat API client: a service account, OAuth 2.0, or bearer token config. | Required | + | **HTTP Listener Config** | Optional inbound HTTP listener settings. | `{}` | + +4. Click **Create**. + +5. WSO2 Integrator opens the service in the **Service Designer**. The canvas shows the attached listener pill and an empty **Event Handlers** section. + +6. Click **+ Add Handler** to define how incoming events are processed. + + + + +```ballerina +import ballerinax/googleapis.chat; +import ballerina/log; + +configurable chat:ServiceAccountFileConfig authConfig = ?; +configurable string endpointUrl = ?; +configurable int listenerPort = 8000; + +listener chat:Listener chatListener = new (listenerPort, {auth: authConfig}); + +@chat:ServiceConfig { + endpointUrl: endpointUrl +} +service chat:ChatService on chatListener { + + remote function onMessage(chat:MessageEvent event, chat:MessageCaller caller) returns error? { + string text = event.message.text ?: ""; + log:printInfo("Google Chat message received", text = text); + check caller->respond({text: "Echo: " + text}); + } +} +``` + +Save this as `main.bal` and run `bal run` from the project directory. Configure your Chat app's **HTTP endpoint URL** to point at the listener and make sure **Authentication audience** matches the `@chat:ServiceConfig` annotation. + + + + +## Service configuration + +Service configuration controls the Google Chat trigger's audience validation, and the name, port, and auth settings of each attached listener. + + + + +In the **Service Designer**, click **Configure** to open the **Google Chat Configuration** panel. + +The left panel shows **Attached Listeners**. Pick a listener under **Attached Listeners** to configure its connection settings in the main configuration panel. + + + +### Main configurations + +| Field | Description | +|---|---| +| **HTTP Endpoint URL Config** or **Project Number** | The audience the listener validates incoming bearer tokens against. Must match the **Authentication audience** configured for your Chat app. Required. | + +### Listener configurations + +| Field | Description | +|---|---| +| **Name** | The name of the listener. Required. | +| **Listen On** | The port or HTTP listener to listen on. Defaults to `8000`. | +| **Auth Config** | Authentication for the Chat API client: a service account, OAuth 2.0, or bearer token config. Required. | +| **HTTP Listener Config** | Optional inbound HTTP listener settings. | + +Click **Attach Listener** to attach an additional listener to the same service. + +Click **Save Changes** to apply updates. + + + + +The service-level `@chat:ServiceConfig` annotation configures which bearer-token audience the listener validates: + +```ballerina +@chat:ServiceConfig { + endpointUrl: "https://my-app.example.com" +} +service chat:ChatService on chatListener { + // handlers +} +``` + +Use `projectNumber` instead of `endpointUrl` if your Chat app's **Authentication audience** is set to **Project Number**. + +Listener configuration maps to the `chat:ListenerConfig` passed when constructing the listener: + +```ballerina +listener chat:Listener chatListener = new (listenerPort, {auth: authConfig}); +``` + +| Field | Type | Default | Description | +|---|---|---|---| +| `auth` | ServiceAccountAuthConfig|OAuth2Config|http:BearerTokenConfig | Required | Authentication for the internal Chat API client (service account, OAuth2, or bearer token). | +| `httpListenerConfig` | `http:ListenerConfiguration` | `{}` | Optional inbound HTTP listener settings. | + + + + +## Event handlers + +An event handler is a `remote function` that WSO2 Integrator calls for each Google Chat interaction event received. + +### Adding an event handler + + + + +In the **Service Designer**, click **+ Add Handler**. A **Select Handler to Add** panel opens on the right listing the available event types. + + + +Pick **On Message**, then click **Save**. This opens the **Flow Designer** for `onMessage`. + + + +Every handler is scaffolded with a built-in **Error Handler** block. Use the flow canvas to add integration steps such as database writes, HTTP calls, and transformations, and edit the **Error Handler** block to define recovery logic for errors raised in the flow. Repeat these steps to add the other handlers you need. + + + + +**onMessage handler** — called when a user sends a message, @mentions the app, or invokes a slash command: + +```ballerina +service chat:ChatService on chatListener { + + remote function onMessage(chat:MessageEvent event, chat:MessageCaller caller) returns error? { + do { + check caller->respond({text: "Echo: " + (event.message.text ?: "")}); + } on fail error err { + log:printError("Failed to handle onMessage event", err); + } + } +} +``` + +Return `error?` from a handler to allow unhandled errors to propagate to the listener, which logs them. Return `()` to suppress them. + + + + +### Handler types + +`chat:ChatService` exposes one optional handler per Chat event type. Implement only the ones you need. + +| Handler | Triggered when | +|---|---| +| `onMessage` | A user sends a message, @mentions the app, or invokes a slash command. | +| `onAddedToSpace` | The app is added to a space. | +| `onRemovedFromSpace` | The app is removed from a space. | +| `onCardClicked` | A user clicks a button or interactive element on a card. | +| `onWidgetUpdated` | A widget requests an autocomplete or similar update. | +| `onAppCommand` | A user invokes a Chat app command. | +| `onAppHome` | A user opens the app's home page. | +| `onSubmitForm` | A user submits a dialog or form. | + +Each handler receives the event and, for most event types, an event-specific caller (`chat:MessageCaller`, `chat:CardClickedCaller`, `chat:AppHomeCaller`, or `chat:SubmitFormCaller`) pre-configured with the event's space context. Use the caller to `respond` synchronously within the event window, or to call Chat APIs asynchronously (`sendMessage`, `updateMessage`, `deleteMessage`, `getSpace`). + +## What's next + +- [WhatsApp Business](whatsapp-business.md) — react to WhatsApp Business Cloud webhook events +- [Telegram](telegram.md) — react to Telegram Bot API webhook updates +- [Connections](../supporting/connections.md) — reuse Google Chat credentials across services +- [Google Chat connector reference](../../../connectors/catalog/communication/google-chat/overview.md) — full connector API reference +- [Google Chat setup guide](../../../connectors/catalog/communication/google-chat/setup-guide.md) — create a GCP project and configure the Chat app diff --git a/en/docs/develop/integration-artifacts/event/telegram.md b/en/docs/develop/integration-artifacts/event/telegram.md new file mode 100644 index 0000000000..575434cc03 --- /dev/null +++ b/en/docs/develop/integration-artifacts/event/telegram.md @@ -0,0 +1,264 @@ +--- +title: Telegram +description: React to Telegram Bot API webhook updates, such as messages, callback queries, and inline queries, using pre-built event handlers for each update type. +keywords: [wso2 integrator, telegram, telegram bot, event integration, webhook listener, telegram bot api] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Telegram + +Telegram event integrations receive webhook updates from the Telegram Bot API and trigger handler functions as messages, callback queries, and other update types arrive. Use them to build chatbots, handle inline keyboard interactions, and automate replies without polling `getUpdates`. + +:::note +The Telegram webhook listener must be reachable over HTTPS from the internet. For local development, use a tunneling tool such as [ngrok](https://ngrok.com) to create a public HTTPS URL for your local port. + +Create a bot and get a token by messaging [@BotFather](https://t.me/BotFather) on Telegram and sending `/newbot`. See the [setup guide](../../../connectors/catalog/communication/telegram/setup-guide.md) for details. +::: + +## Creating a Telegram listener + + + + +1. Click **+ Add Artifact** in the canvas or click **+** next to **Entry Points** in the sidebar. +2. In the **Artifacts** panel, select **Telegram** under **Event Integration**. +3. In the creation form, fill in the following fields: + + + + | Field | Description | Default | + |---|---|---| + | **Listener Name** | Identifier for the listener created with this service. | `telegramListener` | + | **Webhook Listener Port** | The port on which the webhook listener accepts incoming HTTP requests. | `8090` | + | **Access Token** | The bot access token issued by @BotFather. Used both to derive the webhook secret token and, together with **Callback URL**, to auto-register the webhook. | Required | + | **Callback URL** | This listener's public HTTPS URL, used to auto-register the webhook when set. | `()` | + + Expand **Advanced Configurations** to override the Telegram Bot API base URL. + + | Field | Description | Default | + |---|---|---| + | **Service URL** | The Telegram Bot API base URL. | `https://api.telegram.org` | + +4. Click **Create**. + +5. WSO2 Integrator opens the service in the **Service Designer**. The canvas shows the attached listener pill and an empty **Event Handlers** section. + +6. Click **+ Add Handler** to define how incoming updates are processed. + + + + +```ballerina +import ballerinax/telegram; +import ballerina/log; + +configurable string botAccessToken = ?; +configurable string callbackUrl = ?; +configurable int listenerPort = 8090; + +listener telegram:Listener telegramListener = new (listenerPort, accessToken = botAccessToken, callbackUrl = callbackUrl); + +service telegram:TelegramService on telegramListener { + + remote function onMessage(telegram:Message message) returns error? { + log:printInfo("Telegram message received", chatId = message.chat.id, text = message.text); + } + + remote function onCallbackQuery(telegram:CallbackQuery callbackQuery) returns error? { + log:printInfo("Telegram callback query received", queryId = callbackQuery.id, data = callbackQuery.data); + } +} +``` + +Save this as `main.bal` and run `bal run` from the project directory. Passing both `accessToken` and `callbackUrl` registers the webhook automatically when the listener starts. No separate `Client->setWebhook` call is needed. + + + + +## Service configuration + +Service configuration controls the service name and the Telegram listener it is attached to. + + + + +In the **Service Designer**, click **Configure** to open the **Telegram Configuration** panel. + +The panel shows a **Service Configuration** field for the service's acknowledgement behavior, and an **Attached Listeners** list. Pick a listener under **Attached Listeners** to configure its connection settings. + + + +### Service configuration + +| Field | Description | +|---|---| +| **Service Configuration** | Advanced acknowledgement settings as a `@telegram:ServiceConfig` record expression (e.g., `{ autoAck: false }`). Defaults to `autoAck: true`, acknowledging automatically. See [Manual acknowledgement](#manual-acknowledgement). | + +### Main configurations + +| Field | Description | +|---|---| +| **Name** | The name of the listener. Required. | +| **Listen To** | A port number to bind a new `http:Listener` to, or an existing `http:Listener`. Required. | +| **Access Token** | The bot access token issued by @BotFather. Used both to derive the webhook secret token and, together with **Callback URL**, to auto-register the webhook. | +| **Service URL** | The Telegram Bot API base URL; override only for a self-hosted Bot API server, tests, or a proxy. Defaults to `https://api.telegram.org`. | +| **Callback URL** | This listener's public HTTPS URL, used to auto-register the webhook when set. | + +Click **Attach Listener** to attach an additional listener to the same service. + +Click **Save Changes** to apply updates. + + + + +Service configuration maps to the `telegram:ListenerConfig` passed when constructing the listener, which requires `accessToken`. The `@telegram:ServiceConfig` annotation placed before the `service` declaration sets the acknowledgement mode: + +```ballerina +listener telegram:Listener telegramListener = new ( + listenerPort, + accessToken = botAccessToken, + callbackUrl = callbackUrl +); + +@telegram:ServiceConfig { + autoAck: false +} +service on telegramListener { + // handlers +} +``` + +`telegram:ListenerConfig` fields: + +| Field | Type | Default | Description | +|---|---|---|---| +| `accessToken` | `string` | Required | The bot access token issued by @BotFather. Used both to derive the webhook secret token and, together with `callbackUrl`, to auto-register the webhook. | +| `callbackUrl` | `string?` | `()` | This listener's public HTTPS URL, used to auto-register the webhook when set. | +| `serviceUrl` | `string` | `https://api.telegram.org` | The Telegram Bot API base URL; override only for a self-hosted Bot API server, tests, or a proxy. | + +`@telegram:ServiceConfig` fields: + +| Field | Type | Default | Description | +|---|---|---|---| +| `autoAck` | `boolean` | `true` | When `false`, updates must be manually acknowledged using `telegram:Caller`. See [Manual acknowledgement](#manual-acknowledgement). | + + + + +## Event handlers + +An event handler is a `remote function` that WSO2 Integrator calls for each Telegram Bot API update received. + +### Adding an event handler + + + + +In the **Service Designer**, click **+ Add Handler**. A **Select Handler to Add** panel opens on the right listing the available update types. + + + +Pick **On Message**, then click **Save**. This opens the **Flow Designer** for `onMessage`. + + + +Use the flow canvas to add integration steps such as database writes, HTTP calls, and transformations. Repeat these steps to add the other handlers you need. + + + + +**onMessage handler** — called for each new incoming message: + +```ballerina +service telegram:TelegramService on telegramListener { + + remote function onMessage(telegram:Message message) returns error? { + log:printInfo("Telegram message received", chatId = message.chat.id, text = message.text); + } +} +``` + +**onCallbackQuery handler** — called when a user presses an inline keyboard button: + +```ballerina +service telegram:TelegramService on telegramListener { + + remote function onCallbackQuery(telegram:CallbackQuery callbackQuery) returns error? { + log:printInfo("Telegram callback query received", queryId = callbackQuery.id, data = callbackQuery.data); + } +} +``` + + + + +### Manual acknowledgement + +By default, the listener acknowledges (`200 OK`) each update automatically, before any handler runs. Telegram requires a fast `2xx` and retries otherwise, so this is the safe default for slow handlers. Annotate the service `@telegram:ServiceConfig {autoAck: false}` to decide exactly when an update is acknowledged instead, and declare a handler's optional second parameter as a `telegram:Caller`: + +```ballerina +@telegram:ServiceConfig { + autoAck: false +} +service telegram:TelegramService on telegramListener { + + remote function onMessage(telegram:Message message, telegram:Caller caller) returns error? { + check persistMessage(message); + check caller->complete(); + } +} +``` + +If a handler declared with a `Caller` never calls `caller->complete()`, the listener never sends its own `200 OK` for that request, and Telegram's own retry behavior treats the resulting failure the same as any other failed delivery. Telegram may redeliver an update if the acknowledgement is slow, dropped, or never sent, under either `autoAck` setting, not just `false`, so make handler processing idempotent, or deduplicate using `update_id`. + +### Handler types + +Telegram delivers nine update types, one per `TelegramService` handler. All are optional. Implement only the ones your bot needs. Telegram's `allowed_updates` already keeps unsupported update types from reaching your webhook; among the nine supported types, an update whose handler you didn't declare is logged and dropped after reaching the listener. + +| Handler | Triggered when | +|---|---| +| `onMessage` | A new incoming message arrives. | +| `onEditedMessage` | An existing message is edited. | +| `onChannelPost` | A new channel post is published. | +| `onEditedChannelPost` | An existing channel post is edited. | +| `onCallbackQuery` | A user presses an inline keyboard button. | +| `onInlineQuery` | A user sends a new inline query. | +| `onPoll` | A poll's state changes. | +| `onPreCheckoutQuery` | A user confirms a payment, just before it's charged. | +| `onShippingQuery` | A user provides a shipping address for an invoice with flexible pricing. | + +## What's next + +- [WhatsApp Business](whatsapp-business.md) — react to WhatsApp Business Cloud webhook events +- [Google Chat](google-chat.md) — react to Google Chat interaction events +- [Connections](../supporting/connections.md) — reuse Telegram credentials across services +- [Telegram connector reference](../../../connectors/catalog/communication/telegram/overview.md) — full connector API reference +- [Telegram setup guide](../../../connectors/catalog/communication/telegram/setup-guide.md) — create a bot and configure the webhook diff --git a/en/docs/develop/integration-artifacts/event/whatsapp-business.md b/en/docs/develop/integration-artifacts/event/whatsapp-business.md new file mode 100644 index 0000000000..cdcbbf3008 --- /dev/null +++ b/en/docs/develop/integration-artifacts/event/whatsapp-business.md @@ -0,0 +1,275 @@ +--- +title: WhatsApp Business +description: React to WhatsApp Business Cloud webhook events, such as inbound messages, status updates, and account or template lifecycle changes, using pre-built event handlers. +keywords: [wso2 integrator, whatsapp business, whatsapp webhook, event integration, webhook listener, meta cloud api] +--- + +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# WhatsApp Business + +WhatsApp Business event integrations receive webhook notifications from the WhatsApp Business Cloud API and trigger handler functions as messages arrive and account, template, or phone number state changes occur. Use them to build chatbots, automate customer replies, and track message delivery and template quality in real time. + +:::note +The WhatsApp Business webhook listener must be reachable from the internet. For local development, use a tunneling tool such as [ngrok](https://ngrok.com) to create a public URL for your local port. In production, deploy the integration to a publicly accessible host. + +After starting the integration, configure the webhook in the **Meta App Dashboard** under **WhatsApp → Configuration → Webhooks**: set the **Callback URL** to your listener's public URL, set a **Verify token** matching `verifyToken`, and subscribe to the fields you want to receive. +::: + +## Creating a WhatsApp Business listener + + + + +1. Click **+ Add Artifact** in the canvas or click **+** next to **Entry Points** in the sidebar. +2. In the **Artifacts** panel, select **WhatsApp Business** under **Event Integration**. +3. In the creation form, fill in the following fields: + + + + | Field | Description | Default | + |---|---|---| + | **Listener Name** | Identifier for the listener created with this service. | `businessListener` | + | **Webhook Listener Port** | The port on which the webhook listener accepts incoming HTTP requests. | `8090` | + | **Verify Token** | The verification token configured in the Meta App dashboard. | Required | + | **App Secret** | The Meta app secret, used to verify inbound webhook notifications via `X-Hub-Signature-256`. | Required | + +4. Click **Create**. + +5. WSO2 Integrator opens the service in the **Service Designer**. The canvas shows the attached listener pill and an empty **Event Handlers** section. + +6. Click **+ Add Handler** to define how incoming events are processed. + + + + +```ballerina +import ballerinax/whatsapp.business as whatsapp; +import ballerina/log; + +configurable string verifyToken = ?; +configurable string appSecret = ?; +configurable int listenerPort = 8090; + +listener whatsapp:Listener whatsappListener = new (listenerPort, verifyToken = verifyToken, appSecret = appSecret); + +service whatsapp:WhatsAppService on whatsappListener { + + remote function onMessages(whatsapp:MessagesNotification notification) returns error? { + if notification is whatsapp:Messages { + foreach whatsapp:InboundMessage message in notification.messages { + log:printInfo("Inbound WhatsApp message received", sender = message.'from, messageId = message.messageId); + } + } else { + foreach whatsapp:MessageStatusUpdate statusUpdate in notification.statuses { + log:printInfo("WhatsApp message status update", messageId = statusUpdate.messageId, status = statusUpdate.status); + } + } + } + + remote function onAccountUpdate(whatsapp:AccountUpdate update) returns error? { + log:printInfo("WhatsApp account update received", wabaId = update.wabaId, event = update.event); + } +} +``` + +Save this as `main.bal` and run `bal run` from the project directory. Configure your Meta app's webhook callback URL to point at the listener and use the same `verifyToken` and `appSecret` values in the Meta App Dashboard. + + + + +## Service configuration + +Service configuration controls the service name and the WhatsApp Business listener it is attached to. + + + + +In the **Service Designer**, click **Configure** to open the **WhatsApp Business Configuration** panel. + +The panel shows a **Service Configuration** field for the service's acknowledgement behavior, and an **Attached Listeners** list. Pick a listener under **Attached Listeners** to configure its connection settings. + + + +### Service configuration + +| Field | Description | +|---|---| +| **Service Configuration** | Advanced acknowledgement settings as a `@business:ServiceConfig` record expression (e.g., `{ autoAck: false }`). Defaults to `autoAck: true`, acknowledging automatically. See [Manual acknowledgement](#manual-acknowledgement). | + +### Main configurations + +| Field | Description | +|---|---| +| **Name** | The name of the listener. Required. | +| **Listen To** | A port number to bind a new `http:Listener` to, or an existing `http:Listener`. Required. | +| **Verify Token** | The verification token configured in the Meta App dashboard. Required. | +| **App Secret** | The Meta app secret, used to verify inbound webhook notifications. Required. | + +Click **Attach Listener** to attach an additional listener to the same service. + +Click **Save Changes** to apply updates. + + + + +Service configuration maps to the `ListenerConfig` passed when constructing the listener. The `@business:ServiceConfig` annotation placed before the `service` declaration sets the acknowledgement mode: + +```ballerina +listener whatsapp:Listener businessListener = new ( + 8090, + verifyToken = verifyToken, + appSecret = appSecret +); + +@business:ServiceConfig { + autoAck: false +} +service on businessListener { + // handlers +} +``` + +`whatsapp:ListenerConfig` fields: + +| Field | Type | Default | Description | +|---|---|---|---| +| `verifyToken` | `string` | Required | The verification token configured in the Meta App dashboard. | +| `appSecret` | `string` | Required | The Meta app secret, used to verify the `X-Hub-Signature-256` header on inbound webhook notifications. | + +`@business:ServiceConfig` fields: + +| Field | Type | Default | Description | +|---|---|---|---| +| `autoAck` | `boolean` | `true` | When `false`, notifications must be manually acknowledged using `whatsapp:Caller`. See [Manual acknowledgement](#manual-acknowledgement). | + + + + +## Event handlers + +An event handler is a `remote function` that WSO2 Integrator calls for each WhatsApp Business Cloud webhook event received. + +### Adding an event handler + + + + +In the **Service Designer**, click **+ Add Handler**. A **Select Handler to Add** panel opens on the right listing the available event types. + + + +Pick **On Messages**, then click **Save**. This opens the **Flow Designer** for `onMessages`. + + + +Use the flow canvas to add integration steps such as database writes, HTTP calls, and transformations. Repeat these steps to add the other handlers you need. + + + + +**onMessages handler** — called for each inbound message or message status update: + +```ballerina +service whatsapp:WhatsAppService on businessListener { + + remote function onMessages(whatsapp:MessagesNotification notification) returns error? { + if notification is whatsapp:Messages { + foreach whatsapp:InboundMessage message in notification.messages { + log:printInfo("Inbound WhatsApp message received", sender = message.'from, messageId = message.messageId); + } + } else { + foreach whatsapp:MessageStatusUpdate statusUpdate in notification.statuses { + log:printInfo("WhatsApp message status update", messageId = statusUpdate.messageId, status = statusUpdate.status); + } + } + } +} +``` + +**onError handler** — called when a handler returns an error while being dispatched: + +```ballerina +service whatsapp:WhatsAppService on businessListener { + + remote function onError(whatsapp:HandlerError handlerError) returns error? { + log:printError("Failed to process a WhatsApp webhook event", + 'error = handlerError.'error, 'field = handlerError.'field); + } +} +``` + + + + +### Manual acknowledgement + +By default, the listener acknowledges (`200 OK`) each notification automatically, before any handler runs. Meta requires a fast `2xx` and retries otherwise, so this is the safe default for slow handlers. Annotate the service `@business:ServiceConfig {autoAck: false}` to decide exactly when a notification is acknowledged instead, and declare a handler's optional second parameter as a `whatsapp:Caller`: + +```ballerina +@business:ServiceConfig { + autoAck: false +} +service whatsapp:WhatsAppService on businessListener { + + remote function onMessages(whatsapp:MessagesNotification notification, whatsapp:Caller caller) returns error? { + check persistNotification(notification); + check caller->complete(); + } +} +``` + +If a handler declared with a `Caller` never calls `caller->complete()`, the listener never sends its own `200 OK` for that request, and Meta's own retry behavior treats the resulting failure the same as any other failed delivery. Meta may redeliver a notification if the acknowledgement is slow, dropped, or never sent, under either `autoAck` setting, not just `false`, so make handler processing idempotent, or deduplicate using `InboundMessage.messageId` (`wamid`) for messages, or `MessageStatusUpdate.messageId` plus `status` for status updates. + +### Handler types + +WhatsApp Business Cloud has ten webhook fields, one per `WhatsAppService` handler, plus an eleventh `onError` handler that fires whenever one of the ten returns an error while being dispatched. Declare only the handlers you need. A field whose handler you didn't declare is logged and dropped. + +| Handler | Webhook field | Triggered when | +|---|---|---| +| `onMessages` | `messages` | An inbound message or an outbound message status update arrives. Narrow the `MessagesNotification` parameter with `notification is Messages` to tell the two apart. | +| `onAccountReviewUpdate` | `account_review_update` | A WhatsApp Business Account (WABA) review decision changes. | +| `onAccountUpdate` | `account_update` | An account-lifecycle or compliance change occurs. | +| `onBusinessCapabilityUpdate` | `business_capability_update` | A WABA's messaging or phone-number capability limits change. | +| `onMessageTemplateQualityUpdate` | `message_template_quality_update` | A message template's quality score changes. | +| `onMessageTemplateStatusUpdate` | `message_template_status_update` | A message template's status changes (approved, rejected, disabled, and so on). | +| `onPhoneNumberNameUpdate` | `phone_number_name_update` | A phone number's display-name review outcome is available. | +| `onPhoneNumberQualityUpdate` | `phone_number_quality_update` | A phone number's messaging throughput or quality tier changes. | +| `onSecurity` | `security` | A two-step-verification PIN change or reset request occurs for a phone number. | +| `onTemplateCategoryUpdate` | `template_category_update` | A message template's category changes, or is about to change. | +| `onError` | N/A | Any of the handlers above returned an error while being dispatched. | + +## What's next + +- [Telegram](telegram.md) — react to Telegram Bot API webhook updates +- [Google Chat](google-chat.md) — react to Google Chat interaction events +- [Connections](../supporting/connections.md) — reuse WhatsApp Business credentials across services +- [WhatsApp Business connector reference](../../../connectors/catalog/communication/whatsapp-business/overview.md) — full connector API reference +- [WhatsApp Business setup guide](../../../connectors/catalog/communication/whatsapp-business/setup-guide.md) — create a Meta app and configure the webhook diff --git a/en/sidebars.ts b/en/sidebars.ts index 2f10bf2f64..93c4c0fe7c 100644 --- a/en/sidebars.ts +++ b/en/sidebars.ts @@ -165,6 +165,9 @@ const sidebars: SidebarsConfig = { 'develop/integration-artifacts/event/solace', 'develop/integration-artifacts/event/cdc-mssql', 'develop/integration-artifacts/event/cdc-postgresql', + 'develop/integration-artifacts/event/whatsapp-business', + 'develop/integration-artifacts/event/telegram', + 'develop/integration-artifacts/event/google-chat', ], }, { @@ -731,6 +734,17 @@ const sidebars: SidebarsConfig = { 'connectors/catalog/productivity-collaboration/googleapis.calendar/example', ], }, + { + type: 'category', + label: 'Google Chat', + link: { type: 'doc', id: 'connectors/catalog/communication/google-chat/overview' }, + items: [ + 'connectors/catalog/communication/google-chat/setup-guide', + 'connectors/catalog/communication/google-chat/actions', + 'connectors/catalog/communication/google-chat/triggers', + 'connectors/catalog/communication/google-chat/example', + ], + }, { type: 'category', label: 'Google Cloud Pub/Sub', @@ -1922,6 +1936,17 @@ const sidebars: SidebarsConfig = { 'connectors/catalog/built-in/tcp/example', ], }, + { + type: 'category', + label: 'Telegram', + link: { type: 'doc', id: 'connectors/catalog/communication/telegram/overview' }, + items: [ + 'connectors/catalog/communication/telegram/setup-guide', + 'connectors/catalog/communication/telegram/actions', + 'connectors/catalog/communication/telegram/triggers', + 'connectors/catalog/communication/telegram/example', + ], + }, { type: 'category', label: 'Trello', @@ -1996,6 +2021,17 @@ const sidebars: SidebarsConfig = { 'connectors/catalog/built-in/websub/example', ], }, + { + type: 'category', + label: 'WhatsApp Business', + link: { type: 'doc', id: 'connectors/catalog/communication/whatsapp-business/overview' }, + items: [ + 'connectors/catalog/communication/whatsapp-business/setup-guide', + 'connectors/catalog/communication/whatsapp-business/actions', + 'connectors/catalog/communication/whatsapp-business/triggers', + 'connectors/catalog/communication/whatsapp-business/example', + ], + }, { type: 'category', label: 'Zoom Meetings', diff --git a/en/static/img/connectors/catalog/communication/google-chat/integration-overview.png b/en/static/img/connectors/catalog/communication/google-chat/integration-overview.png new file mode 100644 index 0000000000..ab44039e7f Binary files /dev/null and b/en/static/img/connectors/catalog/communication/google-chat/integration-overview.png differ diff --git a/en/static/img/connectors/catalog/communication/google-chat/service-designer.png b/en/static/img/connectors/catalog/communication/google-chat/service-designer.png new file mode 100644 index 0000000000..94250a88e7 Binary files /dev/null and b/en/static/img/connectors/catalog/communication/google-chat/service-designer.png differ diff --git a/en/static/img/connectors/catalog/communication/telegram/integration-overview.png b/en/static/img/connectors/catalog/communication/telegram/integration-overview.png new file mode 100644 index 0000000000..de8bdbb7ce Binary files /dev/null and b/en/static/img/connectors/catalog/communication/telegram/integration-overview.png differ diff --git a/en/static/img/connectors/catalog/communication/telegram/service-designer.png b/en/static/img/connectors/catalog/communication/telegram/service-designer.png new file mode 100644 index 0000000000..a0debf4668 Binary files /dev/null and b/en/static/img/connectors/catalog/communication/telegram/service-designer.png differ diff --git a/en/static/img/connectors/catalog/communication/whatsapp-business/integration-overview.png b/en/static/img/connectors/catalog/communication/whatsapp-business/integration-overview.png new file mode 100644 index 0000000000..c2bbf76dae Binary files /dev/null and b/en/static/img/connectors/catalog/communication/whatsapp-business/integration-overview.png differ diff --git a/en/static/img/connectors/catalog/communication/whatsapp-business/service-designer.png b/en/static/img/connectors/catalog/communication/whatsapp-business/service-designer.png new file mode 100644 index 0000000000..45940061bd Binary files /dev/null and b/en/static/img/connectors/catalog/communication/whatsapp-business/service-designer.png differ diff --git a/en/static/img/develop/integration-artifacts/event/google-chat/service-config.png b/en/static/img/develop/integration-artifacts/event/google-chat/service-config.png new file mode 100644 index 0000000000..1feb26b680 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/google-chat/service-config.png differ diff --git a/en/static/img/develop/integration-artifacts/event/google-chat/step-onmessage-flow.png b/en/static/img/develop/integration-artifacts/event/google-chat/step-onmessage-flow.png new file mode 100644 index 0000000000..592c18cca2 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/google-chat/step-onmessage-flow.png differ diff --git a/en/static/img/develop/integration-artifacts/event/google-chat/step-service-designer.png b/en/static/img/develop/integration-artifacts/event/google-chat/step-service-designer.png new file mode 100644 index 0000000000..bc2d5b906d Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/google-chat/step-service-designer.png differ diff --git a/en/static/img/develop/integration-artifacts/event/google-chat/step-service-form.png b/en/static/img/develop/integration-artifacts/event/google-chat/step-service-form.png new file mode 100644 index 0000000000..89ef84cbc0 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/google-chat/step-service-form.png differ diff --git a/en/static/img/develop/integration-artifacts/event/telegram/service-config.png b/en/static/img/develop/integration-artifacts/event/telegram/service-config.png new file mode 100644 index 0000000000..0daf99bd7b Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/telegram/service-config.png differ diff --git a/en/static/img/develop/integration-artifacts/event/telegram/step-onmessage-flow.png b/en/static/img/develop/integration-artifacts/event/telegram/step-onmessage-flow.png new file mode 100644 index 0000000000..46179cf47a Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/telegram/step-onmessage-flow.png differ diff --git a/en/static/img/develop/integration-artifacts/event/telegram/step-service-designer.png b/en/static/img/develop/integration-artifacts/event/telegram/step-service-designer.png new file mode 100644 index 0000000000..5467f3f6b5 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/telegram/step-service-designer.png differ diff --git a/en/static/img/develop/integration-artifacts/event/telegram/step-service-form.png b/en/static/img/develop/integration-artifacts/event/telegram/step-service-form.png new file mode 100644 index 0000000000..a98698a0b6 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/telegram/step-service-form.png differ diff --git a/en/static/img/develop/integration-artifacts/event/whatsapp-business/service-config.png b/en/static/img/develop/integration-artifacts/event/whatsapp-business/service-config.png new file mode 100644 index 0000000000..bd6cf2af53 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/whatsapp-business/service-config.png differ diff --git a/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-onmessages-flow.png b/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-onmessages-flow.png new file mode 100644 index 0000000000..8a96e116d2 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-onmessages-flow.png differ diff --git a/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-service-designer.png b/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-service-designer.png new file mode 100644 index 0000000000..85d216f007 Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-service-designer.png differ diff --git a/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-service-form.png b/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-service-form.png new file mode 100644 index 0000000000..c9ac8c62fe Binary files /dev/null and b/en/static/img/develop/integration-artifacts/event/whatsapp-business/step-service-form.png differ