From f1b143fa06dcb52887fc487adeee4799b8de0c1d Mon Sep 17 00:00:00 2001 From: JW Date: Wed, 16 Aug 2023 18:35:56 +0200 Subject: [PATCH] Test application --- .../tiktok/events/TikTokEventBuilder.java | 20 +++ .../events/messages/TikTokErrorEvent.java | 12 ++ .../TikTokLiveMessageParsingException.java | 23 +++ .../jwdeveloper/tiktok/TikTokLiveClient.java | 5 +- .../tiktok/TikTokLiveClientBuilder.java | 125 ++++++++++------ .../handlers/WebResponseHandlerBase.java | 48 +++---- .../websocket/TikTokWebSocketListener.java | 90 ++++++------ .../websocket/TikTokWebsocketClient.java | 133 +++++++----------- .../tiktok/websocket/WebSocketClientTest.java | 120 ---------------- .../jwdeveloper/tiktok/TikTokLiveTest.java | 84 ----------- TestApplication/pom.xml | 28 ++++ .../io/github/jwdeveloper/tiktok/Main.java | 87 ++++++++++++ .../io/github/jwdeveloper/tiktok/Main.java | 7 - .../intefacee/EventsInterfaceGenerator.java | 22 ++- .../io/github/jwdeveloper/tiktok/Main.class | Bin 574 -> 0 bytes .../intefacee/EventsInterfaceGenerator.class | Bin 6016 -> 6200 bytes pom.xml | 1 + 17 files changed, 395 insertions(+), 410 deletions(-) create mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/events/messages/TikTokErrorEvent.java create mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/exceptions/TikTokLiveMessageParsingException.java delete mode 100644 Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/WebSocketClientTest.java delete mode 100644 Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveTest.java create mode 100644 TestApplication/pom.xml create mode 100644 TestApplication/src/main/java/io/github/jwdeveloper/tiktok/Main.java delete mode 100644 Tools/src/main/java/io/github/jwdeveloper/tiktok/Main.java delete mode 100644 Tools/target/classes/io/github/jwdeveloper/tiktok/Main.class diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/events/TikTokEventBuilder.java b/API/src/main/java/io/github/jwdeveloper/tiktok/events/TikTokEventBuilder.java index 17dd64d..b25600c 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/events/TikTokEventBuilder.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/events/TikTokEventBuilder.java @@ -7,6 +7,8 @@ import java.util.function.Consumer; public interface TikTokEventBuilder { + T onUnhandledSocial(Consumer event); + T onLinkMicFanTicket(Consumer event); T onEnvelope(Consumer event); @@ -17,6 +19,8 @@ public interface TikTokEventBuilder { T onLinkLayerMessage(Consumer event); + T onConnected(Consumer event); + T onCaption(Consumer event); T onQuestion(Consumer event); @@ -25,6 +29,8 @@ public interface TikTokEventBuilder { T onRoomMessage(Consumer event); + T onLivePaused(Consumer event); + T onLike(Consumer event); T onLinkMessage(Consumer event); @@ -59,17 +65,31 @@ public interface TikTokEventBuilder { T onIMDelete(Consumer event); + T onLiveEnded(Consumer event); + + T onError(Consumer event); + + T onUnhandled(Consumer event); + T onJoin(Consumer event); T onRankText(Consumer event); T onShare(Consumer event); + T onUnhandledMember(Consumer event); + T onSubNotify(Consumer event); T onLinkMicBattle(Consumer event); + T onDisconnected(Consumer event); + T onGiftBroadcast(Consumer event); + + T onUnhandledControl(Consumer event); + + T onEvent(Consumer event); } diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/events/messages/TikTokErrorEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/events/messages/TikTokErrorEvent.java new file mode 100644 index 0000000..20d0731 --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/events/messages/TikTokErrorEvent.java @@ -0,0 +1,12 @@ +package io.github.jwdeveloper.tiktok.events.messages; + +import io.github.jwdeveloper.tiktok.events.TikTokEvent; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TikTokErrorEvent extends TikTokEvent +{ + private final Throwable exception; +} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/exceptions/TikTokLiveMessageParsingException.java b/API/src/main/java/io/github/jwdeveloper/tiktok/exceptions/TikTokLiveMessageParsingException.java new file mode 100644 index 0000000..1a1c7a9 --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/exceptions/TikTokLiveMessageParsingException.java @@ -0,0 +1,23 @@ +package io.github.jwdeveloper.tiktok.exceptions; + +public class TikTokLiveMessageParsingException extends TikTokLiveException +{ + public TikTokLiveMessageParsingException() { + } + + public TikTokLiveMessageParsingException(String message) { + super(message); + } + + public TikTokLiveMessageParsingException(String message, Throwable cause) { + super(message, cause); + } + + public TikTokLiveMessageParsingException(Throwable cause) { + super(cause); + } + + public TikTokLiveMessageParsingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} 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 9f0ae51..3b89a22 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -82,8 +82,9 @@ public class TikTokLiveClient implements LiveClient { setState(ConnectionState.CONNECTED); } - public void disconnect() { - + public void disconnect() + { + webSocketClient.stop(); } 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 5b22c4f..9bb6c3b 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -102,7 +102,13 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder event) { + tikTokEventHandler.subscribe(TikTokUnhandledSocialEvent.class,event); + return this; + } + public TikTokLiveClientBuilder onLinkMicFanTicket(Consumer event) { - tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event); + tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class,event); return this; } public TikTokLiveClientBuilder onEnvelope(Consumer event) { - tikTokEventHandler.subscribe(TikTokEnvelopeEvent.class, event); + tikTokEventHandler.subscribe(TikTokEnvelopeEvent.class,event); return this; } public TikTokLiveClientBuilder onShopMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokShopMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokShopMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onDetectMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokDetectMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokDetectMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onLinkLayerMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokLinkLayerMessageEvent.class, event); - return this; - } - - public TikTokLiveClientBuilder onDisconnected(Consumer event) { - tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class, event); + tikTokEventHandler.subscribe(TikTokLinkLayerMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onConnected(Consumer event) { - tikTokEventHandler.subscribe(TikTokConnectedEvent.class, event); + tikTokEventHandler.subscribe(TikTokConnectedEvent.class,event); return this; } public TikTokLiveClientBuilder onCaption(Consumer event) { - tikTokEventHandler.subscribe(TikTokCaptionEvent.class, event); + tikTokEventHandler.subscribe(TikTokCaptionEvent.class,event); return this; } public TikTokLiveClientBuilder onQuestion(Consumer event) { - tikTokEventHandler.subscribe(TikTokQuestionEvent.class, event); + tikTokEventHandler.subscribe(TikTokQuestionEvent.class,event); return this; } public TikTokLiveClientBuilder onRoomPinMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokRoomPinMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokRoomPinMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onRoomMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokRoomMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokRoomMessageEvent.class,event); + return this; + } + + public TikTokLiveClientBuilder onLivePaused(Consumer event) { + tikTokEventHandler.subscribe(TikTokLivePausedEvent.class,event); return this; } public TikTokLiveClientBuilder onLike(Consumer event) { - tikTokEventHandler.subscribe(TikTokLikeEvent.class, event); + tikTokEventHandler.subscribe(TikTokLikeEvent.class,event); return this; } public TikTokLiveClientBuilder onLinkMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokLinkMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokLinkMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onBarrageMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokBarrageMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokBarrageMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onGiftMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokGiftMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokGiftMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onLinkMicArmies(Consumer event) { - tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event); + tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class,event); return this; } public TikTokLiveClientBuilder onEmote(Consumer event) { - tikTokEventHandler.subscribe(TikTokEmoteEvent.class, event); + tikTokEventHandler.subscribe(TikTokEmoteEvent.class,event); return this; } - public TikTokLiveClientBuilder onUnauthorizedMember(Consumer event) { - tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class, event); + public TikTokLiveClientBuilder onUnauthorizedMember( + Consumer event) { + tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class,event); return this; } public TikTokLiveClientBuilder onInRoomBanner(Consumer event) { - tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class, event); + tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class,event); return this; } public TikTokLiveClientBuilder onLinkMicMethod(Consumer event) { - tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class, event); + tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class,event); return this; } public TikTokLiveClientBuilder onSubscribe(Consumer event) { - tikTokEventHandler.subscribe(TikTokSubscribeEvent.class, event); + tikTokEventHandler.subscribe(TikTokSubscribeEvent.class,event); return this; } public TikTokLiveClientBuilder onPollMessage(Consumer event) { - tikTokEventHandler.subscribe(TikTokPollMessageEvent.class, event); + tikTokEventHandler.subscribe(TikTokPollMessageEvent.class,event); return this; } public TikTokLiveClientBuilder onFollow(Consumer event) { - tikTokEventHandler.subscribe(TikTokFollowEvent.class, event); + tikTokEventHandler.subscribe(TikTokFollowEvent.class,event); return this; } public TikTokLiveClientBuilder onRoomViewerData(Consumer event) { - tikTokEventHandler.subscribe(TikTokRoomViewerDataEvent.class, event); + tikTokEventHandler.subscribe(TikTokRoomViewerDataEvent.class,event); return this; } public TikTokLiveClientBuilder onGoalUpdate(Consumer event) { - tikTokEventHandler.subscribe(TikTokGoalUpdateEvent.class, event); + tikTokEventHandler.subscribe(TikTokGoalUpdateEvent.class,event); return this; } public TikTokLiveClientBuilder onComment(Consumer event) { - tikTokEventHandler.subscribe(TikTokCommentEvent.class, event); + tikTokEventHandler.subscribe(TikTokCommentEvent.class,event); return this; } public TikTokLiveClientBuilder onRankUpdate(Consumer event) { - tikTokEventHandler.subscribe(TikTokRankUpdateEvent.class, event); + tikTokEventHandler.subscribe(TikTokRankUpdateEvent.class,event); return this; } public TikTokLiveClientBuilder onIMDelete(Consumer event) { - tikTokEventHandler.subscribe(TikTokIMDeleteEvent.class, event); + tikTokEventHandler.subscribe(TikTokIMDeleteEvent.class,event); + return this; + } + + public TikTokLiveClientBuilder onLiveEnded(Consumer event) { + tikTokEventHandler.subscribe(TikTokLiveEndedEvent.class,event); + return this; + } + + public TikTokLiveClientBuilder onError(Consumer event) { + tikTokEventHandler.subscribe(TikTokErrorEvent.class,event); + return this; + } + + public TikTokLiveClientBuilder onUnhandled(Consumer event) { + tikTokEventHandler.subscribe(TikTokUnhandledEvent.class,event); return this; } public TikTokLiveClientBuilder onJoin(Consumer event) { - tikTokEventHandler.subscribe(TikTokJoinEvent.class, event); + tikTokEventHandler.subscribe(TikTokJoinEvent.class,event); return this; } public TikTokLiveClientBuilder onRankText(Consumer event) { - tikTokEventHandler.subscribe(TikTokRankTextEvent.class, event); + tikTokEventHandler.subscribe(TikTokRankTextEvent.class,event); return this; } public TikTokLiveClientBuilder onShare(Consumer event) { - tikTokEventHandler.subscribe(TikTokShareEvent.class, event); + tikTokEventHandler.subscribe(TikTokShareEvent.class,event); + return this; + } + + public TikTokLiveClientBuilder onUnhandledMember(Consumer event) { + tikTokEventHandler.subscribe(TikTokUnhandledMemberEvent.class,event); return this; } public TikTokLiveClientBuilder onSubNotify(Consumer event) { - tikTokEventHandler.subscribe(TikTokSubNotifyEvent.class, event); + tikTokEventHandler.subscribe(TikTokSubNotifyEvent.class,event); return this; } public TikTokLiveClientBuilder onLinkMicBattle(Consumer event) { - tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class, event); + tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class,event); + return this; + } + + public TikTokLiveClientBuilder onDisconnected(Consumer event) { + tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class,event); return this; } public TikTokLiveClientBuilder onGiftBroadcast(Consumer event) { - tikTokEventHandler.subscribe(TikTokGiftBroadcastEvent.class, event); + tikTokEventHandler.subscribe(TikTokGiftBroadcastEvent.class,event); return this; } - public TikTokLiveClientBuilder onEvent(Consumer event) - { + public TikTokLiveClientBuilder onUnhandledControl(Consumer event) { + tikTokEventHandler.subscribe(TikTokUnhandledControlEvent.class,event); + return this; + } + + public TikTokLiveClientBuilder onEvent(Consumer event) { tikTokEventHandler.subscribe(TikTokEvent.class,event); return this; } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/WebResponseHandlerBase.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/WebResponseHandlerBase.java index b478b6c..9a44eb7 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/WebResponseHandlerBase.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/handlers/WebResponseHandlerBase.java @@ -2,12 +2,15 @@ package io.github.jwdeveloper.tiktok.handlers; import com.google.protobuf.ByteString; -import io.github.jwdeveloper.tiktok.events.messages.*; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.events.TikTokEvent; -import io.github.jwdeveloper.tiktok.messages.*; +import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent; +import io.github.jwdeveloper.tiktok.events.messages.TikTokUnhandledEvent; +import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; +import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageParsingException; +import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import java.util.Arrays; +import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -18,36 +21,33 @@ public abstract class WebResponseHandlerBase { private final Map handlers; private final TikTokEventHandler tikTokEventHandler; - public WebResponseHandlerBase(TikTokEventHandler tikTokEventHandler) - { + public WebResponseHandlerBase(TikTokEventHandler tikTokEventHandler) { handlers = new HashMap<>(); - this.tikTokEventHandler =tikTokEventHandler; + this.tikTokEventHandler = tikTokEventHandler; + init(); } public abstract void init(); - public void register(Class input, Class output) - { - register(input,(e)-> + public void register(Class input, Class output) { + register(input, (e) -> { - try - { + try { var parseMethod = input.getDeclaredMethod("parseFrom", ByteString.class); - var deserialized = parseMethod.invoke(null,e.getBinary()); + var deserialized = parseMethod.invoke(null, e.getBinary()); var constructors = Arrays.stream(output.getConstructors()).filter(ea -> Arrays.stream(ea.getParameterTypes()).toList().contains(input)).findFirst(); var tiktokEvent = constructors.get().newInstance(deserialized); return (TikTokEvent)tiktokEvent; - } - catch (Exception ex) + } catch (Exception ex) { - throw new TikTokLiveException("Unable to handle parsing from class: "+input.getSimpleName()+" to class "+output.getSimpleName(),ex); + throw new TikTokLiveMessageParsingException("Unable to handle parsing from class: " + input.getSimpleName() + " to class " + output.getSimpleName(), ex); } }); } - public void register(Class clazz, Function func) - { + + public void register(Class clazz, Function func) { var haandler = new TikTokMessageHandler() { @Override public Class getHandleClazz() { @@ -64,21 +64,21 @@ public abstract class WebResponseHandlerBase { } public void handle(WebcastResponse webcastResponse) { - // System.out.println("=============================================================="); - // System.out.println("Getting messages: " + webcastResponse.getMessagesList().size()); for (var message : webcastResponse.getMessagesList()) { try { handleSingleMessage(message); - } catch (Exception e) { - throw new TikTokLiveException("Error whilst Handling Message. Stopping Client.{Environment.NewLine}Final Message: {Convert.ToBase64String(message.Binary)}", e); + } catch (Exception e) + { + var decoded = Base64.getEncoder().encodeToString(message.getBinary().toByteArray()); + + var exception = new TikTokLiveException("Error whilst Handling Message. Stopping Client. Final Message: \n"+decoded, e); + tikTokEventHandler.publish(new TikTokErrorEvent(exception)); } } - // System.out.println("=============================================================="); } private void handleSingleMessage(WebcastResponse.Message message) throws Exception { - if(!handlers.containsKey(message.getType())) - { + if (!handlers.containsKey(message.getType())) { tikTokEventHandler.publish(new TikTokUnhandledEvent(message)); return; } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketListener.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketListener.java index 126665e..1c5cc06 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketListener.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketListener.java @@ -1,7 +1,12 @@ package io.github.jwdeveloper.tiktok.websocket; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; +import io.github.jwdeveloper.tiktok.events.messages.TikTokConnectedEvent; +import io.github.jwdeveloper.tiktok.events.messages.TikTokDisconnectedEvent; +import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent; +import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageParsingException; +import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler; +import io.github.jwdeveloper.tiktok.handlers.WebResponseHandler; import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck; import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage; @@ -9,35 +14,55 @@ import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage; import java.io.ByteArrayOutputStream; import java.net.http.WebSocket; import java.nio.ByteBuffer; -import java.util.Base64; import java.util.concurrent.CompletionStage; public class TikTokWebSocketListener implements java.net.http.WebSocket.Listener { - private ByteArrayOutputStream accumulatedData = new ByteArrayOutputStream(); + private final ByteArrayOutputStream accumulatedData = new ByteArrayOutputStream(); + private final WebResponseHandler webResponseHandler; + private final TikTokEventHandler tikTokEventHandler; + + public TikTokWebSocketListener(WebResponseHandler webResponseHandler, TikTokEventHandler tikTokEventHandler) { + this.webResponseHandler = webResponseHandler; + this.tikTokEventHandler = tikTokEventHandler; + } @Override public CompletionStage onBinary(WebSocket webSocket, ByteBuffer data, boolean last) { try { - var decoded = Base64.getEncoder().encodeToString(data.array()); - System.out.println(decoded); var bytes = new byte[data.remaining()]; data.get(bytes); accumulatedData.write(bytes); if (last) { - - //handleBinary(webSocket, accumulatedData.toByteArray()); + handleBinary(webSocket, accumulatedData.toByteArray()); accumulatedData.reset(); - accumulatedData = new ByteArrayOutputStream(); } } catch (Exception e) { - e.printStackTrace(); + tikTokEventHandler.publish(new TikTokErrorEvent(e)); } webSocket.request(1); return null; } + @Override + public void onOpen(java.net.http.WebSocket webSocket) { + tikTokEventHandler.publish(new TikTokConnectedEvent()); + webSocket.request(1); + } + + @Override + public void onError(java.net.http.WebSocket webSocket, Throwable error) { + tikTokEventHandler.publish(new TikTokErrorEvent(error)); + webSocket.request(1); + } + + @Override + public CompletionStage onClose(java.net.http.WebSocket webSocket, int statusCode, String reason) { + tikTokEventHandler.publish(new TikTokDisconnectedEvent()); + return java.net.http.WebSocket.Listener.super.onClose(webSocket, statusCode, reason); + } + private void handleBinary(WebSocket webSocket, byte[] buffer) { try { @@ -46,22 +71,28 @@ public class TikTokWebSocketListener implements java.net.http.WebSocket.Listener return; } sendAckId(webSocket, websocketMessage.getId()); - - try { - - //error here var response = WebcastResponse.parseFrom(websocketMessage.getBinary()); - System.out.println("Works"); - // handleResponse(response); + webResponseHandler.handle(response); } catch (Exception e) { - throw new TikTokLiveException("Unabel to read WebcastResponse", e); + throw new TikTokLiveMessageParsingException("Unable to read WebcastResponse", e); } } catch (Exception e) { - throw new TikTokLiveException("Unabel to read WebcastWebsocketMessage", e); + throw new TikTokLiveMessageParsingException("Unable to read WebcastWebsocketMessage", e); } } + private void pingTask(WebSocket webSocket) throws InterruptedException { + while (true) { + byte[] message = new byte[]{58, 2, 104, 98}; + ByteBuffer buffer = ByteBuffer.wrap(message); + while (buffer.hasRemaining()) { + webSocket.sendPing(buffer); + } + buffer.clear(); + Thread.sleep(10); + } + } private void sendAckId(WebSocket webSocket, long id) { var serverInfo = WebcastWebsocketAck @@ -72,29 +103,4 @@ public class TikTokWebSocketListener implements java.net.http.WebSocket.Listener webSocket.sendBinary(serverInfo.toByteString().asReadOnlyByteBuffer(), true); } - @Override - public void onOpen(java.net.http.WebSocket webSocket) { - System.out.println("WebSocket opened"); - webSocket.request(1); - } - - @Override - public void onError(java.net.http.WebSocket webSocket, Throwable error) { - System.out.println("Error occurred: " + error.getMessage()); - webSocket.request(1); - } - - @Override - public CompletionStage onText(java.net.http.WebSocket webSocket, CharSequence data, boolean last) { - System.out.println("Received onText: " + data); - return java.net.http.WebSocket.Listener.super.onText(webSocket, data, last); - } - - @Override - public CompletionStage onClose(java.net.http.WebSocket webSocket, int statusCode, String reason) { - System.out.println("WebSocket closed with status code: " + statusCode + " and reason: " + reason); - return java.net.http.WebSocket.Listener.super.onClose(webSocket, statusCode, reason); - } - - } \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebsocketClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebsocketClient.java index 440b914..10add03 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 @@ -4,14 +4,15 @@ package io.github.jwdeveloper.tiktok.websocket; import io.github.jwdeveloper.tiktok.ClientSettings; import io.github.jwdeveloper.tiktok.Constants; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; -import io.github.jwdeveloper.tiktok.handlers.WebResponseHandlerBase; +import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler; +import io.github.jwdeveloper.tiktok.handlers.WebResponseHandler; import io.github.jwdeveloper.tiktok.http.HttpUtils; import io.github.jwdeveloper.tiktok.http.TikTokCookieJar; import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory; import io.github.jwdeveloper.tiktok.messages.WebcastResponse; -import org.java_websocket.drafts.Draft_6455; import java.net.URI; +import java.net.http.WebSocket; import java.nio.ByteBuffer; import java.time.Duration; import java.util.HashMap; @@ -25,119 +26,85 @@ public class TikTokWebsocketClient { private final ClientSettings clientSettings; private final TikTokCookieJar tikTokCookieJar; private final TikTokHttpRequestFactory factory; - private final WebResponseHandlerBase webResponseHandler; + private final WebResponseHandler webResponseHandler; + private final TikTokEventHandler tikTokEventHandler; + + private WebSocket webSocket; + + private boolean isConnected; public TikTokWebsocketClient(Logger logger, TikTokCookieJar tikTokCookieJar, Map clientParams, TikTokHttpRequestFactory factory, ClientSettings clientSettings, - WebResponseHandlerBase webResponseHandler) { + WebResponseHandler webResponseHandler, + TikTokEventHandler tikTokEventHandler) { this.logger = logger; this.clientParams = clientParams; this.tikTokCookieJar = tikTokCookieJar; this.clientSettings = clientSettings; this.factory = factory; this.webResponseHandler = webResponseHandler; + this.tikTokEventHandler = tikTokEventHandler; + isConnected = false; } - public void start(WebcastResponse webcastResponse) { + public void start(WebcastResponse webcastResponse) + { + if(isConnected) + { + stop(); + } if (webcastResponse.getSocketUrl().isEmpty() || webcastResponse.getSocketParamsList().isEmpty()) { throw new TikTokLiveException("Could not find Room"); } try { - - var params = webcastResponse.getSocketParamsList().get(0); - var name = params.getName(); - var value = params.getValue(); - // System.out.println("KEY: " + name + " value: " + value); - - - var headers = Constants.DefaultRequestHeaders(); - - - var clone = new TreeMap<>(clientParams); - clone.putAll(headers); - clone.put(name, value); - //clone.put("compress", "gzip"); - var url = webcastResponse.getSocketUrl(); - var wsUrl = HttpUtils.parseParametersEncode(url, clone); - logger.info("Starting Socket-Threads"); - //runningTask = Task.Run(WebSocketLoop, token); - //pollingTask = Task.Run(PingLoop, token); - startWS2(wsUrl); + var url =getWebSocketUrl(webcastResponse); + startWebSocket(url); + if (clientSettings.isHandleExistingMessagesOnConnect()) { + // HandleWebcastMessages(webcastResponse); + } } catch (Exception e) { throw new TikTokLiveException("Failed to connect to the websocket", e); } - if (clientSettings.isHandleExistingMessagesOnConnect()) { - try { - // HandleWebcastMessages(webcastResponse); - } catch (Exception e) { - throw new TikTokLiveException("Error Handling Initial Messages", e); - } - } } - public void startWS(String url) { - try { - var cookie = tikTokCookieJar.parseCookies(); - System.out.println("WssIP: " + url); - System.out.println("Cookie: " + cookie); - var map = new HashMap(); - map.put("Cookie", cookie); - - var ws = factory.openSocket() - .subprotocols("echo-protocol") - .connectTimeout(Duration.ofSeconds(15)) - .header("Cookie", cookie) - .buildAsync(URI.create(url), new TikTokWebSocketListener()).get(); + private String getWebSocketUrl(WebcastResponse webcastResponse) { + var params = webcastResponse.getSocketParamsList().get(0); + var name = params.getName(); + var value = params.getValue(); + var headers = Constants.DefaultRequestHeaders(); - while (true) { - byte[] message = new byte[]{58, 2, 104, 98}; - ByteBuffer buffer = ByteBuffer.wrap(message); - while (buffer.hasRemaining()) { - ws.sendPing(buffer); - } - buffer.clear(); - Thread.sleep(10); - } - - } catch (Exception e) { - e.printStackTrace(); - } + var clone = new TreeMap<>(clientParams); + clone.putAll(headers); + clone.put(name, value); + var url = webcastResponse.getSocketUrl(); + return HttpUtils.parseParametersEncode(url, clone); } - public void startWS2(String url) { - try { - var cookie = tikTokCookieJar.parseCookies(); - System.out.println("WssIP: " + url); - System.out.println("Cookie: " + cookie); + private WebSocket startWebSocket(String url) throws Exception { + var cookie = tikTokCookieJar.parseCookies(); + // System.out.println("WssIP: " + url); + // System.out.println("Cookie: " + cookie); - var map = new HashMap(); - map.put("Cookie", cookie); + var map = new HashMap(); + map.put("Cookie", cookie); - var client = new WebSocketClientTest(URI.create(url), new Draft_6455(), map, 1500,webResponseHandler); - client.connect(); - /* - while (true) { - byte[] message = new byte[]{58, 2, 104, 98}; - ByteBuffer buffer = ByteBuffer.wrap(message); - while (buffer.hasRemaining()) { - // client.send(buffer); - client.sendPing(); - } - buffer.clear(); - Thread.sleep(10); - }*/ - - } catch (Exception e) { - e.printStackTrace(); - } + return factory.openSocket() + .subprotocols("echo-protocol") + .connectTimeout(Duration.ofSeconds(15)) + .header("Cookie", cookie) + .buildAsync(URI.create(url), new TikTokWebSocketListener(webResponseHandler, tikTokEventHandler)).get(); } + public void stop() { - + if(isConnected && webSocket != null) + { + webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok"); + } } } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/WebSocketClientTest.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/WebSocketClientTest.java deleted file mode 100644 index 3eb5bf2..0000000 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/WebSocketClientTest.java +++ /dev/null @@ -1,120 +0,0 @@ -package io.github.jwdeveloper.tiktok.websocket; - -import com.google.protobuf.ByteString; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; -import io.github.jwdeveloper.tiktok.handlers.WebResponseHandlerBase; -import io.github.jwdeveloper.tiktok.messages.WebcastResponse; -import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck; -import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.drafts.Draft; -import org.java_websocket.handshake.ServerHandshake; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.util.Base64; -import java.util.Map; -import java.util.zip.GZIPInputStream; - -public class WebSocketClientTest extends WebSocketClient { - - private boolean debbug = false; - - private final WebResponseHandlerBase webResponseHandler; - - - public WebSocketClientTest(URI serverUri, - Draft protocolDraft, - Map httpHeaders, - int connectTimeout, - WebResponseHandlerBase webResponseHandler) { - super(serverUri, protocolDraft, httpHeaders, connectTimeout); - this.webResponseHandler = webResponseHandler; - } - - @Override - public void onMessage(ByteBuffer data) { - sendPing(); - //System.out.println("onMessage Binary"); - var bytes = new byte[data.remaining()]; - data.get(bytes); - if(debbug) - { - var decoded = Base64.getEncoder().encodeToString(bytes); - // System.out.println(decoded); - } - handleBinary(bytes); - } - - private void handleBinary(byte[] buffer) { - try { - - var websocketMessage = WebcastWebsocketMessage.parseFrom(buffer); - if (websocketMessage.getBinary().isEmpty()) { - return; - } - try { - var response = WebcastResponse.parseFrom(websocketMessage.getBinary()); - sendAckId(websocketMessage.getId()); - webResponseHandler.handle(response); - } catch (Exception e) { - throw new TikTokLiveException("Unabel to read WebcastResponse", e); - } - } catch (Exception e) { - throw new TikTokLiveException("Unabel to read WebcastWebsocketMessage", e); - } - } - - - public byte[] unGunzipFile(ByteString byteString) { - - try { - - GZIPInputStream gZIPInputStream = new GZIPInputStream(byteString.newInput()); - - - var bytes = gZIPInputStream.readAllBytes(); - - gZIPInputStream.close(); - - - return bytes; - - } catch (Exception ex) { - ex.printStackTrace(); - } - return new byte[0]; - } - private void sendAckId(long id) { - var serverInfo = WebcastWebsocketAck - .newBuilder() - .setType("ack") - .setId(id) - .build(); - send(serverInfo.toByteString().asReadOnlyByteBuffer()); - } - - - - @Override - public void onOpen(ServerHandshake serverHandshake) { - System.out.println("onOpen"); - sendPing(); - } - - @Override - public void onMessage(String s) { - sendPing(); - } - - @Override - public void onClose(int i, String s, boolean b) { - System.out.println("onClose"); - } - - @Override - public void onError(Exception e) { - System.out.println("error"); - e.printStackTrace(); - } -} diff --git a/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveTest.java b/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveTest.java deleted file mode 100644 index 9408487..0000000 --- a/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.github.jwdeveloper.tiktok; - - -import io.github.jwdeveloper.tiktok.events.messages.*; -import org.junit.Test; - -import java.io.IOException; - -public class TikTokLiveTest { - public static String TEST_USER_SUBJECT = "tv_asahi_news"; - - - @Test - public void ShouldConnect() throws IOException { - var client = TikTokLive.newClient(TEST_USER_SUBJECT) - .onConnected(this::onConnected) - .onDisconnected(this::onDisconnected) - .onRoomViewerData(this::onViewerData) - .onJoin(this::onJoin) - .onComment(this::onComment) - .onFollow(this::onFollow) - .onShare(this::onShare) - .onSubscribe(this::onSubscribe) - .onLike(this::onLike) - .onGiftMessage(this::onGiftMessage) - .onEmote(this::onEmote) - .buildAndRun(); - System.in.read(); - - } - private void onConnected(TikTokConnectedEvent e) { - print("Connected"); - } - - private void onDisconnected(TikTokDisconnectedEvent e) { - print("Disconnected"); - } - - private void onViewerData(TikTokRoomViewerDataEvent e) { - print("Viewer count is:", e.getViewerCount()); - } - - private void onJoin(TikTokJoinEvent e) { - print(e.getUser().getUniqueId(), "joined!"); - } - - private void onComment(TikTokCommentEvent e) { - print(e.getUser().getUniqueId(), e.getText()); - } - - private void onFollow(TikTokFollowEvent e) { - print(e.getNewFollower().getUniqueId(), "followed!"); - } - - private void onShare(TikTokShareEvent e) { - print(e.getUser().getUniqueId(), "shared!"); - } - - private void onSubscribe(TikTokSubscribeEvent e) { - print(e.getNewSubscriber().getUniqueId(), "subscribed!"); - } - - private void onLike(TikTokLikeEvent e) { - - print(e.getSender().getUniqueId(), "liked!"); - } - - private void onGiftMessage(TikTokGiftMessageEvent e) { - print(e.getSender().getUniqueId(), "sent", e.getAmount(), "x", e.getGift().getName()); - } - - private void onEmote(TikTokEmoteEvent e) { - print(e.getUser().getUniqueId(), "sent", e.getEmoteId()); - } - - private static void print(Object... messages) { - var sb = new StringBuilder(); - for (var message : messages) { - sb.append(message).append(" "); - } - System.out.println(sb.toString()); - } - -} \ No newline at end of file diff --git a/TestApplication/pom.xml b/TestApplication/pom.xml new file mode 100644 index 0000000..0ebd64c --- /dev/null +++ b/TestApplication/pom.xml @@ -0,0 +1,28 @@ + + + + TikTokLiveJava + io.github.jwdeveloper.tiktok + 1.0-SNAPSHOT + + 4.0.0 + + TestApplication + + + io.github.jwdeveloper.tiktok + Client + 1.0-SNAPSHOT + compile + + + + + 16 + 16 + UTF-8 + + + \ No newline at end of file diff --git a/TestApplication/src/main/java/io/github/jwdeveloper/tiktok/Main.java b/TestApplication/src/main/java/io/github/jwdeveloper/tiktok/Main.java new file mode 100644 index 0000000..caa6026 --- /dev/null +++ b/TestApplication/src/main/java/io/github/jwdeveloper/tiktok/Main.java @@ -0,0 +1,87 @@ +package io.github.jwdeveloper.tiktok; + +import io.github.jwdeveloper.tiktok.events.messages.*; + +import java.io.IOException; + +public class Main { + + public static String TEST_USER_SUBJECT = "tv_asahi_news"; + + public static void main(String[] args) throws IOException { + var client = TikTokLive.newClient(TEST_USER_SUBJECT) + .onConnected(Main::onConnected) + .onDisconnected(Main::onDisconnected) + .onRoomViewerData(Main::onViewerData) + .onJoin(Main::onJoin) + .onComment(Main::onComment) + .onFollow(Main::onFollow) + .onShare(Main::onShare) + .onSubscribe(Main::onSubscribe) + .onLike(Main::onLike) + .onGiftMessage(Main::onGiftMessage) + .onEmote(Main::onEmote) + .onError(tikTokErrorEvent -> + { + tikTokErrorEvent.getException().printStackTrace(); + }) + .buildAndRun(); + + + var viewers = client.getMeta().getViewersCount(); + System.in.read(); + } + + private static void onConnected(TikTokConnectedEvent e) { + print("Connected"); + } + + private static void onDisconnected(TikTokDisconnectedEvent e) { + print("Disconnected"); + } + + private static void onViewerData(TikTokRoomViewerDataEvent e) { + print("Viewer count is:", e.getViewerCount()); + } + + private static void onJoin(TikTokJoinEvent e) { + print(e.getUser().getUniqueId(), "joined!"); + } + + private static void onComment(TikTokCommentEvent e) { + print(e.getUser().getUniqueId(), e.getText()); + } + + private static void onFollow(TikTokFollowEvent e) { + print(e.getNewFollower().getUniqueId(), "followed!"); + } + + private static void onShare(TikTokShareEvent e) { + print(e.getUser().getUniqueId(), "shared!"); + } + + private static void onSubscribe(TikTokSubscribeEvent e) { + print(e.getNewSubscriber().getUniqueId(), "subscribed!"); + } + + private static void onLike(TikTokLikeEvent e) { + + print(e.getSender().getUniqueId(), "liked!"); + } + + private static void onGiftMessage(TikTokGiftMessageEvent e) { + print(e.getSender().getUniqueId(), "sent", e.getAmount(), "x", e.getGift().getName()); + } + + private static void onEmote(TikTokEmoteEvent e) { + print(e.getUser().getUniqueId(), "sent", e.getEmoteId()); + } + + private static void print(Object... messages) { + var sb = new StringBuilder(); + for (var message : messages) { + sb.append(message).append(" "); + } + System.out.println(sb.toString()); + } +} \ No newline at end of file diff --git a/Tools/src/main/java/io/github/jwdeveloper/tiktok/Main.java b/Tools/src/main/java/io/github/jwdeveloper/tiktok/Main.java deleted file mode 100644 index e73011b..0000000 --- a/Tools/src/main/java/io/github/jwdeveloper/tiktok/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.jwdeveloper.tiktok; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world!"); - } -} \ No newline at end of file diff --git a/Tools/src/main/java/io/github/jwdeveloper/tiktok/intefacee/EventsInterfaceGenerator.java b/Tools/src/main/java/io/github/jwdeveloper/tiktok/intefacee/EventsInterfaceGenerator.java index d7355cf..e541d6d 100644 --- a/Tools/src/main/java/io/github/jwdeveloper/tiktok/intefacee/EventsInterfaceGenerator.java +++ b/Tools/src/main/java/io/github/jwdeveloper/tiktok/intefacee/EventsInterfaceGenerator.java @@ -20,10 +20,10 @@ public class EventsInterfaceGenerator { // System.out.println(clazz.getName()); } - // var result = generateInterface("io.github.jwdeveloper.tiktok.events", classes);System.out.println(result); + //var result = generateInterface("io.github.jwdeveloper.tiktok.events", classes);System.out.println(result); - var result = getBuilderImplementation("x",classes); System.out.println(result); + var result = getBuilderImplementation("x",classes); System.out.println(result); } @@ -36,8 +36,13 @@ public class EventsInterfaceGenerator { // Generate constructors for (var clazz : eventsClasses) { var clazzName = clazz.getSimpleName(); - var methodName = clazzName.replace("TikTok", ""); - methodName = methodName.replace("Event", ""); + + var methodName = clazzName; + methodName = clazzName.replace("TikTok", ""); + if(!clazz.equals(TikTokEvent.class)) + { + methodName = methodName.replace("Event", ""); + } MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder("on" + methodName); @@ -76,8 +81,13 @@ public class EventsInterfaceGenerator { // Generate constructors for (var clazz : eventsClasses) { var clazzName = clazz.getSimpleName(); - var methodName = clazzName.replace("TikTok", ""); - methodName ="on" + methodName.replace("Event", ""); + var methodName = clazzName; + methodName = clazzName.replace("TikTok", ""); + if(!clazz.equals(TikTokEvent.class)) + { + methodName = methodName.replace("Event", ""); + } + methodName ="on" + methodName; MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder( methodName); diff --git a/Tools/target/classes/io/github/jwdeveloper/tiktok/Main.class b/Tools/target/classes/io/github/jwdeveloper/tiktok/Main.class deleted file mode 100644 index 29e7a4b7f125a5703058e4cca2150bdbe74fc9a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574 zcmaiyJx>Bb5Qg97>o`0GLGW8SZNS1SEg&`~#sWVQ42{(S8(27Q$+1BESz2gf;Scaf z8D|d@H6d|}*~~jL&rEjq=xByiT?SI0 zGsG*^mW?!Q3mFG1aQ>t>A17LjC`(PLsMv@=Qh9fw40Kb6f{$uxtT8xO!uM5qu0p@- zW{|_Wg}j3THW&&^su--XVfX=ID+_KkAVFo&t5sVTN(?Ub>Pg+7w!Oi;D`vu1V-b2< z4z(J3*IZJ0;zD%=L$)CUaWfsYMR?EKJ}LQz>TtisLuvePF`@f%!f@R9ms}(CDC$tE zJS>}#;ymmTtgs}^kZG!E*b$e~%wn5aPt8NXMUmEHP7KT}i0meL9^H)YG#XCNx=IQ0u+;{b=GGnC23;C=yO C_I@4! diff --git a/Tools/target/classes/io/github/jwdeveloper/tiktok/intefacee/EventsInterfaceGenerator.class b/Tools/target/classes/io/github/jwdeveloper/tiktok/intefacee/EventsInterfaceGenerator.class index 2858c95b8a460720802d4c513aff03ca8bef2a91..46a980590eb1dc4560255e69b35e0a77b1b68998 100644 GIT binary patch delta 2252 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 DV%NtW diff --git a/pom.xml b/pom.xml index 814d26a..5a50bc9 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ API Client Tools + TestApplication