Skip to content

Event lifetime

Jacek W edited this page Dec 11, 2023 · 6 revisions

In this articule you will learn what happends under the hood when you are using this example code

     TikTokLive.newClient("test")
                .onGift((liveClient, event) ->
                {
                    System.out.println("hello world");
                }).buildAndConnect();

TikTokWebSocketListener

Initialy library runs webosocket client that listen for incoming data from tiktok. Data is coming in the binary format.

 @Override
    public void onMessage(ByteBuffer bytes)
    {
        try {
            handleBinary(bytes.array());
        } catch (Exception e) {
            tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e));
        }
        if(isNotClosing())
        {
            sendPing();
        }
    }
 private void handleBinary(byte[] buffer) {
        var websocketMessageOptional = getWebcastWebsocketMessage(buffer);
        if (websocketMessageOptional.isEmpty()) {
            return;
        }
        var websocketMessage = websocketMessageOptional.get();
        var webResponse = getWebResponseMessage(websocketMessage.getPayload());

        if(webResponse.getNeedsAck())
        {
            //For some reason while send ack id, server get disconnected
           // sendAckId(webResponse.getFetchInterval());
        }

        webResponseHandler.handle(tikTokLiveClient, webResponse);
    }

WebcastPushFrame

First step is to parse binary to WebcastPushFrame class. This class contains most rudamenday data such as Timestand, payload, header, sesionId

   private Optional<WebcastPushFrame> getWebcastWebsocketMessage(byte[] buffer) {
        try {
            var websocketMessage = WebcastPushFrame.parseFrom(buffer);
            if (websocketMessage.getPayload().isEmpty()) {
                return Optional.empty();
            }
            return Optional.of(websocketMessage);
        } catch (Exception e) {
            throw new TikTokProtocolBufferException("Unable to parse WebcastPushFrame", buffer, e);
        }

WebcastResponse

Next we are parsing WebcastPushFrame.Payload to WebcastResponse. WebcastResponse give use way more data, but the most important one is WebcastResponse.getMessagesList(). It is lists of messages "events" that tiktok is sending to us.

   private WebcastResponse getWebResponseMessage(ByteString buffer) {
        try {
            return WebcastResponse.parseFrom(buffer);
        } catch (Exception e) {
            throw new TikTokProtocolBufferException("Unable to parse WebcastResponse", buffer.toByteArray(), e);
        }
    }

That's it for the websocket part, we now have WebcastResponse that is then processed in the TikTokMessageHandler

TikTokMessageHandler

This class is responsible from manage WebcastResponse and map events from tiktok to TikTokLiveJava events It gets all messages from WebcastResponse object and then map then one by one

worth to mention, before mapping TikTokWebsocketResponseEvent is triggered, that contains WebcastResponse object

   public void handle(LiveClient client, WebcastResponse webcastResponse) {
        tikTokEventHandler.publish(client, new TikTokWebsocketResponseEvent(webcastResponse));
        for (var message : webcastResponse.getMessagesList()) {
            try {
                handleSingleMessage(client, message);
            } catch (Exception e) {
                var exception = new TikTokLiveMessageException(message, webcastResponse, e);
                tikTokEventHandler.publish(client, new TikTokErrorEvent(exception));
            }
        }
    }

Each message is processed by method handleSingleMessage

public void handleSingleMessage(LiveClient client, WebcastResponse.Message message) throws Exception {
        var messageClassName = message.getMethod(); //getting message name, for example WebcastGiftEvent
        if (!handlers.containsKey(messageClassName)) {  //checking if mapping handler was registered for message name
            tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(message)); //if there is no handler, fire TikTokWebsocketUnhandledMessageEvent
            return;
        }
        var handler = handlers.get(messageClassName); //getitng handler
        var stopwatch = new Stopwatch();
        stopwatch.start();     
        var events = handler.handle(message.getPayload().toByteArray()); //handle mapping
        var handlingTimeInMs = stopwatch.stop();
        var metadata = new MessageMetaData(Duration.ofNanos(handlingTimeInMs)); //creating mapping metdataobject

        for (var event : events) {   //mapping handler might map TikTok message to more then one events, thats way there is a list
            tikTokEventHandler.publish(client, new TikTokWebsocketMessageEvent(message, event, metadata)); // for every mapped message there is triggered TikTokWebsocketMessageEvent 
            tikTokEventHandler.publish(client, event); //fire actual event, for example WebcastGiftMessage, was mapped to TikTokGiftEvent
        }
    }

TikTokMessageHandlerRegistration

In this class all the mapping handlers are registered, here you can see some of them

    public void init() {

        //ConnectionEvents events
        registerMapping(WebcastControlMessage.class, this::handleWebcastControlMessage);

        //Room status events
        registerMapping(WebcastLiveIntroMessage.class, roomInfoHandler::handleIntro);
        registerMapping(WebcastRoomUserSeqMessage.class, roomInfoHandler::handleUserRanking);

        registerMapping(WebcastCaptionMessage.class, TikTokCaptionEvent.class);

        //User Interactions events
        registerMapping(WebcastChatMessage.class, TikTokCommentEvent.class);
        registerMappings(WebcastLikeMessage.class, this::handleLike);
        registerMappings(WebcastGiftMessage.class, giftHandler::handleGift);
        registerMapping(WebcastSocialMessage.class, socialHandler::handle);
        registerMappings(WebcastMemberMessage.class, this::handleMemberMessage);
     }

TikTokEventObserver

mapped event was send to TikTokEventObserver class by using tikTokEventHandler.publish() method this is the last step From the example code that was showed at the beggining of articule

.onGift((liveClient, event) ->
                {
                    System.out.println("hello world");
                })

has been registed to events map in way

events.put(TikTokGiftEvent.class, (liveclient,event) -> { System.out.printLine("hello world")});
    private final Map<Class<?>, Set<EventConsumer>> events;

    public void publish(LiveClient tikTokLiveClient, TikTokEvent tikTokEvent) {
        if (events.containsKey(TikTokEvent.class)) {
            var handlers = events.get(TikTokEvent.class);
            for (var handle : handlers) {
                handle.onEvent(tikTokLiveClient, tikTokEvent);
            }
        }


        if (!events.containsKey(tikTokEvent.getClass())) {
            return;
        }
        var handlers = events.get(tikTokEvent.getClass());
        for (var handler : handlers) {
            handler.onEvent(tikTokLiveClient, tikTokEvent);
        }
    }

this method is getting class of tikTokEvent object and is checking if there was registered some eventhandlers on this class if so handler.onEvent(tikTokLiveClient, tikTokEvent); handler is triggered so as final result you will see "Hello world" message in your console, that's it