From 1977cbe8dc0d54cc82e7d8f324c6cc93cfb82b63 Mon Sep 17 00:00:00 2001 From: JW Date: Tue, 14 Nov 2023 00:07:14 +0100 Subject: [PATCH] Changes: New Events - onLiveUnpaused() - onRoomInfo() triggered when LiveRoomInfo got updated Removed: - clientSettings.setHandleExistingEvents - onRoom Replaced with onRoomInfo event - onRoomUserInfo Replaced with onRoomInfo event Gifts: - onGift event was not triggered for the more expensive gifts - onGiftCombo with more expensive gifts was stuck in the GiftSendType.Begin state Fixed: - setPrintToConsole(false) was not disabling logs --- .../jwdeveloper/tiktok/ClientSettings.java | 5 - .../github/jwdeveloper/tiktok/Constants.java | 1 - .../data/events/TikTokLiveUnpausedEvent.java | 10 + .../data/events/room/TikTokRoomEvent.java | 67 -- .../data/events/room/TikTokRoomInfoEvent.java | 20 + .../events/room/TikTokRoomUserInfoEvent.java | 55 -- .../tiktok/data/models/users/User.java | 19 + .../jwdeveloper/tiktok/live/LiveRoomInfo.java | 17 + .../jwdeveloper/tiktok/live/LiveRoomMeta.java | 6 + .../tiktok/live/builder/EventsBuilder.java | 13 +- API/src/main/proto/enums.proto | 1 + .../jwdeveloper/tiktok/TikTokLiveClient.java | 17 +- .../tiktok/TikTokLiveClientBuilder.java | 37 +- .../jwdeveloper/tiktok/TikTokRoomInfo.java | 33 +- .../TikTokMessageHandlerRegistration.java | 88 +-- .../events/TikTokGiftEventHandler.java | 18 +- .../events/TikTokRoomInfoEventHandler.java | 57 ++ .../events/TikTokSocialMediaEventHandler.java | 49 ++ .../tiktok/http/TikTokHttpClient.java | 1 + .../tiktok/mappers/LiveRoomMetaMapper.java | 68 +- .../websocket/TikTokWebSocketClient.java | 8 +- .../events/TikTokGiftEventHandlerTest.java | 43 +- .../jwdeveloper/tiktok/SimpleExample.java | 7 +- .../jwdeveloper/tiktok/SimpleExample$1.class | Bin 879 -> 879 bytes README.md | 683 +++++++++--------- .../tiktok/tools/collector/RunCollector.java | 23 +- .../tiktok/tools/collector/RunCollector.class | Bin 4606 -> 5540 bytes .../tiktok/webviewer/TikTokManager.java | 9 +- .../jwdeveloper/tiktok/CodeExample.java | 8 +- .../src/main/resources/output.md | 681 +++++++++++++++++ .../tiktok/mockClient/TikTokMockBuilder.java | 7 +- .../tiktok/mockClient/TikTokMockBuilder.class | Bin 6797 -> 7139 bytes 32 files changed, 1446 insertions(+), 605 deletions(-) create mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java delete mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomEvent.java create mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java delete mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomUserInfoEvent.java create mode 100644 Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokRoomInfoEventHandler.java create mode 100644 Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokSocialMediaEventHandler.java create mode 100644 Tools-ReadmeGenerator/src/main/resources/output.md diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/ClientSettings.java b/API/src/main/java/io/github/jwdeveloper/tiktok/ClientSettings.java index 5c6c2ed..b2ead8b 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/ClientSettings.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/ClientSettings.java @@ -52,11 +52,6 @@ public class ClientSettings { */ private Duration retryConnectionTimeout; - /** - * Whether to handle Events received from Room when Connecting - */ - private boolean handleExistingEvents; - /** * Whether to print Logs to Console */ diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/Constants.java b/API/src/main/java/io/github/jwdeveloper/tiktok/Constants.java index a2236fd..acc994f 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/Constants.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/Constants.java @@ -57,7 +57,6 @@ public class Constants { var clientSettings = new ClientSettings(); clientSettings.setTimeout(Duration.ofSeconds(DEFAULT_TIMEOUT)); clientSettings.setClientLanguage("en-US"); - clientSettings.setHandleExistingEvents(true); clientSettings.setRetryOnConnectionFailure(false); clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); clientSettings.setPrintToConsole(false); diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java new file mode 100644 index 0000000..babd49a --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java @@ -0,0 +1,10 @@ +package io.github.jwdeveloper.tiktok.data.events; + +import io.github.jwdeveloper.tiktok.annotations.EventMeta; +import io.github.jwdeveloper.tiktok.annotations.EventType; +import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; + + +@EventMeta(eventType = EventType.Message) +public class TikTokLiveUnpausedEvent extends TikTokEvent { +} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomEvent.java deleted file mode 100644 index 1e9f67e..0000000 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomEvent.java +++ /dev/null @@ -1,67 +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.data.events.room; - -import io.github.jwdeveloper.tiktok.annotations.EventMeta; -import io.github.jwdeveloper.tiktok.annotations.EventType; -import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent; -import io.github.jwdeveloper.tiktok.data.models.users.User; -import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute; -import io.github.jwdeveloper.tiktok.messages.webcast.RoomMessage; -import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLiveIntroMessage; -import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomMessage; -import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSystemMessage; -import lombok.Getter; - -@Getter -@EventMeta(eventType = EventType.Message) -public class TikTokRoomEvent extends TikTokHeaderEvent -{ - private User hostUser; - private String hostLanguage; - private final String welcomeMessage; - - public TikTokRoomEvent(WebcastRoomMessage msg) { - super(msg.getCommon()); - welcomeMessage = msg.getContent(); - } - - public TikTokRoomEvent(RoomMessage msg) { - super(msg.getCommon()); - welcomeMessage = msg.getContent(); - } - - public TikTokRoomEvent(WebcastSystemMessage msg) { - super(msg.getCommon()); - welcomeMessage = msg.getMessage(); - } - - public TikTokRoomEvent(WebcastLiveIntroMessage msg) { - super(msg.getCommon()); - hostUser = User.map(msg.getHost()); - hostUser.addAttribute(UserAttribute.LiveHost); - welcomeMessage = msg.getContent(); - hostLanguage = msg.getLanguage(); - } - -} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java new file mode 100644 index 0000000..b5195a1 --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java @@ -0,0 +1,20 @@ +package io.github.jwdeveloper.tiktok.data.events.room; + +import io.github.jwdeveloper.tiktok.annotations.EventMeta; +import io.github.jwdeveloper.tiktok.annotations.EventType; +import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; +import io.github.jwdeveloper.tiktok.live.LiveRoomInfo; +import lombok.AllArgsConstructor; +import lombok.Getter; + + +/* +Triggered when LiveRoomInfo got updated such as likes, viewers, ranking .... + */ +@Getter +@AllArgsConstructor +@EventMeta(eventType = EventType.Message) +public class TikTokRoomInfoEvent extends TikTokEvent +{ + LiveRoomInfo roomInfo; +} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomUserInfoEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomUserInfoEvent.java deleted file mode 100644 index 3a86def..0000000 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomUserInfoEvent.java +++ /dev/null @@ -1,55 +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.data.events.room; - -import io.github.jwdeveloper.tiktok.annotations.EventMeta; -import io.github.jwdeveloper.tiktok.annotations.EventType; -import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent; -import io.github.jwdeveloper.tiktok.data.models.RankingUser; -import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomUserSeqMessage; -import lombok.Getter; - -import java.util.List; -import java.util.stream.Collectors; - - - -@Getter -@EventMeta(eventType = EventType.Message) -public class TikTokRoomUserInfoEvent extends TikTokHeaderEvent { - private final int totalUsers; - - /** - * Only top 5 users in ranking has detailed data - * rest has only ID - */ - private final List usersRanking; - public TikTokRoomUserInfoEvent(WebcastRoomUserSeqMessage msg) { - super(msg.getCommon()); - totalUsers = msg.getTotalUser(); - usersRanking = msg.getRanksListList().stream().map(RankingUser::new) - .sorted((ru1, ru2) -> Integer.compare(ru2.getScore(), ru1.getScore())) - .collect(Collectors.toList()); - } - -} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java index 0b331f3..21d95d4 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java @@ -45,6 +45,8 @@ public class User { @Getter(AccessLevel.NONE) private Set attributes; + + public List getAttributes() { return attributes.stream().toList(); } @@ -107,6 +109,23 @@ public class User { this.attributes = new HashSet<>(); } + public User(Long id, + String name, + String profileName, + Picture picture, + long following, + long followers, + List badges) { + this.id = id; + this.name = name; + this.profileName = profileName; + this.picture = picture; + this.following = following; + this.followers = followers; + this.badges = badges; + this.attributes = new HashSet<>(); + } + public User(Long userId, String nickName) { this.id = userId; diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java index b868b20..8671324 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java @@ -22,14 +22,31 @@ */ package io.github.jwdeveloper.tiktok.live; +import io.github.jwdeveloper.tiktok.data.models.RankingUser; +import io.github.jwdeveloper.tiktok.data.models.users.User; import io.github.jwdeveloper.tiktok.models.ConnectionState; +import java.util.List; + public interface LiveRoomInfo { + /** + * + * @return get current count of viewers of live + */ int getViewersCount(); + + /** + * + * @return get total current count of viewers since beginning of live + */ + int getTotalViewersCount(); int getLikesCount(); boolean isAgeRestricted(); String getRoomId(); String getHostName(); + String getTitle(); + User getHostUser(); + List getUsersRanking(); ConnectionState getConnectionState(); } diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomMeta.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomMeta.java index 14930af..a27ddcd 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomMeta.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomMeta.java @@ -22,6 +22,7 @@ */ package io.github.jwdeveloper.tiktok.live; +import io.github.jwdeveloper.tiktok.data.models.users.User; import lombok.Data; @Data @@ -29,6 +30,11 @@ public class LiveRoomMeta { private LiveRoomStatus status; private boolean ageRestricted; + private String titie; + private int likeCount; + private int totalViewers; + private int viewers; + private User host; public enum LiveRoomStatus { diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/EventsBuilder.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/EventsBuilder.java index 368418b..33229a0 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/EventsBuilder.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/EventsBuilder.java @@ -24,11 +24,9 @@ package io.github.jwdeveloper.tiktok.live.builder; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.data.events.*; -import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent; -import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomEvent; -import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent; +import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent; @@ -40,9 +38,7 @@ import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandl public interface EventsBuilder { - T onRoom(EventConsumer event); - - T onRoomUserInfo(EventConsumer event); + T onRoomInfo(EventConsumer event); T onComment(EventConsumer event); @@ -53,8 +49,6 @@ public interface EventsBuilder { T onWebsocketUnhandledMessage(EventConsumer event); - - T onGiftCombo(EventConsumer event); T onGift(EventConsumer event); @@ -71,12 +65,13 @@ public interface EventsBuilder { T onJoin(EventConsumer event); T onShare(EventConsumer event); - T onUnhandledSocial(EventConsumer event); // T onChest(EventConsumer event); T onLivePaused(EventConsumer event); + T onLiveUnpaused(EventConsumer event); + T onLiveEnded(EventConsumer event); T onConnected(EventConsumer event); diff --git a/API/src/main/proto/enums.proto b/API/src/main/proto/enums.proto index 822c25a..a16be68 100644 --- a/API/src/main/proto/enums.proto +++ b/API/src/main/proto/enums.proto @@ -68,6 +68,7 @@ enum MemberMessageAction { enum ControlAction { ControlActionUNKNOWN = 0; STREAM_PAUSED = 1; // Stream Paused by Host + STREAM_UNPAUSED = 2; STREAM_ENDED = 3; // Stream Ended by Host } 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 8547c03..a6cea96 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -25,6 +25,7 @@ package io.github.jwdeveloper.tiktok; 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.room.TikTokRoomInfoEvent; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; @@ -138,14 +139,24 @@ public class TikTokLiveClient implements LiveClient { } - var roomData = apiService.fetchRoomInfo(); - if (roomData.getStatus() != LiveRoomMeta.LiveRoomStatus.HostOnline) { - throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found. Is the Host online?"); + var liveRoomMeta = apiService.fetchRoomInfo(); + if (liveRoomMeta.getStatus() == LiveRoomMeta.LiveRoomStatus.HostNotFound) { + throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found."); } + if (liveRoomMeta.getStatus() == LiveRoomMeta.LiveRoomStatus.HostOffline) { + throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?"); + } + + liveRoomInfo.setTitle(liveRoomMeta.getTitie()); + liveRoomInfo.setViewersCount(liveRoomMeta.getViewers()); + liveRoomInfo.setTotalViewersCount(liveRoomMeta.getTotalViewers()); + liveRoomInfo.setAgeRestricted(liveRoomMeta.isAgeRestricted()); + liveRoomInfo.setHost(liveRoomMeta.getHost()); var clientData = apiService.fetchClientData(); webSocketClient.start(clientData, this); setState(ConnectionState.CONNECTED); + tikTokEventHandler.publish(this,new TikTokRoomInfoEvent(liveRoomInfo)); } 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 43c312f..d384559 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -28,9 +28,8 @@ import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent; import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent; -import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomEvent; +import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent; import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomPinEvent; -import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent; @@ -43,6 +42,8 @@ import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver; import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration; import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler; +import io.github.jwdeveloper.tiktok.handlers.events.TikTokRoomInfoEventHandler; +import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler; import io.github.jwdeveloper.tiktok.http.TikTokApiService; import io.github.jwdeveloper.tiktok.http.TikTokCookieJar; import io.github.jwdeveloper.tiktok.http.TikTokHttpClient; @@ -131,8 +132,8 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { logger.setLevel(clientSettings.getLogLevel()); - if (clientSettings.isPrintToConsole() && clientSettings.getLogLevel() == Level.OFF) { - logger.setLevel(Level.ALL); + if (!clientSettings.isPrintToConsole()) { + logger.setLevel(Level.OFF); } @@ -152,12 +153,16 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { var apiService = new TikTokApiService(apiClient, logger, clientSettings); var giftManager = new TikTokGiftManager(logger); var eventMapper = new TikTokGenericEventMapper(); + var giftHandler = new TikTokGiftEventHandler(giftManager); + var roomInfoHandler = new TikTokRoomInfoEventHandler(tiktokRoomInfo); + var socialHandler = new TikTokSocialMediaEventHandler(tiktokRoomInfo); var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler, - tiktokRoomInfo, + roomInfoHandler, eventMapper, - giftHandler + giftHandler, + socialHandler ); var webSocketClient = new TikTokWebSocketClient(logger, @@ -247,16 +252,25 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - public TikTokLiveClientBuilder onRoom(EventConsumer event) { - tikTokEventHandler.subscribe(TikTokRoomEvent.class, event); + @Override + public LiveClientBuilder onRoomInfo(EventConsumer event) { + tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event); return this; } + + public TikTokLiveClientBuilder onLivePaused(EventConsumer event) { tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event); return this; } + @Override + public LiveClientBuilder onLiveUnpaused(EventConsumer event) { + tikTokEventHandler.subscribe(TikTokLiveUnpausedEvent.class, event); + return this; + } + public TikTokLiveClientBuilder onLike(EventConsumer event) { tikTokEventHandler.subscribe(TikTokLikeEvent.class, event); return this; @@ -329,13 +343,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - public TikTokLiveClientBuilder onRoomUserInfo( - EventConsumer event) { - tikTokEventHandler.subscribe(TikTokRoomUserInfoEvent.class, event); - return this; - } - - public TikTokLiveClientBuilder onComment(EventConsumer event) { tikTokEventHandler.subscribe(TikTokCommentEvent.class, event); return this; diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java index 17827fc..ec87a1a 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java @@ -23,34 +23,51 @@ package io.github.jwdeveloper.tiktok; import io.github.jwdeveloper.tiktok.data.models.Picture; -import io.github.jwdeveloper.tiktok.messages.data.User; +import io.github.jwdeveloper.tiktok.data.models.RankingUser; +import io.github.jwdeveloper.tiktok.data.models.users.User; import io.github.jwdeveloper.tiktok.models.ConnectionState; import io.github.jwdeveloper.tiktok.live.LiveRoomInfo; import lombok.Data; +import java.util.LinkedList; +import java.util.List; + @Data -public class TikTokRoomInfo implements LiveRoomInfo -{ +public class TikTokRoomInfo implements LiveRoomInfo { private String roomId; private int likesCount; private int viewersCount; + private int totalViewersCount; + private boolean ageRestricted; private User host; - private Picture picture; - - private String description; + private List usersRanking = new LinkedList<>(); private String hostName; + private String title; + + private String language = "en"; + private ConnectionState connectionState = ConnectionState.DISCONNECTED; - public boolean hasConnectionState(ConnectionState state) - { + public boolean hasConnectionState(ConnectionState state) { return connectionState == state; } + + @Override + public User getHostUser() { + return null; + } + + public void updateRanking(List rankingUsers) { + usersRanking.clear(); + usersRanking.addAll(rankingUsers); + } + } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/TikTokMessageHandlerRegistration.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/TikTokMessageHandlerRegistration.java index 5d70c44..e04b4a6 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/TikTokMessageHandlerRegistration.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/TikTokMessageHandlerRegistration.java @@ -22,7 +22,6 @@ */ package io.github.jwdeveloper.tiktok.handlers; -import io.github.jwdeveloper.tiktok.TikTokRoomInfo; import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent; @@ -30,39 +29,36 @@ import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEndEvent; import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent; import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollStartEvent; import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollUpdateEvent; -import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomEvent; import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomPinEvent; -import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent; -import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent; -import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent; -import io.github.jwdeveloper.tiktok.data.models.Text; import io.github.jwdeveloper.tiktok.data.models.chest.Chest; import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler; +import io.github.jwdeveloper.tiktok.handlers.events.TikTokRoomInfoEventHandler; +import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler; import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper; import io.github.jwdeveloper.tiktok.messages.enums.EnvelopeDisplay; import io.github.jwdeveloper.tiktok.messages.webcast.*; -import io.github.jwdeveloper.tiktok.models.SocialTypes; import lombok.SneakyThrows; import java.util.Collections; import java.util.List; -import java.util.regex.Pattern; public class TikTokMessageHandlerRegistration extends TikTokMessageHandler { - private final TikTokRoomInfo roomInfo; private final TikTokGiftEventHandler giftHandler; - private final Pattern socialMediaPattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share"); + private final TikTokRoomInfoEventHandler roomInfoHandler; + private final TikTokSocialMediaEventHandler socialHandler; public TikTokMessageHandlerRegistration(TikTokEventObserver tikTokEventHandler, - TikTokRoomInfo roomInfo, + TikTokRoomInfoEventHandler roomInfoHandler, TikTokGenericEventMapper genericTikTokEventMapper, - TikTokGiftEventHandler tikTokGiftEventHandler) { + TikTokGiftEventHandler tikTokGiftEventHandler, + TikTokSocialMediaEventHandler tikTokSocialMediaEventHandler) { super(tikTokEventHandler, genericTikTokEventMapper); this.giftHandler = tikTokGiftEventHandler; - this.roomInfo = roomInfo; + this.roomInfoHandler = roomInfoHandler; + this.socialHandler = tikTokSocialMediaEventHandler; init(); } @@ -70,22 +66,19 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler { //ConnectionEvents events registerMapping(WebcastControlMessage.class, this::handleWebcastControlMessage); - registerMapping(WebcastSystemMessage.class, TikTokRoomEvent.class); - //Room status events - registerMapping(WebcastLiveIntroMessage.class, TikTokRoomEvent.class); - registerMapping(WebcastRoomUserSeqMessage.class, this::handleRoomUserSeqMessage); - registerMapping(RoomMessage.class, TikTokRoomEvent.class); - registerMapping(WebcastRoomMessage.class, TikTokRoomEvent.class); + registerMapping(WebcastLiveIntroMessage.class, roomInfoHandler::handleIntro); + registerMapping(WebcastRoomUserSeqMessage.class, roomInfoHandler::handleUserRanking); + registerMapping(WebcastCaptionMessage.class, TikTokCaptionEvent.class); //User Interactions events registerMapping(WebcastChatMessage.class, TikTokCommentEvent.class); - registerMapping(WebcastLikeMessage.class, this::handleLike); + registerMappings(WebcastLikeMessage.class, this::handleLike); registerMappings(WebcastGiftMessage.class, giftHandler::handleGift); - registerMapping(WebcastSocialMessage.class, this::handleSocialMedia); - registerMapping(WebcastMemberMessage.class, this::handleMemberMessage); + registerMapping(WebcastSocialMessage.class, socialHandler::handle); + registerMappings(WebcastMemberMessage.class, this::handleMemberMessage); //Host Interaction events registerMapping(WebcastPollMessage.class, this::handlePollEvent); @@ -123,54 +116,37 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler { return switch (message.getAction()) { case STREAM_PAUSED -> new TikTokLivePausedEvent(); case STREAM_ENDED -> new TikTokLiveEndedEvent(); + case STREAM_UNPAUSED -> new TikTokLiveUnpausedEvent(); default -> new TikTokUnhandledControlEvent(message); }; } @SneakyThrows - private TikTokEvent handleSocialMedia(byte[] msg) { - var message = WebcastSocialMessage.parseFrom(msg); - - var socialType = Text.map(message.getCommon().getDisplayText()).getKey(); - var matcher = socialMediaPattern.matcher(socialType); - - if (matcher.find()) { - var value = matcher.group(1); - var number = Integer.parseInt(value); - return new TikTokShareEvent(message, number); - } - - return switch (socialType) { - case SocialTypes.LikeType -> new TikTokLikeEvent(message, roomInfo.getLikesCount()); - case SocialTypes.FollowType -> new TikTokFollowEvent(message); - case SocialTypes.ShareType -> new TikTokShareEvent(message); - case SocialTypes.JoinType -> new TikTokJoinEvent(message, roomInfo.getViewersCount()); - default -> new TikTokUnhandledSocialEvent(message); - }; - } - - @SneakyThrows - private TikTokEvent handleMemberMessage(byte[] msg) { + private List handleMemberMessage(byte[] msg) { var message = WebcastMemberMessage.parseFrom(msg); - roomInfo.setViewersCount(message.getMemberCount()); - return switch (message.getAction()) { + + var event = switch (message.getAction()) { case JOINED -> new TikTokJoinEvent(message); case SUBSCRIBED -> new TikTokSubscribeEvent(message); default -> new TikTokUnhandledMemberEvent(message); }; + + var roomInfoEvent = roomInfoHandler.handleRoomInfo(tikTokRoomInfo -> + { + tikTokRoomInfo.setViewersCount(message.getMemberCount()); + }); + + return List.of(event, roomInfoEvent); } - private TikTokEvent handleRoomUserSeqMessage(byte[] msg) { - var event = (TikTokRoomUserInfoEvent) mapper.mapToEvent(WebcastRoomUserSeqMessage.class, TikTokRoomUserInfoEvent.class, msg); - roomInfo.setViewersCount(event.getTotalUsers()); - return event; - } - - private TikTokEvent handleLike(byte[] msg) { + private List handleLike(byte[] msg) { var event = (TikTokLikeEvent) mapper.mapToEvent(WebcastLikeMessage.class, TikTokLikeEvent.class, msg); - roomInfo.setLikesCount(event.getTotalLikes()); - return event; + var roomInfoEvent = roomInfoHandler.handleRoomInfo(tikTokRoomInfo -> + { + tikTokRoomInfo.setLikesCount(event.getTotalLikes()); + }); + return List.of(event, roomInfoEvent); } @SneakyThrows diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandler.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandler.java index 1f41100..42b3f44 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandler.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokGiftEventHandler.java @@ -53,12 +53,19 @@ public class TikTokGiftEventHandler { return handleGift(currentMessage); } - public List handleGift(WebcastGiftMessage currentMessage) - { + public List handleGift(WebcastGiftMessage currentMessage) { var userId = currentMessage.getUser().getId(); var currentType = GiftSendType.fromNumber(currentMessage.getSendType()); var containsPreviousMessage = giftsMessages.containsKey(userId); + + //If gift is not streakable just return onGift event + if (currentMessage.getGift().getType() != 1) { + var comboEvent = getGiftComboEvent(currentMessage, GiftSendType.Finished); + var giftEvent = getGiftEvent(currentMessage); + return List.of(comboEvent, giftEvent); + } + if (!containsPreviousMessage) { if (currentType == GiftSendType.Finished) { return List.of(getGiftEvent(currentMessage)); @@ -89,7 +96,6 @@ public class TikTokGiftEventHandler { } - private TikTokGiftEvent getGiftEvent(WebcastGiftMessage message) { var gift = getGiftObject(message); return new TikTokGiftEvent(gift, message); @@ -100,15 +106,13 @@ public class TikTokGiftEventHandler { return new TikTokGiftComboEvent(gift, message, state); } - private Gift getGiftObject(WebcastGiftMessage giftMessage) - { + private Gift getGiftObject(WebcastGiftMessage giftMessage) { var giftId = (int) giftMessage.getGiftId(); var gift = giftManager.findById(giftId); if (gift == Gift.UNDEFINED) { gift = giftManager.findByName(giftMessage.getGift().getName()); } - if (gift == Gift.UNDEFINED) - { + if (gift == Gift.UNDEFINED) { gift = giftManager.registerGift( giftId, giftMessage.getGift().getName(), diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokRoomInfoEventHandler.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokRoomInfoEventHandler.java new file mode 100644 index 0000000..b681b4a --- /dev/null +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokRoomInfoEventHandler.java @@ -0,0 +1,57 @@ +package io.github.jwdeveloper.tiktok.handlers.events; + +import io.github.jwdeveloper.tiktok.TikTokRoomInfo; +import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; +import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent; +import io.github.jwdeveloper.tiktok.data.models.RankingUser; +import io.github.jwdeveloper.tiktok.data.models.users.User; +import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLiveIntroMessage; +import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomUserSeqMessage; +import lombok.SneakyThrows; + +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class TikTokRoomInfoEventHandler { + private final TikTokRoomInfo roomInfo; + + public TikTokRoomInfoEventHandler(TikTokRoomInfo roomInfo) { + this.roomInfo = roomInfo; + } + + public TikTokEvent handleRoomInfo(Consumer consumer) { + consumer.accept(roomInfo); + return new TikTokRoomInfoEvent(roomInfo); + } + + @SneakyThrows + public TikTokEvent handleUserRanking(byte[] msg) { + var message = WebcastRoomUserSeqMessage.parseFrom(msg); + var totalUsers = message.getTotalUser(); + var userRanking = message.getRanksListList().stream().map(RankingUser::new) + .sorted((ru1, ru2) -> Integer.compare(ru2.getScore(), ru1.getScore())) + .collect(Collectors.toList()); + + return handleRoomInfo(tikTokRoomInfo -> + { + tikTokRoomInfo.setTotalViewersCount(totalUsers); + tikTokRoomInfo.updateRanking(userRanking); + }); + } + + @SneakyThrows + public TikTokEvent handleIntro(byte[] msg) { + var message = WebcastLiveIntroMessage.parseFrom(msg); + var hostUser = User.map(message.getHost()); + var language = message.getLanguage(); + + return handleRoomInfo(tikTokRoomInfo -> + { + if(tikTokRoomInfo.getHost() == null) + { + tikTokRoomInfo.setHost(hostUser); + } + tikTokRoomInfo.setLanguage(language); + }); + } +} diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokSocialMediaEventHandler.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokSocialMediaEventHandler.java new file mode 100644 index 0000000..1ddd5e2 --- /dev/null +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/events/TikTokSocialMediaEventHandler.java @@ -0,0 +1,49 @@ +package io.github.jwdeveloper.tiktok.handlers.events; + +import io.github.jwdeveloper.tiktok.TikTokRoomInfo; +import io.github.jwdeveloper.tiktok.data.events.TikTokUnhandledSocialEvent; +import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; +import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent; +import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent; +import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent; +import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent; +import io.github.jwdeveloper.tiktok.data.models.Text; +import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage; +import io.github.jwdeveloper.tiktok.models.SocialTypes; +import lombok.SneakyThrows; +import java.util.regex.Pattern; + +public class TikTokSocialMediaEventHandler +{ + private final TikTokRoomInfo roomInfo; + private final Pattern socialMediaPattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share"); + + public TikTokSocialMediaEventHandler(TikTokRoomInfo roomInfo) { + this.roomInfo = roomInfo; + } + + @SneakyThrows + public TikTokEvent handle(byte[] msg) + { + var message = WebcastSocialMessage.parseFrom(msg); + + var socialType = Text.map(message.getCommon().getDisplayText()).getKey(); + var matcher = socialMediaPattern.matcher(socialType); + + if (matcher.find()) { + var value = matcher.group(1); + var number = Integer.parseInt(value); + return new TikTokShareEvent(message, number); + } + + return switch (socialType) { + case SocialTypes.LikeType -> new TikTokLikeEvent(message, roomInfo.getLikesCount()); + case SocialTypes.FollowType -> new TikTokFollowEvent(message); + case SocialTypes.ShareType -> new TikTokShareEvent(message); + case SocialTypes.JoinType -> new TikTokJoinEvent(message, roomInfo.getViewersCount()); + default -> new TikTokUnhandledSocialEvent(message); + }; + } + + +} diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/TikTokHttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/TikTokHttpClient.java index 3b503d9..8ecf63e 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/TikTokHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/TikTokHttpClient.java @@ -70,6 +70,7 @@ public class TikTokHttpClient { public JsonObject getJsonFromWebcastApi(String path, Map parameters) { var get = getRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters); + var json = JsonParser.parseString(get); var jsonObject = json.getAsJsonObject(); return jsonObject; diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/LiveRoomMetaMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/LiveRoomMetaMapper.java index c8457c2..85e1f5a 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/LiveRoomMetaMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/LiveRoomMetaMapper.java @@ -22,9 +22,15 @@ */ package io.github.jwdeveloper.tiktok.mappers; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import io.github.jwdeveloper.tiktok.data.models.Picture; +import io.github.jwdeveloper.tiktok.data.models.users.User; +import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute; import io.github.jwdeveloper.tiktok.live.LiveRoomMeta; +import java.util.ArrayList; + public class LiveRoomMetaMapper { /** * 0 - Unknown @@ -46,15 +52,12 @@ public class LiveRoomMetaMapper { var status = data.get("status"); var statusId = status.getAsInt(); var statusValue = switch (statusId) { - case 0 -> LiveRoomMeta.LiveRoomStatus.HostNotFound; case 2 -> LiveRoomMeta.LiveRoomStatus.HostOnline; case 4 -> LiveRoomMeta.LiveRoomStatus.HostOffline; - default-> LiveRoomMeta.LiveRoomStatus.HostNotFound; + default -> LiveRoomMeta.LiveRoomStatus.HostNotFound; }; liveRoomMeta.setStatus(statusValue); - } - else - { + } else { liveRoomMeta.setStatus(LiveRoomMeta.LiveRoomStatus.HostNotFound); } @@ -63,6 +66,61 @@ public class LiveRoomMetaMapper { var restricted = element.get("restricted").getAsBoolean(); liveRoomMeta.setAgeRestricted(restricted); } + + if (data.has("title")) { + var element = data.get("title"); + var title = element.getAsString(); + liveRoomMeta.setTitie(title); + } + + if (data.has("stats")) { + var statsElement = data.getAsJsonObject("stats"); + var likeElement = statsElement.get("like_count"); + var likes = likeElement.getAsInt(); + + var titalUsersElement = statsElement.get("total_user"); + var totalUsers = titalUsersElement.getAsInt(); + + + liveRoomMeta.setLikeCount(likes); + liveRoomMeta.setTotalViewers(totalUsers); + } + + if(data.has("user_count")) + { + var element = data.get("user_count"); + var viewers = element.getAsInt(); + liveRoomMeta.setViewers(viewers); + } + + if(data.has("owner")) + { + var element = data.getAsJsonObject("owner"); + var user = getUser(element); + liveRoomMeta.setHost(user); + } + return liveRoomMeta; } + + public User getUser(JsonObject jsonElement) + { + var id = jsonElement.get("id").getAsLong(); + var name = jsonElement.get("display_id").getAsString(); + var profileName = jsonElement.get("nickname").getAsString(); + + + var followElement =jsonElement.getAsJsonObject("follow_info"); + var followers = followElement.get("follower_count").getAsInt(); + var followingCount = followElement.get("following_count").getAsInt(); + + + var pictureElement =jsonElement.getAsJsonObject("avatar_large"); + var link = pictureElement.getAsJsonArray("url_list").get(1).getAsString(); + var picture = new Picture(link); + + var user = new User(id,name,profileName,picture,followers,followingCount,new ArrayList<>()); + user.addAttribute(UserAttribute.LiveHost); + return user; + } } 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 9fb0eb4..a7ef317 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 @@ -74,10 +74,7 @@ public class TikTokWebSocketClient implements SocketClient { } try { - if (clientSettings.isHandleExistingEvents()) { - logger.info("Handling existing events"); - webResponseHandler.handle(tikTokLiveClient, webcastResponse); - } + webResponseHandler.handle(tikTokLiveClient, webcastResponse); var url = getWebSocketUrl(webcastResponse); webSocketClient = startWebSocket(url, tikTokLiveClient); webSocketClient.connect(); @@ -85,7 +82,8 @@ public class TikTokWebSocketClient implements SocketClient { pingingTask = new TikTokWebSocketPingingTask(); pingingTask.run(webSocketClient); isConnected = true; - } catch (Exception e) { + } catch (Exception e) + { isConnected = false; throw new TikTokLiveException("Failed to connect to the websocket", e); } 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 b881517..785d58c 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 @@ -22,8 +22,10 @@ */ package io.github.jwdeveloper.tiktok.handlers.events; +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.GiftSendType; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.messages.data.GiftStruct; import io.github.jwdeveloper.tiktok.messages.data.Image; @@ -52,7 +54,20 @@ class TikTokGiftEventHandlerTest { @Test void shouldHandleGifts() { - var message = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1); + var message = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1,false); + var result = handler.handleGift(message); + + Assertions.assertEquals(2, result.size()); + + var event = (TikTokGiftEvent) result.get(0); + var gift = event.getGift(); + Assertions.assertEquals("image-new.png",gift.getPicture().getLink()); + Assertions.assertEquals(123,gift.getId()); + } + + @Test + void shouldHandleStrakableGift() { + var message = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1,true); var result = handler.handleGift(message); Assertions.assertEquals(1, result.size()); @@ -63,12 +78,35 @@ class TikTokGiftEventHandlerTest { Assertions.assertEquals(123,gift.getId()); } + @Test + void shouldHandleStrike() + { + var message1 = getGiftMessage("example-new-name", 123, "image-new.png", 1, 1,true); + var message2 = getGiftMessage("example-new-name", 123, "image-new.png", 2, 1,true); + var message3 = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1,true); + + var result1 = handler.handleGift(message1); + var result2 = handler.handleGift(message2); + var result3 = handler.handleGift(message3); + + var event1 = (TikTokGiftComboEvent) result1.get(0); + var event2 = (TikTokGiftComboEvent) result2.get(0); + + Assertions.assertEquals(2, result3.size()); + var event3 = (TikTokGiftComboEvent) result3.get(0); + + Assertions.assertEquals(GiftSendType.Begin,event1.getComboState()); + Assertions.assertEquals(GiftSendType.Active,event2.getComboState()); + Assertions.assertEquals(GiftSendType.Finished,event3.getComboState()); + } + public WebcastGiftMessage getGiftMessage(String giftName, int giftId, String giftImage, int sendType, - int userId) { + int userId, + boolean streakable) { var builder = WebcastGiftMessage.newBuilder(); var giftBuilder = GiftStruct.newBuilder(); var userBuilder = User.newBuilder(); @@ -77,6 +115,7 @@ class TikTokGiftEventHandlerTest { giftBuilder.setId(giftId); giftBuilder.setName(giftName); giftBuilder.setImage(Image.newBuilder().addUrlList(giftImage).build()); + giftBuilder.setType(streakable?1:0); userBuilder.setId(userId); builder.setGiftId(giftId); diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java index 25a413c..3541b62 100644 --- a/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java +++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java @@ -31,7 +31,7 @@ import java.util.logging.Level; public class SimpleExample { - public static String TIKTOK_HOSTNAME = "seanfitzness"; + public static String TIKTOK_HOSTNAME = "bangbetmenygy"; public static void main(String[] args) throws IOException { showLogo(); @@ -59,7 +59,6 @@ public class SimpleExample clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout clientSettings.setLogLevel(Level.ALL); // Log level clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF - clientSettings.setHandleExistingEvents(true); // Invokes all TikTok events that had occurred before connection clientSettings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection @@ -95,9 +94,9 @@ public class SimpleExample { print(ConsoleColors.RED,"[Disconnected]"); }) - .onRoom((liveClient, event) -> + .onRoomInfo((liveClient, event) -> { - + var info = event.getRoomInfo(); }) .onFollow((liveClient, event) -> { diff --git a/Examples/target/classes/io/github/jwdeveloper/tiktok/SimpleExample$1.class b/Examples/target/classes/io/github/jwdeveloper/tiktok/SimpleExample$1.class index 962937d656b53ee4233ba822cdc68d6d10137a64..aeec8da73a95edd1b6a9ca49fff2e43bc4f62a31 100644 GIT binary patch delta 13 UcmaFQ_MUBnGBcy!WEEy_03z!I6#xJL delta 13 UcmaFQ_MUBnGBcy&WEEy_03xac2mk;8 diff --git a/README.md b/README.md index 2c8cfaa..864f5a7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@

TikTok Live Java

@@ -17,7 +16,6 @@ - @@ -30,13 +28,10 @@
- - - # Introduction -A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/sebheron/TikTokLiveSharp). Use it to receive live stream events such as comments and gifts in realtime from [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal WebCast push service. -The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers. -No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked. +A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp). Use it to receive live stream events such as comments and gifts in realtime from [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal WebCast push service. +The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers. +No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked.
IMAGE ALT TEXT @@ -52,19 +47,12 @@ Do you prefer other programming languages? **NOTE:** This is not an official API. It's a reverse engineering project. - #### Overview - [Getting started](#getting-started) - [Events](#events) - [Listeners](#listeners) - [Contributing](#contributing) - - - - - - ## Getting started 1. Install the package via Maven @@ -81,7 +69,7 @@ Do you prefer other programming languages? com.github.jwdeveloper.TikTok-Live-Java Client - 1.0.4-Release + 1.0.5-Release compile @@ -107,6 +95,13 @@ TikTokLive.newClient("bangbetmenygy") { System.out.println(event.getComboState()+ " " + event.getCombo() + " " + event.getGift().getName()); }) + .onRoomInfo((liveClient, event) -> + { + var roomInfo = event.getRoomInfo(); + System.out.println("Room Id: "+roomInfo.getRoomId()); + System.out.println("Likes: "+roomInfo.getLikesCount()); + System.out.println("Viewers: "+roomInfo.getViewersCount()); + }) .onJoin((liveClient, event) -> { System.out.println(event.getUser().getProfileName() + "Hello on my stream! "); @@ -134,7 +129,6 @@ TikTokLive.newClient("bangbetmenygy") settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout settings.setLogLevel(Level.ALL); // Log level settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF - settings.setHandleExistingEvents(true); // Invokes all TikTok events that had occurred before connection settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline settings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection @@ -156,36 +150,35 @@ TikTokLive.newClient("bangbetmenygy") - **Control**: +**Control**: - [onConnected](#onconnected-tiktokconnectedevent) -- [onReconnecting](#onreconnecting-tiktokreconnectingevent) - [onDisconnected](#ondisconnected-tiktokdisconnectedevent) +- [onReconnecting](#onreconnecting-tiktokreconnectingevent) - [onError](#onerror-tiktokerrorevent) - **Message**: +**Message**: - [onEvent](#onevent-tiktokevent) -- [onShare](#onshare-tiktokshareevent) -- [onLivePaused](#onlivepaused-tiktoklivepausedevent) -- [onRoom](#onroom-tiktokroomevent) -- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent) -- [onJoin](#onjoin-tiktokjoinevent) -- [onRoomUserInfo](#onroomuserinfo-tiktokroomuserinfoevent) -- [onComment](#oncomment-tiktokcommentevent) -- [onGift](#ongift-tiktokgiftevent) -- [onLike](#onlike-tiktoklikeevent) - [onSubscribe](#onsubscribe-tiktoksubscribeevent) - [onQuestion](#onquestion-tiktokquestionevent) -- [onEmote](#onemote-tiktokemoteevent) -- [onLiveEnded](#onliveended-tiktokliveendedevent) - [onFollow](#onfollow-tiktokfollowevent) -- [onUnhandledSocial](#onunhandledsocial-tiktokunhandledsocialevent) +- [onLike](#onlike-tiktoklikeevent) +- [onLiveEnded](#onliveended-tiktokliveendedevent) +- [onRoomInfo](#onroominfo-tiktokroominfoevent) +- [onShare](#onshare-tiktokshareevent) +- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent) +- [onEmote](#onemote-tiktokemoteevent) +- [onGift](#ongift-tiktokgiftevent) +- [onComment](#oncomment-tiktokcommentevent) +- [onLivePaused](#onlivepaused-tiktoklivepausedevent) +- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent) +- [onJoin](#onjoin-tiktokjoinevent) - **Debug**: +**Debug**: -- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent) - [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent) +- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent) - [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent) # Examples
@@ -193,8 +186,8 @@ TikTokLive.newClient("bangbetmenygy") ## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java) - Triggered when the connection is successfully established. - +Triggered when the connection is successfully established. + ```java TikTokLive.newClient("host-name") @@ -207,6 +200,26 @@ TikTokLive.newClient("host-name") +
+ +## onDisconnected [TikTokDisconnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokDisconnectedEvent.java) + + +Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic. +Note that you should wait a little bit before attempting a reconnect to to avoid being rate-limited. + + +```java +TikTokLive.newClient("host-name") +.onDisconnected((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + +
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java) @@ -224,33 +237,13 @@ TikTokLive.newClient("host-name") -
- -## onDisconnected [TikTokDisconnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokDisconnectedEvent.java) - - - Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic. - Note that you should wait a little bit before attempting a reconnect to to avoid being rate-limited. - - -```java -TikTokLive.newClient("host-name") -.onDisconnected((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - -
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java) - General error event. You should handle this. - +General error event. You should handle this. + ```java TikTokLive.newClient("host-name") @@ -268,8 +261,8 @@ TikTokLive.newClient("host-name") ## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java) - Base class for all events - +Base class for all events + ```java TikTokLive.newClient("host-name") @@ -282,13 +275,125 @@ TikTokLive.newClient("host-name") +
+ +## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java) + + +Triggers when a user creates a subscription. + + +```java +TikTokLive.newClient("host-name") +.onSubscribe((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java) + + +Triggered every time someone asks a new question via the question feature. + + +```java +TikTokLive.newClient("host-name") +.onQuestion((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java) + + +Triggers when a user follows the streamer. Based on social event. + + +```java +TikTokLive.newClient("host-name") +.onFollow((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java) + + +Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok. + + +```java +TikTokLive.newClient("host-name") +.onLike((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java) + + +Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event. + + +```java +TikTokLive.newClient("host-name") +.onLiveEnded((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java) + + + +```java +TikTokLive.newClient("host-name") +.onRoomInfo((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + +
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java) - Triggers when a user shares the stream. Based on social event. - +Triggers when a user shares the stream. Based on social event. + ```java TikTokLive.newClient("host-name") @@ -301,6 +406,94 @@ TikTokLive.newClient("host-name") +
+ +## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java) + + +Triggered every time gift is sent + +@see GiftSendType it has 3 states + +

Example when user sends gift with combo

+

>Combo: 1 -> comboState = GiftSendType.Begin

+

Combo: 4 -> comboState = GiftSendType.Active

+

Combo: 8 -> comboState = GiftSendType.Active

+

Combo: 12 -> comboState = GiftSendType.Finsihed

+ +Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered + + +```java +TikTokLive.newClient("host-name") +.onGiftCombo((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java) + + +Triggered every time a subscriber sends an emote (sticker). + + +```java +TikTokLive.newClient("host-name") +.onEmote((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java) + + +Triggered when user sends gifts that has +no combo (most of expensive gifts) +or if combo has finished + + +```java +TikTokLive.newClient("host-name") +.onGift((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java) + + +Triggered every time a new chat comment arrives. + + +```java +TikTokLive.newClient("host-name") +.onComment((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + +
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java) @@ -320,42 +513,13 @@ TikTokLive.newClient("host-name")
-## onRoom [TikTokRoomEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomEvent.java) +## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java) ```java TikTokLive.newClient("host-name") -.onRoom((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java) - - - Triggered every time gift is sent - - @see GiftSendType it has 3 states - -

Example when user sends gift with combo

-

>Combo: 1 -> comboState = GiftSendType.Begin

-

Combo: 4 -> comboState = GiftSendType.Active

-

Combo: 8 -> comboState = GiftSendType.Active

-

Combo: 12 -> comboState = GiftSendType.Finsihed

- - Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered - - -```java -TikTokLive.newClient("host-name") -.onGiftCombo((liveClient, event) -> +.onLiveUnpaused((liveClient, event) -> { }) @@ -383,187 +547,15 @@ TikTokLive.newClient("host-name")
-## onRoomUserInfo [TikTokRoomUserInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomUserInfoEvent.java) +## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java) - Only top 5 users in ranking has detailed data - rest has only ID - - -```java -TikTokLive.newClient("host-name") -.onRoomUserInfo((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java) - - - Triggered every time a new chat comment arrives. - - -```java -TikTokLive.newClient("host-name") -.onComment((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java) - - - Triggered when user sends gifts that has - no combo (most of expensive gifts) - or if combo has finished - - -```java -TikTokLive.newClient("host-name") -.onGift((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java) - - - Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok. - - -```java -TikTokLive.newClient("host-name") -.onLike((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java) - - - Triggers when a user creates a subscription. - - -```java -TikTokLive.newClient("host-name") -.onSubscribe((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java) - - -Triggered every time someone asks a new question via the question feature. - - -```java -TikTokLive.newClient("host-name") -.onQuestion((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java) - - - Triggered every time a subscriber sends an emote (sticker). - - -```java -TikTokLive.newClient("host-name") -.onEmote((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java) - - - Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event. - - -```java -TikTokLive.newClient("host-name") -.onLiveEnded((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java) - - - Triggers when a user follows the streamer. Based on social event. - - -```java -TikTokLive.newClient("host-name") -.onFollow((liveClient, event) -> -{ - -}) -.buildAndConnect(); -``` - - - -
- -## onUnhandledSocial [TikTokUnhandledSocialEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokUnhandledSocialEvent.java) - +Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case. ```java TikTokLive.newClient("host-name") -.onUnhandledSocial((liveClient, event) -> +.onWebsocketUnhandledMessage((liveClient, event) -> { }) @@ -580,30 +572,11 @@ TikTokLive.newClient("host-name") ```java TikTokLive.newClient("host-name") -.onWebsocketResponse((liveClient, event) -> -{ + .onWebsocketResponse((liveClient, event) -> + { -}) -.buildAndConnect(); -``` - - - -
- -## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java) - - - Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case. - - -```java -TikTokLive.newClient("host-name") -.onWebsocketUnhandledMessage((liveClient, event) -> -{ - -}) -.buildAndConnect(); + }) + .buildAndConnect(); ``` @@ -613,16 +586,16 @@ TikTokLive.newClient("host-name") ## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java) - Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case. - +Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case. + ```java TikTokLive.newClient("host-name") -.onWebsocketMessage((liveClient, event) -> -{ + .onWebsocketMessage((liveClient, event) -> + { -}) -.buildAndConnect(); + }) + .buildAndConnect(); ``` @@ -635,72 +608,72 @@ TikTokLive.newClient("host-name") ```java - /** - * - * Listeners are an alternative way of handling events. - * I would to suggest to use then when logic of handing event - * is more complex - * - */ - public static void main(String[] args) throws IOException { +/** + * + * Listeners are an alternative way of handling events. + * I would to suggest to use then when logic of handing event + * is more complex + * + */ +public static void main(String[] args) throws IOException { showLogo(); CustomListener customListener = new CustomListener(); TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME) - .addListener(customListener) - .buildAndConnect(); + .addListener(customListener) + .buildAndConnect(); System.in.read(); + } + +/** + * + * Method in TikTokEventListener should meet 4 requirements to be detected + * - must have @TikTokEventHandler annotation + * - must have 2 parameters + * - first parameter must be LiveClient + * - second must be class that extending TikTokEvent + */ + +public static class CustomListener implements TikTokEventListener { + + @TikTokEventHandler + public void onLike(LiveClient liveClient, TikTokLikeEvent event) { + System.out.println(event.toString()); } - /** - * - * Method in TikTokEventListener should meet 4 requirements to be detected - * - must have @TikTokEventHandler annotation - * - must have 2 parameters - * - first parameter must be LiveClient - * - second must be class that extending TikTokEvent - */ - - public static class CustomListener implements TikTokEventListener { - - @TikTokEventHandler - public void onLike(LiveClient liveClient, TikTokLikeEvent event) { - System.out.println(event.toString()); - } - - @TikTokEventHandler - public void onError(LiveClient liveClient, TikTokErrorEvent event) { - // event.getException().printStackTrace(); - } - - @TikTokEventHandler - public void onComment(LiveClient liveClient, TikTokCommentEvent event) { - var userName = event.getUser().getName(); - var text = event.getText(); - liveClient.getLogger().info(userName + ": " + text); - } - - @TikTokEventHandler - public void onGift(LiveClient liveClient, TikTokGiftEvent event) { - var message = switch (event.getGift()) { - case ROSE -> "Thanks :)"; - case APPETIZERS -> ":OO"; - case APRIL -> ":D"; - case TIKTOK -> ":P"; - case CAP -> ":F"; - default -> ":I"; - }; - liveClient.getLogger().info(message); - } - - @TikTokEventHandler - public void onAnyEvent(LiveClient liveClient, TikTokEvent event) { - liveClient.getLogger().info(event.getClass().getSimpleName()); - } - + @TikTokEventHandler + public void onError(LiveClient liveClient, TikTokErrorEvent event) { + // event.getException().printStackTrace(); } - // + @TikTokEventHandler + public void onComment(LiveClient liveClient, TikTokCommentEvent event) { + var userName = event.getUser().getName(); + var text = event.getText(); + liveClient.getLogger().info(userName + ": " + text); + } + + @TikTokEventHandler + public void onGift(LiveClient liveClient, TikTokGiftEvent event) { + var message = switch (event.getGift()) { + case ROSE -> "Thanks :)"; + case APPETIZERS -> ":OO"; + case APRIL -> ":D"; + case TIKTOK -> ":P"; + case CAP -> ":F"; + default -> ":I"; + }; + liveClient.getLogger().info(message); + } + + @TikTokEventHandler + public void onAnyEvent(LiveClient liveClient, TikTokEvent event) { + liveClient.getLogger().info(event.getClass().getSimpleName()); + } + +} + +// ``` diff --git a/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/collector/RunCollector.java b/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/collector/RunCollector.java index c7809b2..d3de7ac 100644 --- a/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/collector/RunCollector.java +++ b/Tools-EventsCollector/src/main/java/io/github/jwdeveloper/tiktok/tools/collector/RunCollector.java @@ -27,6 +27,7 @@ import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessageCollecto import java.io.IOException; import java.sql.SQLException; +import java.util.logging.Level; public class RunCollector { @@ -38,22 +39,38 @@ public class RunCollector { public static void main(String[] args) throws SQLException, IOException { TikTokMessageCollectorClient.create("giftsCollector") - .addUser("crece.sara") - .addUser("moniczkka") + //.addUser("crece.sara") + //.addUser("moniczkka") + .addUser("valeria.viral") // .addUser("cbcgod") // .addUser("psychotropnazywo") // .addUser("accordionistka") - .addEventFilter(WebcastGiftMessage.class) + //.addEventFilter(WebcastGiftMessage.class) .addOnBuilder(liveClientBuilder -> { + liveClientBuilder.onGiftCombo((liveClient, event) -> + { + liveClient.getLogger().setLevel(Level.OFF); + var gifts = liveClient.getGiftManager().getGifts(); + + var sb = new StringBuilder(); + sb.append("GIFT COMBO User: " + event.getUser().getProfileName()+" "); + sb.append("Name: " + event.getGift().getName() + " "); + sb.append("Combo: " + event.getCombo() + " "); + sb.append("COST: " + event.getGift().getDiamondCost() + " "); + sb.append("STATE: " + event.getComboState() + " "); + System.out.println(sb.toString()); + }); liveClientBuilder.onGift((liveClient, event) -> { + liveClient.getLogger().setLevel(Level.OFF); var gifts = liveClient.getGiftManager().getGifts(); var sb = new StringBuilder(); sb.append("GIFT User: " + event.getUser().getProfileName()+" "); sb.append("Name: " + event.getGift().getName() + " "); + sb.append("COST: " + event.getGift().getDiamondCost() + " "); sb.append("Combo: " + event.getCombo() + " "); System.out.println(sb.toString()); }); diff --git a/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/RunCollector.class b/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/RunCollector.class index 7f619ac7ee86975b37081c9d7f43b79c57cd6ad9..df25c78786024fcf6624f5f55eb874b900edefd8 100644 GIT binary patch literal 5540 zcmcgwcX%7+9ezLC_#F92jKVSvAyAXr2ImNrk;qAi<=8REBS_c*(s7c`^2Mh+p*tCz zmO@*)C!KWfJxN=Im^R&u(!KYld;ZnG`t<$oWa(rZ%b`zr9{Y6P`i=KDzkBe%3nu{d zpk0Bg;&iMRm@pkJXL>WGw08KmtTAU;PQfT@o;m9|vzq5PmaAnJ&uSUVG;B{x znX@TpcGPfPJ!e!Y$Nfc$kSQ9vX9$?xiJE{(uV~u2SWn{rX6V+G87P@n)+ol>&>?X8 zoNgIKQ{Ogc7IiCvC^jlMQ^i?Q&*Uj!E1>Av?2WEbjM6;ks5lp$RA0|zi~>z@L3dA} zf5|f~ZMtMijo3?|MdFpvfe^vjW`6a>Oo_Ji3md z=ZRMjdKFxv;!^YpZ1jDrevd#4w`P6sABjRhQ*jx#(@>7RZ;q}j5No81WzHFzuRi0z zKUB3?JFdVJ6PDGqXr}R@A%V_NnAB~c$wzRVg6mZzFe-5F3WO^1w7HZm zAi60WJoWT?CM5#nDkgA1W=6x4CK%Oi<}oU@eNDa*IudI~5-9~Ys+h!-Kwo1(L#GAW zDiXK?(Ke-RvajTgJ_Q65T2yq$ts?LrwZ&^m3>zCbd3<%p39U! zy_m+OK)ypUN+^y@X98C@Lc%AOcd`cSLy1RRO&+Ad(#R;tsxUAua7AN*meC08GEsD< zP0JY5^DHE1E@Bgcfj-`h85JfD6QsO8%R*#lbZ?5qOa`o{Q%`}dE7qW8Wl5rGsmO!b zr&TW$44X-$Z%t<__MqyAP*8Cz2%iK};(C%mN~^e#&c+?ri?W@#Lqhos=HBj+a6+k+ZK>Q{DxN71OQ-d-Rurwc zL#Fh*RXhjJWjH$}s$9$76D;|)?0pc=SMdU=O`GQgbR&2XUaa6HDqf10g;ILxAhjq+ zGM20hD;Z=mf_r2Tyh320vAeD8|GqkY5JaI!sO~{QKJJo_*Q&S|uVa6)sCUxy+nYTn z6C*XVBTeV{&>;PX!)*mpD_Bgyn+0~P3dqv#jaY@WGIujf__pXkSaur?YyO6r$^L zfomJsq^=~_>f~ZR=%K)8$URHy{;t&_YCv^~m~3qdYZ0?1UIPm?DiSfNR-~quz_vB$ z6JjrLnjeD#dsaW#P!dw|LxFu{sDbb+C;Lb@U2QBvRb$aObh3IcW0;R{wQnhsEn`>|l8Z^p_R40;jO2v#Z#Ij7!4kwFkGSp~>0mt!N zuc#MB4R6NDx*J;Yg$TZgFDdx4im%|S0vFXvuW8RYvxb)NqmC!^XE zvwr1Jz+k^_XD!3+N;uAJsSsP9G(t0V2mSo}s1^g3VOGlthqG~IHLhD$()5g22fntX zjs-V&8j5IrJ%Tsk8!A484|mAFZwuI`%xN-Co((|dGVd3^K#1>3A-<>LQ#jFq6O`(Q za^goS?!(az9OcAMBlsD9uHYB4di_$MYnf4l@*Z?#LyKm;_tYD*(yoLH#3H`S&0RH0 z8-C4b9Ud7nxTOqz0XeDc z?3A2v%5WEOd)>tR0-j!*xN`x|3Pwlq?7lLdSH=satj#{;TL~?AM^-UNS%(PzFd^EH z0bX~LxDGlMpXHTTnxr?76CcCJIike)1m)me7%3dX{nTR%cOJ(AX+OYqX~JOblW3yO zjN*#U|EcJFWGyszbliZnm=g$4U`_0Urs=HdH zT)CRM0H6&&%_*hKr9={1L|bX72~{++SfLX)GGxc%dNeXKKCa3rV(1V<_R_5}%`nt4 z%?KXSMk1<_42CmfZ**U>KeML5i(-xvlrp&0(b4W{O;7ejCz4u%VZT3gK%G{D6RJKI z?3z#wLwIaq#Y<$$Iee%PXIN0G;r;!k!|s^RdXR!rG;yXsIcqk&^3g5nojlWP^4 zWZcSe8=6Idy+&HYaXZ$EPWvWzE81kN<7mfvhE`%85P!S!au}qzZc%0E_lr(fa5{TGUSUn zXTJE?St-eQn31wpTa7l4_+8;5sa!01mY~w7X-rK7hlfJlha=i#G8)%q920}CYR4+N zHvj9KLnq)Brb}})GEAnU3+;S{i6ic2_bCPM;{zF|IX=Wk3s>A(jC~@0&MhwdRKY{| zjN^H{;IV#l40@(yJ8uzT&x-N|E8`1}mvG#JmT2)YpA#3U@c}iaWxPvt-_tr;E(7n=buE#;pPC`#viL18ko5yYnfN2GjHHo5 z@%Z?`NmLNDjqWZ;eFg_4vX77#q><6+2rPol(Zq&F$y&>F(eW5ro=L#tc!Ioa9Kn;6 zWo_1JwDSC3`5dynxhC?wH&|wciJMX=n*~f%nz$uAkAR8B@O6MnIIWG4QA+>;fVll=7dZzeJ$V;5mFV3=A~XnHV&&J%!y?XM4j| zAYx)Hg^BBdSPGLFZ+0F=gNZ{XW~|QbX=%%-XV9mB6Vn2$K{b5~R%0za=N1f5Czj-a z3RF$HoVi0cWL6M<9?5V jAL9%@PtVR`9$!)e3}2<~g|z(!-%_?h(eLm*enioK?Xo28 diff --git a/Tools-EventsWebViewer/src/main/java/io/github/jwdeveloper/tiktok/webviewer/TikTokManager.java b/Tools-EventsWebViewer/src/main/java/io/github/jwdeveloper/tiktok/webviewer/TikTokManager.java index dabfd92..1654a90 100644 --- a/Tools-EventsWebViewer/src/main/java/io/github/jwdeveloper/tiktok/webviewer/TikTokManager.java +++ b/Tools-EventsWebViewer/src/main/java/io/github/jwdeveloper/tiktok/webviewer/TikTokManager.java @@ -39,16 +39,21 @@ public class TikTokManager { TikTokMessagessCollectorBuilder client; MessageCollector msgCollector; + public static String dbName= "ab"; + public TikTokManager() { - msgCollector = new MessageCollector("ab"); + msgCollector = new MessageCollector(dbName); } public void connect(String name) throws SQLException { disconnect(); - client = TikTokMessageCollectorClient.create(msgCollector, "ab") + client = TikTokMessageCollectorClient.create(msgCollector, dbName) .addOnBuilder(liveClientBuilder -> { + liveClientBuilder.onRoomInfo((liveClient, event) -> + { + }); liveClientBuilder.onGift((liveClient, event) -> { diff --git a/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java b/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java index bbef55e..eaa80f0 100644 --- a/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java +++ b/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java @@ -54,6 +54,13 @@ public class CodeExample { { System.out.println(event.getComboState()+ " " + event.getCombo() + " " + event.getGift().getName()); }) + .onRoomInfo((liveClient, event) -> + { + var roomInfo = event.getRoomInfo(); + System.out.println("Room Id: "+roomInfo.getRoomId()); + System.out.println("Likes: "+roomInfo.getLikesCount()); + System.out.println("Viewers: "+roomInfo.getViewersCount()); + }) .onJoin((liveClient, event) -> { System.out.println(event.getUser().getProfileName() + "Hello on my stream! "); @@ -80,7 +87,6 @@ public class CodeExample { settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout settings.setLogLevel(Level.ALL); // Log level settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF - settings.setHandleExistingEvents(true); // Invokes all TikTok events that had occurred before connection settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline settings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection diff --git a/Tools-ReadmeGenerator/src/main/resources/output.md b/Tools-ReadmeGenerator/src/main/resources/output.md new file mode 100644 index 0000000..22b9542 --- /dev/null +++ b/Tools-ReadmeGenerator/src/main/resources/output.md @@ -0,0 +1,681 @@ +
+ + + + +
+
+

TikTok Live Java

+ +❤️❤️🎁 *Connect to TikTok live in 3 lines* 🎁❤️❤️ + +
+ + + + + + + + + + + + + + + +
+
+ +# Introduction +A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp). Use it to receive live stream events such as comments and gifts in realtime from [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal WebCast push service. +The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers. +No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked. + +
+ IMAGE ALT TEXT +
+ +Join the support [discord](https://discord.gg/e2XwPNTBBr) and visit the `#java-support` channel for questions, contributions and ideas. Feel free to make pull requests with missing/new features, fixes, etc + +Do you prefer other programming languages? +- **Node** orginal: [TikTok-Live-Connector](https://github.com/isaackogan/TikTok-Live-Connector) by [@zerodytrash](https://github.com/zerodytrash) +- **Python** rewrite: [TikTokLive](https://github.com/isaackogan/TikTokLive) by [@isaackogan](https://github.com/isaackogan) +- **Go** rewrite: [GoTikTokLive](https://github.com/Davincible/gotiktoklive) by [@Davincible](https://github.com/Davincible) +- **C#** rewrite: [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp) by [@frankvHoof93](https://github.com/frankvHoof93) + +**NOTE:** This is not an official API. It's a reverse engineering project. + +#### Overview +- [Getting started](#getting-started) +- [Events](#events) +- [Listeners](#listeners) +- [Contributing](#contributing) + +## Getting started + +1. Install the package via Maven + +```xml + + + jitpack.io + https://jitpack.io + + + + + + com.github.jwdeveloper.TikTok-Live-Java + Client + NOT_FOUND + compile + + +``` + +2. Create your first chat connection + +```java + +TikTokLive.newClient("bangbetmenygy") + .onGift((liveClient, event) -> + { + String message = switch (event.getGift()) { + case ROSE -> "ROSE!"; + case GG -> "GOOD GAME"; + case TIKTOK -> "Ye"; + case CORGI -> "Nice gift"; + default -> "Thank you for " + event.getGift().getName(); + }; + System.out.println(event.getUser().getProfileName() + " sends " + message); + }) + .onGiftCombo((liveClient, event) -> + { + System.out.println(event.getComboState()+ " " + event.getCombo() + " " + event.getGift().getName()); + }) + .onRoomInfo((liveClient, event) -> + { + var roomInfo = event.getRoomInfo(); + System.out.println("Room Id: "+roomInfo.getRoomId()); + System.out.println("Likes: "+roomInfo.getLikesCount()); + System.out.println("Viewers: "+roomInfo.getViewersCount()); + }) + .onJoin((liveClient, event) -> + { + System.out.println(event.getUser().getProfileName() + "Hello on my stream! "); + }) + .onConnected((liveClient, event) -> + { + System.out.println("Connected to live "); + }) + .onError((liveClient, event) -> + { + System.out.println("Error! " + event.getException().getMessage()); + }) + .buildAndConnect(); + +``` +3. Configure (optional) + +```java + + TikTokLive.newClient("bangbetmenygy") + .configure((settings) -> + { + settings.setHostName("bangbetmenygy"); // This method is useful in case you want change hostname later + settings.setClientLanguage("en"); // Language + settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout + settings.setLogLevel(Level.ALL); // Log level + settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF + settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline + settings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection + + //Optional: Sometimes not every message 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 + settings.setSessionId("86c3c8bf4b17ebb2d74bb7fa66fd0000"); + + //Optional: + //RoomId can be used as an override if you're having issues with HostId. + //You can find it in the HTML for the livestream-page + settings.setRoomId("XXXXXXXXXXXXXXXXX"); + }) + .buildAndConnect(); + // +``` + + +## Events + + + + **Control**: + +- [onConnected](#onconnected-tiktokconnectedevent) +- [onDisconnected](#ondisconnected-tiktokdisconnectedevent) +- [onReconnecting](#onreconnecting-tiktokreconnectingevent) +- [onError](#onerror-tiktokerrorevent) + + **Message**: + +- [onEvent](#onevent-tiktokevent) +- [onSubscribe](#onsubscribe-tiktoksubscribeevent) +- [onQuestion](#onquestion-tiktokquestionevent) +- [onFollow](#onfollow-tiktokfollowevent) +- [onLike](#onlike-tiktoklikeevent) +- [onLiveEnded](#onliveended-tiktokliveendedevent) +- [onRoomInfo](#onroominfo-tiktokroominfoevent) +- [onShare](#onshare-tiktokshareevent) +- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent) +- [onEmote](#onemote-tiktokemoteevent) +- [onGift](#ongift-tiktokgiftevent) +- [onComment](#oncomment-tiktokcommentevent) +- [onLivePaused](#onlivepaused-tiktoklivepausedevent) +- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent) +- [onJoin](#onjoin-tiktokjoinevent) + + **Debug**: + +- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent) +- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent) +- [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent) +# Examples +
+ +## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java) + + + Triggered when the connection is successfully established. + + +```java +TikTokLive.newClient("host-name") +.onConnected((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onDisconnected [TikTokDisconnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokDisconnectedEvent.java) + + + Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic. + Note that you should wait a little bit before attempting a reconnect to to avoid being rate-limited. + + +```java +TikTokLive.newClient("host-name") +.onDisconnected((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java) + + + +```java +TikTokLive.newClient("host-name") +.onReconnecting((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java) + + + General error event. You should handle this. + + +```java +TikTokLive.newClient("host-name") +.onError((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java) + + + Base class for all events + + +```java +TikTokLive.newClient("host-name") +.onEvent((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java) + + + Triggers when a user creates a subscription. + + +```java +TikTokLive.newClient("host-name") +.onSubscribe((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java) + + +Triggered every time someone asks a new question via the question feature. + + +```java +TikTokLive.newClient("host-name") +.onQuestion((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java) + + + Triggers when a user follows the streamer. Based on social event. + + +```java +TikTokLive.newClient("host-name") +.onFollow((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java) + + + Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok. + + +```java +TikTokLive.newClient("host-name") +.onLike((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java) + + + Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event. + + +```java +TikTokLive.newClient("host-name") +.onLiveEnded((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java) + + + +```java +TikTokLive.newClient("host-name") +.onRoomInfo((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java) + + + Triggers when a user shares the stream. Based on social event. + + +```java +TikTokLive.newClient("host-name") +.onShare((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java) + + + Triggered every time gift is sent + + @see GiftSendType it has 3 states + +

Example when user sends gift with combo

+

>Combo: 1 -> comboState = GiftSendType.Begin

+

Combo: 4 -> comboState = GiftSendType.Active

+

Combo: 8 -> comboState = GiftSendType.Active

+

Combo: 12 -> comboState = GiftSendType.Finsihed

+ + Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered + + +```java +TikTokLive.newClient("host-name") +.onGiftCombo((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java) + + + Triggered every time a subscriber sends an emote (sticker). + + +```java +TikTokLive.newClient("host-name") +.onEmote((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java) + + + Triggered when user sends gifts that has + no combo (most of expensive gifts) + or if combo has finished + + +```java +TikTokLive.newClient("host-name") +.onGift((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java) + + + Triggered every time a new chat comment arrives. + + +```java +TikTokLive.newClient("host-name") +.onComment((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java) + + + +```java +TikTokLive.newClient("host-name") +.onLivePaused((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java) + + + +```java +TikTokLive.newClient("host-name") +.onLiveUnpaused((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java) + + + +```java +TikTokLive.newClient("host-name") +.onJoin((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java) + + + Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case. + + +```java +TikTokLive.newClient("host-name") +.onWebsocketUnhandledMessage((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java) + + + +```java +TikTokLive.newClient("host-name") +.onWebsocketResponse((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + +
+ +## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java) + + + Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case. + + +```java +TikTokLive.newClient("host-name") +.onWebsocketMessage((liveClient, event) -> +{ + +}) +.buildAndConnect(); +``` + + + + + +
+ +## Listeners + +```java + + /** + * + * Listeners are an alternative way of handling events. + * I would to suggest to use then when logic of handing event + * is more complex + * + */ + public static void main(String[] args) throws IOException { + showLogo(); + CustomListener customListener = new CustomListener(); + + TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME) + .addListener(customListener) + .buildAndConnect(); + System.in.read(); + } + + /** + * + * Method in TikTokEventListener should meet 4 requirements to be detected + * - must have @TikTokEventHandler annotation + * - must have 2 parameters + * - first parameter must be LiveClient + * - second must be class that extending TikTokEvent + */ + + public static class CustomListener implements TikTokEventListener { + + @TikTokEventHandler + public void onLike(LiveClient liveClient, TikTokLikeEvent event) { + System.out.println(event.toString()); + } + + @TikTokEventHandler + public void onError(LiveClient liveClient, TikTokErrorEvent event) { + // event.getException().printStackTrace(); + } + + @TikTokEventHandler + public void onComment(LiveClient liveClient, TikTokCommentEvent event) { + var userName = event.getUser().getName(); + var text = event.getText(); + liveClient.getLogger().info(userName + ": " + text); + } + + @TikTokEventHandler + public void onGift(LiveClient liveClient, TikTokGiftEvent event) { + var message = switch (event.getGift()) { + case ROSE -> "Thanks :)"; + case APPETIZERS -> ":OO"; + case APRIL -> ":D"; + case TIKTOK -> ":P"; + case CAP -> ":F"; + default -> ":I"; + }; + liveClient.getLogger().info(message); + } + + @TikTokEventHandler + public void onAnyEvent(LiveClient liveClient, TikTokEvent event) { + liveClient.getLogger().info(event.getClass().getSimpleName()); + } + + } + + // +``` + + +## Contributing +Your improvements are welcome! Feel free to open an issue or pull request. diff --git a/Tools/src/main/java/io/github/jwdeveloper/tiktok/mockClient/TikTokMockBuilder.java b/Tools/src/main/java/io/github/jwdeveloper/tiktok/mockClient/TikTokMockBuilder.java index 7f35a48..c733da1 100644 --- a/Tools/src/main/java/io/github/jwdeveloper/tiktok/mockClient/TikTokMockBuilder.java +++ b/Tools/src/main/java/io/github/jwdeveloper/tiktok/mockClient/TikTokMockBuilder.java @@ -28,6 +28,8 @@ import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration; import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler; +import io.github.jwdeveloper.tiktok.handlers.events.TikTokRoomInfoEventHandler; +import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler; import io.github.jwdeveloper.tiktok.http.TikTokCookieJar; import io.github.jwdeveloper.tiktok.http.TikTokHttpClient; import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory; @@ -98,9 +100,10 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder { var apiClient = new TikTokHttpClient(cookie, requestFactory); var apiService = new ApiServiceMock(apiClient, logger, clientSettings); var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler, - tiktokRoomInfo, + new TikTokRoomInfoEventHandler(tiktokRoomInfo), new TikTokGenericEventMapper(), - new TikTokGiftEventHandler(giftManager)); + new TikTokGiftEventHandler(giftManager), + new TikTokSocialMediaEventHandler(tiktokRoomInfo)); var webSocketClient = new WebsocketClientMock(logger, responses, webResponseHandler); return new LiveClientMock(tiktokRoomInfo, diff --git a/Tools/target/classes/io/github/jwdeveloper/tiktok/mockClient/TikTokMockBuilder.class b/Tools/target/classes/io/github/jwdeveloper/tiktok/mockClient/TikTokMockBuilder.class index 79a2a1ffeea008649d2b00438a7d4e0aa3efdcc5..ce0c172523a2b1f268d870a624e1576482dd390d 100644 GIT binary patch delta 1377 zcmZ`&S!@$m6g@AsGxp5bBr)I^vH`6MCXk3K1%-eJOK2%hAzhHw0u6~H5Q!~NibVOy z7hezod8}dW;x3dxAYiAsPy%#e?FL=h0`#+g5Hft3 z8g>iJ8{p{)z80%nQQH`b)UT-he+BK>tYZWADU|&}EE+2p`U+*MQU18{oWYgLO7AH7 zkI^7!d!JY2@}Gik5qV!OHLF|)@S)1^k?b&Ij*kT@WUp_ctoCG?aeS)&d?v)+v;JUC{e3msi+hZJY}j#K#zoPZmGXqj9S zvU}L*mL%<7Jj1n&_7{x!o{8|%p%)6jp@ayTnp62f==Trx(l2^5BG#zHN4s?t^gWCXJ72UkY?Pu3FJP_(9E5{517~rLrx7gv6me}*j|r6FJXR9_YoeLl72)&vhNo|N zein|0&@_zu4@j5oBr5-hi z@M+);U5PlFEFKMZg?&6sj^$-sVf{nN;wrwUY9;jl0Y9>b6LGSc|mT*_I9Z z+y7j;3e@xHjJwr!UNJZF%FHvbJn7Po8)LgeqbVwA^j)@LJR*GRFsaf!j+B`P$zp}K z(qY3p9iE2a2?YuBp&KsxP1bE>65U(!%i&d!N3HBe}>3W_xfX%We)b9m$WXkJ)lEzTpfZ@T@qKsWQ&f1FR| z*pqxMZR@;lJ7jd0%XL?%vu1m0ZCiDg_H>3%vTRGI@-ytn@L63v%dWHpKenQ&c!oW7 zi7cJz>QpS1NaZWKMo)UYP08^2KtYq#C8XuYJ376~5SCERQfgSH-{d`-SgvYYXytu2 zvyzTBRt1Hx2utcO+~Ww}@U03S!hUY@o%*d2;CpWA((2j3ZSF{d**gX>SX??c-px%sn!0pp|g2H;8+qaGPFW zuf5ved4N;cpnv6$e%2c8c6cIHeH@*!}