IM安全通(内容审核)
更新时间: 2024/08/14 11:41:36
网易云信 IM 安全通(内容安全审核)服务,融合了网易易盾的内容审核能力,支持对文本消息、图片消息、自定义消息、用户头像和用户资料等类型的内容安全检测,实现全流程的审-查-禁功能,降低人力投入。
功能介绍
开通安全通功能并配置安全通检测规则后,指定类型的消息经过相关的接口调用和配置后,都会先经由安全通进行内容安全检测,之后才会转发给接收端的用户。安全通支持如下几种消息类型的内容审核。
API 关键字 | 说明 |
---|---|
text | 文本消息 |
image | 图片消息 |
audio | 音频消息 |
video | 视频消息 |
avchat | 音视频通话事件消息 |
notification | 通知消息 |
tip | 提醒消息 |
custom | 自定义消息 |
技术原理
针对不同的内容类型,安全通提供两套内容审核方案:
审核方案 |
说明 |
---|---|
实时(同步)内容审核 | 主要针对文本内容。由云信服务端进行内容提交、过滤和投递,客户端会收到内容审核结果,且云信服务器会同时抄送审核结果给应用服务器。客户端上需根据内容审核结果自行实现相关业务处理。 |
异步内容审核 | 主要针对音频、视频。由云信服务端先行投递消息,违规内容由云信服务端删除。内容审核结果抄送至应用服务器,后者根据审核结果对消息进行处理(如撤回)。客户端不会收到内容审核结果。 |
若您需要云信服务器将审核结果抄送到您的应用服务器,即上图中的流程 6,请先开通易盾异步反垃圾抄送。
实现流程
graph LR
%% 定义样式类
classDef default fill:#337EFF,stroke:#337EFF,stroke-width:1px,color:#FFFFFF;
开通安全通 --> 配置安全通检测规则 --> 实现安全通审核 --> 获取审核结果 --> 处理安全审核结果
步骤1:开通安全通并配置安全通检测规则
- 开通安全通,具体步骤请参见开通能力。
- 在控制台首页选择对应的应用,在左侧导航栏选择 产品功能 > IM 即时通讯,顶部选择安全通页签,单击子功能配置。
-
配置内容安全抄送地址。
接收 IM 内容审核结果的抄送地址与 IM、音视频通话 2.0 的消息抄送地址相同,若您此前从未开通抄送功能、未配置抄送地址,请单击前往配置,配置抄送地址。
如果您已经开通了抄送功能,此处默认使用同一个抄送地址。
-
配置内容安全范围。
单击具体审核类型的配置选择需要的模块。
-
配置内容安全疑似处理规则。
单击具体业务的配置进行过审内容和疑似处理配置。
步骤2:配置关键词名单
您可以配置关键词名单,对文本消息中的敏感词进行检测。
-
在网易云信控制台的IM 安全通配置 页面底部,单击更多安全配置。
-
在易盾智能审核平台左侧导航栏选择策略配置 > 关键词名单,将需要检测的敏感词添加到关键词清单中。
步骤3:实现安全通审核
开通安全通服务后,调用发送消息方法时传入相关参数(NIMAntiSpamOption
)可实现消息内容过安全通审核。消息会根据检测规则进行内容安全检测。
通过 1 对 1 UIKit 实现安全通审核的示例代码如下:
override public func willSend(_ message: NIMMessage) {
super.willSend(message)
// 构造反垃圾对象
let antiSpamOption = NIMAntiSpamOption()
antiSpamOption.yidunEnabled = true
message.antiSpamOption = antiSpamOption
}
步骤4:获取审核结果
匹配消息体命中敏感词后,可通过 IMMessage#getYidunAntiSpamRes
字段通知客户端结果,该字段需要通过 observeMsgStatus
回调获取。
只有被安全通拦截的消息才会有yidunAntiSpamRes
返回。对于疑似消息根据云信控制台设置的策略来判断,如果疑似消息被拦截会有yidunAntiSpamRes返
回,如果疑似消息放行则没有yidunAntiSpamRes
返回。
返回的审核结果 yidunAntiSpamRes
为 JSON 字符串格式,请自行解析或者反转成 JSON 对象使用。yidunAntiSpamRes 字段定义如下:
名称 | 类型 | 说明 |
---|---|---|
code | Integer | 状态码:
|
type | String | 内容审核类型
|
version | String | 易盾内容审核的接口版本 |
taskId | String | 审核任务的 ID |
suggestion | Integer | 建议处理方式
|
status | Integer | 内容审核请求结果
|
ext | String | 内容审核结果,对应易盾的 result 字段,result 字段详情参见易盾文档(注:本链接仅以“单次同步文本检测的 result 字段说明”为例) |
安全通重要参数
参数 | 类型 | 说明 |
---|---|---|
yidunAntiSpamExt | 易盾反垃圾扩展字段 | 透传给易盾的反垃圾增强版的检测参数,具体请参见易盾的反垃圾增强版用户可扩展参数,格式为json,长度限制1024 |
yidunAntiSpamRes | 易盾反垃圾结果 | 易盾反垃圾触发时返回的结果字段,格式为json |
yidunAntiCheating | 易盾反作弊字段 | 透传给易盾的反作弊检测参数,格式为json,长度限制1024 ,具体请参见文本防刷版开发文档 |
示例代码
重写发送完成方法
override public func send(_ message: NIMMessage, didCompleteWithError error: Error?) {
super.send(message, didCompleteWithError: error)
if let yidunAntiSpamExt = message.yidunAntiSpamRes {
// 易盾审核
let jsonData = yidunAntiSpamExt.data(using: .utf8)!
do {
if let dictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
if dictionary.keys.contains("suggestion"), let suggestion = dictionary["suggestion"] as? Int {
if suggestion != 0 {
// 不通过
if let extString = dictionary["ext"] as? String,
let extObjectData = extString.data(using: .utf8),
let extObject = try? JSONSerialization.jsonObject(with: extObjectData, options: []) as? [String: Any],
let antispam = extObject["antispam"] as? [String: Any],
let labels = antispam["labels"] as? [[String: Any]],
let subLabels = labels.first?["subLabels"] as? [[String: Any]],
let details = subLabels.first?["details"] as? [String: Any],
let keywords = details["keywords"] as? [[String: Any]],
let keyword = keywords.first?["word"] as? String {
if keyword.count > 0 {
sendSalutionMsg(type: 3, antiSpamMessage: message)
return
}
}
sendSalutionMsg(type: 4, antiSpamMessage: message)
} else {}
}
}
} catch {
print(error.localizedDescription)
}
}
}
步骤5:处理安全审核结果
当消息体命中敏感词后,在客户端页面展示类似下图的消息通知。
您可以通过 IM 自定义消息 实现该功能,实现方法如下:
// 发送打招呼消息:本地消息
func sendSalutionMsg(type: Int, antiSpamMessage: NIMMessage?) {
let message = NIMMessage()
if let antiSpamMessage = antiSpamMessage {
message.timestamp = antiSpamMessage.timestamp + 1
}
let object = NIMCustomObject()
let attachment = CustomAttachment()
// 本地消息不回走到 CustomAttachment的解析,所以需要手动 cellHeight 以及 customType ,否则不会刷新。发送消息不需要
attachment.cellHeight = 50 + 20
switch type {
case 1:
// audio
attachment.type = OneOnOneChatCustomMessageType.TRY_AUDIO_CALL_MESSAGE_TYPE
attachment.customType = OneOnOneChatCustomMessageType.TRY_AUDIO_CALL_MESSAGE_TYPE
case 2:
// video
attachment.type = OneOnOneChatCustomMessageType.TRY_VIDEO_CALL_MESSAGE_TYPE
attachment.customType = OneOnOneChatCustomMessageType.TRY_VIDEO_CALL_MESSAGE_TYPE
case 3:
// 三方消息违规
attachment.type = OneOnOneChatCustomMessageType.PRIVACY_RISK_MESSAGE_TYPE
attachment.customType = OneOnOneChatCustomMessageType.PRIVACY_RISK_MESSAGE_TYPE
case 4:
// 通用违规消息
attachment.type = OneOnOneChatCustomMessageType.COMMON_RISK_MESSAGE_TYPE
attachment.customType = OneOnOneChatCustomMessageType.COMMON_RISK_MESSAGE_TYPE
default:
attachment.type = OneOnOneChatCustomMessageType.ACCOST_MESSAGE_TIPS_TYPE
attachment.customType = OneOnOneChatCustomMessageType.ACCOST_MESSAGE_TIPS_TYPE
attachment.cellHeight = 30 + 20
}
object.attachment = attachment
message.messageObject = object
let setting = NIMMessageSetting()
setting.shouldBeCounted = false
message.setting = setting
viewmodel.repo.saveMessageToDB(message, viewmodel.session) { error in
print("send custom message error : ", error?.localizedDescription as Any)
}
}
//注:customType 需要有对应的注册:如下所示
registerCellDic[String(OneOnOneChatCustomMessageType.ACCOST_MESSAGE_TIPS_TYPE)] = NEOneOnOneTextSalutuionCell.self
registerCellDic[String(OneOnOneChatCustomMessageType.PRIVACY_RISK_MESSAGE_TYPE)] = NEOneOnOneTextThirdPrivacyCell.self
registerCellDic[String(OneOnOneChatCustomMessageType.TRY_AUDIO_CALL_MESSAGE_TYPE)] = NEOneOnOneAudioSalutuionCell.self
registerCellDic[String(OneOnOneChatCustomMessageType.TRY_VIDEO_CALL_MESSAGE_TYPE)] = NEOneOnOneVideoSalutuionCell.self
registerCellDic[String(OneOnOneChatCustomMessageType.COMMON_RISK_MESSAGE_TYPE)] = NEOneOnOneTextNonComplianceCell.self
registerCellDic[String(SEND_GIFT_TYPE_SEND)] = NEOneOnOneRewardRightCell.self
registerCellDic[String(SEND_GIFT_TYPE_RECV)] = NEOneOnOneRewardLeftCell.self
registerCellDic[String(OneOnOneChatCustomMessageType.OFFICIAL_GIFT_TYPE)] = NEOneOnOneOfficialCell.self
注:上述的cell 都是基于NEChatBaseCell 的自定义UI