onCustomEvent() <- registering custom events
  onMapping() <-  custom mappings
  check out 'CustomMappingExample'

  more gifs has been added
  exceptions are more explicit
This commit is contained in:
JW
2023-12-11 23:18:40 +01:00
parent 46d5f15d3f
commit 3832db111e
28 changed files with 841 additions and 412 deletions

View File

@@ -40,10 +40,14 @@ import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandl
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
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.handlers.TikTokMessageHandler;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.mappers.TikTokLiveMapper;
import io.github.jwdeveloper.tiktok.mappers.TikTokMapper;
import io.github.jwdeveloper.tiktok.mappers.events.TikTokCommonEventHandler;
import io.github.jwdeveloper.tiktok.mappers.events.TikTokGiftEventHandler;
import io.github.jwdeveloper.tiktok.mappers.events.TikTokRoomInfoEventHandler;
import io.github.jwdeveloper.tiktok.mappers.events.TikTokSocialMediaEventHandler;
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
@@ -54,6 +58,7 @@ import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
import io.github.jwdeveloper.tiktok.messages.webcast.*;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
@@ -67,9 +72,11 @@ import java.util.logging.*;
public class TikTokLiveClientBuilder implements LiveClientBuilder {
protected final ClientSettings clientSettings;
protected final Logger logger;
protected final TikTokEventObserver tikTokEventHandler;
protected final List<TikTokEventListener> listeners;
protected Consumer<TikTokMapper> onCustomMappings;
public TikTokLiveClientBuilder(String userName) {
this.tikTokEventHandler = new TikTokEventObserver();
@@ -77,10 +84,18 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
this.clientSettings.setHostName(userName);
this.logger = Logger.getLogger(TikTokLive.class.getSimpleName() + " " + userName);
this.listeners = new ArrayList<>();
this.onCustomMappings = (e) -> {
};
}
public TikTokLiveClientBuilder configure(Consumer<ClientSettings> consumer) {
consumer.accept(clientSettings);
public LiveClientBuilder onMapping(Consumer<TikTokMapper> onCustomMappings) {
this.onCustomMappings = onCustomMappings;
return this;
}
public TikTokLiveClientBuilder configure(Consumer<ClientSettings> onConfigure) {
onConfigure.accept(clientSettings);
return this;
}
@@ -104,8 +119,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
throw new TikTokLiveException("HostName can not be null");
}
if (clientSettings.getHostName().startsWith("@"))
{
if (clientSettings.getHostName().startsWith("@")) {
clientSettings.setHostName(clientSettings.getHostName().substring(1));
}
@@ -135,9 +149,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
if (!clientSettings.isPrintToConsole()) {
logger.setLevel(Level.OFF);
}
}
public LiveClient build() {
@@ -152,23 +163,14 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
var apiService = new TikTokApiService(apiClient, logger, clientSettings);
var giftManager = new TikTokGiftManager(logger);
var eventMapper = new TikTokGenericEventMapper();
var eventsMapper = createMapper(giftManager, tiktokRoomInfo);
var messageHandler = new TikTokMessageHandler(tikTokEventHandler, eventsMapper);
var giftHandler = new TikTokGiftEventHandler(giftManager);
var roomInfoHandler = new TikTokRoomInfoEventHandler(tiktokRoomInfo);
var socialHandler = new TikTokSocialMediaEventHandler(tiktokRoomInfo);
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler,
roomInfoHandler,
eventMapper,
giftHandler,
socialHandler
);
var webSocketClient = new TikTokWebSocketClient(logger,
cookieJar,
clientSettings,
webResponseHandler,
messageHandler,
tikTokEventHandler);
return new TikTokLiveClient(tiktokRoomInfo,
@@ -181,6 +183,64 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
logger);
}
public TikTokLiveMapper createMapper(GiftManager giftManager, TikTokRoomInfo roomInfo) {
var eventMapper = new TikTokGenericEventMapper();
var mapper = new TikTokLiveMapper(eventMapper);
//ConnectionEvents events
var commonHandler = new TikTokCommonEventHandler();
var giftHandler = new TikTokGiftEventHandler(giftManager);
var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo);
var socialHandler = new TikTokSocialMediaEventHandler(roomInfo);
mapper.bytesToEvent(WebcastControlMessage.class, commonHandler::handleWebcastControlMessage);
//Room status events
mapper.bytesToEvent(WebcastLiveIntroMessage.class, roomInfoHandler::handleIntro);
mapper.bytesToEvent(WebcastRoomUserSeqMessage.class, roomInfoHandler::handleUserRanking);
mapper.webcastObjectToConstructor(WebcastCaptionMessage.class, TikTokCaptionEvent.class);
//User Interactions events
mapper.webcastObjectToConstructor(WebcastChatMessage.class, TikTokCommentEvent.class);
mapper.bytesToEvents(WebcastLikeMessage.class, roomInfoHandler::handleLike);
mapper.bytesToEvents(WebcastGiftMessage.class, giftHandler::handleGift);
mapper.bytesToEvent(WebcastSocialMessage.class, socialHandler::handle);
mapper.bytesToEvents(WebcastMemberMessage.class, roomInfoHandler::handleMemberMessage);
//Host Interaction events
mapper.bytesToEvent(WebcastPollMessage.class, commonHandler::handlePollEvent);
mapper.bytesToEvent(WebcastRoomPinMessage.class, commonHandler::handlePinMessage);
mapper.webcastObjectToConstructor(WebcastGoalUpdateMessage.class, TikTokGoalUpdateEvent.class);
//LinkMic events
mapper.webcastObjectToConstructor(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class);
mapper.webcastObjectToConstructor(WebcastLinkMicArmies.class, TikTokLinkMicArmiesEvent.class);
mapper.webcastObjectToConstructor(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class);
mapper.webcastObjectToConstructor(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class);
//Rank events
mapper.webcastObjectToConstructor(WebcastRankTextMessage.class, TikTokRankTextEvent.class);
mapper.webcastObjectToConstructor(WebcastRankUpdateMessage.class, TikTokRankUpdateEvent.class);
mapper.webcastObjectToConstructor(WebcastHourlyRankMessage.class, TikTokRankUpdateEvent.class);
//Others events
mapper.webcastObjectToConstructor(WebcastInRoomBannerMessage.class, TikTokInRoomBannerEvent.class);
mapper.webcastObjectToConstructor(WebcastMsgDetectMessage.class, TikTokDetectEvent.class);
mapper.webcastObjectToConstructor(WebcastBarrageMessage.class, TikTokBarrageEvent.class);
mapper.webcastObjectToConstructor(WebcastUnauthorizedMemberMessage.class, TikTokUnauthorizedMemberEvent.class);
mapper.webcastObjectToConstructor(WebcastOecLiveShoppingMessage.class, TikTokShopEvent.class);
mapper.webcastObjectToConstructor(WebcastImDeleteMessage.class, TikTokIMDeleteEvent.class);
mapper.webcastObjectToConstructor(WebcastQuestionNewMessage.class, TikTokQuestionEvent.class);
mapper.bytesToEvents(WebcastEnvelopeMessage.class, commonHandler::handleEnvelop);
mapper.webcastObjectToConstructor(WebcastSubNotifyMessage.class, TikTokSubscribeEvent.class);
mapper.webcastObjectToConstructor(WebcastEmoteChatMessage.class, TikTokEmoteEvent.class);
onCustomMappings.accept(mapper);
return mapper;
}
public LiveClient buildAndConnect() {
var client = build();
client.connect();
@@ -191,11 +251,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return build().connectAsync();
}
public LiveClientBuilder onCustomEvent(EventConsumer<CustomEvent> event) {
tikTokEventHandler.subscribe(CustomEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onUnhandledSocial(
EventConsumer<TikTokUnhandledSocialEvent> event) {
@@ -203,7 +258,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
// @Override
// @Override
public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) {
tikTokEventHandler.subscribe(TikTokChestEvent.class, event);
return this;
@@ -259,6 +314,13 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
@Override
public <E extends TikTokEvent> LiveClientBuilder onCustomEvent(Class<E> eventClazz, EventConsumer<E> event) {
tikTokEventHandler.subscribe(eventClazz, event);
return this;
}
@Override
public LiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event);
@@ -266,7 +328,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
}
public TikTokLiveClientBuilder onLivePaused(EventConsumer<TikTokLivePausedEvent> event) {
tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
return this;

View File

@@ -25,46 +25,28 @@ package io.github.jwdeveloper.tiktok.handlers;
import io.github.jwdeveloper.tiktok.data.dto.MessageMetaData;
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
import io.github.jwdeveloper.tiktok.mappers.TikTokLiveMapper;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import io.github.jwdeveloper.tiktok.utils.Stopwatch;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public abstract class TikTokMessageHandler {
public class TikTokMessageHandler {
private final Map<String, io.github.jwdeveloper.tiktok.handler.TikTokMessageHandler> handlers;
private final TikTokEventObserver tikTokEventHandler;
protected final TikTokGenericEventMapper mapper;
private final TikTokLiveMapper mapper;
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, TikTokGenericEventMapper mapper) {
handlers = new HashMap<>();
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, TikTokLiveMapper mapper) {
this.tikTokEventHandler = tikTokEventHandler;
this.mapper = mapper;
}
public void registerMapping(Class<?> clazz, Function<byte[], TikTokEvent> func) {
handlers.put(clazz.getSimpleName(), messagePayload -> List.of(func.apply(messagePayload)));
}
public void registerMappings(Class<?> clazz, Function<byte[], List<TikTokEvent>> func) {
handlers.put(clazz.getSimpleName(), func::apply);
}
public void registerMapping(Class<?> input, Class<?> output) {
registerMapping(input, (e) -> mapper.mapToEvent(input, output, e));
}
public void handle(LiveClient client, WebcastResponse webcastResponse) {
tikTokEventHandler.publish(client, new TikTokWebsocketResponseEvent(webcastResponse));
@@ -80,14 +62,13 @@ public abstract class TikTokMessageHandler {
public void handleSingleMessage(LiveClient client, WebcastResponse.Message message) throws Exception {
var messageClassName = message.getMethod();
if (!handlers.containsKey(messageClassName)) {
if (!mapper.isRegistered(messageClassName)) {
tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(message));
return;
}
var handler = handlers.get(messageClassName);
var stopwatch = new Stopwatch();
stopwatch.start();
var events = handler.handle(message.getPayload().toByteArray());
var events = mapper.handleMapping(messageClassName, message.getPayload().toByteArray());
var handlingTimeInMs = stopwatch.stop();
var metadata = new MessageMetaData(Duration.ofNanos(handlingTimeInMs));

View File

@@ -1,206 +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.handlers;
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;
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.TikTokRoomPinEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.data.models.chest.Chest;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
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 lombok.SneakyThrows;
import java.util.Collections;
import java.util.List;
public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
private final TikTokGiftEventHandler giftHandler;
private final TikTokRoomInfoEventHandler roomInfoHandler;
private final TikTokSocialMediaEventHandler socialHandler;
public TikTokMessageHandlerRegistration(TikTokEventObserver tikTokEventHandler,
TikTokRoomInfoEventHandler roomInfoHandler,
TikTokGenericEventMapper genericTikTokEventMapper,
TikTokGiftEventHandler tikTokGiftEventHandler,
TikTokSocialMediaEventHandler tikTokSocialMediaEventHandler) {
super(tikTokEventHandler, genericTikTokEventMapper);
this.giftHandler = tikTokGiftEventHandler;
this.roomInfoHandler = roomInfoHandler;
this.socialHandler = tikTokSocialMediaEventHandler;
init();
}
public void init() {
registerMapping(WebcastGiftMessage.class, bytes ->
{
try {
WebcastGiftMessage tiktokData = WebcastGiftMessage.parseFrom(bytes);
io.github.jwdeveloper.tiktok.messages.data.User tiktokProtocolBufferUser = tiktokData.getUser();
io.github.jwdeveloper.tiktok.data.models.users.User tiktokLiveJavaUser = User.map(tiktokProtocolBufferUser);
return new CustomEvent(tiktokLiveJavaUser, "hello word");
} catch (Exception e) {
throw new TikTokLiveException("Unable to parse our custom event", e);
}
});
//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);
//Host Interaction events
registerMapping(WebcastPollMessage.class, this::handlePollEvent);
registerMapping(WebcastRoomPinMessage.class, this::handlePinMessage);
registerMapping(WebcastGoalUpdateMessage.class, TikTokGoalUpdateEvent.class);
//LinkMic events
registerMapping(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class);
registerMapping(WebcastLinkMicArmies.class, TikTokLinkMicArmiesEvent.class);
registerMapping(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class);
registerMapping(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class);
//Rank events
registerMapping(WebcastRankTextMessage.class, TikTokRankTextEvent.class);
registerMapping(WebcastRankUpdateMessage.class, TikTokRankUpdateEvent.class);
registerMapping(WebcastHourlyRankMessage.class, TikTokRankUpdateEvent.class);
//Others events
registerMapping(WebcastInRoomBannerMessage.class, TikTokInRoomBannerEvent.class);
registerMapping(WebcastMsgDetectMessage.class, TikTokDetectEvent.class);
registerMapping(WebcastBarrageMessage.class, TikTokBarrageEvent.class);
registerMapping(WebcastUnauthorizedMemberMessage.class, TikTokUnauthorizedMemberEvent.class);
registerMapping(WebcastOecLiveShoppingMessage.class, TikTokShopEvent.class);
registerMapping(WebcastImDeleteMessage.class, TikTokIMDeleteEvent.class);
registerMapping(WebcastQuestionNewMessage.class, TikTokQuestionEvent.class);
registerMappings(WebcastEnvelopeMessage.class, this::handleEnvelop);
registerMapping(WebcastSubNotifyMessage.class, TikTokSubscribeEvent.class);
registerMapping(WebcastEmoteChatMessage.class, TikTokEmoteEvent.class);
}
@SneakyThrows
private TikTokEvent handleWebcastControlMessage(byte[] msg) {
var message = WebcastControlMessage.parseFrom(msg);
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 List<TikTokEvent> handleMemberMessage(byte[] msg) {
var message = WebcastMemberMessage.parseFrom(msg);
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 List<TikTokEvent> handleLike(byte[] msg) {
var event = (TikTokLikeEvent) mapper.mapToEvent(WebcastLikeMessage.class, TikTokLikeEvent.class, msg);
var roomInfoEvent = roomInfoHandler.handleRoomInfo(tikTokRoomInfo ->
{
tikTokRoomInfo.setLikesCount(event.getTotalLikes());
});
return List.of(event, roomInfoEvent);
}
@SneakyThrows
private TikTokEvent handlePinMessage(byte[] msg) {
var pinMessage = WebcastRoomPinMessage.parseFrom(msg);
var chatMessage = WebcastChatMessage.parseFrom(pinMessage.getPinnedMessage());
var chatEvent = new TikTokCommentEvent(chatMessage);
return new TikTokRoomPinEvent(pinMessage, chatEvent);
}
//TODO Probably not working
@SneakyThrows
private TikTokEvent handlePollEvent(byte[] msg) {
var poolMessage = WebcastPollMessage.parseFrom(msg);
return switch (poolMessage.getMessageType()) {
case 0 -> new TikTokPollStartEvent(poolMessage);
case 1 -> new TikTokPollEndEvent(poolMessage);
case 2 -> new TikTokPollUpdateEvent(poolMessage);
default -> new TikTokPollEvent(poolMessage);
};
}
@SneakyThrows
private List<TikTokEvent> handleEnvelop(byte[] data) {
var msg = WebcastEnvelopeMessage.parseFrom(data);
if (msg.getDisplay() != EnvelopeDisplay.EnvelopeDisplayNew) {
return Collections.emptyList();
}
var totalDiamonds = msg.getEnvelopeInfo().getDiamondCount();
var totalUsers = msg.getEnvelopeInfo().getPeopleCount();
var chest = new Chest(totalDiamonds, totalUsers);
return List.of(new TikTokChestEvent(chest, msg));
}
}

View File

@@ -74,7 +74,7 @@ public class TikTokGenericEventMapper {
}
}
private Method getParsingMethod(Class<?> input) throws NoSuchMethodException {
public Method getParsingMethod(Class<?> input) throws NoSuchMethodException {
if (methodCache.containsKey(input)) {
return methodCache.get(input);
}

View File

@@ -0,0 +1,88 @@
package io.github.jwdeveloper.tiktok.mappers;
import com.google.protobuf.GeneratedMessageV3;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokMessageMappingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class TikTokLiveMapper implements TikTokMapper {
private final Map<String, Function<byte[], List<TikTokEvent>>> mappers;
private final TikTokGenericEventMapper genericMapper;
public TikTokLiveMapper(TikTokGenericEventMapper genericMapper) {
this.mappers = new HashMap<>();
this.genericMapper = genericMapper;
}
@Override
public void bytesToEvent(String messageName, Function<byte[], TikTokEvent> onMapping) {
mappers.put(messageName, messagePayload -> List.of(onMapping.apply(messagePayload)));
}
@Override
public void bytesToEvents(String messageName, Function<byte[], List<TikTokEvent>> onMapping) {
mappers.put(messageName, onMapping::apply);
}
public void bytesToEvent(Class<? extends GeneratedMessageV3> clazz, Function<byte[], TikTokEvent> onMapping) {
mappers.put(clazz.getSimpleName(), messagePayload -> List.of(onMapping.apply(messagePayload)));
}
public void bytesToEvents(Class<? extends GeneratedMessageV3> clazz, Function<byte[], List<TikTokEvent>> onMapping) {
mappers.put(clazz.getSimpleName(), onMapping::apply);
}
@Override
public void webcastObjectToConstructor(Class<? extends GeneratedMessageV3> sourceClass, Class<? extends TikTokEvent> outputClass) {
bytesToEvent(sourceClass, (e) -> genericMapper.mapToEvent(sourceClass, outputClass, e));
}
@Override
public <T extends GeneratedMessageV3> void webcastObjectToEvent(Class<T> source, Function<T, TikTokEvent> onMapping) {
bytesToEvent(source, (bytes) ->
{
try {
var parsingMethod = genericMapper.getParsingMethod(source);
var sourceObject = parsingMethod.invoke(null, bytes);
var event = onMapping.apply((T) sourceObject);
return event;
} catch (Exception e) {
throw new TikTokMessageMappingException(source, "can't find parsing method", e);
}
});
}
@Override
public <T extends GeneratedMessageV3> void webcastObjectToEvents(Class<T> source, Function<T, List<TikTokEvent>> onMapping) {
bytesToEvents(source, (bytes) ->
{
try {
var parsingMethod = genericMapper.getParsingMethod(source);
var sourceObject = parsingMethod.invoke(null, bytes);
var event = onMapping.apply((T) sourceObject);
return event;
} catch (Exception e) {
throw new TikTokMessageMappingException(source, "can't find parsing method", e);
}
});
}
public boolean isRegistered(String input) {
return mappers.containsKey(input);
}
public List<TikTokEvent> handleMapping(String input, byte[] bytes) {
if (!isRegistered(input)) {
return List.of();
}
var mapper = mappers.get(input);
var events = mapper.apply(bytes);
return events;
}
}

View File

@@ -20,7 +20,7 @@
* 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.handlers.events;
package io.github.jwdeveloper.tiktok.mappers.events;
public class TikTokChestEventHandler {
}

View File

@@ -0,0 +1,66 @@
package io.github.jwdeveloper.tiktok.mappers.events;
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;
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.TikTokRoomPinEvent;
import io.github.jwdeveloper.tiktok.data.models.chest.Chest;
import io.github.jwdeveloper.tiktok.messages.enums.EnvelopeDisplay;
import io.github.jwdeveloper.tiktok.messages.webcast.*;
import lombok.SneakyThrows;
import java.util.Collections;
import java.util.List;
public class TikTokCommonEventHandler
{
@SneakyThrows
public TikTokEvent handleWebcastControlMessage(byte[] msg) {
var message = WebcastControlMessage.parseFrom(msg);
return switch (message.getAction()) {
case STREAM_PAUSED -> new TikTokLivePausedEvent();
case STREAM_ENDED -> new TikTokLiveEndedEvent();
case STREAM_UNPAUSED -> new TikTokLiveUnpausedEvent();
default -> new TikTokUnhandledControlEvent(message);
};
}
@SneakyThrows
public TikTokEvent handlePinMessage(byte[] msg) {
var pinMessage = WebcastRoomPinMessage.parseFrom(msg);
var chatMessage = WebcastChatMessage.parseFrom(pinMessage.getPinnedMessage());
var chatEvent = new TikTokCommentEvent(chatMessage);
return new TikTokRoomPinEvent(pinMessage, chatEvent);
}
//TODO Probably not working
@SneakyThrows
public TikTokEvent handlePollEvent(byte[] msg) {
var poolMessage = WebcastPollMessage.parseFrom(msg);
return switch (poolMessage.getMessageType()) {
case 0 -> new TikTokPollStartEvent(poolMessage);
case 1 -> new TikTokPollEndEvent(poolMessage);
case 2 -> new TikTokPollUpdateEvent(poolMessage);
default -> new TikTokPollEvent(poolMessage);
};
}
@SneakyThrows
public List<TikTokEvent> handleEnvelop(byte[] data) {
var msg = WebcastEnvelopeMessage.parseFrom(data);
if (msg.getDisplay() != EnvelopeDisplay.EnvelopeDisplayNew) {
return Collections.emptyList();
}
var totalDiamonds = msg.getEnvelopeInfo().getDiamondCount();
var totalUsers = msg.getEnvelopeInfo().getPeopleCount();
var chest = new Chest(totalDiamonds, totalUsers);
return List.of(new TikTokChestEvent(chest, msg));
}
}

View File

@@ -20,7 +20,7 @@
* 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.handlers.events;
package io.github.jwdeveloper.tiktok.mappers.events;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;

View File

@@ -20,17 +20,24 @@
* 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.handlers.events;
package io.github.jwdeveloper.tiktok.mappers.events;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.data.events.TikTokSubscribeEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokUnhandledMemberEvent;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.data.models.RankingUser;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLikeMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLiveIntroMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastMemberMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomUserSeqMessage;
import lombok.SneakyThrows;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -76,4 +83,33 @@ public class TikTokRoomInfoEventHandler {
tikTokRoomInfo.setLanguage(language);
});
}
@SneakyThrows
public List<TikTokEvent> handleMemberMessage(byte[] msg) {
var message = WebcastMemberMessage.parseFrom(msg);
var event = switch (message.getAction()) {
case JOINED -> new TikTokJoinEvent(message);
case SUBSCRIBED -> new TikTokSubscribeEvent(message);
default -> new TikTokUnhandledMemberEvent(message);
};
var roomInfoEvent = this.handleRoomInfo(tikTokRoomInfo ->
{
tikTokRoomInfo.setViewersCount(message.getMemberCount());
});
return List.of(event, roomInfoEvent);
}
@SneakyThrows
public List<TikTokEvent> handleLike(byte[] msg)
{
var message = WebcastLikeMessage.parseFrom(msg);
var event = new TikTokLikeEvent(message);
var roomInfoEvent = this.handleRoomInfo(tikTokRoomInfo ->
{
tikTokRoomInfo.setLikesCount(event.getTotalLikes());
});
return List.of(event, roomInfoEvent);
}
}

View File

@@ -20,7 +20,7 @@
* 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.handlers.events;
package io.github.jwdeveloper.tiktok.mappers.events;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.data.events.TikTokUnhandledSocialEvent;

View File

@@ -24,11 +24,9 @@ package io.github.jwdeveloper.tiktok.websocket;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.Constants;
import io.github.jwdeveloper.tiktok.TikTokLiveClient;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
import io.github.jwdeveloper.tiktok.http.HttpUtils;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.live.LiveClient;
@@ -44,7 +42,7 @@ public class TikTokWebSocketClient implements SocketClient {
private final Logger logger;
private final ClientSettings clientSettings;
private final TikTokCookieJar tikTokCookieJar;
private final TikTokMessageHandlerRegistration webResponseHandler;
private final TikTokMessageHandler messageHandler;
private final TikTokEventObserver tikTokEventHandler;
private WebSocketClient webSocketClient;
private TikTokWebSocketPingingTask pingingTask;
@@ -53,12 +51,12 @@ public class TikTokWebSocketClient implements SocketClient {
public TikTokWebSocketClient(Logger logger,
TikTokCookieJar tikTokCookieJar,
ClientSettings clientSettings,
TikTokMessageHandlerRegistration webResponseHandler,
TikTokMessageHandler messageHandler,
TikTokEventObserver tikTokEventHandler) {
this.logger = logger;
this.tikTokCookieJar = tikTokCookieJar;
this.clientSettings = clientSettings;
this.webResponseHandler = webResponseHandler;
this.messageHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler;
isConnected = false;
}
@@ -74,7 +72,7 @@ public class TikTokWebSocketClient implements SocketClient {
}
try {
webResponseHandler.handle(tikTokLiveClient, webcastResponse);
messageHandler.handle(tikTokLiveClient, webcastResponse);
var url = getWebSocketUrl(webcastResponse);
webSocketClient = startWebSocket(url, tikTokLiveClient);
webSocketClient.connect();
@@ -107,7 +105,7 @@ public class TikTokWebSocketClient implements SocketClient {
return new TikTokWebSocketListener(url,
headers,
3000,
webResponseHandler,
messageHandler,
tikTokEventHandler,
liveClient);
}

View File

@@ -28,7 +28,7 @@ import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastPushFrame;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
@@ -44,33 +44,22 @@ import java.util.Optional;
public class TikTokWebSocketListener extends WebSocketClient {
private final TikTokMessageHandlerRegistration webResponseHandler;
private final TikTokMessageHandler messageHandler;
private final TikTokEventObserver tikTokEventHandler;
private final LiveClient tikTokLiveClient;
public TikTokWebSocketListener(URI serverUri,
Map<String, String> httpHeaders,
int connectTimeout,
TikTokMessageHandlerRegistration webResponseHandler,
TikTokMessageHandler messageHandler,
TikTokEventObserver tikTokEventHandler,
LiveClient tikTokLiveClient) {
super(serverUri, new Draft_6455(), httpHeaders,connectTimeout);
this.webResponseHandler = webResponseHandler;
this.messageHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler;
this.tikTokLiveClient = tikTokLiveClient;
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
if(isNotClosing())
{
sendPing();
}
}
@Override
public void onMessage(ByteBuffer bytes)
{
@@ -85,6 +74,19 @@ public class TikTokWebSocketListener extends WebSocketClient {
}
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
if(isNotClosing())
{
sendPing();
}
}
@Override
public void onClose(int i, String s, boolean b) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokDisconnectedEvent());
@@ -114,7 +116,7 @@ public class TikTokWebSocketListener extends WebSocketClient {
// sendAckId(webResponse.getFetchInterval());
}
webResponseHandler.handle(tikTokLiveClient, webResponse);
messageHandler.handle(tikTokLiveClient, webResponse);
}
private Optional<WebcastPushFrame> getWebcastWebsocketMessage(byte[] buffer) {

View File

@@ -27,6 +27,7 @@ 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.mappers.events.TikTokGiftEventHandler;
import io.github.jwdeveloper.tiktok.messages.data.GiftStruct;
import io.github.jwdeveloper.tiktok.messages.data.Image;
import io.github.jwdeveloper.tiktok.messages.data.User;