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 0767b8a..dfd7a2f 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 @@ -8,9 +8,7 @@ import java.util.*; @Data @AllArgsConstructor -public class Gift -{ - @Getter private static final Set gifts = new HashSet<>(); +public class Gift { public static final Gift UNDEFINED = new Gift(-1, "undefined", -1, "", null); private final int id; @@ -31,6 +29,22 @@ public class Gift this.properties = properties; } + public Gift(int id, String name, int diamondCost, String pictureLink) { + this.id = id; + this.name = name; + this.diamondCost = diamondCost; + this.picture = new Picture(pictureLink); + this.properties = new JsonObject(); + } + + public Gift(int id, String name, int diamondCost, Picture picture) { + this.id = id; + this.name = name; + this.diamondCost = diamondCost; + this.picture = picture; + this.properties = new JsonObject(); + } + public boolean hasDiamondCostRange(int minimalCost, int maximalCost) { return diamondCost >= minimalCost && diamondCost <= maximalCost; } diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/GiftsData.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/GiftsData.java index e6d98b8..a024c30 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/GiftsData.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/GiftsData.java @@ -22,6 +22,7 @@ */ package io.github.jwdeveloper.tiktok.data.requests; +import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; @@ -41,16 +42,7 @@ public class GiftsData public static final class Response { private String json; - private List gifts; - } - - @Data - public static class GiftModel - { - private int id; - private String name; - private int diamondCost; - private String image; + private List gifts; } } diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java b/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java index 7646bf7..2fcdd09 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java @@ -34,6 +34,7 @@ public interface LiveHttpClient */ GiftsData.Response fetchGiftsData(); + /** * Returns information about user that is having a livestream * @param userName name of user diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/GiftManager.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/GiftsManager.java similarity index 58% rename from API/src/main/java/io/github/jwdeveloper/tiktok/live/GiftManager.java rename to API/src/main/java/io/github/jwdeveloper/tiktok/live/GiftsManager.java index 9eab743..08c1b55 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/GiftManager.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/GiftsManager.java @@ -25,43 +25,62 @@ package io.github.jwdeveloper.tiktok.live; import com.google.gson.JsonObject; import io.github.jwdeveloper.tiktok.data.models.Picture; import io.github.jwdeveloper.tiktok.data.models.gifts.*; +import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.function.Predicate; -public interface GiftManager { +public interface GiftsManager { /** - * In case you can't find your gift in Gift enum. You can register gift - * manually here to make it detected while TikTokGiftEvent + * You can create and attach your own custom gift to manager * - * @param id gift's id - * @param name gift's name - * @param diamondCost diamond cost - * @return + * @param gift */ - default Gift registerGift(int id, String name, int diamondCost, Picture picture) { - return registerGift(id, name, diamondCost, picture, null); - } - - Gift registerGift(int id, String name, int diamondCost, Picture picture, JsonObject properties); + void attachGift(Gift gift); /** + * You can create and attach your own custom gift to manager * - * @param giftId - * @return + * @param gifts */ - Gift findById(int giftId); + void attachGiftsList(List gifts); /** + * finds gift by name + * When gift not found return Gift.UNDEFINED; * - * @param giftName - * @return + * @param name gift name */ - Gift findByName(String giftName); + Gift getByName(String name); /** + * finds gift by id + * When gift not found return Gift.UNDEFINED; * - * @return all gifts + * @param giftId giftId */ - List getGifts(); + Gift getById(int giftId); + + + /** + * finds gift by filter + * When gift not found return Gift.UNDEFINED; + */ + Gift getByFilter(Predicate filter); + + List getManyByFilter(Predicate filter); + + /** + * @return list of all gifts + */ + List toList(); + + + /** + * @return list of all map of all gifts where Integer is gift Id + */ + Map toMap(); } \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java index 7aa4634..3161079 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java @@ -64,7 +64,7 @@ public interface LiveClient { /** * Get information about gifts */ - GiftManager getGiftManager(); + GiftsManager getGiftManager(); /** * Gets the current room info from TikTok API including streamer info, room status and statistics. 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 3c2f1e0..d26e1db 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java @@ -23,15 +23,19 @@ package io.github.jwdeveloper.tiktok; +import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager; 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 { /** * Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo' + * * @param hostName profile name of Tiktok user could be found in profile link * @return LiveClientBuilder */ @@ -41,42 +45,42 @@ public class TikTokLive { /** * Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo' + * * @param hostName profile name of Tiktok user could be found in profile link * @return true if live is Online, false if is offline */ - public static boolean isLiveOnline(String hostName) - { + public static boolean isLiveOnline(String hostName) { return requests().fetchLiveUserData(hostName).isLiveOnline(); } /** * Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo' + * * @param hostName profile name of Tiktok user could be found in profile link * @return true if live is Online, false if is offline */ - public static CompletableFuture isLiveOnlineAsync(String hostName) - { - return CompletableFuture.supplyAsync(()-> isLiveOnline(hostName)); + public static CompletableFuture isLiveOnlineAsync(String hostName) { + return CompletableFuture.supplyAsync(() -> isLiveOnline(hostName)); } /** * Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo' + * * @param hostName profile name of Tiktok user could be found in profile link * @return true is hostName name is valid and exists, false if not */ - public static boolean isHostNameValid(String hostName) - { + public static boolean isHostNameValid(String hostName) { return requests().fetchLiveUserData(hostName).isHostNameValid(); } /** * Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo' + * * @param hostName profile name of Tiktok user could be found in profile link * @return true is hostName name is valid and exists, false if not */ - public static CompletableFuture isHostNameValidAsync(String hostName) - { - return CompletableFuture.supplyAsync(()-> isHostNameValid(hostName)); + public static CompletableFuture isHostNameValidAsync(String hostName) { + return CompletableFuture.supplyAsync(() -> isHostNameValid(hostName)); } /** @@ -87,4 +91,26 @@ public class TikTokLive { public static LiveHttpClient requests() { return new TikTokLiveHttpClient(); } + + + /** + * Fetch gifts from endpoint and returns GiftManager + * + * @return GiftsManager + */ + public static GiftsManager gifts() + { + return new TikTokGiftsManager(requests().fetchGiftsData().getGifts()); + } + + /** + * @param fetchGifts fetch gifts from internet or return empty giftManager + * @return + */ + public static GiftsManager gifts(boolean fetchGifts) { + if (fetchGifts) { + return gifts(); + } + return new TikTokGiftsManager(List.of()); + } } \ No newline at end of file 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 807fd79..9826653 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -33,10 +33,9 @@ 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.exceptions.*; -import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.listener.ListenersManager; import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager; -import io.github.jwdeveloper.tiktok.live.GiftManager; +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.models.ConnectionState; @@ -49,24 +48,24 @@ import java.util.logging.Logger; public class TikTokLiveClient implements LiveClient { private final TikTokRoomInfo liveRoomInfo; - private final TikTokGiftManager tikTokGiftManager; private final TikTokLiveHttpClient httpClient; private final SocketClient webSocketClient; private final TikTokLiveEventHandler tikTokEventHandler; private final LiveClientSettings clientSettings; private final TikTokListenersManager listenersManager; private final Logger logger; + private final GiftsManager giftsManager; - public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta, + public TikTokLiveClient(GiftsManager giftsManager, + TikTokRoomInfo tikTokLiveMeta, TikTokLiveHttpClient tiktokHttpClient, SocketClient webSocketClient, - TikTokGiftManager tikTokGiftManager, TikTokLiveEventHandler tikTokEventHandler, LiveClientSettings clientSettings, TikTokListenersManager listenersManager, Logger logger) { + this.giftsManager = giftsManager; this.liveRoomInfo = tikTokLiveMeta; - this.tikTokGiftManager = tikTokGiftManager; this.httpClient = tiktokHttpClient; this.webSocketClient = webSocketClient; this.tikTokEventHandler = tikTokEventHandler; @@ -102,7 +101,8 @@ public class TikTokLiveClient implements LiveClient { if (e instanceof TikTokLiveOfflineHostException && clientSettings.isRetryOnConnectionFailure()) { try { Thread.sleep(clientSettings.getRetryConnectionTimeout().toMillis()); - } catch (Exception ignored) {} + } catch (Exception ignored) { + } logger.info("Reconnecting"); tikTokEventHandler.publish(this, new TikTokReconnectingEvent()); this.connect(); @@ -121,29 +121,29 @@ public class TikTokLiveClient implements LiveClient { } setState(ConnectionState.CONNECTING); - tikTokEventHandler.publish(this,new TikTokConnectingEvent()); + tikTokEventHandler.publish(this, new TikTokConnectingEvent()); var userDataRequest = new LiveUserData.Request(liveRoomInfo.getHostName()); var userData = httpClient.fetchLiveUserData(userDataRequest); liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp()); liveRoomInfo.setRoomId(userData.getRoomId()); if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) - throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostName()); + throw new TikTokLiveOfflineHostException("User is offline: " + liveRoomInfo.getHostName()); if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) - throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostName()); + throw new TikTokLiveOfflineHostException("User not found: " + liveRoomInfo.getHostName()); var liveDataRequest = new LiveData.Request(userData.getRoomId()); var liveData = httpClient.fetchLiveData(liveDataRequest); if (liveData.isAgeRestricted()) - throw new TikTokLiveException("Livestream for "+liveRoomInfo.getHostName()+" is 18+ or age restricted!"); + throw new TikTokLiveException("Livestream for " + liveRoomInfo.getHostName() + " is 18+ or age restricted!"); if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) - throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" could not be found."); + throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " could not be found."); if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) - throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" not found, is the Host offline?"); + throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " not found, is the Host offline?"); tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData)); @@ -158,7 +158,7 @@ public class TikTokLiveClient implements LiveClient { if (preconnectEvent.isCancelConnection()) throw new TikTokLiveException("TikTokPreConnectionEvent cancelled connection!"); - var liveConnectionRequest =new LiveConnectionData.Request(userData.getRoomId()); + var liveConnectionRequest = new LiveConnectionData.Request(userData.getRoomId()); var liveConnectionData = httpClient.fetchLiveConnectionData(liveConnectionRequest); webSocketClient.start(liveConnectionData, this); @@ -183,6 +183,11 @@ public class TikTokLiveClient implements LiveClient { tikTokEventHandler.publish(this, event); } + @Override + public GiftsManager getGiftManager() { + return giftsManager; + } + public LiveRoomInfo getRoomInfo() { return liveRoomInfo; } @@ -196,9 +201,4 @@ public class TikTokLiveClient implements LiveClient { public Logger getLogger() { return logger; } - - @Override - public GiftManager getGiftManager() { - return tikTokGiftManager; - } } \ 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 e63f45e..62cd1f1 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -35,7 +35,7 @@ import io.github.jwdeveloper.tiktok.data.events.social.*; import io.github.jwdeveloper.tiktok.data.events.websocket.*; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; -import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; +import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager; import io.github.jwdeveloper.tiktok.http.HttpClientFactory; import io.github.jwdeveloper.tiktok.listener.*; import io.github.jwdeveloper.tiktok.live.*; @@ -64,7 +64,8 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { this.clientSettings.setHostName(userName); this.tikTokEventHandler = new TikTokLiveEventHandler(); this.listeners = new ArrayList<>(); - this.onCustomMappings = (e) -> {}; + this.onCustomMappings = (e) -> { + }; } public LiveClientBuilder onMapping(Consumer onCustomMappings) { @@ -108,33 +109,33 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler); - var giftManager = new TikTokGiftManager(logger); - var eventsMapper = createMapper(giftManager, tiktokRoomInfo); - var messageHandler = new TikTokLiveMessageHandler(tikTokEventHandler, eventsMapper); - - var httpClientFactory = new HttpClientFactory(clientSettings); var tikTokLiveHttpClient = new TikTokLiveHttpClient(httpClientFactory, clientSettings); + var gifts = tikTokLiveHttpClient.getGiftsData().getGifts(); + var giftsManager = new TikTokGiftsManager(gifts); + + var eventsMapper = createMapper(giftsManager, tiktokRoomInfo); + var messageHandler = new TikTokLiveMessageHandler(tikTokEventHandler, eventsMapper); + + var webSocketClient = new TikTokWebSocketClient( clientSettings, messageHandler, tikTokEventHandler); - return new TikTokLiveClient(tiktokRoomInfo, + return new TikTokLiveClient( + giftsManager, + tiktokRoomInfo, tikTokLiveHttpClient, webSocketClient, - giftManager, tikTokEventHandler, clientSettings, listenerManager, logger); } - public TikTokLiveMapper createMapper(GiftManager giftManager, TikTokRoomInfo roomInfo) { - /* - // - */ + public TikTokLiveMapper createMapper(GiftsManager giftsManager, TikTokRoomInfo roomInfo) { var eventMapper = new TikTokGenericEventMapper(); @@ -142,7 +143,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { //ConnectionEvents events var commonHandler = new TikTokCommonEventHandler(); - var giftHandler = new TikTokGiftEventHandler(giftManager, roomInfo); + var giftHandler = new TikTokGiftEventHandler(giftsManager, roomInfo); var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo); var socialHandler = new TikTokSocialMediaEventHandler(roomInfo); 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 7f22e04..eadde29 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -42,6 +42,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch"; private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/"; private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/"; + public static final String TIKTOK_GIFTS_URL = "https://raw.githubusercontent.com/TikTok-LIVE-Private/GiftsGenerator/master/page/public/gifts.json"; public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110; private final HttpClientFactory httpFactory; @@ -77,8 +78,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient } public GiftsData.Response getGiftsData() { - var url = TIKTOK_URL_WEBCAST + "gift/list/"; - var result = httpFactory.client(url) + var result = httpFactory.client(TIKTOK_GIFTS_URL) .build() .toJsonResponse(); diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftManager.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftManager.java deleted file mode 100644 index 34dd6af..0000000 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftManager.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.github.jwdeveloper.tiktok.gifts; - -import com.google.gson.JsonObject; -import io.github.jwdeveloper.tiktok.data.models.Picture; -import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; -import io.github.jwdeveloper.tiktok.live.GiftManager; - -import java.util.*; -import java.util.logging.Logger; - -public class TikTokGiftManager implements GiftManager { - - private final Map indexById; - private final Map indexByName; - private final Logger logger; - - public TikTokGiftManager(Logger logger) - { - indexById = new HashMap<>(); - indexByName = new HashMap<>(); - this.logger = logger; - init(); - } - - protected void init() { - for (var gift : Gift.getGifts()) { - indexById.put(gift.getId(), gift); - indexByName.put(gift.getName(), gift); - } - } - - public Gift registerGift(int id, String name, int diamondCost, Picture picture, JsonObject properties) { - Gift gift = new Gift(id, name, diamondCost, picture, properties); - indexById.put(gift.getId(), gift); - indexByName.put(gift.getName(), gift); - return gift; - } - - public Gift findById(int giftId) { - return indexById.getOrDefault(giftId, Gift.UNDEFINED); - } - - public Gift findByName(String giftName) { - return indexByName.getOrDefault(giftName, Gift.UNDEFINED); - } - - @Override - public List getGifts() { - return indexById.values().stream().toList(); - } -} \ No newline at end of file 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 new file mode 100644 index 0000000..9d2a54f --- /dev/null +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java @@ -0,0 +1,64 @@ +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.function.Predicate; +import java.util.stream.Collectors; + +public class TikTokGiftsManager implements GiftsManager { + private final Map giftsByIdIndex; + + public TikTokGiftsManager(List giftList) { + giftsByIdIndex = giftList.stream().collect(Collectors.toConcurrentMap(Gift::getId, e -> e)); + } + + public void attachGift(Gift gift) { + giftsByIdIndex.put(gift.getId(), gift); + } + + public void attachGiftsList(List gifts) { + gifts.forEach(this::attachGift); + } + + public Gift getByName(String name) { + return getByFilter(e -> e.getName().equalsIgnoreCase(name)); + } + + public Gift getById(int giftId) { + if (!giftsByIdIndex.containsKey(giftId)) { + return Gift.UNDEFINED; + } + + return giftsByIdIndex.get(giftId); + } + + public Gift getByFilter(Predicate filter) { + return giftsByIdIndex.values() + .stream() + .filter(filter) + .findFirst() + .orElseGet(() -> Gift.UNDEFINED); + } + + @Override + public List getManyByFilter(Predicate filter) { + return giftsByIdIndex.values() + .stream() + .filter(filter) + .toList(); + } + + public List toList() { + return giftsByIdIndex.values().stream().toList(); + } + + public Map toMap() { + return Collections.unmodifiableMap(giftsByIdIndex); + } +} diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java index 91f2b90..5518eb1 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java @@ -21,51 +21,34 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package io.github.jwdeveloper.tiktok.http.mappers; + import com.google.gson.JsonElement; import com.google.gson.JsonParser; +import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; import io.github.jwdeveloper.tiktok.data.requests.GiftsData; + import java.util.ArrayList; public class GiftsDataMapper { public GiftsData.Response map(String json) { var parsedJson = JsonParser.parseString(json); var jsonObject = parsedJson.getAsJsonObject(); - - if (!jsonObject.has("data")) { - return new GiftsData.Response(json, new ArrayList<>()); - } - var dataElement = jsonObject.getAsJsonObject("data"); - if (!dataElement.has("gifts")) { - return new GiftsData.Response(json, new ArrayList<>()); - } - - var gifts = dataElement.get("gifts").getAsJsonArray() - .asList() - .stream() - .map(this::mapSingleGift) + var gifts = jsonObject.entrySet() + .parallelStream() + .map(e -> mapSingleGift(e.getValue())) .toList(); return new GiftsData.Response(json, gifts); } - private GiftsData.GiftModel mapSingleGift(JsonElement jsonElement) { - var id = jsonElement.getAsJsonObject().get("id").getAsInt(); - var name = jsonElement.getAsJsonObject().get("name").getAsString(); - var diamondCost = jsonElement.getAsJsonObject().get("diamond_count").getAsInt(); - var image = jsonElement.getAsJsonObject() - .get("image").getAsJsonObject() - .get("url_list").getAsJsonArray().get(0).getAsString(); + private Gift mapSingleGift(JsonElement jsonElement) { + var jsonObject = jsonElement.getAsJsonObject(); - if (image.endsWith(".webp")) { - image = image.replace(".webp", ".jpg"); - } - var gift = new GiftsData.GiftModel(); - gift.setId(id); - gift.setName(name); - gift.setDiamondCost(diamondCost); - gift.setImage(image); - - return gift; + var id = jsonObject.get("id").getAsInt(); + var name = jsonObject.get("name").getAsString(); + var diamondCost = jsonObject.get("diamondCost").getAsInt(); + var image =jsonObject.get("image").getAsString(); + return new Gift(id, name, diamondCost, image, jsonObject); } } 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 b552b29..899e13f 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 @@ -28,7 +28,7 @@ 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.GiftManager; +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; @@ -38,14 +38,15 @@ import sun.misc.Unsafe; import java.util.*; public class TikTokGiftEventHandler { - private final GiftManager giftManager; private final Map giftsMessages; private final TikTokRoomInfo tikTokRoomInfo; - public TikTokGiftEventHandler(GiftManager giftManager, TikTokRoomInfo tikTokRoomInfo) { - this.giftManager = giftManager; + private final GiftsManager giftsManager; + + public TikTokGiftEventHandler(GiftsManager giftsManager, TikTokRoomInfo tikTokRoomInfo) { giftsMessages = new HashMap<>(); this.tikTokRoomInfo = tikTokRoomInfo; + this.giftsManager = giftsManager; } @SneakyThrows @@ -110,23 +111,39 @@ public class TikTokGiftEventHandler { private Gift getGiftObject(WebcastGiftMessage giftMessage) { var giftId = (int) giftMessage.getGiftId(); - var gift = giftManager.findById(giftId); + var gift = giftsManager.getById(giftId); if (gift == Gift.UNDEFINED) - gift = giftManager.findByName(giftMessage.getGift().getName()); + gift = giftsManager.getByName(giftMessage.getGift().getName()); if (gift == Gift.UNDEFINED) { - gift = giftManager.registerGift( - giftId, - giftMessage.getGift().getName(), - giftMessage.getGift().getDiamondCount(), - Picture.map(giftMessage.getGift().getImage())); + gift = new Gift(giftId, + giftMessage.getGift().getName(), + giftMessage.getGift().getDiamondCount(), + Picture.map(giftMessage.getGift().getImage())); + + giftsManager.attachGift(gift); } if (gift.getPicture().getLink().endsWith(".webp")) + { updatePicture(gift, giftMessage); + } + 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()); diff --git a/Client/src/test/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftManagerTest.java b/Client/src/test/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftManagerTest.java index b1543d0..25d0081 100644 --- a/Client/src/test/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftManagerTest.java +++ b/Client/src/test/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftManagerTest.java @@ -35,38 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension; public class TikTokGiftManagerTest { - @InjectMocks - TikTokGiftManager giftManager; - - private static final Picture rosePicture = new Picture("https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png"); - - @Test - void registerGift() { - var fakeGift = giftManager.registerGift(123, "Fake gift", 123123, rosePicture); - var gifts = giftManager.getGifts(); - var optional = gifts.stream().filter(r -> r == fakeGift).findFirst(); - Assertions.assertTrue(optional.isPresent()); -// Assertions.assertNotNull(optional.get().name()); - } - - @Test - void findById() { - var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture); - var result = giftManager.findById(target.getId()); - Assertions.assertEquals(target, result); - } - - @Test - void findByName() { - var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture); - var result = giftManager.findByName(target.getName()); - Assertions.assertEquals(target, result); - } - - @Test - void getGifts() { - Assertions.assertEquals(GiftOld.values().length, giftManager.getGifts().size()); - } diff --git a/Client/src/test/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandlerTest.java b/Client/src/test/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandlerTest.java index cb119da..b77ca91 100644 --- a/Client/src/test/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandlerTest.java +++ b/Client/src/test/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandlerTest.java @@ -26,8 +26,9 @@ import io.github.jwdeveloper.tiktok.TikTokRoomInfo; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent; import io.github.jwdeveloper.tiktok.data.models.Picture; +import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType; -import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; +import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager; import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokGiftEventHandler; import io.github.jwdeveloper.tiktok.messages.data.GiftStruct; import io.github.jwdeveloper.tiktok.messages.data.Image; @@ -38,6 +39,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import java.util.List; import java.util.logging.Logger; @@ -46,13 +48,12 @@ class TikTokGiftEventHandlerTest { public static TikTokGiftEventHandler handler; - @BeforeAll public void before() { - var manager = new TikTokGiftManager(Logger.getLogger("x")); + var manager = new TikTokGiftsManager(List.of()); var info = new TikTokRoomInfo(); info.setHost(new io.github.jwdeveloper.tiktok.data.models.users.User(123L, "test", new Picture(""))); - manager.registerGift(123, "example", 123, new Picture("image.webp")); + manager.attachGift(new Gift(123, "example", 123, "image.webp")); handler = new TikTokGiftEventHandler(manager, info); } diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/CustomGiftExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/CustomGiftExample.java deleted file mode 100644 index 499b6dc..0000000 --- a/Examples/src/main/java/io/github/jwdeveloper/tiktok/CustomGiftExample.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.github.jwdeveloper.tiktok; - -import io.github.jwdeveloper.tiktok.data.models.Picture; -import io.github.jwdeveloper.tiktok.live.GiftManager; -import io.github.jwdeveloper.tiktok.live.LiveClient; -import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage; - -public class CustomGiftExample { - /** - * If you can't find your wanted Gift inside Gift enum register it manually - */ - - - public static void main(String[] args) { - LiveClient client = TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME) - .onConnected((liveClient, event) -> - { - liveClient.disconnect(); - }) - .onWebsocketResponse((liveClient, event) -> - { - var packets =event.getResponse().getMessagesList(); - for(var packet : packets) - { - var name = packet.getMethod(); - var data = packet.getPayload(); - if(name.equals("WebcastGiftMessage")) - { - // var message = WebcastGiftMessage.parseFrom(data); - - } - } - }) - .onGift((liveClient, event) -> - { - liveClient.getLogger().info(event.getGift().getName()); - }).build(); - - GiftManager giftManager = client.getGiftManager(); - - //If you can't find your wanted Gift inside Gift enum register it manually - giftManager.registerGift(123, "my custom gift", 69, new Picture("https://as2.ftcdn.net/v2/jpg/03/03/62/45/1000_F_303624505_u0bFT1Rnoj8CMUSs8wMCwoKlnWlh5Jiq.jpg")); - - - //You can also override existing gift, for example Rose has Id 5655 - //We can make our custom gift appear in the event instead of rose - giftManager.registerGift(5655, "custom-rose", 999, new Picture("https://as2.ftcdn.net/v2/jpg/03/03/62/45/1000_F_303624505_u0bFT1Rnoj8CMUSs8wMCwoKlnWlh5Jiq.jpg")); - - client.connect(); - } -} diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/GiftsExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/GiftsExample.java new file mode 100644 index 0000000..732f436 --- /dev/null +++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/GiftsExample.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package io.github.jwdeveloper.tiktok; + +import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; + +public class GiftsExample { + + public static void main(String[] args) { + var giftsManager = TikTokLive.gifts(); + + var giftsList = giftsManager.toList(); + for (var gift : giftsList) { + System.out.println("Gift: " + gift); + } + + var giftsMap = giftsManager.toMap(); + for (var entry : giftsMap.entrySet()) { + System.out.println("GiftId: " + entry.getKey() + " Gift: " + entry.getValue()); + } + + System.out.println("total number of gifts: " + giftsManager.toList().size()); + + var giftRose = giftsManager.getById(5655); + var giftRoseByName = giftsManager.getByName("Rose"); + var giftByFilter = giftsManager.getByFilter(e -> e.getDiamondCost() > 50); + + var giftsByFilter = giftsManager.getManyByFilter(e -> e.getDiamondCost() > 100); + System.out.println("total number of gifts with cost higher then 100: " + giftsByFilter.size()); + /** + * In case searched gift not exists getByName returns you Gift.UNDEFINED + */ + var undefiedGift = giftsManager.getByName("GIFT WITH WRONG NAME"); + + + var customGift = new Gift(123213213, "Custom gift", 50, "https://images.pexels.com/photos/2071882/pexels-photo-2071882.jpeg?cs=srgb&dl=pexels-wojciech-kumpicki-2071882.jpg&fm=jpg"); + giftsManager.attachGift(customGift); + + + } +} diff --git a/Examples/target/classes/io/github/jwdeveloper/tiktok/ListenerExample$1.class b/Examples/target/classes/io/github/jwdeveloper/tiktok/ListenerExample$1.class deleted file mode 100644 index 0bc82c0..0000000 Binary files a/Examples/target/classes/io/github/jwdeveloper/tiktok/ListenerExample$1.class and /dev/null differ diff --git a/Examples/target/classes/io/github/jwdeveloper/tiktok/SimpleExample$1.class b/Examples/target/classes/io/github/jwdeveloper/tiktok/SimpleExample$1.class deleted file mode 100644 index 3562c02..0000000 Binary files a/Examples/target/classes/io/github/jwdeveloper/tiktok/SimpleExample$1.class and /dev/null differ diff --git a/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/TikTokMockBuilder.java b/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/TikTokMockBuilder.java index 087aea2..ba214eb 100644 --- a/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/TikTokMockBuilder.java +++ b/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/TikTokMockBuilder.java @@ -25,9 +25,9 @@ package io.github.jwdeveloper.tiktok.tools.tester.mockClient; import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder; import io.github.jwdeveloper.tiktok.TikTokRoomInfo; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; -import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler; import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient; +import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager; import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.LiveClientMock; @@ -87,15 +87,13 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder { tiktokRoomInfo.setHostName(clientSettings.getHostName()); var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler); - var giftManager = new TikTokGiftManager(logger); - var mapper = createMapper(giftManager, tiktokRoomInfo); + var mapper = createMapper(new TikTokGiftsManager(List.of()), tiktokRoomInfo); var handler = new TikTokLiveMessageHandler(tikTokEventHandler, mapper); var webSocketClient = new WebsocketClientMock(logger, responses, handler); return new LiveClientMock(tiktokRoomInfo, new TikTokLiveHttpClient(), webSocketClient, - giftManager, tikTokEventHandler, clientSettings, listenerManager, diff --git a/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/mocks/LiveClientMock.java b/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/mocks/LiveClientMock.java index 12ff4c1..3077316 100644 --- a/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/mocks/LiveClientMock.java +++ b/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/tester/mockClient/mocks/LiveClientMock.java @@ -25,12 +25,13 @@ package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.TikTokLiveClient; import io.github.jwdeveloper.tiktok.TikTokRoomInfo; -import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler; import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient; +import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager; import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; +import java.util.List; import java.util.logging.Logger; public class LiveClientMock extends TikTokLiveClient { @@ -41,16 +42,15 @@ public class LiveClientMock extends TikTokLiveClient { TikTokRoomInfo tikTokLiveMeta, TikTokLiveHttpClient httpClient, WebsocketClientMock webSocketClient, - TikTokGiftManager tikTokGiftManager, TikTokLiveEventHandler tikTokEventHandler, LiveClientSettings clientSettings, TikTokListenersManager listenersManager, Logger logger) { super( + new TikTokGiftsManager(List.of()), tikTokLiveMeta, httpClient, webSocketClient, - tikTokGiftManager, tikTokEventHandler, clientSettings, listenersManager,