diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java index 3ac9b9d..0f141a9 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java @@ -86,9 +86,15 @@ public class LiveClientSettings { private HttpClientSettings httpSettings; /** - * Optional: Sometimes not every messages from chat are send to TikTokLiveJava to fix this issue you can set sessionId - * documentation how to obtain sessionId https://github.com/isaackogan/TikTok-Live-Connector#send-chat-messages + * Interval of time in milliseconds between pings to TikTok + * @apiNote Min: 250 (0.25 seconds), Default: 5000 (5 seconds) */ + private long pingInterval = 5000; + + /** + * Optional: Sometimes not every messages from chat are send to TikTokLiveJava to fix this issue you can set sessionId + * @see Documentation: How to obtain sessionId + */ private String sessionId; /** diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java index cde7406..204e9cb 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java @@ -28,7 +28,6 @@ import io.github.jwdeveloper.tiktok.http.LiveHttpClient; import io.github.jwdeveloper.tiktok.live.GiftsManager; import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder; -import java.util.List; import java.util.concurrent.CompletableFuture; public class TikTokLive { @@ -102,14 +101,9 @@ public class TikTokLive { * @return GiftsManager */ public static GiftsManager gifts() { - if (giftsManager != null) { - return giftsManager; - } - synchronized (GiftsManager.class) - { - if (giftsManager == null) - { - return new TikTokGiftsManager(requests().fetchGiftsData().getGifts()); + if (giftsManager == null) { + synchronized (GiftsManager.class) { + giftsManager = new TikTokGiftsManager(requests().fetchGiftsData().getGifts()); } } return giftsManager; diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java index e0e1fe4..d23aad5 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -96,6 +96,9 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { if (clientSettings.getHostName().startsWith("@")) clientSettings.setHostName(clientSettings.getHostName().substring(1)); + if (clientSettings.getPingInterval() < 250) + throw new TikTokLiveException("Minimum allowed ping interval is 250 millseconds"); + var httpSettings = clientSettings.getHttpSettings(); httpSettings.getParams().put("app_language", clientSettings.getClientLanguage()); httpSettings.getParams().put("webcast_language", clientSettings.getClientLanguage()); diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java index eadde29..0d1098d 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -83,7 +83,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient .toJsonResponse(); if (result.isFailure()) - throw new TikTokLiveRequestException("Unable to fetch gifts information's"+result.toStack()); + throw new TikTokLiveRequestException("Unable to fetch gifts information's - "+result); var json = result.getContent(); return giftsDataMapper.map(json); @@ -111,10 +111,10 @@ public class TikTokLiveHttpClient implements LiveHttpClient .toJsonResponse(); if (result.isFailure()) - throw new TikTokLiveRequestException("Unable to get information's about user"+result.toStack()); + throw new TikTokLiveRequestException("Unable to get information's about user - "+result); var json = result.getContent(); - return liveUserDataMapper.map(json); + return liveUserDataMapper.map(json, logger); } @Override @@ -138,7 +138,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient .toJsonResponse(); if (result.isFailure()) - throw new TikTokLiveRequestException("Unable to get info about live room"+result.toStack()); + throw new TikTokLiveRequestException("Unable to get info about live room - "+result); var json = result.getContent(); return liveDataMapper.map(json); @@ -153,7 +153,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient var resultHeader = ActionResult.of(credentialsResponse.headers().firstValue("x-set-tt-cookie")); if (resultHeader.isFailure()) { logger.warning("SignServer Headers: "+request.getRoomId()+" - "+credentialsResponse.headers().map()); - throw new TikTokSignServerException("Sign server did not return the x-set-tt-cookie header"+result.toStack()); + throw new TikTokSignServerException("Sign server did not return the x-set-tt-cookie header - "+result); } var websocketCookie = resultHeader.getContent(); var webcastResponse = WebcastResponse.parseFrom(credentialsResponse.body()); @@ -169,7 +169,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient return new LiveConnectionData.Response(websocketCookie, webSocketUrl, webcastResponse); } catch (InvalidProtocolBufferException e) { - throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse"+result.toStack()); + throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse - "+result); } } @@ -197,7 +197,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient var result = builder.build().toResponse(); if (result.isFailure()) - throw new TikTokSignServerException("Unable to get websocket connection credentials"+result.toStack()); + throw new TikTokSignServerException("Unable to get websocket connection credentials - "+result); return result; } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java index 34ab7b3..ac8cd60 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java @@ -1,6 +1,8 @@ package io.github.jwdeveloper.tiktok.common; +import com.google.gson.*; import lombok.Data; +import lombok.experimental.Accessors; import java.util.Optional; import java.util.function.Function; @@ -11,6 +13,8 @@ public class ActionResult { private boolean success = true; private T content; private String message; + @Accessors(chain = true, fluent = true) + private ActionResult previous; protected ActionResult(T object) { this.content = object; @@ -41,8 +45,9 @@ public class ActionResult { public boolean hasMessage() { return message != null; } - public String toStack() { - return hasMessage() ? " - "+message : ""; + + public boolean hasPrevious() { + return previous != null; } public boolean hasContent() { @@ -84,4 +89,18 @@ public class ActionResult { public static ActionResult failure() { return failure(null); } + + public JsonObject toJson() { + JsonObject map = new JsonObject(); + map.addProperty("success", success); + map.add("content", new Gson().toJsonTree(content)); + map.addProperty("message", message); + map.add("previous", hasPrevious() ? previous.toJson() : null); + return map; + } + + @Override + public String toString() { + return "ActionResult: "+new Gson().newBuilder().setPrettyPrinting().create().toJson(toJson()); + } } \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResultBuilder.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResultBuilder.java index 369d35f..f9383d9 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResultBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResultBuilder.java @@ -1,5 +1,8 @@ package io.github.jwdeveloper.tiktok.common; +import lombok.Setter; +import lombok.experimental.Accessors; + import java.util.Arrays; import java.util.stream.Collectors; @@ -7,6 +10,8 @@ public class ActionResultBuilder { private final T content; private String message; + @Setter @Accessors(fluent = true, chain = true) + private ActionResult previous; public ActionResultBuilder(T content) { this.content = content; @@ -18,10 +23,10 @@ public class ActionResultBuilder } public ActionResult success() { - return ActionResult.success(content, message); + return ActionResult.success(content, message).previous(previous); } public ActionResult failure() { - return ActionResult.success(content, message); + return ActionResult.success(content, message).previous(previous); } } \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveUserDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveUserDataMapper.java index 6dd2aed..9b9cd4b 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveUserDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveUserDataMapper.java @@ -22,45 +22,52 @@ */ package io.github.jwdeveloper.tiktok.http.mappers; -import com.google.gson.JsonParser; +import com.google.gson.*; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException; +import java.util.logging.Logger; + public class LiveUserDataMapper { - public LiveUserData.Response map(String json) { - var jsonObject = JsonParser.parseString(json).getAsJsonObject(); + public LiveUserData.Response map(String json, Logger logger) { + try { + var jsonObject = JsonParser.parseString(json).getAsJsonObject(); - var message = jsonObject.get("message").getAsString(); + var message = jsonObject.get("message").getAsString(); - if (message.equals("params_error")) { - throw new TikTokLiveRequestException("fetchRoomIdFromTiktokApi -> Unable to fetch roomID, contact the developer"); - } - if (message.equals("user_not_found")) { + if (message.equals("params_error")) { + throw new TikTokLiveRequestException("fetchRoomIdFromTiktokApi -> Unable to fetch roomID, contact the developer"); + } + if (message.equals("user_not_found")) { + return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1); + } + //live -> status 2 + //live paused -> 3 + //not live -> status 4 + var element = jsonObject.get("data"); + if (element.isJsonNull()) { + return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1); + } + var data = element.getAsJsonObject(); + var user = data.getAsJsonObject("user"); + var roomId = user.get("roomId").getAsString(); + var status = user.get("status").getAsInt(); + + var liveRoom = data.getAsJsonObject("liveRoom"); + long startTime = liveRoom.get("startTime").getAsLong(); + + var statusEnum = switch (status) { + case 2 -> LiveUserData.UserStatus.Live; + case 3 -> LiveUserData.UserStatus.LivePaused; + case 4 -> LiveUserData.UserStatus.Offline; + default -> LiveUserData.UserStatus.NotFound; + }; + + return new LiveUserData.Response(json, statusEnum, roomId, startTime); + } catch (JsonSyntaxException e) { + logger.warning("Malformed Json: '"+json+"' - Error Message: "+e.getMessage()); return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1); } - //live -> status 2 - //live paused -> 3 - //not live -> status 4 - var element = jsonObject.get("data"); - if (element.isJsonNull()) { - return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1); - } - var data = element.getAsJsonObject(); - var user = data.getAsJsonObject("user"); - var roomId = user.get("roomId").getAsString(); - var status = user.get("status").getAsInt(); - - var liveRoom = data.getAsJsonObject("liveRoom"); - long startTime = liveRoom.get("startTime").getAsLong(); - - var statusEnum = switch (status) { - case 2 -> LiveUserData.UserStatus.Live; - case 3 -> LiveUserData.UserStatus.LivePaused; - case 4 -> LiveUserData.UserStatus.Offline; - default -> LiveUserData.UserStatus.NotFound; - }; - - return new LiveUserData.Response(json, statusEnum, roomId, startTime); } } \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java index d3f0f19..d1c8a62 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java @@ -82,7 +82,7 @@ public class TikTokWebSocketClient implements SocketClient { private void connectDefault() { try { webSocketClient.connect(); - pingingTask.run(webSocketClient); + pingingTask.run(webSocketClient, clientSettings.getPingInterval()); isConnected = true; } catch (Exception e) { isConnected = false; @@ -112,7 +112,7 @@ public class TikTokWebSocketClient implements SocketClient { proxySettings.remove(); continue; } - pingingTask.run(webSocketClient); + pingingTask.run(webSocketClient, clientSettings.getPingInterval()); isConnected = true; break; } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketPingingTask.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketPingingTask.java index 90adbc4..e44987b 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketPingingTask.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketPingingTask.java @@ -8,13 +8,13 @@ public class TikTokWebSocketPingingTask { private Thread thread; private boolean isRunning = false; - private final int MIN_TIMEOUT = 250; - private final int MAX_TIMEOUT = 500; + private final int MAX_TIMEOUT = 250; + private final int SLEEP_TIME = 500; - public void run(WebSocket webSocket) + public void run(WebSocket webSocket, long pingTaskTime) { stop(); - thread = new Thread(() -> pingTask(webSocket)); + thread = new Thread(() -> pingTask(webSocket, pingTaskTime)); isRunning = true; thread.start(); } @@ -26,20 +26,18 @@ public class TikTokWebSocketPingingTask isRunning = false; } - - private void pingTask(WebSocket webSocket) + private void pingTask(WebSocket webSocket, long pingTaskTime) { var random = new Random(); while (isRunning) { try { if (!webSocket.isOpen()) { - Thread.sleep(100); + Thread.sleep(SLEEP_TIME); continue; } webSocket.sendPing(); - var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT; - Thread.sleep(timeout); + Thread.sleep(pingTaskTime+random.nextInt(MAX_TIMEOUT)); } catch (Exception e) { isRunning = false; diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/CollectorExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/CollectorExample.java index 9972bc7..aed2c8d 100644 --- a/Examples/src/main/java/io/github/jwdeveloper/tiktok/CollectorExample.java +++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/CollectorExample.java @@ -27,8 +27,7 @@ import io.github.jwdeveloper.tiktok.extension.collector.TikTokLiveCollector; import java.io.File; import java.io.IOException; -import java.util.List; -import java.util.Map; +import java.util.*; public class CollectorExample { @@ -60,4 +59,4 @@ public class CollectorExample { System.in.read(); collector.disconnect(); } -} +} \ No newline at end of file diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/mongo/MongoDataCollectorSettings.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/mongo/MongoDataCollectorSettings.java index 3d319ab..bca2564 100644 --- a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/mongo/MongoDataCollectorSettings.java +++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/mongo/MongoDataCollectorSettings.java @@ -29,7 +29,6 @@ import java.util.function.Consumer; @Data public class MongoDataCollectorSettings { - @Setter private String connectionUrl; private String databaseName = "tiktok";