From 301df6392d13ab0aab09deeb39319da35613097f Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Fri, 19 Apr 2024 13:41:07 -0400 Subject: [PATCH 1/4] More updated to TikTokLinkMicBattleEvent, updated Gift since its no longer enum we do not need to use Unsafe, and added default mappings for WebcastLinkMicBattle and WebcastLinkMicArmies --- .../data/events/TikTokLinkMicBattleEvent.java | 14 +++++++- .../tiktok/data/models/gifts/Gift.java | 6 ++-- .../github/jwdeveloper/tiktok/TikTokLive.java | 3 -- .../tiktok/TikTokLiveClientBuilder.java | 12 +++++-- .../tiktok/gifts/TikTokGiftsManager.java | 16 +++------- .../handlers/TikTokGiftEventHandler.java | 32 +------------------ 6 files changed, 29 insertions(+), 54 deletions(-) diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java index 1b55357b..adcea270 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java @@ -27,7 +27,7 @@ import io.github.jwdeveloper.tiktok.data.models.battles.*; import io.github.jwdeveloper.tiktok.messages.enums.LinkMicBattleStatus; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMicBattle; -import lombok.Getter; +import lombok.*; import java.util.*; @@ -43,6 +43,8 @@ public class TikTokLinkMicBattleEvent extends TikTokHeaderEvent true if battle is finished otherwise false */ private final boolean finished; + @Getter(AccessLevel.NONE) + private final boolean oneVsOne; private final List teams; public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) { @@ -53,6 +55,7 @@ public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) { if (msg.getHostTeamCount() == 2) { // 1v1 battle teams.add(new Team1v1(msg.getHostTeam(0), msg)); teams.add(new Team1v1(msg.getHostTeam(1), msg)); + oneVsOne = true; } else { // 2v2 battle if (isFinished()) { teams.add(new Team2v2(msg.getHostData2V2List().stream().filter(data -> data.getTeamNumber() == 1).findFirst().orElse(null), msg)); @@ -61,6 +64,7 @@ public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) { teams.add(new Team2v2(msg.getHostTeam(0), msg.getHostTeam(1), msg)); teams.add(new Team2v2(msg.getHostTeam(2), msg.getHostTeam(3), msg)); } + oneVsOne = false; } // Info: @@ -68,4 +72,12 @@ public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) { // - msg.getDetailsCount() & msg.getViewerTeamCount() always is 2 only when battle is finished // - msg.getHostTeamCount() always is 2 for 1v1 or 4 for 2v2 } + + public boolean is1v1() { + return oneVsOne; + } + + public boolean is2v2() { + return !oneVsOne; + } } \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/gifts/Gift.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/gifts/Gift.java index 6217fe95..95f8b91f 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/gifts/Gift.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/gifts/Gift.java @@ -2,9 +2,7 @@ import com.google.gson.JsonObject; import io.github.jwdeveloper.tiktok.data.models.Picture; -import lombok.*; - -import java.util.*; +import lombok.Data; @Data public class Gift { @@ -16,7 +14,7 @@ public class Gift { private final int diamondCost; - private final Picture picture; + private Picture picture; private final JsonObject properties; 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 204e9cbc..02f81dff 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java @@ -91,7 +91,6 @@ public static LiveHttpClient requests() { return new TikTokLiveHttpClient(); } - //I don't like it, but it is reasonable for now private static GiftsManager giftsManager; @@ -108,6 +107,4 @@ public static GiftsManager gifts() { } return giftsManager; } - - } \ No newline at end of file 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 d23aad5e..41ae03a3 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -207,9 +207,15 @@ public TikTokLiveMapper createMapper(GiftsManager giftsManager, TikTokRoomInfo r //LinkMic events - // mapper.webcastObjectToConstructor(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class); - // mapper.webcastObjectToConstructor(WebcastLinkMicArmies.class, TikTokLinkMicArmiesEvent.class); - // mapper.webcastObjectToConstructor(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class); + mapper.forMessage(WebcastLinkMicBattle.class, (inputBytes, messageName, mapperHelper) -> { + var message = mapperHelper.bytesToWebcastObject(inputBytes, WebcastLinkMicBattle.class); + return MappingResult.of(message, new TikTokLinkMicBattleEvent(message)); + }); + mapper.forMessage(WebcastLinkMicArmies.class, (inputBytes, messageName, mapperHelper) -> { + var message = mapperHelper.bytesToWebcastObject(inputBytes, WebcastLinkMicArmies.class); + return MappingResult.of(message, new TikTokLinkMicArmiesEvent(message)); + }); + // mapper.webcastObjectToConstructor(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class); // mapper.webcastObjectToConstructor(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class); //Rank events diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java index 763b710e..60e3b767 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java @@ -1,13 +1,9 @@ package io.github.jwdeveloper.tiktok.gifts; import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.live.GiftsManager; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; +import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -32,11 +28,7 @@ public Gift getByName(String name) { } public Gift getById(int giftId) { - if (!giftsByIdIndex.containsKey(giftId)) { - return Gift.UNDEFINED; - } - - return giftsByIdIndex.get(giftId); + return giftsByIdIndex.getOrDefault(giftId, Gift.UNDEFINED); } public Gift getByFilter(Predicate filter) { @@ -44,7 +36,7 @@ public Gift getByFilter(Predicate filter) { .stream() .filter(filter) .findFirst() - .orElseGet(() -> Gift.UNDEFINED); + .orElse(Gift.UNDEFINED); } @Override @@ -62,4 +54,4 @@ public List toList() { public Map toMap() { return Collections.unmodifiableMap(giftsByIdIndex); } -} +} \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokGiftEventHandler.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokGiftEventHandler.java index 0547994e..0cda6abe 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokGiftEventHandler.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokGiftEventHandler.java @@ -27,13 +27,11 @@ import io.github.jwdeveloper.tiktok.data.events.gift.*; import io.github.jwdeveloper.tiktok.data.models.Picture; import io.github.jwdeveloper.tiktok.data.models.gifts.*; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.live.GiftsManager; import io.github.jwdeveloper.tiktok.mappers.TikTokMapperHelper; import io.github.jwdeveloper.tiktok.mappers.data.MappingResult; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage; import lombok.SneakyThrows; -import sun.misc.Unsafe; import java.util.*; @@ -124,36 +122,8 @@ private Gift getGiftObject(WebcastGiftMessage giftMessage) { } if (gift.getPicture().getLink().endsWith(".webp")) - { - updatePicture(gift, giftMessage); - } + gift.setPicture(Picture.map(giftMessage.getGift().getImage())); return gift; } - - // TODO-kohlerpop1: I do not think this method is needed for any reason? - // TODO response: - - /** - * Some generated gifts in JSON file contains .webp image format, - * that's bad since java by the defult is not supporing .webp and when URL is - * converted to Java.io.Image then image is null - * - * However, TikTok in GiftWebcast event always has image in .jpg format, - * so I take advantage of it and swap .webp url with .jpg url - * - */ - - private void updatePicture(Gift gift, WebcastGiftMessage webcastGiftMessage) { - try { - var picture = Picture.map(webcastGiftMessage.getGift().getImage()); - var constructor = Unsafe.class.getDeclaredConstructors()[0]; - constructor.setAccessible(true); - var field = Gift.class.getDeclaredField("picture"); - field.setAccessible(true); - field.set(gift, picture); - } catch (Exception e) { - throw new TikTokLiveException("Unable to update picture in gift: " + gift.toString()); - } - } } \ No newline at end of file From 9c96c8899a27011cbdfc0e82421551851514b0c8 Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Sat, 4 May 2024 15:21:34 -0400 Subject: [PATCH 2/4] Added option to use File Locking as through testing, some events occur simultaneously causing the file to become overlapped or corrupted. --- .../settings/FileDataCollectorSettings.java | 5 ++++ .../collector/impl/storages/FileStorage.java | 29 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/FileDataCollectorSettings.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/FileDataCollectorSettings.java index 0eee68f7..13dec3ab 100644 --- a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/FileDataCollectorSettings.java +++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/settings/FileDataCollectorSettings.java @@ -25,9 +25,14 @@ import lombok.Data; import java.io.File; +import java.util.function.*; @Data public class FileDataCollectorSettings { private File parentFile; + private BiPredicate typeFilter = (dataType, dataTypeName) -> true; + private Predicate userFilter = (tiktokUser) -> true; + private boolean useFileLocks = false; + private boolean appendUserName = false; } \ No newline at end of file diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java index 08ffcaa5..05625ec1 100644 --- a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java +++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java @@ -5,17 +5,20 @@ import org.bson.Document; import org.bson.json.JsonWriterSettings; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardOpenOption; +import java.io.*; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.*; public class FileStorage implements Storage { private final FileDataCollectorSettings settings; + private final Map locks; public FileStorage(FileDataCollectorSettings fileDataCollectorSettings) { this.settings = fileDataCollectorSettings; + this.locks = settings.isUseFileLocks() ? new ConcurrentHashMap<>() : null; } @Override @@ -30,13 +33,25 @@ public void disconnect() { @Override public void insert(Document document) { - var fileName = document.get("dataType") + "_" + document.get("dataTypeName") + ".json"; + if (settings.getTypeFilter().test(document.getString("dataType"), document.getString("dataTypeName")) && settings.getUserFilter().test(document.getString("tiktokUser"))) { + var fileName = document.get("dataType") + "_" + document.get("dataTypeName") + (settings.isAppendUserName() ? document.getString("tiktokUser") : "") + ".json"; + if (settings.isUseFileLocks()) { + var lock = locks.computeIfAbsent(fileName, s -> new ReentrantLock()); + lock.lock(); + save(document, fileName); + lock.unlock(); + } else + save(document, fileName); + } + } + + private void save(Document document, String fileName) { try { var file = new File(settings.getParentFile(), fileName); file.createNewFile(); - Files.writeString(file.toPath(), document.toJson(JsonWriterSettings.builder().indent(true).build()), StandardOpenOption.APPEND); + Files.writeString(file.toPath(), document.toJson(JsonWriterSettings.builder().indent(true).build())+'\n', StandardOpenOption.APPEND); } catch (IOException e) { e.printStackTrace(); } } -} +} \ No newline at end of file From d09c90ef54114354b2abf5a55cef71ca9f6730ca Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Fri, 10 May 2024 16:44:42 -0400 Subject: [PATCH 3/4] Added append live username option to FileStorage and now support connection to 18+ age restricted! --- .../data/settings/LiveClientSettings.java | 3 +++ .../jwdeveloper/tiktok/TikTokLiveClient.java | 19 ++++++------------- .../tiktok/http/mappers/LiveDataMapper.java | 3 +-- .../collector/impl/storages/FileStorage.java | 2 +- 4 files changed, 11 insertions(+), 16 deletions(-) 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 0f141a96..03a3a696 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 @@ -91,6 +91,9 @@ public class LiveClientSettings { */ private long pingInterval = 5000; + /** Throw an exception on 18+ Age Restriction */ + private boolean throwOnAgeRestriction; + /** * 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 diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java index 6cde52fd..cfbb449e 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -23,26 +23,19 @@ package io.github.jwdeveloper.tiktok; import com.google.protobuf.ByteString; -import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent; -import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent; -import io.github.jwdeveloper.tiktok.data.events.TikTokReconnectingEvent; +import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.data.events.control.*; import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent; import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent; -import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData; -import io.github.jwdeveloper.tiktok.data.requests.LiveData; -import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; +import io.github.jwdeveloper.tiktok.data.requests.*; +import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.exceptions.*; import io.github.jwdeveloper.tiktok.http.LiveHttpClient; -import io.github.jwdeveloper.tiktok.listener.ListenersManager; -import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager; -import io.github.jwdeveloper.tiktok.live.GiftsManager; -import io.github.jwdeveloper.tiktok.live.LiveClient; -import io.github.jwdeveloper.tiktok.live.LiveRoomInfo; +import io.github.jwdeveloper.tiktok.listener.*; +import io.github.jwdeveloper.tiktok.live.*; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; import io.github.jwdeveloper.tiktok.models.ConnectionState; -import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.websocket.SocketClient; import java.util.Base64; @@ -144,7 +137,7 @@ public void tryConnect() { var liveDataRequest = new LiveData.Request(userData.getRoomId()); var liveData = httpClient.fetchLiveData(liveDataRequest); - if (liveData.isAgeRestricted()) + if (liveData.isAgeRestricted() && clientSettings.isThrowOnAgeRestriction()) throw new TikTokLiveException("Livestream for " + liveRoomInfo.getHostName() + " is 18+ or age restricted!"); if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java index 4eb05ace..02bae22b 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java @@ -65,8 +65,7 @@ public LiveData.Response map(String json) { default -> LiveData.LiveStatus.HostNotFound; }; response.setLiveStatus(statusValue); - } else if (data.has("prompts") && jsonObject.has("status_code") && - data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) { + } else if (data.has("prompts") && jsonObject.has("status_code") && data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) { response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == TikTokLiveHttpClient.TIKTOK_AGE_RESTRICTED_CODE); } else { response.setLiveStatus(LiveData.LiveStatus.HostNotFound); diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java index 05625ec1..e7e2f8a4 100644 --- a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java +++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/storages/FileStorage.java @@ -34,7 +34,7 @@ public void disconnect() { @Override public void insert(Document document) { if (settings.getTypeFilter().test(document.getString("dataType"), document.getString("dataTypeName")) && settings.getUserFilter().test(document.getString("tiktokUser"))) { - var fileName = document.get("dataType") + "_" + document.get("dataTypeName") + (settings.isAppendUserName() ? document.getString("tiktokUser") : "") + ".json"; + var fileName = document.get("dataType") + "_" + document.get("dataTypeName") + (settings.isAppendUserName() ? "_"+document.getString("tiktokUser") : "") + ".json"; if (settings.isUseFileLocks()) { var lock = locks.computeIfAbsent(fileName, s -> new ReentrantLock()); lock.lock(); From 4297af1349b4b1b167994c860460559ca084deea Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Sun, 12 May 2024 20:47:13 -0400 Subject: [PATCH 4/4] Simplify LiveDataMapper#map --- .../github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java index 02bae22b..4006e9cf 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java @@ -65,7 +65,7 @@ public LiveData.Response map(String json) { default -> LiveData.LiveStatus.HostNotFound; }; response.setLiveStatus(statusValue); - } else if (data.has("prompts") && jsonObject.has("status_code") && data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) { + } else if (data.has("prompts") && data.get("prompts").getAsString().isEmpty() && jsonObject.has("status_code")) { response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == TikTokLiveHttpClient.TIKTOK_AGE_RESTRICTED_CODE); } else { response.setLiveStatus(LiveData.LiveStatus.HostNotFound);