members = new ArrayList<>();
+// members.add(randomMemberUsername);
+// String resource = "ios_100000";
+// String status = "1";
+// String ext = "在线";
+//
+// assertDoesNotThrow(() -> this.service.user().create(randomOwnerUsername, randomPassword)
+// .block(Utilities.IT_TIMEOUT));
+// assertDoesNotThrow(() -> this.service.user().create(randomMemberUsername, randomPassword)
+// .block(Utilities.IT_TIMEOUT));
+//
+// String groupId = assertDoesNotThrow(() -> this.service.group()
+// .createPublicGroup(randomOwnerUsername, "group", "group description", members, 200,
+// true).block(Utilities.IT_TIMEOUT));
+//
+// assertDoesNotThrow(() -> this.service.presence().setUserStatus(randomOwnerUsername, resource, status, ext)
+// .block(Utilities.IT_TIMEOUT));
+//
+// assertDoesNotThrow(() -> this.service.presence().setUserStatus(randomMemberUsername, resource, status, ext)
+// .block(Utilities.IT_TIMEOUT));
+//
+// Thread.sleep(3000);
+//
+// Integer onlineCount = assertDoesNotThrow(() -> this.service.presence().getUserOnlineCount(groupId, 1)
+// .block(Utilities.IT_TIMEOUT));
+// assertEquals(2, onlineCount);
+//
+// assertDoesNotThrow(
+// () -> this.service.group().destroyGroup(groupId).block(Utilities.IT_TIMEOUT));
+// assertDoesNotThrow(() -> this.service.user().delete(randomOwnerUsername)
+// .block(Utilities.IT_TIMEOUT));
+// assertDoesNotThrow(() -> this.service.user().delete(randomMemberUsername)
+// .block(Utilities.IT_TIMEOUT));
+ }
+
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/EMService.java b/im-sdk-core/src/main/java/com/easemob/im/server/EMService.java
index a87de67fc..84c084018 100644
--- a/im-sdk-core/src/main/java/com/easemob/im/server/EMService.java
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/EMService.java
@@ -8,6 +8,7 @@
import com.easemob.im.server.api.metadata.MetadataApi;
import com.easemob.im.server.api.moderation.ModerationApi;
import com.easemob.im.server.api.mute.MuteApi;
+import com.easemob.im.server.api.presence.PresenceApi;
import com.easemob.im.server.api.push.PushApi;
import com.easemob.im.server.api.token.TokenApi;
import com.easemob.im.server.api.room.RoomApi;
@@ -50,6 +51,8 @@ public class EMService {
private final MuteApi muteApi;
+ private final PresenceApi presenceApi;
+
public EMService(EMProperties properties) {
log.debug("EMService properties: {}", properties);
this.context = new DefaultContext(properties);
@@ -66,6 +69,7 @@ public EMService(EMProperties properties) {
this.pushApi = new PushApi(this.context);
this.moderationApi = new ModerationApi(this.context);
this.muteApi = new MuteApi(this.context);
+ this.presenceApi = new PresenceApi(this.context);
}
public Context getContext() {
@@ -265,4 +269,20 @@ public ModerationApi moderation() {
public MuteApi mute() {
return this.muteApi;
}
+
+ /**
+ * Presence用户在线状态API.
+ * 支持:
+ * - 设置用户在线状态信息
+ * - 批量订阅在线状态
+ * - 批量获取在线状态信息
+ * - 查询单个群组的在线成员数量
+ * - 取消订阅多个用户的在线状态
+ * - 查询订阅列表
+ *
+ * @return {@code MuteApi}
+ */
+ public PresenceApi presence() {
+ return this.presenceApi;
+ }
}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceApi.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceApi.java
new file mode 100644
index 000000000..978f7dba2
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceApi.java
@@ -0,0 +1,186 @@
+package com.easemob.im.server.api.presence;
+
+import com.easemob.im.server.api.Context;
+import com.easemob.im.server.api.presence.get.UserStatusGet;
+import com.easemob.im.server.api.presence.online.UserOnlineCountGet;
+import com.easemob.im.server.api.presence.set.UserStatusSet;
+import com.easemob.im.server.api.presence.subscribe.PresenceUserStatusSubscribeResult;
+import com.easemob.im.server.api.presence.subscribe.UserStatusSubscribe;
+import com.easemob.im.server.api.presence.subscribe.UserStatusSubscribeListGet;
+import com.easemob.im.server.api.presence.subscribe.UserStatusUnSubscribe;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+/**
+ * Presence 在线状态 API。
+ */
+public class PresenceApi {
+
+ private UserStatusGet userStatusGet;
+
+ private UserStatusSet userStatusSet;
+
+ private UserStatusSubscribe userStatusSubscribe;
+
+ private UserStatusUnSubscribe userStatusUnSubscribe;
+
+ private UserStatusSubscribeListGet userStatusSubscribeListGet;
+
+ private UserOnlineCountGet userOnlineCountGet;
+
+ public PresenceApi(Context context) {
+ this.userStatusGet = new UserStatusGet(context);
+ this.userStatusSet = new UserStatusSet(context);
+ this.userStatusSubscribe = new UserStatusSubscribe(context);
+ this.userStatusUnSubscribe = new UserStatusUnSubscribe(context);
+ this.userStatusSubscribeListGet = new UserStatusSubscribeListGet(context);
+ this.userOnlineCountGet = new UserOnlineCountGet(context);
+ }
+
+ /**
+ * 设置用户在线状态信息。
+ *
+ * API使用示例:
+ *
{@code
+ * EMService service;
+ * try {
+ * service.presence().setUserStatus("username", "resource", "1", "ext").block();
+ * } catch (EMException e) {
+ * e.getErrorCode();
+ * e.getMessage();
+ * }
+ * }
+ *
+ * @param username 设置哪个用户的在线状态信息。
+ * @param resource 要设置用户在哪个设备的在线状态信息,即传入服务器分配给每个设备资源的唯一标识符,格式为 {device type}_{resource ID},其中设备类型 device type 可以是 android、ios 或 web,资源 ID resource ID 由 SDK 分配。例如,android_123423453246。
+ * @param status 用户的在线状态:- 0:离线;- 1:在线;- 其它数字字符串:自定义在线状态。
+ * @param ext 在线状态扩展信息。建议不超过 1024 字节。
+ * @return 成功或错误
+ * @see 设置用户在线状态信息
+ */
+ public Mono setUserStatus(String username, String resource, String status, String ext) {
+ return this.userStatusSet.execute(username, resource, status, ext);
+ }
+
+ /**
+ * 批量获取在线状态信息。
+ *
+ * API使用示例:
+ *
{@code
+ * EMService service;
+ * try {
+ * List usernames = Arrays.asList("test_user1", "test_user2");
+ * List userStatus = service.presence().getUserStatus("operator", usernames).block();
+ * } catch (EMException e) {
+ * e.getErrorCode();
+ * e.getMessage();
+ * }
+ * }
+ *
+ * @param operator 操作者用户名
+ * @param usernames 需要获取其在线状态的用户列表,例如 ["user1", "user2"],最多可传 100 个用户 ID。
+ * @return 成功或错误
+ * @see 批量获取在线状态信息
+ */
+ public Mono> getUserStatus(String operator, List usernames) {
+ return this.userStatusGet.execute(operator, usernames);
+ }
+
+ /**
+ * 批量订阅在线状态。
+ *
+ * API使用示例:
+ *
{@code
+ * EMService service;
+ * try {
+ * List usernames = Arrays.asList("test_user1", "test_user2");
+ * List subscribeUserStatus = service.presence().subscribeUserStatus("operator", usernames, 86400).block();
+ * } catch (EMException e) {
+ * e.getErrorCode();
+ * e.getMessage();
+ * }
+ * }
+ *
+ * @param operator 为哪个用户订阅在线状态。
+ * @param usernames 被订阅用户的用户 ID 数组,例如 ["user1", "user2"]。该数组最多可包含 100 个用户 ID。
+ * @param expiry 订阅时长,单位为秒,最大值为 2,592,000,即 30 天。
+ * @return 成功或错误
+ * @see 批量订阅在线状态
+ */
+ public Mono> subscribeUserStatus(String operator, List usernames, Long expiry) {
+ return this.userStatusSubscribe.execute(operator, usernames, expiry);
+ }
+
+ /**
+ * 取消订阅多个用户的在线状态。
+ *
+ * API使用示例:
+ *
{@code
+ * EMService service;
+ * try {
+ * List usernames = Arrays.asList("test_user1", "test_user2");
+ * service.presence().unsubscribeUserStatus("operator", usernames).block();
+ * } catch (EMException e) {
+ * e.getErrorCode();
+ * e.getMessage();
+ * }
+ * }
+ *
+ * @param operator 为哪个用户取消订阅的在线状态。
+ * @param usernames 要取消订阅在线状态的用户 ID 数组,例如 ["user1", "user2"],最多可传 100 个用户 ID。
+ * @return 成功或错误
+ * @see 取消订阅多个用户的在线状态
+ */
+ public Mono unsubscribeUserStatus(String operator, List usernames) {
+ return this.userStatusUnSubscribe.execute(operator, usernames);
+ }
+
+ /**
+ * 查询订阅列表。
+ *
+ * API使用示例:
+ *
{@code
+ * EMService service;
+ * try {
+ * PresenceUserStatusSubscribeResult result = service.presence().getSubscribeList("operator", 1, 10).block();
+ * } catch (EMException e) {
+ * e.getErrorCode();
+ * e.getMessage();
+ * }
+ * }
+ *
+ * @param operator 查询哪个用户的订阅列表。若传入的用户 ID 不存在或未订阅其他用户的在线状态,返回空列表。
+ * @param pageNum 要查询的页码。该参数的值须大于等于 1。若不传,默认值为 1。
+ * @param pageSize 每页显示的订阅用户数量。取值范围为 [1,500],若不传默认值为 1。
+ * @return 成功或错误
+ * @see 查询订阅列表
+ */
+ public Mono getSubscribeList(String operator, int pageNum, int pageSize) {
+ return this.userStatusSubscribeListGet.execute(operator, pageNum, pageSize);
+ }
+
+ /**
+ * 查询单个群组的在线成员数量。
+ *
+ * API使用示例:
+ *
{@code
+ * EMService service;
+ * try {
+ * Integer onlineCount = service.presence().getUserOnlineCount("1324236456", 1).block();
+ * } catch (EMException e) {
+ * e.getErrorCode();
+ * e.getMessage();
+ * }
+ * }
+ *
+ * @param id 群组 ID。
+ * @param type 查询类型,查询群组的在线成员数量,传 1 即可。
+ * @return 成功或错误
+ * @see 查询单个群组的在线成员数量
+ */
+ public Mono getUserOnlineCount(String id, int type) {
+ return this.userOnlineCountGet.execute(id, type);
+ }
+
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceSubscribeResource.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceSubscribeResource.java
new file mode 100644
index 000000000..f05d7865b
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceSubscribeResource.java
@@ -0,0 +1,28 @@
+package com.easemob.im.server.api.presence;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PresenceSubscribeResource {
+
+ @JsonProperty("uid")
+ private String uid;
+
+ @JsonProperty("expiry")
+ private String expiry;
+
+ @JsonCreator
+ public PresenceSubscribeResource(@JsonProperty("uid") String uid,
+ @JsonProperty("expiry") String expiry) {
+ this.uid = uid;
+ this.expiry = expiry;
+ }
+
+ public String getUid() {
+ return uid;
+ }
+
+ public String getExpiry() {
+ return expiry;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceUserStatusResource.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceUserStatusResource.java
new file mode 100644
index 000000000..01c0d3ee9
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceUserStatusResource.java
@@ -0,0 +1,46 @@
+package com.easemob.im.server.api.presence;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PresenceUserStatusResource {
+
+ @JsonProperty("uid")
+ private String uid;
+
+ @JsonProperty("last_time")
+ private String lastTime;
+
+ @JsonProperty("ext")
+ private String ext;
+
+ @JsonProperty("status")
+ private Object status;
+
+ @JsonCreator
+ public PresenceUserStatusResource(@JsonProperty("uid") String uid,
+ @JsonProperty("last_time") String lastTime,
+ @JsonProperty("ext") String ext,
+ @JsonProperty("status") Object status) {
+ this.uid = uid;
+ this.lastTime = lastTime;
+ this.ext = ext;
+ this.status = status;
+ }
+
+ public String getUid() {
+ return uid;
+ }
+
+ public String getLastTime() {
+ return lastTime;
+ }
+
+ public String getExt() {
+ return ext;
+ }
+
+ public Object getStatus() {
+ return status;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceUserStatusSubscribeResource.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceUserStatusSubscribeResource.java
new file mode 100644
index 000000000..86e29ed26
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/PresenceUserStatusSubscribeResource.java
@@ -0,0 +1,55 @@
+package com.easemob.im.server.api.presence;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PresenceUserStatusSubscribeResource {
+
+ @JsonProperty("uid")
+ private String uid;
+
+ @JsonProperty("last_time")
+ private String lastTime;
+
+ @JsonProperty("ext")
+ private String ext;
+
+ @JsonProperty("expiry")
+ private String expiry;
+
+ @JsonProperty("status")
+ private Object status;
+
+ @JsonCreator
+ public PresenceUserStatusSubscribeResource(@JsonProperty("uid") String uid,
+ @JsonProperty("last_time") String lastTime,
+ @JsonProperty("ext") String ext,
+ @JsonProperty("expiry") String expiry,
+ @JsonProperty("status") Object status) {
+ this.uid = uid;
+ this.lastTime = lastTime;
+ this.ext = ext;
+ this.expiry = expiry;
+ this.status = status;
+ }
+
+ public String getUid() {
+ return uid;
+ }
+
+ public String getLastTime() {
+ return lastTime;
+ }
+
+ public String getExt() {
+ return ext;
+ }
+
+ public String getExpiry() {
+ return expiry;
+ }
+
+ public Object getStatus() {
+ return status;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/PresenceUserStatusGetRequest.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/PresenceUserStatusGetRequest.java
new file mode 100644
index 000000000..ed64a0121
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/PresenceUserStatusGetRequest.java
@@ -0,0 +1,21 @@
+package com.easemob.im.server.api.presence.get;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class PresenceUserStatusGetRequest {
+
+ @JsonProperty("usernames")
+ private List usernames;
+
+ @JsonCreator
+ public PresenceUserStatusGetRequest(@JsonProperty("usernames") List usernames) {
+ this.usernames = usernames;
+ }
+
+ public List getUsernames() {
+ return usernames;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/PresenceUserStatusGetResponse.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/PresenceUserStatusGetResponse.java
new file mode 100644
index 000000000..7d0f0f850
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/PresenceUserStatusGetResponse.java
@@ -0,0 +1,22 @@
+package com.easemob.im.server.api.presence.get;
+
+import com.easemob.im.server.api.presence.PresenceUserStatusResource;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class PresenceUserStatusGetResponse {
+
+ @JsonProperty("result")
+ private List userStatusResources;
+
+ @JsonCreator
+ public PresenceUserStatusGetResponse(@JsonProperty("result") List userStatusResources) {
+ this.userStatusResources = userStatusResources;
+ }
+
+ public List getUserStatusResources() {
+ return userStatusResources;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/UserStatusGet.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/UserStatusGet.java
new file mode 100644
index 000000000..42cc11251
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/get/UserStatusGet.java
@@ -0,0 +1,47 @@
+package com.easemob.im.server.api.presence.get;
+
+import com.easemob.im.server.api.Context;
+import com.easemob.im.server.api.DefaultErrorMapper;
+import com.easemob.im.server.api.ErrorMapper;
+import com.easemob.im.server.api.presence.PresenceUserStatusResource;
+import com.easemob.im.server.api.user.status.UserStatusResponse;
+import com.easemob.im.server.exception.EMUnknownException;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+public class UserStatusGet {
+
+ private Context context;
+
+ public UserStatusGet(Context context) {
+ this.context = context;
+ }
+
+ public Mono> execute(String operator, List usernames) {
+ return this.context.getHttpClient()
+ .flatMap(httpClient -> httpClient
+ .headers(header -> header.add("Content-Type", "application/json"))
+ .post()
+ .uri(String.format("/users/%s/presence", operator))
+ .send(Mono.create(sink -> sink.success(this.context.getCodec().encode(new PresenceUserStatusGetRequest(usernames)))))
+ .responseSingle((rsp, buf) -> {
+ return buf.switchIfEmpty(
+ Mono.error(new EMUnknownException("response is null")))
+ .flatMap(byteBuf -> {
+ ErrorMapper mapper = new DefaultErrorMapper();
+ mapper.statusCode(rsp);
+ mapper.checkError(byteBuf);
+ return Mono.just(byteBuf);
+ });
+ }))
+ .map(buf -> {
+ PresenceUserStatusGetResponse response =
+ this.context.getCodec().decode(buf, PresenceUserStatusGetResponse.class);
+ buf.release();
+ return response;
+ })
+ .map(PresenceUserStatusGetResponse::getUserStatusResources);
+ }
+
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/online/PresenceUserOnlineCountResponse.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/online/PresenceUserOnlineCountResponse.java
new file mode 100644
index 000000000..f2c499818
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/online/PresenceUserOnlineCountResponse.java
@@ -0,0 +1,19 @@
+package com.easemob.im.server.api.presence.online;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PresenceUserOnlineCountResponse {
+
+ @JsonProperty("result")
+ private Integer result;
+
+ @JsonCreator
+ public PresenceUserOnlineCountResponse(@JsonProperty("result") Integer result) {
+ this.result = result;
+ }
+
+ public Integer getResult() {
+ return result;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/online/UserOnlineCountGet.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/online/UserOnlineCountGet.java
new file mode 100644
index 000000000..1c637541a
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/online/UserOnlineCountGet.java
@@ -0,0 +1,41 @@
+package com.easemob.im.server.api.presence.online;
+
+import com.easemob.im.server.api.Context;
+import com.easemob.im.server.api.DefaultErrorMapper;
+import com.easemob.im.server.api.ErrorMapper;
+import com.easemob.im.server.exception.EMUnknownException;
+import reactor.core.publisher.Mono;
+
+public class UserOnlineCountGet {
+
+ private Context context;
+
+ public UserOnlineCountGet(Context context) {
+ this.context = context;
+ }
+
+ public Mono execute(String id, int type) {
+ return this.context.getHttpClient()
+ .flatMap(httpClient -> httpClient
+ .headers(header -> header.add("Content-Type", "application/json"))
+ .get()
+ .uri(String.format("/presence/online/%s/type/%d", id, type))
+ .responseSingle((rsp, buf) -> {
+ return buf.switchIfEmpty(
+ Mono.error(new EMUnknownException("response is null")))
+ .flatMap(byteBuf -> {
+ ErrorMapper mapper = new DefaultErrorMapper();
+ mapper.statusCode(rsp);
+ mapper.checkError(byteBuf);
+ return Mono.just(byteBuf);
+ });
+ }))
+ .map(buf -> {
+ PresenceUserOnlineCountResponse response =
+ this.context.getCodec().decode(buf, PresenceUserOnlineCountResponse.class);
+ buf.release();
+ return response;
+ })
+ .map(PresenceUserOnlineCountResponse::getResult);
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/set/PresenceUserStatusSetRequest.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/set/PresenceUserStatusSetRequest.java
new file mode 100644
index 000000000..2628faca7
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/set/PresenceUserStatusSetRequest.java
@@ -0,0 +1,21 @@
+package com.easemob.im.server.api.presence.set;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class PresenceUserStatusSetRequest {
+
+ @JsonProperty("ext")
+ private String ext;
+
+ @JsonCreator
+ public PresenceUserStatusSetRequest(@JsonProperty("ext") String ext) {
+ this.ext = ext;
+ }
+
+ public String getExt() {
+ return ext;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/set/UserStatusSet.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/set/UserStatusSet.java
new file mode 100644
index 000000000..b87c57346
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/set/UserStatusSet.java
@@ -0,0 +1,46 @@
+package com.easemob.im.server.api.presence.set;
+
+import com.easemob.im.server.api.Context;
+import com.easemob.im.server.api.DefaultErrorMapper;
+import com.easemob.im.server.api.ErrorMapper;
+import com.easemob.im.server.api.presence.PresenceUserStatusResource;
+import com.easemob.im.server.api.presence.get.PresenceUserStatusGetRequest;
+import com.easemob.im.server.api.presence.get.PresenceUserStatusGetResponse;
+import com.easemob.im.server.exception.EMNotFoundException;
+import com.easemob.im.server.exception.EMUnknownException;
+import io.netty.util.ReferenceCounted;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+public class UserStatusSet {
+
+ private Context context;
+
+ public UserStatusSet(Context context) {
+ this.context = context;
+ }
+
+ public Mono execute(String username, String resource, String status, String ext) {
+ return this.context.getHttpClient()
+ .flatMap(httpClient -> httpClient
+ .headers(header -> header.add("Content-Type", "application/json"))
+ .post()
+ .uri(String.format("/users/%s/presence/%s/%s", username, resource, status))
+ .send(Mono.create(sink -> sink.success(this.context.getCodec().encode(new PresenceUserStatusSetRequest(ext)))))
+ .responseSingle((rsp, buf) -> {
+ return buf.switchIfEmpty(
+ Mono.error(new EMUnknownException("response is null")))
+ .flatMap(byteBuf -> {
+ ErrorMapper mapper = new DefaultErrorMapper();
+ mapper.statusCode(rsp);
+ mapper.checkError(byteBuf);
+ return Mono.just(byteBuf);
+ });
+ }))
+ .doOnSuccess(ReferenceCounted::release)
+ .onErrorResume(EMNotFoundException.class, errorIgnored -> Mono.empty())
+ .then();
+ }
+
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeListGetResponse.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeListGetResponse.java
new file mode 100644
index 000000000..b256adef7
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeListGetResponse.java
@@ -0,0 +1,19 @@
+package com.easemob.im.server.api.presence.subscribe;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PresenceUserStatusSubscribeListGetResponse {
+
+ @JsonProperty("result")
+ private PresenceUserStatusSubscribeResult result;
+
+ @JsonCreator
+ public PresenceUserStatusSubscribeListGetResponse(@JsonProperty("result") PresenceUserStatusSubscribeResult result) {
+ this.result = result;
+ }
+
+ public PresenceUserStatusSubscribeResult getResult() {
+ return result;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeRequest.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeRequest.java
new file mode 100644
index 000000000..df229d3cc
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeRequest.java
@@ -0,0 +1,21 @@
+package com.easemob.im.server.api.presence.subscribe;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class PresenceUserStatusSubscribeRequest {
+
+ @JsonProperty("usernames")
+ private List usernames;
+
+ @JsonCreator
+ public PresenceUserStatusSubscribeRequest(@JsonProperty("usernames") List usernames) {
+ this.usernames = usernames;
+ }
+
+ public List getUsernames() {
+ return usernames;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeResponse.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeResponse.java
new file mode 100644
index 000000000..16933ecde
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeResponse.java
@@ -0,0 +1,22 @@
+package com.easemob.im.server.api.presence.subscribe;
+
+import com.easemob.im.server.api.presence.PresenceUserStatusSubscribeResource;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class PresenceUserStatusSubscribeResponse {
+
+ @JsonProperty("result")
+ private List userStatusSubscribeResources;
+
+ @JsonCreator
+ public PresenceUserStatusSubscribeResponse(@JsonProperty("result") List userStatusSubscribeResources) {
+ this.userStatusSubscribeResources = userStatusSubscribeResources;
+ }
+
+ public List getUserStatusSubscribeResources() {
+ return userStatusSubscribeResources;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeResult.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeResult.java
new file mode 100644
index 000000000..f62829bda
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/PresenceUserStatusSubscribeResult.java
@@ -0,0 +1,31 @@
+package com.easemob.im.server.api.presence.subscribe;
+
+import com.easemob.im.server.api.presence.PresenceSubscribeResource;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class PresenceUserStatusSubscribeResult {
+
+ @JsonProperty("totalnum")
+ private String totalNumber;
+
+ @JsonProperty("sublist")
+ private List subscribeResourceList;
+
+ @JsonCreator
+ public PresenceUserStatusSubscribeResult(@JsonProperty("totalnum") String totalNumber,
+ @JsonProperty("sublist") List subscribeResourceList) {
+ this.totalNumber = totalNumber;
+ this.subscribeResourceList = subscribeResourceList;
+ }
+
+ public String getTotalNumber() {
+ return totalNumber;
+ }
+
+ public List getSubscribeResourceList() {
+ return subscribeResourceList;
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusSubscribe.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusSubscribe.java
new file mode 100644
index 000000000..e3202d3a9
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusSubscribe.java
@@ -0,0 +1,45 @@
+package com.easemob.im.server.api.presence.subscribe;
+
+import com.easemob.im.server.api.Context;
+import com.easemob.im.server.api.DefaultErrorMapper;
+import com.easemob.im.server.api.ErrorMapper;
+import com.easemob.im.server.api.presence.PresenceUserStatusSubscribeResource;
+import com.easemob.im.server.exception.EMUnknownException;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+public class UserStatusSubscribe {
+
+ private Context context;
+
+ public UserStatusSubscribe(Context context) {
+ this.context = context;
+ }
+
+ public Mono> execute(String operator, List usernames, Long expiry) {
+ return this.context.getHttpClient()
+ .flatMap(httpClient -> httpClient
+ .headers(header -> header.add("Content-Type", "application/json"))
+ .post()
+ .uri(String.format("/users/%s/presence/%s", operator, expiry))
+ .send(Mono.create(sink -> sink.success(this.context.getCodec().encode(new PresenceUserStatusSubscribeRequest(usernames)))))
+ .responseSingle((rsp, buf) -> {
+ return buf.switchIfEmpty(
+ Mono.error(new EMUnknownException("response is null")))
+ .flatMap(byteBuf -> {
+ ErrorMapper mapper = new DefaultErrorMapper();
+ mapper.statusCode(rsp);
+ mapper.checkError(byteBuf);
+ return Mono.just(byteBuf);
+ });
+ }))
+ .map(buf -> {
+ PresenceUserStatusSubscribeResponse response =
+ this.context.getCodec().decode(buf, PresenceUserStatusSubscribeResponse.class);
+ buf.release();
+ return response;
+ })
+ .map(PresenceUserStatusSubscribeResponse::getUserStatusSubscribeResources);
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusSubscribeListGet.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusSubscribeListGet.java
new file mode 100644
index 000000000..10e94cffc
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusSubscribeListGet.java
@@ -0,0 +1,41 @@
+package com.easemob.im.server.api.presence.subscribe;
+
+import com.easemob.im.server.api.Context;
+import com.easemob.im.server.api.DefaultErrorMapper;
+import com.easemob.im.server.api.ErrorMapper;
+import com.easemob.im.server.exception.EMUnknownException;
+import reactor.core.publisher.Mono;
+
+public class UserStatusSubscribeListGet {
+
+ private Context context;
+
+ public UserStatusSubscribeListGet(Context context) {
+ this.context = context;
+ }
+
+ public Mono execute(String operator, int pageNum, int pageSize) {
+ return this.context.getHttpClient()
+ .flatMap(httpClient -> httpClient
+ .headers(header -> header.add("Content-Type", "application/json"))
+ .get()
+ .uri(String.format("/users/%s/presence/sublist?pageNum=%d&pageSize=%d", operator, pageNum, pageSize))
+ .responseSingle((rsp, buf) -> {
+ return buf.switchIfEmpty(
+ Mono.error(new EMUnknownException("response is null")))
+ .flatMap(byteBuf -> {
+ ErrorMapper mapper = new DefaultErrorMapper();
+ mapper.statusCode(rsp);
+ mapper.checkError(byteBuf);
+ return Mono.just(byteBuf);
+ });
+ }))
+ .map(buf -> {
+ PresenceUserStatusSubscribeListGetResponse response =
+ this.context.getCodec().decode(buf, PresenceUserStatusSubscribeListGetResponse.class);
+ buf.release();
+ return response;
+ })
+ .map(PresenceUserStatusSubscribeListGetResponse::getResult);
+ }
+}
diff --git a/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusUnSubscribe.java b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusUnSubscribe.java
new file mode 100644
index 000000000..2dc4ea9c8
--- /dev/null
+++ b/im-sdk-core/src/main/java/com/easemob/im/server/api/presence/subscribe/UserStatusUnSubscribe.java
@@ -0,0 +1,42 @@
+package com.easemob.im.server.api.presence.subscribe;
+
+import com.easemob.im.server.api.Context;
+import com.easemob.im.server.api.DefaultErrorMapper;
+import com.easemob.im.server.api.ErrorMapper;
+import com.easemob.im.server.exception.EMNotFoundException;
+import com.easemob.im.server.exception.EMUnknownException;
+import io.netty.util.ReferenceCounted;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+public class UserStatusUnSubscribe {
+
+ private Context context;
+
+ public UserStatusUnSubscribe(Context context) {
+ this.context = context;
+ }
+
+ public Mono execute(String operator, List usernames) {
+ return this.context.getHttpClient()
+ .flatMap(httpClient -> httpClient
+ .headers(header -> header.add("Content-Type", "application/json"))
+ .delete()
+ .uri(String.format("/users/%s/presence", operator))
+ .send(Mono.create(sink -> sink.success(this.context.getCodec().encode(usernames))))
+ .responseSingle((rsp, buf) -> {
+ return buf.switchIfEmpty(
+ Mono.error(new EMUnknownException("response is null")))
+ .flatMap(byteBuf -> {
+ ErrorMapper mapper = new DefaultErrorMapper();
+ mapper.statusCode(rsp);
+ mapper.checkError(byteBuf);
+ return Mono.just(byteBuf);
+ });
+ }))
+ .doOnSuccess(ReferenceCounted::release)
+ .onErrorResume(EMNotFoundException.class, errorIgnored -> Mono.empty())
+ .then();
+ }
+}