mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-02-27 16:59:39 -05:00
Compare commits
32 Commits
develop-1-
...
develop-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46d229869e | ||
|
|
dd417df0ff | ||
|
|
bc24436269 | ||
|
|
1308b86567 | ||
|
|
20ba88c0ac | ||
|
|
2d260dd3f9 | ||
|
|
77533ea4be | ||
|
|
3231924f8f | ||
|
|
ea525470e2 | ||
|
|
b0bf4ac606 | ||
|
|
0b9f1570d0 | ||
|
|
7a4c7fecbd | ||
|
|
0ae9068858 | ||
|
|
8905958207 | ||
|
|
c12f3cc4dc | ||
|
|
7402899f52 | ||
|
|
1b8b150d61 | ||
|
|
b2305b7bed | ||
|
|
7b911838a2 | ||
|
|
e44cb71869 | ||
|
|
af8c689417 | ||
|
|
81ac92fb33 | ||
|
|
34a78b5435 | ||
|
|
534cb7906d | ||
|
|
0bb8edfe5c | ||
|
|
4979c1b27a | ||
|
|
f7c8ffdaa5 | ||
|
|
c1fda687d3 | ||
|
|
05c49c4545 | ||
|
|
7d36f36cee | ||
|
|
f8a716429d | ||
|
|
fee805f0ea |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,10 +1,10 @@
|
||||
backend-infrastructure/.aws-sam
|
||||
|
||||
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
|
||||
|
||||
*.db
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
.db
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.0.9-Release</version>
|
||||
<version>1.0.14-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>API</artifactId>
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@Data
|
||||
public class ClientSettings {
|
||||
/**
|
||||
* Timeout for Connections
|
||||
*/
|
||||
private Duration timeout;
|
||||
|
||||
/**
|
||||
* ISO-Language for Client
|
||||
*/
|
||||
|
||||
private String clientLanguage;
|
||||
|
||||
/**
|
||||
* Whether to Retry if Connection Fails
|
||||
*/
|
||||
private boolean retryOnConnectionFailure;
|
||||
|
||||
|
||||
/**
|
||||
* Before retrying connect, wait for select amount of time
|
||||
*/
|
||||
private Duration retryConnectionTimeout;
|
||||
|
||||
/**
|
||||
* Whether to print Logs to Console
|
||||
*/
|
||||
|
||||
private boolean printToConsole = true;
|
||||
/**
|
||||
* LoggingLevel for Logs
|
||||
*/
|
||||
private Level logLevel;
|
||||
|
||||
|
||||
/**
|
||||
* Optional: Use it if you need to change TikTok live hostname in builder
|
||||
*/
|
||||
private String hostName;
|
||||
|
||||
|
||||
/**
|
||||
* Parameters used in requests to TikTok api
|
||||
*/
|
||||
private Map<String, Object> clientParameters;
|
||||
|
||||
|
||||
/*
|
||||
* Optional: Sometimes not every messages 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
|
||||
*/
|
||||
private String sessionId;
|
||||
|
||||
/*
|
||||
* Optional: By default roomID is fetched before connect to live, but you can set it manually
|
||||
*
|
||||
*/
|
||||
private String roomId;
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -40,7 +41,7 @@ import lombok.Getter;
|
||||
* <p>Combo: 4 -> comboState = GiftSendType.Active</p>
|
||||
* <p>Combo: 8 -> comboState = GiftSendType.Active</p>
|
||||
* <p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
|
||||
*
|
||||
* <p>
|
||||
* Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
|
||||
*/
|
||||
@EventMeta(eventType = EventType.Message)
|
||||
@@ -48,8 +49,8 @@ import lombok.Getter;
|
||||
public class TikTokGiftComboEvent extends TikTokGiftEvent {
|
||||
private final GiftSendType comboState;
|
||||
|
||||
public TikTokGiftComboEvent(Gift gift, WebcastGiftMessage msg, GiftSendType comboState) {
|
||||
super(gift, msg);
|
||||
public TikTokGiftComboEvent(Gift gift, User host, WebcastGiftMessage msg, GiftSendType comboState) {
|
||||
super(gift, host, msg);
|
||||
this.comboState = comboState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +49,15 @@ public class TikTokGiftEvent extends TikTokHeaderEvent {
|
||||
private final User toUser;
|
||||
private final int combo;
|
||||
|
||||
public TikTokGiftEvent(Gift gift, WebcastGiftMessage msg) {
|
||||
public TikTokGiftEvent(Gift gift, User liveHost, WebcastGiftMessage msg) {
|
||||
super(msg.getCommon());
|
||||
this.gift = gift;
|
||||
user = User.map(msg.getUser(), msg.getUserIdentity());
|
||||
toUser = new User(msg.getUserGiftReciever().getUserId(), "", "", new Picture(""), 0, 0, new ArrayList<>());
|
||||
if (msg.getToUser().getNickname().isEmpty()) {
|
||||
toUser = liveHost;
|
||||
} else {
|
||||
toUser = User.map(msg.getToUser());
|
||||
}
|
||||
combo = msg.getComboCount();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.data.events.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||
|
||||
@@ -28,15 +28,10 @@ import lombok.Getter;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
|
||||
public class Picture {
|
||||
|
||||
@Getter
|
||||
@@ -49,7 +44,6 @@ public class Picture {
|
||||
}
|
||||
|
||||
public static Picture map(io.github.jwdeveloper.tiktok.messages.data.Image profilePicture) {
|
||||
|
||||
var index = profilePicture.getUrlListCount() - 1;
|
||||
if (index < 0) {
|
||||
return new Picture("");
|
||||
@@ -74,12 +68,11 @@ public class Picture {
|
||||
return CompletableFuture.supplyAsync(this::downloadImage);
|
||||
}
|
||||
|
||||
private BufferedImage download(String urlString)
|
||||
{
|
||||
if(urlString.isEmpty())
|
||||
{
|
||||
private BufferedImage download(String urlString) {
|
||||
if (urlString.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var baos = new ByteArrayOutputStream();
|
||||
try (var is = new URL(urlString).openStream()) {
|
||||
var byteChunk = new byte[4096];
|
||||
@@ -103,4 +96,9 @@ public class Picture {
|
||||
public static Picture Empty() {
|
||||
return new Picture("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Picture{link='" + link + "', image=" + image + "}";
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,4 @@ public class Badge {
|
||||
public static Badge empty() {
|
||||
return new Badge();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -24,19 +24,22 @@ package io.github.jwdeveloper.tiktok.data.models.badges;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.messages.data.BadgeStruct;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class CombineBadge extends Badge {
|
||||
private final Picture picture;
|
||||
private final String text;
|
||||
private final String subText;
|
||||
|
||||
|
||||
public CombineBadge(BadgeStruct.CombineBadge combineBadge) {
|
||||
|
||||
picture = Picture.map(combineBadge.getIcon());
|
||||
text = combineBadge.getText().getDefaultPattern();
|
||||
subText = combineBadge.getStr();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CombineBadge{picture=" + picture +", text='" + text + "', subText='" + subText + "'}";
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,18 @@ package io.github.jwdeveloper.tiktok.data.models.badges;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.messages.data.BadgeStruct;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class PictureBadge extends Badge {
|
||||
|
||||
private final Picture picture;
|
||||
public PictureBadge(BadgeStruct.ImageBadge imageBadge) {
|
||||
|
||||
public PictureBadge(BadgeStruct.ImageBadge imageBadge) {
|
||||
picture = Picture.map(imageBadge.getImage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PictureBadge{picture=" + picture + "}";
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,18 @@
|
||||
package io.github.jwdeveloper.tiktok.data.models.badges;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.messages.data.BadgeStruct;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StringBadge extends Badge {
|
||||
private final String text;
|
||||
|
||||
public String text;
|
||||
public StringBadge(BadgeStruct.StringBadge stringBadge) {
|
||||
|
||||
this.text = stringBadge.getStr();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StringBadge{text='" + text + "'}";
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,18 @@
|
||||
package io.github.jwdeveloper.tiktok.data.models.badges;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.messages.data.BadgeStruct;
|
||||
import lombok.Getter;
|
||||
|
||||
public class TextBadge extends Badge
|
||||
{
|
||||
@Getter
|
||||
public class TextBadge extends Badge {
|
||||
private final String text;
|
||||
|
||||
public TextBadge(BadgeStruct.TextBadge textBadge)
|
||||
{
|
||||
public TextBadge(BadgeStruct.TextBadge textBadge) {
|
||||
this.text = textBadge.getDefaultPattern();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TextBadge{text='" + text + "'}";
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,10 @@ public enum Gift {
|
||||
|
||||
LOVE_CHAT(6205, "Love Chat", 400, "https://storage.streamdps.com/iblock/440/4402267722e227b72adc97db92504b75/ae0f562146e701f32ae96761ac67c5cc.png"),
|
||||
|
||||
SPRING_TRAIN(8152, "Spring train", 3999, "https://storage.streamdps.com/iblock/035/035862dc0952468fc95f02995cec0f22/eeb69650806ea4c2e22558ef4b5e2b47.webp"),
|
||||
|
||||
CHRISTMAS_MARKET_G(7377, "Christmas Market G", 2000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/f498f29ef628c8318006a9ff2f49bf08~tplv-obj.png"),
|
||||
|
||||
CHICK(8165, "Chick", 10, "https://storage.streamdps.com/iblock/54e/54e5c232c74094c8e4b4d5678552f756/8132c0b012e7100540e1f1e2a5b3265d.webp"),
|
||||
|
||||
DOUBLE_TROUBLE(8038, "Double trouble", 2988, "https://storage.streamdps.com/iblock/a23/a23f89b59cebf6d82ba64437e0ce52c9/d13464a899047febd2bd3db61835cb1b.webp"),
|
||||
@@ -49,6 +53,8 @@ public enum Gift {
|
||||
|
||||
FALCON_6367(6367, "Falcon", 10999, "https://storage.streamdps.com/iblock/f88/f886e7678bef35f8c762a323386e6d23/7249e0af64c78d1d569a8d7a86ab58cd.png"),
|
||||
|
||||
HAPPY_FRIDAY(8265, "Happy Friday", 399, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/65e8fcb76825b9ec36a24faf9a3e9495~tplv-obj.png"),
|
||||
|
||||
SNEAKERHEAD(7394, "Sneakerhead", 1, "https://storage.streamdps.com/iblock/f64/f648c76bae6ef273077c74cc9312b126/87f4891550b2cfd3e49973f7f87dbdb2.webp"),
|
||||
|
||||
PANTHER_PAWS(7204, "Panther Paws", 199, "https://storage.streamdps.com/iblock/6e0/6e097d88e5e088d0228c702456e58450/72afb8bfa2231766da6817e911702d4b.webp"),
|
||||
@@ -73,6 +79,8 @@ public enum Gift {
|
||||
|
||||
PLAY_SAMBA(5793, "Play Samba", 99, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/fd3d6cc127464bacded6ed009074ae2f~tplv-obj.png"),
|
||||
|
||||
WOOLY_HAT(7458, "Wooly Hat", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a234d0187047fa48805c8ea2e1f1f756~tplv-obj.png"),
|
||||
|
||||
LITTLE_CROWN(6097, "Little Crown", 99, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/cf3db11b94a975417043b53401d0afe1~tplv-obj.jpg"),
|
||||
|
||||
RUBY_RED(8434, "Ruby red", 88, "https://storage.streamdps.com/iblock/405/405fcf52a1de3d14ab9834c1f30cc330/0deed9ee2c79ba6bf2005b0ce667bf60.webp"),
|
||||
@@ -81,6 +89,8 @@ public enum Gift {
|
||||
|
||||
COTTON_CANDY(7265, "Cotton Candy", 700, "https://storage.streamdps.com/iblock/51f/51f64a93c515f4a45169f24a52179f2f/730beb9631b1af4edfaf714d7686df04.webp"),
|
||||
|
||||
HOT(6756, "Hot", 10, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ec679890070187b61620b9662afb814e~tplv-obj.png"),
|
||||
|
||||
KNIGHT_HELMET(8672, "Knight Helmet", 199, "https://storage.streamdps.com/iblock/291/2915da07301fcb6a9a4d3e515931c2c8/31ebb4cad7a264fe9657a3ddfaca4eaa.webp"),
|
||||
|
||||
SUB_STAR(7072, "Sub Star", 1, "https://storage.streamdps.com/iblock/98f/98fea40fc19cc9dbd9a083b0844c163b/af7dd985812299d89f6cfa49c84e7eaf.webp"),
|
||||
@@ -97,6 +107,8 @@ public enum Gift {
|
||||
|
||||
MIKE(7789, "Mike", 4000, "https://storage.streamdps.com/iblock/de0/de0da7b6ce6ba19125b1c4eb2fd2966a/6804a72c00714de05f9239be7bd5b515.webp"),
|
||||
|
||||
STAY_WARM(9682, "Stay Warm", 450, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/abd104eb08ce0c351292036d8897fb8d.png~tplv-obj.png"),
|
||||
|
||||
HEADPHONE(6609, "Headphone", 199, "https://storage.streamdps.com/iblock/ce9/ce95ea6922db1e776296819861d69ddb/b0b11c041a28d46e51ff1ed8f288fe91.webp"),
|
||||
|
||||
HEADPHONE_8017(8017, "Headphone", 199, "https://storage.streamdps.com/iblock/055/05573a16af395b896b26847bc77fbb5e/55c0f27976902374940cfb54f22728d0.webp"),
|
||||
@@ -105,6 +117,8 @@ public enum Gift {
|
||||
|
||||
ICE_CREAM_8963(8963, "Ice cream", 5, "https://storage.streamdps.com/iblock/f72/f726165be6e93bdc69724375e7931dde/2e749d8d397b3ce5e6bcc90402f27c7d.webp"),
|
||||
|
||||
_2024_GLASSES(9640, "2024 Glasses", 224, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/512d2e9934cbae8d1019a391814edbd2.png~tplv-obj.jpg"),
|
||||
|
||||
GIVE_IT_ALL(6649, "Give It All", 1, "https://storage.streamdps.com/iblock/de8/de8468d1003361452021c2d4796bb0f6/574aa0cdd7b418a2a3af2ca4739e9e7f.webp"),
|
||||
|
||||
MARVELOUS_CONFETTI(7121, "Marvelous Confetti", 100, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/fccc851d351716bc8b34ec65786c727d~tplv-obj.jpg"),
|
||||
@@ -145,12 +159,16 @@ public enum Gift {
|
||||
|
||||
WATERMELON(8826, "Watermelon", 10, "https://storage.streamdps.com/iblock/84e/84e29ce96978961b12f1e88dd985b938/08e2a0ac2c2e2794aa2558e67d387639.webp"),
|
||||
|
||||
PEGASUS(9427, "Pegasus", 42999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f600a2495ab5d250e7da2066484a9383.png~tplv-obj.jpg"),
|
||||
|
||||
SHOW_TIME(6907, "Show Time", 3999, "https://storage.streamdps.com/iblock/d72/d722e6d78821a169ff9a6d128127c696/f457c04596d723f9033842f3417b6a72.webp"),
|
||||
|
||||
LOVE_FOCUS(6436, "Love Focus", 199, "https://storage.streamdps.com/iblock/cd1/cd1096cb1507fe07b633dad0b0aee967/d6b2e544219ed4d3c3263d319ab9bc5f.png"),
|
||||
|
||||
CHICKEN_LEG(6209, "Chicken Leg", 10, "https://storage.streamdps.com/iblock/ef7/ef776169ede6c4a635cef2b3ab35d29a/7683229a73330c04463d2b97984ea114.png"),
|
||||
|
||||
KING_CAKE_(6112, "King Cake ", 9, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/aa99da9f6b499ff879c3860e888a53ae~tplv-obj.png"),
|
||||
|
||||
BEACH_DAY(6431, "Beach Day", 2999, "https://storage.streamdps.com/iblock/c2d/c2df4a57f2ab16e641630b7077b40900/ba3cf6fd32d224f0014bd395b011f7a0.webp"),
|
||||
|
||||
CUBE(9184, "Cube", 10, "https://storage.streamdps.com/iblock/69d/69dab4e352882c0bd29c3864e24d80de/258857221189c76260b6af5eeb43e93b.webp"),
|
||||
@@ -159,10 +177,16 @@ public enum Gift {
|
||||
|
||||
SPEEDBOAT(5763, "Speedboat", 1888, "https://storage.streamdps.com/iblock/55f/55f832ac0d4e25f2527b2cf87ae8af08/ec99908e1787ae32c1387a20db7ca5ac.png"),
|
||||
|
||||
SNOWMAN(7551, "Snowman", 99, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e094e0fafc14aaf127fa0d0a7926619a~tplv-obj.png"),
|
||||
|
||||
YELLOW_BUS(8263, "Yellow Bus", 6000, "https://storage.streamdps.com/iblock/88d/88df4387d65bcc77b691098fd649bd59/ad401a92ddba9aae15bb777f9f38638d.webp"),
|
||||
|
||||
ROSA_NEBULA(8912, "Rosa Nebula", 15000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f722088231103b66875dae33f13f8719.png~tplv-obj.jpg"),
|
||||
|
||||
I_M_HERE(9354, "I'm here", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/7006392a82d57452d5ef08dd90e169c1.png~tplv-obj.png"),
|
||||
|
||||
HOT_CHOCO_GDM_23(7523, "Hot Choco GDM 23", 30, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/f62f5912077d9af84256de288399125a~tplv-obj.png"),
|
||||
|
||||
KISS(5284, "Kiss", 150, "https://storage.streamdps.com/iblock/d3d/d3df4a0ecebd25c21e7ce5a6f910f8f1/d9ce143ac4707f74d8b1fe4708a92ab3.png"),
|
||||
|
||||
KISS_5481(5481, "Kiss", 150, "https://storage.streamdps.com/iblock/5cc/5cca201687ef878daf36dfe39fd26807/b2171e9cc191783679794f42246c4ceb.webp"),
|
||||
@@ -181,6 +205,8 @@ public enum Gift {
|
||||
|
||||
COWBOY_HAT(8842, "Cowboy Hat", 199, "https://storage.streamdps.com/iblock/5f3/5f3df5eccbc82f458fdacd0f82d13e40/40980853c80e3da0e902a1db49ea9798.webp"),
|
||||
|
||||
TURKEY_FACE_GDDEC(9581, "Turkey Face GDDec", 399, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/65349d1ef783fc207c1d2b54a8d521a7.png~tplv-obj.png"),
|
||||
|
||||
FESTA_JUNINA_S_HAT(8638, "Festa Junina's Hat", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/61b32ccce11b289b3c1db7438dfb4450~tplv-obj.png"),
|
||||
|
||||
SPORTS_CAR(6089, "Sports Car", 7000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/e7ce188da898772f18aaffe49a7bd7db~tplv-obj.jpg"),
|
||||
@@ -191,6 +217,8 @@ public enum Gift {
|
||||
|
||||
CHOCO_PIE(6503, "Choco Pie", 10, "https://storage.streamdps.com/iblock/5a7/5a7610069bd417a2847f34c6c0b2821d/5faa955edd066d1140abb048f32be815.webp"),
|
||||
|
||||
I_M_BLUE(7707, "I'm blue", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c560ec76d5599198aaea9377c5ffab6e~tplv-obj.png"),
|
||||
|
||||
WHALE_DIVING(6084, "Whale diving", 1750, "https://storage.streamdps.com/iblock/5b2/5b27c388fe0d4dbe0a5f0a44ba7a8410/602a5a7cf538240f48ccf47c13237aa2.png"),
|
||||
|
||||
WHALE_DIVING_6820(6820, "Whale diving", 2150, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/46fa70966d8e931497f5289060f9a794~tplv-obj.jpg"),
|
||||
@@ -209,8 +237,12 @@ public enum Gift {
|
||||
|
||||
PEARL(5664, "Pearl", 800, "https://storage.streamdps.com/iblock/d42/d4241b9de546fb190964c12adeecabca/d03fe09dd3400422c55953555066487e.png"),
|
||||
|
||||
PLAY_FOR_YOU(9535, "Play for you", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/182659e90a3432aa155e61c9c0d89df0.png~tplv-obj.png"),
|
||||
|
||||
LET_US_DANCE(7196, "Let Us Dance", 1999, "https://storage.streamdps.com/iblock/cae/caeaf097812661e65ff761aa60d5300a/444a1217ff8fbdeaf8e4682405871c7a.webp"),
|
||||
|
||||
_2024_COUNTDOWN(9641, "2024 Countdown", 2024, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/a358a3ce18241dcc6e7d0b02d091d563.png~tplv-obj.jpg"),
|
||||
|
||||
MINI_SPEAKER(6042, "Mini Speaker", 1, "https://storage.streamdps.com/iblock/1b1/1b117cbff78bfb7f50ba4d90a16c6112/30f4b176fd30683e3cbfc9013fe96d82.png"),
|
||||
|
||||
FLORAL_BLOOM(5788, "Floral Bloom", 1500, "https://storage.streamdps.com/iblock/858/85827a8e5266c8d4c697d9aa930fead6/149392b39b041febde90bc4ea80ce1a5.png"),
|
||||
@@ -233,10 +265,14 @@ public enum Gift {
|
||||
|
||||
ARCADE_GAME_7041(7041, "Arcade Game", 1200, "https://storage.streamdps.com/iblock/fd0/fd0785612b024900444a0a69083400ff/3181d6af50b05dd65a7ba75902bb5b94.webp"),
|
||||
|
||||
GORGEOUS_TROPHY(6741, "Gorgeous Trophy", 7000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/279c9495c2150e333bc4bc13761d177e~tplv-obj.png"),
|
||||
|
||||
IT_S_CORN(6928, "It's corn", 1, "https://storage.streamdps.com/iblock/d48/d48869b41c99cf004857fb74aff97552/75f200300cdaf3424287814ec55e9656.webp"),
|
||||
|
||||
SUSHI_SET(7226, "Sushi Set", 20, "https://storage.streamdps.com/iblock/097/09752a51af505fbde2e9aa853d1ada62/3b981d4797111c44c45fbd8de5201fbe.webp"),
|
||||
|
||||
YEAH_NAH(9576, "Yeah Nah", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4b20c5aab3841657a343be3769307805.png~tplv-obj.png"),
|
||||
|
||||
BIRTHDAY_GLASSES(6776, "Birthday Glasses", 199, "https://storage.streamdps.com/iblock/98d/98deaf0a4a818ba6b0333ca9aee4db59/97520c1ceae957c77ef1dbcc0f092187.webp"),
|
||||
|
||||
SOCKS_AND_SANDALS(6618, "Socks and Sandals", 150, "https://storage.streamdps.com/iblock/da2/da28ef4030197f812686f10b2c3f06c7/7cb8ebff6f6028e2a56b2c0c268c3620.webp"),
|
||||
@@ -265,6 +301,8 @@ public enum Gift {
|
||||
|
||||
TIKTOK_VOLCANO(6869, "TikTok Volcano", 4000, "https://storage.streamdps.com/iblock/e6d/e6d4c0d014c552ec6e8eccb804a7659f/9678f5e24b6e9b069b43c4f84a536d9f.webp"),
|
||||
|
||||
KITTEN_PAW(9647, "Kitten Paw", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/332520d7b5085ce591396c8d2bb9d352.png~tplv-obj.png"),
|
||||
|
||||
_2023(7604, "2023", 1, "https://storage.streamdps.com/iblock/8fb/8fba3d5f6bf547ba8c94d3f393992d46/e3cfc1cfea30d7c139f7c4943f5d3b26.webp"),
|
||||
|
||||
ROMAN_EMPIRE(7166, "Roman Empire", 199, "https://storage.streamdps.com/iblock/c77/c778c4e5cd1c68a50dcc06e4bfc3aa08/48edf8b190d98b0a3cc4623e6cc9a22c.webp"),
|
||||
@@ -285,6 +323,8 @@ public enum Gift {
|
||||
|
||||
PIZZA(7055, "Pizza", 40, "https://storage.streamdps.com/iblock/c9d/c9d7f483cc0059a1e8165bfbd1341688/307a559eb2b371b92b8ea36ae96bfa30.webp"),
|
||||
|
||||
CHRISTMAS_POTATO(9587, "Christmas Potato", 10, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/5448f1f5157d3a4a88e0f57acf3dbfe0.png~tplv-obj.png"),
|
||||
|
||||
AMUSEMENT_PARK(9466, "Amusement Park", 17000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/12ecc01c2984c5d85bb508e80103a3cb.png~tplv-obj.jpg"),
|
||||
|
||||
CLUB(6417, "Club", 2000, "https://storage.streamdps.com/iblock/49b/49be18ae5914346ffcaf15a519ba9c1c/41326cb23d22010f0c4a8edf5bd27615.webp"),
|
||||
@@ -293,11 +333,15 @@ public enum Gift {
|
||||
|
||||
DANCING_CAPYBARAS(8806, "Dancing Capybaras", 2200, "https://storage.streamdps.com/iblock/ac2/ac2606f1dc2504c9a1b7974f40074c87/c243031480e8f2e4bbd8e7a43228ff1f.webp"),
|
||||
|
||||
HUSKY(7920, "Husky", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a2f5d595e9d96aec19a7c0ed5fa9b017~tplv-obj.png"),
|
||||
|
||||
DUCK(6265, "Duck", 299, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/e172f660a1d4f95813a3ace0fde42323~tplv-obj.jpg"),
|
||||
|
||||
XMAS_MISHKA_BEAR(9617, "Xmas Mishka Bear", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/700c1c8817847317407cc2b8c6c9da42.png~tplv-obj.png"),
|
||||
|
||||
FLOWER_OVERFLOW(6148, "Flower Overflow", 4000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/743c4bb44e7e0bf251a7f2f5ada231ee~tplv-obj.jpg"),
|
||||
|
||||
PINK_SHOES(8815, "Pink shoes", 5, "https://storage.streamdps.com/iblock/387/387c559abfc868aa8f7d605a25748c14/06e08ba736cb17076b9c314058160ad2.webp"),
|
||||
PINK_SHOES(8890, "Pink Shoes", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/cba8a7c718988bd51c7b6055e9ab1ec4.png~tplv-obj.png"),
|
||||
|
||||
PINK_SHOES_8843(8843, "Pink shoes", 5, "https://storage.streamdps.com/iblock/e32/e328784531bfcd4773983c6a8e205a44/a3b5a6f4fa914fdf10b754ee59dc34a4.webp"),
|
||||
|
||||
@@ -313,8 +357,14 @@ public enum Gift {
|
||||
|
||||
SUNDAY_ROAST(6634, "Sunday Roast", 199, "https://storage.streamdps.com/iblock/218/218658dfe16bf8eeb11824cae5788028/95bbb526ea861ef2ba3dbe020431374f.webp"),
|
||||
|
||||
_2024_JOYLENS(9643, "2024 JoyLens", 224, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/36d82bbcd87c1914df84262d9bdd9b95.png~tplv-obj.jpg"),
|
||||
|
||||
LEDERHOSEN(6975, "Lederhosen", 10, "https://storage.streamdps.com/iblock/7c7/7c72a908dce6d9df4db0a6159be1751b/2ff181aa1fae6088a37f942d51401176.webp"),
|
||||
|
||||
LOVE_U(7697, "LOVE U", 899, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/79d45877691333e2ba69a9098406e95c~tplv-obj.png"),
|
||||
|
||||
_2024(9639, "2024", 1, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/484a44bbe62ce47687d1da31a6602bbd.png~tplv-obj.jpg"),
|
||||
|
||||
MATE_TEA(7089, "Mate tea", 10, "https://storage.streamdps.com/iblock/506/506e98699cdfefd679b35ea5170823b0/a95e9e3721c9b86e3342169b3211b30e.webp"),
|
||||
|
||||
TEAM_BRACELET(9139, "Team Bracelet", 2, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/54cb1eeca369e5bea1b97707ca05d189.png~tplv-obj.png"),
|
||||
@@ -333,12 +383,16 @@ public enum Gift {
|
||||
|
||||
BIGFOOT(9147, "Bigfoot", 3000, "https://storage.streamdps.com/iblock/f95/f95a4fcfa57150610fa50542db5b0990/ecb879cd751e580d3fe92770788c1735.webp"),
|
||||
|
||||
BOO_THE_GHOST(9304, "Boo the Ghost", 88, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/cb909c78f2412e4927ea68d6af8e048f.png~tplv-obj.png"),
|
||||
|
||||
SO_BEAUTIFUL(7024, "So Beautiful", 1, "https://storage.streamdps.com/iblock/ad6/ad67c8d6c93ff4c375568b0bfabbed6f/c68dfd6fda7e8bd84f0bc7fa9ce47af0.webp"),
|
||||
|
||||
SUNGLASSES(5509, "Sunglasses", 199, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/08af67ab13a8053269bf539fd27f3873.png~tplv-obj.jpg"),
|
||||
|
||||
PANDA(37, "Panda", 5, "https://storage.streamdps.com/iblock/833/833aadcba552a8a2cc779dd8d4c537c7/f952c72ee1f40e4fcd07d713b3da6565.png"),
|
||||
|
||||
MISS_YOU(8803, "Miss You", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/3c53396b922691a7520698f47105a753.png~tplv-obj.png"),
|
||||
|
||||
TIKTOK_UNIVERSE(6038, "TikTok Universe", 34999, "https://storage.streamdps.com/iblock/a79/a79204e0fab55cdc35ca0bdfa019face/8f06121e1c15be8566b3fc02982a2027.png"),
|
||||
|
||||
TIKTOK_UNIVERSE_6039(6039, "TikTok Universe", 34999, "https://storage.streamdps.com/iblock/49d/49d934dc15cf5efc3ebef902a5974d56/04799e79cb4bd04a20d77d2f3fa9922d.png"),
|
||||
@@ -353,6 +407,8 @@ public enum Gift {
|
||||
|
||||
PINCH_CHEEK(6694, "Pinch Cheek", 199, "https://storage.streamdps.com/iblock/f6c/f6c95968ca266cbb2527af09989eaea0/27be132509198253b5c48e5495038e5b.png"),
|
||||
|
||||
HOLIDAY_STOCKING(7504, "Holiday Stocking", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e05de50999ebb446e15c4947b30d3140~tplv-obj.png"),
|
||||
|
||||
MARVIN_THE_MONKEY(6843, "Marvin the Monkey", 10, "https://storage.streamdps.com/iblock/31a/31a03cf8430fa062064dd9e544910de2/e7939198db3920aeaf3d95167712af0e.webp"),
|
||||
|
||||
ELEPHANT_TRUNK(8260, "Elephant trunk", 299, "https://storage.streamdps.com/iblock/1ea/1eafea22e99969312cda7c142d8eb3c5/59f72e0dce1bc4fcf83a34f56872b492.webp"),
|
||||
@@ -369,6 +425,8 @@ public enum Gift {
|
||||
|
||||
ROSE(5655, "Rose", 1, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.jpg"),
|
||||
|
||||
THE_VAN_CAT(9650, "The Van Cat", 799, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/6973dd1b6d3dee3ca3f0ebac3c1d2977.png~tplv-obj.png"),
|
||||
|
||||
TRENDING_FIGURE(9138, "Trending Figure", 999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/df7b556ccf369bf9a42fe83ec8a77acf.png~tplv-obj.jpg"),
|
||||
|
||||
ROSA(7997, "Rosa", 10, "https://storage.streamdps.com/iblock/486/486a2490c987c2bb97b6068fd5aac5ab/49d9045fcfe94bbfbd08c3363bb4512a.webp"),
|
||||
@@ -385,12 +443,16 @@ public enum Gift {
|
||||
|
||||
COTTON_S_SHELL(8352, "Cotton's Shell", 5, "https://storage.streamdps.com/iblock/766/7665d59f0ef96aecd2dac6fc5b0c19a4/3b169a12b4f8686c68d596f6d47d2f77.webp"),
|
||||
|
||||
KIWI_BIRD(9667, "Kiwi Bird", 10, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/b73cb4aaa76a33efd881192589d65351.png~tplv-obj.png"),
|
||||
|
||||
FLAME_HEART(9087, "Flame heart", 1, "https://storage.streamdps.com/iblock/10d/10df10624cdeebe8ff5e0e89e8c8e960/28b8da2878a420f8465cbbc1ec1e6b58.webp"),
|
||||
|
||||
FAIRY_BREAD(5823, "Fairy Bread", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a42f9ac9cd6b26da03818ff65ac919f1~tplv-obj.png"),
|
||||
|
||||
TIKTOK_SHUTTLE(6751, "TikTok Shuttle", 20000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/8ef48feba8dd293a75ae9d4376fb17c9~tplv-obj.jpg"),
|
||||
|
||||
INDEPENDENCE_DAY(6633, "Independence Day", 10, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b967993872a6e40f3477d30545f8d2eb~tplv-obj.png"),
|
||||
|
||||
MAGGIE(7911, "Maggie", 15000, "https://storage.streamdps.com/iblock/a12/a12a1b23f1f6a19d728de84e1f43e21d/ff288346e9855a9bb6deb4450491028f.webp"),
|
||||
|
||||
PANDA_SKYDIVING(8812, "Panda skydiving", 2000, "https://storage.streamdps.com/iblock/a29/a29903a975ce45f7b9939b510412fcee/051afc0510a7349a9ebfcde9e0fdec24.webp"),
|
||||
@@ -401,6 +463,8 @@ public enum Gift {
|
||||
|
||||
GAMER_CYBER_MASK(7895, "Gamer Cyber Mask", 399, "https://storage.streamdps.com/iblock/383/383652cc1fd3cae9402eeae3a8f5ee1e/df8a16397bb0ed28c0e522b4cfb26500.webp"),
|
||||
|
||||
XMAS_IN_LONDON(9680, "Xmas in London", 20000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/876204a6ad0b1b0e4675d9be42439183.png~tplv-obj.png"),
|
||||
|
||||
HAPPY_FATHER_S_DAY(8712, "Happy Father's Day", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/193eba78ded4d388a0b5a7ae95943796~tplv-obj.png"),
|
||||
|
||||
KO(7542, "KO", 20, "https://storage.streamdps.com/iblock/e5e/e5efb63a21695a08d9647508aca3c95e/cffda8af4cc1a9f4a66eb01b11f4db85.webp"),
|
||||
@@ -417,6 +481,8 @@ public enum Gift {
|
||||
|
||||
DON_T_DO_IT(7688, "Don’t Do It", 500, "https://storage.streamdps.com/iblock/dca/dcac97e4190d46d113f4bdf2918ee173/4fae166b3f3273b9dbbc2a86bea0ec18.webp"),
|
||||
|
||||
BIRDS(5514, "Birds", 600, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/0911b5726d912dabbf6ee4b0383352ea.png~tplv-obj.png"),
|
||||
|
||||
GOLDEN_TRUMPET(8767, "Golden Trumpet", 15, "https://storage.streamdps.com/iblock/a44/a4441a11d3cb073e855088a4eff72fdb/020b0d041c38b00b730b28806dbe6cc5.webp"),
|
||||
|
||||
HANDS_UP(8244, "Hands Up", 499, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/f4d906542408e6c87cf0a42f7426f0c6~tplv-obj.jpg"),
|
||||
@@ -475,6 +541,8 @@ public enum Gift {
|
||||
|
||||
FOOTBALL_HELMET(7851, "Football Helmet", 50, "https://storage.streamdps.com/iblock/9cc/9cce61670c1a81b7954fcf3520dc15a2/b78182e9fd2ff1c6ae1256abd8e2e2bf.webp"),
|
||||
|
||||
NEMO(9704, "Nemo", 15, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/68fcf30cb3fb07e9546f5e7fbc2b0ac0.png~tplv-obj.png"),
|
||||
|
||||
SCORPIO_STAR_SIGN(7159, "Scorpio Star Sign", 9999, "https://storage.streamdps.com/iblock/c91/c91f3a3685b5c54d9e96d5f9443c4fda/50c48574ff60f328b7a50b80cd9aa4b8.webp"),
|
||||
|
||||
SOCCER_BALL(5852, "Soccer Ball", 39, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e1932db6aea81bbddc4e7dc0229ac155~tplv-obj.png"),
|
||||
@@ -487,8 +555,12 @@ public enum Gift {
|
||||
|
||||
HANGING_LIGHTS(5937, "Hanging Lights", 199, "https://storage.streamdps.com/iblock/e03/e03da22fa8c302dbf1d9439c65380549/6d9f912b5a9253f91c01ed58e3ccbe47.png"),
|
||||
|
||||
VACATION(8804, "Vacation", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/8f46e8eef9cbd5304fb802104c2b4ef4.png~tplv-obj.png"),
|
||||
|
||||
FOOTY(5893, "Footy", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/94f8ac5c7b6f90aba713b44ddac40bf1~tplv-obj.png"),
|
||||
|
||||
GRUMPY_GLASSES(7846, "Grumpy Glasses", 99, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/6f38f8ed7442f69a105788b5c0c74a38~tplv-obj.png"),
|
||||
|
||||
BATIK_CLOTHES(5461, "Batik Clothes", 1000, "https://storage.streamdps.com/iblock/46d/46d0f497391a934d27d9b993f444d8b2/121af719b172eed61d8a75c1b1341c9d.png"),
|
||||
|
||||
DANCING_QUEENS(9240, "Dancing queens", 20000, "https://storage.streamdps.com/iblock/c79/c793af446369ecef5238e73312c84ccd/464a76f3e6eaee9afc771f45a4bba9df.webp"),
|
||||
@@ -581,12 +653,16 @@ public enum Gift {
|
||||
|
||||
BIRTHDAY_CAKE_9097(9097, "Birthday Cake", 1, "https://storage.streamdps.com/iblock/5b9/5b9eca4a99e965cb25183681a07a5276/c28f7e9c4a8e42460225ff2d12300ae7.webp"),
|
||||
|
||||
WITCHY_KITTY(7084, "Witchy Kitty", 30, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/dfce46f99a1206cca84f9092603e4783~tplv-obj.png"),
|
||||
|
||||
BANANA_LEAF_VESSEL(5991, "Banana leaf vessel", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/8e635863e20cfa3651bd8a5b762ae72d~tplv-obj.png"),
|
||||
|
||||
ICE_LOLLY(6545, "Ice Lolly", 10, "https://storage.streamdps.com/iblock/93d/93dda2498d64aa0a29d444103a7804dd/92fd7397fffbaa8755cb233815964bbf.png"),
|
||||
|
||||
CONFETTI(5585, "Confetti", 100, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/cb4e11b3834e149f08e1cdcc93870b26~tplv-obj.jpg"),
|
||||
|
||||
PANETTONE_GDM_23(7477, "Panettone GDM 23", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/64ce2413a362442819b4551703b7b26c~tplv-obj.png"),
|
||||
|
||||
MATCH__MATCH_(7068, "Match! Match!", 200, "https://storage.streamdps.com/iblock/cb4/cb43e14c94694d3d3ae355bdfc517afd/494cd902b8018b35b6dc0f0016c89694.webp"),
|
||||
|
||||
SHINY_AIR_BALLOON(7123, "Shiny air balloon", 1000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/9e7ebdca64b8f90fcc284bb04ab92d24~tplv-obj.jpg"),
|
||||
@@ -599,12 +675,18 @@ public enum Gift {
|
||||
|
||||
SUNSET_SPEEDWAY(6203, "Sunset Speedway", 10000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/df63eee488dc0994f6f5cb2e65f2ae49~tplv-obj.jpg"),
|
||||
|
||||
NEW_YEAR_JOURNEY(9645, "New Year Journey", 12024, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/0ef9654d92354172fb9b3b364827940c.png~tplv-obj.jpg"),
|
||||
|
||||
DANCING_ADAM(7468, "Dancing Adam", 5000, "https://storage.streamdps.com/iblock/f5c/f5cda80a1f9853c49226a450faf26e8f/6318d17d7a2526f521123402d19a4c3e.webp"),
|
||||
|
||||
SCEPTRE(5300, "Sceptre", 150, "https://storage.streamdps.com/iblock/080/080d7e9dc934f98dd8cf5dce3b5075b2/a62a3963f6d2822177763b51d4328d37.png"),
|
||||
|
||||
SCEPTRE_7364(7364, "Sceptre", 150, "https://storage.streamdps.com/iblock/d2d/d2d1b0359f480a7db08e490364d056b2/bcb44a039dfa4d148af6cde9f233ea13.webp"),
|
||||
|
||||
GOBBLE_GOBBLE(9604, "Gobble Gobble", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/ada9babc0b55cf005e8c8d13dfc30b42.png~tplv-obj.png"),
|
||||
|
||||
REINDEER(9670, "Reindeer", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4565fa0cd1dbf76463144b0d4cc50bf1.png~tplv-obj.png"),
|
||||
|
||||
SEAL_AND_WHALE(8381, "Seal and Whale", 34500, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/3781e9159ff09272826d3f2216ba36ef.png~tplv-obj.jpg"),
|
||||
|
||||
HAPPY_PARTY(8247, "Happy Party", 6999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/41774a8ba83c59055e5f2946d51215b4~tplv-obj.jpg"),
|
||||
@@ -615,8 +697,6 @@ public enum Gift {
|
||||
|
||||
CELEBRATION_TIME(6790, "Celebration Time", 6999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/e73e786041d8218d8e9dbbc150855f1b~tplv-obj.jpg"),
|
||||
|
||||
EXCLUSIVE_YACHT(9524, "Exclusive Yacht", 20000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/da8c85d5ae09ebf320216202e8fa015d.png~tplv-obj.jpg"),
|
||||
|
||||
MONTY(7742, "Monty", 1, "https://storage.streamdps.com/iblock/c70/c70e3a9404b18068056d04d5394d739a/4e0e55d9d10a7747b7caf462cd87b4b3.webp"),
|
||||
|
||||
PYRAMIDS(8416, "Pyramids", 15000, "https://storage.streamdps.com/iblock/988/988ffe82e8f3b235bd91dac1e31e708d/ad0365d14ba0480e5d6d60f6eb798608.webp"),
|
||||
@@ -631,8 +711,12 @@ public enum Gift {
|
||||
|
||||
HI_JULY(6603, "Hi July", 1, "https://storage.streamdps.com/iblock/e03/e0301a9670584be92d945ff3cb889b99/0fbb7b11f916953201588b5bfbcb3f5a.png"),
|
||||
|
||||
SUMMER_IRIS_(6655, "Summer Iris ", 30, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/cb591f5b5729fa6e64cac57c78724981~tplv-obj.png"),
|
||||
|
||||
LIGHTNING_BOLT_(6652, "Lightning Bolt ", 1, "https://storage.streamdps.com/iblock/265/2655cafe6afc1fa0fca76a732bad4730/bfb4abdf65da281c7ccf0b682f3406a3.webp"),
|
||||
|
||||
AURORA(8754, "Aurora", 12000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1f59f5593ce135325c1a034825cec18c.png~tplv-obj.png"),
|
||||
|
||||
CAMPING_NIGHT(6520, "Camping Night", 13999, "https://storage.streamdps.com/iblock/be3/be3c39c622d80d029c5e752134ac6978/c95701f2e894403ca47de971f2ced0d8.png"),
|
||||
|
||||
MAKE_IT_RAIN(5336, "Make it rain", 500, "https://storage.streamdps.com/iblock/770/770e03c64144e6d7830e884cd7140a8a/47af803e978121e760d649d47e67de50.png"),
|
||||
@@ -657,10 +741,14 @@ public enum Gift {
|
||||
|
||||
STORMS_AT_SEA(9514, "Storms at sea", 2200, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4918fbbdf220873dd8cae4c94d1ae037.png~tplv-obj.png"),
|
||||
|
||||
CHRISTMAS_WREATH_G(7527, "Christmas Wreath G", 10, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7842b50135e089334fc40d9705bb53c7~tplv-obj.png"),
|
||||
|
||||
SUPERHERO_FIGHT(8814, "Superhero fight", 30000, "https://storage.streamdps.com/iblock/d6b/d6b1c955153c8f8c5048d6c8f0d1b418/97d04b889e64328e9ab07224f6072b5f.webp"),
|
||||
|
||||
COOKIE(6883, "Cookie", 5, "https://storage.streamdps.com/iblock/fd2/fd20c8c619b1d43efb9f2fe1923c48a7/45c056f74c9f214dc55d464eab43b224.webp"),
|
||||
|
||||
ELF_GDM_23(9363, "Elf GDM 23", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/60e5289b379660cc562742cf987a2d35.png~tplv-obj.png"),
|
||||
|
||||
DOG_BONE(8108, "Dog Bone", 10, "https://storage.streamdps.com/iblock/8ba/8badf8e0a5bcbf8d98ed6c4fc0e16c69/b0a8a8020986eb564713c042d23f83b2.webp"),
|
||||
|
||||
RUSSIAN_CREPES(5547, "Russian Crepes", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/8525a07c6bf16a74eee66e9ad119b3b8.png~tplv-obj.png"),
|
||||
@@ -679,12 +767,16 @@ public enum Gift {
|
||||
|
||||
ICE_CREAM_CONE(5827, "Ice Cream Cone", 1, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/968820bc85e274713c795a6aef3f7c67~tplv-obj.jpg"),
|
||||
|
||||
GOOD_MORNING(8269, "Good Morning", 399, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5c1a28f3aa7eefc27491f3020748ce54~tplv-obj.png"),
|
||||
|
||||
FLY_LOVE(8248, "Fly Love", 19999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/a598ba4c7024f4d46c1268be4d82f901~tplv-obj.jpg"),
|
||||
|
||||
BEACH_DATE(6132, "Beach Date", 899, "https://storage.streamdps.com/iblock/504/504a5dfef033a7e90e4f07987b0c0f28/70ec484fc4c798d3e09a7fbcae83ee95.png"),
|
||||
|
||||
TRAVEL_WITH_YOU(6233, "Travel with You", 999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/753098e5a8f45afa965b73616c04cf89~tplv-obj.jpg"),
|
||||
|
||||
GINGEBREAD_MAN(9671, "Gingebread Man", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/2399f65414f77419ec7d5e9274dc8e0e.png~tplv-obj.png"),
|
||||
|
||||
CHOCOLATE_STICK(6002, "Chocolate Stick", 1, "https://storage.streamdps.com/iblock/5c9/5c9487af2038c340fdbeb0b9ea4ff83e/b377ae8024881b93822f7b0a6bfe04e8.png"),
|
||||
|
||||
DJ_ALIEN(8988, "DJ Alien", 5000, "https://storage.streamdps.com/iblock/67c/67cd7b9372f25b4f3558eacdfb83dc8b/059b6bf7b8c268d525fd9295fac0eb61.webp"),
|
||||
@@ -703,12 +795,16 @@ public enum Gift {
|
||||
|
||||
TULIP_BOX(5325, "Tulip Box", 200, "https://storage.streamdps.com/iblock/d44/d4471e5deb9cb5831f846ca4c9df9c5d/7d1236ecd67b3e655c3dfd72673a423d.png"),
|
||||
|
||||
COOL_(9583, "Cool!", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/424c61f16c16919f169fd0352bd24661.png~tplv-obj.png"),
|
||||
|
||||
SQUIRREL(7213, "Squirrel", 1, "https://storage.streamdps.com/iblock/5c3/5c37dce1eab0d67386329f3a2920a874/38104bd52d316ea76464433b3b07dea7.webp"),
|
||||
|
||||
CHOC_CHIP_COOKIE(6416, "Choc Chip Cookie", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7dd2731de2e644301a329d3eb437b427~tplv-obj.png"),
|
||||
|
||||
METEOR_SHOWER(6563, "Meteor Shower", 3000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/71883933511237f7eaa1bf8cd12ed575~tplv-obj.jpg"),
|
||||
|
||||
GINGERMAN_PARTY(9668, "Gingerman Party", 1200, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/008a9554e736642f1b2dca9f198bb710.png~tplv-obj.png"),
|
||||
|
||||
CAKE(5720, "Cake", 20, "https://storage.streamdps.com/iblock/edb/edbe349c5a4be01ec1fbf2225d0f48dc/4169ef7f0263177384205df6663451c8.png"),
|
||||
|
||||
MARCH(7976, "March", 1, "https://storage.streamdps.com/iblock/ba4/ba44cb084cab8c9c63b4513a145813f4/56531d239586a3d4552859cb2b23314d.webp"),
|
||||
@@ -721,12 +817,16 @@ public enum Gift {
|
||||
|
||||
STAR(6432, "Star", 99, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/485175fda92f4d2f862e915cbcf8f5c4~tplv-obj.jpg"),
|
||||
|
||||
SPRING_TRAIN(8152, "Spring train", 3999, "https://storage.streamdps.com/iblock/035/035862dc0952468fc95f02995cec0f22/eeb69650806ea4c2e22558ef4b5e2b47.webp"),
|
||||
|
||||
DALLAH(8097, "Dallah", 10, "https://storage.streamdps.com/iblock/402/402ec89b471788374f63bd0d906e49c2/bbb7055a407d84bd3be843f5ca9fdc4b.webp"),
|
||||
|
||||
GB_NORTH_POLE(9657, "GB North Pole", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/79715a53c41619e7b205eb26e57926d4.png~tplv-obj.png"),
|
||||
|
||||
CHRISTMAS_CAROUSEG(7525, "Christmas CarouseG", 2000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b5ba3941f7389da7495b659e888ea61a~tplv-obj.png"),
|
||||
|
||||
THUNDER_HAMMER(6635, "Thunder Hammer", 1, "https://storage.streamdps.com/iblock/401/401ff4c96ee1f2301db5a6fed5d53103/830012ba80bac708f9281417ede8696c.png"),
|
||||
|
||||
FESTIVE_TINY_DINY(9615, "Festive Tiny Diny", 15, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f2a8c2967c7153e9077bb469f2e42317.png~tplv-obj.png"),
|
||||
|
||||
WOLF(8778, "Wolf", 5000, "https://storage.streamdps.com/iblock/70f/70fa80dd2d07f44f28db148328735a6b/68c7215817c6143ac33036933fcf777d.webp"),
|
||||
|
||||
DAISIES(6447, "Daisies", 1, "https://storage.streamdps.com/iblock/e11/e110e47562d77ab5fa26cc31e840f801/a4a1823ef2c1bc65c4dc2a4e82ec446b.png"),
|
||||
@@ -735,6 +835,12 @@ public enum Gift {
|
||||
|
||||
CAMPING(6322, "Camping", 250, "https://storage.streamdps.com/iblock/9a9/9a9370a392311149be37e7c40c3e960d/ecb9dcdacf3a2ae0abef79baf0c4f41c.webp"),
|
||||
|
||||
LUCKY_AIRDROP_BOX(9717, "Lucky Airdrop Box", 999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/6ae56f08ae3ee57ea2dda0025bfd39d3.png~tplv-obj.jpg"),
|
||||
|
||||
GOLDEN(7921, "Golden", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b97f58dcb0250489ae98529bcb0542ca~tplv-obj.png"),
|
||||
|
||||
BALLET_DANCER(5549, "Ballet Dancer", 500, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c09cc8ce49476d2c46e9c8af6189d5f4.png~tplv-obj.png"),
|
||||
|
||||
FAIRY_WINGS(9463, "Fairy Wings", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/e504dc2f313b8c6df9e99a848e1b3a99.png~tplv-obj.png"),
|
||||
|
||||
TICKET(6856, "Ticket", 10, "https://storage.streamdps.com/iblock/434/434746bffe494ac6ad2eb5e7e4384955/92e426ea0b4d4a9f89d7e2786115cd20.webp"),
|
||||
@@ -751,10 +857,14 @@ public enum Gift {
|
||||
|
||||
RIO_DE_JANEIRO(7218, "Rio de Janeiro", 9999, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/34c0eb43c3d50e8ab64408171ebbe733~tplv-obj.png"),
|
||||
|
||||
REALLY_CURIOUS(9703, "Really Curious", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/793ba68723567b695b12f2ef08dc1484.png~tplv-obj.png"),
|
||||
|
||||
BLOOMING_RIBBONS(9498, "Blooming Ribbons", 1000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f76750ab58ee30fc022c9e4e11d25c9d.png~tplv-obj.jpg"),
|
||||
|
||||
CORNFLOWER(8186, "Cornflower", 5, "https://storage.streamdps.com/iblock/025/025c50c390f6a12148a69728284c7298/36b50fe529db9d7db028b0774842e103.webp"),
|
||||
|
||||
SPIN_WITH_ME_GDM(9152, "Spin with me GDM", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/149ac2e87d05490d7d251149cefe27a2.png~tplv-obj.png"),
|
||||
|
||||
ASMR_TIME_(6990, "ASMR Time ", 10, "https://storage.streamdps.com/iblock/49d/49dccba4525df92ed17678cc6ea47e95/b2c8c52d5294bb531d7d87a4c3ff97fe.webp"),
|
||||
|
||||
POOL_PARTY(5938, "Pool Party", 4999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/4147c5bcfad9623c693f83d5d6cba1f7~tplv-obj.jpg"),
|
||||
@@ -793,6 +903,8 @@ public enum Gift {
|
||||
|
||||
SPACESHIP(6588, "Spaceship", 13999, "https://storage.streamdps.com/iblock/fb1/fb1096568dcc97c2575dec7441d0d651/245c4c7ce9bf5d5378586eb3a2478b42.webp"),
|
||||
|
||||
PINATA(6800, "Pinata", 699, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c8a18d43dc9fb4598d7e991ebeb958ae~tplv-obj.png"),
|
||||
|
||||
BIRTHDAY_CROWN(9096, "Birthday Crown", 99, "https://storage.streamdps.com/iblock/c07/c073f2d950a252aa24b7343655208c8a/68f6af6dc16ab51396cef18f50a43792.webp"),
|
||||
|
||||
LION_S_MANE(7985, "Lion's Mane", 500, "https://storage.streamdps.com/iblock/267/2670a5a8c9666b7afffb3255c2c104ee/abe9a0e7a6ef8b83d94df90f3a356748.webp"),
|
||||
@@ -819,8 +931,12 @@ public enum Gift {
|
||||
|
||||
NEW_UNIVERSE(9081, "New Universe", 1, "https://storage.streamdps.com/iblock/ff9/ff906a964a6ad9c4504438302d9354b8/3ee4796c239930c395afb3d7ef10295a.webp"),
|
||||
|
||||
SPARKLING_COUNTDOWN(9644, "Sparkling Countdown", 2024, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/d6b4539ed9683707bdefe268f6575e74.png~tplv-obj.jpg"),
|
||||
|
||||
DRUMS(7882, "Drums", 1000, "https://storage.streamdps.com/iblock/449/449c40e5064f776737e24fd6460195a1/477a014b033108643c2d674b2cce2d0a.webp"),
|
||||
|
||||
ELFS_HAT_(9706, "Elfs Hat ", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9857a040c92b34d6a261201a93c185f.png~tplv-obj.png"),
|
||||
|
||||
PUMPKIN_PIE(7396, "Pumpkin Pie", 5, "https://storage.streamdps.com/iblock/abf/abf5efb8fac6f64568b472c3afdb3e25/f85e4ef55b8c7d03f81351babd833c69.webp"),
|
||||
|
||||
EMAIL_MESSAGE(6199, "Email Message", 1000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/c959df6dbffd6f07849d22d2c3c07861~tplv-obj.jpg"),
|
||||
@@ -831,10 +947,14 @@ public enum Gift {
|
||||
|
||||
HI_MARCH(7977, "Hi March", 88, "https://storage.streamdps.com/iblock/e22/e2266686271c7a90ff04517f248c6f73/0459d679c01a5bfa5a4be1d61ec81ec8.webp"),
|
||||
|
||||
CANDY_CANE_GUN(7498, "Candy Cane Gun", 799, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/58ef7964e32adc5fc47c5706a02e4ff0~tplv-obj.png"),
|
||||
|
||||
FANTASTIC(6813, "Fantastic", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a1b2204b06aa19d45a0338e9f0099ea7~tplv-obj.png"),
|
||||
|
||||
HEDGEHOG(6868, "Hedgehog", 299, "https://storage.streamdps.com/iblock/841/841e924150793d6961df0a1c89cc67ca/5886839b7de0b1289303081f9af380f8.webp"),
|
||||
|
||||
JAKARTA_ROUNDABOUT(6452, "Jakarta Roundabout", 16999, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/31f67910fc5858cf087da65746f1f9f3~tplv-obj.png"),
|
||||
|
||||
LIVE_FEST(9334, "LIVE Fest", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1e98afffef90ed4b2cc9c9ebb88e3608.png~tplv-obj.png"),
|
||||
|
||||
I_LOVE_TR(7139, "I LOVE TR", 1, "https://storage.streamdps.com/iblock/84d/84d68e92c471e7da792aa98d856c824c/7728ac60043efb9c96e2ce0f77dbef31.webp"),
|
||||
@@ -869,10 +989,16 @@ public enum Gift {
|
||||
|
||||
CRYSTAL_BALL(6428, "Crystal Ball", 1700, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7e4f9a99b7003ae05186f5324aae9fbf~tplv-obj.png"),
|
||||
|
||||
HAPPY_WEEKEND(8264, "Happy Weekend", 599, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b04f104e717798235cd3edaa6703e6a3~tplv-obj.png"),
|
||||
|
||||
PHOENIX(7319, "Phoenix", 25999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/ef248375c4167d70c1642731c732c982~tplv-obj.jpg"),
|
||||
|
||||
BOUQUET_FLOWER(5780, "Bouquet Flower", 30, "https://storage.streamdps.com/iblock/ceb/cebb5d5f7004d6ccf9336ae20281be88/5061b1767c2325fe6704eb08d97c5cb8.png"),
|
||||
|
||||
PUMPKIN_SPICE_LATTE(9242, "Pumpkin Spice Latte", 10, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/0636d91615f7417ddd5f29438bf5debe~tplv-obj.png"),
|
||||
|
||||
SHIBA_COOKIE(9770, "Shiba Cookie", 10, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4ea5282e7f61cbeee1214422d40ad407.png~tplv-obj.png"),
|
||||
|
||||
CHILI(7086, "Chili", 1, "https://storage.streamdps.com/iblock/4e4/4e476335c1d0a47efc33a40688d0fc75/ab85d5eccda4bf6509874c9533739b62.webp"),
|
||||
|
||||
GO_SNACKING(7021, "Go Snacking", 1, "https://storage.streamdps.com/iblock/666/6661d244aca6ec5f3de19372316e871e/f967ba18a333cd1489396cb608371824.webp"),
|
||||
@@ -883,6 +1009,8 @@ public enum Gift {
|
||||
|
||||
CHILL(6704, "Chill", 5, "https://storage.streamdps.com/iblock/7df/7dfcee6b2702691bf9c8ca0966b3c4b1/144aa8fff9ce8c64aa7fcb507bf6c1cd.webp"),
|
||||
|
||||
GINGERBREAD_MAN(9656, "Gingerbread man", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/af01db3e3cb9f54ea2cb421fab6062bc.png~tplv-obj.png"),
|
||||
|
||||
TAKE_A_DRIVE(7624, "Take a Drive", 1200, "https://storage.streamdps.com/iblock/fb5/fb50bcd0bb83c3a338d7d5196a7e987c/aa0e7affca4b6d34877244af7f5b611c.webp"),
|
||||
|
||||
TAKE_A_DRIVE_7631(7631, "Take a Drive", 1200, "https://storage.streamdps.com/iblock/c5b/c5b1ae3782864918bcb70d9e92046b87/8f3b4f952004f1aaef4bccfd69b19568.webp"),
|
||||
@@ -901,6 +1029,8 @@ public enum Gift {
|
||||
|
||||
BALALAIKA(5927, "Balalaika", 100, "https://storage.streamdps.com/iblock/d88/d88bc38371769262c006dccbaa43c9ff/1e0f5adda0546879e07126492ba6001c.webp"),
|
||||
|
||||
POPCORN(9111, "Popcorn", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/676d2d4c31a8979f1fd06cdf5ecd922f~tplv-obj.png"),
|
||||
|
||||
DRACO(8613, "Draco", 5000, "https://storage.streamdps.com/iblock/48f/48f1a8d280e271929718525560ad42a8/3021d84608e0c5da388f1f6534011a6f.webp"),
|
||||
|
||||
SPEEDSTER(8418, "Speedster", 15000, "https://storage.streamdps.com/iblock/96a/96a5a249a1701c3c03e0b2427bad3b2f/63fb5582c89c17f275fc99505505b719.webp"),
|
||||
@@ -941,8 +1071,6 @@ public enum Gift {
|
||||
|
||||
COOPER_SKATES_HOME(6865, "Cooper Skates Home", 599, "https://storage.streamdps.com/iblock/041/04184b09ec8e7bf137d33cf57ce4eec9/3c2e360b023b9980e54e9d9a394883b9.webp"),
|
||||
|
||||
EXCLUSIVE_JET(9523, "Exclusive Jet", 5000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1cc167a00aa4d5dfe48018afb38c3daa.png~tplv-obj.jpg"),
|
||||
|
||||
BIRTHDAY_PARTY(6787, "Birthday Party", 6999, "https://storage.streamdps.com/iblock/135/135b2ac0877de059f56e510b0ac70d08/07285ba7471fb98743bfe308d0b58ce2.webp"),
|
||||
|
||||
BIRTHDAY_PARTY_9095(9095, "Birthday Party", 6999, "https://storage.streamdps.com/iblock/d0d/d0d1164a9ed81239b70cb25b93927023/d0dba293643c67dc33c1f4dda04e5b50.webp"),
|
||||
@@ -963,10 +1091,14 @@ public enum Gift {
|
||||
|
||||
STAR_THRONE_8420(8420, "Star Throne", 7999, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/30063f6bc45aecc575c49ff3dbc33831~tplv-obj.jpg"),
|
||||
|
||||
MISTLETOE_GDM_23(7475, "Mistletoe GDM 23", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/3527969b8c27e3194e61ff0787a9c3c2~tplv-obj.png"),
|
||||
|
||||
PLANET(5954, "Planet", 15000, "https://storage.streamdps.com/iblock/dc5/dc50bbe9b153d9f714919d386325a223/b296c2101cb24bc65e8abd2977d6c123.png"),
|
||||
|
||||
DISCO_BALL(5540, "Disco Ball", 1000, "https://storage.streamdps.com/iblock/3e5/3e5e6d701c936bef5b85a0315b841184/e46e6c47d88c9bb81d27eb700456137a.webp"),
|
||||
|
||||
DISCO_BALL_8250(8250, "Disco ball", 1000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a53d3ef956eb2f1aa7a7db46024c70bb~tplv-obj.png"),
|
||||
|
||||
OUD(5301, "Oud", 300, "https://storage.streamdps.com/iblock/6ba/6ba340c152f9154c8d7c45d18bcb5914/6be86ee5d8a8ebaa17d93b766589b151.png"),
|
||||
|
||||
SUITCASE(8597, "Suitcase", 199, "https://storage.streamdps.com/iblock/50f/50f04937063753d6de255d2b5a080c1c/4f101c7c50ddbe8bd26a2ce5f8c16896.webp"),
|
||||
@@ -981,6 +1113,8 @@ public enum Gift {
|
||||
|
||||
DAFFODILS(6435, "Daffodils", 99, "https://storage.streamdps.com/iblock/2ed/2edf26fa90a7b3ca44c0d7c77a765c77/c333c68579488e9a36f4130481932b7c.png"),
|
||||
|
||||
MARACAS(7032, "Maracas", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/00204efcf0573192ad5d872c7beeaf5b~tplv-obj.png"),
|
||||
|
||||
TINY_DINY(6560, "Tiny Diny", 10, "https://storage.streamdps.com/iblock/504/50412c09b6d36020e28ee09ceb45f22b/aa96e43206d46ed5f25e8f476f67da45.png"),
|
||||
|
||||
TINY_DINY_7591(7591, "Tiny Diny", 10, "https://storage.streamdps.com/iblock/b24/b24309d4ea6722875678e492ae12fb3f/864ac7928a78b43be2d1ee93915a53f5.webp"),
|
||||
@@ -993,10 +1127,16 @@ public enum Gift {
|
||||
|
||||
TTEOKBOKKI(5645, "Tteokbokki", 5, "https://storage.streamdps.com/iblock/81c/81ca5954462f21f506095fe410dd2aaf/c07e9b0bb8d0559874b780495cc0e451.png"),
|
||||
|
||||
CRYSTAL_HEART(5559, "Crystal Heart", 499, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ae46ac6582a606009643440fe4138eb4.png~tplv-obj.png"),
|
||||
|
||||
LOLLIPOP(5657, "Lollipop", 10, "https://storage.streamdps.com/iblock/857/85755cf3d5e2e5349efff7eeedbfff46/b5e02fcff0a73b906d530028d460e59d.png"),
|
||||
|
||||
FLOWER_FESTIVAL(8442, "Flower Festival", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/9bfe63e39b581a69ff944758c3eae5a0~tplv-obj.png"),
|
||||
|
||||
GRAPES(7234, "Grapes", 1, "https://storage.streamdps.com/iblock/442/442580106ac8748b79ef450eb25b5981/df624c619c48b583adee184bca134c80.webp"),
|
||||
|
||||
TSAR(5524, "Tsar", 100, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/cb1c3e6263d4b6c08301f8798dcb5a9b.png~tplv-obj.png"),
|
||||
|
||||
LLAMA_GREETINGS(6531, "Llama Greetings", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a6b95ce6350f5f4bdff6880ac6993789~tplv-obj.png"),
|
||||
|
||||
SAM_THE_WHALE(8391, "Sam the Whale", 30000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/f48a1887eb88238738996bb997b31c0f.png~tplv-obj.jpg"),
|
||||
@@ -1017,6 +1157,8 @@ public enum Gift {
|
||||
|
||||
GOOD_NIGHT(8268, "Good Night", 399, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b7b55087141bd5f965eb31a99a5f157b~tplv-obj.png"),
|
||||
|
||||
TRAIN_TO_2024(9642, "Train to 2024", 12024, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/0cb12913e0a96bbcc45f97b450e74cd3.png~tplv-obj.jpg"),
|
||||
|
||||
ALIEN_PEACE_SIGN(7831, "Alien Peace Sign", 1, "https://storage.streamdps.com/iblock/5f7/5f7b29f5c7a4ca3a4dbbe8dc0e195459/cd83433a0f1697a0b66a891cbd7cf1af.webp"),
|
||||
|
||||
THE_MAGIC_LAMP(7161, "The Magic Lamp", 1000, "https://storage.streamdps.com/iblock/e0d/e0d45fccd69220f321531383d97f51fc/4296cc4b886f31bb5b2cf106ebf640ab.webp"),
|
||||
@@ -1025,6 +1167,8 @@ public enum Gift {
|
||||
|
||||
TIARA(8496, "Tiara", 299, "https://storage.streamdps.com/iblock/1b1/1b1ee7b697bae41ee2cbf834d1f1099e/303eec791a710c2417bb5075529681d9.webp"),
|
||||
|
||||
THE_CROWN(8207, "The Crown", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5bf798f92fe96ba53c0f4d28f052f9bb~tplv-obj.png"),
|
||||
|
||||
COFFEE(5333, "Coffee", 1, "https://storage.streamdps.com/iblock/920/920b64634d946a2238950c353c16df81/0fe22d9bdee1bd6d9d77f66bcd8cf45a.png"),
|
||||
|
||||
COFFEE_5479(5479, "Coffee", 1, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/02492214b9bd50fee2d69fd0d089c025.png~tplv-obj.jpg"),
|
||||
@@ -1033,6 +1177,8 @@ public enum Gift {
|
||||
|
||||
COFFEE_5961(5961, "Coffee", 30, "https://storage.streamdps.com/iblock/87b/87b2a811f1ca8c09060e2bd63a3b3be7/ae6288eb1eab67474807c64b9d69b5e6.webp"),
|
||||
|
||||
MAGIC_HAT(6393, "Magic Hat", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b156ffd21bb3849a52144ab1688bbc43~tplv-obj.png"),
|
||||
|
||||
HAND_HEART(5924, "Hand Heart", 100, "https://storage.streamdps.com/iblock/5ce/5cebff2a4b737063778ac5374e9e4792/f9bf5c945eb61002916feff420a1cc3a.png"),
|
||||
|
||||
HAND_HEART_6968(6968, "Hand Heart", 100, "https://storage.streamdps.com/iblock/9f0/9f0bfed08f1d3b9e852469d6a4debeda/519497b062ded1019c958d5d0b352a7e.webp"),
|
||||
@@ -1063,6 +1209,8 @@ public enum Gift {
|
||||
|
||||
TIKTOK_CROWN(8873, "TikTok Crown", 299, "https://storage.streamdps.com/iblock/a79/a790613bdf2e83725d0519bbf289529d/83bb670c15ab91b9192c50300f4c8054.webp"),
|
||||
|
||||
KFC_CHICKEN(9771, "KFC Chicken", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9d59ccd2328b8a46841b3b1c87d9e55.png~tplv-obj.png"),
|
||||
|
||||
LOVED(6705, "Loved", 5, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/2a41781b0a29ba3c409c5dd83eed07f8~tplv-obj.png"),
|
||||
|
||||
ON_FIRE(6840, "On Fire", 200, "https://storage.streamdps.com/iblock/cba/cba95075d6b63b84fbc52abb9d1d8208/d93ecc0b966bf972f01e77339a68e124.webp"),
|
||||
@@ -1081,22 +1229,38 @@ public enum Gift {
|
||||
|
||||
FERRIS_WHEEL(5652, "Ferris Wheel", 3000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/3c7291ad4c2a6d4f70505c3e296ecebe~tplv-obj.jpg"),
|
||||
|
||||
DE_NORTH_POLE(9658, "DE North Pole", 199, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/130e17b5b561a93cefbd236586881477.png~tplv-obj.png"),
|
||||
|
||||
RUGBY_BALL(6249, "Rugby Ball", 10, "https://storage.streamdps.com/iblock/d53/d53d3efae1c69b949373db455da077cf/fa22f0507a304597b013268524a8573f.png"),
|
||||
|
||||
TROPHY(5712, "Trophy", 500, "https://storage.streamdps.com/iblock/26f/26f17d2bc63c5e3f218ea2f25b245fa2/95d88e55486d8188f4b73c75def4354c.png"),
|
||||
|
||||
ACROSS_THE_BOARD(8793, "Across the board", 450, "https://storage.streamdps.com/iblock/285/285070af9d4f72b74e7d74c22157f2d9/67d9fa3239a7f9a09ef78c832a66e624.webp"),
|
||||
|
||||
GOOD_AFTERNOON(8266, "Good Afternoon", 399, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/bff3b908c4dd9cf19ab431cc99dc7940~tplv-obj.png"),
|
||||
|
||||
GARDEN_GNOME(7002, "Garden Gnome", 1, "https://storage.streamdps.com/iblock/c1e/c1efcf386f4ffc5626e0be1ef1ecd93a/210fa9d66c1f0c1968608b40c4e698ea.webp"),
|
||||
|
||||
MIMI___FIFI(9672, "Mimi & Fifi", 5000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/0a72d0084695d03586fea7d854dc3a47.png~tplv-obj.png"),
|
||||
|
||||
CANDY_CANE(9698, "Candy Cane", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1fa0a4ed666304c78a46de200b85c84b.png~tplv-obj.png"),
|
||||
|
||||
FAKE_SMILE(9536, "Fake smile", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/35ce62173962e33834703212d0b845a7.png~tplv-obj.png"),
|
||||
|
||||
SO_CUTE(9355, "So cute", 1, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/d40d31241efcf57c630e894bb3007b8a.png~tplv-obj.png"),
|
||||
|
||||
LILI_THE_LEOPARD(9467, "Lili the Leopard", 6599, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/7be03e1af477d1dbc6eb742d0c969372.png~tplv-obj.jpg"),
|
||||
|
||||
MUSIC_BOX(5964, "Music Box", 2399, "https://storage.streamdps.com/iblock/f01/f01f2da5e18be863eb7e3a1375bb6206/499e06f8f76e5e90964184c25365cdec.png"),
|
||||
|
||||
ELF_S_HAT_(9625, "Elf's Hat ", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9857a040c92b34d6a261201a93c185f.png~tplv-obj.png"),
|
||||
|
||||
PRINCE(7398, "Prince", 500, "https://storage.streamdps.com/iblock/38b/38b4963191222c66267858149e662b7d/d98b625b2a3a261d2c12caaae61b479f.webp"),
|
||||
|
||||
DASH(6757, "Dash", 299, "https://storage.streamdps.com/iblock/b35/b356ce71b1272dffc836a14df85700d2/16e177319d9f5cf312440139715612f5.webp"),
|
||||
|
||||
SNOWGLOBE(9688, "Snowglobe", 499, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/ea5ac5f8e186897456bed2e78fc78ca5.png~tplv-obj.png"),
|
||||
|
||||
BABY_FOX(8552, "Baby fox", 20, "https://storage.streamdps.com/iblock/b7d/b7df9be24aa630c9b04db3974f103a73/17a445fd5256e13281e64c718b5112fe.webp"),
|
||||
|
||||
RAYA_RICE(6383, "Raya Rice", 1, "https://storage.streamdps.com/iblock/e0c/e0c375df5bdce1c926f46244ced54ecc/1bd688843c1c24370b8c4a74686c2c0d.png"),
|
||||
@@ -1119,12 +1283,16 @@ public enum Gift {
|
||||
|
||||
GOLD_NECKLACE(5599, "Gold necklace", 200, "https://storage.streamdps.com/iblock/aa2/aa26035cd47797211a9ce1b5e51fd7ac/85e66a118c564c318e369974510f371d.png"),
|
||||
|
||||
HOLIDAY_CAROUSEL(9678, "Holiday Carousel", 2000, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/b5ba3941f7389da7495b659e888ea61a.png~tplv-obj.png"),
|
||||
|
||||
MPL_TROPHY(7119, "MPL Trophy", 450, "https://storage.streamdps.com/iblock/981/9816c81ee3e2fdc62ac221051e9ec290/75707f488be3f80faf5affd1cef38deb.webp"),
|
||||
|
||||
OCTOPUS(8417, "Octopus", 10000, "https://storage.streamdps.com/iblock/419/4197c396a3fcdd28f0477d9af50cd964/1196ad0f243ca976832319a46c7935ed.webp"),
|
||||
|
||||
PIM_BEAR(9043, "Pim Bear", 1500, "https://storage.streamdps.com/iblock/204/2043f85b8f2e2ee638ff3a1799eda329/2319b052e0e64799842751d9fee4d438.webp"),
|
||||
|
||||
FALLING_FOR_YOU(8005, "Falling For You", 299, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a198bd39d2511dbba6a68867740e3ff9~tplv-obj.png"),
|
||||
|
||||
SIGNATURE_JET(7124, "Signature Jet", 4888, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/fe27eba54a50c0a687e3dc0f2c02067d~tplv-obj.jpg"),
|
||||
|
||||
CAP(6104, "Cap", 99, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/6c2ab2da19249ea570a2ece5e3377f04~tplv-obj.jpg"),
|
||||
@@ -1141,6 +1309,8 @@ public enum Gift {
|
||||
|
||||
SUPERB_TEAM(8202, "Superb Team", 1, "https://storage.streamdps.com/iblock/e6e/e6e8cdd5e84f701dc627b8cc3e280d4c/29ce845878feb46152b20a75a3259d56.webp"),
|
||||
|
||||
MAGIC_POTION(7105, "Magic Potion", 499, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e055625e9239df7e833702c768e033d2~tplv-obj.png"),
|
||||
|
||||
GLOWING_JELLYFISH(8978, "Glowing Jellyfish", 1000, "https://storage.streamdps.com/iblock/e65/e65b1f71b4fe5709b454299439cb2674/36471857a2ba78694be934a54a0fa8d2.webp"),
|
||||
|
||||
RAINING_GIFTS(8769, "Raining gifts", 999, "https://storage.streamdps.com/iblock/916/91661303a8dc3660acaf2f4e47a94f75/221a1f185676496ebcdbaf55f90aeb70.webp"),
|
||||
@@ -1163,8 +1333,6 @@ public enum Gift {
|
||||
|
||||
CAKE_SLICE(6784, "Cake Slice", 1, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/f681afb4be36d8a321eac741d387f1e2~tplv-obj.jpg"),
|
||||
|
||||
EXCLUSIVE_SPARK(9522, "Exclusive Spark", 1000, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f0bda1eb6856e2feea9cfcb6c575c8a0.png~tplv-obj.jpg"),
|
||||
|
||||
CROCODILE(8740, "Crocodile", 10, "https://storage.streamdps.com/iblock/4e2/4e2d9df24c472158b8ed93546fc73b16/75722a173b75d601e0a80a679902529f.webp"),
|
||||
|
||||
LOVE_YOU_6671(6671, "Love You", 199, "https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/134e51c00f46e01976399883ca4e4798~tplv-obj.jpg"),
|
||||
@@ -1199,6 +1367,8 @@ public enum Gift {
|
||||
|
||||
HOT_AIR_BALLOON(5976, "Hot Air Balloon", 999, "https://storage.streamdps.com/iblock/33a/33a5eb58a8dd71677072c9482aad209a/61be5fe5d3d639e3729edbf003a536c7.png"),
|
||||
|
||||
RABBIT_AND_MOCHI(9303, "Rabbit and Mochi", 999, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/213ef2549fbb10ec783c95a41d28cf0a.png~tplv-obj.png"),
|
||||
|
||||
TEA(5303, "Tea", 50, "https://storage.streamdps.com/iblock/240/24051d7263606ed2b02f24f8455cb0a5/4f1cc7de604a1369c5770cc02cbee920.png"),
|
||||
|
||||
TEA_6726(6726, "Tea", 20, "https://storage.streamdps.com/iblock/b0b/b0ba111b6319a8c9e384d5ca7b814e4c/6cd6f620512cd42711bc1235124b3265.webp"),
|
||||
@@ -1211,6 +1381,8 @@ public enum Gift {
|
||||
|
||||
PIRATE_S_SHIP(7598, "Pirate’s Ship", 15000, "https://storage.streamdps.com/iblock/475/4753e54cae562b34edbf1a157cd60b21/722409ec69cfaf707d611b0987799296.webp"),
|
||||
|
||||
DOMBRA(6426, "Dombra", 20, "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ccd9fea1988521d1e81051a916800d6c~tplv-obj.png"),
|
||||
|
||||
GRIFFIN(7987, "Griffin", 25999, "https://storage.streamdps.com/iblock/609/6092240118fdb3ad46036c0533dd23c8/0f700fd4e875174d01ad04a8db2ae94e.webp"),
|
||||
|
||||
BEACH_HUT(6719, "Beach Hut", 5000, "https://storage.streamdps.com/iblock/227/22716035cef6112f66035eca2b60fa31/ffce7cd46aaa6c2b27ccba610cf35a39.webp"),
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.data.models.http;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -43,13 +43,12 @@ public class User {
|
||||
private long followers;
|
||||
private List<Badge> badges;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Set<UserAttribute> attributes;
|
||||
|
||||
|
||||
private final Set<UserAttribute> attributes = new HashSet<>();
|
||||
|
||||
public List<UserAttribute> getAttributes() {
|
||||
return attributes.stream().toList();
|
||||
}
|
||||
|
||||
public boolean hasAttribute(UserAttribute userFlag) {
|
||||
return attributes.contains(userFlag);
|
||||
}
|
||||
@@ -106,7 +105,6 @@ public class User {
|
||||
this.following = following;
|
||||
this.followers = followers;
|
||||
this.badges = badges;
|
||||
this.attributes = new HashSet<>();
|
||||
}
|
||||
|
||||
public User(Long id,
|
||||
@@ -123,14 +121,12 @@ public class User {
|
||||
this.following = following;
|
||||
this.followers = followers;
|
||||
this.badges = badges;
|
||||
this.attributes = new HashSet<>();
|
||||
}
|
||||
|
||||
public User(Long userId,
|
||||
String nickName) {
|
||||
this.id = userId;
|
||||
this.name = nickName;
|
||||
this.attributes = new HashSet<>();
|
||||
}
|
||||
|
||||
public User(Long userId,
|
||||
@@ -213,4 +209,4 @@ public class User {
|
||||
0,
|
||||
List.of(Badge.empty()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,17 +20,38 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.live;
|
||||
package io.github.jwdeveloper.tiktok.data.requests;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TrackedUser
|
||||
public class GiftsData
|
||||
{
|
||||
List<TikTokEvent> getInvokedEvents();
|
||||
List<Gift> getGifs();
|
||||
User getUserData();
|
||||
@Getter
|
||||
public final class Request
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static final class Response
|
||||
{
|
||||
private String json;
|
||||
private List<GiftModel> gifts;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class GiftModel
|
||||
{
|
||||
private int id;
|
||||
private String name;
|
||||
private int diamondCost;
|
||||
private String image;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.requests;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
public class LiveConnectionData {
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Request {
|
||||
private String roomId;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Response {
|
||||
private String websocketCookies;
|
||||
private URI websocketUrl;
|
||||
private WebcastResponse webcastResponse;
|
||||
}
|
||||
}
|
||||
@@ -20,26 +20,35 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.live;
|
||||
package io.github.jwdeveloper.tiktok.data.requests;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
@Data
|
||||
public class LiveRoomMeta {
|
||||
public class LiveData {
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Request {
|
||||
private String roomId;
|
||||
}
|
||||
|
||||
private LiveRoomStatus status;
|
||||
private boolean ageRestricted;
|
||||
private String titie;
|
||||
private int likeCount;
|
||||
private int totalViewers;
|
||||
private int viewers;
|
||||
private User host;
|
||||
@Data
|
||||
public static class Response {
|
||||
private String json;
|
||||
private LiveStatus liveStatus;
|
||||
private String title;
|
||||
private int likes;
|
||||
private int viewers;
|
||||
private int totalViewers;
|
||||
private boolean ageRestricted;
|
||||
private User host;
|
||||
}
|
||||
|
||||
public enum LiveRoomStatus
|
||||
{
|
||||
public enum LiveStatus {
|
||||
HostNotFound,
|
||||
HostOnline,
|
||||
HostOffline
|
||||
HostOffline,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.requests;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class LiveUserData {
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Request {
|
||||
private String userName;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Response {
|
||||
|
||||
private String json;
|
||||
|
||||
private UserStatus userStatus;
|
||||
|
||||
private String roomId;
|
||||
|
||||
|
||||
private long startedAtTimeStamp;
|
||||
}
|
||||
|
||||
public enum UserStatus {
|
||||
NotFound,
|
||||
Offline,
|
||||
LivePaused,
|
||||
Live,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,24 +20,16 @@
|
||||
* 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.dto;
|
||||
package io.github.jwdeveloper.tiktok.data.requests;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class TikTokUserInfo
|
||||
public class SignServerResponse
|
||||
{
|
||||
UserStatus userStatus;
|
||||
private String signedUrl;
|
||||
|
||||
String roomId;
|
||||
|
||||
public enum UserStatus
|
||||
{
|
||||
NotFound,
|
||||
Offline,
|
||||
LivePaused,
|
||||
Live
|
||||
}
|
||||
private String userAgent;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
public class HttpClientSettings {
|
||||
|
||||
@Getter
|
||||
final Map<String, Object> params;
|
||||
|
||||
@Getter
|
||||
final Map<String, String> headers;
|
||||
|
||||
@Getter
|
||||
final Map<String, String> cookies;
|
||||
|
||||
@Getter
|
||||
ProxyClientSettings proxyClientSettings;
|
||||
|
||||
@Getter
|
||||
Consumer<HttpClient.Builder> onClientCreating;
|
||||
|
||||
@Getter
|
||||
Consumer<HttpRequest.Builder> onRequestCreating;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
Duration timeout;
|
||||
|
||||
public HttpClientSettings() {
|
||||
this.params = new TreeMap<>();
|
||||
this.headers = new HashMap<>();
|
||||
this.cookies = new HashMap<>();
|
||||
this.timeout = Duration.ofSeconds(2);
|
||||
this.proxyClientSettings = new ProxyClientSettings();
|
||||
this.onClientCreating = (x) -> {
|
||||
};
|
||||
this.onRequestCreating = (x) -> {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param consumer Use to configure proxy settings for http client
|
||||
*/
|
||||
public void configureProxy(Consumer<ProxyClientSettings> consumer) {
|
||||
proxyClientSettings.setUseProxy(true);
|
||||
consumer.accept(proxyClientSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onRequestCreating Every time new Http request in created this method will be triggered
|
||||
* use to modify request
|
||||
*/
|
||||
public void onRequestCreating(Consumer<HttpRequest.Builder> onRequestCreating) {
|
||||
this.onRequestCreating = onRequestCreating;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onClientCreating Every time new instance of Http client request in created this method will be triggered
|
||||
* use to modify http client
|
||||
*/
|
||||
public void onClientCreating(Consumer<HttpClient.Builder> onClientCreating) {
|
||||
this.onClientCreating = onClientCreating;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClientSettings clone() {
|
||||
|
||||
var newSettings = new HttpClientSettings();
|
||||
newSettings.setTimeout(this.getTimeout());
|
||||
newSettings.onRequestCreating(this.onRequestCreating);
|
||||
newSettings.onClientCreating(this.onClientCreating);
|
||||
newSettings.getHeaders().putAll(new TreeMap<>(this.headers));
|
||||
newSettings.getCookies().putAll(new TreeMap<>(this.cookies));
|
||||
newSettings.getParams().putAll(new TreeMap<>(this.params));
|
||||
newSettings.proxyClientSettings = this.proxyClientSettings.clone();
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,9 @@
|
||||
* 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;
|
||||
package io.github.jwdeveloper.tiktok.data.settings;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
@@ -28,40 +30,81 @@ import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Constants {
|
||||
@Data
|
||||
public class LiveClientSettings {
|
||||
|
||||
/**
|
||||
* Web-URL for TikTok
|
||||
* ISO-Language for Client
|
||||
*/
|
||||
public static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
|
||||
/**
|
||||
* WebCast-BaseURL for TikTok
|
||||
*/
|
||||
public static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
|
||||
/**
|
||||
* Signing API by Isaac Kogan
|
||||
* https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures
|
||||
*/
|
||||
public static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url";
|
||||
|
||||
private String clientLanguage;
|
||||
|
||||
/**
|
||||
* Default TimeOut for Connections
|
||||
* Whether to Retry if Connection Fails
|
||||
*/
|
||||
public static final int DEFAULT_TIMEOUT = 20;
|
||||
private boolean retryOnConnectionFailure;
|
||||
|
||||
|
||||
/**
|
||||
* Default Settings for Client
|
||||
* Before retrying connect, wait for select amount of time
|
||||
*/
|
||||
public static ClientSettings DefaultClientSettings() {
|
||||
var clientSettings = new ClientSettings();
|
||||
clientSettings.setTimeout(Duration.ofSeconds(DEFAULT_TIMEOUT));
|
||||
private Duration retryConnectionTimeout;
|
||||
|
||||
/**
|
||||
* Whether to print Logs to Console
|
||||
*/
|
||||
|
||||
private boolean printToConsole = true;
|
||||
/**
|
||||
* LoggingLevel for Logs
|
||||
*/
|
||||
private Level logLevel;
|
||||
|
||||
|
||||
/**
|
||||
* Optional: Use it if you need to change TikTok live hostname in builder
|
||||
*/
|
||||
private String hostName;
|
||||
|
||||
|
||||
/**
|
||||
* Parameters used in requests to TikTok api
|
||||
*/
|
||||
private HttpClientSettings httpSettings;
|
||||
|
||||
|
||||
/*
|
||||
* Optional: Sometimes not every messages 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
|
||||
*/
|
||||
private String sessionId;
|
||||
|
||||
/*
|
||||
* Optional: By default roomID is fetched before connect to live, but you can set it manually
|
||||
*
|
||||
*/
|
||||
private String roomId;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static LiveClientSettings createDefault()
|
||||
{
|
||||
var httpSettings = new HttpClientSettings();
|
||||
httpSettings.getParams().putAll(DefaultClientParams());
|
||||
httpSettings.getHeaders().putAll(DefaultRequestHeaders());
|
||||
httpSettings.setTimeout(Duration.ofSeconds(3));
|
||||
|
||||
var clientSettings = new LiveClientSettings();
|
||||
clientSettings.setClientLanguage("en-US");
|
||||
clientSettings.setRetryOnConnectionFailure(false);
|
||||
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1));
|
||||
clientSettings.setPrintToConsole(false);
|
||||
clientSettings.setLogLevel(Level.ALL);
|
||||
clientSettings.setClientParameters(Constants.DefaultClientParams());
|
||||
|
||||
|
||||
clientSettings.setHttpSettings(httpSettings);
|
||||
return clientSettings;
|
||||
}
|
||||
|
||||
@@ -115,15 +158,15 @@ public class Constants {
|
||||
public static Map<String, String> DefaultRequestHeaders() {
|
||||
var headers = new HashMap<String, String>();
|
||||
|
||||
headers.put("authority","www.tiktok.com");
|
||||
headers.put("Connection", "keep-alive");
|
||||
headers.put("authority", "www.tiktok.com");
|
||||
headers.put("Cache-Control", "max-age=0");
|
||||
headers.put("Accept", "text/html,application/json,application/protobuf");
|
||||
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36");
|
||||
headers.put("Referer", "https://www.tiktok.com/");
|
||||
headers.put("Origin", "https://www.tiktok.com");
|
||||
headers.put("Accept-Language", "en-US,en; q=0.9");
|
||||
headers.put("Accept-Encoding", "gzip, deflate");
|
||||
return headers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,17 +20,21 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.live;
|
||||
package io.github.jwdeveloper.tiktok.data.settings;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserManager
|
||||
//TODO proxy implementation
|
||||
@Getter
|
||||
public class ProxyClientSettings
|
||||
{
|
||||
TrackedUser observeUser(User user);
|
||||
@Setter
|
||||
private boolean useProxy;
|
||||
|
||||
TrackedUser getTrackedUser(Long id);
|
||||
|
||||
List<TrackedUser> getTrackedUsers();
|
||||
public ProxyClientSettings clone()
|
||||
{
|
||||
return new ProxyClientSettings();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.exceptions;
|
||||
|
||||
public class TikTokSignServerException extends TikTokLiveRequestException
|
||||
{
|
||||
public TikTokSignServerException() {
|
||||
}
|
||||
|
||||
public TikTokSignServerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TikTokSignServerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TikTokSignServerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public TikTokSignServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.requests.GiftsData;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
||||
|
||||
public interface LiveHttpClient {
|
||||
|
||||
|
||||
/**
|
||||
* @return list of gifts that are available in your country
|
||||
*/
|
||||
GiftsData.Response fetchGiftsData();
|
||||
|
||||
/**
|
||||
* Returns information about user that is having a livestream
|
||||
*
|
||||
* @param userName
|
||||
* @return
|
||||
*/
|
||||
LiveUserData.Response fetchLiveUserData(String userName);
|
||||
|
||||
LiveUserData.Response fetchLiveUserData(LiveUserData.Request request);
|
||||
|
||||
/**
|
||||
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
||||
* @return
|
||||
*/
|
||||
LiveData.Response fetchLiveData(String roomId);
|
||||
|
||||
LiveData.Response fetchLiveData(LiveData.Request request);
|
||||
|
||||
|
||||
/**
|
||||
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
||||
* @return
|
||||
*/
|
||||
LiveConnectionData.Response fetchLiveConnectionData(String roomId);
|
||||
|
||||
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public interface LiveClient {
|
||||
|
||||
|
||||
/**
|
||||
* You to manually trigger event
|
||||
* Use to manually invoke event
|
||||
*/
|
||||
void publishEvent(TikTokEvent event);
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public interface LiveRoomInfo
|
||||
*/
|
||||
int getTotalViewersCount();
|
||||
int getLikesCount();
|
||||
long getStartTime();
|
||||
boolean isAgeRestricted();
|
||||
String getRoomId();
|
||||
String getHostName();
|
||||
@@ -49,4 +50,4 @@ public interface LiveRoomInfo
|
||||
User getHostUser();
|
||||
List<RankingUser> getUsersRanking();
|
||||
ConnectionState getConnectionState();
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,13 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.live.builder;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.mappers.TikTokMapper;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
|
||||
|
||||
@@ -48,11 +46,11 @@ public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
|
||||
|
||||
/**
|
||||
* Configuration of client settings
|
||||
* @see ClientSettings
|
||||
* @see LiveClientSettings
|
||||
* @param onConfigure
|
||||
* @return
|
||||
*/
|
||||
LiveClientBuilder configure(Consumer<ClientSettings> onConfigure);
|
||||
LiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure);
|
||||
|
||||
/**
|
||||
* @see TikTokEventListener
|
||||
|
||||
@@ -47,6 +47,9 @@ public interface TikTokMapper {
|
||||
|
||||
TikTokMapperModel forMessage(Class<? extends GeneratedMessageV3> mapperName, Function<byte[], TikTokEvent> onMapping);
|
||||
|
||||
TikTokMapperModel forAnyMessage();
|
||||
|
||||
|
||||
boolean isRegistered(String mapperName);
|
||||
|
||||
<T extends GeneratedMessageV3> boolean isRegistered(Class<T> mapperName);
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers;
|
||||
|
||||
import com.google.protobuf.GeneratedMessageV3;
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers.data;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers.data;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.mappers.TikTokMapperHelper;
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers.data;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
|
||||
@@ -36,6 +36,11 @@ public class ProtoBufferObject {
|
||||
this.fields = new TreeMap<>();
|
||||
}
|
||||
|
||||
public Object getField(int index)
|
||||
{
|
||||
return fields.get(index);
|
||||
}
|
||||
|
||||
public void addField(int index, String type, Object value) {
|
||||
fields.put(index, new ProtoBufferField(type, value));
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.websocket;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
public interface SocketClient {
|
||||
void start(WebcastResponse webcastResponse, LiveClient tikTokLiveClient);
|
||||
|
||||
void start(LiveConnectionData.Response webcastResponse, LiveClient tikTokLiveClient);
|
||||
void stop();
|
||||
}
|
||||
|
||||
@@ -976,131 +976,146 @@ message FanTicketRoomNoticeContent {
|
||||
}
|
||||
|
||||
message LinkerAcceptNoticeContent {
|
||||
int64 FromUserId = 1;
|
||||
int64 FromRoomId = 2;
|
||||
int64 ToUserId = 3;
|
||||
int64 fromUserId = 1;
|
||||
int64 fromRoomId = 2;
|
||||
int64 toUserId = 3;
|
||||
}
|
||||
|
||||
message LinkerCancelContent {
|
||||
int64 FromUserId = 1;
|
||||
int64 ToUserId = 2;
|
||||
int64 CancelType = 3;
|
||||
int64 ActionId = 4;
|
||||
int64 fromUserId = 1;
|
||||
int64 toUserId = 2;
|
||||
int64 cancelType = 3;
|
||||
int64 actionId = 4;
|
||||
}
|
||||
|
||||
message ListUser {
|
||||
User user = 1;
|
||||
int64 linkmicId = 2;
|
||||
string linkmicIdStr = 3;
|
||||
int64 linkStatus = 4; // Enum
|
||||
LinkType linkType = 5; // Enum
|
||||
int32 userPosition = 6;
|
||||
int32 silenceStatus = 7; // Enum
|
||||
int64 modifyTime = 8;
|
||||
int64 linkerId = 9;
|
||||
int32 roleType = 10; // Enum
|
||||
|
||||
enum LinkType {
|
||||
LINK_UNKNOWN = 0;
|
||||
AUDIO = 1;
|
||||
VIDEO = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//it is just empty
|
||||
message LinkerCloseContent {
|
||||
|
||||
}
|
||||
|
||||
message LinkerCreateContent {
|
||||
int64 OwnerId = 1;
|
||||
int64 OwnerRoomId = 2;
|
||||
int64 LinkType = 3;
|
||||
int64 ownerId = 1;
|
||||
int64 ownerRoomId = 2;
|
||||
int64 linkType = 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
message LinkerEnterContent {
|
||||
repeated User LinkedUsersList = 1;
|
||||
// LinkmicMultiLiveEnum AnchorMultiLiveEnum = 2;
|
||||
// Data.LinkmicUserSettingInfo AnchorSettingInfo = 3;
|
||||
repeated ListUser linkedUsersList = 1;
|
||||
int32 anchorMultiLiveEnum = 2; // Enum
|
||||
LinkmicUserSettingInfo anchorSettingInfo = 3;;
|
||||
}
|
||||
|
||||
message LinkerInviteContent {
|
||||
int64 FromUserId = 1;
|
||||
int64 FromRoomId = 2;
|
||||
string ToRtcExtInfo = 3;
|
||||
bool RtcJoinChannel = 4;
|
||||
int64 Vendor = 5;
|
||||
string SecFromUserId = 6;
|
||||
string ToLinkmicIdStr = 7;
|
||||
User FromUser = 8;
|
||||
int64 RequiredMicIdx = 9;
|
||||
map<int64, string> RtcExtInfoMap = 10;
|
||||
//Data.LinkmicMultiLiveEnum AnchorMultiLiveEnum = 11;
|
||||
//Data.LinkmicUserSettingInfo AnchorSettingInfo = 12;
|
||||
string InviterLinkmicIdStr = 13;
|
||||
// InviteTopHostInfo FromTopHostInfo = 16;
|
||||
int64 ActionId = 17;
|
||||
// repeated LinkmicUserInfo LinkedUsersList = 18;
|
||||
// Data.PerceptionDialogInfo Dialog = 19;
|
||||
// Data.PunishEventInfo PunishInfo = 20;
|
||||
int32 FromRoomAgeRestricted = 21;
|
||||
// Data.Tag FromTag = 22;
|
||||
// repeated Data.CohostABTestSetting AbTestSettingList = 23;
|
||||
// Data.LinkerInviteMessageExtra LinkerInviteMsgExtra = 101;
|
||||
int64 fromUserId = 1;
|
||||
int64 fromRoomId = 2;
|
||||
string toRtcExtInfo = 3;
|
||||
bool rtcJoinChannel = 4;
|
||||
int64 vendor = 5;
|
||||
string secFromUserId = 6;
|
||||
string toLinkmicIdStr = 7;
|
||||
User fromUser = 8;
|
||||
int64 requiredMicIdx = 9;
|
||||
}
|
||||
|
||||
message LinkerKickOutContent {
|
||||
int64 FromUserId = 1;
|
||||
//LinkMic.KickoutReason KickoutReason = 2;
|
||||
int64 fromUserId = 1;
|
||||
KickoutReason kickoutReason = 2; // Enum
|
||||
}
|
||||
|
||||
message LinkerLeaveContent {
|
||||
int64 UserId = 1;
|
||||
string LinkmicIdStr = 2;
|
||||
int64 SendLeaveUid = 3;
|
||||
int64 LeaveReason = 4;
|
||||
int64 userId = 1;
|
||||
string linkmicIdStr = 2;
|
||||
int64 sendLeaveUid = 3;
|
||||
int64 leaveReason = 4;
|
||||
}
|
||||
|
||||
//Empty
|
||||
message LinkerLinkedListChangeContent {
|
||||
repeated User LinkedUsersList = 1;
|
||||
|
||||
}
|
||||
//Empty
|
||||
message CohostListChangeContent {
|
||||
}
|
||||
|
||||
|
||||
message LinkerListChangeContent {
|
||||
repeated LinkLayerListUser LinkedUsersList = 1;
|
||||
repeated LinkLayerListUser AppliedUsersList = 2;
|
||||
repeated LinkLayerListUser ConnectingUsersList = 3;
|
||||
repeated ListUser linkedUsersList = 1;
|
||||
repeated ListUser appliedUsersList = 2;
|
||||
repeated ListUser connectingUsersList = 3;
|
||||
}
|
||||
|
||||
message LinkerMediaChangeContent {
|
||||
// MicIdxOperation Op = 1;
|
||||
int64 ToUserId = 2;
|
||||
int64 AnchorId = 3;
|
||||
int64 RoomId = 4;
|
||||
// LinkerSceneType ChangeScene = 5;
|
||||
int64 op = 1; // Enum
|
||||
int64 toUserId = 2;
|
||||
int64 anchorId = 3;
|
||||
int64 roomId = 4;
|
||||
int64 changeScene = 5; // Enum
|
||||
}
|
||||
|
||||
//Empty
|
||||
message LinkerMicIdxUpdateContent {
|
||||
LinkerMicIdxUpdateInfo MicIdxUpdateInfo = 1;
|
||||
|
||||
}
|
||||
|
||||
message LinkerMicIdxUpdateInfo {
|
||||
// MicIdxOperation Op = 1;
|
||||
int64 UserId = 2;
|
||||
int64 MicIdx = 3;
|
||||
}
|
||||
|
||||
message LinkerMuteContent {
|
||||
int64 UserId = 1;
|
||||
// Data.MuteStatus Status = 2;
|
||||
int64 userId = 1;
|
||||
int64 status = 2; // Enum
|
||||
}
|
||||
|
||||
message LinkerRandomMatchContent {
|
||||
User User = 1;
|
||||
int64 RoomId = 2;
|
||||
int64 InviteType = 3;
|
||||
string MatchId = 4;
|
||||
int64 InnerChannelId = 5;
|
||||
User user = 1;
|
||||
int64 roomId = 2;
|
||||
int64 inviteType = 3;
|
||||
string matchId = 4;
|
||||
int64 innerChannelId = 5;
|
||||
}
|
||||
|
||||
message LinkerReplyContent {
|
||||
int64 FromUserId = 1;
|
||||
int64 FromRoomId = 2;
|
||||
// LinkmicInfo FromUserLinkmicInfo = 3;
|
||||
int64 ToUserId = 4;
|
||||
// LinkmicInfo ToUserLinkmicInfo = 5;
|
||||
int64 LinkType = 6;
|
||||
int64 ReplyStatus = 7;
|
||||
LinkerSetting LinkerSetting = 8;
|
||||
User FromUser = 9;
|
||||
User ToUser = 10;
|
||||
map<int64, string> RtcExtInfoMap = 11;
|
||||
LinkerMicIdxUpdateInfo InviteeMicIdxUpdateInfo = 12;
|
||||
map<int64, int64> ApplierMicIdxInfoMap = 13;
|
||||
// Data.LinkmicMultiLiveEnum AnchorMultiLiveEnum = 14;
|
||||
// Data.LinkmicUserSettingInfo AnchorSettingInfo = 15;
|
||||
int64 ActionId = 16;
|
||||
// repeated LinkmicUserInfo LinkedUsersList = 17;
|
||||
int64 SourceType = 18;
|
||||
int64 fromUserId = 1;
|
||||
int64 fromRoomId = 2;
|
||||
LinkmicInfo fromUserLinkmicInfo = 3;
|
||||
int64 toUserId = 4;
|
||||
LinkmicInfo toUserLinkmicInfo = 5;
|
||||
int64 linkType = 6;
|
||||
int64 replyStatus = 7;
|
||||
LinkerSetting linkerSetting = 8;
|
||||
User fromUser = 9;
|
||||
User toUser = 10;
|
||||
|
||||
message LinkmicInfo {
|
||||
string accessKey = 1;
|
||||
int64 linkMicId = 2;
|
||||
bool joinable = 3;
|
||||
int32 confluenceType = 4;
|
||||
string rtcExtInfo = 5;
|
||||
string rtcAppId = 6;
|
||||
string rtcAppSign = 7;
|
||||
string linkmicIdStr = 8;
|
||||
int64 vendor = 9;
|
||||
}
|
||||
}
|
||||
|
||||
message LinkerSetting {
|
||||
@@ -1113,20 +1128,27 @@ message LinkerSetting {
|
||||
}
|
||||
|
||||
message LinkerSysKickOutContent {
|
||||
int64 UserId = 1;
|
||||
string LinkmicIdStr = 2;
|
||||
int64 userId = 1;
|
||||
string linkmicIdStr = 2;
|
||||
}
|
||||
|
||||
message LinkmicUserToastContent {
|
||||
int64 userId = 1;
|
||||
int64 roomId = 2;
|
||||
Text displayText = 3;
|
||||
}
|
||||
|
||||
message LinkerUpdateUserContent {
|
||||
int64 FromUserId = 1;
|
||||
int64 ToUserId = 2;
|
||||
map<string, string> UpdateInfoMap = 3;
|
||||
int64 fromUserId = 1;
|
||||
int64 toUserId = 2;
|
||||
}
|
||||
|
||||
//Empty
|
||||
message LinkerUpdateUserSettingContent {
|
||||
// Data.LinkmicUserSettingInfo UpdateUserSettingInfo = 1;
|
||||
|
||||
}
|
||||
|
||||
//Empty
|
||||
message LinkerWaitingListChangeContent {
|
||||
|
||||
}
|
||||
|
||||
@@ -241,6 +241,35 @@ enum CommonContentCase {
|
||||
}
|
||||
|
||||
|
||||
enum LinkMessageType {
|
||||
TPYE_LINKER_UNKNOWN = 0;
|
||||
TYPE_LINKER_CREATE = 1;
|
||||
TYPE_LINKER_CLOSE = 2;
|
||||
TYPE_LINKER_INVITE = 3;
|
||||
TYPE_LINKER_APPLY = 4;
|
||||
TYPE_LINKER_REPLY = 5;
|
||||
TPYE_LINKER_ENTER = 6;
|
||||
TPYE_LINKER_LEAVE = 7;
|
||||
TYPE_LINKER_PERMIT = 8;
|
||||
TPYE_LINKER_CANCEL_INVITE = 9;
|
||||
TYPE_LINKER_WAITING_LIST_CHANGE = 10;
|
||||
TYPE_LINKER_LINKED_LIST_CHANGE = 11;
|
||||
TYPE_LINKER_UPDATE_USER = 12;
|
||||
TPYE_LINKER_KICK_OUT = 13;
|
||||
TPYE_LINKER_CANCEL_APPLY = 14;
|
||||
TYPE_LINKER_MUTE = 15;
|
||||
TYPE_LINKER_MATCH = 16;
|
||||
TYPE_LINKER_UPDATE_USER_SETTING = 17;
|
||||
TYPE_LINKER_MIC_IDX_UPDATE = 18;
|
||||
TYPE_LINKER_LEAVE_V2 = 19;
|
||||
TYPE_LINKER_WAITING_LIST_CHANGE_V2 = 20;
|
||||
TYPE_LINKER_LINKED_LIST_CHANGE_V2 = 21;
|
||||
TYPE_LINKER_COHOST_LIST_CHANGE = 22;
|
||||
TYPE_LINKER_MEDIA_CHANGE = 23;
|
||||
TYPE_LINKER_ACCEPT_NOTICE = 24;
|
||||
TPYE_LINKER_SYS_KICK_OUT = 101;
|
||||
TPYE_LINKMIC_USER_TOAST = 102;
|
||||
}
|
||||
|
||||
enum MessageType {
|
||||
MESSAGETYPE_SUBSUCCESS = 0;
|
||||
|
||||
@@ -18,17 +18,13 @@ message WebcastPushFrame {
|
||||
uint64 LogId = 2;
|
||||
uint64 Service = 3;
|
||||
uint64 Method = 4;
|
||||
map<string,string> headers = 5;
|
||||
map<string, string> headers = 5;
|
||||
string PayloadEncoding = 6;
|
||||
string PayloadType = 7;
|
||||
bytes Payload = 8;
|
||||
|
||||
}
|
||||
|
||||
message WebcastWebsocketAck {
|
||||
uint64 Id = 1;
|
||||
string Type = 2;
|
||||
}
|
||||
|
||||
|
||||
//@WebcastResponse
|
||||
@@ -68,6 +64,7 @@ message WebcastGiftMessage {
|
||||
int32 repeatCount = 5;
|
||||
int32 comboCount = 6;
|
||||
User user = 7;
|
||||
User toUser = 8;
|
||||
int32 repeatEnd = 9;
|
||||
int64 groupId = 11;
|
||||
int64 incomeTaskgifts = 12;
|
||||
@@ -84,8 +81,8 @@ message WebcastGiftMessage {
|
||||
|
||||
message UserGiftReciever
|
||||
{
|
||||
int64 userId =1;
|
||||
string deviceName = 10;
|
||||
int64 userId = 1;
|
||||
string deviceName = 10;
|
||||
}
|
||||
|
||||
message GiftIMPriority {
|
||||
@@ -203,7 +200,7 @@ message WebcastChatMessage {
|
||||
int32 quickChatScene = 16;
|
||||
int32 communityFlaggedStatus = 17;
|
||||
UserIdentity UserIdentity = 18;
|
||||
map<int32,string> CommentQualityScores = 19;
|
||||
map<int32, string> CommentQualityScores = 19;
|
||||
|
||||
// @EmoteWithIndex
|
||||
// proto.webcast.im.ChatMessage
|
||||
@@ -283,14 +280,14 @@ message WebcastGoalUpdateMessage {
|
||||
int64 contributorId = 4;
|
||||
Image contributorAvatar = 5;
|
||||
string contributorDisplayId = 6;
|
||||
// SubGoal contributeSubgoal = 7;
|
||||
// SubGoal contributeSubgoal = 7;
|
||||
int64 contributeCount = 9;
|
||||
int64 contributeScore = 10;
|
||||
int64 giftRepeatCount = 11;
|
||||
string contributorIdStr = 12;
|
||||
bool pin = 13;
|
||||
bool unpin = 14;
|
||||
// GoalPinInfo pinInfo = 15;
|
||||
// GoalPinInfo pinInfo = 15;
|
||||
}
|
||||
|
||||
// Message related to Chat-moderation?
|
||||
@@ -354,7 +351,7 @@ message WebcastSocialMessage {
|
||||
message WebcastSubNotifyMessage {
|
||||
Common common = 1;
|
||||
User user = 2;
|
||||
// ExhibitionType exhibitionType = 3; // Enum
|
||||
// ExhibitionType exhibitionType = 3; // Enum
|
||||
int64 subMonth = 4;
|
||||
SubscribeType subscribeType = 5; // Enum
|
||||
OldSubscribeStatus oldSubscribeStatus = 6; // Enum
|
||||
@@ -681,7 +678,7 @@ message WebcastMsgDetectMessage {
|
||||
bool detectP2PMsg = 3;
|
||||
bool detectRoomMsg = 4;
|
||||
bool httpOptimize = 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@WebcastOecLiveShoppingMessage
|
||||
@@ -732,7 +729,7 @@ message WebcastSystemMessage {
|
||||
//@WebcastLinkMessage
|
||||
message WebcastLinkMessage {
|
||||
Common common = 1;
|
||||
MessageType MessageType = 2;
|
||||
LinkMessageType MessageType = 2;
|
||||
int64 LinkerId = 3;
|
||||
Scene Scene = 4;
|
||||
LinkerInviteContent InviteContent = 5;
|
||||
@@ -751,14 +748,14 @@ message WebcastLinkMessage {
|
||||
LinkerUpdateUserSettingContent UpdateUserSettingContent = 18;
|
||||
LinkerMicIdxUpdateContent MicIdxUpdateContent = 19;
|
||||
LinkerListChangeContent ListChangeContent = 20;
|
||||
// CohostListChangeContent CohostListChangeContent = 21;
|
||||
CohostListChangeContent CohostListChangeContent = 21;
|
||||
LinkerMediaChangeContent MediaChangeContent = 22;
|
||||
LinkerAcceptNoticeContent ReplyAcceptNoticeContent = 23;
|
||||
LinkerSysKickOutContent SysKickOutContent = 101;
|
||||
// LinkmicUserToastContent UserToastContent = 102;
|
||||
string Extra = 200;
|
||||
int64 ExpireTimestamp = 201;
|
||||
string TransferExtra = 202;
|
||||
LinkmicUserToastContent UserToastContent = 102;
|
||||
string extra = 200;
|
||||
int64 expireTimestamp = 201;
|
||||
string transferExtra = 202;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.0.9-Release</version>
|
||||
<version>1.0.14-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -23,69 +23,30 @@
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.http.TikTokDataChecker;
|
||||
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class TikTokLive
|
||||
{
|
||||
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
|
||||
* example: https://www.tiktok.com/@dostawcavideo hostName would be dostawcavideo
|
||||
* @return LiveClientBuilder
|
||||
*/
|
||||
public static LiveClientBuilder newClient(String hostName)
|
||||
{
|
||||
public static LiveClientBuilder newClient(String hostName) {
|
||||
return new TikTokLiveClientBuilder(hostName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use to get some data from TikTok about users are lives
|
||||
*
|
||||
* @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
|
||||
* @return LiveHttpClient
|
||||
*/
|
||||
public static boolean isLiveOnline(String hostName)
|
||||
{
|
||||
return new TikTokDataChecker().isOnline(hostName);
|
||||
}
|
||||
public static LiveHttpClient requests() {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @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);
|
||||
return new TikTokLiveHttpClient();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,18 +27,19 @@ import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.TikTokReconnectingEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
||||
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
|
||||
import io.github.jwdeveloper.tiktok.models.ConnectionState;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.websocket.SocketClient;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -48,24 +49,24 @@ import java.util.logging.Logger;
|
||||
public class TikTokLiveClient implements LiveClient {
|
||||
private final TikTokRoomInfo liveRoomInfo;
|
||||
private final TikTokGiftManager tikTokGiftManager;
|
||||
private final TikTokApiService apiService;
|
||||
private final TikTokLiveHttpClient httpClient;
|
||||
private final SocketClient webSocketClient;
|
||||
private final TikTokEventObserver tikTokEventHandler;
|
||||
private final ClientSettings clientSettings;
|
||||
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||
private final LiveClientSettings clientSettings;
|
||||
private final TikTokListenersManager listenersManager;
|
||||
private final Logger logger;
|
||||
|
||||
public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta,
|
||||
TikTokApiService tikTokApiService,
|
||||
TikTokLiveHttpClient tiktokHttpClient,
|
||||
SocketClient webSocketClient,
|
||||
TikTokGiftManager tikTokGiftManager,
|
||||
TikTokEventObserver tikTokEventHandler,
|
||||
ClientSettings clientSettings,
|
||||
TikTokLiveEventHandler tikTokEventHandler,
|
||||
LiveClientSettings clientSettings,
|
||||
TikTokListenersManager listenersManager,
|
||||
Logger logger) {
|
||||
this.liveRoomInfo = tikTokLiveMeta;
|
||||
this.tikTokGiftManager = tikTokGiftManager;
|
||||
this.apiService = tikTokApiService;
|
||||
this.httpClient = tiktokHttpClient;
|
||||
this.webSocketClient = webSocketClient;
|
||||
this.tikTokEventHandler = tikTokEventHandler;
|
||||
this.clientSettings = clientSettings;
|
||||
@@ -81,8 +82,10 @@ public class TikTokLiveClient implements LiveClient {
|
||||
onConnection.accept(this);
|
||||
return this;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public CompletableFuture<LiveClient> connectAsync() {
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
@@ -116,6 +119,51 @@ public class TikTokLiveClient implements LiveClient {
|
||||
}
|
||||
}
|
||||
|
||||
public void tryConnect() {
|
||||
if (!liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED))
|
||||
{
|
||||
throw new TikTokLiveException("Already connected");
|
||||
}
|
||||
|
||||
setState(ConnectionState.CONNECTING);
|
||||
|
||||
|
||||
var userDataRequest = new LiveUserData.Request(liveRoomInfo.getHostName());
|
||||
var userData = httpClient.fetchLiveUserData(userDataRequest);
|
||||
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
|
||||
liveRoomInfo.setRoomId(userData.getRoomId());
|
||||
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) {
|
||||
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostUser());
|
||||
}
|
||||
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) {
|
||||
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostUser());
|
||||
}
|
||||
|
||||
|
||||
var liveDataRequest = new LiveData.Request(userData.getRoomId());
|
||||
var liveData = httpClient.fetchLiveData(liveDataRequest);
|
||||
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) {
|
||||
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found.");
|
||||
}
|
||||
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) {
|
||||
throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?");
|
||||
}
|
||||
|
||||
liveRoomInfo.setTitle(liveData.getTitle());
|
||||
liveRoomInfo.setViewersCount(liveData.getViewers());
|
||||
liveRoomInfo.setTotalViewersCount(liveData.getTotalViewers());
|
||||
liveRoomInfo.setAgeRestricted(liveData.isAgeRestricted());
|
||||
liveRoomInfo.setHost(liveData.getHost());
|
||||
|
||||
|
||||
var liveConnectionRequest =new LiveConnectionData.Request(userData.getRoomId());
|
||||
var liveConnectionData = httpClient.fetchLiveConnectionData(liveConnectionRequest);
|
||||
webSocketClient.start(liveConnectionData, this);
|
||||
|
||||
setState(ConnectionState.CONNECTED);
|
||||
tikTokEventHandler.publish(this, new TikTokRoomInfoEvent(liveRoomInfo));
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
|
||||
return;
|
||||
@@ -124,44 +172,13 @@ public class TikTokLiveClient implements LiveClient {
|
||||
setState(ConnectionState.DISCONNECTED);
|
||||
}
|
||||
|
||||
public void tryConnect() {
|
||||
if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED))
|
||||
throw new TikTokLiveException("Already connected");
|
||||
if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTING))
|
||||
throw new TikTokLiveException("Already connecting");
|
||||
private void setState(ConnectionState connectionState) {
|
||||
logger.info("TikTokLive client state: " + connectionState.name());
|
||||
liveRoomInfo.setConnectionState(connectionState);
|
||||
}
|
||||
|
||||
setState(ConnectionState.CONNECTING);
|
||||
|
||||
|
||||
apiService.updateSessionId();
|
||||
|
||||
if (clientSettings.getRoomId() != null) {
|
||||
liveRoomInfo.setRoomId(clientSettings.getRoomId());
|
||||
logger.info("Using roomID from settings: " + clientSettings.getRoomId());
|
||||
} else {
|
||||
var roomId = apiService.fetchRoomId(liveRoomInfo.getHostName());
|
||||
liveRoomInfo.setRoomId(roomId);
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
public void publishEvent(TikTokEvent event) {
|
||||
tikTokEventHandler.publish(this, event);
|
||||
}
|
||||
|
||||
|
||||
@@ -185,13 +202,4 @@ public class TikTokLiveClient implements LiveClient {
|
||||
}
|
||||
|
||||
|
||||
private void setState(ConnectionState connectionState) {
|
||||
logger.info("TikTokLive client state: " + connectionState.name());
|
||||
liveRoomInfo.setConnectionState(connectionState);
|
||||
}
|
||||
|
||||
public void publishEvent(TikTokEvent event)
|
||||
{
|
||||
tikTokEventHandler.publish(this, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,12 +40,7 @@ import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketRespons
|
||||
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
||||
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.http.HttpClientFactory;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
@@ -62,10 +57,10 @@ import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokGiftEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokRoomInfoEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokSocialMediaEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.*;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -74,17 +69,17 @@ import java.util.logging.*;
|
||||
|
||||
public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
|
||||
protected final ClientSettings clientSettings;
|
||||
|
||||
protected final LiveClientSettings clientSettings;
|
||||
protected final Logger logger;
|
||||
protected final TikTokEventObserver tikTokEventHandler;
|
||||
protected final TikTokLiveEventHandler tikTokEventHandler;
|
||||
protected final List<TikTokEventListener> listeners;
|
||||
protected Consumer<TikTokMapper> onCustomMappings;
|
||||
|
||||
public TikTokLiveClientBuilder(String userName) {
|
||||
this.tikTokEventHandler = new TikTokEventObserver();
|
||||
this.clientSettings = Constants.DefaultClientSettings();
|
||||
public TikTokLiveClientBuilder(String userName)
|
||||
{
|
||||
this.clientSettings = LiveClientSettings.createDefault();
|
||||
this.clientSettings.setHostName(userName);
|
||||
this.tikTokEventHandler = new TikTokLiveEventHandler();
|
||||
this.logger = Logger.getLogger(TikTokLive.class.getSimpleName() + " " + userName);
|
||||
this.listeners = new ArrayList<>();
|
||||
this.onCustomMappings = (e) -> {
|
||||
@@ -97,7 +92,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
}
|
||||
|
||||
|
||||
public TikTokLiveClientBuilder configure(Consumer<ClientSettings> onConfigure) {
|
||||
public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure) {
|
||||
onConfigure.accept(clientSettings);
|
||||
return this;
|
||||
}
|
||||
@@ -108,17 +103,12 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
}
|
||||
|
||||
protected void validate() {
|
||||
|
||||
if (clientSettings.getTimeout() == null) {
|
||||
clientSettings.setTimeout(Duration.ofSeconds(Constants.DEFAULT_TIMEOUT));
|
||||
}
|
||||
|
||||
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().equals("")) {
|
||||
clientSettings.setClientLanguage(Constants.DefaultClientSettings().getClientLanguage());
|
||||
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty()) {
|
||||
clientSettings.setClientLanguage("en");
|
||||
}
|
||||
|
||||
|
||||
if (clientSettings.getHostName() == null || clientSettings.getHostName().equals("")) {
|
||||
if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty()) {
|
||||
throw new TikTokLiveException("HostName can not be null");
|
||||
}
|
||||
|
||||
@@ -127,9 +117,9 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
}
|
||||
|
||||
|
||||
var params = clientSettings.getClientParameters();
|
||||
params.put("app_language", clientSettings.getClientLanguage());
|
||||
params.put("webcast_language", clientSettings.getClientLanguage());
|
||||
var httpSettings = clientSettings.getHttpSettings();
|
||||
httpSettings.getParams().put("app_language", clientSettings.getClientLanguage());
|
||||
httpSettings.getParams().put("webcast_language", clientSettings.getClientLanguage());
|
||||
|
||||
|
||||
var handler = new ConsoleHandler();
|
||||
@@ -146,9 +136,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
});
|
||||
logger.setUseParentHandlers(false);
|
||||
logger.addHandler(handler);
|
||||
|
||||
logger.setLevel(clientSettings.getLogLevel());
|
||||
|
||||
if (!clientSettings.isPrintToConsole()) {
|
||||
logger.setLevel(Level.OFF);
|
||||
}
|
||||
@@ -161,23 +149,22 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
tiktokRoomInfo.setHostName(clientSettings.getHostName());
|
||||
|
||||
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
||||
var cookieJar = new TikTokCookieJar();
|
||||
var requestFactory = new TikTokHttpRequestFactory(cookieJar, tikTokEventHandler);
|
||||
var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
|
||||
var apiService = new TikTokApiService(apiClient, logger, clientSettings);
|
||||
|
||||
var giftManager = new TikTokGiftManager(logger);
|
||||
var eventsMapper = createMapper(giftManager, tiktokRoomInfo);
|
||||
var messageHandler = new TikTokMessageHandler(tikTokEventHandler, eventsMapper);
|
||||
var messageHandler = new TikTokLiveMessageHandler(tikTokEventHandler, eventsMapper);
|
||||
|
||||
|
||||
var httpClientFactory = new HttpClientFactory(clientSettings);
|
||||
var tikTokLiveHttpClient = new TikTokLiveHttpClient(httpClientFactory);
|
||||
|
||||
var webSocketClient = new TikTokWebSocketClient(
|
||||
cookieJar,
|
||||
clientSettings,
|
||||
messageHandler,
|
||||
tikTokEventHandler);
|
||||
|
||||
return new TikTokLiveClient(tiktokRoomInfo,
|
||||
apiService,
|
||||
tikTokLiveHttpClient,
|
||||
webSocketClient,
|
||||
giftManager,
|
||||
tikTokEventHandler,
|
||||
@@ -187,12 +174,17 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
}
|
||||
|
||||
public TikTokLiveMapper createMapper(GiftManager giftManager, TikTokRoomInfo roomInfo) {
|
||||
/*
|
||||
//
|
||||
*/
|
||||
|
||||
|
||||
var eventMapper = new TikTokGenericEventMapper();
|
||||
var mapper = new TikTokLiveMapper(new TikTokLiveMapperHelper(eventMapper));
|
||||
|
||||
//ConnectionEvents events
|
||||
var commonHandler = new TikTokCommonEventHandler();
|
||||
var giftHandler = new TikTokGiftEventHandler(giftManager);
|
||||
var giftHandler = new TikTokGiftEventHandler(giftManager, roomInfo);
|
||||
var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo);
|
||||
var socialHandler = new TikTokSocialMediaEventHandler(roomInfo);
|
||||
|
||||
@@ -248,14 +240,14 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
|
||||
|
||||
//LinkMic events
|
||||
// mapper.webcastObjectToConstructor(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastLinkMicArmies.class, TikTokLinkMicArmiesEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class);
|
||||
|
||||
//Rank events
|
||||
// mapper.webcastObjectToConstructor(WebcastRankTextMessage.class, TikTokRankTextEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastRankUpdateMessage.class, TikTokRankUpdateEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastRankUpdateMessage.class, TikTokRankUpdateEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastHourlyRankMessage.class, TikTokRankUpdateEvent.class);
|
||||
|
||||
//Others events
|
||||
@@ -263,7 +255,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
// mapper.webcastObjectToConstructor(WebcastMsgDetectMessage.class, TikTokDetectEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastBarrageMessage.class, TikTokBarrageEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastUnauthorizedMemberMessage.class, TikTokUnauthorizedMemberEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastOecLiveShoppingMessage.class, TikTokShopEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastOecLiveShoppingMessage.class, TikTokShopEvent.class);
|
||||
// mapper.webcastObjectToConstructor(WebcastImDeleteMessage.class, TikTokIMDeleteEvent.class);
|
||||
// mapper.bytesToEvents(WebcastEnvelopeMessage.class, commonHandler::handleEnvelop);
|
||||
|
||||
@@ -552,11 +544,4 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||
tikTokEventHandler.subscribe(TikTokReconnectingEvent.class, event);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.handlers;
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
|
||||
@@ -31,10 +31,10 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class TikTokEventObserver {
|
||||
public class TikTokLiveEventHandler {
|
||||
private final Map<Class<?>, Set<EventConsumer>> events;
|
||||
|
||||
public TikTokEventObserver() {
|
||||
public TikTokLiveEventHandler() {
|
||||
events = new HashMap<>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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 com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.*;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokSignServerException;
|
||||
import io.github.jwdeveloper.tiktok.http.HttpClientFactory;
|
||||
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.http.mappers.GiftsDataMapper;
|
||||
import io.github.jwdeveloper.tiktok.http.mappers.LiveDataMapper;
|
||||
import io.github.jwdeveloper.tiktok.http.mappers.LiveUserDataMapper;
|
||||
import io.github.jwdeveloper.tiktok.http.mappers.SignServerResponseMapper;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
public class TikTokLiveHttpClient implements LiveHttpClient {
|
||||
|
||||
/**
|
||||
* Signing API by Isaac Kogan
|
||||
* https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures
|
||||
*/
|
||||
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url";
|
||||
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
|
||||
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
|
||||
|
||||
private final HttpClientFactory httpFactory;
|
||||
private final LiveUserDataMapper liveUserDataMapper;
|
||||
private final LiveDataMapper liveDataMapper;
|
||||
private final SignServerResponseMapper singServerResponseMapper;
|
||||
private final GiftsDataMapper giftsDataMapper;
|
||||
|
||||
public TikTokLiveHttpClient(HttpClientFactory factory) {
|
||||
this.httpFactory = factory;
|
||||
liveUserDataMapper = new LiveUserDataMapper();
|
||||
liveDataMapper = new LiveDataMapper();
|
||||
singServerResponseMapper = new SignServerResponseMapper();
|
||||
giftsDataMapper = new GiftsDataMapper();
|
||||
}
|
||||
|
||||
public TikTokLiveHttpClient() {
|
||||
this(new HttpClientFactory(LiveClientSettings.createDefault()));
|
||||
}
|
||||
|
||||
|
||||
public GiftsData.Response fetchGiftsData() {
|
||||
var url = TIKTOK_URL_WEBCAST + "gift/list/";
|
||||
var optional = httpFactory.client(url)
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to fetch gifts information's");
|
||||
}
|
||||
var json = optional.get();
|
||||
return giftsDataMapper.map(json);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LiveUserData.Response fetchLiveUserData(String userName) {
|
||||
return fetchLiveUserData(new LiveUserData.Request(userName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
|
||||
|
||||
var url = TIKTOK_URL_WEB + "api-live/user/room";
|
||||
var optional = httpFactory.client(url)
|
||||
.withParam("uniqueId", request.getUserName())
|
||||
.withParam("sourceType", "54")
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to get information's about user");
|
||||
}
|
||||
|
||||
var json = optional.get();
|
||||
return liveUserDataMapper.map(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveData.Response fetchLiveData(String roomId) {
|
||||
return fetchLiveData(new LiveData.Request(roomId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveData.Response fetchLiveData(LiveData.Request request) {
|
||||
|
||||
var url = TIKTOK_URL_WEBCAST + "room/info";
|
||||
var optional = httpFactory.client(url)
|
||||
.withParam("room_id", request.getRoomId())
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokLiveRequestException("Unable to get info about live room");
|
||||
}
|
||||
|
||||
var json = optional.get();
|
||||
return liveDataMapper.map(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
|
||||
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
|
||||
|
||||
var signServerResponse = getSignedUrl(request.getRoomId());
|
||||
var credentialsResponse = getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
|
||||
|
||||
try {
|
||||
var optionalHeader = credentialsResponse.headers().firstValue("set-cookie");
|
||||
if (optionalHeader.isEmpty()) {
|
||||
throw new TikTokSignServerException("Sign server does not returned set-cookie header");
|
||||
}
|
||||
var websocketCookie = optionalHeader.get();
|
||||
var webcastResponse = WebcastResponse.parseFrom(credentialsResponse.body());
|
||||
var webSocketUrl = httpFactory
|
||||
.client(webcastResponse.getPushServer())
|
||||
.withParam("room_id", request.getRoomId())
|
||||
.withParam("cursor", webcastResponse.getCursor())
|
||||
.withParam("resp_content_type", "protobuf")
|
||||
.withParam("internal_ext", webcastResponse.getInternalExt())
|
||||
.withParams(webcastResponse.getRouteParamsMapMap())
|
||||
.build()
|
||||
.toUrl();
|
||||
|
||||
return new LiveConnectionData.Response(websocketCookie, webSocketUrl, webcastResponse);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse");
|
||||
}
|
||||
}
|
||||
|
||||
SignServerResponse getSignedUrl(String roomId) {
|
||||
var urlToSign = httpFactory
|
||||
.client(TikTokLiveHttpClient.TIKTOK_URL_WEBCAST + "im/fetch")
|
||||
.withParam("room_id", roomId)
|
||||
.build()
|
||||
.toUrl();
|
||||
|
||||
|
||||
var optional = httpFactory
|
||||
.client(TikTokLiveHttpClient.TIKTOK_SIGN_API)
|
||||
.withParam("client", "ttlive-java")
|
||||
.withParam("uuc", "1")
|
||||
.withParam("url", urlToSign.toString())
|
||||
.build()
|
||||
.toJsonResponse();
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
throw new TikTokSignServerException("Unable to sign url: " + urlToSign);
|
||||
}
|
||||
|
||||
var json = optional.get();
|
||||
return singServerResponseMapper.map(json);
|
||||
}
|
||||
|
||||
HttpResponse<byte[]> getWebsocketCredentialsResponse(String signedUrl) {
|
||||
var optionalResponse = httpFactory
|
||||
.clientEmpty(signedUrl)
|
||||
.build()
|
||||
.toResponse(HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (optionalResponse.isEmpty()) {
|
||||
throw new TikTokSignServerException("Unable to get websocket connection credentials");
|
||||
}
|
||||
return optionalResponse.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.handlers;
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.dto.MessageMetaData;
|
||||
@@ -36,18 +36,16 @@ import io.github.jwdeveloper.tiktok.utils.Stopwatch;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class TikTokLiveMessageHandler {
|
||||
|
||||
public class TikTokMessageHandler {
|
||||
|
||||
private final TikTokEventObserver tikTokEventHandler;
|
||||
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||
private final TikTokLiveMapper mapper;
|
||||
|
||||
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, TikTokLiveMapper mapper) {
|
||||
public TikTokLiveMessageHandler(TikTokLiveEventHandler tikTokEventHandler, TikTokLiveMapper mapper) {
|
||||
this.tikTokEventHandler = tikTokEventHandler;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
|
||||
public void handle(LiveClient client, WebcastResponse webcastResponse) {
|
||||
tikTokEventHandler.publish(client, new TikTokWebsocketResponseEvent(webcastResponse));
|
||||
for (var message : webcastResponse.getMessagesList()) {
|
||||
@@ -60,7 +58,8 @@ public class TikTokMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public void handleSingleMessage(LiveClient client, WebcastResponse.Message message) throws Exception {
|
||||
public void handleSingleMessage(LiveClient client, WebcastResponse.Message message)
|
||||
{
|
||||
var messageClassName = message.getMethod();
|
||||
if (!mapper.isRegistered(messageClassName)) {
|
||||
tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(message));
|
||||
@@ -22,11 +22,10 @@
|
||||
*/
|
||||
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 io.github.jwdeveloper.tiktok.models.ConnectionState;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@@ -42,6 +41,8 @@ public class TikTokRoomInfo implements LiveRoomInfo {
|
||||
|
||||
private int totalViewersCount;
|
||||
|
||||
private long startTime;
|
||||
|
||||
private boolean ageRestricted;
|
||||
|
||||
private User host;
|
||||
@@ -69,5 +70,4 @@ public class TikTokRoomInfo implements LiveRoomInfo {
|
||||
usersRanking.clear();
|
||||
usersRanking.addAll(rankingUsers);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,12 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.gifts;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -89,23 +87,17 @@ public class TikTokGiftManager implements GiftManager {
|
||||
}
|
||||
|
||||
public Gift findById(int giftId) {
|
||||
if (!indexById.containsKey(giftId)) {
|
||||
return Gift.UNDEFINED;
|
||||
}
|
||||
return indexById.get(giftId);
|
||||
Gift gift = indexById.get(giftId);
|
||||
return gift == null ? Gift.UNDEFINED : gift;
|
||||
}
|
||||
|
||||
public Gift findByName(String giftName) {
|
||||
if (!indexByName.containsKey(giftName)) {
|
||||
return Gift.UNDEFINED;
|
||||
}
|
||||
return indexByName.get(giftName);
|
||||
Gift gift = indexByName.get(giftName);
|
||||
return gift == null ? Gift.UNDEFINED : gift;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Gift> getGifts()
|
||||
{
|
||||
public List<Gift> getGifts() {
|
||||
return indexById.values().stream().toList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.settings.HttpClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.net.CookieManager;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class HttpClient {
|
||||
private final HttpClientSettings httpClientSettings;
|
||||
private final String url;
|
||||
|
||||
|
||||
public <T> Optional<HttpResponse<T>> toResponse(HttpResponse.BodyHandler<T> bodyHandler) {
|
||||
var client = prepareClient();
|
||||
var request = prepareGetRequest();
|
||||
try
|
||||
{
|
||||
var response = client.send(request, bodyHandler);
|
||||
if(response.statusCode() != 200)
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return Optional.of(response);
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Optional<String> toJsonResponse() {
|
||||
var optional = toResponse(HttpResponse.BodyHandlers.ofString());
|
||||
if (optional.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
var response = optional.get();
|
||||
|
||||
|
||||
var body = response.body();
|
||||
return Optional.of(body);
|
||||
}
|
||||
|
||||
public Optional<byte[]> toBinaryResponse() {
|
||||
var optional = toResponse(HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (optional.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
var body = optional.get().body();
|
||||
return Optional.of(body);
|
||||
}
|
||||
|
||||
|
||||
public URI toUrl() {
|
||||
var stringUrl = prepareUrlWithParameters(url, httpClientSettings.getParams());
|
||||
return URI.create(stringUrl);
|
||||
}
|
||||
|
||||
private HttpRequest prepareGetRequest() {
|
||||
var requestBuilder = HttpRequest.newBuilder().GET();
|
||||
requestBuilder.uri(toUrl());
|
||||
requestBuilder.timeout(httpClientSettings.getTimeout());
|
||||
httpClientSettings.getHeaders().forEach(requestBuilder::setHeader);
|
||||
|
||||
httpClientSettings.getOnRequestCreating().accept(requestBuilder);
|
||||
return requestBuilder.build();
|
||||
}
|
||||
|
||||
private java.net.http.HttpClient prepareClient() {
|
||||
var builder = java.net.http.HttpClient.newBuilder()
|
||||
.followRedirects(java.net.http.HttpClient.Redirect.NORMAL)
|
||||
.cookieHandler(new CookieManager())
|
||||
.connectTimeout(httpClientSettings.getTimeout());
|
||||
|
||||
|
||||
httpClientSettings.getOnClientCreating().accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private String prepareUrlWithParameters(String url, Map<String, Object> parameters) {
|
||||
if (parameters.isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return url + "?" + parameters.entrySet().stream().map(entry ->
|
||||
{
|
||||
var encodedKey = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8);
|
||||
var encodedValue = URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8);
|
||||
return encodedKey + "=" + encodedValue;
|
||||
}).collect(Collectors.joining("&"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.settings.HttpClientSettings;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class HttpClientBuilder {
|
||||
|
||||
private final HttpClientSettings httpClientSettings;
|
||||
private String url;
|
||||
|
||||
public HttpClientBuilder(String url, HttpClientSettings httpClientSettings) {
|
||||
this.httpClientSettings = httpClientSettings;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public HttpClientBuilder(String url) {
|
||||
httpClientSettings = new HttpClientSettings();
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public HttpClientBuilder withUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder withHttpClientSettings(Consumer<HttpClientSettings> consumer) {
|
||||
consumer.accept(httpClientSettings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder withCookie(String name, String value) {
|
||||
httpClientSettings.getCookies().put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder withHeader(String name, String value) {
|
||||
httpClientSettings.getHeaders().put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder withParam(String name, String value) {
|
||||
httpClientSettings.getParams().put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder withParams(Map<String, String> parameters) {
|
||||
httpClientSettings.getParams().putAll(parameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder withHeaders(Map<String, String> headers) {
|
||||
httpClientSettings.getHeaders().putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public HttpClient build() {
|
||||
|
||||
return new HttpClient(httpClientSettings, url);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -22,31 +22,21 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.http;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
|
||||
public class TikTokCookieJar {
|
||||
private final Map<String, String> cookies;
|
||||
public TikTokCookieJar() {
|
||||
cookies = new HashMap<>();
|
||||
public class HttpClientFactory {
|
||||
private final LiveClientSettings liveClientSettings;
|
||||
|
||||
public HttpClientFactory(LiveClientSettings liveClientSettings) {
|
||||
this.liveClientSettings = liveClientSettings;
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
return cookies.get(key);
|
||||
public HttpClientBuilder client(String url) {
|
||||
return new HttpClientBuilder(url, liveClientSettings.getHttpSettings().clone());
|
||||
}
|
||||
|
||||
public void set(String key, String value) {
|
||||
cookies.put(key, value);
|
||||
}
|
||||
|
||||
public String parseCookies()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for(var entry : cookies.entrySet())
|
||||
{
|
||||
sb.append(entry.getKey()).append("=").append(entry.getValue()).append(";");
|
||||
}
|
||||
return sb.toString();
|
||||
//Does not contains default httpClientSettings, Params, headers, etd
|
||||
public HttpClientBuilder clientEmpty(String url) {
|
||||
return new HttpClientBuilder(url);
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.http;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpUtils
|
||||
{
|
||||
public static String parseParameters(String url, Map<String,Object> parameters)
|
||||
{
|
||||
var parameterString = "";
|
||||
if (!parameters.isEmpty()) {
|
||||
var builder = new StringBuilder();
|
||||
builder.append("?");
|
||||
var first = false;
|
||||
for (var param : parameters.entrySet()) {
|
||||
|
||||
if (first) {
|
||||
builder.append("&");
|
||||
}
|
||||
builder.append(param.getKey()).append("=").append(param.getValue());
|
||||
first = true;
|
||||
}
|
||||
parameterString = builder.toString();
|
||||
}
|
||||
|
||||
return url+parameterString;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String parseParametersEncode(String url, Map<String,Object> parameters)
|
||||
{
|
||||
|
||||
var parameterString = "";
|
||||
if (!parameters.isEmpty()) {
|
||||
var builder = new StringBuilder();
|
||||
builder.append("?");
|
||||
var first = false;
|
||||
for (var param : parameters.entrySet()) {
|
||||
|
||||
if (first) {
|
||||
builder.append("&");
|
||||
}
|
||||
|
||||
final String encodedKey = URLEncoder.encode(param.getKey(), StandardCharsets.UTF_8);
|
||||
final String encodedValue = URLEncoder.encode(param.getValue().toString(), StandardCharsets.UTF_8);
|
||||
builder.append(encodedKey).append("=").append(encodedValue);
|
||||
first = true;
|
||||
}
|
||||
parameterString = builder.toString();
|
||||
}
|
||||
|
||||
return url+parameterString;
|
||||
}
|
||||
}
|
||||
@@ -1,141 +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.http;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.data.dto.TikTokUserInfo;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
|
||||
import io.github.jwdeveloper.tiktok.mappers.LiveRoomMetaMapper;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TikTokApiService {
|
||||
private final TikTokHttpClient tiktokHttpClient;
|
||||
private final Logger logger;
|
||||
private final ClientSettings clientSettings;
|
||||
|
||||
public TikTokApiService(TikTokHttpClient apiClient, Logger logger, ClientSettings clientSettings) {
|
||||
this.tiktokHttpClient = apiClient;
|
||||
this.logger = logger;
|
||||
this.clientSettings = clientSettings;
|
||||
}
|
||||
|
||||
|
||||
public void updateSessionId() {
|
||||
if (clientSettings.getSessionId() == null) {
|
||||
return;
|
||||
}
|
||||
if (clientSettings.getSessionId().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
tiktokHttpClient.setSessionId(clientSettings.getSessionId());
|
||||
}
|
||||
|
||||
public String fetchRoomId(String userName) {
|
||||
var userInfo = fetchUserInfoFromTikTokApi(userName);
|
||||
clientSettings.getClientParameters().put("room_id", userInfo.getRoomId());
|
||||
logger.info("RoomID -> " + userInfo.getRoomId());
|
||||
return userInfo.getRoomId();
|
||||
}
|
||||
|
||||
public TikTokUserInfo fetchUserInfoFromTikTokApi(String userName) {
|
||||
|
||||
var params = new HashMap<>(clientSettings.getClientParameters());
|
||||
params.put("uniqueId", userName);
|
||||
params.put("sourceType", 54);
|
||||
JsonObject roomData = null;
|
||||
try {
|
||||
roomData = tiktokHttpClient.getJsonFromTikTokApi("api-live/user/room/", params);
|
||||
} catch (Exception e) {
|
||||
|
||||
|
||||
throw new TikTokLiveRequestException("Failed to fetch pre connection room information, it happens when TikTok temporary blocks you. Try to connect again in few minutes");
|
||||
}
|
||||
|
||||
var message = roomData.get("message").getAsString();
|
||||
|
||||
if (message.equals("params_error")) {
|
||||
throw new TikTokLiveRequestException("fetchRoomIdFromTiktokApi -> Unable to fetch roomID, contact with developer");
|
||||
}
|
||||
if (message.equals("user_not_found")) {
|
||||
return new TikTokUserInfo(TikTokUserInfo.UserStatus.NotFound, "");
|
||||
}
|
||||
//live -> status 2
|
||||
//live paused -> 3
|
||||
//not live -> status 4
|
||||
var data = roomData.getAsJsonObject("data");
|
||||
var user = data.getAsJsonObject("user");
|
||||
var roomId = user.get("roomId").getAsString();
|
||||
var status = user.get("status").getAsInt();
|
||||
|
||||
var statusEnum = switch (status) {
|
||||
case 2 -> TikTokUserInfo.UserStatus.Live;
|
||||
case 3 -> TikTokUserInfo.UserStatus.LivePaused;
|
||||
case 4 -> TikTokUserInfo.UserStatus.Offline;
|
||||
default -> TikTokUserInfo.UserStatus.NotFound;
|
||||
};
|
||||
|
||||
return new TikTokUserInfo(statusEnum, roomId);
|
||||
}
|
||||
|
||||
|
||||
public LiveRoomMeta fetchRoomInfo() {
|
||||
logger.info("Fetching RoomInfo");
|
||||
try {
|
||||
var response = tiktokHttpClient.getJsonFromWebcastApi("room/info/", clientSettings.getClientParameters());
|
||||
if (!response.has("data")) {
|
||||
var gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
var json = gson.toJson(response);
|
||||
throw new TikTokLiveRequestException("room info response does not contains data field: \n"+ json);
|
||||
}
|
||||
|
||||
var mapper = new LiveRoomMetaMapper();
|
||||
var liveRoomMeta = mapper.map(response);
|
||||
logger.info("RoomInfo status -> " + liveRoomMeta.getStatus());
|
||||
return liveRoomMeta;
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Failed to fetch room info from WebCast server, see stacktrace for more info.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public WebcastResponse fetchClientData() {
|
||||
|
||||
logger.info("Fetching ClientData");
|
||||
try {
|
||||
var response = tiktokHttpClient.getSigningServerResponse("im/fetch/", clientSettings.getClientParameters());
|
||||
clientSettings.getClientParameters().put("cursor", response.getCursor());
|
||||
clientSettings.getClientParameters().put("internal_ext", response.getInternalExt());
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Failed to fetch live websocket connection data", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +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.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.Constants;
|
||||
import io.github.jwdeveloper.tiktok.data.dto.TikTokUserInfo;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TikTokDataChecker {
|
||||
|
||||
public CompletableFuture<Boolean> isOnlineAsync(String hostName) {
|
||||
return CompletableFuture.supplyAsync(() -> isOnline(hostName));
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> isHostNameValidAsync(String hostName) {
|
||||
return CompletableFuture.supplyAsync(() -> isOnline(hostName));
|
||||
}
|
||||
|
||||
public boolean isOnline(String hostName) {
|
||||
var data = getApiService().fetchUserInfoFromTikTokApi(hostName);
|
||||
return data.getUserStatus() == TikTokUserInfo.UserStatus.Live ||
|
||||
data.getUserStatus() == TikTokUserInfo.UserStatus.LivePaused;
|
||||
}
|
||||
|
||||
public boolean isHostNameValid(String hostName) {
|
||||
var data = getApiService().fetchUserInfoFromTikTokApi(hostName);
|
||||
return data.getUserStatus() != TikTokUserInfo.UserStatus.NotFound;
|
||||
}
|
||||
|
||||
public TikTokApiService getApiService() {
|
||||
var jar = new TikTokCookieJar();
|
||||
var factory = new TikTokHttpRequestFactory(jar,new TikTokEventObserver());
|
||||
var client = new TikTokHttpClient(jar, factory);
|
||||
var settings = new ClientSettings();
|
||||
settings.setClientParameters(Constants.DefaultClientParams());
|
||||
var apiService = new TikTokApiService(client, Logger.getGlobal(), settings);
|
||||
return apiService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,160 +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.http;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.github.jwdeveloper.tiktok.Constants;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class TikTokHttpClient {
|
||||
private final TikTokHttpRequestFactory requestFactory;
|
||||
private final TikTokCookieJar tikTokCookieJar;
|
||||
|
||||
public TikTokHttpClient(TikTokCookieJar tikTokCookieJar, TikTokHttpRequestFactory requestFactory) {
|
||||
this.requestFactory = requestFactory;
|
||||
this.tikTokCookieJar = tikTokCookieJar;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId)
|
||||
{
|
||||
tikTokCookieJar.set("sessionid", sessionId);
|
||||
tikTokCookieJar.set("sessionid_ss", sessionId);
|
||||
tikTokCookieJar.set("sid_tt", sessionId);
|
||||
}
|
||||
|
||||
|
||||
public String getLivestreamPage(String userName) {
|
||||
|
||||
var url = Constants.TIKTOK_URL_WEB + "@" + userName + "/live/";
|
||||
var get = getRequest(url, null);
|
||||
return get;
|
||||
}
|
||||
|
||||
public JsonObject getJsonFromTikTokApi(String path, Map<String,Object> params)
|
||||
{
|
||||
var get = getRequest(Constants.TIKTOK_URL_WEB + path, params);
|
||||
var json = JsonParser.parseString(get);
|
||||
var jsonObject = json.getAsJsonObject();
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new TikTokLiveRequestException("Unable to deserialize message: "+path,e);
|
||||
}
|
||||
}
|
||||
|
||||
private String postRequest(String url, Map<String, Object> parameters) {
|
||||
if (parameters == null) {
|
||||
parameters = new HashMap<>();
|
||||
}
|
||||
var request = requestFactory.setQueries(parameters);
|
||||
return request.post(url);
|
||||
}
|
||||
|
||||
private String getRequest(String url, Map<String, Object> parameters) {
|
||||
if (parameters == null) {
|
||||
parameters = new HashMap<>();
|
||||
}
|
||||
|
||||
var request = requestFactory.setQueries(parameters);
|
||||
return request.get(url);
|
||||
}
|
||||
private byte[] getSignRequest(String url, Map<String, Object> parameters) {
|
||||
url = getSignedUrl(url, parameters);
|
||||
try {
|
||||
var client = HttpClient.newHttpClient();
|
||||
var request = HttpRequest.newBuilder()
|
||||
.uri(new URI(url))
|
||||
.build();
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||
|
||||
var cookies = response.headers().allValues("Set-Cookie");
|
||||
for(var cookie : cookies)
|
||||
{
|
||||
var split = cookie.split(";")[0].split("=");
|
||||
|
||||
|
||||
var key = split[0];
|
||||
var value = split[1];
|
||||
tikTokCookieJar.set(key, value);
|
||||
}
|
||||
|
||||
return response.body();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new TikTokLiveRequestException("Unable to send signature");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getSignedUrl(String url, Map<String, Object> parameters) {
|
||||
|
||||
var fullUrl = HttpUtils.parseParameters(url,parameters);
|
||||
var signParams = new TreeMap<String,Object>();
|
||||
signParams.put("client", "ttlive-java");
|
||||
signParams.put("uuc", 1);
|
||||
signParams.put("url", fullUrl);
|
||||
|
||||
var request = requestFactory.setQueries(signParams);
|
||||
var content = request.get(Constants.TIKTOK_SIGN_API);
|
||||
|
||||
|
||||
try {
|
||||
var json = JsonParser.parseString(content);
|
||||
var jsonObject = json.getAsJsonObject();
|
||||
var signedUrl = jsonObject.get("signedUrl").getAsString();
|
||||
var userAgent = jsonObject.get("User-Agent").getAsString();
|
||||
|
||||
requestFactory.setAgent(userAgent);
|
||||
return signedUrl;
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Insufficient values have been supplied for signing. Likely due to an update. Post an issue on GitHub.", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,171 +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.http;
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.Constants;
|
||||
import io.github.jwdeveloper.tiktok.data.events.http.TikTokHttpResponseEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.models.http.HttpData;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.net.CookieManager;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class TikTokHttpRequestFactory implements TikTokHttpRequest {
|
||||
private final CookieManager cookieManager;
|
||||
private final Map<String, String> defaultHeaders;
|
||||
private final TikTokCookieJar tikTokCookieJar;
|
||||
private final HttpClient client;
|
||||
private final TikTokEventObserver eventHandler;
|
||||
private String query;
|
||||
|
||||
public TikTokHttpRequestFactory(TikTokCookieJar tikTokCookieJar, TikTokEventObserver eventHandler) {
|
||||
this.tikTokCookieJar = tikTokCookieJar;
|
||||
this.cookieManager = new CookieManager();
|
||||
this.eventHandler = eventHandler;
|
||||
defaultHeaders = Constants.DefaultRequestHeaders();
|
||||
client = HttpClient.newBuilder()
|
||||
.cookieHandler(cookieManager)
|
||||
.connectTimeout(Duration.ofSeconds(2))
|
||||
.build();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String get(String url) {
|
||||
var uri = URI.create(url);
|
||||
var requestBuilder = HttpRequest.newBuilder().GET();
|
||||
|
||||
for (var header : defaultHeaders.entrySet()) {
|
||||
if (header.getKey().equals("Connection") || header.getKey().equals("Accept-Encoding")) {
|
||||
continue;
|
||||
}
|
||||
requestBuilder.setHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
if (query != null) {
|
||||
var baseUri = uri.toString();
|
||||
var requestUri = URI.create(baseUri + "?" + query);
|
||||
requestBuilder.uri(requestUri);
|
||||
} else {
|
||||
requestBuilder.uri(uri);
|
||||
}
|
||||
|
||||
var result = requestBuilder.build();
|
||||
|
||||
return getContent(result);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String post(String url) {
|
||||
var uri = URI.create(url);
|
||||
var request = HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(""));
|
||||
for (var header : defaultHeaders.entrySet()) {
|
||||
if (header.getKey().equals("Connection")) {
|
||||
continue;
|
||||
}
|
||||
request.setHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
request.setHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
request.setHeader("Cookie", tikTokCookieJar.parseCookies());
|
||||
|
||||
|
||||
if (query != null) {
|
||||
var baseUri = uri.toString();
|
||||
var requestUri = URI.create(baseUri + "?" + query);
|
||||
request.uri(requestUri);
|
||||
}
|
||||
|
||||
return getContent(request.build());
|
||||
}
|
||||
|
||||
public TikTokHttpRequest setHeader(String key, String value) {
|
||||
defaultHeaders.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TikTokHttpRequest setAgent(String value) {
|
||||
defaultHeaders.put("User-Agent", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TikTokHttpRequest setQueries(Map<String, Object> queries) {
|
||||
if (queries == null)
|
||||
return this;
|
||||
var testMap = new TreeMap<String, Object>(queries);
|
||||
query = String.join("&", testMap.entrySet().stream().map(x ->
|
||||
{
|
||||
var key = x.getKey();
|
||||
var value = "";
|
||||
try {
|
||||
value = URLEncoder.encode(x.getValue().toString(), StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return key + "=" + value;
|
||||
}).toList());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private String getContent(HttpRequest request) throws Exception {
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
var event = new TikTokHttpResponseEvent(response.uri().toString(), HttpData.map(request), HttpData.map(response));
|
||||
eventHandler.publish(null, event);
|
||||
if (response.statusCode() == 404) {
|
||||
throw new TikTokLiveRequestException("Request responded with 404 NOT_FOUND");
|
||||
}
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
throw new TikTokLiveRequestException("Request was unsuccessful " + response.statusCode());
|
||||
}
|
||||
|
||||
var cookies = response.headers().allValues("Set-Cookie");
|
||||
for (var cookie : cookies) {
|
||||
var split = cookie.split(";")[0].split("=");
|
||||
var uri = request.uri();
|
||||
var key = split[0];
|
||||
var value = split[1];
|
||||
tikTokCookieJar.set(key, value);
|
||||
|
||||
var map = new HashMap<String, List<String>>();
|
||||
map.put(key, List.of(value));
|
||||
cookieManager.put(uri, map);
|
||||
|
||||
}
|
||||
return response.body();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.http.mappers;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.GiftsData;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GiftsDataMapper {
|
||||
public GiftsData.Response map(String json) {
|
||||
var parsedJson = JsonParser.parseString(json);
|
||||
var jsonObject = parsedJson.getAsJsonObject();
|
||||
|
||||
if (!jsonObject.has("data")) {
|
||||
return new GiftsData.Response(json, new ArrayList<>());
|
||||
}
|
||||
var dataElement = jsonObject.getAsJsonObject("data");
|
||||
if (!dataElement.has("gifts")) {
|
||||
return new GiftsData.Response(json, new ArrayList<>());
|
||||
}
|
||||
|
||||
var gifts = dataElement.get("gifts").getAsJsonArray()
|
||||
.asList()
|
||||
.stream()
|
||||
.map(this::mapSingleGift)
|
||||
.toList();
|
||||
|
||||
return new GiftsData.Response(json, gifts);
|
||||
}
|
||||
|
||||
|
||||
private GiftsData.GiftModel mapSingleGift(JsonElement jsonElement) {
|
||||
var id = jsonElement.getAsJsonObject().get("id").getAsInt();
|
||||
var name = jsonElement.getAsJsonObject().get("name").getAsString();
|
||||
var diamondCost = jsonElement.getAsJsonObject().get("diamond_count").getAsInt();
|
||||
var image = jsonElement.getAsJsonObject()
|
||||
.get("image").getAsJsonObject()
|
||||
.get("url_list").getAsJsonArray().get(0).getAsString();
|
||||
|
||||
if (image.endsWith(".webp")) {
|
||||
image = image.replace(".webp", ".jpg");
|
||||
}
|
||||
var gift = new GiftsData.GiftModel();
|
||||
gift.setId(id);
|
||||
gift.setName(name);
|
||||
gift.setDiamondCost(diamondCost);
|
||||
gift.setImage(image);
|
||||
|
||||
return gift;
|
||||
}
|
||||
}
|
||||
@@ -20,18 +20,19 @@
|
||||
* 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.mappers;
|
||||
package io.github.jwdeveloper.tiktok.http.mappers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
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 io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LiveRoomMetaMapper {
|
||||
public class LiveDataMapper {
|
||||
/**
|
||||
* 0 - Unknown
|
||||
* 1 - ?
|
||||
@@ -39,38 +40,43 @@ public class LiveRoomMetaMapper {
|
||||
* 3 - ?
|
||||
* 4 - Offline
|
||||
*/
|
||||
public LiveRoomMeta map(JsonObject input) {
|
||||
var liveRoomMeta = new LiveRoomMeta();
|
||||
public LiveData.Response map(String json) {
|
||||
var response = new LiveData.Response();
|
||||
|
||||
if (!input.has("data")) {
|
||||
return liveRoomMeta;
|
||||
|
||||
var parsedJson = JsonParser.parseString(json);
|
||||
var jsonObject = parsedJson.getAsJsonObject();
|
||||
|
||||
|
||||
if (!jsonObject.has("data")) {
|
||||
throw new TikTokLiveRequestException("Data section not found in LiveData.Response");
|
||||
}
|
||||
var data = input.getAsJsonObject("data");
|
||||
var data = jsonObject.getAsJsonObject("data");
|
||||
|
||||
|
||||
if (data.has("status")) {
|
||||
var status = data.get("status");
|
||||
var statusId = status.getAsInt();
|
||||
var statusValue = switch (statusId) {
|
||||
case 2 -> LiveRoomMeta.LiveRoomStatus.HostOnline;
|
||||
case 4 -> LiveRoomMeta.LiveRoomStatus.HostOffline;
|
||||
default -> LiveRoomMeta.LiveRoomStatus.HostNotFound;
|
||||
case 2 -> LiveData.LiveStatus.HostOnline;
|
||||
case 4 -> LiveData.LiveStatus.HostOffline;
|
||||
default -> LiveData.LiveStatus.HostNotFound;
|
||||
};
|
||||
liveRoomMeta.setStatus(statusValue);
|
||||
response.setLiveStatus(statusValue);
|
||||
} else {
|
||||
liveRoomMeta.setStatus(LiveRoomMeta.LiveRoomStatus.HostNotFound);
|
||||
response.setLiveStatus(LiveData.LiveStatus.HostNotFound);
|
||||
}
|
||||
|
||||
if (data.has("age_restricted")) {
|
||||
var element = data.getAsJsonObject("age_restricted");
|
||||
var restricted = element.get("restricted").getAsBoolean();
|
||||
liveRoomMeta.setAgeRestricted(restricted);
|
||||
response.setAgeRestricted(restricted);
|
||||
}
|
||||
|
||||
if (data.has("title")) {
|
||||
var element = data.get("title");
|
||||
var title = element.getAsString();
|
||||
liveRoomMeta.setTitie(title);
|
||||
response.setTitle(title);
|
||||
}
|
||||
|
||||
if (data.has("stats")) {
|
||||
@@ -82,44 +88,41 @@ public class LiveRoomMetaMapper {
|
||||
var totalUsers = titalUsersElement.getAsInt();
|
||||
|
||||
|
||||
liveRoomMeta.setLikeCount(likes);
|
||||
liveRoomMeta.setTotalViewers(totalUsers);
|
||||
response.setLikes(likes);
|
||||
response.setTotalViewers(totalUsers);
|
||||
}
|
||||
|
||||
if(data.has("user_count"))
|
||||
{
|
||||
if (data.has("user_count")) {
|
||||
var element = data.get("user_count");
|
||||
var viewers = element.getAsInt();
|
||||
liveRoomMeta.setViewers(viewers);
|
||||
response.setViewers(viewers);
|
||||
}
|
||||
|
||||
if(data.has("owner"))
|
||||
{
|
||||
if (data.has("owner")) {
|
||||
var element = data.getAsJsonObject("owner");
|
||||
var user = getUser(element);
|
||||
liveRoomMeta.setHost(user);
|
||||
response.setHost(user);
|
||||
}
|
||||
|
||||
return liveRoomMeta;
|
||||
return response;
|
||||
}
|
||||
|
||||
public User getUser(JsonObject jsonElement)
|
||||
{
|
||||
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 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 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<>());
|
||||
var user = new User(id, name, profileName, picture, followers, followingCount, new ArrayList<>());
|
||||
user.addAttribute(UserAttribute.LiveHost);
|
||||
return user;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.http.mappers;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
|
||||
public class LiveUserDataMapper {
|
||||
|
||||
|
||||
public LiveUserData.Response map(String json) {
|
||||
var parsedJson = JsonParser.parseString(json);
|
||||
var jsonObject = parsedJson.getAsJsonObject();
|
||||
|
||||
var message = jsonObject.get("message").getAsString();
|
||||
|
||||
if (message.equals("params_error")) {
|
||||
throw new TikTokLiveRequestException("fetchRoomIdFromTiktokApi -> Unable to fetch roomID, contact the developer");
|
||||
}
|
||||
if (message.equals("user_not_found")) {
|
||||
return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1);
|
||||
}
|
||||
//live -> status 2
|
||||
//live paused -> 3
|
||||
//not live -> status 4
|
||||
var element = jsonObject.get("data");
|
||||
if (element.isJsonNull()) {
|
||||
return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1);
|
||||
}
|
||||
var data = element.getAsJsonObject();
|
||||
var user = data.getAsJsonObject("user");
|
||||
var roomId = user.get("roomId").getAsString();
|
||||
var status = user.get("status").getAsInt();
|
||||
|
||||
var liveRoom = data.getAsJsonObject("liveRoom");
|
||||
long startTime = liveRoom.get("startTime").getAsLong();
|
||||
|
||||
var statusEnum = switch (status) {
|
||||
case 2 -> LiveUserData.UserStatus.Live;
|
||||
case 3 -> LiveUserData.UserStatus.LivePaused;
|
||||
case 4 -> LiveUserData.UserStatus.Offline;
|
||||
default -> LiveUserData.UserStatus.NotFound;
|
||||
};
|
||||
|
||||
return new LiveUserData.Response(json, statusEnum, roomId, startTime);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,16 +20,18 @@
|
||||
* 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.http;
|
||||
package io.github.jwdeveloper.tiktok.http.mappers;
|
||||
|
||||
import java.util.Map;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.SignServerResponse;
|
||||
|
||||
public interface TikTokHttpRequest {
|
||||
TikTokHttpRequest setQueries(Map<String, Object> queries);
|
||||
|
||||
TikTokHttpRequest setHeader(String key, String value);
|
||||
String get(String url);
|
||||
|
||||
String post(String url);
|
||||
public class SignServerResponseMapper {
|
||||
public SignServerResponse map(String json) {
|
||||
var parsedJson = JsonParser.parseString(json);
|
||||
var jsonObject = parsedJson.getAsJsonObject();
|
||||
|
||||
var signUrl = jsonObject.get("signedUrl").getAsString();
|
||||
var userAgent = jsonObject.get("User-Agent").getAsString();
|
||||
return new SignServerResponse(signUrl, userAgent);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
package io.github.jwdeveloper.tiktok.listener;
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException;
|
||||
@@ -36,10 +37,10 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class TikTokListenersManager implements ListenersManager {
|
||||
private final io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver eventObserver;
|
||||
private final TikTokLiveEventHandler eventObserver;
|
||||
private final List<ListenerBindingModel> bindingModels;
|
||||
|
||||
public TikTokListenersManager(List<TikTokEventListener> listeners, io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver tikTokEventHandler) {
|
||||
public TikTokListenersManager(List<TikTokEventListener> listeners, TikTokLiveEventHandler tikTokEventHandler) {
|
||||
this.eventObserver = tikTokEventHandler;
|
||||
this.bindingModels = new ArrayList<>(listeners.size());
|
||||
for (var listener : listeners) {
|
||||
@@ -115,8 +116,8 @@ public class TikTokListenersManager implements ListenersManager {
|
||||
throw new TikTokEventListenerMethodException(e);
|
||||
}
|
||||
};
|
||||
eventsMap.computeIfAbsent(eventClazz, (a) -> new ArrayList<EventConsumer<?>>()).add(eventMethodRef);
|
||||
eventsMap.computeIfAbsent(eventClazz, (a) -> new ArrayList<>()).add(eventMethodRef);
|
||||
}
|
||||
return new ListenerBindingModel(listener, eventsMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
package io.github.jwdeveloper.tiktok.mappers;
|
||||
|
||||
import com.google.protobuf.GeneratedMessageV3;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLive;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.mappers.data.MappingAction;
|
||||
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
||||
@@ -36,10 +37,12 @@ public class TikTokLiveMapper implements TikTokMapper {
|
||||
|
||||
private final Map<String, TikTokLiveMapperModel> mappers;
|
||||
private final TikTokMapperHelper mapperUtils;
|
||||
private final TikTokLiveMapperModel globalMapperModel;
|
||||
|
||||
public TikTokLiveMapper(TikTokMapperHelper mapperUtils) {
|
||||
this.mappers = new HashMap<>();
|
||||
this.mapperUtils = mapperUtils;
|
||||
this.globalMapperModel = new TikTokLiveMapperModel("any message");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,6 +79,11 @@ public class TikTokLiveMapper implements TikTokMapper {
|
||||
return forMessage(mapperName, (inputBytes, messageName, mapperHelper) -> MappingResult.of(inputBytes, onMapping.apply(inputBytes)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TikTokMapperModel forAnyMessage() {
|
||||
return globalMapperModel;
|
||||
}
|
||||
|
||||
|
||||
public boolean isRegistered(String mapperName) {
|
||||
return mappers.containsKey(mapperName);
|
||||
@@ -84,6 +92,7 @@ public class TikTokLiveMapper implements TikTokMapper {
|
||||
public <T extends GeneratedMessageV3> boolean isRegistered(Class<T> mapperName) {
|
||||
return mappers.containsKey(mapperName.getSimpleName());
|
||||
}
|
||||
|
||||
public List<TikTokEvent> handleMapping(String messageName, byte[] bytes) {
|
||||
if (!isRegistered(messageName)) {
|
||||
return List.of();
|
||||
@@ -91,10 +100,17 @@ public class TikTokLiveMapper implements TikTokMapper {
|
||||
var mapperModel = mappers.get(messageName);
|
||||
|
||||
var inputBytes = mapperModel.getOnBeforeMapping().onMapping(bytes, messageName, mapperUtils);
|
||||
var globalInputBytes = globalMapperModel.getOnBeforeMapping().onMapping(inputBytes, messageName, mapperUtils);
|
||||
|
||||
var mappingResult = mapperModel.getOnMapping().onMapping(inputBytes, messageName, mapperUtils);
|
||||
|
||||
var mappingResult = mapperModel.getOnMapping().onMapping(globalInputBytes, messageName, mapperUtils);
|
||||
|
||||
if (mappingResult == null) {
|
||||
mappingResult = globalMapperModel.getOnMapping().onMapping(globalInputBytes, messageName, mapperUtils);
|
||||
}
|
||||
|
||||
var afterMappingResult = mapperModel.getOnAfterMapping().apply(mappingResult);
|
||||
return afterMappingResult;
|
||||
var globalAfterMappingResult = globalMapperModel.getOnAfterMapping().apply(MappingResult.of(mappingResult.getSource(), afterMappingResult));
|
||||
return globalAfterMappingResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers;
|
||||
|
||||
import com.google.protobuf.GeneratedMessageV3;
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.mappers.handlers;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||
@@ -43,17 +44,19 @@ import java.util.Map;
|
||||
public class TikTokGiftEventHandler {
|
||||
private final GiftManager giftManager;
|
||||
private final Map<Long, WebcastGiftMessage> giftsMessages;
|
||||
private final TikTokRoomInfo tikTokRoomInfo;
|
||||
|
||||
public TikTokGiftEventHandler(GiftManager giftManager) {
|
||||
public TikTokGiftEventHandler(GiftManager giftManager, TikTokRoomInfo tikTokRoomInfo) {
|
||||
this.giftManager = giftManager;
|
||||
giftsMessages = new HashMap<>();
|
||||
this.tikTokRoomInfo = tikTokRoomInfo;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public MappingResult handleGifts(byte[] msg, String name, TikTokMapperHelper helper) {
|
||||
var currentMessage = WebcastGiftMessage.parseFrom(msg);
|
||||
var gifts = handleGift(currentMessage);
|
||||
return MappingResult.of(currentMessage,gifts);
|
||||
return MappingResult.of(currentMessage, gifts);
|
||||
}
|
||||
|
||||
public List<TikTokEvent> handleGift(WebcastGiftMessage currentMessage) {
|
||||
@@ -101,12 +104,12 @@ public class TikTokGiftEventHandler {
|
||||
|
||||
private TikTokGiftEvent getGiftEvent(WebcastGiftMessage message) {
|
||||
var gift = getGiftObject(message);
|
||||
return new TikTokGiftEvent(gift, message);
|
||||
return new TikTokGiftEvent(gift, tikTokRoomInfo.getHost(), message);
|
||||
}
|
||||
|
||||
private TikTokGiftEvent getGiftComboEvent(WebcastGiftMessage message, GiftSendType state) {
|
||||
var gift = getGiftObject(message);
|
||||
return new TikTokGiftComboEvent(gift, message, state);
|
||||
return new TikTokGiftComboEvent(gift, tikTokRoomInfo.getHost(), message, state);
|
||||
}
|
||||
|
||||
private Gift getGiftObject(WebcastGiftMessage giftMessage) {
|
||||
|
||||
@@ -23,60 +23,54 @@
|
||||
package io.github.jwdeveloper.tiktok.websocket;
|
||||
|
||||
|
||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.http.HttpUtils;
|
||||
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TikTokWebSocketClient implements SocketClient {
|
||||
private final ClientSettings clientSettings;
|
||||
private final TikTokCookieJar tikTokCookieJar;
|
||||
private final TikTokMessageHandler messageHandler;
|
||||
private final TikTokEventObserver tikTokEventHandler;
|
||||
private final LiveClientSettings clientSettings;
|
||||
private final TikTokLiveMessageHandler messageHandler;
|
||||
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||
private WebSocketClient webSocketClient;
|
||||
private TikTokWebSocketPingingTask pingingTask;
|
||||
private boolean isConnected;
|
||||
|
||||
public TikTokWebSocketClient(
|
||||
TikTokCookieJar tikTokCookieJar,
|
||||
ClientSettings clientSettings,
|
||||
TikTokMessageHandler messageHandler,
|
||||
TikTokEventObserver tikTokEventHandler) {
|
||||
this.tikTokCookieJar = tikTokCookieJar;
|
||||
LiveClientSettings clientSettings,
|
||||
TikTokLiveMessageHandler messageHandler,
|
||||
TikTokLiveEventHandler tikTokEventHandler) {
|
||||
this.clientSettings = clientSettings;
|
||||
this.messageHandler = messageHandler;
|
||||
this.tikTokEventHandler = tikTokEventHandler;
|
||||
isConnected = false;
|
||||
}
|
||||
@Override
|
||||
public void start(LiveConnectionData.Response connectionData, LiveClient liveClient)
|
||||
{
|
||||
|
||||
public void start(WebcastResponse webcastResponse, LiveClient tikTokLiveClient) {
|
||||
if (isConnected) {
|
||||
stop();
|
||||
}
|
||||
|
||||
if (webcastResponse.getPushServer().isEmpty() || webcastResponse.getRouteParamsMapMap().isEmpty())
|
||||
messageHandler.handle(liveClient, connectionData.getWebcastResponse());
|
||||
|
||||
var headers = new HashMap<String, String>();
|
||||
headers.put("Cookie", connectionData.getWebsocketCookies());
|
||||
webSocketClient = new TikTokWebSocketListener(connectionData.getWebsocketUrl(),
|
||||
headers,
|
||||
clientSettings.getHttpSettings().getTimeout().toMillisPart(),
|
||||
messageHandler,
|
||||
tikTokEventHandler,
|
||||
liveClient);
|
||||
|
||||
try
|
||||
{
|
||||
throw new TikTokLiveException("Could not find Room");
|
||||
}
|
||||
|
||||
try {
|
||||
messageHandler.handle(tikTokLiveClient, webcastResponse);
|
||||
var url = getWebSocketUrl(webcastResponse);
|
||||
webSocketClient = startWebSocket(url, tikTokLiveClient);
|
||||
webSocketClient.connect();
|
||||
|
||||
pingingTask = new TikTokWebSocketPingingTask();
|
||||
pingingTask.run(webSocketClient);
|
||||
isConnected = true;
|
||||
} catch (Exception e)
|
||||
{
|
||||
@@ -85,36 +79,15 @@ public class TikTokWebSocketClient implements SocketClient {
|
||||
}
|
||||
}
|
||||
|
||||
private URI getWebSocketUrl(WebcastResponse webcastResponse) {
|
||||
var tiktokAccessKey = webcastResponse.getRouteParamsMapMap();
|
||||
|
||||
var parameters = new TreeMap<>(clientSettings.getClientParameters());
|
||||
parameters.putAll(tiktokAccessKey);
|
||||
|
||||
var url = webcastResponse.getPushServer();
|
||||
var parsed = HttpUtils.parseParametersEncode(url, parameters);
|
||||
return URI.create(parsed);
|
||||
}
|
||||
|
||||
private WebSocketClient startWebSocket(URI url, LiveClient liveClient) {
|
||||
var cookie = tikTokCookieJar.parseCookies();
|
||||
var headers = new HashMap<String, String>();
|
||||
headers.put("Cookie", cookie);
|
||||
return new TikTokWebSocketListener(url,
|
||||
headers,
|
||||
3000,
|
||||
messageHandler,
|
||||
tikTokEventHandler,
|
||||
liveClient);
|
||||
}
|
||||
public void stop()
|
||||
{
|
||||
|
||||
public void stop() {
|
||||
if (isConnected && webSocketClient != null) {
|
||||
webSocketClient.closeConnection(0, "");
|
||||
pingingTask.stop();
|
||||
}
|
||||
webSocketClient = null;
|
||||
pingingTask = null;
|
||||
isConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,11 @@ import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastPushFrame;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastWebsocketAck;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.drafts.Draft_6455;
|
||||
import org.java_websocket.handshake.ServerHandshake;
|
||||
@@ -44,82 +43,81 @@ import java.util.Optional;
|
||||
|
||||
public class TikTokWebSocketListener extends WebSocketClient {
|
||||
|
||||
private final TikTokMessageHandler messageHandler;
|
||||
private final TikTokEventObserver tikTokEventHandler;
|
||||
private final TikTokLiveMessageHandler messageHandler;
|
||||
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||
private final LiveClient tikTokLiveClient;
|
||||
|
||||
public TikTokWebSocketListener(URI serverUri,
|
||||
Map<String, String> httpHeaders,
|
||||
int connectTimeout,
|
||||
TikTokMessageHandler messageHandler,
|
||||
TikTokEventObserver tikTokEventHandler,
|
||||
TikTokLiveMessageHandler messageHandler,
|
||||
TikTokLiveEventHandler tikTokEventHandler,
|
||||
LiveClient tikTokLiveClient) {
|
||||
super(serverUri, new Draft_6455(), httpHeaders,connectTimeout);
|
||||
super(serverUri, new Draft_6455(), httpHeaders, connectTimeout);
|
||||
this.messageHandler = messageHandler;
|
||||
this.tikTokEventHandler = tikTokEventHandler;
|
||||
this.tikTokLiveClient = tikTokLiveClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ByteBuffer bytes)
|
||||
{
|
||||
public void onMessage(ByteBuffer bytes) {
|
||||
try {
|
||||
handleBinary(bytes.array());
|
||||
} catch (Exception e) {
|
||||
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e));
|
||||
}
|
||||
if(isNotClosing())
|
||||
{
|
||||
sendPing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onOpen(ServerHandshake serverHandshake) {
|
||||
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
|
||||
if(isNotClosing())
|
||||
{
|
||||
sendPing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onClose(int i, String s, boolean b) {
|
||||
tikTokEventHandler.publish(tikTokLiveClient,new TikTokDisconnectedEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error)
|
||||
{
|
||||
tikTokEventHandler.publish(tikTokLiveClient,new TikTokErrorEvent(error));
|
||||
if(isNotClosing())
|
||||
{
|
||||
if (isNotClosing()) {
|
||||
sendPing();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBinary(byte[] buffer) {
|
||||
var websocketMessageOptional = getWebcastWebsocketMessage(buffer);
|
||||
if (websocketMessageOptional.isEmpty()) {
|
||||
var websocketPushFrameOptional = getWebcastPushFrame(buffer);
|
||||
if (websocketPushFrameOptional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var websocketMessage = websocketMessageOptional.get();
|
||||
var webResponse = getWebResponseMessage(websocketMessage.getPayload());
|
||||
var websocketPushFrame = websocketPushFrameOptional.get();
|
||||
var webcastResponse = getWebResponseMessage(websocketPushFrame.getPayload());
|
||||
|
||||
if(webResponse.getNeedsAck())
|
||||
{
|
||||
//For some reason while send ack id, server get disconnected
|
||||
// sendAckId(webResponse.getFetchInterval());
|
||||
if (webcastResponse.getNeedsAck()) {
|
||||
var pushFrameBuilder = WebcastPushFrame.newBuilder();
|
||||
pushFrameBuilder.setPayloadType("ack");
|
||||
pushFrameBuilder.setLogId(websocketPushFrame.getLogId());
|
||||
pushFrameBuilder.setPayload(webcastResponse.getInternalExtBytes());
|
||||
if (isNotClosing())
|
||||
{
|
||||
this.send(pushFrameBuilder.build().toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
messageHandler.handle(tikTokLiveClient, webResponse);
|
||||
messageHandler.handle(tikTokLiveClient, webcastResponse);
|
||||
}
|
||||
|
||||
private Optional<WebcastPushFrame> getWebcastWebsocketMessage(byte[] buffer) {
|
||||
|
||||
@Override
|
||||
public void onOpen(ServerHandshake serverHandshake) {
|
||||
tikTokEventHandler.publish(tikTokLiveClient, new TikTokConnectedEvent());
|
||||
if (isNotClosing()) {
|
||||
sendPing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClose(int i, String s, boolean b) {
|
||||
tikTokEventHandler.publish(tikTokLiveClient, new TikTokDisconnectedEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(error));
|
||||
if (isNotClosing()) {
|
||||
sendPing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Optional<WebcastPushFrame> getWebcastPushFrame(byte[] buffer) {
|
||||
try {
|
||||
var websocketMessage = WebcastPushFrame.parseFrom(buffer);
|
||||
if (websocketMessage.getPayload().isEmpty()) {
|
||||
@@ -139,25 +137,10 @@ public class TikTokWebSocketListener extends WebSocketClient {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotClosing()
|
||||
{
|
||||
private boolean isNotClosing() {
|
||||
return !isClosed() && !isClosing();
|
||||
}
|
||||
|
||||
private void sendAckId(long id) {
|
||||
var serverInfo = WebcastWebsocketAck
|
||||
.newBuilder()
|
||||
.setType("ack")
|
||||
.setId(id)
|
||||
.build();
|
||||
if(isNotClosing())
|
||||
{
|
||||
System.out.println("SEND ICK ID "+id);
|
||||
send(serverInfo.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onMessage(String s) {
|
||||
|
||||
@@ -1,81 +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.websocket;
|
||||
|
||||
import org.java_websocket.WebSocket;
|
||||
import java.util.Random;
|
||||
|
||||
public class TikTokWebSocketPingingTask
|
||||
{
|
||||
private Thread thread;
|
||||
private boolean isRunning = false;
|
||||
private final int MIN_TIMEOUT = 5;
|
||||
private final int MAX_TIMEOUT = 100;
|
||||
|
||||
|
||||
public void run(WebSocket webSocket)
|
||||
{
|
||||
stop();
|
||||
thread = new Thread(() ->
|
||||
{
|
||||
pingTask(webSocket);
|
||||
});
|
||||
isRunning =true;
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
if(thread != null)
|
||||
{
|
||||
thread.interrupt();
|
||||
}
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
|
||||
private void pingTask(WebSocket webSocket)
|
||||
{
|
||||
var random = new Random();
|
||||
while (isRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!webSocket.isOpen())
|
||||
{
|
||||
Thread.sleep(100);
|
||||
continue;
|
||||
}
|
||||
webSocket.sendPing();
|
||||
|
||||
var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT;
|
||||
Thread.sleep(timeout);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.handlers.events;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
@@ -49,42 +50,43 @@ class TikTokGiftEventHandlerTest {
|
||||
@BeforeAll
|
||||
public void before() {
|
||||
var manager = new TikTokGiftManager(Logger.getLogger("x"));
|
||||
var info = new TikTokRoomInfo();
|
||||
info.setHost(new io.github.jwdeveloper.tiktok.data.models.users.User(123L, "test", new Picture("")));
|
||||
manager.registerGift(123, "example", 123, new Picture("image.webp"));
|
||||
handler = new TikTokGiftEventHandler(manager);
|
||||
handler = new TikTokGiftEventHandler(manager, info);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleGifts() {
|
||||
var message = getGiftMessage("example-new-name", 123, "image-new.png", 0, 1,false);
|
||||
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());
|
||||
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 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());
|
||||
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);
|
||||
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);
|
||||
@@ -96,9 +98,9 @@ class TikTokGiftEventHandlerTest {
|
||||
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());
|
||||
Assertions.assertEquals(GiftSendType.Begin, event1.getComboState());
|
||||
Assertions.assertEquals(GiftSendType.Active, event2.getComboState());
|
||||
Assertions.assertEquals(GiftSendType.Finished, event3.getComboState());
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +118,7 @@ class TikTokGiftEventHandlerTest {
|
||||
giftBuilder.setId(giftId);
|
||||
giftBuilder.setName(giftName);
|
||||
giftBuilder.setImage(Image.newBuilder().addUrlList(giftImage).build());
|
||||
giftBuilder.setType(streakable?1:0);
|
||||
giftBuilder.setType(streakable ? 1 : 0);
|
||||
userBuilder.setId(userId);
|
||||
|
||||
builder.setGiftId(giftId);
|
||||
@@ -127,5 +129,4 @@ class TikTokGiftEventHandlerTest {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,133 +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.http;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
|
||||
import io.github.jwdeveloper.tiktok.mappers.LiveRoomMetaMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class TikTokApiServiceTest
|
||||
{
|
||||
@Mock
|
||||
TikTokHttpClient tiktokHttpClient;
|
||||
|
||||
@Mock
|
||||
Logger logger;
|
||||
|
||||
@Mock
|
||||
ClientSettings clientSettings;
|
||||
|
||||
@InjectMocks
|
||||
TikTokApiService tikTokApiService;
|
||||
|
||||
@Test
|
||||
void updateSessionId_NullSessionId_DoesNotSetSessionId() {
|
||||
when(clientSettings.getSessionId()).thenReturn(null);
|
||||
|
||||
tikTokApiService.updateSessionId();
|
||||
|
||||
verify(tiktokHttpClient, times(0)).setSessionId(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateSessionId_EmptySessionId_DoesNotSetSessionId() {
|
||||
when(clientSettings.getSessionId()).thenReturn("");
|
||||
|
||||
tikTokApiService.updateSessionId();
|
||||
|
||||
verify(tiktokHttpClient, times(0)).setSessionId(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateSessionId_ValidSessionId_SetsSessionId() {
|
||||
when(clientSettings.getSessionId()).thenReturn("validSessionId");
|
||||
|
||||
tikTokApiService.updateSessionId();
|
||||
|
||||
verify(tiktokHttpClient, times(1)).setSessionId("validSessionId");
|
||||
}
|
||||
|
||||
|
||||
// @Test
|
||||
void fetchRoomId_ValidResponse_ReturnsRoomId() throws Exception {
|
||||
String expectedRoomId = "123456";
|
||||
String htmlResponse = "room_id=" + expectedRoomId ;
|
||||
when(tiktokHttpClient.getLivestreamPage(anyString())).thenReturn(htmlResponse);
|
||||
|
||||
String roomId = tikTokApiService.fetchRoomId("username");
|
||||
|
||||
assertEquals(expectedRoomId, roomId);
|
||||
verify(clientSettings.getClientParameters()).put("room_id", expectedRoomId);
|
||||
}
|
||||
|
||||
// @Test
|
||||
void fetchRoomId_ExceptionThrown_ThrowsTikTokLiveRequestException() throws Exception {
|
||||
when(tiktokHttpClient.getLivestreamPage(anyString())).thenThrow(new Exception("some exception"));
|
||||
|
||||
assertThrows(TikTokLiveRequestException.class, () -> {
|
||||
tikTokApiService.fetchRoomId("username");
|
||||
});
|
||||
}
|
||||
|
||||
//@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.getJsonFromWebcastApi(anyString(), any())).thenReturn(mockResponse);
|
||||
when(new LiveRoomMetaMapper().map(mockResponse)).thenReturn(expectedLiveRoomMeta); // Assuming LiveRoomMetaMapper is a simple mapper class
|
||||
|
||||
LiveRoomMeta liveRoomMeta = tikTokApiService.fetchRoomInfo();
|
||||
|
||||
assertEquals(expectedLiveRoomMeta, liveRoomMeta);
|
||||
}
|
||||
|
||||
// @Test
|
||||
void fetchRoomInfo_ExceptionThrown_ThrowsTikTokLiveRequestException() throws Exception {
|
||||
when(tiktokHttpClient.getJsonFromWebcastApi(anyString(), any())).thenThrow(new Exception("some exception"));
|
||||
|
||||
assertThrows(TikTokLiveRequestException.class, () -> {
|
||||
tikTokApiService.fetchRoomInfo();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.http;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.TikTokLive;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class TikTokLiveOnlineCheckerTest {
|
||||
|
||||
public boolean enableTests = false;
|
||||
|
||||
@Test
|
||||
public void shouldTestOnline() {
|
||||
|
||||
if(!enableTests)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var TARGET_USER = "bangbetmenygy";
|
||||
var sut = new TikTokDataChecker();
|
||||
var result = sut.isOnline(TARGET_USER);
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeOffline() {
|
||||
|
||||
var TARGET_USER = "karacomparetto";
|
||||
var sut = new TikTokDataChecker();
|
||||
var result = sut.isOnline(TARGET_USER);
|
||||
|
||||
Assertions.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeValid() throws InterruptedException {
|
||||
|
||||
var TARGET_USER = "dostawcavideo";
|
||||
var sut = new TikTokDataChecker();
|
||||
var result = sut.isHostNameValid(TARGET_USER);
|
||||
|
||||
|
||||
TikTokLive.newClient("asdasd")
|
||||
.onWebsocketResponse((liveClient, event) ->
|
||||
{
|
||||
for(var message : event.getResponse().getMessagesList())
|
||||
{
|
||||
if(message.getMethod().equals("WebcastGiftMessage"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytes = message.getMethodBytes();
|
||||
var rawMessage = WebcastGiftMessage.parseFrom(bytes);
|
||||
var giftName =rawMessage.getGift().getName();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotBeValid() {
|
||||
var TARGET_USER = "dqagdagda , asdaaasd";
|
||||
var sut = new TikTokDataChecker();
|
||||
var result = sut.isHostNameValid(TARGET_USER);
|
||||
|
||||
Assertions.assertFalse(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.listener;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||
@@ -41,12 +42,12 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
class TikTokListenersManagerTest {
|
||||
|
||||
private io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver eventObserver;
|
||||
private TikTokLiveEventHandler eventObserver;
|
||||
private TikTokListenersManager tikTokListenersManager;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
eventObserver = Mockito.mock(io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver.class);
|
||||
eventObserver = Mockito.mock(TikTokLiveEventHandler.class);
|
||||
List<TikTokEventListener> listeners = new ArrayList<>();
|
||||
tikTokListenersManager = new TikTokListenersManager(listeners, eventObserver);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.0.9-Release</version>
|
||||
<version>1.0.14-Release</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -20,44 +20,36 @@
|
||||
* 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.tools.tester.mockClient.mocks;
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
||||
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import java.time.Duration;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ApiServiceMock extends TikTokApiService {
|
||||
public class ChatMessageExample {
|
||||
public static void main(String[] args) {
|
||||
|
||||
|
||||
public ApiServiceMock(TikTokHttpClient apiClient, Logger logger, ClientSettings clientSettings) {
|
||||
super(apiClient, logger, clientSettings);
|
||||
}
|
||||
var roomData = TikTokLive.requests()
|
||||
.fetchLiveData("X");
|
||||
|
||||
@Override
|
||||
public void updateSessionId() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveRoomMeta fetchRoomInfo()
|
||||
{
|
||||
var meta = new LiveRoomMeta();
|
||||
meta.setStatus(LiveRoomMeta.LiveRoomStatus.HostOnline);
|
||||
return meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebcastResponse fetchClientData() {
|
||||
return WebcastResponse.newBuilder().build();
|
||||
}
|
||||
var gifts = TikTokLive.requests().fetchGiftsData();
|
||||
|
||||
|
||||
@Override
|
||||
public String fetchRoomId(String userName) {
|
||||
return "mock-room-id";
|
||||
var user = TikTokLive.requests()
|
||||
.fetchLiveUserData("mark");
|
||||
|
||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||
.configure(clientSettings ->
|
||||
{
|
||||
clientSettings.setPrintToConsole(true);
|
||||
clientSettings.getHttpSettings().setTimeout(Duration.ofSeconds(21));
|
||||
})
|
||||
.onComment((liveClient, event) ->
|
||||
{
|
||||
System.out.println("Chat message: " + event.getUser().getName() + " " + event.getText());
|
||||
})
|
||||
.onWebsocketUnhandledMessage((liveClient, event) ->
|
||||
{
|
||||
liveClient.getLogger().info(event.getMessage().getMethod());
|
||||
}).buildAndConnect();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
public class CustomEventExample {
|
||||
@AllArgsConstructor
|
||||
public static class CheapGiftEvent extends TikTokEvent {
|
||||
Gift gift;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public static class ExpensiveGiftEvent extends TikTokEvent {
|
||||
Gift gift;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||
.configure(clientSettings ->
|
||||
{
|
||||
clientSettings.setPrintToConsole(true);
|
||||
})
|
||||
|
||||
.onGift((liveClient, event) ->
|
||||
{
|
||||
if (event.getGift().getDiamondCost() > 100)
|
||||
liveClient.publishEvent(new ExpensiveGiftEvent(event.getGift()));
|
||||
else
|
||||
liveClient.publishEvent(new CheapGiftEvent(event.getGift()));
|
||||
})
|
||||
.onEvent(CheapGiftEvent.class, (liveClient, event) ->
|
||||
{
|
||||
System.out.println("Thanks for cheap gift");
|
||||
})
|
||||
.onEvent(ExpensiveGiftEvent.class, (liveClient, event) ->
|
||||
{
|
||||
System.out.println("Thanks for expensive gift!");
|
||||
})
|
||||
.buildAndConnect();
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.TikTokSubNotifyEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.TikTokSubscribeEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
@@ -31,56 +32,20 @@ import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SimpleExample {
|
||||
public static String TIKTOK_HOSTNAME = "bangbetmenygy";
|
||||
public static String TIKTOK_HOSTNAME = "dash4214";
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
|
||||
showLogo();
|
||||
// set tiktok username
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
//Optional checking if host name is correct
|
||||
if(TikTokLive.isHostNameValid(TIKTOK_HOSTNAME))
|
||||
{
|
||||
System.out.println("user name exists!");
|
||||
}
|
||||
*/
|
||||
|
||||
// Optional checking if live is online
|
||||
if (TikTokLive.isLiveOnline(TIKTOK_HOSTNAME)) {
|
||||
System.out.println("Live is online!");
|
||||
}
|
||||
|
||||
|
||||
TikTokLive.newClient("test")
|
||||
.onWebsocketResponse((liveClient, event) ->
|
||||
{
|
||||
var response = event.getResponse();
|
||||
for (var message : response.getMessagesList()) {
|
||||
var name = message.getMethod();
|
||||
var binaryData = message.getPayload();
|
||||
System.out.println("Event name: " + name);
|
||||
if (name.equals("WebcastGiftEvent")) {
|
||||
try {
|
||||
WebcastGiftMessage giftMessage = WebcastGiftMessage.parseFrom(binaryData);
|
||||
var giftName = giftMessage.getGift().getName();
|
||||
var giftId = giftMessage.getGiftId();
|
||||
var userName = giftMessage.getUser().getNickname();
|
||||
System.out.println("Gift: "+giftName+" id: "+giftId+" from user: "+userName);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Mapping error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).buildAndConnect();
|
||||
|
||||
|
||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||
@@ -88,12 +53,14 @@ public class SimpleExample {
|
||||
{
|
||||
clientSettings.setHostName(SimpleExample.TIKTOK_HOSTNAME); // This method is useful in case you want change hostname later
|
||||
clientSettings.setClientLanguage("en"); // Language
|
||||
clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
|
||||
clientSettings.setLogLevel(Level.ALL); // Log level
|
||||
clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
|
||||
clientSettings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
|
||||
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
|
||||
|
||||
|
||||
clientSettings.getHttpSettings();
|
||||
|
||||
//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
|
||||
|
||||
@@ -105,15 +72,6 @@ public class SimpleExample {
|
||||
|
||||
//clientSettings.setRoomId("XXXXXXXXXXXXXXXXX");
|
||||
})
|
||||
.onConnected((liveClient, event) ->
|
||||
{
|
||||
for (var gift : liveClient.getGiftManager().getGifts()) {
|
||||
gift.getPicture().downloadImageAsync().thenAccept(image ->
|
||||
{
|
||||
|
||||
});
|
||||
}
|
||||
})
|
||||
.onWebsocketMessage((liveClient, event) ->
|
||||
{
|
||||
|
||||
@@ -124,9 +82,14 @@ public class SimpleExample {
|
||||
}
|
||||
|
||||
})
|
||||
.onWebsocketResponse((liveClient, event) ->
|
||||
.onEvent((liveClient, event) ->
|
||||
{
|
||||
event.getResponse();
|
||||
if (event instanceof TikTokGiftEvent giftEvent) {
|
||||
System.out.println("1");
|
||||
}
|
||||
if (event instanceof TikTokChestEvent chestEvent) {
|
||||
System.out.println("2");
|
||||
}
|
||||
})
|
||||
.onGift((liveClient, event) ->
|
||||
{
|
||||
@@ -142,7 +105,7 @@ public class SimpleExample {
|
||||
{
|
||||
print(ConsoleColors.RED, "GIFT COMBO", event.getGift().getName(), event.getCombo());
|
||||
})
|
||||
.onConnected((client, event) ->
|
||||
.onConnected((liveClient, event) ->
|
||||
{
|
||||
print(ConsoleColors.GREEN, "[Connected]");
|
||||
})
|
||||
@@ -158,19 +121,19 @@ public class SimpleExample {
|
||||
{
|
||||
print(ConsoleColors.BLUE, "Follow:", ConsoleColors.WHITE_BRIGHT, event.getUser().getName());
|
||||
})
|
||||
.onJoin((client, event) ->
|
||||
.onJoin((liveClient, event) ->
|
||||
{
|
||||
print(ConsoleColors.WHITE, "Join:", ConsoleColors.WHITE_BRIGHT, event.getUser().getName());
|
||||
})
|
||||
.onComment((client, event) ->
|
||||
.onComment((liveClient, event) ->
|
||||
{
|
||||
print(ConsoleColors.GREEN, event.getUser().getName(), ":", ConsoleColors.WHITE_BRIGHT, event.getText());
|
||||
})
|
||||
.onEvent((client, event) ->
|
||||
.onEvent((liveClient, event) ->
|
||||
{
|
||||
//System.out.println("Event: " +event.getClass().getSimpleName());
|
||||
})
|
||||
.onError((client, event) ->
|
||||
.onError((liveClient, event) ->
|
||||
{
|
||||
event.getException().printStackTrace();
|
||||
})
|
||||
|
||||
Binary file not shown.
@@ -69,7 +69,7 @@ Maven
|
||||
<dependency>
|
||||
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
|
||||
<artifactId>Client</artifactId>
|
||||
<version>1.0.8-Release</version>
|
||||
<version>1.0.15-Release</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -86,7 +86,7 @@ dependencyResolutionManagement {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.0.8-Release'
|
||||
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.0.15-Release'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -693,4 +693,4 @@ public static class CustomListener implements TikTokEventListener {
|
||||
|
||||
|
||||
## 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>.
|
||||
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>.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.0.9-Release</version>
|
||||
<version>1.0.14-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -26,14 +26,10 @@ import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
|
||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
||||
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.TikTokLiveMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.ApiServiceMock;
|
||||
import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.LiveClientMock;
|
||||
import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.WebsocketClientMock;
|
||||
|
||||
@@ -87,21 +83,17 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder {
|
||||
public LiveClientMock build() {
|
||||
validate();
|
||||
|
||||
var cookie = new TikTokCookieJar();
|
||||
var tiktokRoomInfo = new TikTokRoomInfo();
|
||||
tiktokRoomInfo.setHostName(clientSettings.getHostName());
|
||||
|
||||
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
||||
var giftManager = new TikTokGiftManager(logger);
|
||||
var requestFactory = new TikTokHttpRequestFactory(cookie, new TikTokEventObserver());
|
||||
var apiClient = new TikTokHttpClient(cookie, requestFactory);
|
||||
var apiService = new ApiServiceMock(apiClient, logger, clientSettings);
|
||||
var mapper = createMapper(giftManager, tiktokRoomInfo);
|
||||
var handler = new TikTokMessageHandler(tikTokEventHandler, mapper);
|
||||
var handler = new TikTokLiveMessageHandler(tikTokEventHandler, mapper);
|
||||
var webSocketClient = new WebsocketClientMock(logger, responses, handler);
|
||||
|
||||
return new LiveClientMock(tiktokRoomInfo,
|
||||
apiService,
|
||||
new TikTokLiveHttpClient(),
|
||||
webSocketClient,
|
||||
giftManager,
|
||||
tikTokEventHandler,
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveClient;
|
||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
@@ -37,16 +37,18 @@ public class LiveClientMock extends TikTokLiveClient {
|
||||
|
||||
private final WebsocketClientMock websocketClientMock;
|
||||
|
||||
public LiveClientMock(TikTokRoomInfo tikTokLiveMeta,
|
||||
TikTokApiService tikTokApiService,
|
||||
WebsocketClientMock webSocketClient,
|
||||
TikTokGiftManager tikTokGiftManager,
|
||||
TikTokEventObserver tikTokEventHandler,
|
||||
ClientSettings clientSettings,
|
||||
TikTokListenersManager listenersManager,
|
||||
Logger logger) {
|
||||
super(tikTokLiveMeta,
|
||||
tikTokApiService,
|
||||
public LiveClientMock(
|
||||
TikTokRoomInfo tikTokLiveMeta,
|
||||
TikTokLiveHttpClient httpClient,
|
||||
WebsocketClientMock webSocketClient,
|
||||
TikTokGiftManager tikTokGiftManager,
|
||||
TikTokLiveEventHandler tikTokEventHandler,
|
||||
LiveClientSettings clientSettings,
|
||||
TikTokListenersManager listenersManager,
|
||||
Logger logger) {
|
||||
super(
|
||||
tikTokLiveMeta,
|
||||
httpClient,
|
||||
webSocketClient,
|
||||
tikTokGiftManager,
|
||||
tikTokEventHandler,
|
||||
@@ -58,7 +60,6 @@ public class LiveClientMock extends TikTokLiveClient {
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void publishMessage(String type, String base64) {
|
||||
websocketClientMock.addMessage(type, base64);
|
||||
}
|
||||
|
||||
@@ -22,10 +22,8 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
|
||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import io.github.jwdeveloper.tiktok.websocket.SocketClient;
|
||||
@@ -40,7 +38,7 @@ public class WebsocketClientMock implements SocketClient {
|
||||
Logger logger;
|
||||
Stack<WebcastResponse> responses;
|
||||
Stack<MsgStruct> messages;
|
||||
TikTokMessageHandler messageHandler;
|
||||
TikTokLiveMessageHandler messageHandler;
|
||||
|
||||
LiveClient client;
|
||||
|
||||
@@ -58,7 +56,7 @@ public class WebsocketClientMock implements SocketClient {
|
||||
byte[] messageValue;
|
||||
}
|
||||
|
||||
public WebsocketClientMock(Logger logger, Stack<WebcastResponse> responses, TikTokMessageHandler messageHandler) {
|
||||
public WebsocketClientMock(Logger logger, Stack<WebcastResponse> responses, TikTokLiveMessageHandler messageHandler) {
|
||||
this.logger = logger;
|
||||
this.responses = responses;
|
||||
this.messageHandler = messageHandler;
|
||||
@@ -91,8 +89,9 @@ public class WebsocketClientMock implements SocketClient {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void start(WebcastResponse webcastResponse, LiveClient tikTokLiveClient) {
|
||||
public void start(LiveConnectionData.Response webcastResponse, LiveClient tikTokLiveClient) {
|
||||
logger.info("Running message: " + responses.size());
|
||||
|
||||
|
||||
@@ -110,7 +109,6 @@ public class WebsocketClientMock implements SocketClient {
|
||||
});
|
||||
isRunning = true;
|
||||
thread.start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
package io.github.jwdeveloper.tiktok.tools.util;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
||||
@@ -34,8 +35,8 @@ public class MessageUtil {
|
||||
var methodName = message.getMethod();
|
||||
var inputClazz = Class.forName("io.github.jwdeveloper.tiktok.messages.webcast." + methodName);
|
||||
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", ByteString.class);
|
||||
var deserialized = parseMethod.invoke(null, message.getPayload());
|
||||
return JsonUtil.messageToJson(deserialized);
|
||||
var webcastObject = parseMethod.invoke(null, message.getPayload());
|
||||
return JsonUtil.messageToJson(webcastObject);
|
||||
} catch (Exception ex) {
|
||||
|
||||
return ConsoleColors.RED + "Can not find mapper for " + message.getMethod();
|
||||
@@ -48,6 +49,7 @@ public class MessageUtil {
|
||||
var inputClazz = Class.forName("io.github.jwdeveloper.tiktok.messages.webcast." + messageName);
|
||||
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", byte[].class);
|
||||
var deserialized = parseMethod.invoke(null, bytes);
|
||||
|
||||
return JsonUtil.messageToJson(deserialized);
|
||||
} catch (Exception ex) {
|
||||
var sb = new StringBuilder();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.0.9-Release</version>
|
||||
<version>1.0.14-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>Tools-EventsWebViewer</artifactId>
|
||||
|
||||
@@ -37,7 +37,8 @@ import java.sql.SQLException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws SQLException, ExecutionException, InterruptedException, IOException {
|
||||
public static void main(String[] args) throws SQLException
|
||||
{
|
||||
var settings = new Settings();
|
||||
settings.setUserName("szalonamoniaxx");
|
||||
settings.setSessionTag("battle");
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.webviewer;
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
package io.github.jwdeveloper.tiktok.webviewer;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkLayerMessage;
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMessage;
|
||||
@@ -32,11 +33,11 @@ import java.io.IOException;
|
||||
|
||||
public class ToolsExamples {
|
||||
|
||||
private static final String tiktokUser = "debb.cl";
|
||||
private static final String tiktokUser = "k.peaks";
|
||||
|
||||
private static final String db = "db-battle";
|
||||
private static final String db = "a";
|
||||
|
||||
private static final String sessionTag = "gifts";
|
||||
private static final String sessionTag = "a";
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
// runCollector();
|
||||
@@ -53,6 +54,7 @@ public class ToolsExamples {
|
||||
//WebcastLinkMicBattlePunishFinish end of battle?
|
||||
//WebcastLinkLayerMessage send after end of battle
|
||||
// send after LinkLayer -> WebcastLinkMessage
|
||||
|
||||
private static void runCollector() {
|
||||
TikTokLiveTools.createCollector(db)
|
||||
.addUser(tiktokUser)
|
||||
@@ -60,15 +62,17 @@ public class ToolsExamples {
|
||||
.configureLiveClient(liveClientBuilder ->
|
||||
{
|
||||
liveClientBuilder.configure(clientSettings ->
|
||||
{
|
||||
clientSettings.setPrintToConsole(true);
|
||||
});
|
||||
liveClientBuilder.onWebsocketResponse((liveClient, event) ->
|
||||
{
|
||||
for (var msg : event.getResponse().getMessagesList()) {
|
||||
System.out.println(msg.getMethod());
|
||||
}
|
||||
});
|
||||
{
|
||||
clientSettings.setPrintToConsole(true);
|
||||
})
|
||||
.onComment((liveClient, event) ->
|
||||
{
|
||||
System.out.println("Chat message: " + event.getUser().getName() + " " + event.getText());
|
||||
})
|
||||
.onWebsocketUnhandledMessage((liveClient, event) ->
|
||||
{
|
||||
liveClient.getLogger().info(event.getMessage().getMethod());
|
||||
});
|
||||
liveClientBuilder.onConnected((liveClient, event) ->
|
||||
{
|
||||
liveClient.getLogger().info("Connected");
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.webviewer.services;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.tools.TikTokLiveTools;
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.webviewer.services;
|
||||
|
||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||
|
||||
@@ -25,7 +25,6 @@ var data =
|
||||
}
|
||||
|
||||
|
||||
|
||||
dropDown("usersDropDown", () => `${baseUrl}/tiktok/users`, async (e) => {
|
||||
data.user = e;
|
||||
update();
|
||||
@@ -51,28 +50,15 @@ function update() {
|
||||
}
|
||||
|
||||
async function updateAsync() {
|
||||
console.log(data);
|
||||
console.log("Updating", data);
|
||||
await updateConnectionButton()
|
||||
await updateDataNamesList(async (dataName) => {
|
||||
data.dataName = dataName;
|
||||
data.page = 0;
|
||||
await updateContent();
|
||||
await updateContent(`${baseUrl}/tiktok/data?name=${data.dataName}&type=${data.dataType}&page=${data.page}`);
|
||||
await updatePagination(async (page, link) => {
|
||||
data.page = page;
|
||||
let response = await fetch(link);
|
||||
let json = await response.text();
|
||||
console.log(link)
|
||||
let root = JSON.parse(json);
|
||||
editor.setValue(root.content);
|
||||
|
||||
if(data.dataType === 'message')
|
||||
{
|
||||
console.log("sending proto version")
|
||||
let response2 = await fetch(`${link}&asProto=true`);
|
||||
let json2 = await response2.text();
|
||||
let root2 = JSON.parse(json2);
|
||||
editor2.setValue(root2.content);
|
||||
}
|
||||
await updateContent(link)
|
||||
});
|
||||
});
|
||||
await fetch(`${baseUrl}/tiktok/update?user=${data.user}&session=${data.sessionTag}`);
|
||||
@@ -100,21 +86,30 @@ async function updatePagination(onSelect) {
|
||||
});
|
||||
}
|
||||
|
||||
async function updateContent() {
|
||||
async function updateContent(link) {
|
||||
console.log("updating content", data)
|
||||
let response = await fetch(`${baseUrl}/tiktok/data?name=${data.dataName}&type=${data.dataType}&page=${data.page}`);
|
||||
let response = await fetch(link);
|
||||
let json = await response.text();
|
||||
console.log(link)
|
||||
let root = JSON.parse(json);
|
||||
editor.setValue(root.content);
|
||||
|
||||
|
||||
if(data.dataType === 'message')
|
||||
{
|
||||
$("#editor2").hide()
|
||||
if (data.dataType === 'message') {
|
||||
console.log("sending proto version")
|
||||
let response2 = await fetch(`${baseUrl}/tiktok/data?name=${data.dataName}&type=${data.dataType}&page=${data.page}&asProto=true`);
|
||||
let response2 = await fetch(`${link}&asProto=true`);
|
||||
let json2 = await response2.text();
|
||||
let root2 = JSON.parse(json2);
|
||||
editor2.setValue(root2.content);
|
||||
$("#editor2").show()
|
||||
}
|
||||
|
||||
if (data.dataType === 'response' && data.dataName === 'Http') {
|
||||
|
||||
var content = JSON.parse(root.content);
|
||||
var body = JSON.parse(content.request.body)
|
||||
var asJson = JSON.stringify(body, null, 2)
|
||||
editor2.setValue(asJson);
|
||||
$("#editor2").show()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -194,9 +189,9 @@ async function updateConnectionButton() {
|
||||
|
||||
async function connect() {
|
||||
let name = document.getElementById('name').value;
|
||||
let session = document.getElementById('sessionTag').value;
|
||||
let session = document.getElementById('sessionTag').value;
|
||||
data.collector.name = name
|
||||
data.collector.sessionTag =session
|
||||
data.collector.sessionTag = session
|
||||
|
||||
let response = await fetch(`${baseUrl}/tiktok/connect?name=${data.collector.name}&session=${data.collector.sessionTag}`);
|
||||
let greeting = await response.text();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.0.9-Release</version>
|
||||
<version>1.0.14-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@ public class CodeExample {
|
||||
{
|
||||
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
|
||||
|
||||
@@ -83,6 +83,17 @@ Do you prefer other programming languages?
|
||||
|
||||
{{events-content}}
|
||||
|
||||
{{for item of data }}
|
||||
|
||||
{{if item is 2}}
|
||||
|
||||
my name is {{item.name}}
|
||||
|
||||
{{else}}
|
||||
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
<br>
|
||||
|
||||
## Listeners
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>TikTokLiveJava</artifactId>
|
||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||
<version>1.0.9-Release</version>
|
||||
<version>1.0.14-Release</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ package io.github.jwdeveloper.tiktok.gifts;
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLive;
|
||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftDto;
|
||||
import lombok.Getter;
|
||||
@@ -32,6 +33,7 @@ import lombok.Getter;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -42,10 +44,17 @@ public class GenerateGiftsEnum {
|
||||
public static void main(String args[]) throws IOException {
|
||||
|
||||
|
||||
|
||||
TikTokLive.newClient("X")
|
||||
.configure(liveClientSettings ->
|
||||
{
|
||||
var httpSetting = liveClientSettings.getHttpSettings();
|
||||
httpSetting.setTimeout(Duration.ofSeconds(12));
|
||||
});
|
||||
|
||||
var downloader = new GiftsDownloader();
|
||||
var gifts = downloader.getGiftsFromFile();
|
||||
for(var link : gifts)
|
||||
{
|
||||
for (var link : gifts) {
|
||||
System.out.println(link.getImage());
|
||||
}
|
||||
var groupedByName = gifts.stream().collect(Collectors.groupingBy(GiftDto::getName));
|
||||
@@ -105,9 +114,16 @@ public class GenerateGiftsEnum {
|
||||
.replace("'", "_")
|
||||
.replace(".", "_")
|
||||
.replace("-", "_")
|
||||
.replace("!","_")
|
||||
.replace("&", "_")
|
||||
.replace("!", "_")
|
||||
.toUpperCase();
|
||||
|
||||
|
||||
boolean startsWithNumber = name.matches("^[0-9].*");
|
||||
if (startsWithNumber) {
|
||||
name = "_" + name;
|
||||
}
|
||||
|
||||
if (isNumeric(name)) {
|
||||
name = "_" + name;
|
||||
}
|
||||
@@ -141,8 +157,7 @@ public class GenerateGiftsEnum {
|
||||
}
|
||||
}
|
||||
|
||||
public static TypeSpec addGift(int id, String name, int diamond, String picture)
|
||||
{
|
||||
public static TypeSpec addGift(int id, String name, int diamond, String picture) {
|
||||
return TypeSpec.anonymousClassBuilder(
|
||||
"$L, $S, $L, $S",
|
||||
id,
|
||||
|
||||
@@ -68,10 +68,10 @@ public class GiftsDownloader {
|
||||
var officialGifts = officalGift.run();
|
||||
System.out.println("Official Gifts: " + officialGifts.size());
|
||||
|
||||
System.out.println("Downlooading Official Gifts");
|
||||
System.out.println("Downlooading GiftExtraJson Gifts");
|
||||
var extraGiftsJson = new GiftExtraJson();
|
||||
var extraGifts = extraGiftsJson.run();
|
||||
System.out.println("Official Gifts: " + extraGifts.size());
|
||||
System.out.println("GiftExtraJson Gifts: " + extraGifts.size());
|
||||
|
||||
var outputHashMap = new TreeMap<Integer, GiftDto>();
|
||||
for (var gift : scraperGifts) {
|
||||
@@ -86,7 +86,7 @@ public class GiftsDownloader {
|
||||
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_15.json", json);
|
||||
System.out.println("Gifts saved to file!");
|
||||
return outputHashMap.values().stream().toList();
|
||||
}
|
||||
|
||||
@@ -23,15 +23,8 @@
|
||||
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;
|
||||
|
||||
|
||||
@@ -22,21 +22,14 @@
|
||||
*/
|
||||
package io.github.jwdeveloper.tiktok.gifts.downloader;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import io.github.jwdeveloper.tiktok.Constants;
|
||||
import io.github.jwdeveloper.tiktok.TikTokLive;
|
||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
||||
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;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GiftOfficialJson {
|
||||
|
||||
@@ -45,63 +38,30 @@ public class GiftOfficialJson {
|
||||
}
|
||||
|
||||
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("diamond_count").getAsInt();
|
||||
var image = jsonElement.getAsJsonObject()
|
||||
.get("image").getAsJsonObject()
|
||||
.get("url_list").getAsJsonArray().get(0).getAsString();
|
||||
|
||||
if(image.endsWith(".webp"))
|
||||
{
|
||||
image = image.replace(".webp",".jpg");
|
||||
}
|
||||
var gift = new GiftDto();
|
||||
gift.setId(id);
|
||||
gift.setName(name);
|
||||
gift.setDiamondCost(diamondCost);
|
||||
gift.setImage(image);
|
||||
return gift;
|
||||
}
|
||||
|
||||
public static JsonArray getJsonGifts() {
|
||||
var jar = new TikTokCookieJar();
|
||||
var tiktokHttpClient = new TikTokHttpClient(jar, new TikTokHttpRequestFactory(jar, new TikTokEventObserver()));
|
||||
var settings = Constants.DefaultClientSettings();
|
||||
|
||||
|
||||
var dtf = DateTimeFormatter.ofPattern("dd/MM/yyyy");
|
||||
var now = LocalDateTime.now();
|
||||
var date = now.format(dtf).replace("/", "_");
|
||||
var fileName = "official_" + date + ".json";
|
||||
|
||||
try {
|
||||
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")) {
|
||||
return new JsonArray();
|
||||
}
|
||||
var dataJson = response.getAsJsonObject("data");
|
||||
if (!dataJson.has("gifts")) {
|
||||
return new JsonArray();
|
||||
}
|
||||
return dataJson.get("gifts").getAsJsonArray();
|
||||
var dtf = DateTimeFormatter.ofPattern("dd/MM/yyyy");
|
||||
var now = LocalDateTime.now();
|
||||
var date = now.format(dtf).replace("/", "_");
|
||||
var fileName = "official_" + date + ".json";
|
||||
|
||||
var httpClient = TikTokLive.requests();
|
||||
var giftsInfo = httpClient.fetchGiftsData();
|
||||
FilesUtility.saveFile("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\official\\" + fileName, giftsInfo.getJson());
|
||||
|
||||
|
||||
return giftsInfo.getGifts().stream().map(e ->
|
||||
{
|
||||
var gift = new GiftDto();
|
||||
gift.setId(e.getId());
|
||||
gift.setImage(e.getImage());
|
||||
gift.setName(e.getName());
|
||||
gift.setDiamondCost(e.getDiamondCost());
|
||||
return gift;
|
||||
}).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
throw new TikTokLiveRequestException("Failed to fetch giftTokens from WebCast, see stacktrace for more info.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -298,5 +298,509 @@
|
||||
"name": "Team Bracelet",
|
||||
"diamondCost": 2,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/54cb1eeca369e5bea1b97707ca05d189.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5514,
|
||||
"name": "Birds",
|
||||
"diamondCost": 600,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/0911b5726d912dabbf6ee4b0383352ea.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5524,
|
||||
"name": "Tsar",
|
||||
"diamondCost": 100,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/cb1c3e6263d4b6c08301f8798dcb5a9b.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5549,
|
||||
"name": "Ballet Dancer",
|
||||
"diamondCost": 500,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c09cc8ce49476d2c46e9c8af6189d5f4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5559,
|
||||
"name": "Crystal Heart",
|
||||
"diamondCost": 499,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ae46ac6582a606009643440fe4138eb4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 5680,
|
||||
"name": "Disco ball",
|
||||
"diamondCost": 1000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/8d0cb854bbe8eeea654f3f9c353c5cf0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6112,
|
||||
"name": "King Cake ",
|
||||
"diamondCost": 9,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/aa99da9f6b499ff879c3860e888a53ae~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6393,
|
||||
"name": "Magic Hat",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b156ffd21bb3849a52144ab1688bbc43~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6426,
|
||||
"name": "Dombra",
|
||||
"diamondCost": 20,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ccd9fea1988521d1e81051a916800d6c~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6452,
|
||||
"name": "Jakarta Roundabout",
|
||||
"diamondCost": 16999,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/31f67910fc5858cf087da65746f1f9f3~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6633,
|
||||
"name": "Independence Day",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b967993872a6e40f3477d30545f8d2eb~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6655,
|
||||
"name": "Summer Iris ",
|
||||
"diamondCost": 30,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/cb591f5b5729fa6e64cac57c78724981~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6741,
|
||||
"name": "Gorgeous Trophy",
|
||||
"diamondCost": 7000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/279c9495c2150e333bc4bc13761d177e~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6756,
|
||||
"name": "Hot",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/ec679890070187b61620b9662afb814e~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6800,
|
||||
"name": "Pinata",
|
||||
"diamondCost": 699,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c8a18d43dc9fb4598d7e991ebeb958ae~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 6967,
|
||||
"name": "Autumn Leaves",
|
||||
"diamondCost": 500,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/30adcaf443df63e3bfd2751ad251f87d~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7032,
|
||||
"name": "Maracas",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/00204efcf0573192ad5d872c7beeaf5b~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7084,
|
||||
"name": "Witchy Kitty",
|
||||
"diamondCost": 30,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/dfce46f99a1206cca84f9092603e4783~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7105,
|
||||
"name": "Magic Potion",
|
||||
"diamondCost": 499,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e055625e9239df7e833702c768e033d2~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7377,
|
||||
"name": "Christmas Market G",
|
||||
"diamondCost": 2000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/f498f29ef628c8318006a9ff2f49bf08~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7458,
|
||||
"name": "Wooly Hat",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a234d0187047fa48805c8ea2e1f1f756~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7475,
|
||||
"name": "Mistletoe GDM 23",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/3527969b8c27e3194e61ff0787a9c3c2~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7477,
|
||||
"name": "Panettone GDM 23",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/64ce2413a362442819b4551703b7b26c~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7498,
|
||||
"name": "Candy Cane Gun",
|
||||
"diamondCost": 799,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/58ef7964e32adc5fc47c5706a02e4ff0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7504,
|
||||
"name": "Holiday Stocking",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e05de50999ebb446e15c4947b30d3140~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7523,
|
||||
"name": "Hot Choco GDM 23",
|
||||
"diamondCost": 30,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/f62f5912077d9af84256de288399125a~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7525,
|
||||
"name": "Christmas CarouseG",
|
||||
"diamondCost": 2000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b5ba3941f7389da7495b659e888ea61a~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7527,
|
||||
"name": "Christmas Wreath G",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/7842b50135e089334fc40d9705bb53c7~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7551,
|
||||
"name": "Snowman",
|
||||
"diamondCost": 99,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/e094e0fafc14aaf127fa0d0a7926619a~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7697,
|
||||
"name": "LOVE U",
|
||||
"diamondCost": 899,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/79d45877691333e2ba69a9098406e95c~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7707,
|
||||
"name": "I'm blue",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/c560ec76d5599198aaea9377c5ffab6e~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7846,
|
||||
"name": "Grumpy Glasses",
|
||||
"diamondCost": 99,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/6f38f8ed7442f69a105788b5c0c74a38~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7920,
|
||||
"name": "Husky",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a2f5d595e9d96aec19a7c0ed5fa9b017~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 7921,
|
||||
"name": "Golden",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b97f58dcb0250489ae98529bcb0542ca~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8005,
|
||||
"name": "Falling For You",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a198bd39d2511dbba6a68867740e3ff9~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8207,
|
||||
"name": "The Crown",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5bf798f92fe96ba53c0f4d28f052f9bb~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8250,
|
||||
"name": "Disco ball",
|
||||
"diamondCost": 1000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/a53d3ef956eb2f1aa7a7db46024c70bb~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8253,
|
||||
"name": "Spring Train",
|
||||
"diamondCost": 3999,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b859c413a241fec75bc78668aeb0f581~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8264,
|
||||
"name": "Happy Weekend",
|
||||
"diamondCost": 599,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/b04f104e717798235cd3edaa6703e6a3~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8265,
|
||||
"name": "Happy Friday",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/65e8fcb76825b9ec36a24faf9a3e9495~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8266,
|
||||
"name": "Good Afternoon",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/bff3b908c4dd9cf19ab431cc99dc7940~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8269,
|
||||
"name": "Good Morning",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/5c1a28f3aa7eefc27491f3020748ce54~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8442,
|
||||
"name": "Flower Festival",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/9bfe63e39b581a69ff944758c3eae5a0~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8754,
|
||||
"name": "Aurora",
|
||||
"diamondCost": 12000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1f59f5593ce135325c1a034825cec18c.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8803,
|
||||
"name": "Miss You",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/3c53396b922691a7520698f47105a753.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8804,
|
||||
"name": "Vacation",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/8f46e8eef9cbd5304fb802104c2b4ef4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 8890,
|
||||
"name": "Pink Shoes",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/cba8a7c718988bd51c7b6055e9ab1ec4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9111,
|
||||
"name": "Popcorn",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/676d2d4c31a8979f1fd06cdf5ecd922f~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9152,
|
||||
"name": "Spin with me GDM",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/149ac2e87d05490d7d251149cefe27a2.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9242,
|
||||
"name": "Pumpkin Spice Latte",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/0636d91615f7417ddd5f29438bf5debe~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9303,
|
||||
"name": "Rabbit and Mochi",
|
||||
"diamondCost": 999,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/213ef2549fbb10ec783c95a41d28cf0a.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9304,
|
||||
"name": "Boo the Ghost",
|
||||
"diamondCost": 88,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/cb909c78f2412e4927ea68d6af8e048f.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9354,
|
||||
"name": "I'm here",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/7006392a82d57452d5ef08dd90e169c1.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9355,
|
||||
"name": "So cute",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/d40d31241efcf57c630e894bb3007b8a.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9363,
|
||||
"name": "Elf GDM 23",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/60e5289b379660cc562742cf987a2d35.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9535,
|
||||
"name": "Play for you",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/182659e90a3432aa155e61c9c0d89df0.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9536,
|
||||
"name": "Fake smile",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/35ce62173962e33834703212d0b845a7.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9576,
|
||||
"name": "Yeah Nah",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4b20c5aab3841657a343be3769307805.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9581,
|
||||
"name": "Turkey Face GDDec",
|
||||
"diamondCost": 399,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/65349d1ef783fc207c1d2b54a8d521a7.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9583,
|
||||
"name": "Cool!",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/424c61f16c16919f169fd0352bd24661.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9587,
|
||||
"name": "Christmas Potato",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/5448f1f5157d3a4a88e0f57acf3dbfe0.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9604,
|
||||
"name": "Gobble Gobble",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/ada9babc0b55cf005e8c8d13dfc30b42.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9615,
|
||||
"name": "Festive Tiny Diny",
|
||||
"diamondCost": 15,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f2a8c2967c7153e9077bb469f2e42317.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9617,
|
||||
"name": "Xmas Mishka Bear",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/700c1c8817847317407cc2b8c6c9da42.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9625,
|
||||
"name": "Elf's Hat ",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9857a040c92b34d6a261201a93c185f.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9647,
|
||||
"name": "Kitten Paw",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/332520d7b5085ce591396c8d2bb9d352.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9650,
|
||||
"name": "The Van Cat",
|
||||
"diamondCost": 799,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/6973dd1b6d3dee3ca3f0ebac3c1d2977.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9656,
|
||||
"name": "Gingerbread man",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/af01db3e3cb9f54ea2cb421fab6062bc.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9657,
|
||||
"name": "GB North Pole",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/79715a53c41619e7b205eb26e57926d4.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9658,
|
||||
"name": "DE North Pole",
|
||||
"diamondCost": 199,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/130e17b5b561a93cefbd236586881477.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9667,
|
||||
"name": "Kiwi Bird",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/b73cb4aaa76a33efd881192589d65351.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9668,
|
||||
"name": "Gingerman Party",
|
||||
"diamondCost": 1200,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/008a9554e736642f1b2dca9f198bb710.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9670,
|
||||
"name": "Reindeer",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4565fa0cd1dbf76463144b0d4cc50bf1.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9671,
|
||||
"name": "Gingebread Man",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/2399f65414f77419ec7d5e9274dc8e0e.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9672,
|
||||
"name": "Mimi & Fifi",
|
||||
"diamondCost": 5000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/0a72d0084695d03586fea7d854dc3a47.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9678,
|
||||
"name": "Holiday Carousel",
|
||||
"diamondCost": 2000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/b5ba3941f7389da7495b659e888ea61a.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9680,
|
||||
"name": "Xmas in London",
|
||||
"diamondCost": 20000,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/876204a6ad0b1b0e4675d9be42439183.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9682,
|
||||
"name": "Stay Warm",
|
||||
"diamondCost": 450,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/abd104eb08ce0c351292036d8897fb8d.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9688,
|
||||
"name": "Snowglobe",
|
||||
"diamondCost": 499,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/ea5ac5f8e186897456bed2e78fc78ca5.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9698,
|
||||
"name": "Candy Cane",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/1fa0a4ed666304c78a46de200b85c84b.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9703,
|
||||
"name": "Really Curious",
|
||||
"diamondCost": 1,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/793ba68723567b695b12f2ef08dc1484.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9704,
|
||||
"name": "Nemo",
|
||||
"diamondCost": 15,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/68fcf30cb3fb07e9546f5e7fbc2b0ac0.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9706,
|
||||
"name": "Elfs Hat ",
|
||||
"diamondCost": 299,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9857a040c92b34d6a261201a93c185f.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9770,
|
||||
"name": "Shiba Cookie",
|
||||
"diamondCost": 10,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/4ea5282e7f61cbeee1214422d40ad407.png~tplv-obj.png"
|
||||
},
|
||||
{
|
||||
"id": 9771,
|
||||
"name": "KFC Chicken",
|
||||
"diamondCost": 5,
|
||||
"image": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/resource/f9d59ccd2328b8a46841b3b1c87d9e55.png~tplv-obj.png"
|
||||
}
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user