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:
JW
2023-10-11 01:20:07 +02:00
parent 043dfe95d8
commit de27e71e93
64 changed files with 548 additions and 630 deletions

View File

@@ -25,5 +25,5 @@ package io.github.jwdeveloper.tiktok.annotations;
public enum EventType
{
Control, Message, Custom, Debug
Control, Message, Debug
}

View File

@@ -29,6 +29,6 @@ import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
/**
* Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
*/
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokLiveEndedEvent extends TikTokEvent {
}

View File

@@ -26,6 +26,6 @@ import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokLivePausedEvent extends TikTokEvent {
}

View File

@@ -33,7 +33,7 @@ import lombok.Getter;
* Triggers when a user creates a subscription.
*/
@Getter
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokSubscribeEvent extends TikTokHeaderEvent {
private User user;

View File

@@ -29,7 +29,7 @@ import io.github.jwdeveloper.tiktok.messages.webcast.WebcastControlMessage;
import lombok.Getter;
@Getter
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokUnhandledControlEvent extends TikTokUnhandledEvent<WebcastControlMessage> {
public TikTokUnhandledControlEvent(WebcastControlMessage data) {

View File

@@ -29,7 +29,7 @@ import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage;
import lombok.Getter;
@Getter
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokUnhandledSocialEvent extends TikTokUnhandledEvent<WebcastSocialMessage>
{
public TikTokUnhandledSocialEvent(WebcastSocialMessage data) {

View File

@@ -29,12 +29,11 @@ import io.github.jwdeveloper.tiktok.data.models.Gift;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import lombok.Getter;
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
@Getter
public class TikTokGiftComboFinishedEvent extends TikTokGiftEvent
public class TikTokGiftComboEvent extends TikTokGiftEvent
{
public TikTokGiftComboFinishedEvent(Gift gift, WebcastGiftMessage msg) {
public TikTokGiftComboEvent(Gift gift, WebcastGiftMessage msg) {
super(gift, msg);
}
}

View File

@@ -32,7 +32,7 @@ import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import lombok.Getter;
/**
/*
* Triggered every time a gift arrives.
*/
@EventMeta(eventType = EventType.Message)

View File

@@ -31,7 +31,7 @@ import lombok.Getter;
import java.util.List;
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
@Getter
public class TikTokPollEndEvent extends TikTokPollEvent
{

View File

@@ -32,7 +32,7 @@ import lombok.Getter;
import java.util.List;
@Getter
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokPollStartEvent extends TikTokPollEvent {
private final List<PollOption> options;

View File

@@ -31,7 +31,7 @@ import lombok.Getter;
import java.util.List;
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
@Getter
public class TikTokPollUpdateEvent extends TikTokPollEvent {
private final List<PollOption> options;

View File

@@ -33,7 +33,7 @@ import lombok.Value;
* Triggers when a user follows the streamer. Based on social event.
*/
@Value
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokFollowEvent extends TikTokHeaderEvent
{
User user;

View File

@@ -31,7 +31,7 @@ import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage;
import lombok.Getter;
@Getter
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokJoinEvent extends TikTokHeaderEvent {
private final User user;
private final int totalUsers;

View File

@@ -35,7 +35,7 @@ import lombok.Getter;
* Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
*/
@Getter
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokLikeEvent extends TikTokHeaderEvent
{
private final User user;

View File

@@ -33,7 +33,7 @@ import lombok.Getter;
* Triggers when a user shares the stream. Based on social event.
*/
@Getter
@EventMeta(eventType = EventType.Custom)
@EventMeta(eventType = EventType.Message)
public class TikTokShareEvent extends TikTokHeaderEvent {
private final User user;
private final int totalShares;

View File

@@ -34,8 +34,7 @@ import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.CompletableFuture;
public class Picture {
@@ -67,21 +66,20 @@ public class Picture {
if (isDownloaded()) {
return image;
}
if (link.equalsIgnoreCase("")) {
return null;
}
image = download(link);
return image;
}
public Future<Image> downloadImageAsync() {
var executor = Executors.newSingleThreadExecutor();
var future = executor.submit(this::downloadImage);
executor.shutdown();
return future;
public CompletableFuture<Image> downloadImageAsync() {
return CompletableFuture.supplyAsync(this::downloadImage);
}
private BufferedImage download(String urlString) {
private BufferedImage download(String urlString)
{
if(urlString.isEmpty())
{
return null;
}
var baos = new ByteArrayOutputStream();
try (var is = new URL(urlString).openStream()) {
var byteChunk = new byte[4096];
@@ -105,8 +103,4 @@ public class Picture {
public static Picture Empty() {
return new Picture("");
}
public static List<Picture> EmptyList() {
return new ArrayList<Picture>();
}
}

View File

@@ -27,18 +27,22 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
@Getter
public class Text {
String key;
String pattern;
List<TextPiece> textPieces;
String value;
public Text(String key, String pattern, List<TextPiece> textPieces) {
this.key = key;
this.pattern = pattern;
this.textPieces = textPieces;
this.value = computeValue();
}
public static Text map(io.github.jwdeveloper.tiktok.messages.data.Text input) {
@@ -53,15 +57,31 @@ public class Text {
var user = User.map(input.getUserValue().getUser());
yield new UserTextPiece(user);
}
//case 12 -> new GiftTextPiece(input.getStringValue());
case 12 -> new GiftTextPiece(input.getGiftValue().getGiftId());
default -> new StringTextPiece(input.getStringValue());
};
}
private String computeValue() {
var regexPattern = Pattern.compile("\\{.*?\\}");
var matcher = regexPattern.matcher(pattern);
var format = matcher.replaceAll("%s");
var output = new ArrayList<String>();
for (var piece : textPieces) {
output.add(piece.getText());
}
return String.format(format, output.toArray());
}
@Getter
@AllArgsConstructor
public static class TextPiece {
public String getText() {
return "";
}
}
@Value
@@ -79,13 +99,25 @@ public class Text {
public UserTextPiece(User user) {
this.user = user;
}
@Override
public String getText() {
return user.getDisplayName();
}
}
public static class GiftTextPiece extends TextPiece {
Gift gift;
public GiftTextPiece(String value) {
int giftId;
public GiftTextPiece(int giftId) {
this.giftId = giftId;
}
@Override
public String getText() {
return giftId + "";
}
}
}

View File

@@ -25,6 +25,8 @@ package io.github.jwdeveloper.tiktok.live;
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.logging.Logger;
public interface LiveClient {
@@ -34,6 +36,19 @@ public interface LiveClient {
*/
void connect();
/**
* Connects in asynchronous way
* When connected Consumer returns instance of LiveClient
*/
void connectAsync(Consumer<LiveClient> onConnection);
/**
* Connects in asynchronous way
*/
CompletableFuture<LiveClient> connectAsync();
/**
* Disconnects the connection.
*/
@@ -45,12 +60,6 @@ public interface LiveClient {
*/
GiftManager getGiftManager();
/**
* Get user manager
* @return
*/
UserManager getUserManager();
/**
* Gets the current room info from TikTok API including streamer info, room status and statistics.
*/
@@ -58,13 +67,13 @@ public interface LiveClient {
/**
* Manage TikTokEventListener
*
* @see TikTokEventListener
*/
ListenersManager getListenersManager();
/**
* Logger
* @return
*/
Logger getLogger();
}

View File

@@ -26,15 +26,17 @@ import lombok.Data;
@Data
public class LiveRoomMeta {
/**
* 0 - Unknown
* 1 - ?
* 2 - Online
* 3 - ?
* 4 - Offline
*/
private int status;
private LiveRoomStatus status;
private boolean ageRestricted;
public enum LiveRoomStatus
{
HostNotFound,
HostOnline,
HostOffline
}
}

View File

@@ -24,7 +24,7 @@ package io.github.jwdeveloper.tiktok.live.builder;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboFinishedEvent;
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.room.TikTokRoomEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent;
@@ -37,7 +37,7 @@ import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketRespons
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
public interface EventBuilder<T> {
public interface EventsBuilder<T> {
T onRoom(EventConsumer<TikTokRoomEvent> event);
@@ -45,15 +45,13 @@ public interface EventBuilder<T> {
T onComment(EventConsumer<TikTokCommentEvent> event);
T onWebsocketMessage(EventConsumer<TikTokWebsocketMessageEvent> event);
T onWebsocketResponse(EventConsumer<TikTokWebsocketResponseEvent> event);
T onWebsocketUnhandledMessage(EventConsumer<TikTokWebsocketUnhandledMessageEvent> event);
T onGiftCombo(EventConsumer<TikTokGiftComboFinishedEvent> event);
T onGiftCombo(EventConsumer<TikTokGiftComboEvent> event);
T onGift(EventConsumer<TikTokGiftEvent> event);

View File

@@ -1,3 +1,25 @@
/*
* 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.live.builder;
import io.github.jwdeveloper.tiktok.ClientSettings;
@@ -7,7 +29,7 @@ import io.github.jwdeveloper.tiktok.live.LiveClient;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public interface LiveClientBuilder extends EventBuilder<LiveClientBuilder> {
public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
LiveClientBuilder configure(Consumer<ClientSettings> consumer);
LiveClientBuilder addListener(TikTokEventListener listener);

View File

@@ -66,7 +66,7 @@ public class JsonUtil {
}
if (fieldAttributes.getName().equals("common_")) {
return true;
// return true;
}
if (fieldAttributes.getName().equals("bytes")) {
return true;

View File

@@ -82,7 +82,7 @@ message Text {
}
message TextPieceGift {
int64 giftId = 1;
int32 giftId = 1;
// PatternRef nameRef = 2;
// ShowType showType = 3; // Enum
int64 colorId = 4;

View File

@@ -32,13 +32,15 @@ import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
import io.github.jwdeveloper.tiktok.live.UserManager;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import io.github.jwdeveloper.tiktok.websocket.SocketClient;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.logging.Logger;
public class TikTokLiveClient implements LiveClient {
@@ -70,22 +72,37 @@ public class TikTokLiveClient implements LiveClient {
}
public void connectAsync(Consumer<LiveClient> onConnection) {
CompletableFuture.supplyAsync(() ->
{
connect();
onConnection.accept(this);
return this;
});
}
public CompletableFuture<LiveClient> connectAsync() {
return CompletableFuture.supplyAsync(() ->
{
connect();
return this;
});
}
public void connect() {
try {
tryConnect();
}
catch (TikTokLiveException e)
} catch (TikTokLiveException e)
{
setState(ConnectionState.DISCONNECTED);
tikTokEventHandler.publish(this, new TikTokErrorEvent(e));
tikTokEventHandler.publish(this, new TikTokDisconnectedEvent());
if(e instanceof TikTokLiveOfflineHostException && clientSettings.isRetryOnConnectionFailure())
{
if (e instanceof TikTokLiveOfflineHostException && clientSettings.isRetryOnConnectionFailure()) {
try {
Thread.sleep(clientSettings.getRetryConnectionTimeout().toMillis());
} catch (Exception ignored) {
}
catch (Exception ignored){}
logger.info("Reconnecting");
tikTokEventHandler.publish(this, new TikTokReconnectingEvent());
this.connect();
@@ -108,27 +125,22 @@ public class TikTokLiveClient implements LiveClient {
if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTING))
throw new TikTokLiveException("Already connecting");
logger.info("Connecting");
setState(ConnectionState.CONNECTING);
apiService.updateSessionId();
if(clientSettings.getRoomId() != null)
{
if (clientSettings.getRoomId() != null) {
liveRoomInfo.setRoomId(clientSettings.getRoomId());
logger.info("Using roomID from settings: " + clientSettings.getRoomId());
}
else
{
} else {
var roomId = apiService.fetchRoomId(liveRoomInfo.getHostName());
liveRoomInfo.setRoomId(roomId);
}
var roomData = apiService.fetchRoomInfo();
if (roomData.getStatus() == 0 || roomData.getStatus() == 4) {
throw new TikTokLiveOfflineHostException("LiveStream for HostID could not be found. Is the Host online?");
if (roomData.getStatus() != LiveRoomMeta.LiveRoomStatus.HostOnline) {
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found. Is the Host online?");
}
var clientData = apiService.fetchClientData();
@@ -140,9 +152,9 @@ public class TikTokLiveClient implements LiveClient {
public LiveRoomInfo getRoomInfo() {
return liveRoomInfo;
}
@Override
public ListenersManager getListenersManager()
{
public ListenersManager getListenersManager() {
return listenersManager;
}
@@ -156,11 +168,6 @@ public class TikTokLiveClient implements LiveClient {
return tikTokGiftManager;
}
@Override
public UserManager getUserManager() {
return null;
}
private void setState(ConnectionState connectionState) {
logger.info("TikTokLive client state: " + connectionState.name());

View File

@@ -22,26 +22,21 @@
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.live.builder.EventBuilder;
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboFinishedEvent;
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.TikTokPollEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomPinEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent;
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.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokBarrageEvent;
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
@@ -53,6 +48,7 @@ import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
@@ -64,8 +60,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.logging.*;
public class TikTokLiveClientBuilder implements LiveClientBuilder
{
public class TikTokLiveClientBuilder implements LiveClientBuilder {
protected final ClientSettings clientSettings;
protected final Logger logger;
@@ -105,16 +100,21 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
throw new TikTokLiveException("HostName can not be null");
}
if (clientSettings.getHostName().startsWith("@"))
{
clientSettings.setHostName(clientSettings.getHostName().substring(1));
}
var params = clientSettings.getClientParameters();
params.put("app_language", clientSettings.getClientLanguage());
params.put("webcast_language", clientSettings.getClientLanguage());
logger.setLevel(clientSettings.getLogLevel());
var handler = new ConsoleHandler();
handler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record)
{
public String format(LogRecord record) {
var sb = new StringBuilder();
sb.append(ConsoleColors.GREEN).append("[").append(record.getLoggerName()).append("] ");
sb.append(ConsoleColors.GREEN).append("[").append(record.getLevel()).append("]: ");
@@ -126,9 +126,13 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
logger.setUseParentHandlers(false);
logger.addHandler(handler);
logger.setLevel(clientSettings.getLogLevel());
if (clientSettings.isPrintToConsole() && clientSettings.getLogLevel() == Level.OFF) {
logger.setLevel(Level.ALL);
}
}
public LiveClient build() {
@@ -171,7 +175,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
}
public CompletableFuture<LiveClient> buildAndConnectAsync() {
return CompletableFuture.supplyAsync(this::buildAndConnect);
return build().connectAsync();
}
public TikTokLiveClientBuilder onUnhandledSocial(
@@ -261,8 +265,8 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
return this;
}
public TikTokLiveClientBuilder onGiftCombo(EventConsumer<TikTokGiftComboFinishedEvent> event) {
tikTokEventHandler.subscribe(TikTokGiftComboFinishedEvent.class, event);
public TikTokLiveClientBuilder onGiftCombo(EventConsumer<TikTokGiftComboEvent> event) {
tikTokEventHandler.subscribe(TikTokGiftComboEvent.class, event);
return this;
}
@@ -318,7 +322,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
}
public TikTokLiveClientBuilder onComment(EventConsumer<TikTokCommentEvent> event) {
tikTokEventHandler.subscribe(TikTokCommentEvent.class, event);
return this;
@@ -328,6 +331,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
tikTokEventHandler.subscribe(TikTokGoalUpdateEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onRankUpdate(EventConsumer<TikTokRankUpdateEvent> event) {
tikTokEventHandler.subscribe(TikTokRankUpdateEvent.class, event);
return this;
@@ -349,7 +353,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
}
public TikTokLiveClientBuilder onJoin(EventConsumer<TikTokJoinEvent> event) {
tikTokEventHandler.subscribe(TikTokJoinEvent.class, event);
return this;
@@ -404,6 +407,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder
tikTokEventHandler.subscribe(TikTokWebsocketResponseEvent.class, event);
return this;
}
@Override
public TikTokLiveClientBuilder onWebsocketMessage(EventConsumer<TikTokWebsocketMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokWebsocketMessageEvent.class, event);

View File

@@ -26,7 +26,7 @@ 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.TikTokBarrageEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboFinishedEvent;
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.TikTokPollEvent;
@@ -142,7 +142,7 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
}
if (giftMessage.getRepeatEnd() > 0) {
return new TikTokGiftComboFinishedEvent(gift, giftMessage);
return new TikTokGiftComboEvent(gift, giftMessage);
}
return new TikTokGiftEvent(gift, giftMessage);

View File

@@ -30,6 +30,7 @@ import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import io.github.jwdeveloper.tiktok.mappers.LiveRoomMetaMapper;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import javax.script.ScriptEngineManager;
import java.util.HashMap;
import java.util.logging.Logger;
import java.util.regex.Pattern;
@@ -116,22 +117,24 @@ public class TikTokApiService {
public LiveRoomMeta fetchRoomInfo() {
logger.info("Fetch RoomInfo");
logger.info("Fetching RoomInfo");
try {
var response = tiktokHttpClient.getJObjectFromWebcastAPI("room/info/", clientSettings.getClientParameters());
var mapper = new LiveRoomMetaMapper();
var liveRoomMeta = mapper.map(response);
logger.info("RoomInfo status -> " + liveRoomMeta.getStatus());
return liveRoomMeta;
} catch (Exception e) {
} catch (Exception e)
{
throw new TikTokLiveRequestException("Failed to fetch room info from WebCast, see stacktrace for more info.", e);
}
}
public WebcastResponse fetchClientData() {
logger.info("Fetch ClientData");
logger.info("Fetching ClientData");
try {
var response = tiktokHttpClient.getDeserializedMessage("im/fetch/", clientSettings.getClientParameters());
var response = tiktokHttpClient.getSigningServerMessage("im/fetch/", clientSettings.getClientParameters());
clientSettings.getClientParameters().put("cursor", response.getCursor());
clientSettings.getClientParameters().put("internal_ext", response.getInternalExt());
return response;

View File

@@ -73,7 +73,7 @@ public class TikTokHttpClient {
return jsonObject;
}
public WebcastResponse getDeserializedMessage(String path, Map<String, Object> parameters) {
public WebcastResponse getSigningServerMessage(String path, Map<String, Object> parameters) {
var bytes = getSignRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters);
try {
return WebcastResponse.parseFrom(bytes);

View File

@@ -25,11 +25,11 @@ package io.github.jwdeveloper.tiktok.listener;
import io.github.jwdeveloper.tiktok.annotations.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -42,7 +42,10 @@ public class TikTokListenersManager implements ListenersManager {
public TikTokListenersManager(List<TikTokEventListener> listeners, TikTokEventObserver tikTokEventHandler) {
this.eventObserver = tikTokEventHandler;
this.bindingModels = new ArrayList<>(listeners.stream().map(this::bindToEvents).toList());
this.bindingModels = new ArrayList<>(listeners.size());
for (var listener : listeners) {
addListener(listener);
}
}
@Override

View File

@@ -25,9 +25,14 @@ package io.github.jwdeveloper.tiktok.mappers;
import com.google.gson.JsonObject;
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
public class LiveRoomMetaMapper
{
public class LiveRoomMetaMapper {
/**
* 0 - Unknown
* 1 - ?
* 2 - Online
* 3 - ?
* 4 - Offline
*/
public LiveRoomMeta map(JsonObject input) {
var liveRoomMeta = new LiveRoomMeta();
@@ -35,14 +40,25 @@ public class LiveRoomMetaMapper
return liveRoomMeta;
}
var data = input.getAsJsonObject("data");
if (data.has("status")) {
var status = data.get("status");
liveRoomMeta.setStatus(status.getAsInt());
var statusId = status.getAsInt();
var statusValue = switch (statusId) {
case 0 -> LiveRoomMeta.LiveRoomStatus.HostNotFound;
case 2 -> LiveRoomMeta.LiveRoomStatus.HostOnline;
case 4 -> LiveRoomMeta.LiveRoomStatus.HostOffline;
default-> LiveRoomMeta.LiveRoomStatus.HostNotFound;
};
liveRoomMeta.setStatus(statusValue);
}
else
{
liveRoomMeta.setStatus(LiveRoomMeta.LiveRoomStatus.HostNotFound);
}
if(data.has("age_restricted"))
{
if (data.has("age_restricted")) {
var element = data.getAsJsonObject("age_restricted");
var restricted = element.get("restricted").getAsBoolean();
liveRoomMeta.setAgeRestricted(restricted);

View File

@@ -75,7 +75,7 @@ public class TikTokWebSocketClient implements SocketClient {
try {
if (clientSettings.isHandleExistingEvents()) {
logger.info("Handling existing messages");
logger.info("Handling existing events");
webResponseHandler.handle(tikTokLiveClient, webcastResponse);
}
var url = getWebSocketUrl(webcastResponse);

View File

@@ -30,6 +30,7 @@ import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import java.io.IOException;
@@ -42,13 +43,12 @@ public class ListenerExample {
*
*/
public static void main(String[] args) throws IOException {
showLogo();
CustomListener customListener = new CustomListener();
TikTokLive.newClient(Main.TEST_TIKTOK_USER)
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.addListener(customListener)
.buildAndConnect();
System.in.read();
}
@@ -70,7 +70,7 @@ public class ListenerExample {
@TikTokEventHandler
public void onError(LiveClient liveClient, TikTokErrorEvent event) {
event.getException().printStackTrace();
// event.getException().printStackTrace();
}
@TikTokEventHandler
@@ -99,4 +99,17 @@ public class ListenerExample {
}
}
private static void showLogo()
{
System.out.println(ConsoleColors.GREEN+"""
_____ _ _ _____ _ _ _ \s
|_ _(_) | _|_ _|__ | | _| | (_)_ _____\s
| | | | |/ / | |/ _ \\| |/ / | | \\ \\ / / _ \\
| | | | < | | (_) | <| |___| |\\ V / __/
|_| |_|_|\\_\\ |_|\\___/|_|\\_\\_____|_| \\_/ \\___|
""");
}
}

View File

@@ -25,40 +25,71 @@ package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import java.io.IOException;
import java.time.Duration;
import java.util.logging.Level;
public class SimpleExample {
public class SimpleExample
{
public static String TIKTOK_HOSTNAME = "szwagierkaqueen";
public static void main(String[] args) throws IOException {
showLogo();
// set tiktok username
TikTokLive.newClient(Main.TEST_TIKTOK_USER)
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.configure(clientSettings ->
{
clientSettings.setHostName(SimpleExample.TIKTOK_HOSTNAME); // This method is useful in case you want change hostname later
clientSettings.setClientLanguage("en"); // Language
clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
clientSettings.setLogLevel(Level.ALL); // Log level
clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
clientSettings.setHandleExistingEvents(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
//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
// clientSettings.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
//clientSettings.setRoomId("XXXXXXXXXXXXXXXXX");
})
.onGift((liveClient, event) ->
{
switch (event.getGift()) {
case ROSE -> print(ConsoleColors.RED, " roses!", "\uD83D\uDC95");
case ROSE -> print(ConsoleColors.RED, "Rose!");
case GG -> print(ConsoleColors.YELLOW, " GOOD GAME!");
case TIKTOK -> print(ConsoleColors.CYAN,"Thanks for TikTok");
default -> print(ConsoleColors.GREEN, "[Thanks for gift] ", ConsoleColors.YELLOW, event.getGift().getName(), "X", event.getCombo());
default -> print(ConsoleColors.GREEN, "[Thanks for gift] ", ConsoleColors.YELLOW, event.getGift().getName(), "x", event.getCombo());
}
})
.onGiftCombo((liveClient, event) ->
{
print(ConsoleColors.RED,"GIFT COMBO",event.getGift().getName(),event.getCombo());
})
.onConnected((client, event) ->
{
print(ConsoleColors.GREEN, "[Connected]");
})
.onDisconnected((liveClient, event) ->
{
print(ConsoleColors.RED,"[Disconnected]");
})
.onFollow((liveClient, event) ->
{
print(ConsoleColors.BLUE, "Follow -> ", ConsoleColors.WHITE_BRIGHT, event.getUser().getName());
print(ConsoleColors.BLUE, "Follow:", ConsoleColors.WHITE_BRIGHT, event.getUser().getName());
})
.onJoin((client, event) ->
{
print(ConsoleColors.WHITE_BRIGHT, "Join ->", ConsoleColors.GREEN, event.getUser().getName());
print(ConsoleColors.WHITE, "Join:", ConsoleColors.WHITE_BRIGHT, event.getUser().getName());
})
.onComment((client, event) ->
{
print(ConsoleColors.WHITE, event.getUser().getName(), ":", ConsoleColors.WHITE_BRIGHT, event.getText());
print(ConsoleColors.GREEN, event.getUser().getName(), ":", ConsoleColors.WHITE_BRIGHT, event.getText());
})
.onEvent((client, event) ->
{
@@ -69,7 +100,6 @@ public class SimpleExample {
event.getException().printStackTrace();
})
.buildAndConnectAsync();
System.in.read();
}
@@ -80,4 +110,17 @@ public class SimpleExample {
}
System.out.println(sb);
}
private static void showLogo()
{
System.out.println(ConsoleColors.GREEN+"""
_____ _ _ _____ _ _ _ \s
|_ _(_) | _|_ _|__ | | _| | (_)_ _____\s
| | | | |/ / | |/ _ \\| |/ / | | \\ \\ / / _ \\
| | | | < | | (_) | <| |___| |\\ V / __/
|_| |_|_|\\_\\ |_|\\___/|_|\\_\\_____|_| \\_/ \\___|
""");
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok;
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 hostname
clientSettings.setClientLanguage("en"); // Language
clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
clientSettings.setLogLevel(Level.ALL); // Log level
clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
clientSettings.setHandleExistingEvents(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
//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
clientSettings.setSessionId("86c3c8bf4b17ebb2d74bb7fa66fd0000");
//Optional:
clientSettings.setRoomId("XXXXXXXXXXXXXXXXX");
})
.buildAndConnect();
System.in.read();
}
}

View File

@@ -1,149 +0,0 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent;
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.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import java.io.IOException;
import java.time.Duration;
public class Main {
public static String TEST_TIKTOK_USER = "bangbetmenygy";
public static void main(String[] args) throws IOException
{
showLogo();
LiveClient client = TikTokLive.newClient(TEST_TIKTOK_USER)
.configure(clientSettings ->
{
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(5));
clientSettings.setRetryOnConnectionFailure(true);
clientSettings.setHandleExistingEvents(true);
})
.onError((liveClient, event) ->
{
})
.onConnected(Main::onConnected)
.onDisconnected(Main::onDisconnected)
.onRoomUserInfo(Main::onRoomUserInfo)
.onJoin(Main::onJoin)
.onComment(Main::onComment)
.onFollow(Main::onFollow)
.onShare(Main::onShare)
.onSubscribe(Main::onSubscribe)
.onLike(Main::onLike)
.onGift(Main::onGift)
.onEmote(Main::onEmote)
.onError((_client, error) ->
{
error.getException().printStackTrace();
})
.buildAndConnect();
System.in.read();
}
private static void onConnected(LiveClient tikTokLive, TikTokConnectedEvent e) {
print(ConsoleColors.GREEN, "[Connected]");
}
private static void onGift(LiveClient tikTokLive, TikTokGiftEvent e)
{
switch (e.getGift()) {
case ROSE -> print( ConsoleColors.YELLOW,"x", e.getCombo(), " roses!", "\uD83D\uDC95");
default -> print(ConsoleColors.YELLOW,"Thanks for gift: ", e.getGift().getName(),"X",e.getCombo());
}
if(e.getGift().hasDiamondCostRange(1000,10000))
{
print("Thank you for expensive Gift!");
}
}
private static void onDisconnected(LiveClient tikTokLive, TikTokDisconnectedEvent e) {
print(ConsoleColors.GREEN, "[Disconnected]");
}
private static void onRoomUserInfo(LiveClient tikTokLive, TikTokRoomUserInfoEvent e) {
print("Viewer count is:", e.getTotalUsers());
}
private static void onJoin(LiveClient tikTokLive, TikTokJoinEvent e) {
print(ConsoleColors.GREEN, "Join -> ", ConsoleColors.WHITE_BRIGHT, e.getUser().getName());
}
private static void onComment(LiveClient tikTokLive, TikTokCommentEvent e) {
print(ConsoleColors.WHITE, e.getUser().getId(), ":", ConsoleColors.WHITE_BRIGHT, e.getText());
}
private static void onFollow(LiveClient tikTokLive, TikTokFollowEvent e) {
print(e.getUser().getId(), "followed!");
}
private static void onShare(LiveClient tikTokLive, TikTokShareEvent e) {
print(e.getUser().getId(), "shared!");
}
private static void onSubscribe(LiveClient tikTokLive, TikTokSubscribeEvent e) {
print(e.getUser().getId(), "subscribed!");
}
private static void onLike(LiveClient tikTokLive, TikTokLikeEvent e) {
print(e.getUser().getDisplayName(), "liked!");
}
private static void onEmote(LiveClient tikTokLive, TikTokEmoteEvent e) {
print(e.getUser().getId(), "sent", e.getEmotes().size());
}
private static void showLogo()
{
System.out.println(ConsoleColors.GREEN+"""
_____ _ _ _____ _ _ _ \s
|_ _(_) | _|_ _|__ | | _| | (_)_ _____\s
| | | | |/ / | |/ _ \\| |/ / | | \\ \\ / / _ \\
| | | | < | | (_) | <| |___| |\\ V / __/
|_| |_|_|\\_\\ |_|\\___/|_|\\_\\_____|_| \\_/ \\___|
""");
}
private static void print(Object... messages) {
var sb = new StringBuilder();
for (var message : messages) {
sb.append(message).append(" ");
}
System.out.println(sb);
}
}

View File

@@ -26,6 +26,7 @@ import io.github.jwdeveloper.tiktok.TikTokLive;
import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
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;
@@ -45,7 +46,7 @@ public class TikTokClientFactory {
this.tikTokDatabase = tikTokDatabase;
}
public CompletableFuture<LiveClient> runClientAsync(String tiktokUser, Consumer<TikTokLiveClientBuilder> onBuilder) {
public CompletableFuture<LiveClient> runClientAsync(String tiktokUser, Consumer<LiveClientBuilder> onBuilder) {
var builder = TikTokLive.newClient(tiktokUser);
onBuilder.accept(builder);
return builder.onConnected((liveClient, event) ->

View File

@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.tools.collector.client;
import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
import io.github.jwdeveloper.tiktok.tools.collector.db.TikTokDatabase;
import java.sql.SQLException;
@@ -35,7 +36,7 @@ public class TikTokMessagessCollectorBuilder {
List<String> users;
String outputFileName;
List<Class<?>> filters;
Consumer<TikTokLiveClientBuilder> onBuilder;
Consumer<LiveClientBuilder> onBuilder;
List<LiveClient> tiktokclients;
MessageCollector messageCollector;
@@ -61,7 +62,7 @@ public class TikTokMessagessCollectorBuilder {
}
public TikTokMessagessCollectorBuilder addOnBuilder(Consumer<TikTokLiveClientBuilder> consumer) {
public TikTokMessagessCollectorBuilder addOnBuilder(Consumer<LiveClientBuilder> consumer) {
onBuilder = consumer;
return this;
}

View File

@@ -1,3 +1,25 @@
/*
* 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.webviewer;
import io.github.jwdeveloper.tiktok.webviewer.handlers.TikTokHandler;

View File

@@ -1,3 +1,25 @@
/*
* 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.webviewer;
import com.google.protobuf.InvalidProtocolBufferException;

View File

@@ -1,3 +1,25 @@
/*
* 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.webviewer.handlers;
import com.google.gson.Gson;

View File

@@ -0,0 +1,178 @@
/*
* 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.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.live.builder.EventsBuilder;
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import io.github.jwdeveloper.tiktok.utils.TemplateUtility;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.reflections.Reflections;
import java.util.*;
import java.util.regex.Pattern;
public class EventsInfoGenerator {
public static void main(String[] args) throws ClassNotFoundException {
var res = new EventsInfoGenerator().run();
System.out.println(res);
}
public String run() {
var events = getEventsDtos();
var builder = new StringBuilder();
for (var entry : events.entrySet()) {
builder.append(System.lineSeparator());
builder.append(System.lineSeparator());
builder.append(" **" + entry.getKey().name() + "**:").append(System.lineSeparator());
for (var dto : entry.getValue()) {
var link = getLink(dto);
builder.append(System.lineSeparator());
builder.append(link);
}
}
builder.append(System.lineSeparator());
builder.append("# Examples");
builder.append(System.lineSeparator());
for (var entry : events.entrySet()) {
for (var dto : entry.getValue()) {
builder.append("<br>");
builder.append(getMethodContent(dto));
}
}
return builder.toString();
}
public StringBuilder getLink(EventDto dto) {
var sb = new StringBuilder();
var name = dto.getMethodName().toLowerCase()+"-"+dto.getEventClazz().getSimpleName().toLowerCase();
sb.append("- [").append(dto.getMethodName()).append("](#").append(name).append(")");
return sb;
}
public String getMethodContent(EventDto dto) {
var variables = new HashMap<String, Object>();
var doc = getClazzDocumentation(dto.getEventClazz());
variables.put("method-name", dto.getMethodName());
variables.put("content", doc);
variables.put("event-name", dto.getEventClazz().getSimpleName());
var temp = """
#### {{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}}
```java
TikTokLive.newClient("host-name")
.{{method-name}}((liveClient, event) ->
{
})
.buildAndConnect();
```
""";
return TemplateUtility.generateTemplate(temp, variables);
}
public String getClazzDocumentation(Class<?> clazz) {
var path = clazz.getName();
path = path.replace(".", "\\");
var fullPath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\" + path + ".java";
var content = FilesUtility.loadFileContent(fullPath);
var index = content.indexOf(" */");
content = content.substring(index+4);
String pattern = "(?s)\\/\\\\*(.*?)\\*\\/";
var r = Pattern.compile(pattern);
var m = r.matcher(content);
if (!m.find()) {
return "";
}
var group = m.group(1);
var reuslt = group.replace("*","").replaceAll("\\*", "");
return reuslt;
}
public Map<EventType, List<EventDto>> getEventsDtos(){
var result = new TreeMap<EventType, List<EventDto>>();
var baseClazz = EventsBuilder.class;
var reflections = new Reflections("io.github.jwdeveloper.tiktok.data.events");
var classes = reflections.getSubTypesOf(TikTokEvent.class);
var methods = baseClazz.getDeclaredMethods();
for (var method : methods) {
if (method.getName().equals("onEvent")) {
var dto = new EventDto(EventType.Message, "onEvent", TikTokEvent.class);
result.computeIfAbsent(EventType.Message, eventType -> new ArrayList<>()).add(dto);
continue;
}
var parsedName = method.getName().replaceFirst("on", "");
var name = "TikTok" + parsedName + "Event";
var optional = classes.stream().filter(e -> e.getSimpleName().equals(name)).findFirst();
if (optional.isEmpty()) {
System.out.println("Not found!: " + name);
continue;
}
var clazz = optional.get();
var annotation = clazz.getAnnotation(EventMeta.class);
var dto = new EventDto(annotation.eventType(), method.getName(), clazz);
result.computeIfAbsent(dto.eventType, eventType -> new ArrayList<>()).add(dto);
}
return result;
}
@AllArgsConstructor
@Getter
public static final class EventDto {
private EventType eventType;
private String methodName;
private Class eventClazz;
@Override
public String toString() {
return "EventDto{" +
"eventType=" + eventType +
", methodName='" + methodName + '\'' +
", eventClazz=" + eventClazz.getSimpleName() +
'}';
}
}
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import org.reflections.Reflections;
import java.util.*;
public class EventsListGenerator
{
//[a](https://github.com/jwdeveloper/TikTok-Live-Java/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/events/messages/TikTokBarrageMessageEvent.java)
//Message Events:
//- [member](#member)
public static String GetEventsList()
{
var classes = getClasses();
var builder = new StringBuilder();
for(var entry : classes.entrySet())
{
builder.append(System.lineSeparator());
builder.append(" **"+entry.getKey().name()+"**:").append(System.lineSeparator());
for(var clazz : entry.getValue())
{
var name = clazz.getSimpleName();
var baseUrl ="https://github.com/jwdeveloper/TikTok-Live-Java/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/events/messages/"+name+".java";
builder.append("- [").append(name).append("](").append(baseUrl).append(")").append(System.lineSeparator());
}
}
return builder.toString();
}
private static Map<EventType, List<Class<?>>> getClasses()
{
Reflections reflections = new Reflections("io.github.jwdeveloper.tiktok.events.messages");
var classes = reflections.getSubTypesOf(TikTokEvent.class).stream().toList();
Map<EventType, List<Class<?>>> classMap = new HashMap<>();
// Group classes by EventType
for (Class<?> clazz : classes) {
EventType eventType = getEventType(clazz);
classMap.computeIfAbsent(eventType, k -> new ArrayList<>()).add(clazz);
}
return classMap;
}
private static EventType getEventType(Class<?> clazz) {
EventMeta annotation = clazz.getAnnotation(EventMeta.class);
if (annotation != null) {
return annotation.eventType();
}
return EventType.Custom; // Default value if annotation not present
}
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok;
public class FullEventsExampleClass
{
public void run()
{
TikTokLive.newClient("username")
.onConnected((liveClient, event) ->
{
})
.onDisconnected((liveClient, event) ->
{
})
.onError((liveClient, event) ->
{
})
.onGift((liveClient, event) ->
{
})
.onLike((liveClient, event) ->
{
})
.onJoin((liveClient, event) ->
{
})
.onFollow((liveClient, event) ->
{
})
.onShare((liveClient, event) ->
{
})
.onRoomUserInfo((liveClient, event) ->
{
})
.onComment((liveClient, event) ->
{
})
.onLiveEnded((liveClient, event) ->
{
})
.onLivePaused((liveClient, event) ->
{
}).onEmote((liveClient, event) ->
{
});
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import java.util.regex.Pattern;
public class LiveClientMethodsGenerator
{
//| Method Name | Description |
//| ----------- | ----------- |
public static String generate(String path)
{
var sb = new StringBuilder();
sb.append("| Method Name | Description |");
sb.append(System.lineSeparator());
sb.append("| ----------- | ----------- |");
var content = FilesUtility.loadFileContent(path);
var pattern = "// \\s*(.*?)\\n\\s*(\\w+)\\s*\\(.*?\\)";
// Create a Pattern object
var regex = Pattern.compile(pattern);
// Create a Matcher object
var matcher = regex.matcher(content);
// Find and print method names and comments
while (matcher.find()) {
String comment = matcher.group(1);
String methodName = matcher.group(2);
sb.append(System.lineSeparator());
sb.append("| ").append(methodName).append(" | ").append(comment).append(" |");
}
sb.append(System.lineSeparator());
return sb.toString();
}
}

View File

@@ -43,20 +43,14 @@ public class ReadmeGenerator
variables.put("version", getCurrentVersion());
var exampleCodePath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\TestApplication\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\SimpleExample.java";
variables.put("Code-Example", getCodeExample(exampleCodePath));
var exampleCodePath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Examples\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\SimpleExample.java";
variables.put("code-content", getCodeExample(exampleCodePath));
variables.put("events-content", new EventsInfoGenerator().run());
var exampleConfigurationPath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\TestApplication\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\ConfigurationExample.java";
variables.put("Configuration-Example", getCodeExample(exampleConfigurationPath));
var listenerExamplePath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Examples\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\ListenerExample.java";
// variables.put("listener-content", getCodeExample(listenerExamplePath));
variables.put("Events", EventsListGenerator.GetEventsList());
var listenerExamplePath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\TestApplication\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\ListenerExample.java";
variables.put("Listener-Example", getCodeExample(listenerExamplePath));
// var liveClientPath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\live\\LiveClient.java";
// variables.put("methods", LiveClientMethodsGenerator.generate(liveClientPath));
template = TemplateUtility.generateTemplate(template, variables);
var outputPath = "C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools-ReadmeGenerator\\src\\main\\resources\\output.md";
@@ -72,7 +66,9 @@ public class ReadmeGenerator
public String getCodeExample(String path)
{
return FilesUtility.loadFileContent(path);
var content = FilesUtility.loadFileContent(path);
content = content.substring(content.indexOf("*/")+2);
return content;
}
}

View File

@@ -51,38 +51,20 @@ Do you prefer other programming languages?
2. Create your first chat connection
```java
{{Code-Example}}
{{code-content}}
```
## Configuration
## Events
{{events-content}}
<br>
<br>
```java
{{Configuration-Example}}
```
## Listener Example
```java
{{Listener-Example}}
{{listener-content}}
```
## Methods
A `client (LiveClient)` object contains the following methods.
| Method Name | Description |
|---------------------| ----------- |
| connect | Connects to the live stream. |
| 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. |
| getListenersManager | Gets and manage TikTokEventListeners |
## Events
A `TikTokLive` object has the following events
{{Events}}
<br><br>
## Contributing
Your improvements are welcome! Feel free to open an <a href="https://github.com/jwdeveloper/TikTok-Live-Java/issues">issue</a> or <a href="https://github.com/jwdeveloper/TikTok-Live-Java/pulls">pull request</a>.

View File

@@ -46,7 +46,7 @@ public class ApiServiceMock extends TikTokApiService {
public LiveRoomMeta fetchRoomInfo()
{
var meta = new LiveRoomMeta();
meta.setStatus(1);
meta.setStatus(LiveRoomMeta.LiveRoomStatus.HostOnline);
return meta;
}

View File

@@ -34,13 +34,6 @@ public class TemplateUtility
return template;
}
public static String generateTemplate2(String template, Map<String, Object> values) {
for(var entry : values.entrySet())
{
template = doReplacement2(template,entry.getKey(), entry.getValue().toString());
}
return template;
}
private static String doReplacement(String template, String keyword, String value)
{
@@ -48,9 +41,5 @@ public class TemplateUtility
return template.replaceAll(key, value);
}
private static String doReplacement2(String template, String keyword, String value)
{
var key = "(\\$)("+keyword+")(\\$)";
return template.replaceAll(key, value);
}
}

View File

@@ -12,7 +12,7 @@
<module>API</module>
<module>Client</module>
<module>Tools</module>
<module>TestApplication</module>
<module>Examples</module>
<module>Tools-EventsCollector</module>
<module>Tools-ReadmeGenerator</module>
<module>Tools-EventsWebViewer</module>