Compare commits

..

40 Commits

Author SHA1 Message Date
JW
818c4cb364 Update maven-publish.yml 2023-09-04 12:06:33 +02:00
JW
911e2b12a5 Merge remote-tracking branch 'origin/master' 2023-09-04 12:05:19 +02:00
JW
1aff710523 Changes:
`onWebsocketMessage()` TikTokWebsocketMessageEvent new event that is
   triggered when new ProtocolBuffer message come from TikTok server.
   Should be mainly use for debuging purpose

Bugs:
 - Fixed bug: WebcastSocialMessage was always triggering `TikTokShareEvent` events such as `TikTokLikeEvent`, `TikTokFollowEvent`, `TikTokShareEvent`, `TikTokJoinEvent` was ignored

 - Fixed bug: Websocket was disconnecting when there was no incoming events for the while. Fixed by implementing background loop that pinging TikTok server every few ms.

 - Fixed bug: Disconnect method was not working
2023-09-04 12:05:13 +02:00
JW
c0f8e6d904 Update README.md 2023-09-04 11:41:33 +02:00
JW
b059afd621 Update README.md 2023-08-31 00:22:45 +02:00
JW
1275878822 Merge pull request #3 from isaackogan/patch-1
Update README.md
2023-08-30 22:39:44 +02:00
Isaac Kogan
761f3ab633 Update README.md 2023-08-30 15:58:50 -04:00
Jacek Wolniewicz
66def92316 quick fix 2023-08-30 21:50:25 +02:00
Jacek Wolniewicz
cc85d7c124 Changes:
- New event `onSuccessResponseMapping`
  triggered when Webcast.Message was  successfully mapped to TikTokEvent

- New Project Tools-collector:
  Tool generates instance of SqlLite
  database and collect to it both to
 events and exceptions occurred while TikTokLive client was running
2023-08-30 21:47:57 +02:00
Jacek Wolniewicz
384cfade5a Changes:
- New event `onSuccessResponseMapping`
  triggered when Webcast.Message was  successfully mapped to TikTokEvent

- New Project Tools-collector:
  Tool generates instance of SqlLite
  database and collect to it both to
 events and exceptions occurred while TikTokLive client was running
2023-08-30 21:47:45 +02:00
JW
483dceadcf Update README.md 2023-08-27 14:23:25 +02:00
JW
5f8cba5126 Update README.md 2023-08-26 03:42:09 +02:00
JW
f9966c9a5f Update README.md 2023-08-26 01:34:43 +02:00
JW
48a79736ad Update README.md 2023-08-26 01:34:19 +02:00
JW
eb82d0df78 Update README.md 2023-08-25 21:03:51 +02:00
GitHub Action
4ed821925d Update version in pom.xml 2023-08-25 19:03:24 +00:00
JW
6e9244aa67 Changes:
- Fixed bug: WebcastSocialMessage was always triggering `TikTokShareEvent` events such as `TikTokLikeEvent`, `TikTokFollowEvent`, `TikTokShareEvent`, `TikTokJoinEvent` was ignored
2023-08-25 21:01:56 +02:00
JW
fadb1ab267 changeing client name 2023-08-24 22:37:56 +02:00
JW
4273375eb9 Update README.md 2023-08-24 16:51:04 +02:00
JW
3daeee6316 Update README.md 2023-08-24 16:45:25 +02:00
GitHub Action
cde38df1b3 Update version in pom.xml 2023-08-24 14:38:53 +00:00
JW
15d6351d65 Changes:
- Fixed bug: library was not working on certain java versions
   due to different WebSocket implementation. Instead of using java websocket api now there is `org.java-websocket`
2023-08-24 16:36:40 +02:00
JW
44ba999b83 Update README.md 2023-08-23 21:56:08 +02:00
JW
5cf0d30962 Update README.md 2023-08-23 21:10:12 +02:00
JW
71ebc6e05e Update README.md 2023-08-23 21:00:37 +02:00
GitHub Action
c6d09927a0 Update version in pom.xml 2023-08-23 18:57:15 +00:00
JW
f03cb14262 Merge remote-tracking branch 'origin/master' 2023-08-23 20:55:46 +02:00
JW
d9ef60ccad Changes:
- Implementation on all features in `clientSettings`
  - Code optimization
  - More detail exceptions
  - Downloading gifts
2023-08-23 20:55:40 +02:00
JW
6fb89e72d4 Update README.md 2023-08-22 21:10:34 +02:00
JW
230764ed6a Update README.md 2023-08-22 21:08:38 +02:00
JW
b9a4fb193e Update README.md 2023-08-22 21:03:55 +02:00
JW
af209fc2ca Update README.md 2023-08-22 21:00:16 +02:00
JW
74bfe0b9e7 Merge remote-tracking branch 'origin/master' 2023-08-22 20:58:50 +02:00
JW
3545167873 Update example project 2023-08-22 20:58:45 +02:00
JW
c22483043d Update README.md 2023-08-22 20:51:17 +02:00
GitHub Action
8155d49366 Update version in pom.xml 2023-08-22 18:49:05 +00:00
JW
321b7c0eda Merge remote-tracking branch 'origin/master' 2023-08-22 20:47:33 +02:00
JW
26c7db8f99 Fix messages
- WebcastSocialMessage
  - ImDeleteMessage
2023-08-22 20:47:27 +02:00
JW
5f7ead2f05 Update README.md 2023-08-22 20:01:05 +02:00
GitHub Action
dffae3a521 Update version in pom.xml 2023-08-22 18:00:21 +00:00
77 changed files with 1549 additions and 666 deletions

View File

@@ -1,4 +1,4 @@
name: Publish Artifacts name: Publish New Version
on: on:
workflow_dispatch: workflow_dispatch:

81
.gitignore vendored
View File

@@ -1,3 +1,82 @@
# Project exclude paths # Project exclude paths
/API/target/ /API/target/
/Client/target/ /Client/target/
*.db
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.8-Release</version> <version>0.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>API</artifactId> <artifactId>API</artifactId>

View File

@@ -12,16 +12,7 @@ public class ClientSettings {
/// <summary> /// <summary>
/// Timeout for Connections /// Timeout for Connections
/// </summary> /// </summary>
private Duration timeout; private Duration timeout;
/// <summary>
/// Polling-Interval for Socket-Connection
/// </summary
private Duration pollingInterval;
/// <summary>
/// Proxy for Connection
/// </summary>
// public RotatingProxy Proxy; // public RotatingProxy Proxy;
/// <summary> /// <summary>
@@ -29,17 +20,17 @@ public class ClientSettings {
/// </summary> /// </summary>
private String clientLanguage; private String clientLanguage;
/// <summary>
/// Size for Buffer for Socket-Connection
/// </summary>
private int socketBufferSize;
/// <summary> /// <summary>
/// Whether to Retry if Connection Fails /// Whether to Retry if Connection Fails
/// </summary> /// </summary>
private boolean retryOnConnectionFailure; private boolean retryOnConnectionFailure;
/// <summary>
/// Wait to connect again for selected amount of time
/// </summary>
private Duration retryConnectionTimeout;
/// <summary> /// <summary>
/// Whether to handle Messages received from Room when Connecting /// Whether to handle Messages received from Room when Connecting
/// </summary> /// </summary>
@@ -65,12 +56,14 @@ public class ClientSettings {
private boolean printMessageData; private boolean printMessageData;
/// <summary> /// <summary>
/// Whether to check Messages for Unparsed Data /// Tiktok user name
/// </summary> /// </summary>
private boolean checkForUnparsedData;
private String hostName; private String hostName;
/// <summary>
/// Parameters used in requests to tiktok api
/// </summary>
private Map<String, Object> clientParameters; private Map<String, Object> clientParameters;
} }

View File

@@ -40,15 +40,13 @@ public class Constants {
public static ClientSettings DefaultClientSettings() { public static ClientSettings DefaultClientSettings() {
var clientSettings = new ClientSettings(); var clientSettings = new ClientSettings();
clientSettings.setTimeout(Duration.ofSeconds(DEFAULT_TIMEOUT)); clientSettings.setTimeout(Duration.ofSeconds(DEFAULT_TIMEOUT));
clientSettings.setPollingInterval(Duration.ofSeconds(DEFAULT_POLLTIME));
clientSettings.setClientLanguage("en-US"); clientSettings.setClientLanguage("en-US");
clientSettings.setHandleExistingMessagesOnConnect(true); clientSettings.setHandleExistingMessagesOnConnect(true);
clientSettings.setDownloadGiftInfo(true); clientSettings.setDownloadGiftInfo(true);
clientSettings.setRetryOnConnectionFailure(true); clientSettings.setRetryOnConnectionFailure(false);
clientSettings.setSocketBufferSize(500_000); clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1));
clientSettings.setPrintToConsole(true); clientSettings.setPrintToConsole(false);
clientSettings.setLogLevel(Level.ALL); clientSettings.setLogLevel(Level.ALL);
clientSettings.setCheckForUnparsedData(false);
clientSettings.setPrintMessageData(false); clientSettings.setPrintMessageData(false);
clientSettings.setClientParameters(Constants.DefaultClientParams()); clientSettings.setClientParameters(Constants.DefaultClientParams());
return clientSettings; return clientSettings;

View File

@@ -2,94 +2,94 @@ package io.github.jwdeveloper.tiktok.events;
import io.github.jwdeveloper.tiktok.events.messages.*; import io.github.jwdeveloper.tiktok.events.messages.*;
import java.util.function.Consumer;
public interface TikTokEventBuilder<T> { public interface TikTokEventBuilder<T> {
T onUnhandledSocial(Consumer<TikTokUnhandledSocialEvent> event); T onUnhandledSocial(TikTokEventConsumer<TikTokUnhandledSocialEvent> event);
T onLinkMicFanTicket(Consumer<TikTokLinkMicFanTicketEvent> event); T onLinkMicFanTicket(TikTokEventConsumer<TikTokLinkMicFanTicketEvent> event);
T onEnvelope(Consumer<TikTokEnvelopeEvent> event); T onEnvelope(TikTokEventConsumer<TikTokEnvelopeEvent> event);
T onShopMessage(Consumer<TikTokShopMessageEvent> event); T onShopMessage(TikTokEventConsumer<TikTokShopMessageEvent> event);
T onDetectMessage(Consumer<TikTokDetectMessageEvent> event); T onDetectMessage(TikTokEventConsumer<TikTokDetectMessageEvent> event);
T onLinkLayerMessage(Consumer<TikTokLinkLayerMessageEvent> event); T onLinkLayerMessage(TikTokEventConsumer<TikTokLinkLayerMessageEvent> event);
T onConnected(Consumer<TikTokConnectedEvent> event); T onConnected(TikTokEventConsumer<TikTokConnectedEvent> event);
T onCaption(Consumer<TikTokCaptionEvent> event); T onCaption(TikTokEventConsumer<TikTokCaptionEvent> event);
T onQuestion(Consumer<TikTokQuestionEvent> event); T onQuestion(TikTokEventConsumer<TikTokQuestionEvent> event);
T onRoomPinMessage(Consumer<TikTokRoomPinMessageEvent> event); T onRoomPinMessage(TikTokEventConsumer<TikTokRoomPinMessageEvent> event);
T onRoomMessage(Consumer<TikTokRoomMessageEvent> event); T onRoomMessage(TikTokEventConsumer<TikTokRoomMessageEvent> event);
T onLivePaused(Consumer<TikTokLivePausedEvent> event); T onLivePaused(TikTokEventConsumer<TikTokLivePausedEvent> event);
T onLike(Consumer<TikTokLikeEvent> event); T onLike(TikTokEventConsumer<TikTokLikeEvent> event);
T onLinkMessage(Consumer<TikTokLinkMessageEvent> event); T onLinkMessage(TikTokEventConsumer<TikTokLinkMessageEvent> event);
T onBarrageMessage(Consumer<TikTokBarrageMessageEvent> event); T onBarrageMessage(TikTokEventConsumer<TikTokBarrageMessageEvent> event);
T onGiftMessage(Consumer<TikTokGiftMessageEvent> event); T onGiftMessage(TikTokEventConsumer<TikTokGiftMessageEvent> event);
T onLinkMicArmies(Consumer<TikTokLinkMicArmiesEvent> event); T onLinkMicArmies(TikTokEventConsumer<TikTokLinkMicArmiesEvent> event);
T onEmote(Consumer<TikTokEmoteEvent> event); T onEmote(TikTokEventConsumer<TikTokEmoteEvent> event);
T onUnauthorizedMember(Consumer<TikTokUnauthorizedMemberEvent> event); T onUnauthorizedMember(TikTokEventConsumer<TikTokUnauthorizedMemberEvent> event);
T onInRoomBanner(Consumer<TikTokInRoomBannerEvent> event); T onInRoomBanner(TikTokEventConsumer<TikTokInRoomBannerEvent> event);
T onLinkMicMethod(Consumer<TikTokLinkMicMethodEvent> event); T onLinkMicMethod(TikTokEventConsumer<TikTokLinkMicMethodEvent> event);
T onSubscribe(Consumer<TikTokSubscribeEvent> event); T onSubscribe(TikTokEventConsumer<TikTokSubscribeEvent> event);
T onPollMessage(Consumer<TikTokPollMessageEvent> event); T onPollMessage(TikTokEventConsumer<TikTokPollMessageEvent> event);
T onFollow(Consumer<TikTokFollowEvent> event); T onFollow(TikTokEventConsumer<TikTokFollowEvent> event);
T onRoomViewerData(Consumer<TikTokRoomViewerDataEvent> event); T onRoomViewerData(TikTokEventConsumer<TikTokRoomViewerDataEvent> event);
T onGoalUpdate(Consumer<TikTokGoalUpdateEvent> event); T onGoalUpdate(TikTokEventConsumer<TikTokGoalUpdateEvent> event);
T onComment(Consumer<TikTokCommentEvent> event); T onComment(TikTokEventConsumer<TikTokCommentEvent> event);
T onRankUpdate(Consumer<TikTokRankUpdateEvent> event); T onRankUpdate(TikTokEventConsumer<TikTokRankUpdateEvent> event);
T onIMDelete(Consumer<TikTokIMDeleteEvent> event); T onIMDelete(TikTokEventConsumer<TikTokIMDeleteEvent> event);
T onLiveEnded(Consumer<TikTokLiveEndedEvent> event); T onLiveEnded(TikTokEventConsumer<TikTokLiveEndedEvent> event);
T onError(Consumer<TikTokErrorEvent> event); T onError(TikTokEventConsumer<TikTokErrorEvent> event);
T onUnhandled(Consumer<TikTokUnhandledEvent> event); T onUnhandled(TikTokEventConsumer<TikTokUnhandledEvent> event);
T onJoin(Consumer<TikTokJoinEvent> event); T onJoin(TikTokEventConsumer<TikTokJoinEvent> event);
T onRankText(Consumer<TikTokRankTextEvent> event); T onRankText(TikTokEventConsumer<TikTokRankTextEvent> event);
T onShare(Consumer<TikTokShareEvent> event); T onShare(TikTokEventConsumer<TikTokShareEvent> event);
T onUnhandledMember(Consumer<TikTokUnhandledMemberEvent> event); T onUnhandledMember(TikTokEventConsumer<TikTokUnhandledMemberEvent> event);
T onSubNotify(Consumer<TikTokSubNotifyEvent> event); T onSubNotify(TikTokEventConsumer<TikTokSubNotifyEvent> event);
T onLinkMicBattle(Consumer<TikTokLinkMicBattleEvent> event); T onLinkMicBattle(TikTokEventConsumer<TikTokLinkMicBattleEvent> event);
T onDisconnected(Consumer<TikTokDisconnectedEvent> event); T onDisconnected(TikTokEventConsumer<TikTokDisconnectedEvent> event);
T onGiftBroadcast(Consumer<TikTokGiftBroadcastEvent> event); T onGiftBroadcast(TikTokEventConsumer<TikTokGiftBroadcastEvent> event);
T onUnhandledControl(Consumer<TikTokUnhandledControlEvent> event); T onUnhandledControl(TikTokEventConsumer<TikTokUnhandledControlEvent> event);
T onEvent(TikTokEventConsumer<TikTokEvent> event);
T onWebsocketMessage(TikTokEventConsumer<TikTokWebsocketMessageEvent> event);
T onEvent(Consumer<TikTokEvent> event);
} }

View File

@@ -2,7 +2,7 @@ package io.github.jwdeveloper.tiktok.events;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
public interface TikTokLiveEvent<T extends TikTokEvent> public interface TikTokEventConsumer<T extends TikTokEvent>
{ {
void onEvent(LiveClient liveClient, T event); void onEvent(LiveClient liveClient, T event);
} }

View File

@@ -3,38 +3,39 @@ package io.github.jwdeveloper.tiktok.events.messages;
import io.github.jwdeveloper.tiktok.annotations.Nullable; import io.github.jwdeveloper.tiktok.annotations.Nullable;
import io.github.jwdeveloper.tiktok.events.TikTokEvent; import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.objects.Gift; import io.github.jwdeveloper.tiktok.events.objects.Gift;
import io.github.jwdeveloper.tiktok.events.objects.TikTokGift;
import io.github.jwdeveloper.tiktok.events.objects.User; import io.github.jwdeveloper.tiktok.events.objects.User;
import io.github.jwdeveloper.tiktok.messages.WebcastGiftMessage; import io.github.jwdeveloper.tiktok.messages.WebcastGiftMessage;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public class TikTokGiftMessageEvent extends TikTokEvent { public class TikTokGiftMessageEvent extends TikTokEvent {
private final Gift gift;
@Nullable private final Gift gift;
private User sender;
private final String purchaseId; @Nullable
private User sender;
private final String receipt; private final String purchaseId;
private final Integer amount; private final String receipt;
private final Boolean streakFinished; private final Integer amount;
private final Integer streakIndex; private final Boolean streakFinished;
public TikTokGiftMessageEvent(WebcastGiftMessage msg) { private final Integer streakIndex;
super(msg.getHeader());;
gift = new Gift(msg.getGiftDetails()); public TikTokGiftMessageEvent(WebcastGiftMessage msg) {
if(msg.hasSender()) super(msg.getHeader());
{ gift = new Gift(msg.getGiftDetails());
sender = new User(msg.getSender()); if (msg.hasSender()) {
sender = new User(msg.getSender());
}
purchaseId = msg.getLogId();
receipt = msg.getReceiptJson();
amount = msg.getAmount();
streakFinished = msg.getRepeatEnd();
streakIndex = msg.getRepeatCount();
} }
purchaseId = msg.getLogId();
receipt = msg.getReceiptJson();
amount = msg.getAmount();
streakFinished = msg.getRepeatEnd();
streakIndex = msg.getRepeatCount();
}
} }

View File

@@ -6,12 +6,10 @@ import lombok.Getter;
@Getter @Getter
public class TikTokIMDeleteEvent extends TikTokEvent { public class TikTokIMDeleteEvent extends TikTokEvent {
private final String data1; private final byte[] data;
private final String data2;
public TikTokIMDeleteEvent(WebcastImDeleteMessage msg) { public TikTokIMDeleteEvent(WebcastImDeleteMessage msg) {
super(msg.getHeader());; super(msg.getHeader());
data1 = msg.getData1(); data = msg.getData().toByteArray();
data2 = msg.getData2(); }
}
} }

View File

@@ -20,10 +20,15 @@ public class TikTokRankUpdateEvent extends TikTokEvent {
var rankData = msg.getData().getRankings(); var rankData = msg.getData().getRankings();
eventType = rankData.getType(); eventType = rankData.getType();
label = rankData.getLabel(); label = rankData.getLabel();
rank = rankData.getDetails(0).getLabel(); if(rankData.getDetailsList().isEmpty())
{
rank = "";
}
else
{
rank = rankData.getDetails(0).getLabel();
}
color = rankData.getColor().getColor(); color = rankData.getColor().getColor();
} }
public TikTokRankUpdateEvent(WebcastRankUpdateMessage msg) { public TikTokRankUpdateEvent(WebcastRankUpdateMessage msg) {
@@ -31,7 +36,14 @@ public class TikTokRankUpdateEvent extends TikTokEvent {
var rankData = msg.getData().getRankData(); var rankData = msg.getData().getRankData();
eventType = rankData.getType(); eventType = rankData.getType();
label = rankData.getLabel(); label = rankData.getLabel();
rank = rankData.getDetails(0).getLabel(); if(rankData.getDetailsList().isEmpty())
{
rank = "";
}
else
{
rank = rankData.getDetails(0).getLabel();
}
color = rankData.getColor().getColor(); color = rankData.getColor().getColor();
} }

View File

@@ -0,0 +1,19 @@
package io.github.jwdeveloper.tiktok.events.messages;
import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* Happens when TikTok websocket receive message from server
*/
@Data
@AllArgsConstructor
public class TikTokWebsocketMessageEvent extends TikTokEvent
{
private TikTokEvent event;
private WebcastResponse.Message message;
}

View File

@@ -0,0 +1,38 @@
package io.github.jwdeveloper.tiktok.exceptions;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import lombok.Getter;
import java.util.Base64;
public class TikTokLiveMessageException extends TikTokLiveException {
@Getter
private final WebcastResponse.Message webcastMessage;
@Getter
private final WebcastResponse webcastResponse;
public TikTokLiveMessageException(WebcastResponse.Message message,
WebcastResponse webcastResponse,
Throwable cause) {
super("Error while handling Message: " + message.getType() + ": \n", cause);
this.webcastMessage = message;
this.webcastResponse = webcastResponse;
}
public String messageName()
{
return webcastMessage.getType();
}
public String messageToBase64()
{
return Base64.getEncoder().encodeToString(webcastMessage.getBinary().toByteArray());
}
public String webcastResponseToBase64()
{
return Base64.getEncoder().encodeToString(webcastResponse.toByteArray());
}
}

View File

@@ -1,23 +0,0 @@
package io.github.jwdeveloper.tiktok.exceptions;
public class TikTokLiveMessageParsingException extends TikTokLiveException
{
public TikTokLiveMessageParsingException() {
}
public TikTokLiveMessageParsingException(String message) {
super(message);
}
public TikTokLiveMessageParsingException(String message, Throwable cause) {
super(message, cause);
}
public TikTokLiveMessageParsingException(Throwable cause) {
super(cause);
}
public TikTokLiveMessageParsingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -0,0 +1,8 @@
package io.github.jwdeveloper.tiktok.exceptions;
public class TikTokLiveOfflineHostException extends TikTokLiveException
{
public TikTokLiveOfflineHostException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,15 @@
package io.github.jwdeveloper.tiktok.exceptions;
public class TikTokMessageMappingException extends TikTokLiveException
{
public TikTokMessageMappingException(Class<?> inputClazz, Class<?> outputClass, Throwable throwable)
{
super("Unable to handle mapping from class: " + inputClazz.getSimpleName() + " to class " + outputClass.getSimpleName(),throwable);
}
public TikTokMessageMappingException(Class<?> inputClazz, Class<?> outputClass, String message)
{
super("Unable to handle mapping from class: " + inputClazz.getSimpleName() + " to class " + outputClass.getSimpleName()+": "+message);
}
}

View File

@@ -0,0 +1,15 @@
package io.github.jwdeveloper.tiktok.exceptions;
import lombok.Getter;
public class TikTokProtocolBufferException extends TikTokLiveException
{
@Getter
private final byte[] bytes;
public TikTokProtocolBufferException(String message, byte[] bytes, Throwable cause)
{
super(message, cause);
this.bytes = bytes;
}
}

View File

@@ -0,0 +1,10 @@
package io.github.jwdeveloper.tiktok.handler;
import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
public interface TikTokMessageHandler
{
TikTokEvent handle(WebcastResponse.Message message) throws Exception;
}

View File

@@ -1,7 +0,0 @@
package io.github.jwdeveloper.tiktok.http.Resource;
import lombok.Data;
@Data
public class ClientFetchDataResponse {
}

View File

@@ -0,0 +1,14 @@
package io.github.jwdeveloper.tiktok.live;
import io.github.jwdeveloper.tiktok.events.objects.TikTokGift;
import io.github.jwdeveloper.tiktok.models.GiftId;
import io.github.jwdeveloper.tiktok.models.gifts.TikTokGiftInfo;
import java.util.Map;
public interface GiftManager
{
Map<Integer, TikTokGiftInfo> getGiftsInfo();
Map<GiftId, TikTokGift> getActiveGifts();
}

View File

@@ -6,5 +6,6 @@ public interface LiveClient {
void disconnect(); void disconnect();
GiftManager getGiftManager();
LiveRoomInfo getRoomInfo(); LiveRoomInfo getRoomInfo();
} }

View File

@@ -3,7 +3,6 @@ package io.github.jwdeveloper.tiktok.live;
public interface LiveRoomInfo public interface LiveRoomInfo
{ {
int getViewersCount(); int getViewersCount();
String getRoomId(); String getRoomId();
String getUserName(); String getUserName();
} }

View File

@@ -7,7 +7,7 @@ import lombok.Data;
@AllArgsConstructor @AllArgsConstructor
public class GiftId public class GiftId
{ {
public long Gift; private long giftId;
public String UserName; private String userName;
} }

View File

@@ -0,0 +1,6 @@
package io.github.jwdeveloper.tiktok.models;
public enum GiftStrike
{
BEGIN, UPDATE, ENDED
}

View File

@@ -3,14 +3,13 @@ package io.github.jwdeveloper.tiktok.models.gifts;
import lombok.Data; import lombok.Data;
@Data @Data
public class DefaultFormat public class DefaultFormat {
{ private boolean bold;
private boolean bold ; private String color;
private String color ; private int font_size;
private int font_size ; private boolean italic;
private boolean italic ; private int italic_angle;
private int italic_angle ; private boolean use_highlight_color;
private boolean use_highlight_color ; private boolean use_remote_color;
private boolean use_remote_color ; private int weight;
private int weight ;
} }

View File

@@ -5,10 +5,9 @@ import lombok.Data;
import java.util.List; import java.util.List;
@Data @Data
public class DisplayText public class DisplayText {
{ private DefaultFormat default_format;
private DefaultFormat default_format ; private String default_pattern;
private String default_pattern ; private String key;
private String key ; private List<Object> pieces;
private List<Object> pieces ;
} }

View File

@@ -5,14 +5,13 @@ import lombok.Data;
import java.util.List; import java.util.List;
@Data @Data
public class GiftLabelIcon public class GiftLabelIcon {
{ private String avg_color;
private String avg_color ; private int height;
private int height ; private int image_type;
private int image_type ; private boolean is_animated;
private boolean is_animated ; private String open_web_url;
private String open_web_url ; private String uri;
private String uri ; private List<String> url_list;
private List<String> url_list ; private int width;
private int width ;
} }

View File

@@ -6,10 +6,9 @@ import java.util.List;
@Data @Data
public class GiftPanelBanner public class GiftPanelBanner {
{ private List<Object> bg_color_values;
private List<Object> bg_color_values ; private DisplayText display_text;
private DisplayText display_text ; private LeftIcon left_icon;
private LeftIcon left_icon ; private String schema_url;
private String schema_url ;
} }

View File

@@ -5,14 +5,13 @@ import lombok.Data;
import java.util.List; import java.util.List;
@Data @Data
public class Image public class Image {
{ private String avg_color;
private String avg_color ; private int height;
private int height ; private int image_type;
private int image_type ; private boolean is_animated;
private boolean is_animated ; private String open_web_url;
private String open_web_url ; private String uri;
private String uri ; private List<String> url_list;
private List<String> url_list ; private int width;
private int width ;
} }

View File

@@ -5,16 +5,15 @@ import lombok.Data;
import java.util.List; import java.util.List;
@Data @Data
public class LeftIcon public class LeftIcon {
{ private String avg_color;
private String avg_color ; private int height;
private int height ; private int image_type;
private int image_type ; private boolean is_animated;
private boolean is_animated ; private String open_web_url;
private String open_web_url ; private String uri;
private String uri ; private List<String> url_list;
private List<String> url_list ; private int width;
private int width ;
} }

View File

@@ -1,6 +1,9 @@
package io.github.jwdeveloper.tiktok.models.gifts; package io.github.jwdeveloper.tiktok.models.gifts;
import lombok.Data;
@Data
public class LockInfo public class LockInfo
{ {
public int lock_type; private int lock_type;
} }

View File

@@ -5,7 +5,7 @@ import lombok.Data;
import java.util.List; import java.util.List;
@Data @Data
public class TikTokGift public class TikTokGiftInfo
{ {
private int action_type; private int action_type;
private int app_id; private int app_id;

View File

@@ -0,0 +1,38 @@
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

@@ -414,13 +414,16 @@ message User {
Picture profilePicture = 9; // Avatar Picture profilePicture = 9; // Avatar
Picture picture720 = 10; // 720p Picture picture720 = 10; // 720p
Picture picture1080 = 11; // 1080p Picture picture1080 = 11; // 1080p
uint32 data2 = 15; int32 status = 15;
uint64 data3 = 16; int64 createTime = 16;
int64 modifyTime = 17;
int32 secret = 18;
string shareQrcodeUri = 19;
repeated Picture additionalPictures = 21; repeated Picture additionalPictures = 21;
FollowerData followerData = 22; FollowerData followerData = 22;
string userString1 = 23; // string userString1 = 23;
UserRanking userRank1 = 25; UserRanking userRank1 = 25;
string userString2 = 32; // string userString2 = 32;
uint64 data4 = 37; uint64 data4 = 37;
string uniqueId = 38; // @-ID for user string uniqueId = 38; // @-ID for user
string data5 = 46; string data5 = 46;
@@ -749,8 +752,7 @@ message WebcastHourlyRankMessage {
// Message related to Chat-moderation? // Message related to Chat-moderation?
message WebcastImDeleteMessage { message WebcastImDeleteMessage {
MessageHeader header = 1; MessageHeader header = 1;
string data1 = 2; bytes data = 3;
string data2 = 3;
} }
message WebcastInRoomBannerMessage { message WebcastInRoomBannerMessage {

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.8-Release</version> <version>0.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -29,6 +29,23 @@
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
<version>3.24.1</version> <version>3.24.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
<scope>runtime</scope>
</dependency>
</dependencies> </dependencies>

View File

@@ -1,45 +1,47 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.events.objects.TikTokGift; import io.github.jwdeveloper.tiktok.events.objects.TikTokGift;
import io.github.jwdeveloper.tiktok.http.TikTokApiService; import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.messages.WebcastGiftMessage;
import io.github.jwdeveloper.tiktok.models.GiftId; import io.github.jwdeveloper.tiktok.models.GiftId;
import io.github.jwdeveloper.tiktok.models.gifts.TikTokGiftInfo;
import lombok.Getter; import lombok.Getter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger;
public class TikTokGiftManager { public class TikTokGiftManager implements GiftManager {
private Logger logger;
private ClientSettings clientSettings;
private TikTokApiService apiService;
private Map<Integer, TikTokGift> gifts;
@Getter @Getter
private Map<GiftId, TikTokGift> activeGifts; private final Map<Integer, TikTokGiftInfo> giftsInfo;
@Getter
private final Map<GiftId, TikTokGift> activeGifts;
public TikTokGiftManager(Logger logger, TikTokApiService apiService, ClientSettings clientSettings) { public TikTokGiftManager() {
this.logger = logger; giftsInfo = new HashMap<>();
this.clientSettings = clientSettings;
this.apiService = apiService;
this.gifts = new HashMap<>();
activeGifts = new HashMap<>(); activeGifts = new HashMap<>();
} }
public void loadGifts() { public TikTokGift updateActiveGift(WebcastGiftMessage giftMessage) {
if (!clientSettings.isDownloadGiftInfo()) { var giftId = new GiftId(giftMessage.getGiftId(), giftMessage.getSender().getUniqueId());
return; if (activeGifts.containsKey(giftId)) {
var gift = activeGifts.get(giftId);
gift.setAmount(giftMessage.getAmount());
} else {
var newGift = new TikTokGift(giftMessage);
activeGifts.put(giftId, newGift);
} }
logger.info("Fetching gifts");
//TODO gifts =apiService.fetchAvailableGifts(); var gift = activeGifts.get(giftId);
if (giftMessage.getRepeatEnd()) {
gift.setStreakFinished(true);
activeGifts.remove(giftId);
}
return gift;
} }
public List<TikTokGift> getGifts() public void loadGifsInfo(Map<Integer, TikTokGiftInfo> gifts) {
{ this.giftsInfo.putAll(gifts);
return gifts.values().stream().toList();
} }
} }

View File

@@ -8,6 +8,4 @@ public class TikTokLive
{ {
return new TikTokLiveClientBuilder(userName); return new TikTokLiveClientBuilder(userName);
} }
} }

View File

@@ -1,53 +1,70 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.events.messages.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler; import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.http.TikTokApiService; import io.github.jwdeveloper.tiktok.http.TikTokApiService;
import io.github.jwdeveloper.tiktok.live.ConnectionState; import io.github.jwdeveloper.tiktok.live.ConnectionState;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo; import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
import io.github.jwdeveloper.tiktok.live.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient; import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
import java.util.logging.Logger; import java.util.logging.Logger;
public class TikTokLiveClient implements LiveClient { public class TikTokLiveClient implements LiveClient {
private final TikTokRoomInfo meta; private final TikTokRoomInfo liveRoomInfo;
private final TikTokGiftManager giftManager; private final TikTokGiftManager tikTokGiftManager;
private final TikTokApiService apiClient; private final TikTokApiService apiClient;
private final TikTokWebSocketClient webSocketClient; private final TikTokWebSocketClient webSocketClient;
private final TikTokEventHandler tikTokEventHandler; private final TikTokEventHandler tikTokEventHandler;
private final ClientSettings clientSettings;
private final Logger logger; private final Logger logger;
public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta, public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta,
TikTokApiService tikTokApiService, TikTokApiService tikTokApiService,
TikTokWebSocketClient webSocketClient, TikTokWebSocketClient webSocketClient,
TikTokGiftManager tikTokGiftManager, TikTokGiftManager tikTokGiftManager,
TikTokEventHandler tikTokEventHandler, TikTokEventHandler tikTokEventHandler,
ClientSettings clientSettings,
Logger logger) { Logger logger) {
this.meta = tikTokLiveMeta; this.liveRoomInfo = tikTokLiveMeta;
this.giftManager = tikTokGiftManager; this.tikTokGiftManager = tikTokGiftManager;
this.apiClient = tikTokApiService; this.apiClient = tikTokApiService;
this.webSocketClient = webSocketClient; this.webSocketClient = webSocketClient;
this.logger = logger;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
this.clientSettings = clientSettings;
this.logger = logger;
} }
public void connect() { public void connect() {
try { try {
tryConnect(); tryConnect();
} catch (Exception e) { }
e.printStackTrace(); catch (TikTokLiveException e)
{
setState(ConnectionState.DISCONNECTED); setState(ConnectionState.DISCONNECTED);
tikTokEventHandler.publish(this, new TikTokErrorEvent(e));
tikTokEventHandler.publish(this, new TikTokDisconnectedEvent());
if(e instanceof TikTokLiveOfflineHostException && clientSettings.isRetryOnConnectionFailure())
{
try {
Thread.sleep(clientSettings.getRetryConnectionTimeout().toMillis());
}
catch (Exception ignored){}
logger.info("Reconnecting");
this.connect();
}
throw e;
} }
} }
public void disconnect() { public void disconnect() {
if (!meta.hasConnectionState(ConnectionState.CONNECTED)) { if (!liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED)) {
return; return;
} }
webSocketClient.stop(); webSocketClient.stop();
@@ -55,40 +72,48 @@ public class TikTokLiveClient implements LiveClient {
} }
public void tryConnect() { public void tryConnect() {
if (meta.hasConnectionState(ConnectionState.CONNECTED)) if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED))
throw new TikTokLiveException("Already connected"); throw new TikTokLiveException("Already connected");
if (meta.hasConnectionState(ConnectionState.CONNECTING)) if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTING))
throw new TikTokLiveException("Already connecting"); throw new TikTokLiveException("Already connecting");
logger.info("Connecting"); logger.info("Connecting");
setState(ConnectionState.CONNECTING); setState(ConnectionState.CONNECTING);
var roomId = apiClient.fetchRoomId(meta.getUserName()); var roomId = apiClient.fetchRoomId(liveRoomInfo.getUserName());
meta.setRoomId(roomId); liveRoomInfo.setRoomId(roomId);
var roomData =apiClient.fetchRoomInfo(); var roomData = apiClient.fetchRoomInfo();
if (roomData.getStatus() == 0 || roomData.getStatus() == 4) if (roomData.getStatus() == 0 || roomData.getStatus() == 4) {
{ throw new TikTokLiveOfflineHostException("LiveStream for HostID could not be found. Is the Host online?");
throw new TikTokLiveException("LiveStream for HostID could not be found. Is the Host online?");
} }
// giftManager.loadGifts(); if (clientSettings.isDownloadGiftInfo())
{
logger.info("Fetch Gift info");
var gifts = apiClient.fetchAvailableGifts();
tikTokGiftManager.loadGifsInfo(gifts);
}
var clientData = apiClient.fetchClientData(); var clientData = apiClient.fetchClientData();
webSocketClient.start(clientData); webSocketClient.start(clientData, this);
setState(ConnectionState.CONNECTED); setState(ConnectionState.CONNECTED);
} }
public LiveRoomInfo getRoomInfo() { public LiveRoomInfo getRoomInfo() {
return meta; return liveRoomInfo;
}
@Override
public GiftManager getGiftManager() {
return tikTokGiftManager;
} }
private void setState(ConnectionState connectionState) { private void setState(ConnectionState connectionState) {
logger.info("TikTokLive client state: " + connectionState.name()); logger.info("TikTokLive client state: " + connectionState.name());
meta.setConnectionState(connectionState); liveRoomInfo.setConnectionState(connectionState);
} }

View File

@@ -2,19 +2,22 @@ package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.events.TikTokEvent; import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.TikTokEventBuilder; import io.github.jwdeveloper.tiktok.events.TikTokEventBuilder;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler; import io.github.jwdeveloper.tiktok.events.TikTokEventConsumer;
import io.github.jwdeveloper.tiktok.events.messages.*; import io.github.jwdeveloper.tiktok.events.messages.*;
import io.github.jwdeveloper.tiktok.handlers.WebResponseHandler; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
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.TikTokHttpApiClient; import io.github.jwdeveloper.tiktok.http.TikTokHttpApiClient;
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory; import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.TikTokRoomInfo; import io.github.jwdeveloper.tiktok.utils.CancelationToken;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient; import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
import java.time.Duration; import java.time.Duration;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveClientBuilder> { public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveClientBuilder> {
@@ -40,50 +43,47 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli
clientSettings.setTimeout(Duration.ofSeconds(Constants.DEFAULT_TIMEOUT)); clientSettings.setTimeout(Duration.ofSeconds(Constants.DEFAULT_TIMEOUT));
} }
if (clientSettings.getPollingInterval() == null) {
clientSettings.setPollingInterval(Duration.ofSeconds(Constants.DEFAULT_POLLTIME));
}
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().equals("")) { if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().equals("")) {
clientSettings.setClientLanguage(Constants.DefaultClientSettings().getClientLanguage()); clientSettings.setClientLanguage(Constants.DefaultClientSettings().getClientLanguage());
} }
if (clientSettings.getSocketBufferSize() < 500_000) {
clientSettings.setSocketBufferSize(Constants.DefaultClientSettings().getSocketBufferSize());
}
if (clientSettings.getHostName() == null || clientSettings.getHostName().equals("")) {
if (clientSettings.getHostName() == null || clientSettings.getHostName() .equals("")) { throw new TikTokLiveException("HostName can not be null");
throw new RuntimeException("HostName can not be null");
} }
var params = clientSettings.getClientParameters(); var params = clientSettings.getClientParameters();
params.put("app_language", clientSettings.getClientLanguage()); params.put("app_language", clientSettings.getClientLanguage());
params.put("webcast_language", clientSettings.getClientLanguage()); params.put("webcast_language", clientSettings.getClientLanguage());
logger.setLevel(clientSettings.getLogLevel());
if(clientSettings.isPrintToConsole() && clientSettings.getLogLevel() == Level.OFF)
{
logger.setLevel(Level.ALL);
}
} }
public LiveClient build() { public LiveClient build() {
validate(); validate();
var meta = new TikTokRoomInfo(); var tiktokRoomInfo = new TikTokRoomInfo();
meta.setUserName(clientSettings.getHostName()); tiktokRoomInfo.setUserName(clientSettings.getHostName());
var cookieJar = new TikTokCookieJar(); var cookieJar = new TikTokCookieJar();
var requestFactory = new TikTokHttpRequestFactory(cookieJar); var requestFactory = new TikTokHttpRequestFactory(cookieJar);
var apiClient = new TikTokHttpApiClient(cookieJar, requestFactory); var apiClient = new TikTokHttpApiClient(cookieJar, requestFactory);
var apiService = new TikTokApiService(apiClient, logger, clientSettings); var apiService = new TikTokApiService(apiClient, logger, clientSettings);
var giftManager = new TikTokGiftManager(logger, apiService, clientSettings); var giftManager = new TikTokGiftManager();
var webResponseHandler = new WebResponseHandler(tikTokEventHandler,giftManager); var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler, clientSettings, logger, giftManager, tiktokRoomInfo);
var webSocketClient = new TikTokWebSocketClient(logger, var webSocketClient = new TikTokWebSocketClient(logger,
cookieJar, cookieJar,
requestFactory,
clientSettings, clientSettings,
webResponseHandler, webResponseHandler,
tikTokEventHandler); tikTokEventHandler);
return new TikTokLiveClient(meta, apiService, webSocketClient, giftManager, tikTokEventHandler, logger); return new TikTokLiveClient(tiktokRoomInfo, apiService, webSocketClient, giftManager, tikTokEventHandler, clientSettings, logger);
} }
public LiveClient buildAndRun() { public LiveClient buildAndRun() {
@@ -92,214 +92,236 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli
return client; return client;
} }
public TikTokLiveClientBuilder onUnhandledSocial(Consumer<TikTokUnhandledSocialEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledSocialEvent.class,event); public TikTokLiveClientBuilder onUnhandledSocial(
TikTokEventConsumer<TikTokUnhandledSocialEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledSocialEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicFanTicket(Consumer<TikTokLinkMicFanTicketEvent> event) { public TikTokLiveClientBuilder onLinkMicFanTicket(
tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class,event); TikTokEventConsumer<TikTokLinkMicFanTicketEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onEnvelope(Consumer<TikTokEnvelopeEvent> event) { public TikTokLiveClientBuilder onEnvelope(TikTokEventConsumer<TikTokEnvelopeEvent> event) {
tikTokEventHandler.subscribe(TikTokEnvelopeEvent.class,event); tikTokEventHandler.subscribe(TikTokEnvelopeEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onShopMessage(Consumer<TikTokShopMessageEvent> event) { public TikTokLiveClientBuilder onShopMessage(TikTokEventConsumer<TikTokShopMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokShopMessageEvent.class,event); tikTokEventHandler.subscribe(TikTokShopMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onDetectMessage(Consumer<TikTokDetectMessageEvent> event) { public TikTokLiveClientBuilder onDetectMessage(
tikTokEventHandler.subscribe(TikTokDetectMessageEvent.class,event); TikTokEventConsumer<TikTokDetectMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokDetectMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkLayerMessage(Consumer<TikTokLinkLayerMessageEvent> event) { public TikTokLiveClientBuilder onLinkLayerMessage(
tikTokEventHandler.subscribe(TikTokLinkLayerMessageEvent.class,event); TikTokEventConsumer<TikTokLinkLayerMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkLayerMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onConnected(Consumer<TikTokConnectedEvent> event) { public TikTokLiveClientBuilder onConnected(TikTokEventConsumer<TikTokConnectedEvent> event) {
tikTokEventHandler.subscribe(TikTokConnectedEvent.class,event); tikTokEventHandler.subscribe(TikTokConnectedEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onCaption(Consumer<TikTokCaptionEvent> event) { public TikTokLiveClientBuilder onCaption(TikTokEventConsumer<TikTokCaptionEvent> event) {
tikTokEventHandler.subscribe(TikTokCaptionEvent.class,event); tikTokEventHandler.subscribe(TikTokCaptionEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onQuestion(Consumer<TikTokQuestionEvent> event) { public TikTokLiveClientBuilder onQuestion(TikTokEventConsumer<TikTokQuestionEvent> event) {
tikTokEventHandler.subscribe(TikTokQuestionEvent.class,event); tikTokEventHandler.subscribe(TikTokQuestionEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRoomPinMessage(Consumer<TikTokRoomPinMessageEvent> event) { public TikTokLiveClientBuilder onRoomPinMessage(
tikTokEventHandler.subscribe(TikTokRoomPinMessageEvent.class,event); TikTokEventConsumer<TikTokRoomPinMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomPinMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRoomMessage(Consumer<TikTokRoomMessageEvent> event) { public TikTokLiveClientBuilder onRoomMessage(TikTokEventConsumer<TikTokRoomMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomMessageEvent.class,event); tikTokEventHandler.subscribe(TikTokRoomMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLivePaused(Consumer<TikTokLivePausedEvent> event) { public TikTokLiveClientBuilder onLivePaused(TikTokEventConsumer<TikTokLivePausedEvent> event) {
tikTokEventHandler.subscribe(TikTokLivePausedEvent.class,event); tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLike(Consumer<TikTokLikeEvent> event) { public TikTokLiveClientBuilder onLike(TikTokEventConsumer<TikTokLikeEvent> event) {
tikTokEventHandler.subscribe(TikTokLikeEvent.class,event); tikTokEventHandler.subscribe(TikTokLikeEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMessage(Consumer<TikTokLinkMessageEvent> event) { public TikTokLiveClientBuilder onLinkMessage(TikTokEventConsumer<TikTokLinkMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMessageEvent.class,event); tikTokEventHandler.subscribe(TikTokLinkMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onBarrageMessage(Consumer<TikTokBarrageMessageEvent> event) { public TikTokLiveClientBuilder onBarrageMessage(
tikTokEventHandler.subscribe(TikTokBarrageMessageEvent.class,event); TikTokEventConsumer<TikTokBarrageMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokBarrageMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onGiftMessage(Consumer<TikTokGiftMessageEvent> event) { public TikTokLiveClientBuilder onGiftMessage(TikTokEventConsumer<TikTokGiftMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokGiftMessageEvent.class,event); tikTokEventHandler.subscribe(TikTokGiftMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicArmies(Consumer<TikTokLinkMicArmiesEvent> event) { public TikTokLiveClientBuilder onLinkMicArmies(
tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class,event); TikTokEventConsumer<TikTokLinkMicArmiesEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onEmote(Consumer<TikTokEmoteEvent> event) { public TikTokLiveClientBuilder onEmote(TikTokEventConsumer<TikTokEmoteEvent> event) {
tikTokEventHandler.subscribe(TikTokEmoteEvent.class,event); tikTokEventHandler.subscribe(TikTokEmoteEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onUnauthorizedMember( public TikTokLiveClientBuilder onUnauthorizedMember(
Consumer<TikTokUnauthorizedMemberEvent> event) { TikTokEventConsumer<TikTokUnauthorizedMemberEvent> event) {
tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class,event); tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onInRoomBanner(Consumer<TikTokInRoomBannerEvent> event) { public TikTokLiveClientBuilder onInRoomBanner(
tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class,event); TikTokEventConsumer<TikTokInRoomBannerEvent> event) {
tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicMethod(Consumer<TikTokLinkMicMethodEvent> event) { public TikTokLiveClientBuilder onLinkMicMethod(
tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class,event); TikTokEventConsumer<TikTokLinkMicMethodEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onSubscribe(Consumer<TikTokSubscribeEvent> event) { public TikTokLiveClientBuilder onSubscribe(TikTokEventConsumer<TikTokSubscribeEvent> event) {
tikTokEventHandler.subscribe(TikTokSubscribeEvent.class,event); tikTokEventHandler.subscribe(TikTokSubscribeEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onPollMessage(Consumer<TikTokPollMessageEvent> event) { public TikTokLiveClientBuilder onPollMessage(TikTokEventConsumer<TikTokPollMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokPollMessageEvent.class,event); tikTokEventHandler.subscribe(TikTokPollMessageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onFollow(Consumer<TikTokFollowEvent> event) { public TikTokLiveClientBuilder onFollow(TikTokEventConsumer<TikTokFollowEvent> event) {
tikTokEventHandler.subscribe(TikTokFollowEvent.class,event); tikTokEventHandler.subscribe(TikTokFollowEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRoomViewerData(Consumer<TikTokRoomViewerDataEvent> event) { public TikTokLiveClientBuilder onRoomViewerData(
tikTokEventHandler.subscribe(TikTokRoomViewerDataEvent.class,event); TikTokEventConsumer<TikTokRoomViewerDataEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomViewerDataEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onGoalUpdate(Consumer<TikTokGoalUpdateEvent> event) { public TikTokLiveClientBuilder onGoalUpdate(TikTokEventConsumer<TikTokGoalUpdateEvent> event) {
tikTokEventHandler.subscribe(TikTokGoalUpdateEvent.class,event); tikTokEventHandler.subscribe(TikTokGoalUpdateEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onComment(Consumer<TikTokCommentEvent> event) { public TikTokLiveClientBuilder onComment(TikTokEventConsumer<TikTokCommentEvent> event) {
tikTokEventHandler.subscribe(TikTokCommentEvent.class,event); tikTokEventHandler.subscribe(TikTokCommentEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRankUpdate(Consumer<TikTokRankUpdateEvent> event) { public TikTokLiveClientBuilder onRankUpdate(TikTokEventConsumer<TikTokRankUpdateEvent> event) {
tikTokEventHandler.subscribe(TikTokRankUpdateEvent.class,event); tikTokEventHandler.subscribe(TikTokRankUpdateEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onIMDelete(Consumer<TikTokIMDeleteEvent> event) { public TikTokLiveClientBuilder onIMDelete(TikTokEventConsumer<TikTokIMDeleteEvent> event) {
tikTokEventHandler.subscribe(TikTokIMDeleteEvent.class,event); tikTokEventHandler.subscribe(TikTokIMDeleteEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLiveEnded(Consumer<TikTokLiveEndedEvent> event) { public TikTokLiveClientBuilder onLiveEnded(TikTokEventConsumer<TikTokLiveEndedEvent> event) {
tikTokEventHandler.subscribe(TikTokLiveEndedEvent.class,event); tikTokEventHandler.subscribe(TikTokLiveEndedEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onError(Consumer<TikTokErrorEvent> event) { public TikTokLiveClientBuilder onError(TikTokEventConsumer<TikTokErrorEvent> event) {
tikTokEventHandler.subscribe(TikTokErrorEvent.class,event); tikTokEventHandler.subscribe(TikTokErrorEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onUnhandled(Consumer<TikTokUnhandledEvent> event) { public TikTokLiveClientBuilder onUnhandled(TikTokEventConsumer<TikTokUnhandledEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledEvent.class,event); tikTokEventHandler.subscribe(TikTokUnhandledEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onJoin(Consumer<TikTokJoinEvent> event) { public TikTokLiveClientBuilder onJoin(TikTokEventConsumer<TikTokJoinEvent> event) {
tikTokEventHandler.subscribe(TikTokJoinEvent.class,event); tikTokEventHandler.subscribe(TikTokJoinEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRankText(Consumer<TikTokRankTextEvent> event) { public TikTokLiveClientBuilder onRankText(TikTokEventConsumer<TikTokRankTextEvent> event) {
tikTokEventHandler.subscribe(TikTokRankTextEvent.class,event); tikTokEventHandler.subscribe(TikTokRankTextEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onShare(Consumer<TikTokShareEvent> event) { public TikTokLiveClientBuilder onShare(TikTokEventConsumer<TikTokShareEvent> event) {
tikTokEventHandler.subscribe(TikTokShareEvent.class,event); tikTokEventHandler.subscribe(TikTokShareEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onUnhandledMember(Consumer<TikTokUnhandledMemberEvent> event) { public TikTokLiveClientBuilder onUnhandledMember(
tikTokEventHandler.subscribe(TikTokUnhandledMemberEvent.class,event); TikTokEventConsumer<TikTokUnhandledMemberEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledMemberEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onSubNotify(Consumer<TikTokSubNotifyEvent> event) { public TikTokLiveClientBuilder onSubNotify(TikTokEventConsumer<TikTokSubNotifyEvent> event) {
tikTokEventHandler.subscribe(TikTokSubNotifyEvent.class,event); tikTokEventHandler.subscribe(TikTokSubNotifyEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicBattle(Consumer<TikTokLinkMicBattleEvent> event) { public TikTokLiveClientBuilder onLinkMicBattle(
tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class,event); TikTokEventConsumer<TikTokLinkMicBattleEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onDisconnected(Consumer<TikTokDisconnectedEvent> event) { public TikTokLiveClientBuilder onDisconnected(
tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class,event); TikTokEventConsumer<TikTokDisconnectedEvent> event) {
tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onGiftBroadcast(Consumer<TikTokGiftBroadcastEvent> event) { public TikTokLiveClientBuilder onGiftBroadcast(
tikTokEventHandler.subscribe(TikTokGiftBroadcastEvent.class,event); TikTokEventConsumer<TikTokGiftBroadcastEvent> event) {
tikTokEventHandler.subscribe(TikTokGiftBroadcastEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onUnhandledControl(Consumer<TikTokUnhandledControlEvent> event) { public TikTokLiveClientBuilder onUnhandledControl(
tikTokEventHandler.subscribe(TikTokUnhandledControlEvent.class,event); TikTokEventConsumer<TikTokUnhandledControlEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledControlEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onEvent(Consumer<TikTokEvent> event) { public TikTokLiveClientBuilder onEvent(TikTokEventConsumer<TikTokEvent> event) {
tikTokEventHandler.subscribe(TikTokEvent.class,event); tikTokEventHandler.subscribe(TikTokEvent.class, event);
return this;
}
@Override
public TikTokLiveClientBuilder onWebsocketMessage(TikTokEventConsumer<TikTokWebsocketMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokWebsocketMessageEvent.class, event);
return this; return this;
} }
} }
@@ -307,3 +329,6 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli

View File

@@ -1,5 +1,7 @@
package io.github.jwdeveloper.tiktok.live; package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.live.ConnectionState;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
import lombok.Data; import lombok.Data;
@Data @Data

View File

@@ -1,40 +1,38 @@
package io.github.jwdeveloper.tiktok.handlers; package io.github.jwdeveloper.tiktok.handlers;
import io.github.jwdeveloper.tiktok.TikTokLiveClient;
import io.github.jwdeveloper.tiktok.events.TikTokEvent; import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.TikTokEventConsumer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer;
public class TikTokEventHandler { public class TikTokEventHandler {
private final Map<String, Consumer> events; private final Map<String, TikTokEventConsumer> events;
public TikTokEventHandler() public TikTokEventHandler() {
{
events = new HashMap<>(); events = new HashMap<>();
} }
public void publish(TikTokEvent tikTokEvent) public void publish(TikTokLiveClient tikTokLiveClient, TikTokEvent tikTokEvent) {
{ if (events.containsKey(TikTokEvent.class.getSimpleName())) {
if(events.containsKey(TikTokEvent.class.getSimpleName()))
{
var handler = events.get(TikTokEvent.class.getSimpleName()); var handler = events.get(TikTokEvent.class.getSimpleName());
handler.accept(tikTokEvent); handler.onEvent(tikTokLiveClient, tikTokEvent);
} }
var name = tikTokEvent.getClass().getSimpleName(); var name = tikTokEvent.getClass().getSimpleName();
if(!events.containsKey(name)) if (!events.containsKey(name)) {
{
return; return;
} }
var handler = events.get(name); var handler = events.get(name);
handler.accept(tikTokEvent); handler.onEvent(tikTokLiveClient, tikTokEvent);
} }
public <T extends TikTokEvent> void subscribe(Class<?> clazz, Consumer<T> event) public <T extends TikTokEvent> void subscribe(Class<?> clazz, TikTokEventConsumer<T> event) {
{ events.put(clazz.getSimpleName(), event);
events.put(clazz.getSimpleName(),event);
} }
public <T extends TikTokEvent> void unsubscribe(Class<?> clazz) {
events.remove(clazz);
}
} }

View File

@@ -1,12 +1,99 @@
package io.github.jwdeveloper.tiktok.handlers; package io.github.jwdeveloper.tiktok.handlers;
import com.google.protobuf.ByteString;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.TikTokLiveClient;
import io.github.jwdeveloper.tiktok.events.TikTokEvent; import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokUnhandledEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokMessageMappingException;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
public interface TikTokMessageHandler<T> import java.util.Arrays;
{ import java.util.Base64;
Class<T> getHandleClazz(); import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Logger;
TikTokEvent handle(WebcastResponse.Message message) throws Exception;
public abstract class TikTokMessageHandler {
private final Map<String, io.github.jwdeveloper.tiktok.handler.TikTokMessageHandler> handlers;
private final TikTokEventHandler tikTokEventHandler;
private final ClientSettings clientSettings;
protected final Logger logger;
public TikTokMessageHandler(TikTokEventHandler tikTokEventHandler,ClientSettings clientSettings, Logger logger) {
handlers = new HashMap<>();
this.tikTokEventHandler = tikTokEventHandler;
this.clientSettings = clientSettings;
this.logger = logger;
init();
}
public abstract void init();
public void register(Class<?> clazz, Function<WebcastResponse.Message, TikTokEvent> func) {
handlers.put(clazz.getSimpleName(), func::apply);
}
public void register(Class<?> input, Class<?> output) {
register(input, (e) -> mapMessageToEvent(input, output, e));
}
public void handle(TikTokLiveClient client, WebcastResponse webcastResponse) {
for (var message : webcastResponse.getMessagesList()) {
try
{
if(clientSettings.isPrintMessageData())
{
var type= message.getType();
var base64 = Base64.getEncoder().encodeToString(message.getBinary().toByteArray());
logger.info(type+": \n "+base64);
}
handleSingleMessage(client, message);
} catch (Exception e) {
var exception = new TikTokLiveMessageException(message, webcastResponse, e);
tikTokEventHandler.publish(client, new TikTokErrorEvent(exception));
}
}
}
private void handleSingleMessage(TikTokLiveClient client, WebcastResponse.Message message) throws Exception {
if (!handlers.containsKey(message.getType())) {
tikTokEventHandler.publish(client, new TikTokUnhandledEvent(message));
return;
}
var handler = handlers.get(message.getType());
var tiktokEvent = handler.handle(message);
tikTokEventHandler.publish(client, new TikTokWebsocketMessageEvent(tiktokEvent, message));
tikTokEventHandler.publish(client, tiktokEvent);
}
protected TikTokEvent mapMessageToEvent(Class<?> inputClazz, Class<?> outputClass, WebcastResponse.Message message) {
try {
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", ByteString.class);
var deserialized = parseMethod.invoke(null, message.getBinary());
var constructors = Arrays.stream(outputClass.getConstructors())
.filter(ea -> Arrays.stream(ea.getParameterTypes())
.toList()
.contains(inputClazz))
.findFirst();
if(constructors.isEmpty())
{
throw new TikTokMessageMappingException(inputClazz, outputClass, "Unable to find constructor with input class type");
}
var tiktokEvent = constructors.get().newInstance(deserialized);
return (TikTokEvent) tiktokEvent;
} catch (Exception ex) {
throw new TikTokMessageMappingException(inputClazz, outputClass, ex);
}
}
} }

View File

@@ -1,6 +1,8 @@
package io.github.jwdeveloper.tiktok.handlers; package io.github.jwdeveloper.tiktok.handlers;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.TikTokGiftManager; import io.github.jwdeveloper.tiktok.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.events.TikTokEvent; import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.messages.*; import io.github.jwdeveloper.tiktok.events.messages.*;
import io.github.jwdeveloper.tiktok.events.objects.TikTokGift; import io.github.jwdeveloper.tiktok.events.objects.TikTokGift;
@@ -9,28 +11,35 @@ import io.github.jwdeveloper.tiktok.models.GiftId;
import io.github.jwdeveloper.tiktok.models.SocialTypes; import io.github.jwdeveloper.tiktok.models.SocialTypes;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class WebResponseHandler extends WebResponseHandlerBase { public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
private final TikTokGiftManager giftManager; private final TikTokGiftManager giftManager;
private final TikTokRoomInfo roomInfo;
public WebResponseHandler(TikTokEventHandler tikTokEventHandler, TikTokGiftManager giftManager) { public TikTokMessageHandlerRegistration(TikTokEventHandler tikTokEventHandler,
super(tikTokEventHandler); ClientSettings clientSettings,
Logger logger,
TikTokGiftManager giftManager,
TikTokRoomInfo roomInfo) {
super(tikTokEventHandler, clientSettings, logger);
this.giftManager = giftManager; this.giftManager = giftManager;
this.roomInfo = roomInfo;
} }
@Override @Override
public void init() { public void init() {
//ConnectionEvents events //ConnectionEvents events
register(WebcastControlMessage.class, TikTokRoomMessageEvent.class); register(WebcastControlMessage.class, this::handleWebcastControlMessage);
register(SystemMessage.class, this::handleWebcastControlMessage); register(SystemMessage.class,TikTokRoomMessageEvent.class);
//Room status events //Room status events
register(WebcastLiveIntroMessage.class, TikTokRoomMessageEvent.class); register(WebcastLiveIntroMessage.class, TikTokRoomMessageEvent.class);
register(WebcastRoomUserSeqMessage.class, TikTokRoomViewerDataEvent.class); //TODO update viewer count ViewerCount = userSeqMessage.ViewerCount; register(WebcastRoomUserSeqMessage.class, this::handleRoomUserSeqMessage);
register(RoomMessage.class, TikTokRoomMessageEvent.class); register(RoomMessage.class, TikTokRoomMessageEvent.class);
register(WebcastRoomMessage.class, TikTokRoomMessageEvent.class); register(WebcastRoomMessage.class, TikTokRoomMessageEvent.class);
register(WebcastCaptionMessage.class, TikTokCaptionEvent.class); register(WebcastCaptionMessage.class, TikTokCaptionEvent.class);
@@ -74,9 +83,9 @@ public class WebResponseHandler extends WebResponseHandlerBase {
} }
@SneakyThrows @SneakyThrows
private TikTokEvent handleWebcastControlMessage(WebcastResponse.Message msg) private TikTokEvent handleWebcastControlMessage(WebcastResponse.Message msg) {
{
var message = WebcastControlMessage.parseFrom(msg.getBinary()); var message = WebcastControlMessage.parseFrom(msg.getBinary());
return switch (message.getAction()) { return switch (message.getAction()) {
case STREAM_PAUSED -> new TikTokLivePausedEvent(); case STREAM_PAUSED -> new TikTokLivePausedEvent();
@@ -88,29 +97,7 @@ public class WebResponseHandler extends WebResponseHandlerBase {
@SneakyThrows @SneakyThrows
private TikTokEvent handleGift(WebcastResponse.Message msg) { private TikTokEvent handleGift(WebcastResponse.Message msg) {
var giftMessage = WebcastGiftMessage.parseFrom(msg.getBinary()); var giftMessage = WebcastGiftMessage.parseFrom(msg.getBinary());
var giftId = new GiftId(giftMessage.getGiftId(), giftMessage.getSender().getUniqueId()); giftManager.updateActiveGift(giftMessage);
var activeGifts = giftManager.getActiveGifts();
if (activeGifts.containsKey(giftId)) {
// Debug.Log($"Updating Gift[{giftId.Gift}]Amount[{message.Amount}]");
var gift = activeGifts.get(giftId);
gift.setAmount(giftMessage.getAmount());
} else {
TikTokGift newGift = new TikTokGift(giftMessage);
activeGifts.put(giftId, newGift);
// Debug.Log($"New Gift[{giftId.Gift}]Amount[{message.Amount}]");
// RunEvent(OnGift, newGift);
}
if (giftMessage.getRepeatEnd()) {
//if (ShouldLog(LogLevel.Verbose))
// Debug.Log($"GiftStreak Ended: [{giftId.Gift}] Amount[{message.Amount}]")
var gift = activeGifts.get(giftId);
gift.setStreakFinished(true);
activeGifts.remove(gift);
}
// Debug.Log($"Handling GiftMessage");
return new TikTokGiftMessageEvent(giftMessage); return new TikTokGiftMessageEvent(giftMessage);
} }
@@ -118,16 +105,18 @@ public class WebResponseHandler extends WebResponseHandlerBase {
private TikTokEvent handleSocialMedia(WebcastResponse.Message msg) { private TikTokEvent handleSocialMedia(WebcastResponse.Message msg) {
var message = WebcastSocialMessage.parseFrom(msg.getBinary()); var message = WebcastSocialMessage.parseFrom(msg.getBinary());
String type = message.getHeader().getSocialData().getType(); var socialType = message.getHeader().getSocialData().getType();
Pattern pattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share"); var pattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share");
Matcher matcher = pattern.matcher(type); var matcher = pattern.matcher(socialType);
if (matcher.find()) {
if (matcher.find())
{
var value = matcher.group(0); var value = matcher.group(0);
var number = Integer.parseInt(value); var number = Integer.parseInt(value);
return new TikTokShareEvent(message, number); return new TikTokShareEvent(message, number);
} }
var socialType = message.getHeader().getSocialData().getType();
return switch (socialType) { return switch (socialType) {
case SocialTypes.LikeType -> new TikTokLikeEvent(message); case SocialTypes.LikeType -> new TikTokLikeEvent(message);
case SocialTypes.FollowType -> new TikTokFollowEvent(message); case SocialTypes.FollowType -> new TikTokFollowEvent(message);
@@ -146,4 +135,11 @@ public class WebResponseHandler extends WebResponseHandlerBase {
default -> new TikTokUnhandledMemberEvent(message); default -> new TikTokUnhandledMemberEvent(message);
}; };
} }
private TikTokEvent handleRoomUserSeqMessage(WebcastResponse.Message msg)
{
var event = (TikTokRoomViewerDataEvent)mapMessageToEvent(WebcastRoomUserSeqMessage.class, TikTokRoomViewerDataEvent.class, msg);
roomInfo.setViewersCount(event.getViewerCount());
return event;
}
} }

View File

@@ -1,90 +0,0 @@
package io.github.jwdeveloper.tiktok.handlers;
import com.google.protobuf.ByteString;
import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokUnhandledEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageParsingException;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public abstract class WebResponseHandlerBase {
private final Map<String, TikTokMessageHandler> handlers;
private final TikTokEventHandler tikTokEventHandler;
public WebResponseHandlerBase(TikTokEventHandler tikTokEventHandler) {
handlers = new HashMap<>();
this.tikTokEventHandler = tikTokEventHandler;
init();
}
public abstract void init();
public void register(Class<?> input, Class<?> output) {
register(input, (e) ->
{
try {
var parseMethod = input.getDeclaredMethod("parseFrom", ByteString.class);
var deserialized = parseMethod.invoke(null, e.getBinary());
var constructors = Arrays.stream(output.getConstructors()).filter(ea -> Arrays.stream(ea.getParameterTypes()).toList().contains(input)).findFirst();
var tiktokEvent = constructors.get().newInstance(deserialized);
return (TikTokEvent)tiktokEvent;
} catch (Exception ex)
{
throw new TikTokLiveMessageParsingException("Unable to handle parsing from class: " + input.getSimpleName() + " to class " + output.getSimpleName(), ex);
}
});
}
public <T> void register(Class clazz, Function<WebcastResponse.Message, TikTokEvent> func) {
var haandler = new TikTokMessageHandler<T>() {
@Override
public Class<T> getHandleClazz() {
return clazz;
}
@Override
public TikTokEvent handle(WebcastResponse.Message message) throws Exception {
return func.apply(message);
}
};
handlers.put(haandler.getHandleClazz().getSimpleName(), haandler);
}
public void handle(WebcastResponse webcastResponse) {
for (var message : webcastResponse.getMessagesList()) {
try {
handleSingleMessage(message);
} catch (Exception e)
{
var decoded = Base64.getEncoder().encodeToString(message.getBinary().toByteArray());
var exception = new TikTokLiveException("Error whilst Handling Message"+message.getType()+": \n"+decoded, e);
tikTokEventHandler.publish(new TikTokErrorEvent(exception));
}
}
}
private void handleSingleMessage(WebcastResponse.Message message) throws Exception {
if (!handlers.containsKey(message.getType())) {
tikTokEventHandler.publish(new TikTokUnhandledEvent(message));
return;
}
var handler = handlers.get(message.getType());
var tiktokEvent = handler.handle(message);
tikTokEventHandler.publish(tiktokEvent);
}
}

View File

@@ -2,10 +2,10 @@ package io.github.jwdeveloper.tiktok.http;
import com.google.gson.Gson; import com.google.gson.Gson;
import io.github.jwdeveloper.tiktok.ClientSettings; import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta; import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import io.github.jwdeveloper.tiktok.models.gifts.TikTokGift; import io.github.jwdeveloper.tiktok.models.gifts.TikTokGiftInfo;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import java.util.HashMap; import java.util.HashMap;
@@ -50,7 +50,7 @@ public class TikTokApiService {
} }
if (id.isEmpty()) { if (id.isEmpty()) {
throw new TikTokLiveException("Unable to fetch room ID"); throw new TikTokLiveOfflineHostException("Unable to fetch room ID, live host could be offline or name is misspelled");
} }
clientSettings.getClientParameters().put("room_id", id); clientSettings.getClientParameters().put("room_id", id);
@@ -99,7 +99,7 @@ public class TikTokApiService {
} }
} }
public Map<Integer, TikTokGift> fetchAvailableGifts() { public Map<Integer, TikTokGiftInfo> fetchAvailableGifts() {
try { try {
var response = apiClient.GetJObjectFromWebcastAPI("gift/list/", clientSettings.getClientParameters()); var response = apiClient.GetJObjectFromWebcastAPI("gift/list/", clientSettings.getClientParameters());
if(!response.has("data")) if(!response.has("data"))
@@ -112,11 +112,11 @@ public class TikTokApiService {
return new HashMap<>(); return new HashMap<>();
} }
var giftsJsonList = dataJson.get("gifts").getAsJsonArray(); var giftsJsonList = dataJson.get("gifts").getAsJsonArray();
var gifts = new HashMap<Integer, TikTokGift>(); var gifts = new HashMap<Integer, TikTokGiftInfo>();
var gson = new Gson(); var gson = new Gson();
for(var jsonGift : giftsJsonList) for(var jsonGift : giftsJsonList)
{ {
var gift = gson.fromJson(jsonGift, TikTokGift.class); var gift = gson.fromJson(jsonGift, TikTokGiftInfo.class);
logger.info("Found Available Gift "+ gift.getName()+ " with ID "+gift.getId()); logger.info("Found Available Gift "+ gift.getName()+ " with ID "+gift.getId());
gifts.put(gift.getId(),gift); gifts.put(gift.getId(),gift);
} }

View File

@@ -90,7 +90,7 @@ public class TikTokHttpApiClient {
var fullUrl = HttpUtils.parseParameters(url,parameters); var fullUrl = HttpUtils.parseParameters(url,parameters);
var singHeaders = new TreeMap<String,Object>(); var singHeaders = new TreeMap<String,Object>();
singHeaders.put("client", "ttlive-net"); singHeaders.put("client", "ttlive-java");
singHeaders.put("uuc", 1); singHeaders.put("uuc", 1);
singHeaders.put("url", fullUrl); singHeaders.put("url", fullUrl);

View File

@@ -3,17 +3,18 @@ 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.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.TikTokEventHandler; import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.WebResponseHandler; import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
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.http.TikTokHttpRequestFactory; import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import org.java_websocket.client.WebSocketClient;
import java.net.URI; import java.net.URI;
import java.net.http.WebSocket; import java.net.http.WebSocket;
import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -22,45 +23,49 @@ public class TikTokWebSocketClient {
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 TikTokHttpRequestFactory factory; private final TikTokMessageHandlerRegistration webResponseHandler;
private final WebResponseHandler webResponseHandler;
private final TikTokEventHandler tikTokEventHandler; private final TikTokEventHandler tikTokEventHandler;
private WebSocketClient webSocketClient;
private WebSocket webSocket; private TikTokLiveClient tikTokLiveClient;
private TikTokWebSocketPingingTask pingingTask;
private boolean isConnected; private boolean isConnected;
public TikTokWebSocketClient(Logger logger, public TikTokWebSocketClient(Logger logger,
TikTokCookieJar tikTokCookieJar, TikTokCookieJar tikTokCookieJar,
TikTokHttpRequestFactory factory,
ClientSettings clientSettings, ClientSettings clientSettings,
WebResponseHandler webResponseHandler, TikTokMessageHandlerRegistration webResponseHandler,
TikTokEventHandler tikTokEventHandler) { TikTokEventHandler tikTokEventHandler) {
this.logger = logger; this.logger = logger;
this.tikTokCookieJar = tikTokCookieJar; this.tikTokCookieJar = tikTokCookieJar;
this.clientSettings = clientSettings; this.clientSettings = clientSettings;
this.factory = factory;
this.webResponseHandler = webResponseHandler; this.webResponseHandler = webResponseHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
isConnected = false; isConnected = false;
} }
public void start(WebcastResponse webcastResponse) public void start(WebcastResponse webcastResponse, TikTokLiveClient tikTokLiveClient) {
{ this.tikTokLiveClient = tikTokLiveClient;
if(isConnected) if (isConnected) {
{
stop(); stop();
} }
if (webcastResponse.getSocketUrl().isEmpty() || webcastResponse.getSocketParamsList().isEmpty()) { if (webcastResponse.getSocketUrl().isEmpty() || webcastResponse.getSocketParamsList().isEmpty()) {
throw new TikTokLiveException("Could not find Room"); throw new TikTokLiveException("Could not find Room");
} }
try { try {
var url =getWebSocketUrl(webcastResponse); var url = getWebSocketUrl(webcastResponse);
webSocket =startWebSocket(url);
if (clientSettings.isHandleExistingMessagesOnConnect()) { if (clientSettings.isHandleExistingMessagesOnConnect()) {
// HandleWebcastMessages(webcastResponse); logger.info("Handling existing messages");
webResponseHandler.handle(tikTokLiveClient, webcastResponse);
} }
} catch (Exception e) { webSocketClient = startWebSocket(url);
webSocketClient.connect();
pingingTask = new TikTokWebSocketPingingTask();
pingingTask.run(webSocketClient);
isConnected = true;
} catch (Exception e)
{
isConnected =false;
throw new TikTokLiveException("Failed to connect to the websocket", e); throw new TikTokLiveException("Failed to connect to the websocket", e);
} }
} }
@@ -80,26 +85,27 @@ public class TikTokWebSocketClient {
return HttpUtils.parseParametersEncode(url, clone); return HttpUtils.parseParametersEncode(url, clone);
} }
private WebSocket startWebSocket(String url) throws Exception { private WebSocketClient startWebSocket(String url) {
var cookie = tikTokCookieJar.parseCookies(); var cookie = tikTokCookieJar.parseCookies();
// System.out.println("WssIP: " + url);
// System.out.println("Cookie: " + cookie);
var map = new HashMap<String, String>(); var map = new HashMap<String, String>();
map.put("Cookie", cookie); map.put("Cookie", cookie);
return new TikTokWebSocketListener(URI.create(url),
return factory.openSocket() map,
.subprotocols("echo-protocol") 3000,
.connectTimeout(Duration.ofSeconds(15)) webResponseHandler,
.header("Cookie", cookie) tikTokEventHandler,
.buildAsync(URI.create(url), new TikTokWebSocketListener(webResponseHandler, tikTokEventHandler)).get(); tikTokLiveClient);
} }
public void stop() {
if(isConnected && webSocket != null) public void stop()
{ {
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok"); if (isConnected && webSocketClient != null) {
webSocketClient.close(1);
} }
webSocketClient = null;
pingingTask = null;
isConnected = false;
} }
} }

View File

@@ -1,106 +1,121 @@
package io.github.jwdeveloper.tiktok.websocket; package io.github.jwdeveloper.tiktok.websocket;
import com.google.protobuf.ByteString;
import io.github.jwdeveloper.tiktok.TikTokLiveClient;
import io.github.jwdeveloper.tiktok.events.messages.TikTokConnectedEvent; import io.github.jwdeveloper.tiktok.events.messages.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokDisconnectedEvent; import io.github.jwdeveloper.tiktok.events.messages.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent; import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageParsingException; import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler; import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.WebResponseHandler; import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck; import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage; import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.io.ByteArrayOutputStream; import java.net.URI;
import java.net.http.WebSocket; import java.net.http.WebSocket;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.CompletionStage; import java.util.Map;
import java.util.Optional;
public class TikTokWebSocketListener extends WebSocketClient {
public class TikTokWebSocketListener implements java.net.http.WebSocket.Listener { private final TikTokMessageHandlerRegistration webResponseHandler;
private final ByteArrayOutputStream accumulatedData = new ByteArrayOutputStream();
private final WebResponseHandler webResponseHandler;
private final TikTokEventHandler tikTokEventHandler; private final TikTokEventHandler tikTokEventHandler;
private final TikTokLiveClient tikTokLiveClient;
public TikTokWebSocketListener(WebResponseHandler webResponseHandler, TikTokEventHandler tikTokEventHandler) { public TikTokWebSocketListener(URI serverUri,
Map<String, String> httpHeaders,
int connectTimeout,
TikTokMessageHandlerRegistration webResponseHandler,
TikTokEventHandler tikTokEventHandler,
TikTokLiveClient tikTokLiveClient) {
super(serverUri, new Draft_6455(), httpHeaders,connectTimeout);
this.webResponseHandler = webResponseHandler; this.webResponseHandler = webResponseHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
this.tikTokLiveClient = tikTokLiveClient;
} }
@Override @Override
public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer data, boolean last) { public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
sendPing();
}
@Override
public void onMessage(ByteBuffer bytes)
{
try { try {
var bytes = new byte[data.remaining()]; handleBinary(bytes.array());
data.get(bytes);
accumulatedData.write(bytes);
if (last) {
handleBinary(webSocket, accumulatedData.toByteArray());
accumulatedData.reset();
}
} catch (Exception e) { } catch (Exception e) {
tikTokEventHandler.publish(new TikTokErrorEvent(e)); tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e));
} }
webSocket.request(1); sendPing();
return null;
} }
@Override @Override
public void onOpen(java.net.http.WebSocket webSocket) { public void onClose(int i, String s, boolean b) {
tikTokEventHandler.publish(new TikTokConnectedEvent()); tikTokEventHandler.publish(tikTokLiveClient,new TikTokDisconnectedEvent());
webSocket.request(1);
} }
@Override @Override
public void onError(java.net.http.WebSocket webSocket, Throwable error) { public void onError(Exception error) {
tikTokEventHandler.publish(new TikTokErrorEvent(error)); tikTokEventHandler.publish(tikTokLiveClient,new TikTokErrorEvent(error));
webSocket.request(1); sendPing();
} }
@Override private void handleBinary(byte[] buffer) {
public CompletionStage<?> onClose(java.net.http.WebSocket webSocket, int statusCode, String reason) { var websocketMessageOptional = getWebcastWebsocketMessage(buffer);
tikTokEventHandler.publish(new TikTokDisconnectedEvent()); if (websocketMessageOptional.isEmpty()) {
return java.net.http.WebSocket.Listener.super.onClose(webSocket, statusCode, reason); return;
}
var websocketMessage = websocketMessageOptional.get();
sendAckId(websocketMessage.getId());
var webResponse = getWebResponseMessage(websocketMessage.getBinary());
webResponseHandler.handle(tikTokLiveClient, webResponse);
} }
private void handleBinary(WebSocket webSocket, byte[] buffer) { private Optional<WebcastWebsocketMessage> getWebcastWebsocketMessage(byte[] buffer) {
try { try {
var websocketMessage = WebcastWebsocketMessage.parseFrom(buffer); var websocketMessage = WebcastWebsocketMessage.parseFrom(buffer);
if (websocketMessage.getBinary().isEmpty()) { if (websocketMessage.getBinary().isEmpty()) {
return; return Optional.empty();
}
sendAckId(webSocket, websocketMessage.getId());
try {
var response = WebcastResponse.parseFrom(websocketMessage.getBinary());
webResponseHandler.handle(response);
} catch (Exception e) {
throw new TikTokLiveMessageParsingException("Unable to read WebcastResponse", e);
} }
return Optional.of(websocketMessage);
} catch (Exception e) { } catch (Exception e) {
throw new TikTokLiveMessageParsingException("Unable to read WebcastWebsocketMessage", e); throw new TikTokProtocolBufferException("Unable to parse WebcastWebsocketMessage", buffer, e);
} }
} }
private void pingTask(WebSocket webSocket) throws InterruptedException { private WebcastResponse getWebResponseMessage(ByteString buffer) {
while (true) { try {
byte[] message = new byte[]{58, 2, 104, 98}; return WebcastResponse.parseFrom(buffer);
ByteBuffer buffer = ByteBuffer.wrap(message); } catch (Exception e) {
while (buffer.hasRemaining()) { throw new TikTokProtocolBufferException("Unable to parse WebcastResponse", buffer.toByteArray(), e);
webSocket.sendPing(buffer);
}
buffer.clear();
Thread.sleep(10);
} }
} }
private void sendAckId(WebSocket webSocket, long id) {
private void sendAckId(long id) {
var serverInfo = WebcastWebsocketAck var serverInfo = WebcastWebsocketAck
.newBuilder() .newBuilder()
.setType("ack") .setType("ack")
.setId(id) .setId(id)
.build(); .build();
webSocket.sendBinary(serverInfo.toByteString().asReadOnlyByteBuffer(), true); send(serverInfo.toByteString().asReadOnlyByteBuffer());
} }
}
@Override
public void onMessage(String s) {
}
}

View File

@@ -0,0 +1,58 @@
package io.github.jwdeveloper.tiktok.websocket;
import org.java_websocket.WebSocket;
import java.util.Random;
public class TikTokWebSocketPingingTask
{
private Thread thread;
private boolean isRunning = false;
private final int MIN_TIMEOUT = 5;
private final int MAX_TIMEOUT = 100;
public void run(WebSocket webSocket)
{
var thread = new Thread(() ->
{
pingTask(webSocket);
});
isRunning =true;
thread.start();
}
public void stop()
{
if(thread != null)
{
thread.interrupt();
}
isRunning = false;
}
private void pingTask(WebSocket webSocket)
{
var random = new Random();
while (isRunning)
{
try
{
if(!webSocket.isOpen())
{
Thread.sleep(100);
continue;
}
webSocket.sendPing();
var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT;
Thread.sleep(timeout);
}
catch (Exception e)
{
isRunning = false;
}
}
}
}

View File

@@ -1,9 +1,8 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import io.github.jwdeveloper.tiktok.messages.WebcastChatMessage; import io.github.jwdeveloper.tiktok.common.TikTokBaseTest;
import io.github.jwdeveloper.tiktok.messages.WebcastGiftMessage; import io.github.jwdeveloper.tiktok.messages.*;
import io.github.jwdeveloper.tiktok.messages.WebcastLikeMessage;
import org.junit.Test; import org.junit.Test;
public class ParseMessagesTests extends TikTokBaseTest public class ParseMessagesTests extends TikTokBaseTest
@@ -11,13 +10,13 @@ public class ParseMessagesTests extends TikTokBaseTest
@Test @Test
public void ShouldParseLikeMessage() throws InvalidProtocolBufferException { public void ShouldParseMessageWebcastLikeMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("LikeMessage.bin"); var bytes = getFileBytesUtf("LikeMessage.bin");
var message = WebcastLikeMessage.parseFrom(bytes); var message = WebcastLikeMessage.parseFrom(bytes);
} }
@Test @Test
public void ShouldParseGiftMessage() throws InvalidProtocolBufferException { public void ShouldParseMessageWebcastGiftMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("MessageWebcastGiftMessage.bin"); var bytes = getFileBytesUtf("MessageWebcastGiftMessage.bin");
var message = WebcastGiftMessage.parseFrom(bytes); var message = WebcastGiftMessage.parseFrom(bytes);
} }
@@ -27,5 +26,24 @@ public class ParseMessagesTests extends TikTokBaseTest
var message = WebcastChatMessage.parseFrom(bytes); var message = WebcastChatMessage.parseFrom(bytes);
} }
@Test
public void ShouldParseMessageWebcastImDeleteMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("MessageWebcastImDeleteMessage.bin");
var message = WebcastImDeleteMessage.parseFrom(bytes);
}
@Test
public void ShouldParseMessageWebcastSocialMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("MessageWebcastSocialMessage.bin");
var message = WebcastSocialMessage.parseFrom(bytes);
}
@Test
public void ShouldParseMessageWebcastMemberMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("WebcastMemberMessage.bin");
var message = WebcastMemberMessage.parseFrom(bytes);
}
} }

View File

@@ -0,0 +1,26 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.common.TikTokBaseTest;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import org.junit.Before;
import java.util.logging.Logger;
import static org.mockito.Mockito.mock;
public class WebResponseHandlerTests extends TikTokBaseTest {
public static TikTokMessageHandlerRegistration sut;
@Before
public void before() {
var mockEventHandler = mock(TikTokEventHandler.class);
var mockGiftManager = mock(TikTokGiftManager.class);
var mockRoomInfo = mock(TikTokRoomInfo.class);
var mockClientSettings = mock(ClientSettings.class);
var mockLogger = mock(Logger.class);
sut = new TikTokMessageHandlerRegistration(mockEventHandler,mockClientSettings,mockLogger, mockGiftManager, mockRoomInfo);
}
}

View File

@@ -1,4 +1,4 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok.common;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage; import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage;

View File

@@ -0,0 +1 @@
CjUKFldlYmNhc3RJbURlbGV0ZU1lc3NhZ2UQhZab+vyKvvJkGJKWhYz+o7DxZCC0sajzoTEwARoJhojH2ojIjaBk

View File

@@ -0,0 +1 @@
CskHMAFChgcaDgoJI2ZmZmZmZmZmIJADIp8GCAsSDgoJI2ZmZmZmZmZmIJADqgGJBgqGBgiFiNKIpZnf0WIaCm1yIEEuTC5GLkFK/wQKxgFodHRwczovL3AxNi1zaWduLXVzZWFzdDJhLnRpa3Rva2Nkbi5jb20vdG9zLXVzZWFzdDJhLWF2dC0wMDY4LWV1dHRwLzkxODE3OWQ4MzQ0YWZiYjk3OTAzYjYyMGQwOTg1ZGEzfnRwbHYtdGlrdG9rLXNocmluazo3Mjo3Mi53ZWJwP3gtZXhwaXJlcz0xNjkyOTAwMDAwJngtc2lnbmF0dXJlPUE0RmtOWDMlMkIxNiUyRjlxeENvR1JReEczNFRnRUUlM0QKtAFodHRwczovL3AxNi1zaWduLXVzZWFzdDJhLnRpa3Rva2Nkbi5jb20vdG9zLXVzZWFzdDJhLWF2dC0wMDY4LWV1dHRwLzkxODE3OWQ4MzQ0YWZiYjk3OTAzYjYyMGQwOTg1ZGEzfmM1XzEwMHgxMDAud2VicD94LWV4cGlyZXM9MTY5MjkwMDAwMCZ4LXNpZ25hdHVyZT1EOHlYQUhNVGxGeUR5N3dqT3BXRllrUlBBWnclM0QKtgFodHRwczovL3AxNi1zaWduLXVzZWFzdDJhLnRpa3Rva2Nkbi5jb20vdG9zLXVzZWFzdDJhLWF2dC0wMDY4LWV1dHRwLzkxODE3OWQ4MzQ0YWZiYjk3OTAzYjYyMGQwOTg1ZGEzfmM1XzEwMHgxMDAuanBlZz94LWV4cGlyZXM9MTY5MjkwMDAwMCZ4LXNpZ25hdHVyZT1IYkIwNk00N2Y0JTJGT29jUWxla0gwREdDdkY3MCUzRBJEMTAweDEwMC90b3MtdXNlYXN0MmEtYXZ0LTAwNjgtZXV0dHAvOTE4MTc5ZDgzNDRhZmJiOTc5MDNiNjIwZDA5ODVkYTOyAQgI9h8Qmw0YAboBAIICALICCy56YXBpc3l3YW5l8gJMTVM0d0xqQUJBQUFBYXdVMzNGbnBIYllkMHdra1djeC1DTnBzeDdXT0lqOXdtOTBwQ2tRUmZPekJuZVdZczdXT0plSm5GVU5kdFh0MwoecG1fbXRfZ3VpZGFuY2Vfdmlld2VyXzEwX3NoYXJlEjJ7MDp1c2VyfSBzaGFyZWQgdGhlIExJVkUgd2l0aCBtb3JlIHRoYW4gMTAgZnJpZW5kc1ACsAEFwAECChRXZWJjYXN0U29jaWFsTWVzc2FnZRCglsC+2OG+8mQYoJaZxvrevPJkIOTf0vOhMUgBuAECEoYGSv8ECsYBaHR0cHM6Ly9wMTYtc2lnbi11c2Vhc3QyYS50aWt0b2tjZG4uY29tL3Rvcy11c2Vhc3QyYS1hdnQtMDA2OC1ldXR0cC85MTgxNzlkODM0NGFmYmI5NzkwM2I2MjBkMDk4NWRhM350cGx2LXRpa3Rvay1zaHJpbms6NzI6NzIud2VicD94LWV4cGlyZXM9MTY5MjkwMDAwMCZ4LXNpZ25hdHVyZT1BNEZrTlgzJTJCMTYlMkY5cXhDb0dSUXhHMzRUZ0VFJTNECrQBaHR0cHM6Ly9wMTYtc2lnbi11c2Vhc3QyYS50aWt0b2tjZG4uY29tL3Rvcy11c2Vhc3QyYS1hdnQtMDA2OC1ldXR0cC85MTgxNzlkODM0NGFmYmI5NzkwM2I2MjBkMDk4NWRhM35jNV8xMDB4MTAwLndlYnA/eC1leHBpcmVzPTE2OTI5MDAwMDAmeC1zaWduYXR1cmU9RDh5WEFITVRsRnlEeTd3ak9wV0ZZa1JQQVp3JTNECrYBaHR0cHM6Ly9wMTYtc2lnbi11c2Vhc3QyYS50aWt0b2tjZG4uY29tL3Rvcy11c2Vhc3QyYS1hdnQtMDA2OC1ldXR0cC85MTgxNzlkODM0NGFmYmI5NzkwM2I2MjBkMDk4NWRhM35jNV8xMDB4MTAwLmpwZWc/eC1leHBpcmVzPTE2OTI5MDAwMDAmeC1zaWduYXR1cmU9SGJCMDZNNDdmNCUyRk9vY1FsZWtIMERHQ3ZGNzAlM0QSRDEwMHgxMDAvdG9zLXVzZWFzdDJhLWF2dC0wMDY4LWV1dHRwLzkxODE3OWQ4MzQ0YWZiYjk3OTAzYjYyMGQwOTg1ZGEzsgEIEJsNGAEI9h+6AQCCAgCyAgsuemFwaXN5d2FuZfICTE1TNHdMakFCQUFBQWF3VTMzRm5wSGJZZDB3a2tXY3gtQ05wc3g3V09Jajl3bTkwcENrUVJmT3pCbmVXWXM3V09KZUpuRlVOZHRYdDMIhYjSiKWZ39FiGgptciBBLkwuRi5BGAEgAyoCLTFAGQ==

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,13 @@
[![](https://jitpack.io/v/jwdeveloper/TikTok-Live-Java.svg)](https://jitpack.io/#jwdeveloper/TikTok-Live-Java) [![](https://jitpack.io/v/jwdeveloper/TikTok-Live-Java.svg)](https://jitpack.io/#jwdeveloper/TikTok-Live-Java)
# TikTok-Live-Java # TikTokLive Java
A Java library based on [TikTok-Connector](https://github.com/isaackogan/TikTok-Live-Connector) and [TikTokLiveSharp](https://github.com/sebheron/TikTokLiveSharp). Use it to receive live stream events such as comments and gifts in realtime from [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal WebCast push service. The package includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers. No credentials are required. Besides [Chat Comments](#chat), other events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked. You can also send [automatic messages](#send-chat-messages) into the chat by providing your Session ID. A Java library based on [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/sebheron/TikTokLiveSharp). Use it to receive live stream events such as comments and gifts in realtime from [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal WebCast push service. The package includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers. No credentials are required. Besides [Chat Comments](#chat), other events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked. You can also send [automatic messages](#send-chat-messages) into the chat by providing your Session ID.
Join the support [discord](https://discord.gg/e2XwPNTBBr) and visit the `#java-support` channel for questions, contributions and ideas. Feel free to make pull requests with missing/new features, fixes, etc
Do you prefer other programming languages? Do you prefer other programming languages?
- **Node** orginal: [TikTok-Live-Connector](https://github.com/isaackogan/TikTok-Live-Connector) by [@isaackogan](https://github.com/isaackogan) - **Node** orginal: [TikTok-Live-Connector](https://github.com/isaackogan/TikTok-Live-Connector) by [@zerodytrash](https://github.com/zerodytrash)
- **Python** rewrite: [TikTokLive](https://github.com/isaackogan/TikTokLive) by [@isaackogan](https://github.com/isaackogan) - **Python** rewrite: [TikTokLive](https://github.com/isaackogan/TikTokLive) by [@isaackogan](https://github.com/isaackogan)
- **Go** rewrite: [GoTikTokLive](https://github.com/Davincible/gotiktoklive) by [@Davincible](https://github.com/Davincible) - **Go** rewrite: [GoTikTokLive](https://github.com/Davincible/gotiktoklive) by [@Davincible](https://github.com/Davincible)
- **C#** rewrite: [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp) by [@frankvHoof93](https://github.com/frankvHoof93) - **C#** rewrite: [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp) by [@frankvHoof93](https://github.com/frankvHoof93)
@@ -15,10 +16,9 @@ Do you prefer other programming languages?
#### Overview #### Overview
- [Getting started](#getting-started) - [Getting started](#getting-started)
- [Params and options](#params-and-options) - [Configuration](#configuration)
- [Methods](#methods) - [Methods](#methods)
- [Events](#events) - [Events](#events)
- [Examples](#examples)
- [Contributing](#contributing) - [Contributing](#contributing)
## Getting started ## Getting started
@@ -32,27 +32,19 @@ Do you prefer other programming languages?
<url>https://jitpack.io</url> <url>https://jitpack.io</url>
</repository> </repository>
</repositories> </repositories>
```
```xml
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>Client</artifactId>
<version>3.24.1</version> <version>0.0.14-Release</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.10.1</version> <version>2.10.1</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.jwdeveloper</groupId>
<artifactId>TikTok-Live-Java</artifactId>
<version>0.0.9-Release</version>
</dependency>
</dependencies> </dependencies>
``` ```
@@ -62,36 +54,69 @@ Do you prefer other programming languages?
public static void main(String[] args) public static void main(String[] args)
{ {
// Username of someone who is currently live // Username of someone who is currently live
var tiktokUsername = "officialgeilegisela"; var tiktokUsername = "jwdevtiktok";
TikTokLive.newClient(tiktokUsername) TikTokLive.newClient(tiktokUsername)
.onConnected(event -> .onConnected((client, event) ->
{ {
System.out.println("Connected"); System.out.println("Connected");
}) })
.onJoin(event -> .onJoin((client, event) ->
{ {
System.out.println("User joined -> " + event.getUser().getNickName()); System.out.println("User joined -> " + event.getUser().getNickName());
}) })
.onComment(event -> .onComment((client, event) ->
{ {
System.out.println(event.getUser().getUniqueId() + ": " + event.getText()); System.out.println(event.getUser().getUniqueId() + ": " + event.getText());
}) })
.onError(event -> .onEvent((client, event) ->
{
System.out.println("Viewers count: "+client.getRoomInfo().getViewersCount());
})
.onError((client, event) ->
{ {
event.getException().printStackTrace(); event.getException().printStackTrace();
}) })
.buildAndRun(); .buildAndRun();
System.in.read();
} }
``` ```
## Configuration
```java
public class ConfigurationExample
{
public static void main(String[] args) throws IOException {
var tiktokUsername = "jwdevtiktok";
TikTokLive.newClient(tiktokUsername)
.configure(clientSettings ->
{
clientSettings.setHostName(Main.TEST_TIKTOK_USER); // TikTok user name
clientSettings.setClientLanguage("en"); // Language
clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
clientSettings.setLogLevel(Level.ALL); // Log level
clientSettings.setDownloadGiftInfo(true); // Downloading meta information about gifts. You can access it by client.getGiftManager().getGiftsInfo();
clientSettings.setPrintMessageData(true); // Printing TikTok Protocol buffer messages in Base64 format
clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
clientSettings.setHandleExistingMessagesOnConnect(true); // Invokes all TikTok events that had occurred before connection
clientSettings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
})
.buildAndRun();
System.in.read();
}
}
```
## Methods ## Methods
A `TikTokLive` object contains the following methods. A `client (LiveClient)` object contains the following methods.
| Method Name | Description | | Method Name | Description |
| ----------- | ----------- | | ----------- | ----------- |
| connect | Connects to the live stream chat.<br>Returns a `Promise` which will be resolved when the connection is successfully established. | | connect | Connects to the live stream. |
| disconnect | Disconnects the connection. | | disconnect | Disconnects the connection. |
| getGiftManager | Gets the meta informations about all gifts. |
| getRoomInfo | Gets the current room info from TikTok API including streamer info, room status and statistics. | | getRoomInfo | Gets the current room info from TikTok API including streamer info, room status and statistics. |
## Events ## Events

View File

@@ -2,11 +2,48 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- READ ME
This POM file is configured to run only inside TikTokLiveJava project
In case you have copied it follows this stets to fix console errors
1. Remember to remove
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.13-Release</version>
</parent>
2. Paste
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId>
<version>0.0.14-Release</version> <- Change with latest version of TikTokJava
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
I hope it will help you :)
-->
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.8-Release</version> <version>0.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>TestApplication</artifactId> <artifactId>TestApplication</artifactId>

View File

@@ -0,0 +1,27 @@
package io.github.jwdeveloper.tiktok;
import java.io.IOException;
import java.time.Duration;
import java.util.logging.Level;
public class ConfigurationExample {
public static void main(String[] args) throws IOException {
TikTokLive.newClient(Main.TEST_TIKTOK_USER)
.configure(clientSettings ->
{
clientSettings.setHostName(Main.TEST_TIKTOK_USER); // TikTok user name
clientSettings.setClientLanguage("en"); // Language
clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
clientSettings.setLogLevel(Level.ALL); // Log level
clientSettings.setDownloadGiftInfo(true); // Downloading meta information about gifts. You can access it by client.getGiftManager().getGiftsInfo();
clientSettings.setPrintMessageData(true); // Printing TikTok Protocol buffer messages in Base64 format
clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
clientSettings.setHandleExistingMessagesOnConnect(true); // Invokes all TikTok events that had occurred before connection
clientSettings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
})
.buildAndRun();
System.in.read();
}
}

View File

@@ -1,15 +1,22 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.events.messages.*; import io.github.jwdeveloper.tiktok.events.messages.*;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import java.io.IOException; import java.io.IOException;
import java.time.Duration;
public class Main { public class Main {
public static String TEST_USER_SUBJECT = "stiflerhub"; public static String TEST_TIKTOK_USER = "olchik.m1";
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
var client = TikTokLive.newClient(TEST_USER_SUBJECT) var client = TikTokLive.newClient(TEST_TIKTOK_USER)
.configure(clientSettings ->
{
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(5));
clientSettings.setRetryOnConnectionFailure(true);
})
.onConnected(Main::onConnected) .onConnected(Main::onConnected)
.onDisconnected(Main::onDisconnected) .onDisconnected(Main::onDisconnected)
.onRoomViewerData(Main::onViewerData) .onRoomViewerData(Main::onViewerData)
@@ -21,59 +28,63 @@ public class Main {
.onLike(Main::onLike) .onLike(Main::onLike)
.onGiftMessage(Main::onGiftMessage) .onGiftMessage(Main::onGiftMessage)
.onEmote(Main::onEmote) .onEmote(Main::onEmote)
.onError(tikTokErrorEvent -> .onError((_client, error) ->
{ {
// tikTokErrorEvent.getException().printStackTrace(); error.getException().printStackTrace();
})
.onEvent((liveClient, event) ->
{
var viewers = liveClient.getRoomInfo().getViewersCount();
}) })
.buildAndRun(); .buildAndRun();
var viewers = client.getRoomInfo().getViewersCount();
System.in.read(); System.in.read();
} }
private static void onConnected(TikTokConnectedEvent e) { private static void onConnected(LiveClient tikTokLive, TikTokConnectedEvent e) {
print("Connected"); print("Connected");
} }
private static void onDisconnected(TikTokDisconnectedEvent e) { private static void onDisconnected(LiveClient tikTokLive, TikTokDisconnectedEvent e) {
print("Disconnected"); print("Disconnected");
} }
private static void onViewerData(TikTokRoomViewerDataEvent e) { private static void onViewerData(LiveClient tikTokLive, TikTokRoomViewerDataEvent e) {
print("Viewer count is:", e.getViewerCount()); print("Viewer count is:", e.getViewerCount());
} }
private static void onJoin(TikTokJoinEvent e) { private static void onJoin(LiveClient tikTokLive, TikTokJoinEvent e) {
print(e.getUser().getUniqueId(), "joined!"); print(e.getUser().getUniqueId(), "joined!");
} }
private static void onComment(TikTokCommentEvent e) { private static void onComment(LiveClient tikTokLive, TikTokCommentEvent e) {
print(e.getUser().getUniqueId(), e.getText()); print(e.getUser().getUniqueId(), e.getText());
} }
private static void onFollow(TikTokFollowEvent e) { private static void onFollow(LiveClient tikTokLive, TikTokFollowEvent e) {
print(e.getNewFollower().getUniqueId(), "followed!"); print(e.getNewFollower().getUniqueId(), "followed!");
} }
private static void onShare(TikTokShareEvent e) { private static void onShare(LiveClient tikTokLive, TikTokShareEvent e) {
print(e.getUser().getUniqueId(), "shared!"); print(e.getUser().getUniqueId(), "shared!");
} }
private static void onSubscribe(TikTokSubscribeEvent e) { private static void onSubscribe(LiveClient tikTokLive, TikTokSubscribeEvent e) {
print(e.getNewSubscriber().getUniqueId(), "subscribed!"); print(e.getNewSubscriber().getUniqueId(), "subscribed!");
} }
private static void onLike(TikTokLikeEvent e) { private static void onLike(LiveClient tikTokLive, TikTokLikeEvent e) {
print(e.getSender().getUniqueId(), "liked!"); print(e.getSender().getUniqueId(), "liked!");
} }
private static void onGiftMessage(TikTokGiftMessageEvent e) { private static void onGiftMessage(LiveClient tikTokLive, TikTokGiftMessageEvent e)
{
print(e.getSender().getUniqueId(), "sent", e.getAmount(), "x", e.getGift().getName()); print(e.getSender().getUniqueId(), "sent", e.getAmount(), "x", e.getGift().getName());
} }
private static void onEmote(TikTokEmoteEvent e) { private static void onEmote(LiveClient tikTokLive, TikTokEmoteEvent e) {
print(e.getUser().getUniqueId(), "sent", e.getEmoteId()); print(e.getUser().getUniqueId(), "sent", e.getEmoteId());
} }

View File

@@ -4,26 +4,30 @@ import java.io.IOException;
public class SimpleExample { public class SimpleExample {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
// Username of someone who is currently live
var tiktokUsername = "szwagierkaqueen";
TikTokLive.newClient(tiktokUsername) TikTokLive.newClient(Main.TEST_TIKTOK_USER)
.configure(settings -> .onFollow((liveClient, event) ->
{ {
System.out.println("Follow joined -> " + event.getNewFollower().getNickName());
}) })
.onConnected(event -> .onConnected((client, event) ->
{ {
System.out.println("Connected"); System.out.println("Connected");
}) })
.onJoin(event ->
.onJoin((client, event) ->
{ {
System.out.println("User joined -> " + event.getUser().getNickName()); System.out.println("User joined -> " + event.getUser().getNickName());
}) })
.onComment(event -> .onComment((client, event) ->
{ {
System.out.println(event.getUser().getUniqueId() + ": " + event.getText()); System.out.println(event.getUser().getUniqueId() + ": " + event.getText());
}) })
.onError(event -> .onEvent((client, event) ->
{
System.out.println("Viewers count: "+client.getRoomInfo().getViewersCount());
})
.onError((client, event) ->
{ {
event.getException().printStackTrace(); event.getException().printStackTrace();
}) })

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.14-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Tools-EventsCollector</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.34.0</version> <!-- Use the latest version available -->
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-core</artifactId>
<version>3.23.0</version>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-sqlobject</artifactId>
<version>3.23.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.32</version> <!-- Use the latest version available -->
</dependency>
<dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>Client</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,99 @@
package io.github.jwdeveloper.tiktok.tools.collector;
import io.github.jwdeveloper.tiktok.TikTokLive;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
import io.github.jwdeveloper.tiktok.tools.collector.db.TikTokDatabase;
import io.github.jwdeveloper.tiktok.tools.collector.tables.ExceptionInfoModel;
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokErrorModel;
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokMessageModel;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
public class Main {
//https://protobuf-decoder.netlify.app/
/*
mia_tattoo
moniczkka
besin1276
*/
public static List<String> ignoredEvents;
public static void main(String[] args) throws SQLException {
ignoredEvents = List.of("TikTokJoinEvent","TikTokLikeEvent");
var db = new TikTokDatabase("test");
db.init();
var users = new ArrayList<String>();
users.add("mia_tattoo");
users.add("moniczkka");
users.add("besin1276");
for(var user : users)
{
runTikTokLiveInstance(user, db);
}
}
private static void runTikTokLiveInstance(String tiktokUser, TikTokDatabase tikTokDatabase)
{
TikTokLive.newClient(tiktokUser)
.onWebsocketMessage((liveClient, event) ->
{
var eventName = event.getEvent().getClass().getSimpleName();
if(ignoredEvents.contains(eventName))
{
return;
}
var binary = Base64.getEncoder().encodeToString(event.getMessage().getBinary().toByteArray());
var model = TikTokMessageModel.builder()
.type("message")
.hostName(tiktokUser)
.eventName(eventName)
.eventContent(binary)
.build();
tikTokDatabase.insertMessage(model);
System.out.println("EVENT: ["+tiktokUser+"] " + eventName);
})
.onError((liveClient, event) ->
{
var exception = event.getException();
var exceptionContent = ExceptionInfoModel.getStackTraceAsString(exception);
var builder = TikTokErrorModel.builder();
if (exception instanceof TikTokLiveMessageException ex) {
builder.hostName(tiktokUser)
.errorName(ex.messageName())
.errorType("error-message")
.exceptionContent(exceptionContent)
.message(ex.messageToBase64())
.response(ex.webcastResponseToBase64());
} else {
builder.hostName(tiktokUser)
.errorName(exception.getClass().getSimpleName())
.errorType("error-system")
.exceptionContent(exceptionContent)
.message("")
.response("");
}
var error = builder.build();
tikTokDatabase.insertError(error);
System.out.println("ERROR: "+error.getErrorName());
exception.printStackTrace();
})
.buildAndRun();
}
}

View File

@@ -0,0 +1,29 @@
package io.github.jwdeveloper.tiktok.tools.collector.db;
public class SqlConsts
{
public static String CREATE_MESSAGES_TABLE = """
CREATE TABLE IF NOT EXISTS TikTokMessageModel (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hostName TEXT,
type TEXT,
eventName TEXT,
eventContent TEXT,
createdAt TEXT
);
""";
public static String CREATE_ERROR_TABLE = """
CREATE TABLE IF NOT EXISTS TikTokErrorModel (
id INT AUTO_INCREMENT PRIMARY KEY,
hostName VARCHAR(255),
errorName VARCHAR(255),
errorType VARCHAR(255),
exceptionContent TEXT,
message TEXT,
response TEXT,
createdAt DATETIME
);
""";
}

View File

@@ -0,0 +1,50 @@
package io.github.jwdeveloper.tiktok.tools.collector.db;
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokErrorModel;
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokMessageModel;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.sqlobject.SqlObjectPlugin;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TikTokDatabase
{
private final String database;
private TikTokMessageModelDAO messagesTable;
private TikTokErrorModelDAO errorTable;
public TikTokDatabase(String database) {
this.database =database;
}
public void init() throws SQLException {
var jdbcUrl ="jdbc:sqlite:"+database+".db";
var connection = DriverManager.getConnection(jdbcUrl);
Jdbi jdbi = Jdbi.create(jdbcUrl)
.installPlugin(new SqlObjectPlugin());
jdbi.useHandle(handle -> {
handle.execute(SqlConsts.CREATE_MESSAGES_TABLE);
handle.execute(SqlConsts.CREATE_ERROR_TABLE);
});
messagesTable = jdbi.onDemand(TikTokMessageModelDAO.class);
errorTable = jdbi.onDemand(TikTokErrorModelDAO.class);
}
public void insertMessage(TikTokMessageModel message)
{
var dateFormat = new SimpleDateFormat("dd:MM:yyyy HH:mm:ss.SSS");
message.setCreatedAt(dateFormat.format(new Date()));
messagesTable.insertTikTokMessage(message);
}
public void insertError(TikTokErrorModel message)
{
var dateFormat = new SimpleDateFormat("dd:MM:yyyy HH:mm:ss.SSS");
message.setCreatedAt(dateFormat.format(new Date()));
errorTable.insertTikTokMessage(message);
}
}

View File

@@ -0,0 +1,13 @@
package io.github.jwdeveloper.tiktok.tools.collector.db;
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokErrorModel;
import org.jdbi.v3.sqlobject.customizer.BindBean;
import org.jdbi.v3.sqlobject.statement.SqlUpdate;
public interface TikTokErrorModelDAO
{
@SqlUpdate("INSERT INTO TikTokErrorModel (hostName, errorName, errorType, exceptionContent, message, response, createdAt) " +
"VALUES (:hostName, :errorName, :errorType, :exceptionContent, :message, :response, :createdAt)")
void insertTikTokMessage(@BindBean TikTokErrorModel message);
}

View File

@@ -0,0 +1,12 @@
package io.github.jwdeveloper.tiktok.tools.collector.db;
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokMessageModel;
import org.jdbi.v3.sqlobject.customizer.BindBean;
import org.jdbi.v3.sqlobject.statement.SqlUpdate;
public interface TikTokMessageModelDAO
{
@SqlUpdate("INSERT INTO TikTokMessageModel (hostName, eventName,type, eventContent, createdAt) " +
"VALUES (:hostName, :eventName, :type, :eventContent, :createdAt)")
void insertTikTokMessage(@BindBean TikTokMessageModel message);
}

View File

@@ -0,0 +1,31 @@
package io.github.jwdeveloper.tiktok.tools.collector.tables;
import java.io.PrintWriter;
import java.io.StringWriter;
public class ExceptionInfoModel
{
private String message;
private String stackTrace;
public ExceptionInfoModel(Throwable throwable) {
this.message = throwable.getMessage();
this.stackTrace = getStackTraceAsString(throwable);
}
public static String getStackTraceAsString(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
throwable.printStackTrace(pw);
return sw.toString();
}
// Getters for message and stackTrace
public String getMessage() {
return message;
}
public String getStackTrace() {
return stackTrace;
}
}

View File

@@ -0,0 +1,25 @@
package io.github.jwdeveloper.tiktok.tools.collector.tables;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TikTokErrorModel
{
private Integer id;
private String hostName;
private String errorName;
private String errorType;
private String exceptionContent;
private String message;
private String response;
private String createdAt;
}

View File

@@ -0,0 +1,24 @@
package io.github.jwdeveloper.tiktok.tools.collector.tables;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
@Data
@Builder
public class TikTokMessageModel
{
private Integer id;
private String hostName;
private String eventName;
private String type;
private String eventContent;
private String createdAt;
}

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.8-Release</version> <version>0.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -33,6 +33,11 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version> <!-- Check for the latest version -->
</dependency>
<dependency> <dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>Client</artifactId> <artifactId>Client</artifactId>

View File

@@ -1,12 +1,9 @@
package io.github.jwdeveloper.tiktok.events_generator; package io.github.jwdeveloper.tiktok.events_generator;
import io.github.jwdeveloper.tiktok.FilesUtility; import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
public class EventsGenerator public class EventsGenerator
{ {

View File

@@ -20,16 +20,16 @@ public class EventsInterfaceGenerator {
// System.out.println(clazz.getName()); // System.out.println(clazz.getName());
} }
//var result = generateInterface("io.github.jwdeveloper.tiktok.events", classes);System.out.println(result); // var result = generateInterface("io.github.jwdeveloper.tiktok.events", classes);System.out.println(result);
var result = getBuilderImplementation("x",classes); System.out.println(result); var result = getBuilderImplementation("x",classes); System.out.println(result);
} }
public String generateInterface(String packageName, Set<Class<? extends TikTokEvent>> eventsClasses) { public String generateInterface(String packageName, Set<Class<? extends TikTokEvent>> eventsClasses) {
TypeSpec.Builder classBuilder = TypeSpec.interfaceBuilder("TikTokEvents"); TypeSpec.Builder classBuilder = TypeSpec.interfaceBuilder("TikTokEventBuilder");
classBuilder.addModifiers(Modifier.PUBLIC); classBuilder.addModifiers(Modifier.PUBLIC);
classBuilder.addTypeVariable(TypeVariableName.get("T")); classBuilder.addTypeVariable(TypeVariableName.get("T"));
@@ -46,7 +46,7 @@ public class EventsInterfaceGenerator {
MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder("on" + methodName); MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder("on" + methodName);
var name = "Consumer<" + clazzName + ">"; var name = "TikTokEventConsumer<" + clazzName + ">";
constructorBuilder.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC); constructorBuilder.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC);
constructorBuilder.addParameter(ClassName.bestGuess(name), "event"); constructorBuilder.addParameter(ClassName.bestGuess(name), "event");
constructorBuilder.returns(TypeVariableName.get("T")); constructorBuilder.returns(TypeVariableName.get("T"));
@@ -91,7 +91,7 @@ public class EventsInterfaceGenerator {
MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder( methodName); MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder( methodName);
var name = "Consumer<" + clazzName + ">"; var name = "TikTokEventConsumer<" + clazzName + ">";
constructorBuilder.addModifiers( Modifier.PUBLIC); constructorBuilder.addModifiers( Modifier.PUBLIC);
constructorBuilder.addParameter(ClassName.bestGuess(name), "event"); constructorBuilder.addParameter(ClassName.bestGuess(name), "event");
constructorBuilder.addStatement("tikTokEventHandler.subscribe("+clazzName+".class,event)"); constructorBuilder.addStatement("tikTokEventHandler.subscribe("+clazzName+".class,event)");

View File

@@ -0,0 +1,43 @@
package io.github.jwdeveloper.tiktok.protocol;
import org.jsoup.Jsoup;
import java.io.File;
import java.io.IOException;
public class ProtocolGenerator
{
public static void main(String[] args) {
// Path to the HTML file
File htmlFile = new File("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\page.html");
try {
// Parse the HTML file with Jsoup
var doc = Jsoup.parse(htmlFile, "UTF-8");
// Find all script tags
var scriptTags = doc.select("script");
// Display all script tags
int counter = 1;
for (var scriptTag : scriptTags) {
String srcValue = scriptTag.attr("src");
if(!srcValue.contains("tiktok/webapp/main/webapp-live/"))
{
continue;
}
// Only print those script tags which have a 'src' attribute
if (!srcValue.isEmpty()) {
System.out.println("Script Tag " + counter + " src attribute: " + srcValue);
}
counter++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -1,4 +1,4 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok.utils;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;

View File

@@ -7,12 +7,13 @@
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>0.0.8-Release</version> <version>0.0.14-Release</version>
<modules> <modules>
<module>API</module> <module>API</module>
<module>Client</module> <module>Client</module>
<module>Tools</module> <module>Tools</module>
<module>TestApplication</module> <module>TestApplication</module>
<module>Tools-EventsCollector</module>
</modules> </modules>
<properties> <properties>
@@ -54,6 +55,7 @@
<configuration> <configuration>
<shadedArtifactAttached>true</shadedArtifactAttached> <shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>all</shadedClassifierName> <shadedClassifierName>all</shadedClassifierName>
<createDependencyReducedPom>false</createDependencyReducedPom>
<minimizeJar>true</minimizeJar> <minimizeJar>true</minimizeJar>
<artifactSet> <artifactSet>
<includes> <includes>
@@ -64,8 +66,6 @@
<filter> <filter>
<artifact>*:*</artifact> <artifact>*:*</artifact>
<excludes> <excludes>
<exclude>**/proto/**</exclude>
<exclude>**/google/**</exclude>
<exclude>**/tiktokSchema.proto/**</exclude> <exclude>**/tiktokSchema.proto/**</exclude>
</excludes> </excludes>
</filter> </filter>
@@ -99,6 +99,8 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>