Windows(PC) SDK 开发手册

网易云通信服务概要

网易云通信以提供客户端SDK(覆盖Android、iOS、Web、PC)和服务端OPEN API 的形式提供即时通讯云服务。开发者只需想好APP 的创意及UI 展现方案,根据APP 业务需求选择网易云通信的相应功能接入即可。

注:SDK 兼容的系统有 Windows xp(sp2及以上)、Windows 7、Windows 8/8.1、Windows 10。SDK 从V3.2.5版本开始全面支持32位和64位程序接入。

开发准备

SDK内容

SDK不提供debug版本的动态链接库供开发者调试,如果遇到问题请联系技术支持或在线客服。

快速接入SDK

关于如何快速集成SDK到现有项目,初始化SDK登录退出,发送/接收第一条IM消息,发送/接收第一条聊天室消息,如何开始基于IM Demo源码(官网下载页面可以下载)开发的信息请前往Windows(PC) SDK Getting Started

SDK数据目录

当收到多媒体消息后,SDK 会负责下载这些多媒体文件,同时SDK 还要记录一些log,因此SDK 需要一个数据缓存目录。该目录由第三方App 通过nim_client_init 初始化接口传入,默认为存放到系统的AppData 目录下,App 传入一个目录名即可,SDK 会自动生成用户数据缓存目录。数据缓存目录默认为"{系统的AppData 目录}\{App 传入的目录名}\NIM\{某个用户对应的用户数据目录}”,还可以由App 完全自定义用户数据目录,需要传入完整的路径,并确保读写权限正确。如果第三方App 需要清除缓存功能,可扫描该目录下的文件,按照你们的规则清理即可。 具体某个用户对应的缓存目录下面包含如下子目录:

SDK 提供了接口nim_tool_get_user_specific_appdata_dir 获取某个用户对应的具体类型的App data 目录(如图片消息文件存放目录,语音消息文件存放目录等)(注意:需要调用nim_free_buf 接口释放其返回的内存)。

接口调用与约定

SDK 提供的所有API 都是 C接口 ,根据模块划分为不同的API 文件,根据业务需要可以显式或者隐式的调用(3.4.0开始)。显式调用下,开发者可以在程序进行到任何时间点按需加载SDK dll, 功能完成后如果不需要继续使用NIM可以卸载SDK dll, 更加灵活和节省内存, 该模式下开发者只需要将SDK包里nim_c_sdk\include 目录下的模块定义头文件(命名方式如nim_xxx_def.h)加入到工程项目中。隐式调用下,开发者除了链接lib文件外,需要将SDK包里nim_c_sdk 目录里的API 头文件(命名方式如nim_xxx.h)以及相关常量定义的头文件等其他头文件一并加入到工程项目中。一般来说,每个模块都有对应的API 头文件和相关常量的定义头文件,命名上一一对应。

SDK 提供了3种类型的接口。

  1. 全局广播类通知的注册接口。

    常见的几类消息或通知是通过该注册的回调函数中通知开发者的,比如收到的消息,会话数据更新,用户相关数据的变更等,开发者需要在登录前提前注册好这些接口,例如:

    C++

     //注册数据同步结果的回调
     nim::DataSync::RegCompleteCb(nbase::Bind(&nim_comp::DataSyncCallback::SyncCallback, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    
     /* 以下注册的回调函数,都是在收到服务器推送的消息或事件时执行的。因此需要在程序开始时就注册好。 */
     //注册重连、被踢、掉线、多点登录、把移动端踢下线的回调
     nim::Client::RegReloginCb(&nim_comp::LoginCallback::OnReLoginCallback);
     nim::Client::RegKickoutCb(&nim_comp::LoginCallback::OnKickoutCallback);
     nim::Client::RegDisconnectCb(&nim_comp::LoginCallback::OnDisconnectCallback);
     nim::Client::RegMultispotLoginCb(&nim_comp::LoginCallback::OnMultispotLoginCallback);
     nim::Client::RegKickOtherClientCb(&nim_comp::LoginCallback::OnKickoutOtherClientCallback);
     nim::Client::RegSyncMultiportPushConfigCb(&nim_comp::MultiportPushCallback::OnMultiportPushConfigChange);
    
     //注册返回发送消息结果的回调,和收到消息的回调。
     nim::Talk::RegSendMsgCb(nbase::Bind(&nim_comp::TalkCallback::OnSendMsgCallback, std::placeholders::_1));
     nim::Talk::RegReceiveCb(nbase::Bind(&nim_comp::TalkCallback::OnReceiveMsgCallback, std::placeholders::_1));
     nim::Talk::RegRecallMsgsCallback(nbase::Bind(&nim_comp::TalkCallback::OnReceiveRecallMsgCallback, std::placeholders::_1, std::placeholders::_2));
     nim::Talk::RegReceiveMessagesCb(nbase::Bind(&nim_comp::TalkCallback::OnReceiveMsgsCallback, std::placeholders::_1));
     nim::MsgLog::RegMessageStatusChangedCb(nbase::Bind(&nim_comp::TalkCallback::OnMsgStatusChangedCallback, std::placeholders::_1));

    C#

    //注册数据同步结果的回调
    NIM.DataSync.RegCompleteCb(DataSyncDelegate cb);
    //登录结果通知,开发者可以通过注册该事件获取登录结果,或者在调用登录接口NIM.ClientAPI.Login 时填充回调函数
    NIM.ClientAPI.LoginResultHandler
    //注册客户端掉线回调
    NIM.ClientAPI.RegDisconnectedCb(Action handler);
    //注册客户端多点登录通知回调
    NIM.ClientAPI.RegMultiSpotLoginNotifyCb(MultiSpotLoginNotifyResultHandler handler);
    //注册将登录当前账号的其他端踢下线的结果回调
    NIM.ClientAPI.RegKickOtherClientCb(KickOtherClientResultHandler handler);
    //注册当前客户端被踢下线通知回调
    NIM.ClientAPI.RegKickoutCb(KickoutResultHandler handler)
    //注册自动重连结果回调
    NIM.ClientAPI.RegAutoReloginCb(LoginResultDelegate handler, string jsonExtension = null)

     //接收消息
    NIM.TalkAPI.OnReceiveMessageHandler += ReceiveMessageHandler;
    //发送消息结果
    NIM.TalkAPI.OnSendMessageCompleted += SendMessageResultHandler;
    //注册消息撤回结果回调
    NIM.TalkAPI.RegRecallMessageCallback(RecallMessageDelegate cb);

C

    // 数据同步结果通知(nim_data_sync)
    void nim_data_sync_reg_complete_cb(nim_data_sync_cb_func cb, const void *user_data);        
    // 接收会话消息通知(nim_talk)
    void nim_talk_reg_receive_cb(const char *json_extension, nim_talk_receive_cb_func cb, const void *user_data);        
    // 接收系统消息通知(nim_sysmsg)
    void nim_sysmsg_reg_sysmsg_cb(const char *json_extension, nim_sysmsg_receive_cb_func cb, const void *user_data);    
    // 发送消息结果通知(nim_talk)
    void nim_talk_reg_arc_cb(const char *json_extension, nim_talk_arc_cb_func cb, const void *user_data);
    // 发送自定义系统通知的结果通知(nim_talk)
    void nim_talk_reg_custom_sysmsg_arc_cb(const char *json_extension, nim_custom_sysmsg_arc_cb_func cb, const void *user_data);
    // 群组事件通知(nim_team)
    void nim_team_reg_team_event_cb(const char *json_extension, nim_team_event_cb_func cb, const void *user_data);    
    // 帐号被踢通知(nim_client)
    void nim_client_reg_kickout_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);        
    // 网络连接断开通知(nim_client)
    void nim_client_reg_disconnect_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);    
    // 将本帐号的其他端踢下线的结果通知(nim_client)
    void nim_client_reg_kickout_other_client_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data); 
    // 好友通知 
    void nim_friend_reg_changed_cb(const char *json_extension, nim_friend_change_cb_func cb, const void *user_data);    
    // 用户特殊关系通知
    void nim_user_reg_special_relationship_changed_cb(const char *json_extension, nim_user_special_relationship_change_cb_func cb, const void *user_data);    
    ...
  1. 异步接口。

    回调函数作为参数,传入执行接口,然后执行接口时,会触发传入的回调函数。注意,回调函数中如果涉及到跨线程资源的需要开发者切换线程操作,即使不涉及到资源问题,也要保证回调函数中不会处理耗时任务,以免堵塞SDK底层线程。

  2. 同步接口。

    为方便开发者使用,我们提供了一些同步接口,调用接口获取的内容同步返回,以block命名的都是同步接口,该类会堵塞SDK线程,谨慎使用。

从版本2.7.0开始,服务器和客户端上线了频控策略,与服务器有交互的接口(接口命名结尾为_online的查询接口以及命令类接口,如同意群邀请等)增加频控控制,开发者关注错误码416。

C++ SDK

为了方便桌面应用开发者更快的接入网易云通信SDK,PC SDK下载包中还提供了官方的C++ 封装层 项目文件及源码,接入和使用方法请看Windows(PC) SDK开发手册(C++ 封装层),目前IM Demo(C++)源码就是通过接入和调用该SDK完成IM功能的。

C# SDK

网易云通信SDK还提供了C# 程序集,方便.net 开发人员接入,PC SDK下载包中包括官方的C# 封装层 项目文件及源码,接入和使用方法请看Windows(PC) SDK开发手册(C# 封装层),目前IM Demo(C#)源码就是通过接入和调用该SDK完成IM功能的。

如果开发者在调用C接口过程中或者解析接口返回结果过程中出现疑问,可以参考和借鉴C++封装层。

初始化SDK

准备工作:将SDK 相关的dll 文件(nim.dll,nim_audio.dll, nim_tools_http.dll, nrtc.dll)放到App 的运行目录下,并将SDK 的配置文件目录nim_conf(目录里包含一个ver_ctrl.dll文件)放到App 的运行目录下。SDK 基于vs2010 开发,如果App 没有对应的运行时库文件(msvcp100.dll和msvcr100.dll),请将其放到App 的运行目录下。

准备工作完成后,在程序启动时,如果直接使用C接口,需要调用LoadLibrary 函数动态加载nim.dll,然后调用GetProcAddress获取API 接口:nim_client_init,调用此接口初始化NIM SDK。同时,SDK 能力的一些参数以及如果SDK 需要连接独立部署的服务器的地址等配置也是在初始化SDK 时传入。例如:

C++

nim::SDKConfig config;

//组装SDK能力参数(必填)
config.database_encrypt_key_ = "Netease";     //string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
config.sdk_log_level_ = ;                     //bool 选填,是否需要预下载附件(图片和语音),SDK默认预下载,如果有对带宽流量有较高要求的请关闭该选项,改为上层开发者按需下载附件文件
config.preload_image_quality_ = ;             //int 预下载图片质量,选填,范围0-100
config.preload_image_resize_ = ;             //string 预下载图片基于长宽做内缩略,选填,比如宽100高50,则赋值为100x50,中间为字母小写x
config.sync_session_ack_ = true;             //bool 选填,消息已读未读是否多端同步,默认true
config.login_max_retry_times_ = ;             //int 登录重试最大次数,如需设置建议设置大于3次,默认填0,SDK默认设置次数

//组装SDK独立部署的服务器配置(选填)
config.use_private_server_ = true;
config.rsa_public_key_module_ = "http://xxx";
config.default_nos_download_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");
config.lbs_address_ = "http://xxx";
config.nos_lbs_address_ = "http://xxx";
config.default_link_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");
config.default_nos_upload_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");
config.default_nos_access_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");

bool ret = nim::Client::Init("45c6af3c98409b18a84451215d0bdd6e", "Netease", "", config); // 载入网易云通信sdk,初始化安装目录和用户目录,第一个参数是appkey(此处填写的是测试示例)

C#

C# 程序在运行时需要使用到 C++ SDK 的dll,必须将SDK相关的dll 文件(nim.dll,nim\_audio.dll, nim\_tools\_http.dll, nrtc.dll)放到最终的生成文件目录,如果缺少运行时库文件(msvcp100.dll和msvcr100.dll)也需要拷贝到生成目录中,否则可能会出现找不到dll的异常。

C# 提供了参数配置类 NimUtility.NimConfig,通过创建该类对象完成参数配置。
var config = new NimUtility.NimConfig();
config.CommonSetting = new SdkCommonSetting();

//组装SDK能力参数
config.CommonSetting.DataBaseEncryptKey = "Netease"; //string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
config.CommonSetting.LogLevel = SdkLogLevel.Pro;  //SDK 日志记录级别 
config.CommonSetting.PredownloadAttachmentThumbnail = True;   //bool 选填,是否需要预下载附件(图片和语音),SDK默认预下载,如果有对带宽流量有较高要求的请关闭该选项,改为上层开发者按需下载附件文件
config.CommonSetting.UsePriviteServer = False;    //是否使用自定义服务器,默认False,如果使用自定义服务器,需设置config.PrivateServerSetting 对象

//组装SDK独立部署的服务器配置
config.PrivateServerSetting = new SdkPrivateServerSetting();
config.PrivateServerSetting.RSAPublicKey = "http://xxx";
config.PrivateServerSetting.DownloadServerList = new List<string>({"",""});
config.PrivateServerSetting.LbsAddress = "http://xxx";
config.PrivateServerSetting.NOSLbsAddress = "http://xxx";
config.PrivateServerSetting.LinkServerList = new List<string>({"",""});
config.PrivateServerSetting.UploadServerList = new List<string>({"",""});
config.PrivateServerSetting.AccessServerList = new List<string>({"",""});

bool ret = NIM.ClientAPI.Init("45c6af3c98409b18a84451215d0bdd6e", "NIMCSharpDemo", null, config); // 载入网易云通信sdk,初始化安装目录和用户目录,第一个参数是appkey(此处填写的是测试示例)

C

typedef bool(*nim_client_init)(const char *app_data_dir, const char *app_install_dir, const char *json_extension);

void foo()
{
    //获取SDK初始化接口
    HINSTANCE hInst = LoadLibraryW(L"nim.dll");
    nim_client_init func = (nim_client_init) GetProcAddress(hInst, "nim_client_init");

    //组装SDK初始化参数
    Json::Value config_root;

    //组装SDK能力参数(必填)
    Json::Value config_values;
    config_values[kNIMAppKey] = "xxxx"; //用户的APP key
    config_values[kNIMDataBaseEncryptKey] = ""; //string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
    config_values[kNIMPreloadAttach] = ;        //bool 选填,是否需要预下载附件(图片和语音),SDK默认预下载,如果有对带宽流量有较高要求的请关闭该选项,改为上层开发者按需下载附件文件
    config_values[kNIMPreloadImageQuality] = ;  //int 预下载图片质量,选填,范围0-100
    config_values[kNIMPreloadImageResize] = ;   //string 预下载图片基于长宽做内缩略,选填,比如宽100高50,则赋值为100x50,中间为字母小写x
    config_values[nim::kNIMSyncSessionAck] = ;            //bool 选填,消息已读未读是否多端同步,默认true
    config_values[nim::kNIMLoginRetryMaxTimes] = ;        //int 登录重试最大次数,如需设置建议设置大于3次,默认填0,SDK默认设置次数
    config_root[kNIMGlobalConfig] = config_values;

    //组装SDK独立部署的服务器配置(选填)
    Json::Value server_values;
    server_values[kNIMLbsAddress] = "http://xxx";             //lbs地址
    server_values[kNIMNosLbsAddress] = "http://xxx";        //nos lbs地址
    server_values[kNIMDefaultLinkAddress].append("xxx.xxx.xxx.xxx:xxxx");                //默认的link地址
    server_values[kNIMDefaultNosUploadAddress].append("http://xxx.xxx.xxx.xxx:xxxx");     //默认的nos上传地址
    server_values[kNIMDefaultNosDownloadAddress].append("http://xxx.xxx.xxx.xxx:xxxx");    //默认的nos下载地址
    server_values[kNIMRsaPublicKeyModule] = "";                //密钥
    server_values[kNIMRsaVersion] = 0;                        //密钥版本号      
    config_root[kNIMPrivateServerSetting] = server_values;
    config_root[kNIMAppKey] = "45c6af3c98409b18a84451215d0bdd6e"; //必填appkey(此处填写的是测试示例)

    //初始化SDK
    func("appdata path", "app installation path", config_root.toStyledString().c_str());
}

在程序退出前,调用接口nim_client_cleanup 进行NIM SDK 的清理工作,然后调用FreeLibrary 函数释放nim.dll,对于清理工作的注意事项请查看后续的"登出/退出和清理SDK"章节。

C++

nim::Client::Cleanup();

C#

NIM.ClientAPI.Cleanup();

C

typedef    void(*nim_client_cleanup)(const char *json_extension);

void foo()
{
    nim_client_cleanup func = (nim_client_cleanup) GetProcAddress(hInst, "nim_client_cleanup");
    func("");
    FreeLibrary(hInst);
}

登录与登出

登录集成必读

客户端代理

SDK目前支持配置代理,支持Socket4/5,暂不支持Http代理,音视频模块只支持Socket5代理。

C++

void foo()
{
    nim::Global::SetProxy(nim::kNIMProxySocks5, "123.123.123.123", "400", "test", "test");
}

C#

void foo()
{
    NIM.GlobalAPI.SetProxy(NIM.NIMProxyType.kNIMProxySocks5, "123.123.123.123", "400", "test", "test");
}

C

typedef void(*nim_global_set_proxy)(NIMProxyType, const char*, int, const char*, const char*);

void foo()
{
    nim_global_set_proxy func = (nim_global_set_proxy) GetProcAddress(hInst, "nim_global_set_proxy");
    func(kNIMProxySocks5, "123.123.123.123", "400", "test", "test");
}

首次登录

SDK 登录后会同步群信息,离线消息,漫游消息,系统通知等数据,所以首次登录前需要 提前注册所需的全局广播类通知的回调 (具体说明请参阅API 文档),以下以注册最近会话变更通知回调为例:

C++

void OnSessionChangeCallback(nim::NIMResCode rescode, const nim::SessionData& data, int total_unread_counts)
{
    if (rescode != nim::kNIMResSuccess)
    {
        return;
    }

    switch (data.command_)
    {
    case nim::kNIMSessionCommandAdd:
    case nim::kNIMSessionCommandUpdate:
    case nim::kNIMSessionCommandMsgDeleted:
    break;
    ···
    }
}

void foo()
{
    //注册全局会话列表变更通知函数
    nim::Session::RegChangeCb(&OnSessionChangeCallback);
}

C#

//C#通过订阅会话列表变更事件来获取更新通知:
NIM.Session.SessionAPI.RecentSessionChangedHandler += OnSessionChanged;

private void OnSessionChanged(object sender, SessionChangedEventArgs e)
{
    //根据会话列表变更类型进行相应的操作
    if (e.Info.Command == NIMSessionCommand.kNIMSessionCommandAdd ||
        e.Info.Command == NIMSessionCommand.kNIMSessionCommandUpdate)
    {
        //TODO:会话列表新增、更新

    }
    else if (e.Info.Command == NIMSessionCommand.kNIMSessionCommandRemove)
    {
        //TODO:删除会话项
    }
}

C

//全局会话列表变更通知函数
void CallbackSessionChange(int rescode, const char *result, int total_unread_counts, const char *json_extension, const void *user_data)
{
    if (rescode == kNIMResSuccess)
    {
        Json::Value value;
        Json::Reader reader;
        if (reader.parse(result, value))
        { 
            nim::NIMSessionCommand command = nim::NIMSessionCommand(value[nim::kNIMSessionCommand].asInt());
            switch (command)
            {
                case kNIMSessionCommandAdd:
                    ...
                case kNIMSessionCommandUpdate:    
                    ...
                ...                
            }

        }
    }
    else
    {
        ...
    }
}

typedef void(*nim_session_reg_change_cb)(const char *json_extension, nim_session_change_cb_func cb, const void *user_data);

void foo()
{
    //注册全局会话列表变更通知函数
    nim_session_reg_change_cb func = (nim_session_reg_change_cb)GetProcAddress(hInst, "nim_session_reg_change_cb");
    func(nullptr, &CallbackSessionChange, nullptr);    // 会话列表变更通知(nim_session)
}

登录

C++

void OnLoginCallback(const nim::LoginRes& login_res, const void* user_data)
{
    if (login_res.res_code_ == nim::kNIMResSuccess)
    {
        if (login_res.login_step_ == nim::kNIMLoginStepLogin)
        {
            ···
        }
    }
    else
    {
        ···
    }
}

void foo()
{
    nim::Client::Login(app_key, "app acount", "token", &OnLoginCallback);
}

C#

private void HandleLoginResult(NIM.NIMLoginResult result)
{
    //在UI线程进行处理登录结果
    Action action = () =>
    {
        if (result.LoginStep == NIM.NIMLoginStep.kNIMLoginStepLogin)
        {
            if (result.Code == NIM.ResponseCode.kNIMResSuccess)
            {
                //TODO:登录成功
            }
            else
            {
                //TODO:登录失败
            }
        }
    };
    this.Invoke(action);
}

private void foo()
{
    NIM.ClientAPI.Login(appkey, "app acount", "token", HandleLoginResult);
}

C

void CallbackLogin(const char* res, const void *user_data)
{
    Json::Value values;
    Json::Reader reader;
    if (reader.parse(res, values) && values.isObject())
    {
        if (values[kNIMLoginStep].asInt() == kNIMLoginStepLogin && values[kNIMErrorCode].asInt() == 200)
        {
            ...
        }
        ...
    }
}

typedef    void(*nim_client_login)(const char *app_key, const char *account, const char *password, const char *json_extension, nim_json_transport_cb_func cb, const void* user_data);

void foo()
{
    nim_client_login func = (nim_client_login) GetProcAddress(hInst, "nim_client_login");
    func("app key", "app account", "token", nullptr, &CallbackLogin, nullptr);
    //app key: 应用标识,不同应用之间的数据(用户,消息,群组等)是完全隔离的。开发自己的应用时,需要替换成自己申请来的app key
    //注意:替换成客户自己的app key之后,请不要对登录密码进行md5加密。
}

自动重新登录

SDK 在网络连接断开后,会监听网络状况,如果网络状况好转,那么SDK 会进行重新登录,登录结果可以在注册的重新登录回调函数中处理。

注意 自动重新登录的机制可能会失效:

如果重新登录回调函数返回的的错误号既不是kNIMResSuccess,也不是网络错误相关错误号(kNIMResTimeoutError或者kNIMResConnectionError),那么说明自动重新登录的机制已经失效,需要开发者退回到登录界面进行手动重新登录。(详见nim_client_reg_auto_relogin_cb接口说明)

手动重新登录

SDK 在重新登录失败后,可以由用户手动调用重新登录接口nim_client_relogin。

c++

nim::Client::Relogin();

c#

NIM.ClientAPI.Relogin();

c

typedef void(*nim_client_relogin)(const char *json_extension);
void foo()
{
    nim_client_relogin func = (nim_client_relogin) GetProcAddress(hInst, "nim_client_relogin");
    func(nullptr);
}

登出/退出和清理SDK

执行接口nim_client_logout 进行登出或退出,登出或退出的过程因为涉及到和服务器的交互以及需要将缓存中的数据持久化到本地,因此根据实际情况回调通知会有1s到20s左右的延迟。清理SDK必须在完全退出(收到退出回调)后执行。

Trick:因为退出和其他接口一样,总是在某些情况下会出现未及时返回需要上层界面等待的情况(比如网络原因导致服务器返回慢甚至超时,缓存数据大持久化到本地慢等原因),因此开发者可以通过暂时隐藏UI,让进程等待退出完成的回调后再执行清理SDK,退出程序的工作。

C++

void LoginCallback::OnLogoutCallback(nim::NIMResCode res_code)
{
    //如果是退出程序
    nim::Client::Cleanup();
    ...

    //如果是登出到登录界面,这里不能执行清理SDK工作,否则会报错
    ...
}

void foo()
{
    //登出到登录界面(nim::kNIMLogoutChangeAccout) 或者 退出程序(nim::kNIMLogoutAppExit)
    nim::Client::Logout(nim::kNIMLogoutAppExit, &OnLogoutCallback);
}

C#

private void foo()
{
    System.Threading.Semaphore s = new System.Threading.Semaphore(0, 1);
    NIM.ClientAPI.Logout(NIM.NIMLogoutType.kNIMLogoutAppExit, (r) =>
    {
        s.Release();
    });
    //需要logout执行完才能退出程序
    s.WaitOne(TimeSpan.FromSeconds(10));
    NIM.ClientAPI.Cleanup();
}

C

void CallbackLogout(const char* res, const void *user_data)
{
    ...
}

typedef    void(*nim_client_logout)(nim::NIMLogoutType logout_type, const char *json_extension, nim_json_transport_cb_func cb, const void* user_data);

void foo()
{
    nim_client_logout func = (nim_client_logout) GetProcAddress(hInst, "nim_client_logout");
    func(kNIMLogoutAppExit, nullptr, &CallbackLogout, nullptr);
}

断网重连通知

通过接口nim_client_reg_disconnect_cb 来注册连接断开回调函数,断开连接后根据实际需要做后续处理(比如提示用户)。

C++

void OnDisconnectCallback()
{
    ···
}

void foo()
{
    nim::Client::RegDisconnectCb(&OnDisconnectCallback);
}

C#

private foo()
{
    NIM.ClientAPI.RegDisconnectedCb(() =>
    {
         MessageBox.Show("网络连接断开,网络恢复后后会自动重连");
    });
}

C

void CallbackDisconnect(const char* res, const void* user_data)
{
    //解析res
}

typedef void(*nim_client_reg_disconnect_cb)(const char *json_extension, nim_json_transport_cb_func cb, const void* user_data);

void foo()
{
    nim_client_reg_disconnect_cb func = (nim_client_reg_disconnect_cb) GetProcAddress(hInst, "nim_client_reg_disconnect_cb");
    func(nullptr, &CallbackDisconnect, nullptr);
}

被踢出通知

通过接口nim_client_reg_kickout_cb 来注册被踢出回调函数,当收到此通知时,一般是退出程序然后回到登录窗口,回调函数参数中详细说明了被踢出的原因。

注意:聊天室被踢出通知不是通过这个接口监听的,具体请看下文 聊天室开发说明

C++

void OnKickoutCallback(const nim::KickoutRes& res)
{
    ···
}

void foo()
{
    nim::Client::RegKickoutCb(&OnKickoutCallback);
}

C#

private foo()
{
    NIM.ClientAPI.RegKickoutCb((r) =>
    {
        MessageBox.Show(r.Dump(), "被踢下线,请注意账号安全");
    });
}

C

void CallbackKickout(const char* res, const void* user_data)
{
    //解析res
}

typedef void(*nim_client_reg_kickout_cb)(const char *json_extension, nim_json_transport_cb_func cb, const void* user_data);

void foo()
{
    nim_client_reg_kickout_cb func = (nim_client_reg_kickout_cb) GetProcAddress(hInst, "nim_client_reg_kickout_cb");
    func(nullptr, &CallbackKickout, nullptr);
}

多端登录

通过调用nim_client_reg_multispot_login_notify_cb 来注册多端登录通知的回调函数,当用户在某个客户端登录时,其他没有被踢掉的端会触发这个回调函数,并携带当前时间登录的设备列表的数据。

网易云通信内置踢人策略为:移动端(Android,iOS)互踢,桌面端(PC,Web)互踢,移动端和桌面端共存。

如果当前的互踢策略无法满足业务需求的话,可以联系我们取消内置互踢,根据多端登录的回调和当前的设备列表,判断本设备是否需要被踢出。如果需要踢出,直接调用登出接口并在界面上给出相关提示即可。

c++

void LoginCallback::OnMultispotLoginCallback(const nim::MultiSpotLoginRes& res)
{
    ...
}
nim::Client::RegMultispotLoginCb(&nim_comp::LoginCallback::OnMultispotLoginCallback);

c#

//注册多端登录通知回调
NIM.ClientAPI.RegMultiSpotLoginNotifyCb(OnMultiSpotLogin);

//result 包含多端登录的其他端信息
private static void OnMultiSpotLogin(NIMMultiSpotLoginNotifyResult result)
{

}

c

static void CallbackMutliSpotLogin(const char* json_res, const void* user_data)
{
    Json::Reader reader;
    Json::Value values;
    if (json_res != nullptr && reader.parse((std::string)json_res, values) && values.isObject())
    {
        ...
    }
}

typedef void(*nim_client_reg_multispot_login_notify_cb)(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);
void foo()
{
    nim_client_reg_multispot_login_notify_cb func = (nim_client_reg_multispot_login_notify_cb) GetProcAddress(hInst, "nim_client_reg_multispot_login_notify_cb");
    func(nullptr, &CallbackMutliSpotLogin, nullptr);
}

注意:聊天室多端登录相关的通知不是通过这个接口监听的,具体请看下文 聊天室开发说明

网易云通信 ID

网易云通信的每个用户由网易云通信 ID 唯一标识。网易云通信 ID是由用户帐号和token成功同步到网易云通信服务器后自动分配的,可以通过 NIMMessage 的 from 字段获取消息发送方的网易云通信 ID。

基础消息功能

消息功能概述

SDK(PC) 支持文本、图片、音频、视频、地理位置、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息。

发送消息

以发送图片消息的为例:.

C++

//代码摘抄自DEMO源码
void SessionBox::SendImage( const std::wstring &src )
{
    nim::IMMessage msg;
    PackageMsg(msg);
    msg.type_ = nim::kNIMMessageTypeImage;

    std::wstring filename = nbase::UTF8ToUTF16(msg.client_msg_id_);
    std::wstring dest = GetUserImagePath() + filename;
    GenerateUploadImage(src, dest);
    msg.local_res_path_ = nbase::UTF16ToUTF8(dest);

    nim::IMImage img;
    img.md5_ = GetFileMD5(dest);
    img.size_ = (long)nbase::GetFileSize(dest);

    Gdiplus::Image image(dest.c_str());
    if (image.GetLastStatus() != Gdiplus::Ok)
    {
      assert(0);
    }
    else
    {
      img.width_ = image.GetWidth();
      img.height_ = image.GetHeight();
    }

    msg.attach_ = img.ToJsonString();

    std::string json_msg = nim::Talk::CreateImageMessage(msg.receiver_accid_, msg.session_type_, msg.client_msg_id_, img, msg.local_res_path_, msg.timetag_);
    nim::Talk::SendMsg(json_msg);
}

C#

void SendImage(string path)
{
    string fileName = System.IO.Path.GetFileNameWithoutExtension(path);
    string extension = System.IO.Path.GetExtension(path);

    NIM.NIMImageMessage imageMsg = new NIMImageMessage();
    imageMsg.LocalFilePath = path;
    imageMsg.ImageAttachment = new NIMImageAttachment();
    imageMsg.ImageAttachment.DisplayName = fileName;
    imageMsg.ImageAttachment.FileExtension = extension;
    imageMsg.ReceiverID = _peerId;
    imageMsg.SessionType = _sessionType;
    using (var i = Image.FromFile(path))
    {
        imageMsg.ImageAttachment.Height = i.Height;
        imageMsg.ImageAttachment.Width = i.Width;
    }
    TalkAPI.SendMessage(imageMsg);
}

C

typedef void(*nim_talk_send_msg)(const char* json_msg, const char *json_extension, nim_nos_upload_prg_cb_func prg_cb, const void *prg_user_data);

void foo()
{
    Json::Value json;
    json[kNIMMsgKeyToType]            = ; //int 会话类型(NIMSessionType 个人0 群组1)
    json[kNIMMsgKeyToAccount]        = ; //string 消息接收者id
    json[kNIMMsgKeyTime]            = ; //int64  消息发送时间(毫秒),可选
    json[kNIMMsgKeyType]            = ; //NIMMessageType 消息类型
    json[kNIMMsgKeyBody]            = ; //string 消息内容
    json[kNIMMsgKeyAttach]            = ; //string 附加内容
    json[kNIMMsgKeyClientMsgid]        = ; //string 消息id,一般使用guid
    json[kNIMMsgKeyResendFlag]        = ; //int    重发标记(0不是重发, 1是重发)
    json[kNIMMsgKeyLocalLogStatus]    = ; //NIMMsgLogStatus 消息状态
    json[kNIMMsgKeyType]             = kNIMMessageTypeImage;

    //图片详细信息
    Json::Value attach;
    attach[kNIMImgMsgKeyMd5] = ;      //string 文件md5,选填
    attach[kNIMImgMsgKeySize] = ;     //long   文件大小,选填
    attach[kNIMImgMsgKeyWidth] = ;  //int     图片宽度,必填
    attach[kNIMImgMsgKeyHeight] = ; //int     图片高度,必填
    json[kNIMMsgKeyAttach] = attach.toStyledString();

    json[kNIMMsgKeyLocalFilePath]    = ; //string    图片本地路径
    json[kNIMMsgKeyLocalLogStatus] = kNIMMsgLogStatusSending;

    nim_talk_send_msg func = (nim_talk_send_msg) GetProcAddress(hInst, "nim_talk_send_msg");
    func(json.toStyledString().c_str(), "", NULL, "");
}

发送消息状态通过登录前注册的全局回调获取。

C++

void OnSendMsgCallback(const nim::SendMessageArc& arc)
{
    ···
}


void foo()
{
    nim::Talk::RegSendMsgCb(&OnSendMsgCallback);
}

C#

void SendMessageResultHandler(object sender, MessageArcEventArgs args)
{
    if(args.ArcInfo.Response == ResponseCode.kNIMResSuccess)
    {
        ···
    }
    if (args.ArcInfo.Response == ResponseCode.kNIMResSuccess)
        return;

    Action action = () =>
    {
        MessageBox.Show(args.Dump(), "发送失败");
    };
    this.Invoke(action);
}

private void foo()
{
    NIM.TalkAPI.OnSendMessageCompleted += SendMessageResultHandler;
}

C

void CallbackSendMsgResult(const char* result, const void *user_data)
{
    //解析result {"msg_id" : "" , "talk_id" : "" , "rescode" : ""}
}

typedef void(*nim_talk_reg_arc_cb)(const char *json_extension, nim_talk_arc_cb_func cb, const void *user_data);

void foo()
{
    nim_talk_reg_arc_cb func = (nim_talk_reg_arc_cb) GetProcAddress(hInst, "nim_talk_reg_arc_cb");
    func(nullptr, &CallbackSendMsgResult, nullptr);
}

如果要停止正在发送中的消息(目前只支持发送文件消息时的终止),可以调用nim_talk_stop_send_msg 接口实现。

此外,发送消息还提供了以下消息属性设置:

针对群组消息,提供强推属性设置(可以用于实现@功能等):

反垃圾字段:

消息多端推送开关

C++

void OnMultiportPushConfigChange(int rescode, bool switch_on)
{
    if (rescode == nim::kNIMResSuccess)
    {
        ···
    }
}

void OnInit()
{
    //登录前注册的全局回调获取当前多端推送开关状态
    nim::Client::RegSyncMultiportPushConfigCb(&OnMultiportPushConfigChange);
}

void SetMultiportConfig(bool switch_on)
{
    //当PC/Web端在线时,可以配置消息是否推送给移动端
    nim::Client::SetMultiportPushConfigAsync(switch_on, &OnMultiportPushConfigChange);
}

void GetMultiportConfig()
{
    //主动获取当前多端推送开关状态
    nim::Client::GetMultiportPushConfigAsync(&OnMultiportPushConfigChange);
}

C#

public delegate void ConfigMultiportPushDelegate(ResponseCode response,ConfigMultiportPushParam param)
{
     if (response == ResponseCode.kNIMResSuccess)
    {
        ···
    }
}

/// <summary>
/// 开启多端推送
/// </summary>
/// <param name="cb">操作结果委托</param>
public static void EnableMultiportPush(ConfigMultiportPushDelegate cb)
{
    ConfigMultiportPushParam param = new ConfigMultiportPushParam();
    param.Enabled = true;
    var ptr = DelegateConverter.ConvertToIntPtr(cb);
    ClientNativeMethods.nim_client_set_multiport_push_config(param.Serialize(), null, ConfigMultiportPushCb, ptr);
}

/// <summary>
/// 禁止多端推送
/// </summary>
/// <param name="cb">操作结果委托</param>
public static void DisableMultiportPush(ConfigMultiportPushDelegate cb)
{
    ConfigMultiportPushParam param = new ConfigMultiportPushParam();
    param.Enabled = false;
    var ptr = DelegateConverter.ConvertToIntPtr(cb);
    ClientNativeMethods.nim_client_set_multiport_push_config(param.Serialize(), null, ConfigMultiportPushCb, ptr);
}

/// <summary>
/// 获取多端推送控制开关
/// </summary>
/// <param name="cb"></param>
public static void IsMultiportPushEnabled(ConfigMultiportPushDelegate cb)
{
    var ptr = DelegateConverter.ConvertToIntPtr(cb);
    ClientNativeMethods.nim_client_get_multiport_push_config(null, ConfigMultiportPushCb, ptr);
}

/// <summary>
/// 注册多端推送设置同步回调
/// </summary>
/// <param name="cb"></param>
public static void RegMulitiportPushEnableChangedCb(ConfigMultiportPushDelegate cb)
{
    var ptr = DelegateConverter.ConvertToIntPtr(cb);
    ClientNativeMethods.nim_client_reg_sync_multiport_push_config_cb(null, OnMultiportPushEnableChanged, ptr);
}

C

static void CallbackMultiportPushConfig(int rescode, const char *content, const char *json_extension, const void *user_data)
{
    Json::Value values;
    Json::Reader reader;
    if (rescode == nim::kNIMResSuccess && reader.parse(content, values) && values.isObject())
    {
        bool open = values[kNIMMultiportPushConfigContentKeyOpen].asInt() == 1;
        ···
    }
}

typedef void(*nim_client_reg_sync_multiport_push_config_cb)(const char *json_extension, nim_client_multiport_push_config_cb_func cb, const void *user_data);
typedef void(*nim_client_set_multiport_push_config)(const char *switch_content, const char *json_extension, nim_client_multiport_push_config_cb_func cb, const void *user_data);
typedef void(*nim_client_get_multiport_push_config)(const char *json_extension, nim_client_multiport_push_config_cb_func cb, const void *user_data);

void OnInit()
{
    //登录前注册的全局回调获取当前多端推送开关状态
    nim_client_reg_sync_multiport_push_config_cb func = (nim_client_reg_sync_multiport_push_config_cb) GetProcAddress(hInst, "nim_client_reg_sync_multiport_push_config_cb");
    func("", &CallbackMultiportPushConfig, nullptr);
}

void SetMultiportConfig(bool switch_on)
{
    //当PC/Web端在线时,可以配置消息是否推送给移动端
    nim_client_set_multiport_push_config func = (nim_client_set_multiport_push_config) GetProcAddress(hInst, "nim_client_set_multiport_push_config");
    func(switch_on, "", &CallbackMultiportPushConfig, nullptr);
}

void GetMultiportConfig()
{
    //主动获取当前多端推送开关状态
    nim_client_get_multiport_push_config func = (nim_client_get_multiport_push_config) GetProcAddress(hInst, "nim_client_get_multiport_push_config");
    func("", &CallbackMultiportPushConfig, nullptr);
}

接收消息

提前注册好接收消息的回调函数。在接收到消息时,如果是图片、语音消息,那么SDK 会自动下载,然后通过注册的NOS回调函数通知。如果下载失败,则调用接口nim_nos_download_media 重新下载。此外,还可以调用nim_nos_stop_download_media 停止下载(目前仅对文件消息类型有效)。

C++

void OnReceiveMsgCallback(const nim::IMMessage& message)
{
    std::string id = GetSessionId(message);

    if (message.feature_ == nim::kNIMMessageFeatureDefault)
    {
        ···
    }
    else if (message.feature_ == nim::kNIMMessageFeatureSyncMsg || message.feature_ == nim::kNIMMessageFeatureRoamMsg)
    {
        ···
    }
    else if (message.feature_ == nim::kNIMMessageFeatureCustomizedMsg)
    {
        ···
    }
}

void foo()
{
    nim::Talk::RegReceiveCb(&OnReceiveMsgCallback);
}

C#

void OnReceiveMessage(object sender, NIM.NIMReceiveMessageEventArgs args)
{
    if (args.Message.MessageContent.SessionType == NIM.Session.NIMSessionType.kNIMSessionTypeP2P && args.Message.MessageContent.SenderID != _peerId)
        return;
    if (args.Message.MessageContent.SessionType == NIM.Session.NIMSessionType.kNIMSessionTypeTeam && args.Message.MessageContent.ReceiverID != _peerId)
        return;
    //处理自定义消息
    if (args.Message != null && args.Message.MessageContent.MessageType == NIMMessageType.kNIMMessageTypeCustom)
    {
        ···
    }
    if (args.Message != null && args.Message.MessageContent.MessageType == NIM.NIMMessageType.kNIMMessageTypeText)
    {
        ···
    }
}

private void foo()
{
    NIM.TalkAPI.OnReceiveMessageHandler += OnReceiveMessage;
}

C

void CallbackReceiveMsg(const char* msg, const char *json_exten, const void *user_data)
{
    Json::Value value;
    Json::Reader reader;
    if (reader.parse(msg, value))
    {
        int rescode = value[nim::kNIMMsgKeyLocalRescode].asInt();
        int feature = value[nim::kNIMMsgKeyLocalMsgFeature].asInt();

        switch (feature)
        {
        case kNIMMessageFeatureDefault:
            ...
        case kNIMMessageFeatureSyncMsg:
            ...
        ...
        }
    }
}

typedef void(*nim_talk_reg_receive_cb)(const char *json_extension, nim_talk_receive_cb_func cb, const void* user_data);

void foo()
{
    nim_talk_reg_receive_cb func = (nim_talk_reg_receive_cb) GetProcAddress(hInst, "nim_talk_reg_receive_cb");
    func(nullptr, &CallbackReceiveMsg, nullptr);
}

转发消息

用户通过转发消息接口获取消息结构Json字符串,然后需要调用发送消息接口。

C++

void foo(nim::IMMessage msg)
{
    std::string send_msg = nim::Talk::CreateRetweetMessage(msg.ToJsonString(false), QString::GetGUID(), nim::kNIMSessionTypeP2P, session_id, msg.msg_setting_, 1000 * nbase::Time::Now().ToTimeT());
    nim::Talk::SendMsg(send_msg);
}

C#

void foo(NIMImageMessage msg)
{
    NIM.NIMMessageSetting setting = new NIMMessageSetting();
    NIMImageMessage send_msg = TalkAPI.CreateRetweetMessage(msg, setting, NimUtility.Utilities.GenerateGuid(), session_id, NIM.Session.NIMSessionType.kNIMSessionTypeP2P, 0);
    TalkAPI.SendMessage(send_msg);  
}

C

typedef char*(*nim_talk_create_retweet_msg)(const char* src_msg_json, const char* client_msg_id, const NIMSessionType retweet_to_session_type, const char* retweet_to_session_id, const char* msg_setting, __int64 timetag);
typedef    void (*nim_global_free_buf)(void *data);

void foo(const char *src_msg_json, const char *msg_id, NIMSessionType session_type, const char *session_id, const char* msg_sessiong_json, __int64 timetag)
{
    nim_talk_create_retweet_msg func = (nim_talk_create_retweet_msg) GetProcAddress(hInst, "nim_talk_create_retweet_msg");
    nim_global_free_buf free_func = (nim_global_free_buf) GetProcAddress(hInst, "nim_global_free_buf");

    const char *msg = func(src_msg_json, msg_id, session_type, session_id, msg_sessiong_json, timetag);
    free_func((void *)msg);
}

撤回消息

用户通过发送消息发的消息可以通过该接口执行撤回操作(不支持聊天室消息),撤回操作一般有时限限制(该限制为全局的APP设置),超过限制返回508。 开发者通过注册撤回消息通知回调,接收其他端的撤回消息的通知,收到通知后SDK会标记主动在消息历史中标记该条消息为删除状态,同时开发者根据自身需求,删除界面上显示的消息,甚至插入一条提示,IM Demo有开发示例。

C++

void OnReceiveRecallMsgCallback(nim::NIMResCode code, const std::list<nim::RecallMsgNotify>& message)
{
    for (auto notify : message)
    {
        if (code == nim::kNIMResSuccess)
        {
            ···
        }
    }
}

void OnInit()
{
    nim::Talk::RegRecallMsgsCallback(&OnReceiveRecallMsgCallback);
}

void foo(const nim::IMMessage& msg)
{
    nim::Talk::RecallMsg(msg, "test notify when recall", &OnReceiveRecallMsgCallback);
}

C#

private void OnRecallMessage(ResponseCode result, RecallNotification[] notify)
{
    ···
}

void OnInit()
{
    NIM.TalkAPI.RegRecallMessageCallback(OnRecallMessage);
}

private void OnRecallMessageCompleted(ResponseCode result, RecallNotification[] notify)
{
    ···
}

void foo(string msg_id)
{
     NIM.TalkAPI.RecallMessage(msg_id, "撤回消息", OnRecallMessageCompleted);
}

C

void nim_talk_recall_msg(const char *json_msg, const char *notify, const char *json_extension, nim_talk_recall_msg_func cb, const void *user_data);

void nim_talk_reg_recall_msg_cb(const char *json_extension, nim_talk_recall_msg_func cb, const void *user_data);

void OnInit()
{
    nim_talk_reg_recall_msg_cb func = (nim_talk_reg_recall_msg_cb) GetProcAddress(hInst, "nim_talk_reg_recall_msg_cb");

    func("", &OnReceiveRecallMsgCallback, nullptr);
}

void foo(const nim::IMMessage& msg)
{
    nim_talk_recall_msg func = (nim_talk_recall_msg) GetProcAddress(hInst, "nim_talk_recall_msg");

    func(msg, "test notify when recall", "", &OnReceiveRecallMsgCallback, nullptr);
}

最近会话

最近会话记录了与用户最近有过会话的联系人信息,包括联系人帐号、联系人类型、最近一条消息的时间、消息状态、消息缩略和未读条数等信息。

C++

void OnQuerySessionListCallback(int unread_count, const nim::SessionDataList& session_list)
{
    ···
}

void foo()
{
    nim::Session::QueryAllRecentSessionAsync(&OnQuerySessionListCallback);
}

C#

void foo()
{
    NIM.Session.SessionAPI.QueryAllRecentSession((a, b) =>
    {
        if (b == null || b.SessionList == null) return;
        foreach (var item in b.SessionList)
        {
            ···
        }
    });
}

C

void CallbackQuerySessionListCb(int total_unread_count, const char* session_list, const char *json_exten, const void *user_data)
{
    ...
}

typedef void(*nim_session_query_all_recent_session_async)(const char *json_extension, nim_session_query_recent_session_cb_func cb, const void* user_data);

void foo()
{
    nim_session_query_all_recent_session_async func = (nim_session_query_all_recent_session_async) GetProcAddress(hInst, "nim_session_query_all_recent_session_async");
    func(nullptr, &CallbackQuerySessionListCb, nullptr);
}

在收发消息的同时,SDK会更新对应聊天对象的最近联系人资料。当有消息收发时,SDK 会发出最近联系人更新通知,使用nim_session_reg_change_cb 注册通知回调函数。

SDK 也提供了删除单个和删除全部最近联系人的接口。需要注意:删除会话项时本地和服务器会同步删除!

自定义消息

除了内建消息类型外,SDK 也支持收发自定义消息类型。和透传消息一样,SDK 也不会解析自定义消息的具体内容,解释工作由开发者完成。

消息已读回执

在会话界面中调用发送已读回执的接口并传入最后一条消息,即表示这之前的消息都已读,对端将收到此回执。

在发送端将需要发送的消息作为参数传入,而接收端可以通过解析result中的msg_timetag来得知发送端当前已读时间戳。此功能仅在 P2P 消息中有效。重复发送和通过无效消息构造的已读回执都将被 SDK 忽略。

多媒体消息

语音消息录制和播放

语音转文字

SDK 提供语音转文字接口。

void nim_tool_get_audio_text_async(const char *json_audio_info, const char *json_extension, nim_tool_get_audio_text_cb_func cb, const void *user_data);

例:

C++

void foo(nim::IMMessage &msg_data)
{
    nim::IMAudio audio;
    nim::Talk::ParseAudioMessageAttach(msg_data, audio);
    nim::AudioInfo audio_info;
    audio_info.samplerate_ = "16000";
    audio_info.url_ = audio.url_;
    audio_info.duration_ = audio.duration_;
    audio_info.mime_type_ = audio.file_extension_;

    nim::Tool::GetAudioTextAsync(audio_info, ToWeakCallback([this](int rescode, const std::string& text) {
        if (rescode == nim::kNIMResSuccess) {
            ···
        }
        else {
            ···
        }
    }));
}

C#

private void foo(NIM.NIMIMMessage msg)
{
    NIM.NIMAudioInfo info = new NIMAudioInfo();
    info.Duration = 1000;
    info.SampleRate = "16000";
    info.URL = "···";
    info.MimeType = "aac";
    NIM.ToolsAPI.GetAudioTextAsync(info, "", OnGetText);
}
private void OnGetText(int rescode, string text, string json_extension, IntPtr user_data)
{
}

C

void CallbackGetAudioText(int rescode, const char *text, const char *json_extension, const void *user_data)
{
    ...
}

typedef void(*nim_tool_get_audio_text_async)(const char *json_audio_info, const char *json_extension, nim_tool_get_audio_text_cb_func cb, const void *user_data);

void foo()
{
    Json::Value json_value;
    json_value[nim::kNIMTransAudioKeyMime] = ; //mime类型
    json_value[nim::kNIMTransAudioKeySample] = ; //采样率
    json_value[nim::kNIMTransAudioKeyAudioUrl] = ; //下载地址
    json_value[nim::kNIMTransAudioKeyDuration] = ; //时长(毫秒)

    nim_tool_get_audio_text_async func = (nim_tool_get_audio_text_async) GetProcAddress(hInst, "nim_tool_get_audio_text_async");
    func(json_value.toStyledString().c_str(), nullptr, &CallbackGetAudioText, nullptr);
}

获取图片缩略图

网易云通信PC SDK目前默认收到图片消息后会提前下载原图缓存到本地,如果开发者想控制下载图片的质量,可以通过初始化SDK时设置kNIMPreloadImageQuality来控制图片质量,设置kNIMPreloadImageResize来控制图片长宽, IM Demo开发范例中默认下载原图,所以开发者如果想基于IM Demo工程源码开发,需要在图片预览环节或者其他需要显示原图的地方自行下载原图。

以下但不限于以下场景下开发者可以自行下载缩略图:

目前SDK提供两种获取缩略图方案:

目前IM Demo工程在消息显示图片的时候对图片进行了压缩处理,开发者可以参考bubble_image.cpp

群组功能

群组功能概述

网易云通信SDK提供了普通群(kNIMTeamTypeNormal),以及高级群(kNIMTeamTypeAdvanced)两种形式的群聊功能。高级群拥有更多的权限操作,两种群聊形式在共有操作上保持了接口一致。在群组中,当前会话的ID就是群组的ID。

开发手册中所提及的普通群都等同于DEMO中的讨论组。普通群(讨论组)没有权限操作,适用于快速创建多人会话的场景。每个普通群只有一个管理员。管理员可以对群进行增减员操作,普通成员只能对群进行增员操作。在添加新成员的时候,并不需要经过对方同意。

高级群在权限上有更多的限制,权限分为群主、管理员、以及群成员。在添加成员的时候需要对方接受邀请。高级群的群成员资料提供了实时同步功能,并提供了群开关设置字段、第三方扩展字段(仅负责存储和透传)和第三方服务器扩展字段(该配置项只能通过服务器接口设置,对客户端只读)。

群操作 普通群 高级群
邀请成员 任何人 群主、管理员
踢出成员 群主 群主、管理员(管理员之间无法互相踢)
解散群 群主 群主
退群 任何人 管理员、普通成员
处理入群申请 / 群主、管理员
更改自己的群昵称 / 任何人
更改他人群昵称 / 群主、管理员
更改群名称 任何人 群主、管理员
更改群公告 / 群主、管理员
更改群介绍 / 群主、管理员
更新验证方式 / 群主、管理员
添加(删除)管理员 / 群主
移交群主 / 群主
成员禁言 / 群主、管理员
更新群头像 / 群主、管理员

群聊消息

群聊消息收发和管理与双人聊天完全相同,仅在消息类型(kNIMMsgKeyToType)上做了区分。

获取群组

SDK 在程序启动时会对本地群信息进行同步,所以只需要调用本地缓存接口获取群就可以了。SDK 提供了批量获取自己的群接口、以及根据单个群 ID 查询的接口。同样SDK 也提供了远程获取群信息的接口。

创建群组

网易云通信群组分为两类:普通群和高级群,两种群组的消息功能都是相同的,区别在于管理功能。

普通群所有人都可以拉人入群,除群主外,其他人都不能踢人。

固定群则拥有完善的成员权限体系及管理功能。创建群的接口相同,传入不同的类型参数即可。

void nim_team_create_team_async(
const char *team_info, //群信息
const char *jsonlist_uids, //邀请的成员
const char *invitation_postscript, //邀请附言
const char *json_extension, //附加数据
nim_team_opt_cb_func cb,
const void *user_data);

例:

C++

void OnTeamEventCallback(const nim::TeamEvent& result)
{
    ...
}

void foo()
{
    std::list<std::string> id_list;
    id_list.push_back("test1");
    id_list.push_back("test2");

    nim::TeamInfo tinfo;
    tinfo.SetName("test");
    tinfo.SetType(nim::kNIMTeamTypeNormal);
    nim::Team::CreateTeamAsync(tinfo, id_list, "", &OnTeamEventCallback);
}

C#

void foo()
{
    NIM.Team.NIMTeamInfo tinfo = new NIM.Team.NIMTeamInfo();
    tinfo.Name = teamNameBox.Text;
    tinfo.Introduce = teamIntroBox.Text;
    tinfo.TeamType = NIM.Team.NIMTeamType.kNIMTeamTypeAdvanced;
    string[] uids = { "test1", "test2"};
    if (uids.Any())
    {
        NIM.Team.TeamAPI.CreateTeam(tinfo, uids, textBox1.Text, (a) =>
        {

        });
    }
}

C

void CallbackCreateTeam(int error, int team_event, const char *tid, const char* str, const char *json_exten, const void *user_data)
{
    ...
}    

typedef void(*nim_team_create_team_async)(const char *team_info, const char *jsonlist_uids, const char *invitation_postscript, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);

void foo()
{
    Json::Value team_info;
    team_info[kNIMTeamInfoKeyName] = ; //群名称
    team_info[kNIMTeamInfoKeyType] = ; //群类型
    team_info[kNIMTeamInfoKeyIntro] = ; //群介绍
    team_info[kNIMTeamInfoKeyJoinMode] = ; //群验证方式
    team_info[kNIMTeamInfoKeyAnnouncement] = ; //群公告

    Json::Value team_member;
    team_member.append("litianyi01");    
    team_member.append("litianyi02");

    nim_team_create_team_async func = (nim_team_create_team_async) GetProcAddress(hInst, "nim_team_create_team_async");
    func(team_info.toStyledString().c_str(), team_member.toStyledString().c_str(), "welcome to new team", nullptr, &CallbackCreateTeam, nullptr);
}

加入群组

用户可以通过被动接受邀请和主动加入两种方式进入群组。

踢人出群

普通群仅拥有者可以踢人,高级群拥有者和管理员可以踢人,且管理员不能踢拥有者和其他管理员。

C++

void TeamEventCb(const nim::TeamEvent& team_event)
{
    ···
}

void foo()
{
    std::list<std::string> uids_list;
    uids_list.push_back("test_user");
    nim::Team::RejectJoinApplyAsync("12345", uids_list, &TeamEventCb);
}

C#

NIM.Team.TeamAPI.KickMemberOutFromTeam("12345", new string[] {"test_user"}, (a) =>
{
    if (a.TeamEvent.ResponseCode == NIM.ResponseCode.kNIMResSuccess)
    {
        ···
    }
});

C

void TeamEventCb(int res_code, int notification_id, const char *tid, const char *result, const char *json_extension, const void *user_data)
{
    ···
}

typedef void(*nim_team_kick_async)(const char *tid, const char *jsonlist_uids, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);

void foo()
{
    nim_team_kick_async func = (nim_team_kick_async) GetProcAddress(hInst, "nim_team_kick_async");

    Json::Value json_value;
    json_value.append("litianyi01");    
    json_value.append("litianyi02");

    func("12345", json_value.toStyledString().c_str(), "", &TeamEventCb, nullptr);
}

主动退群

除拥有者外,其他用户均可以主动退群:

C++

void TeamEventCb(const nim::TeamEvent& team_event)
{
    ···
}

void foo()
{
    nim::Team::LeaveAsync("12345", &TeamEventCb);
}

C#

NIM.Team.TeamAPI.LeaveTeam("12345", (ret) =>
{
    if (ret.TeamEvent.ResponseCode == NIM.ResponseCode.kNIMResSuccess)
    {
        ···
    }
});

C

void TeamEventCb(int res_code, int notification_id, const char *tid, const char *result, const char *json_extension, const void *user_data)
{
    ···
}

typedef void(*nim_team_leave_async)(const char *tid, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);

void foo()
{
    nim_team_leave_async func = (nim_team_leave_async) GetProcAddress(hInst, "nim_team_leave_async");

    func("12345", "", &TeamEventCb, nullptr);
}

编辑群组资料

普通群所有人均可以修改群名,高级群仅拥有者和管理员可修改群名及其他群资料。

void nim_team_update_team_info_async(const char *tid, const char *json_info, const char *json_extension, nim_team_opt_cb_func cb, const void *user_data);

例:

C++

void OnUpdateBroadCb(const nim::TeamEvent& team_event)
{
    if (team_event.res_code_ == 200)
    {
        ···
    }
}

void foo()
{
    Json::Value broad;
    broad["title"] = "title";
    broad["content"] = "内容";
    broad["creator"] = "test_user";

    Json::Value broads;
    broads.append(broad);

    Json::FastWriter writer;
    nim::TeamInfo param;
    param.SetAnnouncement(writer.write(broads));
    param.SetTeamID("tid_");

    nim::Team::UpdateTeamInfoAsync("tid_", param, &OnUpdateBroadCb);
}

C#

void foo()
{
    NIM.Team.NIMTeamInfo tinfo = new NIM.Team.NIMTeamInfo();
    tinfo.Announcement = "公告";
    tinfo.TeamId = "tid";

    NIM.Team.TeamAPI.UpdateTeamInfo("tid", tinfo, (ret) =>
    {
        if (ret.TeamEvent.ResponseCode == NIM.ResponseCode.kNIMResSuccess)
        {
            ···
        }
    });
}

C

void CallbackTeamOperate(int error, int team_operate, const char *tid, const char* str, const char *json_exten, const void *user_data)
{
    if (error == kNIMResSuccess)
    {
        ...
    }
    else
    {
        ...
    }
}

typedef void(*nim_team_update_team_info_async)(const char *tid, const char *json_info, const char *json_extension, nim_team_event_cb_func cb_func, const void* user_data);

void foo()
{
    Json::Value values;
    values[kNIMTeamInfoKeyID] = "tid";    
    values[kNIMTeamInfoKeyAnnouncement] = "123"; //修改群公告,同样的,目前可以修改群名称,群简介,具体参阅api文档
    values[kNIMTeamInfoKeyBits] = 1; //修改群开关设置(如开启或关闭群消息提醒),具体参阅api文档里NIMTeamBitsConfigMask(群组信息Bits属性kNIMTeamInfoKeyBits的配置定义)

    nim_team_update_team_info_async func = (nim_team_update_team_info_async) GetProcAddress(hInst, "nim_team_update_team_info_async");
    func("tid", values.toStyledString().c_str(), nullptr, &CallbackTeamOperate, nullptr);
}

管理群组权限

高级群群主可以对群进行权限管理,权限管理包括:

群组成员

解散群

群主可以调用接口解散所拥有的群:

C++

void OnTeamEventCallback(const nim::TeamEvent& result)
{
    ···
}

foo()
{
    nim::Team::DismissAsync("tid_", &OnTeamEventCallback);
}

C#

NIM.Team.TeamAPI.DismissTeam("_teamId", (ret) =>
{
    ···
});

C

void CallbackTeamOperate(int error, int team_operate, const char *tid, const char* str, const char *json_exten, const void *user_data)
{
    if (error == kNIMResSuccess)
    {
        ...
    }
    else
    {
        ...
    }
}

typedef void(*nim_team_dismiss_async)(const char *tid, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);

void foo()
{
    nim_team_dismiss_async func = (nim_team_dismiss_async) GetProcAddress(hInst, "nim_team_dismiss_async");
    func("tid", nullptr, &CallbackTeamOperate, nullptr);
}

群组通知

用户在创建群或者进入群成功之后,任何关于群的变动(群组资料变动,群成员变动等),网易云通信服务器都会下发一条群通知消息。APP 可以通过注册全局回调函数接受群组通知。

void nim_team_reg_team_event_cb(const char *json_extension, nim_team_event_cb_func cb, const void *user_data);

例:

C++

void OnTeamEventCallback(const nim::TeamEvent& result)
{
    ···
}

foo()
{
    nim::Team::RegTeamEventCb(&OnTeamEventCallback);
}

C#

void OnTeamEventNotify(object sender, NIMTeamEventArgs e)
{
    if (e.Data.TeamEvent.NotificationType == NIMNotificationType.kNIMNotificationIdLocalGetTeamList)
    {
        ···
    }
}

void foo()
{
    NIM.Team.TeamAPI.TeamEventNotificationHandler += OnTeamEventNotify;
}

C

void CallbackTeamEvent(int error, int team_event, const char *tid, const char* str, const char *json_exten, const void *user_data)
{
    switch (team_event)
    {
    case kNIMNotificationIdLocalCreateTeam:
        ...
    ...    
    }
}

typedef void(*nim_team_reg_team_event_cb)(const char *json_extension, nim_team_event_cb_func cb, const void *user_data);

void foo()
{
    nim_team_reg_team_event_cb func = (nim_team_reg_team_event_cb) GetProcAddress(hInst, "nim_team_reg_team_event_cb");
    func(nullptr, &CallbackTeamEvent, nullptr);
}

自定义拓展

SDK 提供了群信息的拓展接口,开发者可以通过维护群信息的两个属性来自行定义内容。

群成员禁言

历史记录

本地记录

nim_msglog.h 文件中的接口提供了比较完善的消息记录管理功能,开发者可本地查阅、在线查阅、删除和清空聊天记录,还可以调用nim_msglog_write_db_only_async 接口只往本地消息历史数据库里写入一条消息(通常是APP 的本地自定义消息,并不会发给服务器)。详见API 文档。此外,还要注意:本地消息历史操作(如删除)及状态变化(如发送失败或成功)会与会话列表里的消息状态自动同步,App 上层需要根据nim_session_reg_change_cb 注册好的会话列表通知回调进行相应通知事件的处理。

导入导出

云端记录

系统通知

除消息通道外,SDK 还提供系统通知这种通道用于消息之外的通知分发。目前有两种类型:内置系统通知和自定义系统通知。

所有的系统通知(包括内置系统通知和自定义系统通知)都是通过

C++

void UIReceiveSysmsgCallback(nim::SysMessage& msg)
{
    if (msg.type_ == nim::kNIMSysMsgTypeCustomP2PMsg || msg.type_ == nim::kNIMSysMsgTypeCustomTeamMsg)
    {

    }
    else
    {

    }
}

void foo()
{
    nim::SystemMsg::RegSysmsgCb(&OnReceiveSysmsgCallback);
}

C#

void OnReceivedSysNotification(object sender, NIMSysMsgEventArgs e)
{
    if (e.Message == null || e.Message.Content == null)
        return;

    if (e.Message.Content.MsgType == NIMSysMsgType.kNIMSysMsgTypeTeamInvite)
    {

    }
}

void foo()
{
    NIM.SysMessage.SysMsgAPI.ReceiveSysMsgHandler += OnReceivedSysNotification;
}

C

void CallbackSysmsgChange(const char *result, const char *json_extension, const void *callback)
{
    //解析result
}

typedef void(*nim_sysmsg_reg_sysmsg_cb)(const char *json_extension, nim_sysmsg_receive_cb_func cb, const void* user_data);

void foo()
{
    nim_sysmsg_reg_sysmsg_cb func = (nim_sysmsg_reg_sysmsg_cb) GetProcAddress(hInst, "nim_sysmsg_reg_sysmsg_cb");
    func("", &CallbackSysmsgChange, nullptr);
}

注册后的回调通知给APP。为了保证整个程序逻辑的一致性,APP 需要针对不同类型的系统通知进行相应的操作。

内置系统通知

这是由SDK 预定义的通知类型,目前仅支持几种群操作的通知,如被邀请入群,SDK 负责这些通知的持久化。

此外,SDK 提供了以下接口来获取和维护内置系统通知记录:

查询系统消息列表(按时间逆序查询,逆序排列):

void nim_sysmsg_query_msg_async(int limit_count, __int64 last_time, const char *json_extension, nim_sysmsg_query_cb_func cb, const void *user_data);

C++

void LoadEventsCb(int count, int unread, const std::list<nim::SysMessage> &result)
{

}

void foo()
{
    nim::SystemMsg::QueryMsgAsync(20, 0, &LoadEventsCb);
}

C#

NIM.SysMessage.SysMsgAPI.QueryMessage(100, 0, (r) =>
{

});

C

void CallbackSysmsgChange(int count, const char *result, const char *json_extension, const void *callback)
{
    //解析result
}

typedef void(*nim_sysmsg_query_msg_async)(int limit_count, __int64 last_time, const char *json_extension, nim_sysmsg_query_cb_func cb, const void* user_data);

void foo()
{
    nim_sysmsg_query_msg_async func = (nim_sysmsg_query_msg_async) GetProcAddress(hInst, "nim_sysmsg_query_msg_async");
    func(20, 0, "", &CallbackSysmsgChange, nullptr);
}

查询未读消息数:

void nim_sysmsg_query_unread_count(const char *json_extension, nim_sysmsg_res_cb_func cb, const void *user_data);

C++

void OnQuerySysmsgUnreadCb(nim::NIMResCode res_code, int unread_count)
{
    if (res_code == 200)
        ···
}

void foo()
{
    nim::SystemMsg::QueryUnreadCount(&OnQuerySysmsgUnreadCb);
}

C#

NIM.SysMessage.SysMsgAPI.QueryUnreadCount((response, count) =>
{

});

C

void CallbackNotifySysmsgRes(int res_code, int unread_count, const char *json_extension, const void *callback)
{

}

typedef void(*nim_sysmsg_query_unread_count)(const char *json_extension, nim_sysmsg_res_cb_func cb, const void *user_data);

void foo()
{
    nim_sysmsg_query_unread_count func = (nim_sysmsg_query_unread_count) GetProcAddress(hInst, "nim_sysmsg_query_unread_count");
    func("", &CallbackNotifySysmsgRes, nullptr);
}

设置消息状态:

void nim_sysmsg_set_status_async(__int64 msg_id, NIMSysMsgStatus status, const char *json_extension, nim_sysmsg_res_ex_cb_func cb, const void *user_data);

C++

void SetStatusCb(nim::NIMResCode code, __int64 msg_id, int unread)
{

}

void foo(__int64 msg_id)
{
    nim::SystemMsg::SetStatusAsync(msg_id_, nim::kNIMSysMsgStatusInvalid, &SetStatusCb);
}

C#

void foo(long msg_id)
{
    NIM.SysMessage.SysMsgAPI.SetMsgStatus(msg_id, NIM.SysMessage.NIMSysMsgStatus.kNIMSysMsgStatusRead, 
        (res_code, _msg_id, unread_count, json_extension, user_data) =>
    {

    });
}

C

void CallbackNotifySingleSysmsg(int res_code, __int64 msg_id, int unread_count, const char *json_extension, const void *callback)
{

}

typedef void(*nim_sysmsg_set_status_async)(__int64 msg_id, nim::NIMSysMsgStatus status, const char *json_extension, nim_sysmsg_res_ex_cb_func cb, const void* user_data);

void foo(__int64 msg_id, nim::NIMSysMsgStatus status)
{
    nim_sysmsg_set_status_async func = (nim_sysmsg_set_status_async) GetProcAddress(hInst, "nim_sysmsg_set_status_async");
    func(msg_d, status, "", &CallbackNotifySingleSysmsg, nullptr);
}

删除单条消息:

void nim_sysmsg_delete_async(__int64 msg_id, const char *json_extension, nim_sysmsg_res_ex_cb_func cb, const void *user_data);

C++

void DeleteCb(nim::NIMResCode code, __int64 msg_id, int unread)
{

}

void foo(__int64 msg_id)
{
    nim::SystemMsg::DeleteAsync(msg_id, &DeleteCb);
}

C#

void foo(long msg_id)
{
    NIM.SysMessage.SysMsgAPI.DeleteByMsgId(msg_id,
        (res_code, _msg_id, unread_count, json_extension, user_data) =>
    {

    });
}

C

void CallbackNotifySingleSysmsg(int res_code, __int64 msg_id, int unread_count, const char *json_extension, const void *callback)
{

}

typedef void(*nim_sysmsg_delete_async)(__int64 msg_id, const char *json_extension, nim_sysmsg_res_ex_cb_func cb, const void *user_data);

void foo(__int64 msg_id)
{
    nim_sysmsg_delete_async func = (nim_sysmsg_delete_async) GetProcAddress(hInst, "nim_sysmsg_delete_async");
    func(msg_d, "", &CallbackNotifySingleSysmsg, nullptr);
}

设置全部消息为已读:

void nim_sysmsg_read_all_async(const char *json_extension, nim_sysmsg_res_cb_func cb, const void *user_data);

C++

void SysMsgReadAllCb(nim::NIMResCode code, int unread)
{

}

void foo()
{
    nim::SystemMsg::ReadAllAsync(&SysMsgReadAllCb);
}

C#

void foo()
{
    NIM.SysMessage.SysMsgAPI.SetAllMsgRead(
        (res_code, unread_count, json_extension, user_data) =>
    {

    });
}

C

void CallbackNotifySysmsgRes(int res_code, int unread_count, const char *json_extension, const void *callback)
{

}

typedef void(*nim_sysmsg_read_all_async)(const char *json_extension, nim_sysmsg_res_cb_func cb, const void* user_data);

void foo()
{
    nim_sysmsg_read_all_async func = (nim_sysmsg_read_all_async) GetProcAddress(hInst, "nim_sysmsg_read_all_async");
    func("", &CallbackNotifySysmsgRes, nullptr);
}

删除全部消息:

void nim_sysmsg_delete_all_async(const char *json_extension, nim_sysmsg_res_cb_func cb, const void *user_data);

C++

void DeleteAllCb(nim::NIMResCode res_code, int unread)
{

}

void foo()
{
    nim::SystemMsg::DeleteAllAsync(&DeleteAllCb);
}

C#

void foo()
{
    NIM.SysMessage.SysMsgAPI.DeleteAll(
        (res_code, unread_count, json_extension, user_data) =>
    {

    });
}

C

void CallbackNotifySysmsgRes(int res_code, int unread_count, const char *json_extension, const void *callback)
{

}

typedef void(*nim_sysmsg_delete_all_async)(const char *json_extension, nim_sysmsg_res_cb_func cb, const void *user_data);

void foo()
{
    nim_sysmsg_delete_all_async func = (nim_sysmsg_delete_all_async) GetProcAddress(hInst, "nim_sysmsg_delete_all_async");
    func("", &CallbackNotifySysmsgRes, nullptr);
}

自定义系统通知

除了内置系统通知外,SDK 也额外提供了自定义系统给开发者,方便开发者进行业务逻辑的通知。这个通知既可以由客户端发起也可以由开发者服务器发起。

客户端发起的自定义通知,该类型通知格式由开发者自定义(kNIMSysMsgKeyAttach 里),SDK 仅负责发送、接收,支持在线或离线发送,且支持点对点通知和群通知,不做任何解析,也不会存储,因此也不会在聊天记录中体现,可以使用的场景例如发送正在输入的状态等。代码示例如下:

C++

void foo()
{
    Json::Value json;
    Json::FastWriter writer;
    json["id"] = "1";

    nim::SysMessage msg;
    msg.receiver_accid_ = ;    //接收者id
    msg.sender_accid_ = ;     //自己id
    msg.client_msg_id_ = QString::GetGUID();    //本地定义的消息id
    msg.attach_ = writer.write(json);            //通知附件内容
    msg.type_ = nim::kNIMSysMsgTypeCustomP2PMsg; //通知类型

    nim::SystemMsg::SendCustomNotificationMsg(msg.ToJsonString());
}

C#

void foo()
{
    NIM.SysMessage.NIMSysMessageContent content = new NIM.SysMessage.NIMSysMessageContent();
    content.ClientMsgId = Guid.NewGuid().ToString();
    content.ReceiverId = ""; //接收者id
    content.SenderId = ""; //自己id
    if (_sessionType == NIM.Session.NIMSessionType.kNIMSessionTypeP2P)
        content.MsgType = NIM.SysMessage.NIMSysMsgType.kNIMSysMsgTypeCustomP2PMsg; //通知类型
    else if (_sessionType == NIM.Session.NIMSessionType.kNIMSessionTypeTeam)
        content.MsgType = NIM.SysMessage.NIMSysMsgType.kNIMSysMsgTypeCustomTeamMsg;
    content.Attachment = ""; //通知附件内容
    NIM.SysMessage.SysMsgAPI.SendCustomMessage(content);
}

C

typedef void(*nim_sysmsg_send_custom_notification)(const char *json_msg, const char *json_extension);

void foo()
{
    //json_msg:key的定义详见系统消息字段nim_sysmsg_def.h
    Json::Value json_msg;
    json_msg[kNIMSysMsgKeyToAccount] = ; //接收者id
    json_msg[kNIMSysMsgKeyFromAccount] = ; //自己id
    json_msg[kNIMSysMsgKeyLocalClientMsgId] = 本地定义的消息id; 
    json_msg[kNIMSysMsgKeyTime] = ; //时间戳
    json_msg[kNIMSysMsgKeyAttach] = ; //通知附件内容
    json_msg[kNIMSysMsgKeyType] = ; //通知类型

    nim_sysmsg_send_custom_notification func = (nim_sysmsg_send_custom_notification) GetProcAddress(hInst, "nim_sysmsg_send_custom_notification");
    func(json_msg.toStyledString().c_str(), nullptr);
}

客户端发起的自定义通知的回执结果通过APP 预先通过

void nim_sysmsg_reg_sysmsg_cb(const char *json_extension, nim_sysmsg_receive_cb_func cb, const void *user_data);

注册的回调告知开发者。代码实力如下:

C++

void OnReceiveSysmsgCallback( const nim::SysMessage& msg )
{

}

void foo()
{
    nim::SystemMsg::RegSysmsgCb(&OnReceiveSysmsgCallback);
}

C#

void OnReceivedSysNotification(object sender, NIMSysMsgEventArgs e)
{
    if (e.Message == null || e.Message.Content == null)
        return;

    if (e.Message.Content.MsgType == NIMSysMsgType.kNIMSysMsgTypeTeamInvite)
    {
        ···
    }
}

void foo()
{
    NIM.SysMessage.SysMsgAPI.ReceiveSysMsgHandler += OnReceivedSysNotification;
}

C

void CallbackSysmsgChange(const char *result, const char *json_extension, const void *callback)
{
    // 解析result
}

typedef void(*nim_sysmsg_reg_sysmsg_cb)(const char *json_extension, nim_sysmsg_receive_cb_func cb, const void* user_data);

void foo()
{
    nim_sysmsg_reg_sysmsg_cb func = (nim_sysmsg_reg_sysmsg_cb) GetProcAddress(hInst, "nim_sysmsg_reg_sysmsg_cb");
    func("", &CallbackSysmsgChange, nullptr);
}

此外,自定义系统通知还提供了属性设置如下:

SDK 并不负责自定义通知的持久化,APP 需要根据自己的业务逻辑按需进行解析和持久化的工作。

用户名片

概述

SDK 提供了用户帐号资料管理。以下几个接口仅当选择网易云通信托管用户资料时有效,如果开发者不希望网易云通信获取自己的用户数据,则需自行维护用户资料。

nim_user_def.h 里定义了用户信息的字段 kUInfoKeyXXX。

用户信息变更会通过注册的全用户信息变更通知回调告知APP:

C++

void OnUserInfoChange(const std::list<nim::UserNameCard> &uinfo_list)
{
    for (auto& info : uinfo_list)
    {

        if (info.ExistValue(nim::kUserNameCardKeyName) || info.ExistValue(nim::kUserNameCardKeyIconUrl)) //用户名或头像变化了
            ···;
        if (info.ExistValue((nim::UserNameCardValueKey)(nim::kUserNameCardKeyAll - nim::kUserNameCardKeyName - nim::kUserNameCardKeyIconUrl))) //用户其他信息变化了
            ···;
    }
}

void foo()
{
    //向SDK注册监听用户名片变化
    nim::User::RegUserNameCardChangedCb(&OnUserInfoChange);
}

C#

void OnUserNameCardChanged(object sender, UserNameCardChangedArgs e)
{

}

void foo()
{
    NIM.User.UserAPI.UserNameCardChangedHandler += OnUserNameCardChanged;
}

C

void CallbackUserNameCardChange(const char *result_json, const char *json_extension, const void *callback)
{
    // 解析result_json
}

typedef void (*nim_user_reg_user_name_card_changed_cb)(const char *json_extension, nim_user_name_card_change_cb_func cb, const void *user_data);

void foo()
{
    nim_user_reg_user_name_card_changed_cb func = (nim_user_reg_user_name_card_changed_cb) GetProcAddress(hInst, "nim_user_reg_user_name_card_changed_cb");
    func("", &CallbackUserNameCardChange, nullptr);
}

获取本地用户信息

C++

void OnGetUserCard(const std::list<nim::UserNameCard> &json_result)
{
    for (auto& info : json_result)
    {
        ···
    }
}

void foo()
{
    std::list<std::string> account_list;
    account_list.push_back("test1");
    account_list.push_back("test2");
    nim::User::GetUserNameCard(account_list, &OnGetUserCard);
}

C#

UserAPI.GetUserNameCard(new List<string>() {"test1", "test2"}, (ret) =>
{
    if (ret.Any())
    {

    }
});

C

void CallbackGetUserNameCard(const char *result_json, const char *json_extension, const void *callback)
{
    // 解析result_json
}

typedef void (*nim_user_get_user_name_card)(const char *accids, const char *json_extension, nim_user_get_user_name_card_cb_func cb, const void *user_data);

void foo(const std::list<std::string>& accids)
{
    nim_user_get_user_name_card func = (nim_user_get_user_name_card) GetProcAddress(hInst, "nim_user_get_user_name_card");

    Json::Value values;
    for (auto iter = accids.cbegin(); iter != accids.cend(); ++iter)
        values.append(*iter);

    Json::FastWriter fw;

    func(fw.write(values).c_str(), "", &CallbackGetUserNameCard, nullptr);
}

获取服务器用户信息

单次查询限制150人。

C++

void OnGetUserCard(const std::list<nim::UserNameCard> &json_result)
{
    for (auto& info : json_result)
    {
        ···
    }
}

void foo()
{
    std::list<std::string> account_list;
    account_list.push_back("test1");
    account_list.push_back("test2");
    nim::User::GetUserNameCardOnline(account_list, &OnGetUserCard);
}

C#

NIM.User.UserAPI.QueryUserNameCardOnline(new List<string>() {"test1", "test2"}, (ret) =>
{
    if (ret == null || !ret.Any())
    {
        return;
    }

});

C

void CallbackGetUserNameCard(const char *result_json, const char *json_extension, const void *callback)
{
    // 解析result_json
}

typedef void (*nim_user_get_user_name_card_online)(const char *accids, const char *json_extension, nim_user_get_user_name_card_cb_func cb, const void *user_data);

void foo(const std::list<std::string>& accids)
{
    nim_user_get_user_name_card_online func = (nim_user_get_user_name_card_online) GetProcAddress(hInst, "nim_user_get_user_name_card_online");

    Json::Value values;
    for (auto iter = accids.cbegin(); iter != accids.cend(); ++iter)
        values.append(*iter);

    Json::FastWriter fw;

    func(fw.write(values).c_str(), "", &CallbackGetUserNameCard, nullptr);
}

编辑用户资料

例:

C++

 void OnUpdateMyInfo(nim::NIMResCode res) 
{
    if (res == nim::kNIMResSuccess)
    {

    }
}

void foo()
{
    nim::UserNameCard info;
    info.SetName("new_name");
    nim::User::UpdateMyUserNameCard(info, &OnUpdateMyInfo);
}

C#

void foo()
{
    NIM.User.UserNameCard user_card = new NIM.User.UserNameCard;
    user_card.NickName = "new_name";
    NIM.User.UserAPI.UpdateMyCard(user_card, (a) =>
    {
        if (a == NIM.ResponseCode.kNIMResSuccess)
        {
            MessageBox.Show("修改成功");
        }
        else
        {
            MessageBox.Show("修改失败");
        }
    });
}

C

void CallbackUpdateNameCard(int res_code, const char *json_extension, const void *callback)
{
    if (res_code == kNIMResSuccess)
    ...
    else
    ...
}

typedef void (*nim_user_update_my_user_name_card)(const char *info_json, const char *json_extension, nim_user_update_my_name_card_cb_func cb, const void *user_data);

void foo()
{
    //修改昵称
    Json::Value values;
    values[kUInfoKeyAccid] = "litianyi02";
    values[kUInfoKeyName] = "修改后的大意的昵称";

    nim_user_update_my_user_name_card func = (nim_user_update_my_user_name_card) GetProcAddress(hInst, "nim_user_update_my_user_name_card");
    func(values.toStyledString().c_str(), nullptr, &CallbackUpdateNameCard, nullptr);
}

用户关系托管

SDK 提供了用户好友关系管理,以及对用户会话的消息设置。在网易云通信中,不是好友也允许聊天。好友关系如果不托管给网易云通信,开发者需要自己在应用服务器维护。

添加/被添加,请求/被请求,删除/被删除的通知以及多端同步等通知通过注册的好友数据变更通知回调告知APP:

例:

C++

void OnFriendListChange(const nim::FriendChangeEvent& change_event)
{
    switch (change_event.type_)
    {
    case nim::kNIMFriendChangeTypeDel:
    {
        break;
    }
    case nim::kNIMFriendChangeTypeRequest:
    {
        break;
    }
    case nim::kNIMFriendChangeTypeSyncList:
    {
        break;
    }
    case nim::kNIMFriendChangeTypeUpdate:
    {
        break;
    }
    default:
        break;
    }
}

void foo()
{
    //向SDK注册监听好友列表变化
    nim::Friend::RegChangeCb(&OnFriendListChange);
}

C#

void OnFriendChanged(object sender, NIM.Friend.NIMFriendProfileChangedArgs args)
{
    if (args.ChangedInfo == null)
        return;

    if (args.ChangedInfo.ChangedType == NIM.Friend.NIMFriendChangeType.kNIMFriendChangeTypeDel)
    {

    }
    if (args.ChangedInfo.ChangedType == NIM.Friend.NIMFriendChangeType.kNIMFriendChangeTypeRequest)
    {

    }
    if (args.ChangedInfo.ChangedType == NIM.Friend.NIMFriendChangeType.kNIMFriendChangeTypeSyncList)
    {

    }
    if (args.ChangedInfo.ChangedType == NIM.Friend.NIMFriendChangeType.kNIMFriendChangeTypeUpdate)
    {

    }
}

void foo()
{
    NIM.Friend.FriendAPI.FriendProfileChangedHandler += OnFriendChanged;
}

C

void CallbackFriendChange(NIMFriendChangeType type, const char *result_json, const char *json_extension, const void *user_data)
{
    switch (type)
    {
        case kNIMFriendChangeTypeRequest:
            // 解析result_json
            break;
        case kNIMFriendChangeTypeDel:
            // 解析result_json
            break;
        ...
}

typedef void(*nim_friend_reg_changed_cb)(const char *json_extension, nim_friend_change_cb_func cb, const void *user_data);

void foo()
{
    nim_friend_reg_changed_cb func = (nim_friend_reg_changed_cb) GetProcAddress(hInst, "nim_friend_reg_changed_cb");
    func("", &CallbackFriendChange, nullptr);
}

好友关系

黑名单

网易云通信中,黑名单和用户关系是互相独立的,即修改用户关系不会影响黑名单关系,同时,修改黑名单也不会对用户关系进行操作。

黑名单列表有本地缓存,缓存会在手动/自动登录后与服务器自动进行同步更新。通过注册用户关系变更通知回调获取当前数据变化:

例:

C++

void OnMuteBlackEventCallback(const nim::SpecialRelationshipChangeEvent& change_event)
{
    switch (change_event.type_)
    {
    case nim::NIMUserSpecialRelationshipChangeType::kNIMUserSpecialRelationshipChangeTypeMarkBlack:
    {
        break;
    }
    case nim::NIMUserSpecialRelationshipChangeType::kNIMUserSpecialRelationshipChangeTypeMarkMute:
    {
        break;
    }
    case nim::NIMUserSpecialRelationshipChangeType::kNIMUserSpecialRelationshipChangeTypeSyncMuteAndBlackList:
    {
        break;
    }
    }
}

void foo()
{
    nim::User::RegSpecialRelationshipChangedCb(&OnMuteBlackEventCallback);
}

C#

void OnUserRelationshipSync(object sender, UserRelationshipSyncArgs e)
{
    if (e.Items == null)
        return;
}

void OnUserRelationshipChanged(object sender, UserRelationshipChangedArgs e)
{
    if (e.ChangedType == NIMUserRelationshipChangeType.AddRemoveBlacklist)
    {
    }
}

void foo()
{
    NIM.User.UserAPI.UserRelationshipListSyncHander += OnUserRelationshipSync;
    NIM.User.UserAPI.UserRelationshipChangedHandler += OnUserRelationshipChanged;
}

C

void CallbackUserRelationshipChanged(NIMUserSpecialRelationshipChangeType type, const char *result_json ,const char *json_extension, const void *user_data)
{
    switch (type)
    {
        case kNIMUserSpecialRelationshipChangeTypeMarkBlack:
            //解析result_json
            break;
        case kNIMUserSpecialRelationshipChangeTypeMarkMute:
            //解析result_json
            break;
        ...
    }
}

typedef    void (*nim_user_reg_special_relationship_changed_cb)(const char *json_extension, nim_user_special_relationship_change_cb_func cb, const void *user_data);

void foo()
{
    nim_user_reg_special_relationship_changed_cb func = (nim_user_reg_special_relationship_changed_cb) GetProcAddress(hInst, "nim_user_reg_special_relationship_changed_cb");
    func(nullptr, &CallbackUserRelationshipChanged, nullptr);
}

消息提醒

网易云通信中,可以单独设置是否开启某个用户的消息提醒,即对某个用户静音。静音关系和用户关系是互相独立的,修改用户关系不会影响静音关系,同时,修改静音关系也不会对用户关系进行操作。

静音名单列表有本地缓存,缓存会在手动/自动登录后与服务器自动进行同步更新。通过注册用户关系变更通知回调获取当前数据变化:

C++

void OnMuteBlackEventCallback(const nim::SpecialRelationshipChangeEvent& change_event)
{
    switch (change_event.type_)
    {
    case nim::NIMUserSpecialRelationshipChangeType::kNIMUserSpecialRelationshipChangeTypeMarkBlack:
    {
        break;
    }
    case nim::NIMUserSpecialRelationshipChangeType::kNIMUserSpecialRelationshipChangeTypeMarkMute:
    {
        break;
    }
    case nim::NIMUserSpecialRelationshipChangeType::kNIMUserSpecialRelationshipChangeTypeSyncMuteAndBlackList:
    {
        break;
    }
    }
}

void foo()
{
    nim::User::RegSpecialRelationshipChangedCb(&OnMuteBlackEventCallback);
}

C#

void OnUserRelationshipSync(object sender, UserRelationshipSyncArgs e)
{
    if (e.Items == null)
        return;
}

void OnUserRelationshipChanged(object sender, UserRelationshipChangedArgs e)
{
    if (e.ChangedType == NIMUserRelationshipChangeType.AddRemoveBlacklist)
    {
    }
}

void foo()
{
    NIM.User.UserAPI.UserRelationshipListSyncHander += OnUserRelationshipSync;
    NIM.User.UserAPI.UserRelationshipChangedHandler += OnUserRelationshipChanged;
}

C

void CallbackUserRelationshipChanged(NIMUserSpecialRelationshipChangeType type, const char *result_json ,const char *json_extension, const void *user_data)
{
    switch (type)
    {
        case kNIMUserSpecialRelationshipChangeTypeMarkBlack:
            //解析result_json
            break;
        case kNIMUserSpecialRelationshipChangeTypeMarkMute:
            //解析result_json
            break;
        ...
    }
}

typedef    void (*nim_user_reg_special_relationship_changed_cb)(const char *json_extension, nim_user_special_relationship_change_cb_func cb, const void *user_data);

void foo()
{
    nim_user_reg_special_relationship_changed_cb func = (nim_user_reg_special_relationship_changed_cb) GetProcAddress(hInst, "nim_user_reg_special_relationship_changed_cb");
    func(nullptr, &CallbackUserRelationshipChanged, nullptr);
}

NOS云存储服务

下载

下载资源,回调函数包括了下载结果以及下载进度

下载结果回调函数
@param rescode:200表示下载成功
@param file_path:下载文件的完整路径
@param call_id:对话id
@param res_id:消息id,与对话id一起可定位到具体的消息,然后根据rescode调整UI呈现
typedef void(*nim_nos_download_cb_func)(int rescode, const char *file_path, const char *call_id, const char *res_id, const char *json_extension, const void *user_data);

下载进度回调函数
@param downloaded_size:已下载大小
@param file_size:文件大小,单位都是字节
typedef void(*nim_nos_download_prg_cb_func)(__int64 downloaded_size, __int64 file_size, const char *json_extension, const void *user_data);

示例代码如下:

C++

void DownloadResourceCallback(nim::NIMResCode res_code, const std::string& file_path, const std::string& call_id, const std::string& res_id)
{

}

void DownloadResourceProgressCallback(__int64 downloaded_size, __int64 file_size)
{

}

void foo(const IMMessage& msg)
{
    nim::NOS::FetchMedia(msg, &DownloadResourceCallback, &DownloadResourceProgressCallback);
}

C#

void foo(NIM.NIMIMMessage message)
{
    NIM.Nos.NosAPI.DownloadMedia(message, (rescode, filePath, callId, resId) =>
    {
        if (rescode == 200)
        {

        }
    },
    (prgData) =>
    {

    });
}

C

void CallbackDownload(int res_code, const char *file_path, const char *call_id, const char *res_id, const char *json_extension, const void *user_data)
{
    ···
}

void CallbackProgress(__int64 completed_size, __int64 total_size, const char *json_extension, const void *callback)
{
    ···
}

typedef void(*nim_nos_download_media)(const char *json_msg, nim_nos_download_cb_func callback_result, const void *download_user_data, nim_nos_download_prg_cb_func prg_cb, const void *prg_user_data);

void foo(const IMMessage& msg)
{
    nim_nos_download_media func = (nim_nos_download_media) GetProcAddress(hInst, "nim_nos_download_media");
    func(msg.ToJsonString(false).c_str(), &CallbackDownload, nullptr, &CallbackProgress, nullptr);
}

下载与暂停

从3.3.0开始SDK新增了下载的扩展类接口,支持断点续传,开发者可以通过该接口设置自定义的超时时间,下载文件的本地存放路径,以及获取下载瞬时速度、下载平均速度等信息。

C++

//下载结果回调
void CallbackDownload(nim::NIMResCode res_code, const nim::DownloadMediaResult& result)
{
    if (res_code == kNIMResSuccess)
    {
        //下载成功
    }
    else if (res_code == kNIMLocalResMsgNosDownloadCancel)
    {
        //下载暂停
    }
    else
    {
        //下载失败
    }
}

//下载过程回调,参数:当前已下载文件大小, 文件大小, 下载相关信息
void CallbackHttpPro(__int64 completed_size, __int64 file_size, const ProgressData& data)
{
    ...
}

//下载瞬时速度回调
void CallbackSpeed(__int64 speed)
{
    if (speed > 0)
    {
        char res[1024];
        sprintf_s(res, "speed : %lld B/s(%lf KB/s)", speed, (static_cast<double>(speed))/1024);
    }
}

static __int64 actual_size_ = 0;
//下载结束后信息回调,参数:此次下载文件的大小, 平均速度
void CallbackTransferInfo(__int64 actual_size, __int64 speed)
{    
    actual_size_ += actual_size;
    char res[1024];
    sprintf_s(res, "average speed : %lld(%lf KB/s)", speed, (static_cast<double>(speed))/1024);

    sprintf_s(res, "actual size : %lld, block size : %lld", actual_size_, actual_size);
}

//发起下载任务
void foo_start_http_download(const std::string& task_uuid)
{
    Json::FastWriter writer;
    Json::Value value;
    value[kNIMNosLowLimit] = ; //int(选填) HTTP通用配置,传输速度,每秒字节数(默认10)
    value[kNIMNosLowTime] = ;  //int(选填) HTTP通用配置,传输过程中当low_time秒时间内传输速度小于low_limit时(字节每秒),下载任务会返回超时而取消(默认60)
    value[kNIMNosTimeout] = ;  //int(选填) HTTP通用配置,超时时间,单位ms,下载时最小10000,上传时最小30000,不设置默认30000
    value[kNIMNosSaveAsFilePath] = ; //string(选填) HTTP下载任务的文件存放本地路径,不填则默认路径回调中返回
    value[kNIMNosFileSize] = ; //int64(断点续传必填) HTTP下载任务的文件大小,需要续传功能必填,单位Byte
    value[kNIMNosTaskId] = task_uuid;   //string(断点续传必填) HTTP通用配置,任务ID,如果传入的ID是曾经未完成的传输任务,则会开始续传(用户需要保证ID的唯一性)
    nim::NOS::DownloadResourceEx(url, writer.write(value), &CallbackDownload, &CallbackHttpPro, &CallbackSpeed, &CallbackTransferInfo);
}

//暂停下载任务
void foo_pause_http_download(const std::string& task_uuid)
{
    nim::NOS::StopDownloadResourceEx(task_uuid);
}

C#

//发起下载任务
private void StartDownloadEx()
{
    //设置下载控制属性
    HttpExtendedParameters params = new HttpExtendedParameters();
    NosAPI.DownloadEx(NosUploadData.Url,params, ReportDownloadResult, IntPtr.Zero,
        ReportDownloadPrg, IntPtr.Zero,
        ReportDownloadSpeed, IntPtr.Zero,
        ReportDownloadInfo, IntPtr.Zero);
}

private void ReportDownloadInfo(long actual_download_size, long download_speed, string json_extension, IntPtr user_data)
{
    //TODO:下载信息
}

private void ReportDownloadSpeed(long download_speed, string json_extension, IntPtr user_data)
{
    //TODO:下载速度
}

private void ReportDownloadPrg(long downloaded_size, long file_size, string json_extension, IntPtr user_data)
{
    //TODO:下载进度
}

private void ReportDownloadResult(int rescode, string file_path, string call_id, string res_id, string json_extension, IntPtr user_data)
{
    //TODO:下载结果
}

//停止下载任务,taskId 为调用DownloadEx 时传入的参数,参考 HttpExtendedParameters
NIM.Nos.NosAPI.StopDownloadEx(string taskId, string ext = null)

C

//下载结果回调
static void CallbackDownloadEx(int res_code, const char *file_path, const char *call_id, const char *res_id, const char *json_extension, const void *user_data)
{
    if (res_code == kNIMResSuccess)
    {
        //下载成功
    }
    else if (res_code == kNIMLocalResMsgNosDownloadCancel)
    {
        //下载暂停
    }
    else
    {
        //下载失败
    }
}

//下载过程回调,参数:当前已下载文件大小, 文件大小, 下载相关信息
static void CallbackProgressEx(int64_t completed_size, int64_t total_size, const char *json_extension, const void *callback)
{
    ···
}

//下载瞬时速度回调
static void CallbackSpeed(int64_t speed, const char *json_extension, const void *callback)
{
    if (speed > 0)
    {
        char res[1024];
        sprintf_s(res, "speed : %lld B/s(%lf KB/s)", speed, (static_cast<double>(speed))/1024);
    }
}

static __int64 actual_size_ = 0;
//下载结束后信息回调,参数:此次下载文件的大小, 平均速度
static void CallbackTransferInfo(int64_t actual_size, int64_t speed, const char *json_extension, const void *callback)
{
    actual_size_ += actual_size;
    char res[1024];
    sprintf_s(res, "average speed : %lld(%lf KB/s)", speed, (static_cast<double>(speed))/1024);

    sprintf_s(res, "actual size : %lld, block size : %lld", actual_size_, actual_size);
}

typedef void(*nim_nos_download_ex)(const char *nos_url, const char *json_extension, nim_nos_download_cb_func callback_result, const void *res_user_data, nim_nos_download_prg_cb_func prg_cb, const void *prg_user_data, nim_nos_download_speed_cb_func speed_cb, const void *speed_user_data, nim_nos_download_info_cb_func info_cb, const void *info_user_data);

typedef void(*nim_nos_stop_download_ex)(const char *task_id, const char *json_extension);

//发起下载任务
void foo_start_http_download(const std::string& task_uuid)
{
    Json::FastWriter writer;
    Json::Value value;
    value[kNIMNosLowLimit] = ; //int(选填) HTTP通用配置,传输速度,每秒字节数(默认10)
    value[kNIMNosLowTime] = ;  //int(选填) HTTP通用配置,传输过程中当low_time秒时间内传输速度小于low_limit时(字节每秒),下载任务会返回超时而取消(默认60)
    value[kNIMNosTimeout] = ;  //int(选填) HTTP通用配置,超时时间,单位ms,下载时最小10000,上传时最小30000,不设置默认30000
    value[kNIMNosSaveAsFilePath] = ; //string(选填) HTTP下载任务的文件存放本地路径,不填则默认路径回调中返回
    value[kNIMNosFileSize] = ; //int64(断点续传必填) HTTP下载任务的文件大小,需要续传功能必填,单位Byte
    value[kNIMNosTaskId] = task_uuid;   //string(断点续传必填) HTTP通用配置,任务ID,如果传入的ID是曾经未完成的传输任务,则会开始续传(用户需要保证ID的唯一性)

    nim_nos_download_ex func = (nim_nos_download_ex) GetProcAddress(hInst, "nim_nos_download_ex");
    func(nos_url.c_str(), writer.write(value).c_str(), &CallbackDownloadEx, callback_result_userdata, &CallbackProgressEx, callback_progress_pointer, &CallbackSpeed, callback_speed_pointer, &CallbackTransferInfo, callback_transfer_pointer);
}    

//暂停下载任务
void foo_pause_http_download(const std::string& task_uuid)
{
    nim_nos_stop_download_ex func = (nim_nos_stop_download_ex) GetProcAddress(hInst, "nim_nos_stop_download_ex");
    func(task_uuid.c_str(), nullptr);
}

上传

上传资源,回调函数包括了上传结果以及上传进度

上传结果回调函数
@param rescode:错误码,200表示成功
@param url:上传成功后返回的url
typedef void(*nim_nos_upload_cb_func)(int rescode, const char *url, const char *json_extension, const void *user_data);

上传进度回调函数
@uploaded_size:已上传大小
@file_size:文件大小
typedef void(*nim_nos_upload_prg_cb_func)(__int64 uploaded_size, __int64 file_size, const char *json_extension, const void *user_data);    

示例代码如下:

C++

void OnUploadCallback(int res_code, const std::string& url)
{

}

void foo()
{
    nim::NOS::UploadResource("D://test_file.txt", &OnUploadCallback);
}

C#

void foo()
{
    NIM.Nos.NosAPI.Upload("D://test_file.txt", (res_code, url) =>
    {
    }, null);
}

C

void CallbackUpload(int res_code, const char *url, const char *json_extension, const void *user_data)
{
    ···
}

void CallbackProgress(__int64 completed_size, __int64 total_size, const char *json_extension, const void *callback)
{
    ···
}

typedef void(*nim_nos_upload)(const char *local_file, nim_nos_upload_cb_func callback_result, const void *res_user_data, nim_nos_upload_prg_cb_func prg_cb, const void *prg_user_data);

void foo()
{
    nim_nos_upload func = (nim_nos_upload) GetProcAddress(hInst, "nim_nos_upload");
    func("D://test_file.txt", &CallbackUpload, nullptr, &CallbackProgress, nullptr);
}

上传与暂停

从3.3.0开始SDK新增了上传的扩展类接口,支持断点续传,开发者可以通过该接口设置自定义的超时时间,以及获取上传瞬时速度、上传平均速度等信息。

C++

//上传结果回调
void CallbackUpload(nim::NIMResCode res_code, const nim::DownloadMediaResult& result)
{
    if (res_code == kNIMResSuccess)
    {
        //上传成功
    }
    else if (res_code == kNIMLocalResMsgNosUploadCancel)
    {
        //上传暂停
    }
    else
    {
        //上传失败
    }
}

//上传过程回调,参数:当前已上传文件大小, 文件大小, 上传相关信息
void CallbackHttpPro(__int64 completed_size, __int64 file_size, const ProgressData& data)
{
    ...
}

//上传瞬时速度回调
void CallbackSpeed(__int64 speed)
{
    if (speed > 0)
    {
        char res[1024];
        sprintf_s(res, "speed : %lld B/s(%lf KB/s)", speed, (static_cast<double>(speed))/1024);
    }
}

static __int64 actual_size_ = 0;
//上传结束后信息回调,参数:此次上传文件的大小, 平均速度
void CallbackTransferInfo(__int64 actual_size, __int64 speed)
{    
    actual_size_ += actual_size;
    char res[1024];
    sprintf_s(res, "average speed : %lld(%lf KB/s)", speed, (static_cast<double>(speed))/1024);

    sprintf_s(res, "actual size : %lld, block size : %lld", actual_size_, actual_size);
}

//发起上传任务
void foo_start_http_upload(const std::string& task_uuid)
{
    Json::FastWriter writer;
    Json::Value value;
    value[kNIMNosLowLimit] = ; //int(选填) HTTP通用配置,传输速度,每秒字节数(默认10)
    value[kNIMNosLowTime] = ;  //int(选填) HTTP通用配置,传输过程中当low_time秒时间内传输速度小于low_limit时(字节每秒),下载任务会返回超时而取消(默认60)
    value[kNIMNosTimeout] = ;  //int(选填) HTTP通用配置,超时时间,单位ms,下载时最小10000,上传时最小30000,不设置默认30000
    value[kNIMNosNeedContinueTrans] = ; //bool 需要支持断点续传True
    value[kNIMNosTaskId] = task_uuid;   //string(断点续传必填) HTTP通用配置,任务ID,如果传入的ID是曾经未完成的传输任务,则会开始续传(用户需要保证ID的唯一性)
    nim::NOS::UploadResourceEx(file_path, writer.write(value), &CallbackUpload, &CallbackHttpPro, &CallbackSpeed, &CallbackTransferInfo);
}

//暂停上传任务
void foo_pause_http_upload(const std::string& task_uuid)
{
    nim::NOS::StopUploadResourceEx(task_uuid);
}

C#

//开始上传任务
private void StartUploadEx(string filePath)
{
    HttpExtendedParameters params = new HttpExtendedParameters();
    params.TaskID = ""; //任务ID,停止上传任务时需要用到该值
    NIM.Nos.NosAPI.UploadEx(filePath,params, ResultCb, Marshal.StringToHGlobalAnsi("UploadResult"),
    ReportPrg, Marshal.StringToHGlobalAnsi("UploadProgress"),
    ReportSpeed, Marshal.StringToHGlobalAnsi("UploadSpeed"),
    ReportInfo, Marshal.StringToHGlobalAnsi("UploadInfo"));
}

private void ReportInfo(long actual_upload_size, long upload_speed, string json_extension, IntPtr user_data)
{
    //TODO:上传信息
}

private void ReportPrg(long uploaded_size, long file_size, string json_extension, IntPtr user_data)
{
    //TODO:上传进度    
}

private void ReportSpeed(long upload_speed, string json_extension, IntPtr user_data)
{
    //TODO:上传速度
}

private void ResultCb(int rescode, string url, string json_extension, IntPtr user_data)
{
    //TODO:上传结果
}

//停止上传,taskId 为调用 UploadEx 时传入的参数,参考 HttpExtendedParameters
NIM.Nos.NosAPI.StopUploadEx(string taskId, string ext = null)

C

//上传结果回调
static void CallbackUploadEx(int res_code, const char *url, const char *json_extension, const void *user_data)
{
    if (res_code == kNIMResSuccess)
    {
        //下载成功
    }
    else if (res_code == kNIMLocalResMsgNosDownloadCancel)
    {
        //下载暂停
    }
    else
    {
        //下载失败
    }
}

//上传过程回调,参数:当前已上传文件大小, 文件大小, 上传相关信息
static void CallbackProgressEx(int64_t completed_size, int64_t total_size, const char *json_extension, const void *callback)
{
    ···
}

//上传瞬时速度回调
static void CallbackSpeed(int64_t speed, const char *json_extension, const void *callback)
{
    if (speed > 0)
    {
        char res[1024];
        sprintf_s(res, "speed : %lld B/s(%lf KB/s)", speed, (static_cast<double>(speed))/1024);
    }
}

static __int64 actual_size_ = 0;
//上传结束后信息回调,参数:此次上传文件的大小, 平均速度
static void CallbackTransferInfo(int64_t actual_size, int64_t speed, const char *json_extension, const void *callback)
{
    actual_size_ += actual_size;
    char res[1024];
    sprintf_s(res, "average speed : %lld(%lf KB/s)", speed, (static_cast<double>(speed))/1024);

    sprintf_s(res, "actual size : %lld, block size : %lld", actual_size_, actual_size);
}

typedef void(*nim_nos_upload_ex)(const char *local_file, const char *json_extension, nim_nos_upload_cb_func callback_result, const void *res_user_data, nim_nos_upload_prg_cb_func prg_cb, const void *prg_user_data, nim_nos_upload_speed_cb_func speed_cb, const void *speed_user_data, nim_nos_upload_info_cb_func info_cb, const void *info_user_data);

typedef void(*nim_nos_stop_upload_ex)(const char *task_id, const char *json_extension);


//发起上传任务
void foo_start_http_download(const std::string& task_uuid)
{
    Json::FastWriter writer;
    Json::Value value;
    value[kNIMNosLowLimit] = ; //int(选填) HTTP通用配置,传输速度,每秒字节数(默认10)
    value[kNIMNosLowTime] = ;  //int(选填) HTTP通用配置,传输过程中当low_time秒时间内传输速度小于low_limit时(字节每秒),下载任务会返回超时而取消(默认60)
    value[kNIMNosTimeout] = ;  //int(选填) HTTP通用配置,超时时间,单位ms,下载时最小10000,上传时最小30000,不设置默认30000
    value[kNIMNosNeedContinueTrans] = ; //bool 需要支持断点续传True
    value[kNIMNosTaskId] = task_uuid;   //string(断点续传必填) HTTP通用配置,任务ID,如果传入的ID是曾经未完成的传输任务,则会开始续传(用户需要保证ID的唯一性)

    nim_nos_upload_ex func = (nim_nos_upload_ex) GetProcAddress(hInst, "nim_nos_upload_ex");
    func(local_file.c_str(), writer.write(value).c_str(), &CallbackUploadEx, callback_result_userdata, &CallbackProgressEx, callback_progress_pointer, &CallbackSpeed, callback_speed_pointer, &CallbackTransferInfo, callback_transfer_pointer);
}    

//暂停上传任务
void foo_pause_http_download(const std::string& task_uuid)
{
    nim_nos_stop_upload_ex func = (nim_nos_stop_upload_ex) GetProcAddress(hInst, "nim_nos_stop_upload_ex");
    func(task_uuid.c_str(), nullptr);
}

文档转换

文档转换,允许用户上传ppt、pptx、pdf等文档到服务器,服务器提供文档转换成图片、保存用户文档列表、删除、查询文档等功能。其中文件的上传下载使用nos的服务实现nim_nos_upload_ex/nim_nos_download/nim_nos_download_ex。

注册转换结果通知回调

注册文档转换的结果的回调通知(服务器异步转换,客户端需要等待通知才知道转换结果)

@param[in] json_extension 无效扩展字段
@param[in] cb 结果回调见nim_doc_trans_def.h,成功返回的json_extension中带有一条记录
@param[in] user_data APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理!
@return void 无返回值
typedef void(*nim_doctrans_reg_notify_cb)(const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

示例代码如下:

C++

void OnDocInfoCallback(int32_t code, const DocTransInfo& doc_info)
{

}

void foo()
{
    nim::DocTrans::RegNotifyCb(&OnDocInfoCallback);
}

C#

NIM.DocTransition.DocTransApi.RegisterNotifyCallback(DocTransDelegate cb)

C

void CallbackDocTrans(int32_t code, const char *json_extension, const void *user_data)
{
    ···
}

typedef void(*void nim_doctrans_reg_notify_cb)(const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

void foo()
{
    nim_doctrans_reg_notify_cb func = (nim_doctrans_reg_notify_cb) GetProcAddress(hInst, "nim_doctrans_reg_notify_cb");
    func("", &CallbackDocTrans, nullptr);
}

查询文档信息

根据文档id查询文档信息

@param[in] id 文档id
@param[in] json_extension 无效扩展字段
@param[in] cb 结果回调见nim_doc_trans_def.h,成功返回的json_extension中带有一条记录
@param[in] user_data APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理!
@return void 无返回值
typedef void(*nim_doctrans_get_info)(const char *id, const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

示例代码如下:

C++

void OnDocInfoCallback(int32_t code, const DocTransInfo& doc_info)
{

}

void foo()
{
    nim::DocTrans::GetInfo("id...", "", const DocInfoCallback& cb);
}

C#

private void QueryTransInfoById(string id)
{
    NIM.DocTransition.DocTransApi.GetTransitionInfo(id, OnGetTransInfo);
}

private void OnGetTransInfo(int code, DocTransInfo info)
{
    //TODO:处理查询文档结果
}

C

void CallbackDocTrans(int32_t code, const char *json_extension, const void *user_data)
{
    ···
}

typedef void(*nim_doctrans_get_info)(const char *id, const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

void foo()
{
    nim_doctrans_get_info func = (nim_doctrans_get_info) GetProcAddress(hInst, "nim_doctrans_get_info");
    func("id...", "", &CallbackDocTrans, nullptr);
}

分页查询文档信息

查询文档信息列表

@param[in] id        查询的起始docId,若为空,表示从头开始查找,按照文档转码的发起时间降序排列
@param[in] limit    查询的文档的最大数目,有最大值限制,目前为30
@param[in] json_extension 无效扩展字段
@param[in] cb        结果回调见nim_doc_trans_def.h,成功返回的json_extension中带有记录列表
@param[in] user_data APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理!
@return void 无返回值
typedef void(*nim_doctrans_get_info_list)(const char *id, int32_t limit, const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

示例代码如下:

C++

void OnDocInfosCallback(int32_t code, int32_t count, const std::list<DocTransInfo>& doc_infos)
{

}

void foo()
{
    nim::DocTrans::GetInfoList("", 30, "", const OnDocInfosCallback& cb);
}

C#

/// <summary>
/// 根据文档id查询文档信息
/// </summary>
/// <param name="id">查询的起始docId,若为空,表示从头开始查找,按照文档转码的发起时间降序排列</param>
/// <param name="limit">查询的文档的最大数目,有最大值限制,目前为30</param>
/// <param name="cb"></param>
NIM.DocTransition.DocTransApi.GetTransitionInfoList(string id, int limit, GetTransListDelegate cb)

C

void CallbackDocTrans(int32_t code, const char *json_extension, const void *user_data)
{
    ···
}

typedef void(*nim_doctrans_get_info_list)(const char *id, int32_t limit, const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

void foo()
{
    nim_doctrans_get_info_list func = (nim_doctrans_get_info_list) GetProcAddress(hInst, "nim_doctrans_get_info_list");
    func("", 30, "", &CallbackDocTrans, nullptr);
}

删除文档信息

根据文档id删除服务器记录,对于正在转码中的文档,删除后将不会收到转码结果的通知

@param[in] id 文档id
@param[in] json_extension 无效扩展字段
@param[in] cb 结果回调见nim_doc_trans_def.h,返回的json_extension无效
@param[in] user_data APP的自定义用户数据,SDK只负责传回给回调函数cb,不做任何处理!
@return void 无返回值
typedef void(*nim_doctrans_del_info)(const char *id, const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

示例代码如下:

C++

void OnDocInfoCallback(int32_t code, const DocTransInfo& doc_info)
{

}

void foo()
{
    nim::DocTrans::DeleteInfo("id...", "", const OnDocInfoCallback& cb);
}

C#

/// <summary>
/// 根据文档id删除服务器记录,对于正在转码中的文档,删除后将不会收到转码结果的通知
/// </summary>
/// <param name="id">文档id</param>
/// <param name="cb"></param>
NIM.DocTransition.DocTransApi.DeleteTransition(string id, DocTransDelegate cb)

C

void CallbackDocTrans(int32_t code, const char *json_extension, const void *user_data)
{
    ···
}

typedef void(*nim_doctrans_del_info)(const char *id, const char *json_extension, nim_doctrans_opt_cb_func cb, const void *user_data);

void foo()
{
    nim_doctrans_del_info func = (nim_doctrans_del_info) GetProcAddress(hInst, "nim_doctrans_del_info");
    func("id...", "", &CallbackDocTrans, nullptr);
}

文档源的下载地址

拼接文档源的下载地址

@param[in] url_prefix    文档信息中的url前缀
@param[in] file_type    文档源类型
@return char * 返回文档源的下载地址,需要上层调用nim_global.h提供的内存释放接口释放。
typedef char *(*nim_doctrans_get_source_file_url)(const char *url_prefix, NIMDocTranscodingFileType file_type);

示例代码如下:

C++

std::string foo()
{
    return nim::DocTrans::GetSourceFileUrl("http....", kNIMDocTranscodingFileTypePPT);
}

C#

private void GetSourUrl(DocTransInfo info)
{
    var sourceUrl = NIM.DocTransition.DocTransApi.GetSourceFileUrl(info.UrlPrefix, info.SourceFileType);
}

C

typedef char *(*nim_doctrans_get_source_file_url)(const char *url_prefix, NIMDocTranscodingFileType file_type);
typedef    void (*nim_global_free_buf)(void *data);

void foo()
{
    nim_doctrans_get_source_file_url func = (nim_doctrans_get_source_file_url) GetProcAddress(hInst, "nim_doctrans_get_source_file_url");
    nim_global_free_buf free_func = (nim_global_free_buf) GetProcAddress(hInst, "nim_global_free_buf");
    const char *url = func("http....", kNIMDocTranscodingFileTypePPT);

    ....

    free_func((void *)url);
}

文档图片的下载地址

拼接文档图片的下载地址

@param[in] url_prefix    文档信息中的url前缀
@param[in] img_type        文档转换的图片类型
@param[in] quality        需要的图片清晰度
@param[in] page_num        图片页码(从1开始计算)
@return char * 返回文档图片的下载地址,需要上层调用nim_global.h提供的内存释放接口释放。
typedef char *(*nim_doctrans_get_page_url)(const char *url_prefix, NIMDocTranscodingImageType img_type, NIMDocTranscodingQuality quality, int32_t page_num);

示例代码如下:

C++

std::string foo()
{
    return nim::DocTrans::GetPageUrl("http....", kNIMDocTranscodingImageTypeJPG, kNIMDocTranscodingQualityHigh, 1);
}

C#

private void GetSourUrl(DocTransInfo info)
{
    var destUrl = NIM.DocTransition.DocTransApi.GetPageUrl(info.UrlPrefix, info.DestImageType,NIMDocTranscodingQuality.kNIMDocTranscodingQualityHigh, info.PageNum);
}

C

typedef char *(*nim_doctrans_get_page_url)(const char *url_prefix, NIMDocTranscodingImageType img_type, NIMDocTranscodingQuality quality, int32_t page_num);
typedef    void (*nim_global_free_buf)(void *data);

void foo()
{
    nim_doctrans_get_page_url func = (nim_doctrans_get_page_url) GetProcAddress(hInst, "nim_doctrans_get_page_url");
    nim_global_free_buf free_func = (nim_global_free_buf) GetProcAddress(hInst, "nim_global_free_buf");
    const char *url = func("http....", kNIMDocTranscodingImageTypeJPG, kNIMDocTranscodingQualityHigh, 1);

    ....

    free_func((void *)url);
}

事件订阅

事件订阅,允许用户订阅其他人发布的事件,当被订阅人发布事件后,订阅者可以收到响应的通知。事件分为系统预定义事件和自定义事件,系统预定义事件类型范围为1-99999,用户自定义事件范围必须大于99999。 对于用户自定义事件,用户可以根据自己的需求场景来指定不同的事件类型和事件来定义不同的含义;对于系统预定义事件,系统保留1~9999的事件值,用户如果要发送系统预定义事件,事件值必须大于9999。 目前系统预定义事件只有在线状态事件,事件值为1,当用户登录、登出、异常退出后,服务器会下发响应的在线状态事件值来通知订阅者。用户在某端订阅或者取消订阅某些事件,会同时对其他端的生效,所以取消订阅事件时需谨慎,防止影响了其他端。

接收事件

提前注册好接收事件的回调函数,在登录后,回调函数会收到订阅的事件信息。需要注册两个回调函数:接收订阅事件回调函数、批量接收订阅的事件的回调函数。

示例代码如下:

C++

void OnPushEventCallback(nim::NIMResCode res_code, const nim::EventData& event_data)
{    
    if (res_code == nim::kNIMResSuccess)
    {
        ...
    }
}

void :OnBatchPushEventCallback(nim::NIMResCode res_code, const std::list<nim::EventData>& event_list)
{
    if (res_code == nim::kNIMResSuccess)
    {
        ...
    }
}

std::string foo()
{
    nim::SubscribeEvent::RegPushEventCb(&OnPushEventCallback);
    nim::SubscribeEvent::RegBatchPushEventCb(&OnBatchPushEventCallback);
));
}

C#

...

C

void CallbackPushEvent(int res_code, const char *event_info_json, const char *json_extension, const void *user_data)
{
    if (user_data)
    {
        ...
    }
}

void CallbackBatchPushEvent(int res_code, const char *event_list_json, const char *json_extension, const void *user_data)
{
    if (user_data)
    {
        ...
    }
}

typedef void(*nim_subscribe_event_reg_push_event_cb)(const char *json_extension, nim_push_event_cb_func cb, const void *user_data);
typedef void(*nim_subscribe_event_reg_batch_push_event_cb)(const char *json_extension, nim_batch_push_event_cb_func cb, const void *user_data);

void foo()
{
    nim_subscribe_event_reg_push_event_cb func = (nim_subscribe_event_reg_push_event_cb) GetProcAddress(hInst, "nim_subscribe_event_reg_push_event_cb");
    nim_subscribe_event_reg_batch_push_event_cb batch_func = (nim_subscribe_event_reg_batch_push_event_cb) GetProcAddress(hInst, "nim_subscribe_event_reg_batch_push_event_cb");

    func(nullptr, &CallbackPushEvent, nullptr);
    batch_func(nullptr, &CallbackBatchPushEvent, nullptr);
}

发布事件

用户可以根据自己的需求来发布系统预定义事件或自定义事件。发布事件时,需要指定事件类型、事件值、事件消息id、事件有效期、事件广播类型、事件同步类型,可以根据需求选择是否附加自定义事件扩展属性。其中事件有效期范围为:60s到7天,单位秒,订阅者在有效期内登录才可以收到对应的事件;事件广播类型指定是否通知离线订阅者;事件同步类型指定是否把事件同步给自己的其他端;自定义事件扩展属性可以设置任意字符串。

示例代码如下:

C++

void EventCallback(nim::NIMResCode res_code, int event_type, const nim::EventData& event_data)
{
    ...
}

std::string foo()
{
    nim::EventData event_data;
    event_data.event_type_ = nim::kNIMEventTypeOnlineState;
    event_data.event_value_ = nim::kNIMEventOnlineStateValueUpdateConfig;
    event_data.client_msg_id_ =本地定义的消息id;
    event_data.ttl_ = 7*24*60*60;
    event_data.broadcast_type_ = nim::kNIMEventBroadcastTypeAll;
    event_data.sync_self_ = nim::kNIMEventSyncTypeSelf;

    nim::SubscribeEvent::Publish(event_data, &EventCallback);
}

C#

...

C

void CallbackPublishEvent(int res_code, int event_type, const char *event_info_json, const char *json_extension, const void *user_data)
{
    if (user_data)
    {
        ...
    }
}

typedef void(*nim_publish_event)(const char *event_json, const char *json_extension, nim_publish_event_cb_func cb, const void *user_data);

void foo()
{
    nim_publish_event func = (nim_publish_event) GetProcAddress(hInst, "nim_publish_event");

    Json::FastWriter fs;
    Json::Value values;
    values[kNIMEventEventType] = nim::kNIMEventTypeOnlineState;
    values[kNIMEventEventValue] = nim::kNIMEventOnlineStateValueCustom + 1;
    values[kNIMEventMsgIdClient] = 本地定义的消息id;
    values[kNIMEventConfig] = config_;
    values[kNIMEventTTL] = 7*24*60*60;
    values[kNIMEventBroadcastType] = nim::kNIMEventBroadcastTypeAll;
    values[kNIMEventSyncSelf] = nim::kNIMEventSyncTypeSelf;
    std::string json_value = fs.write(values);

    func(json_value.c_str(), nullptr, &CallbackPublishEvent, nullptr);
}

订阅事件

只有先订阅了某个用户的某个事件后,才可以收到对应用户发布的事件。订阅事件,需要指定事件类型、订阅有效期、订阅后是否立即同步最新事件。其中订阅有效期范围为:60s到30天,单位秒,如果订阅有效期超时后,需要重新订阅才可以继续收到事件

示例代码如下:

C++

void EventCallback(nim::NIMResCode res_code, int event_type, const std::list<std::string>& faild_list)
{
    ...
}

std::string foo()
{
    std::list<std::string> subscribe_list;
    subscribe_list.push_back("test1");
    subscribe_list.push_back("test2");

    nim::SubscribeEvent::Subscribe(nim::kNIMEventTypeOnlineState,
        1*24*60*60,
        nim::kNIMEventSubscribeSyncTypeSync,
        subscribe_ids,
        &EventCallback,
    );
}

C#

...

C

void CallbackSubscribe(int res_code, int event_type, const char *faild_list_json, const char *json_extension, const void *user_data)
{
    ...
}

typedef void(*nim_subscribe_event)(int event_type, int64_t ttl, int sync_event, const char *accid_list_json, const char *json_extension, nim_subscribe_event_cb_func cb, const void *user_data);

void foo()
{
    Json::FastWriter fs;
    Json::Value value;
    value.append("test1");
    value.append("test2");
    std::string json_value = fs.write(value);

    nim_subscribe_event func = (nim_subscribe_event) GetProcAddress(hInst, "nim_subscribe_event");
    func(nim::kNIMEventTypeOnlineState,
    1*24*60*60,
    nim::kNIMEventSubscribeSyncTypeSync,
    json_value.c_str(),
    nullptr,
    &CallbackSubscribe,
    nullptr);
}

取消订阅事件

示例代码如下:

C++

void EventCallback(nim::NIMResCode res_code, int event_type, const std::list<std::string>& faild_list)
{
    ...
}

std::string foo()
{
    std::list<std::string> unsubscribe_list;
    unsubscribe_list.push_back("test1");
    unsubscribe_list.push_back("test2");

    nim::SubscribeEvent::UnSubscribe(nim::kNIMEventTypeOnlineState, unsubscribe_ids, &EventCallback);
}

C#

...

C

void CallbackUnSubscribe(int res_code, int event_type, const char *faild_list_json, const char *json_extension, const void *user_data)
{
    ...
}

typedef void(*nim_unsubscribe_event)(int event_type, const char *accid_list_json, const char *json_extension, nim_unsubscribe_event_cb_func cb, const void *user_data);

void foo()
{
    Json::FastWriter fs;
    Json::Value value;
    value.append("test1");
    value.append("test2");
    std::string json_value = fs.write(value);

    nim_unsubscribe_event func = (nim_unsubscribe_event) GetProcAddress(hInst, "nim_unsubscribe_event");
    func(nim::kNIMEventTypeOnlineState, json_value.c_str(),    nullptr, &CallbackUnSubscribe, nullptr);
}

批量取消订阅事件

示例代码如下:

C++

void EventCallback(nim::NIMResCode res_code, int event_type)
{
    ...
}

std::string foo()
{
    nim::SubscribeEvent::BatchUnSubscribe(nim::kNIMEventTypeOnlineState, &EventCallback);
}

C#

...

C

void CallbackBatchUnSubscribe(int res_code, int event_type, const char *json_extension, const void *user_data)
{
    if (user_data)
    {
        ...
    }
}

typedef void(*nim_batch_unsubscribe_event)(int event_type, const char *json_extension, nim_batch_unsubscribe_event_cb_func cb, const void *user_data);

void foo()
{
    nim_batch_unsubscribe_event func = (nim_batch_unsubscribe_event) GetProcAddress(hInst, "nim_batch_unsubscribe_event");
    func(nim::kNIMEventTypeOnlineState, nullptr, &CallbackBatchUnSubscribe, nullptr);
}

查询订阅关系

示例代码如下:

C++

void EventCallback(nim::NIMResCode res_code, int event_type, const std::list<nim::EventSubscribeData>& subscribe_list)
{
    ...
}

std::string foo()
{
    std::list<std::string> accid_list;
    accid_list.push_back("test1");
    accid_list.push_back("test2");

    nim::SubscribeEvent::QuerySubscribe(nim::kNIMEventTypeOnlineState, accid_list, &EventCallback);
}

C#

...

C

void CallbackQuerySubscribe(int res_code, int event_type, const char *subscribe_list_json, const char *json_extension, const void *user_data)
{
    if (user_data)
    {
        ...
    }
}

typedef void(*nim_query_subscribe_event)(int event_type, const char *accid_list_json, const char *json_extension, nim_query_subscribe_event_cb_func cb, const void *user_data);

void foo()
{
    Json::FastWriter fs;
    Json::Value value;
    value.append("test1");
    value.append("test2");
    std::string json_value = fs.write(value);

    nim_query_subscribe_event func = (nim_query_subscribe_event) GetProcAddress(hInst, "nim_query_subscribe_event");
    func(nim::kNIMEventTypeOnlineState, json_value.c_str(),    nullptr, &CallbackQuerySubscribe, nullptr);
}

智能机器人

智能聊天机器人解决方案依托网易IM即时通讯、语音识别、语义理解等服务,为开发者提供人机交互API/SDK、语音识别、意图识别、知识库配置、动态接口等功能,可以在应用IM内快速集成场景丰富的智能聊天机器人。

如果开发者通过管理后台关联了网易波特机器人,SDK会在登陆过程中同步机器人列表,开发者和客户可以通过云信消息接口与机器人交互。开发者在初始化SDK后,可以通过以下接口订阅机器人信息变更的广播通知:

C++

void CallbackRobotChanged(NIMResCode rescode, NIMRobotInfoChangeType type, const RobotInfos&)
{
    ...
} 

void foo()
{
    nim::Robot::RegChangedCallback(&CallbackRobotChanged);
}

C#

...

C

static void CallbackRobotInfoChange(int rescode, NIMRobotInfoChangeType type, const char *res, const char *json_extension, const void *callback)
{
    ...
}

typedef void (*nim_robot_reg_changed_callback)(const char *json_extension, nim_robot_change_cb_func cb, const void *user_data);

void foo()
{
    nim_robot_reg_changed_callback func = (nim_robot_reg_changed_callback)GetProcAddress(hInst, "nim_robot_reg_changed_callback");
    func(nullptr, &CallbackRobotInfoChange, nullptr);
}

查询机器人列表

C++

void foo()
{
    auto robot_infos = nim::Robot::QueryAllRobotInfosBlock("");
}

C#

...

C

typedef char* (*nim_robot_query_all_robots_block)(const char *json_extension);
void foo()
{
    nim_robot_query_all_robots_block func = (nim_robot_query_all_robots_block)GetProcAddress(hInst, "nim_robot_query_all_robots_block");
    auto robot_infos = func(nullptr);
}

C++

void foo()
{
    auto robot = nim::Robot::QueryRobotInfoByAccidBlock(robot_accid, "");
}

C#

...

C

typedef char* (*nim_robot_query_robot_by_accid_block)(const char *accid, const char *json_extension);
void foo()
{
    nim_robot_query_robot_by_accid_block func = (nim_robot_query_robot_by_accid_block)GetProcAddress(hInst, "nim_robot_query_robot_by_accid_block");
    auto robot = func(robot_accid);
}        

发消息

机器人消息类型为kNIMMessageTypeRobot,涉及到机器人消息附件内容模板可以参阅机器人消息体模板说明,此外发送机器人消息与发送其他消息没有差别,以C++为例:

void SendRobotMessage(const std::string &content)
{
    nim::IMMessage msg;
    msg.type_                 = nim::kNIMMessageTypeRobot; //定义为机器人消息类型
    msg.content_             = content;                      //消息正文需要赋值
    msg.session_type_        = ; //当前会话类型,P2P Or 群组
    msg.receiver_accid_        = ;    //接收者ID
    msg.client_msg_id_       = ;    //消息id,一般使用guid

    nim::IMBotRobot bot;          //组装机器人类型消息的附件内容
    bot.robot_accid_             = robot_accid; //机器人云信ID
    bot.sent_param_["content"]    = content; 
    bot.sent_param_["type"]     = "01"; //01为文本,具体定义可详见机器人消息体模板说明或波特官网开发文档
    msg.attach_ = bot.ToJsonString();

    std::string json_msg = nim::Talk::CreateBotRobotMessage(msg.receiver_accid_, msg.session_type_, msg.client_msg_id_, content, bot, setting, msg.timetag_);
    nim::Talk::SendMsg(json_msg);
}

收消息

接收机器人消息与接收其他消息没有太多差异,主要注意的是无论是发消息还是收消息,发送方ID都是自己,接收方ID都是发送时填写的接受者ID,这里判断是否是机器人回复的消息可以通过消息附件中的outMsg字段,涉及到机器人消息附件内容模板可以参阅机器人消息体模板说明,以C++为例:

//部分源码来自C++ Demo(bubble_robot.cpp)
void OnReceiveMsgCallback(const nim::IMMessage& message)
{
    if (message.tyoe_ == nim::kNIMMessageTypeRobot)
    {
        nim::IMBotRobot msg_attach;
        nim::Talk::ParseBotRobotMessageAttach(message, msg_attach);
        if (!msg_attach.out_msg_)
        {
            //客户端发给机器人的上行消息
        }
        else
        {
            //机器人回复的下行消息
        }

        if (msg_attach.robot_msg_flag_ == "faq")
        {
            //faq为波特平台定义的机器人消息类型,具体定义可详见机器人消息体模板说明或波特官网开发文档
        }
        else if (msg_attach.robot_msg_flag_ == "bot")
        {
            //bot为波特平台定义的机器人消息类型,具体定义可详见机器人消息体模板说明或波特官网开发文档
        }
    }
}

IM Demo v4.0.0提供了智能机器人的初步体验,开发者可以通过源码进一步了解机器人消息的解析过程。

SDK 开发示例

#include "stdafx.h"
#include <windows.h>
#include <cassert>
#include <iostream>
#include <string>
#include "json/json.h"
#include "nim_res_code_def.h"
#include "nim_client_def.h"

//初始化和卸载
typedef bool (*nim_client_init)(const char *app_data_dir, const char *app_install_dir, const char *json_extension);
typedef void (*nim_client_cleanup)(const char *json_extension);

//登录和退出
typedef void (*nim_client_login)(const char *app_key, const char *account, const char *password, const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);
typedef void (*nim_client_logout)(NIMLogoutType logout_type, const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);

//加载模块和接口
class SdkNim
{
public:
    template <typename F>
    static F Function(const char* function_name)
    {
        F f = (F) ::GetProcAddress(nim_sdk_, function_name);
        assert(f);
        return f;
    }
public:
    static bool Load()
    {
        nim_sdk_ = ::LoadLibraryW(L"nim_sdk_dll.dll");
        if( nim_sdk_ == NULL )
        {
            wprintf_s(L"Load nim_sdk_dll.dll Fail: %d", ::GetLastError());
            return false;
        }
        else
        {
            return true;
        }
    }
    static void UnLoad()
    {
        assert(nim_sdk_ != NULL);
        if( nim_sdk_ != NULL )
        {
            ::FreeLibrary(nim_sdk_);
            nim_sdk_ = NULL;
        }
    }
    static bool Init()
    {
        nim_client_init f_init = Function<nim_client_init>("nim_client_init");
        return f_init("Netease", "", "");
    }
    static void UnInit()
    {
        assert(nim_sdk_ != NULL);
        if( nim_sdk_ != NULL )
        {
            nim_client_cleanup f_cleanup = Function<nim_client_cleanup>("nim_client_cleanup");
            f_cleanup("");
        }
    }
private:
    static HINSTANCE nim_sdk_;
};

HINSTANCE SdkNim::nim_sdk_ = NULL;

//设置简单的标记
bool wait_for_login = false, wait_for_logout = false;
bool login_success = false;

//回调函数
void OnLoginCallback(const char *json_params, const void *user_data)
{
    printf_s("OnLoginCallback:%s", json_params);

    Json::Value json;
    Json::Reader reader;
    if( reader.parse(json_params, json) )
    {
        int code = json[kNIMErrorCode].asInt();
        int step = json[kNIMLoginStep].asInt();
        if( code == kNIMResSuccess )
        {
            if( step == kNIMLoginStepLogin )
            {
                login_success = true;
                wait_for_login = false;
            }
        }
        else
        {
            wait_for_login = false;
        }
    }
    else
    {
        assert(0);
        wait_for_login = false;
    }
}

void OnLogoutCallback(const char *json_params, const void *user_data)
{
    printf_s("OnLogoutCallback:%s", json_params);

    wait_for_logout = false;
}

//登录
void NimLogin()
{
    nim_client_login f_login = SdkNim::Function<nim_client_login>("nim_client_login");
    f_login("app_key", "account", "password", "", &OnLoginCallback, NULL);
}

//退出
void NimLogout()
{
    nim_client_logout f_logout = SdkNim::Function<nim_client_logout>("nim_client_logout");
    f_logout(kNIMLogoutAppExit, "", &OnLogoutCallback, NULL);
}

int main(int, char**)
{
    if( SdkNim::Load() )
    {
        if( SdkNim::Init() )
        {
            wait_for_login = true;
            NimLogin();

            while( wait_for_login )
            {
                ::Sleep(1000);
            }

            if( login_success )
            {
                ::Sleep(3000);

                wait_for_logout = true;
                NimLogout();

                while( wait_for_logout )
                {
                    ::Sleep(1000);
                }
            }

            SdkNim::UnInit();
        }
        else
        {
            wprintf_s(L"SdkNim::Init Fail");
        }
        SdkNim::UnLoad();
    }
    system("pause");
    return 0;
}

聊天室

集成聊天室必读

聊天室功能概述

聊天室特点:

PC端聊天室SDK提供了不同于IMSDK的动态链接库(nim_chatroom.dll),因此加载使用聊天室功能有以下准备工作:

将SDK 相关的dll 文件(nim_chatroom.dll)放到App 的运行目录下。SDK 基于vs2010 开发,如果App 没有对应的运行时库文件(msvcp100.dll和msvcr100.dll),请将其放到App 的运行目录下。

准备工作完成后,在程序启动时,初始化SDK;在程序退出前,清理SDK。示例代码如下:

C++

void Init()
{
    nim_chatroom::ChatRoom::Init("");
}

void Cleanup()
{
    nim_chatroom::ChatRoom::Cleanup();
}

C#

void Init()
{
    NIMChatRoom.ChatRoomApi.Init();
}

void Cleanup()
{
    NIMChatRoom.ChatRoomApi.Cleanup();
}

C

typedef void(*nim_chatroom_init)(const char *json_extension);
typedef    void(*nim_chatroom_cleanup)(const char *json_extension);

void Init()
{
    //获取SDK初始化接口
    HINSTANCE hInst = LoadLibraryW(L"nim_chatroom.dll");
    nim_chatroom_init func = (nim_chatroom_init) GetProcAddress(hInst, "nim_chatroom_init");

    //初始化SDK
    func("");
}

void Cleanup()
{
    nim_chatroom_cleanup func = (nim_chatroom_cleanup) GetProcAddress(hInst, "nim_chatroom_cleanup");
    func("");
    FreeLibrary(hInst);
}

进入聊天室

进入聊天室前,需要通过调用IM模块接口获取进入聊天室的权限,注意:这里调用的是IMSDK模块的接口

C++

void OnChatRoomRequestEnterCallback(int error_code, const std::string& result)
{
}

void foo()
{
    //获取聊天室登录信息
    nim::PluginIn::ChatRoomRequestEnterAsync(room_id, &OnChatRoomRequestEnterCallback);
}

C#

void foo()
{
    NIM.Plugin.ChatRoom.RequestLoginInfo(roomId, (response, authResult) =>
    {
        if (response == NIM.ResponseCode.kNIMResSuccess)
        {

        }
    });
}

C

typedef void(*nim_plugin_chatroom_request_enter_async)(const __int64 room_id, const char *json_extension, nim_plugin_chatroom_request_enter_cb_func cb, const void *user_data);

void CallbackRequestChatRoomEnter(int error_code, const char *result, const char *json_extension, const void *user_data)
{    
    //result为权限信息
}

void foo()
{
    HINSTANCE hInst = LoadLibraryW(L"nim.dll");
    nim_plugin_chatroom_request_enter_async func = (nim_plugin_chatroom_request_enter_async) GetProcAddress(hInst, "nim_plugin_chatroom_request_enter_async");

    func(room_id, null, &CallbackRequestChatRoomEnter, null);
}

获取权限成功后会得到权限信息(回调函数第二个参数result),接下去再调用进入聊天室的接口。

C++

void foo()
{
    ChatRoomEnterInfo info;
    ChatRoom::Enter(room_id_, room_enter_token, info);
}

C#

void foo()
{
    NIMChatRoom.LoginData data = new NIMChatRoom.LoginData();
    data.Nick = "【C# Client】";
    NIMChatRoom.ChatRoomApi.Login(roomId, authResult, data);
}

C typedef bool(nim_chatroom_enter)(const __int64 room_id, const char request_enter_data, const char enter_info, const char json_extension);

void foo()
{
    //获取进入聊天室接口
    HINSTANCE hInst = LoadLibraryW(L"nim_chatroom.dll");
    nim_chatroom_enter func = (nim_chatroom_enter) GetProcAddress(hInst, "nim_chatroom_enter");

    //聊天室可选参数
    Json::Value info_value;
    info_value[kNIMChatRoomEnterKeyNick] = "my_nickname_of_room";//设置聊天室内的昵称
    info_value[kNIMChatRoomEnterKeyAvatar] = "http://my_avatar_url";//设置聊天室内的头像地址
    info_value[kNIMChatRoomEnterKeyExt] = GetJsonStringWithNoStyled(JSON_OBJECT);//设置扩展字段
    info_value[kNIMChatRoomEnterKeyNotifyExt] = GetJsonStringWithNoStyled(JSON_OBJECT);//设置扩展字段用于通知

    //这里直接将权限信息作为参数传入
    func(room_id, result, GetJsonStringWithNoStyled(info_value).c_str(), "");
}

进入聊天室前需要提前注册一些全局回调(具体说明请参阅API 文档),以下以注册进入聊天室结果通知回调为例:

C++

void OnEnterCallback(__int64 room_id, const NIMChatRoomEnterStep step, int error_code, const ChatRoomInfo& info, const ChatRoomMemberInfo& my_info)
{
    if (step != kNIMChatRoomEnterStepRoomAuthOver)
        return;

        if (error_code != nim::kNIMResSuccess && error_code != nim::kNIMResTimeoutError)
        {
            std::wstring kick_tip_str;
            switch (error_code)
            {
            case nim::kNIMResNotExist:
                kick_tip_str = L"聊天室不存在";
                break;
            case nim::kNIMResForbidden:
                kick_tip_str = L"权限问题";
                break;
            case nim::kNIMResRoomLinkError:
            case nim::kNIMResRoomError:
                kick_tip_str = L"聊天室异常";
                break;
            case nim::kNIMResRoomBlackBeOut:
                kick_tip_str = L"黑名单用户禁止进入聊天室";
                break;
            case nim::kNIMResFrequently:
                kick_tip_str = L"操作太频繁,稍后重试";
                break;
            default:
                return;
            }
        }
        else
        {

        }
}

void foo()
{
    ChatRoom::RegEnterCb(&OnEnterCallback);
}

C#

void ChatRoomApi_LoginHandler(NIMChatRoom.NIMChatRoomLoginStep loginStep, NIM.ResponseCode errorCode, NIMChatRoom.ChatRoomInfo roomInfo, NIMChatRoom.MemberInfo memberInfo)
{
    if (loginStep == NIMChatRoom.NIMChatRoomLoginStep.kNIMChatRoomLoginStepRoomAuthOver && errorCode == NIM.ResponseCode.kNIMResSuccess)
    {
    }
    if (errorCode != NIM.ResponseCode.kNIMResSuccess)
    {
        MessageBox.Show(loginStep.ToString() + " " + errorCode.ToString(), "进入聊天室出错");
    }
}

void foo()
{
    NIMChatRoom.ChatRoomApi.LoginHandler += ChatRoomApi_LoginHandler;
}

C

typedef void(*nim_chatroom_reg_enter_cb)(const char *json_extension, nim_chatroom_enter_cb_func cb, const void *user_data);

static void CallbackEnter(__int64 room_id, int step, int error_code, const char *result, const char *json_extension, const void *user_data)
{

}

void foo()
{
    nim_chatroom_reg_enter_cb func = (nim_chatroom_reg_enter_cb)GetProcAddress(hInst, "nim_chatroom_reg_enter_cb");
    func(nullptr, &CallbackEnter, nullptr);    
}

离开聊天室(被踢)

调用接口nim_chatroom_exit 离开聊天室。

C++

void foo()
{
    ChatRoom::Exit(room_id);
}

C#

void foo()
{
    NIMChatRoom.ChatRoomApi.Exit(roomId);
}

C typedef void(nim_chatroom_exit)(const __int64 room_id, const char json_extension);

void foo()
{
    nim_chatroom_exit func = (nim_chatroom_exit) GetProcAddress(hInst, "nim_chatroom_exit");
    func(room_id, nullptr);
}

被踢出聊天室时,开发者可以通过接口nim_chatroom_reg_exit_cb注册全局通知回调函数获取离开聊天室的原因。

C++

void OnEnterCallback(__int64 room_id, int error_code, NIMChatRoomExitReason exit_reason)
{

}

void foo()
{
    ChatRoom::RegExitCb(&OnExitCallback);
}

C#

void ChatRoomApi_ExitHandler(long roomId, NIM.ResponseCode errorCode, NIMChatRoom.NIMChatRoomExitReason reason)
{
    if (errorCode == NIM.ResponseCode.kNIMResSuccess)
    {
    }
}

void foo()
{
    NIMChatRoom.ChatRoomApi.ExitHandler += ChatRoomApi_ExitHandler;
}

C

static void CallbackExit(__int64 room_id, int error_code, int exit_reason, const char *json_extension, const void *user_data)
{
    ...
}

typedef void(*nim_chatroom_reg_exit_cb)(const char *json_extension, nim_chatroom_exit_cb_func cb, const void *user_data);

void foo()
{
    nim_chatroom_reg_exit_cb func = (nim_chatroom_reg_exit_cb) GetProcAddress(hInst, "nim_chatroom_reg_exit_cb");
    func("", &CallbackExit, "");
}    

除了被多端、主播和管理员踢出以外,聊天室被关闭或者被解散也会收到离开聊天室的通知。

聊天室连接情况通知

通过接口nim_chatroom_reg_link_condition_cb 来当前连接情况变更回调函数,开发者可以通过回调函数获取当前连接情况。

C++

void OnRegLinkConditionCallback(__int64 room_id, const NIMChatRoomLinkCondition condition)
{

}

void foo()
{
    ChatRoom::RegLinkConditionCb(&OnRegLinkConditionCallback);
}

C#

void ChatRoomApi_LinkStateChanged(long roomId, NIMChatRoomLinkCondition state)
{

}

void foo()
{
    NIMChatRoom.ChatRoomApi.LinkStateChanged += ChatRoomApi_LinkStateChanged;
}

C

static void CallbackLinkCondition(__int64 room_id, int condition, const char *json_extension, const void *user_data)
{

}

typedef void(*nim_chatroom_reg_link_condition_cb)(const char *json_extension, nim_chatroom_link_condition_cb_func cb, const void *user_data);

void foo()
{
    nim_chatroom_reg_link_condition_cb func = (nim_chatroom_reg_link_condition_cb) GetProcAddress(hInst, "nim_chatroom_reg_link_condition_cb");
    func(nullptr, &CallbackLinkCondition, nullptr);
}

获取/更新聊天室信息

进入聊天室的回调函数中会有聊天室的信息,此外还能通过单独的接口获取聊天室信息。SDK 本地不缓存聊天室信息,只提供向服务器查询的接口,因此接口调用遵循服务器接口使用频率限制。

C++

void OnGetChatRoomInfoCallback(__int64 room_id, int error_code, const ChatRoomInfo& info)
{
    if (error_code != nim::kNIMResSuccess || room_id != room_id_)
    {
        return;
    }
}

void foo()
{
    ChatRoom::GetInfoAsync(room_id, &OnGetChatRoomInfoCallback);
}

C#

void foo()
{
    NIMChatRoom.ChatRoomApi.GetRoomInfo(roomId, (room_Id, errorCode, info) =>
    {
    });
}

C

static void CallbackGetChatRoomInfo(__int64 room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
{

}

typedef void(*nim_chatroom_get_info_async)(const __int64 room_id, const char *json_extension, nim_chatroom_get_info_cb_func cb, const void *user_data);

void foo()
{
    nim_chatroom_get_info_async func = (nim_chatroom_get_info_async) GetProcAddress(hInst, "nim_chatroom_get_info_async");
    func(room_id, nullptr, &CallbackGetChatRoomInfo, nullptr);
}

更新聊天室信息(需要管理员权限),目前只支持更新kNIMChatRoomInfoKeyName, kNIMChatRoomInfoKeyAnnouncement, kNIMChatRoomInfoKeyBroadcastUrl, kNIMChatRoomInfoKeyExt四个字段。

C++

void UpdateRoomCallback(int64_t room_id, int error_code)
{

}

void foo()
{
    ChatRoomInfo info;
    info.announcement_ = ; //聊天室公告
    info.name_ = ; //聊天室名称
    ChatRoom::UpdateRoomInfoAsync(room_id, info, true, "广播通知", &UpdateRoomCallback);
}

C#

NIMChatRoom.ChatRoomApi.UpdateRoomInfo(long roomId, ChatRoomInfo info, UpdateRoomInfoDelegate cb, bool notify, string notify_ext);

目前只支持更新ChatRoomInfo 的RoomName,Announcement,BroadcastUrl,Extension 四个属性。

C

void CallbackUpdateRoomInfo(int64_t room_id, int error_code, const char *json_extension, const void *user_data)
{

}

typedef void(*nim_chatroom_update_room_info_async)(const int64_t room_id, const char *room_info_json_str, bool need_notify, const char *notify_ext, const char *json_extension, nim_chatroom_update_room_info_cb_func cb, const void *user_data);

void foo()
{
    nim_chatroom_update_room_info_async func = (nim_chatroom_update_room_info_async) GetProcAddress(hInst, "nim_chatroom_update_room_info_async");

    Json::Value values;
    values[kNIMChatRoomInfoKeyName] = name_; //聊天室名称
    values[kNIMChatRoomInfoKeyAnnouncement] = announcement_; //聊天室公告
    values[kNIMChatRoomInfoKeyBroadcastUrl] = broadcast_url_; //视频直播拉流地址
    values[kNIMChatRoomInfoKeyExt] = ext_; //第三方扩展字段
    Json::FastWriter fw;

    func(room_id, fw.write(values).c_str(), true, "广播通知", nullptr, &CallbackUpdateRoomInfo, nullptr);
}

更新自己的信息

目前只支持更新kNIMChatRoomMemberInfoKeyNick, kNIMChatRoomMemberInfoKeyAvatar, kNIMChatRoomMemberInfoKeyExt三个字段, V3.8.0开始可以通过参数持久化固定成员的资料:

C++

void UpdateMyRoleCallback(int64_t room_id, int error_code)
{

}

void foo()
{
    ChatRoomMemberInfo info;
    info.nick_ = ; //聊天室内的昵称字段
    info.avatar_ = ; //聊天室内的头像
    info.ext_ = ;  //开发者扩展字段

    Json::Value values;
    values[nim_chatroom::kNIMChatRoomUpdateMyRoleExtNeedSave] = ; //bool 是否持久化
    Json::FastWriter fw;
    std::string extension = fw.write(values);

    ChatRoom::UpdateMyRoomRoleAsync(room_id, info, true, "广播通知", &UpdateMyRoleCallback, extension);
}

C#

ChatRoomApi.UpdateMyRoleInfo.UpdateMyRoleInfo(long roomId, MemberInfo info, UpdateMyRoleDelegate cb, bool notify, string notify_ext, string json_extension)

目前只支持更新 MemberInfo 的 Nick, MemberIcon,Extension 三个属性

C

void CallbackUpdateMyRoomRole(int64_t room_id, int error_code, const char *json_extension, const void *user_data)
{

}

typedef void(*nim_chatroom_update_my_role_async)(const int64_t room_id, const char *role_info_json_str, bool need_notify, const char *notify_ext, const char *json_extension, nim_chatroom_update_my_role_cb_func cb, const void *user_data);

void foo()
{
    nim_chatroom_update_my_role_async func = (nim_chatroom_update_my_role_async) GetProcAddress(hInst, "nim_chatroom_update_my_role_async");

    Json::Value values;
    values[kNIMChatRoomMemberInfoKeyNick] = id_; //聊天室内的昵称字段
    values[kNIMChatRoomMemberInfoKeyAvatar] = name_; //聊天室内的头像
    values[kNIMChatRoomMemberInfoKeyExt] = announcement_; //开发者扩展字段
    Json::FastWriter fw;


    Json::Value ext_values;
    ext_values[nim_chatroom::kNIMChatRoomUpdateMyRoleExtNeedSave] = ; //bool 是否持久化
    std::string extension = fw.write(values);

    func(room_id, fw.write(values).c_str(), true, "广播通知", extension.c_str(), &CallbackUpdateMyRoomRole, nullptr);
}

聊天室消息收发

聊天室SDK 支持文本、图片、音频、视频、地理位置、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息。

查询消息历史

SDK 本地不缓存任何消息历史,只提供向服务器查询消息历史的接口,因此接口调用遵循服务器接口使用频率限制。

C++

void GetMsgHistoryCallback(__int64 room_id, int error_code, const std::list<ChatRoomMessage>& msgs)
{
    if (error_code != nim::kNIMResSuccess)
        return;
}

void foo()
{
    ChatRoomGetMsgHistoryParameters history_param;
    history_param.limit_ = 10;
    history_param.start_timetag_ = 0;
    ChatRoom::GetMessageHistoryOnlineAsync(room_id, history_param, &GetMsgHistoryCallback);
}

C#

void foo()
{
    NIMChatRoom.ChatRoomApi.QueryMessageHistoryOnline(roomId, 0, 50, (room_Id, errorCode, messages) =>
    {
        if (errorCode == NIM.ResponseCode.kNIMResSuccess)

    });
}

C

typedef void(*nim_chatroom_get_msg_history_online_async)(const __int64 room_id, const char *parameters_json_str, const char *json_extension, nim_chatroom_get_msg_cb_func cb, const void *user_data);

static void CallbackGetMsgHistoryOnline(__int64 room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
{

}

void foo()
{
    nim_chatroom_get_msg_history_online_async func = (nim_chatroom_get_msg_history_online_async) GetProcAddress(hInst, "nim_chatroom_get_msg_history_online_async");

    Json::Value values;
    values[kNIMChatRoomGetMsgHistoryKeyStartTime] = start_timetag_;    //开始时间,单位毫秒
    values[kNIMChatRoomGetMsgHistoryKeyLimit] = limit_;                //本次返回的消息数量

    Json::FastWriter fw;

    func(room_id, fw.write(values).c_str(), nullptr, &CallbackGetMsgHistoryOnline, nullptr);
}

获取聊天室在线成员

SDK 本地不缓存聊天室成员信息,只提供向服务器查询的接口,因此接口调用遵循服务器接口使用频率限制。

SDK 提供了两种获取聊天室在线成员信息的接口:

踢出在线成员

C++

void KickMemberCallback(__int64 room_id, int error_code)
{
    if (error_code != nim::kNIMResSuccess)
        return;
}

void foo()
{
    ChatRoom::KickMemberAsync(room_id, user_account, "", &KickMemberCallback);
}

C#

void foo()
{
    NIMChatRoom.ChatRoomApi.RemoveMember(roomId, "userId", "踢出提示文本", (room_Id, errorCode) =>
    {
    });
}

C

typedef void(*nim_chatroom_kick_member_async)(const __int64 room_id, const char *id, const char *notify_ext, const char *json_extension, nim_chatroom_kick_member_cb_func cb, const void *user_data);

static void CallbackKickMember(__int64 room_id, int error_code, const char *json_extension, const void *user_data)
{

}

void foo()
{
    nim_chatroom_kick_member_async func = (nim_chatroom_kick_member_async) GetProcAddress(hInst, "nim_chatroom_kick_member_async");

    func(room_id, id, nullptr, nullptr, &CallbackKickMember, nullptr);
}

聊天室权限设置

设置管理员、设置普通成员、设置黑名单、禁言通过调用同一个接口nim_chatroom_set_member_attribute_async实现。

聊天室队列服务

C++ SDK开发手册

C# SDK开发手册