mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-02-27 08:49:40 -05:00
Breaking changes:
'Gift': changed from class to enum, so now you can handle
incoming gifts in switch
`Events`
- new:
onGiftComboFinished
- Removed:
onGiftBrodcast
- Rename:
onGiftMessage -> onGift
onRoomPinMessage -> onRoomPin
onRoomMessage -> onRoom
onLinkMessage -> onLink
onBarrageMessage -> onBarrage
onPollMessage -> onPoll
onShopMessage -> onShop
onDetectMessage -> onDetect
`GiftManager`
added:
registerGift
findById
findByName
getGifts
removed:
getActiveGifts
This commit is contained in:
19
API/pom.xml
19
API/pom.xml
@@ -58,6 +58,25 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-javadocs</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<doclint>-html,-syntax,-accessibility,-missing</doclint>
|
||||||
|
<failOnError>false</failOnError>
|
||||||
|
<quiet>true</quiet>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
@@ -116,6 +116,7 @@ public class Constants {
|
|||||||
public static Map<String, String> DefaultRequestHeaders() {
|
public static Map<String, String> DefaultRequestHeaders() {
|
||||||
var headers = new HashMap<String, String>();
|
var headers = new HashMap<String, String>();
|
||||||
|
|
||||||
|
headers.put("authority","www.tiktok.com");
|
||||||
headers.put("Connection", "keep-alive");
|
headers.put("Connection", "keep-alive");
|
||||||
headers.put("Cache-Control", "max-age=0");
|
headers.put("Cache-Control", "max-age=0");
|
||||||
headers.put("Accept", "text/html,application/json,application/protobuf");
|
headers.put("Accept", "text/html,application/json,application/protobuf");
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.data.events.common;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TikTokEmptyEvent extends TikTokEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,8 +26,8 @@ package io.github.jwdeveloper.tiktok.data.events.common;
|
|||||||
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Base class for all events
|
* Base class for all events
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public abstract class TikTokEvent {
|
public abstract class TikTokEvent {
|
||||||
|
|||||||
@@ -24,16 +24,32 @@ package io.github.jwdeveloper.tiktok.data.events.gift;
|
|||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered every time gift is sent
|
||||||
|
*
|
||||||
|
* @see GiftSendType it has 3 states
|
||||||
|
*
|
||||||
|
* Example when user sends gift with combo
|
||||||
|
* Combo: 1 -> comboState = GiftSendType.Begin
|
||||||
|
* Combo: 4 -> comboState = GiftSendType.Active
|
||||||
|
* Combo: 8 -> comboState = GiftSendType.Active
|
||||||
|
* Combo: 12 -> comboState = GiftSendType.Finsihed
|
||||||
|
*
|
||||||
|
* Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
|
||||||
|
*/
|
||||||
@EventMeta(eventType = EventType.Message)
|
@EventMeta(eventType = EventType.Message)
|
||||||
@Getter
|
@Getter
|
||||||
public class TikTokGiftComboEvent extends TikTokGiftEvent
|
public class TikTokGiftComboEvent extends TikTokGiftEvent {
|
||||||
{
|
private final GiftSendType comboState;
|
||||||
public TikTokGiftComboEvent(Gift gift, WebcastGiftMessage msg) {
|
|
||||||
|
public TikTokGiftComboEvent(Gift gift, WebcastGiftMessage msg, GiftSendType comboState) {
|
||||||
super(gift, msg);
|
super(gift, msg);
|
||||||
|
this.comboState = comboState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,14 +26,17 @@ package io.github.jwdeveloper.tiktok.data.events.gift;
|
|||||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Triggered every time a gift arrives.
|
* Triggered when user sends gifts that has
|
||||||
|
* no combo (most of expensive gifts)
|
||||||
|
* or if combo has finished
|
||||||
*/
|
*/
|
||||||
@EventMeta(eventType = EventType.Message)
|
@EventMeta(eventType = EventType.Message)
|
||||||
@Getter
|
@Getter
|
||||||
@@ -41,15 +44,10 @@ public class TikTokGiftEvent extends TikTokHeaderEvent {
|
|||||||
private final Gift gift;
|
private final Gift gift;
|
||||||
private final User user;
|
private final User user;
|
||||||
private final int combo;
|
private final int combo;
|
||||||
private final boolean comboFinished;
|
|
||||||
private final int comboIndex;
|
|
||||||
|
|
||||||
public TikTokGiftEvent(Gift gift, WebcastGiftMessage msg) {
|
public TikTokGiftEvent(Gift gift, WebcastGiftMessage msg) {
|
||||||
super(msg.getCommon());
|
super(msg.getCommon());
|
||||||
this.gift = gift;
|
this.gift = gift;
|
||||||
user = User.map(msg.getUser(),msg.getUserIdentity());
|
user = User.map(msg.getUser(), msg.getUserIdentity());
|
||||||
combo = msg.getComboCount();
|
combo = msg.getComboCount();
|
||||||
comboFinished = msg.getRepeatEnd() > 0;
|
|
||||||
comboIndex = msg.getRepeatCount();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
|||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,4 +44,12 @@ public class TikTokWebsocketMessageEvent extends TikTokEvent
|
|||||||
private TikTokEvent event;
|
private TikTokEvent event;
|
||||||
|
|
||||||
private WebcastResponse.Message message;
|
private WebcastResponse.Message message;
|
||||||
|
|
||||||
|
private MetaData metaData;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public static class MetaData
|
||||||
|
{
|
||||||
|
Duration handlingTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,9 +69,14 @@ public class Text {
|
|||||||
var format = matcher.replaceAll("%s");
|
var format = matcher.replaceAll("%s");
|
||||||
|
|
||||||
var output = new ArrayList<String>();
|
var output = new ArrayList<String>();
|
||||||
for (var piece : textPieces) {
|
for (var piece : textPieces)
|
||||||
|
{
|
||||||
output.add(piece.getText());
|
output.add(piece.getText());
|
||||||
}
|
}
|
||||||
|
if(matcher.groupCount() != output.size())
|
||||||
|
{
|
||||||
|
return format;
|
||||||
|
}
|
||||||
return String.format(format, output.toArray());
|
return String.format(format, output.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,9 @@
|
|||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
// This enum is generated
|
// This enum is generated
|
||||||
package io.github.jwdeveloper.tiktok.data.models;
|
package io.github.jwdeveloper.tiktok.data.models.gifts;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.data.models.gifts;
|
||||||
|
|
||||||
|
public enum GiftSendType
|
||||||
|
{
|
||||||
|
Finished,
|
||||||
|
Begin,
|
||||||
|
Active;
|
||||||
|
|
||||||
|
|
||||||
|
public static GiftSendType fromNumber(long number)
|
||||||
|
{
|
||||||
|
return switch ((int) number) {
|
||||||
|
case 0 -> GiftSendType.Finished;
|
||||||
|
case 1, 2, 4 -> GiftSendType.Active;
|
||||||
|
default -> GiftSendType.Finished;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,9 +46,6 @@ public class User {
|
|||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private Set<UserAttribute> attributes;
|
private Set<UserAttribute> attributes;
|
||||||
|
|
||||||
@Setter
|
|
||||||
private boolean tracked;
|
|
||||||
|
|
||||||
public List<UserAttribute> getAttributes() {
|
public List<UserAttribute> getAttributes() {
|
||||||
return attributes.stream().toList();
|
return attributes.stream().toList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ package io.github.jwdeveloper.tiktok.handler;
|
|||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
public interface TikTokMessageHandler
|
public interface TikTokMessageHandler
|
||||||
{
|
{
|
||||||
TikTokEvent handle(byte[] messagePayload) throws Exception;
|
List<TikTokEvent> handle(byte[] messagePayload) throws Exception;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.live;
|
package io.github.jwdeveloper.tiktok.live;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
package io.github.jwdeveloper.tiktok.live;
|
package io.github.jwdeveloper.tiktok.live;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ public interface EventsBuilder<T> {
|
|||||||
T onWebsocketUnhandledMessage(EventConsumer<TikTokWebsocketUnhandledMessageEvent> event);
|
T onWebsocketUnhandledMessage(EventConsumer<TikTokWebsocketUnhandledMessageEvent> event);
|
||||||
|
|
||||||
T onGiftCombo(EventConsumer<TikTokGiftComboEvent> event);
|
T onGiftCombo(EventConsumer<TikTokGiftComboEvent> event);
|
||||||
|
|
||||||
T onGift(EventConsumer<TikTokGiftEvent> event);
|
T onGift(EventConsumer<TikTokGiftEvent> event);
|
||||||
|
|
||||||
T onQuestion(EventConsumer<TikTokQuestionEvent> event);
|
T onQuestion(EventConsumer<TikTokQuestionEvent> event);
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.utils;
|
||||||
|
|
||||||
|
public class Stopwatch {
|
||||||
|
private long startTime;
|
||||||
|
private long stopTime;
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
startTime = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long stop() {
|
||||||
|
stopTime = System.nanoTime();
|
||||||
|
return getElapsedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getElapsedTime() {
|
||||||
|
return stopTime - startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
<filter>
|
<filter>
|
||||||
<artifact>*:*</artifact>
|
<artifact>*:*</artifact>
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>**/tiktokSchema.proto/**</exclude>
|
<exclude>**/*.proto</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</filter>
|
</filter>
|
||||||
</filters>
|
</filters>
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
|||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
|
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
|
||||||
|
import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
|
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
|
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
|
||||||
@@ -50,6 +51,7 @@ import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
|||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
|
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
|
||||||
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
|
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
|
||||||
|
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
|
||||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||||
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
|
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
|
||||||
|
|
||||||
@@ -147,10 +149,14 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
|
var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
|
||||||
var apiService = new TikTokApiService(apiClient, logger, clientSettings);
|
var apiService = new TikTokApiService(apiClient, logger, clientSettings);
|
||||||
var giftManager = new TikTokGiftManager();
|
var giftManager = new TikTokGiftManager();
|
||||||
|
var eventMapper = new TikTokGenericEventMapper();
|
||||||
|
var giftHandler = new TikTokGiftEventHandler(giftManager);
|
||||||
|
|
||||||
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler,
|
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler,
|
||||||
giftManager,
|
tiktokRoomInfo,
|
||||||
tiktokRoomInfo);
|
eventMapper,
|
||||||
|
giftHandler
|
||||||
|
);
|
||||||
|
|
||||||
var webSocketClient = new TikTokWebSocketClient(logger,
|
var webSocketClient = new TikTokWebSocketClient(logger,
|
||||||
cookieJar,
|
cookieJar,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.gifts;
|
package io.github.jwdeveloper.tiktok.gifts;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||||
|
|||||||
@@ -23,18 +23,20 @@
|
|||||||
package io.github.jwdeveloper.tiktok.handlers;
|
package io.github.jwdeveloper.tiktok.handlers;
|
||||||
|
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
|
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketMessageEvent;
|
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketMessageEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent;
|
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
|
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokMessageMappingException;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||||
|
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
|
import io.github.jwdeveloper.tiktok.utils.Stopwatch;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@@ -43,21 +45,24 @@ public abstract class TikTokMessageHandler {
|
|||||||
|
|
||||||
private final Map<String, io.github.jwdeveloper.tiktok.handler.TikTokMessageHandler> handlers;
|
private final Map<String, io.github.jwdeveloper.tiktok.handler.TikTokMessageHandler> handlers;
|
||||||
private final TikTokEventObserver tikTokEventHandler;
|
private final TikTokEventObserver tikTokEventHandler;
|
||||||
|
protected final TikTokGenericEventMapper mapper;
|
||||||
|
|
||||||
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler) {
|
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, TikTokGenericEventMapper mapper) {
|
||||||
handlers = new HashMap<>();
|
handlers = new HashMap<>();
|
||||||
this.tikTokEventHandler = tikTokEventHandler;
|
this.tikTokEventHandler = tikTokEventHandler;
|
||||||
init();
|
this.mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void init();
|
|
||||||
|
|
||||||
public void registerMapping(Class<?> clazz, Function<byte[], TikTokEvent> func) {
|
public void registerMapping(Class<?> clazz, Function<byte[], TikTokEvent> func) {
|
||||||
|
handlers.put(clazz.getSimpleName(), messagePayload -> List.of(func.apply(messagePayload)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerMappings(Class<?> clazz, Function<byte[], List<TikTokEvent>> func) {
|
||||||
handlers.put(clazz.getSimpleName(), func::apply);
|
handlers.put(clazz.getSimpleName(), func::apply);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerMapping(Class<?> input, Class<?> output) {
|
public void registerMapping(Class<?> input, Class<?> output) {
|
||||||
registerMapping(input, (e) -> mapMessageToEvent(input, output, e));
|
registerMapping(input, (e) -> mapper.mapToEvent(input, output, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(LiveClient client, WebcastResponse webcastResponse) {
|
public void handle(LiveClient client, WebcastResponse webcastResponse) {
|
||||||
@@ -71,18 +76,6 @@ public abstract class TikTokMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void handleSingleMessage(LiveClient client, String type, byte[] bytes) throws Exception {
|
|
||||||
|
|
||||||
if (!handlers.containsKey(type)) {
|
|
||||||
tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(WebcastResponse.Message.newBuilder().setMethod(type).build()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var handler = handlers.get(type);
|
|
||||||
var tiktokEvent = handler.handle(bytes);
|
|
||||||
tikTokEventHandler.publish(client, new TikTokWebsocketMessageEvent(tiktokEvent, WebcastResponse.Message.newBuilder().build()));
|
|
||||||
tikTokEventHandler.publish(client, tiktokEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void handleSingleMessage(LiveClient client, WebcastResponse.Message message) throws Exception {
|
public void handleSingleMessage(LiveClient client, WebcastResponse.Message message) throws Exception {
|
||||||
var messageClassName = message.getMethod();
|
var messageClassName = message.getMethod();
|
||||||
@@ -91,29 +84,16 @@ public abstract class TikTokMessageHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var handler = handlers.get(messageClassName);
|
var handler = handlers.get(messageClassName);
|
||||||
var tiktokEvent = handler.handle(message.getPayload().toByteArray());
|
var stopwatch = new Stopwatch();
|
||||||
tikTokEventHandler.publish(client, new TikTokWebsocketMessageEvent(tiktokEvent, message));
|
stopwatch.start();
|
||||||
tikTokEventHandler.publish(client, tiktokEvent);
|
var events = handler.handle(message.getPayload().toByteArray());
|
||||||
}
|
var handlingTimeInMs = stopwatch.stop();
|
||||||
|
var metadata = new TikTokWebsocketMessageEvent.MetaData(Duration.ofNanos(handlingTimeInMs));
|
||||||
|
|
||||||
protected TikTokEvent mapMessageToEvent(Class<?> inputClazz, Class<?> outputClass, byte[] payload) {
|
for (var event : events) {
|
||||||
try {
|
tikTokEventHandler.publish(client, new TikTokWebsocketMessageEvent(event, message, metadata));
|
||||||
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", byte[].class);
|
tikTokEventHandler.publish(client, event);
|
||||||
var deserialized = parseMethod.invoke(null,payload);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,8 @@
|
|||||||
package io.github.jwdeveloper.tiktok.handlers;
|
package io.github.jwdeveloper.tiktok.handlers;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.*;
|
import io.github.jwdeveloper.tiktok.data.events.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokBarrageEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEndEvent;
|
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEndEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent;
|
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollStartEvent;
|
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollStartEvent;
|
||||||
@@ -39,10 +36,9 @@ import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent;
|
|||||||
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
|
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
|
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent;
|
import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Gift;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Text;
|
import io.github.jwdeveloper.tiktok.data.models.Text;
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler;
|
||||||
|
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.*;
|
import io.github.jwdeveloper.tiktok.messages.webcast.*;
|
||||||
import io.github.jwdeveloper.tiktok.models.SocialTypes;
|
import io.github.jwdeveloper.tiktok.models.SocialTypes;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@@ -50,19 +46,21 @@ import lombok.SneakyThrows;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
|
public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
|
||||||
private final TikTokGiftManager giftManager;
|
|
||||||
private final TikTokRoomInfo roomInfo;
|
private final TikTokRoomInfo roomInfo;
|
||||||
|
private final TikTokGiftEventHandler giftHandler;
|
||||||
private final Pattern socialMediaPattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share");
|
private final Pattern socialMediaPattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share");
|
||||||
|
|
||||||
public TikTokMessageHandlerRegistration(TikTokEventObserver tikTokEventHandler,
|
public TikTokMessageHandlerRegistration(TikTokEventObserver tikTokEventHandler,
|
||||||
TikTokGiftManager giftManager,
|
TikTokRoomInfo roomInfo,
|
||||||
TikTokRoomInfo roomInfo) {
|
TikTokGenericEventMapper genericTikTokEventMapper,
|
||||||
super(tikTokEventHandler);
|
TikTokGiftEventHandler tikTokGiftEventHandler) {
|
||||||
this.giftManager = giftManager;
|
super(tikTokEventHandler, genericTikTokEventMapper);
|
||||||
|
this.giftHandler = tikTokGiftEventHandler;
|
||||||
this.roomInfo = roomInfo;
|
this.roomInfo = roomInfo;
|
||||||
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() {
|
public void init() {
|
||||||
|
|
||||||
//ConnectionEvents events
|
//ConnectionEvents events
|
||||||
@@ -80,7 +78,7 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
|
|||||||
//User Interactions events
|
//User Interactions events
|
||||||
registerMapping(WebcastChatMessage.class, TikTokCommentEvent.class);
|
registerMapping(WebcastChatMessage.class, TikTokCommentEvent.class);
|
||||||
registerMapping(WebcastLikeMessage.class, this::handleLike);
|
registerMapping(WebcastLikeMessage.class, this::handleLike);
|
||||||
registerMapping(WebcastGiftMessage.class, this::handleGift);
|
registerMappings(WebcastGiftMessage.class, giftHandler::handleGift);
|
||||||
registerMapping(WebcastSocialMessage.class, this::handleSocialMedia);
|
registerMapping(WebcastSocialMessage.class, this::handleSocialMedia);
|
||||||
registerMapping(WebcastMemberMessage.class, this::handleMemberMessage);
|
registerMapping(WebcastMemberMessage.class, this::handleMemberMessage);
|
||||||
|
|
||||||
@@ -124,29 +122,6 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private TikTokEvent handleGift(byte[] msg) {
|
|
||||||
var giftMessage = WebcastGiftMessage.parseFrom(msg);
|
|
||||||
|
|
||||||
|
|
||||||
var gift = giftManager.findById((int) giftMessage.getGiftId());
|
|
||||||
if (gift == Gift.UNDEFINED) {
|
|
||||||
gift = giftManager.findByName(giftMessage.getGift().getName());
|
|
||||||
}
|
|
||||||
if (gift == Gift.UNDEFINED) {
|
|
||||||
gift = giftManager.registerGift(
|
|
||||||
(int) giftMessage.getGift().getId(),
|
|
||||||
giftMessage.getGift().getName(),
|
|
||||||
giftMessage.getGift().getDiamondCount(),
|
|
||||||
Picture.map(giftMessage.getGift().getImage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (giftMessage.getRepeatEnd() > 0) {
|
|
||||||
return new TikTokGiftComboEvent(gift, giftMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TikTokGiftEvent(gift, giftMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private TikTokEvent handleSocialMedia(byte[] msg) {
|
private TikTokEvent handleSocialMedia(byte[] msg) {
|
||||||
@@ -181,13 +156,13 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TikTokEvent handleRoomUserSeqMessage(byte[] msg) {
|
private TikTokEvent handleRoomUserSeqMessage(byte[] msg) {
|
||||||
var event = (TikTokRoomUserInfoEvent) mapMessageToEvent(WebcastRoomUserSeqMessage.class, TikTokRoomUserInfoEvent.class, msg);
|
var event = (TikTokRoomUserInfoEvent) mapper.mapToEvent(WebcastRoomUserSeqMessage.class, TikTokRoomUserInfoEvent.class, msg);
|
||||||
roomInfo.setViewersCount(event.getTotalUsers());
|
roomInfo.setViewersCount(event.getTotalUsers());
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TikTokEvent handleLike(byte[] msg) {
|
private TikTokEvent handleLike(byte[] msg) {
|
||||||
var event = (TikTokLikeEvent) mapMessageToEvent(WebcastLikeMessage.class, TikTokLikeEvent.class, msg);
|
var event = (TikTokLikeEvent) mapper.mapToEvent(WebcastLikeMessage.class, TikTokLikeEvent.class, msg);
|
||||||
roomInfo.setLikesCount(event.getTotalLikes());
|
roomInfo.setLikesCount(event.getTotalLikes());
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
@@ -200,7 +175,7 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
|
|||||||
return new TikTokRoomPinEvent(pinMessage, chatEvent);
|
return new TikTokRoomPinEvent(pinMessage, chatEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO check
|
//TODO Probably not working
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private TikTokEvent handlePollEvent(byte[] msg) {
|
private TikTokEvent handlePollEvent(byte[] msg) {
|
||||||
var poolMessage = WebcastPollMessage.parseFrom(msg);
|
var poolMessage = WebcastPollMessage.parseFrom(msg);
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.handlers.events;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
||||||
|
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||||
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TikTokGiftEventHandler {
|
||||||
|
private final GiftManager giftManager;
|
||||||
|
private final Map<Long, WebcastGiftMessage> giftsMessages;
|
||||||
|
|
||||||
|
public TikTokGiftEventHandler(GiftManager giftManager) {
|
||||||
|
this.giftManager = giftManager;
|
||||||
|
giftsMessages = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public List<TikTokEvent> handleGift(byte[] msg) {
|
||||||
|
var currentMessage = WebcastGiftMessage.parseFrom(msg);
|
||||||
|
var userId = currentMessage.getUser().getId();
|
||||||
|
var currentType = GiftSendType.fromNumber(currentMessage.getSendType());
|
||||||
|
var containsPreviousMessage = giftsMessages.containsKey(userId);
|
||||||
|
|
||||||
|
if (!containsPreviousMessage) {
|
||||||
|
if (currentType == GiftSendType.Finished) {
|
||||||
|
return List.of(getGiftEvent(currentMessage));
|
||||||
|
} else {
|
||||||
|
giftsMessages.put(userId, currentMessage);
|
||||||
|
return List.of(getGiftComboEvent(currentMessage, GiftSendType.Begin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousMessage = giftsMessages.get(userId);
|
||||||
|
var previousType = GiftSendType.fromNumber(previousMessage.getSendType());
|
||||||
|
if (currentType == GiftSendType.Active &&
|
||||||
|
previousType == GiftSendType.Active) {
|
||||||
|
giftsMessages.put(userId, currentMessage);
|
||||||
|
return List.of(getGiftComboEvent(currentMessage, GiftSendType.Active));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (currentType == GiftSendType.Finished &&
|
||||||
|
previousType == GiftSendType.Active) {
|
||||||
|
giftsMessages.clear();
|
||||||
|
return List.of(
|
||||||
|
getGiftComboEvent(currentMessage, GiftSendType.Finished),
|
||||||
|
getGiftEvent(currentMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TikTokGiftEvent getGiftEvent(WebcastGiftMessage message) {
|
||||||
|
var gift = getGiftObject(message);
|
||||||
|
return new TikTokGiftEvent(gift, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TikTokGiftEvent getGiftComboEvent(WebcastGiftMessage message, GiftSendType state) {
|
||||||
|
var gift = getGiftObject(message);
|
||||||
|
return new TikTokGiftComboEvent(gift, message, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Gift getGiftObject(WebcastGiftMessage giftMessage) {
|
||||||
|
var gift = giftManager.findById((int) giftMessage.getGiftId());
|
||||||
|
if (gift == Gift.UNDEFINED) {
|
||||||
|
gift = giftManager.findByName(giftMessage.getGift().getName());
|
||||||
|
}
|
||||||
|
if (gift == Gift.UNDEFINED) {
|
||||||
|
gift = giftManager.registerGift(
|
||||||
|
(int) giftMessage.getGift().getId(),
|
||||||
|
giftMessage.getGift().getName(),
|
||||||
|
giftMessage.getGift().getDiamondCount(),
|
||||||
|
Picture.map(giftMessage.getGift().getImage()));
|
||||||
|
}
|
||||||
|
return gift;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,12 +133,12 @@ public class TikTokHttpClient {
|
|||||||
private String getSignedUrl(String url, Map<String, Object> parameters) {
|
private String getSignedUrl(String url, Map<String, Object> parameters) {
|
||||||
|
|
||||||
var fullUrl = HttpUtils.parseParameters(url,parameters);
|
var fullUrl = HttpUtils.parseParameters(url,parameters);
|
||||||
var singHeaders = new TreeMap<String,Object>();
|
var signParams = new TreeMap<String,Object>();
|
||||||
singHeaders.put("client", "ttlive-java");
|
signParams.put("client", "ttlive-java");
|
||||||
singHeaders.put("uuc", 1);
|
signParams.put("uuc", 1);
|
||||||
singHeaders.put("url", fullUrl);
|
signParams.put("url", fullUrl);
|
||||||
|
|
||||||
var request = requestFactory.setQueries(singHeaders);
|
var request = requestFactory.setQueries(signParams);
|
||||||
var content = request.get(Constants.TIKTOK_SIGN_API);
|
var content = request.get(Constants.TIKTOK_SIGN_API);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.mappers;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokMessageMappingException;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goal of this class is to map ProtocolBuffer objects to TikTok Event in generic way
|
||||||
|
*
|
||||||
|
* First parameter is ProtocolBuffer class type
|
||||||
|
* Second parameters is TikTokEvent class type
|
||||||
|
* Third parameters is bytes payload
|
||||||
|
*
|
||||||
|
* mapToEvent(WebcastGiftMessage.class, TikTokGiftEvent.class, payload)
|
||||||
|
*
|
||||||
|
* How does it work?
|
||||||
|
* 1. Finds method `parseFrom(byte[] bytes)` inside ProtocolBuffer class
|
||||||
|
* 2. put payload to the method methods and create new instance of ProtcolBuffer object
|
||||||
|
* 3. Finds in TikTokEvent constructor that takes ProtocolBuffer type as parameter
|
||||||
|
* 4. create new Instance in TikTokEvents using object from step 2 and constructor from step 3
|
||||||
|
*
|
||||||
|
* methodCache and constructorCache are used to boost performance
|
||||||
|
*/
|
||||||
|
public class TikTokGenericEventMapper {
|
||||||
|
|
||||||
|
private record TypePair(Class<?> a, Class<?> b) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<Class<?>, Method> methodCache;
|
||||||
|
private final Map<TypePair, Constructor<?>> constructorCache;
|
||||||
|
|
||||||
|
public TikTokGenericEventMapper() {
|
||||||
|
this.methodCache = new HashMap<>();
|
||||||
|
this.constructorCache = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TikTokEvent mapToEvent(Class<?> inputClazz, Class<?> outputClass, byte[] payload) {
|
||||||
|
try {
|
||||||
|
var method = getParsingMethod(inputClazz);
|
||||||
|
var deserializedMessage = method.invoke(null, payload);
|
||||||
|
var constructor = getParsingConstructor(inputClazz, outputClass);
|
||||||
|
|
||||||
|
var tiktokEvent = constructor.newInstance(deserializedMessage);
|
||||||
|
return (TikTokEvent) tiktokEvent;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new TikTokMessageMappingException(inputClazz, outputClass, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method getParsingMethod(Class<?> input) throws NoSuchMethodException {
|
||||||
|
if (methodCache.containsKey(input)) {
|
||||||
|
return methodCache.get(input);
|
||||||
|
}
|
||||||
|
var method = input.getDeclaredMethod("parseFrom", byte[].class);
|
||||||
|
methodCache.put(input, method);
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Constructor<?> getParsingConstructor(Class<?> input, Class<?> output) {
|
||||||
|
var pair = new TypePair(input, output);
|
||||||
|
if (constructorCache.containsKey(pair)) {
|
||||||
|
return constructorCache.get(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
var optional = Arrays.stream(output.getConstructors())
|
||||||
|
.filter(ea -> Arrays.stream(ea.getParameterTypes())
|
||||||
|
.toList()
|
||||||
|
.contains(input))
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if (optional.isEmpty()) {
|
||||||
|
throw new TikTokMessageMappingException(input, output, "Unable to find constructor with input class type");
|
||||||
|
}
|
||||||
|
|
||||||
|
constructorCache.put(pair, optional.get());
|
||||||
|
return optional.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -125,7 +125,7 @@ public class TikTokWebSocketListener extends WebSocketClient {
|
|||||||
}
|
}
|
||||||
return Optional.of(websocketMessage);
|
return Optional.of(websocketMessage);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TikTokProtocolBufferException("Unable to parse WebcastWebsocketMessage", buffer, e);
|
throw new TikTokProtocolBufferException("Unable to parse WebcastPushFrame", buffer, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import java.util.Random;
|
|||||||
public class TikTokWebSocketPingingTask
|
public class TikTokWebSocketPingingTask
|
||||||
{
|
{
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
|
||||||
private boolean isRunning = false;
|
private boolean isRunning = false;
|
||||||
private final int MIN_TIMEOUT = 5;
|
private final int MIN_TIMEOUT = 5;
|
||||||
private final int MAX_TIMEOUT = 100;
|
private final int MAX_TIMEOUT = 100;
|
||||||
@@ -37,7 +36,7 @@ public class TikTokWebSocketPingingTask
|
|||||||
public void run(WebSocket webSocket)
|
public void run(WebSocket webSocket)
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
var thread = new Thread(() ->
|
thread = new Thread(() ->
|
||||||
{
|
{
|
||||||
pingTask(webSocket);
|
pingTask(webSocket);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.gifts;
|
package io.github.jwdeveloper.tiktok.gifts;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>TestApplication</artifactId>
|
<artifactId>Examples</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
|
|||||||
Binary file not shown.
@@ -22,8 +22,10 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.tools.collector;
|
package io.github.jwdeveloper.tiktok.tools.collector;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||||
import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessageCollectorClient;
|
import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessageCollectorClient;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public class RunCollector {
|
public class RunCollector {
|
||||||
@@ -33,15 +35,48 @@ public class RunCollector {
|
|||||||
|
|
||||||
//WebcastLinkMicBattleItemCard does streamer win battle?
|
//WebcastLinkMicBattleItemCard does streamer win battle?
|
||||||
|
|
||||||
public static void main(String[] args) throws SQLException {
|
public static void main(String[] args) throws SQLException, IOException {
|
||||||
|
|
||||||
TikTokMessageCollectorClient.create("messageCollector")
|
TikTokMessageCollectorClient.create("giftsCollector")
|
||||||
.addUser("bangbetmenygy")
|
.addUser("cbcgod")
|
||||||
.addUser("mr_cios")
|
// .addUser("mr_cios")
|
||||||
.addUser("sleepstreamxxx")
|
// .addUser("cbcgod")
|
||||||
.addUser("psychotropnazywo")
|
// .addUser("psychotropnazywo")
|
||||||
.addUser("accordionistka")
|
// .addUser("accordionistka")
|
||||||
|
.addEventFilter(WebcastGiftMessage.class)
|
||||||
|
.addOnBuilder(liveClientBuilder ->
|
||||||
|
{
|
||||||
|
liveClientBuilder.onGift((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
liveClientBuilder.onGiftCombo((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
liveClientBuilder.onGift((liveClient, event) ->
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("GIFT User: " + event.getUser().getDisplayName()+" ");
|
||||||
|
sb.append("Name: " + event.getGift().name() + " ");
|
||||||
|
sb.append("Combo: " + event.getCombo() + " ");
|
||||||
|
System.out.println(sb.toString());
|
||||||
|
});
|
||||||
|
liveClientBuilder.onGiftCombo((liveClient, event) ->
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("COMBO User: " + event.getUser().getDisplayName()+" ");
|
||||||
|
sb.append("Name: " + event.getGift().name() + " ");
|
||||||
|
sb.append("Combo: " + event.getCombo() + " ");
|
||||||
|
sb.append("Type: " + event.getComboState().name());
|
||||||
|
System.out.println(sb.toString());
|
||||||
|
});
|
||||||
|
})
|
||||||
.buildAndRun();
|
.buildAndRun();
|
||||||
|
|
||||||
|
System.in.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class MessageCollector {
|
|||||||
|
|
||||||
var queue = messages.get(name);
|
var queue = messages.get(name);
|
||||||
if (queue.size() > limit) {
|
if (queue.size() > limit) {
|
||||||
queue.poll();
|
queue.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.add(new MessageData(base64, host, LocalDateTime.now().toString()));
|
queue.add(new MessageData(base64, host, LocalDateTime.now().toString()));
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokMessageModel;
|
|||||||
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokResponseModel;
|
import io.github.jwdeveloper.tiktok.tools.collector.tables.TikTokResponseModel;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@@ -46,8 +47,9 @@ public class TikTokClientFactory {
|
|||||||
this.tikTokDatabase = tikTokDatabase;
|
this.tikTokDatabase = tikTokDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<LiveClient> runClientAsync(String tiktokUser, Consumer<LiveClientBuilder> onBuilder) {
|
public CompletableFuture<LiveClient> runClientAsync(String tiktokUser, List<Class<?>> filters, Consumer<LiveClientBuilder> onBuilder) {
|
||||||
var builder = TikTokLive.newClient(tiktokUser);
|
var builder = TikTokLive.newClient(tiktokUser);
|
||||||
|
var msgFilter = filters.stream().map(Class::getSimpleName).toList();
|
||||||
onBuilder.accept(builder);
|
onBuilder.accept(builder);
|
||||||
return builder.onConnected((liveClient, event) ->
|
return builder.onConnected((liveClient, event) ->
|
||||||
{
|
{
|
||||||
@@ -62,7 +64,12 @@ public class TikTokClientFactory {
|
|||||||
responseModel.setHostName(liveClient.getRoomInfo().getHostName());
|
responseModel.setHostName(liveClient.getRoomInfo().getHostName());
|
||||||
tikTokDatabase.insertResponse(responseModel);
|
tikTokDatabase.insertResponse(responseModel);
|
||||||
liveClient.getLogger().info("Response");
|
liveClient.getLogger().info("Response");
|
||||||
for (var message : event.getResponse().getMessagesList()) {
|
for (var message : event.getResponse().getMessagesList())
|
||||||
|
{
|
||||||
|
if(msgFilter.size() > 0 && !msgFilter.contains(message.getMethod()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
messageCollector.addMessage(liveClient.getLogger(), liveClient.getRoomInfo().getHostName(), message);
|
messageCollector.addMessage(liveClient.getLogger(), liveClient.getRoomInfo().getHostName(), message);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -71,7 +78,7 @@ public class TikTokClientFactory {
|
|||||||
var eventName = event.getEvent().getClass().getSimpleName();
|
var eventName = event.getEvent().getClass().getSimpleName();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (filter.size() != 0 && !filter.contains(event.getEvent().getClass())) {
|
if (msgFilter.size() != 0 && !msgFilter.contains(event.getEvent().getClass())) {
|
||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
@@ -83,7 +90,7 @@ public class TikTokClientFactory {
|
|||||||
model.setMessage(messageBinary);
|
model.setMessage(messageBinary);
|
||||||
|
|
||||||
// tikTokDatabase.insertMessage(model);
|
// tikTokDatabase.insertMessage(model);
|
||||||
liveClient.getLogger().info("EVENT: [" + tiktokUser + "] " + eventName);
|
// liveClient.getLogger().info("EVENT: [" + tiktokUser + "] " + eventName);
|
||||||
})
|
})
|
||||||
.onError((liveClient, event) ->
|
.onError((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class TikTokMessagessCollectorBuilder {
|
|||||||
db.init();
|
db.init();
|
||||||
var factory = new TikTokClientFactory(messageCollector, db);
|
var factory = new TikTokClientFactory(messageCollector, db);
|
||||||
for (var user : users) {
|
for (var user : users) {
|
||||||
var client = factory.runClientAsync(user, onBuilder);
|
var client = factory.runClientAsync(user,filters, onBuilder);
|
||||||
client.thenAccept(liveClient ->
|
client.thenAccept(liveClient ->
|
||||||
{
|
{
|
||||||
tiktokclients.add(liveClient);
|
tiktokclients.add(liveClient);
|
||||||
|
|||||||
@@ -47,7 +47,15 @@ public class RunJsonTester {
|
|||||||
sb.append(MessageUtil.getContent(event.getData()));
|
sb.append(MessageUtil.getContent(event.getData()));
|
||||||
|
|
||||||
|
|
||||||
liveClient.getLogger().info(sb.toString());
|
// liveClient.getLogger().info(sb.toString());
|
||||||
|
}).
|
||||||
|
onGift((liveClient, event) ->
|
||||||
|
{
|
||||||
|
liveClient.getLogger().info("Gift event: "+event.toJson());
|
||||||
|
})
|
||||||
|
.onGiftCombo((liveClient, event) ->
|
||||||
|
{
|
||||||
|
liveClient.getLogger().info("GiftCombo event"+event.toJson());
|
||||||
})
|
})
|
||||||
.onError((liveClient, event) ->
|
.onError((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|||||||
Binary file not shown.
@@ -45,6 +45,7 @@ public class Main {
|
|||||||
app.get("/tiktok/connect", handler::connect);
|
app.get("/tiktok/connect", handler::connect);
|
||||||
app.get("/tiktok/disconnect", handler::disconnect);
|
app.get("/tiktok/disconnect", handler::disconnect);
|
||||||
app.get("/tiktok/events", handler::events);
|
app.get("/tiktok/events", handler::events);
|
||||||
|
app.get("/tiktok/events/pages", handler::eventPages);
|
||||||
app.get("/tiktok/events/message", handler::eventMessage);
|
app.get("/tiktok/events/message", handler::eventMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
package io.github.jwdeveloper.tiktok.webviewer;
|
package io.github.jwdeveloper.tiktok.webviewer;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
|
||||||
import io.github.jwdeveloper.tiktok.tools.collector.client.MessageCollector;
|
import io.github.jwdeveloper.tiktok.tools.collector.client.MessageCollector;
|
||||||
import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessageCollectorClient;
|
import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessageCollectorClient;
|
||||||
import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessagessCollectorBuilder;
|
import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessagessCollectorBuilder;
|
||||||
@@ -34,6 +33,7 @@ import java.sql.SQLException;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class TikTokManager {
|
public class TikTokManager {
|
||||||
TikTokMessagessCollectorBuilder client;
|
TikTokMessagessCollectorBuilder client;
|
||||||
@@ -45,7 +45,17 @@ public class TikTokManager {
|
|||||||
|
|
||||||
public void connect(String name) throws SQLException {
|
public void connect(String name) throws SQLException {
|
||||||
disconnect();
|
disconnect();
|
||||||
client = TikTokMessageCollectorClient.create(msgCollector, "web").addUser(name);
|
client = TikTokMessageCollectorClient.create(msgCollector, "web")
|
||||||
|
.addOnBuilder(liveClientBuilder ->
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
liveClientBuilder.onGift((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.addUser(name);
|
||||||
client.buildAndRun();
|
client.buildAndRun();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,19 +68,49 @@ public class TikTokManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public MessageDto getMessage(String event) throws InvalidProtocolBufferException {
|
public MessageDto getMessage(String event, String index) throws InvalidProtocolBufferException {
|
||||||
var eventData = msgCollector.getMessages().get(event);
|
var eventData = msgCollector.getMessages().get(event);
|
||||||
var messages = eventData.stream().toList();
|
var messages = eventData.stream().toList();
|
||||||
var random = new Random();
|
var random = new Random();
|
||||||
var index = random.nextInt(messages.size()-1);
|
|
||||||
var msg = messages.get(index);
|
var msgIndex = 0;
|
||||||
|
if (index != null && !index.isEmpty()) {
|
||||||
|
msgIndex = Integer.parseInt(index);
|
||||||
|
msgIndex = Math.min(msgIndex, messages.size() - 1);
|
||||||
|
msgIndex = Math.max(msgIndex, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var msg = messages.get(msgIndex);
|
||||||
|
|
||||||
|
|
||||||
var bytes = Base64.getDecoder().decode(msg.getEventData());
|
var bytes = Base64.getDecoder().decode(msg.getEventData());
|
||||||
var content = MessageUtil.getContent(event,bytes);
|
var content = MessageUtil.getContent(event, bytes);
|
||||||
return new MessageDto(content, msg.getEventData(), event);
|
return new MessageDto(content, msg.getEventData(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PagesDto getPages(String event) throws InvalidProtocolBufferException {
|
||||||
|
var eventData = msgCollector.getMessages().get(event);
|
||||||
|
var messages = eventData.stream().toList();
|
||||||
|
|
||||||
|
var counter = new AtomicInteger(-1);
|
||||||
|
var pages = messages.stream().map(e ->
|
||||||
|
{
|
||||||
|
return "http://localhost:8001/tiktok/events/message?eventName=" + event + "&page=" + counter.incrementAndGet();
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
return new PagesDto(event, messages.size(), pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public class PagesDto {
|
||||||
|
String eventName;
|
||||||
|
int pages;
|
||||||
|
List<String> links;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
public class MessageDto {
|
public class MessageDto {
|
||||||
String content;
|
String content;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
package io.github.jwdeveloper.tiktok.webviewer.handlers;
|
package io.github.jwdeveloper.tiktok.webviewer.handlers;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import io.github.jwdeveloper.tiktok.webviewer.TikTokManager;
|
import io.github.jwdeveloper.tiktok.webviewer.TikTokManager;
|
||||||
import io.javalin.http.Context;
|
import io.javalin.http.Context;
|
||||||
@@ -55,7 +56,7 @@ public class TikTokHandler {
|
|||||||
|
|
||||||
public void events(Context context) throws SQLException {
|
public void events(Context context) throws SQLException {
|
||||||
var events = tikTokManager.getEventsNames();
|
var events = tikTokManager.getEventsNames();
|
||||||
var gson = new Gson();
|
var gson = getGson();
|
||||||
var result = gson.toJson(events);
|
var result = gson.toJson(events);
|
||||||
context.result(result);
|
context.result(result);
|
||||||
context.status(200);
|
context.status(200);
|
||||||
@@ -63,8 +64,22 @@ public class TikTokHandler {
|
|||||||
|
|
||||||
public void eventMessage(Context context) throws InvalidProtocolBufferException {
|
public void eventMessage(Context context) throws InvalidProtocolBufferException {
|
||||||
String name = context.queryParam("eventName");
|
String name = context.queryParam("eventName");
|
||||||
var result = tikTokManager.getMessage(name);
|
String page = context.queryParam("page");
|
||||||
var gson = new Gson();
|
|
||||||
|
var result = tikTokManager.getMessage(name, page);
|
||||||
|
var gson = getGson();
|
||||||
context.result(gson.toJson(result));
|
context.result(gson.toJson(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void eventPages(Context context) throws InvalidProtocolBufferException {
|
||||||
|
String name = context.queryParam("eventName");
|
||||||
|
var result = tikTokManager.getPages(name);
|
||||||
|
var gson = getGson();
|
||||||
|
context.result(gson.toJson(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Gson getGson() {
|
||||||
|
return new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,10 @@
|
|||||||
language: 'json',
|
language: 'json',
|
||||||
theme: 'vs-dark'
|
theme: 'vs-dark'
|
||||||
});
|
});
|
||||||
|
editor.onDidChangeModelContent(function () {
|
||||||
|
console.log("hello")
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -130,11 +134,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-10 editor-container ">
|
<div class="col-md-10 editor-container ">
|
||||||
<nav aria-label="Page navigation example">
|
<nav aria-label="Page navigation example">
|
||||||
<ul class="pagination">
|
<ul id="pages" class="pagination">
|
||||||
<li class="page-item btn-primary"><a class="page-link" href="#">Previous</a></li>
|
<li class="page-item btn-primary"><a class="page-link" href="#">Previous</a></li>
|
||||||
<li class="page-item"><a class="page-link" href="#">1</a></li>
|
|
||||||
<li class="page-item"><a class="page-link" href="#">2</a></li>
|
|
||||||
<li class="page-item"><a class="page-link" href="#">3</a></li>
|
|
||||||
<li class="page-item"><a class="page-link" href="#">Next</a></li>
|
<li class="page-item"><a class="page-link" href="#">Next</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -156,10 +157,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
async function connect() {
|
async function connect() {
|
||||||
let name = document.getElementById('name').value;
|
let name = document.getElementById('name').value;
|
||||||
name = "bangbetmenygy"
|
// name = "bangbetmenygy"
|
||||||
let response = await fetch(`http://localhost:8001/tiktok/connect?name=${name}`);
|
let response = await fetch(`http://localhost:8001/tiktok/connect?name=${name}`);
|
||||||
let greeting = await response.text();
|
let greeting = await response.text();
|
||||||
console.log("connect",greeting);
|
console.log("connect",greeting);
|
||||||
@@ -176,13 +176,39 @@
|
|||||||
{
|
{
|
||||||
let response = await fetch(`http://localhost:8001/tiktok/events/message?eventName=${event}`);
|
let response = await fetch(`http://localhost:8001/tiktok/events/message?eventName=${event}`);
|
||||||
let json = await response.text();
|
let json = await response.text();
|
||||||
// json = json.replace(/\/n/g, "\n");
|
|
||||||
let root= JSON.parse(json);
|
let root= JSON.parse(json);
|
||||||
console.log(root)
|
|
||||||
|
|
||||||
editor.setValue(root.content);
|
editor.setValue(root.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadMessageLink(link)
|
||||||
|
{
|
||||||
|
let response = await fetch(link);
|
||||||
|
let json = await response.text();
|
||||||
|
let root= JSON.parse(json);
|
||||||
|
editor.setValue(root.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPagination(event)
|
||||||
|
{
|
||||||
|
let response = await fetch(`http://localhost:8001/tiktok/events/pages?eventName=${event}`);
|
||||||
|
let json = await response.text();
|
||||||
|
let object = JSON.parse(json);
|
||||||
|
let pages = object.links;
|
||||||
|
console.log("PAGES: ",pages)
|
||||||
|
$("#pages").empty();
|
||||||
|
$.each(pages, function(index, element) {
|
||||||
|
let content = $('<button>',{
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
text: index
|
||||||
|
}).click(async function()
|
||||||
|
{
|
||||||
|
await loadMessageLink(element);
|
||||||
|
console.log(editor)
|
||||||
|
});
|
||||||
|
$("#pages").append(content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function showEvents() {
|
async function showEvents() {
|
||||||
let response = await fetch(`http://localhost:8001/tiktok/events`);
|
let response = await fetch(`http://localhost:8001/tiktok/events`);
|
||||||
let json = await response.text();
|
let json = await response.text();
|
||||||
@@ -197,6 +223,7 @@
|
|||||||
}).click(async function()
|
}).click(async function()
|
||||||
{
|
{
|
||||||
await loadMessage(event);
|
await loadMessage(event);
|
||||||
|
await loadPagination(event);
|
||||||
});
|
});
|
||||||
$("#eventList").append(listItem);
|
$("#eventList").append(listItem);
|
||||||
});
|
});
|
||||||
@@ -213,6 +240,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var connected = false;
|
var connected = false;
|
||||||
|
var paginationIndex = 0;
|
||||||
|
var maxPages = 10;
|
||||||
|
var pages = [];
|
||||||
setInterval(loop, 1000)
|
setInterval(loop, 1000)
|
||||||
showEvents()
|
showEvents()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class CodeExample
|
||||||
|
{
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
TikTokLive.newClient("mrBeast")
|
||||||
|
.onGift((liveClient, event) ->
|
||||||
|
{
|
||||||
|
System.out.println("Thank you for Money!");
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void codeExample()
|
||||||
|
{
|
||||||
|
TikTokLive.newClient("bangbetmenygy")
|
||||||
|
.onGift((liveClient, event) ->
|
||||||
|
{
|
||||||
|
if(event.getGift() == Gift.ROSE)
|
||||||
|
{
|
||||||
|
liveClient.getLogger().info("Rose from "+event.getUser().getDisplayName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
liveClient.getLogger().info("Thank you for "+event.getGift().getName());
|
||||||
|
})
|
||||||
|
.onJoin((liveClient, event) ->
|
||||||
|
{
|
||||||
|
liveClient.getLogger().info("Hello "+event.getUser().getDisplayName());
|
||||||
|
})
|
||||||
|
.onConnected((liveClient, event) ->
|
||||||
|
{
|
||||||
|
liveClient.getLogger().info("Connected to live ");
|
||||||
|
})
|
||||||
|
.onError((liveClient, event) ->
|
||||||
|
{
|
||||||
|
liveClient.getLogger().info("ERROR! "+event.getException().getMessage());
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void configExample()
|
||||||
|
{
|
||||||
|
TikTokLive.newClient("bangbetmenygy")
|
||||||
|
.configure((settings) ->
|
||||||
|
{
|
||||||
|
settings.setHostName("bangbetmenygy"); // This method is useful in case you want change hostname later
|
||||||
|
settings.setClientLanguage("en"); // Language
|
||||||
|
settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
|
||||||
|
settings.setLogLevel(Level.ALL); // Log level
|
||||||
|
settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
|
||||||
|
settings.setHandleExistingEvents(true); // Invokes all TikTok events that had occurred before connection
|
||||||
|
settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
|
||||||
|
settings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
|
||||||
|
|
||||||
|
//Optional: Sometimes not every message from chat are send to TikTokLiveJava to fix this issue you can set sessionId
|
||||||
|
// documentation how to obtain sessionId https://github.com/isaackogan/TikTok-Live-Connector#send-chat-messages
|
||||||
|
settings.setSessionId("86c3c8bf4b17ebb2d74bb7fa66fd0000");
|
||||||
|
|
||||||
|
//Optional:
|
||||||
|
//RoomId can be used as an override if you're having issues with HostId.
|
||||||
|
//You can find it in the HTML for the livestream-page
|
||||||
|
settings.setRoomId("XXXXXXXXXXXXXXXXX");
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -86,7 +86,7 @@ public class EventsInfoGenerator {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### {{method-name}} [{{event-name}}](https://github.com/jwdeveloper/TikTok-Live-Java/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/events/messages.java)
|
## {{method-name}} [{{event-name}}](https://github.com/jwdeveloper/TikTok-Live-Java/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/events/messages.java)
|
||||||
|
|
||||||
{{content}}
|
{{content}}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,35 @@
|
|||||||
[](https://jitpack.io/#jwdeveloper/TikTok-Live-Java)
|
<div align="center" >
|
||||||
|
<a target="blank" >
|
||||||
|
<img src="https://raw.githubusercontent.com/jwdeveloper/TikTokLiveJava/develop-1_0_0/Tools-ReadmeGenerator/src/main/resources/logo.svg" width="15%" >
|
||||||
|
</img>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div align="center" >
|
||||||
|
<h1>TikTok Live Java</h1>
|
||||||
|
|
||||||
|
❤️❤️🎁 *Connect to TikTok live in 3 lines* 🎁❤️❤️
|
||||||
|
|
||||||
|
<div align="center" >
|
||||||
|
<a href="https://jitpack.io/#jwdeveloper/TikTok-Live-Java" target="blank" >
|
||||||
|
<img src="https://jitpack.io/v/jwdeveloper/TikTok-Live-Java.svg" width="20%" >
|
||||||
|
</img>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
# TikTokLive Java
|
<a href="https://discord.gg/e2XwPNTBBr" target="blank" >
|
||||||
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.
|
<img src="https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white" >
|
||||||
|
</img>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a target="blank" >
|
||||||
|
<img src="https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white" >
|
||||||
|
</img>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
A Java library inspired by [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 library 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.
|
The library 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. 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.
|
No credentials are required. 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.
|
||||||
|
|
||||||
@@ -42,11 +69,6 @@ Do you prefer other programming languages?
|
|||||||
<version>{{version}}</version>
|
<version>{{version}}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<version>2.10.1</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -59,7 +81,6 @@ Do you prefer other programming languages?
|
|||||||
|
|
||||||
{{events-content}}
|
{{events-content}}
|
||||||
|
|
||||||
<br>
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Listener Example
|
## Listener Example
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
|||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
|
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
|
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
|
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
|
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
|
||||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||||
|
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
import io.github.jwdeveloper.tiktok.mockClient.mocks.ApiServiceMock;
|
import io.github.jwdeveloper.tiktok.mockClient.mocks.ApiServiceMock;
|
||||||
import io.github.jwdeveloper.tiktok.mockClient.mocks.LiveClientMock;
|
import io.github.jwdeveloper.tiktok.mockClient.mocks.LiveClientMock;
|
||||||
@@ -97,8 +98,9 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder {
|
|||||||
var apiClient = new TikTokHttpClient(cookie, requestFactory);
|
var apiClient = new TikTokHttpClient(cookie, requestFactory);
|
||||||
var apiService = new ApiServiceMock(apiClient, logger, clientSettings);
|
var apiService = new ApiServiceMock(apiClient, logger, clientSettings);
|
||||||
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler,
|
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler,
|
||||||
giftManager,
|
tiktokRoomInfo,
|
||||||
tiktokRoomInfo);
|
new TikTokGenericEventMapper(),
|
||||||
|
new TikTokGiftEventHandler(giftManager));
|
||||||
var webSocketClient = new WebsocketClientMock(logger, responses, webResponseHandler);
|
var webSocketClient = new WebsocketClientMock(logger, responses, webResponseHandler);
|
||||||
|
|
||||||
return new LiveClientMock(tiktokRoomInfo,
|
return new LiveClientMock(tiktokRoomInfo,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.mockClient.mocks;
|
package io.github.jwdeveloper.tiktok.mockClient.mocks;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
||||||
@@ -102,7 +103,11 @@ public class WebsocketClientMock implements SocketClient {
|
|||||||
if (!messages.isEmpty()) {
|
if (!messages.isEmpty()) {
|
||||||
var messageStr = messages.pop();
|
var messageStr = messages.pop();
|
||||||
try {
|
try {
|
||||||
messageHandler.handleSingleMessage(tikTokLiveClient, messageStr.getMessageType(), messageStr.getMessageValue());
|
var msg = WebcastResponse.Message.newBuilder()
|
||||||
|
.setMethod(messageStr.messageType)
|
||||||
|
.setPayload(ByteString.copyFrom(messageStr.getMessageValue()))
|
||||||
|
.build();
|
||||||
|
messageHandler.handleSingleMessage(tikTokLiveClient, msg);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.info("Unable to parse message for response " + messageStr.getMessageType());
|
logger.info("Unable to parse message for response " + messageStr.getMessageType());
|
||||||
throw new TikTokLiveException(e);
|
throw new TikTokLiveException(e);
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
15
pom.xml
15
pom.xml
@@ -69,20 +69,7 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
|
||||||
<version>3.3.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>attach-javadocs</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>jar</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user