Compare commits

...

20 Commits

Author SHA1 Message Date
Jacek W
8ff4236452 Merge pull request #16 from jwdeveloper/develop-1-0-4
Changes:
   Generated new Gifts Json
   
   TikTokLive.isLiveOnline() check if live if online
   TikTokLive.isLiveOnlineAsync()
   
   TikTokLive.isHostNameValid() check if hostName is correct
   TikTokLive.isHostNameValidAsync()
2023-11-10 22:28:01 +01:00
Jacek W
7817aeb652 Merge pull request #15 from jwdeveloper/develop-1-0-4
Changes:
   Generated new Gifts Json
   
   TikTokLive.isLiveOnline() check if live if online
   TikTokLive.isLiveOnlineAsync()
   
   TikTokLive.isHostNameValid() check if hostName is correct
   TikTokLive.isHostNameValidAsync()
2023-11-10 22:24:53 +01:00
JW
4c122ab754 Changes:
Generated new Gifts Json

   TikTokLive.isLiveOnline() check if live if online
   TikTokLive.isLiveOnlineAsync()

   TikTokLive.isHostNameValid() check if hostName is correct
   TikTokLive.isHostNameValidAsync()
2023-11-10 22:24:14 +01:00
JW
519c22de8e Changes:
Generated new Gifts Json

   TikTokLive.isLiveOnline() check if live if online
   TikTokLive.isLiveOnlineAsync()

   TikTokLive.isHostNameValid() check if hostName is correct
   TikTokLive.isHostNameValidAsync()
2023-11-10 22:20:40 +01:00
JW
6d268c42f1 Changes:
Generated new Gifts Json

   TikTokLive.isLiveOnline() new method for checking if live if online
   TikTokLive.isLiveOnlineAsync()
2023-11-10 22:05:01 +01:00
Jacek W
788653484f Update README.md 2023-10-24 22:02:36 +02:00
GitHub Action
6cebbf891d Update version in pom.xml 2023-10-24 20:01:26 +00:00
Jacek W
cc32648988 Merge pull request #10 from Prokac/disconnect-fix
THX
2023-10-24 21:58:49 +02:00
Lysinka
4c797724d3 Update TikTokLiveClient.java 2023-10-24 19:07:08 +02:00
Jacek W
6941107db8 Update README.md 2023-10-13 14:00:20 +02:00
GitHub Action
ed70799cd9 Update version in pom.xml 2023-10-13 11:59:25 +00:00
JW
e12b0901f7 Merge remote-tracking branch 'origin/master' 2023-10-13 13:57:16 +02:00
JW
563e9618e2 Bug fixed: Not all tests was passing 2023-10-13 13:57:08 +02:00
Jacek W
82112f0140 Update README.md 2023-10-12 15:19:42 +02:00
GitHub Action
77e30de5e1 Update version in pom.xml 2023-10-12 13:19:01 +00:00
JW
a2b10ba7f6 Merge remote-tracking branch 'origin/master' 2023-10-12 15:16:53 +02:00
JW
957e38a5d2 Bug fixed: All the gifts had before same image link 2023-10-12 15:16:43 +02:00
Jacek W
5e77b3f57f Update README.md 2023-10-12 09:01:29 +02:00
Jacek W
690b9eb272 Update README.md 2023-10-12 06:19:43 +02:00
Jacek W
fc91991c2c Merge pull request #9 from jwdeveloper/jwdeveloper-patch-1
Update README.md
2023-10-12 06:13:48 +02:00
66 changed files with 45995 additions and 823 deletions

309
.gitignore vendored
View File

@@ -1,61 +1,85 @@
# Project exclude paths
/API/target/
/Client/target/
*.db
backend-infrastructure/.aws-sam
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### OSX ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### PyCharm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
.idea/dictionaries
.idea/
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
cmake-build-debug/
# Mongo Explorer plugin
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
# File-based project format
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
@@ -66,8 +90,8 @@ atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Ruby plugin and RubyMine
/.rakeTasks
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
@@ -75,69 +99,150 @@ crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
### PyCharm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
/.idea/.gitignore
/.idea/.name
/.idea/compiler.xml
/TestApplication/target/classes/io/github/jwdeveloper/tiktok/ConfigurationExample.class
/TestApplication/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
/TestApplication/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
/Tools/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
/Tools/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
/Tools-EventsCollector/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
/Tools-ReadmeGenerator/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
/Tools-ReadmeGenerator/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst
/.idea/encodings.xml
/Tools-ReadmeGenerator/target/classes/io/github/jwdeveloper/tiktok/EventsListGenerator$EventTypeComparator.class
/Tools-ReadmeGenerator/target/classes/io/github/jwdeveloper/tiktok/EventsListGenerator.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/tables/ExceptionInfoModel.class
/Tools/target/classes/io/github/jwdeveloper/tiktok/utils/FilesUtility.class
/TestApplication/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
/TestApplication/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
/Tools/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
/Tools/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
/Tools-EventsCollector/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
/Tools-ReadmeGenerator/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
/Tools-ReadmeGenerator/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
/.idea/jarRepositories.xml
/TestApplication/target/classes/io/github/jwdeveloper/tiktok/ListenerExample$CustomListener.class
/TestApplication/target/classes/io/github/jwdeveloper/tiktok/ListenerExample.class
/Tools-ReadmeGenerator/target/classes/io/github/jwdeveloper/tiktok/LiveClientMethodsGenerator.class
/TestApplication/target/classes/io/github/jwdeveloper/tiktok/Main.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/Main.class
/Tools-ReadmeGenerator/target/classes/io/github/jwdeveloper/tiktok/Main.class
/.idea/misc.xml
/Tools-ReadmeGenerator/src/main/resources/output.md
/Tools-ReadmeGenerator/target/classes/output.md
/TestApplication/target/maven-archiver/pom.properties
/Tools/target/maven-archiver/pom.properties
/Tools-EventsCollector/target/maven-archiver/pom.properties
/Tools-ReadmeGenerator/target/maven-archiver/pom.properties
/.idea/inspectionProfiles/Project_Default.xml
/Tools/target/classes/io/github/jwdeveloper/tiktok/protocol/ProtocolGenerator.class
/Tools-ReadmeGenerator/target/classes/io/github/jwdeveloper/tiktok/ReadmeGenerator.class
/TestApplication/target/classes/io/github/jwdeveloper/tiktok/SimpleExample.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/db/SqlConsts.class
/Tools-ReadmeGenerator/target/classes/template.md
/Tools/target/classes/io/github/jwdeveloper/tiktok/utils/TemplateUtility.class
/TestApplication/target/TestApplication-0.0.18-Release.jar
/TestApplication/target/TestApplication-0.0.18-Release-all.jar
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/db/TikTokDatabase.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/tables/TikTokErrorModel$TikTokErrorModelBuilder.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/tables/TikTokErrorModel.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/db/TikTokErrorModelDAO.class
/target/TikTokLiveJava-0.0.18-Release-all.pom
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/tables/TikTokMessageModel$TikTokMessageModelBuilder.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/tables/TikTokMessageModel.class
/Tools-EventsCollector/target/classes/io/github/jwdeveloper/tiktok/tools/collector/db/TikTokMessageModelDAO.class
/Tools/target/Tools-0.0.18-Release.jar
/Tools/target/Tools-0.0.18-Release-all.jar
/Tools-EventsCollector/target/Tools-EventsCollector-0.0.18-Release.jar
/Tools-EventsCollector/target/Tools-EventsCollector-0.0.18-Release-all.jar
/Tools-ReadmeGenerator/target/Tools-ReadmeGenerator-0.0.18-Release.jar
/Tools-ReadmeGenerator/target/Tools-ReadmeGenerator-0.0.18-Release-all.jar
/.idea/vcs.xml
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
.pytest_cache/
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule.*
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
**/target/
# Build folder
*/build/*
# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
/python-app-backend/samconfig.toml
/java-app-backend/BackendFunction/dependency-reduced-pom.xml

3
.idea/protoeditor.xml generated
View File

@@ -64,6 +64,9 @@
<ImportPathEntry>
<option name="location" value="file://$PROJECT_DIR$/API/src/main/proto" />
</ImportPathEntry>
<ImportPathEntry>
<option name="location" value="file://$USER_HOME$/AppData/Local/JetBrains/IntelliJIdea2022.3/protoeditor" />
</ImportPathEntry>
</list>
</option>
<option name="descriptorPath" value="google/protobuf/descriptor.proto" />

View File

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

View File

@@ -30,3 +30,6 @@ public @interface EventMeta
{
EventType eventType();
}

View File

@@ -0,0 +1,71 @@
/*
* 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.data.events.envelop;
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
import io.github.jwdeveloper.tiktok.data.models.Text;
import io.github.jwdeveloper.tiktok.data.models.chest.Chest;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastEnvelopeMessage;
import lombok.Value;
import java.util.Date;
@EventMeta(eventType = EventType.Message)
@Value
public class TikTokChestEvent extends TikTokHeaderEvent {
/**
* Chest target
*/
Chest chest;
/**
* User that send a chest
*/
User user;
/**
* Time when chest has been open
*/
Date openedAt;
public TikTokChestEvent(Chest chest, WebcastEnvelopeMessage msg) {
super(msg.getCommon());
this.chest = chest;
var text = Text.map(msg.getCommon().getDisplayText());
var userPiece = (Text.UserTextPiece) text.getTextPieces().get(0);
user = userPiece.getUser();
var envelopInfo = msg.getEnvelopeInfo();
openedAt = new Date(envelopInfo.getUnpackAt());
}
}

View File

@@ -51,7 +51,7 @@ public class Picture {
public static Picture map(io.github.jwdeveloper.tiktok.messages.data.Image profilePicture) {
var index = profilePicture.getUrlListCount() - 1;
if (index <= 0) {
if (index < 0) {
return new Picture("");
}
var url = profilePicture.getUrlList(index);

View File

@@ -29,6 +29,7 @@ import lombok.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
@Getter
@@ -45,6 +46,12 @@ public class Text {
this.value = computeValue();
}
public <T extends TextPiece> Optional<TextPiece> getTextPiece(Class<T> type)
{
return textPieces.stream().filter(e -> e.getClass().equals(type)).findFirst();
}
public static Text map(io.github.jwdeveloper.tiktok.messages.data.Text input) {
var pieces = input.getPiecesListList().stream().map(Text::mapTextPiece).toList();
return new Text(input.getKey(), input.getDefaultPattern(), pieces);
@@ -98,6 +105,7 @@ public class Text {
}
}
@Value
public static class UserTextPiece extends TextPiece {
User user;

View File

@@ -0,0 +1,23 @@
package io.github.jwdeveloper.tiktok.data.models.chest;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Chest {
/**
* Total diamonds inside the chest
*/
int totalDiamonds;
/**
* Total users participated in chest
*/
int totalUsers;
}

View File

@@ -31,4 +31,5 @@ public interface TikTokHttpRequest {
String get(String url);
String post(String url);
}

View File

@@ -28,11 +28,8 @@ import lombok.Data;
public class LiveRoomMeta {
private LiveRoomStatus status;
private boolean ageRestricted;
public enum LiveRoomStatus
{
HostNotFound,

View File

@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.live.builder;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.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.room.TikTokRoomEvent;
@@ -51,6 +52,9 @@ public interface EventsBuilder<T> {
T onWebsocketUnhandledMessage(EventConsumer<TikTokWebsocketUnhandledMessageEvent> event);
T onGiftCombo(EventConsumer<TikTokGiftComboEvent> event);
T onGift(EventConsumer<TikTokGiftEvent> event);
@@ -69,6 +73,8 @@ public interface EventsBuilder<T> {
T onShare(EventConsumer<TikTokShareEvent> event);
T onUnhandledSocial(EventConsumer<TikTokUnhandledSocialEvent> event);
// T onChest(EventConsumer<TikTokChestEvent> event);
T onLivePaused(EventConsumer<TikTokLivePausedEvent> event);
T onLiveEnded(EventConsumer<TikTokLiveEndedEvent> event);
@@ -80,10 +86,11 @@ public interface EventsBuilder<T> {
T onDisconnected(EventConsumer<TikTokDisconnectedEvent> event);
T onError(EventConsumer<TikTokErrorEvent> event);
T onEvent(EventConsumer<TikTokEvent> event);
// TODO Figure out how those events works
//T onLinkMicFanTicket(TikTokEventConsumer<TikTokLinkMicFanTicketEvent> event);

View File

@@ -110,4 +110,29 @@ enum BarrageType
FansLevelUpgrade = 10;
FansLevelEntrance = 11;
GamePartnership = 12;
}
enum EnvelopeBusinessType
{
BusinessTypeUnknown = 0;
BusinessTypeUserDiamond = 1;
BusinessTypePlatformDiamond = 2;
BusinessTypePlatformShell = 3;
BusinessTypePortal = 4;
BusinessTypePlatformMerch = 5;
BusinessTypeEoYDiamond = 6;
BusinessTypeFanClubGtM = 7;
}
enum EnvelopeFollowShowStatus
{
EnvelopeFollowShowUnknown = 0;
EnvelopeFollowShow = 1;
EnvelopeFollowNotShow = 2;
}
enum EnvelopeDisplay
{
EnvelopeDisplayUnknown = 0;
EnvelopeDisplayNew = 1;
EnvelopeDisplayHide = 2;
}

View File

@@ -246,13 +246,13 @@ message WebcastEmoteChatMessage {
message WebcastEnvelopeMessage {
Common common = 1;
EnvelopeInfo envelopeInfo = 2;
int64 display = 3; // @warning Enum not found, should be Display
EnvelopeDisplay display = 3; // @warning Enum not found, should be Display
// @EnvelopeInfo
// proto.webcast.im.EnvelopeMessage
message EnvelopeInfo {
string envelopeId = 1;
int64 businessType = 2; // @warning Enum not found, should be BusinessType
EnvelopeBusinessType businessType = 2;
string envelopeIdc = 3;
string sendUserName = 4;
int32 diamondCount = 5;
@@ -262,7 +262,7 @@ message WebcastEnvelopeMessage {
Image sendUserAvatar = 9;
string createAt = 10;
string roomId = 11;
int64 followShowStatus = 12; // @warning Enum not found, should be FollowShowStatus
EnvelopeFollowShowStatus followShowStatus = 12; // @warning Enum not found, should be FollowShowStatus
int32 skinId = 13;
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.25-Release</version>
<version>1.0.3-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -41,12 +41,28 @@
<artifactId>Java-WebSocket</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version> <!-- Use the desired TestNG version -->
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>-Xmx512m</argLine>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>

View File

@@ -23,12 +23,68 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.http.TikTokDataChecker;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
import java.util.concurrent.CompletableFuture;
public class TikTokLive
{
/**
*
* @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)
{
return new TikTokLiveClientBuilder(hostName);
}
/**
*
* @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)
{
return new TikTokDataChecker().isOnline(hostName);
}
/**
*
* @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 new TikTokDataChecker().isOnlineAsync(hostName);
}
/**
*
* @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)
{
return new TikTokDataChecker().isHostNameValid(hostName);
}
/**
*
* @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 new TikTokDataChecker().isHostNameValidAsync(hostName);
}
}

View File

@@ -112,7 +112,7 @@ public class TikTokLiveClient implements LiveClient {
}
public void disconnect() {
if (!liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED)) {
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
return;
}
webSocketClient.stop();

View File

@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
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.poll.TikTokPollEvent;
@@ -135,6 +136,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
}
}
public LiveClient build() {
@@ -148,7 +150,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
var requestFactory = new TikTokHttpRequestFactory(cookieJar);
var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
var apiService = new TikTokApiService(apiClient, logger, clientSettings);
var giftManager = new TikTokGiftManager();
var giftManager = new TikTokGiftManager(logger);
var eventMapper = new TikTokGenericEventMapper();
var giftHandler = new TikTokGiftEventHandler(giftManager);
@@ -190,6 +192,12 @@ 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);

View File

@@ -22,6 +22,8 @@
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.messages.data.User;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
import lombok.Data;
@@ -29,15 +31,20 @@ import lombok.Data;
@Data
public class TikTokRoomInfo implements LiveRoomInfo
{
private String roomId;
private int likesCount;
private int viewersCount;
private String roomId;
private boolean ageRestricted;
private User host;
private Picture picture;
private String description;
private String hostName;
private ConnectionState connectionState = ConnectionState.DISCONNECTED;

View File

@@ -28,18 +28,22 @@ import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import sun.misc.Unsafe;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
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() {
public TikTokGiftManager(Logger logger)
{
indexById = new HashMap<>();
indexByName = new HashMap<>();
this.logger = logger;
init();
}
@@ -66,6 +70,7 @@ public class TikTokGiftManager implements GiftManager {
field.set(enumInstance, name);
// EnumSet
field = Gift.class.getDeclaredField("diamondCost");
field.setAccessible(true);
field.set(enumInstance, diamondCost);

View File

@@ -25,6 +25,7 @@ package io.github.jwdeveloper.tiktok.handlers;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent;
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEndEvent;
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent;
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollStartEvent;
@@ -37,12 +38,16 @@ 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.models.Text;
import io.github.jwdeveloper.tiktok.data.models.chest.Chest;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler;
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
import io.github.jwdeveloper.tiktok.messages.enums.EnvelopeDisplay;
import io.github.jwdeveloper.tiktok.messages.webcast.*;
import io.github.jwdeveloper.tiktok.models.SocialTypes;
import lombok.SneakyThrows;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
@@ -106,7 +111,7 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
registerMapping(WebcastOecLiveShoppingMessage.class, TikTokShopEvent.class);
registerMapping(WebcastImDeleteMessage.class, TikTokIMDeleteEvent.class);
registerMapping(WebcastQuestionNewMessage.class, TikTokQuestionEvent.class);
registerMapping(WebcastEnvelopeMessage.class, TikTokEnvelopeEvent.class);
registerMappings(WebcastEnvelopeMessage.class, this::handleEnvelop);
registerMapping(WebcastSubNotifyMessage.class, TikTokSubNotifyEvent.class);
registerMapping(WebcastEmoteChatMessage.class, TikTokEmoteEvent.class);
}
@@ -148,6 +153,7 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
@SneakyThrows
private TikTokEvent handleMemberMessage(byte[] msg) {
var message = WebcastMemberMessage.parseFrom(msg);
roomInfo.setViewersCount(message.getMemberCount());
return switch (message.getAction()) {
case JOINED -> new TikTokJoinEvent(message);
case SUBSCRIBED -> new TikTokSubscribeEvent(message);
@@ -187,5 +193,18 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
};
}
@SneakyThrows
private List<TikTokEvent> handleEnvelop(byte[] data) {
var msg = WebcastEnvelopeMessage.parseFrom(data);
if (msg.getDisplay() != EnvelopeDisplay.EnvelopeDisplayNew) {
return Collections.emptyList();
}
var totalDiamonds = msg.getEnvelopeInfo().getDiamondCount();
var totalUsers = msg.getEnvelopeInfo().getPeopleCount();
var chest = new Chest(totalDiamonds, totalUsers);
return List.of(new TikTokChestEvent(chest, msg));
}
}

View File

@@ -0,0 +1,4 @@
package io.github.jwdeveloper.tiktok.handlers.events;
public class TikTokChestEventHandler {
}

View File

@@ -28,9 +28,11 @@ 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.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import lombok.SneakyThrows;
import sun.misc.Unsafe;
import java.util.HashMap;
import java.util.List;
@@ -48,6 +50,11 @@ public class TikTokGiftEventHandler {
@SneakyThrows
public List<TikTokEvent> handleGift(byte[] msg) {
var currentMessage = WebcastGiftMessage.parseFrom(msg);
return handleGift(currentMessage);
}
public List<TikTokEvent> handleGift(WebcastGiftMessage currentMessage)
{
var userId = currentMessage.getUser().getId();
var currentType = GiftSendType.fromNumber(currentMessage.getSendType());
var containsPreviousMessage = giftsMessages.containsKey(userId);
@@ -81,6 +88,8 @@ public class TikTokGiftEventHandler {
return List.of();
}
private TikTokGiftEvent getGiftEvent(WebcastGiftMessage message) {
var gift = getGiftObject(message);
return new TikTokGiftEvent(gift, message);
@@ -91,18 +100,39 @@ public class TikTokGiftEventHandler {
return new TikTokGiftComboEvent(gift, message, state);
}
private Gift getGiftObject(WebcastGiftMessage giftMessage) {
var gift = giftManager.findById((int) giftMessage.getGiftId());
private Gift getGiftObject(WebcastGiftMessage giftMessage)
{
var giftId = (int) giftMessage.getGiftId();
var gift = giftManager.findById(giftId);
if (gift == Gift.UNDEFINED) {
gift = giftManager.findByName(giftMessage.getGift().getName());
}
if (gift == Gift.UNDEFINED) {
if (gift == Gift.UNDEFINED)
{
gift = giftManager.registerGift(
(int) giftMessage.getGift().getId(),
giftId,
giftMessage.getGift().getName(),
giftMessage.getGift().getDiamondCount(),
Picture.map(giftMessage.getGift().getImage()));
}
if (gift.getPicture().getLink().endsWith(".webp")) {
updatePicture(gift, giftMessage);
}
return gift;
}
private void updatePicture(Gift gift, WebcastGiftMessage webcastGiftMessage) {
try {
var picture = Picture.map(webcastGiftMessage.getGift().getImage());
var constructor = Unsafe.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
var field = Gift.class.getDeclaredField("picture");
field.setAccessible(true);
field.set(gift, picture);
} catch (Exception e) {
throw new TikTokLiveException("Unable to update picture in gift: " + gift.toString());
}
}
}

View File

@@ -30,7 +30,6 @@ import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import io.github.jwdeveloper.tiktok.mappers.LiveRoomMetaMapper;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import javax.script.ScriptEngineManager;
import java.util.HashMap;
import java.util.logging.Logger;
import java.util.regex.Pattern;
@@ -47,49 +46,34 @@ public class TikTokApiService {
}
public void updateSessionId()
{
if(clientSettings.getSessionId() == null)
{
public void updateSessionId() {
if (clientSettings.getSessionId() == null) {
return;
}
if(clientSettings.getSessionId().isEmpty())
{
return;
if (clientSettings.getSessionId().isEmpty()) {
return;
}
tiktokHttpClient.setSessionId(clientSettings.getSessionId());
}
public boolean sendMessage(String message, String sessionId) {
if (sessionId.isEmpty()) {
throw new TikTokLiveException("Session ID must not be Empty");
}
var roomId = clientSettings.getClientParameters().get("room_id");
if (roomId == null) {
throw new TikTokLiveException("Room ID must not be Empty");
}
logger.info("Sending message to chat");
try {
var params = new HashMap<String, Object>(clientSettings.getClientParameters());
params.put("content", message);
params.put("channel", "tiktok_web");
params.remove("cursor");
tiktokHttpClient.setSessionId(sessionId);
tiktokHttpClient.postMessageToChat(params);
return true;
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
}
public String fetchRoomId(String userName) {
var roomId = fetchRoomIdFromTiktokApi(userName);
clientSettings.getClientParameters().put("room_id", roomId);
logger.info("RoomID -> " + roomId);
return roomId;
}
public String fetchRoomId(String userName) {
private String fetchRoomIdFromTikTokPage(String userName)
{
/* var roomId = RequestChain.<String>create()
.then(() -> fetchRoomIdFromTikTokPage(userName))
.then(() -> fetchRoomIdFromTiktokApi(userName))
.run();*/
logger.info("Fetching room ID");
String html;
try {
html = tiktokHttpClient.getLivestreamPage(userName);
}
catch (Exception e)
{
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
}
@@ -112,22 +96,35 @@ public class TikTokApiService {
throw new TikTokLiveOfflineHostException("Unable to fetch room ID, live host could be offline or name is misspelled");
}
clientSettings.getClientParameters().put("room_id", id);
logger.info("RoomID -> " + id);
return id;
}
private String fetchRoomIdFromTiktokApi(String userName) {
var params = new HashMap<>(clientSettings.getClientParameters());
params.put("uniqueId", userName);
params.put("sourceType", 54);
var roomData = tiktokHttpClient.getJsonFromTikTokApi("api-live/user/room/", params);
var data = roomData.getAsJsonObject("data");
var user =data.getAsJsonObject("user");
var roomId = user.get("roomId").getAsString();
return roomId;
}
public LiveRoomMeta fetchRoomInfo() {
logger.info("Fetching RoomInfo");
try {
var response = tiktokHttpClient.getJObjectFromWebcastAPI("room/info/", clientSettings.getClientParameters());
var response = tiktokHttpClient.getJsonFromWebcastApi("room/info/", clientSettings.getClientParameters());
var mapper = new LiveRoomMetaMapper();
var liveRoomMeta = mapper.map(response);
logger.info("RoomInfo status -> " + liveRoomMeta.getStatus());
return liveRoomMeta;
} catch (Exception e)
{
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch room info from WebCast, see stacktrace for more info.", e);
}
}
@@ -136,7 +133,7 @@ public class TikTokApiService {
logger.info("Fetching ClientData");
try {
var response = tiktokHttpClient.getSigningServerMessage("im/fetch/", clientSettings.getClientParameters());
var response = tiktokHttpClient.getSigningServerResponse("im/fetch/", clientSettings.getClientParameters());
clientSettings.getClientParameters().put("cursor", response.getCursor());
clientSettings.getClientParameters().put("internal_ext", response.getInternalExt());
return response;

View File

@@ -0,0 +1,79 @@
package io.github.jwdeveloper.tiktok.http;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
public class TikTokDataChecker
{
public CompletableFuture<Boolean> isOnlineAsync(String hostName) {
return CompletableFuture.supplyAsync(() -> isOnline(hostName));
}
public CompletableFuture<Boolean> isHostNameValidAsync(String hostName) {
return CompletableFuture.supplyAsync(() -> isOnline(hostName));
}
public boolean isOnline(String hostName) {
var factory = new TikTokHttpRequestFactory(new TikTokCookieJar());
var url = getLiveUrl(hostName);
try {
var response = factory.get(url);
var titleContent = extractTitleContent(response);
return isTitleLiveOnline(titleContent);
} catch (Exception e)
{
throw new TikTokLiveRequestException("Unable to make check live online request",e);
}
}
public boolean isHostNameValid(String hostName) {
var factory = new TikTokHttpRequestFactory(new TikTokCookieJar());
var url = getProfileUrl(hostName);
try {
var response = factory.get(url);
var titleContent = extractTitleContent(response);
return isTitleHostNameValid(titleContent, hostName);
} catch (Exception e)
{
throw new TikTokLiveRequestException("Unable to make check host name valid request",e);
}
}
private boolean isTitleLiveOnline(String title) {
return title.contains("is LIVE");
}
private boolean isTitleHostNameValid(String title, String hostName)
{
return title.contains(hostName);
}
private String extractTitleContent(String html) {
var regex = "<title\\b[^>]*>(.*?)<\\/title>";
var pattern = Pattern.compile(regex);
var matcher = pattern.matcher(html);
if (matcher.find()) {
return matcher.group(1);
} else {
return "";
}
}
private String getLiveUrl(String hostName) {
var sb = new StringBuilder();
sb.append("https://www.tiktok.com/@");
sb.append(hostName);
sb.append("/live");
return sb.toString();
}
private String getProfileUrl(String hostName) {
var sb = new StringBuilder();
sb.append("https://www.tiktok.com/@");
sb.append(hostName);
return sb.toString();
}
}

View File

@@ -60,20 +60,22 @@ public class TikTokHttpClient {
return get;
}
public String postMessageToChat(Map<String,Object> parameters)
public JsonObject getJsonFromTikTokApi(String path, Map<String,Object> params)
{
var get = postRequest(Constants.TIKTOK_URL_WEBCAST + "room/chat/", parameters);
return get;
var get = getRequest(Constants.TIKTOK_URL_WEB + path, params);
var json = JsonParser.parseString(get);
var jsonObject = json.getAsJsonObject();
return jsonObject;
}
public JsonObject getJObjectFromWebcastAPI(String path, Map<String, Object> parameters) {
public JsonObject getJsonFromWebcastApi(String path, Map<String, Object> parameters) {
var get = getRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters);
var json = JsonParser.parseString(get);
var jsonObject = json.getAsJsonObject();
return jsonObject;
}
public WebcastResponse getSigningServerMessage(String path, Map<String, Object> parameters) {
public WebcastResponse getSigningServerResponse(String path, Map<String, Object> parameters) {
var bytes = getSignRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters);
try {
return WebcastResponse.parseFrom(bytes);

View File

@@ -59,22 +59,29 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest {
@SneakyThrows
public String get(String url) {
var uri = URI.create(url);
var request = HttpRequest.newBuilder().GET();
var requestBuilder = HttpRequest.newBuilder().GET();
for (var header : defaultHeaders.entrySet())
{
if(header.getKey().equals("Connection") || header.getKey().equals("Accept-Encoding"))
{
continue;
}
request.setHeader(header.getKey(), header.getValue());
requestBuilder.setHeader(header.getKey(), header.getValue());
}
if (query != null) {
var baseUri = uri.toString();
var requestUri = URI.create(baseUri + "?" + query);
request.uri(requestUri);
requestBuilder.uri(requestUri);
}
else
{
requestBuilder.uri(uri);
}
return getContent(request.build());
var result = requestBuilder.build();
return getContent(result);
}
@SneakyThrows

View File

@@ -46,6 +46,7 @@ public class TikTokGiftManagerTest {
var gifts = giftManager.getGifts();
var optional = gifts.stream().filter(r -> r == fakeGift).findFirst();
Assertions.assertTrue(optional.isPresent());
// Assertions.assertNotNull(optional.get().name());
}
@Test

View File

@@ -0,0 +1,89 @@
/*
* 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.handlers.events;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.messages.data.GiftStruct;
import io.github.jwdeveloper.tiktok.messages.data.Image;
import io.github.jwdeveloper.tiktok.messages.data.User;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import java.util.logging.Logger;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TikTokGiftEventHandlerTest {
public static TikTokGiftEventHandler handler;
@BeforeAll
public void before() {
var manager = new TikTokGiftManager(Logger.getLogger("x"));
manager.registerGift(123, "example", 123, new Picture("image.webp"));
handler = new TikTokGiftEventHandler(manager);
}
@Test
void shouldHandleGifts() {
var message = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1);
var result = handler.handleGift(message);
Assertions.assertEquals(1, result.size());
var event = (TikTokGiftEvent) result.get(0);
var gift = event.getGift();
Assertions.assertEquals("image-new.png",gift.getPicture().getLink());
Assertions.assertEquals(123,gift.getId());
}
public WebcastGiftMessage getGiftMessage(String giftName,
int giftId,
String giftImage,
int sendType,
int userId) {
var builder = WebcastGiftMessage.newBuilder();
var giftBuilder = GiftStruct.newBuilder();
var userBuilder = User.newBuilder();
giftBuilder.setId(giftId);
giftBuilder.setName(giftName);
giftBuilder.setImage(Image.newBuilder().addUrlList(giftImage).build());
userBuilder.setId(userId);
builder.setGiftId(giftId);
builder.setUser(userBuilder);
builder.setSendType(sendType);
builder.setGift(giftBuilder);
return builder.build();
}
}

View File

@@ -84,23 +84,8 @@ public class TikTokApiServiceTest
verify(tiktokHttpClient, times(1)).setSessionId("validSessionId");
}
@Test
void sendMessage_EmptySessionId_ThrowsException() {
assertThrows(TikTokLiveException.class, () -> {
tikTokApiService.sendMessage("some message", "");
});
}
@Test
void sendMessage_NullRoomId_ThrowsException() {
when(clientSettings.getClientParameters()).thenReturn(new HashMap<>());
assertThrows(TikTokLiveException.class, () -> {
tikTokApiService.sendMessage("some message", "someSessionId");
});
}
@Test
// @Test
void fetchRoomId_ValidResponse_ReturnsRoomId() throws Exception {
String expectedRoomId = "123456";
String htmlResponse = "room_id=" + expectedRoomId ;
@@ -112,7 +97,7 @@ public class TikTokApiServiceTest
verify(clientSettings.getClientParameters()).put("room_id", expectedRoomId);
}
@Test
// @Test
void fetchRoomId_ExceptionThrown_ThrowsTikTokLiveRequestException() throws Exception {
when(tiktokHttpClient.getLivestreamPage(anyString())).thenThrow(new Exception("some exception"));
@@ -121,14 +106,14 @@ public class TikTokApiServiceTest
});
}
@Test
//@Test
void fetchRoomInfo_ValidResponse_ReturnsLiveRoomMeta() throws Exception {
HashMap<String, Object> clientParameters = new HashMap<>();
var mockResponse = new JsonObject(); // Assume JsonObject is from the Gson library
var expectedLiveRoomMeta = new LiveRoomMeta(); // Assume LiveRoomMeta is a simple POJO
when(clientSettings.getClientParameters()).thenReturn(clientParameters);
when(tiktokHttpClient.getJObjectFromWebcastAPI(anyString(), any())).thenReturn(mockResponse);
when(tiktokHttpClient.getJsonFromWebcastApi(anyString(), any())).thenReturn(mockResponse);
when(new LiveRoomMetaMapper().map(mockResponse)).thenReturn(expectedLiveRoomMeta); // Assuming LiveRoomMetaMapper is a simple mapper class
LiveRoomMeta liveRoomMeta = tikTokApiService.fetchRoomInfo();
@@ -136,9 +121,9 @@ public class TikTokApiServiceTest
assertEquals(expectedLiveRoomMeta, liveRoomMeta);
}
@Test
// @Test
void fetchRoomInfo_ExceptionThrown_ThrowsTikTokLiveRequestException() throws Exception {
when(tiktokHttpClient.getJObjectFromWebcastAPI(anyString(), any())).thenThrow(new Exception("some exception"));
when(tiktokHttpClient.getJsonFromWebcastApi(anyString(), any())).thenThrow(new Exception("some exception"));
assertThrows(TikTokLiveRequestException.class, () -> {
tikTokApiService.fetchRoomInfo();

View File

@@ -0,0 +1,18 @@
package io.github.jwdeveloper.tiktok.http;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class TikTokLiveOnlineCheckerTest {
private final String TARGET_USER = "bangbetmenygy";
@Test
public void shouldTestOnline() {
var sut = new TikTokDataChecker();
var result = sut.isOnline(TARGET_USER);
Assertions.assertTrue(result);
}
}

View File

@@ -41,7 +41,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.25-Release</version>
<version>1.0.3-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,59 @@
/*
* 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;
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();
})
.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();
}
}

View File

@@ -22,6 +22,7 @@
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import java.io.IOException;
@@ -30,11 +31,26 @@ import java.util.logging.Level;
public class SimpleExample
{
public static String TIKTOK_HOSTNAME = "szwagierkaqueen";
public static String TIKTOK_HOSTNAME = "seanfitzness";
public static void main(String[] args) throws IOException {
showLogo();
// set tiktok username
/*
Optional checking if host name is correct
if(TikTokLive.isHostNameValid(TIKTOK_HOSTNAME))
{
System.out.println("Live is online!");
}
Optional checking if live is online
if(TikTokLive.isLiveOnline(TIKTOK_HOSTNAME))
{
System.out.println("Live is online!");
}
*/
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.configure(clientSettings ->
{
@@ -78,6 +94,10 @@ public class SimpleExample
.onDisconnected((liveClient, event) ->
{
print(ConsoleColors.RED,"[Disconnected]");
})
.onRoom((liveClient, event) ->
{
})
.onFollow((liveClient, event) ->
{

View File

@@ -30,11 +30,18 @@
</div>
</div>
# Introduction
A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/sebheron/TikTokLiveSharp). Use it to receive live stream events such as comments and gifts in realtime from [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal WebCast push service.
The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers.
No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked.
<div align="center">
<a href="https://www.youtube.com/watch?v=eerWGgUKc6c" align="right" target="blank"><img src="https://img.youtube.com/vi/eerWGgUKc6c/hqdefault.jpg" alt="IMAGE ALT TEXT" width="38%" align="right"></a>
</div>
Join the support [discord](https://discord.gg/e2XwPNTBBr) and visit the `#java-support` channel for questions, contributions and ideas. Feel free to make pull requests with missing/new features, fixes, etc
Do you prefer other programming languages?
@@ -45,12 +52,19 @@ Do you prefer other programming languages?
**NOTE:** This is not an official API. It's a reverse engineering project.
#### Overview
- [Getting started](#getting-started)
- [Events](#events)
- [Listeners](#listeners)
- [Contributing](#contributing)
## Getting started
1. Install the package via Maven
@@ -67,7 +81,7 @@ Do you prefer other programming languages?
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId>
<version>1.0.0</version>
<version>1.0.3-Release</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.25-Release</version>
<version>1.0.3-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -29,6 +29,11 @@
<artifactId>jdbi3-core</artifactId>
<version>3.23.0</version>
</dependency>
<dependency>
<groupId>com.googlecode.protobuf-java-format</groupId>
<artifactId>protobuf-java-format</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-sqlobject</artifactId>

View File

@@ -38,39 +38,23 @@ public class RunCollector {
public static void main(String[] args) throws SQLException, IOException {
TikTokMessageCollectorClient.create("giftsCollector")
.addUser("cbcgod")
// .addUser("mr_cios")
.addUser("crece.sara")
.addUser("moniczkka")
// .addUser("cbcgod")
// .addUser("psychotropnazywo")
// .addUser("accordionistka")
.addEventFilter(WebcastGiftMessage.class)
.addOnBuilder(liveClientBuilder ->
{
liveClientBuilder.onGift((liveClient, event) ->
{
});
liveClientBuilder.onGiftCombo((liveClient, event) ->
{
});
liveClientBuilder.onGift((liveClient, event) ->
{
var sb = new StringBuilder();
sb.append("GIFT User: " + event.getUser().getProfileName()+" ");
sb.append("Name: " + event.getGift().name() + " ");
sb.append("Combo: " + event.getCombo() + " ");
System.out.println(sb.toString());
});
liveClientBuilder.onGiftCombo((liveClient, event) ->
{
var gifts = liveClient.getGiftManager().getGifts();
var sb = new StringBuilder();
sb.append("COMBO User: " + event.getUser().getProfileName()+" ");
sb.append("Name: " + event.getGift().name() + " ");
sb.append("GIFT User: " + event.getUser().getProfileName()+" ");
sb.append("Name: " + event.getGift().getName() + " ");
sb.append("Combo: " + event.getCombo() + " ");
sb.append("Type: " + event.getComboState().name());
System.out.println(sb.toString());
});
})
@@ -78,6 +62,7 @@ public class RunCollector {
System.in.read();
}
}

View File

@@ -37,6 +37,7 @@ public class MessageUtil
var deserialized = parseMethod.invoke(null, message.getPayload());
return JsonUtil.messageToJson(deserialized);
} catch (Exception ex) {
return ConsoleColors.RED+ "Can not find mapper for "+message.getMethod();
}
}
@@ -48,8 +49,13 @@ public class MessageUtil
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", byte[].class);
var deserialized = parseMethod.invoke(null, bytes);
return JsonUtil.messageToJson(deserialized);
} catch (Exception ex) {
return ConsoleColors.RED+ "Can not find mapper for "+methodName;
} catch (Exception ex)
{
var sb = new StringBuilder();
sb.append("Can not find mapper for "+methodName);
sb.append("\n");
//String jsonString = JsonFormat.printToString(protobufData);
return sb.toString();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.25-Release</version>
<version>1.0.3-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Tools-EventsWebViewer</artifactId>
@@ -20,6 +20,7 @@
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>Tools-EventsCollector</artifactId>

View File

@@ -40,16 +40,15 @@ public class TikTokManager {
MessageCollector msgCollector;
public TikTokManager() {
msgCollector = new MessageCollector("web");
msgCollector = new MessageCollector("ab");
}
public void connect(String name) throws SQLException {
disconnect();
client = TikTokMessageCollectorClient.create(msgCollector, "web")
client = TikTokMessageCollectorClient.create(msgCollector, "ab")
.addOnBuilder(liveClientBuilder ->
{
liveClientBuilder.onGift((liveClient, event) ->
{

View File

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

View File

@@ -33,6 +33,10 @@ A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive
The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers.
No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked.
<div align="center">
<a href="https://www.youtube.com/watch?v=eerWGgUKc6c" align="right" target="blank"><img src="https://img.youtube.com/vi/eerWGgUKc6c/hqdefault.jpg" alt="IMAGE ALT TEXT" width="38%" align="right"></a>
</div>
Join the support [discord](https://discord.gg/e2XwPNTBBr) and visit the `#java-support` channel for questions, contributions and ideas. Feel free to make pull requests with missing/new features, fixes, etc
Do you prefer other programming languages?

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.25-Release</version>
<version>1.0.3-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -22,6 +22,7 @@
<artifactId>javapoet</artifactId>
<version>1.13.0</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>

View File

@@ -44,6 +44,10 @@ public class GenerateGiftsEnum {
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);
@@ -66,11 +70,11 @@ public class GenerateGiftsEnum {
.addParameter(int.class, "id")
.addParameter(String.class, "name")
.addParameter(int.class, "diamondCost")
.addParameter(Picture.class, "picture")
.addParameter(String.class, "pictureLink")
.addStatement("this.id = id")
.addStatement("this.name = name")
.addStatement("this.diamondCost = diamondCost")
.addStatement("this.picture = picture")
.addStatement("this.picture = new Picture(pictureLink)")
.build();
var inRangeMethod = MethodSpec.methodBuilder("hasDiamondCostRange")
@@ -91,7 +95,7 @@ public class GenerateGiftsEnum {
enumBuilder.addMethod(constructor);
enumBuilder.addEnumConstant("UNDEFINED", addGift(-1, "undefined", -1, new Picture("")));
enumBuilder.addEnumConstant("UNDEFINED", addGift(-1, "undefined", -1, ""));
for (var giftInfo : giftInfoMap.entrySet()) {
@@ -117,16 +121,13 @@ public class GenerateGiftsEnum {
if (contier > 1) {
enumName += "_" + value.getId();
}
enumBuilder.addEnumConstant(enumName, addGift(value.getId(), value.getName(), value.getDiamondCost(), new Picture(value.getImage())));
enumBuilder.addEnumConstant(enumName, addGift(value.getId(), value.getName(), value.getDiamondCost(), value.getImage()));
contier++;
}
}
onEnums(enumBuilder);
var output = JavaFile.builder("io.github.jwdeveloper.tiktok.events.objects", enumBuilder.build());
var output = JavaFile.builder("io.github.jwdeveloper.tiktok.data.models.gifts", enumBuilder.build());
output.addFileComment("This enum is generated");
return output.build();
}
@@ -140,22 +141,14 @@ public class GenerateGiftsEnum {
}
}
public static void onEnums(TypeSpec.Builder builder) {
// builder.addEnumConstant("RUGBY_BALL", addGift(6249, "Rugby Ball", 10));
// builder.addEnumConstant("I_LOVE_YOU", addGift(5779, "I Love you", 10));
// builder.addEnumConstant("BOUQUET_FLOWER", addGift(5780, "Bouquet Flower", 30));
}
public static TypeSpec addGift(int id, String name, int diamont, Picture picture)
public static TypeSpec addGift(int id, String name, int diamond, String picture)
{
var pictureValue = "new Picture(\""+picture.getLink()+"\")";
return TypeSpec.anonymousClassBuilder(
"$L, $S, $L, $S",
id,
name,
diamont,
pictureValue)
diamond,
picture)
.build();
}

View File

@@ -26,6 +26,7 @@ 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;
@@ -38,11 +39,15 @@ import java.util.TreeMap;
public class GiftsDownloader {
public static void main(String[] run) {
new GiftsDownloader().getGifts();
var gifts = new GiftsDownloader().getGifts();
for(var gift : gifts)
{
System.out.println(gift.toString());
}
}
public List<GiftDto> getGiftsFromFile() {
var content = FilesUtility.loadFileContent("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\output.json");
var content = FilesUtility.loadFileContent("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\output_1_0_4.json");
Type mapType = new TypeToken<Map<Integer, GiftDto>>() {
}.getType();
var mapper = new Gson().fromJson(content, mapType);
@@ -62,6 +67,10 @@ public class GiftsDownloader {
var officialGifts = officalGift.run();
System.out.println("Official Gifts: " + officialGifts.size());
System.out.println("Downlooading Official Gifts");
var extraGiftsJson = new GiftExtraJson();
var extraGifts = extraGiftsJson.run();
System.out.println("Official Gifts: " + extraGifts.size());
var outputHashMap = new TreeMap<Integer, GiftDto>();
for (var gift : scraperGifts) {
@@ -70,10 +79,13 @@ public class GiftsDownloader {
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.json", json);
FilesUtility.saveFile("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\output_1_0_4.json", json);
System.out.println("Gifts saved to file!");
return outputHashMap.values().stream().toList();
}

View File

@@ -0,0 +1,56 @@
package io.github.jwdeveloper.tiktok.gifts.downloader;
import com.google.gson.*;
import io.github.jwdeveloper.tiktok.Constants;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
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();
}
}

View File

@@ -88,7 +88,7 @@ public class GiftOfficialJson {
var fileName = "official_" + date + ".json";
try {
var response = tiktokHttpClient.getJObjectFromWebcastAPI("gift/list/", settings.getClientParameters());
var response = tiktokHttpClient.getJsonFromWebcastApi("gift/list/", settings.getClientParameters());
var gson = new GsonBuilder().setPrettyPrinting().create();
FilesUtility.saveFile("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\official\\" + fileName, gson.toJson(response));
if (!response.has("data")) {

View File

@@ -93,7 +93,7 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder {
tiktokRoomInfo.setHostName(clientSettings.getHostName());
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
var giftManager = new TikTokGiftManager();
var giftManager = new TikTokGiftManager(logger);
var requestFactory = new TikTokHttpRequestFactory(cookie);
var apiClient = new TikTokHttpClient(cookie, requestFactory);
var apiService = new ApiServiceMock(apiClient, logger, clientSettings);

View File

@@ -0,0 +1,44 @@
[
{
"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"
}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -553,9 +553,9 @@
},
"5631": {
"id": 5631,
"name": "",
"name": "Power hug",
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/ecf/ecf0b721b1b346a31fba8b0a17fe6287/5412c67a1cacdfbfc4c01407b8b4263c.png"
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/9578adce6e3da2d211583212bdfd1b0e~tplv-obj.png"
},
"5632": {
"id": 5632,
@@ -2021,6 +2021,12 @@
"diamondCost": 500,
"image": "https://storage.streamdps.com/iblock/25f/25f030f47cfc60d296bb1041ddb91f6e/ca3357a76a2be178c581530009ce215a.png"
},
"6367": {
"id": 6367,
"name": "Falcon",
"diamondCost": 10999,
"image": "https://storage.streamdps.com/iblock/f88/f886e7678bef35f8c762a323386e6d23/7249e0af64c78d1d569a8d7a86ab58cd.png"
},
"6369": {
"id": 6369,
"name": "Lion",
@@ -2099,6 +2105,12 @@
"diamondCost": 5,
"image": "https://storage.streamdps.com/iblock/ce5/ce57f012363358333397b6c72704b466/aa71c1c351b698c09a151a434bfd2652.png"
},
"6417": {
"id": 6417,
"name": "Club",
"diamondCost": 2000,
"image": "https://storage.streamdps.com/iblock/49b/49be18ae5914346ffcaf15a519ba9c1c/41326cb23d22010f0c4a8edf5bd27615.webp"
},
"6427": {
"id": 6427,
"name": "Hat and Mustache",
@@ -2819,6 +2831,12 @@
"diamondCost": 2150,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/46fa70966d8e931497f5289060f9a794~tplv-obj.jpg"
},
"6833": {
"id": 6833,
"name": "Castle Fantasy",
"diamondCost": 20000,
"image": "https://storage.streamdps.com/iblock/a08/a088a2975c7d4a68b8146a4c6b5c97c1/2729c82ccd54828bd950675e7491d71c.webp"
},
"6834": {
"id": 6834,
"name": "Gift Box",
@@ -3251,6 +3269,12 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/341/341ed57767654fa7df9660988af5aa8c/b8ef51ac15bd2af523d9010fc0259d7f.webp"
},
"7041": {
"id": 7041,
"name": "Arcade Game",
"diamondCost": 1200,
"image": "https://storage.streamdps.com/iblock/fd0/fd0785612b024900444a0a69083400ff/3181d6af50b05dd65a7ba75902bb5b94.webp"
},
"7048": {
"id": 7048,
"name": "I LOVE KR",
@@ -3803,6 +3827,12 @@
"diamondCost": 10,
"image": "https://storage.streamdps.com/iblock/b24/b24309d4ea6722875678e492ae12fb3f/864ac7928a78b43be2d1ee93915a53f5.webp"
},
"7598": {
"id": 7598,
"name": "Pirates Ship",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/475/4753e54cae562b34edbf1a157cd60b21/722409ec69cfaf707d611b0987799296.webp"
},
"7604": {
"id": 7604,
"name": "2023",
@@ -3905,6 +3935,24 @@
"diamondCost": 100,
"image": "https://storage.streamdps.com/iblock/841/841037f168f5e2757ead3d4989d40850/cac3e62b0c75d0914fe2e588832e14ee.webp"
},
"7812": {
"id": 7812,
"name": "Bravo",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b25e72d59e9771b09da8c8c70f395f82~tplv-obj.png"
},
"7813": {
"id": 7813,
"name": "Health Potion",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/13f6a46b763c496306ff541daf3021a4~tplv-obj.png"
},
"7814": {
"id": 7814,
"name": "Panettone",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/64ce2413a362442819b4551703b7b26c~tplv-obj.png"
},
"7823": {
"id": 7823,
"name": "Leon and Lion",
@@ -3995,6 +4043,12 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/923/92341a47e85be94fb6a6699a6c430a93/d60527955f9597a43d339357fed6a5fc.webp"
},
"7911": {
"id": 7911,
"name": "Maggie",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/a12/a12a1b23f1f6a19d728de84e1f43e21d/ff288346e9855a9bb6deb4450491028f.webp"
},
"7927": {
"id": 7927,
"name": "Puppy Love",
@@ -4043,6 +4097,12 @@
"diamondCost": 88,
"image": "https://storage.streamdps.com/iblock/e22/e2266686271c7a90ff04517f248c6f73/0459d679c01a5bfa5a4be1d61ec81ec8.webp"
},
"7978": {
"id": 7978,
"name": "Leopard",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/eb4/eb4d116b15c03c2974b86fa400fa6a07/9a34b020e29f2d25f434387ae01b6386.webp"
},
"7984": {
"id": 7984,
"name": "Counting Sheep",
@@ -4229,6 +4289,12 @@
"diamondCost": 88,
"image": "https://storage.streamdps.com/iblock/88e/88e25becb6f23daa0e97669a3b2905fb/d7b74b5b1e20c22e9baa4f1f02f1c6f5.webp"
},
"8239": {
"id": 8239,
"name": "White Rose",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a2d81f3847457be9083a9c76a59b08cb~tplv-obj.png"
},
"8243": {
"id": 8243,
"name": "Cheer You Up",
@@ -4349,6 +4415,12 @@
"diamondCost": 34500,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/3781e9159ff09272826d3f2216ba36ef.png~tplv-obj.jpg"
},
"8387": {
"id": 8387,
"name": "peacock",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/f9f/f9f23f00af57e8fb8a421a2a7f24aacc/a5eb745418085f1be7692f577ff04b9c.webp"
},
"8391": {
"id": 8391,
"name": "Sam the Whale",
@@ -4409,6 +4481,12 @@
"diamondCost": 88,
"image": "https://storage.streamdps.com/iblock/405/405fcf52a1de3d14ab9834c1f30cc330/0deed9ee2c79ba6bf2005b0ce667bf60.webp"
},
"8435": {
"id": 8435,
"name": "Pyramids",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/bfc/bfcf491b940e478b6410047bc047af1b/abbbdd13015a9f31be1b905268873d73.webp"
},
"8448": {
"id": 8448,
"name": "Raccoon",
@@ -4421,6 +4499,12 @@
"diamondCost": 99,
"image": "https://storage.streamdps.com/iblock/a0f/a0ff283ce42ad27a03d6b8b98e81463b/9e5a49a9bae80f0afa30257d562cec8e.webp"
},
"8456": {
"id": 8456,
"name": "Zeus",
"diamondCost": 34000,
"image": "https://storage.streamdps.com/iblock/f4e/f4e74e07fff3d3b48143a5c56af7fec4/8b15ef2f342dcd2066bcdcf82e5f07e9.webp"
},
"8457": {
"id": 8457,
"name": "Zeus",
@@ -4457,12 +4541,24 @@
"diamondCost": 199,
"image": "https://storage.streamdps.com/iblock/50f/50f04937063753d6de255d2b5a080c1c/4f101c7c50ddbe8bd26a2ce5f8c16896.webp"
},
"8599": {
"id": 8599,
"name": "Convertible Car",
"diamondCost": 12000,
"image": "https://storage.streamdps.com/iblock/2cf/2cfc5af50894de318b81438a7e137710/060001e901992f5462c841b987876eeb.webp"
},
"8600": {
"id": 8600,
"name": "Sending positivity",
"diamondCost": 199,
"image": "https://storage.streamdps.com/iblock/29b/29b0e9cb18e3479d17188235f8fdf480/58c6e916f44dcdda9d2f68dbdae77ddb.webp"
},
"8602": {
"id": 8602,
"name": "Gorilla",
"diamondCost": 30000,
"image": "https://storage.streamdps.com/iblock/1e2/1e29b9d1a0263f1487498dc556cdcbc1/bec227242f8c9b258855071aa050ac17.webp"
},
"8604": {
"id": 8604,
"name": "Starfish Bay",
@@ -4553,6 +4649,12 @@
"diamondCost": 2000,
"image": "https://storage.streamdps.com/iblock/a29/a29903a975ce45f7b9939b510412fcee/051afc0510a7349a9ebfcde9e0fdec24.webp"
},
"8814": {
"id": 8814,
"name": "Superhero fight",
"diamondCost": 30000,
"image": "https://storage.streamdps.com/iblock/d6b/d6b1c955153c8f8c5048d6c8f0d1b418/97d04b889e64328e9ab07224f6072b5f.webp"
},
"8815": {
"id": 8815,
"name": "Pink shoes",
@@ -4661,6 +4763,12 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/ff9/ff906a964a6ad9c4504438302d9354b8/3ee4796c239930c395afb3d7ef10295a.webp"
},
"9086": {
"id": 9086,
"name": "Man V Seagull",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/e5d/e5d95d519ee0ed7922de14f224a9504d/e80d8e840dd44cdf20de4c572c25e0f4.webp"
},
"9087": {
"id": 9087,
"name": "Flame heart",
@@ -4673,6 +4781,12 @@
"diamondCost": 41999,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/bfb8425a7e8fa03f9fec05a973a4a506.png~tplv-obj.jpg"
},
"9095": {
"id": 9095,
"name": "Birthday Party",
"diamondCost": 6999,
"image": "https://storage.streamdps.com/iblock/d0d/d0d1164a9ed81239b70cb25b93927023/d0dba293643c67dc33c1f4dda04e5b50.webp"
},
"9096": {
"id": 9096,
"name": "Birthday Crown",
@@ -4685,6 +4799,18 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/5b9/5b9eca4a99e965cb25183681a07a5276/c28f7e9c4a8e42460225ff2d12300ae7.webp"
},
"9138": {
"id": 9138,
"name": "Trending Figure",
"diamondCost": 999,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/df7b556ccf369bf9a42fe83ec8a77acf.png~tplv-obj.jpg"
},
"9139": {
"id": 9139,
"name": "Team Bracelet",
"diamondCost": 2,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/54cb1eeca369e5bea1b97707ca05d189.png~tplv-obj.png"
},
"9147": {
"id": 9147,
"name": "Bigfoot",
@@ -4708,5 +4834,53 @@
"name": "Pretzel",
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/a67/a6797793eb382a99d38b2a0c37ec9b58/04ea1042707a361ad0f4668d0d759daa.webp"
},
"9184": {
"id": 9184,
"name": "Cube",
"diamondCost": 10,
"image": "https://storage.streamdps.com/iblock/69d/69dab4e352882c0bd29c3864e24d80de/258857221189c76260b6af5eeb43e93b.webp"
},
"9240": {
"id": 9240,
"name": "Dancing queens",
"diamondCost": 20000,
"image": "https://storage.streamdps.com/iblock/c79/c793af446369ecef5238e73312c84ccd/464a76f3e6eaee9afc771f45a4bba9df.webp"
},
"9255": {
"id": 9255,
"name": "Aerobic headband",
"diamondCost": 99,
"image": "https://storage.streamdps.com/iblock/3d9/3d98c2fbc96922da37a9d22881bb06b9/0a99af132ab8e3fe9806d2412abc6bf0.webp"
},
"9463": {
"id": 9463,
"name": "Fairy Wings",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/e504dc2f313b8c6df9e99a848e1b3a99.png~tplv-obj.png"
},
"9465": {
"id": 9465,
"name": "Fruit Friends",
"diamondCost": 299,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1153dd51308c556cb4fcc48c7d62209f.png~tplv-obj.jpg"
},
"9466": {
"id": 9466,
"name": "Amusement Park",
"diamondCost": 17000,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/12ecc01c2984c5d85bb508e80103a3cb.png~tplv-obj.jpg"
},
"9467": {
"id": 9467,
"name": "Lili the Leopard",
"diamondCost": 6599,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/7be03e1af477d1dbc6eb742d0c969372.png~tplv-obj.jpg"
},
"9468": {
"id": 9468,
"name": "Rhythmic Bear",
"diamondCost": 2999,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/16eacf541e4bd6816e88139d079519f5.png~tplv-obj.jpg"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -553,9 +553,9 @@
},
"5631": {
"id": 5631,
"name": "",
"name": "Power hug",
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/ecf/ecf0b721b1b346a31fba8b0a17fe6287/5412c67a1cacdfbfc4c01407b8b4263c.png"
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/9578adce6e3da2d211583212bdfd1b0e~tplv-obj.png"
},
"5632": {
"id": 5632,
@@ -2021,6 +2021,12 @@
"diamondCost": 500,
"image": "https://storage.streamdps.com/iblock/25f/25f030f47cfc60d296bb1041ddb91f6e/ca3357a76a2be178c581530009ce215a.png"
},
"6367": {
"id": 6367,
"name": "Falcon",
"diamondCost": 10999,
"image": "https://storage.streamdps.com/iblock/f88/f886e7678bef35f8c762a323386e6d23/7249e0af64c78d1d569a8d7a86ab58cd.png"
},
"6369": {
"id": 6369,
"name": "Lion",
@@ -2099,6 +2105,12 @@
"diamondCost": 5,
"image": "https://storage.streamdps.com/iblock/ce5/ce57f012363358333397b6c72704b466/aa71c1c351b698c09a151a434bfd2652.png"
},
"6417": {
"id": 6417,
"name": "Club",
"diamondCost": 2000,
"image": "https://storage.streamdps.com/iblock/49b/49be18ae5914346ffcaf15a519ba9c1c/41326cb23d22010f0c4a8edf5bd27615.webp"
},
"6427": {
"id": 6427,
"name": "Hat and Mustache",
@@ -2819,6 +2831,12 @@
"diamondCost": 2150,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/46fa70966d8e931497f5289060f9a794~tplv-obj.jpg"
},
"6833": {
"id": 6833,
"name": "Castle Fantasy",
"diamondCost": 20000,
"image": "https://storage.streamdps.com/iblock/a08/a088a2975c7d4a68b8146a4c6b5c97c1/2729c82ccd54828bd950675e7491d71c.webp"
},
"6834": {
"id": 6834,
"name": "Gift Box",
@@ -3251,6 +3269,12 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/341/341ed57767654fa7df9660988af5aa8c/b8ef51ac15bd2af523d9010fc0259d7f.webp"
},
"7041": {
"id": 7041,
"name": "Arcade Game",
"diamondCost": 1200,
"image": "https://storage.streamdps.com/iblock/fd0/fd0785612b024900444a0a69083400ff/3181d6af50b05dd65a7ba75902bb5b94.webp"
},
"7048": {
"id": 7048,
"name": "I LOVE KR",
@@ -3803,6 +3827,12 @@
"diamondCost": 10,
"image": "https://storage.streamdps.com/iblock/b24/b24309d4ea6722875678e492ae12fb3f/864ac7928a78b43be2d1ee93915a53f5.webp"
},
"7598": {
"id": 7598,
"name": "Pirates Ship",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/475/4753e54cae562b34edbf1a157cd60b21/722409ec69cfaf707d611b0987799296.webp"
},
"7604": {
"id": 7604,
"name": "2023",
@@ -3905,6 +3935,24 @@
"diamondCost": 100,
"image": "https://storage.streamdps.com/iblock/841/841037f168f5e2757ead3d4989d40850/cac3e62b0c75d0914fe2e588832e14ee.webp"
},
"7812": {
"id": 7812,
"name": "Bravo",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b25e72d59e9771b09da8c8c70f395f82~tplv-obj.png"
},
"7813": {
"id": 7813,
"name": "Health Potion",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/13f6a46b763c496306ff541daf3021a4~tplv-obj.png"
},
"7814": {
"id": 7814,
"name": "Panettone",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/64ce2413a362442819b4551703b7b26c~tplv-obj.png"
},
"7823": {
"id": 7823,
"name": "Leon and Lion",
@@ -3995,6 +4043,12 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/923/92341a47e85be94fb6a6699a6c430a93/d60527955f9597a43d339357fed6a5fc.webp"
},
"7911": {
"id": 7911,
"name": "Maggie",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/a12/a12a1b23f1f6a19d728de84e1f43e21d/ff288346e9855a9bb6deb4450491028f.webp"
},
"7927": {
"id": 7927,
"name": "Puppy Love",
@@ -4043,6 +4097,12 @@
"diamondCost": 88,
"image": "https://storage.streamdps.com/iblock/e22/e2266686271c7a90ff04517f248c6f73/0459d679c01a5bfa5a4be1d61ec81ec8.webp"
},
"7978": {
"id": 7978,
"name": "Leopard",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/eb4/eb4d116b15c03c2974b86fa400fa6a07/9a34b020e29f2d25f434387ae01b6386.webp"
},
"7984": {
"id": 7984,
"name": "Counting Sheep",
@@ -4229,6 +4289,12 @@
"diamondCost": 88,
"image": "https://storage.streamdps.com/iblock/88e/88e25becb6f23daa0e97669a3b2905fb/d7b74b5b1e20c22e9baa4f1f02f1c6f5.webp"
},
"8239": {
"id": 8239,
"name": "White Rose",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a2d81f3847457be9083a9c76a59b08cb~tplv-obj.png"
},
"8243": {
"id": 8243,
"name": "Cheer You Up",
@@ -4349,6 +4415,12 @@
"diamondCost": 34500,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/3781e9159ff09272826d3f2216ba36ef.png~tplv-obj.jpg"
},
"8387": {
"id": 8387,
"name": "peacock",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/f9f/f9f23f00af57e8fb8a421a2a7f24aacc/a5eb745418085f1be7692f577ff04b9c.webp"
},
"8391": {
"id": 8391,
"name": "Sam the Whale",
@@ -4409,6 +4481,12 @@
"diamondCost": 88,
"image": "https://storage.streamdps.com/iblock/405/405fcf52a1de3d14ab9834c1f30cc330/0deed9ee2c79ba6bf2005b0ce667bf60.webp"
},
"8435": {
"id": 8435,
"name": "Pyramids",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/bfc/bfcf491b940e478b6410047bc047af1b/abbbdd13015a9f31be1b905268873d73.webp"
},
"8448": {
"id": 8448,
"name": "Raccoon",
@@ -4421,6 +4499,12 @@
"diamondCost": 99,
"image": "https://storage.streamdps.com/iblock/a0f/a0ff283ce42ad27a03d6b8b98e81463b/9e5a49a9bae80f0afa30257d562cec8e.webp"
},
"8456": {
"id": 8456,
"name": "Zeus",
"diamondCost": 34000,
"image": "https://storage.streamdps.com/iblock/f4e/f4e74e07fff3d3b48143a5c56af7fec4/8b15ef2f342dcd2066bcdcf82e5f07e9.webp"
},
"8457": {
"id": 8457,
"name": "Zeus",
@@ -4457,12 +4541,24 @@
"diamondCost": 199,
"image": "https://storage.streamdps.com/iblock/50f/50f04937063753d6de255d2b5a080c1c/4f101c7c50ddbe8bd26a2ce5f8c16896.webp"
},
"8599": {
"id": 8599,
"name": "Convertible Car",
"diamondCost": 12000,
"image": "https://storage.streamdps.com/iblock/2cf/2cfc5af50894de318b81438a7e137710/060001e901992f5462c841b987876eeb.webp"
},
"8600": {
"id": 8600,
"name": "Sending positivity",
"diamondCost": 199,
"image": "https://storage.streamdps.com/iblock/29b/29b0e9cb18e3479d17188235f8fdf480/58c6e916f44dcdda9d2f68dbdae77ddb.webp"
},
"8602": {
"id": 8602,
"name": "Gorilla",
"diamondCost": 30000,
"image": "https://storage.streamdps.com/iblock/1e2/1e29b9d1a0263f1487498dc556cdcbc1/bec227242f8c9b258855071aa050ac17.webp"
},
"8604": {
"id": 8604,
"name": "Starfish Bay",
@@ -4553,6 +4649,12 @@
"diamondCost": 2000,
"image": "https://storage.streamdps.com/iblock/a29/a29903a975ce45f7b9939b510412fcee/051afc0510a7349a9ebfcde9e0fdec24.webp"
},
"8814": {
"id": 8814,
"name": "Superhero fight",
"diamondCost": 30000,
"image": "https://storage.streamdps.com/iblock/d6b/d6b1c955153c8f8c5048d6c8f0d1b418/97d04b889e64328e9ab07224f6072b5f.webp"
},
"8815": {
"id": 8815,
"name": "Pink shoes",
@@ -4661,6 +4763,12 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/ff9/ff906a964a6ad9c4504438302d9354b8/3ee4796c239930c395afb3d7ef10295a.webp"
},
"9086": {
"id": 9086,
"name": "Man V Seagull",
"diamondCost": 15000,
"image": "https://storage.streamdps.com/iblock/e5d/e5d95d519ee0ed7922de14f224a9504d/e80d8e840dd44cdf20de4c572c25e0f4.webp"
},
"9087": {
"id": 9087,
"name": "Flame heart",
@@ -4673,6 +4781,12 @@
"diamondCost": 41999,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/bfb8425a7e8fa03f9fec05a973a4a506.png~tplv-obj.jpg"
},
"9095": {
"id": 9095,
"name": "Birthday Party",
"diamondCost": 6999,
"image": "https://storage.streamdps.com/iblock/d0d/d0d1164a9ed81239b70cb25b93927023/d0dba293643c67dc33c1f4dda04e5b50.webp"
},
"9096": {
"id": 9096,
"name": "Birthday Crown",
@@ -4685,6 +4799,18 @@
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/5b9/5b9eca4a99e965cb25183681a07a5276/c28f7e9c4a8e42460225ff2d12300ae7.webp"
},
"9138": {
"id": 9138,
"name": "Trending Figure",
"diamondCost": 999,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/df7b556ccf369bf9a42fe83ec8a77acf.png~tplv-obj.jpg"
},
"9139": {
"id": 9139,
"name": "Team Bracelet",
"diamondCost": 2,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/54cb1eeca369e5bea1b97707ca05d189.png~tplv-obj.png"
},
"9147": {
"id": 9147,
"name": "Bigfoot",
@@ -4708,5 +4834,53 @@
"name": "Pretzel",
"diamondCost": 1,
"image": "https://storage.streamdps.com/iblock/a67/a6797793eb382a99d38b2a0c37ec9b58/04ea1042707a361ad0f4668d0d759daa.webp"
},
"9184": {
"id": 9184,
"name": "Cube",
"diamondCost": 10,
"image": "https://storage.streamdps.com/iblock/69d/69dab4e352882c0bd29c3864e24d80de/258857221189c76260b6af5eeb43e93b.webp"
},
"9240": {
"id": 9240,
"name": "Dancing queens",
"diamondCost": 20000,
"image": "https://storage.streamdps.com/iblock/c79/c793af446369ecef5238e73312c84ccd/464a76f3e6eaee9afc771f45a4bba9df.webp"
},
"9255": {
"id": 9255,
"name": "Aerobic headband",
"diamondCost": 99,
"image": "https://storage.streamdps.com/iblock/3d9/3d98c2fbc96922da37a9d22881bb06b9/0a99af132ab8e3fe9806d2412abc6bf0.webp"
},
"9463": {
"id": 9463,
"name": "Fairy Wings",
"diamondCost": 1,
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/e504dc2f313b8c6df9e99a848e1b3a99.png~tplv-obj.png"
},
"9465": {
"id": 9465,
"name": "Fruit Friends",
"diamondCost": 299,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1153dd51308c556cb4fcc48c7d62209f.png~tplv-obj.jpg"
},
"9466": {
"id": 9466,
"name": "Amusement Park",
"diamondCost": 17000,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/12ecc01c2984c5d85bb508e80103a3cb.png~tplv-obj.jpg"
},
"9467": {
"id": 9467,
"name": "Lili the Leopard",
"diamondCost": 6599,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/7be03e1af477d1dbc6eb742d0c969372.png~tplv-obj.jpg"
},
"9468": {
"id": 9468,
"name": "Rhythmic Bear",
"diamondCost": 2999,
"image": "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/16eacf541e4bd6816e88139d079519f5.png~tplv-obj.jpg"
}
}

View File

@@ -7,7 +7,7 @@
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>TikTokLiveJava</artifactId>
<packaging>pom</packaging>
<version>0.0.25-Release</version>
<version>1.0.3-Release</version>
<modules>
<module>API</module>
<module>Client</module>