Compare commits

...

27 Commits

Author SHA1 Message Date
Jacek W
69a2a6a338 Merge pull request #18 from jwdeveloper/develop-1-0-6
Changes:
  fixed TikTokLive.isHostNameValid()
  fixed TikTokLive.isLiveOnline()
  
  those methods was not working in some regions
2023-11-14 18:46:28 +01:00
JW
530551763c Changes:
fixed TikTokLive.isHostNameValid()
  fixed TikTokLive.isLiveOnline()

  those methods was not working in some regions
2023-11-14 18:44:09 +01:00
GitHub Action
ca1827853a Update version in pom.xml 2023-11-13 23:09:58 +00:00
Jacek W
ea8c740faa Merge pull request #17 from jwdeveloper/develop-1-0-5
New Events
- onLiveUnpaused()
- onRoomInfo() triggered when LiveRoomInfo got updated

Removed:
- clientSettings.setHandleExistingEvents - onRoom Replaced with onRoomInfo event - onRoomUserInfo Replaced with onRoomInfo event

Gifts:
- onGift event was not triggered for the more expensive gifts
- onGiftCombo with more expensive gifts was stuck in the GiftSendType.Begin state

Fixed:
- setPrintToConsole(false) was not disabling logs
2023-11-14 00:08:34 +01:00
JW
1977cbe8dc Changes:
New Events
     -  onLiveUnpaused()
     -  onRoomInfo() triggered when LiveRoomInfo got updated

  Removed:
      - clientSettings.setHandleExistingEvents
      - onRoom  Replaced with onRoomInfo event
      - onRoomUserInfo  Replaced with onRoomInfo event

  Gifts:
    - onGift event was not triggered for the more expensive gifts
    - onGiftCombo with more expensive gifts was stuck in the GiftSendType.Begin state

  Fixed:
     - setPrintToConsole(false) was not disabling logs
2023-11-14 00:07:14 +01:00
Jacek W
c3a48c4d70 Update README.md 2023-11-10 22:29:53 +01:00
GitHub Action
2c51844fd9 Update version in pom.xml 2023-11-10 21:29:22 +00:00
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
80 changed files with 47398 additions and 1380 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.5-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>API</artifactId>

View File

@@ -52,11 +52,6 @@ public class ClientSettings {
*/
private Duration retryConnectionTimeout;
/**
* Whether to handle Events received from Room when Connecting
*/
private boolean handleExistingEvents;
/**
* Whether to print Logs to Console
*/

View File

@@ -57,7 +57,6 @@ public class Constants {
var clientSettings = new ClientSettings();
clientSettings.setTimeout(Duration.ofSeconds(DEFAULT_TIMEOUT));
clientSettings.setClientLanguage("en-US");
clientSettings.setHandleExistingEvents(true);
clientSettings.setRetryOnConnectionFailure(false);
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1));
clientSettings.setPrintToConsole(false);

View File

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

View File

@@ -0,0 +1,21 @@
package io.github.jwdeveloper.tiktok.data.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class TikTokUserInfo
{
UserStatus userStatus;
String roomId;
public enum UserStatus
{
NotFound,
Offline,
LivePaused,
Live
}
}

View File

@@ -0,0 +1,10 @@
package io.github.jwdeveloper.tiktok.data.events;
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
@EventMeta(eventType = EventType.Message)
public class TikTokLiveUnpausedEvent extends TikTokEvent {
}

View File

@@ -20,36 +20,52 @@
* 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.room;
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.RankingUser;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomUserSeqMessage;
import lombok.Getter;
import io.github.jwdeveloper.tiktok.data.models.Text;
import java.util.List;
import java.util.stream.Collectors;
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;
@Getter
@EventMeta(eventType = EventType.Message)
public class TikTokRoomUserInfoEvent extends TikTokHeaderEvent {
private final int totalUsers;
@Value
public class TikTokChestEvent extends TikTokHeaderEvent {
/**
* Only top 5 users in ranking has detailed data
* rest has only ID
* Chest target
*/
private final List<RankingUser> usersRanking;
public TikTokRoomUserInfoEvent(WebcastRoomUserSeqMessage msg) {
super(msg.getCommon());
totalUsers = msg.getTotalUser();
usersRanking = msg.getRanksListList().stream().map(RankingUser::new)
.sorted((ru1, ru2) -> Integer.compare(ru2.getScore(), ru1.getScore()))
.collect(Collectors.toList());
}
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

@@ -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.data.events.room;
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.users.User;
import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute;
import io.github.jwdeveloper.tiktok.messages.webcast.RoomMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLiveIntroMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSystemMessage;
import lombok.Getter;
@Getter
@EventMeta(eventType = EventType.Message)
public class TikTokRoomEvent extends TikTokHeaderEvent
{
private User hostUser;
private String hostLanguage;
private final String welcomeMessage;
public TikTokRoomEvent(WebcastRoomMessage msg) {
super(msg.getCommon());
welcomeMessage = msg.getContent();
}
public TikTokRoomEvent(RoomMessage msg) {
super(msg.getCommon());
welcomeMessage = msg.getContent();
}
public TikTokRoomEvent(WebcastSystemMessage msg) {
super(msg.getCommon());
welcomeMessage = msg.getMessage();
}
public TikTokRoomEvent(WebcastLiveIntroMessage msg) {
super(msg.getCommon());
hostUser = User.map(msg.getHost());
hostUser.addAttribute(UserAttribute.LiveHost);
welcomeMessage = msg.getContent();
hostLanguage = msg.getLanguage();
}
}

View File

@@ -0,0 +1,20 @@
package io.github.jwdeveloper.tiktok.data.events.room;
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
import lombok.AllArgsConstructor;
import lombok.Getter;
/*
Triggered when LiveRoomInfo got updated such as likes, viewers, ranking ....
*/
@Getter
@AllArgsConstructor
@EventMeta(eventType = EventType.Message)
public class TikTokRoomInfoEvent extends TikTokEvent
{
LiveRoomInfo roomInfo;
}

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

@@ -45,6 +45,8 @@ public class User {
@Getter(AccessLevel.NONE)
private Set<UserAttribute> attributes;
public List<UserAttribute> getAttributes() {
return attributes.stream().toList();
}
@@ -107,6 +109,23 @@ public class User {
this.attributes = new HashSet<>();
}
public User(Long id,
String name,
String profileName,
Picture picture,
long following,
long followers,
List<Badge> badges) {
this.id = id;
this.name = name;
this.profileName = profileName;
this.picture = picture;
this.following = following;
this.followers = followers;
this.badges = badges;
this.attributes = new HashSet<>();
}
public User(Long userId,
String nickName) {
this.id = userId;

View File

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

View File

@@ -22,14 +22,31 @@
*/
package io.github.jwdeveloper.tiktok.live;
import io.github.jwdeveloper.tiktok.data.models.RankingUser;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import java.util.List;
public interface LiveRoomInfo
{
/**
*
* @return get current count of viewers of live
*/
int getViewersCount();
/**
*
* @return get total current count of viewers since beginning of live
*/
int getTotalViewersCount();
int getLikesCount();
boolean isAgeRestricted();
String getRoomId();
String getHostName();
String getTitle();
User getHostUser();
List<RankingUser> getUsersRanking();
ConnectionState getConnectionState();
}

View File

@@ -22,16 +22,19 @@
*/
package io.github.jwdeveloper.tiktok.live;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import lombok.Data;
@Data
public class LiveRoomMeta {
private LiveRoomStatus status;
private boolean ageRestricted;
private String titie;
private int likeCount;
private int totalViewers;
private int viewers;
private User host;
public enum LiveRoomStatus
{

View File

@@ -26,8 +26,7 @@ import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
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;
@@ -39,9 +38,7 @@ import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandl
public interface EventsBuilder<T> {
T onRoom(EventConsumer<TikTokRoomEvent> event);
T onRoomUserInfo(EventConsumer<TikTokRoomUserInfoEvent> event);
T onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event);
T onComment(EventConsumer<TikTokCommentEvent> event);
@@ -51,6 +48,7 @@ public interface EventsBuilder<T> {
T onWebsocketUnhandledMessage(EventConsumer<TikTokWebsocketUnhandledMessageEvent> event);
T onGiftCombo(EventConsumer<TikTokGiftComboEvent> event);
T onGift(EventConsumer<TikTokGiftEvent> event);
@@ -67,10 +65,13 @@ public interface EventsBuilder<T> {
T onJoin(EventConsumer<TikTokJoinEvent> event);
T onShare(EventConsumer<TikTokShareEvent> event);
T onUnhandledSocial(EventConsumer<TikTokUnhandledSocialEvent> event);
// T onChest(EventConsumer<TikTokChestEvent> event);
T onLivePaused(EventConsumer<TikTokLivePausedEvent> event);
T onLiveUnpaused(EventConsumer<TikTokLiveUnpausedEvent> event);
T onLiveEnded(EventConsumer<TikTokLiveEndedEvent> event);
T onConnected(EventConsumer<TikTokConnectedEvent> event);
@@ -80,10 +81,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

@@ -68,6 +68,7 @@ enum MemberMessageAction {
enum ControlAction {
ControlActionUNKNOWN = 0;
STREAM_PAUSED = 1; // Stream Paused by Host
STREAM_UNPAUSED = 2;
STREAM_ENDED = 3; // Stream Ended by Host
}
@@ -111,3 +112,28 @@ enum BarrageType
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.5-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

@@ -25,6 +25,7 @@ package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokReconnectingEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
@@ -92,8 +93,7 @@ public class TikTokLiveClient implements LiveClient {
public void connect() {
try {
tryConnect();
} catch (TikTokLiveException e)
{
} catch (TikTokLiveException e) {
setState(ConnectionState.DISCONNECTED);
tikTokEventHandler.publish(this, new TikTokErrorEvent(e));
tikTokEventHandler.publish(this, new TikTokDisconnectedEvent());
@@ -108,11 +108,15 @@ public class TikTokLiveClient implements LiveClient {
this.connect();
}
throw e;
} catch (Exception e) {
logger.info("Unhandled exception report this bug to github https://github.com/jwdeveloper/TikTokLiveJava/issues");
this.disconnect();
e.printStackTrace();
}
}
public void disconnect() {
if (!liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED)) {
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
return;
}
webSocketClient.stop();
@@ -138,14 +142,24 @@ public class TikTokLiveClient implements LiveClient {
}
var roomData = apiService.fetchRoomInfo();
if (roomData.getStatus() != LiveRoomMeta.LiveRoomStatus.HostOnline) {
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found. Is the Host online?");
var liveRoomMeta = apiService.fetchRoomInfo();
if (liveRoomMeta.getStatus() == LiveRoomMeta.LiveRoomStatus.HostNotFound) {
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found.");
}
if (liveRoomMeta.getStatus() == LiveRoomMeta.LiveRoomStatus.HostOffline) {
throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?");
}
liveRoomInfo.setTitle(liveRoomMeta.getTitie());
liveRoomInfo.setViewersCount(liveRoomMeta.getViewers());
liveRoomInfo.setTotalViewersCount(liveRoomMeta.getTotalViewers());
liveRoomInfo.setAgeRestricted(liveRoomMeta.isAgeRestricted());
liveRoomInfo.setHost(liveRoomMeta.getHost());
var clientData = apiService.fetchClientData();
webSocketClient.start(clientData, this);
setState(ConnectionState.CONNECTED);
tikTokEventHandler.publish(this, new TikTokRoomInfoEvent(liveRoomInfo));
}

View File

@@ -24,12 +24,12 @@ 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;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomEvent;
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.room.TikTokRoomUserInfoEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
@@ -42,6 +42,8 @@ import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokRoomInfoEventHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler;
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
@@ -130,11 +132,12 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
logger.setLevel(clientSettings.getLogLevel());
if (clientSettings.isPrintToConsole() && clientSettings.getLogLevel() == Level.OFF) {
logger.setLevel(Level.ALL);
if (!clientSettings.isPrintToConsole()) {
logger.setLevel(Level.OFF);
}
}
public LiveClient build() {
@@ -148,14 +151,18 @@ 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);
var roomInfoHandler = new TikTokRoomInfoEventHandler(tiktokRoomInfo);
var socialHandler = new TikTokSocialMediaEventHandler(tiktokRoomInfo);
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler,
tiktokRoomInfo,
roomInfoHandler,
eventMapper,
giftHandler
giftHandler,
socialHandler
);
var webSocketClient = new TikTokWebSocketClient(logger,
@@ -190,6 +197,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);
@@ -239,16 +252,25 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onRoom(EventConsumer<TikTokRoomEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomEvent.class, event);
@Override
public LiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onLivePaused(EventConsumer<TikTokLivePausedEvent> event) {
tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
return this;
}
@Override
public LiveClientBuilder onLiveUnpaused(EventConsumer<TikTokLiveUnpausedEvent> event) {
tikTokEventHandler.subscribe(TikTokLiveUnpausedEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onLike(EventConsumer<TikTokLikeEvent> event) {
tikTokEventHandler.subscribe(TikTokLikeEvent.class, event);
return this;
@@ -321,13 +343,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onRoomUserInfo(
EventConsumer<TikTokRoomUserInfoEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomUserInfoEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onComment(EventConsumer<TikTokCommentEvent> event) {
tikTokEventHandler.subscribe(TikTokCommentEvent.class, event);
return this;

View File

@@ -22,28 +22,52 @@
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.data.models.RankingUser;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
import lombok.Data;
import java.util.LinkedList;
import java.util.List;
@Data
public class TikTokRoomInfo implements LiveRoomInfo
{
public class TikTokRoomInfo implements LiveRoomInfo {
private String roomId;
private int likesCount;
private int viewersCount;
private String roomId;
private int totalViewersCount;
private boolean ageRestricted;
private User host;
private List<RankingUser> usersRanking = new LinkedList<>();
private String hostName;
private String title;
private String language = "en";
private ConnectionState connectionState = ConnectionState.DISCONNECTED;
public boolean hasConnectionState(ConnectionState state)
{
public boolean hasConnectionState(ConnectionState state) {
return connectionState == state;
}
@Override
public User getHostUser() {
return null;
}
public void updateRanking(List<RankingUser> rankingUsers) {
usersRanking.clear();
usersRanking.addAll(rankingUsers);
}
}

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

@@ -22,42 +22,43 @@
*/
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;
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollUpdateEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomPinEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomUserInfoEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.data.events.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.handlers.events.TikTokRoomInfoEventHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler;
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.regex.Pattern;
import java.util.Collections;
import java.util.List;
public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
private final TikTokRoomInfo roomInfo;
private final TikTokGiftEventHandler giftHandler;
private final Pattern socialMediaPattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share");
private final TikTokRoomInfoEventHandler roomInfoHandler;
private final TikTokSocialMediaEventHandler socialHandler;
public TikTokMessageHandlerRegistration(TikTokEventObserver tikTokEventHandler,
TikTokRoomInfo roomInfo,
TikTokRoomInfoEventHandler roomInfoHandler,
TikTokGenericEventMapper genericTikTokEventMapper,
TikTokGiftEventHandler tikTokGiftEventHandler) {
TikTokGiftEventHandler tikTokGiftEventHandler,
TikTokSocialMediaEventHandler tikTokSocialMediaEventHandler) {
super(tikTokEventHandler, genericTikTokEventMapper);
this.giftHandler = tikTokGiftEventHandler;
this.roomInfo = roomInfo;
this.roomInfoHandler = roomInfoHandler;
this.socialHandler = tikTokSocialMediaEventHandler;
init();
}
@@ -65,22 +66,19 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
//ConnectionEvents events
registerMapping(WebcastControlMessage.class, this::handleWebcastControlMessage);
registerMapping(WebcastSystemMessage.class, TikTokRoomEvent.class);
//Room status events
registerMapping(WebcastLiveIntroMessage.class, TikTokRoomEvent.class);
registerMapping(WebcastRoomUserSeqMessage.class, this::handleRoomUserSeqMessage);
registerMapping(RoomMessage.class, TikTokRoomEvent.class);
registerMapping(WebcastRoomMessage.class, TikTokRoomEvent.class);
registerMapping(WebcastLiveIntroMessage.class, roomInfoHandler::handleIntro);
registerMapping(WebcastRoomUserSeqMessage.class, roomInfoHandler::handleUserRanking);
registerMapping(WebcastCaptionMessage.class, TikTokCaptionEvent.class);
//User Interactions events
registerMapping(WebcastChatMessage.class, TikTokCommentEvent.class);
registerMapping(WebcastLikeMessage.class, this::handleLike);
registerMappings(WebcastLikeMessage.class, this::handleLike);
registerMappings(WebcastGiftMessage.class, giftHandler::handleGift);
registerMapping(WebcastSocialMessage.class, this::handleSocialMedia);
registerMapping(WebcastMemberMessage.class, this::handleMemberMessage);
registerMapping(WebcastSocialMessage.class, socialHandler::handle);
registerMappings(WebcastMemberMessage.class, this::handleMemberMessage);
//Host Interaction events
registerMapping(WebcastPollMessage.class, this::handlePollEvent);
@@ -106,7 +104,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);
}
@@ -118,53 +116,37 @@ public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
return switch (message.getAction()) {
case STREAM_PAUSED -> new TikTokLivePausedEvent();
case STREAM_ENDED -> new TikTokLiveEndedEvent();
case STREAM_UNPAUSED -> new TikTokLiveUnpausedEvent();
default -> new TikTokUnhandledControlEvent(message);
};
}
@SneakyThrows
private TikTokEvent handleSocialMedia(byte[] msg) {
var message = WebcastSocialMessage.parseFrom(msg);
var socialType = Text.map(message.getCommon().getDisplayText()).getKey();
var matcher = socialMediaPattern.matcher(socialType);
if (matcher.find()) {
var value = matcher.group(1);
var number = Integer.parseInt(value);
return new TikTokShareEvent(message, number);
}
return switch (socialType) {
case SocialTypes.LikeType -> new TikTokLikeEvent(message, roomInfo.getLikesCount());
case SocialTypes.FollowType -> new TikTokFollowEvent(message);
case SocialTypes.ShareType -> new TikTokShareEvent(message);
case SocialTypes.JoinType -> new TikTokJoinEvent(message, roomInfo.getViewersCount());
default -> new TikTokUnhandledSocialEvent(message);
};
}
@SneakyThrows
private TikTokEvent handleMemberMessage(byte[] msg) {
private List<TikTokEvent> handleMemberMessage(byte[] msg) {
var message = WebcastMemberMessage.parseFrom(msg);
return switch (message.getAction()) {
var event = switch (message.getAction()) {
case JOINED -> new TikTokJoinEvent(message);
case SUBSCRIBED -> new TikTokSubscribeEvent(message);
default -> new TikTokUnhandledMemberEvent(message);
};
var roomInfoEvent = roomInfoHandler.handleRoomInfo(tikTokRoomInfo ->
{
tikTokRoomInfo.setViewersCount(message.getMemberCount());
});
return List.of(event, roomInfoEvent);
}
private TikTokEvent handleRoomUserSeqMessage(byte[] msg) {
var event = (TikTokRoomUserInfoEvent) mapper.mapToEvent(WebcastRoomUserSeqMessage.class, TikTokRoomUserInfoEvent.class, msg);
roomInfo.setViewersCount(event.getTotalUsers());
return event;
}
private TikTokEvent handleLike(byte[] msg) {
private List<TikTokEvent> handleLike(byte[] msg) {
var event = (TikTokLikeEvent) mapper.mapToEvent(WebcastLikeMessage.class, TikTokLikeEvent.class, msg);
roomInfo.setLikesCount(event.getTotalLikes());
return event;
var roomInfoEvent = roomInfoHandler.handleRoomInfo(tikTokRoomInfo ->
{
tikTokRoomInfo.setLikesCount(event.getTotalLikes());
});
return List.of(event, roomInfoEvent);
}
@SneakyThrows
@@ -187,5 +169,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,10 +50,22 @@ 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);
//If gift is not streakable just return onGift event
if (currentMessage.getGift().getType() != 1) {
var comboEvent = getGiftComboEvent(currentMessage, GiftSendType.Finished);
var giftEvent = getGiftEvent(currentMessage);
return List.of(comboEvent, giftEvent);
}
if (!containsPreviousMessage) {
if (currentType == GiftSendType.Finished) {
return List.of(getGiftEvent(currentMessage));
@@ -81,6 +95,7 @@ public class TikTokGiftEventHandler {
return List.of();
}
private TikTokGiftEvent getGiftEvent(WebcastGiftMessage message) {
var gift = getGiftObject(message);
return new TikTokGiftEvent(gift, message);
@@ -92,17 +107,36 @@ public class TikTokGiftEventHandler {
}
private Gift getGiftObject(WebcastGiftMessage giftMessage) {
var gift = giftManager.findById((int) giftMessage.getGiftId());
var giftId = (int) giftMessage.getGiftId();
var gift = giftManager.findById(giftId);
if (gift == Gift.UNDEFINED) {
gift = giftManager.findByName(giftMessage.getGift().getName());
}
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

@@ -0,0 +1,57 @@
package io.github.jwdeveloper.tiktok.handlers.events;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
import io.github.jwdeveloper.tiktok.data.models.RankingUser;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLiveIntroMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastRoomUserSeqMessage;
import lombok.SneakyThrows;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class TikTokRoomInfoEventHandler {
private final TikTokRoomInfo roomInfo;
public TikTokRoomInfoEventHandler(TikTokRoomInfo roomInfo) {
this.roomInfo = roomInfo;
}
public TikTokEvent handleRoomInfo(Consumer<TikTokRoomInfo> consumer) {
consumer.accept(roomInfo);
return new TikTokRoomInfoEvent(roomInfo);
}
@SneakyThrows
public TikTokEvent handleUserRanking(byte[] msg) {
var message = WebcastRoomUserSeqMessage.parseFrom(msg);
var totalUsers = message.getTotalUser();
var userRanking = message.getRanksListList().stream().map(RankingUser::new)
.sorted((ru1, ru2) -> Integer.compare(ru2.getScore(), ru1.getScore()))
.collect(Collectors.toList());
return handleRoomInfo(tikTokRoomInfo ->
{
tikTokRoomInfo.setTotalViewersCount(totalUsers);
tikTokRoomInfo.updateRanking(userRanking);
});
}
@SneakyThrows
public TikTokEvent handleIntro(byte[] msg) {
var message = WebcastLiveIntroMessage.parseFrom(msg);
var hostUser = User.map(message.getHost());
var language = message.getLanguage();
return handleRoomInfo(tikTokRoomInfo ->
{
if(tikTokRoomInfo.getHost() == null)
{
tikTokRoomInfo.setHost(hostUser);
}
tikTokRoomInfo.setLanguage(language);
});
}
}

View File

@@ -0,0 +1,49 @@
package io.github.jwdeveloper.tiktok.handlers.events;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.data.events.TikTokUnhandledSocialEvent;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
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.models.Text;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage;
import io.github.jwdeveloper.tiktok.models.SocialTypes;
import lombok.SneakyThrows;
import java.util.regex.Pattern;
public class TikTokSocialMediaEventHandler
{
private final TikTokRoomInfo roomInfo;
private final Pattern socialMediaPattern = Pattern.compile("pm_mt_guidance_viewer_([0-9]+)_share");
public TikTokSocialMediaEventHandler(TikTokRoomInfo roomInfo) {
this.roomInfo = roomInfo;
}
@SneakyThrows
public TikTokEvent handle(byte[] msg)
{
var message = WebcastSocialMessage.parseFrom(msg);
var socialType = Text.map(message.getCommon().getDisplayText()).getKey();
var matcher = socialMediaPattern.matcher(socialType);
if (matcher.find()) {
var value = matcher.group(1);
var number = Integer.parseInt(value);
return new TikTokShareEvent(message, number);
}
return switch (socialType) {
case SocialTypes.LikeType -> new TikTokLikeEvent(message, roomInfo.getLikesCount());
case SocialTypes.FollowType -> new TikTokFollowEvent(message);
case SocialTypes.ShareType -> new TikTokShareEvent(message);
case SocialTypes.JoinType -> new TikTokJoinEvent(message, roomInfo.getViewersCount());
default -> new TikTokUnhandledSocialEvent(message);
};
}
}

View File

@@ -23,14 +23,13 @@
package io.github.jwdeveloper.tiktok.http;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.data.dto.TikTokUserInfo;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import io.github.jwdeveloper.tiktok.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,87 +46,66 @@ public class TikTokApiService {
}
public void updateSessionId()
{
if(clientSettings.getSessionId() == null)
{
public void updateSessionId() {
if (clientSettings.getSessionId() == null) {
return;
}
if(clientSettings.getSessionId().isEmpty())
{
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) {
logger.info("Fetching room ID");
String html;
try {
html = tiktokHttpClient.getLivestreamPage(userName);
}
catch (Exception e)
{
throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
var userInfo = fetchUserInfoFromTikTokApi(userName);
clientSettings.getClientParameters().put("room_id", userInfo.getRoomId());
logger.info("RoomID -> " + userInfo.getRoomId());
return userInfo.getRoomId();
}
var firstPattern = Pattern.compile("room_id=([0-9]*)");
var firstMatcher = firstPattern.matcher(html);
var id = "";
public TikTokUserInfo fetchUserInfoFromTikTokApi(String userName) {
if (firstMatcher.find()) {
id = firstMatcher.group(1);
} else {
var secondPattern = Pattern.compile("\"roomId\":\"([0-9]*)\"");
var secondMatcher = secondPattern.matcher(html);
var params = new HashMap<>(clientSettings.getClientParameters());
params.put("uniqueId", userName);
params.put("sourceType", 54);
var roomData = tiktokHttpClient.getJsonFromTikTokApi("api-live/user/room/", params);
if (secondMatcher.find()) {
id = secondMatcher.group(1);
var message = roomData.get("message").getAsString();
if (message.equals("params_error")) {
throw new TikTokLiveRequestException("fetchRoomIdFromTiktokApi -> Unable to fetch roomID, contact with developer");
}
if (message.equals("user_not_found")) {
return new TikTokUserInfo(TikTokUserInfo.UserStatus.NotFound, "");
}
//live -> status 2
//live paused -> 3
//not live -> status 4
var data = roomData.getAsJsonObject("data");
var user = data.getAsJsonObject("user");
var roomId = user.get("roomId").getAsString();
var status = user.get("status").getAsInt();
if (id.isEmpty()) {
throw new TikTokLiveOfflineHostException("Unable to fetch room ID, live host could be offline or name is misspelled");
}
var statusEnum = switch (status) {
case 2 -> TikTokUserInfo.UserStatus.Live;
case 3 -> TikTokUserInfo.UserStatus.LivePaused;
case 4 -> TikTokUserInfo.UserStatus.Offline;
default -> TikTokUserInfo.UserStatus.NotFound;
};
clientSettings.getClientParameters().put("room_id", id);
logger.info("RoomID -> " + id);
return id;
return new TikTokUserInfo(statusEnum, 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 +114,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,43 @@
package io.github.jwdeveloper.tiktok.http;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.Constants;
import io.github.jwdeveloper.tiktok.data.dto.TikTokUserInfo;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
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 data = getApiService().fetchUserInfoFromTikTokApi(hostName);
return data.getUserStatus() == TikTokUserInfo.UserStatus.Live ||
data.getUserStatus() == TikTokUserInfo.UserStatus.LivePaused;
}
public boolean isHostNameValid(String hostName) {
var data = getApiService().fetchUserInfoFromTikTokApi(hostName);
return data.getUserStatus() != TikTokUserInfo.UserStatus.NotFound;
}
public TikTokApiService getApiService() {
var jar = new TikTokCookieJar();
var factory = new TikTokHttpRequestFactory(jar);
var client = new TikTokHttpClient(jar, factory);
var settings = new ClientSettings();
settings.setClientParameters(Constants.DefaultClientParams());
var apiService = new TikTokApiService(client, Logger.getGlobal(), settings);
return apiService;
}
}

View File

@@ -60,20 +60,23 @@ 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;
}
public JsonObject getJObjectFromWebcastAPI(String path, Map<String, Object> parameters) {
var get = getRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters);
var get = getRequest(Constants.TIKTOK_URL_WEB + path, params);
var json = JsonParser.parseString(get);
var jsonObject = json.getAsJsonObject();
return jsonObject;
}
public WebcastResponse getSigningServerMessage(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 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

@@ -22,9 +22,15 @@
*/
package io.github.jwdeveloper.tiktok.mappers;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute;
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import java.util.ArrayList;
public class LiveRoomMetaMapper {
/**
* 0 - Unknown
@@ -46,15 +52,12 @@ public class LiveRoomMetaMapper {
var status = data.get("status");
var statusId = status.getAsInt();
var statusValue = switch (statusId) {
case 0 -> LiveRoomMeta.LiveRoomStatus.HostNotFound;
case 2 -> LiveRoomMeta.LiveRoomStatus.HostOnline;
case 4 -> LiveRoomMeta.LiveRoomStatus.HostOffline;
default-> LiveRoomMeta.LiveRoomStatus.HostNotFound;
default -> LiveRoomMeta.LiveRoomStatus.HostNotFound;
};
liveRoomMeta.setStatus(statusValue);
}
else
{
} else {
liveRoomMeta.setStatus(LiveRoomMeta.LiveRoomStatus.HostNotFound);
}
@@ -63,6 +66,61 @@ public class LiveRoomMetaMapper {
var restricted = element.get("restricted").getAsBoolean();
liveRoomMeta.setAgeRestricted(restricted);
}
if (data.has("title")) {
var element = data.get("title");
var title = element.getAsString();
liveRoomMeta.setTitie(title);
}
if (data.has("stats")) {
var statsElement = data.getAsJsonObject("stats");
var likeElement = statsElement.get("like_count");
var likes = likeElement.getAsInt();
var titalUsersElement = statsElement.get("total_user");
var totalUsers = titalUsersElement.getAsInt();
liveRoomMeta.setLikeCount(likes);
liveRoomMeta.setTotalViewers(totalUsers);
}
if(data.has("user_count"))
{
var element = data.get("user_count");
var viewers = element.getAsInt();
liveRoomMeta.setViewers(viewers);
}
if(data.has("owner"))
{
var element = data.getAsJsonObject("owner");
var user = getUser(element);
liveRoomMeta.setHost(user);
}
return liveRoomMeta;
}
public User getUser(JsonObject jsonElement)
{
var id = jsonElement.get("id").getAsLong();
var name = jsonElement.get("display_id").getAsString();
var profileName = jsonElement.get("nickname").getAsString();
var followElement =jsonElement.getAsJsonObject("follow_info");
var followers = followElement.get("follower_count").getAsInt();
var followingCount = followElement.get("following_count").getAsInt();
var pictureElement =jsonElement.getAsJsonObject("avatar_large");
var link = pictureElement.getAsJsonArray("url_list").get(1).getAsString();
var picture = new Picture(link);
var user = new User(id,name,profileName,picture,followers,followingCount,new ArrayList<>());
user.addAttribute(UserAttribute.LiveHost);
return user;
}
}

View File

@@ -74,10 +74,7 @@ public class TikTokWebSocketClient implements SocketClient {
}
try {
if (clientSettings.isHandleExistingEvents()) {
logger.info("Handling existing events");
webResponseHandler.handle(tikTokLiveClient, webcastResponse);
}
var url = getWebSocketUrl(webcastResponse);
webSocketClient = startWebSocket(url, tikTokLiveClient);
webSocketClient.connect();
@@ -85,7 +82,8 @@ public class TikTokWebSocketClient implements SocketClient {
pingingTask = new TikTokWebSocketPingingTask();
pingingTask.run(webSocketClient);
isConnected = true;
} catch (Exception e) {
} catch (Exception e)
{
isConnected = false;
throw new TikTokLiveException("Failed to connect to the websocket", e);
}

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,128 @@
/*
* 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.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.GiftSendType;
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,false);
var result = handler.handleGift(message);
Assertions.assertEquals(2, 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());
}
@Test
void shouldHandleStrakableGift() {
var message = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1,true);
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());
}
@Test
void shouldHandleStrike()
{
var message1 = getGiftMessage("example-new-name", 123, "image-new.png", 1, 1,true);
var message2 = getGiftMessage("example-new-name", 123, "image-new.png", 2, 1,true);
var message3 = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1,true);
var result1 = handler.handleGift(message1);
var result2 = handler.handleGift(message2);
var result3 = handler.handleGift(message3);
var event1 = (TikTokGiftComboEvent) result1.get(0);
var event2 = (TikTokGiftComboEvent) result2.get(0);
Assertions.assertEquals(2, result3.size());
var event3 = (TikTokGiftComboEvent) result3.get(0);
Assertions.assertEquals(GiftSendType.Begin,event1.getComboState());
Assertions.assertEquals(GiftSendType.Active,event2.getComboState());
Assertions.assertEquals(GiftSendType.Finished,event3.getComboState());
}
public WebcastGiftMessage getGiftMessage(String giftName,
int giftId,
String giftImage,
int sendType,
int userId,
boolean streakable) {
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());
giftBuilder.setType(streakable?1:0);
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,54 @@
package io.github.jwdeveloper.tiktok.http;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class TikTokLiveOnlineCheckerTest {
public boolean enableTests = false;
@Test
public void shouldTestOnline() {
if(!enableTests)
{
return;
}
var TARGET_USER = "bangbetmenygy";
var sut = new TikTokDataChecker();
var result = sut.isOnline(TARGET_USER);
Assertions.assertTrue(result);
}
@Test
public void shouldBeOffline() {
var TARGET_USER = "dostawcavideo";
var sut = new TikTokDataChecker();
var result = sut.isOnline(TARGET_USER);
Assertions.assertFalse(result);
}
@Test
public void shouldBeValid() {
var TARGET_USER = "dostawcavideo";
var sut = new TikTokDataChecker();
var result = sut.isHostNameValid(TARGET_USER);
Assertions.assertTrue(result);
}
@Test
public void shouldNotBeValid() {
var TARGET_USER = "adadsdadadadadadadadddasdadsafafsafsafas";
var sut = new TikTokDataChecker();
var result = sut.isHostNameValid(TARGET_USER);
Assertions.assertFalse(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.5-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 = "adasdsadadadasdasdsadasdasad";
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("user name exists!");
}
//Optional checking if live is online
if(TikTokLive.isLiveOnline(TIKTOK_HOSTNAME))
{
System.out.println("Live is online!");
}
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.configure(clientSettings ->
{
@@ -43,7 +59,6 @@ public class SimpleExample
clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
clientSettings.setLogLevel(Level.ALL); // Log level
clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
clientSettings.setHandleExistingEvents(true); // Invokes all TikTok events that had occurred before connection
clientSettings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
@@ -79,6 +94,10 @@ public class SimpleExample
{
print(ConsoleColors.RED,"[Disconnected]");
})
.onRoomInfo((liveClient, event) ->
{
var info = event.getRoomInfo();
})
.onFollow((liveClient, event) ->
{
print(ConsoleColors.BLUE, "Follow:", ConsoleColors.WHITE_BRIGHT, event.getUser().getName());

587
README.md
View File

@@ -1,9 +1,8 @@
<div align="center" >
<a target="blank" >
<img src="https://github.com/jwdeveloper/TikTokLiveJava/assets/79764581/1f9951b4-a270-4535-bf38-a6f41cdf8f43" width="15%" >
<img src="https://raw.githubusercontent.com/jwdeveloper/TikTokLiveJava/develop-1_0_0/Tools-ReadmeGenerator/src/main/resources/logo.svg" width="15%" >
</img>
</a>
</div>
<div align="center" >
<h1>TikTok Live Java</h1>
@@ -17,7 +16,6 @@
</a>
<a href="https://discord.gg/e2XwPNTBBr" target="blank" >
<img src="https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white" >
</img>
@@ -31,10 +29,14 @@
</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.
A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/frankvHoof93/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?
@@ -67,7 +69,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.5-Release</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -93,6 +95,13 @@ TikTokLive.newClient("bangbetmenygy")
{
System.out.println(event.getComboState()+ " " + event.getCombo() + " " + event.getGift().getName());
})
.onRoomInfo((liveClient, event) ->
{
var roomInfo = event.getRoomInfo();
System.out.println("Room Id: "+roomInfo.getRoomId());
System.out.println("Likes: "+roomInfo.getLikesCount());
System.out.println("Viewers: "+roomInfo.getViewersCount());
})
.onJoin((liveClient, event) ->
{
System.out.println(event.getUser().getProfileName() + "Hello on my stream! ");
@@ -120,7 +129,6 @@ TikTokLive.newClient("bangbetmenygy")
settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
settings.setLogLevel(Level.ALL); // Log level
settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
settings.setHandleExistingEvents(true); // Invokes all TikTok events that had occurred before connection
settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
settings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
@@ -142,36 +150,35 @@ TikTokLive.newClient("bangbetmenygy")
**Control**:
**Control**:
- [onConnected](#onconnected-tiktokconnectedevent)
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
**Message**:
**Message**:
- [onEvent](#onevent-tiktokevent)
- [onShare](#onshare-tiktokshareevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
- [onRoom](#onroom-tiktokroomevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onJoin](#onjoin-tiktokjoinevent)
- [onRoomUserInfo](#onroomuserinfo-tiktokroomuserinfoevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onGift](#ongift-tiktokgiftevent)
- [onLike](#onlike-tiktoklikeevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onQuestion](#onquestion-tiktokquestionevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onUnhandledSocial](#onunhandledsocial-tiktokunhandledsocialevent)
- [onLike](#onlike-tiktoklikeevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onShare](#onshare-tiktokshareevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onGift](#ongift-tiktokgiftevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onJoin](#onjoin-tiktokjoinevent)
**Debug**:
**Debug**:
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent)
# Examples
<br>
@@ -179,7 +186,7 @@ TikTokLive.newClient("bangbetmenygy")
## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java)
Triggered when the connection is successfully established.
Triggered when the connection is successfully established.
```java
@@ -193,6 +200,26 @@ TikTokLive.newClient("host-name")
<br>
## onDisconnected [TikTokDisconnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokDisconnectedEvent.java)
Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic.
Note that you should wait a little bit before attempting a reconnect to to avoid being rate-limited.
```java
TikTokLive.newClient("host-name")
.onDisconnected((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
@@ -210,32 +237,12 @@ TikTokLive.newClient("host-name")
<br>
## onDisconnected [TikTokDisconnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokDisconnectedEvent.java)
Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic.
Note that you should wait a little bit before attempting a reconnect to to avoid being rate-limited.
```java
TikTokLive.newClient("host-name")
.onDisconnected((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
General error event. You should handle this.
General error event. You should handle this.
```java
@@ -254,7 +261,7 @@ TikTokLive.newClient("host-name")
## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java)
Base class for all events
Base class for all events
```java
@@ -268,190 +275,12 @@ TikTokLive.newClient("host-name")
<br>
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
Triggers when a user shares the stream. Based on social event.
```java
TikTokLive.newClient("host-name")
.onShare((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLivePaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onRoom [TikTokRoomEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomEvent.java)
```java
TikTokLive.newClient("host-name")
.onRoom((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onGiftCombo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onRoomUserInfo [TikTokRoomUserInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomUserInfoEvent.java)
Only top 5 users in ranking has detailed data
rest has only ID
```java
TikTokLive.newClient("host-name")
.onRoomUserInfo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
Triggered every time a new chat comment arrives.
```java
TikTokLive.newClient("host-name")
.onComment((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
Triggered when user sends gifts that has
no combo (most of expensive gifts)
or if combo has finished
```java
TikTokLive.newClient("host-name")
.onGift((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
```java
TikTokLive.newClient("host-name")
.onLike((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
Triggers when a user creates a subscription.
Triggers when a user creates a subscription.
```java
@@ -484,50 +313,12 @@ TikTokLive.newClient("host-name")
<br>
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
Triggered every time a subscriber sends an emote (sticker).
```java
TikTokLive.newClient("host-name")
.onEmote((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
```java
TikTokLive.newClient("host-name")
.onLiveEnded((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java)
Triggers when a user follows the streamer. Based on social event.
Triggers when a user follows the streamer. Based on social event.
```java
@@ -543,13 +334,228 @@ TikTokLive.newClient("host-name")
<br>
## onUnhandledSocial [TikTokUnhandledSocialEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokUnhandledSocialEvent.java)
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
```java
TikTokLive.newClient("host-name")
.onLike((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
```java
TikTokLive.newClient("host-name")
.onLiveEnded((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
```java
TikTokLive.newClient("host-name")
.onUnhandledSocial((liveClient, event) ->
.onRoomInfo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
Triggers when a user shares the stream. Based on social event.
```java
TikTokLive.newClient("host-name")
.onShare((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onGiftCombo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
Triggered every time a subscriber sends an emote (sticker).
```java
TikTokLive.newClient("host-name")
.onEmote((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
Triggered when user sends gifts that has
no combo (most of expensive gifts)
or if combo has finished
```java
TikTokLive.newClient("host-name")
.onGift((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
Triggered every time a new chat comment arrives.
```java
TikTokLive.newClient("host-name")
.onComment((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLivePaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLiveUnpaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java)
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
```java
TikTokLive.newClient("host-name")
.onWebsocketUnhandledMessage((liveClient, event) ->
{
})
@@ -566,30 +572,11 @@ TikTokLive.newClient("host-name")
```java
TikTokLive.newClient("host-name")
.onWebsocketResponse((liveClient, event) ->
{
.onWebsocketResponse((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java)
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
```java
TikTokLive.newClient("host-name")
.onWebsocketUnhandledMessage((liveClient, event) ->
{
})
.buildAndConnect();
})
.buildAndConnect();
```
@@ -599,16 +586,16 @@ TikTokLive.newClient("host-name")
## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java)
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
```java
TikTokLive.newClient("host-name")
.onWebsocketMessage((liveClient, event) ->
{
.onWebsocketMessage((liveClient, event) ->
{
})
.buildAndConnect();
})
.buildAndConnect();
```
@@ -621,14 +608,14 @@ TikTokLive.newClient("host-name")
```java
/**
/**
*
* Listeners are an alternative way of handling events.
* I would to suggest to use then when logic of handing event
* is more complex
*
*/
public static void main(String[] args) throws IOException {
public static void main(String[] args) throws IOException {
showLogo();
CustomListener customListener = new CustomListener();
@@ -638,7 +625,7 @@ TikTokLive.newClient("host-name")
System.in.read();
}
/**
/**
*
* Method in TikTokEventListener should meet 4 requirements to be detected
* - must have @TikTokEventHandler annotation
@@ -647,7 +634,7 @@ TikTokLive.newClient("host-name")
* - second must be class that extending TikTokEvent
*/
public static class CustomListener implements TikTokEventListener {
public static class CustomListener implements TikTokEventListener {
@TikTokEventHandler
public void onLike(LiveClient liveClient, TikTokLikeEvent event) {
@@ -684,9 +671,9 @@ TikTokLive.newClient("host-name")
liveClient.getLogger().info(event.getClass().getSimpleName());
}
}
}
//
//
```

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.5-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

@@ -27,6 +27,7 @@ import io.github.jwdeveloper.tiktok.tools.collector.client.TikTokMessageCollecto
import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Level;
public class RunCollector {
@@ -38,39 +39,39 @@ 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("valeria.viral")
// .addUser("cbcgod")
// .addUser("psychotropnazywo")
// .addUser("accordionistka")
.addEventFilter(WebcastGiftMessage.class)
//.addEventFilter(WebcastGiftMessage.class)
.addOnBuilder(liveClientBuilder ->
{
liveClientBuilder.onGift((liveClient, event) ->
{
});
liveClientBuilder.onGiftCombo((liveClient, event) ->
{
liveClient.getLogger().setLevel(Level.OFF);
var gifts = liveClient.getGiftManager().getGifts();
});
liveClientBuilder.onGift((liveClient, event) ->
{
var sb = new StringBuilder();
sb.append("GIFT User: " + event.getUser().getProfileName()+" ");
sb.append("Name: " + event.getGift().name() + " ");
sb.append("GIFT COMBO User: " + event.getUser().getProfileName()+" ");
sb.append("Name: " + event.getGift().getName() + " ");
sb.append("Combo: " + event.getCombo() + " ");
sb.append("COST: " + event.getGift().getDiamondCost() + " ");
sb.append("STATE: " + event.getComboState() + " ");
System.out.println(sb.toString());
});
liveClientBuilder.onGiftCombo((liveClient, event) ->
liveClientBuilder.onGift((liveClient, event) ->
{
liveClient.getLogger().setLevel(Level.OFF);
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("COST: " + event.getGift().getDiamondCost() + " ");
sb.append("Combo: " + event.getCombo() + " ");
sb.append("Type: " + event.getComboState().name());
System.out.println(sb.toString());
});
})
@@ -78,6 +79,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.5-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

@@ -39,17 +39,21 @@ public class TikTokManager {
TikTokMessagessCollectorBuilder client;
MessageCollector msgCollector;
public static String dbName= "ab";
public TikTokManager() {
msgCollector = new MessageCollector("web");
msgCollector = new MessageCollector(dbName);
}
public void connect(String name) throws SQLException {
disconnect();
client = TikTokMessageCollectorClient.create(msgCollector, "web")
client = TikTokMessageCollectorClient.create(msgCollector, dbName)
.addOnBuilder(liveClientBuilder ->
{
liveClientBuilder.onRoomInfo((liveClient, event) ->
{
});
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.5-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -54,6 +54,13 @@ public class CodeExample {
{
System.out.println(event.getComboState()+ " " + event.getCombo() + " " + event.getGift().getName());
})
.onRoomInfo((liveClient, event) ->
{
var roomInfo = event.getRoomInfo();
System.out.println("Room Id: "+roomInfo.getRoomId());
System.out.println("Likes: "+roomInfo.getLikesCount());
System.out.println("Viewers: "+roomInfo.getViewersCount());
})
.onJoin((liveClient, event) ->
{
System.out.println(event.getUser().getProfileName() + "Hello on my stream! ");
@@ -80,7 +87,6 @@ public class CodeExample {
settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
settings.setLogLevel(Level.ALL); // Log level
settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
settings.setHandleExistingEvents(true); // Invokes all TikTok events that had occurred before connection
settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
settings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection

View File

@@ -0,0 +1,681 @@
<div align="center" >
<a target="blank" >
<img src="https://raw.githubusercontent.com/jwdeveloper/TikTokLiveJava/develop-1_0_0/Tools-ReadmeGenerator/src/main/resources/logo.svg" width="15%" >
</img>
</a>
</div>
<div align="center" >
<h1>TikTok Live Java</h1>
❤️❤️🎁 *Connect to TikTok live in 3 lines* 🎁❤️❤️
<div align="center" >
<a href="https://jitpack.io/#jwdeveloper/TikTok-Live-Java" target="blank" >
<img src="https://jitpack.io/v/jwdeveloper/TikTok-Live-Java.svg" width="20%" >
</img>
</a>
<a href="https://discord.gg/e2XwPNTBBr" target="blank" >
<img src="https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white" >
</img>
</a>
<a target="blank" >
<img src="https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white" >
</img>
</a>
</div>
</div>
# Introduction
A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/frankvHoof93/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?
- **Node** orginal: [TikTok-Live-Connector](https://github.com/isaackogan/TikTok-Live-Connector) by [@zerodytrash](https://github.com/zerodytrash)
- **Python** rewrite: [TikTokLive](https://github.com/isaackogan/TikTokLive) by [@isaackogan](https://github.com/isaackogan)
- **Go** rewrite: [GoTikTokLive](https://github.com/Davincible/gotiktoklive) by [@Davincible](https://github.com/Davincible)
- **C#** rewrite: [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp) by [@frankvHoof93](https://github.com/frankvHoof93)
**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
```xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId>
<version>NOT_FOUND</version>
<scope>compile</scope>
</dependency>
</dependencies>
```
2. Create your first chat connection
```java
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";
default -> "Thank you for " + event.getGift().getName();
};
System.out.println(event.getUser().getProfileName() + " sends " + message);
})
.onGiftCombo((liveClient, event) ->
{
System.out.println(event.getComboState()+ " " + event.getCombo() + " " + event.getGift().getName());
})
.onRoomInfo((liveClient, event) ->
{
var roomInfo = event.getRoomInfo();
System.out.println("Room Id: "+roomInfo.getRoomId());
System.out.println("Likes: "+roomInfo.getLikesCount());
System.out.println("Viewers: "+roomInfo.getViewersCount());
})
.onJoin((liveClient, event) ->
{
System.out.println(event.getUser().getProfileName() + "Hello on my stream! ");
})
.onConnected((liveClient, event) ->
{
System.out.println("Connected to live ");
})
.onError((liveClient, event) ->
{
System.out.println("Error! " + event.getException().getMessage());
})
.buildAndConnect();
```
3. Configure (optional)
```java
TikTokLive.newClient("bangbetmenygy")
.configure((settings) ->
{
settings.setHostName("bangbetmenygy"); // This method is useful in case you want change hostname later
settings.setClientLanguage("en"); // Language
settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
settings.setLogLevel(Level.ALL); // Log level
settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
settings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
//Optional: Sometimes not every message from chat are send to TikTokLiveJava to fix this issue you can set sessionId
// documentation how to obtain sessionId https://github.com/isaackogan/TikTok-Live-Connector#send-chat-messages
settings.setSessionId("86c3c8bf4b17ebb2d74bb7fa66fd0000");
//Optional:
//RoomId can be used as an override if you're having issues with HostId.
//You can find it in the HTML for the livestream-page
settings.setRoomId("XXXXXXXXXXXXXXXXX");
})
.buildAndConnect();
//
```
## Events
**Control**:
- [onConnected](#onconnected-tiktokconnectedevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
**Message**:
- [onEvent](#onevent-tiktokevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onQuestion](#onquestion-tiktokquestionevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onLike](#onlike-tiktoklikeevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onShare](#onshare-tiktokshareevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onGift](#ongift-tiktokgiftevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onJoin](#onjoin-tiktokjoinevent)
**Debug**:
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent)
# Examples
<br>
## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java)
Triggered when the connection is successfully established.
```java
TikTokLive.newClient("host-name")
.onConnected((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onDisconnected [TikTokDisconnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokDisconnectedEvent.java)
Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic.
Note that you should wait a little bit before attempting a reconnect to to avoid being rate-limited.
```java
TikTokLive.newClient("host-name")
.onDisconnected((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
```java
TikTokLive.newClient("host-name")
.onReconnecting((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
General error event. You should handle this.
```java
TikTokLive.newClient("host-name")
.onError((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java)
Base class for all events
```java
TikTokLive.newClient("host-name")
.onEvent((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
Triggers when a user creates a subscription.
```java
TikTokLive.newClient("host-name")
.onSubscribe((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
Triggered every time someone asks a new question via the question feature.
```java
TikTokLive.newClient("host-name")
.onQuestion((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java)
Triggers when a user follows the streamer. Based on social event.
```java
TikTokLive.newClient("host-name")
.onFollow((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
```java
TikTokLive.newClient("host-name")
.onLike((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
```java
TikTokLive.newClient("host-name")
.onLiveEnded((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
```java
TikTokLive.newClient("host-name")
.onRoomInfo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
Triggers when a user shares the stream. Based on social event.
```java
TikTokLive.newClient("host-name")
.onShare((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onGiftCombo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
Triggered every time a subscriber sends an emote (sticker).
```java
TikTokLive.newClient("host-name")
.onEmote((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
Triggered when user sends gifts that has
no combo (most of expensive gifts)
or if combo has finished
```java
TikTokLive.newClient("host-name")
.onGift((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
Triggered every time a new chat comment arrives.
```java
TikTokLive.newClient("host-name")
.onComment((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLivePaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLiveUnpaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java)
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
```java
TikTokLive.newClient("host-name")
.onWebsocketUnhandledMessage((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
```java
TikTokLive.newClient("host-name")
.onWebsocketResponse((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java)
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
```java
TikTokLive.newClient("host-name")
.onWebsocketMessage((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## Listeners
```java
/**
*
* Listeners are an alternative way of handling events.
* I would to suggest to use then when logic of handing event
* is more complex
*
*/
public static void main(String[] args) throws IOException {
showLogo();
CustomListener customListener = new CustomListener();
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.addListener(customListener)
.buildAndConnect();
System.in.read();
}
/**
*
* Method in TikTokEventListener should meet 4 requirements to be detected
* - must have @TikTokEventHandler annotation
* - must have 2 parameters
* - first parameter must be LiveClient
* - second must be class that extending TikTokEvent
*/
public static class CustomListener implements TikTokEventListener {
@TikTokEventHandler
public void onLike(LiveClient liveClient, TikTokLikeEvent event) {
System.out.println(event.toString());
}
@TikTokEventHandler
public void onError(LiveClient liveClient, TikTokErrorEvent event) {
// event.getException().printStackTrace();
}
@TikTokEventHandler
public void onComment(LiveClient liveClient, TikTokCommentEvent event) {
var userName = event.getUser().getName();
var text = event.getText();
liveClient.getLogger().info(userName + ": " + text);
}
@TikTokEventHandler
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";
default -> ":I";
};
liveClient.getLogger().info(message);
}
@TikTokEventHandler
public void onAnyEvent(LiveClient liveClient, TikTokEvent event) {
liveClient.getLogger().info(event.getClass().getSimpleName());
}
}
//
```
## Contributing
Your improvements are welcome! Feel free to open an <a href="https://github.com/jwdeveloper/TikTok-Live-Java/issues">issue</a> or <a href="https://github.com/jwdeveloper/TikTok-Live-Java/pulls">pull request</a>.

View File

@@ -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.5-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

@@ -28,6 +28,8 @@ import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokGiftEventHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokRoomInfoEventHandler;
import io.github.jwdeveloper.tiktok.handlers.events.TikTokSocialMediaEventHandler;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
@@ -93,14 +95,15 @@ 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);
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler,
tiktokRoomInfo,
new TikTokRoomInfoEventHandler(tiktokRoomInfo),
new TikTokGenericEventMapper(),
new TikTokGiftEventHandler(giftManager));
new TikTokGiftEventHandler(giftManager),
new TikTokSocialMediaEventHandler(tiktokRoomInfo));
var webSocketClient = new WebsocketClientMock(logger, responses, webResponseHandler);
return new LiveClientMock(tiktokRoomInfo,

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.5-Release</version>
<modules>
<module>API</module>
<module>Client</module>