前提条件
了解在线客服相关术语及相关配置,并已完成以下步骤:添加客服、配置技能组、创建会话服务流,详情请参见 快速入门。
集成 IM SDK ,并跑通登录、发单聊消息等功能。请参见 iOS(含 UI)。
UI 插件介绍
您可以集成腾讯云即时通信官方提供的在线客服插件 TUICustomerServicePlugin,集成后,您可以在 IM 应用中集成在线客服功能。在线客服功能包括解析在线客服自定义消息、发送卡片消息、主动评价客服等。
环境与版本
本插件依赖插件以及环境
Xcode 10 及以上
iOS 9.0 及以上
插件集成
通过 CocoaPods 可将在线客服插件引入到项目中:
# 集成客服插件(7.6.5011 及以上版本支持)pod 'TUICustomerServicePlugin'# 客服插件依赖 TUIChat 和 TUIContact 两个组件,这两个组件也需同时集成,客服插件版本需要和组件版本保持一致pod 'TUIChat'pod 'TUIContact'
步骤1:管理端客服插件设置
步骤2:终端客服列表设置
在进入客服虚拟号的聊天页面时会有特殊的消息发送操作,因此我们建议在终端将客服虚拟号配置保存起来,方便全局调用。
// 1. 设置默认的客服虚拟号// 代码参考 TUICustomerServicePluginPrivateConfig.m 中的 customerServiceAccounts 函数@implementation TUICustomerServicePluginPrivateConfig- (NSArray *)customerServiceAccounts {return @[gOnlineShopping, gOnlineDoctor];}@end// 2. 监听 TUIContact 组件的 TUICore_TUIContactExtension_ContactMenu_ClassicExtensionID 扩展事件添加客服到联系人列表中// 代码参考 TUICustomerServicePluginExtensionObserver.m 的 load 函数和 onGetExtension 函数@implementation TUICustomerServicePluginExtensionObserver+ (void)load {[TUICore registerExtension:TUICore_TUIContactExtension_ContactMenu_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];}- (NSArray<TUIExtensionInfo *> *)onGetExtension:(NSString *)extensionID param:(NSDictionary *)param {if (![extensionID isKindOfClass:NSString.class]) {return nil;}if ([extensionID isEqualToString:TUICore_TUIContactExtension_ContactMenu_ClassicExtensionID]) {TUIExtensionInfo *customerService = [[TUIExtensionInfo alloc] init];customerService.weight = 50;customerService.text = TIMCommonLocalizableString(TUICustomerServiceAccounts);customerService.icon = TUICustomerServicePluginBundleThemeImage(@"customer_service_contact_menu_icon_img", @"contact_customer_service");customerService.onClicked = ^(NSDictionary *_Nonnull param) {// 在线客服被点击后事件处理};return @[customerService];}}// 3. 获取在线客服的资料信息// 代码参考 TUICustomerServicePluginAccountController 的 getUserInfo 函数@implementation TUICustomerServicePluginAccountController- (void)getUserInfo {NSArray *accounts = TUICustomerServicePluginPrivateConfig.sharedInstance.customerServiceAccounts;[[V2TIMManager sharedInstance] getUsersInfo:accountssucc:^(NSArray<V2TIMUserFullInfo *> *infoList) {for (V2TIMUserFullInfo *info in infoList) {TUICommonContactCellData *data = [TUICommonContactCellData new];data.identifier = info.userID;data.avatarUrl = [NSURL URLWithString:info.faceURL];data.title = info.nickName;}} fail:^(int code, NSString *desc) {// 获取资料信息失败}];}@end
步骤3:在线客服消息解析
在线客服的特殊消息,例如分支消息、卡片消息、评价消息等,都是通过自定义消息实现,因此您需要在接收消息时判断自定义消息类型并解析渲染。TUICustomerServicePlugin 组件提供了各种
CellData
和 Cell
来解析和渲染不同的自定义消息。使用方法如下:// 1. 注册客服自定义消息,绑定自定义消息的 BussinessID 和消息解析以及渲染的 CellData 和 Cell// TUIChat 组件收到客服自定义消息,会根据 BussinessID 自动调用 CellData 的 getCellData 函数解析消息,调用 Cell 的 fillWithData 函数渲染消息// 代码参考 TUICustomerServicePluginService 的 registerCustomMessageCell 函数@implementation TUICustomerServicePluginService- (void)registerCustomMessageCell {// 分支选择自定义消息[TUICore callService:TUICore_TUIChatServicemethod:TUICore_TUIChatService_AppendCustomMessageMethodparam:@{BussinessID : BussinessID_Src_CustomerService_Branch,TMessageCell_Name : @"TUICustomerServicePluginBranchCell",TMessageCell_Data_Name : @"TUICustomerServicePluginBranchCellData"}];// 表单收集自定义消息[TUICore callService:TUICore_TUIChatServicemethod:TUICore_TUIChatService_AppendCustomMessageMethodparam:@{BussinessID : BussinessID_Src_CustomerService_Collection,TMessageCell_Name : @"TUICustomerServicePluginCollectionCell",TMessageCell_Data_Name : @"TUICustomerServicePluginCollectionCellData"}];//...}@end// 2. 通过 CellData 解析自定义消息@implementation TUICustomerServicePluginXXXCellData+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {// 解析自定义消息}@end// 3. 通过 Cell 展示自定义消息@implementation TUICustomerServicePluginXXXCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];if (self) {// 初始化 Cell}return self;}- (void)fillWithData:(TUICustomerServicePluginCollectionCellData *)data {[super fillWithData:data];// 根据 data 绘制 Cell}
在线客服消息中有一些特殊的消息为标志消息,例如会话结束、会话开始、客服配置等,这一类消息不需要渲染在消息列表中。CellData 提供了
shouldHide
方法用于判断在线客服消息是否需要在消息列表中渲染。使用方法如下:// 参考 TUICustomerServicePluginInvisibleCellData 的 shouldHide 函数@implementation TUICustomerServicePluginXXXCellData- (BOOL)shouldHide {return YES;}@end
步骤4:主动发送启动消息
在进入在线客服用户的聊天时,需要发送一条特殊的自定义消息用于启动在线客服会话。TUICustomerServicePlugin 提供了
sendCustomMessageWithoutUpdateUI
方法用于快速发送启动消息。使用方法如下:// 1. 注册聊天界面进入事件监听// 参考 TUICustomerServicePluginService.m 的 registerEvent 函数@implementation TUICustomerServicePluginService- (void)registerEvent {[TUICore registerEvent:TUICore_TUIChatNotifysubKey:TUICore_TUIChatNotify_ViewDidLoadSubKeyobject:self];}// 2. 收到监听回调后,主动发送一条特殊的自定义消息启动在线客服会话// 参考 TUICustomerServicePluginService.m 的 onNotifyEvent 函数- (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(nullable id)anObject param:(nullable NSDictionary *)param {if ([key isEqualToString:TUICore_TUIChatNotify] &&[subKey isEqualToString:TUICore_TUIChatNotify_ViewDidLoadSubKey]) {if (param == nil) {NSLog(@"TUIChat notify param is invalid");return;}NSString *userID = [param objectForKey:TUICore_TUIChatNotify_ViewDidLoadSubKey_UserID];if (![TUICustomerServicePluginPrivateConfig.sharedInstance isCustomerServiceAccount:userID]) {return;}NSData *data = [TUITool dictionary2JsonData:@{@"src": BussinessID_Src_CustomerService_Request}];[TUICustomerServicePluginDataProvider sendCustomMessageWithoutUpdateUI:data];}}@end
获取在线客服输入状态(可选)
当座席端正在输入消息时,用户端可以收到下发的自定义消息用于确定客服的输入状态。TUICustomerServicePlugin 提供了
TUICustomerServicePluginTypingCellData
类用于解析自定义消息,使用方法如下:// 1. 注册座席端正在输入自定义消息,绑定自定义消息的 BussinessID 和消息解析的 CellData// TUIChat 组件收到自定义消息,会根据 BussinessID 自动调用 CellData 的 getCellData 函数解析消息// 代码参考 TUICustomerServicePluginService 的 registerCustomMessageCell 函数@implementation TUICustomerServicePluginService- (void)registerCustomMessageCell {// 座席端正在输入自定义消息[TUICore callService:TUICore_TUIChatServicemethod:TUICore_TUIChatService_AppendCustomMessageMethodparam:@{BussinessID : BussinessID_Src_CustomerService_Typing,TMessageCell_Name : @"TUIMessageCell",TMessageCell_Data_Name : @"TUICustomerServicePluginTypingCellData"}];}@end// 2. 通过 CellData 解析自定义消息// 参考 TUICustomerServicePluginTypingCellData.m 的 getCellData 函数// 其中 TUICustomerServicePluginTypingCellData 需要继承自 TUITypingStatusCellData,并设置对方正在输入状态(typingStatus)@interface TUICustomerServicePluginTypingCellData : TUITypingStatusCellData@end@implementation TUICustomerServicePluginTypingCellData+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];TUITypingStatusCellData *cellData = [[TUITypingStatusCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];cellData.msgID = message.msgID;if ([param[@"src"] isEqualToString: BussinessID_Src_CustomerService_Typing]) {cellData.typingStatus = 1;}return cellData;}@end// 3.TUIChat 组件根据 CellData 的 typingStatus 状态自动在聊天界面 NavgationBar 展示或隐藏 "对方正在输入..." 状态
主动发送卡片消息(可选)
在与在线客服的对话中,用户端可主动发送带跳转地址的卡片消息。卡片消息包括名称、描述、图片、点击时的跳转地址四个属性。
1.添加入快速操作栏
您可以将发送卡片消息的快捷按钮放在聊天页面中,具体实现参考如下:
// 1. 注册聊天页面快捷操作栏扩展事件// 代码参考 TUICustomerServicePluginExtensionObserver 的 load 函数@implementation TUICustomerServicePluginExtensionObserver+ (void)load {[TUICore registerExtension:TUICore_TUIChatExtension_ChatVCBottomContainer_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];}// 2. 在事件回调添加快捷操作栏// 代码参考 TUICustomerServicePluginExtensionObserver 的 onRaiseExtension 函数- (BOOL)onRaiseExtension:(NSString *)extensionID parentView:(UIView *)parentView param:(nullable NSDictionary *)param {if ([extensionID isEqualToString:TUICore_TUIChatExtension_ChatVCBottomContainer_ClassicExtensionID]) {if (param == nil) {NSLog(@"TUIChat notify param is invalid");return NO;}NSString *userID = [param objectForKey:TUICore_TUIChatExtension_ChatVCBottomContainer_UserID];if (![TUICustomerServicePluginPrivateConfig.sharedInstance isOnlineShopping:userID]) {return NO;}if (![parentView isKindOfClass:UIView.class]) {return NO;}TUICustomerServicePluginMenuView *view = [[TUICustomerServicePluginMenuView alloc] initWithDataSource:TUICustomerServicePluginConfig.sharedInstance.menuItems];[parentView addSubview:view];[view updateFrame];return YES;}return NO;}@end// 3. 自定义快捷操作栏为 "发送商品"// 代码参考 TUICustomerServicePluginConfig 的 defaultMenuItems 函数@implementation TUICustomerServicePluginConfig- (NSArray *)defaultMenuItems {NSMutableArray *dataSource = [NSMutableArray new];TUICustomerServicePluginMenuCellData *product = [TUICustomerServicePluginMenuCellData new];product.title = TIMCommonLocalizableString(TUICustomerServiceSendProduct);product.target = TUICustomerServicePluginExtensionObserver.shareInstance;product.cselector = @selector(onProductClicked);[dataSource addObject:product];return [dataSource copy];}@end
2.发送卡片消息
上一步骤注册了发送卡片的快捷入口后,在快捷入口被点击后会触发
onProductClicked
回调,您可以在该回调下发送卡片消息:// 1. 快捷入口被点击// 代码参考 TUICustomerServicePluginExtensionObserver 的 onProductClicked 函数@implementation TUICustomerServicePluginExtensionObserver- (void)onProductClicked {TUICustomerServicePluginProductInfo *info = TUICustomerServicePluginConfig.sharedInstance.productInfo;NSDictionary *dict = @{BussinessID_CustomerService: @0,@"src": BussinessID_Src_CustomerService_Card,@"content": @{@"header": info.title ?: @"",@"desc": info.desc ?: @"",@"pic": info.picURL ?: @"",@"url": info.linkURL ?: @""}};NSData *data = [TUITool dictionary2JsonData:dict];[TUICustomerServicePluginDataProvider sendCustomMessage:data];}@end// 2. 自定义商品信息// productInfo 的组成方式可以参考 TUICustomerServicePluginConfig 的 defaultProductInfo 函数@implementation TUICustomerServicePluginConfig- (TUICustomerServicePluginProductInfo *)defaultProductInfo {TUICustomerServicePluginProductInfo *info = [TUICustomerServicePluginProductInfo new];info.title = @"手工编织皮革提包2023新品女士迷你简约大方高端有档次";info.desc = @"¥788";info.picURL = @"https://qcloudimg.tencent-cloud.cn/raw/a811f634eab5023f973c9b224bc07a51.png";info.linkURL = @"https://cloud.tencent.com/document/product/269";return info;}@end
3.展示卡片消息
上一步骤发送了卡片消息后,我们需要在聊天界面展示卡片消息,可以参考如下代码实现:
// 1. 注册卡片自定义消息,绑定自定义消息的 BussinessID 和消息解析以及渲染的 CellData 和 Cell// TUIChat 组件收到自定义消息,会根据 BussinessID 自动调用 CellData 的 getCellData 函数解析消息,调用 Cell 的 fillWithData 函数渲染消息// 代码参考 TUICustomerServicePluginService 的 registerCustomMessageCell 函数@implementation TUICustomerServicePluginService- (void)registerCustomMessageCell {// 卡片自定义消息[TUICore callService:TUICore_TUIChatServicemethod:TUICore_TUIChatService_AppendCustomMessageMethodparam:@{BussinessID : BussinessID_Src_CustomerService_Card,TMessageCell_Name : @"TUICustomerServicePluginCardCell",TMessageCell_Data_Name : @"TUICustomerServicePluginCardCellData"}];}@end// 2. 通过 CellData 解析卡片消息// 代码参考 TUICustomerServicePluginCardCellData.m 的 getCellData 函数@implementation TUICustomerServicePluginCardCellData+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.dataoptions:NSJSONReadingAllowFragments error:nil];if (param == nil) {return nil;}TUICustomerServicePluginCardCellData *cellData = [[TUICustomerServicePluginCardCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];cellData.innerMessage = message;NSDictionary *content = param[@"content"];cellData.header = content[@"header"];cellData.desc = content[@"desc"];cellData.picURL = content[@"pic"];cellData.jumpURL = content[@"url"];return cellData;}@end// 3. 通过 Cell 展示卡片消息// 代码参考 TUICustomerServicePluginCardCell.m 的 initWithStyle 和 fillWithData 函数@implementation TUICustomerServicePluginCardCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];if (self) {self.headerLabel = [[UILabel alloc] init];[self.container addSubview:self.headerLabel];self.descLabel = [[UITableView alloc] init];[self.container addSubview:self.descLabel];//...}return self;}- (void)fillWithData:(TUICustomerServicePluginCardCellData *)data {[super fillWithData:data];self.customData = data;self.headerLabel.text = data.header;self.descLabel.text = data.desc;//...}@end
主动评价客服(可选)
当在在线客服的管理端设置了用户可主动评价坐席时,用户端就可以主动拉取评价消息并评价。
TUICustomerServicePluginPrivateConfig 提供了
canEvaluate
参数用于快速判断是否能主动发送评价, canEvaluate
参数的赋值方法如下:// 1. 注册评价规则自定义消息,绑定自定义消息的 BussinessID 和消息解析以及渲染的 CellData 和 Cell// TUIChat 组件收到评价规则自定义消息(主动发送启动消息后会收到),会根据 BussinessID 自动调用 CellData 的 getCellData 函数解析消息// 代码参考 TUICustomerServicePluginService 的 registerCustomMessageCell 函数@implementation TUICustomerServicePluginService- (void)registerCustomMessageCell {// 评价规则自定义消息[TUICore callService:TUICore_TUIChatServicemethod:TUICore_TUIChatService_AppendCustomMessageMethodparam:@{BussinessID : BussinessID_Src_CustomerService_EvaluationRule,TMessageCell_Name : @"TUICustomerServicePluginInvisibleCell",TMessageCell_Data_Name : @"TUICustomerServicePluginInvisibleCellData"}];}@end// 2. 通过 CellData 解析自定义消息,判断是否可以主动发送评价@implementation TUICustomerServicePluginInvisibleCellData+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.dataoptions:NSJSONReadingAllowFragments error:nil];if (param == nil) {return nil;}TUICustomerServicePluginInvisibleCellData *cellData = [[TUICustomerServicePluginInvisibleCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];cellData.innerMessage = message;if ([param[@"src"] isEqualToString: BussinessID_Src_CustomerService_EvaluationRule]) {NSDictionary *content = param[@"content"];// 判断是否能发送评价消息NSInteger menuSendRuleFlag = [content[@"menuSendRuleFlag"] integerValue];[TUICustomerServicePluginPrivateConfig sharedInstance].canEvaluate = menuSendRuleFlag >> 2;}return cellData;}@end
如果可以主动发送评价,我们可以注册输入栏的快捷入口,当入口被点击后,主动发送评价请求自定义消息。
// 1. 注册输入栏快捷入口扩展监听// 参考 TUICustomerServicePluginExtensionObserver 的 load 函数@implementation TUICustomerServicePluginExtensionObserver+ (void)load {[TUICore registerExtension:TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];}// 2. 在监听回调注册 "服务评价" 按钮// 参考 TUICustomerServicePluginExtensionObserver 的 onGetExtension 函数- (NSArray<TUIExtensionInfo *> *)onGetExtension:(NSString *)extensionID param:(NSDictionary *)param {if (![extensionID isKindOfClass:NSString.class]) {return nil;}if ([extensionID isEqualToString:TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID]) {NSString *userID = [param tui_objectForKey:TUICore_TUIChatExtension_InputViewMoreItem_UserID asClass:NSString.class];if (![TUICustomerServicePluginPrivateConfig.sharedInstance isCustomerServiceAccount:userID]) {return nil;}if (![TUICustomerServicePluginPrivateConfig sharedInstance].canEvaluate) {return nil;}TUIExtensionInfo *evaluation = [[TUIExtensionInfo alloc] init];evaluation.weight = 100;evaluation.text = TIMCommonLocalizableString(TUIKitMoreEvaluation);evaluation.icon = TIMCommonBundleThemeImage(@"service_more_customer_service_evaluation_img", @"more_customer_service_evaluation");evaluation.onClicked = ^(NSDictionary *_Nonnull param) {// 3. "服务评价" 按钮被点击后发送主动评价请求信令NSData *data = [TUITool dictionary2JsonData:@{@"src": BussinessID_Src_CustomerService_EvaluationTrigger}];[TUICustomerServicePluginDataProvider sendCustomMessageWithoutUpdateUI:data];};return @[evaluation];} else {return nil;}}@end
服务器收到终端发送的评价请求自定义消息,会下发评价消息,终端收到评价消息后解析展示:
// 1. 注册评价自定义消息,绑定自定义消息的 BussinessID 和消息解析以及渲染的 CellData 和 Cell// TUIChat 组件收到自定义消息,会根据 BussinessID 自动调用 CellData 的 getCellData 函数解析消息,调用 Cell 的 fillWithData 函数渲染消息// 代码参考 TUICustomerServicePluginService 的 registerCustomMessageCell 函数@implementation TUICustomerServicePluginService- (void)registerCustomMessageCell {// 评价自定义消息[TUICore callService:TUICore_TUIChatServicemethod:BussinessID_Src_CustomerService_Evaluationparam:@{BussinessID : BussinessID_Src_CustomerService_Card,TMessageCell_Name : @"TUICustomerServicePluginEvaluationCell",TMessageCell_Data_Name : @"TUICustomerServicePluginEvaluationCellData"}];}@end// 2. 通过 CellData 解析评价消息// 参考 TUICustomerServicePluginEvaluationCellData.m 的 getCellData 函数@implementation TUICustomerServicePluginEvaluationCellData+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.dataoptions:NSJSONReadingAllowFragments error:nil];if (param == nil) {return nil;}TUICustomerServicePluginEvaluationCellData *cellData = [[TUICustomerServicePluginEvaluationCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];cellData.innerMessage = message;NSDictionary *content = param[@"menuContent"];cellData.type = [content[@"type"] integerValue];cellData.header = content[@"head"];cellData.tail = content[@"tail"];//...return cellData;}@end// 3. 通过 Cell 展示评价消息// 参考 TUICustomerServicePluginEvaluationCell.m 的 initWithStyle 和 fillWithData 函数@implementation TUICustomerServicePluginEvaluationCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];if (self) {self.headerLabel = [[UILabel alloc] init];[self.container addSubview:self.headerLabel];self.bottomLabel = [[UILabel alloc] init];[self.container addSubview:self.bottomLabel];//...}return self;}- (void)fillWithData:(TUICustomerServicePluginCollectionCellData *)data {[super fillWithData:data];self.customData = data;self.headerLabel.text = data.header;self.bottomLabel.text = data.tail;//...}@end