mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-02-27 08:49:40 -05:00
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,10 +1,11 @@
|
||||
backend-infrastructure/.aws-sam
|
||||
|
||||
|
||||
.db
|
||||
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
|
||||
*.db
|
||||
### Linux ###
|
||||
*~
|
||||
.db
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
|
||||
@@ -22,10 +22,8 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.data.events.gift;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
||||
import io.github.jwdeveloper.tiktok.annotations.*;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.*;
|
||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import lombok.Getter;
|
||||
@@ -53,4 +51,4 @@ public class TikTokGiftComboEvent extends TikTokGiftEvent {
|
||||
super(gift, host, msg);
|
||||
this.comboState = comboState;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,18 +23,13 @@
|
||||
package io.github.jwdeveloper.tiktok.data.events.gift;
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||
import io.github.jwdeveloper.tiktok.annotations.*;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
|
||||
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.data.models.gifts.*;
|
||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Triggered when user sends gifts that has
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -28,13 +28,11 @@ public enum GiftSendType
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,16 +42,7 @@ public class GiftsData
|
||||
public static final class Response
|
||||
{
|
||||
private String json;
|
||||
private List<GiftModel> gifts;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class GiftModel
|
||||
{
|
||||
private int id;
|
||||
private String name;
|
||||
private int diamondCost;
|
||||
private String image;
|
||||
private List<Gift> gifts;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,13 @@ import java.util.logging.Level;
|
||||
@Data
|
||||
public class LiveClientSettings {
|
||||
|
||||
|
||||
/**
|
||||
* Determines if gifts data is downloaded before TikTokLive starts,
|
||||
* when `false` then client.giftManager() does not contain initial gifts
|
||||
*/
|
||||
private boolean fetchGifts;
|
||||
|
||||
/**
|
||||
* ISO-Language for Client
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ public interface LiveHttpClient
|
||||
*/
|
||||
GiftsData.Response fetchGiftsData();
|
||||
|
||||
|
||||
/**
|
||||
* Returns information about user that is having a livestream
|
||||
* @param userName name of user
|
||||
|
||||
@@ -22,42 +22,65 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.live;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.*;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface GiftManager {
|
||||
public interface GiftsManager {
|
||||
|
||||
/**
|
||||
* In case you can't find your gift in Gift enum. You can register gift
|
||||
* manually here to make it detected while TikTokGiftEvent
|
||||
* You can create and attach your own custom gift to manager
|
||||
*
|
||||
* @param id gift's id
|
||||
* @param name gift's name
|
||||
* @param diamondCost diamond cost
|
||||
* @return
|
||||
* @param gift
|
||||
*/
|
||||
Gift registerGift(int id, String name, int diamondCost, Picture picture);
|
||||
void attachGift(Gift gift);
|
||||
|
||||
/**
|
||||
* You can create and attach your own custom gift to manager
|
||||
*
|
||||
* @param gifts
|
||||
*/
|
||||
void attachGiftsList(List<Gift> gifts);
|
||||
|
||||
/**
|
||||
* finds gift by name
|
||||
* When gift not found return Gift.UNDEFINED;
|
||||
*
|
||||
* @param name gift name
|
||||
*/
|
||||
Gift getByName(String name);
|
||||
|
||||
/**
|
||||
* finds gift by id
|
||||
* When gift not found return Gift.UNDEFINED;
|
||||
*
|
||||
* @param giftId giftId
|
||||
*/
|
||||
Gift getById(int giftId);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param giftId
|
||||
* @return
|
||||
* finds gift by filter
|
||||
* When gift not found return Gift.UNDEFINED;
|
||||
*/
|
||||
Gift findById(int giftId);
|
||||
Gift getByFilter(Predicate<Gift> filter);
|
||||
|
||||
List<Gift> getManyByFilter(Predicate<Gift> filter);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param giftName
|
||||
* @return
|
||||
* @return list of all gifts
|
||||
*/
|
||||
Gift findByName(String giftName);
|
||||
List<Gift> toList();
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return all gifts
|
||||
* @return list of all map of all gifts where Integer is gift Id
|
||||
*/
|
||||
List<Gift> getGifts();
|
||||
}
|
||||
Map<Integer, Gift> toMap();
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public interface LiveClient {
|
||||
/**
|
||||
* Get information about gifts
|
||||
*/
|
||||
GiftManager getGiftManager();
|
||||
GiftsManager getGiftManager();
|
||||
|
||||
/**
|
||||
* Gets the current room info from TikTok API including streamer info, room status and statistics.
|
||||
|
||||
@@ -23,16 +23,20 @@
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class TikTokLive {
|
||||
|
||||
/**
|
||||
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||
*
|
||||
* @param hostName profile name of Tiktok user could be found in profile link
|
||||
* example: https://www.tiktok.com/@dostawcavideo hostName would be dostawcavideo
|
||||
* @return LiveClientBuilder
|
||||
*/
|
||||
public static LiveClientBuilder newClient(String hostName) {
|
||||
@@ -40,47 +44,43 @@ public class TikTokLive {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||
*
|
||||
* @param hostName profile name of Tiktok user could be found in profile link
|
||||
* example: https://www.tiktok.com/@dostawcavideo hostName would be dostawcavideo
|
||||
* @return true if live is Online, false if is offline
|
||||
*/
|
||||
public static boolean isLiveOnline(String hostName)
|
||||
{
|
||||
public static boolean isLiveOnline(String hostName) {
|
||||
return requests().fetchLiveUserData(hostName).isLiveOnline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||
*
|
||||
* @param hostName profile name of Tiktok user could be found in profile link
|
||||
* example: https://www.tiktok.com/@dostawcavideo hostName would be dostawcavideo
|
||||
* @return true if live is Online, false if is offline
|
||||
*/
|
||||
public static CompletableFuture<Boolean> isLiveOnlineAsync(String hostName)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(()-> isLiveOnline(hostName));
|
||||
public static CompletableFuture<Boolean> isLiveOnlineAsync(String hostName) {
|
||||
return CompletableFuture.supplyAsync(() -> isLiveOnline(hostName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||
*
|
||||
* @param hostName profile name of Tiktok user could be found in profile link
|
||||
* example: https://www.tiktok.com/@dostawcavideo hostName would be dostawcavideo
|
||||
* @return true is hostName name is valid and exists, false if not
|
||||
*/
|
||||
public static boolean isHostNameValid(String hostName)
|
||||
{
|
||||
public static boolean isHostNameValid(String hostName) {
|
||||
return requests().fetchLiveUserData(hostName).isHostNameValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||
*
|
||||
* @param hostName profile name of Tiktok user could be found in profile link
|
||||
* example: https://www.tiktok.com/@dostawcavideo hostName would be dostawcavideo
|
||||
* @return true is hostName name is valid and exists, false if not
|
||||
*/
|
||||
public static CompletableFuture<Boolean> isHostNameValidAsync(String hostName)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(()-> isHostNameValid(hostName));
|
||||
public static CompletableFuture<Boolean> isHostNameValidAsync(String hostName) {
|
||||
return CompletableFuture.supplyAsync(() -> isHostNameValid(hostName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,4 +91,29 @@ public class TikTokLive {
|
||||
public static LiveHttpClient requests() {
|
||||
return new TikTokLiveHttpClient();
|
||||
}
|
||||
|
||||
|
||||
//I don't like it, but it is reasonable for now
|
||||
private static GiftsManager giftsManager;
|
||||
|
||||
/**
|
||||
* Fetch gifts from endpoint and returns GiftManager
|
||||
*
|
||||
* @return GiftsManager
|
||||
*/
|
||||
public static GiftsManager gifts() {
|
||||
if (giftsManager != null) {
|
||||
return giftsManager;
|
||||
}
|
||||
synchronized (GiftsManager.class)
|
||||
{
|
||||
if (giftsManager == null)
|
||||
{
|
||||
return new TikTokGiftsManager(requests().fetchGiftsData().getGifts());
|
||||
}
|
||||
}
|
||||
return giftsManager;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -33,10 +33,9 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.models.ConnectionState;
|
||||
@@ -49,24 +48,24 @@ import java.util.logging.Logger;
|
||||
|
||||
public class TikTokLiveClient implements LiveClient {
|
||||
private final TikTokRoomInfo liveRoomInfo;
|
||||
private final TikTokGiftManager tikTokGiftManager;
|
||||
private final TikTokLiveHttpClient httpClient;
|
||||
private final SocketClient webSocketClient;
|
||||
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||
private final LiveClientSettings clientSettings;
|
||||
private final TikTokListenersManager listenersManager;
|
||||
private final Logger logger;
|
||||
private final GiftsManager giftsManager;
|
||||
|
||||
public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta,
|
||||
public TikTokLiveClient(GiftsManager giftsManager,
|
||||
TikTokRoomInfo tikTokLiveMeta,
|
||||
TikTokLiveHttpClient tiktokHttpClient,
|
||||
SocketClient webSocketClient,
|
||||
TikTokGiftManager tikTokGiftManager,
|
||||
TikTokLiveEventHandler tikTokEventHandler,
|
||||
LiveClientSettings clientSettings,
|
||||
TikTokListenersManager listenersManager,
|
||||
Logger logger) {
|
||||
this.giftsManager = giftsManager;
|
||||
this.liveRoomInfo = tikTokLiveMeta;
|
||||
this.tikTokGiftManager = tikTokGiftManager;
|
||||
this.httpClient = tiktokHttpClient;
|
||||
this.webSocketClient = webSocketClient;
|
||||
this.tikTokEventHandler = tikTokEventHandler;
|
||||
@@ -102,7 +101,8 @@ public class TikTokLiveClient implements LiveClient {
|
||||
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();
|
||||
@@ -121,29 +121,29 @@ public class TikTokLiveClient implements LiveClient {
|
||||
}
|
||||
|
||||
setState(ConnectionState.CONNECTING);
|
||||
tikTokEventHandler.publish(this,new TikTokConnectingEvent());
|
||||
tikTokEventHandler.publish(this, new TikTokConnectingEvent());
|
||||
var userDataRequest = new LiveUserData.Request(liveRoomInfo.getHostName());
|
||||
var userData = httpClient.fetchLiveUserData(userDataRequest);
|
||||
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
|
||||
liveRoomInfo.setRoomId(userData.getRoomId());
|
||||
|
||||
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline)
|
||||
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostName());
|
||||
throw new TikTokLiveOfflineHostException("User is offline: " + liveRoomInfo.getHostName());
|
||||
|
||||
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound)
|
||||
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostName());
|
||||
throw new TikTokLiveOfflineHostException("User not found: " + liveRoomInfo.getHostName());
|
||||
|
||||
var liveDataRequest = new LiveData.Request(userData.getRoomId());
|
||||
var liveData = httpClient.fetchLiveData(liveDataRequest);
|
||||
|
||||
if (liveData.isAgeRestricted())
|
||||
throw new TikTokLiveException("Livestream for "+liveRoomInfo.getHostName()+" is 18+ or age restricted!");
|
||||
throw new TikTokLiveException("Livestream for " + liveRoomInfo.getHostName() + " is 18+ or age restricted!");
|
||||
|
||||
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound)
|
||||
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" could not be found.");
|
||||
throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " could not be found.");
|
||||
|
||||
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline)
|
||||
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" not found, is the Host offline?");
|
||||
throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " not found, is the Host offline?");
|
||||
|
||||
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
|
||||
|
||||
@@ -158,7 +158,7 @@ public class TikTokLiveClient implements LiveClient {
|
||||
if (preconnectEvent.isCancelConnection())
|
||||
throw new TikTokLiveException("TikTokPreConnectionEvent cancelled connection!");
|
||||
|
||||
var liveConnectionRequest =new LiveConnectionData.Request(userData.getRoomId());
|
||||
var liveConnectionRequest = new LiveConnectionData.Request(userData.getRoomId());
|
||||
var liveConnectionData = httpClient.fetchLiveConnectionData(liveConnectionRequest);
|
||||
webSocketClient.start(liveConnectionData, this);
|
||||
|
||||
@@ -183,6 +183,11 @@ public class TikTokLiveClient implements LiveClient {
|
||||
tikTokEventHandler.publish(this, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GiftsManager getGiftManager() {
|
||||
return giftsManager;
|
||||
}
|
||||
|
||||
public LiveRoomInfo getRoomInfo() {
|
||||
return liveRoomInfo;
|
||||
}
|
||||
@@ -196,9 +201,4 @@ public class TikTokLiveClient implements LiveClient {
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GiftManager getGiftManager() {
|
||||
return tikTokGiftManager;
|
||||
}
|
||||
}
|
||||
@@ -22,66 +22,48 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.common.LoggerFactory;
|
||||
import io.github.jwdeveloper.tiktok.data.events.*;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent;
|
||||
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.gift.*;
|
||||
import io.github.jwdeveloper.tiktok.data.events.http.TikTokHttpResponseEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomPinEvent;
|
||||
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.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.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.http.HttpClientFactory;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
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.mappers.TikTokGenericEventMapper;
|
||||
import io.github.jwdeveloper.tiktok.mappers.TikTokLiveMapper;
|
||||
import io.github.jwdeveloper.tiktok.mappers.TikTokLiveMapperHelper;
|
||||
import io.github.jwdeveloper.tiktok.mappers.TikTokMapper;
|
||||
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokCommonEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokGiftEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokRoomInfoEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokSocialMediaEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.*;
|
||||
import io.github.jwdeveloper.tiktok.data.events.room.*;
|
||||
import io.github.jwdeveloper.tiktok.data.events.social.*;
|
||||
import io.github.jwdeveloper.tiktok.data.events.websocket.*;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.http.HttpClientFactory;
|
||||
import io.github.jwdeveloper.tiktok.listener.*;
|
||||
import io.github.jwdeveloper.tiktok.live.*;
|
||||
import io.github.jwdeveloper.tiktok.live.builder.*;
|
||||
import io.github.jwdeveloper.tiktok.mappers.*;
|
||||
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.*;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.*;
|
||||
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
|
||||
protected final LiveClientSettings clientSettings;
|
||||
protected final Logger logger;
|
||||
protected final TikTokLiveEventHandler tikTokEventHandler;
|
||||
protected final List<TikTokEventListener> listeners;
|
||||
protected Consumer<TikTokMapper> onCustomMappings;
|
||||
protected Logger logger;
|
||||
protected GiftsManager giftsManager;
|
||||
|
||||
public TikTokLiveClientBuilder(String userName)
|
||||
{
|
||||
public TikTokLiveClientBuilder(String userName) {
|
||||
this.clientSettings = LiveClientSettings.createDefault();
|
||||
this.clientSettings.setHostName(userName);
|
||||
this.tikTokEventHandler = new TikTokLiveEventHandler();
|
||||
this.logger = Logger.getLogger(TikTokLive.class.getSimpleName() + " " + userName);
|
||||
this.listeners = new ArrayList<>();
|
||||
this.onCustomMappings = (e) -> {
|
||||
};
|
||||
@@ -92,7 +74,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure) {
|
||||
onConfigure.accept(clientSettings);
|
||||
return this;
|
||||
@@ -105,43 +86,21 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
}
|
||||
|
||||
protected void validate() {
|
||||
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty()) {
|
||||
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty())
|
||||
clientSettings.setClientLanguage("en");
|
||||
}
|
||||
|
||||
|
||||
if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty()) {
|
||||
if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty())
|
||||
throw new TikTokLiveException("HostName can not be null");
|
||||
}
|
||||
|
||||
if (clientSettings.getHostName().startsWith("@")) {
|
||||
if (clientSettings.getHostName().startsWith("@"))
|
||||
clientSettings.setHostName(clientSettings.getHostName().substring(1));
|
||||
}
|
||||
|
||||
|
||||
var httpSettings = clientSettings.getHttpSettings();
|
||||
httpSettings.getParams().put("app_language", clientSettings.getClientLanguage());
|
||||
httpSettings.getParams().put("webcast_language", clientSettings.getClientLanguage());
|
||||
|
||||
|
||||
var handler = new ConsoleHandler();
|
||||
handler.setFormatter(new Formatter() {
|
||||
@Override
|
||||
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("]: ");
|
||||
sb.append(ConsoleColors.WHITE_BRIGHT).append(record.getMessage());
|
||||
sb.append(ConsoleColors.RESET).append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
});
|
||||
logger.setUseParentHandlers(false);
|
||||
logger.addHandler(handler);
|
||||
logger.setLevel(clientSettings.getLogLevel());
|
||||
if (!clientSettings.isPrintToConsole()) {
|
||||
logger.setLevel(Level.OFF);
|
||||
}
|
||||
this.logger = LoggerFactory.create(clientSettings.getHostName(), clientSettings);
|
||||
this.giftsManager = clientSettings.isFetchGifts() ? TikTokLive.gifts() : new TikTokGiftsManager(List.of());
|
||||
}
|
||||
|
||||
public LiveClient build() {
|
||||
@@ -152,33 +111,31 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
|
||||
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
||||
|
||||
var giftManager = new TikTokGiftManager(logger);
|
||||
var eventsMapper = createMapper(giftManager, tiktokRoomInfo);
|
||||
var messageHandler = new TikTokLiveMessageHandler(tikTokEventHandler, eventsMapper);
|
||||
|
||||
|
||||
var httpClientFactory = new HttpClientFactory(clientSettings);
|
||||
var tikTokLiveHttpClient = new TikTokLiveHttpClient(httpClientFactory, clientSettings);
|
||||
|
||||
|
||||
var eventsMapper = createMapper(giftsManager, tiktokRoomInfo);
|
||||
var messageHandler = new TikTokLiveMessageHandler(tikTokEventHandler, eventsMapper);
|
||||
|
||||
|
||||
var webSocketClient = new TikTokWebSocketClient(
|
||||
clientSettings,
|
||||
messageHandler,
|
||||
tikTokEventHandler);
|
||||
|
||||
return new TikTokLiveClient(tiktokRoomInfo,
|
||||
return new TikTokLiveClient(
|
||||
giftsManager,
|
||||
tiktokRoomInfo,
|
||||
tikTokLiveHttpClient,
|
||||
webSocketClient,
|
||||
giftManager,
|
||||
tikTokEventHandler,
|
||||
clientSettings,
|
||||
listenerManager,
|
||||
logger);
|
||||
}
|
||||
|
||||
public TikTokLiveMapper createMapper(GiftManager giftManager, TikTokRoomInfo roomInfo) {
|
||||
/*
|
||||
//
|
||||
*/
|
||||
public TikTokLiveMapper createMapper(GiftsManager giftsManager, TikTokRoomInfo roomInfo) {
|
||||
|
||||
|
||||
var eventMapper = new TikTokGenericEventMapper();
|
||||
@@ -186,7 +143,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
|
||||
//ConnectionEvents events
|
||||
var commonHandler = new TikTokCommonEventHandler();
|
||||
var giftHandler = new TikTokGiftEventHandler(giftManager, roomInfo);
|
||||
var giftHandler = new TikTokGiftEventHandler(giftsManager, roomInfo);
|
||||
var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo);
|
||||
var socialHandler = new TikTokSocialMediaEventHandler(roomInfo);
|
||||
|
||||
@@ -282,13 +239,11 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokChestEvent.class, event);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TikTokLiveClientBuilder onLinkMicFanTicket(EventConsumer<TikTokLinkMicFanTicketEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event);
|
||||
return this;
|
||||
@@ -345,14 +300,12 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TikTokLiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TikTokLiveClientBuilder onLivePaused(EventConsumer<TikTokLivePausedEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
|
||||
return this;
|
||||
@@ -379,7 +332,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TikTokLiveClientBuilder onGift(EventConsumer<TikTokGiftEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokGiftEvent.class, event);
|
||||
return this;
|
||||
@@ -390,7 +342,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TikTokLiveClientBuilder onLinkMicArmies(EventConsumer<TikTokLinkMicArmiesEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event);
|
||||
return this;
|
||||
@@ -467,7 +418,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public TikTokLiveClientBuilder onJoin(EventConsumer<TikTokJoinEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokJoinEvent.class, event);
|
||||
return this;
|
||||
|
||||
@@ -23,24 +23,26 @@
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.github.jwdeveloper.tiktok.common.*;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.*;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.*;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||
import io.github.jwdeveloper.tiktok.http.*;
|
||||
import io.github.jwdeveloper.tiktok.http.mappers.*;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.Optional;
|
||||
|
||||
public class TikTokLiveHttpClient implements LiveHttpClient {
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TikTokLiveHttpClient implements LiveHttpClient
|
||||
{
|
||||
/**
|
||||
* <a href="https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures">Signing API by Isaac Kogan</a>
|
||||
*/
|
||||
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";
|
||||
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
|
||||
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
|
||||
public static final String TIKTOK_GIFTS_URL = "https://raw.githubusercontent.com/TikTok-LIVE-Private/GiftsGenerator/master/page/public/gifts.json";
|
||||
public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110;
|
||||
|
||||
private final HttpClientFactory httpFactory;
|
||||
@@ -48,10 +50,12 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
||||
private final LiveUserDataMapper liveUserDataMapper;
|
||||
private final LiveDataMapper liveDataMapper;
|
||||
private final GiftsDataMapper giftsDataMapper;
|
||||
private final Logger logger;
|
||||
|
||||
public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) {
|
||||
this.httpFactory = factory;
|
||||
clientSettings = settings;
|
||||
this.clientSettings = settings;
|
||||
this.logger = LoggerFactory.create("HttpClient", clientSettings);
|
||||
liveUserDataMapper = new LiveUserDataMapper();
|
||||
liveDataMapper = new LiveDataMapper();
|
||||
giftsDataMapper = new GiftsDataMapper();
|
||||
@@ -61,117 +65,97 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
||||
this(new HttpClientFactory(LiveClientSettings.createDefault()), LiveClientSettings.createDefault());
|
||||
}
|
||||
|
||||
|
||||
public GiftsData.Response fetchGiftsData() {
|
||||
var url = TIKTOK_URL_WEBCAST + "gift/list/";
|
||||
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||
if (proxyClientSettings.isEnabled()) {
|
||||
while (proxyClientSettings.hasNext()) {
|
||||
try {
|
||||
var optional = httpFactory.client(url)
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to fetch gifts information's");
|
||||
}
|
||||
var json = optional.get();
|
||||
return giftsDataMapper.map(json);
|
||||
return getGiftsData();
|
||||
} catch (TikTokProxyRequestException ignored) {}
|
||||
}
|
||||
}
|
||||
var optional = httpFactory.client(url)
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
return getGiftsData();
|
||||
}
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to fetch gifts information's");
|
||||
}
|
||||
public GiftsData.Response getGiftsData() {
|
||||
var result = httpFactory.client(TIKTOK_GIFTS_URL)
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
var json = optional.get();
|
||||
if (result.isFailure())
|
||||
throw new TikTokLiveRequestException("Unable to fetch gifts information's"+result.toStack());
|
||||
|
||||
var json = result.getContent();
|
||||
return giftsDataMapper.map(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
|
||||
var url = TIKTOK_URL_WEB + "api-live/user/room";
|
||||
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||
if (proxyClientSettings.isEnabled()) {
|
||||
while (proxyClientSettings.hasNext()) {
|
||||
try {
|
||||
var optional = httpFactory.client(url)
|
||||
.withParam("uniqueId", request.getUserName())
|
||||
.withParam("sourceType", "54")
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to get information's about user");
|
||||
}
|
||||
|
||||
var json = optional.get();
|
||||
return liveUserDataMapper.map(json);
|
||||
return getLiveUserData(request);
|
||||
} catch (TikTokProxyRequestException ignored) {}
|
||||
}
|
||||
}
|
||||
var optional = httpFactory.client(url)
|
||||
.withParam("uniqueId", request.getUserName())
|
||||
.withParam("sourceType", "54")
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
return getLiveUserData(request);
|
||||
}
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to get information's about user");
|
||||
}
|
||||
public LiveUserData.Response getLiveUserData(LiveUserData.Request request) {
|
||||
var url = TIKTOK_URL_WEB + "api-live/user/room";
|
||||
var result = httpFactory.client(url)
|
||||
.withParam("uniqueId", request.getUserName())
|
||||
.withParam("sourceType", "54")
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
var json = optional.get();
|
||||
if (result.isFailure())
|
||||
throw new TikTokLiveRequestException("Unable to get information's about user"+result.toStack());
|
||||
|
||||
var json = result.getContent();
|
||||
return liveUserDataMapper.map(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveData.Response fetchLiveData(LiveData.Request request) {
|
||||
var url = TIKTOK_URL_WEBCAST + "room/info";
|
||||
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||
if (proxyClientSettings.isEnabled()) {
|
||||
while (proxyClientSettings.hasNext()) {
|
||||
try {
|
||||
var optional = httpFactory.client(url)
|
||||
.withParam("room_id", request.getRoomId())
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to get info about live room");
|
||||
}
|
||||
|
||||
var json = optional.get();
|
||||
return liveDataMapper.map(json);
|
||||
return getLiveData(request);
|
||||
} catch (TikTokProxyRequestException ignored) {}
|
||||
}
|
||||
}
|
||||
var optional = httpFactory.client(url)
|
||||
.withParam("room_id", request.getRoomId())
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
return getLiveData(request);
|
||||
}
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to get info about live room");
|
||||
}
|
||||
public LiveData.Response getLiveData(LiveData.Request request) {
|
||||
var url = TIKTOK_URL_WEBCAST + "room/info";
|
||||
var result = httpFactory.client(url)
|
||||
.withParam("room_id", request.getRoomId())
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
var json = optional.get();
|
||||
if (result.isFailure())
|
||||
throw new TikTokLiveRequestException("Unable to get info about live room"+result.toStack());
|
||||
|
||||
var json = result.getContent();
|
||||
return liveDataMapper.map(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
|
||||
HttpResponse<byte[]> credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId()));
|
||||
var result = getStartingPayload(request);
|
||||
HttpResponse<byte[]> credentialsResponse = result.getContent(); // Always guaranteed to have response
|
||||
|
||||
try {
|
||||
var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie");
|
||||
if (optionalHeader.isEmpty()) {
|
||||
throw new TikTokSignServerException("Sign server did not return the set-cookie header");
|
||||
var resultHeader = ActionResult.of(credentialsResponse.headers().firstValue("x-set-tt-cookie"));
|
||||
if (resultHeader.isFailure()) {
|
||||
logger.warning("SignServer Headers: "+request.getRoomId()+" - "+credentialsResponse.headers().map());
|
||||
throw new TikTokSignServerException("Sign server did not return the x-set-tt-cookie header"+result.toStack());
|
||||
}
|
||||
var websocketCookie = optionalHeader.get();
|
||||
var websocketCookie = resultHeader.getContent();
|
||||
var webcastResponse = WebcastResponse.parseFrom(credentialsResponse.body());
|
||||
var webSocketUrl = httpFactory
|
||||
.client(webcastResponse.getPushServer())
|
||||
@@ -185,11 +169,23 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
||||
|
||||
return new LiveConnectionData.Response(websocketCookie, webSocketUrl, webcastResponse);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse");
|
||||
throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse"+result.toStack());
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponse<byte[]> getStarterPayload(String room_id) {
|
||||
private ActionResult<HttpResponse<byte[]>> getStartingPayload(LiveConnectionData.Request request) {
|
||||
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||
if (proxyClientSettings.isEnabled()) {
|
||||
while (proxyClientSettings.hasNext()) {
|
||||
try {
|
||||
return getByteResponse(request.getRoomId());
|
||||
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
|
||||
}
|
||||
}
|
||||
return getByteResponse(request.getRoomId());
|
||||
}
|
||||
|
||||
private ActionResult<HttpResponse<byte[]>> getByteResponse(String room_id) {
|
||||
HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API)
|
||||
.withParam("client", "ttlive-java")
|
||||
.withParam("uuc", "1")
|
||||
@@ -198,24 +194,11 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
||||
if (clientSettings.getApiKey() != null)
|
||||
builder.withParam("apiKey", clientSettings.getApiKey());
|
||||
|
||||
var optional = builder.build().toResponse();
|
||||
var result = builder.build().toResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokSignServerException("Unable to get websocket connection credentials");
|
||||
}
|
||||
return optional.get();
|
||||
}
|
||||
if (result.isFailure())
|
||||
throw new TikTokSignServerException("Unable to get websocket connection credentials"+result.toStack());
|
||||
|
||||
Optional<HttpResponse<byte[]>> getOptionalProxyResponse(LiveConnectionData.Request request) {
|
||||
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||
if (proxyClientSettings.isEnabled()) {
|
||||
while (proxyClientSettings.hasNext()) {
|
||||
try {
|
||||
HttpResponse<byte[]> credentialsResponse = getStarterPayload(request.getRoomId());
|
||||
return Optional.of(credentialsResponse);
|
||||
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package io.github.jwdeveloper.tiktok.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Data
|
||||
public class ActionResult<T> {
|
||||
|
||||
private boolean success = true;
|
||||
private T content;
|
||||
private String message;
|
||||
|
||||
protected ActionResult(T object) {
|
||||
this.content = object;
|
||||
}
|
||||
|
||||
protected ActionResult(T object, boolean success) {
|
||||
this(object);
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
protected ActionResult(T object, boolean success, String message) {
|
||||
this(object, success);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public static <T> ActionResultBuilder<T> of(T content) {
|
||||
return new ActionResultBuilder<>(content);
|
||||
}
|
||||
|
||||
public static <T> ActionResult<T> of(Optional<T> optional) {
|
||||
return new ActionResult<>(optional.orElse(null), optional.isPresent());
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
return !isSuccess();
|
||||
}
|
||||
|
||||
public boolean hasMessage() {
|
||||
return message != null;
|
||||
}
|
||||
public String toStack() {
|
||||
return hasMessage() ? " - "+message : "";
|
||||
}
|
||||
|
||||
public boolean hasContent() {
|
||||
return content != null;
|
||||
}
|
||||
|
||||
public <Output> ActionResult<Output> cast(Output output) {
|
||||
return new ActionResult<>(output, this.isSuccess(), this.getMessage());
|
||||
}
|
||||
|
||||
public <Output> ActionResult<Output> cast() {
|
||||
return cast(null);
|
||||
}
|
||||
|
||||
public <U> ActionResult<U> map(Function<? super T, ? extends U> mapper) {
|
||||
return hasContent() ? cast(mapper.apply(content)) : cast();
|
||||
}
|
||||
|
||||
public static <T> ActionResult<T> success(T payload, String message) {
|
||||
return new ActionResult<>(payload, true, message);
|
||||
}
|
||||
|
||||
public static <T> ActionResult<T> success(T payload) {
|
||||
return success(payload, null);
|
||||
}
|
||||
|
||||
public static <T> ActionResult<T> success() {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
public static <T> ActionResult<T> failure(T target, String message) {
|
||||
return new ActionResult<>(target, false, message);
|
||||
}
|
||||
|
||||
public static <T> ActionResult<T> failure(String message) {
|
||||
return failure(null, message);
|
||||
}
|
||||
|
||||
public static <T> ActionResult<T> failure() {
|
||||
return failure(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.github.jwdeveloper.tiktok.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ActionResultBuilder<T>
|
||||
{
|
||||
private final T content;
|
||||
private String message;
|
||||
|
||||
public ActionResultBuilder(T content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public ActionResultBuilder<T> message(Object... messages) {
|
||||
this.message = Arrays.stream(messages).map(Object::toString).collect(Collectors.joining(" "));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActionResult<T> success() {
|
||||
return ActionResult.success(content, message);
|
||||
}
|
||||
|
||||
public ActionResult<T> failure() {
|
||||
return ActionResult.success(content, message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.github.jwdeveloper.tiktok.common;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||
|
||||
import java.util.logging.*;
|
||||
|
||||
public class LoggerFactory
|
||||
{
|
||||
public static Logger create(String name, LiveClientSettings settings) {
|
||||
Logger logger = Logger.getLogger(name);
|
||||
if (logger.getHandlers().length == 0) {
|
||||
var handler = new ConsoleHandler();
|
||||
handler.setFormatter(new Formatter()
|
||||
{
|
||||
@Override
|
||||
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("]: ");
|
||||
sb.append(ConsoleColors.WHITE_BRIGHT).append(record.getMessage());
|
||||
sb.append(ConsoleColors.RESET).append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
});
|
||||
logger.setUseParentHandlers(false);
|
||||
logger.addHandler(handler);
|
||||
logger.setLevel(settings.getLogLevel());
|
||||
if (!settings.isPrintToConsole())
|
||||
logger.setLevel(Level.OFF);
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +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.gifts;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TikTokGiftManager implements GiftManager {
|
||||
|
||||
private final Map<Integer, Gift> indexById;
|
||||
private final Map<String, Gift> indexByName;
|
||||
private final Logger logger;
|
||||
|
||||
public TikTokGiftManager(Logger logger)
|
||||
{
|
||||
indexById = new HashMap<>();
|
||||
indexByName = new HashMap<>();
|
||||
this.logger = logger;
|
||||
init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
for (var gift : Gift.values()) {
|
||||
indexById.put(gift.getId(), gift);
|
||||
indexByName.put(gift.getName(), gift);
|
||||
}
|
||||
}
|
||||
|
||||
public Gift registerGift(int id, String name, int diamondCost, Picture picture) {
|
||||
try {
|
||||
var constructor = Unsafe.class.getDeclaredConstructors()[0];
|
||||
constructor.setAccessible(true);
|
||||
var unsafe = (Unsafe) constructor.newInstance();
|
||||
Gift enumInstance = (Gift) unsafe.allocateInstance(Gift.class);
|
||||
|
||||
var field = Gift.class.getDeclaredField("id");
|
||||
field.setAccessible(true);
|
||||
field.set(enumInstance, id);
|
||||
|
||||
field = Gift.class.getDeclaredField("name");
|
||||
field.setAccessible(true);
|
||||
field.set(enumInstance, name);
|
||||
|
||||
|
||||
// EnumSet
|
||||
field = Gift.class.getDeclaredField("diamondCost");
|
||||
field.setAccessible(true);
|
||||
field.set(enumInstance, diamondCost);
|
||||
|
||||
field = Gift.class.getDeclaredField("picture");
|
||||
field.setAccessible(true);
|
||||
field.set(enumInstance, picture);
|
||||
|
||||
indexById.put(enumInstance.getId(), enumInstance);
|
||||
indexByName.put(enumInstance.getName(), enumInstance);
|
||||
|
||||
return enumInstance;
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveException("Unable to register gift: " + name + ": " + id);
|
||||
}
|
||||
}
|
||||
|
||||
public Gift findById(int giftId) {
|
||||
Gift gift = indexById.get(giftId);
|
||||
return gift == null ? Gift.UNDEFINED : gift;
|
||||
}
|
||||
|
||||
public Gift findByName(String giftName) {
|
||||
Gift gift = indexByName.get(giftName);
|
||||
return gift == null ? Gift.UNDEFINED : gift;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Gift> getGifts() {
|
||||
return indexById.values().stream().toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.github.jwdeveloper.tiktok.gifts;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TikTokGiftsManager implements GiftsManager {
|
||||
private final Map<Integer, Gift> giftsByIdIndex;
|
||||
|
||||
public TikTokGiftsManager(List<Gift> giftList)
|
||||
{
|
||||
giftsByIdIndex = giftList.stream().collect(Collectors.toConcurrentMap(Gift::getId, e -> e));
|
||||
}
|
||||
|
||||
public void attachGift(Gift gift) {
|
||||
giftsByIdIndex.put(gift.getId(), gift);
|
||||
}
|
||||
|
||||
public void attachGiftsList(List<Gift> gifts) {
|
||||
gifts.forEach(this::attachGift);
|
||||
}
|
||||
|
||||
public Gift getByName(String name) {
|
||||
return getByFilter(e -> e.getName().equalsIgnoreCase(name));
|
||||
}
|
||||
|
||||
public Gift getById(int giftId) {
|
||||
if (!giftsByIdIndex.containsKey(giftId)) {
|
||||
return Gift.UNDEFINED;
|
||||
}
|
||||
|
||||
return giftsByIdIndex.get(giftId);
|
||||
}
|
||||
|
||||
public Gift getByFilter(Predicate<Gift> filter) {
|
||||
return giftsByIdIndex.values()
|
||||
.stream()
|
||||
.filter(filter)
|
||||
.findFirst()
|
||||
.orElseGet(() -> Gift.UNDEFINED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Gift> getManyByFilter(Predicate<Gift> filter) {
|
||||
return giftsByIdIndex.values()
|
||||
.stream()
|
||||
.filter(filter)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<Gift> toList() {
|
||||
return giftsByIdIndex.values().stream().toList();
|
||||
}
|
||||
|
||||
public Map<Integer, Gift> toMap() {
|
||||
return Collections.unmodifiableMap(giftsByIdIndex);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.common.ActionResult;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.HttpClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -35,35 +36,25 @@ import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class HttpClient {
|
||||
|
||||
protected final HttpClientSettings httpClientSettings;
|
||||
protected final String url;
|
||||
private final Pattern pattern = Pattern.compile("charset=(.*?)(?=&|$)");
|
||||
|
||||
public Optional<HttpResponse<byte[]>> toResponse() {
|
||||
public ActionResult<HttpResponse<byte[]>> toResponse() {
|
||||
var client = prepareClient();
|
||||
var request = prepareGetRequest();
|
||||
try {
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (response.statusCode() != 200) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(response);
|
||||
} catch (Exception e) {
|
||||
var result = ActionResult.of(response);
|
||||
return response.statusCode() != 200 ? result.message("HttpResponse Code: ", response.statusCode()).failure() : result.success();
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> toJsonResponse() {
|
||||
var optional = toResponse();
|
||||
if (optional.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
var response = optional.get();
|
||||
var body = response.body();
|
||||
var charset = charsetFrom(response.headers());
|
||||
return Optional.of(new String(body,charset));
|
||||
public ActionResult<String> toJsonResponse() {
|
||||
return toResponse().map(content -> new String(content.body(), charsetFrom(content.headers())));
|
||||
}
|
||||
|
||||
private Charset charsetFrom(HttpHeaders headers) {
|
||||
@@ -80,13 +71,8 @@ public class HttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<byte[]> toBinaryResponse() {
|
||||
var optional = toResponse();
|
||||
if (optional.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
var body = optional.get().body();
|
||||
return Optional.of(body);
|
||||
public ActionResult<byte[]> toBinaryResponse() {
|
||||
return toResponse().map(HttpResponse::body);
|
||||
}
|
||||
|
||||
public URI toUrl() {
|
||||
|
||||
@@ -78,7 +78,8 @@ public class HttpClientBuilder {
|
||||
}
|
||||
|
||||
public HttpClient build() {
|
||||
if (httpClientSettings.getProxyClientSettings().isEnabled())
|
||||
var proxyClientSettings = httpClientSettings.getProxyClientSettings();
|
||||
if (proxyClientSettings.isEnabled() && proxyClientSettings.hasNext())
|
||||
return new HttpProxyClient(httpClientSettings, url);
|
||||
return new HttpClient(httpClientSettings, url);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.common.ActionResult;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.*;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||
|
||||
@@ -35,8 +36,8 @@ import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class HttpProxyClient extends HttpClient
|
||||
{
|
||||
public class HttpProxyClient extends HttpClient {
|
||||
|
||||
private final ProxyClientSettings proxySettings;
|
||||
|
||||
public HttpProxyClient(HttpClientSettings httpClientSettings, String url) {
|
||||
@@ -44,14 +45,14 @@ public class HttpProxyClient extends HttpClient
|
||||
this.proxySettings = httpClientSettings.getProxyClientSettings();
|
||||
}
|
||||
|
||||
public Optional<HttpResponse<byte[]>> toResponse() {
|
||||
public ActionResult<HttpResponse<byte[]>> toResponse() {
|
||||
return switch (proxySettings.getType()) {
|
||||
case HTTP, DIRECT -> handleHttpProxyRequest();
|
||||
default -> handleSocksProxyRequest();
|
||||
};
|
||||
}
|
||||
|
||||
public Optional<HttpResponse<byte[]>> handleHttpProxyRequest() {
|
||||
public ActionResult<HttpResponse<byte[]>> handleHttpProxyRequest() {
|
||||
var builder = java.net.http.HttpClient.newBuilder()
|
||||
.followRedirects(java.net.http.HttpClient.Redirect.NORMAL)
|
||||
.cookieHandler(new CookieManager())
|
||||
@@ -69,7 +70,7 @@ public class HttpProxyClient extends HttpClient
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (response.statusCode() != 200)
|
||||
continue;
|
||||
return Optional.of(response);
|
||||
return ActionResult.success(response);
|
||||
} catch (HttpConnectTimeoutException | ConnectException e) {
|
||||
if (proxySettings.isAutoDiscard())
|
||||
proxySettings.remove();
|
||||
@@ -85,7 +86,7 @@ public class HttpProxyClient extends HttpClient
|
||||
throw new TikTokLiveRequestException("No more proxies available!");
|
||||
}
|
||||
|
||||
private Optional<HttpResponse<byte[]>> handleSocksProxyRequest() {
|
||||
private ActionResult<HttpResponse<byte[]>> handleSocksProxyRequest() {
|
||||
try {
|
||||
SSLContext sc = SSLContext.getInstance("SSL");
|
||||
sc.init(null, new TrustManager[]{ new X509TrustManager() {
|
||||
@@ -117,7 +118,7 @@ public class HttpProxyClient extends HttpClient
|
||||
|
||||
var response = createHttpResponse(body, toUrl(), responseInfo);
|
||||
|
||||
return Optional.of(response);
|
||||
return ActionResult.success(response);
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
|
||||
return super.toResponse();
|
||||
@@ -133,11 +134,10 @@ public class HttpProxyClient extends HttpClient
|
||||
// Should never be reached!
|
||||
System.out.println("handleSocksProxyRequest: If you see this, message us on discord!");
|
||||
e.printStackTrace();
|
||||
return Optional.empty();
|
||||
} catch (TikTokLiveRequestException e) {
|
||||
e.printStackTrace();
|
||||
return Optional.empty();
|
||||
}
|
||||
return ActionResult.failure();
|
||||
}
|
||||
|
||||
private ResponseInfo createResponseInfo(int code, Map<String, List<String>> headers) {
|
||||
|
||||
@@ -21,51 +21,35 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.http.mappers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.GiftsData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GiftsDataMapper {
|
||||
public GiftsData.Response map(String json) {
|
||||
var parsedJson = JsonParser.parseString(json);
|
||||
var jsonObject = parsedJson.getAsJsonObject();
|
||||
|
||||
if (!jsonObject.has("data")) {
|
||||
return new GiftsData.Response(json, new ArrayList<>());
|
||||
}
|
||||
var dataElement = jsonObject.getAsJsonObject("data");
|
||||
if (!dataElement.has("gifts")) {
|
||||
return new GiftsData.Response(json, new ArrayList<>());
|
||||
}
|
||||
|
||||
var gifts = dataElement.get("gifts").getAsJsonArray()
|
||||
.asList()
|
||||
.stream()
|
||||
.map(this::mapSingleGift)
|
||||
var gifts = jsonObject.entrySet()
|
||||
.parallelStream()
|
||||
.map(e -> mapSingleGift(e.getValue()))
|
||||
.toList();
|
||||
|
||||
return new GiftsData.Response(json, gifts);
|
||||
}
|
||||
|
||||
|
||||
private GiftsData.GiftModel mapSingleGift(JsonElement jsonElement) {
|
||||
var id = jsonElement.getAsJsonObject().get("id").getAsInt();
|
||||
var name = jsonElement.getAsJsonObject().get("name").getAsString();
|
||||
var diamondCost = jsonElement.getAsJsonObject().get("diamond_count").getAsInt();
|
||||
var image = jsonElement.getAsJsonObject()
|
||||
.get("image").getAsJsonObject()
|
||||
.get("url_list").getAsJsonArray().get(0).getAsString();
|
||||
private Gift mapSingleGift(JsonElement jsonElement) {
|
||||
var jsonObject = jsonElement.getAsJsonObject();
|
||||
|
||||
if (image.endsWith(".webp")) {
|
||||
image = image.replace(".webp", ".jpg");
|
||||
}
|
||||
var gift = new GiftsData.GiftModel();
|
||||
gift.setId(id);
|
||||
gift.setName(name);
|
||||
gift.setDiamondCost(diamondCost);
|
||||
gift.setImage(image);
|
||||
|
||||
return gift;
|
||||
var id = jsonObject.get("id").getAsInt();
|
||||
var name = jsonObject.get("name").getAsString();
|
||||
var diamondCost = jsonObject.get("diamondCost").getAsInt();
|
||||
var image = jsonObject.get("image").getAsString();
|
||||
return new Gift(id, name, diamondCost, new Picture(image), jsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,32 +24,29 @@ package io.github.jwdeveloper.tiktok.mappers.handlers;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
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.gift.*;
|
||||
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.data.models.gifts.*;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.mappers.TikTokMapperHelper;
|
||||
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import lombok.SneakyThrows;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class TikTokGiftEventHandler {
|
||||
private final GiftManager giftManager;
|
||||
private final Map<Long, WebcastGiftMessage> giftsMessages;
|
||||
private final TikTokRoomInfo tikTokRoomInfo;
|
||||
|
||||
public TikTokGiftEventHandler(GiftManager giftManager, TikTokRoomInfo tikTokRoomInfo) {
|
||||
this.giftManager = giftManager;
|
||||
private final GiftsManager giftsManager;
|
||||
|
||||
public TikTokGiftEventHandler(GiftsManager giftsManager, TikTokRoomInfo tikTokRoomInfo) {
|
||||
giftsMessages = new HashMap<>();
|
||||
this.tikTokRoomInfo = tikTokRoomInfo;
|
||||
this.giftsManager = giftsManager;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -114,24 +111,38 @@ public class TikTokGiftEventHandler {
|
||||
|
||||
private Gift getGiftObject(WebcastGiftMessage giftMessage) {
|
||||
var giftId = (int) giftMessage.getGiftId();
|
||||
var gift = giftManager.findById(giftId);
|
||||
var gift = giftsManager.getById(giftId);
|
||||
if (gift == Gift.UNDEFINED)
|
||||
gift = giftsManager.getByName(giftMessage.getGift().getName());
|
||||
if (gift == Gift.UNDEFINED) {
|
||||
gift = giftManager.findByName(giftMessage.getGift().getName());
|
||||
}
|
||||
if (gift == Gift.UNDEFINED) {
|
||||
gift = giftManager.registerGift(
|
||||
giftId,
|
||||
gift = new Gift(giftId,
|
||||
giftMessage.getGift().getName(),
|
||||
giftMessage.getGift().getDiamondCount(),
|
||||
Picture.map(giftMessage.getGift().getImage()));
|
||||
|
||||
giftsManager.attachGift(gift);
|
||||
}
|
||||
|
||||
if (gift.getPicture().getLink().endsWith(".webp")) {
|
||||
if (gift.getPicture().getLink().endsWith(".webp"))
|
||||
{
|
||||
updatePicture(gift, giftMessage);
|
||||
}
|
||||
|
||||
return gift;
|
||||
}
|
||||
|
||||
// TODO-kohlerpop1: I do not think this method is needed for any reason?
|
||||
// TODO response:
|
||||
|
||||
/**
|
||||
* Some generated gifts in JSON file contains .webp image format,
|
||||
* that's bad since java by the defult is not supporing .webp and when URL is
|
||||
* converted to Java.io.Image then image is null
|
||||
*
|
||||
* However, TikTok in GiftWebcast event always has image in .jpg format,
|
||||
* so I take advantage of it and swap .webp url with .jpg url
|
||||
*
|
||||
*/
|
||||
|
||||
private void updatePicture(Gift gift, WebcastGiftMessage webcastGiftMessage) {
|
||||
try {
|
||||
@@ -145,4 +156,4 @@ public class TikTokGiftEventHandler {
|
||||
throw new TikTokLiveException("Unable to update picture in gift: " + gift.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.gifts;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftOld;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -35,38 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
public class TikTokGiftManagerTest {
|
||||
|
||||
@InjectMocks
|
||||
TikTokGiftManager giftManager;
|
||||
|
||||
private static final Picture rosePicture = new Picture("https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png");
|
||||
|
||||
@Test
|
||||
void registerGift() {
|
||||
var fakeGift = giftManager.registerGift(123, "Fake gift", 123123, rosePicture);
|
||||
var gifts = giftManager.getGifts();
|
||||
var optional = gifts.stream().filter(r -> r == fakeGift).findFirst();
|
||||
Assertions.assertTrue(optional.isPresent());
|
||||
// Assertions.assertNotNull(optional.get().name());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findById() {
|
||||
var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture);
|
||||
var result = giftManager.findById(target.getId());
|
||||
Assertions.assertEquals(target, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByName() {
|
||||
var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture);
|
||||
var result = giftManager.findByName(target.getName());
|
||||
Assertions.assertEquals(target, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getGifts() {
|
||||
Assertions.assertEquals(Gift.values().length, giftManager.getGifts().size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,8 +26,9 @@ import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
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.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokGiftEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.messages.data.GiftStruct;
|
||||
import io.github.jwdeveloper.tiktok.messages.data.Image;
|
||||
@@ -38,6 +39,7 @@ import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
@@ -46,13 +48,12 @@ class TikTokGiftEventHandlerTest {
|
||||
|
||||
public static TikTokGiftEventHandler handler;
|
||||
|
||||
|
||||
@BeforeAll
|
||||
public void before() {
|
||||
var manager = new TikTokGiftManager(Logger.getLogger("x"));
|
||||
var manager = new TikTokGiftsManager(List.of());
|
||||
var info = new TikTokRoomInfo();
|
||||
info.setHost(new io.github.jwdeveloper.tiktok.data.models.users.User(123L, "test", new Picture("")));
|
||||
manager.registerGift(123, "example", 123, new Picture("image.webp"));
|
||||
manager.attachGift(new Gift(123, "example", 123, "image.webp"));
|
||||
handler = new TikTokGiftEventHandler(manager, info);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.extension.collector.TikTokLiveCollector;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -49,7 +48,7 @@ public class CollectorExample {
|
||||
collector.connectDatabase();
|
||||
|
||||
var users = List.of("tehila_723", "dino123597", "domaxyzx", "dash4214", "obserwacje_live");
|
||||
var sessionTag = "Tag1";
|
||||
Map<String, Object> additionalDataFields = Map.of("sessionTag", "ExampleTag");
|
||||
for (var user : users) {
|
||||
TikTokLive.newClient(user)
|
||||
.configure(liveClientSettings ->
|
||||
@@ -60,8 +59,9 @@ public class CollectorExample {
|
||||
{
|
||||
event.getException().printStackTrace();
|
||||
})
|
||||
.addListener(collector.newListener(Map.of("sessionTag", sessionTag), document ->
|
||||
.addListener(collector.newListener(additionalDataFields, document ->
|
||||
{
|
||||
//filtering document data before it is inserted to database
|
||||
if (document.get("dataType") == "message") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -23,10 +23,7 @@
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
public class CustomEventExample {
|
||||
@@ -40,13 +37,13 @@ public class CustomEventExample {
|
||||
Gift gift;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args)
|
||||
{
|
||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||
.configure(clientSettings ->
|
||||
{
|
||||
clientSettings.setPrintToConsole(true);
|
||||
})
|
||||
|
||||
.onGift((liveClient, event) ->
|
||||
{
|
||||
if (event.getGift().getDiamondCost() > 100)
|
||||
@@ -64,4 +61,4 @@ public class CustomEventExample {
|
||||
})
|
||||
.buildAndConnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +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.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
|
||||
public class CustomGiftExample {
|
||||
/**
|
||||
* If you can't find your wanted Gift inside Gift enum register it manually
|
||||
*/
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
LiveClient client = TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||
.onConnected((liveClient, event) ->
|
||||
{
|
||||
liveClient.disconnect();
|
||||
})
|
||||
.onWebsocketResponse((liveClient, event) ->
|
||||
{
|
||||
var packets =event.getResponse().getMessagesList();
|
||||
for(var packet : packets)
|
||||
{
|
||||
var name = packet.getMethod();
|
||||
var data = packet.getPayload();
|
||||
if(name.equals("WebcastGiftMessage"))
|
||||
{
|
||||
// var message = WebcastGiftMessage.parseFrom(data);
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
.onGift((liveClient, event) ->
|
||||
{
|
||||
liveClient.getLogger().info(event.getGift().getName());
|
||||
}).build();
|
||||
|
||||
GiftManager giftManager = client.getGiftManager();
|
||||
|
||||
//If you can't find your wanted Gift inside Gift enum register it manually
|
||||
giftManager.registerGift(123, "my custom gift", 69, new Picture("https://as2.ftcdn.net/v2/jpg/03/03/62/45/1000_F_303624505_u0bFT1Rnoj8CMUSs8wMCwoKlnWlh5Jiq.jpg"));
|
||||
|
||||
|
||||
//You can also override existing gift, for example Rose has Id 5655
|
||||
//We can make our custom gift appear in the event instead of rose
|
||||
giftManager.registerGift(5655, "custom-rose", 999, new Picture("https://as2.ftcdn.net/v2/jpg/03/03/62/45/1000_F_303624505_u0bFT1Rnoj8CMUSs8wMCwoKlnWlh5Jiq.jpg"));
|
||||
|
||||
client.connect();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class GiftsExample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
var giftsManager = TikTokLive.gifts();
|
||||
|
||||
|
||||
|
||||
var giftsList = giftsManager.toList();
|
||||
for (var gift : giftsList) {
|
||||
System.out.println("Gift: " + gift);
|
||||
}
|
||||
|
||||
var giftsMap = giftsManager.toMap();
|
||||
for (var entry : giftsMap.entrySet()) {
|
||||
System.out.println("GiftId: " + entry.getKey() + " Gift: " + entry.getValue());
|
||||
}
|
||||
|
||||
System.out.println("total number of gifts: " + giftsManager.toList().size());
|
||||
|
||||
var giftRose = giftsManager.getById(5655);
|
||||
var giftRoseByName = giftsManager.getByName("Rose");
|
||||
var giftByFilter = giftsManager.getByFilter(e -> e.getDiamondCost() > 50);
|
||||
|
||||
var giftsByFilter = giftsManager.getManyByFilter(e -> e.getDiamondCost() > 100);
|
||||
System.out.println("total number of gifts with cost higher then 100: " + giftsByFilter.size());
|
||||
/**
|
||||
* In case searched gift not exists getByName returns you Gift.UNDEFINED
|
||||
*/
|
||||
var undefiedGift = giftsManager.getByName("GIFT WITH WRONG NAME");
|
||||
|
||||
|
||||
var customGift = new Gift(123213213, "Custom gift", 50, "https://images.pexels.com/photos/2071882/pexels-photo-2071882.jpeg?cs=srgb&dl=pexels-wojciech-kumpicki-2071882.jpg&fm=jpg");
|
||||
giftsManager.attachGift(customGift);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -84,12 +84,12 @@ public class ListenerExample
|
||||
|
||||
@TikTokEventObserver
|
||||
public void onGift(LiveClient liveClient, TikTokGiftEvent event) {
|
||||
var message = switch (event.getGift()) {
|
||||
case ROSE -> "Thanks :)";
|
||||
case APPETIZERS -> ":OO";
|
||||
case APRIL -> ":D";
|
||||
case TIKTOK -> ":P";
|
||||
case CAP -> ":F";
|
||||
var message = switch (event.getGift().getName()) {
|
||||
case "ROSE" -> "Thanks :)";
|
||||
case "APPETIZERS" -> ":OO";
|
||||
case "APRIL" -> ":D";
|
||||
case "TIKTOK" -> ":P";
|
||||
case "CAP" -> ":F";
|
||||
default -> ":I";
|
||||
};
|
||||
liveClient.getLogger().info(message);
|
||||
@@ -115,4 +115,4 @@ public class ListenerExample
|
||||
""");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,13 @@ import java.time.Duration;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SimpleExample {
|
||||
public static String TIKTOK_HOSTNAME = "dash4114";
|
||||
public static String TIKTOK_HOSTNAME = "kvadromama_marina1";
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
showLogo();
|
||||
|
||||
var gifts = TikTokLive.gifts();
|
||||
|
||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||
.configure(clientSettings ->
|
||||
@@ -84,10 +85,10 @@ public class SimpleExample {
|
||||
})
|
||||
.onGift((liveClient, event) ->
|
||||
{
|
||||
switch (event.getGift()) {
|
||||
case ROSE -> print(ConsoleColors.RED, "Rose!");
|
||||
case GG -> print(ConsoleColors.YELLOW, " GOOD GAME!");
|
||||
case TIKTOK -> print(ConsoleColors.CYAN, "Thanks for TikTok");
|
||||
switch (event.getGift().getName()) {
|
||||
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());
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -52,7 +52,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<artifactId>Tools</artifactId>
|
||||
<artifactId>Tools-ReadmeGenerator</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -24,8 +24,8 @@ package io.github.jwdeveloper.tiktok.tools.collector.client;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.github.jwdeveloper.tiktok.FilesUtility;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
||||
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -25,9 +25,9 @@ package io.github.jwdeveloper.tiktok.tools.tester.mockClient;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
|
||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.LiveClientMock;
|
||||
@@ -87,15 +87,13 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder {
|
||||
tiktokRoomInfo.setHostName(clientSettings.getHostName());
|
||||
|
||||
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
||||
var giftManager = new TikTokGiftManager(logger);
|
||||
var mapper = createMapper(giftManager, tiktokRoomInfo);
|
||||
var mapper = createMapper(new TikTokGiftsManager(List.of()), tiktokRoomInfo);
|
||||
var handler = new TikTokLiveMessageHandler(tikTokEventHandler, mapper);
|
||||
var webSocketClient = new WebsocketClientMock(logger, responses, handler);
|
||||
|
||||
return new LiveClientMock(tiktokRoomInfo,
|
||||
new TikTokLiveHttpClient(),
|
||||
webSocketClient,
|
||||
giftManager,
|
||||
tikTokEventHandler,
|
||||
clientSettings,
|
||||
listenerManager,
|
||||
|
||||
@@ -25,12 +25,13 @@ package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveClient;
|
||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class LiveClientMock extends TikTokLiveClient {
|
||||
@@ -41,16 +42,15 @@ public class LiveClientMock extends TikTokLiveClient {
|
||||
TikTokRoomInfo tikTokLiveMeta,
|
||||
TikTokLiveHttpClient httpClient,
|
||||
WebsocketClientMock webSocketClient,
|
||||
TikTokGiftManager tikTokGiftManager,
|
||||
TikTokLiveEventHandler tikTokEventHandler,
|
||||
LiveClientSettings clientSettings,
|
||||
TikTokListenersManager listenersManager,
|
||||
Logger logger) {
|
||||
super(
|
||||
new TikTokGiftsManager(List.of()),
|
||||
tikTokLiveMeta,
|
||||
httpClient,
|
||||
webSocketClient,
|
||||
tikTokGiftManager,
|
||||
tikTokEventHandler,
|
||||
clientSettings,
|
||||
listenersManager,
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.webviewer;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkLayerMessage;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMessage;
|
||||
import io.github.jwdeveloper.tiktok.tools.TikTokLiveTools;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -23,12 +23,6 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<artifactId>Tools</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jwdeveloper.Descrabble</groupId>
|
||||
<artifactId>Descrabble-Full</artifactId>
|
||||
@@ -41,5 +35,11 @@
|
||||
<version>0.9.12</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<artifactId>Client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -41,11 +41,12 @@ public class CodeExample {
|
||||
TikTokLive.newClient("bangbetmenygy")
|
||||
.onGift((liveClient, event) ->
|
||||
{
|
||||
String message = switch (event.getGift()) {
|
||||
case ROSE -> "ROSE!";
|
||||
case GG -> "GOOD GAME";
|
||||
case TIKTOK -> "Ye";
|
||||
case CORGI -> "Nice gift";
|
||||
String message = switch (event.getGift().getName())
|
||||
{
|
||||
case "Rose" -> "ROSE!";
|
||||
case "Good game" -> "GOOD GAME";
|
||||
case "Ye" -> "Ye";
|
||||
case "Nice gift" -> "Nice gift";
|
||||
default -> "Thank you for " + event.getGift().getName();
|
||||
};
|
||||
System.out.println(event.getUser().getProfileName() + " sends " + message);
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
||||
import io.github.jwdeveloper.tiktok.utils.TemplateUtility;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -27,8 +27,7 @@ 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;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
@@ -20,7 +21,7 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.utils;
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -37,7 +38,7 @@ public class FilesUtility
|
||||
public static List<Path> getFiles(String input) {
|
||||
Path path = Paths.get(input);
|
||||
try (Stream<Path> paths = Files.list(path)) {
|
||||
return paths.filter(Files::isRegularFile).toList();
|
||||
return paths.filter(Files::isRegularFile).toList();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -96,13 +97,13 @@ public class FilesUtility
|
||||
file.createNewFile();
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String loadFileContent(String path) {
|
||||
public static String loadFileContent(String path) {
|
||||
ensureFile(path);
|
||||
Path pathh = Paths.get(path);
|
||||
try {
|
||||
@@ -22,7 +22,6 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
* 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 io.github.jwdeveloper.tiktok.utils.TemplateUtility;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ReadmeGenerator {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
@@ -20,24 +21,21 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.utils;
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TemplateUtility
|
||||
{
|
||||
public class TemplateUtility {
|
||||
public static String generateTemplate(String template, Map<String, Object> values) {
|
||||
for(var entry : values.entrySet())
|
||||
{
|
||||
template = doReplacement(template,entry.getKey(), entry.getValue().toString());
|
||||
for (var entry : values.entrySet()) {
|
||||
template = doReplacement(template, entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
|
||||
private static String doReplacement(String template, String keyword, String value)
|
||||
{
|
||||
var key = "(\\{\\{)"+keyword+"(}})";
|
||||
private static String doReplacement(String template, String keyword, String value) {
|
||||
var key = "(\\{\\{)" + keyword + "(}})";
|
||||
return template.replaceAll(key, value);
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.3.0-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>Tools</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.22</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup</groupId>
|
||||
<artifactId>javapoet</artifactId>
|
||||
<version>1.13.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.9.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<artifactId>API</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.13.1</version> <!-- Check for the latest version -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<artifactId>Client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.14.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -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.events_generator.EventGeneratorSettings;
|
||||
import io.github.jwdeveloper.tiktok.events_generator.EventsGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class EventsGeneratorRun {
|
||||
|
||||
private static boolean lock = false;
|
||||
|
||||
//Run objects
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
if(lock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//generatesObjects()
|
||||
// generateEventsMessages();
|
||||
}
|
||||
|
||||
|
||||
private static void generatesEventsObjects() throws IOException {
|
||||
var settings = new EventGeneratorSettings();
|
||||
settings.setTikTokEvent(false);
|
||||
settings.setInputDictionary("C:\\Users\\ja\\RiderProjects\\TikTokLiveSharp\\TikTokLiveSharp\\Events\\Objects");
|
||||
settings.setOutputDictionary("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\events\\objects");
|
||||
var generator = new EventsGenerator();
|
||||
generator.compile(settings);
|
||||
}
|
||||
|
||||
private static void generateEventsMessages() throws IOException {
|
||||
var settings = new EventGeneratorSettings();
|
||||
settings.setTikTokEvent(true);
|
||||
settings.setPrefix("TikTok");
|
||||
settings.setEndFix("Event");
|
||||
settings.setInputDictionary("C:\\Users\\ja\\RiderProjects\\TikTokLiveSharp\\TikTokLiveSharp\\Events\\Messages");
|
||||
settings.setOutputDictionary("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\events\\messages");
|
||||
var generator = new EventsGenerator();
|
||||
generator.compile(settings);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,42 +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.events_generator.EventGeneratorSettings;
|
||||
import io.github.jwdeveloper.tiktok.intefacee.EventsInterfaceGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class EventsInterfaceGeneratorRun
|
||||
{
|
||||
public static void main(String[] args) throws IOException {
|
||||
var settings = new EventGeneratorSettings();
|
||||
settings.setTikTokEvent(true);
|
||||
settings.setPrefix("TikTok");
|
||||
settings.setEndFix("Event");
|
||||
settings.setInputDictionary("C:\\Users\\ja\\RiderProjects\\TikTokLiveSharp\\TikTokLiveSharp\\Events\\Messages");
|
||||
settings.setOutputDictionary("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\events\\messages");
|
||||
var generator = new EventsInterfaceGenerator();
|
||||
generator.compile(settings);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +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.events_generator;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class CSharpClassInfo
|
||||
{
|
||||
private String className;
|
||||
private List<FieldInfo> fields = new ArrayList<>();
|
||||
private List<ConstructorInfo> constructors = new ArrayList<>();
|
||||
|
||||
public void addField(String type, String fields)
|
||||
{
|
||||
this.fields.add(new FieldInfo(type,fields));
|
||||
}
|
||||
|
||||
public void addConstructor(List<FieldInfo> arguments)
|
||||
{
|
||||
this.constructors.add(new ConstructorInfo(arguments));
|
||||
}
|
||||
|
||||
public record FieldInfo(String type, String name){};
|
||||
|
||||
public record ConstructorInfo(List<FieldInfo> arguemtns){};
|
||||
}
|
||||
@@ -1,124 +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.events_generator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CSharpClassParser {
|
||||
private CSharpClassInfo classInfo;
|
||||
|
||||
public CSharpClassInfo parse(Path filePath) throws IOException {
|
||||
classInfo = new CSharpClassInfo();
|
||||
|
||||
List<String> lines = Files.readAllLines(filePath);
|
||||
String content = String.join("\n", lines);
|
||||
parseClassName(content);
|
||||
parseFields(content);
|
||||
parseConstructors(content);
|
||||
return classInfo;
|
||||
}
|
||||
|
||||
|
||||
private void parseClassName(String content) {
|
||||
Pattern pattern = Pattern.compile("\\b(?:sealed )?class\\s+(\\w+)");
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
if (matcher.find()) {
|
||||
classInfo.setClassName(matcher.group(1));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFields(String content) {
|
||||
Pattern pattern = Pattern.compile("\\b(public|private|protected)\\s+(readonly\\s+)?(\\w+\\.?\\w*)\\s+(\\w+);");
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
while (matcher.find()) {
|
||||
var typeName = mapTypeToJava(matcher.group(3));
|
||||
var name = lowerCaseFirstLetter(matcher.group(4));
|
||||
classInfo.addField(typeName, name);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseConstructors(String content) {
|
||||
Pattern pattern = Pattern.compile("\\b(public|private|protected|internal)\\s+" + classInfo.getClassName() + "\\s*\\(([^)]*)\\)");
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
while (matcher.find()) {
|
||||
List<CSharpClassInfo.FieldInfo> args = new ArrayList<>();
|
||||
String[] arguments = matcher.group(2).split(",");
|
||||
for (String argument : arguments) {
|
||||
if (argument.trim().length() > 0) {
|
||||
String[] parts = argument.trim().split("\\s+");
|
||||
|
||||
if (parts.length != 2) {
|
||||
args.add(new CSharpClassInfo.FieldInfo("Object", "error"));
|
||||
continue;
|
||||
}
|
||||
var typeName = mapTypeToJava(parts[0]);
|
||||
var name = lowerCaseFirstLetter(parts[1]);
|
||||
args.add(new CSharpClassInfo.FieldInfo(typeName, name));
|
||||
}
|
||||
}
|
||||
classInfo.addConstructor(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String mapTypeToJava(String type) {
|
||||
if (type.equals("string")) {
|
||||
return "String";
|
||||
}
|
||||
if (type.equals("uint")) {
|
||||
return "Integer";
|
||||
}
|
||||
if (type.equals("int")) {
|
||||
return "Integer";
|
||||
}
|
||||
if (type.equals("ulong")) {
|
||||
return "Long";
|
||||
}
|
||||
if (type.equals("bool")) {
|
||||
return "Boolean";
|
||||
}
|
||||
if (type.contains("Models.Protobuf.Objects")) {
|
||||
return type.replace("Models.Protobuf.Objects", "io.github.jwdeveloper.tiktok.messages");
|
||||
}
|
||||
|
||||
if(type.contains("Objects."))
|
||||
{
|
||||
return type.replace("Objects.","io.github.jwdeveloper.tiktok.events.objects.");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static String lowerCaseFirstLetter(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return str; // Return original string if it is empty or null
|
||||
}
|
||||
return Character.toLowerCase(str.charAt(0)) + str.substring(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +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.events_generator;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class EventGeneratorSettings
|
||||
{
|
||||
private String inputDictionary;
|
||||
|
||||
private String outputDictionary;
|
||||
private List<String> ignoredFiles = new ArrayList<>();
|
||||
|
||||
private String prefix;
|
||||
|
||||
private String endFix;
|
||||
|
||||
private boolean isTikTokEvent;
|
||||
|
||||
public void addIgnoredClass(String name)
|
||||
{
|
||||
ignoredFiles.add(name);
|
||||
}
|
||||
}
|
||||
@@ -1,79 +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.events_generator;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class EventsGenerator
|
||||
{
|
||||
|
||||
|
||||
public void compile(EventGeneratorSettings settings) throws IOException {
|
||||
var files = FilesUtility.getFiles(settings.getInputDictionary());
|
||||
|
||||
var packageName = convertToPackageName(settings.getOutputDictionary());
|
||||
for(var file : files)
|
||||
{
|
||||
var fileName = file.getFileName().toString();
|
||||
if(settings.getIgnoredFiles().contains(fileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(fileName.contains("meta"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var parser = new CSharpClassParser();
|
||||
var cSharpClass =parser.parse(file);
|
||||
|
||||
var name = settings.getPrefix()+cSharpClass.getClassName()+settings.getEndFix();
|
||||
cSharpClass.setClassName(name);
|
||||
var javaClassGenerator = new JavaClassGenerator();
|
||||
|
||||
|
||||
var result =javaClassGenerator.generate(cSharpClass, packageName,settings);
|
||||
System.out.println(result);
|
||||
|
||||
var path = settings.getOutputDictionary()+ File.separator+cSharpClass.getClassName()+".java";
|
||||
FilesUtility.saveFile(path, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static String convertToPackageName(String path) {
|
||||
String marker = "src\\main\\java\\";
|
||||
int index = path.indexOf(marker);
|
||||
|
||||
if (index != -1) {
|
||||
String packagePath = path.substring(index + marker.length());
|
||||
return packagePath.replace('\\', '.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +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.events_generator;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
|
||||
public class JavaClassGenerator {
|
||||
public String generate(CSharpClassInfo cSharpClassInfo, String packageName, EventGeneratorSettings settings) {
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(cSharpClassInfo.getClassName())
|
||||
.addAnnotation(Getter.class);
|
||||
if (settings.isTikTokEvent()) {
|
||||
classBuilder.superclass(TikTokEvent.class);
|
||||
}
|
||||
classBuilder.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
// Generate fields
|
||||
for (var field : cSharpClassInfo.getFields()) {
|
||||
|
||||
FieldSpec fieldSpec = FieldSpec.builder(ClassName.bestGuess(field.type()), field.name(), Modifier.PRIVATE).build();
|
||||
classBuilder.addField(fieldSpec);
|
||||
}
|
||||
|
||||
// Generate constructors
|
||||
for (var constructor : cSharpClassInfo.getConstructors()) {
|
||||
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder();
|
||||
|
||||
if(settings.isTikTokEvent())
|
||||
{
|
||||
constructorBuilder.addStatement("super(msg.getHeader());") ;
|
||||
}
|
||||
|
||||
constructorBuilder.addModifiers(Modifier.PUBLIC);
|
||||
for (var arg : constructor.arguemtns()) {
|
||||
constructorBuilder.addParameter(ClassName.bestGuess(arg.type()), arg.name());
|
||||
}
|
||||
classBuilder.addMethod(constructorBuilder.build());
|
||||
}
|
||||
|
||||
// Generate Java class
|
||||
TypeSpec javaClass = classBuilder.build();
|
||||
var result = JavaFile.builder(packageName, javaClass).build();
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,170 +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.gifts;
|
||||
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLive;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftDto;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class GenerateGiftsEnum {
|
||||
|
||||
public static void main(String args[]) throws IOException {
|
||||
|
||||
|
||||
|
||||
TikTokLive.newClient("X")
|
||||
.configure(liveClientSettings ->
|
||||
{
|
||||
var httpSetting = liveClientSettings.getHttpSettings();
|
||||
httpSetting.setTimeout(Duration.ofSeconds(12));
|
||||
});
|
||||
|
||||
var downloader = new GiftsDownloader();
|
||||
var gifts = downloader.getGiftsFromFile();
|
||||
for (var link : gifts) {
|
||||
System.out.println(link.getImage());
|
||||
}
|
||||
var groupedByName = gifts.stream().collect(Collectors.groupingBy(GiftDto::getName));
|
||||
System.out.println("Total gifts" + gifts.size());
|
||||
var result = generate(groupedByName);
|
||||
result.writeTo(new File("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java"));
|
||||
System.out.println("DONE");
|
||||
}
|
||||
|
||||
|
||||
public static JavaFile generate(Map<String, List<GiftDto>> giftInfoMap) {
|
||||
var enumBuilder = TypeSpec.enumBuilder("Gift")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(Getter.class)
|
||||
.addField(int.class, "id", Modifier.PRIVATE, Modifier.FINAL)
|
||||
.addField(String.class, "name", Modifier.PRIVATE, Modifier.FINAL)
|
||||
.addField(int.class, "diamondCost", Modifier.PRIVATE, Modifier.FINAL)
|
||||
.addField(Picture.class, "picture", Modifier.PRIVATE, Modifier.FINAL);
|
||||
|
||||
var constructor = MethodSpec.constructorBuilder()
|
||||
.addModifiers(Modifier.PRIVATE)
|
||||
.addParameter(int.class, "id")
|
||||
.addParameter(String.class, "name")
|
||||
.addParameter(int.class, "diamondCost")
|
||||
.addParameter(String.class, "pictureLink")
|
||||
.addStatement("this.id = id")
|
||||
.addStatement("this.name = name")
|
||||
.addStatement("this.diamondCost = diamondCost")
|
||||
.addStatement("this.picture = new Picture(pictureLink)")
|
||||
.build();
|
||||
|
||||
var inRangeMethod = MethodSpec.methodBuilder("hasDiamondCostRange")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(int.class, "minimalCost")
|
||||
.addParameter(int.class, "maximalCost")
|
||||
.addStatement(" return diamondCost >= minimalCost && diamondCost <= maximalCost")
|
||||
.returns(boolean.class);
|
||||
|
||||
var hasCostMethod = MethodSpec.methodBuilder("hasDiamondCost")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(int.class, "cost")
|
||||
.addStatement(" return diamondCost == cost")
|
||||
.returns(boolean.class);
|
||||
|
||||
enumBuilder.addMethod(inRangeMethod.build());
|
||||
enumBuilder.addMethod(hasCostMethod.build());
|
||||
enumBuilder.addMethod(constructor);
|
||||
|
||||
|
||||
enumBuilder.addEnumConstant("UNDEFINED", addGift(-1, "undefined", -1, ""));
|
||||
for (var giftInfo : giftInfoMap.entrySet()) {
|
||||
|
||||
|
||||
var name = giftInfo.getKey().replace(" ", "_")
|
||||
.replace("’", "_")
|
||||
.replace("+", "_")
|
||||
.replace("'", "_")
|
||||
.replace(".", "_")
|
||||
.replace("-", "_")
|
||||
.replace("&", "_")
|
||||
.replace("!", "_")
|
||||
.toUpperCase();
|
||||
|
||||
|
||||
boolean startsWithNumber = name.matches("^[0-9].*");
|
||||
if (startsWithNumber) {
|
||||
name = "_" + name;
|
||||
}
|
||||
|
||||
if (isNumeric(name)) {
|
||||
name = "_" + name;
|
||||
}
|
||||
|
||||
if (name.equalsIgnoreCase("")) {
|
||||
continue;
|
||||
}
|
||||
var contier = 1;
|
||||
for (var value : giftInfo.getValue()) {
|
||||
var enumName = name;
|
||||
if (contier > 1) {
|
||||
enumName += "_" + value.getId();
|
||||
}
|
||||
enumBuilder.addEnumConstant(enumName, addGift(value.getId(), value.getName(), value.getDiamondCost(), value.getImage()));
|
||||
contier++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
var output = JavaFile.builder("io.github.jwdeveloper.tiktok.data.models.gifts", enumBuilder.build());
|
||||
output.addFileComment("This enum is generated");
|
||||
return output.build();
|
||||
}
|
||||
|
||||
public static boolean isNumeric(String str) {
|
||||
try {
|
||||
Double.parseDouble(str);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static TypeSpec addGift(int id, String name, int diamond, String picture) {
|
||||
return TypeSpec.anonymousClassBuilder(
|
||||
"$L, $S, $L, $S",
|
||||
id,
|
||||
name,
|
||||
diamond,
|
||||
picture)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +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.gifts;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftDto;
|
||||
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftExtraJson;
|
||||
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftOfficialJson;
|
||||
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftScraperJson;
|
||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class GiftsDownloader {
|
||||
|
||||
public static void main(String[] run) {
|
||||
var gifts = new GiftsDownloader().getGifts();
|
||||
for(var gift : gifts)
|
||||
{
|
||||
System.out.println(gift.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public List<GiftDto> getGiftsFromFile() {
|
||||
var version = "";
|
||||
var content = FilesUtility.loadFileContent("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\output.json");
|
||||
Type mapType = new TypeToken<Map<Integer, GiftDto>>() {
|
||||
}.getType();
|
||||
var mapper = new Gson().fromJson(content, mapType);
|
||||
|
||||
var gifts = (Map<Integer, GiftDto>) mapper;
|
||||
return gifts.values().stream().toList();
|
||||
}
|
||||
|
||||
public List<GiftDto> getGifts() {
|
||||
var scraper = new GiftScraperJson();
|
||||
System.out.println("Downlooading Scraped Gifts");
|
||||
var scraperGifts = scraper.run();
|
||||
System.out.println("Scraped Gifts: " + scraperGifts.size());
|
||||
|
||||
System.out.println("Downlooading Official Gifts");
|
||||
var officalGift = new GiftOfficialJson();
|
||||
var officialGifts = officalGift.run();
|
||||
System.out.println("Official Gifts: " + officialGifts.size());
|
||||
|
||||
System.out.println("Downlooading GiftExtraJson Gifts");
|
||||
var extraGiftsJson = new GiftExtraJson();
|
||||
var extraGifts = extraGiftsJson.run();
|
||||
System.out.println("GiftExtraJson Gifts: " + extraGifts.size());
|
||||
|
||||
var outputHashMap = new TreeMap<Integer, GiftDto>();
|
||||
for (var gift : scraperGifts) {
|
||||
outputHashMap.put(gift.getId(), gift);
|
||||
}
|
||||
for (var gift : officialGifts) {
|
||||
outputHashMap.put(gift.getId(), gift);
|
||||
}
|
||||
for (var gift : extraGifts) {
|
||||
outputHashMap.put(gift.getId(), gift);
|
||||
}
|
||||
var gson = new GsonBuilder().setPrettyPrinting()
|
||||
.create();
|
||||
var json = gson.toJson(outputHashMap);
|
||||
FilesUtility.saveFile("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\output_1_0_15.json", json);
|
||||
System.out.println("Gifts saved to file!");
|
||||
return outputHashMap.values().stream().toList();
|
||||
}
|
||||
}
|
||||
@@ -1,34 +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.gifts.downloader;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GiftDto
|
||||
{
|
||||
private int id;
|
||||
private String name;
|
||||
private int diamondCost;
|
||||
private String image;
|
||||
}
|
||||
@@ -1,71 +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.gifts.downloader;
|
||||
|
||||
import com.google.gson.*;
|
||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GiftExtraJson
|
||||
{
|
||||
public static void main(String[] args) {
|
||||
var reuslt = new GiftExtraJson().run();
|
||||
|
||||
System.out.println(reuslt.size());
|
||||
}
|
||||
|
||||
public List<GiftDto> run() {
|
||||
|
||||
var output = new ArrayList<GiftDto>();
|
||||
var jsonGifts = getJsonGifts();
|
||||
for (var jsonElement : jsonGifts) {
|
||||
var gift = getGift(jsonElement);
|
||||
output.add(gift);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
private GiftDto getGift(JsonElement jsonElement) {
|
||||
|
||||
var id = jsonElement.getAsJsonObject().get("id").getAsInt();
|
||||
var name = jsonElement.getAsJsonObject().get("name").getAsString();
|
||||
var diamondCost = jsonElement.getAsJsonObject().get("diamondCost").getAsInt();
|
||||
var image = jsonElement.getAsJsonObject().get("image").getAsString();
|
||||
var gift = new GiftDto();
|
||||
gift.setId(id);
|
||||
gift.setName(name);
|
||||
gift.setDiamondCost(diamondCost);
|
||||
gift.setImage(image);
|
||||
return gift;
|
||||
}
|
||||
|
||||
public static JsonArray getJsonGifts() {
|
||||
|
||||
var extraGifts =FilesUtility.loadFileContent("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\extra_gifts.json");
|
||||
JsonElement jsonElement = JsonParser.parseString(extraGifts);
|
||||
return jsonElement.getAsJsonArray();
|
||||
}
|
||||
}
|
||||
@@ -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.gifts.downloader;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.TikTokLive;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GiftOfficialJson {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new GiftOfficialJson().run();
|
||||
}
|
||||
|
||||
public List<GiftDto> run() {
|
||||
try {
|
||||
var dtf = DateTimeFormatter.ofPattern("dd/MM/yyyy");
|
||||
var now = LocalDateTime.now();
|
||||
var date = now.format(dtf).replace("/", "_");
|
||||
var fileName = "official_" + date + ".json";
|
||||
|
||||
var httpClient = TikTokLive.requests();
|
||||
var giftsInfo = httpClient.fetchGiftsData();
|
||||
FilesUtility.saveFile("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\official\\" + fileName, giftsInfo.getJson());
|
||||
|
||||
|
||||
return giftsInfo.getGifts().stream().map(e ->
|
||||
{
|
||||
var gift = new GiftDto();
|
||||
gift.setId(e.getId());
|
||||
gift.setImage(e.getImage());
|
||||
gift.setName(e.getName());
|
||||
gift.setDiamondCost(e.getDiamondCost());
|
||||
return gift;
|
||||
}).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Failed to fetch giftTokens from WebCast, see stacktrace for more info.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,121 +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.gifts.downloader;
|
||||
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GiftScraperJson {
|
||||
|
||||
private final String baseUrl = "https://streamdps.com/tiktok-widgets/gifts/";
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
var instance = new GiftScraperJson();
|
||||
instance.run();
|
||||
}
|
||||
|
||||
|
||||
public List<GiftDto> run() {
|
||||
var mainPage = getPageContent(baseUrl);
|
||||
var countries = getCountriesLinks(mainPage);
|
||||
|
||||
var allDocuments = getAllPagesDocuments(countries);
|
||||
allDocuments.add(mainPage);
|
||||
|
||||
var output = new ArrayList<GiftDto>();
|
||||
for (var document : allDocuments) {
|
||||
var gifts = getGifts(document);
|
||||
output.addAll(gifts);
|
||||
}
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public List<Document> getAllPagesDocuments(List<String> pages) {
|
||||
List<Document> content = new ArrayList<>();
|
||||
for (var page : pages) {
|
||||
content.add(getPageContent(baseUrl + page));
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
public List<String> getCountriesLinks(Document document) {
|
||||
var output = new ArrayList<String>();
|
||||
var countriesElements = document.getElementsByTag("a");
|
||||
for (var element : countriesElements) {
|
||||
var value = element.attr("href");
|
||||
if (!value.contains("/tiktok-widgets/gifts/?")) {
|
||||
continue;
|
||||
}
|
||||
value = value.replace("/tiktok-widgets/gifts/", "");
|
||||
output.add(value);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public List<GiftDto> getGifts(Document document) {
|
||||
var container = document.getElementsByClass("section-block bkg-charcoal");
|
||||
var giftsContainers = container.get(0).getElementsByClass("column width-1 center");
|
||||
|
||||
var output = new ArrayList<GiftDto>();
|
||||
for (var giftContainer : giftsContainers) {
|
||||
var imageElement = giftContainer.getElementsByTag("img").get(0);
|
||||
var link = imageElement.attr("src");
|
||||
|
||||
var coinsElement = giftContainer.getElementsByClass("color-white").get(0);
|
||||
var coins = coinsElement.text();
|
||||
|
||||
var inputsElements = giftContainer.getElementsByTag("input");
|
||||
var idElement = inputsElements.get(0);
|
||||
var nameElement = inputsElements.get(1);
|
||||
|
||||
var id = idElement.attr("value");
|
||||
var name = nameElement.attr("value");
|
||||
|
||||
|
||||
var gift = new GiftDto();
|
||||
gift.setImage(link);
|
||||
gift.setDiamondCost(Integer.parseInt(coins));
|
||||
gift.setId(Integer.parseInt(id));
|
||||
gift.setName(name);
|
||||
output.add(gift);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public Document getPageContent(String url) {
|
||||
try {
|
||||
var result = Jsoup.connect(url).get();
|
||||
System.out.println("Downloaded page: " + url);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +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.intefacee;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.events_generator.EventGeneratorSettings;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.util.Set;
|
||||
|
||||
public class EventsInterfaceGenerator {
|
||||
public void compile(EventGeneratorSettings settings) {
|
||||
Reflections reflections = new Reflections("io.github.jwdeveloper.tiktok.events.messages");
|
||||
|
||||
// Get all types (i.e., classes) in the specified package
|
||||
var classes = reflections.getSubTypesOf(TikTokEvent.class);
|
||||
classes.add(TikTokEvent.class);
|
||||
|
||||
// var result = generateInterface("io.github.jwdeveloper.tiktok.events", classes);System.out.println(result);
|
||||
var result = getBuilderImplementation("x",classes); System.out.println(result);
|
||||
|
||||
}
|
||||
|
||||
public String generateInterface(String packageName, Set<Class<? extends TikTokEvent>> eventsClasses) {
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.interfaceBuilder("TikTokEventBuilder");
|
||||
classBuilder.addModifiers(Modifier.PUBLIC);
|
||||
classBuilder.addTypeVariable(TypeVariableName.get("T"));
|
||||
|
||||
// Generate constructors
|
||||
for (var clazz : eventsClasses) {
|
||||
var clazzName = clazz.getSimpleName();
|
||||
|
||||
var methodName = clazzName;
|
||||
methodName = clazzName.replace("TikTok", "");
|
||||
if(!clazz.equals(TikTokEvent.class))
|
||||
{
|
||||
methodName = methodName.replace("Event", "");
|
||||
}
|
||||
MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder("on" + methodName);
|
||||
|
||||
|
||||
var name = "TikTokEventConsumer<" + clazzName + ">";
|
||||
constructorBuilder.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC);
|
||||
constructorBuilder.addParameter(ClassName.bestGuess(name), "event");
|
||||
constructorBuilder.returns(TypeVariableName.get("T"));
|
||||
classBuilder.addMethod(constructorBuilder.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Generate Java class
|
||||
TypeSpec javaClass = classBuilder.build();
|
||||
var result = JavaFile.builder(packageName, javaClass).build();
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getBuilderImplementation(String packageName, Set<Class<? extends TikTokEvent>> eventsClasses) {
|
||||
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder("TikTokEvents");
|
||||
classBuilder.addModifiers(Modifier.PUBLIC);
|
||||
|
||||
/*
|
||||
public TikTokClientBuilder onLinkMicFanTicket(Consumer<TikTokLinkMicFanTicketEvent> event) {
|
||||
tikTokEventHandler.subscribe(TikTokEventHandler.class, event);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// Generate constructors
|
||||
for (var clazz : eventsClasses) {
|
||||
var clazzName = clazz.getSimpleName();
|
||||
var methodName = clazzName;
|
||||
methodName = clazzName.replace("TikTok", "");
|
||||
if(!clazz.equals(TikTokEvent.class))
|
||||
{
|
||||
methodName = methodName.replace("Event", "");
|
||||
}
|
||||
methodName ="on" + methodName;
|
||||
MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder( methodName);
|
||||
|
||||
|
||||
var name = "TikTokEventConsumer<" + clazzName + ">";
|
||||
constructorBuilder.addModifiers( Modifier.PUBLIC);
|
||||
constructorBuilder.addParameter(ClassName.bestGuess(name), "event");
|
||||
constructorBuilder.addStatement("tikTokEventHandler.subscribe("+clazzName+".class,event)");
|
||||
constructorBuilder.addStatement("return this");
|
||||
constructorBuilder.returns(TikTokLiveClientBuilder.class);
|
||||
classBuilder.addMethod(constructorBuilder.build());
|
||||
|
||||
}
|
||||
|
||||
// Generate Java class
|
||||
TypeSpec javaClass = classBuilder.build();
|
||||
var result = JavaFile.builder(packageName, javaClass).build();
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,65 +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.protocol;
|
||||
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ProtocolGenerator
|
||||
{
|
||||
public static void main(String[] args) {
|
||||
// Path to the HTML file
|
||||
File htmlFile = new File("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\page.html");
|
||||
|
||||
try {
|
||||
// Parse the HTML file with Jsoup
|
||||
var doc = Jsoup.parse(htmlFile, "UTF-8");
|
||||
|
||||
// Find all script tags
|
||||
var scriptTags = doc.select("script");
|
||||
|
||||
// Display all script tags
|
||||
int counter = 1;
|
||||
for (var scriptTag : scriptTags) {
|
||||
String srcValue = scriptTag.attr("src");
|
||||
|
||||
|
||||
|
||||
if(!srcValue.contains("tiktok/webapp/main/webapp-live/"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Only print those script tags which have a 'src' attribute
|
||||
if (!srcValue.isEmpty()) {
|
||||
System.out.println("Script Tag " + counter + " src attribute: " + srcValue);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.utils;
|
||||
|
||||
public class ConsoleColors
|
||||
{
|
||||
public static final String RESET = "\033[0m"; // Text Reset
|
||||
|
||||
// Regular Colors
|
||||
public static final String BLACK = "\033[0;30m"; // BLACK
|
||||
public static final String RED = "\033[0;31m"; // RED
|
||||
public static final String GREEN = "\033[0;32m"; // GREEN
|
||||
public static final String YELLOW = "\033[0;33m"; // YELLOW
|
||||
public static final String BLUE = "\033[0;34m"; // BLUE
|
||||
public static final String PURPLE = "\033[0;35m"; // PURPLE
|
||||
public static final String CYAN = "\033[0;36m"; // CYAN
|
||||
public static final String WHITE = "\033[0;37m"; // WHITE
|
||||
|
||||
// Bold
|
||||
public static final String BLACK_BOLD = "\033[1;30m"; // BLACK
|
||||
public static final String RED_BOLD = "\033[1;31m"; // RED
|
||||
public static final String GREEN_BOLD = "\033[1;32m"; // GREEN
|
||||
public static final String YELLOW_BOLD = "\033[1;33m"; // YELLOW
|
||||
public static final String BLUE_BOLD = "\033[1;34m"; // BLUE
|
||||
public static final String PURPLE_BOLD = "\033[1;35m"; // PURPLE
|
||||
public static final String CYAN_BOLD = "\033[1;36m"; // CYAN
|
||||
public static final String WHITE_BOLD = "\033[1;37m"; // WHITE
|
||||
|
||||
// Underline
|
||||
public static final String BLACK_UNDERLINED = "\033[4;30m"; // BLACK
|
||||
public static final String RED_UNDERLINED = "\033[4;31m"; // RED
|
||||
public static final String GREEN_UNDERLINED = "\033[4;32m"; // GREEN
|
||||
public static final String YELLOW_UNDERLINED = "\033[4;33m"; // YELLOW
|
||||
public static final String BLUE_UNDERLINED = "\033[4;34m"; // BLUE
|
||||
public static final String PURPLE_UNDERLINED = "\033[4;35m"; // PURPLE
|
||||
public static final String CYAN_UNDERLINED = "\033[4;36m"; // CYAN
|
||||
public static final String WHITE_UNDERLINED = "\033[4;37m"; // WHITE
|
||||
|
||||
// Background
|
||||
public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK
|
||||
public static final String RED_BACKGROUND = "\033[41m"; // RED
|
||||
public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN
|
||||
public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW
|
||||
public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE
|
||||
public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE
|
||||
public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN
|
||||
public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE
|
||||
|
||||
// High Intensity
|
||||
public static final String BLACK_BRIGHT = "\033[0;90m"; // BLACK
|
||||
public static final String RED_BRIGHT = "\033[0;91m"; // RED
|
||||
public static final String GREEN_BRIGHT = "\033[0;92m"; // GREEN
|
||||
public static final String YELLOW_BRIGHT = "\033[0;93m"; // YELLOW
|
||||
public static final String BLUE_BRIGHT = "\033[0;94m"; // BLUE
|
||||
public static final String PURPLE_BRIGHT = "\033[0;95m"; // PURPLE
|
||||
public static final String CYAN_BRIGHT = "\033[0;96m"; // CYAN
|
||||
public static final String WHITE_BRIGHT = "\033[0;97m"; // WHITE
|
||||
|
||||
// Bold High Intensity
|
||||
public static final String BLACK_BOLD_BRIGHT = "\033[1;90m"; // BLACK
|
||||
public static final String RED_BOLD_BRIGHT = "\033[1;91m"; // RED
|
||||
public static final String GREEN_BOLD_BRIGHT = "\033[1;92m"; // GREEN
|
||||
public static final String YELLOW_BOLD_BRIGHT = "\033[1;93m";// YELLOW
|
||||
public static final String BLUE_BOLD_BRIGHT = "\033[1;94m"; // BLUE
|
||||
public static final String PURPLE_BOLD_BRIGHT = "\033[1;95m";// PURPLE
|
||||
public static final String CYAN_BOLD_BRIGHT = "\033[1;96m"; // CYAN
|
||||
public static final String WHITE_BOLD_BRIGHT = "\033[1;97m"; // WHITE
|
||||
|
||||
// High Intensity backgrounds
|
||||
public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK
|
||||
public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED
|
||||
public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN
|
||||
public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW
|
||||
public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE
|
||||
public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE
|
||||
public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN
|
||||
public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.utils;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface JsonIgnore {
|
||||
}
|
||||
@@ -1,806 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 5547,
|
||||
"name": "Russian Crepes",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/8525a07c6bf16a74eee66e9ad119b3b8.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5793,
|
||||
"name": "Play Samba",
|
||||
"diamondCost": 99,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/fd3d6cc127464bacded6ed009074ae2f~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5794,
|
||||
"name": "Coconut Tree",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/eb0923dbab5251f4c2e0496b11b55c4f~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5822,
|
||||
"name": "Koala",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/22c8fa54da366c111f7bb915d4429e2d~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5823,
|
||||
"name": "Fairy Bread",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a42f9ac9cd6b26da03818ff65ac919f1~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5831,
|
||||
"name": "Flower Show",
|
||||
"diamondCost": 500,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b6266323ef3ea0d313cbab6911ff8c46~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5843,
|
||||
"name": "Campfire",
|
||||
"diamondCost": 388,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e280eb1b7fe92b4efe612d98064d5a2d~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5852,
|
||||
"name": "Soccer Ball",
|
||||
"diamondCost": 39,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e1932db6aea81bbddc4e7dc0229ac155~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5890,
|
||||
"name": "Autumn leaves",
|
||||
"diamondCost": 500,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/30adcaf443df63e3bfd2751ad251f87d~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5893,
|
||||
"name": "Footy",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/94f8ac5c7b6f90aba713b44ddac40bf1~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5956,
|
||||
"name": "Fishing Gear",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/1b2353958374f585e25b2f2344c6d0ad~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5983,
|
||||
"name": "Amazing",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/18256fd3f4402601dd07c83adae3e9a2~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5991,
|
||||
"name": "Banana leaf vessel",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/8e635863e20cfa3651bd8a5b762ae72d~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5992,
|
||||
"name": "Frangipani",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7464fad59650123fe0989e426618847d~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6006,
|
||||
"name": "Cricket",
|
||||
"diamondCost": 99,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/408d55c0526ada808be7db3e22c02a56~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6034,
|
||||
"name": "Flower",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/9c20971eeb28b6b4ba37e57df3983da0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6050,
|
||||
"name": "Love Bomb",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/2a1c1b14f5e9f7be5d76fa4928f574f1~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6113,
|
||||
"name": "Taco ",
|
||||
"diamondCost": 9,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/43d06db8c962623dbed6ecf70fb89ca8~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6194,
|
||||
"name": "Top Host",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5947dc37282c417b411c61f20ee7d6d4~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6240,
|
||||
"name": "ASMR",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/748e74c8309e08dbc5b03e03f28a0ea0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6411,
|
||||
"name": "Snag",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/aa2d9b162c766a7fdf71fcead6d7bbcd~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6416,
|
||||
"name": "Choc Chip Cookie",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7dd2731de2e644301a329d3eb437b427~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6428,
|
||||
"name": "Crystal Ball",
|
||||
"diamondCost": 1700,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7e4f9a99b7003ae05186f5324aae9fbf~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6483,
|
||||
"name": "Spinning Top",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/6cde70e04a6b40a9879f7b99ff191808~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6486,
|
||||
"name": "Cheems Dog",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/d2c9e50efa3b9ff1ed31c96440a9d3a1~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6531,
|
||||
"name": "Llama Greetings",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a6b95ce6350f5f4bdff6880ac6993789~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6592,
|
||||
"name": "TGIF",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/2734231d880b5cd20149f4cc8c760279~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6705,
|
||||
"name": "Loved",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/2a41781b0a29ba3c409c5dd83eed07f8~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6744,
|
||||
"name": "Fruits Hat ",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/2316b31fc5259cc29f281d88fbca0568~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6813,
|
||||
"name": "Fantastic",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a1b2204b06aa19d45a0338e9f0099ea7~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7218,
|
||||
"name": "Rio de Janeiro",
|
||||
"diamondCost": 9999,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/34c0eb43c3d50e8ab64408171ebbe733~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8225,
|
||||
"name": "Coconut Drink",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ce27ad017f987240dc447e65ae866f4f~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8267,
|
||||
"name": "Good Evening",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/0015a756ff783f37a2cf3b5d634b3cd6~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8268,
|
||||
"name": "Good Night",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b7b55087141bd5f965eb31a99a5f157b~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8616,
|
||||
"name": "Rainbow",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5fb7267489192fc77c4c8b647c124680~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8638,
|
||||
"name": "Festa Junina's Hat",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/61b32ccce11b289b3c1db7438dfb4450~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8712,
|
||||
"name": "Happy Father's Day",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/193eba78ded4d388a0b5a7ae95943796~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9135,
|
||||
"name": "Magic Forest",
|
||||
"diamondCost": 6000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/63a758dbef9788f690e97cd65dbbb8d2~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9333,
|
||||
"name": "LIVE Fest Clappers",
|
||||
"diamondCost": 100,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/63e85e00169ec5be3bfa90bb004cda5e.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9334,
|
||||
"name": "LIVE Fest",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1e98afffef90ed4b2cc9c9ebb88e3608.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9514,
|
||||
"name": "Storms at sea",
|
||||
"diamondCost": 2200,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4918fbbdf220873dd8cae4c94d1ae037.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9515,
|
||||
"name": "Lightning Storm",
|
||||
"diamondCost": 6000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/6f673fbb0ae6860e2b1e254538c958ba.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9516,
|
||||
"name": "Mountains",
|
||||
"diamondCost": 12000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/51a7d74bcb4a6417be59f0ffc0b77e96.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7812,
|
||||
"name": "Bravo",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b25e72d59e9771b09da8c8c70f395f82~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8239,
|
||||
"name": "White Rose",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a2d81f3847457be9083a9c76a59b08cb~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7813,
|
||||
"name": "Health Potion",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/13f6a46b763c496306ff541daf3021a4~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7814,
|
||||
"name": "Panettone",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/64ce2413a362442819b4551703b7b26c~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5631,
|
||||
"name": "Power hug",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/9578adce6e3da2d211583212bdfd1b0e~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9463,
|
||||
"name": "Fairy Wings",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/e504dc2f313b8c6df9e99a848e1b3a99.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9139,
|
||||
"name": "Team Bracelet",
|
||||
"diamondCost": 2,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/54cb1eeca369e5bea1b97707ca05d189.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5514,
|
||||
"name": "Birds",
|
||||
"diamondCost": 600,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/0911b5726d912dabbf6ee4b0383352ea.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5524,
|
||||
"name": "Tsar",
|
||||
"diamondCost": 100,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/cb1c3e6263d4b6c08301f8798dcb5a9b.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5549,
|
||||
"name": "Ballet Dancer",
|
||||
"diamondCost": 500,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c09cc8ce49476d2c46e9c8af6189d5f4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5559,
|
||||
"name": "Crystal Heart",
|
||||
"diamondCost": 499,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ae46ac6582a606009643440fe4138eb4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5680,
|
||||
"name": "Disco ball",
|
||||
"diamondCost": 1000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/8d0cb854bbe8eeea654f3f9c353c5cf0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6112,
|
||||
"name": "King Cake ",
|
||||
"diamondCost": 9,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/aa99da9f6b499ff879c3860e888a53ae~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6393,
|
||||
"name": "Magic Hat",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b156ffd21bb3849a52144ab1688bbc43~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6426,
|
||||
"name": "Dombra",
|
||||
"diamondCost": 20,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ccd9fea1988521d1e81051a916800d6c~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6452,
|
||||
"name": "Jakarta Roundabout",
|
||||
"diamondCost": 16999,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/31f67910fc5858cf087da65746f1f9f3~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6633,
|
||||
"name": "Independence Day",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b967993872a6e40f3477d30545f8d2eb~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6655,
|
||||
"name": "Summer Iris ",
|
||||
"diamondCost": 30,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/cb591f5b5729fa6e64cac57c78724981~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6741,
|
||||
"name": "Gorgeous Trophy",
|
||||
"diamondCost": 7000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/279c9495c2150e333bc4bc13761d177e~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6756,
|
||||
"name": "Hot",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ec679890070187b61620b9662afb814e~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6800,
|
||||
"name": "Pinata",
|
||||
"diamondCost": 699,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c8a18d43dc9fb4598d7e991ebeb958ae~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6967,
|
||||
"name": "Autumn Leaves",
|
||||
"diamondCost": 500,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/30adcaf443df63e3bfd2751ad251f87d~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7032,
|
||||
"name": "Maracas",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/00204efcf0573192ad5d872c7beeaf5b~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7084,
|
||||
"name": "Witchy Kitty",
|
||||
"diamondCost": 30,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/dfce46f99a1206cca84f9092603e4783~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7105,
|
||||
"name": "Magic Potion",
|
||||
"diamondCost": 499,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e055625e9239df7e833702c768e033d2~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7377,
|
||||
"name": "Christmas Market G",
|
||||
"diamondCost": 2000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/f498f29ef628c8318006a9ff2f49bf08~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7458,
|
||||
"name": "Wooly Hat",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a234d0187047fa48805c8ea2e1f1f756~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7475,
|
||||
"name": "Mistletoe GDM 23",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/3527969b8c27e3194e61ff0787a9c3c2~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7477,
|
||||
"name": "Panettone GDM 23",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/64ce2413a362442819b4551703b7b26c~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7498,
|
||||
"name": "Candy Cane Gun",
|
||||
"diamondCost": 799,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/58ef7964e32adc5fc47c5706a02e4ff0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7504,
|
||||
"name": "Holiday Stocking",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e05de50999ebb446e15c4947b30d3140~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7523,
|
||||
"name": "Hot Choco GDM 23",
|
||||
"diamondCost": 30,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/f62f5912077d9af84256de288399125a~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7525,
|
||||
"name": "Christmas CarouseG",
|
||||
"diamondCost": 2000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b5ba3941f7389da7495b659e888ea61a~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7527,
|
||||
"name": "Christmas Wreath G",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7842b50135e089334fc40d9705bb53c7~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7551,
|
||||
"name": "Snowman",
|
||||
"diamondCost": 99,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e094e0fafc14aaf127fa0d0a7926619a~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7697,
|
||||
"name": "LOVE U",
|
||||
"diamondCost": 899,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/79d45877691333e2ba69a9098406e95c~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7707,
|
||||
"name": "I'm blue",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c560ec76d5599198aaea9377c5ffab6e~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7846,
|
||||
"name": "Grumpy Glasses",
|
||||
"diamondCost": 99,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/6f38f8ed7442f69a105788b5c0c74a38~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7920,
|
||||
"name": "Husky",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a2f5d595e9d96aec19a7c0ed5fa9b017~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7921,
|
||||
"name": "Golden",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b97f58dcb0250489ae98529bcb0542ca~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8005,
|
||||
"name": "Falling For You",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a198bd39d2511dbba6a68867740e3ff9~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8207,
|
||||
"name": "The Crown",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5bf798f92fe96ba53c0f4d28f052f9bb~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8250,
|
||||
"name": "Disco ball",
|
||||
"diamondCost": 1000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a53d3ef956eb2f1aa7a7db46024c70bb~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8253,
|
||||
"name": "Spring Train",
|
||||
"diamondCost": 3999,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b859c413a241fec75bc78668aeb0f581~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8264,
|
||||
"name": "Happy Weekend",
|
||||
"diamondCost": 599,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b04f104e717798235cd3edaa6703e6a3~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8265,
|
||||
"name": "Happy Friday",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/65e8fcb76825b9ec36a24faf9a3e9495~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8266,
|
||||
"name": "Good Afternoon",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/bff3b908c4dd9cf19ab431cc99dc7940~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8269,
|
||||
"name": "Good Morning",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5c1a28f3aa7eefc27491f3020748ce54~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8442,
|
||||
"name": "Flower Festival",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/9bfe63e39b581a69ff944758c3eae5a0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8754,
|
||||
"name": "Aurora",
|
||||
"diamondCost": 12000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1f59f5593ce135325c1a034825cec18c.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8803,
|
||||
"name": "Miss You",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/3c53396b922691a7520698f47105a753.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8804,
|
||||
"name": "Vacation",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/8f46e8eef9cbd5304fb802104c2b4ef4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8890,
|
||||
"name": "Pink Shoes",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/cba8a7c718988bd51c7b6055e9ab1ec4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9111,
|
||||
"name": "Popcorn",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/676d2d4c31a8979f1fd06cdf5ecd922f~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9152,
|
||||
"name": "Spin with me GDM",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/149ac2e87d05490d7d251149cefe27a2.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9242,
|
||||
"name": "Pumpkin Spice Latte",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/0636d91615f7417ddd5f29438bf5debe~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9303,
|
||||
"name": "Rabbit and Mochi",
|
||||
"diamondCost": 999,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/213ef2549fbb10ec783c95a41d28cf0a.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9304,
|
||||
"name": "Boo the Ghost",
|
||||
"diamondCost": 88,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/cb909c78f2412e4927ea68d6af8e048f.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9354,
|
||||
"name": "I'm here",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/7006392a82d57452d5ef08dd90e169c1.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9355,
|
||||
"name": "So cute",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/d40d31241efcf57c630e894bb3007b8a.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9363,
|
||||
"name": "Elf GDM 23",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/60e5289b379660cc562742cf987a2d35.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9535,
|
||||
"name": "Play for you",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/182659e90a3432aa155e61c9c0d89df0.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9536,
|
||||
"name": "Fake smile",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/35ce62173962e33834703212d0b845a7.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9576,
|
||||
"name": "Yeah Nah",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4b20c5aab3841657a343be3769307805.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9581,
|
||||
"name": "Turkey Face GDDec",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/65349d1ef783fc207c1d2b54a8d521a7.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9583,
|
||||
"name": "Cool!",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/424c61f16c16919f169fd0352bd24661.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9587,
|
||||
"name": "Christmas Potato",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/5448f1f5157d3a4a88e0f57acf3dbfe0.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9604,
|
||||
"name": "Gobble Gobble",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/ada9babc0b55cf005e8c8d13dfc30b42.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9615,
|
||||
"name": "Festive Tiny Diny",
|
||||
"diamondCost": 15,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f2a8c2967c7153e9077bb469f2e42317.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9617,
|
||||
"name": "Xmas Mishka Bear",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/700c1c8817847317407cc2b8c6c9da42.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9625,
|
||||
"name": "Elf's Hat ",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9857a040c92b34d6a261201a93c185f.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9647,
|
||||
"name": "Kitten Paw",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/332520d7b5085ce591396c8d2bb9d352.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9650,
|
||||
"name": "The Van Cat",
|
||||
"diamondCost": 799,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/6973dd1b6d3dee3ca3f0ebac3c1d2977.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9656,
|
||||
"name": "Gingerbread man",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/af01db3e3cb9f54ea2cb421fab6062bc.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9657,
|
||||
"name": "GB North Pole",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/79715a53c41619e7b205eb26e57926d4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9658,
|
||||
"name": "DE North Pole",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/130e17b5b561a93cefbd236586881477.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9667,
|
||||
"name": "Kiwi Bird",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/b73cb4aaa76a33efd881192589d65351.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9668,
|
||||
"name": "Gingerman Party",
|
||||
"diamondCost": 1200,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/008a9554e736642f1b2dca9f198bb710.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9670,
|
||||
"name": "Reindeer",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4565fa0cd1dbf76463144b0d4cc50bf1.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9671,
|
||||
"name": "Gingebread Man",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/2399f65414f77419ec7d5e9274dc8e0e.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9672,
|
||||
"name": "Mimi & Fifi",
|
||||
"diamondCost": 5000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/0a72d0084695d03586fea7d854dc3a47.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9678,
|
||||
"name": "Holiday Carousel",
|
||||
"diamondCost": 2000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/b5ba3941f7389da7495b659e888ea61a.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9680,
|
||||
"name": "Xmas in London",
|
||||
"diamondCost": 20000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/876204a6ad0b1b0e4675d9be42439183.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9682,
|
||||
"name": "Stay Warm",
|
||||
"diamondCost": 450,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/abd104eb08ce0c351292036d8897fb8d.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9688,
|
||||
"name": "Snowglobe",
|
||||
"diamondCost": 499,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/ea5ac5f8e186897456bed2e78fc78ca5.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9698,
|
||||
"name": "Candy Cane",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1fa0a4ed666304c78a46de200b85c84b.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9703,
|
||||
"name": "Really Curious",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/793ba68723567b695b12f2ef08dc1484.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9704,
|
||||
"name": "Nemo",
|
||||
"diamondCost": 15,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/68fcf30cb3fb07e9546f5e7fbc2b0ac0.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9706,
|
||||
"name": "Elfs Hat ",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9857a040c92b34d6a261201a93c185f.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9770,
|
||||
"name": "Shiba Cookie",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4ea5282e7f61cbeee1214422d40ad407.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9771,
|
||||
"name": "KFC Chicken",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9d59ccd2328b8a46841b3b1c87d9e55.png~tplv-obj.png"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user