Skip to content

BOT Application Backend Development

1.1 Bot Application and Bot Application Server Interaction Flowchart

Bot Application and Bot Application Server Interaction Flowchart

1.2 MosApp's Bot Session Sends Messages to Bot Application Server

After users send messages to Bot applications, MosApp will transparently forward the messages to the Bot application server through the webhook configured by the Bot application server.

1.2.1 Message Example

java
{
    "reqId": "4E3FC140-73E6-4E18-BD98-99E19A4496B9",
    "botAppId": "696290824258245",
    "eventType": "MESSAGE",
    "conversation": {
        "conversationId": "696638485037509"
    },
    "message": {
        "messageId": "1950519940851331072",
        "text": "/start",
        "timestamp": 1753875167470,
        "type": -1
    },
    "miniApp": {
        "openid": "47179fef0cff7ab186e2bcc3ac46790c"
    },
    "sender": {
        "nickname": "mosUser"
    },
    "timestamp": 1753875167470
}

1.2.2 Webhook Parameter Description

PropertyTypeRequiredDescription
reqIdStringYesRequest id, unique for each request
eventTypeStringYesEvent type: message/callbackQuery
timestampLongYesEvent occurrence timestamp
botAppIdStringYesbot application id
senderSender info object
--nicknameStringYesUser nickname
conversationConversation info object
--conversationIdStringYesConversation id
messageUser sent message content (Message object), only exists when eventType is message
--messageIdStringNoMessage id of user sent message
--textStringNoSent text
--timestampLongNoMessage send time
callbackQueryInline button click event details (CallbackQuery object), only exists when eventType is callback_query
--dataStringNoCallback data (usually defined by developer)
--inlineMessageIdStringNoInline message id
miniAppMini-app info object, has value when BotApp is bound to mini-app
--openidStringNoUser's openid in corresponding mini-app

1.2.3 Java Usage Example

Request Parameters

java
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * Click inline button parameters
 * @author admin001
 * @date 2025/7/8 19:55
 */
@Data
@Accessors(chain = true)
public class WebhookParam implements Serializable {

    private static final long serialVersionUID = 6142195922243220516L;

    /**
     * Request id, unique for each request
     */
    private String reqId;

    /**
     * Event type: message/callbackQuery
     */
    private String eventType;

    /**
     * Event occurrence timestamp
     */
    private Long timestamp;

    /**
     * botApp id
     */
    private String botAppId;

    /**
     * Sender information, represents the user who initiated the event.
     */
    private UserInfo sender;

    /**
     * Conversation information
     */
    private Conversation conversation;

    /**
     * User sent message content (Message object), only exists when eventType is message
     */
    private Message message;

    /**
     * Inline button click event details (CallbackQuery object), only exists when eventType is callback_query.
     */
    private CallbackQuery callbackQuery;

    /**
     * Mini-app information, has value when BotApp is bound to mini-app
     */
    private MiniApp miniApp;

    // Nested class: User information
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Data
    public static class UserInfo implements Serializable {
        private static final long serialVersionUID = 1L;

        /**
         * User nickname
         */
        private String nickname;
    }

    // Nested class: Conversation information
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Data
    public static class Conversation implements Serializable {
        private static final long serialVersionUID = 1L;

        /**
         * Conversation id
         */
        private String conversationId;
    }

    // Nested class: Message information
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Data
    public static class Message implements Serializable {
        private static final long serialVersionUID = 1L;

        /**
         * Message id of user sent message
         */
        private String messageId;
        /**
         * Sent text
         */
        private String text;
        /**
         * Message send time
         */
        private Long timestamp;

    }

    // Nested class: Callback query (inline button click)
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Data
    public static class CallbackQuery implements Serializable {
        private static final long serialVersionUID = 1L;

        /**
         * Callback data (usually defined by developer)
         */
        private String data;
        /**
         * Inline message id
         */
        private String inlineMessageId;

    }

    // Nested class: Mini-app information
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Data
    public static class MiniApp implements Serializable {
        private static final long serialVersionUID = 1L;

        /**
         * User's openid in corresponding mini-app
         */
        private String openid;

    }

}

Controller Class

java
@RestController
@RequestMapping("/botApp")
@Slf4j
public class BotAppController {

    @PostMapping("/webhook")
    public Void miniAppLogin(@RequestBody WebhookParam webhookParam) {
        log.info("webhookParam: {}", webhookParam);
        // other code
    }
}

1.3 Bot Application Server Sends Messages to MosApp's Bot Session

1.3.1 MosApp Open API Address

https://mos-test.mos.me/open-apis/botApp/v1/sendMessage/{token}

The {token} in the address is a unique string generated after application creation, which can be viewed and copied in the Bot application details page; please keep it secure and do not leak it.

MosApp Open API Address

1.3.2 Message Example

java
{
    "reqId": "2530D143-2C3E-4970-B4AE-8216CEA5653A",
    "conversationId": "696638485037509",
    "inlineMessageId": "",
    "editCurrent": false,
    "text": "Hello Bot app",
    "inlineButtons": [
        [
            {
                "data": "callbackData",
                "text": "callback Button",
                "type": "callback"
            }
        ],
        [
            {
                "data": "https://www.google.com/",
                "text": "Google",
                "type": "url"
            }
        ],
        [
            {
                "data": "",
                "text": "Mini app",
                "type": "miniApp"
            }
        ]
    ]
}

1.3.3 Parameter Description

PropertyTypeRequiredDescription
reqIdStringYesRequest id, unique for each request
conversationIdStringYesConversation id
editCurrentBooleanYesWhether to edit single message
inlineMessageIdStringYesInline message id
textStringYesMessage content
inlineButtonsList<List<InlineButton>>inline button list object
--textStringYesButton text
--typeStringYesButton type (callback/url/miniApp) See "Inline Button Type Description" below for details
--dataStringYesButton key value See "Inline Button Key Value Description" below for details

Inline Button Type Description:

  1. callback (Callback type): When a user clicks, it triggers a message with eventType=callbackQuery, sent to the Bot application server via webhook; this message will not be displayed in the conversation, only passing the button's data value to the Bot application server.
  2. url (Jump link type): When clicked, it opens a browser to display the specified link; if it's an internal link (such as user, group, channel links), it will open within MosApp.
  3. miniApp (Mini-app jump type): If the Bot application is bound to a mini-app, users will directly enter the bound mini-app when clicked.

Inline Button Key Value Description:

  1. When the type is callback, data is a string configured by the Bot application server; when users click the button, MosApp will transparently pass this data content to the Bot application server, making it convenient for the Bot application server to distinguish which specific button the user clicked.
  2. When the type is url, data is an external URL link or MosApp internal link configured by the Bot application server.
  3. When the type is miniApp, data can configure page path information; when opening the mini-app, this path will be carried as a parameter in the URL.

1.3.4 Java Usage Example

Request Parameters

java
package com.wecloud.modules.openapi.bot.ao;

import lombok.Data;

import java.util.List;

@Data
public class BotAppMessageAo {

    /**
     * Request id
     */
    private String reqId;

    /**
     * Conversation id
     */
    private String conversationId;

    /**
     * Message content
     */
    private String text;

    /**
     * Inline buttons 2D array
     */
    private List<List<InlineButton>> inlineButtons;

    /**
     * Whether to edit single message
     */
    private Boolean editCurrent;

    /**
     * Inline message id
     */
    private String inlineMessageId;

    @Data
    public static class InlineButton {
        /**
         * Button text
         */
        private String text;
    
        /**
         * Button type
         */
        private String type;
    
        /**
         * Button key value, used by developer to distinguish which button
         */
        private String data;
    }
}

Process User Messages and Respond

java
@RestController
@RequestMapping("/botApp")
@Slf4j
public class BotAppController {

    // bot app token
    private static final String token = "bot app token";
    // open api base Request
    private static final String openApiBaseUrl = "https://mos.mos.me/open-apis/botApp/v1/sendMessage/";

    // event type
    private static final String EVENT_TYPE_CALLBACK_QUERY = "CALLBACK_QUERY";
    private static final String EVENT_TYPE_MESSAGE = "MESSAGE";

    @Autowired
    private RestTemplate restTemplate;

    @PostMapping("/webhook")
    public Void miniAppLogin(@RequestBody WebhookParam webhookParam) {
        log.info("webhookParam: {}", webhookParam);
        BotAppMessageAo botAppMessageAo = new BotAppMessageAo();
        WebhookParam.Conversation conversation = webhookParam.getConversation();
        botAppMessageAo.setReqId(webhookParam.getReqId());
        botAppMessageAo.setConversationId(conversation.getConversationId());
        // Inline button request
        if (EVENT_TYPE_CALLBACK_QUERY.equals(webhookParam.getEventType())) {
            WebhookParam.CallbackQuery callbackQuery = webhookParam.getCallbackQuery();
            String data = callbackQuery.getData();
            botAppMessageAo.setText(data);
            // Inline buttons
            List<List<InlineButton>> inlineButtons = new ArrayList<>();
            // First row
            List<InlineButton> inlineButtonRow1 = new ArrayList<>();
            InlineButton row1Column1 = new InlineButton();
            row1Column1.setText("row1Column1");
            row1Column1.setType("callback");
            row1Column1.setData("row1Column1");
            inlineButtonRow1.add(row1Column1);
            InlineButton row2Column2 = new InlineButton();
            row2Column2.setText("row2Column2");
            row2Column2.setType("callback");
            row2Column2.setData("row2Column2");
            inlineButtonRow1.add(row2Column2);
            inlineButtons.add(inlineButtonRow1);
            // Second row
            List<InlineButton> inlineButtonRow2 = new ArrayList<>();
            InlineButton row2Column1 = new InlineButton();
            row2Column1.setText("row2Column1");
            row2Column1.setType("url");
            row2Column1.setData("https://www.google.com/");
            inlineButtonRow2.add(row2Column1);
            inlineButtons.add(inlineButtonRow2);
            // Third row
            List<InlineButton> inlineButtonRow3 = new ArrayList<>();
            InlineButton row3Column1 = new InlineButton();
            row3Column1.setText("row3Column1");
            row3Column1.setType("miniApp");
            row3Column1.setData("https://www.google.com/");
            inlineButtonRow3.add(row3Column1);
            inlineButtons.add(inlineButtonRow3);
            botAppMessageAo.setInlineButtons(inlineButtons);
            botAppMessageAo.setEditCurrent(true);
            botAppMessageAo.setInlineMessageId(callbackQuery.getInlineMessageId());
        }
        // Message request
        else if (EVENT_TYPE_MESSAGE.equals(webhookParam.getEventType())) {
            WebhookParam.Message message = webhookParam.getMessage();
            String data = message.getText();
            if ("/start".equals(data)) {
                botAppMessageAo.setText(data);
                // Inline buttons
                List<List<InlineButton>> inlineButtons = new ArrayList<>();
                // First row
                List<InlineButton> inlineButtonRow1 = new ArrayList<>();
                InlineButton row1Column1 = new InlineButton();
                row1Column1.setText("row1Column1");
                row1Column1.setType("callback");
                row1Column1.setData("row1Column1");
                inlineButtonRow1.add(row1Column1);
                InlineButton row2Column2 = new InlineButton();
                row2Column2.setText("row2Column2");
                row2Column2.setType("callback");
                row2Column2.setData("row2Column2");
                inlineButtonRow1.add(row2Column2);
                inlineButtons.add(inlineButtonRow1);
                // Second row
                List<InlineButton> inlineButtonRow2 = new ArrayList<>();
                InlineButton row2Column1 = new InlineButton();
                row2Column1.setText("row2Column1");
                row2Column1.setType("url");
                row2Column1.setData("https://www.google.com/");
                inlineButtonRow2.add(row2Column1);
                inlineButtons.add(inlineButtonRow2);
                // Third row
                List<InlineButton> inlineButtonRow3 = new ArrayList<>();
                InlineButton row3Column1 = new InlineButton();
                row3Column1.setText("row3Column1");
                row3Column1.setType("miniApp");
                row3Column1.setData("https://www.google.com/");
                inlineButtonRow3.add(row3Column1);
                inlineButtons.add(inlineButtonRow3);
        
                botAppMessageAo.setInlineButtons(inlineButtons);
                botAppMessageAo.setEditCurrent(false);
                botAppMessageAo.setInlineMessageId("");
            } else {
                botAppMessageAo.setText(data);
                botAppMessageAo.setEditCurrent(false);
                botAppMessageAo.setInlineMessageId("");
            }
        }
        try {
            String url = openApiBaseUrl + token;
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<BotAppMessageAo> requestEntity = new HttpEntity<>(botAppMessageAo, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
        } catch (Exception e) {
            log.error("error ", e);
        }
    }
}