Compare commits

...

51 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
JW
6254443755 Merge remote-tracking branch 'origin/master' 2023-08-22 19:58:51 +02:00
JW
c001eacbce Remove old test 2023-08-22 19:58:46 +02:00
JW
1bbb704d14 Update README.md 2023-08-22 19:54:56 +02:00
JW
8c3a5c6627 Remove old test 2023-08-22 19:54:10 +02:00
JW
470b154c5e Merge remote-tracking branch 'origin/master' 2023-08-22 19:53:39 +02:00
JW
2391b12598 Fix Message parsing for
- LikeMessage
- MessageWebcastGiftMessage
- MessageWebcastChatMessage
2023-08-22 19:53:33 +02:00
JW
cb68050e24 Update README.md 2023-08-22 19:14:46 +02:00
JW
a9e347b8da Update README.md 2023-08-22 17:50:22 +02:00
JW
73823c82ea Update README.md 2023-08-22 17:49:35 +02:00
JW
e0542d39af Update README.md 2023-08-22 17:46:42 +02:00
GitHub Action
d33dab0a98 Update version in pom.xml 2023-08-22 15:40:34 +00:00
86 changed files with 1729 additions and 868 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>1.0.0</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

@@ -3,6 +3,8 @@ package io.github.jwdeveloper.tiktok;
import lombok.Data; import lombok.Data;
import java.time.Duration; import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
@Data @Data
@@ -10,62 +12,58 @@ 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>
/// ISO-Language for Client /// ISO-Language for Client
/// </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>
private boolean HandleExistingMessagesOnConnect; private boolean handleExistingMessagesOnConnect;
/// <summary> /// <summary>
/// Whether to download List of Gifts for Room when Connecting /// Whether to download List of Gifts for Room when Connecting
/// </summary> /// </summary>
private boolean DownloadGiftInfo; private boolean downloadGiftInfo;
/// <summary> /// <summary>
/// Whether to print Logs to Console /// Whether to print Logs to Console
/// </summary> /// </summary>
private boolean PrintToConsole; private boolean printToConsole;
/// <summary> /// <summary>
/// LoggingLevel for Logs /// LoggingLevel for Logs
/// </summary> /// </summary>
private Level LogLevel; private Level logLevel;
/// <summary> /// <summary>
/// Whether to print Base64-Data for Messages to Console /// Whether to print Base64-Data for Messages to Console
/// </summary> /// </summary>
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;
/// <summary>
/// Parameters used in requests to tiktok api
/// </summary>
private Map<String, Object> clientParameters;
} }

View File

@@ -39,18 +39,16 @@ 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());
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

@@ -7,14 +7,14 @@ import java.util.List;
@Getter @Getter
public class Badge { public class Badge {
private final ComboBadge comboBadges; private ComboBadge comboBadges;
private final List<TextBadge> textBadges; private final List<TextBadge> textBadges;
private final List<ImageBadge> imageBadges; private final List<ImageBadge> imageBadges;
public Badge(io.github.jwdeveloper.tiktok.messages.Badge badge) { public Badge(io.github.jwdeveloper.tiktok.messages.Badge badge) {
textBadges = badge.getTextBadgesList().stream().map(b -> new TextBadge(b.getType(), b.getName())).toList(); textBadges = badge.getTextBadgesList().stream().map(b -> new TextBadge(b.getType(), b.getName())).toList();
imageBadges = badge.getImageBadgesList().stream().map(b -> new ImageBadge(b.getDisplayType(), new Picture(b.getImage()))).toList(); imageBadges = badge.getImageBadgesList().stream().map(b -> new ImageBadge(b.getDisplayType(), new Picture(b.getImage()))).toList();
comboBadges = new ComboBadge(new Picture(badge.getComplexBadge().getImageUrl()), badge.getComplexBadge().getData()); comboBadges = new ComboBadge(new Picture("badge.getComplexBadge().getImageUrl()"), badge.getComplexBadge().getData());
} }

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,23 @@
package io.github.jwdeveloper.tiktok.exceptions;
public class TikTokLiveRequestException extends TikTokLiveException
{
public TikTokLiveRequestException() {
}
public TikTokLiveRequestException(String message) {
super(message);
}
public TikTokLiveRequestException(String message, Throwable cause) {
super(message, cause);
}
public TikTokLiveRequestException(Throwable cause) {
super(cause);
}
public TikTokLiveRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

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

@@ -186,7 +186,7 @@ message Badge {
message BadgeComplex { message BadgeComplex {
uint32 data1 = 1; uint32 data1 = 1;
string imageUrl = 2; //string imageUrl = 2; Protocol message had invalid UTF-8
string data = 4; string data = 4;
DataContainer detail1 = 5; DataContainer detail1 = 5;
string data2 = 6; string data2 = 6;
@@ -377,7 +377,7 @@ message RankTextMessage {
// Links to Image-Files on the TikTok CDN // Links to Image-Files on the TikTok CDN
message Picture { message Picture {
repeated string urls = 1; // Usually has 3 different urls with different sizes/extensions repeated string urls = 1; // Usually has 3 different urls with different sizes/extensions
string prefix = 2; // uri // string prefix = 2; // uri not working
uint32 data1 = 3; uint32 data1 = 3;
uint32 data2 = 4; uint32 data2 = 4;
string color = 5; string color = 5;
@@ -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;
@@ -681,7 +684,7 @@ message WebcastGiftMessage {
uint32 data11 = 34; uint32 data11 = 34;
message GiftData1 { message GiftData1 {
string data1 = 1; // string data1 = 1; not working
uint32 data2 = 2; uint32 data2 = 2;
uint32 data3 = 3; uint32 data3 = 3;
} }
@@ -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>1.0.0</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 RuntimeException("Already connected"); throw new TikTokLiveException("Already connected");
if (meta.hasConnectionState(ConnectionState.CONNECTING)) if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTING))
throw new RuntimeException("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,114 +2,88 @@ 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.Map;
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> {
private String userName;
private final ClientSettings clientSettings; private final ClientSettings clientSettings;
private Map<String, Object> clientParameters;
private final Logger logger; private final Logger logger;
private final TikTokEventHandler tikTokEventHandler; private final TikTokEventHandler tikTokEventHandler;
public TikTokLiveClientBuilder(String userName) { public TikTokLiveClientBuilder(String userName) {
this.tikTokEventHandler = new TikTokEventHandler(); this.tikTokEventHandler = new TikTokEventHandler();
this.userName = userName;
this.clientSettings = Constants.DefaultClientSettings(); this.clientSettings = Constants.DefaultClientSettings();
this.clientParameters = Constants.DefaultClientParams(); this.clientSettings.setHostName(userName);
this.logger = Logger.getLogger(TikTokLive.class.getName()); this.logger = Logger.getLogger(TikTokLive.class.getName());
} }
public TikTokLiveClientBuilder configure(Consumer<ClientSettings> consumer) {
public TikTokLiveClientBuilder clientSettings(Consumer<ClientSettings> consumer) {
consumer.accept(clientSettings); consumer.accept(clientSettings);
return this; return this;
} }
public TikTokLiveClientBuilder hostUserName(String userName) {
this.userName = userName;
return this;
}
public TikTokLiveClientBuilder clientParameters(Map<String, Object> clientParameters) {
this.clientParameters = clientParameters;
return this;
}
public TikTokLiveClientBuilder addClientParameters(String key, Object value) {
this.clientParameters.put(key, value);
return this;
}
private void validate() { private void validate() {
if (clientSettings.getTimeout() == null) { if (clientSettings.getTimeout() == null) {
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("")) {
throw new TikTokLiveException("HostName can not be null");
} }
var params = clientSettings.getClientParameters();
params.put("app_language", clientSettings.getClientLanguage());
params.put("webcast_language", clientSettings.getClientLanguage());
if (userName == null || userName.equals("")) { logger.setLevel(clientSettings.getLogLevel());
throw new RuntimeException("UserName can not be null");
if(clientSettings.isPrintToConsole() && clientSettings.getLogLevel() == Level.OFF)
{
logger.setLevel(Level.ALL);
} }
if (clientParameters == null) {
clientParameters = Constants.DefaultClientParams();
}
clientParameters.put("app_language", clientSettings.getClientLanguage());
clientParameters.put("webcast_language", clientSettings.getClientLanguage());
} }
public LiveClient build() { public LiveClient build() {
validate(); validate();
var meta = new TikTokRoomInfo(); var tiktokRoomInfo = new TikTokRoomInfo();
meta.setUserName(userName); 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, clientSettings, requestFactory); var apiClient = new TikTokHttpApiClient(cookieJar, requestFactory);
var apiService = new TikTokApiService(apiClient, logger, clientParameters); 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,
clientParameters,
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() {
@@ -118,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;
} }
} }
@@ -333,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,89 +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. Stopping Client. Final Message: \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

@@ -44,8 +44,8 @@ public class HttpUtils
builder.append("&"); builder.append("&");
} }
final String encodedKey = URLEncoder.encode(param.getKey().toString(), StandardCharsets.UTF_8.toString()); final String encodedKey = URLEncoder.encode(param.getKey(), StandardCharsets.UTF_8);
final String encodedValue = URLEncoder.encode(param.getValue().toString(), StandardCharsets.UTF_8.toString()); final String encodedValue = URLEncoder.encode(param.getValue().toString(), StandardCharsets.UTF_8);
builder.append(encodedKey).append("=").append(encodedValue); builder.append(encodedKey).append("=").append(encodedValue);
first = true; first = true;
} }

View File

@@ -1,9 +1,11 @@
package io.github.jwdeveloper.tiktok.http; package io.github.jwdeveloper.tiktok.http;
import com.google.gson.Gson; import com.google.gson.Gson;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
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;
@@ -15,12 +17,12 @@ import java.util.regex.Pattern;
public class TikTokApiService { public class TikTokApiService {
private final TikTokHttpApiClient apiClient; private final TikTokHttpApiClient apiClient;
private final Logger logger; private final Logger logger;
private final Map<String, Object> clientParams; private final ClientSettings clientSettings;
public TikTokApiService(TikTokHttpApiClient apiClient, Logger logger, Map<String, Object> clientParams) { public TikTokApiService(TikTokHttpApiClient apiClient, Logger logger, ClientSettings clientSettings) {
this.apiClient = apiClient; this.apiClient = apiClient;
this.logger = logger; this.logger = logger;
this.clientParams = clientParams; this.clientSettings = clientSettings;
} }
public String fetchRoomId(String userName) { public String fetchRoomId(String userName) {
@@ -29,7 +31,7 @@ public class TikTokApiService {
try { try {
html = apiClient.GetLivestreamPage(userName); html = apiClient.GetLivestreamPage(userName);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to fetch room id from WebCast, see stacktrace for more info.", e); throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
} }
Pattern firstPattern = Pattern.compile("room_id=([0-9]*)"); Pattern firstPattern = Pattern.compile("room_id=([0-9]*)");
@@ -48,10 +50,10 @@ 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");
} }
clientParams.put("room_id", id); clientSettings.getClientParameters().put("room_id", id);
logger.info("RoomID -> "+id); logger.info("RoomID -> "+id);
return id; return id;
} }
@@ -60,7 +62,7 @@ public class TikTokApiService {
public LiveRoomMeta fetchRoomInfo() { public LiveRoomMeta fetchRoomInfo() {
logger.info("Fetch RoomInfo"); logger.info("Fetch RoomInfo");
try { try {
var response = apiClient.GetJObjectFromWebcastAPI("room/info/", clientParams); var response = apiClient.GetJObjectFromWebcastAPI("room/info/", clientSettings.getClientParameters());
if (!response.has("data")) { if (!response.has("data")) {
return new LiveRoomMeta(); return new LiveRoomMeta();
} }
@@ -78,7 +80,7 @@ public class TikTokApiService {
logger.info("RoomInfo status -> "+info.getStatus()); logger.info("RoomInfo status -> "+info.getStatus());
return info; return info;
} catch (Exception e) { } catch (Exception e) {
throw new TikTokLiveException("Failed to fetch room info from WebCast, see stacktrace for more info.", e); throw new TikTokLiveRequestException("Failed to fetch room info from WebCast, see stacktrace for more info.", e);
} }
} }
@@ -86,20 +88,20 @@ public class TikTokApiService {
{ {
logger.info("Fetch ClientData"); logger.info("Fetch ClientData");
try { try {
var response = apiClient.GetDeserializedMessage("im/fetch/", clientParams); var response = apiClient.GetDeserializedMessage("im/fetch/", clientSettings.getClientParameters());
clientParams.put("cursor",response.getCursor()); clientSettings.getClientParameters().put("cursor",response.getCursor());
clientParams.put("internal_ext", response.getAckIds()); clientSettings.getClientParameters().put("internal_ext", response.getAckIds());
return response; return response;
} }
catch (Exception e) catch (Exception e)
{ {
throw new TikTokLiveException("Failed to fetch client data", e); throw new TikTokLiveRequestException("Failed to fetch client data", e);
} }
} }
public Map<Integer, TikTokGift> fetchAvailableGifts() { public Map<Integer, TikTokGiftInfo> fetchAvailableGifts() {
try { try {
var response = apiClient.GetJObjectFromWebcastAPI("gift/list/", clientParams); var response = apiClient.GetJObjectFromWebcastAPI("gift/list/", clientSettings.getClientParameters());
if(!response.has("data")) if(!response.has("data"))
{ {
return new HashMap<>(); return new HashMap<>();
@@ -110,17 +112,17 @@ 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);
} }
return gifts; return gifts;
} catch (Exception e) { } catch (Exception e) {
throw new TikTokLiveException("Failed to fetch giftTokens from WebCast, see stacktrace for more info.", e); throw new TikTokLiveRequestException("Failed to fetch giftTokens from WebCast, see stacktrace for more info.", e);
} }
} }
} }

View File

@@ -4,7 +4,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
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.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import java.net.URI; import java.net.URI;
@@ -16,27 +16,23 @@ import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
public class TikTokHttpApiClient { public class TikTokHttpApiClient {
private final ClientSettings clientSettings;
private final TikTokHttpRequestFactory requestFactory; private final TikTokHttpRequestFactory requestFactory;
private final TikTokCookieJar tikTokCookieJar; private final TikTokCookieJar tikTokCookieJar;
public TikTokHttpApiClient(TikTokCookieJar tikTokCookieJar, TikTokHttpRequestFactory requestFactory) {
public TikTokHttpApiClient(TikTokCookieJar tikTokCookieJar, ClientSettings clientSettings, TikTokHttpRequestFactory requestFactory) {
this.clientSettings = clientSettings;
this.requestFactory = requestFactory; this.requestFactory = requestFactory;
this.tikTokCookieJar = tikTokCookieJar; this.tikTokCookieJar = tikTokCookieJar;
} }
public String GetLivestreamPage(String userName) { public String GetLivestreamPage(String userName) {
var url = Constants.TIKTOK_URL_WEB + "@" + userName + "/live/"; var url = Constants.TIKTOK_URL_WEB + "@" + userName + "/live/";
var get = getRequest(url, null, false); var get = getRequest(url, null);
return get; return get;
} }
public JsonObject GetJObjectFromWebcastAPI(String path, Map<String, Object> parameters) { public JsonObject GetJObjectFromWebcastAPI(String path, Map<String, Object> parameters) {
var get = getRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters, false); var get = getRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters);
var json = JsonParser.parseString(get); var json = JsonParser.parseString(get);
var jsonObject = json.getAsJsonObject(); var jsonObject = json.getAsJsonObject();
return jsonObject; return jsonObject;
@@ -49,12 +45,11 @@ public class TikTokHttpApiClient {
} }
catch (Exception e) catch (Exception e)
{ {
throw new TikTokLiveException("Unable to deserialize message: "+path,e); throw new TikTokLiveRequestException("Unable to deserialize message: "+path,e);
} }
} }
private String getRequest(String url, Map<String, Object> parameters) {
private String getRequest(String url, Map<String, Object> parameters, boolean signURL) {
if (parameters == null) { if (parameters == null) {
parameters = new HashMap<>(); parameters = new HashMap<>();
} }
@@ -86,7 +81,7 @@ public class TikTokHttpApiClient {
} }
catch (Exception e) catch (Exception e)
{ {
throw new TikTokLiveException("unabel to send signature"); throw new TikTokLiveRequestException("Unable to send signature");
} }
} }
@@ -95,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);
@@ -112,7 +107,7 @@ public class TikTokHttpApiClient {
requestFactory.setAgent(userAgent); requestFactory.setAgent(userAgent);
return signedUrl; return signedUrl;
} catch (Exception e) { } catch (Exception e) {
throw new TikTokLiveException("Insufficent values have been supplied for signing. Likely due to an update. Post an issue on GitHub.", e); throw new TikTokLiveRequestException("Insufficient values have been supplied for signing. Likely due to an update. Post an issue on GitHub.", e);
} }
} }

View File

@@ -2,10 +2,10 @@ package io.github.jwdeveloper.tiktok.http;
import io.github.jwdeveloper.tiktok.Constants; import io.github.jwdeveloper.tiktok.Constants;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.ProxySelector;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.net.http.HttpClient; import java.net.http.HttpClient;
@@ -18,24 +18,16 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class TikTokHttpRequestFactory implements TikTokHttpRequest public class TikTokHttpRequestFactory implements TikTokHttpRequest {
{ private final CookieManager cookieManager;
private CookieManager cookieManager; private final Map<String, String> defaultHeaders;
private HttpClient client; private final TikTokCookieJar tikTokCookieJar;
private final HttpClient client;
private Duration timeout;
private ProxySelector webProxy;
private String query; private String query;
private Boolean sent;
private Map<String, String> defaultHeaders;
private TikTokCookieJar tikTokCookieJar;
public TikTokHttpRequestFactory(TikTokCookieJar tikTokCookieJar) { public TikTokHttpRequestFactory(TikTokCookieJar tikTokCookieJar) {
cookieManager = new CookieManager();
this.tikTokCookieJar = tikTokCookieJar; this.tikTokCookieJar = tikTokCookieJar;
this.cookieManager = new CookieManager();
defaultHeaders = Constants.DefaultRequestHeaders(); defaultHeaders = Constants.DefaultRequestHeaders();
client = HttpClient.newBuilder() client = HttpClient.newBuilder()
.cookieHandler(cookieManager) .cookieHandler(cookieManager)
@@ -43,8 +35,7 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest
.build(); .build();
} }
public WebSocket.Builder openSocket() public WebSocket.Builder openSocket() {
{
return client.newWebSocketBuilder(); return client.newWebSocketBuilder();
} }
@@ -52,26 +43,21 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest
public String Get(String url) { public String Get(String url) {
var uri = URI.create(url); var uri = URI.create(url);
var request = HttpRequest.newBuilder().GET(); var request = HttpRequest.newBuilder().GET();
for(var header : defaultHeaders.entrySet())
{
//request.setHeader(header.getKey(),header.getValue());
}
if (query != null) { if (query != null) {
var baseUri = uri.toString(); var baseUri = uri.toString();
var requestUri = URI.create(baseUri + "?" + query); var requestUri = URI.create(baseUri + "?" + query);
request.uri(requestUri); request.uri(requestUri);
} }
return GetContent(request.build()); return GetContent(request.build());
} }
@SneakyThrows @SneakyThrows
public String Post(String url, HttpRequest.BodyPublisher data) { public String Post(String url, HttpRequest.BodyPublisher data) {
var uri = URI.create(url); var uri = URI.create(url);
var request = HttpRequest.newBuilder().POST(data); var request = HttpRequest.newBuilder().POST(data);
for(var header : defaultHeaders.entrySet()) for (var header : defaultHeaders.entrySet()) {
{ request.setHeader(header.getKey(), header.getValue());
request.setHeader(header.getKey(),header.getValue());
} }
if (query != null) { if (query != null) {
var baseUri = uri.toString(); var baseUri = uri.toString();
@@ -81,14 +67,12 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest
return GetContent(request.build()); return GetContent(request.build());
} }
public TikTokHttpRequest setHeader(String key, String value) public TikTokHttpRequest setHeader(String key, String value) {
{ defaultHeaders.put(key, value);
defaultHeaders.put(key,value);
return this; return this;
} }
public TikTokHttpRequest setAgent( String value) public TikTokHttpRequest setAgent(String value) {
{
defaultHeaders.put("User-Agent", value); defaultHeaders.put("User-Agent", value);
return this; return this;
} }
@@ -111,26 +95,18 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest
} }
private String GetContent(HttpRequest request) throws Exception { private String GetContent(HttpRequest request) throws Exception {
var response = client.send(request, HttpResponse.BodyHandlers.ofString()); var response = client.send(request, HttpResponse.BodyHandlers.ofString());
sent = true; if (response.statusCode() == 404) {
if (response.statusCode() == 404) throw new TikTokLiveRequestException("Request responded with 404 NOT_FOUND");
{
throw new RuntimeException("Request responded with 404 NOT_FOUND");
} }
if(response.statusCode() != 200) if (response.statusCode() != 200) {
{ throw new TikTokLiveRequestException("Request was unsuccessful " + response.statusCode());
throw new RuntimeException("Request was unsuccessful "+response.statusCode());
} }
var cookies = response.headers().allValues("Set-Cookie"); var cookies = response.headers().allValues("Set-Cookie");
for(var cookie : cookies) for (var cookie : cookies) {
{
var split = cookie.split(";")[0].split("="); var split = cookie.split(";")[0].split("=");
var uri = request.uri(); var uri = request.uri();
@@ -139,9 +115,9 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest
var value = split[1]; var value = split[1];
tikTokCookieJar.set(key, value); tikTokCookieJar.set(key, value);
var map = new HashMap<String,List<String>>(); var map = new HashMap<String, List<String>>();
map.put(key,List.of(value)); map.put(key, List.of(value));
cookieManager.put(uri,map); cookieManager.put(uri, map);
} }
return response.body(); return response.body();

View File

@@ -3,69 +3,69 @@ 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.nio.ByteBuffer;
import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.logging.Logger; import java.util.logging.Logger;
public class TikTokWebsocketClient { public class TikTokWebSocketClient {
private final Logger logger; private final Logger logger;
private final Map<String, Object> clientParams;
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,
Map<String, Object> clientParams,
TikTokHttpRequestFactory factory,
ClientSettings clientSettings, ClientSettings clientSettings,
WebResponseHandler webResponseHandler, TikTokMessageHandlerRegistration webResponseHandler,
TikTokEventHandler tikTokEventHandler) { TikTokEventHandler tikTokEventHandler) {
this.logger = logger; this.logger = logger;
this.clientParams = clientParams;
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);
} }
} }
@@ -78,33 +78,34 @@ public class TikTokWebsocketClient {
var headers = Constants.DefaultRequestHeaders(); var headers = Constants.DefaultRequestHeaders();
var clone = new TreeMap<>(clientParams); var clone = new TreeMap<>(clientSettings.getClientParameters());
clone.putAll(headers); clone.putAll(headers);
clone.put(name, value); clone.put(name, value);
var url = webcastResponse.getSocketUrl(); var url = webcastResponse.getSocketUrl();
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

@@ -0,0 +1,49 @@
package io.github.jwdeveloper.tiktok;
import com.google.protobuf.InvalidProtocolBufferException;
import io.github.jwdeveloper.tiktok.common.TikTokBaseTest;
import io.github.jwdeveloper.tiktok.messages.*;
import org.junit.Test;
public class ParseMessagesTests extends TikTokBaseTest
{
@Test
public void ShouldParseMessageWebcastLikeMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("LikeMessage.bin");
var message = WebcastLikeMessage.parseFrom(bytes);
}
@Test
public void ShouldParseMessageWebcastGiftMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("MessageWebcastGiftMessage.bin");
var message = WebcastGiftMessage.parseFrom(bytes);
}
@Test
public void ShouldParseMessageWebcastChatMessage() throws InvalidProtocolBufferException {
var bytes = getFileBytesUtf("MessageWebcastChatMessage.bin");
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

@@ -1,30 +0,0 @@
package io.github.jwdeveloper.tiktok;
import com.google.protobuf.InvalidProtocolBufferException;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage;
import org.junit.Test;
import java.io.IOException;
public class SerializeWebMessageTest
{
@Test
public void WebcastWebsocketMessage()
{
/*
try (var str = getClass().getClassLoader().getResourceAsStream("WebcastWebsocketMessage.bin"))
{
var bytes = str.readAllBytes();
var person = WebcastWebsocketMessage.parseFrom(bytes);
System.out.println("id: " + person.getId());
System.out.println("type: " + person.getType());
System.out.println("binary: " + person.getBinary().size());
// System.out.println("Email: " + person.getEmail());
} catch (InvalidProtocolBufferException e) {
System.out.println("Error parsing the protobuf message: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.out.println("Error reading the file: " + e.getMessage());
}*/
}
}

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

@@ -0,0 +1,34 @@
package io.github.jwdeveloper.tiktok.common;
import com.google.protobuf.InvalidProtocolBufferException;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage;
import java.io.IOException;
import java.util.Base64;
public class TikTokBaseTest
{
public byte[] getFileBytes(String path)
{
try {
var stream = getClass().getClassLoader().getResourceAsStream(path);
var bytes= stream.readAllBytes();
stream.close();
return bytes;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public byte[] getFileBytesUtf(String path)
{
try {
var stream = getClass().getClassLoader().getResourceAsStream(path);
var bytes= stream.readAllBytes();
stream.close();
return Base64.getDecoder().decode(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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,32 +0,0 @@
 ŔĆł»„ÖF¸E *
compress_typenone2pb:msgBš
Ű
WebcastLikeMessageş

WebcastLikeMessageˇ¤ŚöÍííd żÚřęëíd0
pm_mt_msg_viewer{0:user} liked the LIVE
#ffffffff "
Ş}
{ ˆ•ÉÔëdhulajnoga czek˛hulajnoga.czek5ňLMS4wLjABAAAAx4-lIrRjH0pMT0D3laROVsnU-4u6P1tN4td82AQTCHjKZ-CHaF_DluNSFQQp1s4DHP°¸Ŕ ĆĐŇź1†q*Ü ˆ•ÉÔëdhulajnoga czek˛ş ˛hulajnoga.czek5J˙
Čhttps://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-euttp/26aa68e29831ff0c9d4fb37875efffdd~tplv-tiktok-shrink:72:72.webp?x-expires=1692291600&x-signature=Z2ZM2T%2FckIxKSj%2BJY%2BONoypyMwo%3D
´https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-euttp/26aa68e29831ff0c9d4fb37875efffdd~c5_100x100.webp?x-expires=1692291600&x-signature=gJgrVHIZWnVCif4Z6gXuhnl1pn8%3D
´https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-euttp/26aa68e29831ff0c9d4fb37875efffdd~c5_100x100.jpeg?x-expires=1692291600&x-signature=ITqTLj6v7UVl12HeeGa6a6FYNpc%3DD100x100/tos-useast2a-avt-0068-euttp/26aa68e29831ff0c9d4fb37875efffddŞ0 ňLMS4wLjABAAAAx4-lIrRjH0pMT0D3laROVsnU-4u6P1tN4td82AQTCHjKZ-CHaF_DluNSFQQp1s4DîR„sslocal://webcast_webview_popup?gravity=bottom&show_mask=1&url=https%3A%2F%2Flf16-web.tiktokcdn.com%2Fobj%2Fies-hotsoon-draft-sg%2Ftiktok-live-faq%2Ftiktok_live_revenue_new_gifter_details.html&web_bg_color=FFFFFF&height=60%25&mask_bg_color=000000b3&use_spark=1b:"10000037267585271263611681*0
27154120369731914523 pm_mt_live_ng_im
New gifter( Ü sslocal://webcast_lynxview_popup?use_spark=1&url=https%3A%2F%2Flf16-gecko-source.tiktokcdn.com%2Fobj%2Fbyte-gurd-source-sg%2Ftiktok%2Ffe%2Flive%2Ftiktok_live_revenue_user_level_main%2Fsrc%2Fpages%2Fprivilege%2Fpanel%2Ftemplate.js&hide_status_bar=0&hide_nav_bar=1&container_bg_color=00000000&height=90%25&bdhm_bid=tiktok_live_revenue_user_level_main&use_forest=1XbN
271383811767874578280".mock_fix_width_transparent_7138381176787457828*Ő+webcast-va/grade_badge_icon_lite_lv1_v1.png:ésslocal://webcast_lynxview_popup?use_spark=1&url=https%3A%2F%2Flf16-gecko-source.tiktokcdn.com%2Fobj%2Fbyte-gurd-source-sg%2Ftiktok%2Ffe%2Flive%2Ftiktok_live_revenue_user_level_main%2Fsrc%2Fpages%2Fprivilege%2Fpanel%2Ftemplate.js&hide_status_bar=0&hide_nav_bar=1&container_bg_color=00000000&height=90%25&bdhm_bid=tiktok_live_revenue_user_level_main&use_forest=1
\https://p16-webcast.tiktokcdn.com/webcast-va/grade_badge_icon_lite_lv1_v1.png~tplv-obj.image
\https://p19-webcast.tiktokcdn.com/webcast-va/grade_badge_icon_lite_lv1_v1.png~tplv-obj.image"2* b
 #99789EE7x2 : " Z
 #99789EE7€÷( R„sslocal://webcast_webview_popup?gravity=bottom&show_mask=1&url=https%3A%2F%2Flf16-web.tiktokcdn.com%2Fobj%2Fies-hotsoon-draft-sg%2Ftiktok-live-faq%2Ftiktok_live_revenue_new_gifter_details.html&web_bg_color=FFFFFF&height=60%25&mask_bg_color=000000b3&use_spark=1Xb:
271541203697319145231"10000037267585271263611681*¦x ˆŰ 0:„sslocal://webcast_webview_popup?gravity=bottom&show_mask=1&url=https%3A%2F%2Flf16-web.tiktokcdn.com%2Fobj%2Fies-hotsoon-draft-sg%2Ftiktok-live-faq%2Ftiktok_live_revenue_new_gifter_details.html&web_bg_color=FFFFFF&height=60%25&mask_bg_color=000000b3&use_spark=1
Shttps://p16-webcast.tiktokcdn.com/webcast-va/new_gifter_badge_v3.png~tplv-obj.image
Shttps://p19-webcast.tiktokcdn.com/webcast-va/new_gifter_badge_v3.png~tplv-obj.image"webcast-va/new_gifter_badge_v3.png
pm_mt_live_ng_im
New gifterZ
 #803F3F3Fb
 #803F3F3FˆË”ʍÁcŔ
pm_mt_msg_viewer{0:user} liked the LIVE
#ffffffff "
Ş}
{˛hulajnoga.czek5ňLMS4wLjABAAAAx4-lIrRjH0pMT0D3laROVsnU-4u6P1tN4td82AQTCHjKZ-CHaF_DluNSFQQp1s4D ˆ•ÉÔëdhulajnoga czekHˇ¤ŚöÍííd)1692120689751_7267603020139989890_1_1_0_0č ×ŘŇź1*-@H

View File

@@ -1,9 +1,13 @@
# TikTok-Live-Java [![](https://jitpack.io/v/jwdeveloper/TikTok-Live-Java.svg)](https://jitpack.io/#jwdeveloper/TikTok-Live-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.
# TikTokLive Java
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)
@@ -12,15 +16,14 @@ 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
1. Install the package via Maven (dependecies will be simplify in future) 1. Install the package via Maven
```xml ```xml
<repositories> <repositories>
@@ -29,32 +32,20 @@ Do you prefer other programming languages?
<url>https://jitpack.io</url> <url>https://jitpack.io</url>
</repository> </repository>
</repositories> </repositories>
```
```xml <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>
</dependencies>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>API</artifactId>
<version>0.0.4-Release</version>
</dependency>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId>
<version>0.0.4-Release</version>
</dependency>
``` ```
2. Create your first chat connection 2. Create your first chat connection
@@ -63,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>1.0.0</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,60 +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("DUPA: "+e.getText()); 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());
} }
@@ -83,6 +93,6 @@ public class Main {
for (var message : messages) { for (var message : messages) {
sb.append(message).append(" "); sb.append(message).append(" ");
} }
System.out.println(sb.toString()); System.out.println(sb);
} }
} }

View File

@@ -1,30 +1,37 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
public class SimpleExample { import java.io.IOException;
public static void main(String[] args) {
// Username of someone who is currently live
var tiktokUsername = "officialgeilegisela";
TikTokLive.newClient(tiktokUsername) public class SimpleExample {
.clientSettings(settings -> public static void main(String[] args) throws IOException {
TikTokLive.newClient(Main.TEST_TIKTOK_USER)
.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();
}) })
.buildAndRun(); .buildAndRun();
System.in.read();
} }
} }

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>1.0.0</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>1.0.0</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>