# 消息推送
# 主动消息推送
在 4.1 版本中,为了增强企业的主动营销能力,加入给 iOS 和 Android 用户主动推送消息的服务端接口。服务端推送说明见 消息推送。
推送消息到达访客端,体现出来的,其实就是一条带扩展字段的普通消息,用户进入客服界面后,也会在消息流中正常展示出来。如果 App 也需要处理这条消息,可以添加对这一事件的监听。接口使用示例如下:
private void addPushMessageListener() {
if (qiyuPushMessageListener == null) {
qiyuPushMessageListener = new OnPushMessageListener() {
@Override
public void onReceive(IMMessage message, PushMessageExtension extension) {
qiyuPushMessage = message;
pushMessageExtension = extension;
showPushMessageIfResumed();
}
};
Unicorn.addPushMessageListener(qiyuPushMessageListener);
}
}
# 省电策略(V5.7.7 以上版本已删除)
七鱼 SDK 作为用户端的 IM 工具,需要能够及时收到客服回复的消息。同时,就算用户没有主动咨询过客服,客服也可以主动发起会话,进行主动营销。因此,在 2.0 版本之前,SDK一旦初始化之后,会一直保持长连接,定时与服务器交换心跳。
但是,毕竟用户咨询以及客服主动营销都是不太常用的操作,为此保持一个长连接,性价比不高。同时,我们了解到,很多客户的 APP 本身就自带了推送模块,或者集成了第三方的推送 SDK,由此就存在了两条长连接,浪费了用户的电量和流量。因此,在2.0版本中,我们针对 SDK 的长连接策略做了更改,以优化在用户没有发起咨询时的电量消耗。如果开发者需要开启省电特性,需要在 YSFOptions
中设置 savePowerConfig
。从版本3.1.0开始,该选项默认开启,并使用默认间隔。
在咨询客服时,SDK 会保持长连接以便及时收到推送。当一次会话结束后,用户极有可能还会继续发起咨询会话,同时客服也很有可能会主动发起会话,因此,一次会话结束后,长连接可以在继续保持一段时间(SavePowerConfig.activeDelay
),以便及时收到消息,同时也可以减少重新连接造成的消耗。如果在这段时间内,再也没有开始过会话,则在计时结束后即可转入省电模式。根据用户配置,省电模式分为两种状态:
- 推送模式:用户需要在七鱼的管理后台配置推送服务器,且
SavePowerConfig.customPush
设置为 true。此时,如果有新消息,会自动转到后台配置的推送通道上。由于推送不受 SDK 控制,因此收到推送后 SDK 不会自动建立长连接,需要用户进入咨询界面后,才会建立长连接。开启自定义推送后,还需要设置推送的设备 ID:SavePowerConfig.deviceIdentifier
。在七鱼推送的消息结构体中,会包含该字段。 - 轮询模式:SDK 每隔
SavePowerConfig.checkInterval
秒去服务器检测一次有没有新消息。如果有新消息,会自动建立长连接,并收取新消息,弹出通知。
# 配置推送服务器地址
客服发送消息给用户,而用户此时已经转入推送模式后,消息将被推送给开发者的服务器端,然后再由开发者推送到 APP 端。
要配置推送服务器,请使用管理员帐号登录七鱼管理后台,在「设置」 -> 「APP设置」 -> 「添加/编辑APP」中设置。
开发者服务器收到推送请求后,应对立即返回一个 空字符串 告诉七鱼服务器。七鱼服务器发出 POST 请求后,如果在 10 秒内收不到响应,或者收到的响应非空,会断掉连接,并且重新发起请求,总共重试 2 次。 如果连续 10 次都推送失败,该推送服务器 url 将被暂停推送 1 个小时。
# 推送消息数据结构
当有消息需要推送时,七鱼服务器会向开发者设置的服务器地址发送推送消息,方法类型为 POST,数据格式为 JSON 。
POST 请求url中会包含以下参数:
参数 | 参数说明 |
---|---|
nonce | 随机数字符串 |
time | 当前 UTC 时间戳,从 1970 年 1 月 1 日 0 点 0 分 0 秒开始到现在的毫秒数 |
checksum | SHA1(AppSecret + nonce + time), 三个参数拼接的字符串,进行SHA1哈希计算,转化成16进制字符(String,小写) |
其中,checksum 可用于校验该请求是否由七鱼服务器发出,出于安全性考虑,还应根据 time 参数校验 checksum 的有效期,超过一定时间(比如 5 分钟)的请求也应判定为非法。请确认发起请求的服务器是与标准时间同步的,比如有NTP服务。
重要提示: 本文档中提供的所有接口均面向开发者服务器端调用,用于计算CheckSum的AppSecret是添加App时生成的secret key,开发者应妥善保管,可在应用的服务器端存储和使用,但不应存储或传递到客户端,也不应在网页等前端代码中嵌入。
计算 checksum 的 java 示例代码如下:
public class QiyuPushCheckSum {
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static String encode(String appSecret, String nonce, String time) {
String content = appSecret + nonce + time;
try {
MessageDigest messageDigest = MessageDigest.getInstance("sha1");
messageDigest.update(content.getBytes());
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String getFormattedText(byte[] bytes) {
int len = bytes.length;
StringBuilder buf = new StringBuilder(len * 2);
for (int j = 0; j < len; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
}
POST 请求的内容为 JSON 格式,编码格式为 UTF-8,其各个字段说明如下:
字段 | 字段说明 |
---|---|
content | 推送的消息内容 |
time | 消息的发出时间 |
messageId | 消息的唯一 ID,当消息有重发时,开发者的推送服务器可依据此去重。 |
staffName | 说话的客服昵称 |
deviceIdentifier | 消息发送对象用户的 deviceIdentifier |
package | 消息接收对象 APP 的 package name |
请求实体示例如下:
{
"content":"hello world",
"time":1592903619000,
"messageId":"44568613556234662#566ds3651s13e89",
"staffName":"客服小宁",
"deviceIdentifier":"13f568551d966564s45e4587",
"package":"com.kaixinggongfang.zaome"
}
# 新消息提醒
当用户不处在聊天界面时,收到客服的消息,App 应当在通知栏或者聊天入口给出提醒。
通知栏提醒可以显示最近一条消息的内容,并提供给用户快速进入 App 的入口。要打开通知栏提醒功能,只需给 YSFOptions
的 statusBarNotificationConfig
域赋予非 null 值即可。同时,通过定制该域的各配置项,还能实现提醒开关,免打扰等功能。
要实现点击通知栏提醒直接跳转到会话窗口的功能,需要设置 StatusBarNotificationConfig
的 notificationEntrance
(不可配置 SDK 内部 Activity ,例如 ServiceMessageActivity),并在对应的 Activity 里添加处理。还需要设置StatusBarNotificationConfig
的 notificationExtraType
属性为 NotificationExtraTypeEnum.MESSAGE。如果没有设置 notificationEntrance
,则是在 AndroidManifest
中设置的入口 Activity 中处理。示例代码如下:
Intent intent = getIntent();
if (intent.hasExtra(NimIntent.EXTRA_NOTIFY_CONTENT)) {
// 打开客服窗口
Unicorn.openServiceActivity(context, title, source);
// 最好将intent清掉,以免从堆栈恢复时又打开客服窗口
setIntent(new Intent());
}
在聊天入口的地方,App 可以给出是否有未读消息,以及未读数的提示。App 可以通过添加以下监听来跟踪未读数变化,更新界面,反馈给用户:
// 添加未读数变化监听,add 为 true 是添加,为 false 是撤销监听。
// 退出界面时,必须撤销,以免造成资源泄露
private UnreadCountChangeListener listener = new UnreadCountChangeListener() { // 声明一个成员变量
@Override
public void onUnreadCountChange(int count) {
// 在此更新界面, count 为当前未读数,
// 也可以用 Unicorn.getUnreadCount() 获取总的未读数
}
}
private void addUnreadCountChangeListener(boolean add) {
Unicorn.addUnreadCountChangeListener(listener, add);
}
默认情况下,只有访客在聊天界面时,才不会有通知栏提醒,其他界面以及 App 在后台时,都会有消息提醒。如果当 App 在前台时,不需要通知栏提醒新消息,可以调用Unicorn.toggleNotification(false)
关闭消息提醒,然后在 App 退到后台时,调用Unicorn.toggleNotification(true)
重新打开。
用户收到新消息后(通过未读数变化监听接口可获知此事件),如果开发者需要显示新收到的消息,可通过下面接口获取最近一条消息:
/**
* 获取和客服的最后一条聊天消息内容。
* 可用于未读消息变化时,展示最后一条未读消息,或者展示客服的最后一条消息。
* @return 最后一条消息
* 在 <= 5.7.7 版本为 UnicornMessage
*/
IMMessage message = Unicorn.queryLastMessage();
在 V3.2 版本中增加了该接口的平台商家企业版本,需要传入商家ID,用于平台商家企业调用。
/**
* 获取和客服的最后一条聊天消息内容。
* 可用于未读消息变化时,展示最后一条未读消息,或者展示客服的最后一条消息。
*
* @param shopId 商家ID
* @return 最后一条消息
* 在 > V5.7.7 版本中 POPManager.queryLastMessage(String shopId) 获取结果类型为 UnicornMessage
*/
UnicornMessage message = POPManager.queryLastMessage(String shopId);