mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-02-27 16:59:39 -05:00
Compare commits
24 Commits
1.0.0-Rele
...
develop-1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1977cbe8dc | ||
|
|
c3a48c4d70 | ||
|
|
2c51844fd9 | ||
|
|
8ff4236452 | ||
|
|
7817aeb652 | ||
|
|
4c122ab754 | ||
|
|
519c22de8e | ||
|
|
6d268c42f1 | ||
|
|
788653484f | ||
|
|
6cebbf891d | ||
|
|
cc32648988 | ||
|
|
4c797724d3 | ||
|
|
6941107db8 | ||
|
|
ed70799cd9 | ||
|
|
e12b0901f7 | ||
|
|
563e9618e2 | ||
|
|
82112f0140 | ||
|
|
77e30de5e1 | ||
|
|
a2b10ba7f6 | ||
|
|
957e38a5d2 | ||
|
|
5e77b3f57f | ||
|
|
690b9eb272 | ||
|
|
fc91991c2c | ||
|
|
0f735a7876 |
309
.gitignore
vendored
309
.gitignore
vendored
@@ -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
3
.idea/protoeditor.xml
generated
@@ -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" />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>0.0.25-Release</version>
|
||||
<version>1.0.4-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>API</artifactId>
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -30,3 +30,6 @@ public @interface EventMeta
|
||||
{
|
||||
EventType eventType();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -31,4 +31,5 @@ public interface TikTokHttpRequest {
|
||||
String get(String url);
|
||||
|
||||
String post(String url);
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -110,4 +111,29 @@ enum BarrageType
|
||||
FansLevelUpgrade = 10;
|
||||
FansLevelEntrance = 11;
|
||||
GamePartnership = 12;
|
||||
}
|
||||
|
||||
enum EnvelopeBusinessType
|
||||
{
|
||||
BusinessTypeUnknown = 0;
|
||||
BusinessTypeUserDiamond = 1;
|
||||
BusinessTypePlatformDiamond = 2;
|
||||
BusinessTypePlatformShell = 3;
|
||||
BusinessTypePortal = 4;
|
||||
BusinessTypePlatformMerch = 5;
|
||||
BusinessTypeEoYDiamond = 6;
|
||||
BusinessTypeFanClubGtM = 7;
|
||||
}
|
||||
enum EnvelopeFollowShowStatus
|
||||
{
|
||||
EnvelopeFollowShowUnknown = 0;
|
||||
EnvelopeFollowShow = 1;
|
||||
EnvelopeFollowNotShow = 2;
|
||||
}
|
||||
|
||||
enum EnvelopeDisplay
|
||||
{
|
||||
EnvelopeDisplayUnknown = 0;
|
||||
EnvelopeDisplayNew = 1;
|
||||
EnvelopeDisplayHide = 2;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>0.0.25-Release</version>
|
||||
<version>1.0.4-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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -112,7 +113,7 @@ public class TikTokLiveClient implements LiveClient {
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (!liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED)) {
|
||||
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
|
||||
return;
|
||||
}
|
||||
webSocketClient.stop();
|
||||
@@ -138,14 +139,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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package io.github.jwdeveloper.tiktok.handlers.events;
|
||||
|
||||
public class TikTokChestEventHandler {
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
|
||||
import io.github.jwdeveloper.tiktok.mappers.LiveRoomMetaMapper;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
import javax.script.ScriptEngineManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -47,49 +46,34 @@ public class TikTokApiService {
|
||||
}
|
||||
|
||||
|
||||
public void updateSessionId()
|
||||
{
|
||||
if(clientSettings.getSessionId() == null)
|
||||
{
|
||||
public void updateSessionId() {
|
||||
if (clientSettings.getSessionId() == null) {
|
||||
return;
|
||||
}
|
||||
if(clientSettings.getSessionId().isEmpty())
|
||||
{
|
||||
return;
|
||||
if (clientSettings.getSessionId().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
tiktokHttpClient.setSessionId(clientSettings.getSessionId());
|
||||
}
|
||||
|
||||
public boolean sendMessage(String message, String sessionId) {
|
||||
if (sessionId.isEmpty()) {
|
||||
throw new TikTokLiveException("Session ID must not be Empty");
|
||||
}
|
||||
var roomId = clientSettings.getClientParameters().get("room_id");
|
||||
if (roomId == null) {
|
||||
throw new TikTokLiveException("Room ID must not be Empty");
|
||||
}
|
||||
logger.info("Sending message to chat");
|
||||
try {
|
||||
var params = new HashMap<String, Object>(clientSettings.getClientParameters());
|
||||
params.put("content", message);
|
||||
params.put("channel", "tiktok_web");
|
||||
params.remove("cursor");
|
||||
tiktokHttpClient.setSessionId(sessionId);
|
||||
tiktokHttpClient.postMessageToChat(params);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
|
||||
}
|
||||
public String fetchRoomId(String userName) {
|
||||
var roomId = fetchRoomIdFromTiktokApi(userName);
|
||||
clientSettings.getClientParameters().put("room_id", roomId);
|
||||
logger.info("RoomID -> " + roomId);
|
||||
return roomId;
|
||||
}
|
||||
|
||||
public String fetchRoomId(String userName) {
|
||||
private String fetchRoomIdFromTikTokPage(String userName)
|
||||
{
|
||||
/* var roomId = RequestChain.<String>create()
|
||||
.then(() -> fetchRoomIdFromTikTokPage(userName))
|
||||
.then(() -> fetchRoomIdFromTiktokApi(userName))
|
||||
.run();*/
|
||||
logger.info("Fetching room ID");
|
||||
String html;
|
||||
try {
|
||||
html = tiktokHttpClient.getLivestreamPage(userName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
|
||||
}
|
||||
|
||||
@@ -112,22 +96,35 @@ public class TikTokApiService {
|
||||
throw new TikTokLiveOfflineHostException("Unable to fetch room ID, live host could be offline or name is misspelled");
|
||||
}
|
||||
|
||||
clientSettings.getClientParameters().put("room_id", id);
|
||||
logger.info("RoomID -> " + id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private String fetchRoomIdFromTiktokApi(String userName) {
|
||||
|
||||
var params = new HashMap<>(clientSettings.getClientParameters());
|
||||
params.put("uniqueId", userName);
|
||||
params.put("sourceType", 54);
|
||||
var roomData = tiktokHttpClient.getJsonFromTikTokApi("api-live/user/room/", params);
|
||||
|
||||
var data = roomData.getAsJsonObject("data");
|
||||
var user =data.getAsJsonObject("user");
|
||||
var roomId = user.get("roomId").getAsString();
|
||||
|
||||
|
||||
return roomId;
|
||||
}
|
||||
|
||||
|
||||
public LiveRoomMeta fetchRoomInfo() {
|
||||
logger.info("Fetching RoomInfo");
|
||||
try {
|
||||
var response = tiktokHttpClient.getJObjectFromWebcastAPI("room/info/", clientSettings.getClientParameters());
|
||||
var response = tiktokHttpClient.getJsonFromWebcastApi("room/info/", clientSettings.getClientParameters());
|
||||
var mapper = new LiveRoomMetaMapper();
|
||||
var liveRoomMeta = mapper.map(response);
|
||||
logger.info("RoomInfo status -> " + liveRoomMeta.getStatus());
|
||||
return liveRoomMeta;
|
||||
} catch (Exception e)
|
||||
{
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Failed to fetch room info from WebCast, see stacktrace for more info.", e);
|
||||
}
|
||||
}
|
||||
@@ -136,7 +133,7 @@ public class TikTokApiService {
|
||||
|
||||
logger.info("Fetching ClientData");
|
||||
try {
|
||||
var response = tiktokHttpClient.getSigningServerMessage("im/fetch/", clientSettings.getClientParameters());
|
||||
var response = tiktokHttpClient.getSigningServerResponse("im/fetch/", clientSettings.getClientParameters());
|
||||
clientSettings.getClientParameters().put("cursor", response.getCursor());
|
||||
clientSettings.getClientParameters().put("internal_ext", response.getInternalExt());
|
||||
return response;
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.github.jwdeveloper.tiktok.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TikTokDataChecker
|
||||
{
|
||||
|
||||
public CompletableFuture<Boolean> isOnlineAsync(String hostName) {
|
||||
return CompletableFuture.supplyAsync(() -> isOnline(hostName));
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> isHostNameValidAsync(String hostName) {
|
||||
return CompletableFuture.supplyAsync(() -> isOnline(hostName));
|
||||
}
|
||||
|
||||
public boolean isOnline(String hostName) {
|
||||
var factory = new TikTokHttpRequestFactory(new TikTokCookieJar());
|
||||
var url = getLiveUrl(hostName);
|
||||
try {
|
||||
var response = factory.get(url);
|
||||
var titleContent = extractTitleContent(response);
|
||||
return isTitleLiveOnline(titleContent);
|
||||
} catch (Exception e)
|
||||
{
|
||||
throw new TikTokLiveRequestException("Unable to make check live online request",e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHostNameValid(String hostName) {
|
||||
var factory = new TikTokHttpRequestFactory(new TikTokCookieJar());
|
||||
var url = getProfileUrl(hostName);
|
||||
try {
|
||||
var response = factory.get(url);
|
||||
var titleContent = extractTitleContent(response);
|
||||
return isTitleHostNameValid(titleContent, hostName);
|
||||
} catch (Exception e)
|
||||
{
|
||||
throw new TikTokLiveRequestException("Unable to make check host name valid request",e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTitleLiveOnline(String title) {
|
||||
return title.contains("is LIVE");
|
||||
}
|
||||
|
||||
private boolean isTitleHostNameValid(String title, String hostName)
|
||||
{
|
||||
return title.contains(hostName);
|
||||
}
|
||||
|
||||
private String extractTitleContent(String html) {
|
||||
var regex = "<title\\b[^>]*>(.*?)<\\/title>";
|
||||
var pattern = Pattern.compile(regex);
|
||||
var matcher = pattern.matcher(html);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String getLiveUrl(String hostName) {
|
||||
var sb = new StringBuilder();
|
||||
sb.append("https://www.tiktok.com/@");
|
||||
sb.append(hostName);
|
||||
sb.append("/live");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getProfileUrl(String hostName) {
|
||||
var sb = new StringBuilder();
|
||||
sb.append("https://www.tiktok.com/@");
|
||||
sb.append(hostName);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +74,7 @@ public class TikTokWebSocketClient implements SocketClient {
|
||||
}
|
||||
|
||||
try {
|
||||
if (clientSettings.isHandleExistingEvents()) {
|
||||
logger.info("Handling existing events");
|
||||
webResponseHandler.handle(tikTokLiveClient, webcastResponse);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.github.jwdeveloper.tiktok.http;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TikTokLiveOnlineCheckerTest {
|
||||
|
||||
private final String TARGET_USER = "bangbetmenygy";
|
||||
|
||||
@Test
|
||||
public void shouldTestOnline() {
|
||||
var sut = new TikTokDataChecker();
|
||||
var result = sut.isOnline(TARGET_USER);
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>0.0.25-Release</version>
|
||||
<version>1.0.4-Release</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 = "bangbetmenygy";
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
showLogo();
|
||||
// set tiktok username
|
||||
|
||||
/*
|
||||
Optional checking if host name is correct
|
||||
if(TikTokLive.isHostNameValid(TIKTOK_HOSTNAME))
|
||||
{
|
||||
System.out.println("Live is online!");
|
||||
}
|
||||
|
||||
Optional checking if live is online
|
||||
if(TikTokLive.isLiveOnline(TIKTOK_HOSTNAME))
|
||||
{
|
||||
System.out.println("Live is online!");
|
||||
}
|
||||
*/
|
||||
|
||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||
.configure(clientSettings ->
|
||||
{
|
||||
@@ -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());
|
||||
|
||||
Binary file not shown.
673
README.md
673
README.md
@@ -29,9 +29,13 @@
|
||||
</div>
|
||||
|
||||
# Introduction
|
||||
A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive) and [TikTokLiveSharp](https://github.com/sebheron/TikTokLiveSharp). Use it to receive live stream events such as comments and gifts in realtime from [TikTok LIVE](https://www.tiktok.com/live) by connecting to TikTok's internal WebCast push service.
|
||||
The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers.
|
||||
No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked.
|
||||
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
|
||||
|
||||
@@ -65,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>
|
||||
@@ -91,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! ");
|
||||
@@ -118,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
|
||||
|
||||
@@ -140,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>
|
||||
@@ -177,8 +186,8 @@ 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
|
||||
TikTokLive.newClient("host-name")
|
||||
@@ -191,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)
|
||||
@@ -208,33 +237,13 @@ 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
|
||||
TikTokLive.newClient("host-name")
|
||||
@@ -252,8 +261,8 @@ 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
|
||||
TikTokLive.newClient("host-name")
|
||||
@@ -266,13 +275,125 @@ TikTokLive.newClient("host-name")
|
||||
|
||||
|
||||
|
||||
<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.
|
||||
|
||||
Triggers when a user shares the stream. Based on social event.
|
||||
|
||||
|
||||
```java
|
||||
TikTokLive.newClient("host-name")
|
||||
@@ -285,6 +406,94 @@ TikTokLive.newClient("host-name")
|
||||
|
||||
|
||||
|
||||
<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)
|
||||
@@ -304,42 +513,13 @@ TikTokLive.newClient("host-name")
|
||||
|
||||
<br>
|
||||
|
||||
## onRoom [TikTokRoomEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomEvent.java)
|
||||
## 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")
|
||||
.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) ->
|
||||
.onLiveUnpaused((liveClient, event) ->
|
||||
{
|
||||
|
||||
})
|
||||
@@ -367,187 +547,15 @@ TikTokLive.newClient("host-name")
|
||||
|
||||
<br>
|
||||
|
||||
## onRoomUserInfo [TikTokRoomUserInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomUserInfoEvent.java)
|
||||
## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.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.
|
||||
|
||||
|
||||
```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>
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
```java
|
||||
TikTokLive.newClient("host-name")
|
||||
.onFollow((liveClient, event) ->
|
||||
{
|
||||
|
||||
})
|
||||
.buildAndConnect();
|
||||
```
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
## onUnhandledSocial [TikTokUnhandledSocialEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokUnhandledSocialEvent.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")
|
||||
.onUnhandledSocial((liveClient, event) ->
|
||||
.onWebsocketUnhandledMessage((liveClient, event) ->
|
||||
{
|
||||
|
||||
})
|
||||
@@ -564,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();
|
||||
```
|
||||
|
||||
|
||||
@@ -597,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();
|
||||
```
|
||||
|
||||
|
||||
@@ -619,72 +608,72 @@ 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 {
|
||||
/**
|
||||
*
|
||||
* 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();
|
||||
.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());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 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());
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>0.0.25-Release</version>
|
||||
<version>1.0.4-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>
|
||||
|
||||
@@ -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() + " ");
|
||||
var sb = new StringBuilder();
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1247
Tools-EventsCollector/src/main/resources/web.json
Normal file
1247
Tools-EventsCollector/src/main/resources/web.json
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>0.0.25-Release</version>
|
||||
<version>1.0.4-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>
|
||||
|
||||
@@ -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) ->
|
||||
{
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>0.0.25-Release</version>
|
||||
<version>1.0.4-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
681
Tools-ReadmeGenerator/src/main/resources/output.md
Normal file
681
Tools-ReadmeGenerator/src/main/resources/output.md
Normal 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>.
|
||||
@@ -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?
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>0.0.25-Release</version>
|
||||
<version>1.0.4-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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
44
Tools/src/main/resources/gifts/extra_gifts.json
Normal file
44
Tools/src/main/resources/gifts/extra_gifts.json
Normal 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"
|
||||
}
|
||||
]
|
||||
17129
Tools/src/main/resources/gifts/official/official_10_11_2023.json
Normal file
17129
Tools/src/main/resources/gifts/official/official_10_11_2023.json
Normal file
File diff suppressed because it is too large
Load Diff
15950
Tools/src/main/resources/gifts/official/official_12_10_2023.json
Normal file
15950
Tools/src/main/resources/gifts/official/official_12_10_2023.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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": "Pirate’s 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"
|
||||
}
|
||||
}
|
||||
4820
Tools/src/main/resources/gifts/used_outputs/output_1_0_0.json
Normal file
4820
Tools/src/main/resources/gifts/used_outputs/output_1_0_0.json
Normal file
File diff suppressed because it is too large
Load Diff
4886
Tools/src/main/resources/gifts/used_outputs/output_1_0_4.json
Normal file
4886
Tools/src/main/resources/gifts/used_outputs/output_1_0_4.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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": "Pirate’s 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"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user