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

@@ -26,15 +26,20 @@ package io.github.jwdeveloper.tiktok.exceptions;
/** /**
* Happens when incoming data from TikTok can not be mapped to TikTokEvent's * Happens when incoming data from TikTok can not be mapped to TikTokEvent's
*/ */
public class TikTokMessageMappingException extends TikTokLiveException public class TikTokMessageMappingException extends TikTokLiveException {
{ public TikTokMessageMappingException(Class<?> inputClazz, Class<?> outputClass, Throwable throwable) {
public TikTokMessageMappingException(Class<?> inputClazz, Class<?> outputClass, Throwable throwable) super("Unable to handle mapping from class: " + inputClazz.getSimpleName() + " to class " + outputClass.getSimpleName(), throwable);
{
super("Unable to handle mapping from class: " + inputClazz.getSimpleName() + " to class " + outputClass.getSimpleName(),throwable);
} }
public TikTokMessageMappingException(Class<?> inputClazz, Class<?> outputClass, String message) public TikTokMessageMappingException(Class<?> inputClazz, Class<?> outputClass, String message) {
{ super("Unable to handle mapping from class: " + inputClazz.getSimpleName() + " to class " + outputClass.getSimpleName() + ": " + message);
super("Unable to handle mapping from class: " + inputClazz.getSimpleName() + " to class " + outputClass.getSimpleName()+": "+message); }
public TikTokMessageMappingException(Class<?> inputClazz, String message, Throwable throwable) {
super("Unable to handle mapping from class: " + inputClazz.getSimpleName() + ": " + message, throwable);
}
public TikTokMessageMappingException(String message, Throwable throwable) {
super( message, throwable);
} }
} }

View File

@@ -38,7 +38,12 @@ import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandl
public interface EventsBuilder<T> { public interface EventsBuilder<T> {
T onCustomEvent(EventConsumer<CustomEvent> event); /**
* Method used to register own custom events
* @param eventClazz event class
* @param event action
*/
<E extends TikTokEvent> T onCustomEvent(Class<E> eventClazz, EventConsumer<E> event);
T onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event); T onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event);
@@ -52,6 +57,7 @@ public interface EventsBuilder<T> {
T onGiftCombo(EventConsumer<TikTokGiftComboEvent> event); T onGiftCombo(EventConsumer<TikTokGiftComboEvent> event);
T onGift(EventConsumer<TikTokGiftEvent> event); T onGift(EventConsumer<TikTokGiftEvent> event);
T onQuestion(EventConsumer<TikTokQuestionEvent> event); T onQuestion(EventConsumer<TikTokQuestionEvent> event);
@@ -68,7 +74,7 @@ public interface EventsBuilder<T> {
T onShare(EventConsumer<TikTokShareEvent> event); T onShare(EventConsumer<TikTokShareEvent> event);
// T onChest(EventConsumer<TikTokChestEvent> event); // T onChest(EventConsumer<TikTokChestEvent> event);
T onLivePaused(EventConsumer<TikTokLivePausedEvent> event); T onLivePaused(EventConsumer<TikTokLivePausedEvent> event);
@@ -83,11 +89,10 @@ public interface EventsBuilder<T> {
T onDisconnected(EventConsumer<TikTokDisconnectedEvent> event); T onDisconnected(EventConsumer<TikTokDisconnectedEvent> event);
T onError(EventConsumer<TikTokErrorEvent> event); T onError(EventConsumer<TikTokErrorEvent> event);
T onEvent(EventConsumer<TikTokEvent> event); T onEvent(EventConsumer<TikTokEvent> event);
// TODO Figure out how those events works // TODO Figure out how those events works
//T onLinkMicFanTicket(TikTokEventConsumer<TikTokLinkMicFanTicketEvent> event); //T onLinkMicFanTicket(TikTokEventConsumer<TikTokLinkMicFanTicketEvent> event);

View File

@@ -23,20 +23,60 @@
package io.github.jwdeveloper.tiktok.live.builder; package io.github.jwdeveloper.tiktok.live.builder;
import io.github.jwdeveloper.tiktok.ClientSettings; import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener; import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.mappers.TikTokMapper;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> { public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
LiveClientBuilder configure(Consumer<ClientSettings> consumer);
/**
* This method is triggered after default mappings are registered
* It could be used to OVERRIDE behaviour of mappings and implement custom behaviour
*
* Be aware if for example you override WebcastGiftEvent, onGiftEvent() will not be working
*
* @param onCustomMappings lambda method
* @return
*/
LiveClientBuilder onMapping(Consumer<TikTokMapper> onCustomMappings);
/**
* Configuration of client settings
* @see ClientSettings
* @param onConfigure
* @return
*/
LiveClientBuilder configure(Consumer<ClientSettings> onConfigure);
/**
* @see TikTokEventListener
* Adding events listener class, its fancy way to register events without using lamda method
* but actual method in class that implements TikTokEventListener
* @return
*/
LiveClientBuilder addListener(TikTokEventListener listener); LiveClientBuilder addListener(TikTokEventListener listener);
/**
*
* @return LiveClient object
*/
LiveClient build(); LiveClient build();
/**
*
* @return LiveClient object and connects to TikTok live
*/
LiveClient buildAndConnect(); LiveClient buildAndConnect();
/**
*
* @return LiveClient object and connects to TikTok live asynchronously
*/
CompletableFuture<LiveClient> buildAndConnectAsync(); CompletableFuture<LiveClient> buildAndConnectAsync();
} }

View File

@@ -0,0 +1,73 @@
package io.github.jwdeveloper.tiktok.mappers;
import com.google.protobuf.GeneratedMessageV3;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import java.util.List;
import java.util.function.Function;
public interface TikTokMapper {
/**
* Triggered when `sourceClass` is mapped,
* input is bytes that are coming from TikTok in `sourceClass` packet
* output is TikTok event we want to create
* <p>
* bytesToEvent(WebcastGiftMessage.class, bytes ->
* {
* var giftMessage = WebcastGiftMessage.parseFrom(bytes);
* var giftName = giftMessage.getGift().getName();
* return new TikTokEvent(Gift.ROSE, giftMessage);
* })
*
* @param sourceClass protocol buffer webcast class
* @param onMapping lambda function that is triggered on mapping. takes as input ProtocolBuffer object and as output TikTokEvent
*/
void bytesToEvent(Class<? extends GeneratedMessageV3> sourceClass, Function<byte[], TikTokEvent> onMapping);
void bytesToEvents(Class<? extends GeneratedMessageV3> sourceClass, Function<byte[], List<TikTokEvent>> onMapping);
/**
* In case you found some TikTok message that has not Webcast class use this method
*
* @param messageName Name of TikTok data event
* @param onMapping lambda function that is triggered on mapping. takes as input ProtocolBuffer object and as output TikTokEvent
*/
void bytesToEvent(String messageName, Function<byte[], TikTokEvent> onMapping);
void bytesToEvents(String messageName, Function<byte[], List<TikTokEvent>> onMapping);
/**
* This method can be used to override default mapping for
* certain TikTok incoming data message. For this example
* we are overriding WebcastGiftMessage and retuning CustomGiftEvent
* instead of TikTokGiftEvent
* <p>
* webcastObjectToEvent(WebcastGiftMessage.class, webcastGiftMessage ->
* {
* var giftName = webcastGiftMessage.getGift().getName();
* var user = webcastGiftMessage.getUser().getNickname();
* return new CustomGiftEvent(giftName, user);
* })
*
* @param sourceClass ProtocolBuffer class that represent incoming custom data, hint class should starts with Webcast prefix
* @param onMapping lambda function that is triggered on mapping. takes as input ProtocolBuffer object and as output TikTokEvent
*/
<T extends GeneratedMessageV3> void webcastObjectToEvent(Class<T> sourceClass, Function<T, TikTokEvent> onMapping);
<T extends GeneratedMessageV3> void webcastObjectToEvents(Class<T> sourceClass, Function<T, List<TikTokEvent>> onMapping);
/**
* Triggered when `sourceClass` is mapped,
* looking for constructor in `outputClass` with one parameter that is of type `sourceClass`
* and created instance of object from this constructor
*
* @param sourceClass protocol buffer webcast class
* @param outputClass TikTok event class
*/
void webcastObjectToConstructor(Class<? extends GeneratedMessageV3> sourceClass, Class<? extends TikTokEvent> outputClass);
}

View File

@@ -1,60 +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.utils;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
public class CancelationToken
{
private boolean isCanceled =false;
public void cancel()
{
isCanceled =true;
}
public boolean isCancel()
{
return isCanceled;
}
public void throwIfCancel()
{
if(!isCanceled)
{
return;
}
throw new TikTokLiveException("Token requested cancelation");
}
public boolean isNotCancel()
{
return !isCancel();
}
public static CancelationToken create()
{
return new CancelationToken();
}
}

View File

@@ -0,0 +1,56 @@
package io.github.jwdeveloper.tiktok.utils;
import java.nio.file.Path;
import java.util.Map;
import java.util.TreeMap;
public class ProtoBufferFileGenerator {
public static String generate(ProtoBufferObject protoBuffObj, String name) {
var sb = new StringBuilder();
var offset = 2;
sb.append("message ").append(name).append(" { \n");
var objects = new TreeMap<String, ProtoBufferObject>();
var objectCounter = 0;
for (var entry : protoBuffObj.getFields().entrySet()) {
var index = entry.getKey();
var field = entry.getValue();
var fieldName = field.type.toLowerCase() + "Value";
var value = field.value;
if (field.value instanceof ProtoBufferObject object) {
fieldName += objectCounter;
value = "";
objects.put(fieldName,object);
objectCounter++;
}
for (var i = 0; i < offset; i++) {
sb.append(" ");
}
sb.append(field.type).append(" ").append(fieldName)
.append(" ")
.append("=")
.append(" ")
.append(index)
.append(";")
.append(" //")
.append(value)
.append("\n");
}
sb.append(" \n");
for(var object : objects.entrySet())
{
var structure = generate(object.getValue(),object.getKey());
sb.append(structure);
}
sb.append(" \n");
sb.append("} \n");
return sb.toString();
}
}

View File

@@ -0,0 +1,31 @@
package io.github.jwdeveloper.tiktok.utils;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
public class ProtoBufferJsonGenerator {
public static JsonObject genratejson(ProtoBufferObject protoBuffObj) {
JsonObject jsonObject = new JsonObject();
for (var entry : protoBuffObj.getFields().entrySet()) {
var fieldName = entry.getKey() + "_" + entry.getValue().type;
if (entry.getValue().value instanceof ProtoBufferObject protoObj)
{
JsonObject childJson = genratejson(protoObj);
jsonObject.add(fieldName, childJson);
continue;
}
var value = entry.getValue().value.toString();
jsonObject.addProperty(fieldName, value);
}
return jsonObject;
}
public static String generate(ProtoBufferObject protoBufferObject) {
var json = genratejson(protoBufferObject);
var gson = new GsonBuilder().setPrettyPrinting().create();
return gson.toJson(json);
}
}

View File

@@ -0,0 +1,76 @@
package io.github.jwdeveloper.tiktok.utils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Map;
import java.util.TreeMap;
public class ProtoBufferObject {
@Getter
private final Map<Integer, ProtoBufferField> fields;
public ProtoBufferObject() {
this.fields = new TreeMap<>();
}
public void addField(int index, String type, Object value) {
fields.put(index, new ProtoBufferField(type, value));
}
public void addField(int index, ProtoBufferField value) {
fields.put(index, value);
}
public String toProtoFile()
{
return ProtoBufferFileGenerator.generate(this,"UnknownMessage");
}
public String toJson()
{
return ProtoBufferJsonGenerator.generate(this);
}
@Override
public String toString() {
return toString(0, true);
}
public String toString(int offset ,boolean nested) {
var sb = new StringBuilder();
sb.append("\n");
for (var entry : fields.entrySet()) {
var index = entry.getKey();
var field = entry.getValue();
for(var i =0;i<offset;i++)
{
sb.append(" ");
}
sb.append(index).append(" ")
.append(field.type).append(" ");
var value = field.value;
if (value instanceof ProtoBufferObject child) {
sb.append(child.toString(offset+2,nested));
} else {
sb.append(entry.getValue().value);
}
sb.append("\n");
}
return sb.toString();
}
@AllArgsConstructor
public class ProtoBufferField {
public String type;
public Object value;
}
}

View File

@@ -0,0 +1,94 @@
package io.github.jwdeveloper.tiktok.utils;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.UnknownFieldSet;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import java.util.Base64;
public class ProtocolUtils {
public static String toBase64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
public static String toBase64(WebcastResponse.Message bytes) {
return Base64.getEncoder().encodeToString(bytes.toByteArray());
}
public static byte[] fromBase64(String base64) {
return Base64.getDecoder().decode(base64);
}
public static ProtoBufferObject getProtocolBufferStructure(byte[] bytes) {
try {
var files = UnknownFieldSet.parseFrom(bytes);
var protoBufferObject = new ProtoBufferObject();
createStructure(files, protoBufferObject);
return protoBufferObject;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void createStructure(UnknownFieldSet unknownFieldSet, ProtoBufferObject root) throws InvalidProtocolBufferException {
for (var entry : unknownFieldSet.asMap().entrySet()) {
var objectValue = entry.getValue();
var type = "undefind";
Object value = null;
var index = entry.getKey();
if (!objectValue.getLengthDelimitedList().isEmpty()) {
var nestedObject = new ProtoBufferObject();
for (var str : objectValue.getLengthDelimitedList()) {
try {
var nestedFieldsSet = UnknownFieldSet.parseFrom(str);
createStructure(nestedFieldsSet, nestedObject);
} catch (Exception e)
{
type = "string";
value = str.toStringUtf8();
}
}
if (value != null) {
root.addField(index, "string", value);
} else {
root.addField(index, "object", nestedObject);
}
continue;
}
if (!objectValue.getFixed32List().isEmpty()) {
type = "Fixed32List";
value = objectValue.getFixed32List();
}
if (!objectValue.getFixed64List().isEmpty()) {
type = "Fixed64List";
value = objectValue.getFixed64List();
}
if (!objectValue.getGroupList().isEmpty()) {
type = "getGroupList";
value = objectValue.getGroupList();
}
if (!objectValue.getVarintList().isEmpty()) {
type = "int";
value = objectValue.getVarintList().get(0);
}
root.addField(index, type, value);
}
}
public static WebcastResponse.Message fromBase64ToMessage(String base64) throws InvalidProtocolBufferException {
var bytes = fromBase64(base64);
return WebcastResponse.Message.parseFrom(bytes);
}
}

View File

@@ -177,6 +177,8 @@ message WebcastCaptionMessage {
} }
} }
// Comment sent by User // Comment sent by User
//@WebcastChatMessage //@WebcastChatMessage
message WebcastChatMessage { message WebcastChatMessage {

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.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver; 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.handlers.events.TikTokGiftEventHandler; import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokRoomInfoEventHandler; import io.github.jwdeveloper.tiktok.mappers.TikTokLiveMapper;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler; 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.TikTokApiService;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar; import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient; 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.EventConsumer;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder; import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper; 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.utils.ConsoleColors;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient; import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
@@ -67,9 +72,11 @@ import java.util.logging.*;
public class TikTokLiveClientBuilder implements LiveClientBuilder { public class TikTokLiveClientBuilder implements LiveClientBuilder {
protected final ClientSettings clientSettings; protected final ClientSettings clientSettings;
protected final Logger logger; protected final Logger logger;
protected final TikTokEventObserver tikTokEventHandler; protected final TikTokEventObserver tikTokEventHandler;
protected final List<TikTokEventListener> listeners; protected final List<TikTokEventListener> listeners;
protected Consumer<TikTokMapper> onCustomMappings;
public TikTokLiveClientBuilder(String userName) { public TikTokLiveClientBuilder(String userName) {
this.tikTokEventHandler = new TikTokEventObserver(); this.tikTokEventHandler = new TikTokEventObserver();
@@ -77,10 +84,18 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
this.clientSettings.setHostName(userName); this.clientSettings.setHostName(userName);
this.logger = Logger.getLogger(TikTokLive.class.getSimpleName() + " " + userName); this.logger = Logger.getLogger(TikTokLive.class.getSimpleName() + " " + userName);
this.listeners = new ArrayList<>(); 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; return this;
} }
@@ -104,8 +119,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
throw new TikTokLiveException("HostName can not be null"); throw new TikTokLiveException("HostName can not be null");
} }
if (clientSettings.getHostName().startsWith("@")) if (clientSettings.getHostName().startsWith("@")) {
{
clientSettings.setHostName(clientSettings.getHostName().substring(1)); clientSettings.setHostName(clientSettings.getHostName().substring(1));
} }
@@ -135,9 +149,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
if (!clientSettings.isPrintToConsole()) { if (!clientSettings.isPrintToConsole()) {
logger.setLevel(Level.OFF); logger.setLevel(Level.OFF);
} }
} }
public LiveClient build() { public LiveClient build() {
@@ -152,23 +163,14 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
var apiClient = new TikTokHttpClient(cookieJar, requestFactory); var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
var apiService = new TikTokApiService(apiClient, logger, clientSettings); var apiService = new TikTokApiService(apiClient, logger, clientSettings);
var giftManager = new TikTokGiftManager(logger); 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, var webSocketClient = new TikTokWebSocketClient(logger,
cookieJar, cookieJar,
clientSettings, clientSettings,
webResponseHandler, messageHandler,
tikTokEventHandler); tikTokEventHandler);
return new TikTokLiveClient(tiktokRoomInfo, return new TikTokLiveClient(tiktokRoomInfo,
@@ -181,6 +183,64 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
logger); 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() { public LiveClient buildAndConnect() {
var client = build(); var client = build();
client.connect(); client.connect();
@@ -191,11 +251,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return build().connectAsync(); return build().connectAsync();
} }
public LiveClientBuilder onCustomEvent(EventConsumer<CustomEvent> event) {
tikTokEventHandler.subscribe(CustomEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onUnhandledSocial( public TikTokLiveClientBuilder onUnhandledSocial(
EventConsumer<TikTokUnhandledSocialEvent> event) { EventConsumer<TikTokUnhandledSocialEvent> event) {
@@ -203,7 +258,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this; return this;
} }
// @Override // @Override
public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) { public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) {
tikTokEventHandler.subscribe(TikTokChestEvent.class, event); tikTokEventHandler.subscribe(TikTokChestEvent.class, event);
return this; return this;
@@ -259,6 +314,13 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this; return this;
} }
@Override
public <E extends TikTokEvent> LiveClientBuilder onCustomEvent(Class<E> eventClazz, EventConsumer<E> event) {
tikTokEventHandler.subscribe(eventClazz, event);
return this;
}
@Override @Override
public LiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) { public LiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event); tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event);
@@ -266,7 +328,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
} }
public TikTokLiveClientBuilder onLivePaused(EventConsumer<TikTokLivePausedEvent> event) { public TikTokLiveClientBuilder onLivePaused(EventConsumer<TikTokLivePausedEvent> event) {
tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event); tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
return this; 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.dto.MessageMetaData;
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent; 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.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent; import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent; import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
import io.github.jwdeveloper.tiktok.live.LiveClient; 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.messages.webcast.WebcastResponse;
import io.github.jwdeveloper.tiktok.utils.Stopwatch; import io.github.jwdeveloper.tiktok.utils.Stopwatch;
import java.time.Duration; 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; private final TikTokEventObserver tikTokEventHandler;
protected final TikTokGenericEventMapper mapper; private final TikTokLiveMapper mapper;
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, TikTokGenericEventMapper mapper) { public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, TikTokLiveMapper mapper) {
handlers = new HashMap<>();
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
this.mapper = mapper; 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) { public void handle(LiveClient client, WebcastResponse webcastResponse) {
tikTokEventHandler.publish(client, new TikTokWebsocketResponseEvent(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 { public void handleSingleMessage(LiveClient client, WebcastResponse.Message message) throws Exception {
var messageClassName = message.getMethod(); var messageClassName = message.getMethod();
if (!handlers.containsKey(messageClassName)) { if (!mapper.isRegistered(messageClassName)) {
tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(message)); tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(message));
return; return;
} }
var handler = handlers.get(messageClassName);
var stopwatch = new Stopwatch(); var stopwatch = new Stopwatch();
stopwatch.start(); stopwatch.start();
var events = handler.handle(message.getPayload().toByteArray()); var events = mapper.handleMapping(messageClassName, message.getPayload().toByteArray());
var handlingTimeInMs = stopwatch.stop(); var handlingTimeInMs = stopwatch.stop();
var metadata = new MessageMetaData(Duration.ofNanos(handlingTimeInMs)); 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)) { if (methodCache.containsKey(input)) {
return methodCache.get(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 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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 { 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 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent; 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 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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.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.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent; 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.RankingUser;
import io.github.jwdeveloper.tiktok.data.models.users.User; 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.WebcastLiveIntroMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastMemberMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomUserSeqMessage; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomUserSeqMessage;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -76,4 +83,33 @@ public class TikTokRoomInfoEventHandler {
tikTokRoomInfo.setLanguage(language); 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 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.data.events.TikTokUnhandledSocialEvent; 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.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.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver; 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.HttpUtils;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar; import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
@@ -44,7 +42,7 @@ public class TikTokWebSocketClient implements SocketClient {
private final Logger logger; private final Logger logger;
private final ClientSettings clientSettings; private final ClientSettings clientSettings;
private final TikTokCookieJar tikTokCookieJar; private final TikTokCookieJar tikTokCookieJar;
private final TikTokMessageHandlerRegistration webResponseHandler; private final TikTokMessageHandler messageHandler;
private final TikTokEventObserver tikTokEventHandler; private final TikTokEventObserver tikTokEventHandler;
private WebSocketClient webSocketClient; private WebSocketClient webSocketClient;
private TikTokWebSocketPingingTask pingingTask; private TikTokWebSocketPingingTask pingingTask;
@@ -53,12 +51,12 @@ public class TikTokWebSocketClient implements SocketClient {
public TikTokWebSocketClient(Logger logger, public TikTokWebSocketClient(Logger logger,
TikTokCookieJar tikTokCookieJar, TikTokCookieJar tikTokCookieJar,
ClientSettings clientSettings, ClientSettings clientSettings,
TikTokMessageHandlerRegistration webResponseHandler, TikTokMessageHandler messageHandler,
TikTokEventObserver tikTokEventHandler) { TikTokEventObserver tikTokEventHandler) {
this.logger = logger; this.logger = logger;
this.tikTokCookieJar = tikTokCookieJar; this.tikTokCookieJar = tikTokCookieJar;
this.clientSettings = clientSettings; this.clientSettings = clientSettings;
this.webResponseHandler = webResponseHandler; this.messageHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
isConnected = false; isConnected = false;
} }
@@ -74,7 +72,7 @@ public class TikTokWebSocketClient implements SocketClient {
} }
try { try {
webResponseHandler.handle(tikTokLiveClient, webcastResponse); messageHandler.handle(tikTokLiveClient, webcastResponse);
var url = getWebSocketUrl(webcastResponse); var url = getWebSocketUrl(webcastResponse);
webSocketClient = startWebSocket(url, tikTokLiveClient); webSocketClient = startWebSocket(url, tikTokLiveClient);
webSocketClient.connect(); webSocketClient.connect();
@@ -107,7 +105,7 @@ public class TikTokWebSocketClient implements SocketClient {
return new TikTokWebSocketListener(url, return new TikTokWebSocketListener(url,
headers, headers,
3000, 3000,
webResponseHandler, messageHandler,
tikTokEventHandler, tikTokEventHandler,
liveClient); 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.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException; import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver; 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.live.LiveClient;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastPushFrame; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastPushFrame;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
@@ -44,33 +44,22 @@ import java.util.Optional;
public class TikTokWebSocketListener extends WebSocketClient { public class TikTokWebSocketListener extends WebSocketClient {
private final TikTokMessageHandlerRegistration webResponseHandler; private final TikTokMessageHandler messageHandler;
private final TikTokEventObserver tikTokEventHandler; private final TikTokEventObserver tikTokEventHandler;
private final LiveClient tikTokLiveClient; private final LiveClient tikTokLiveClient;
public TikTokWebSocketListener(URI serverUri, public TikTokWebSocketListener(URI serverUri,
Map<String, String> httpHeaders, Map<String, String> httpHeaders,
int connectTimeout, int connectTimeout,
TikTokMessageHandlerRegistration webResponseHandler, TikTokMessageHandler messageHandler,
TikTokEventObserver tikTokEventHandler, TikTokEventObserver tikTokEventHandler,
LiveClient tikTokLiveClient) { LiveClient tikTokLiveClient) {
super(serverUri, new Draft_6455(), httpHeaders,connectTimeout); super(serverUri, new Draft_6455(), httpHeaders,connectTimeout);
this.webResponseHandler = webResponseHandler; this.messageHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
this.tikTokLiveClient = tikTokLiveClient; this.tikTokLiveClient = tikTokLiveClient;
} }
@Override
public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
if(isNotClosing())
{
sendPing();
}
}
@Override @Override
public void onMessage(ByteBuffer bytes) 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 @Override
public void onClose(int i, String s, boolean b) { public void onClose(int i, String s, boolean b) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokDisconnectedEvent()); tikTokEventHandler.publish(tikTokLiveClient,new TikTokDisconnectedEvent());
@@ -114,7 +116,7 @@ public class TikTokWebSocketListener extends WebSocketClient {
// sendAckId(webResponse.getFetchInterval()); // sendAckId(webResponse.getFetchInterval());
} }
webResponseHandler.handle(tikTokLiveClient, webResponse); messageHandler.handle(tikTokLiveClient, webResponse);
} }
private Optional<WebcastPushFrame> getWebcastWebsocketMessage(byte[] buffer) { 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.Picture;
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType; import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; 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.GiftStruct;
import io.github.jwdeveloper.tiktok.messages.data.Image; import io.github.jwdeveloper.tiktok.messages.data.Image;
import io.github.jwdeveloper.tiktok.messages.data.User; import io.github.jwdeveloper.tiktok.messages.data.User;

View File

@@ -0,0 +1,88 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
import io.github.jwdeveloper.tiktok.exceptions.TikTokMessageMappingException;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastChatMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import io.github.jwdeveloper.tiktok.utils.ProtocolUtils;
public class CustomMappingExample {
public static void main(String[] args) {
TikTokLive.newClient("vadimpyrography")
.onCustomEvent(CustomChatEvent.class, (liveClient, event) ->
{
System.out.println("hello world!");
})
.onError((liveClient, event) ->
{
event.getException().printStackTrace();
})
.onMapping(mapper ->
{
mapper.webcastObjectToEvent(WebcastChatMessage.class, chatMessage ->
{
var language = chatMessage.getContentLanguage();
var userName = chatMessage.getUser().getNickname();
var message = chatMessage.getContent();
return new CustomChatEvent(language, userName, message);
});
mapper.bytesToEvent("WebcastGiftMessage", bytes ->
{
try
{
var event = WebcastGiftMessage.parseFrom(bytes);
return new TikTokGiftEvent(Gift.ROSA, event);
} catch (Exception e) {
throw new TikTokMessageMappingException("unable to map gift message!", e);
}
});
mapper.bytesToEvent("WebcastMemberMessage",bytes ->
{
//displaying unknown messages from tiktok
var structure = ProtocolUtils.getProtocolBufferStructure(bytes);
System.out.println(structure.toJson());
return new TikTokErrorEvent(new RuntimeException("Message not implemented"));
});
}).buildAndConnect();
}
public static class CustomChatEvent extends TikTokEvent {
private final String langauge;
private final String userName;
private final String message;
public CustomChatEvent(String language, String userName, String message) {
this.langauge = language;
this.userName = userName;
this.message = message;
}
public String getLangauge() {
return langauge;
}
public String getUserName() {
return userName;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return "CustomChatEvent{" +
"language='" + langauge + '\'' +
", userName='" + userName + '\'' +
", message='" + message + '\'' +
'}';
}
}
}

View File

@@ -26,7 +26,9 @@ import io.github.jwdeveloper.tiktok.data.events.TikTokSubNotifyEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokSubscribeEvent; import io.github.jwdeveloper.tiktok.data.events.TikTokSubscribeEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors; import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
import java.io.IOException; import java.io.IOException;
import java.time.Duration; import java.time.Duration;
@@ -42,6 +44,8 @@ public class SimpleExample {
// set tiktok username // set tiktok username
/* /*
//Optional checking if host name is correct //Optional checking if host name is correct
if(TikTokLive.isHostNameValid(TIKTOK_HOSTNAME)) if(TikTokLive.isHostNameValid(TIKTOK_HOSTNAME))
@@ -50,14 +54,33 @@ public class SimpleExample {
} }
*/ */
// Optional checking if live is online // Optional checking if live is online
if(TikTokLive.isLiveOnline(TIKTOK_HOSTNAME)) if (TikTokLive.isLiveOnline(TIKTOK_HOSTNAME)) {
{
System.out.println("Live is online!"); System.out.println("Live is online!");
} }
TikTokLive.newClient("test")
.onWebsocketResponse((liveClient, event) ->
{
var response = event.getResponse();
for (var message : response.getMessagesList()) {
var name = message.getMethod();
var binaryData = message.getPayload();
System.out.println("Event name: " + name);
if (name.equals("WebcastGiftEvent")) {
try {
WebcastGiftMessage giftMessage = WebcastGiftMessage.parseFrom(binaryData);
var giftName = giftMessage.getGift().getName();
var giftId = giftMessage.getGiftId();
var userName = giftMessage.getUser().getNickname();
System.out.println("Gift: "+giftName+" id: "+giftId+" from user: "+userName);
} catch (Exception e) {
throw new RuntimeException("Mapping error", e);
}
}
}
}).buildAndConnect();
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME) TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
@@ -96,9 +119,8 @@ public class SimpleExample {
var tiktokLiveEvent = event.getEvent(); var tiktokLiveEvent = event.getEvent();
if(tiktokLiveEvent instanceof TikTokSubNotifyEvent e) if (tiktokLiveEvent instanceof TikTokSubNotifyEvent e) {
{ System.out.println("it was subscrible event");
System.out.println("it was subscrible event");
} }
}) })

View File

@@ -26,6 +26,7 @@ import com.google.protobuf.ByteString;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors; import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import io.github.jwdeveloper.tiktok.utils.JsonUtil; import io.github.jwdeveloper.tiktok.utils.JsonUtil;
import io.github.jwdeveloper.tiktok.utils.ProtocolUtils;
public class MessageUtil { public class MessageUtil {
public static String getContent(WebcastResponse.Message message) { public static String getContent(WebcastResponse.Message message) {
@@ -50,39 +51,11 @@ public class MessageUtil {
return JsonUtil.messageToJson(deserialized); return JsonUtil.messageToJson(deserialized);
} catch (Exception ex) { } catch (Exception ex) {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.append("Can not find mapper for " + methodName); sb.append("Can not find protocolbuffer file message representation for " + methodName);
sb.append("\n"); sb.append("\n");
try { var structure = ProtocolUtils.getProtocolBufferStructure(bytes);
var files = com.google.protobuf.UnknownFieldSet.parseFrom(bytes); var json =structure.toJson();
for (var field : files.asMap().entrySet()) sb.append(json);
{
var value = field.getValue();
if(!value.getLengthDelimitedList().isEmpty())
{
sb.append(field.getKey()+" LengthDelimited "+value.getLengthDelimitedList()+" \n");
}
if(!value.getFixed32List().isEmpty())
{
sb.append(field.getKey()+" fixed32 "+value.getFixed32List()+" \n");
}
if(!value.getFixed64List().isEmpty())
{
sb.append(field.getKey()+" fixed64 "+value.getFixed64List()+" \n");
}
if(!value.getGroupList().isEmpty())
{
sb.append(field.getKey()+" group "+value.getGroupList()+" \n");
}
if(!value.getVarintList().isEmpty())
{
sb.append(field.getKey()+" varint "+value.getVarintList()+" \n");
}
}
} catch (Exception e) {
sb.append("Error while getting unknown fileds "+e.getMessage());
}
//String jsonString = JsonFormat.printToString(protobufData); //String jsonString = JsonFormat.printToString(protobufData);
return sb.toString(); return sb.toString();
} }

View File

@@ -26,10 +26,10 @@ import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo; import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration; import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler; import io.github.jwdeveloper.tiktok.mappers.events.TikTokGiftEventHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokRoomInfoEventHandler; import io.github.jwdeveloper.tiktok.mappers.events.TikTokRoomInfoEventHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler; import io.github.jwdeveloper.tiktok.mappers.events.TikTokSocialMediaEventHandler;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar; import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient; import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory; import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
@@ -99,12 +99,9 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder {
var requestFactory = new TikTokHttpRequestFactory(cookie); var requestFactory = new TikTokHttpRequestFactory(cookie);
var apiClient = new TikTokHttpClient(cookie, requestFactory); var apiClient = new TikTokHttpClient(cookie, requestFactory);
var apiService = new ApiServiceMock(apiClient, logger, clientSettings); var apiService = new ApiServiceMock(apiClient, logger, clientSettings);
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler, var mapper = createMapper(giftManager, tiktokRoomInfo);
new TikTokRoomInfoEventHandler(tiktokRoomInfo), var handler = new TikTokMessageHandler(tikTokEventHandler, mapper);
new TikTokGenericEventMapper(), var webSocketClient = new WebsocketClientMock(logger, responses, handler);
new TikTokGiftEventHandler(giftManager),
new TikTokSocialMediaEventHandler(tiktokRoomInfo));
var webSocketClient = new WebsocketClientMock(logger, responses, webResponseHandler);
return new LiveClientMock(tiktokRoomInfo, return new LiveClientMock(tiktokRoomInfo,
apiService, apiService,