微信小程序集成

互动消息是用于加强直播间消息沟通、提升交互体验的服务。提供了丰富、易集成的SDK,可在用户开发的直播应用中轻松集成评论、弹幕、点赞等能力。本文介绍微信小程序集成互动消息应用的操作步骤。

前提条件

服务端集成

客户端集成前,请确保已完成服务端集成,并提供客户端访问的获取鉴权Token的接口。详细操作,请参见服务端集成

小程序后台配置服务器域名

  1. 登录微信公众平台

  2. 在左侧导航栏选择开发 > 开发管理

  3. 开发设置页面,将如下域名配置到服务器域名中。

    • request合法域名:以HTTPS开头的域名

      https://slsrole.alicdn.com
      https://videocloud.cn-hangzhou.log.aliyuncs.com
    • socket合法域名:以wss开头的域名

      wss://rcoims.rtn.aliyuncs.com

集成小程序SDK文件

下载SDK文件

点击下载 微信小程序SDK 到本地,解压得到 alivc-im.js 、alivc-im.wasm.br 两个文件。

原生小程序项目集成文件

如果您的项目是原生微信小程序项目,可以可通过以下2步集成SDK到项目中:

  1. 将alivc-im.js 、alivc-im.wasm.br 两个文件复制到项目 /lib 文件夹下。

  2. 修改初始化时指定引入的 wasm 的地址,详细请查看初始化

项目目录结构请参考下方的示例:

.
├── lib
│   ├── alivc-im.js
│   ├── alivc-im.wasm.br
├── pages
│   ├── index
│   │   ├── index.js
│   │   ├── index.json
│   │   ├── index.wxml
│   │   └── index.wxss

uni-app 项目集成文件

如果您的项目是基于 uni-app 框架编译为微信小程序的,可通过以下3步集成SDK到项目中:

  1. 将 alivc-im.js 文件放入 /lib 文件夹下。

  2. 将 alivc-im.wasm.br 文件放入 /static/mp-weixin 文件夹下。这样编译时就能将alivc-im.wasm.br 文件拷贝到编译后的生产目录中。

  3. 将初始化 init 接口的 locateFile 返回的地址修改为 /static/mp-weixin/alivc-im.wasm.br,详细请查看初始化

项目目录结构请参考下方的示例:

.
├── lib
│   ├── alivc-im.js
├── static
│   ├── mp-weixin
│   │   ├── alivc-im.wasm.br
├── pages
│   ├── index
│   │   ├── index.vue

Taro 项目集成文件

如果您的项目是基于 Taro 解决方案编译为微信小程序的,可通过以下4步集成SDK到项目中:

  1. 将 alivc-im.js 文件放入 /src/lib 文件夹下。

  2. 将 alivc-im.wasm.br 文件放入 /static 文件夹下。

  3. 修改项目配置文件 /config/index.js 的 copy 配置项将 /static 文件夹从源码目录直接拷贝到编译后的生产目录中。

    参考下方示例修改配置文件:

    const config = {
      projectName: "demo",
      outputRoot: "dist",
      copy: {
        patterns: [
          // 将 static 文件夹拷贝到代码编译后的生产目录下
          { from: 'static', to: 'dist/static' }
        ],
        options: {},
      },
      // 省略其他配置项
    };
    
    module.exports = function (merge) {
      if (process.env.NODE_ENV === "development") {
        return merge({}, config, require("./dev"));
      }
      return merge({}, config, require("./prod"));
    };
    
  4. 将初始化 init 接口的 locateFile 返回的地址修改为 /static/alivc-im.wasm.br,详细请查看初始化

项目目录结构请参考下方的示例:

.
├── config
│   ├── index.js
│   ├── dev.js
│   ├── prod.js
├── static
│   ├── alivc-im.wasm.br
├── src
│   ├── lib
│   │   ├── alivc-im.js
│   ├── pages
│   │   ├── index
│   │   │   ├── index.tsx

使用SDK

SDK使用需遵循如下操作顺序:

  1. 初始化

  2. 登录

  3. 相关操作

  4. 登出

  5. 反初始化

其中相关操作包含群组操作和消息操作,详细说明如下:

相关操作

  • 群组操作

    • 创建群组(需要以管理员身份进行登录才能操作)

    • 关闭群组(仅限群主/群管理员操作)

    • 进入群组

    • 离开群组

    • 查询群组信息

    • 修改群组信息(仅限群主/群管理员操作)

    • 查询群组最近成员列表

    • 查询群组全部成员(仅限群主/群管理员操作)

    • 对群组进行禁言(仅限群主/群管理员操作)

    • 对群组取消禁言(仅限群主/群管理员操作)

    • 对群组的用户进行禁言(仅限群主/群管理员操作)

    • 对群组的用户取消禁言(仅限群主/群管理员操作)

    • 查询群组内被禁言的用户列表(仅限群主/群管理员操作)

  • 消息操作

    • 单发消息

    • 群发消息

    • 查询最近群发消息列表

    • 查询全部群发消息(仅限群主/群管理员操作)

    • 删除/撤回群消息

    • 查询历史消息

注意事项

重要

各ID需遵循以下规则

appid

最长64位,仅限于A~Z,a~z, 0~9及“-”, 不能包含其他字符

userid

最长64位,仅限于A~Z,a~z, 0~9及“-”, 不能包含其他字符

groupid

最长64位,仅限于A~Z,a~z, 0~9及“-”, 不能包含其他字符

初始化

使用前需要进行初始化,可以在相关业务模块的主入口设置。

展开查看代码

// 请结合您项目实际情况引入 SDK
const { ImEngine, ImLogLevel } = import '../../lib/alivc-im.js';

// 获取引擎单例
const engine = ImEngine.createEngine();

try {
  await engine.init({
    deviceId: "xxxx",    // 设备ID,可选传入
    appId: "APP_ID",     // 开通应用后可以在控制台上拷贝
    appSign: "APP_SIGN", // 开通应用后可以在控制台上拷贝
    logLevel: ImLogLevel.ERROR,  // 日志级别,调试时使用 ImLogLevel.DBUG
    // 指定引入的 wasm 的地址
    locateFile: (url: string) => {
      if (url.endsWith('.wasm')) {
        // 需要根据您项目 alivc-im.wasm.br 文件存放位置修改返回的路径
        // 这里是从项目根目录读取文件,而不是当前文件的目录下,因此不需要使用 .. 和 . 这些相对路径
        // 如果您参考了上面 uni-app 工程的目录结构,那么这里返回 /static/mp-weixin/alivc-im.wasm.br
        // 如果您参考了上面 Taro 工程的目录结构,那么这里返回 /static/alivc-im.wasm.br
        return '/lib/alivc-im.wasm.br';
      }
      return url;
    },
  });
  // 需确认init异步操作成功后,才可以继续执行login等操作
  
  // 初始化成功,监听事件
  engine.on("connecting", () => {
    console.log("connecting");
  });
  
  engine.on("connectfailed", (err) => {
    console.log(`connect failed: ${err.message}`;
  });
  
  engine.on("connectsuccess", () => {
    console.log("connect success");
  });
  
  engine.on("disconnect", (code: number) => {
    console.log(`disconnect: ${code}`);
  });
  
  engine.on("tokenexpired", async (cb) => {
    // 这里需要更新为获取新的登录信息的代码
    const auth = await getLoginAuth();
    cb(null, {
      timestamp: 22123123, // 服务端返回timestamp值
      nonce: 'nonce',      // 服务端返回nonce值
      role: 'admin',       // 是否为admin角色,如果不需要可以设置为空
      token: 'xxx'         // 服务端返回token值
    });
  });
} catch (error) {
  // init 错误码含义
  // 1001:重复初始化、1002:创建底层引擎失败、-1:底层重复初始化、-2:初始化配置信息有误
  console.log(`Init Fail: code:${error.code}, message: ${error.msg}`);
}

登录

登录需要鉴权信息,请确保已完成前提条件,即已通过服务端获取到鉴权信息,包括:timestamp、nonce、token等值。

展开查看代码

// 请确保init异步操作成功后,再执行此操作
await engine.login({
  user: {
    userId: 'abc',       // 当前app登录的用户ID
    userExtension: '{}', // 用户扩展信息,可以是头像、昵称等封装为JSON字符串
  },
  userAuth: {
    timestamp: 22123123, // 服务端返回timestamp值
    nonce: 'nonce',      // 服务端返回nonce值
    role: 'admin',       // 是否为admin角色,如果不需要可以设置为空
    token: 'xxx'         // 服务端返回token值
  },
});

群组操作

以下操作需要确保login异步操作成功后,方可执行。

获取群组管理器

展开查看代码

// 必须确保已经初始化,否则会返回空值
const groupManager = engine.getGroupManager();

添加和移除群组操作监听器

展开查看代码

// 在适当的时机(例如进入房间后,且完成登录后)添加群组操作事件监听器
groupManager.on('exit', (groupId: string, reason: number) => {
  // 退出群组
  console.log(`group ${groupId} close, reason: ${reason}`);
})
groupManager.on('memberchange', (groupId: string, memberCount: number, joinUsers: ImUser[], leaveUsers: ImUser[]) => {
  // 有人进入或离开群组
  console.log(`group ${groupId} member change, memberCount: ${memberCount}, joinUsers: ${joinUsers.map(u => u.userId).join(',')}, leaveUsers: ${leaveUsers.map(u => u.userId).join('')}`);
})
groupManager.on('mutechange', (groupId: string, status) => {
  // 群组的禁言状态发生了变化
  console.log(`group ${groupId} mute change`);
})
groupManager.on('infochange', (groupId: string, info) => {
  // 有人离开了群组
  console.log(`group ${groupId} info change`);
})

// 如需移除监听某个事件,请使用 off 方法取消对应的事件
groupManager.off('infochange');

// 如需移除监听所有事件,则使用 removeAllListeners 方法
groupManager.removeAllListeners();

创建群组

仅限管理员调用,否则会调用失败。

展开查看代码

await groupManager.createGroup({
  groupId: '',       // 群组id,为空时,系统创建新群组后会返回唯一id
  groupName: 'xxx',  // 群组昵称,一定要设置,否则会失败
  groupMeta: 'xxx'   // 群组扩展信息,如果有多个字段可以考虑封装为json字符串
});

关闭群组

仅限群主/群管理员调用,否则会调用失败。

展开查看代码

// 参数为 groupId
await groupManager.closeGroup('xxx');

进入群组

展开查看代码

// 参数为 groupId
await groupManager.joinGroup('xxx');

离开群组

展开查看代码

// 参数为 groupId
await groupManager.leaveGroup('xxx');

查询群组信息

展开查看代码

// 参数为 groupId
const groupInfo = await groupManager.queryGroup('xxx');

修改群组信息

支持修改群扩展信息和设置管理员,仅限群主/群管理员调用,否则会调用失败。

展开查看代码

await groupManager.modifyGroup({
  groupId: 'xxx',  // 群组ID
  forceUpdateAdmins: true, // 是否强制修改群管理员
  admins: ['xxx'], // 群管理员ID 列表,需要清空时传空数组且 forceUpdateAdmins 传 true
  forceUpdateGroupMeta: true, // 是否强制修改群扩展信息
  groupMeta: 'xxx' // 群信息扩展信息Meta,需要清空时传空字符串且 forceUpdateGroupMeta 传 true
});

查询群组最近成员列表

展开查看代码

// 参数为 groupId
const recentGroupUserInfo = groupManager.listRecentGroupUser(groupId);

查询群组全部成员

仅限群主/群管理员调用,否则会调用失败。

展开查看代码

const recentGroupUserInfo = groupManager.listGroupUser({
  groupId: 'xxx',    // 群组ID
  nextpagetoken: 12, // 默认表示第一页,遍历时服务端会返回,客户端获取下一页时,应带上
  pageSize: 10,      // 最大不超过50
  sortType: ImSortType.ASC // 排序方式,ASC-先加入优先,DESC-后加入优先
});

对群组进行禁言

仅限群主/群管理员调用,否则会调用失败。

展开查看代码

// 参数为 groupId
await groupManager.muteAll('xxx');

对群组取消禁言

仅限群主/群管理员调用,否则会调用失败。

展开查看代码

// 参数为 groupId
await groupManager.cancelMuteAll('xxx');

对群组的用户进行禁言

仅限群主/群管理员调用,否则会调用失败。

展开查看代码

await groupManager.muteUser({
  groupId: 'xxx',    // 群组id
  userList: ['xxx']  // 需要禁言的用户id列表
});

对群组用户取消禁言

仅限群主/群管理员调用,否则会调用失败。

展开查看代码

await groupManager.cancelMuteUser({
  groupId: 'xxx',    // 群组ID
  userList: ['xxx']  // 需要取消禁言的用户ID列表
});

查询群组内被禁言的用户列表

仅限群主/群管理员调用,否则会调用失败。

展开查看代码

// 参数为 groupId
const muteUsersInfo = await groupManager.listMuteUsers('xxx');

消息操作

获取消息管理器

展开查看代码

// 必须确保已经初始化,否则会返回空值
const messageManager = await engine.getMessageManager();

添加和移除消息操作监听器

展开查看代码

// 收到其他用户发来的单聊消息
messageManager.on("recvc2cmessage", (msg) => {
  console.log('recvc2cmessage', msg);
});

// 收到群聊消息
messageManager.on("recvgroupmessage", (msg, groupId) => {
  console.log('recvgroupmessage', msg, groupId);
});

// 群消息删除
messageManager.on('deletegroupmessage', (msgId, groupId) => {
  console.log(`group ${groupId} delete message ${msgId}`)
});

// 如需移除监听某个事件,请使用 off 方法取消对应的事件
messageManager.off('recvgroupmessage');

// 如需移除监听所有事件,则使用 removeAllListeners 方法
messageManager.removeAllListeners();

单发消息

展开查看代码

try {
  const messageId = await messageManager.sendC2cMessage({
    receiverId: 'xxx',     // 接收者id
    data: 'xxx',           // 发送消息内容,如果是结构化可考虑使用JSON字符串
    type: 88888,           // 自定义消息类型,需大于10000
    skipAudit: false,      // 可选(默认 false),跳过安全审核,true:发送的消息不经过阿里云安全审核服务审核;false:发送的消息经过阿里云安全审核服务审核,审核失败则不发送。
    level: ImMessageLevel.NORMAL // 可选(默认 NORMAL), 消息分级,需要高可靠时,使用 ImMessageLevel.HIGH
  });
  console.log('send success, messageId: ', messageId);
} catch (error) {
  // error.code 为 424 时为对方未在线,建议待对方上线后再重发
  console.log('send fail', error);
}

群发消息

展开查看代码

// 发群组消息前请确保已加入群组
try {
  const messageId = await messageManager.sendGroupMessage({
    groupId: 'xxx',       // 群组id
    data: 'xxx',          // 发送消息内容,如果是结构化可考虑使用json字符串
    type: 88888,          // 自定义消息类型,需大于10000
    skipAudit: false,     // 可选(默认 false),跳过安全审核,true:发送的消息不经过阿里云安全审核服务审核;false:发送的消息经过阿里云安全审核服务审核,审核失败则不发送。
    skipMuteCheck: false, // 可选(默认 false),跳过禁言检测,true:忽略被禁言用户,还可发消息;false:当被禁言时,消息无法发送
    level: ImMessageLevel.NORMAL, // 可选(默认 NORMAL), 消息分级,需要高可靠时,使用 ImMessageLevel.HIGH
    noStorage: true,     // 可选(默认 false)true:表示该消息不需要存储,也无法拉取查询;false:消息进行存储,可以拉取查询。如果在直播间的弹幕场景,需要设置为 false。
    repeatCount: 1        // 可选(默认 1)本消息重复数量,内容完成一样的消息可以使用该字段进行聚合,并发送一次即可。
  });
  console.log('send success, messageId: ', messageId);
} catch (error) {
  // error.code 为 425 时为未加入群组
  console.log('send fail', error);
}

删除/撤回群消息

该接口执行成功后,接收者会收到回调。

展开查看代码

// 删除/撤回群组消息前请确保已加入群组
await messageManager.deleteMessage({
  groupId: 'xxx',       // 群组id
  messageId: 'xxx',     // 需要删除的消息 id
});

查询最近群发消息列表

加入群组后,调用该接口可返回最近50条消息,所有类型的用户均可调用。

展开查看代码

// 查询群发消息列表前请确保已加入群组
const messagesInfo = await messageManager.listRecentMessage({
  groupId: 'xxx',     // 群组ID
})

查询全部群发消息列表

仅限群主/群管理员调用,否则会调用失败。普通用户建议使用listRecentMessage接口获取最近50条消息。

展开查看代码

// ImSortType.ASC 正序,ImSortType.DESC 倒序
// 查询全部群发消息列表前请确保已加入群组
const { ImSortType } = window.AliVCInteraction;

// 需要先调用 engine.getMessageManager 方法得到 messageManager 对象
const messageList = await messageManager.listMessage({
  groupId: 'xxx',    // 群组ID
  type: 88888,       // 自定义消息类型,需大于10000
  nextPageToken: 12, // 不传时表示第一页,遍历时服务端会返回下一页Token,客户端获取下一页时应带上
  pageSize: 10,      // 默认10条,最大30条
  sortType: ImSortType.ASC // 排序方式,默认为时间递增
});

查询历史消息

该接口主要用户直播结束后的历史消息回放,用户无需进入群组可查询,比较耗时,在直播过程中不建议使用,该接口后续会收费。

展开查看代码

// ImSortType.ASC 正序,ImSortType.DESC 倒序
const { ImSortType } = window.AliVCInteraction;

// 需要先调用 engine.getMessageManager 方法得到 messageManager 对象
const messageList = messageManager.listHistoryMessage({
  groupId: 'xxx',    // 群组ID
  type: 88888,       // 消息类型,自定义消息类型需大于10000
  nextPageToken: 12, // 可选,不传时表示第一页,遍历时服务端会返回下一页Token,客户端获取下一页时应带上
  pageSize: 10,      // 默认10条,最大30条
  sortType: ImSortType.ASC, // 排序方式,默认为时间递增
  beginTime: 0,      // 可选(默认 0),开始时间,秒级时间戳
  endTime: 0         // 可选(默认 0),结束时间,秒级时间戳
});

登出

await engine.logout();

反初始化

登出后如无需再进行登录,可以进行反初始化操作,SDK会对底层操作实例进行释放。

engine.unInit()