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内容

  • x64_dlls:存放64位Dll的文件夹
  • x86_dlls:存放32位Dll的文件夹
  • nim.dll: SDK核心功能模块;放在用户程序目录下
  • nim_audio.dll: 负责语音录制和播放;放在用户程序目录下
  • nim_tools_http.dll: 负责通用的http传输;nim.dll模块使用了此功能,C++封装层(需要依赖Demo工程)
  • nrtc.dll: 负责视频聊天功能;放在用户程序目录下
  • nim_audio_hook.dll: 负责辅助采集播放器音频,由nrtc.dll调用;放在用户程序目录下,x64位暂时不提供该Dll。
  • nim_conf: SDK版本相关;放在用户程序目录下
  • nim_c_sdk: IM C 接口说明以及SDK所有定义的头文件(*_def.h头文件用户自己决定放在合适位置)。
  • nim_chatroom_c_sdk: 聊天室 C 接口说明以及SDK所有定义的头文件(*_def.h头文件用户自己决定放在合适位置)。
  • nim_sdk_cpp_vs2010:IM C接口的C++封装层,包括工程文件,工程文件为VS2010创建,用户添加进自己的工程后可根据开发环境升级该工程文件。
  • nim_chatroom_cpp_vs2010:聊天室C接口的C++封装层,包括工程文件,工程文件为VS2010创建,用户添加进自己的工程后可根据开发环境升级该工程文件。
  • nim_tools_c_sdk:SDK工具类(语音录制播放和Http)C接口说明以及所有定义的头文件(*_def.h头文件用户自己决定放在合适位置)。
  • nim_tools_cpp_sdk:SDK工具类(语音录制播放和Http)C接口的C++封装层。

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 需要清除缓存功能,可扫描该目录下的文件,按照你们的规则清理即可。 具体某个用户对应的缓存目录下面包含如下子目录:

  • image:图片消息文件
  • audio:语音消息文件
  • res:其他资源文件

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);	
     ...
    
  2. 异步接口。

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

  3. 同步接口。

    为方便开发者使用,我们提供了一些同步接口,调用接口获取的内容同步返回,以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 接口实现。

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

  • 消息支持扩展字段,扩展字段分为服务器扩展字段kNIMMsgKeyServerExt和本地扩展字段kNIMMsgKeyLocalExt,最大长度1024字节。对于服务器扩展字段,该字段会发送到其他端,而本地扩展字段仅在本地有效。对于这两种扩展字段, SDK 都会存储在数据库中。示例如下:

      json[kNIMMsgKeyServerExt] = ""; // json string
    
  • 推送文案字段(kNIMMsgKeyPushContent,最大长度200字节)和自定义推送属性(kNIMMsgKeyPushPayload,最大长度2048字节),请注意最大长度的限制。设置了推送文案后,接收方收到消息时,在通知栏提醒中会显示该文案,如果不设置则采用 SDK 默认的文案。

      json[kNIMMsgKeyPushContent] = "";
      json[kNIMMsgKeyPushPayload] = ""; // json string
    
  • 是否需要推送字段(kNIMMsgKeyPushEnable),0:不需要,1:需要。

  • 推送是否要做消息计数字段(kNIMMsgKeyNeedBadge),0:不需要,1:需要。

  • 推送是否需要推送昵称(kNIMMsgKeyNeedPushNick),0:不需要,1:需要。

  • 消息是否存储云端(kNIMMsgKeyHistorySave),如果设置为0,则通过拉取服务器消息历史的接口无法获取该条消息。0:不需要,1:需要。

  • 消息是否支持漫游(kNIMMsgKeyMsgRoaming),如果需要,即使某一个客户端收取过这条消息,其他客户端再次登录也会漫游到这条消息。0:不需要,1:需要。

  • 消息是否支持多端同步(kNIMMsgKeyMsgSync),如果需要,则会在发送一条消息后,将这条消息同步到同时登录的其他客户端。0:不需要,1:需要。

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

  • 是否强推字段(kNIMMsgKeyIsForcePush),0:不需要,1:需要。
  • 强推列表字段(kNIMMsgKeyForcePushList),推送指定账号id string array json, 如果强推全员不填。
  • 强推文本字段(kNIMMsgKeyForcePushContent),群组消息强推文本。

反垃圾字段:

  • 是否需要过易盾反垃圾(kNIMMsgKeyAntiSpamEnable),0:不需要,1:需要。
  • 需要过易盾反垃圾的自定义内容(kNIMMsgKeyAntiSpamContent),长度限制5000字符,格式为json string,{"type" : 1:文本,2:图片,3视频, "data" : "文本内容or图片地址or视频地址"}

消息多端推送开关

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

  • 删除单个

    C++

      void DeleteRecentSessionCb(nim::NIMResCode code, const nim::SessionData &result, int total_unread_counts)
      {
      	···
      }
    
      void DeleteSessionItem(const nim::SessionData& msg)
      {
      	nim::Session::DeleteRecentSession(msg.type_, msg.id_, &DeleteRecentSessionCb);
      }
    

    C#

      void foo(SessionInfo info)
      {
      	NIM.Session.SessionAPI.DeleteRecentSession(info.SessionType, info.Id, (a, b, c) =>
      	{
      	    ···
      	});
      }
    

    C

      void CallbackNotifySession(int rescode, const char *result, int total_unread_counts, const char *json_extension, const void *user_data)
      {
      	// 解析result
      }
    
      typedef void(*nim_session_delete_recent_session_async)(NIMSessionType to_type, const char *id, const char *json_extension, nim_session_change_cb_func cb, const void *user_data);
    
      void foo(nim::NIMSessionType to_type, const char *id)
      {
      	nim_session_delete_recent_session_async func = (nim_session_delete_recent_session_async) GetProcAddress(hInst, "nim_session_delete_recent_session_async");
      	func(to_type, id, "", &CallbackNotifySession, nullptr);
      }
    
  • 删除全部

    C++

      void DeleteAllRecentSessionCb(nim::NIMResCode code, const nim::SessionData &result, int total_unread_counts)
      {
      	···
      }
    
      void DeleteAllSessionItem()
      {
      	nim::Session::DeleteAllRecentSession(&DeleteAllRecentSessionCb);
      }
    

    C#

      void foo()
      {
      	NIM.Session.SessionAPI.DeleteAllRecentSession((a, b, c) =>
      	{
      	   ···
      	});
      }
    

    C

      void CallbackNotifySession(int rescode, const char *result, int total_unread_counts, const char *json_extension, const void *user_data)
      {
      	// 解析result
      }
    
      typedef void(*nim_session_delete_all_recent_session_async)(const char *json_extension, nim_session_change_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_session_delete_all_recent_session_async func = (nim_session_delete_all_recent_session_async) GetProcAddress(hInst, "nim_session_delete_all_recent_session_async");
      	func("", &CallbackNotifySession, nullptr);
      }
    
  • 未读数清零 同时需要将该会话的本地消息历史中的未读标记置为已读。

    C++

      void ResetUnread(const std::string &id, nim::NIMSessionType type)
      {
      	nim::Session::SetUnreadCountZeroAsync(type, id, nim::Session::SetUnreadCountZeroCallback());
      	nim::MsgLog::BatchStatusReadAsync(id, type, nim::MsgLog::BatchStatusReadCallback());
      }
    

    C#

      void ResetUnread(SessionInfo info)
      {
          NIM.Session.SessionAPI.SetUnreadCountZero(info.SessionType, info.Id, (a, b, c) =>
          {
              ···
          });
    
      	NIM.Messagelog.MessagelogAPI.MarkMessagesStatusRead(info.Id, info.SessionType, (a, b, c) =>
      	{
      	    ···
      	});
      }
    

    C

      void CallbackNotifySession(int rescode, const char *result, int total_unread_counts, const char *json_extension, const void *user_data)
      {
      	// 解析result
      }
    
      void CallbackModifyMultipleMsglog(int res_code
      	, const char *uid
      	, nim::NIMSessionType to_type
      	, const char *json_extension
      	, const void *callback)
      {
      	···
      }
    
      typedef void(*nim_session_set_unread_count_zero_async)(NIMSessionType to_type, const char *id, const char *json_extension, nim_session_change_cb_func cb, const void *user_data);
      typedef void(*nim_msglog_batch_status_read_async)(const char* account_id, nim::NIMSessionType to_type, const char *json_extension, nim_msglog_res_ex_cb_func cb, const void* user_data);
    
      void foo(nim::NIMSessionType type, const char *id)
      {
      	nim_msglog_batch_status_read_async batch_func = (nim_msglog_batch_status_read_async) GetProcAddress(hInst, "nim_msglog_batch_status_read_async");
      	nim_session_set_unread_count_zero_async unread_func = (nim_session_set_unread_count_zero_async) GetProcAddress(hInst, "nim_session_set_unread_count_zero_async");
    
      	unread_func(type, id, "", &CallbackNotifySession, nullptr);
      	batch_func(id, type, "", &CallbackModifyMultipleMsglog, nullptr);
      }
    

自定义消息

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

消息已读回执

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

  • 发送消息已读回执

    C++

      void foo(nim::IMMessage msg)
      {
      	nim::MsgLog::SendReceiptAsync(msg.ToJsonString(false), [](const nim::MessageStatusChangedResult& res) {
      		auto iter = res.results_.begin();	
      	});
      }
    

    C#

      C#提供了自定义消息的基类 NIMCustomMessage<T> 便于进行扩展,T 为用户自定义的消息体。如下所示代码 CustomMessageContent 是自定义的图文消息,将自定义属性用 Newtonsoft.Json.JsonProperty 属性进行标记,这样方便序列化操作,Extention 用于发送附加内容或者用来区别消息类型。自定义消息的收发与普通消息保持一致,使用相同的函数。
    
      class CustomMessageContent
      {
          [Newtonsoft.Json.JsonProperty("text")]
          public string Text { get; set; }
    
          [Newtonsoft.Json.JsonProperty("imagepath")]
          public string ImagePath { get; set; }
      }
    
      class ImageTextMessage : NIMCustomMessage<CustomMessageContent>
      {
          public override CustomMessageContent CustomContent { get; set; }
    
          public ImageTextMessage()
          {
              MessageType = NIMMessageType.kNIMMessageTypeCustom;
              CustomContent = new CustomMessageContent();
              Extention = "1";
          }
      }
    

    C

      void CallbackMsgStatusChanged(int rescode, const char *result, const char *json_extent, const void *callback)
      {
      	// 解析result
      }
    
      typedef void(*nim_msglog_send_receipt_async)(const char *json_msg, const char *json_extension, nim_msglog_status_changed_cb_func cb, const void *user_data);
    
      void foo(const char *json_msg)
      {
      	nim_msglog_send_receipt_async func = (nim_msglog_send_receipt_async) GetProcAddress(hInst, "nim_msglog_send_receipt_async");
    
      	func(json_msg, "", &CallbackNotifySession, nullptr);
      }
    
  • 查询发送的消息是否被对方已读

    C++

      bool IsMsgReaded(nim::IMMessage &msg)
      {
      	if (nim::MsgLog::QueryMessageBeReaded(msg))
      	{
      		return true;
      	}
    
      	return false;
      }
    

    C#

      bool IsMsgReaded(NIM.NIMTextMessage msg)
      {
      	return NIM.Messagelog.MessagelogAPI.IsMessageBeReaded(msg);
      }
    

    C

      typedef bool(*nim_msglog_query_be_readed)(const char *json_msg, const char *json_extension);
    
      bool foo(const char *json_msg)
      {
      	nim_msglog_query_be_readed func = (nim_msglog_query_be_readed) GetProcAddress(hInst, "nim_msglog_query_be_readed");
    
      	return func(json_msg, "");
      }
    
  • 注册全局的消息状态变更通知的回调 对方收到己方发送的消息后,并发送消息已读回执后,sdk会通过这个回调通知上层。

    C++

      void OnMsgStatusChangedCallback(const nim::MessageStatusChangedResult& res)
      {
      	for (auto res : res.results_)
      	{
      		···
      	}
      }
    
      void OnInit()
      {
      	nim::MsgLog::RegMessageStatusChangedCb(&OnMsgStatusChangedCallback);
      }
    

    C#

      void OnInit()
      {
      	NIM.Messagelog.MessagelogAPI.RegMsglogStatusChangedCb((res, result) =>
      	{
      		···
      	});
      }
    

    C

      void CallbackMsgStatusChanged(int rescode, const char *result, const char *json_extent, const void *callback)
      {
      	// 解析result
      }
    
      typedef void(*nim_msglog_reg_status_changed_cb)(const char *json_extension, nim_msglog_status_changed_cb_func cb, const void *user_data);
    
      void foo(const char *json_msg)
      {
      	nim_msglog_reg_status_changed_cb func = (nim_msglog_reg_status_changed_cb) GetProcAddress(hInst, "nim_msglog_reg_status_changed_cb");
    
      	func( "", &CallbackMsgStatusChanged, nullptr);
      }
    

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

多媒体消息

语音消息录制和播放

  • 初始化与清理

    在程序启动时,调用LoadLibrary 加载nim_audio.dll;然后调用接口nim_audio_init_module 初始化语音模块;在退出程序前,先调用接口nim_audio_uninit_module 释放语音模块,然后调用FreeLibrary 释放dll。

  • 获取语音采集设备

    通过获取语音采集设备,调用语音采集接口时可以指定采集设备,nim_audio.h:

      NIM_SDK_DLL_API bool nim_audio_enum_capture_device();
    

    结果通过注册的回调接口获取:

      NIM_SDK_DLL_API bool nim_audio_reg_enum_capture_device_cb(nim_enum_capture_device_cb cb);
    

    C++

      void OnEnumCaptureDeviceCallback(int rescode, const wchar_t* device_list)
      {
      	···
      }
    
      void foo()
      {
      	nim_audio::Audio::RegEnumCaptureDeviceCb(&OnEnumCaptureDeviceCallback);
      	nim_audio::Audio::EnumCaptureDevice();
      }
    

    C#

      private void OnEnumDevices(int resCode, string deviecs)
      {
          _outputTools.ShowInfo("获取设备:\r\nresCode:{0}\r\ndevices:{1}", resCode, deviecs);
      }
    
      public void foo()
      {
          //注册获取设备列表结果回调
          NIMAudio.AudioAPI.RegEnumDevicesCb(OnEnumDevices);
          NIMAudio.AudioAPI.EnumCaptureDevices();
      }
    

    C

      void OnEnumCaptureDeviceCallback(int rescode, const wchar_t* device_list)
      {
      	···
      }
    
      typedef bool(*nim_audio_enum_capture_device)();
      typedef bool(*nim_audio_reg_enum_capture_device_cb)(nim_enum_capture_device_cb cb);
    
      void foo()
      {
      	nim_audio_enum_capture_device enum_func = (nim_audio_enum_capture_device) GetProcAddress(hInst, "nim_audio_enum_capture_device");
      	nim_audio_reg_enum_capture_device_cb reg_func = (nim_audio_reg_enum_capture_device_cb) GetProcAddress(hInst, "nim_audio_reg_enum_capture_device_cb");
    
      	reg_func(&OnEnumCaptureDeviceCallback);
      	enum_func();
      }
    
  • 语音采集

    语音模块初始化后调用如下接口注册语音采集相关的回调函数:

      typedef bool(*nim_audio_reg_start_capture_cb)(nim_rescode_cb cb);
      typedef bool(*nim_audio_reg_stop_capture_cb)(nim_stop_capture_cb cb);
      typedef bool(*nim_audio_reg_cancel_audio_cb)(nim_rescode_cb cb);
    

    语音采集调用如下接口,采集前确保有录音设备。

      typedef bool(*nim_audio_start_capture)(const char* call_id, const char* res_id, int audio_format, int volume, const wchar_t* capture_device);
      typedef bool(*nim_audio_stop_capture)();
      typedef bool(*nim_audio_cancel_audio)(const wchar_t* file_path);
    

    C++

      void OnStartCaptureCallback(int code)
      {
      	···
      }
    
      void OnStopCaptureCallback(int rescode, const char* sid, const char* cid, const char* file_path, const char* file_ext, long file_size, int audio_duration)
      {
      	···
      }
    
      void OnCancelCaptureCallback(int code)
      {
      	···
      }
    
      void OnInit()
      {	
      	nim_audio::Audio::RegStartCaptureCb(&OnStartCaptureCallback);
      	nim_audio::Audio::RegStopCaptureCb(&OnStopCaptureCallback);
      	nim_audio::Audio::RegCancelAudioCb(&OnCancelCaptureCallback);
      }
    
      bool StartCapture(const std::string session_id, const std::string msg_id, nim_audio::nim_audio_type audio_format /*= nim_audio::AAC*/, int volume /*= 180*/, const wchar_t* capture_device /*= nullptr*/)
      {
      	return nim_audio::Audio::StartCapture(session_id.c_str(), msg_id.c_str(), audio_format, volume, capture_device);
      }
    
      bool StopCapture()
      {
      	return nim_audio::Audio::StopCapture();
      }
    
      bool CancelCapture(const std::wstring file_path)
      {
      	return nim_audio::Audio::CancelAudio(file_path.c_str());
      }
    

    C#

      private void OnCaptureStarted(int resCode)
      {
          _outputTools.ShowInfo("开始录制:{0}", resCode); ;
      }
      //注册开始语音采集的结果回调
      NIMAudio.AudioAPI.RegStartAudioCaptureCb(OnCaptureStarted);
      //开始语音采集
      NIMAudio.AudioAPI.StartCapture("", "", _audioType);
    
      private void OnCaptureStopped(int resCode, string call_id, string res_id, wstring file_path, string file_ext, int file_size, int audio_duration)
      {
          _outputTools.ShowInfo("录制结束:{0}\r\n file_path:{1}\r\n ext:{2}\r\n size:{3}\r\n duration:{4}",
              resCode, file_path, file_ext, file_size, audio_duration);
      }
      //注册停止采集的结果回调
      NIMAudio.AudioAPI.RegStopAudioCaptureCb(OnCaptureStopped);
      //停止采集
      NIMAudio.AudioAPI.StopCapture();
    
      //注册取消采集的结果回调
      NIMAudio.AudioAPI.RegCancelAudioCapturingCb(NIMAudio.NIMResCodeCb cb)
      //取消录制并删除临时文件
      NIMAudio.AudioAPI.CancelCapture(strings audioPath)
    

    C

      void OnStartCaptureCallback(int code)
      {
      	···
      }
    
      void OnStopCaptureCallback(int rescode, const char* sid, const char* cid, const char* file_path, const char* file_ext, long file_size, int audio_duration)
      {
      	···
      }
    
      void OnCancelCaptureCallback(int code)
      {
      	···
      }
    
      typedef bool(*nim_audio_reg_start_capture_cb)(nim_rescode_cb cb);
      typedef bool(*nim_audio_reg_stop_capture_cb)(nim_stop_capture_cb cb);
      typedef bool(*nim_audio_reg_cancel_audio_cb)(nim_rescode_cb cb);
    
      typedef bool(*nim_audio_start_capture)(const char* call_id, const char* res_id, int audio_format, int volume, const wchar_t* capture_device);
      typedef bool(*nim_audio_stop_capture)();
      typedef bool(*nim_audio_cancel_audio)(const char* file_path);
    
      void OnInit()
      {
      	nim_audio_reg_start_capture_cb start_func = (nim_audio_reg_start_capture_cb) GetProcAddress(hInst, "nim_audio_reg_start_capture_cb");
      	nim_audio_reg_stop_capture_cb stop_func = (nim_audio_reg_stop_capture_cb) GetProcAddress(hInst, "nim_audio_reg_stop_capture_cb");
      	nim_audio_reg_cancel_audio_cb cancel_func = (nim_audio_reg_cancel_audio_cb) GetProcAddress(hInst, "nim_audio_reg_cancel_audio_cb");
    
      	nim_audio_reg_stop_capture_cb stop_func = (nim_audio_reg_stop_capture_cb) GetProcAddress(hInst, "nim_audio_reg_stop_capture_cb");
      	nim_audio_reg_cancel_audio_cb cancel_func = (nim_audio_reg_cancel_audio_cb) GetProcAddress(hInst, "nim_audio_reg_cancel_audio_cb");
    
      	start_funcb(&OnStartCaptureCallback);
      	stop_func(&OnStopCaptureCallback);
      	cancel_func(&OnCancelCaptureCallback);
      }
    
      bool StartCapture(const char *session_id, const char *msg_id, nim_audio::nim_audio_type audio_format, int volume, const wchar_t* capture_device)
      {
      	nim_audio_start_capture func = (nim_audio_start_capture) GetProcAddress(hInst, "nim_audio_start_capture");
      	return func(session_id, msg_id, audio_format, volume, capture_device);
      }
    
      bool StopCapture()
      {
      	nim_audio_stop_capture func = (nim_audio_stop_capture) GetProcAddress(hInst, "nim_audio_stop_capture");
      	return func(session_id);
      }
    
      bool CancelCapture(const char *file_path)
      {
      	nim_audio_reg_cancel_audio_cb func = (nim_audio_reg_cancel_audio_cb) GetProcAddress(hInst, "nim_audio_reg_cancel_audio_cb");
      	return func(file_path);
      }
    
  • 语音播放与停止

    调用接口nim_audio_reg_start_play_cb 注册语音播放开始回调函数,调用接口nim_audio_reg_stop_play_cb 注册语音播放结束回调函数。调用接口nim_audio_play_audio 播放语音。调用接口nim_audio_stop_play_audio 停止播放。

语音转文字

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提供两种获取缩略图方案:

  • 基于修改图片质量

    当用户收到图片消息时,附件kNIMMsgKeyAttach(聊天室消息为kNIMChatRoomMsgKeyAttach)内容解析出来会带有图片下载地址,通过拼接下载接口参数获取指定质量的图片: http(s)://?imageView&quality=N;http(s)://xxx为图片下载地址;N为图片质量,范围为0-100。

  • 基于长宽对图片进行 内缩略 (原图等比例缩略,缩略后的图片“一边等于请求长度,另一边小于等于请求长度”)

    当用户收到图片消息时,附件kNIMMsgKeyAttach(聊天室消息为kNIMChatRoomMsgKeyAttach)内容解析出来会带有图片下载地址,通过拼接下载接口参数获取缩略图: http(s)://xxx?imageView&thumbnail=XxY;http(s)://xxx为图片下载地址;X为宽度,Y为高度,中间为小写x,宽度和高度取值范围0-4096。

    几种参数变化: 1、XxY:普通缩略(内缩略) 2、Xx0:限定宽度,高度自适应(内缩略) 3、0xY:限定高度,宽度自适应(内缩略)

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

群组功能

群组功能概述

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

  • 普通群

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

  • 高级群

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

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

群聊消息

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

获取群组

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

  • 本地获取群组id列表

      void nim_team_query_all_my_teams_async(const char *json_extension, nim_team_query_all_my_teams_cb_func cb, const void *user_data);
    
  • 本地获取群组列表(群信息)

      void nim_team_query_all_my_teams_info_async(const char *json_extension, nim_team_query_all_my_teams_info_cb_func cb, const void *user_data);
    

    V2.7.0版本后可以通过该接口获取本地缓存的无效群组(不在群或群已经解散)信息:

      Json::Value values;
      Json::FastWriter fw;
      values[kNIMTeamSearchKeyIncludeInvalid] = true;
      exten = values.toStyledString();
      nim_team_query_all_my_teams_info_async(exten.c_str(), &QueryAllMyTeamsInfoCallback, nullptr);
    

    在回调函数中,开发者通过解析群组信息json后,通过kNIMTeamInfoKeyValidFlag字段区分该群为存在的群还是已经解散的群,通过kNIMTeamInfoKeyMemberValid字段区分自己是否还在该群。

  • 从服务器上获取群组信息

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

创建群组

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

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

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

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 OnTeamEventCallback(const nim::TeamEvent& result)
      {
      	···
      }
    
      void foo()
      {
      	const std::list<std::string> friend_list;
      	friend_list.push_back("test1");
      	friend_list.push_back("test2");
      	nim::Team::InviteAsync("123445", friend_list, "", &OnTeamEventCallback);
      }
    

    C#

      void foo()
      {
      	string[] friend_list = { "test1", "test2" };
      	NIM.Team.TeamAPI.Invite("12345", friend_list, "", (r) =>
      	{
      	    if (r.TeamEvent.ResponseCode == NIM.ResponseCode.kNIMResTeamInviteSuccess)
      	    {
      	        foreach (var id in r.TeamEvent.IdCollection)
      	        {
      	            ···
      	        }
      	    }
      	    else
      	    {
      	        MessageBox.Show("操作失败:" + r.TeamEvent.ResponseCode.ToString());
      	    }
      	});
      }
    

    C

      void CallbackTeamChange(int res_code, int notification_id, const char *tid, const char *result, const char *json_extension, const void *user_data)
      {
      	//
      }
    
      typedef void(*nim_team_invite_async)(const char *tid, 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 friend_list;
      	friend_list.append("litianyi01");	
      	friend_list.append("litianyi02");
    
      	nim_team_invite_async func = (nim_team_invite_async) GetProcAddress(hInst, "nim_team_invite_async");
      	func("12345", friend_list.toStyledString().c_str(), "welcome to new team", "", &CallbackCreateTeam, nullptr);
      }	
    
  • 同意群邀请(仅限高级群):

    C++

      void TeamEventCb(const nim::TeamEvent& team_event)
      {
      	···
      }
    
      void foo()
      {
      	nim::Team::AcceptInvitationAsync("12345", "my_id", &TeamEventCb);
      }
    

    C#

      NIM.Team.TeamAPI.AcceptTeamInvitation("12345", "my_id", (x) =>
      {
    
      });
    

    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_accept_invitation_async)(const char *tid, const char *invitor, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	nim_team_accept_invitation_async func = (nim_team_accept_invitation_async) GetProcAddress(hInst, "nim_team_accept_invitation_async");
      	func("12345", "my_id", "", &TeamEventCb, nullptr);
      }
    
  • 拒绝群邀请(仅限高级群):

    C++

      void TeamEventCb(const nim::TeamEvent& team_event)
      {
      	···
      }
    
      void foo()
      {
      	nim::Team::RejectInvitationAsync("12345", "my_id", "", &TeamEventCb);
      }
    

    C#

      NIM.Team.TeamAPI.RejectTeamInvitation(string tid, string invitor, string reason, TeamChangedNotificationDelegate action);
    

    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_reject_invitation_async)(const char *tid, const char *invitor, const char *reason, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	nim_team_reject_invitation_async func = (nim_team_reject_invitation_async) GetProcAddress(hInst, "nim_team_reject_invitation_async");
      	func("12345", "my_id", "", "", &TeamEventCb, nullptr);
      }
    
  • 主动申请入群(仅限高级群):

    请求完成后,网易云通信服务器会下发一条系统消息给群管理员,管理员可以选择通过或者拒绝申请。

    C++

      void OnApplyJoinCb(const nim::TeamEvent& team_event)
      {
      	QLOG_APP(L"apply join: {0}") << team_event.res_code_;
    
      	switch (team_event.res_code_)
      	{
      	case nim::kNIMResTeamAlreadyIn:
      	{
      		···
      	}
      	break;
      	case nim::kNIMResSuccess:
      	{
      		···
      	}
      	break;
      	case nim::kNIMResTeamApplySuccess:
      		···
      		break;
      	default:
      	{
      		···
      	}
      	break;
      	}
      }
    
      void foo()
      {
      	nim::Team::ApplyJoinAsync("12345", "", &OnApplyJoinCb);
      }
    

    C#

      NIM.Team.TeamAPI.ApplyForJoiningTeam("12345", "", (x) =>
      {
    
      });
    

    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_apply_join_async)(const char *tid, const char *reason, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	nim_team_apply_join_async func = (nim_team_apply_join_async) GetProcAddress(hInst, "nim_team_apply_join_async");
      	func("12345", "", "", &TeamEventCb, nullptr);
      }
    
  • 通过申请(仅限高级群):

    C++

      void TeamEventCb(const nim::TeamEvent& team_event)
      {
      	switch (team_event.res_code_)
      	{
      	case nim::kNIMResTeamAlreadyIn:
      	{
      		···
      	}
      	break;
      	case nim::kNIMResSuccess:
      	{
      		···
      	}
      	break;
      	case nim::kNIMResTeamApplySuccess:
      		···
      		break;
      	default:
      	{
      		···
      	}
      	break;
      	}
      }
    
      void foo()
      {
      	nim::Team::PassJoinApplyAsync("12345", "my_id", &TeamEventCb);
      }
    

    C#

      NIM.Team.TeamAPI.AgreeJoinTeamApplication("12345", "my_id", (x) =>
      {
    
      });
    

    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_pass_join_apply_async)(const char *tid, const char *applicant_id, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	nim_team_pass_join_apply_async func = (nim_team_pass_join_apply_async) GetProcAddress(hInst, "nim_team_pass_join_apply_async");
      	func("12345", "my_id", "", &TeamEventCb, nullptr);
      }
    
  • 拒绝申请(仅限高级群):

    C++

      void TeamEventCb(const nim::TeamEvent& team_event)
      {
      	switch (team_event.res_code_)
      	{
      	case nim::kNIMResTeamAlreadyIn:
      	{
      		···
      	}
      	break;
      	case nim::kNIMResSuccess:
      	{
      		···
      	}
      	break;
      	case nim::kNIMResTeamApplySuccess:
      		···
      		break;
      	default:
      	{
      		···
      	}
      	break;
      	}
      }
    
      void foo()
      {
      	nim::Team::RejectJoinApplyAsync("12345", "sender_id", "", &TeamEventCb);
      }
    

    C#

      NIM.Team.TeamAPI.RejectJoinTeamApplication("12345", "sender_id", "", (x) =>
      {
    
      });
    

    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_reject_join_apply_async)(const char *tid, const char *applicant_id, const char *reason, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	nim_team_reject_join_apply_async func = (nim_team_reject_join_apply_async) GetProcAddress(hInst, "nim_team_reject_join_apply_async");
      	func("12345", "sender_id", "", &TeamEventCb, 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()
      {
      	std::list<std::string> uids_list;
      	uids_list.push_back("user_id");
      	nim::Team::AddManagersAsync("tid_", uids_list, &OnTeamEventCallback);
      }
    

    C#

      NIM.Team.TeamAPI.AddTeamManagers("_teamId", new string[] {"uid"}, (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_add_managers_async)(const char *tid, const char *jsonlist_admin_ids, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	Json::Value values;
      	values.append("user_id");
    
      	nim_team_add_managers_async func = (nim_team_add_managers_async) GetProcAddress(hInst, "nim_team_add_managers_async");
      	func("tid", values.toStyledString().c_str(), nullptr, &CallbackTeamOperate, nullptr);
      }
    
  • 移除管理员(仅限高级群):

    C++

      void OnTeamEventCallback(const nim::TeamEvent& result)
      {
      	···
      }
    
      foo()
      {
      	std::list<std::string> uids_list;
      	uids_list.push_back("user_id");
      	nim::Team::RemoveManagersAsync("tid_", uids_list, &OnTeamEventCallback);
      }
    

    C#

      NIM.Team.TeamAPI.RemoveTeamManagers("_teamId", new string[] {"uid"}, (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_remove_managers_async)(const char *tid, const char *jsonlist_admin_ids, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	Json::Value values;
      	values.append("user_id");
    
      	nim_team_remove_managers_async func = (nim_team_remove_managers_async) GetProcAddress(hInst, "nim_team_remove_managers_async");
      	func("tid", values.toStyledString().c_str(), nullptr, &CallbackTeamOperate, nullptr);
      }
    
  • 转让群(仅限高级群):

    C++

      void OnTeamEventCallback(const nim::TeamEvent& result)
      {
      	···
      }
    
      foo()
      {
      	nim::Team::TransferTeamAsync("tid_", "user_id", false, &OnTeamEventCallback);
      }
    

    C#

      NIM.Team.TeamAPI.TransferTeamAdmin("_teamId", "user_id", false, (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_transfer_team_async)(const char *tid, const char *new_owner, bool is_leave, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);
    
      void foo()
      {
      	nim_team_transfer_team_async func = (nim_team_transfer_team_async) GetProcAddress(hInst, "nim_team_transfer_team_async");
      	func("tid", "user_id", false, nullptr, &CallbackTeamOperate, nullptr);
      }
    

群组成员

  • 获取群成员列表,获取到的群成员只有网易云通信服务器托管的群相关数据,需要开发者结合自己管理的用户数据进行界面显示。

      void nim_team_query_team_members_async(const char *tid, bool include_user_info, const char *json_extension, nim_team_query_team_members_cb_func cb, const void *user_data);
    

    例:

    C++

      void OnGetTeamMembers(const std::string& team_id, int count, const std::list<nim::TeamMemberProperty>& team_member_list)
      {
      	for (const auto& member : team_member_list)
      	{
      		···
      	}
      }
    
      foo()
      {
      	nim::Team::QueryTeamMembersAsync("tid_", &OnGetTeamMembers);
      }
    

    C#

      NIM.Team.TeamAPI.QueryTeamMembersInfo("_teamId", (info) =>
      {
          if (info != null)
          {
              foreach (var i in info)
              {
                  ···
              }
          }
      });
    

    C

      void CallbackQueryTeamMembersCb(const char * tid, int count, bool include_user_info, const char* str, const char *json_exten, const void *user_data)
      {
      	//解析str
      }
    
      typedef void(*nim_team_query_team_members_async)(const char *tid, bool include_user_info, const char *json_extension, nim_team_query_team_members_cb_func cb, const void* user_data);
    
      void foo()
      {
      	nim_team_query_team_members_async func = (nim_team_query_team_members_async) GetProcAddress(hInst, "nim_team_query_team_members_async");
      	func("tid", include_user_info ? true : false, nullptr, &CallbackQueryTeamMembersCb, nullptr);
      }
    

    V2.8.0版本后开发者可以通过调用该接口获取本地缓存中无效的群成员(已经不在群内)信息:

      Json::Value values;
      Json::FastWriter fw;
      values[kNIMTeamSearchKeyIncludeInvalid] = true;
      exten = values.toStyledString();
      //include_user_info必须为true
      func("tid", true, exten.c_str(), &CallbackQueryTeamMembersCb, nullptr);
    

    在回调函数中,开发者通过解析群成员json,通过kNIMTeamUserKeyValidFlag字段判断该成员目前是否还在群里。

  • 用户退群

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

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

    C#

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

    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_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("tid", nullptr, &CallbackTeamOperate, nullptr);
      }
    
  • 踢出用户

    C++

      void OnTeamEventCallback(const nim::TeamEvent& result)
      {
      	···
      }
    
      foo()
      {
      	std::list<std::string> uids_list;
      	uids_list.push_back("user_id");
      	nim::Team::KickAsync("tid_", uids_list, &OnTeamEventCallback);
      }
    

    C#

      NIM.Team.TeamAPI.KickMemberOutFromTeam("_teamId", new string[] {"uid"}, (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_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()
      {
      	Json::Value values;
      	values.append("user_id");
    
      	nim_team_kick_async func = (nim_team_kick_async) GetProcAddress(hInst, "nim_team_kick_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 在收到群通知之后,会对本地缓存的群信息做出对应的修改,然后触发与修改相对应的委托事件回调。
  • 群通知是接收型的消息,开发者不应该自己手动去创建和发送群通知消息。

自定义拓展

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

  • 应用方可以自行拓展这个字段做个性化配置,客户端不可以修改这个字段 kNIMTeamInfoKeyServerCustom (nim_team_def.h)

  • 应用方可以自行拓展这个字段做个性化配置,客户端可以修改这个字段 kNIMTeamInfoKeyCustom (nim_team_def.h)

群成员禁言

  • 禁言

    例子:

    C++

      #include "nim_cpp_team.h"
    
      void CallbackMuteMember(const TeamEvent& team_event)
      {
      	//自定义实现
      	char log[128];
      	sprintf_s(log, "id: %s, rescode: %d, tid: %s", GetTeamEventCommandText((nim::NIMNotificationId)team_event.notification_id_).c_str(), team_event.res_code_, team_event.team_id_.c_str());
      	MessageBoxA(nullptr, log, "team_event", 0);
      }
    
      void foo(const std::string& team_id, const std::string& account_id, bool mute)
      {
      	Team::MuteMemberAsync(team_id, account_id, bool, &CallbackMuteMember);
      }
    

    C#

      NIM.Team.TeamAPI.SetMemberMuted("_teamId", "user_id", true, (ret) =>
      {
          if (ret.TeamEvent.ResponseCode == NIM.ResponseCode.kNIMResSuccess)
          {
              ···
          }
      });
    

    C

      #include "nim_team.h"
    
      nim_team_mute_member_async(const char *tid, const char *member_id, bool set_mute, const char *json_extension, nim_team_opt_cb_func cb, const void *user_data);
    
      解析nim_team_opt_cb_func回调的结果示例:
    
      //nim_cpp_team.cpp
      static void CallbackTeamChange(int res_code, int notification_id, const char *tid, const char *result, const char *json_extension, const void *user_data)
      {
      	if (user_data)
      	{
      		Team::TeamEventCallback* cb_pointer = (Team::TeamEventCallback*)user_data;
      		if (*cb_pointer)
      		{
      			TeamEvent team_event;
      			ParseTeamEvent(res_code, PCharToString(tid), (nim::NIMNotificationId)notification_id, PCharToString(result), team_event);
      			(*cb_pointer)(team_event);
      		}
      		delete cb_pointer;
      	}
      }
    
  • 获取群禁言成员列表

    例子:

    C++

      #include "nim_cpp_team.h"
    
      void CallbackQueryMembersInfoOnline(NIMResCode error_code, const std::string& tid, const std::list<TeamMemberProperty>& team_member_propertys)
      {
      	//自定义实现
      	std::string ids;
      	for (auto iter = team_member_propertys.begin(); iter != team_member_propertys.end(); ++iter)
      	{
      		ids.append(iter->GetAccountID());
      		ids.append(",");
      	}
      	char log[1024];
      	sprintf_s(log, 1024, "tid: %s, member_count: %d\r\nids: %s", tid.c_str(), team_member_propertys.size(),ids.c_str());
      	MessageBoxA(nullptr, log, "CallbackQueryMembersInfoOnline", 0);
      }
    
      void foo(const std::string& team_id)
      {
      	Team::QueryMuteListOnlineAsync(team_id, &CallbackQueryMembersInfoOnline);
      }
    

    C#

      using NIM.Team;
    
      NIM.Team.TeamAPI.QueryMutedListOnlineAsync(tid, (res, count, id, members) => 
      {
      	//自定义实现
          DemoTrace.WriteLine("禁言列表:",res, count, id, members.Dump());
      });
    

    C

      #include "nim_team.h"
    
      nim_team_query_mute_list_online_async(const char *tid, const char *json_extension, nim_team_query_mute_list_cb_func cb, const void *user_data)
    
      解析nim_team_query_mute_list_cb_func回调的结果示例:
    
      //nim_team_helper.cpp
      void ParseTeamMemberPropertyJson(const Json::Value& team_member_prop_json, TeamMemberProperty& team_member_property)
      {
      	team_member_property.SetUserType((nim::NIMTeamUserType)team_member_prop_json[nim::kNIMTeamUserKeyType].asInt());
      	if (team_member_property.GetUserType() != nim::kNIMTeamUserTypeApply && team_member_property.GetUserType() != nim::kNIMTeamUserTypeLocalWaitAccept)
      	{
      		team_member_property.SetAccountID(team_member_prop_json[nim::kNIMTeamUserKeyAccID].asString());
      		team_member_property.SetNick(team_member_prop_json[nim::kNIMTeamUserKeyNick].asString());
      		team_member_property.SetBits(team_member_prop_json[nim::kNIMTeamUserKeyBits].asUInt64());
      		team_member_property.SetCreateTimetag(team_member_prop_json[nim::kNIMTeamUserKeyCreateTime].asUInt64());
      		team_member_property.SetUpdateTimetag(team_member_prop_json[nim::kNIMTeamUserKeyUpdateTime].asUInt64());
      		team_member_property.SetTeamID(team_member_prop_json[nim::kNIMTeamUserKeyID].asString());
      		team_member_property.SetValid(team_member_prop_json[nim::kNIMTeamUserKeyValidFlag].asUInt() == 0 ? false : true);
      		team_member_property.SetCustom(team_member_prop_json[nim::kNIMTeamUserKeyCustom].asString());
      		team_member_property.SetMute(team_member_prop_json[nim::kNIMTeamUserKeyMute].asInt() == 1);
      	}
      }
    
      //nim_cpp_team.cpp
      static void CallbackQueryMembersOnline(int res_code, int count, const char *tid, const char *result, const char *json_extension, const void *user_data)
      {
      	if (user_data)
      	{
      		Team::QueryTeamMembersOnlineCallback* cb_pointer = (Team::QueryTeamMembersOnlineCallback*)user_data;
      		if (*cb_pointer)
      		{
      			Json::Value values;
      			Json::Reader reader;
      			std::list<TeamMemberProperty> members;
      			if (reader.parse(PCharToString(result), values) && values.isArray())
      			{
      				auto size = values.size();
      				for (size_t i = 0; i < size; i++)
      				{
      					TeamMemberProperty prop;
      					ParseTeamMemberPropertyJson(values[i], prop);
      					members.push_back(prop);
      				}
      			}
      			(*cb_pointer)((NIMResCode)res_code, PCharToString(tid), members);
      		}
      		delete cb_pointer;
      	}
      }
    

历史记录

本地记录

  • 获取聊天对象的本地消息记录

    通过传入指定的消息历史记录的参数(会话id,会话类型,指定消息的时间戳)作为锚点,向前或向后查询本地消息记录。

      void nim_msglog_query_msg_async(const char *account_id //会话id,对方的account id或者群组tid
      						, NIMSessionType to_type //会话类型
      						, int limit_count //一次查询数量,建议20
      						, __int64 anchor_msg_time //指定消息的时间戳
      						, const char *json_extension
      						, nim_msglog_query_cb_func cb
      						, const void *user_data);
    

    例:

    C++

      void OnQueryMsgCallback(nim::NIMResCode code, const std::string& query_id, nim::NIMSessionType query_type, const nim::QueryMsglogResult& result)
      {
      	std::vector<nim::IMMessage> vec;
      	for each (auto msg in result.msglogs_)
      	{
      		vec.push_back(msg);
      	}
      }
    
      void foo()
      {
      	nim::MsgLog::QueryMsgAsync("session_id", nim::kNIMSessionTypeTeam, 20, 0, &OnQueryMsgCallback);
      }
    

    C#

      NIM.Messagelog.MessagelogAPI.QueryMsglogLocally("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeP2P, 20, 0, (code, accountId, sType, result) =>
      {
          foreach(var i in result.MsglogCollection)
          {
              ···
          }
      });
    

    C

      //按时间逆序查询,逆序排列
      void CallbackQueryMsgCb(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
      {
      	Json::Value values;
      	Json::Reader reader;
      	if (reader.parse(str, values) && values.isObject())
      	{
      		int count = values[kNIMMsglogQueryKeyCount].asInt();
      		if (count > 0)
      			...
      	}
      }
    
      typedef void(*nim_msglog_query_msg_async)(const char* account_id, nim::NIMSessionType to_type, int limit_count, __int64 last_time, const char *json_extension, nim_msglog_query_cb_func cb, const void* user_data);
    
      void foo()
      {
      	//设置搜索方向为向前
      	Json::Value json_extension_value;
      	json_extension_value[kNIMMsglogQueryJsonExtensionKeyDirection] = kForward;
      	Json::FastWriter fw;
    
      	nim_msglog_query_msg_async func = (nim_msglog_query_msg_async) GetProcAddress(hInst, "nim_msglog_query_msg_async");
      	func("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, 20, 0, fw.write(json_extension_value).c_str(), &CallbackQueryMsgCb, nullptr);
      }
    
  • 删除消息记录:

    C++

      void foo()
      {
      	//删除单条消息
      	nim::MsgLog::DeleteAsync("session_id", nim::kNIMSessionTypeTeam, client_msg_id_, nim::MsgLog::DeleteCallback());
    
      	//删除与某个聊天对象的全部消息记录
      	nim::MsgLog::BatchStatusDeleteAsync("session_id", nim::kNIMSessionTypeTeam, nim::MsgLog::BatchStatusDeleteCallback());
    
      	//删除指定会话类型的所有消息
      	nim::MsgLog::DeleteBySessionTypeAsync(true, nim::kNIMSessionTypeTeam, nim::MsgLog::DeleteBySessionTypeCallback());
    
      	//删除所有消息记录
      	nim::MsgLog::DeleteAllAsync(true, nim::MsgLog::DeleteAllCallback());
      }
    

    C#

      void foo()
      {
      	//删除单条消息
      	NIM.Messagelog.MessagelogAPI.DeleteSpecifiedMsglog("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeP2P, msg_id, (code, msgId) =>
      	{
      	});
    
      	//删除与某个聊天对象的全部消息记录
      	NIM.Messagelog.MessagelogAPI.BatchDeleteMeglog("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeP2P, (code, uid, sType) =>
      	{
      	});
    
      	//删除指定会话类型的所有消息
      	NIM.Messagelog.MessagelogAPI.DeleteMsglogsBySessionType(NIM.Session.NIMSessionType.kNIMSessionTypeP2P, true, (code, uid, sType) =>
          {
          });
    
      	//删除所有消息记录
      	NIM.Messagelog.MessagelogAPI.ClearAll(true, (code) =>
          {
          });
      }
    

    C

      void CallbackModifyMsglog(int res_code, const char *msg_id, const char *json_extension, const void user_data)
      {
      	if (user_data)
      	{
      		···
      	}
      }
    
      typedef void(*nim_msglog_delete_async)(const char *account_id, NIMSessionType to_type, const char *msg_id, const char *json_extension, nim_msglog_res_cb_func cb, const void *user_data);
      typedef void(*nim_msglog_batch_status_delete_async)(const char* account_id, nim::NIMSessionType to_type, const char *json_extension, nim_msglog_res_ex_cb_func cb, const void* user_data);
      typedef void(*nim_msglog_delete_by_session_type_async)(bool delete_sessions, NIMSessionType to_type, const char *json_extension, nim_msglog_res_ex_cb_func cb, const void *user_data);
      typedef void(*nim_msglog_delete_all_async)(bool delete_sessions, const char *json_extension, nim_msglog_modify_res_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_msglog_delete_async func = (nim_msglog_delete_async) GetProcAddress(hInst, "nim_msglog_delete_async");
      	nim_msglog_batch_status_delete_async func_batch_statuc = (nim_msglog_batch_status_delete_async) GetProcAddress(hInst, "nim_msglog_batch_status_delete_async");
      	nim_msglog_delete_by_session_type_async func_session = (nim_msglog_delete_by_session_type_async) GetProcAddress(hInst, "nim_msglog_delete_by_session_type_async");
      	nim_msglog_delete_all_async func_all = (nim_msglog_delete_all_async) GetProcAddress(hInst, "nim_msglog_delete_all_async");
    
      	//删除单条消息
      	func("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, msg_id, nullptr, &CallbackModifyMsglog, nullptr);
    
      	//删除与某个聊天对象的全部消息记录
      	func_batch_statuc("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, nullptr, &CallbackModifyMsglog, nullptr);
    
      	//删除指定会话类型的所有消息
      	func_session(true, is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, nullptr, &CallbackModifyMsglog, nullptr);
    
      	//删除所有消息记录
      	func_all(delete_sessions, nullptr, &CallbackModifyMsglog, nullptr);
      }
    

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

导入导出

  • 导入消息历史记录(不包括系统通知,且不允许拿别人的消息历史记录文件来导入):

      void nim_msglog_import_db_async(const char *src_path, const char *json_extension, nim_msglog_modify_res_cb_func res_cb, const void *res_user_data, nim_msglog_import_prg_cb_func prg_cb, const void *prg_user_data);
    

    例:

    C++

      void OnImportProgressCallback(int64_t imported_count, int64_t total_count)
      {
      	···
      }
    
      void OnImportCompeleteCallback(nim::NIMResCode res_code)
      {
      	if (res_code == nim::kNIMResSuccess)
      	{
      		//导入完成
      	}
      	else
      	{
      		//导入失败
      	}
      }
    
      void foo()
      {
      	nim::MsgLog::ImportDbAsync(path, &OnImportCompeleteCallback, &OnImportProgressCallback);
      }
    

    C#

      NIM.Messagelog.MessagelogAPI.ImportDatabase(path, (code) =>
      {
      }
      , (importedCount, totalCount) =>
      {
      });
    

    C

      void CallbackMsglogRes(int res, const char* msg_id, const char *json_exten, const void *user_data)
      {
      	...
      }
    
      void CallbackMsgImportPrg(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
      {
      	...
      }
    
      typedef void(*nim_msglog_import_db_async)(const char *src_path, const char *json_extension, nim_msglog_modify_res_cb_func res_cb, const void *res_user_data, nim_msglog_import_prg_cb_func prg_cb, const void *prg_user_data);
    
      void foo()
      {
      	nim_msglog_import_db_async func = (nim_msglog_import_db_async) GetProcAddress(hInst, "nim_msglog_import_db_async");
      	func("src_path", "", &CallbackMsglogRes, nullptr, &CallbackMsgImportPrg, nullptr);
      }
    
  • 导出整个消息历史DB文件(不包括系统通知):

    C++

      void OnExportCompeleteCallback(nim::NIMResCode res_code)
      {
      	if (res_code == nim::kNIMResSuccess)
      	{
      		//导出完成
      	}
      	else
      	{
      		//导出失败
      	}
      }
    
      void foo()
      {
      	nim::MsgLog::ExportDbAsync(path, &OnExportCompeleteCallback);
      }
    

    C#

      NIM.Messagelog.MessagelogAPI.ExportDatabaseFile(path, (code) =>
      {
      });
    

    C

      void CallbackMsglogRes(int res, const char* msg_id, const char *json_exten, const void *user_data)
      {
      	...
      }
    
      typedef void(*nim_msglog_export_db_async)(const char *dst_path, const char *json_extension, nim_msglog_modify_res_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_msglog_export_db_async func = (nim_msglog_export_db_async) GetProcAddress(hInst, "nim_msglog_export_db_async");
      	func("src_path", "", &CallbackMsglogRes, nullptr);
      }
    

云端记录

  • 在线查询聊天对象的消息历史记录:

      void nim_msglog_query_msg_online_async(const char *id //会话id,对方的account id或者群组tid
      								, NIMSessionType to_type //会话类型
      								, int limit_count //本次查询的消息条数上限(最多100条)
      								, __int64 from_time //起始时间点,单位:毫秒
      								, __int64 end_time //结束时间点,单位:毫秒
      								, __int64 end_msg_id //结束查询的最后一条消息的server_msg_id(不包含在查询结果中) 
      								, bool reverse //true:反向查询(按时间正序起查,正序排列),false:按时间逆序起查,逆序排列(建议默认为false)
      								, bool need_save_to_local //true: 将在线查询结果保存到本地,false: 不保存
      								, const char *json_extension
      								, nim_msglog_query_cb_func cb
      								, const void *user_data);
    

    例:

    C++

      void QueryMsgOnlineCb(nim::NIMResCode code, const std::string& id, nim::NIMSessionType type, const nim::QueryMsglogResult& result)
      {
      	if (code == nim::kNIMResSuccess)
      	{
      		std::vector<nim::IMMessage> vec;
      		for (auto& msg : result.msglogs_)
      		{
      			···
      		}
      	}
      }
    
      void foo()
      {
      	nim::MsgLog::QueryMsgOnlineAsync("session_id", nim::kNIMSessionTypeP2P, 20, 0, farst_msg_time_, last_server_id_, false, true, &QueryMsgOnlineCb);
      }
    

    C#

      NIM.Messagelog.MessagelogAPI.QueryMsglogOnline("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeTeam, 20, 0, 0, 0, false, false,
      (ResponseCode code, string accountId, NIM.Session.NIMSessionType sType, MsglogQueryResult result) =>
      {
          ···
      });
    

    C

      void CallbackQueryMsgCb(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
      {
      	...
      }
    
      typedef void(*nim_msglog_query_msg_online_async)(const char *id, nim::NIMSessionType to_type, int limit_count, __int64 from_time, __int64 end_time, __int64 end_msg_id, bool reverse,	bool need_save_to_local, const char *json_extension, nim_msglog_query_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_msglog_query_msg_online_async func = (nim_msglog_query_msg_online_async) GetProcAddress(hInst, "nim_msglog_query_msg_online_async");
      	func("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, 20, 0, msg_time, first_msg_server_msg_id, false, false, nullptr, &CallbackQueryMsgCb, nullptr);
      }
    

系统通知

除消息通道外,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);
}

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

  • 自定义通知消息是否存离线(kNIMSysMsgKeyCustomSaveFlag)
  • 自定义通知消息推送文本(kNIMSysMsgKeyCustomApnsText)
  • 第三方自定义的推送属性(kNIMSysMsgKeyPushPayload)
  • 是否需要推送(kNIMSysMsgKeyPushEnable)
  • 推送是否要做消息计数(kNIMSysMsgKeyNeedBadge)
  • 推送是否需要推送昵称(kNIMSysMsgKeyPushNeedNick)

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 OnGetFriendList(nim::NIMResCode res_code, const std::list<nim::FriendProfile>& user_profile_list)
      {
    
      }
    
      void foo()
      {
      	//向SDK注册监听好友列表变化
      	nim::Friend::GetList(&OnGetFriendList);
      }
    

    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 CallbackGetFriendsList(int res_code, const char *result_json, const char *json_extension, const void *user_data)
      {
      	// 解析result_json
      }
    
      typedef void(*nim_friend_get_list)(const char *json_extension, nim_friend_get_list_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_friend_get_list func = (nim_friend_get_list) GetProcAddress(hInst, "nim_friend_get_list");
      	func("", &CallbackGetFriendsList, nullptr);
      }
    
  • 好友请求

    好友请求包括请求添加好友以及同意/拒绝好友请求两种。

    好友验证包括直接添加为好友(kNIMVerifyTypeAdd),请求添加好友(kNIMVerifyTypeAsk),并且可以携带客户端自定义消息(msg)完成附加功能,例如添加好友附言功能。

    回调函数告知接口调用是否成功,以不需要验证方式的好友请求为例:

    C++

      void OnFriendRequest(int res_code)
      {
      	if (res_code == kNIMResSuccess)
      		...
      	else
      		...	
      }
    
      void foo()
      {
      	nim::Friend::Request("id", nim::kNIMVerifyTypeAdd, "", &OnFriendRequest);
      }
    

    C#

      NIM.Friend.FriendAPI.ProcessFriendRequest("id", NIM.Friend.NIMVerifyType.kNIMVerifyTypeAdd, "加我加我",
      (resCode, json, userdata) =>
      {
          if (resCode != 200)
          {
    
          }
      });
    

    C

      void CallbackFriendOpt(int res_code, const char *json_extension, const void *user_data)
      {
      	if (res_code == kNIMResSuccess)
      		...
      	else
      		...	
      }
    
      typedef void(*nim_friend_request)(const char *accid, NIMVerifyType verify_type, const char *msg, const char *json_extension, nim_friend_opt_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_friend_request func = (nim_friend_request) GetProcAddress(hInst, "nim_friend_request");
      	func("id", kNIMVerifyTypeAdd, nullptr, nullptr, &CallbackFriendOpt, nullptr);
      }
    

    如果好友验证方式为需要验证(kNIMVerifyTypeAsk),对方收到消息之后,可以选择同意(kNIMVerifyTypeAgree)或者拒绝好友请求(kNIMVerifyTypeReject),此时同样调用nim_friend_request 接口,传入拒绝对象的ID和验证回复类型即可。

  • 删除好友

    C++

      void OnDeleteFriend(int res_code)
      {
      	if (res_code == kNIMResSuccess)
      		...
      	else
      		...	
      }
    
      void foo()
      {
      	nim::Friend::Delete("id", &OnDeleteFriend);
      }
    

    C#

      NIM.Friend.FriendAPI.DeleteFriend(id, (res_code, json, userdata) =>
      {
          if (res_code != 200)
          {
              MessageBox.Show("删除失败");
          }
      });
    

    C

      void CallbackFriendOpt(int res_code, const char *json_extension, const void *user_data)
      {
      	if (res_code == kNIMResSuccess)
      		...
      	else
      		...	
      }
    
      typedef void(*nim_friend_delete)(const char *accid, const char *json_extension, nim_friend_opt_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_friend_delete func = (nim_friend_delete) GetProcAddress(hInst, "nim_friend_delete");
      	func("id", nullptr, &CallbackFriendOpt, nullptr);
      }
    
  • 更新资料

    C++

      void foo()
      {
      	nim::FriendProfile profile;
      	profile.SetAccId(m_uinfo.GetAccId());
      	profile.SetAlias(alias_edit->GetUTF8Text());
      	nim::Friend::Update(profile, nullptr);
      }
    

    C#

      NIM.Friend.NIMFriendProfile profile = new NIM.Friend.NIMFriendProfile;
      profile.Alias = "alias_name";
      profile.AccountId = "user_id";
      NIM.Friend.FriendAPI.UpdateFriendInfo(profile, (res_code, b, cc) =>
      {
          if (res_code == 200)
          {
              MessageBox.Show("修改成功");
          }
          else
          {
              MessageBox.Show("修改失败");
          }
      });
    

    C

      void CallbackFriendOpt(int res_code, const char *json_extension, const void *user_data)
      {
      	if (res_code == kNIMResSuccess)
      		...
      	else
      		...	
      }
    
      typedef void(*nim_friend_update)(const char *friend_json, const char *json_extension, nim_friend_opt_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_friend_update func = (nim_friend_update) GetProcAddress(hInst, "nim_friend_update");
    
      	Json::Value friend_profile_json;
      	friend_profile_json[kNIMFriendKeyAccid] = "accid";
      	friend_profile_json[kNIMFriendKeyAlias] = "alias_name";
      	friend_profile_json[kNIMFriendKeyCreateTime] = create_timetag_;
      	friend_profile_json[kNIMFriendKeyUpdateTime] = update_timetag_;
      	Json::FastWriter fw;
    
      	func(fw.write(friend_profile_json).c_str(), nullptr, &CallbackFriendOpt, nullptr);
      }
    
  • 查询是否为好友

    在本地缓存数据中查询accid是否为自己的好友,注意的是该接口为同步接口,会堵塞SDK线程,谨慎使用。

    例子:

    C++

      #include "nim_cpp_friend.h"
    
      bool IsFriend(const std::string& accid)
      {
      	return nim::Friend::QueryFriendshipBlock(accid);
      }
    

    C#

      bool IsFriend(string id)
      {
      	return NIM.Friend.FriendAPI.IsActiveFriend(id);
      }
    

    C

      #include "nim_friend.h"
    
      bool IsFriend(const std::string& accid)
      {
      	return nim_friend_query_friendship_block(accid.c_str(), 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 OnSetBlackCb(int res_code, const std::string& accid, bool opt)
      {
      	if (res_code != 200)
      		···
      }
    
      void foo()
      {
      	nim::User::SetBlack("accid", true, &OnSetBlackCb);
      }
    

    C#

      void foo()
      {
      	NIM.User.UserAPI.SetBlacklist("id", true, (response, accid, opt, jsonExtension, userData) =>
      	{
    
      	});
      }
    

    C

      void CallbackUserOpt(int res_code, const char *accid, bool opt, const char *json_extension, const void *user_data)
      {
      	if (res_code == kNIMResSuccess)
          	...
      	else
          	... 
      }
    
      typedef	void (*nim_user_set_black)(const char *accid, bool set_black, const char *json_extension, nim_user_opt_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_user_set_black func = (nim_user_set_black) GetProcAddress(hInst, "nim_user_set_black");
      	func("id", true, nullptr, &CallbackUserOpt, nullptr);
      }
    
  • 获取黑名单

    C++

      void OnGetBlackListCallback(nim::NIMResCode res_code, const std::list<nim::BlackListInfo>& lists)
      {
      	if (res_code == nim::kNIMResSuccess)
      	{
      		for (auto& info : lists)
      		{
    
      		}
      	}
      }
    
      void foo()
      {
      	nim::User::GetBlacklist(&OnGetBlackListCallback);
      }
    

    C#

      void GetUserRelationCompleted(ResponseCode code, UserSpecialRelationshipItem[] list)
      {
    
      }
    
      void foo()
      {
      	NIM.User.UserAPI.GetRelationshipList(GetUserRelationCompleted);
      }
    

    C

      void CallbackGetBlackList(int res_code, const char *mute_black_list_json, const char *json_extension, const void *callback)
      {
      	//解析mute_black_list_json
      }
    
      typedef	void (*nim_user_get_mute_blacklist)(const char *json_extension, nim_user_sync_muteandblacklist_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_user_get_mute_blacklist func = (nim_user_get_mute_blacklist) GetProcAddress(hInst, "nim_user_get_mute_blacklist");
      	func(nullptr, &CallbackGetBlackList, 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 OnSetMuteCb(int res_code, const std::string& accid, bool opt)
      {
      	if (res_code != 200)
      		···
      }
    
      void foo()
      {
      	nim::User::SetMute("accid", true, &OnSetMuteCb);
      }
    

    C#

      void foo()
      {
      	NIM.User.UserAPI.SetUserMuted("id", true, (response, accid, opt, jsonExtension, userData) =>
      	{
    
      	});
      }
    

    C

      void CallbackSetRelation(int res_code, const char *accid, bool opt, const char *json_extension, const void *user_data)
      {
      	if (res_code == kNIMResSuccess)
          	...
      	else
          	... 
      }
    
      typedef	void (*nim_user_set_mute)(const char *accid, bool set_mute, const char *json_extension, nim_user_opt_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_user_set_mute func = (nim_user_set_mute) GetProcAddress(hInst, "nim_user_set_mute");
      	func("id", true, nullptr, &CallbackSetRelation, nullptr);
      }
    
  • 获取静音名单列表

    C++

      void OnGetMuteListCallback(nim::NIMResCode res_code, const std::list<nim::BlackListInfo>& lists)
      {
      	if (res_code == nim::kNIMResSuccess)
      	{
      		for (auto& info : lists)
      		{
    
      		}
      	}
      }
    
      void foo()
      {
      	nim::User::GetMutelist(&OnGetMuteListCallback);
      }
    

    C#

      void GetUserRelationCompleted(ResponseCode code, UserSpecialRelationshipItem[] list)
      {
    
      }
    
      void foo()
      {
      	NIM.User.UserAPI.GetRelationshipList(GetUserRelationCompleted);
      }
    

    C

      void CallbackGetMuteList(int res_code, const char *mute_black_list_json, const char *json_extension, const void *callback)
      {
      	//解析mute_black_list_json
      }
    
      typedef	void (*nim_user_get_mute_blacklist)(const char *json_extension, nim_user_sync_muteandblacklist_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_user_get_mute_blacklist func = (nim_user_get_mute_blacklist) GetProcAddress(hInst, "nim_user_get_mute_blacklist");
      	func(nullptr, &CallbackGetMuteList, 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);
}

初始化

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 支持文本、图片、音频、视频、地理位置、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息。

  • 发送消息

    以发送文本消息的为例:.

    C++

      void foo()
      {
      std::string send_msg = ChatRoom::CreateRoomMessage(kNIMChatRoomMsgTypeText, QString::GetGUID(), text, ChatRoomMessageSetting());
      ChatRoom::SendMsg(room_id_, send_msg);
      }
    

    C#

      void foo()
      {
      	NIMChatRoom.Message msg = new NIMChatRoom.Message();
      	msg.MessageType = NIMChatRoom.NIMChatRoomMsgType.kNIMChatRoomMsgTypeText;
      	msg.MessageAttachment = "这是一条测试消息 " + DateTime.Now.ToString() + " " + new Random().NextDouble().ToString();
      	NIMChatRoom.ChatRoomApi.SendMessage(roomId, msg);
      }
    

    C

      typedef void(*nim_chatroom_send_msg)(const __int64 room_id, const char *msg, const char *json_extension);
    
      void foo()
      {
      	Json::Value values;
      	values[kNIMChatRoomMsgKeyFromNick] = from_nick;			//消息发送方昵称
      	values[kNIMChatRoomMsgKeyType] = msg_type;				//消息类型(NIMChatRoomMsgType)
      	values[kNIMChatRoomMsgKeyAttach] = attach;				//消息内容,json结构(发送时要求传入的是非格式化的)
      	values[kNIMChatRoomMsgKeyClientMsgid] = client_msg_id;	//客户端消息id
      	values[kNIMChatRoomMsgKeyResendFlag] = 0;				//消息重发标记位,第一次发送0,重发1
      	values[kNIMChatRoomMsgKeyExt] = ext;					//第三方扩展字段,  json结构(发送时要求传入的是非格式化的)
    
      	nim_chatroom_send_msg func = (nim_chatroom_send_msg) GetProcAddress(hInst, "nim_chatroom_send_msg");
    
      	Json::FastWriter fw;
      	func(room_id, fw.write(values).c_str(), "");
      }
    

    发送消息状态通过进入聊天室前注册的全局回调获取。

    C++

      void OnSendMsgCallback(__int64 room_id, int error_code, const ChatRoomMessage& result)
      {
      	if (error_code != 200)
      	{
    
      	}
      }
    
      void foo()
      {
      	ChatRoom::RegSendMsgAckCb(&OnSendMsgCallback);
      }
    

    C#

      void ChatRoomApi_SendMessageHandler(long roomId, NIM.ResponseCode code, NIMChatRoom.Message message)
      {
          if (code != NIM.ResponseCode.kNIMResSuccess)
          {
              MessageBox.Show("聊天室消息发送失败");
          } 
      }
    
      void foo()
      {
      	NIMChatRoom.ChatRoomApi.SendMessageHandler += ChatRoomApi_SendMessageHandler;
      }
    

    C

      static void CallbackSendMsgAck(__int64 room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
      {
    
      }
    
      typedef void(*nim_chatroom_reg_send_msg_ack_cb)(const char *json_extension, nim_chatroom_sendmsg_arc_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_reg_send_msg_ack_cb func = (nim_chatroom_reg_send_msg_ack_cb) GetProcAddress(hInst, "nim_chatroom_reg_send_msg_ack_cb");
      	func(nullptr, &CallbackSendMsgAck, nullptr);
      }
    
  • 接收消息

    进入聊天室前注册好接收消息的全局回调函数。

    C++

      void OnReceiveMsgCallback(__int64 room_id, const ChatRoomMessage& result)
      {
    
      }
    
      void foo()
      {
      	ChatRoom::RegReceiveMsgCb(&OnReceiveMsgCallback);
      }
    

    C#

      void ChatRoomApi_ReceiveMessageHandler(long roomId, NIMChatRoom.Message message)
      {
          string item = string.Format("{0}:\r\n{1}\r\n", message.SenderNickName, message.MessageAttachment);
      }
    
      void foo()
      {
      	NIMChatRoom.ChatRoomApi.ReceiveMessageHandler += ChatRoomApi_ReceiveMessageHandler;
      }
    

    C

      static void CallbackReceiveMsg(__int64 room_id, const char *content, const char *json_extension, const void *user_data)
      {
    
      }
    
      typedef void(*nim_chatroom_reg_receive_msg_cb)(const char *json_extension, nim_chatroom_receive_msg_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_reg_receive_msg_cb func = (nim_chatroom_reg_receive_msg_cb) GetProcAddress(hInst, "nim_chatroom_reg_receive_msg_cb");
      	func(nullptr, &CallbackReceiveMsg, nullptr);
      }
    

查询消息历史

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 OnGetMembersCallback(__int64 room_id, int error_code, const std::list<ChatRoomMemberInfo>& infos)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	ChatRoomGetMembersParameters member_param;
      	member_param.type_ = kNIMChatRoomGetMemberTypeSolid;
      	ChatRoom::GetMembersOnlineAsync(room_id, member_param, &OnGetMembersCallback);
      }
    

    C#

      void foo()
      {
      	NIMChatRoom.ChatRoomApi.QueryMembersOnline(roomId, NIMChatRoom.NIMChatRoomGetMemberType.kNIMChatRoomGetMemberTypeSolid, 0, 10, (room_Id, errorCode, Mmembers) =>
          {
    
          });
      }
    

    C

      typedef void(*nim_chatroom_get_members_online_async)(const __int64 room_id, const char *parameters_json_str, const char *json_extension, nim_chatroom_get_members_cb_func cb, const void *user_data);
    
      static void CallbackGetMembersOnline(__int64 room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
      {
    
      }
    
      void foo()
      {
      	nim_chatroom_get_members_online_async func = (nim_chatroom_get_members_online_async) GetProcAddress(hInst, "nim_chatroom_get_members_online_async");
    
      	Json::Value values;
      	values[kNIMChatRoomGetMembersKeyType] = type_;					//成员类型
      	values[kNIMChatRoomGetMembersKeyOffset] = timestamp_offset_;	//成员时间戳偏移量
      	values[kNIMChatRoomGetMembersKeyLimit] = limit_;				//数量
    
      	Json::FastWriter fw;
    
      	func(room_id, fw.write(values).c_str(), nullptr, &CallbackGetMembersOnline, nullptr);
      }
    
  • 获取指定id的在线成员信息

    C++

      void OnGetMembersCallback(__int64 room_id, int error_code, const std::list<ChatRoomMemberInfo>& infos)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	std::list<std::string> ids;
      	ids.push_back("user_id");
      	ChatRoom::GetMemberInfoByIDsAsync(room_id, ids, &OnGetMembersCallback);
      }
    

    C#

      void foo()
      {
      	NIMChatRoom.ChatRoomApi.QueryMemberInfosByIdCollection(roomId, new string[] { "userID"}, (room_Id, errorCode, Mmembers) =>
          {
    
          });
      }
    

    C

      typedef void(*nim_chatroom_get_members_by_ids_online_async)(const __int64 room_id, const char *ids_json_array_string, const char *json_extension, nim_chatroom_get_members_cb_func cb, const void *user_data);
    
      static void CallbackGetMembersOnline(__int64 room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
      {
    
      }
    
      void foo()
      {
      	nim_chatroom_get_members_by_ids_online_async func = (nim_chatroom_get_members_by_ids_online_async) GetProcAddress(hInst, "nim_chatroom_get_members_by_ids_online_async");
    
      	func(room_id, ids, nullptr, &CallbackGetMembersOnline, nullptr);
      }
    

踢出在线成员

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++

      void SetMemberAttributeCallback(__int64 room_id, int error_code, const ChatRoomMemberInfo& info)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	ChatRoomSetMemberAttributeParameters param;
      	param.account_id_ = user_account;
      	param.attribute_ = kNIMChatRoomMemberAttributeMuteList;
      	param.opt_ = true;
    
      	ChatRoom::SetMemberAttributeOnlineAsync(room_id, param, &SetMemberAttributeCallback);
      }
    

    C#

      void foo()
      {
      	MemberProperty prop = new MemberProperty();
      	prop.MemberId = "user_id";
      	prop.Attribute = NIMChatRoomMemberAttribute.kNIMChatRoomMemberAttributeAdminister;
      	NIMChatRoom.ChatRoomApi.SetMemberPropertyOnline(roomId, prop, (room_Id, errorCode, info) =>
      	{
      	});
      }
    

    C

      typedef void(*nim_chatroom_set_member_attribute_async)(const __int64 room_id, const char *parameters_json_str, const char *json_extension, nim_chatroom_set_member_attribute_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_set_member_attribute_async func = (nim_chatroom_set_member_attribute_async) GetProcAddress(hInst, "nim_chatroom_set_member_attribute_async");
    
      	Json::Value values;
      	values[kNIMChatRoomSetMemberAttributeKeyAccoutID] = account_id_;	//成员ID	
      	values[kNIMChatRoomSetMemberAttributeKeyNotifyExt] = notify_ext_;	//通知的扩展字段
    
      	//设置管理员枚举值
      	values[kNIMChatRoomSetMemberAttributeKeyAttribute] = kNIMChatRoomMemberAttributeAdminister;	
    
      	//设置管理员true,取消管理员false	
      	values[kNIMChatRoomSetMemberAttributeKeyOpt] = true;											
    
      	Json::FastWriter fw;
    
      	func(room_id, fw.write(values).c_str(), nullptr, &CallbackSetMemberAtribute, nullptr);
      }
    
  • 设置普通成员

    设置普通成员通过将上面代码片段中的“设置管理员枚举值”修改为设置普通成员枚举值即可

      values[kNIMChatRoomSetMemberAttributeKeyAttribute] = kNIMChatRoomMemberAttributeNomalSold;
    
      //设置普通成员true,取消普通成员false	
      values[kNIMChatRoomSetMemberAttributeKeyOpt] = true;				
    
  • 拉黑

    设置普通成员通过将上面代码片段中的“设置管理员枚举值”修改为设置黑名单枚举值即可

      	values[kNIMChatRoomSetMemberAttributeKeyAttribute] = kNIMChatRoomMemberAttributeBlackList;	
    
      	//拉黑true,取消拉黑false
      	values[kNIMChatRoomSetMemberAttributeKeyOpt] = true;
    
  • 禁言

    设置普通成员通过将上面代码片段中的“设置管理员枚举值”修改为设置禁言枚举值即可

      	values[kNIMChatRoomSetMemberAttributeKeyAttribute] = kNIMChatRoomMemberAttributeMuteList;	
    
      	//禁言true,取消禁言false	
      	values[kNIMChatRoomSetMemberAttributeKeyOpt] = true;
    
  • 临时禁言,可以设置时间长度,当时间到达设置的时长,自动解除禁言

    C++

      void TempMuteCallback(__int64 room_id, int error_code, const ChatRoomMemberInfo& info)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	ChatRoom::TempMuteMemberAsync(room_id, user_account, 60, true, "", &TempMuteCallback);
      }
    

    C#

      void foo()
      {
      	ChatRoomApi.TempMuteMember(roomId, accid, 120,
      	(roomId, errorCode, info) =>
      	{
      	}, true, accid + " 被禁言120s");
      }
    

    C

      void CallbackTempMuteMember(int64_t room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
      {
      	// 解析result
      }
    
      typedef void(*nim_chatroom_temp_mute_member_async)(const int64_t room_id, const char *accid, const int64_t duration, bool need_notify, const char *notify_ext, const char *json_extension, nim_chatroom_temp_mute_member_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_temp_mute_member_async func = (nim_chatroom_temp_mute_member_async) GetProcAddress(hInst, "nim_chatroom_temp_mute_member_async");
    
      	func(room_id, "user_id", 60, true, "某人被禁言了", nullptr, &CallbackTempMuteMember, nullptr);
      }
    

聊天室队列服务

  • 新加(更新)麦序队列元素(聊天室管理员权限)

    C++

      void QueueOfferCallback(int64_t room_id, int error_code)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	ChatRoomQueueElement element;
      	element.key_ = "key";
      	element.value_ = "value";
      	ChatRoom::QueueOfferAsync(room_id, element, &QueueOfferCallback);
      }
    

    C#

      void foo()
      {
      	NIMChatRoom.ChatRoomApi.QueueOfferAsync(roomId, "key", "value", (room_id, error_code, json_extension, user_data) =>
      	{
      	});
      }
    

    C

      void CallbackQueueOffer(int64_t room_id, int error_code, const char *json_extension, const void *user_data)
      {
    
      }
    
      typedef void(*nim_chatroom_queue_offer_async)(const int64_t room_id, const char *element_key, const char *element_value, const char *json_extension, nim_chatroom_queue_offer_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_queue_offer_async func = (nim_chatroom_queue_offer_async) GetProcAddress(hInst, "nim_chatroom_queue_offer_async");
    
      	func(room_id, "key", "value", nullptr, &CallbackQueueOffer, nullptr);
      }
    
  • 取出麦序头元素(聊天室管理员权限)

    C++

      void CallbackQueuePoll(int64_t room_id, int error_code, const ChatRoomQueueElement& element)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	ChatRoom::QueuePollAsync(room_id, "key", &CallbackQueuePoll);
      }
    

    C#

      void foo()
      {
      	NIMChatRoom.ChatRoomApi.QueuePollAsync(roomId, "key", (room_id, error_code, result, json_extension, user_data) =>
      	{
    
      	});
      }
    

    C

      void CallbackQueuePoll(int64_t room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
      {
      	//解析result
      }
    
      typedef void(*nim_chatroom_queue_poll_async)(const int64_t room_id, const char *element_key, const char *json_extension, nim_chatroom_queue_poll_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_queue_poll_async func = (nim_chatroom_queue_poll_async) GetProcAddress(hInst, "nim_chatroom_queue_poll_async");
    
      	func(room_id, "key", nullptr, &CallbackQueuePoll, nullptr);
      }
    
  • 排序列出所有麦序元素

    C++

      void CallbackQueueList(int64_t room_id, int error_code, const ChatRoomQueue& queue)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	ChatRoom::QueueListAsync(room_id, &CallbackQueueList);
      }
    

    C#

      void foo()
      {
      	NIMChatRoom.ChatRoomApi.QueueListAsync(roomId, (room_id, error_code, result, json_extension, user_data) =>
      	{
      		if (error_code == ResponseCode.kNIMResSuccess)
      		{
    
      		}
      	});
      }
    

    C

      void CallbackQueueList(int64_t room_id, int error_code, const char *result, const char *json_extension, const void *user_data)
      {
      	//解析result
      }
    
      typedef void(*nim_chatroom_queue_list_async)(const int64_t room_id, const char *json_extension, nim_chatroom_queue_list_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_queue_list_async func = (nim_chatroom_queue_list_async) GetProcAddress(hInst, "nim_chatroom_queue_list_async");
    
      	func(room_id, nullptr, &CallbackQueueList, nullptr);
      }
    
  • 删除麦序队列(聊天室管理员权限)

    C++

      void CallbackQueueDrop(int64_t room_id, int error_code)
      {
      	if (error_code != nim::kNIMResSuccess)
      		return;
      }
    
      void foo()
      {
      	ChatRoom::QueueDropAsync(room_id, &CallbackQueueDrop);
      }
    

    C#

      void foo()
      {
      	NIMChatRoom.ChatRoomApi.QueueDropAsync(roomId, (room_id, error_code, json_extension, user_data) =>
      	{
    
      	});
      }
    

    C

      void CallbackQueueDrop(int64_t room_id, int error_code, const char *json_extension, const void *user_data)
      {
    
      }
    
      typedef void(*nim_chatroom_queue_drop_async)(const int64_t room_id, const char *json_extension, nim_chatroom_queue_drop_cb_func cb, const void *user_data);
    
      void foo()
      {
      	nim_chatroom_queue_drop_async func = (nim_chatroom_queue_drop_async) GetProcAddress(hInst, "nim_chatroom_queue_drop_async");
    
      	func(room_id, nullptr, &CallbackQueueDrop, nullptr);
      }
    

C++ SDK开发手册

C# SDK开发手册