This document explains how to integrate the interactive messaging SDK into your HarmonyOS application to enable interactive features for live streaming, such as comments, bullet screen, and likes.
Prerequisites
Before you begin client-side integration, ensure you have completed server-side integration and have an API for the client to get an authentication token. For detailed instructions, see Server-side Integration.
Environment requirements
Before you begin the integration, ensure your development environment meets the following requirements:
Contact a Huawei business representative, sign a cooperation plan, and enable the required permissions to obtain DevEco Studio 5.0.3.900 Release or a later version.
You have the HarmonyOS NEXT SDK or a later version from the Documentation Center.
You have a HarmonyOS device that supports audio and video, runs HarmonyOS NEXT 5.0.0.102 or later with API Version 12, and has the "Allow debugging" option enabled.
If you need to perform on-device debugging, refer to the HarmonyOS official documentation for configuration instructions.
Your HarmonyOS device is connected to the Internet.
You have registered a Huawei Developer account and completed real-name verification.
Integrate the SDK
Automated integration with ohpm.
In the oh-package.json5 file within your project's entry directory, add the SDK dependency, and then click "Sync Now".
{
"dependencies": {
"@aliyun_video_cloud/alivc-im-sdk": "latest version x.y.z"
}
}To find the latest version x.y.z, see the Release Notes.
Add permissions required by the SDK.
Permission | Description | Purpose |
ohos.permission.INTERNET | Allows network access. | Required for establishing a connection and for sending and receiving messages. |
Use the SDK
The general workflow for using the SDK is as follows:
Initialization
Login
Related operations
Logout
Deinitialization
Other
Related operations include group and message operations, as detailed below:
Notes
All IDs must adhere to the following rules:
appid: A string of up to 64 characters, containing only uppercase letters (A–Z), lowercase letters (a–z), digits (0–9), and hyphens (-).
userid: A string of up to 64 characters, containing only uppercase letters (A–Z), lowercase letters (a–z), digits (0–9), and hyphens (-).
groupid: A string of up to 64 characters, containing only uppercase letters (A–Z), lowercase letters (a–z), digits (0–9), and hyphens (-).
Initialization
Initialize the SDK before use. This is typically done at the main entry point of the relevant module in your application.
// Create an SDK configuration object.
let config = new ImSdkConfig("your_app_id", "your_app_sign");
config.logLevel = ImLogLevel.DEBUG;
// Initialize the SDK.
let ret = AliVCIMEngine.getInstance().init(this.getUIContext().getHostContext()!, config);
if (ret == 0) {
// Initialization is successful. Add listeners.
addListeners();
}Listen for persistent connection status
After the SDK initializes successfully, add listeners.
// Add an SDK status listener. You can listen for specific callbacks as needed.
let engineListener: AliVCIMEngineListener = new AliVCIMEngineListener()
.setOnLinkStateEventListener((event: ImLinkStateEvent): void => {
// Connection status changed.
})
.setOnExitListener((exitCode: number): void => {
// Logout callback.
// 1: User-initiated logout.
// 2: Kicked out.
// 3: Timeout or other reasons.
// 4: Logged in on another device.
})
.setOnTokenExpiredListener((getTokenCallback: AliVCIMTokenCallback): void => {
// Handle token expiration.
})
.setOnReconnectSuccessListener((groupStatus: ImGroupInfo[]): void => {
// Reconnection successful callback. Group information might have changed during the disconnection.
});
AliVCIMEngine.getInstance().addEngineListener(engineListener);Login
Generate a login token.
private getTokenAuth(userId: string, role: string): ImAuth {
const appId: string = 'your_app_id';
const appKey: string = 'your_app_key';
const nonce: string = "your_nonce";
const tokenDuration: number = 24*60*60; // 1-day validity. This must match the expiration time of the token generated on the server-side.
const timestamp: number = Math.floor(Date.now() / 1000) + tokenDuration;
const pendingShaStr: string = appId + appKey + userId + nonce + timestamp + role;
const appToken: string = CryptoJS.SHA256(pendingShaStr).toString(CryptoJS.enc.Hex);
let auth = new ImAuth(appToken, timestamp);
auth.role = role;
auth.nonce = nonce;
return auth;
}For more information about tokens, see Server-side Integration. You must pass the correct parameters; otherwise, the login will fail.
The role parameter supports two roles:
administrator (value:
admin)
An administrator can also perform all non-administrator operations.
non-administrator (any value other than
admin)
Users with this role cannot perform operations that require administrator permissions. Attempting to do so results in an error.
Log in and wait for the result.
let user = new ImUser(userId);
let auth = this.getTokenAuth(userId, "your_role");
let loginReq = new ImLoginReq(user, auth);
AliVCIMEngine.getInstance().login(loginReq)
.then(() => {
// Login successful.
})
.catch((error: ImError) => {
// Login failed.
});Logout
AliVCIMEngine.getInstance().logout()
.then(() => {
// Logout successful.
})
.catch((error: ImError) => {
// Logout failed.
});Deinitialization
Remove listeners.
AliVCIMEngine.getInstance().removeSdkListener(engineListener);
AliVCIMEngine.getInstance().getGroupManager()?.removeGroupListener(groupListener);
AliVCIMEngine.getInstance().getMessageManager()?.removeMessageListener(messageListener);Deinitialize the SDK.
AliVCIMEngine.getInstance().unInit();Message service
Non-administrator operations
Listen for messages
// Add a message listener.
let messageListener: AliVCIMMessageListener = new AliVCIMMessageListener()
.setOnReceiveGroupMessageListener((groupId: string, message: ImGroupMessage) => {
// Received a group message.
})
.setOnDeleteMessageListener((groupId: string, messageId: string): void => {
// Group message retraction notification.
})
.setOnReceiveC2cMessageListener( (message: ImMessage) => {
// Received a one-to-one message.
});
AliVCIMEngine.getInstance().getMessageManager()?.addMessageListener(messageListener);Send a one-to-one message
let content = new ImMessageContent(this._messageType/*custom message type*/, data/*message content*/);
content.setMessageLevel(ImMessageLevel.NORMAL); // Set the message level. The default is NORMAL. For details, see "Message Prioritization and Throttling".
let sendMessageOption = new ImSendMessageOption();
sendMessageOption.enableAudit(false); // Specify whether to enable security audit. Disabled by default.
let req = new ImSendMessageToUserReq(receiverId/*target user ID*/, content, sendMessageOption);
// Ensure the recipient is online. Otherwise, error code 424 is returned. In this case, we recommend resending the message after the recipient comes online.
AliVCIMEngine.getInstance().getMessageManager()?.sendC2cMessage(req)
.then((rsp: ImSendMessageToUserRsp) => {
this.showToast(`One-to-one message sent successfully: Message ID=${rsp.messageId}`);
})
.catch((error: ImError) => {
this.showToast(`Failed to send one-to-one message: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Send a group message
let content = new ImMessageContent(this._messageType/*custom message type*/, data/*message content*/);
content.setMessageLevel(ImMessageLevel.NORMAL); // Set the message level. The default is NORMAL. For details, see "Message Prioritization and Throttling".
let sendMessageOption = new ImSendGroupMessageOption();
sendMessageOption.enableAudit(false); // Specify whether to enable security audit. Disabled by default.
sendMessageOption.enableStorage(false); // Specify whether to store the message. Disabled by default. If enabled, the message is saved to the database and returned when you query message history or list messages.
sendMessageOption.enableCache(false); // Specify whether to cache the message. Not cached by default. If enabled, the sent message is cached in memory (Note: only the last 50 messages are cached) and is returned when you list recent messages.
sendMessageOption.enableMuteCheck(true); // Specify whether to check for mute status. Enabled by default. If disabled, sent messages are not subject to mute restrictions.
sendMessageOption.setRepeatCount(1); // Set the message count increment. The default is 1. This is mainly used to aggregate messages of the same type, such as likes. For example, you can aggregate n likes into a single message by setting repeatCount to n.
let req = new ImSendMessageToGroupReq(groupId/*target group ID*/, content, sendMessageOption);
// Ensure you have joined the group successfully (that is, after the AliVCIMGroupInterface.joinGroup callback succeeds) before sending a group message. Otherwise, error code 425 is returned.
AliVCIMEngine.getInstance().getMessageManager()?.sendGroupMessage(req)
.then((rsp: ImSendMessageToGroupRsp) => {
this.showToast(`Message sent successfully: Message ID=${rsp.messageId}`);
})
.catch((error: ImError) => {
this.showToast(`Failed to send message: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});List recent messages
let req = new ImListRecentMessageReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getMessageManager()?.listRecentMessage(req)
.then((rsp: ImListRecentMessageRsp) => {
this.showToast(`Successfully listed recent messages: ${rsp.messageList.length} total`);
this.logInfo('Message list: ' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`Failed to list recent messages: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Query message history
let req = new ImListHistoryMessageReq(this._groupId/*target group ID*/, 88888/*target message type*/);
req.nextPageToken = 231231; // If not set, this indicates the first page. The server returns the next page token. Pass it in when requesting the next page.
req.sortType = ImSortType.ASC; // The order in which messages are returned. The default is ascending by time.
req.pageSize = 10; // The maximum number of messages to return per request. Range: 10–30.
req.beginTime = 0; // The start time for time-based traversal, in seconds. If not set, it starts from the earliest time.
req.endTime = 0; // The end time for time-based traversal, in seconds. If not set, it ends at the latest time.
AliVCIMEngine.getInstance().getMessageManager()?.listHistoryMessage(req)
.then((rsp: ImListHistoryMessageRsp) => {
this.showToast(`Successfully queried message history: ${rsp.messageList.length} total`);
this.logInfo('Message list: ' + JSON.stringify(rsp, null, 2));
this._lastMsgPageToken = rsp.nextPageToken;
})
.catch((error: ImError) => {
this.showToast(`Failed to query message history: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Retract a group message
let req = new ImDeleteMessageReq(groupId/*target group ID*/, messageId/*target message ID*/);
AliVCIMEngine.getInstance().getMessageManager()?.deleteMessage(req)
.then(() => {
this.showToast(`Message retracted successfully`);
})
.catch((error: ImError) => {
this.showToast(`Failed to retract message: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Administrator operations
List messages
// This API is for group owners and administrators only.
let req = new ImListMessageReq(this._groupId/*target group ID*/, 88888/*target message type*/);
req.nextPageToken = 231231; // If not set, this indicates the first page. The server returns the next page token. Pass it in when requesting the next page.
req.sortType = ImSortType.ASC; // The order in which messages are returned. The default is ascending by time.
req.pageSize = 10; // The maximum number of messages to return per request. Range: 10–30.
req.beginTime = 0; // The start time for time-based traversal, in seconds. If not set, it starts from the earliest time.
req.endTime = 0; // The end time for time-based traversal, in seconds. If not set, it ends at the latest time.
AliVCIMEngine.getInstance().getMessageManager()?.listMessage(req)
.then((rsp: ImListMessageRsp) => {
this.showToast(`Successfully listed messages: ${rsp.messageList.length} total`);
this.logInfo('Message list: ' + JSON.stringify(rsp, null, 2));
this._lastMsgPageToken = rsp.nextPageToken;
})
.catch((error: ImError) => {
this.showToast(`Failed to list messages: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Group service
Non-administrator operations
Listen for group status
// Add a group listener.
let groupListener: AliVCIMGroupListener = new AliVCIMGroupListener()
.setOnMemberChangeListener((changeInfo: ImGroupMemberChangeInfo) => {
// Group member change notification.
})
.setOnGroupExitListener((groupId: string, reason: number) => {
// Group exit notification: 1 for group disbanded, 2 for kicked out.
})
.setOnMuteChangeListener((muteStatus: ImGroupMuteStatus) => {
// Group mute status change notification.
})
.setOnInfoChangeListener((infoStatus: ImGroupInfoStatus) => {
// Group information change notification.
});
AliVCIMEngine.getInstance().getGroupManager()?.addGroupListener(groupListener);Join a group
let req = new ImJoinGroupReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.joinGroup(req)
.then((rsp: ImJoinGroupRsp) => {
this._groupId = groupId;
this.showToast('Joined group successfully');
this.logInfo('Group information: ' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`Failed to join group: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
this.exitCurrentPage();
});Leave a group
let req = new ImLeaveGroupReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.leaveGroup(req)
.then(() => {
this.showToast('Left group successfully');
})
.catch((error: ImError) => {
this.showToast(`Failed to leave group: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Query group information
let req = new ImQueryGroupReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.queryGroup(req)
.then((rsp: ImQueryGroupRsp) => {
this.showToast(`Successfully queried group: ${rsp.groupInfo.groupId}`);
this.logInfo('Group information: ' + JSON.stringify(rsp.groupInfo, null, 2));
})
.catch((error: ImError) => {
this.showToast(`Failed to query group: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});List recent group members
If the current group is a supergroup, you cannot query the user list, and this API is unavailable.
let req = new ImListRecentGroupUserReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.listRecentGroupUser(req)
.then((rsp: ImListRecentGroupUserRsp) => {
this.showToast(`Successfully listed recent group users: ${rsp.total} total`);
this.logInfo('User list: ' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`Failed to list recent group users: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Administrator operations
Create a group
You can create a maximum of 5,000 groups. Closed groups do not count toward this limit. To ensure efficient resource use, close groups that are no longer in use.
let req = new ImCreateGroupReq(groupId/*target group ID*/);
req.groupName = "Test Group"; // Group name
req.groupMeta = "For testing purposes"; // Custom extended information
AliVCIMEngine.getInstance().getGroupManager()?.createGroup(req)
.then((rsp: ImCreateGroupRsp) => {
this.showToast(`Group created successfully: groupId=${rsp.groupId}, alreadyExist=${rsp.alreadyExist}`);
})
.catch((error: ImError) => {
this.showToast(`Failed to create group: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Close a group
let req = new ImCloseGroupReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.closeGroup(req)
.then(() => {
this.showToast('Group closed successfully');
})
.catch((error: ImError) => {
this.showToast(`Failed to close group: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Modify a group
let req = new ImModifyGroupReq(this._groupId/*target group ID*/);
req.forceUpdateGroupMeta = false; // If true, forces a refresh of groupMeta. If groupMeta is empty, it clears the value. If false, groupMeta is updated only if it is not empty.
req.groupMeta = "";
req.forceUpdateAdmins = false; // If true, forces a refresh of admins. If admins is empty, it clears the value. If false, admins is updated only if it is not empty.
req.admins = [];
AliVCIMEngine.getInstance().getGroupManager()?.modifyGroup(req)
.then(() => {
this.showToast('Group modified successfully');
})
.catch((error: ImError) => {
this.showToast(`Failed to modify group: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});List group members
let req = new ImListGroupUserReq(this._groupId/*group ID*/);
req.sortType = ImSortType.ASC; // Sort order: ASC (oldest members first), DESC (newest members first).
req.nextPageToken = 0; // If not set, this indicates the first page. The server returns the next page token. Pass it in when requesting the next page.
req.pageSize = 30; // The maximum number of users to return per request. The maximum value is 50.
AliVCIMEngine.getInstance().getGroupManager()?.listGroupUser(req)
.then((rsp: ImListGroupUserRsp) => {
this.showToast(`Successfully listed group users: ${rsp.userList.length} total`);
this.logInfo('User list: ' + JSON.stringify(rsp, null, 2));
this._lastUserPageToken = rsp.nextPageToken;
})
.catch((error: ImError) => {
this.showToast(`Failed to list group users: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Mute all members
let req = new ImMuteAllReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.muteAll(req)
.then(() => {
this.showToast('All members muted successfully');
})
.catch((error: ImError) => {
this.showToast(`Failed to mute all members: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Unmute all members
let req = new ImCancelMuteAllReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.cancelMuteAll(req)
.then(() => {
this.showToast('Unmuted all members successfully');
})
.catch((error: ImError) => {
this.showToast(`Failed to unmute all members: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Mute specific users
let req = new ImMuteUserReq(groupId/*target group ID*/, [userId]/*list of target user IDs to mute*/);
AliVCIMEngine.getInstance().getGroupManager()?.muteUser(req)
.then(() => {
this.showToast('Users muted successfully');
})
.catch((error: ImError) => {
this.showToast(`Failed to mute users: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Unmute specific users
let req = new ImCancelMuteUserReq(groupId/*target group ID*/, [userId]/*list of target user IDs to unmute*/);
AliVCIMEngine.getInstance().getGroupManager()?.cancelMuteUser(req)
.then(() => {
this.showToast('Users unmuted successfully');
})
.catch((error: ImError) => {
this.showToast(`Failed to unmute users: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});List muted users
let req = new ImListMuteUsersReq(groupId/*target group ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.listMuteUsers(req)
.then((rsp: ImListMuteUsersRsp) => {
this.showToast(`Successfully listed muted users: All members muted=${rsp.muteAll}`);
this.logInfo('Muted user list: ' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`Failed to list muted users: errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});Other
This section describes helper APIs for checking status and handling specific use cases.
// Check if the SDK is initialized. Use this API to determine if an SDK instance exists.
AliVCIMEngine.getInstance().isInit();
/**
* Gets the current login state.
* 1. You can log in only when the state is LOGOUT.
* 2. You can perform other operations, such as joining a group, only when the state is LOGIN.
*/
AliVCIMEngine.getInstance().getCurrentState();