tencent cloud

文档反馈

Android&iOS&Windows&Mac

最后更新时间:2023-07-17 14:24:34

    功能描述

    消息已读回执指的是,发送端往会话中发送消息,如果他想知道哪些人读过/还没读过这条消息,那么他就需要开启消息已读回执功能。 开启后,发送端发送消息时可以设置消息是否需要已读回执。如果设置为“需要”,接收端查看消息后才会发送自己已读给发送端。
    单聊和群聊均支持消息已读回执功能,操作步骤一致。
    说明
    该功能仅对进阶版客户开放,购买 进阶版 后可使用。
    群聊已读回执仅增强版 6.1.2155 及以上版本支持。
    单聊已读回执仅增强版 6.2.2363 及以上版本支持。

    消息已读回执

    设置支持已读回执的群类型

    如果要支持群聊消息已读回执,需要先在 即时通信 IM 控制台 > 功能配置 > 登录与消息 > 群已读消息回执配置中,设置支持已读回执的群类型,勾选的群类型才能使用已读回执功能。
    单聊消息已读回执不需要在控制台做任何设置,默认支持已读回执。

    发送端设置消息需要已读回执

    发送端创建消息后,先通过消息对象 V2TIMMessageneedReadReceipt(Android / iOS & Mac / Windows) 字段设置这条消息需要已读回执,再发送消息到会话中。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    V2TIMMessage message = V2TIMManager.getMessageManager().createTextMessage("群已读回执消息");
    // 设置消息需要已读回执
    message.setNeedReadReceipt(true);
    // 发送消息
    V2TIMManager.getMessageManager().sendMessage(message, null, "groupA", V2TIMMessage.V2TIM_PRIORITY_NORMAL, false, null, new V2TIMSendCallback<V2TIMMessage>() {
    @Override
    public void onProgress(int progress) {
    }
    
    @Override
    public void onSuccess(V2TIMMessage message) {
    }
    
    @Override
    public void onError(int code, String desc) {
    }
    });
    /// 接口调用示例
    V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:@"群已读回执消息"];
    // 设置消息需要已读回执
    message.needReadReceipt = YES;
    // 发送消息
    [[V2TIMManager sharedInstance] sendMessage:message receiver:nil groupID:@"groupA" priority:V2TIM_PRIORITY_NORMAL onlineUserOnly:NO offlinePushInfo:nil progress:nil succ:nil fail:nil];
    class SendCallback final : public V2TIMSendCallback {
    public:
    using SuccessCallback = std::function<void(const V2TIMMessage&)>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    using ProgressCallback = std::function<void(uint32_t)>;
    
    SendCallback() = default;
    ~SendCallback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback,
    ProgressCallback progress_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    progress_callback_ = std::move(progress_callback);
    }
    
    void OnSuccess(const V2TIMMessage& message) override {
    if (success_callback_) {
    success_callback_(message);
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    void OnProgress(uint32_t progress) override {
    if (progress_callback_) {
    progress_callback_(progress);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    ProgressCallback progress_callback_;
    };
    
    V2TIMMessage message = V2TIMManager::GetInstance()->GetMessageManager()->CreateTextMessage(u8"群已读回执消息");
    // 设置消息需要已读回执
    message.setNeedReadReceipt(true);
    
    auto callback = new SendCallback{};
    callback->SetCallback([=](const V2TIMMessage& message) { delete callback; },
    [=](int error_code, const V2TIMString& error_message) { delete callback; },
    [=](uint32_t progress) {});
    
    V2TIMManager::GetInstance()->GetMessageManager()->SendMessage(
    message, "groupA", {}, V2TIMMessagePriority::V2TIM_PRIORITY_DEFAULT, false, {}, callback);

    接收端发送消息已读回执

    接收端收到消息后,根据消息对象 V2TIMMessageneedReadReceipt 字段判断消息是否需要已读回执。如果需要,当用户查看消息后,调用 sendMessageReadReceipts(Android / iOS & Mac / Windows) 接口发送消息已读回执。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    // 假设 message 消息用户已经查看
    if (!message.isSelf() && message.isNeedReadReceipt()) {
    List<V2TIMMessage> messageList = new ArrayList<>();
    messageList.add(message);
    V2TIMManager.getMessageManager().sendMessageReadReceipts(messageList, new V2TIMCallback() {
    @Override
    public void onSuccess() {
    // 发送消息已读回执成功
    }
    
    @Override
    public void onError(int code, String desc) {
    // 发送消息已读回执失败
    }
    });
    }
    /// 接口调用示例
    /// 假设 msg 消息用户已经查看
    if (!msg.isSelf && msg.needReadReceipt) {
    [[V2TIMManager sharedInstance] sendMessageReadReceipts:@[msg] succ:^{
    // 发送消息已读回执成功
    } fail:^(int code, NSString *desc) {
    // 发送消息已读回执失败
    }];
    }
    class Callback final : public V2TIMCallback {
    public:
    using SuccessCallback = std::function<void()>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    
    Callback() = default;
    ~Callback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    }
    
    void OnSuccess() override {
    if (success_callback_) {
    success_callback_();
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    };
    
    auto callback = new Callback;
    callback->SetCallback(
    [=]() {
    // 发送消息已读回执成功
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // 发送消息已读回执失败
    delete callback;
    });
    
    // 假设 message 消息用户已经查看
    if (!message.isSelf && message.needReadReceipt) {
    V2TIMMessageVector messageList;
    messageList.PushBack(message);
    V2TIMManager::GetInstance()->GetMessageManager()->SendMessageReadReceipts(messageList, callback);
    }

    发送端监听消息已读回执通知

    接收端发送消息已读回执后,发送端可以在 V2TIMAdvancedMsgListeneronRecvMessageReadReceipts(Android / iOS & Mac / Windows) 中收到已读回执通知,在通知中更新 UI,例如更新某条消息为 “2 人已读”。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    V2TIMAdvancedMsgListener advancedMsgListener = new V2TIMAdvancedMsgListener() {
    @Override
    public void onRecvMessageReadReceipts(List<V2TIMMessageReceipt> receiptList) {
    for (V2TIMMessageReceipt receipt : receiptList) {
    // 已读回执消息 ID
    String msgID = receipt.getMsgID();
    // C2C 消息对方 ID
    String userID = receipt.getUserID();
    // C2C 消息对方已读状态
    boolean isPeerRead = receipt.isPeerRead();
    // 群消息最新已读数
    long readCount = receipt.getReadCount();
    // 群消息最新未读数
    long unreadCount = receipt.getUnreadCount();
    }
    }
    };
    V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);
    /// 接口调用示例
    [[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
    - (void)onRecvMessageReadReceipts:(NSArray<V2TIMMessageReceipt *> *)receiptList {
    for(V2TIMMessageReceipt *receipt in receiptList) {
    // 已读回执消息 ID
    NSString *msgID = receipt.msgID;
    // C2C 消息对方 ID
    NSString * userID = receipt.userID;
    // C2C 消息对方已读状态
    BOOL isPeerRead = receipt.isPeerRead;
    // 群组 ID
    NSString * groupID = receipt.groupID;
    // 群消息最新已读数
    uint64_t readCount = receipt.readCount;
    // 群消息最新未读数
    uint64_t unreadCount = receipt.unreadCount;
    }
    }
    class AdvancedMsgListener final : public V2TIMAdvancedMsgListener {
    public:
    /**
    * 消息已读回执通知
    *
    * @param receiptList 已读回执列表
    */
    void OnRecvMessageReadReceipts(const V2TIMMessageReceiptVector& receiptList) override {
    for (size_t i = 0; i < receiptList.Size(); ++i) {
    const V2TIMMessageReceipt& receipt = receiptList[i];
    // 已读回执消息 ID
    V2TIMString msgID = receipt.msgID;
    // C2C 消息对方 ID
    V2TIMString userID = receipt.userID;
    // C2C 消息对方已读状态
    bool isPeerRead = receipt.isPeerRead;
    // 群组 ID
    V2TIMString groupID = receipt.groupID;
    // 群消息最新已读数
    int32_t readCount = receipt.readCount;
    // 群消息最新未读数
    int32_t unreadCount = receipt.unreadCount;
    }
    }
    // 其他成员 ...
    };
    
    // 添加高级消息的事件监听器,注意在移除监听器之前需要保持 advancedMsgListener 的生命期,以免接收不到事件回调
    AdvancedMsgListener advancedMsgListener;
    V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&advancedMsgListener);

    发送端主动拉取消息已读回执信息

    发送端从其他界面进入消息列表后,先拉取历史消息,再调用 getMessageReadReceipts(Android / iOS & Mac / Windows) 接口拉取消息已读回执信息。
    其中已读回执信息 V2TIMMessageReceipt 字段含义如下:
    属性
    含义
    说明
    msgID
    消息 ID
    消息唯一 ID。
    userID
    对端用户 ID
    如果是单聊,该字段表示对端用户 ID。
    isPeerRead
    消息对端用户是否已读
    如果是单聊,该字段表示消息对端用户是否已读。
    timestamp
    对端用户标记会话已读时间
    该字段在消息已读场景无效。如果是单聊,当对端用户调用 markC2CMessageAsRead 接口标记会话已读时,自己会收到的 onRecvC2CReadReceipt 回调,回调里会携带 timestamp 信息。markC2CMessageAsRead 的使用请参考:会话未读数
    groupID
    群组 ID
    如果是群聊,该字段为群组 ID。
    readCount
    群消息已读人数
    如果是群聊,该字段为群消息已读人数。
    unreadCount
    群消息未读人数
    如果是群聊,该字段为群消息未读人数。
    示例代码如下:
    Android
    iOS & Mac
    Windows
    /// 接口调用示例(以 Group 消息为例)
    V2TIMManager.getMessageManager().getGroupHistoryMessageList("groupA", 20, null, new V2TIMValueCallback<List<V2TIMMessage>>() {
    @Override
    public void onSuccess(final List<V2TIMMessage> v2TIMMessages) {
    List<V2TIMMessage> receiptMsgs = new ArrayList<>();
    // 自己发送的消息 && 需要已读回执,需要拉取消息的已读回执信息
    for (V2TIMMessage msg : v2TIMMessages) {
    if (msg.isSelf() && msg.isNeedReadReceipt()) {
    receiptMsgs.add(msg);
    }
    }
    V2TIMManager.getMessageManager().getMessageReadReceipts(receiptMsgs, new V2TIMValueCallback<List<V2TIMMessageReceipt>>() {
    @Override
    public void onSuccess(List<V2TIMMessageReceipt> v2TIMMessageReceipts) {
    Map<String, V2TIMMessageReceipt> messageReceiptMap = new HashMap<>();
    for (V2TIMMessageReceipt receipt : v2TIMMessageReceipts) {
    messageReceiptMap.put(receipt.getMsgID(), receipt);
    }
    for (V2TIMMessage msg : v2TIMMessages) {
    V2TIMMessageReceipt receipt = messageReceiptMap.get(msg.getMsgID());
    if (receipt != null) {
    // C2C 消息对方 ID
    String userID = receipt.getUserID();
    // C2C 消息对方已读状态
    boolean isPeerRead = receipt.isPeerRead();
    // 群组 ID
    String groupID = receipt.getGroupID();
    // 消息已读数,readCount 为 0,表示消息无人已读
    long readCount = receipt.getReadCount();
    // 消息未读数,unreadCount 为 0,表示消息全部已读
    long unreadCount = receipt.getUnreadCount();
    }
    }
    }
    
    @Override
    public void onError(int code, String desc) {
    // 拉取消息已读状态失败
    }
    });
    }
    
    @Override
    public void onError(int code, String desc) {
    // 拉取消息失败
    }
    });
    /// 接口调用示例(以 Group 消息为例)
    [[V2TIMManager sharedInstance] getGroupHistoryMessageList:@"groupA" count:20 lastMsg:nil succ:^(NSArray<V2TIMMessage *> *msgs) {
    NSMutableArray *receiptMsgs = [NSMutableArray array];
    // 自己发送的消息 && 需要已读回执,需要拉取消息的已读回执信息
    for (V2TIMMessage *msg in msgs) {
    if (msg.isSelf && msg.needReadReceipt) {
    [receiptMsgs addObject:msg];
    }
    }
    [[V2TIMManager sharedInstance] getMessageReadReceipts:receiptMsgs succ:^(NSArray<V2TIMMessageReceipt *> *receiptList) {
    NSMutableDictionary *param = [NSMutableDictionary dictionary];
    for (V2TIMMessageReceipt *receipt in receiptList) {
    [param setObject:receipt forKey:receipt.msgID];
    }
    for (V2TIMMessage *msg in msgs) {
    V2TIMMessageReceipt *receipt = param[msg.msgID];
    // C2C 消息对方 ID
    NSString * userID = receipt.userID;
    // C2C 消息对方已读状态
    BOOL isPeerRead = receipt.isPeerRead;
    // 群组 ID
    NSString * groupID = receipt.groupID;
    // 消息已读数,readCount 为 0,表示消息无人已读
    uint64_t readCount = receipt.readCount;
    // 消息未读数,unreadCount 为 0,表示消息全部已读
    uint64_t unreadCount = receipt.unreadCount;
    }
    } fail:^(int code, NSString *desc) {
    // 拉取消息已读状态失败
    }];
    } fail:^(int code, NSString *desc) {
    // 拉取消息失败
    }];
    template <class T>
    class ValueCallback final : public V2TIMValueCallback<T> {
    public:
    using SuccessCallback = std::function<void(const T&)>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    
    ValueCallback() = default;
    ~ValueCallback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    }
    
    void OnSuccess(const T& value) override {
    if (success_callback_) {
    success_callback_(value);
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    };
    
    // 接口调用示例(以 Group 消息为例)
    V2TIMMessageListGetOption option;
    option.getType = V2TIMMessageGetType::V2TIM_GET_CLOUD_OLDER_MSG; // 拉取云端的更老的消息
    option.count = 20; // 每页 20 条
    option.groupID = "groupA"; // 拉取群聊消息
    
    auto callback = new ValueCallback<V2TIMMessageVector>{};
    callback->SetCallback(
    [=](const V2TIMMessageVector& messageList) {
    V2TIMMessageVector receiptMsgs;
    for (size_t i = 0; i < messageList.Size(); ++i) {
    const V2TIMMessage& message = messageList[0];
    // 自己发送的消息 && 需要已读回执,需要拉取消息的已读回执信息
    if (message.isSelf && message.needReadReceipt) {
    receiptMsgs.PushBack(message);
    }
    }
    
    auto receipt_callback = new ValueCallback<V2TIMMessageReceiptVector>{};
    receipt_callback->SetCallback(
    [=](const V2TIMMessageReceiptVector& receiptList) {
    std::unordered_map<V2TIMString, V2TIMMessageReceipt> map;
    for (size_t i = 0; i < receiptList.Size(); ++i) {
    const V2TIMMessageReceipt& receipt = receiptList[i];
    map[receipt.msgID] = receipt;
    }
    for (size_t i = 0; i < messageList.Size(); ++i) {
    const V2TIMMessage& message = messageList[0];
    if (map.count(message.msgID)) {
    const V2TIMMessageReceipt& receipt = map[message.msgID];
    // C2C 消息对方 ID
    V2TIMString userID = receipt.userID;
    // C2C 消息对方已读状态
    bool isPeerRead = receipt.isPeerRead;
    // 群组 ID
    V2TIMString groupID = receipt.groupID;
    // 消息已读数,readCount 为 0,表示消息无人已读
    int32_t readCount = receipt.readCount;
    // 消息未读数,unreadCount 为 0,表示消息全部已读
    int32_t unreadCount = receipt.unreadCount;
    }
    const V2TIMMessageReceipt& receipt = receiptList[i];
    }
    
    delete receipt_callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // 拉取消息已读状态失败
    delete receipt_callback;
    });
    
    V2TIMManager::GetInstance()->GetMessageManager()->GetMessageReadReceipts(messageList, receipt_callback);
    
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // 拉取消息失败
    delete callback;
    });
    
    V2TIMManager::GetInstance()->GetMessageManager()->GetHistoryMessageList(option, callback);

    发送端主动拉取群消息已读或未读成员列表

    发送端在需要查看群消息已读或未读成员列表时,可以调用 getGroupMessageReadMemberList(Android / iOS & Mac / Windows) 接口分页拉取消息已读或未读群成员列表。
    Android
    iOS & Mac
    Windows
    /// 接口调用示例
    V2TIMManager.getMessageManager().getGroupMessageReadMemberList(message, V2TIMMessage.V2TIM_GROUP_MESSAGE_READ_MEMBERS_FILTER_READ, 0, 100, new V2TIMValueCallback<V2TIMGroupMessageReadMemberList>() {
    @Override
    public void onSuccess(V2TIMGroupMessageReadMemberList v2TIMGroupMessageReadMemberList) {
    // members 当前分页拉取的已读成员列表
    List<V2TIMGroupMemberInfo> members = v2TIMGroupMessageReadMemberList.getMemberInfoList();
    // nextSeq 下次分页拉取的游标位置
    long nextSeq = v2TIMGroupMessageReadMemberList.getNextSeq();
    // isFinished 已读列表是否已经全部拉取完毕
    boolean isFinished = v2TIMGroupMessageReadMemberList.isFinished();
    // 如果已读列表没有全部拉取完毕,继续下一页拉取(这里只是示例代码,分页拉取建议由用户点击 UI 触发)
    if (!isFinished) {
    V2TIMManager.getMessageManager().getGroupMessageReadMemberList(message, V2TIMMessage.V2TIM_GROUP_MESSAGE_READ_MEMBERS_FILTER_READ, nextSeq, 100, new V2TIMValueCallback<V2TIMGroupMessageReadMemberList>() {
    @Override
    public void onSuccess(V2TIMGroupMessageReadMemberList v2TIMGroupMessageReadMemberList) {
    // 拉群已读成员列表成功
    }
    
    @Override
    public void onError(int code, String desc) {
    // 拉群已读成员列表失败
    }
    });
    }
    }
    
    @Override
    public void onError(int code, String desc) {
    // 拉群已读成员列表失败
    }
    });
    /// 接口调用示例
    [[V2TIMManager sharedInstance] getGroupMessageReadMemberList:message filter:V2TIM_GROUP_MESSAGE_READ_MEMBERS_FILTER_READ nextSeq:0 count:100 succ:^(NSMutableArray<V2TIMGroupMemberInfo *> *members, uint64_t nextSeq, BOOL isFinished) {
    // members 当前分页拉取的已读成员列表
    // nextSeq 下次分页拉取的游标位置
    // isFinished 已读列表是否已经全部拉取完毕
    // 如果已读列表没有全部拉取完毕,继续下一页拉取(这里只是示例代码,分页拉取建议由用户点击 UI 触发)
    if (!isFinished) {
    [[V2TIMManager sharedInstance] getGroupMessageReadMemberList:message filter:V2TIM_GROUP_MESSAGE_READ_MEMBERS_FILTER_READ nextSeq:nextSeq count:100 succ:^(NSMutableArray<V2TIMGroupMemberInfo *> *members, uint64_t nextSeq, BOOL isFinished) {
    // 拉群已读成员列表成功
    } fail:^(int code, NSString *desc) {
    // 拉群已读成员列表失败
    }];
    }
    } fail:^(int code, NSString *desc) {
    // 拉群已读成员列表失败
    }];
    template <class T>
    class ValueCallback final : public V2TIMValueCallback<T> {
    public:
    using SuccessCallback = std::function<void(const T&)>;
    using ErrorCallback = std::function<void(int, const V2TIMString&)>;
    
    ValueCallback() = default;
    ~ValueCallback() override = default;
    
    void SetCallback(SuccessCallback success_callback, ErrorCallback error_callback) {
    success_callback_ = std::move(success_callback);
    error_callback_ = std::move(error_callback);
    }
    
    void OnSuccess(const T& value) override {
    if (success_callback_) {
    success_callback_(value);
    }
    }
    void OnError(int error_code, const V2TIMString& error_message) override {
    if (error_callback_) {
    error_callback_(error_code, error_message);
    }
    }
    
    private:
    SuccessCallback success_callback_;
    ErrorCallback error_callback_;
    };
    
    auto callback = new ValueCallback<V2TIMGroupMessageReadMemberList>{};
    callback->SetCallback(
    [=](const V2TIMGroupMessageReadMemberList& groupMessageReadMemberList) {
    // members 当前分页拉取的已读成员列表
    const V2TIMGroupMemberInfoVector& members = groupMessageReadMemberList.members;
    // nextSeq 下次分页拉取的游标位置
    uint64_t nextSeq = groupMessageReadMemberList.nextSeq;
    // isFinished 已读列表是否已经全部拉取完毕
    bool isFinished = groupMessageReadMemberList.isFinished;
    // 如果已读列表没有全部拉取完毕,继续下一页拉取(这里只是示例代码,分页拉取建议由用户点击 UI 触发)
    if (!isFinished) {
    auto callback = new ValueCallback<V2TIMGroupMessageReadMemberList>{};
    callback->SetCallback(
    [=](const V2TIMGroupMessageReadMemberList& groupMessageReadMemberList) {
    // 拉群已读成员列表成功
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // 拉群已读成员列表失败
    delete callback;
    });
    V2TIMManager::GetInstance()->GetMessageManager()->GetGroupMessageReadMemberList(
    message, V2TIMGroupMessageReadMembersFilter::V2TIM_GROUP_MESSAGE_READ_MEMBERS_FILTER_READ,
    nextSeq, 100, callback);
    }
    
    delete callback;
    },
    [=](int error_code, const V2TIMString& error_message) {
    // 拉群已读成员列表失败
    delete callback;
    });
    
    V2TIMManager::GetInstance()->GetMessageManager()->GetGroupMessageReadMemberList(
    message, V2TIMGroupMessageReadMembersFilter::V2TIM_GROUP_MESSAGE_READ_MEMBERS_FILTER_READ, 0, 100,
    callback);
    
    联系我们

    联系我们,为您的业务提供专属服务。

    技术支持

    如果你想寻求进一步的帮助,通过工单与我们进行联络。我们提供7x24的工单服务。

    7x24 电话支持