{"id":27689430,"url":"https://github.com/sendbird/sendbird-chat-sdk-windows","last_synced_at":"2025-04-25T10:06:34.982Z","repository":{"id":56142574,"uuid":"107801841","full_name":"sendbird/sendbird-chat-sdk-windows","owner":"sendbird","description":"Sendbird Chat SDK for Windows for enablement of a rich, engaging, scalable, and real-time chat service.","archived":false,"fork":false,"pushed_at":"2023-07-04T07:10:27.000Z","size":182475,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-04-25T10:06:29.451Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sendbird.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-21T17:40:23.000Z","updated_at":"2023-07-04T05:24:35.000Z","dependencies_parsed_at":"2022-08-15T13:31:00.457Z","dependency_job_id":null,"html_url":"https://github.com/sendbird/sendbird-chat-sdk-windows","commit_stats":null,"previous_names":["sendbird/sendbird-chat-sdk-windows"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-chat-sdk-windows","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-chat-sdk-windows/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-chat-sdk-windows/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-chat-sdk-windows/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sendbird","download_url":"https://codeload.github.com/sendbird/sendbird-chat-sdk-windows/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250796310,"owners_count":21488706,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-25T10:06:33.596Z","updated_at":"2025-04-25T10:06:34.943Z","avatar_url":"https://github.com/sendbird.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [Sendbird](https://sendbird.com) Chat SDK for Windows\n\n![Platform](https://img.shields.io/badge/platform-Windows-blue.svg)\n![Language](https://img.shields.io/badge/language-C%2B%2B-red.svg)\n[![Commercial License](https://img.shields.io/badge/license-Commercial-brightgreen.svg)](https://github.com/sendbird/SendBird-Windows/blob/master/LICENSE.md)\n![Status](https://img.shields.io/badge/status-beta-yellowgreen.svg)\n\n## Table of contents\n\n  1. [Introduction](#introduction)\n  1. [Before getting started](#before-getting-started)\n  1. [Getting started](#getting-started)\n  1. [Sending your first message](#sending-your-first-message)\n  1. [Authentication](#authentication)\n  1. [Channel types](#channel-types)\n  1. [Open channel](#open-channel-1)\n  1. [Open channel: Advanced](#open-channel-advanced)\n  1. [Group channel](#group-channel-1)\n  1. [Group channel: Advanced](#group-channel-advanced)\n  1. [Channel metadata \u0026 metacounter](#channel-metadata--metacounter)\n  1. [Event handler](#event-handler)  \n \n\u003cbr /\u003e\n\n## Introduction\n\nThrough Chat SDK for Windows, you can efficiently integrate real-time chat into your client app. On the client-side implementation, you can initialize, configure and build the chat with minimal effort. On the server-side, Sendbird ensures reliable infra-management services for your chat within the app. This read.me provides the Chat SDK’s structure, supplementary features, and the installation steps. \n\n### How it works\n\nIt is simple to implement chat in your client app with the Chat SDK: a **user** logs in, sees a **list of channels**, selects or creates an **open channel** or a **group channel**, and, through the use of the **channel event handlers**, sends **messages** to the channel, while also receiving **them from other users** within the channel. \n\n### More about Sendbird Chat SDK for Windows\n\nIf you have any comments or questions regarding bugs and feature requests, visit [Sendbird community](https://community.sendbird.com). \n\n\u003cbr /\u003e\n\n## Before getting started\n\nThis section shows you the prerequisites you need to know for using Sendbird Chat SDK for Windows.\n\n### Requirements\n\n- Windows 7 (32-bit/64-bit) or higher\n- C++03 or higher\n\n\u003cbr /\u003e\n\n## Getting started\n\n### Try the sample app\n\nThe fastest way to test the Chat SDK is to build your chat app on top of our sample app. To create a project for the sample app, download the app from our GitHub repository. The link is down below. \n\n-  https://github.com/sendbird/SendBird-Windows\n\n### Step 1: Create a Sendbird application from your dashboard\n\nThe first thing you need to do is to log in to the [Sendbird Dashboard](https://dashboard.sendbird.com/auth/signin) and create a Sendbird application. If you do not yet have an account, you can log in with Google, GitHub, or create a new account.\n\nYou should create one application per service, regardless of the platform. For example, an app released in Android, iOS, and Windows would require only one application to be created on the Sendbird Dashboard.\n\nAll users within the same Sendbird application are able to communicate with each other, across all platforms. This means users using iOS, Android, web clients, Windows, etc. can all chat with one another. However, users in different Sendbird applications cannot talk to each other.\n\n\n### Step 2: Build a new app or integrate Sendbird with an existing app\n\n#### 2-1. Dependencies\n\nThis library doesn't have any dependencies. However, if you use boost and `OpenSSL` and there is a symbol conflict with them, contact [Technical Support\n](https://help.sendbird.com/hc/en-us/requests/new).\n\n#### 2-2. Set Visual Studio Project\n\n- Set the directory of header files for Sendbird to **Project Properties** \u003e **Configuration Properties** \u003e **VC++ Directories** \u003e **Include Directories**.\n- Set the directory of `Sendbird.lib` to **Project Properties** \u003e **Configuration Properties** \u003e **VC++ Directories** \u003e **Library Directories**.\n- Set `Sendbird.lib` to **Project Properties** \u003e **Configuration Properties** \u003e **Linker** \u003e **Input** \u003e **Additional Dependencies**.\n- ***Every string parameter and variable is `wstring` type and its value is unicode***.\n\n\u003cbr /\u003e \n\n## Sending your first message\n\n### Step 1: Initialize with APP_ID\n\nTo use the chat features, you must initialize using the `APP_ID` assigned to your Sendbird application. It is recommended that code for initialization be implemented in the user login view controller.\n\n```cpp\n#include \u003cSendbird.h\u003e\n\nSBDMain::Init(APP_ID);\n```\n\n### Step 2: Connect to Sendbird server\n\n#### A. Using a unique user ID\n\nConnect a user to Sendbird server by using a unique user ID or with a user ID and an access token. To connect to Sendbird server, a user is required to log in with a unique ID. A new user can authenticate with any untaken user ID, which gets automatically registered to Sendbird system. An existing ID can log in directly. The ID must be unique within a Sendbird application to be distinguished from others, such as an email address or a UID from your database.\n\nThis simple authentication procedure might be useful when you are in development or if your service does not require additional security.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdConnectHandler : public SBDConnectInterface {\npublic:\n    SendBirdConnectHandler() {\n    }\n    \n    ~SendBirdConnectHandler() {\n    }\n    \n    void CompletionHandler(SBDUser user, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Connected to Sendbird server.\n    }\n};\n\nvoid Connect() {\n    SendBirdConnectHandler *handler = new SendBirdConnectHandler(); // `handler` has to be deallocated later.\n    SBDMain::Connect(USER_ID, SBD_NULL_WSTRING, handler);\n}\n```\n\n#### B. Using a unique user ID and access token\n\nWith Sendbird [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/user#2-create-a-user), you can create a new user with an access token, or you can issue an access token for an existing user. Once an access token is issued, you are required to provide the user's token in the log in method.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdConnectHandler : public SBDConnectInterface {\npublic:\n    SendBirdConnectHandler() {\n    }\n    \n    ~SendBirdConnectHandler() {\n    }\n    \n    void CompletionHandler(SBDUser user, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Connected to SendBird.\n    }\n};\n\nvoid Connect() {\n    SendBirdConnectHandler *handler = new SendBirdConnectHandler(); // `handler` has to be deallocated later.\n    SBDMain::Connect(USER_ID, ACCESS_TOKEN, handler);\n}\n```\n\n**- Tips for user account security**\n\nFrom **Settings** \u003e **Application** \u003e **Security** \u003e **Access token permission** setting in your dashboard, you can prevent users without an access token from logging in to your Sendbird application or restrict their access to **read** and **write** messages.\n\n### Step 3: Create a new open channel\n\nCreate an [open channel](#open-channel-1). Once created, all users in your Sendbird application can easily participate in the channel. Similarly, you can create a [group channel](#group-channel-1) by inviting users as new members to the channel.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateOpenChannelHandler : public SBDCreateOpenChannelInterface {\npublic:\n    SendBirdCreateOpenChannelHandler() {\n    }\n    \n    ~SendBirdCreateOpenChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The open channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateOpenChannel() {\n    SendBirdCreateOpenChannelHandler *handler = new SendBirdCreateOpenChannelHandler(); // `handler` has to be deallocated later.\n    \n    // Every wstring type parameter is an option. If you don't have to set them, set `SBD_NULL_WSTRING`.\n    // OPERATOR_USER_IDS is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    SBDOpenChannel::CreateChannel(CHANNEL_NAME, CHANNEL_URL, COVER_URL, DATA, OPERATOR_USER_IDS, CUSTOM_TYPE, handler);\n}\n```\n\n### Step 4: Enter the channel\n\nEnter the channel to send and receive messages.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdEnterOpenChannelHandler : public SBDEnterOpenChannelInterface {\npublic:\n    SendBirdEnterOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdEnterOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid EnterOpenChannel() {\n    SendBirdEnterOpenChannelHandler *handler = new SendBirdEnterOpenChannelHandler();   // `handler` has to be deallocated later.\n    open_channel-\u003eEnter(handler);\n}\n```\n\n### Step 5: Send a message to the channel\n\nFinally, send a message to the channel. There are two types: a user message, which is a plain text, and a file message, which is a binary file, such as an image or PDF, which can be also sent through the [dashboard](https://dashboard.sendbird.com/auth/signin) or [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/messages#2-send-a-message).\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendUserMessageHandler : public SBDSendUserMessageInterface {\npublic:\n    SendBirdSendUserMessageHandler() {\n    }\n    \n    ~SendBirdSendUserMessageHandler() {\n    }\n    \n    void CompletionHandler(SBDUserMessage *user_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `user_message`.\n    }\n};\n\nvoid SendUserMessage() {\n    SendBirdSendUserMessageHandler *handler = new SendBirdSendUserMessageHandler(); // `handler` has to be deallocated later.\n    \n    // `SendUserMessage()` belongs to `SBDBaseChannel` class, so it can be used by `SBDOpenChannel` and `SBDGroupChannel` instance.\n    // TARGET_LANGUAGES is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    channel-\u003eSendUserMessage(MESSAGE_TEXT, DATA, CUSTOM_TYPE, TARGET_LANGUAGES, handler);\n}\n```\n\n\u003cbr /\u003e\n\n## Authentication\n\n### Initialize with APP_ID\n\nTo use the chat features, you must initialize using the `APP_ID` assigned to your Sendbird application. It is recommended that code for initialization be implemented in the user login view controller.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDMain::Init(APP_ID);\n```\n\n### Connect with a user ID\n\nConnect a user to Sendbird server by using a unique user ID or with a user ID and an access token. To connect to Sendbird server, a user is required to log in with a unique ID. A new user can authenticate with any untaken user ID, which gets automatically registered to Sendbird system. An existing ID can log in directly. The ID must be unique within a Sendbird application to be distinguished from others, such as an email address or a UID from your database.\n\nThis simple authentication procedure might be useful when you are in development or if your service does not require additional security.\n\nFind out more about Sendbird's usage of Handlers and callbacks in the [Event handler](#event-handler) section.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdConnectHandler : public SBDConnectInterface {\npublic:\n    SendBirdConnectHandler() {\n    }\n    \n    ~SendBirdConnectHandler() {\n    }\n    \n    void CompletionHandler(SBDUser user, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Connected to Sendbird.\n    }\n};\n\nvoid Connect() {\n    SendBirdConnectHandler *handler = new SendBirdConnectHandler(); // `handler` has to be deallocated later.\n    SBDMain::Connect(USER_ID, SBD_NULL_WSTRING, handler);\n}\n```\n\n### Connect with a user ID and access token\n\nWith Sendbird [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/getting-started/prepare-to-use-api), you can create a new user with an access token, or you can issue an access token for an existing user. Once an access token is issued, you are required to provide the user's token in the log in method.\n\n1. Using Chat Platform API, create a Sendbird user account with the information submitted when a user signs up or signs in to your service.\n2. Save the user ID along with the issued access token to your securely managed persistent storage. \n3. When a user attempts to log in to the application, load the user ID and access token from the storage, and then pass them to Sendbird login method. \n4. Periodically replacing the user's access token is recommended for account security.\n\n#### - Tips for user account security\n\nFrom **Settings** \u003e **Application** \u003e **Security** \u003e **Access token permission** setting in your dashboard, you can prevent users without an access token from logging in to your Sendbird application or restrict their access to **read** and **write** messages.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdConnectHandler : public SBDConnectInterface {\npublic:\n    SendBirdConnectHandler() {\n    \n    }\n    \n    ~SendBirdConnectHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDUser user, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Connected to Sendbird.\n    }\n};\n\nvoid Connect() {\n    SendBirdConnectHandler *handler = new SendBirdConnectHandler(); // `handler` has to be deallocated later.\n    SBDMain::Connect(USER_ID, ACCESS_TOKEN, handler);\n}\n```\n\n### Disconnect from Sendbird server\n\nYou should disconnect from Sendbird when your user no longer needs to receive messages from an online state.\n\nDisconnecting removes all registered handlers and callbacks. That is, it removes all [event handlers](#event-handler) added through `AddReconnectionHandler()` or `AddChannelHandler()` of `SBDMain`. It also flushes all internally cached data, such as the channels that are cached when `GetChannel()` of `SBDOpenChannel` or `GetChannel()` of `SBDGroupChannel` is called.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdDisconnectHandler : public SBDDisconnectInterface {\npublic:\n    SendBirdDisconnectHandler() {\n    \n    }\n    \n    ~SendBirdDisconnectHandler() {\n    \n    }\n    \n    void CompletionHandler() {\n    \n    }\n};\n\nvoid Disconnect() {\n    SendBirdDisconnectHandler *handler = new SendBirdDisconnectHandler();   // `handler` has to be deallocated later.\n    SBDMain::Disconnect(handler);\n}\n```\n\n### Update user profile\n\nBy calling the `UpdateCurrentUserInfo()`, you can update a user's nickname, and the user’s profile picture with a URL.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdUpdateUserInfoHandler : public SBDUpdateUserInfoInterface {\npublic:\n    SendBirdUpdateUserInfoHandler() {\n    \n    }\n    \n    ~SendBirdUpdateUserInfoHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid UpdateCurrentUserInfo() {\n    SendBirdUpdateUserInfoHandler *handler = new SendBirdUpdateUserInfoHandler(); // `handler` has to be deallocated later.\n    SBDMain::UpdateCurrentUserInfo(NEW_NICKNAME, NEW_PROFILE_IMAGE_URL, handler);\n}\n```\n\nOr, you can pass in an image file directly.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdUpdateUserInfoHandler : public SBDUpdateUserInfoInterface {\npublic:\n    SendBirdUpdateUserInfoHandler() {\n    \n    }\n    \n    ~SendBirdUpdateUserInfoHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid UpdateCurrentUserInfo() {\n    SendBirdUpdateUserInfoHandler *handler = new SendBirdUpdateUserInfoHandler(); // `handler` has to be deallocated later.\n    SBDMain::UpdateCurrentUserInfoWithBinaryProfileImage(NEW_NICKNAME, NEW_PROFILE_IMAGE_FILE_PATH, FILE_MIME_TYPE, handler);\n}\n```\n\n\u003cbr /\u003e\n\n## Channel types\n\nYou should understand the following terminology before proceeding with the rest of this guide.\n\n### Open channel\n\nAn **open channel** is a Twitch-style public chat. In this channel type, anyone can enter and participate without permission. A single channel can handle thousands of simultaneous users.\n\n### Group channel\n\nA **group channel** is a private chat. A user may join the chat only through an invitation by another user who is already a member of the chat.\n\n- **Distinct property**: The **distinct** property allows a channel to be reused for the same members of a group chat. The **distinct** property automatically gets set to false when a new member is invited, or when a member leaves the channel. \n- **1-on-1 messaging**: 1-on-1 messaging is a Twitter’s direct message style  private channel between two users. A distinct property enabled channel can be reused for the same members.\n- **Group messaging**: Group messaging is a WhatsApp-style closed group private channel among multiple users. Up to a few hundred members can be invited to a group channel. \n\n#### Open channel vs. Group channel\n\n|Type|Open channel|Group channel|\n| --- | --- | --- |\n| Access control | Public | Invitation required |\n| Class name | OpenChannel | GroupChannel |\n| Number in a channel | 1,000 participants | 100 members |\n| How to create | SendBird Dashboard / Platform API / Client SDK | Client SDK / Platform API|\n| Operators | Supported | N/A |\n| User ban | Supported | N/A |\n| User mute | Supported | N/A |\n| Freeze channel | Supported | N/A |\n| Push notifications| N/A | Supported |\n| Unread counts | N/A | Supported |\n| Read receipts | N/A | Supported |\n| Typing indicators | N/A | Supported |\n\n\u003cbr /\u003e\n\n## Open channel\n\nAn **open channel** is a Twitch-style public chat. In this channel type, anyone can enter and participate in the chat without permission. A single channel can handle thousands of simultaneous users.\n\n### Create an open channel\n\nYou can create an open channel from the [Sendbird Dashboard](https://dashboard.sendbird.com/auth/signin), or by using the Chat SDK, or using Sendbird [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/open-channel#2-create-a-channel). To create a channel on demand or dynamically, the last two methods are recommended. \n\nA channel URL, a unique identifier, must be specified in order to create a channel. Additionally, you can specify your own channel topic on the name of the channel.\n\nAn open channel is ideal for use cases that require a small and static number of channels such as chatting in a lobby in a game. \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateOpenChannelHandler : public SBDCreateOpenChannelInterface {\npublic:\n    SendBirdCreateOpenChannelHandler() {\n    }\n    \n    ~SendBirdCreateOpenChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The open channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateOpenChannel() {\n    SendBirdCreateOpenChannelHandler *handler = new SendBirdCreateOpenChannelHandler(); // `handler` has to be deallocated later.\n    \n    // Every wstring type parameter is an option. If you don't have to set them, set `SBD_NULL_WSTRING`.\n    // OPERATOR_USER_IDS is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    SBDOpenChannel::CreateChannel(CHANNEL_NAME, CHANNEL_URL, COVER_URL, DATA, OPERATOR_USER_IDS, CUSTOM_TYPE, handler);\n}\n```\n\n### Channel cover images\n\nWhen creating a channel, you can add a cover image by specifying an image URL.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateOpenChannelHandler : public SBDCreateOpenChannelInterface {\npublic:\n    SendBirdCreateOpenChannelHandler() {\n    }\n    \n    ~SendBirdCreateOpenChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The open channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateOpenChannel() {\n    SendBirdCreateOpenChannelHandler *handler = new SendBirdCreateOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::CreateChannel(SBD_NULL_WSTRING, SBD_NULL_WSTRING, COVER_URL, SBD_NULL_WSTRING, vector\u003cwstring\u003e(), SBD_NULL_WSTRING, handler);\n}\n```\n\nYou can get the cover image URL using `cover_url`. You can also update a channel's cover image by calling `UpdateChannel()`.\n\n### Enter an open channel\n\nA user must enter an open channel to receive messages. A user can enter up to 10 open channels at once.\n\nEntered open channels are valid only within the current connection. If a user disconnects from or reconnects to Sendbird server, the user must re-enter channels in order to continue receiving messages from the previously entered open channels.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdEnterOpenChannelHandler : public SBDEnterOpenChannelInterface {\npublic:\n    SendBirdEnterOpenChannelHandler() {\n    }\n    \n    ~SendBirdEnterOpenChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid EnterOpenChannel() {\n    SendBirdEnterOpenChannelHandler *handler = new SendBirdEnterOpenChannelHandler();   // `handler` has to be deallocated later.\n    open_channel-\u003eEnter(handler);\n}\n```\n\n### Exit an open channel\nTo stop receiving messages from an Open Channel, you must exit the channel.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdExitOpenChannelHandler : public SBDExitOpenChannelInterface {\npublic:\n    SendBirdExitOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdExitOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        // Error handling.\n        // Deallocate error.\n        delete error;\n        return;\n    }\n};\n\nvoid ExitOpenChannel() {\n    SendBirdExitOpenChannelHandler *handler = new SendBirdExitOpenChannelHandler(); // `handler` has to be deallocated later.\n    open_channel-\u003eExit(handler);\n}\n```\n\n### Get a list of open channels\n\nUse the `SBDOpenChannelListQuery.LoadNextPage()` to obtain a list of open channels. This method returns a list of `SBDOpenChannel` objects. You must be connected to Sendbird server before requesting a list of open channels.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDOpenChannelListQuery *query;\n\nclass SendBirdOpenChannelListQueryHandler : public SBDLoadNextOpenChannelListInterface {\npublic:\n    SendBirdOpenChannelListQueryHandler() {\n    \n    }\n    \n    ~SendBirdOpenChannelListQueryHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDOpenChannel *\u003e channels, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Do not deallocate the channel items in channels vector.\n    }\n}\n\nvoid InitQueryInstance() {\n    query = SBDOpenChannel::CreateOpenChannelListQuery();\n    query-\u003elimit = 30;\n    query-\u003eSetChannelUrlFilter(CHANNEL_URL);\n    query-\u003eSetChannelNameFilter(CHANNEL_NAME);\n    query-\u003eSetCustomTypeFilter(CUSTOM_TYPE);\n}\n\nvoid GetOpenChannels() {\n    SendBirdOpenChannelListQueryHandler *handler = new SendBirdOpenChannelListQueryHandler();   // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\n### Get an open channel instance with a URL\n\nSince a **channel URL** is a unique identifier of an open channel, use a URL to retrieve a channel instance. It is important to remember that a user must [enter the channel](#enter-an-open-channel) before being able to send or receive messages within the channel.\n\nStore channel URLs to handle lifecycle or state changes in your app. For example,when a user disconnects from Sendbird server by temporarily switching to another app, the stored URL can be used to fetch the appropriate channel instance to provide a smooth restoration of the user’s state. The stored URL can also be used to re-enter the user into the channel.  \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Do not deallocate the channel instance.\n    }\n};\n\nvoid GetOpenChannel() {\n    SendBirdGetOpenChannelHandler *handler = new SendBirdGetOpenChannelHandler();   // `handler` has to be deallocated later.\n    SBDOpenChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n### Send a message\n\nUpon entering a channel, a user will be able to send messages of the following types:\n* **UserMessage** : a User text message.\n* **FileMessage** : a User binary message.\n\nFurthermore, you can specify a `CUSTOM_TYPE` to subclassify a message.\n\nWhen you send a text message, you can additionally attach arbitrary strings via a `DATA` field. You can utilize this field to send structured data such as font sizes, font types, or custom `JSON` objects.\n\nDelivery failures caused by network issues or other reasons will return an exception. By implementing a virtual method, `CompletionHandler()`, it is possible to display only the messages that are successfully sent.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendUserMessageHandler : public SBDSendUserMessageInterface {\npublic:\n    SendBirdSendUserMessageHandler() {\n    }\n    \n    ~SendBirdSendUserMessageHandler() {\n    }\n    \n    void CompletionHandler(SBDUserMessage *user_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `user_message`.\n    }\n};\n\n\nvoid SendUserMessage() {\n    SendBirdSendUserMessageHandler *handler = new SendBirdSendUserMessageHandler(); // `handler` has to be deallocated later.\n    \n    // `SendUserMessage()` belongs to `SBDBaseChannel` class, so it can be used by `SBDOpenChannel` and `SBDGroupChannel` instance.\n    // TARGET_LANGUAGES is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    channel-\u003eSendUserMessage(MESSAGE_TEXT, DATA, CUSTOM_TYPE, TARGET_LANGUAGES, handler);\n}\n```\n\nA user can also send any binary file through the Chat SDK. There are two ways in which a user can send a binary file: by sending the file itself, or sending a **URL**.\nBy sending a raw file, a user can choose to send a file hosted in the user’s own server by passing in a URL that points to the file. In this case, the file will not be hosted in Sendbird server, and downloads of the file will occur through the user’s own server instead.\n\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendFileMessageHandler : public SBDSendFileMessageInterface {\npublic:\n    SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    ~SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDFileMessage *file_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `file_message`.\n    }\n};\n\nvoid SendFileMessage() {\n    SendBirdSendFileMessageHandler *handler = new SendBirdSendFileMessageHandler(); // `handler` has to be deallocated later.\n    channel-\u003eSendFileMessage(FILE_URL, FILE_NAME, FILE_SIZE, FILE_TYPE, CUSTOM_DATA, CUSTOM_TYPE, handler);\n}\n```\n\n### Receive messages\n\nAdd [`SBDChannelInterface`](#event-handler) to receive messages. A received `SBDBaseMessage` object takes one of the three following message types:\n\n* **SBDUserMessage**: a User text message.\n* **SBDFileMessage**: a User binary message.\n* **SBDAdminMessage**: an Admin message which can be sent by an admin through the Platform API.\n\n`UNIQUE_HANDLER_ID` is a unique identifier that registers multiple concurrent handlers.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void MessageReceived(SBDBaseChannel *channel, SBDBaseMessage *message) {\n    \n    }\n    \n    // ...\n};\n\nvoid InitChannelEventHandler() {\n    SBDMain::AddChannelHandler(new SendBirdChannelEventHandler(), UNIQUE_HANDLER_ID);\n}\n```\n\nThe channel handler where the UI is no longer valid should be removed. \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nvoid RemoveChannelHandler() {\n    SBDMain::RemoveChannelHandler(UNIQUE_HANDLER_ID);\n}\n```\n\n### Load previous messages\n\nCreate a `SBDPreviousMessageListQuery` instance to load previous messages. Past messages in your UI will be displayed once they are loaded. \n\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDPreviousMessageListQuery *query;\n\nclass SendBirdLoadPreviousMessageListHandler : public SBDLoadPreviousMessageListInterface {\npublic:\n    SendBirdLoadPreviousMessageListHandler() {\n    \n    }\n    \n    ~SendBirdLoadPreviousMessageListHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDBaseMessage *\u003e messages, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid InitQuery() {\n    query = channel-\u003eCreatePreviousMessageQuery();\n}\n\nvoid GetPreviousMessages() {\n    SendBirdLoadPreviousMessageListHandler *handler = new SendBirdLoadPreviousMessageListHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(30, false, handler);\n}\n```\n\nPast messages are queried in fixed numbers as shown in the code above which queried **30**. A new `SBDPreviousMessageListQuery` instance will load the most recent `n` messages. Calling the `LoadNextPage()` on the same query instance will load n messages before that. Therefore, your query instance should be stored as a member variable in order to traverse through your entire message history.\n\nNote that you must receive your first `CompletionHandler()` callback before invoking the `LoadNextPage()` again.\n\n### Load messages by timestamp\n\nA set number of messages starting from a specific timestamp can be retrieved.\n\nTo load messages sent prior to a specified timestamp, use `GetPreviousMessagesByTimestamp()` of the channel instance.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nint64_t timestamp = INT64_MAX;\n\nclass SendBirdGetPreviousMessagesHandler : public SBDGetMessagesInterface {\npublic:\n    SendBirdGetPreviousMessagesHandler() {\n    \n    }\n    \n    ~SendBirdGetPreviousMessagesHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDBaseMessage *\u003e messages, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid GetPreviousMessage() {\n    SendBirdGetPreviousMessagesHandler *handler = new SendBirdGetPreviousMessagesHandler(); //  `handler` has to be deallocated later.\n    channel-\u003eGetPreviousMessagesByTimestamp(timestamp, limit, reverse, messageType, customType, handler);\n}\n```\n\n- `timestamp` : The reference timestamp.\n- `limit` : The number of messages to load. Note that the actual number of results may be larger than the set value when there are multiple messages retrieved as the earliest message on the same timestamp.\n- `reverse` : Whether to reverse the results.\n- `messageType` : A `SBDMessageTypeFilter` enum type. Should be one of `SBDMessageTypeFilterUser`, `SBDMessageTypeFilterFile`, `SBDMessageTypeFilterAdmin`, or `SBDMessageTypeFilterAll`.\n- `customType` : The Custom Type of the messages to be returned.\n\nTo load messages sent after a specified timestamp, call the `GetNextMessagesByTimestamp()` in a similar fashion. To load results on either side of the reference timestamp, use the `GetMessagesByTimestamp()`.\n\n### Delete a message\n\nUsers are able to delete messages. An error is returned if a user tries to delete messages sent by someone else. Channel Operators are able to delete any messages sent by any users in the channel.\n\nDeleting a message triggers a `MessageDeleted` event to all other online users in the channel.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdDeleteMessageHandler : public SBDDeleteMessageInterface {\npublic:\n    SendBirdDeleteMessageHandler() {\n    \n    }\n    \n    ~SendBirdDeleteMessageHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid DeleteMessage() {\n    SendBirdDeleteMessageHandler *handler = new SendBirdDeleteMessageHandler(); // `handler` has to be deallocated later.\n    channel-\u003eDeleteMessage(USER_MESSAGE, handler);\n}\n```\n\nA `SBDChannelInterface` can be used to receive a `MessageDeleted()` event.\n\n```cpp\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void MessageDeleted(SBDBaseChannel *channel, uint64_t message_id) {\n    \n    }\n    \n    // ...\n};\n\nvoid InitSendBird() {\n    SBDMain::AddChannelHandler(new SendBirdChannelEventHandler(), UNIQUE_CHANNEL_HANDLER_IDENTIFIER);\n}\n```\n\n### Get a list of participants in a channel\n\nParticipants are online users who are currently receiving all messages from the open channel that they are in.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDUserListQuery *query;\n\nclass SendBirdLoadNextParticipantsHandler : public SBDLoadNextUserListInterface {\npublic:\n    SendBirdLoadNextParticipantsHandler() {\n    \n    }\n    \n    ~SendBirdLoadNextParticipantsHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDUser\u003e users, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid InitQuery() {\n    query = open_channel-\u003eCreateParticipantListQuery();\n    query-\u003elimit = 30;\n}\n\nvoid GetParticipants() {\n    SendBirdLoadNextParticipantsHandler *handler = new SendBirdLoadNextParticipantsHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\n### Get participants' online status\n\nTo stay updated on each participant's connection status, you must obtain a new `SBDUserListQuery`, which contains the latest information on each user. To get a `SBDUserListQuery` for a specific channel, call the `CreateOpenChannelListQuery()` of `SBDOpenChannel`. If you wish to get the list of all users on your application, call the `CreateUserListQuery(USER_IDS)` of `SBDMain`.\n\nReference `connection_status` of `SBDUser` to check each user’s connection status. \n\nIf your application needs to keep track of users' connection status in real time, we recommend that you receive a new `SBDUserListQuery` periodically, perhaps in intervals of one minute or more.\n\n`connection_status` can return one of the three following values:\n\n- `SBDUserConnectionStatusNotAvailable`: A user's status information cannot be reached.\n- `SBDUserConnectionStatusOffline`: A user is disconnected from Sendbird server.\n- `SBDUserConnectionStatusOnline`: A user is connected to Sendbird server.\n\n### Get a list of banned or muted users in a channel\n\nA query to get a list of muted or banned users in an open channel can be created. \nThis query is only available for users who are registered as operators of the open channel.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDUserListQuery *query;\n\nclass SendBirdLoadNextBannedUserList : public SBDLoadNextUserListInterface {\npublic:\n    SendBirdLoadNextBannedUserList() {\n    \n    }\n    \n    ~SendBirdLoadNextBannedUserList() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDUser\u003e users, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid InitQuery() {\n    query = open_channel-\u003eCreateMutedUserListQuery();\n    query-\u003elimit = 30;\n}\n\nvoid GetBannedUsers() {\n    SendBirdLoadNextBannedUserList *handler = new SendBirdLoadNextBannedUserList(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDUserListQuery *query;\n\nclass SendBirdLoadNextMutedUserList : public SBDLoadNextUserListInterface {\npublic:\n    SendBirdLoadNextMutedUserList() {\n    \n    }\n    \n    ~SendBirdLoadNextMutedUserList() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDUser\u003e users, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid InitQuery() {\n    query = open_channel-\u003eCreateMutedUserListQuery();\n    query-\u003elimit = 30;\n}\n\nvoid GetBannedUsers() {\n    SendBirdLoadNextMutedUserList *handler = new SendBirdLoadNextMutedUserList(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\n\u003cbr /\u003e\n\n## Open channel: Advanced\n\n### Admin messages\n\nAdmin messages can be sent to users in a channel using the [Sendbird Dashboard](https://dashboard.sendbird.com/auth/signin) or [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/messages#2-send-a-message).\n\nTo send Admin messages using Dashboard, navigate to the Open Channels tab. Inside the message box, there is an option to send an Admin message. Admin messages should not be longer than 1,000 characters.\n\nIf you are currently developing under the Free Plan and therefore cannot access the Moderation Tools from Dashboard, you must send Admin messages through Chat Platform API.\n\n### Custom channel types\n\nWhen creating a channel, you can additionally specify a **Custom Type** to further subclassify your channels. This custom type takes on the form of a `wstring`, and can be handy in searching or filtering channels.\n\n\u003e `DATA` and `CUSTOM_TYPE` are both String fields that allow you to append information to your channels. The intended use case is for `CUSTOM_TYPE` to contain information that can subclassify the channel (e.g., distinguishing \"School\" and \"Work\" channels). However, both these fields can be flexibly utilized.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateOpenChannelHandler : public SBDCreateOpenChannelInterface {\npublic:\n    SendBirdCreateOpenChannelHandler() {\n    }\n    \n    ~SendBirdCreateOpenChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The open channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateOpenChannel() {\n    SendBirdCreateOpenChannelHandler *handler = new SendBirdCreateOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::CreateChannel(SBD_NULL_WSTRING, SBD_NULL_WSTRING, SBD_NULL_WSTRING, DATA, vector\u003cwstring\u003e(), CUSTOM_TYPE, handler);\n}\n```\n\nTo get a channel's custom type, read `channel-\u003ecustom_type`.\n\n### Custom message types\n\nWhen creating a channel, a Custom Type can be additionally specified to further subclassify the channels. This custom type takes on the form of a `wstring`, and can be handy in searching for or filtering channels.\n\n\u003e `DATA` and `CUSTOM_TYPE` are both `String` fields that allow you to append information to your channels. The intended use case is for `CUSTOM_TYPE` to contain information that can subclassify the channel (e.g., distinguishing \"School\" and \"Work\" channels). However, both these fields can be flexibly utilized.\n\nTo embed a custom type into a message, simply pass a `String` argument to the **message** parameter in the `SendUserMessage()` or `SendFileMessage()`.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendUserMessageHandler : public SBDSendUserMessageInterface {\npublic:\n    SendBirdSendUserMessageHandler() {\n    }\n    \n    ~SendBirdSendUserMessageHandler() {\n    }\n    \n    void CompletionHandler(SBDUserMessage *user_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `user_message`.\n    }\n};\n\nvoid SendUserMessage() {\n    SendBirdSendUserMessageHandler *handler = new SendBirdSendUserMessageHandler(); // `handler` has to be deallocated later.\n    // `SendUserMessage()` belongs to `SBDBaseChannel` class, so it can be used by `SBDOpenChannel` and `SBDGroupChannel` instance.\n    // TARGET_LANGUAGES is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    channel-\u003eSendUserMessage(MESSAGE_TEXT, DATA, CUSTOM_TYPE, vector\u003cwstring\u003e(), handler);\n}\n```\n\nTo get a message's custom type, read `message-\u003ecustom_type`.\n\n### Message auto-translation\n\n\u003e This is one of Sendbird's **premium features**. Contact our [sales team](https://get.sendbird.com/talk-to-sales.html) for further assistance.\n\nSendbird makes it possible for messages to be sent in different languages through its auto-translation feature. Pass in a `vector` of language codes to the `SendUserMessage()` to request translated messages in the corresponding languages.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendUserMessageHandler : public SBDSendUserMessageInterface {\npublic:\n    SendBirdSendUserMessageHandler() {\n    }\n    \n    ~SendBirdSendUserMessageHandler() {\n    }\n    \n    void CompletionHandler(SBDUserMessage *user_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `user_message`.\n    }\n};\n\n\nvoid SendUserMessage() {\n    SendBirdSendUserMessageHandler *handler = new SendBirdSendUserMessageHandler(); // `handler` has to be deallocated later.\n    \n    // `SendUserMessage()` belongs to `SBDBaseChannel` class, so it can be used by `SBDOpenChannel` and `SBDGroupChannel` instance.\n    vector\u003cwstring\u003e target_langs;\n    target_langs.push_back(L\"es\");\n    target_langs.push_back(L\"ko\");\n    \n    channel-\u003eSendUserMessage(MESSAGE_TEXT, DATA, CUSTOM_TYPE, target_langs, handler);\n}\n```\n\nUse `user_message-\u003etranslations` to get a translated version of a message. This method returns a `map\u003cwstring, wstring\u003e` which contains the language codes and translated version of the message.\n\n```cpp\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void MessageReceived(SBDBaseChannel *channel, SBDBaseMessage *message) {\n        vector\u003cwstring\u003e translations = (SBDUserMessage *)message;\n        wstring es_translation = translations[L\"es\"];\n        \n        // Display translation in UI.\n    }\n    \n    // ...\n};\n```\n\n\u003e Reference the table below for supported languages and their language codes.\n\n| Language Code | English Name        | Language Code | English Name       |\n|---------------|---------------------|---------------|--------------------|\n| af            | Afrikaans           | tlh-Qaak      | Klingon (pIqaD)    |\n| ar            | Arabic              | ko            | Korean             |\n| bs-Latn       | Bosnian (Latin)     | lv            | Latvian            |\n| bg            | Bulgarian           | lt            | Lithuanian         |\n| ca            | Catalan             | ms            | Malay              |\n| zh-CHS        | Chinese Simplified  | mt            | Maltese            |\n| zh-CHT        | Chinese Traditional | no            | Norwegian          |\n| hr            | Croatian            | fa            | Persian            |\n| cs            | Czech               | pl            | Polish             |\n| da            | Danish              | pt            | Portuguese         |\n| nl            | Dutch               | otq           | Querétaro Otomi    |\n| en            | English             | ro            | Romanian           |\n| et            | Estonian            | ru            | Russian            |\n| fi            | Finnish             | sr-Cyrl       | Serbian (Cyrillic) |\n| fr            | French              | sr-Latn       | Serbian (Latin)    |\n| de            | German              | sk            | Slovak             |\n| el            | Greek               | sl            | Slovenian          |\n| ht            | Haitian Creole      | es            | Spanish            |\n| he            | Hebrew              | sv            | Swedish            |\n| hi            | Hindi               | th            | Thai               |\n| mww           | Hmong Daw           | tr            | Turkish            |\n| hu            | Hungarian           | uk            | Ukrainian          |\n| id            | Indonesian          | ur            | Urdu               |\n| it            | Italian             | vi            | Vietnamese         |\n| ja            | Japanese            | cy            | Welsh              |\n| sw            | Kiswahili           | yua           | Yucatec Maya       |\n| tlh           | Klingon             |     -         |          -         |\n\n\n### Keyword search\n\nYou can search for specific channels by adding a keyword to `SBDOpenChannelListQuery`. There are two types of keywords: a **Name Keyword** and a **URL Keyword**.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDOpenChannelListQuery *query;\n\nclass SendBirdOpenChannelListQueryHandler : public SBDLoadNextOpenChannelListInterface {\npublic:\n    SendBirdOpenChannelListQueryHandler() {\n    \n    }\n    \n    ~SendBirdOpenChannelListQueryHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDOpenChannel *\u003e channels, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Do not deallocate the channel items in channels vector.\n        // Returns a List of channels that have \"NameKeyword\" in their names.\n    }\n}\n\nvoid InitQueryInstance() {\n    query = SBDOpenChannel::CreateOpenChannelListQuery();\n    query-\u003elimit = 30;\n    query-\u003eSetChannelNameFilter(CHANNEL_NAME);\n}\n\nvoid GetOpenChannels() {\n    SendBirdOpenChannelListQueryHandler *handler = new SendBirdOpenChannelListQueryHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\nAdding a **URL Keyword** to a query will return the Open Channel whose URL matches the given keyword.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDOpenChannelListQuery *query;\n\nclass SendBirdOpenChannelListQueryHandler : public SBDLoadNextOpenChannelListInterface {\npublic:\n    SendBirdOpenChannelListQueryHandler() {\n    \n    }\n    \n    ~SendBirdOpenChannelListQueryHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDOpenChannel *\u003e channels, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Do not deallocate the channel items in channels vector.\n        // Returns a List containing a single channel with the URL that matches the URL Keyword.\n    }\n}\n\nvoid InitQueryInstance() {\n    query = SBDOpenChannel::CreateOpenChannelListQuery();\n    query-\u003elimit = 30;\n    query-\u003eSetChannelUrlFilter(CHANNEL_URL);\n}\n\nvoid GetOpenChannels() {\n    SendBirdOpenChannelListQueryHandler *handler = new SendBirdOpenChannelListQueryHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\n### File message thumbnails\n\n\u003e This is one of Sendbird's **premium features**. Contact our [sales team](https://get.sendbird.com/talk-to-sales.html) for further assistance.\n\nWhen sending an image file, you can create a thumbnail of the image, which you can fetch and render into your UI. Up to 3 different dimensions can be specified to generate thumbnail images which can be convenient for supporting various display densities.\n\nSupported file types are files whose MIME type is `image/*` or `video/*`.\n\nThe SDK does not support creating thumbnails when [sending a file message via a file URL](#send-a-message).\n\nSteps to create a thumbnail:\n\n1. Create a `vector` of `SBDThumbnailSize` objects to pass to `SendFileMessageWithPath()`. \n2. A `SBDThumbnailSize` can be created with the constructor `SBDThumbnailSize()`, where the values specify pixels. \n3. The `completion_handler` callback of `SBDSendFileMessageInterface` will subsequently return a `vector` of `SBDThumbnail` objects in the file message object that each contain the URL of the generated thumbnail image file.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendFileMessageHandler : public SBDSendFileMessageInterface {\npublic:\n    SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    ~SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDFileMessage *file_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // file_message-\u003ethumbnails has the thumbnails' information.\n    }\n};\n\nvoid SendFileMessage() {\n    vector\u003cSBDThumbnailSize\u003e thumbnail_sizes;\n    thumbnail_sizes.push_back(SBDThumbnailSize(320, 320));\n    thumbnail_sizes.push_back(SBDThumbnailSize(160, 160));\n    \n    SendBirdSendFileMessageHandler *handler = new SendBirdSendFileMessageHandler(); // `handler` has to be deallocated later.\n    channel-\u003eSendFileMessageWithPath(FILE_PATH, FILE_MIME_TYPE, thumbnail_sizes, DATA, CUSTOM_TYPE, handler));\t\t\n}\n```\n\n`max_width` and `max_height` specify the maximum dimensions of the thumbnail. Your image will be scaled down evenly to fit within the bounds of (`max_width`, `max_height`). Note that if the original image is smaller than the specified dimension, the thumbnail will not be scaled. `GetUrl()` returns the location of the generated thumbnail file within Sendbird server.\n\n\u003cbr /\u003e\n\n## Group channel\n\nA **group channel** is a private chat. A user may join the chat only through an invitation by another user who is already a member of the chat. A Group Channel can have a single member to hundreds of members. Creating a channel with two members allows 1-on-1 messaging.\n\nA user will automatically receive all messages from the group channels that they are a member of.\n\n### Create a group channel\n\nA group channel can be created on demand by a user through Sendbird Chat SDK.\n\n- **Distinct property** : The **distinct** property allows a channel to be reused for the same members of a group chat. When the distinct property is set to **true**, attempting to create a new channel with the same members will just return a reference to the pre-existing channel. \n\nWhen the property is set to **false**, a new channel will be created even if the two users have had a previous conversation in a channel. In other words, the two users will not be able to access previously sent messages or data. \n\nThus, enabling the distinct property for a 1-on-1 message channel is recommended so that the same channel can be reused between two users who were previously engaged in a chat. \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateGroupChannelHandler : public SBDCreateGroupChannelInterface {\npublic:\n    SendBirdCreateGroupChannelHandler() {\n    }\n    \n    ~SendBirdCreateGroupChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDGroupChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The group channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateGroupChannel() {\n    SendBirdCreateGroupChannelHandler *handler = new SendBirdCreateGroupChannelHandler(); // `handler` has to be deallocated later.\n    \n    // Every wstring type parameter is an option. If you don't have to set them, set `SBD_NULL_WSTRING`.\n    // INVITEE_IDS is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    // IS_DISTINCT is bool type.\n    SBDGroupChannel::CreateChannel(INVITEE_IDS, NAME, IS_DISTINCT, COVER_IMAGE_URL, DATA, CUSTOM_TYPE, handler);\n}\n```\n\n* `NAME`: the name of the channel, or the channel topic.\n* `COVER_IMAGE_URL`: the URL of the cover image, which you can fetch to render into the UI.\n* `DATA`: a `wstring` field to store structured information, such as a `JSON` String.\n* `CUSTOM_TYPE`: a `wstring` field that allows you to subclassify your channel.\n\n\u003e See the [Advanced](#group-channel-advanced) section for more information on cover images and custom types.\n\nYou can also create a group channel via SendBird [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/group-channel#2-create-a-channel).\nYou should utilize the Platform API when you wish to control channel creations and member invitations on the server-side.\n\n### 1-on-1 chat\n\nA 1-on-1 chat is just a group channel with two members.\n\nA user can create a group channel through the client SDK on demand. Pass in two user IDs to create a 1-on-1 chat between two users.\nIt is recommended that the **distinct** property be set to be **true** for 1-on-1 chat. If the **distinct** property is set to be **false**, the user will be able to create a new channel with the same user, even if there is a pre-existing channel with the same user. In this case, multiple 1-on-1 chats between the same two users would exist, each channel with its own chat history and data. \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateGroupChannelHandler : public SBDCreateGroupChannelInterface {\npublic:\n    SendBirdCreateGroupChannelHandler() {\n    }\n    \n    ~SendBirdCreateGroupChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDGroupChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The group channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateGroupChannel() {\n    SendBirdCreateGroupChannelHandler *handler = new SendBirdCreateGroupChannelHandler(); // `handler` has to be deallocated later.\n    SBDGroupChannel::CreateChannel(vector\u003cwstring\u003e(), SBD_NULL_WSTRING, IS_DISTINCT, SBD_NULL_WSTRING, SBD_NULL_WSTRING, SBD_NULL_WSTRING, handler);\n}\n```\n\nYou can also append additional information by passing several arguments to the corresponding parameters. \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateGroupChannelHandler : public SBDCreateGroupChannelInterface {\npublic:\n    SendBirdCreateGroupChannelHandler() {\n    }\n    \n    ~SendBirdCreateGroupChannelHandler() {\n    }\n    \n    void CompletionHandler(SBDGroupChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The group channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateGroupChannel() {\n    SendBirdCreateGroupChannelHandler *handler = new SendBirdCreateGroupChannelHandler(); // `handler` has to be deallocated later.\n    SBDGroupChannel::CreateChannel(vector\u003cwstring\u003e(), NAME, IS_DISTINCT, COVER_IMAGE_URL, DATA, CUSTOM_TYPE, handler);\n}\n```\n\n- `NAME`: the name of the channel, or the channel topic.\n- `COVER_IMAGE_URL`: the URL of the cover image, which you can fetch to render into the UI.\n- `DATA`: a `wstring` field to store structured information, such as a `JSON` String.\n- `CUSTOM_TYPE`: a `wstring` field that allows you to subclassify your channel.\n\n\u003e See the [Advanced](##group-channel---advanced) section for more information on cover images and custom types.\n\nYou can also create channels via SendBird [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/group-channel#2-create-a-channel). You should utilize Chat Platform API when you wish to control channel creation and member invitations on the server-side.\n\n### Channel cover images\n\nWhen creating a channel, you can add a cover image by specifying an image URL.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateGroupChannelHandler : public SBDCreateGroupChannelInterface {\npublic:\n    SendBirdCreateGroupChannelHandler() {\n    \n    }\n    \n    ~SendBirdCreateGroupChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDGroupChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The group channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateGroupChannel() {\n    SendBirdCreateGroupChannelHandler *handler = new SendBirdCreateGroupChannelHandler(); // `handler` has to be deallocated later.\n    SBDGroupChannel::CreateChannel(vector\u003cwstring\u003e(), NAME, IS_DISTINCT, COVER_URL, SBD_NULL_WSTRING, SBD_NULL_WSTRING, handler);\n}\n```\n\nUse `cover_url` to get the cover image URL. You can also update a channel's cover image by calling the `UpdateChannel()`method.\n\n### Invite users to an existing channel\n\nOnly the members of the channel are able to invite new members into the channel.\n\nYou can choose whether a newly invited user is able to access past messages in the channel. In your **Dashboard Settings** \u003e **Chat** \u003e **Messages** menu, there is the **Chat history** option which you can turn on to show chat history to the new members. If this option is turned on, new members will be able to view all messages that are sent in the channel once they join the channel. If this option is not turned on, new users will only be able to view messages that are sent after they joined.\n\n\u003e **Show channel history** is enabled by default\n\n```cpp\nclass SendBirdInviteUsersHandler : public SBDInviteUsersInterface {\npublic:\n    SendBirdInviteUsersHandler() {\n    \n    }\n    \n    ~SendBirdInviteUsersHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid InviteUsers() {\n    vector\u003cSBDUser\u003e invitees;\n    \n    invitees.push_back(user_a);\n    invitees.push_back(user_b);\n    \n    SendBirdInviteUsersHandler *handler = new SendBirdInviteUsersHandler(); // `handler` has to be deallocated later.\n    group_channel-\u003eInviteUsers(invitees, handler);\n}\n```\n\n### Leave a group channel\n\nUsers will no longer receive messages from channels they have left.\n\n```cpp\nclass SendBirdLeaveGroupChannelHandler : public SBDLeaveGroupChannelInterface {\npublic:\n    SendBirdLeaveGroupChannelHandler() {\n    \n    }\n    \n    ~SendBirdLeaveGroupChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid LeaveGroupChannel() {\n    SendBirdLeaveGroupChannelHandler *handler = new SendBirdLeaveGroupChannelHandler(); // `handler` has to be deallocated later.\n    group_channel-\u003eLeaveChannel();\n}\n```\n\n\n### Get a list of my Group Channels\n\nUse the `CreateMyGroupChannelListQuery()` and `SBDGroupChannel.LoadNextPage()` to return a list of `SBDGroupChannel` objects to obtain a list of group channels. \n\nYou can also set an option to include **empty channels** with the `include_empty_channel`. Empty channels are channels that have been created but contain no sent messages. By default, empty channels are not shown.\n\n```cpp\nSBDGroupChannelListQuery *query;\n\nclass SendBirdMyGroupChannelListQueryHandler : public SBDLoadNextGroupChannelListInterface {\npublic:\n    SendBirdMyGroupChannelListQueryHandler() {\n    \n    }\n    \n    ~SendBirdMyGroupChannelListQueryHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDGroupChannel *\u003e channels, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Do not deallocate the channel items in channels vector.\n    }\n}\n\nvoid InitQueryInstance() {\n    query = SBDGroupChannel::CreateMyGroupChannelListQuery();\n    query-\u003elimit = 30;\n    query-\u003einclude_empty_channel = true;\n}\n\nvoid GetMyGroupChannels() {\n    SendBirdMyGroupChannelListQueryHandler *handler = new SendBirdMyGroupChannelListQueryHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\n### Get a group channel instance with a URL\n\nSince a **channel URL** is a unique identifier of a group channel, you can use a URL to retrieve a channel instance.\n\nStore channel URLs to handle lifecycle or state changes in your app. For example, when a user disconnects from Sendbird server by temporarily switching to another app, the stored URL can be used to fetch the appropriate channel instance to provide a smooth restoration of the user’s state. The stored URL can also be used to re-enter the user into the channel.  \n\n```cpp\nclass SendBirdGetGroupChannelHandler : public SBDGetGroupChannelInterface {\npublic:\n    SendBirdGetGroupChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetGroupChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDGroupChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid CreateGroupChannel() {\n    SendBirdGetGroupChannelHandler *handler = new SendBirdGetGroupChannelHandler(); // `handler` has to be deallocated later.\n    SBDGroupChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n### Query group channels by user ID\n\nIt is possible to filter a channel search by user IDs. This can be done by calling the `SetUsersExactFilter()` or `SetUsersIncludeFilter()` of `SBDGroupChannelListQuery`.\n\nGiven an example where a user (with the ID \"User\") is part of two group channels:\n\n- channelA: { \"User\", \"John\", \"Jay\" }\n- channelB: { \"User\", \"John\", \"Jay\", \"Jin\" }\n\nAn **ExactFilter** returns the list of channels containing exactly the queried user IDs.\n\n```cpp\nSBDGroupChannelListQuery *query;\n\nclass SendBirdMyGroupChannelListQueryHandler : public SBDLoadNextGroupChannelListInterface {\npublic:\n    SendBirdMyGroupChannelListQueryHandler() {\n    \n    }\n    \n    ~SendBirdMyGroupChannelListQueryHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDGroupChannel *\u003e channels, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Do not deallocate the channel items in channels vector.\n    }\n}\n\nvoid InitQueryInstance() {\n    query = SBDGroupChannel::CreateMyGroupChannelListQuery();\n    query-\u003elimit = 30;\n    \n    query-\u003eSetUsersExactFilter(USERS);\n}\n\nvoid GetMyGroupChannels() {\n    SendBirdMyGroupChannelListQueryHandler *handler = new SendBirdMyGroupChannelListQueryHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\nAn **IncludeFilter** returns channels where the userIDs are included. This method can return one of two different results, based on the parameter `query_type`.\n\n```cpp\nSBDGroupChannelListQuery *query;\n\nclass SendBirdMyGroupChannelListQueryHandler : public SBDLoadNextGroupChannelListInterface {\npublic:\n    SendBirdMyGroupChannelListQueryHandler() {\n    \n    }\n    \n    ~SendBirdMyGroupChannelListQueryHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDGroupChannel *\u003e channels, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Do not deallocate the channel items in channels vector.\n    }\n}\n\nvoid InitQueryInstance() {\n    query = SBDGroupChannel::CreateMyGroupChannelListQuery();\n    query-\u003elimit = 30;\n    \n    query-\u003eSetUsersIncludeFilter(USERS, SBDGroupChannelListQueryTypeAnd);\n}\n\nvoid GetMyGroupChannels() {\n    SendBirdMyGroupChannelListQueryHandler *handler = new SendBirdMyGroupChannelListQueryHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(handler);\n}\n```\n\n### Send a message\n\nUpon joining a channel, a user will be able to send messages of the following types:\n\n- **UserMessage**: A text message sent by a user\n- **FileMessage**: A binary message sent by a user\n\nFurthermore, you can specify a `CUSTOM_TYPE` to subclassify a message.\n\nWhen you send a text message, you can additionally attach arbitrary strings via a `DATA` field. You can utilize this field to send structured data such as font sizes, font types, or custom `JSON` objects.\n\nDelivery failures caused by network issues or other reasons will return an exception. By implementing the virtual method, `CompletionHandler()`, it is possible to display only the messages that are successfully sent.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendUserMessageHandler : public SBDSendUserMessageInterface {\npublic:\n    SendBirdSendUserMessageHandler() {\n    }\n    \n    ~SendBirdSendUserMessageHandler() {\n    }\n    \n    void CompletionHandler(SBDUserMessage *user_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `user_message`.\n    }\n};\n\n\nvoid SendUserMessage() {\n    SendBirdSendUserMessageHandler *handler = new SendBirdSendUserMessageHandler(); // `handler` has to be deallocated later.\n    \n    // `SendUserMessage()` belongs to `SBDBaseChannel` class, so it can be used by `SBDOpenChannel` and `SBDGroupChannel` instance.\n    // TARGET_LANGUAGES is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    channel-\u003eSendUserMessage(MESSAGE_TEXT, DATA, CUSTOM_TYPE, TARGET_LANGUAGES, handler);\n}\n```\n\nA user can also send any binary file through the Chat SDK. There are two ways in which a user can send a binary file: by sending the file itself, or sending a URL.\n\nBy sending a raw file, a user can choose to send a file hosted in the user’s own server by passing in a URL that points to the file. In this case, the file will not be hosted in Sendbird server, and downloads of the file will occur through the user’s own server instead.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendFileMessageHandler : public SBDSendFileMessageInterface {\npublic:\n    SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    ~SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDFileMessage *file_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `file_message`.\n    }\n};\n\nvoid SendFileMessage() {\n    SendBirdSendFileMessageHandler *handler = new SendBirdSendFileMessageHandler(); // `handler` has to be deallocated later.\n    channel-\u003eSendFileMessage(FILE_URL, FILE_NAME, FILE_SIZE, FILE_TYPE, CUSTOM_DATA, CUSTOM_TYPE, handler);\n}\n```\n\n### Receive messages\n\nAdd `SBDChannelInterface` to receive messages. A received `SBDBaseMessage` object takes one of the three following message types:\n\n- **SBDUserMessage**: A text message sent by a user\n- **SBDFileMessage**: A binary file message sent by a user \n- [**SBDAdminMessage**](#open-channel-3-admin-messages): A text message sent by an admin through the [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/messages#2-send-a-message)\n\n`UNIQUE_HANDLER_ID` is a unique identifier to register multiple concurrent handlers.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void MessageReceived(SBDBaseChannel *channel, SBDBaseMessage *message) {\n    \n    }\n    \n    // ...\n};\n\nvoid InitChannelEventHandler() {\n    SBDMain::AddChannelHandler(new SendBirdChannelEventHandler(), UNIQUE_HANDLER_ID);\n}\n```\n\nThe channel handler where the UI is no longer valid should be removed. \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nvoid RemoveChannelHandler() {\n    SBDMain::RemoveChannelHandler(UNIQUE_HANDLER_ID);\n}\n```\n\n### Load previous messages\n\nCreate a `SBDPreviousMessageListQuery` instance to load previous messages. Past messages in your UI will be displayed once they are loaded. \n\n```cpp\n#include \u003cSendBird.h\u003e\n\nSBDPreviousMessageListQuery *query;\n\nclass SendBirdLoadPreviousMessageListHandler : public SBDLoadPreviousMessageListInterface {\npublic:\n    SendBirdLoadPreviousMessageListHandler() {\n    \n    }\n    \n    ~SendBirdLoadPreviousMessageListHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDBaseMessage *\u003e messages, SBDError *error) {\n        // Error handling.\n        // Deallocate error.\n        delete error;\n        return;\n    }\n};\n\nvoid InitQuery() {\n    query = channel-\u003eCreatePreviousMessageQuery();\n}\n\nvoid GetPreviousMessages() {\n    SendBirdLoadPreviousMessageListHandler *handler = new SendBirdLoadPreviousMessageListHandler(); // `handler` has to be deallocated later.\n    query-\u003eLoadNextPage(30, false, handler);\n}\n```\n\nPast messages are queried in fixed numbers as shown in the code above which queried **30**. A new `SBDPreviousMessageListQuery` instance will load the most recent `n` messages. Calling the `LoadNextPage()` on the same query instance will load n messages before that. Therefore, you should store your query instance as a member variable in order to traverse through your entire message history.\n\n\u003e An important note is that you must receive your first `CompletionHandler()` callback before invoking the `LoadNextPage()` again.\n\n### Load messages by timestamp\n\nYou can retrieve a set number of messages starting from a specific timestamp.\n\nTo load messages sent prior to a specifed timestamp, use [`GetPreviousMessagesByTimestamp()`] of the channel instance.\n\nA set number of messages starting from a specific timestamp can be retrieved. To load messages sent prior to a specified timestamp, use the `GetPreviousMessagesByTimestamp()` of the channel instance.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nint64_t timestamp = INT64_MAX;\n\nclass SendBirdGetPreviousMessagesHandler : public SBDGetMessagesInterface {\npublic:\n    SendBirdGetPreviousMessagesHandler() {\n    \n    }\n    \n    ~SendBirdGetPreviousMessagesHandler() {\n    \n    }\n    \n    void CompletionHandler(vector\u003cSBDBaseMessage *\u003e messages, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid GetPreviousMessage() {\n    SendBirdGetPreviousMessagesHandler *handler = new SendBirdGetPreviousMessagesHandler(); // `handler` has to be deallocated later.\n    channel-\u003eGetPreviousMessagesByTimestamp(timestamp, limit, reverse, messageType, customType, handler);\n}\n```\n\n- `timestamp` : The reference timestamp.\n- `limit` : The number of messages to load. Note that the actual number of results may be larger than the set value when there are multiple messages with the same timestamp as the earliest message.\n- `reverse` : Whether to reverse the results.\n- `messageType` : A `SBDMessageTypeFilter` enum type. Should be one of `SBDMessageTypeFilterUser`, `SBDMessageTypeFilterFile`, `SBDMessageTypeFilterAdmin`, or `SBDMessageTypeFilterAll`.\n- `customType` : The custom type of the messages to be returned.\n\nTo load messages sent after a specified timestamp, call the `GetNextMessagesByTimestamp()` in a similar fashion. To load results on either side of the reference timestamp, use the `GetMessagesByTimestamp()`.\n\n### Delete a message\n\nUsers are able to delete messages. An error is returned if a user tries to delete messages sent by someone else. Channel operators are able to delete any messages sent by any users in the channel.\n\nDeleting a message triggers a `MessageDeleted` event to all other online users in the channel.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdDeleteMessageHandler : public SBDDeleteMessageInterface {\npublic:\n    SendBirdDeleteMessageHandler() {\n    \n    }\n    \n    ~SendBirdDeleteMessageHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nvoid DeleteMessage() {\n    SendBirdDeleteMessageHandler *handler = new SendBirdDeleteMessageHandler(); // `handler` has to be deallocated later.\n    channel-\u003eDeleteMessage(USER_MESSAGE, handler);\n}\n```\n\nA `SBDChannelInterface` can be used to receive a `MessageDeleted()` event.\n\n```cpp\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void MessageDeleted(SBDBaseChannel *channel, uint64_t message_id) {\n    \n    }\n    \n    // ...\n};\n\nvoid InitSendBird() {\n    SBDMain::AddChannelHandler(new SendBirdChannelEventHandler(), UNIQUE_CHANNEL_HANDLER_IDENTIFIER);\n}\n```\n\n### Get a list of all channel members\n\nReference the `members` attribute within `SBDGroupChannel` to obtain a list of members in a group channel.\n\n```cpp\nvector\u003cSBDMember\u003e members = group_channel-\u003emembers;\n```\n\n### Get members' online statuses\n\nTo stay updated on each participant's connection status, you must obtain a new `SBDUserListQuery`, which contains the latest information on each user. To get a `SBDUserListQuery` for a specific channel, call `CreateOpenChannelListQuery()` of `SBDOpenChannel`. If you wish to get the list of all users of your service or application, call `CreateUserListQuery(USER_IDS)` of `SBDMain`.\n\nReference the `connection_status` of `SBDUser` to check each of the users' connection statuses.\n\nIf your application needs to keep track of users' connection statuses in real time, we recommend that you receive a new `SBDUserListQuery` periodically, perhaps in intervals of one minute or more.\n\n`connection_status` can return one of the three following values:\n\n- `SBDUserConnectionStatusNotAvailable`: A user's status information cannot be reached.\n- `SBDUserConnectionStatusOffline`: A user is disconnected from Sendbird server.\n- `SBDUserConnectionStatusOnline`: A user is connected to Sendbird server.\n\n\n\u003cbr /\u003e\n\n## Group channel: Advanced\n\n### Typing indicators\nYou can send typing events by invoking `StartTyping()` and `EndTyping()`.\n\n```cpp\ngroup_channel-\u003eStartTyping();\ngroup_channel-\u003eEndTyping();\n```\n\nYou can receive a `TypingStatusUpdated` event with `SBDChannelInterface`.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void TypingStatusUpdated(SBDGroupChannel *channel) {\n    \n    }\n    \n    // ...\n};\n\nvoid InitChannelEventHandler() {\n    SBDMain::AddChannelHandler(new SendBirdChannelEventHandler(), UNIQUE_HANDLER_ID);\n}\n```\n\n### Read receipts\n\nA user can indicate that they have read a message by calling the `MarkAsRead()`.\n\n```cpp\ngroup_channel-\u003eMarkAsRead();\n```\n\nThis broadcasts a `ReadReceiptUpdated` event, which can be handled with a channel interface.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void ReadReceiptUpdated(SBDGroupChannel *channel) {\n    \n    }\n    \n    // ...\n};\n\nvoid InitChannelEventHandler() {\n    SBDMain::AddChannelHandler(new SendBirdChannelEventHandler(), UNIQUE_HANDLER_ID);\n}\n```\n\n`GetReadReceipt()` returns the number of members in the channel who have not read the message.\n\n```cpp\nint unread_count = channel-\u003eGetReadReceipt(message);\n```\n\n### View who has read a message\n\nYou can view who has read a message with `GetReadMembers()`. This list is updated when the message's read receipt is updated. Therefore, you should replace your previous message instance with the newly received message in `ReadReceiptUpdated()` for real-time updates.\n\n```cpp\nvector\u003cSBDMember\u003e members = group_channel-\u003eGetReadMembers(message);\n```\n\nSimilarly, you can also view who has **not** read the message with `GetUnreadMembers():`.\n\n### Admin messages\n\nAdmin messages can be sent to users in a channel using the [SendBird Dashboard](https://dashboard.sendbird.com//auth/signin) or [Chat Platform API](https://sendbird.com/docs/chat/v3/platform-api/guides/messages#2-send-a-message).\n\nTo send Admin messages using the Dashboard, navigate to the **Group channels** tab. Inside the message box, you should see an option to send an admin message. Admin messages should not be longer than **1,000** characters.\n\n\u003e If you are currently developing under the **Free Plan** and therefore cannot access the **Moderation Tools** from the Dashboard, you must send an admin message through Chat Platform API.\n\n### Custom channel types\n\nWhen creating a channel, a custom type can be additionally specified to further subclassify the channels. This custom type takes on the form of a `NSString`, and can be handy in searching for or filtering channels.\n\n\u003e `DATA` and `CUSTOM_TYPE` are both `String` fields that allow you to append information to your channels. The intended use case is for `CUSTOM_TYPE` to contain information that can subclassify the channel (e.g., distinguishing \"School\" and \"Work\" channels). However, both these fields can be flexibly utilized.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateGroupChannelHandler : public SBDCreateGroupChannelInterface {\npublic:\n    SendBirdCreateGroupChannelHandler() {\n    \n    }\n    \n    ~SendBirdCreateGroupChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDGroupChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // The group channel is created.\n        // Do not deallocate `channel` pointer.\n    }\n};\n\nvoid CreateGroupChannel() {\n    SendBirdCreateGroupChannelHandler *handler = new SendBirdCreateGroupChannelHandler(); // `handler` has to be deallocated later.\n    SBDGroupChannel::CreateChannel(vector\u003cwstring\u003e(), NAME, IS_DISTINCT, COVER_URL, DATA, CUSTOM_TYPE, handler);\n}\n```\n\nTo get a channel's custom type, read `channel-\u003ecustom_type`.\n\n### Custom message types\n\nA custom type for messages can be specified to categorize them into more specific groups. This custom type takes on the form of a `wstring`, and can be useful in searching for or filtering messages.\n\n\u003e `DATA` and `CUSTOM_TYPE` are both String fields that allow you to append information to your messages. The intended use case is for `CUSTOM_TYPE` to contain information that can subclassify the message (e.g., distinguishing \"FILE_IMAGE\" and \"FILE_AUDIO\" type messages). However, both these fields can be flexibly utilized.\n\nTo embed a custom type into a message, simply pass a `String` argument to a `custom_type` parameter in the `SendUserMessage()` or `SendFileMessage()`.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendUserMessageHandler : public SBDSendUserMessageInterface {\npublic:\n    SendBirdSendUserMessageHandler() {\n    }\n    \n    ~SendBirdSendUserMessageHandler() {\n    }\n    \n    void CompletionHandler(SBDUserMessage *user_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `user_message`.\n    }\n};\n\n\nvoid SendUserMessage() {\n    SendBirdSendUserMessageHandler *handler = new SendBirdSendUserMessageHandler(); // `handler` has to be deallocated later.\n    \n    // `SendUserMessage()` belongs to `SBDBaseChannel` class, so it can be used by `SBDOpenChannel` and `SBDGroupChannel` instance.\n    // TARGET_LANGUAGES is vector\u003cwstring\u003e type. If you don't have to set it, set vector\u003cwstring\u003e().\n    channel-\u003eSendUserMessage(MESSAGE_TEXT, DATA, CUSTOM_TYPE, vector\u003cwstring\u003e(), handler);\n}\n```\n\nTo get a message's custom type, read `message-\u003ecustom_type`.\n\n### File message thumbnails\n\n\u003e This is one of Sendbird's **premium features**. Contact our [sales team](https://get.sendbird.com/talk-to-sales.html) for further assistance.\n\nWhen sending an image file, you can create a thumbnail of the image, which you can fetch and render into your UI. Up to **3** different dimensions can be specified to generate thumbnail images which can be convenient for supporting various display densities.\n\nSupported file types are files whose **MIME type** is `image/*` or `video/*`.\n\n\u003e The SDK does not support creating thumbnails when [sending a File Message via a file URL](#send-a-message-1).\n\nSteps to create a thumbnail:\n\n1. Create a `vector` of `SBDThumbnailSize` objects to pass to the `SendFileMessageWithPath()`. \n2. A `SBDThumbnailSize` can be created with the constructor `SBDThumbnailSize()`, where the values specify pixels. \n3. The `completion_handler` callback of `SBDSendFileMessageInterface` will subsequently return a vector of `SBDThumbnail` objects in the file message object that each contain the URL of the generated thumbnail image file.\n\n```cpp\nclass SendBirdSendFileMessageHandler : public SBDSendFileMessageInterface {\npublic:\n    SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    ~SendBirdSendFileMessageHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDFileMessage *file_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // file_message-\u003ethumbnails has the thumbnails' information.\n    }\n};\n\nvoid SendFileMessage() {\n    vector\u003cSBDThumbnailSize\u003e thumbnail_sizes;\n    thumbnail_sizes.push_back(SBDThumbnailSize(320, 320));\n    thumbnail_sizes.push_back(SBDThumbnailSize(160, 160));\n    \n    SendBirdSendFileMessageHandler *handler = new SendBirdSendFileMessageHandler(); // `handler` has to be deallocated later.\n    channel-\u003eSendFileMessageWithPath(FILE_PATH, FILE_MIME_TYPE, thumbnail_sizes, DATA, CUSTOM_TYPE, handler));\t\n}\n```\n\n`max_width` and `max_height` specify the maximum dimensions of the thumbnail. Your image will be scaled down evenly to fit within the bounds of (`max_width`, `max_height`). Note that if the original image is smaller than the specified dimensions, the thumbnail will not be scaled. The `GetUrl()` returns the location of the generated thumbnail file within the SendBird servers.\n\n### Message auto-translation\n\n\u003e This is one of Sendbird's **premium features**. Contact our [sales team](https://get.sendbird.com/talk-to-sales.html) for further assistance.\n\nSendbird makes it possible for messages to be sent in different languages through its auto-translation feature. Pass in a `vector` of language codes to the `SendUserMessage()` to request translated messages in the corresponding languages.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdSendUserMessageHandler : public SBDSendUserMessageInterface {\npublic:\n    SendBirdSendUserMessageHandler() {\n    }\n    \n    ~SendBirdSendUserMessageHandler() {\n    }\n    \n    void CompletionHandler(SBDUserMessage *user_message, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        // Handle `user_message`.\n    }\n};\n\n\nvoid SendUserMessage() {\n    SendBirdSendUserMessageHandler *handler = new SendBirdSendUserMessageHandler(); // `handler` has to be deallocated later.\n    \n    // `SendUserMessage()` belongs to `SBDBaseChannel` class, so it can be used by `SBDOpenChannel` and `SBDGroupChannel` instance.\n    vector\u003cwstring\u003e target_langs;\n    target_langs.push_back(L\"es\");\n    target_langs.push_back(L\"ko\");\n    \n    channel-\u003eSendUserMessage(MESSAGE_TEXT, DATA, CUSTOM_TYPE, target_langs, handler);\n}\n```\n\nUse `user_message-\u003etranslations` to get a translated version of a message. This method returns a `map\u003cwstring, wstring\u003e` which contains the language codes and translated version of the message.\n\n```cpp\nclass SendBirdChannelEventHandler : public SBDChannelInterface {\npublic:\n    // ...\n    \n    void MessageReceived(SBDBaseChannel *channel, SBDBaseMessage *message) {\n        vector\u003cwstring\u003e translations = (SBDUserMessage *)message;\n        wstring es_translation = translations[L\"es\"];\n        \n        // Display translation in UI.\n    }\n    \n    // ...\n};\n```\n\n\u003e Reference the table below for supported languages and their language codes.\n\n| Language Code | English Name        | Language Code | English Name       |\n|---------------|---------------------|---------------|--------------------|\n| af            | Afrikaans           | tlh-Qaak      | Klingon (pIqaD)    |\n| ar            | Arabic              | ko            | Korean             |\n| bs-Latn       | Bosnian (Latin)     | lv            | Latvian            |\n| bg            | Bulgarian           | lt            | Lithuanian         |\n| ca            | Catalan             | ms            | Malay              |\n| zh-CHS        | Chinese Simplified  | mt            | Maltese            |\n| zh-CHT        | Chinese Traditional | no            | Norwegian          |\n| hr            | Croatian            | fa            | Persian            |\n| cs            | Czech               | pl            | Polish             |\n| da            | Danish              | pt            | Portuguese         |\n| nl            | Dutch               | otq           | Querétaro Otomi    |\n| en            | English             | ro            | Romanian           |\n| et            | Estonian            | ru            | Russian            |\n| fi            | Finnish             | sr-Cyrl       | Serbian (Cyrillic) |\n| fr            | French              | sr-Latn       | Serbian (Latin)    |\n| de            | German              | sk            | Slovak             |\n| el            | Greek               | sl            | Slovenian          |\n| ht            | Haitian Creole      | es            | Spanish            |\n| he            | Hebrew              | sv            | Swedish            |\n| hi            | Hindi               | th            | Thai               |\n| mww           | Hmong Daw           | tr            | Turkish            |\n| hu            | Hungarian           | uk            | Ukrainian          |\n| id            | Indonesian          | ur            | Urdu               |\n| it            | Italian             | vi            | Vietnamese         |\n| ja            | Japanese            | cy            | Welsh              |\n| sw            | Kiswahili           | yua           | Yucatec Maya       |\n| tlh           | Klingon             |     -         |          -         |\n\n\n\u003cbr /\u003e\n\n## Channel metadata \u0026 metacounter\n\nWith channel metadata and metacounter, you can store additional information within a channel.\n\nChannel metadata allows you to store a `map` of `wstring` **key-value** pairs in a channel instance. If your aim is to store an integer with atomic increasing/decreasing operations, you should use a metacounter instead.\n\nUse cases for metadata and metacounter include: tracking the number of likes, setting the background color, creating a long description of the channel. All of these can be fetched and rendered into the UI.\n\n### Channel metadata\n\nChannel metadata is a `map\u003cwstring, wstring\u003e` that is stored within a channel. Its uses are very flexible, allowing you to customize a channel to fit you and your users' needs.\n\n#### Create\n\nSteps to creating metadata:\n\n1. To store metadata into a channel, create a `map\u003cwstring, wstring\u003e`.\n2. Pass it as an argument when calling the `CreateMetaData()`. You can store multiple **key-value** pairs in the dictionary.\n\n```cpp\n#include \u003cSendBird.h\u003e\n\nclass SendBirdCreateChannelMetaDataHandler : public SBDCreateChannelMetaDataInterface {\npublic:\n    SendBirdCreateChannelMetaDataHandler() {\n    \n    }\n    \n    ~SendBirdCreateChannelMetaDataHandler() {\n    \n    }\n    \n    void CompletionHandler(map\u003cwstring, wstring\u003e meta_data, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        map\u003cwstring, wstring\u003e meta_data;\n        meta_data[KEY_A] = VALUE_A;\n        meta_data[KEY_B] = VALUE_B;\n        \n        SendBirdCreateChannelMetaDataHandler *handler = new SendBirdCreateChannelMetaDataHandler(); // `handler` has to be deallocated later.\n        channel-\u003eCreateMetaData(meta_data, handler);\n        \n        // Do not deallocate the channel instance.\n    }\n};\n\nvoid GetOpenChannel() {\n    SendBirdGetOpenChannelHandler *handler = new SendBirdGetOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n#### Update\n\nThe process for updating metadata is identical to creation. Values will be updated for existing keys, while new **key-value** pairs will be added.\n\n```cpp\nclass SendBirdUpdateChannelMetaDataHandler : public SBDUpdateChannelMetaDataInterface {\npublic:\n    SendBirdUpdateChannelMetaDataHandler() {\n    \n    }\n    \n    ~SendBirdUpdateChannelMetaDataHandler() {\n\n    }\n    \n    void CompletionHandler(map\u003cwstring, wstring\u003e meta_data, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        map\u003cwstring, wstring\u003e meta_data;\n        meta_data[KEY_B] = VALUE_B;\n        meta_data[KEY_C] = VALUE_C;\n        \n        SendBirdUpdateChannelMetaDataHandler *handler = new SendBirdUpdateChannelMetaDataHandler(); // `handler` has to be deallocated later.\n        channel-\u003eUpdateMetaData(meta_data, handler);\n        \n        // Do not deallocate the channel instance.\n    }\n};\n\nvoid GetOpenChannel() {\n    SendBirdGetOpenChannelHandler *handler = new SendBirdGetOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n#### Get\n\nGetting stored metadata requires creating a `vector` of keys to pass as an argument to the `GetMetaData()`. The callback `CompletionHandler` of `SBDGetChannelMetaDataInterface` returns a `map\u003cwstring, wstring\u003e` containing the corresponding **key-value** pairs.\n\n```cpp\nclass SendBirdGetChannelMetaDataHandler : public SBDGetChannelMetaDataInterface {\npublic:\n    SendBirdGetChannelMetaDataHandler() {\n    \n    }\n    \n    ~SendBirdGetChannelMetaDataHandler() {\n    \n    }\n    \n    void CompletionHandler(map\u003cwstring, wstring\u003e meta_data, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        vector\u003cwstring\u003e keys;\n        keys.push_back(KEY_A);\n        keys.push_back(KEY_C);\n        \n        SendBirdGetChannelMetaDataHandler *handler = new SendBirdGetChannelMetaDataHandler(); // `handler` has to be deallocated later.\n        channel-\u003eGetMetaData(keys, handler);\n        \n        // Do not deallocate the channel instance.\n    }\n};\n\nvoid GetOpenChannel() {\n    SendBirdGetOpenChannelHandler *handler = new SendBirdGetOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n### Channel metacounter\n\nA Channel metacounter is a `map\u003cwstring, int64_t\u003e` that is stored within a channel instance. Its primary uses are to track and update discrete indicators within a channel.\n\n#### Create\n\nStoring a MetaCounter into a channel simply requires creation of a `map\u003cwstring, int64_t\u003e`, then passing it as an argument when calling `CreateMetaCounters()`. You can store multiple **key-value** pairs in the dictionary.\n\n```cpp\nclass SendBirdCreateChannelMetaCountersHandler : public SBDCreateChannelMetaCountersInterface {\npublic:\n    SendBirdCreateChannelMetaCountersHandler() {\n    \n    }\n    \n    ~SendBirdCreateChannelMetaCountersHandler() {\n    \n    }\n    \n    void CompletionHandler(map\u003cwstring, int64_t\u003e meta_counters, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        map\u003cwstring, int64_t\u003e meta_counters;\n        meta_counters[KEY_A] = 10;\n        meta_counters[KEY_B] = 10;\n        \n        SendBirdCreateChannelMetaCountersHandler *handler = new SendBirdCreateChannelMetaCountersHandler(); // `handler` has to be deallocated later.\n        channel-\u003eCreateMetaCounters(meta_counters, handler);\n        \n        // Do not deallocate the channel instance.\n    }\n};\n\nvoid GetOpenChannel() {\n    SendBirdGetOpenChannelHandler *handler = new SendBirdGetOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n#### Get\n\nRetrieving stored metacounters requires creating a `vector` of keys to pass as an argument to the `GetMetaCounters()`. The callback `CompletionHandler` `SBDGetChannelMetaDataInterface` returns a `map\u003cwstring, int64_t\u003e` containing the corresponding **key-value** pairs.\n\n```cpp\nclass SendBirdGetChannelMetaCountersHandler : public SBDGetChannelMetaCountersInterface {\npublic:\n    SendBirdGetChannelMetaCountersHandler() {\n    \n    }\n    \n    ~SendBirdGetChannelMetaCountersHandler() {\n    \n    }\n    \n    void CompletionHandler(map\u003cwstring, int64_t\u003e meta_counters, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        vector\u003cwstring\u003e keys;\n        keys.push_back(KEY_A);\n        keys.push_back(KEY_B);\n        \n        SendBirdGetChannelMetaCountersHandler *handler = new SendBirdGetChannelMetaCountersHandler(); // `handler` has to be deallocated later.\n        channel-\u003eGetMetaCounters(keys, handler);\n        \n        // Do not deallocate the channel instance.\n    }\n};\n\nvoid GetOpenChannel() {\n    SendBirdGetOpenChannelHandler *handler = new SendBirdGetOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n#### Increase\n\nThe increase and decrease operations work similarly to getting metacounters, as described above. Create a `vector` of keys to pass to the `IncreaseMetaCounters()`, which increments the corresponding metacounters by 1.\n\n```cpp\nclass SendBirdIncreaseMetaCountersHandler : public SBDIncreaseMetaCountersInterface {\npublic:\n    SendBirdIncreaseMetaCountersHandler() {\n    \n    }\n    \n    ~SendBirdIncreaseMetaCountersHandler() {\n    \n    }\n    \n    void CompletionHandler(map\u003cwstring, int64_t\u003e meta_counters, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        map\u003cwstring, int64_t\u003e meta_counters;\n        meta_counters[KEY_A] = 5;\n        \n        SendBirdCreateChannelMetaCountersHandler *handler = new SendBirdCreateChannelMetaCountersHandler(); // `handler` has to be deallocated later.\n        channel-\u003eIncreaseMetaCounters(meta_counters, handler);\n        \n        // Do not deallocate the channel instance.\n  }\n};\n\nvoid GetOpenChannel() {\n    SendBirdGetOpenChannelHandler *handler = new SendBirdGetOpenChannelHandler(); // `handler` has to be deallocated later.\n    SBDOpenChannel::GetChannel(CHANNEL_URL, handler);\n}\n```\n\n#### Decrease\n\nLikewise, pass a `vector` of keys to `DecreaseMetaCounters()`, which decrements the MetaCounters by 1.\n\n```cpp\nclass SendBirdDecreaseMetaCountersHandler : public SBDDecreaseMetaCountersInterface {\npublic:\n    SendBirdDecreaseMetaCountersHandler() {\n    \n    }\n    \n    ~SendBirdDecreaseMetaCountersHandler() {\n    \n    }\n    \n    void CompletionHandler(map\u003cwstring, int64_t\u003e meta_counters, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n    }\n};\n\nclass SendBirdGetOpenChannelHandler : public SBDGetOpenChannelInterface {\npublic:\n    SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    ~SendBirdGetOpenChannelHandler() {\n    \n    }\n    \n    void CompletionHandler(SBDOpenChannel *channel, SBDError *error) {\n        if (error != NULL) {\n            // Error handling.\n            // Deallocate error.\n            delete error;\n            return;\n        }\n        \n        map\u003cwstring, int64_t\u003e meta_counters;\n        meta_counters[KEY_A] = 5;\n        \n        SendBirdDecreaseMetaCou","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsendbird%2Fsendbird-chat-sdk-windows","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsendbird%2Fsendbird-chat-sdk-windows","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsendbird%2Fsendbird-chat-sdk-windows/lists"}