mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-02-27 16:59:39 -05:00
Compare commits
68 Commits
1.0.14-Rel
...
develop-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fcac60cbe | ||
|
|
63dd8c20ac | ||
|
|
b809bb6cda | ||
|
|
a68eaba5a1 | ||
|
|
0252b9a42f | ||
|
|
1b2a8bad93 | ||
|
|
6b22154c82 | ||
|
|
965816e846 | ||
|
|
6b6e82cd93 | ||
|
|
c93c3144ff | ||
|
|
12c64e1c67 | ||
|
|
5794ff2a57 | ||
|
|
d471e87dd7 | ||
|
|
c89bcad894 | ||
|
|
c9a84c39df | ||
|
|
c1105f1324 | ||
|
|
243ce9bc94 | ||
|
|
4f141edb1a | ||
|
|
359a1508c7 | ||
|
|
bbfa7b410b | ||
|
|
6da40927d0 | ||
|
|
4d97fd9157 | ||
|
|
1ba51476d1 | ||
|
|
9ee5c89f64 | ||
|
|
ffabf098c0 | ||
|
|
7468fc2385 | ||
|
|
abbb557881 | ||
|
|
19c513afe6 | ||
|
|
283024a1d4 | ||
|
|
a0ac9e6d79 | ||
|
|
2385d1e75e | ||
|
|
c9c7f62d4a | ||
|
|
35ef95096d | ||
|
|
46bcfd6eb8 | ||
|
|
38b66395cb | ||
|
|
46e75dec1a | ||
|
|
749cfcf4a6 | ||
|
|
039f2b6a0b | ||
|
|
1a1860e35d | ||
|
|
8a4248daa3 | ||
|
|
ff5310f5bf | ||
|
|
9ddec45740 | ||
|
|
8a7b9e801b | ||
|
|
7b4590d0a1 | ||
|
|
2555edd86f | ||
|
|
a805844522 | ||
|
|
9da96b4417 | ||
|
|
12cf9e641b | ||
|
|
6bfa0b7745 | ||
|
|
cf9b882391 | ||
|
|
913d473442 | ||
|
|
72092bb56b | ||
|
|
b6247feb32 | ||
|
|
0dd952a7fb | ||
|
|
af4f2b4510 | ||
|
|
2c12b71e99 | ||
|
|
bc3386d21e | ||
|
|
4801de58cb | ||
|
|
548a585e90 | ||
|
|
2667f04a1c | ||
|
|
0a857594ea | ||
|
|
b0593ba95c | ||
|
|
c23faffcde | ||
|
|
f7a92d5015 | ||
|
|
6a42da9ecb | ||
|
|
1308b86567 | ||
|
|
20ba88c0ac | ||
|
|
77533ea4be |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,10 +1,11 @@
|
|||||||
backend-infrastructure/.aws-sam
|
backend-infrastructure/.aws-sam
|
||||||
|
|
||||||
|
|
||||||
|
.db
|
||||||
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
|
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
|
||||||
*.db
|
*.db
|
||||||
### Linux ###
|
### Linux ###
|
||||||
*~
|
*~
|
||||||
.db
|
|
||||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
.fuse_hidden*
|
.fuse_hidden*
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>TikTokLiveJava</artifactId>
|
<artifactId>TikTokLiveJava</artifactId>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
<version>1.0.13-Release</version>
|
<version>1.3.0-Release</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>API</artifactId>
|
<artifactId>API</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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.dto;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.net.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ProxyData
|
||||||
|
{
|
||||||
|
private final String address;
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
public static ProxyData map(String string) {
|
||||||
|
if (string == null || string.isBlank())
|
||||||
|
throw new IllegalArgumentException("Provided address cannot be null or empty!");
|
||||||
|
int portIndex = string.lastIndexOf(':');
|
||||||
|
try {
|
||||||
|
String address = string.substring(0, portIndex);
|
||||||
|
int port = Integer.parseInt(string.substring(portIndex+1));
|
||||||
|
|
||||||
|
// Port validation
|
||||||
|
if (port < 0 || port > 65535)
|
||||||
|
throw new IndexOutOfBoundsException("Port out of range: "+port);
|
||||||
|
|
||||||
|
// IP Validation
|
||||||
|
InetAddress res = InetAddress.getByName(address);
|
||||||
|
|
||||||
|
return new ProxyData(address, port);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Port must be a valid integer!");
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new IllegalArgumentException("Address must be valid IPv4, IPv6, or domain name!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProxyData clone() {
|
||||||
|
return new ProxyData(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetSocketAddress toSocketAddress() {
|
||||||
|
return new InetSocketAddress(address, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ package io.github.jwdeveloper.tiktok.data.events;
|
|||||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic.
|
* Triggered when the connection gets disconnected. In that case you can call connect() again to have a reconnect logic.
|
||||||
@@ -32,4 +32,12 @@ import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
|
|||||||
*/
|
*/
|
||||||
@EventMeta(eventType = EventType.Control)
|
@EventMeta(eventType = EventType.Control)
|
||||||
public class TikTokDisconnectedEvent extends TikTokLiveClientEvent {
|
public class TikTokDisconnectedEvent extends TikTokLiveClientEvent {
|
||||||
}
|
@Getter private final String reason;
|
||||||
|
public TikTokDisconnectedEvent(String reason) {
|
||||||
|
this.reason = reason.isBlank() ? "None" : reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TikTokDisconnectedEvent() {
|
||||||
|
this("None");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.control;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
||||||
|
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when client is connecting to live is successfully established.
|
||||||
|
*/
|
||||||
|
@EventMeta(eventType = EventType.Control)
|
||||||
|
public class TikTokConnectingEvent extends TikTokLiveClientEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.control;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.annotations.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.requests.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered before the connection is established.
|
||||||
|
*/
|
||||||
|
@EventMeta(eventType = EventType.Control)
|
||||||
|
public class TikTokPreConnectionEvent extends TikTokLiveClientEvent
|
||||||
|
{
|
||||||
|
@Getter private final LiveUserData.Response userData;
|
||||||
|
@Getter private final LiveData.Response roomData;
|
||||||
|
@Getter @Setter boolean cancelConnection = false;
|
||||||
|
|
||||||
|
public TikTokPreConnectionEvent(LiveUserData.Response userData, LiveData.Response liveData) {
|
||||||
|
this.userData = userData;
|
||||||
|
this.roomData = liveData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,10 +22,8 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.data.events.gift;
|
package io.github.jwdeveloper.tiktok.data.events.gift;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
import io.github.jwdeveloper.tiktok.annotations.*;
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.*;
|
||||||
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.data.models.users.User;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -53,4 +51,4 @@ public class TikTokGiftComboEvent extends TikTokGiftEvent {
|
|||||||
super(gift, host, msg);
|
super(gift, host, msg);
|
||||||
this.comboState = comboState;
|
this.comboState = comboState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,18 +23,13 @@
|
|||||||
package io.github.jwdeveloper.tiktok.data.events.gift;
|
package io.github.jwdeveloper.tiktok.data.events.gift;
|
||||||
|
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventMeta;
|
import io.github.jwdeveloper.tiktok.annotations.*;
|
||||||
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.*;
|
||||||
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.data.models.users.User;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when user sends gifts that has
|
* Triggered when user sends gifts that has
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
import io.github.jwdeveloper.tiktok.annotations.EventType;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EventMeta(eventType = EventType.Debug)
|
||||||
|
public class TikTokRoomDataResponseEvent extends TikTokEvent
|
||||||
|
{
|
||||||
|
private final LiveData.Response liveData;
|
||||||
|
}
|
||||||
@@ -28,15 +28,10 @@ import lombok.Getter;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
|
||||||
public class Picture {
|
public class Picture {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -49,7 +44,6 @@ public class Picture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Picture map(io.github.jwdeveloper.tiktok.messages.data.Image profilePicture) {
|
public static Picture map(io.github.jwdeveloper.tiktok.messages.data.Image profilePicture) {
|
||||||
|
|
||||||
var index = profilePicture.getUrlListCount() - 1;
|
var index = profilePicture.getUrlListCount() - 1;
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return new Picture("");
|
return new Picture("");
|
||||||
@@ -74,12 +68,11 @@ public class Picture {
|
|||||||
return CompletableFuture.supplyAsync(this::downloadImage);
|
return CompletableFuture.supplyAsync(this::downloadImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedImage download(String urlString)
|
private BufferedImage download(String urlString) {
|
||||||
{
|
if (urlString.isEmpty()) {
|
||||||
if(urlString.isEmpty())
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var baos = new ByteArrayOutputStream();
|
var baos = new ByteArrayOutputStream();
|
||||||
try (var is = new URL(urlString).openStream()) {
|
try (var is = new URL(urlString).openStream()) {
|
||||||
var byteChunk = new byte[4096];
|
var byteChunk = new byte[4096];
|
||||||
@@ -103,4 +96,9 @@ public class Picture {
|
|||||||
public static Picture Empty() {
|
public static Picture Empty() {
|
||||||
return new Picture("");
|
return new Picture("");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Picture{link='" + link + "', image=" + image + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,4 @@ public class Badge {
|
|||||||
public static Badge empty() {
|
public static Badge empty() {
|
||||||
return new Badge();
|
return new Badge();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -28,13 +28,11 @@ public enum GiftSendType
|
|||||||
Begin,
|
Begin,
|
||||||
Active;
|
Active;
|
||||||
|
|
||||||
|
|
||||||
public static GiftSendType fromNumber(long number)
|
public static GiftSendType fromNumber(long number)
|
||||||
{
|
{
|
||||||
return switch ((int) number) {
|
return switch ((int) number) {
|
||||||
case 0 -> GiftSendType.Finished;
|
|
||||||
case 1, 2, 4 -> GiftSendType.Active;
|
case 1, 2, 4 -> GiftSendType.Active;
|
||||||
default -> GiftSendType.Finished;
|
default -> GiftSendType.Finished;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,13 +43,12 @@ public class User {
|
|||||||
private long followers;
|
private long followers;
|
||||||
private List<Badge> badges;
|
private List<Badge> badges;
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private Set<UserAttribute> attributes;
|
private final Set<UserAttribute> attributes = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public List<UserAttribute> getAttributes() {
|
public List<UserAttribute> getAttributes() {
|
||||||
return attributes.stream().toList();
|
return attributes.stream().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasAttribute(UserAttribute userFlag) {
|
public boolean hasAttribute(UserAttribute userFlag) {
|
||||||
return attributes.contains(userFlag);
|
return attributes.contains(userFlag);
|
||||||
}
|
}
|
||||||
@@ -106,7 +105,6 @@ public class User {
|
|||||||
this.following = following;
|
this.following = following;
|
||||||
this.followers = followers;
|
this.followers = followers;
|
||||||
this.badges = badges;
|
this.badges = badges;
|
||||||
this.attributes = new HashSet<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User(Long id,
|
public User(Long id,
|
||||||
@@ -123,14 +121,12 @@ public class User {
|
|||||||
this.following = following;
|
this.following = following;
|
||||||
this.followers = followers;
|
this.followers = followers;
|
||||||
this.badges = badges;
|
this.badges = badges;
|
||||||
this.attributes = new HashSet<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User(Long userId,
|
public User(Long userId,
|
||||||
String nickName) {
|
String nickName) {
|
||||||
this.id = userId;
|
this.id = userId;
|
||||||
this.name = nickName;
|
this.name = nickName;
|
||||||
this.attributes = new HashSet<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User(Long userId,
|
public User(Long userId,
|
||||||
@@ -213,4 +209,4 @@ public class User {
|
|||||||
0,
|
0,
|
||||||
List.of(Badge.empty()));
|
List.of(Badge.empty()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,29 +20,29 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.events_generator;
|
package io.github.jwdeveloper.tiktok.data.requests;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
public class GiftsData
|
||||||
public class EventGeneratorSettings
|
|
||||||
{
|
{
|
||||||
private String inputDictionary;
|
@Getter
|
||||||
|
public final class Request
|
||||||
private String outputDictionary;
|
|
||||||
private List<String> ignoredFiles = new ArrayList<>();
|
|
||||||
|
|
||||||
private String prefix;
|
|
||||||
|
|
||||||
private String endFix;
|
|
||||||
|
|
||||||
private boolean isTikTokEvent;
|
|
||||||
|
|
||||||
public void addIgnoredClass(String name)
|
|
||||||
{
|
{
|
||||||
ignoredFiles.add(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static final class Response
|
||||||
|
{
|
||||||
|
private String json;
|
||||||
|
private List<Gift> gifts;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -20,26 +20,28 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* 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 io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
@Data
|
import java.net.URI;
|
||||||
public class LiveRoomMeta {
|
import java.time.Duration;
|
||||||
|
|
||||||
private LiveRoomStatus status;
|
public class LiveConnectionData {
|
||||||
private boolean ageRestricted;
|
@Getter
|
||||||
private String titie;
|
@AllArgsConstructor
|
||||||
private int likeCount;
|
public static class Request {
|
||||||
private int totalViewers;
|
private String roomId;
|
||||||
private int viewers;
|
}
|
||||||
private User host;
|
|
||||||
|
|
||||||
public enum LiveRoomStatus
|
@Getter
|
||||||
{
|
@AllArgsConstructor
|
||||||
HostNotFound,
|
public static class Response {
|
||||||
HostOnline,
|
private String websocketCookies;
|
||||||
HostOffline
|
private URI websocketUrl;
|
||||||
|
private WebcastResponse webcastResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.data.models.users.User;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class LiveData {
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Request {
|
||||||
|
private String roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
private LiveType liveType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LiveStatus {
|
||||||
|
HostNotFound,
|
||||||
|
HostOnline,
|
||||||
|
HostOffline,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LiveType {
|
||||||
|
SOLO,
|
||||||
|
BOX,
|
||||||
|
BATTLE,
|
||||||
|
CO_HOST
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
|
||||||
|
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 boolean isLiveOnline() {
|
||||||
|
return userStatus == LiveUserData.UserStatus.Live || userStatus == LiveUserData.UserStatus.LivePaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHostNameValid() {
|
||||||
|
return userStatus != LiveUserData.UserStatus.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UserStatus {
|
||||||
|
NotFound,
|
||||||
|
Offline,
|
||||||
|
LivePaused,
|
||||||
|
Live,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
@Setter
|
||||||
|
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.setEnabled(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;
|
||||||
|
|
||||||
|
return newSettings;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,9 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* 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.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -28,44 +30,86 @@ import java.util.Map;
|
|||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class Constants {
|
@Data
|
||||||
|
public class LiveClientSettings {
|
||||||
/**
|
|
||||||
* Web-URL for TikTok
|
|
||||||
*/
|
|
||||||
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";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default TimeOut for Connections
|
|
||||||
*/
|
|
||||||
public static final int DEFAULT_TIMEOUT = 20;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Settings for Client
|
* Determines if gifts data is downloaded before TikTokLive starts,
|
||||||
|
* when `false` then client.giftManager() does not contain initial gifts
|
||||||
*/
|
*/
|
||||||
public static ClientSettings DefaultClientSettings() {
|
private boolean fetchGifts;
|
||||||
var clientSettings = new ClientSettings();
|
|
||||||
clientSettings.setTimeout(Duration.ofSeconds(DEFAULT_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 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional: API Key for increased limit to signing server
|
||||||
|
*/
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
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.setClientLanguage("en-US");
|
||||||
clientSettings.setRetryOnConnectionFailure(false);
|
clientSettings.setRetryOnConnectionFailure(false);
|
||||||
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1));
|
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1));
|
||||||
clientSettings.setPrintToConsole(false);
|
clientSettings.setPrintToConsole(false);
|
||||||
clientSettings.setLogLevel(Level.ALL);
|
clientSettings.setLogLevel(Level.ALL);
|
||||||
clientSettings.setClientParameters(Constants.DefaultClientParams());
|
|
||||||
|
clientSettings.setHttpSettings(httpSettings);
|
||||||
return clientSettings;
|
return clientSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Parameters for HTTP-Request
|
* Default Parameters for HTTP-Request
|
||||||
*/
|
*/
|
||||||
@@ -104,26 +148,22 @@ public class Constants {
|
|||||||
clientParams.put("webcast_sdk_version", "1.3.0");
|
clientParams.put("webcast_sdk_version", "1.3.0");
|
||||||
clientParams.put("update_version_code", "1.3.0");
|
clientParams.put("update_version_code", "1.3.0");
|
||||||
|
|
||||||
|
|
||||||
return clientParams;
|
return clientParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Headers for HTTP-Request
|
* Default Headers for HTTP-Request
|
||||||
*/
|
*/
|
||||||
public static Map<String, String> DefaultRequestHeaders() {
|
public static Map<String, String> DefaultRequestHeaders() {
|
||||||
var headers = new HashMap<String, String>();
|
var headers = new HashMap<String, String>();
|
||||||
|
|
||||||
headers.put("authority","www.tiktok.com");
|
headers.put("authority", "www.tiktok.com");
|
||||||
headers.put("Connection", "keep-alive");
|
|
||||||
headers.put("Cache-Control", "max-age=0");
|
headers.put("Cache-Control", "max-age=0");
|
||||||
headers.put("Accept", "text/html,application/json,application/protobuf");
|
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("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("Referer", "https://www.tiktok.com/");
|
||||||
headers.put("Origin", "https://www.tiktok.com");
|
headers.put("Origin", "https://www.tiktok.com");
|
||||||
headers.put("Accept-Language", "en-US,en; q=0.9");
|
headers.put("Accept-Language", "en-US,en; q=0.9");
|
||||||
headers.put("Accept-Encoding", "gzip, deflate");
|
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* 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 io.github.jwdeveloper.tiktok.data.dto.ProxyData;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ProxyClientSettings implements Iterator<ProxyData>
|
||||||
|
{
|
||||||
|
private boolean enabled, autoDiscard = true, fallback = true;
|
||||||
|
private Rotation rotation = Rotation.CONSECUTIVE;
|
||||||
|
private final List<ProxyData> proxyList = new ArrayList<>();
|
||||||
|
private int index = -1;
|
||||||
|
private Proxy.Type type = Proxy.Type.DIRECT;
|
||||||
|
private Consumer<ProxyData> onProxyUpdated = x -> {};
|
||||||
|
|
||||||
|
public boolean addProxy(String addressPort) {
|
||||||
|
return proxyList.add(ProxyData.map(addressPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addProxy(String address, int port) {
|
||||||
|
return addProxy(new InetSocketAddress(address, port));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addProxy(InetSocketAddress inetAddress) {
|
||||||
|
return proxyList.add(new ProxyData(inetAddress.getHostString(), inetAddress.getPort()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProxies(List<String> list) {
|
||||||
|
list.forEach(this::addProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return !proxyList.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProxyData next() {
|
||||||
|
var nextProxy = switch (rotation)
|
||||||
|
{
|
||||||
|
case CONSECUTIVE -> {
|
||||||
|
index = (index+1) % proxyList.size();
|
||||||
|
yield proxyList.get(index).clone();
|
||||||
|
}
|
||||||
|
case RANDOM -> {
|
||||||
|
index = new Random().nextInt(proxyList.size());
|
||||||
|
yield proxyList.get(index).clone();
|
||||||
|
}
|
||||||
|
case NONE -> {
|
||||||
|
index = Math.max(index, 0);
|
||||||
|
yield proxyList.get(index).clone();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onProxyUpdated.accept(nextProxy);
|
||||||
|
return nextProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
proxyList.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
if (index == 0 && proxyList.isEmpty())
|
||||||
|
this.index = 0;
|
||||||
|
else {
|
||||||
|
if (index < 0 || index >= proxyList.size())
|
||||||
|
throw new IndexOutOfBoundsException("Index " + index + " exceeds list of size: " + proxyList.size());
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProxyClientSettings clone()
|
||||||
|
{
|
||||||
|
ProxyClientSettings settings = new ProxyClientSettings();
|
||||||
|
settings.setEnabled(enabled);
|
||||||
|
settings.setRotation(rotation);
|
||||||
|
settings.setIndex(index);
|
||||||
|
settings.setType(type);
|
||||||
|
settings.setOnProxyUpdated(onProxyUpdated);
|
||||||
|
proxyList.forEach(proxyData -> settings.addProxy(proxyData.getAddress(), proxyData.getPort()));
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Rotation
|
||||||
|
{
|
||||||
|
/** Rotate addresses consecutively, from proxy 0 -> 1 -> 2 -> ...etc. */
|
||||||
|
CONSECUTIVE,
|
||||||
|
/** Rotate addresses randomly, from proxy 0 -> 69 -> 420 -> 1 -> ...etc. */
|
||||||
|
RANDOM,
|
||||||
|
/** Don't rotate addresses at all, pin to the indexed address. */
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
package io.github.jwdeveloper.tiktok.exceptions;
|
package io.github.jwdeveloper.tiktok.exceptions;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Happens while bad response from Http request to TikTok
|
* Happens while bad response from Http request to TikTok
|
||||||
*/
|
*/
|
||||||
public class TikTokLiveRequestException extends TikTokLiveException
|
public class TikTokLiveRequestException extends TikTokLiveException
|
||||||
@@ -46,4 +46,4 @@ public class TikTokLiveRequestException extends TikTokLiveException
|
|||||||
public TikTokLiveRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
public TikTokLiveRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,31 +20,30 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.events_generator;
|
package io.github.jwdeveloper.tiktok.exceptions;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
/*
|
||||||
import java.util.List;
|
* Happens while bad response from http proxy request to TikTok
|
||||||
|
*/
|
||||||
@Data
|
public class TikTokProxyRequestException extends TikTokLiveException
|
||||||
public class CSharpClassInfo
|
|
||||||
{
|
{
|
||||||
private String className;
|
public TikTokProxyRequestException() {
|
||||||
private List<FieldInfo> fields = new ArrayList<>();
|
|
||||||
private List<ConstructorInfo> constructors = new ArrayList<>();
|
|
||||||
|
|
||||||
public void addField(String type, String fields)
|
|
||||||
{
|
|
||||||
this.fields.add(new FieldInfo(type,fields));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addConstructor(List<FieldInfo> arguments)
|
public TikTokProxyRequestException(String message) {
|
||||||
{
|
super(message);
|
||||||
this.constructors.add(new ConstructorInfo(arguments));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record FieldInfo(String type, String name){};
|
public TikTokProxyRequestException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
public record ConstructorInfo(List<FieldInfo> arguemtns){};
|
public TikTokProxyRequestException(Throwable cause) {
|
||||||
}
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TikTokProxyRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,29 +20,26 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.http;
|
package io.github.jwdeveloper.tiktok.exceptions;
|
||||||
|
|
||||||
import java.util.*;
|
public class TikTokSignServerException extends TikTokLiveRequestException
|
||||||
import java.util.stream.Collectors;
|
{
|
||||||
|
public TikTokSignServerException() {
|
||||||
public class TikTokCookieJar {
|
|
||||||
private final Map<String, String> cookies;
|
|
||||||
public TikTokCookieJar() {
|
|
||||||
cookies = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String get(String key) {
|
public TikTokSignServerException(String message) {
|
||||||
return cookies.get(key);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(String key, String value) {
|
public TikTokSignServerException(String message, Throwable cause) {
|
||||||
cookies.put(key, value);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String parseCookies() {
|
public TikTokSignServerException(Throwable cause) {
|
||||||
return cookies.entrySet()
|
super(cause);
|
||||||
.stream()
|
|
||||||
.map(entry -> entry.getKey()+"="+entry.getValue()+";")
|
|
||||||
.collect(Collectors.joining());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public TikTokSignServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 name of user
|
||||||
|
* @return {@link LiveUserData.Response}
|
||||||
|
*/
|
||||||
|
default LiveUserData.Response fetchLiveUserData(String userName) {
|
||||||
|
return fetchLiveUserData(new LiveUserData.Request(userName));
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveUserData.Response fetchLiveUserData(LiveUserData.Request request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
||||||
|
* @return {@link LiveData.Response}
|
||||||
|
*/
|
||||||
|
default LiveData.Response fetchLiveData(String roomId) {
|
||||||
|
return fetchLiveData(new LiveData.Request(roomId));
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData.Response fetchLiveData(LiveData.Request request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
||||||
|
* @return {@link LiveConnectionData.Response}
|
||||||
|
*/
|
||||||
|
default LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
|
||||||
|
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
|
||||||
|
}
|
||||||
@@ -20,72 +20,67 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok.live;
|
||||||
|
|
||||||
import lombok.Data;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@Data
|
public interface GiftsManager {
|
||||||
public class ClientSettings {
|
|
||||||
/**
|
|
||||||
* Timeout for Connections
|
|
||||||
*/
|
|
||||||
private Duration timeout;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ISO-Language for Client
|
* You can create and attach your own custom gift to manager
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
*
|
*
|
||||||
|
* @param gift
|
||||||
*/
|
*/
|
||||||
private String roomId;
|
void attachGift(Gift gift);
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* You can create and attach your own custom gift to manager
|
||||||
|
*
|
||||||
|
* @param gifts
|
||||||
|
*/
|
||||||
|
void attachGiftsList(List<Gift> gifts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* finds gift by name
|
||||||
|
* When gift not found return Gift.UNDEFINED;
|
||||||
|
*
|
||||||
|
* @param name gift name
|
||||||
|
*/
|
||||||
|
Gift getByName(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* finds gift by id
|
||||||
|
* When gift not found return Gift.UNDEFINED;
|
||||||
|
*
|
||||||
|
* @param giftId giftId
|
||||||
|
*/
|
||||||
|
Gift getById(int giftId);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* finds gift by filter
|
||||||
|
* When gift not found return Gift.UNDEFINED;
|
||||||
|
*/
|
||||||
|
Gift getByFilter(Predicate<Gift> filter);
|
||||||
|
|
||||||
|
List<Gift> getManyByFilter(Predicate<Gift> filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list of all gifts
|
||||||
|
*/
|
||||||
|
List<Gift> toList();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list of all map of all gifts where Integer is gift Id
|
||||||
|
*/
|
||||||
|
Map<Integer, Gift> toMap();
|
||||||
|
}
|
||||||
@@ -64,7 +64,7 @@ public interface LiveClient {
|
|||||||
/**
|
/**
|
||||||
* Get information about gifts
|
* Get information about gifts
|
||||||
*/
|
*/
|
||||||
GiftManager getGiftManager();
|
GiftsManager getGiftManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current room info from TikTok API including streamer info, room status and statistics.
|
* Gets the current room info from TikTok API including streamer info, room status and statistics.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.live.builder;
|
|||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.*;
|
import io.github.jwdeveloper.tiktok.data.events.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.http.TikTokHttpResponseEvent;
|
import io.github.jwdeveloper.tiktok.data.events.http.TikTokHttpResponseEvent;
|
||||||
@@ -149,6 +150,13 @@ public interface EventsBuilder<T> {
|
|||||||
*/
|
*/
|
||||||
T onConnected(EventConsumer<TikTokConnectedEvent> action);
|
T onConnected(EventConsumer<TikTokConnectedEvent> action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked before client has been successfully connected to live
|
||||||
|
* @param action
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
T onPreConnection(EventConsumer<TikTokPreConnectionEvent> action);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when client tries to reconnect
|
* Invoked when client tries to reconnect
|
||||||
* @param action
|
* @param action
|
||||||
@@ -215,6 +223,4 @@ public interface EventsBuilder<T> {
|
|||||||
//T onLinkMicBattle(TikTokEventConsumer<TikTokLinkMicBattleEvent> event);
|
//T onLinkMicBattle(TikTokEventConsumer<TikTokLinkMicBattleEvent> event);
|
||||||
|
|
||||||
//T onUnhandledControl(TikTokEventConsumer<TikTokUnhandledControlEvent> event);
|
//T onUnhandledControl(TikTokEventConsumer<TikTokUnhandledControlEvent> event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -22,15 +22,13 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.live.builder;
|
package io.github.jwdeveloper.tiktok.live.builder;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
|
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.mappers.TikTokMapper;
|
import io.github.jwdeveloper.tiktok.mappers.TikTokMapper;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
|
public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
|
||||||
|
|
||||||
@@ -48,11 +46,11 @@ public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration of client settings
|
* Configuration of client settings
|
||||||
* @see ClientSettings
|
* @see LiveClientSettings
|
||||||
* @param onConfigure
|
* @param onConfigure
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
LiveClientBuilder configure(Consumer<ClientSettings> onConfigure);
|
LiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see TikTokEventListener
|
* @see TikTokEventListener
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ public class ProtoBufferObject {
|
|||||||
this.fields = new TreeMap<>();
|
this.fields = new TreeMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getField(int index)
|
||||||
|
{
|
||||||
|
return fields.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
public void addField(int index, String type, Object value) {
|
public void addField(int index, String type, Object value) {
|
||||||
fields.put(index, new ProtoBufferField(type, value));
|
fields.put(index, new ProtoBufferField(type, value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.websocket;
|
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.live.LiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
|
|
||||||
public interface SocketClient {
|
public interface SocketClient {
|
||||||
void start(WebcastResponse webcastResponse, LiveClient tikTokLiveClient);
|
void start(LiveConnectionData.Response webcastResponse, LiveClient tikTokLiveClient);
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -976,131 +976,146 @@ message FanTicketRoomNoticeContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message LinkerAcceptNoticeContent {
|
message LinkerAcceptNoticeContent {
|
||||||
int64 FromUserId = 1;
|
int64 fromUserId = 1;
|
||||||
int64 FromRoomId = 2;
|
int64 fromRoomId = 2;
|
||||||
int64 ToUserId = 3;
|
int64 toUserId = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerCancelContent {
|
message LinkerCancelContent {
|
||||||
int64 FromUserId = 1;
|
int64 fromUserId = 1;
|
||||||
int64 ToUserId = 2;
|
int64 toUserId = 2;
|
||||||
int64 CancelType = 3;
|
int64 cancelType = 3;
|
||||||
int64 ActionId = 4;
|
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 LinkerCloseContent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerCreateContent {
|
message LinkerCreateContent {
|
||||||
int64 OwnerId = 1;
|
int64 ownerId = 1;
|
||||||
int64 OwnerRoomId = 2;
|
int64 ownerRoomId = 2;
|
||||||
int64 LinkType = 3;
|
int64 linkType = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message LinkerEnterContent {
|
message LinkerEnterContent {
|
||||||
repeated User LinkedUsersList = 1;
|
repeated ListUser linkedUsersList = 1;
|
||||||
// LinkmicMultiLiveEnum AnchorMultiLiveEnum = 2;
|
int32 anchorMultiLiveEnum = 2; // Enum
|
||||||
// Data.LinkmicUserSettingInfo AnchorSettingInfo = 3;
|
LinkmicUserSettingInfo anchorSettingInfo = 3;;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerInviteContent {
|
message LinkerInviteContent {
|
||||||
int64 FromUserId = 1;
|
int64 fromUserId = 1;
|
||||||
int64 FromRoomId = 2;
|
int64 fromRoomId = 2;
|
||||||
string ToRtcExtInfo = 3;
|
string toRtcExtInfo = 3;
|
||||||
bool RtcJoinChannel = 4;
|
bool rtcJoinChannel = 4;
|
||||||
int64 Vendor = 5;
|
int64 vendor = 5;
|
||||||
string SecFromUserId = 6;
|
string secFromUserId = 6;
|
||||||
string ToLinkmicIdStr = 7;
|
string toLinkmicIdStr = 7;
|
||||||
User FromUser = 8;
|
User fromUser = 8;
|
||||||
int64 RequiredMicIdx = 9;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerKickOutContent {
|
message LinkerKickOutContent {
|
||||||
int64 FromUserId = 1;
|
int64 fromUserId = 1;
|
||||||
//LinkMic.KickoutReason KickoutReason = 2;
|
KickoutReason kickoutReason = 2; // Enum
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerLeaveContent {
|
message LinkerLeaveContent {
|
||||||
int64 UserId = 1;
|
int64 userId = 1;
|
||||||
string LinkmicIdStr = 2;
|
string linkmicIdStr = 2;
|
||||||
int64 SendLeaveUid = 3;
|
int64 sendLeaveUid = 3;
|
||||||
int64 LeaveReason = 4;
|
int64 leaveReason = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Empty
|
||||||
message LinkerLinkedListChangeContent {
|
message LinkerLinkedListChangeContent {
|
||||||
repeated User LinkedUsersList = 1;
|
|
||||||
}
|
}
|
||||||
|
//Empty
|
||||||
|
message CohostListChangeContent {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
message LinkerListChangeContent {
|
message LinkerListChangeContent {
|
||||||
repeated LinkLayerListUser LinkedUsersList = 1;
|
repeated ListUser linkedUsersList = 1;
|
||||||
repeated LinkLayerListUser AppliedUsersList = 2;
|
repeated ListUser appliedUsersList = 2;
|
||||||
repeated LinkLayerListUser ConnectingUsersList = 3;
|
repeated ListUser connectingUsersList = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerMediaChangeContent {
|
message LinkerMediaChangeContent {
|
||||||
// MicIdxOperation Op = 1;
|
int64 op = 1; // Enum
|
||||||
int64 ToUserId = 2;
|
int64 toUserId = 2;
|
||||||
int64 AnchorId = 3;
|
int64 anchorId = 3;
|
||||||
int64 RoomId = 4;
|
int64 roomId = 4;
|
||||||
// LinkerSceneType ChangeScene = 5;
|
int64 changeScene = 5; // Enum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Empty
|
||||||
message LinkerMicIdxUpdateContent {
|
message LinkerMicIdxUpdateContent {
|
||||||
LinkerMicIdxUpdateInfo MicIdxUpdateInfo = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerMicIdxUpdateInfo {
|
|
||||||
// MicIdxOperation Op = 1;
|
|
||||||
int64 UserId = 2;
|
|
||||||
int64 MicIdx = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message LinkerMuteContent {
|
message LinkerMuteContent {
|
||||||
int64 UserId = 1;
|
int64 userId = 1;
|
||||||
// Data.MuteStatus Status = 2;
|
int64 status = 2; // Enum
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerRandomMatchContent {
|
message LinkerRandomMatchContent {
|
||||||
User User = 1;
|
User user = 1;
|
||||||
int64 RoomId = 2;
|
int64 roomId = 2;
|
||||||
int64 InviteType = 3;
|
int64 inviteType = 3;
|
||||||
string MatchId = 4;
|
string matchId = 4;
|
||||||
int64 InnerChannelId = 5;
|
int64 innerChannelId = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerReplyContent {
|
message LinkerReplyContent {
|
||||||
int64 FromUserId = 1;
|
int64 fromUserId = 1;
|
||||||
int64 FromRoomId = 2;
|
int64 fromRoomId = 2;
|
||||||
// LinkmicInfo FromUserLinkmicInfo = 3;
|
LinkmicInfo fromUserLinkmicInfo = 3;
|
||||||
int64 ToUserId = 4;
|
int64 toUserId = 4;
|
||||||
// LinkmicInfo ToUserLinkmicInfo = 5;
|
LinkmicInfo toUserLinkmicInfo = 5;
|
||||||
int64 LinkType = 6;
|
int64 linkType = 6;
|
||||||
int64 ReplyStatus = 7;
|
int64 replyStatus = 7;
|
||||||
LinkerSetting LinkerSetting = 8;
|
LinkerSetting linkerSetting = 8;
|
||||||
User FromUser = 9;
|
User fromUser = 9;
|
||||||
User ToUser = 10;
|
User toUser = 10;
|
||||||
map<int64, string> RtcExtInfoMap = 11;
|
|
||||||
LinkerMicIdxUpdateInfo InviteeMicIdxUpdateInfo = 12;
|
message LinkmicInfo {
|
||||||
map<int64, int64> ApplierMicIdxInfoMap = 13;
|
string accessKey = 1;
|
||||||
// Data.LinkmicMultiLiveEnum AnchorMultiLiveEnum = 14;
|
int64 linkMicId = 2;
|
||||||
// Data.LinkmicUserSettingInfo AnchorSettingInfo = 15;
|
bool joinable = 3;
|
||||||
int64 ActionId = 16;
|
int32 confluenceType = 4;
|
||||||
// repeated LinkmicUserInfo LinkedUsersList = 17;
|
string rtcExtInfo = 5;
|
||||||
int64 SourceType = 18;
|
string rtcAppId = 6;
|
||||||
|
string rtcAppSign = 7;
|
||||||
|
string linkmicIdStr = 8;
|
||||||
|
int64 vendor = 9;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerSetting {
|
message LinkerSetting {
|
||||||
@@ -1113,20 +1128,27 @@ message LinkerSetting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message LinkerSysKickOutContent {
|
message LinkerSysKickOutContent {
|
||||||
int64 UserId = 1;
|
int64 userId = 1;
|
||||||
string LinkmicIdStr = 2;
|
string linkmicIdStr = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LinkmicUserToastContent {
|
||||||
|
int64 userId = 1;
|
||||||
|
int64 roomId = 2;
|
||||||
|
Text displayText = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LinkerUpdateUserContent {
|
message LinkerUpdateUserContent {
|
||||||
int64 FromUserId = 1;
|
int64 fromUserId = 1;
|
||||||
int64 ToUserId = 2;
|
int64 toUserId = 2;
|
||||||
map<string, string> UpdateInfoMap = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Empty
|
||||||
message LinkerUpdateUserSettingContent {
|
message LinkerUpdateUserSettingContent {
|
||||||
// Data.LinkmicUserSettingInfo UpdateUserSettingInfo = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Empty
|
||||||
message LinkerWaitingListChangeContent {
|
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 {
|
enum MessageType {
|
||||||
MESSAGETYPE_SUBSUCCESS = 0;
|
MESSAGETYPE_SUBSUCCESS = 0;
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ message WebcastPushFrame {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message WebcastWebsocketAck {
|
|
||||||
uint64 Id = 1;
|
|
||||||
string Type = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//@WebcastResponse
|
//@WebcastResponse
|
||||||
@@ -733,7 +729,7 @@ message WebcastSystemMessage {
|
|||||||
//@WebcastLinkMessage
|
//@WebcastLinkMessage
|
||||||
message WebcastLinkMessage {
|
message WebcastLinkMessage {
|
||||||
Common common = 1;
|
Common common = 1;
|
||||||
MessageType MessageType = 2;
|
LinkMessageType MessageType = 2;
|
||||||
int64 LinkerId = 3;
|
int64 LinkerId = 3;
|
||||||
Scene Scene = 4;
|
Scene Scene = 4;
|
||||||
LinkerInviteContent InviteContent = 5;
|
LinkerInviteContent InviteContent = 5;
|
||||||
@@ -752,14 +748,14 @@ message WebcastLinkMessage {
|
|||||||
LinkerUpdateUserSettingContent UpdateUserSettingContent = 18;
|
LinkerUpdateUserSettingContent UpdateUserSettingContent = 18;
|
||||||
LinkerMicIdxUpdateContent MicIdxUpdateContent = 19;
|
LinkerMicIdxUpdateContent MicIdxUpdateContent = 19;
|
||||||
LinkerListChangeContent ListChangeContent = 20;
|
LinkerListChangeContent ListChangeContent = 20;
|
||||||
// CohostListChangeContent CohostListChangeContent = 21;
|
CohostListChangeContent CohostListChangeContent = 21;
|
||||||
LinkerMediaChangeContent MediaChangeContent = 22;
|
LinkerMediaChangeContent MediaChangeContent = 22;
|
||||||
LinkerAcceptNoticeContent ReplyAcceptNoticeContent = 23;
|
LinkerAcceptNoticeContent ReplyAcceptNoticeContent = 23;
|
||||||
LinkerSysKickOutContent SysKickOutContent = 101;
|
LinkerSysKickOutContent SysKickOutContent = 101;
|
||||||
// LinkmicUserToastContent UserToastContent = 102;
|
LinkmicUserToastContent UserToastContent = 102;
|
||||||
string Extra = 200;
|
string extra = 200;
|
||||||
int64 ExpireTimestamp = 201;
|
int64 expireTimestamp = 201;
|
||||||
string TransferExtra = 202;
|
string transferExtra = 202;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>TikTokLiveJava</artifactId>
|
<artifactId>TikTokLiveJava</artifactId>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
<version>1.0.13-Release</version>
|
<version>1.3.0-Release</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.java-websocket</groupId>
|
<groupId>org.java-websocket</groupId>
|
||||||
<artifactId>Java-WebSocket</artifactId>
|
<artifactId>Java-WebSocket</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.testng</groupId>
|
<groupId>org.testng</groupId>
|
||||||
|
|||||||
@@ -23,69 +23,97 @@
|
|||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokDataChecker;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||||
|
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
|
||||||
|
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||||
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
|
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class TikTokLive
|
public class TikTokLive {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||||
*
|
*
|
||||||
* @param hostName profile name of Tiktok user could be found in profile link
|
* @param hostName profile name of Tiktok user could be found in profile link
|
||||||
* example: https://www.tiktok.com/@dostawcavideo hostName would be dostawcavideo
|
|
||||||
* @return LiveClientBuilder
|
* @return LiveClientBuilder
|
||||||
*/
|
*/
|
||||||
public static LiveClientBuilder newClient(String hostName)
|
public static LiveClientBuilder newClient(String hostName) {
|
||||||
{
|
|
||||||
return new TikTokLiveClientBuilder(hostName);
|
return new TikTokLiveClientBuilder(hostName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||||
*
|
*
|
||||||
* @param hostName profile name of Tiktok user could be found in profile link
|
* @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 true if live is Online, false if is offline
|
||||||
*/
|
*/
|
||||||
public static boolean isLiveOnline(String hostName)
|
public static boolean isLiveOnline(String hostName) {
|
||||||
{
|
return requests().fetchLiveUserData(hostName).isLiveOnline();
|
||||||
return new TikTokDataChecker().isOnline(hostName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||||
*
|
*
|
||||||
* @param hostName profile name of Tiktok user could be found in profile link
|
* @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 true if live is Online, false if is offline
|
||||||
*/
|
*/
|
||||||
public static CompletableFuture<Boolean> isLiveOnlineAsync(String hostName)
|
public static CompletableFuture<Boolean> isLiveOnlineAsync(String hostName) {
|
||||||
{
|
return CompletableFuture.supplyAsync(() -> isLiveOnline(hostName));
|
||||||
return new TikTokDataChecker().isOnlineAsync(hostName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||||
*
|
*
|
||||||
* @param hostName profile name of Tiktok user could be found in profile link
|
* @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
|
* @return true is hostName name is valid and exists, false if not
|
||||||
*/
|
*/
|
||||||
public static boolean isHostNameValid(String hostName)
|
public static boolean isHostNameValid(String hostName) {
|
||||||
{
|
return requests().fetchLiveUserData(hostName).isHostNameValid();
|
||||||
return new TikTokDataChecker().isHostNameValid(hostName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
|
||||||
*
|
*
|
||||||
* @param hostName profile name of Tiktok user could be found in profile link
|
* @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
|
* @return true is hostName name is valid and exists, false if not
|
||||||
*/
|
*/
|
||||||
public static CompletableFuture<Boolean> isHostNameValidAsync(String hostName)
|
public static CompletableFuture<Boolean> isHostNameValidAsync(String hostName) {
|
||||||
{
|
return CompletableFuture.supplyAsync(() -> isHostNameValid(hostName));
|
||||||
return new TikTokDataChecker().isHostNameValidAsync(hostName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Use to get some data from TikTok about users are lives
|
||||||
|
*
|
||||||
|
* @return LiveHttpClient
|
||||||
|
*/
|
||||||
|
public static LiveHttpClient requests() {
|
||||||
|
return new TikTokLiveHttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//I don't like it, but it is reasonable for now
|
||||||
|
private static GiftsManager giftsManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch gifts from endpoint and returns GiftManager
|
||||||
|
*
|
||||||
|
* @return GiftsManager
|
||||||
|
*/
|
||||||
|
public static GiftsManager gifts() {
|
||||||
|
if (giftsManager != null) {
|
||||||
|
return giftsManager;
|
||||||
|
}
|
||||||
|
synchronized (GiftsManager.class)
|
||||||
|
{
|
||||||
|
if (giftsManager == null)
|
||||||
|
{
|
||||||
|
return new TikTokGiftsManager(requests().fetchGiftsData().getGifts());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return giftsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,24 +22,24 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.dto.TikTokUserInfo;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
|
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
|
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokReconnectingEvent;
|
import io.github.jwdeveloper.tiktok.data.events.TikTokReconnectingEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.control.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
|
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
|
||||||
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
|
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
|
||||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
|
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.models.ConnectionState;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||||
import io.github.jwdeveloper.tiktok.websocket.SocketClient;
|
import io.github.jwdeveloper.tiktok.websocket.SocketClient;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -48,25 +48,25 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
public class TikTokLiveClient implements LiveClient {
|
public class TikTokLiveClient implements LiveClient {
|
||||||
private final TikTokRoomInfo liveRoomInfo;
|
private final TikTokRoomInfo liveRoomInfo;
|
||||||
private final TikTokGiftManager tikTokGiftManager;
|
private final TikTokLiveHttpClient httpClient;
|
||||||
private final TikTokApiService apiService;
|
|
||||||
private final SocketClient webSocketClient;
|
private final SocketClient webSocketClient;
|
||||||
private final TikTokEventObserver tikTokEventHandler;
|
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||||
private final ClientSettings clientSettings;
|
private final LiveClientSettings clientSettings;
|
||||||
private final TikTokListenersManager listenersManager;
|
private final TikTokListenersManager listenersManager;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
|
private final GiftsManager giftsManager;
|
||||||
|
|
||||||
public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta,
|
public TikTokLiveClient(GiftsManager giftsManager,
|
||||||
TikTokApiService tikTokApiService,
|
TikTokRoomInfo tikTokLiveMeta,
|
||||||
|
TikTokLiveHttpClient tiktokHttpClient,
|
||||||
SocketClient webSocketClient,
|
SocketClient webSocketClient,
|
||||||
TikTokGiftManager tikTokGiftManager,
|
TikTokLiveEventHandler tikTokEventHandler,
|
||||||
TikTokEventObserver tikTokEventHandler,
|
LiveClientSettings clientSettings,
|
||||||
ClientSettings clientSettings,
|
|
||||||
TikTokListenersManager listenersManager,
|
TikTokListenersManager listenersManager,
|
||||||
Logger logger) {
|
Logger logger) {
|
||||||
|
this.giftsManager = giftsManager;
|
||||||
this.liveRoomInfo = tikTokLiveMeta;
|
this.liveRoomInfo = tikTokLiveMeta;
|
||||||
this.tikTokGiftManager = tikTokGiftManager;
|
this.httpClient = tiktokHttpClient;
|
||||||
this.apiService = tikTokApiService;
|
|
||||||
this.webSocketClient = webSocketClient;
|
this.webSocketClient = webSocketClient;
|
||||||
this.tikTokEventHandler = tikTokEventHandler;
|
this.tikTokEventHandler = tikTokEventHandler;
|
||||||
this.clientSettings = clientSettings;
|
this.clientSettings = clientSettings;
|
||||||
@@ -76,17 +76,15 @@ public class TikTokLiveClient implements LiveClient {
|
|||||||
|
|
||||||
|
|
||||||
public void connectAsync(Consumer<LiveClient> onConnection) {
|
public void connectAsync(Consumer<LiveClient> onConnection) {
|
||||||
CompletableFuture.supplyAsync(() ->
|
CompletableFuture.runAsync(() -> {
|
||||||
{
|
|
||||||
connect();
|
connect();
|
||||||
onConnection.accept(this);
|
onConnection.accept(this);
|
||||||
return this;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CompletableFuture<LiveClient> connectAsync() {
|
public CompletableFuture<LiveClient> connectAsync() {
|
||||||
return CompletableFuture.supplyAsync(() ->
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
{
|
|
||||||
connect();
|
connect();
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
@@ -117,56 +115,78 @@ public class TikTokLiveClient implements LiveClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnect() {
|
|
||||||
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
webSocketClient.stop();
|
|
||||||
setState(ConnectionState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryConnect() {
|
public void tryConnect() {
|
||||||
if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED))
|
if (!liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
|
||||||
throw new TikTokLiveException("Already connected");
|
throw new TikTokLiveException("Already connected");
|
||||||
if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTING))
|
}
|
||||||
throw new TikTokLiveException("Already connecting");
|
|
||||||
|
|
||||||
setState(ConnectionState.CONNECTING);
|
setState(ConnectionState.CONNECTING);
|
||||||
|
tikTokEventHandler.publish(this, new TikTokConnectingEvent());
|
||||||
|
var userDataRequest = new LiveUserData.Request(liveRoomInfo.getHostName());
|
||||||
|
var userData = httpClient.fetchLiveUserData(userDataRequest);
|
||||||
|
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
|
||||||
|
liveRoomInfo.setRoomId(userData.getRoomId());
|
||||||
|
|
||||||
|
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline)
|
||||||
|
throw new TikTokLiveOfflineHostException("User is offline: " + liveRoomInfo.getHostName());
|
||||||
|
|
||||||
apiService.updateSessionId();
|
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound)
|
||||||
|
throw new TikTokLiveOfflineHostException("User not found: " + liveRoomInfo.getHostName());
|
||||||
|
|
||||||
TikTokUserInfo info = apiService.fetchUserInfoFromTikTokApi(liveRoomInfo.getHostName());
|
var liveDataRequest = new LiveData.Request(userData.getRoomId());
|
||||||
liveRoomInfo.setStartTime(info.getStartTime());
|
var liveData = httpClient.fetchLiveData(liveDataRequest);
|
||||||
if (clientSettings.getRoomId() != null) {
|
|
||||||
liveRoomInfo.setRoomId(clientSettings.getRoomId());
|
|
||||||
logger.info("Using roomID from settings: " + clientSettings.getRoomId());
|
|
||||||
} else {
|
|
||||||
liveRoomInfo.setRoomId(info.getRoomId());
|
|
||||||
}
|
|
||||||
apiService.updateRoomId(liveRoomInfo.getRoomId());
|
|
||||||
|
|
||||||
|
if (liveData.isAgeRestricted())
|
||||||
|
throw new TikTokLiveException("Livestream for " + liveRoomInfo.getHostName() + " is 18+ or age restricted!");
|
||||||
|
|
||||||
var liveRoomMeta = apiService.fetchRoomInfo();
|
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound)
|
||||||
if (liveRoomMeta.getStatus() == LiveRoomMeta.LiveRoomStatus.HostNotFound) {
|
throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " could not be found.");
|
||||||
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());
|
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline)
|
||||||
liveRoomInfo.setViewersCount(liveRoomMeta.getViewers());
|
throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " not found, is the Host offline?");
|
||||||
liveRoomInfo.setTotalViewersCount(liveRoomMeta.getTotalViewers());
|
|
||||||
liveRoomInfo.setAgeRestricted(liveRoomMeta.isAgeRestricted());
|
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
|
||||||
liveRoomInfo.setHost(liveRoomMeta.getHost());
|
|
||||||
|
liveRoomInfo.setTitle(liveData.getTitle());
|
||||||
|
liveRoomInfo.setViewersCount(liveData.getViewers());
|
||||||
|
liveRoomInfo.setTotalViewersCount(liveData.getTotalViewers());
|
||||||
|
liveRoomInfo.setAgeRestricted(liveData.isAgeRestricted());
|
||||||
|
liveRoomInfo.setHost(liveData.getHost());
|
||||||
|
|
||||||
|
var preconnectEvent = new TikTokPreConnectionEvent(userData, liveData);
|
||||||
|
tikTokEventHandler.publish(this, preconnectEvent);
|
||||||
|
if (preconnectEvent.isCancelConnection())
|
||||||
|
throw new TikTokLiveException("TikTokPreConnectionEvent cancelled connection!");
|
||||||
|
|
||||||
|
var liveConnectionRequest = new LiveConnectionData.Request(userData.getRoomId());
|
||||||
|
var liveConnectionData = httpClient.fetchLiveConnectionData(liveConnectionRequest);
|
||||||
|
webSocketClient.start(liveConnectionData, this);
|
||||||
|
|
||||||
var clientData = apiService.fetchClientData();
|
|
||||||
webSocketClient.start(clientData, this);
|
|
||||||
setState(ConnectionState.CONNECTED);
|
setState(ConnectionState.CONNECTED);
|
||||||
tikTokEventHandler.publish(this, new TikTokRoomInfoEvent(liveRoomInfo));
|
tikTokEventHandler.publish(this, new TikTokRoomInfoEvent(liveRoomInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(ConnectionState.DISCONNECTED);
|
||||||
|
webSocketClient.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setState(ConnectionState connectionState) {
|
||||||
|
logger.info("TikTokLive client state: " + connectionState.name());
|
||||||
|
liveRoomInfo.setConnectionState(connectionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publishEvent(TikTokEvent event) {
|
||||||
|
tikTokEventHandler.publish(this, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GiftsManager getGiftManager() {
|
||||||
|
return giftsManager;
|
||||||
|
}
|
||||||
|
|
||||||
public LiveRoomInfo getRoomInfo() {
|
public LiveRoomInfo getRoomInfo() {
|
||||||
return liveRoomInfo;
|
return liveRoomInfo;
|
||||||
@@ -181,20 +201,4 @@ public class TikTokLiveClient implements LiveClient {
|
|||||||
public Logger getLogger() {
|
public Logger getLogger() {
|
||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GiftManager getGiftManager() {
|
|
||||||
return tikTokGiftManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void setState(ConnectionState connectionState) {
|
|
||||||
logger.info("TikTokLive client state: " + connectionState.name());
|
|
||||||
liveRoomInfo.setConnectionState(connectionState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void publishEvent(TikTokEvent event)
|
|
||||||
{
|
|
||||||
tikTokEventHandler.publish(this, event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -22,70 +22,48 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.common.LoggerFactory;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.*;
|
import io.github.jwdeveloper.tiktok.data.events.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent;
|
import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
import io.github.jwdeveloper.tiktok.data.events.gift.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.http.TikTokHttpResponseEvent;
|
import io.github.jwdeveloper.tiktok.data.events.http.TikTokHttpResponseEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent;
|
import io.github.jwdeveloper.tiktok.data.events.poll.TikTokPollEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
|
import io.github.jwdeveloper.tiktok.data.events.room.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomPinEvent;
|
import io.github.jwdeveloper.tiktok.data.events.social.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent;
|
import io.github.jwdeveloper.tiktok.data.events.websocket.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
|
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketMessageEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketUnhandledMessageEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.http.HttpClientFactory;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
import io.github.jwdeveloper.tiktok.listener.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
import io.github.jwdeveloper.tiktok.live.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
|
import io.github.jwdeveloper.tiktok.live.builder.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
|
import io.github.jwdeveloper.tiktok.mappers.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
|
|
||||||
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
|
|
||||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
|
|
||||||
import io.github.jwdeveloper.tiktok.mappers.TikTokGenericEventMapper;
|
|
||||||
import io.github.jwdeveloper.tiktok.mappers.TikTokLiveMapper;
|
|
||||||
import io.github.jwdeveloper.tiktok.mappers.TikTokLiveMapperHelper;
|
|
||||||
import io.github.jwdeveloper.tiktok.mappers.TikTokMapper;
|
|
||||||
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
||||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokCommonEventHandler;
|
import io.github.jwdeveloper.tiktok.mappers.handlers.*;
|
||||||
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.messages.webcast.*;
|
||||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
|
||||||
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
|
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.util.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.*;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
||||||
|
|
||||||
protected final ClientSettings clientSettings;
|
protected final LiveClientSettings clientSettings;
|
||||||
|
protected final TikTokLiveEventHandler tikTokEventHandler;
|
||||||
protected final Logger logger;
|
|
||||||
protected final TikTokEventObserver tikTokEventHandler;
|
|
||||||
protected final List<TikTokEventListener> listeners;
|
protected final List<TikTokEventListener> listeners;
|
||||||
protected Consumer<TikTokMapper> onCustomMappings;
|
protected Consumer<TikTokMapper> onCustomMappings;
|
||||||
|
protected Logger logger;
|
||||||
|
protected GiftsManager giftsManager;
|
||||||
|
|
||||||
public TikTokLiveClientBuilder(String userName) {
|
public TikTokLiveClientBuilder(String userName) {
|
||||||
this.tikTokEventHandler = new TikTokEventObserver();
|
this.clientSettings = LiveClientSettings.createDefault();
|
||||||
this.clientSettings = Constants.DefaultClientSettings();
|
|
||||||
this.clientSettings.setHostName(userName);
|
this.clientSettings.setHostName(userName);
|
||||||
this.logger = Logger.getLogger(TikTokLive.class.getSimpleName() + " " + userName);
|
this.tikTokEventHandler = new TikTokLiveEventHandler();
|
||||||
this.listeners = new ArrayList<>();
|
this.listeners = new ArrayList<>();
|
||||||
this.onCustomMappings = (e) -> {
|
this.onCustomMappings = (e) -> {
|
||||||
};
|
};
|
||||||
@@ -96,62 +74,33 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure) {
|
||||||
public TikTokLiveClientBuilder configure(Consumer<ClientSettings> onConfigure) {
|
|
||||||
onConfigure.accept(clientSettings);
|
onConfigure.accept(clientSettings);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) {
|
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) {
|
||||||
listeners.add(listener);
|
if (listener != null)
|
||||||
|
listeners.add(listener);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void validate() {
|
protected void validate() {
|
||||||
|
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty())
|
||||||
|
clientSettings.setClientLanguage("en");
|
||||||
|
|
||||||
if (clientSettings.getTimeout() == null) {
|
if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty())
|
||||||
clientSettings.setTimeout(Duration.ofSeconds(Constants.DEFAULT_TIMEOUT));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty()) {
|
|
||||||
clientSettings.setClientLanguage(Constants.DefaultClientSettings().getClientLanguage());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty()) {
|
|
||||||
throw new TikTokLiveException("HostName can not be null");
|
throw new TikTokLiveException("HostName can not be null");
|
||||||
}
|
|
||||||
|
|
||||||
if (clientSettings.getHostName().startsWith("@")) {
|
if (clientSettings.getHostName().startsWith("@"))
|
||||||
clientSettings.setHostName(clientSettings.getHostName().substring(1));
|
clientSettings.setHostName(clientSettings.getHostName().substring(1));
|
||||||
}
|
|
||||||
|
|
||||||
|
var httpSettings = clientSettings.getHttpSettings();
|
||||||
|
httpSettings.getParams().put("app_language", clientSettings.getClientLanguage());
|
||||||
|
httpSettings.getParams().put("webcast_language", clientSettings.getClientLanguage());
|
||||||
|
|
||||||
var params = clientSettings.getClientParameters();
|
this.logger = LoggerFactory.create(clientSettings.getHostName(), clientSettings);
|
||||||
params.put("app_language", clientSettings.getClientLanguage());
|
this.giftsManager = clientSettings.isFetchGifts() ? TikTokLive.gifts() : new TikTokGiftsManager(List.of());
|
||||||
params.put("webcast_language", clientSettings.getClientLanguage());
|
|
||||||
|
|
||||||
|
|
||||||
var handler = new ConsoleHandler();
|
|
||||||
handler.setFormatter(new Formatter() {
|
|
||||||
@Override
|
|
||||||
public String format(LogRecord record) {
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.append(ConsoleColors.GREEN).append("[").append(record.getLoggerName()).append("] ");
|
|
||||||
sb.append(ConsoleColors.GREEN).append("[").append(record.getLevel()).append("]: ");
|
|
||||||
sb.append(ConsoleColors.WHITE_BRIGHT).append(record.getMessage());
|
|
||||||
sb.append(ConsoleColors.RESET).append("\n");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
logger.setUseParentHandlers(false);
|
|
||||||
logger.addHandler(handler);
|
|
||||||
|
|
||||||
logger.setLevel(clientSettings.getLogLevel());
|
|
||||||
|
|
||||||
if (!clientSettings.isPrintToConsole()) {
|
|
||||||
logger.setLevel(Level.OFF);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveClient build() {
|
public LiveClient build() {
|
||||||
@@ -161,38 +110,40 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
tiktokRoomInfo.setHostName(clientSettings.getHostName());
|
tiktokRoomInfo.setHostName(clientSettings.getHostName());
|
||||||
|
|
||||||
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
||||||
var cookieJar = new TikTokCookieJar();
|
|
||||||
var requestFactory = new TikTokHttpRequestFactory(cookieJar, tikTokEventHandler);
|
var httpClientFactory = new HttpClientFactory(clientSettings);
|
||||||
var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
|
var tikTokLiveHttpClient = new TikTokLiveHttpClient(httpClientFactory, clientSettings);
|
||||||
var apiService = new TikTokApiService(apiClient, logger, clientSettings);
|
|
||||||
var giftManager = new TikTokGiftManager(logger);
|
|
||||||
var eventsMapper = createMapper(giftManager, tiktokRoomInfo);
|
var eventsMapper = createMapper(giftsManager, tiktokRoomInfo);
|
||||||
var messageHandler = new TikTokMessageHandler(tikTokEventHandler, eventsMapper);
|
var messageHandler = new TikTokLiveMessageHandler(tikTokEventHandler, eventsMapper);
|
||||||
|
|
||||||
|
|
||||||
var webSocketClient = new TikTokWebSocketClient(
|
var webSocketClient = new TikTokWebSocketClient(
|
||||||
cookieJar,
|
|
||||||
clientSettings,
|
clientSettings,
|
||||||
messageHandler,
|
messageHandler,
|
||||||
tikTokEventHandler);
|
tikTokEventHandler);
|
||||||
|
|
||||||
return new TikTokLiveClient(tiktokRoomInfo,
|
return new TikTokLiveClient(
|
||||||
apiService,
|
giftsManager,
|
||||||
|
tiktokRoomInfo,
|
||||||
|
tikTokLiveHttpClient,
|
||||||
webSocketClient,
|
webSocketClient,
|
||||||
giftManager,
|
|
||||||
tikTokEventHandler,
|
tikTokEventHandler,
|
||||||
clientSettings,
|
clientSettings,
|
||||||
listenerManager,
|
listenerManager,
|
||||||
logger);
|
logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveMapper createMapper(GiftManager giftManager, TikTokRoomInfo roomInfo) {
|
public TikTokLiveMapper createMapper(GiftsManager giftsManager, TikTokRoomInfo roomInfo) {
|
||||||
|
|
||||||
|
|
||||||
var eventMapper = new TikTokGenericEventMapper();
|
var eventMapper = new TikTokGenericEventMapper();
|
||||||
var mapper = new TikTokLiveMapper(new TikTokLiveMapperHelper(eventMapper));
|
var mapper = new TikTokLiveMapper(new TikTokLiveMapperHelper(eventMapper));
|
||||||
|
|
||||||
//ConnectionEvents events
|
//ConnectionEvents events
|
||||||
var commonHandler = new TikTokCommonEventHandler();
|
var commonHandler = new TikTokCommonEventHandler();
|
||||||
var giftHandler = new TikTokGiftEventHandler(giftManager, roomInfo);
|
var giftHandler = new TikTokGiftEventHandler(giftsManager, roomInfo);
|
||||||
var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo);
|
var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo);
|
||||||
var socialHandler = new TikTokSocialMediaEventHandler(roomInfo);
|
var socialHandler = new TikTokSocialMediaEventHandler(roomInfo);
|
||||||
|
|
||||||
@@ -248,14 +199,14 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
|
|
||||||
|
|
||||||
//LinkMic events
|
//LinkMic events
|
||||||
// mapper.webcastObjectToConstructor(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastLinkMicBattle.class, TikTokLinkMicBattleEvent.class);
|
||||||
// mapper.webcastObjectToConstructor(WebcastLinkMicArmies.class, TikTokLinkMicArmiesEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastLinkMicArmies.class, TikTokLinkMicArmiesEvent.class);
|
||||||
// mapper.webcastObjectToConstructor(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastLinkMicMethod.class, TikTokLinkMicMethodEvent.class);
|
||||||
// mapper.webcastObjectToConstructor(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastLinkMicFanTicketMethod.class, TikTokLinkMicFanTicketEvent.class);
|
||||||
|
|
||||||
//Rank events
|
//Rank events
|
||||||
// mapper.webcastObjectToConstructor(WebcastRankTextMessage.class, TikTokRankTextEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastRankTextMessage.class, TikTokRankTextEvent.class);
|
||||||
// mapper.webcastObjectToConstructor(WebcastRankUpdateMessage.class, TikTokRankUpdateEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastRankUpdateMessage.class, TikTokRankUpdateEvent.class);
|
||||||
// mapper.webcastObjectToConstructor(WebcastHourlyRankMessage.class, TikTokRankUpdateEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastHourlyRankMessage.class, TikTokRankUpdateEvent.class);
|
||||||
|
|
||||||
//Others events
|
//Others events
|
||||||
@@ -263,7 +214,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
// mapper.webcastObjectToConstructor(WebcastMsgDetectMessage.class, TikTokDetectEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastMsgDetectMessage.class, TikTokDetectEvent.class);
|
||||||
// mapper.webcastObjectToConstructor(WebcastBarrageMessage.class, TikTokBarrageEvent.class);
|
// mapper.webcastObjectToConstructor(WebcastBarrageMessage.class, TikTokBarrageEvent.class);
|
||||||
// mapper.webcastObjectToConstructor(WebcastUnauthorizedMemberMessage.class, TikTokUnauthorizedMemberEvent.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.webcastObjectToConstructor(WebcastImDeleteMessage.class, TikTokIMDeleteEvent.class);
|
||||||
// mapper.bytesToEvents(WebcastEnvelopeMessage.class, commonHandler::handleEnvelop);
|
// mapper.bytesToEvents(WebcastEnvelopeMessage.class, commonHandler::handleEnvelop);
|
||||||
|
|
||||||
@@ -283,22 +234,17 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return build().connectAsync();
|
return build().connectAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TikTokLiveClientBuilder onUnhandledSocial(EventConsumer<TikTokUnhandledSocialEvent> event) {
|
||||||
public TikTokLiveClientBuilder onUnhandledSocial(
|
|
||||||
EventConsumer<TikTokUnhandledSocialEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokUnhandledSocialEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokUnhandledSocialEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
|
||||||
public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) {
|
public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) {
|
||||||
tikTokEventHandler.subscribe(TikTokChestEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokChestEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TikTokLiveClientBuilder onLinkMicFanTicket(EventConsumer<TikTokLinkMicFanTicketEvent> event) {
|
||||||
public TikTokLiveClientBuilder onLinkMicFanTicket(
|
|
||||||
EventConsumer<TikTokLinkMicFanTicketEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -313,14 +259,12 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onDetect(
|
public TikTokLiveClientBuilder onDetect(EventConsumer<TikTokDetectEvent> event) {
|
||||||
EventConsumer<TikTokDetectEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokDetectEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokDetectEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onLinkLayer(
|
public TikTokLiveClientBuilder onLinkLayer(EventConsumer<TikTokLinkLayerEvent> event) {
|
||||||
EventConsumer<TikTokLinkLayerEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokLinkLayerEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokLinkLayerEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -330,6 +274,11 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TikTokLiveClientBuilder onPreConnection(EventConsumer<TikTokPreConnectionEvent> event) {
|
||||||
|
tikTokEventHandler.subscribe(TikTokPreConnectionEvent.class, event);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onCaption(EventConsumer<TikTokCaptionEvent> event) {
|
public TikTokLiveClientBuilder onCaption(EventConsumer<TikTokCaptionEvent> event) {
|
||||||
tikTokEventHandler.subscribe(TikTokCaptionEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokCaptionEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
@@ -340,8 +289,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onRoomPin(
|
public TikTokLiveClientBuilder onRoomPin(EventConsumer<TikTokRoomPinEvent> event) {
|
||||||
EventConsumer<TikTokRoomPinEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokRoomPinEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokRoomPinEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -352,21 +300,19 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) {
|
public TikTokLiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) {
|
||||||
tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onLivePaused(EventConsumer<TikTokLivePausedEvent> event) {
|
public TikTokLiveClientBuilder onLivePaused(EventConsumer<TikTokLivePausedEvent> event) {
|
||||||
tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveClientBuilder onLiveUnpaused(EventConsumer<TikTokLiveUnpausedEvent> event) {
|
public TikTokLiveClientBuilder onLiveUnpaused(EventConsumer<TikTokLiveUnpausedEvent> event) {
|
||||||
tikTokEventHandler.subscribe(TikTokLiveUnpausedEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokLiveUnpausedEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -381,13 +327,11 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onBarrage(
|
public TikTokLiveClientBuilder onBarrage(EventConsumer<TikTokBarrageEvent> event) {
|
||||||
EventConsumer<TikTokBarrageEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokBarrageEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokBarrageEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onGift(EventConsumer<TikTokGiftEvent> event) {
|
public TikTokLiveClientBuilder onGift(EventConsumer<TikTokGiftEvent> event) {
|
||||||
tikTokEventHandler.subscribe(TikTokGiftEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokGiftEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
@@ -398,9 +342,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TikTokLiveClientBuilder onLinkMicArmies(EventConsumer<TikTokLinkMicArmiesEvent> event) {
|
||||||
public TikTokLiveClientBuilder onLinkMicArmies(
|
|
||||||
EventConsumer<TikTokLinkMicArmiesEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -410,20 +352,17 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onUnauthorizedMember(
|
public TikTokLiveClientBuilder onUnauthorizedMember(EventConsumer<TikTokUnauthorizedMemberEvent> event) {
|
||||||
EventConsumer<TikTokUnauthorizedMemberEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onInRoomBanner(
|
public TikTokLiveClientBuilder onInRoomBanner(EventConsumer<TikTokInRoomBannerEvent> event) {
|
||||||
EventConsumer<TikTokInRoomBannerEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onLinkMicMethod(
|
public TikTokLiveClientBuilder onLinkMicMethod(EventConsumer<TikTokLinkMicMethodEvent> event) {
|
||||||
EventConsumer<TikTokLinkMicMethodEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -479,7 +418,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onJoin(EventConsumer<TikTokJoinEvent> event) {
|
public TikTokLiveClientBuilder onJoin(EventConsumer<TikTokJoinEvent> event) {
|
||||||
tikTokEventHandler.subscribe(TikTokJoinEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokJoinEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
@@ -495,8 +433,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onUnhandledMember(
|
public TikTokLiveClientBuilder onUnhandledMember(EventConsumer<TikTokUnhandledMemberEvent> event) {
|
||||||
EventConsumer<TikTokUnhandledMemberEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokUnhandledMemberEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokUnhandledMemberEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -506,20 +443,17 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onLinkMicBattle(
|
public TikTokLiveClientBuilder onLinkMicBattle(EventConsumer<TikTokLinkMicBattleEvent> event) {
|
||||||
EventConsumer<TikTokLinkMicBattleEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onDisconnected(
|
public TikTokLiveClientBuilder onDisconnected(EventConsumer<TikTokDisconnectedEvent> event) {
|
||||||
EventConsumer<TikTokDisconnectedEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder onUnhandledControl(
|
public TikTokLiveClientBuilder onUnhandledControl(EventConsumer<TikTokUnhandledControlEvent> event) {
|
||||||
EventConsumer<TikTokUnhandledControlEvent> event) {
|
|
||||||
tikTokEventHandler.subscribe(TikTokUnhandledControlEvent.class, event);
|
tikTokEventHandler.subscribe(TikTokUnhandledControlEvent.class, event);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* 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.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
|
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
|
||||||
@@ -31,10 +31,10 @@ import java.util.HashSet;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class TikTokEventObserver {
|
public class TikTokLiveEventHandler {
|
||||||
private final Map<Class<?>, Set<EventConsumer>> events;
|
private final Map<Class<?>, Set<EventConsumer>> events;
|
||||||
|
|
||||||
public TikTokEventObserver() {
|
public TikTokLiveEventHandler() {
|
||||||
events = new HashMap<>();
|
events = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.requests.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||||
|
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.http.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.http.mappers.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
|
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class TikTokLiveHttpClient implements LiveHttpClient
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* <a href="https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures">Signing API by Isaac Kogan</a>
|
||||||
|
*/
|
||||||
|
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";
|
||||||
|
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
|
||||||
|
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
|
||||||
|
public static final String TIKTOK_GIFTS_URL = "https://raw.githubusercontent.com/TikTok-LIVE-Private/GiftsGenerator/master/page/public/gifts.json";
|
||||||
|
public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110;
|
||||||
|
|
||||||
|
private final HttpClientFactory httpFactory;
|
||||||
|
private final LiveClientSettings clientSettings;
|
||||||
|
private final LiveUserDataMapper liveUserDataMapper;
|
||||||
|
private final LiveDataMapper liveDataMapper;
|
||||||
|
private final GiftsDataMapper giftsDataMapper;
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
|
public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) {
|
||||||
|
this.httpFactory = factory;
|
||||||
|
this.clientSettings = settings;
|
||||||
|
this.logger = LoggerFactory.create("HttpClient", clientSettings);
|
||||||
|
liveUserDataMapper = new LiveUserDataMapper();
|
||||||
|
liveDataMapper = new LiveDataMapper();
|
||||||
|
giftsDataMapper = new GiftsDataMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TikTokLiveHttpClient() {
|
||||||
|
this(new HttpClientFactory(LiveClientSettings.createDefault()), LiveClientSettings.createDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GiftsData.Response fetchGiftsData() {
|
||||||
|
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||||
|
if (proxyClientSettings.isEnabled()) {
|
||||||
|
while (proxyClientSettings.hasNext()) {
|
||||||
|
try {
|
||||||
|
return getGiftsData();
|
||||||
|
} catch (TikTokProxyRequestException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getGiftsData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GiftsData.Response getGiftsData() {
|
||||||
|
var result = httpFactory.client(TIKTOK_GIFTS_URL)
|
||||||
|
.build()
|
||||||
|
.toJsonResponse();
|
||||||
|
|
||||||
|
if (result.isFailure())
|
||||||
|
throw new TikTokLiveRequestException("Unable to fetch gifts information's"+result.toStack());
|
||||||
|
|
||||||
|
var json = result.getContent();
|
||||||
|
return giftsDataMapper.map(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
|
||||||
|
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||||
|
if (proxyClientSettings.isEnabled()) {
|
||||||
|
while (proxyClientSettings.hasNext()) {
|
||||||
|
try {
|
||||||
|
return getLiveUserData(request);
|
||||||
|
} catch (TikTokProxyRequestException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getLiveUserData(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveUserData.Response getLiveUserData(LiveUserData.Request request) {
|
||||||
|
var url = TIKTOK_URL_WEB + "api-live/user/room";
|
||||||
|
var result = httpFactory.client(url)
|
||||||
|
.withParam("uniqueId", request.getUserName())
|
||||||
|
.withParam("sourceType", "54")
|
||||||
|
.build()
|
||||||
|
.toJsonResponse();
|
||||||
|
|
||||||
|
if (result.isFailure())
|
||||||
|
throw new TikTokLiveRequestException("Unable to get information's about user"+result.toStack());
|
||||||
|
|
||||||
|
var json = result.getContent();
|
||||||
|
return liveUserDataMapper.map(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LiveData.Response fetchLiveData(LiveData.Request request) {
|
||||||
|
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||||
|
if (proxyClientSettings.isEnabled()) {
|
||||||
|
while (proxyClientSettings.hasNext()) {
|
||||||
|
try {
|
||||||
|
return getLiveData(request);
|
||||||
|
} catch (TikTokProxyRequestException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getLiveData(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData.Response getLiveData(LiveData.Request request) {
|
||||||
|
var url = TIKTOK_URL_WEBCAST + "room/info";
|
||||||
|
var result = httpFactory.client(url)
|
||||||
|
.withParam("room_id", request.getRoomId())
|
||||||
|
.build()
|
||||||
|
.toJsonResponse();
|
||||||
|
|
||||||
|
if (result.isFailure())
|
||||||
|
throw new TikTokLiveRequestException("Unable to get info about live room"+result.toStack());
|
||||||
|
|
||||||
|
var json = result.getContent();
|
||||||
|
return liveDataMapper.map(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
|
||||||
|
var result = getStartingPayload(request);
|
||||||
|
HttpResponse<byte[]> credentialsResponse = result.getContent(); // Always guaranteed to have response
|
||||||
|
|
||||||
|
try {
|
||||||
|
var resultHeader = ActionResult.of(credentialsResponse.headers().firstValue("x-set-tt-cookie"));
|
||||||
|
if (resultHeader.isFailure()) {
|
||||||
|
logger.warning("SignServer Headers: "+request.getRoomId()+" - "+credentialsResponse.headers().map());
|
||||||
|
throw new TikTokSignServerException("Sign server did not return the x-set-tt-cookie header"+result.toStack());
|
||||||
|
}
|
||||||
|
var websocketCookie = resultHeader.getContent();
|
||||||
|
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"+result.toStack());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionResult<HttpResponse<byte[]>> getStartingPayload(LiveConnectionData.Request request) {
|
||||||
|
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||||
|
if (proxyClientSettings.isEnabled()) {
|
||||||
|
while (proxyClientSettings.hasNext()) {
|
||||||
|
try {
|
||||||
|
return getByteResponse(request.getRoomId());
|
||||||
|
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getByteResponse(request.getRoomId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionResult<HttpResponse<byte[]>> getByteResponse(String room_id) {
|
||||||
|
HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API)
|
||||||
|
.withParam("client", "ttlive-java")
|
||||||
|
.withParam("uuc", "1")
|
||||||
|
.withParam("room_id", room_id);
|
||||||
|
|
||||||
|
if (clientSettings.getApiKey() != null)
|
||||||
|
builder.withParam("apiKey", clientSettings.getApiKey());
|
||||||
|
|
||||||
|
var result = builder.build().toResponse();
|
||||||
|
|
||||||
|
if (result.isFailure())
|
||||||
|
throw new TikTokSignServerException("Unable to get websocket connection credentials"+result.toStack());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* 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;
|
import io.github.jwdeveloper.tiktok.data.dto.MessageMetaData;
|
||||||
@@ -36,18 +36,16 @@ import io.github.jwdeveloper.tiktok.utils.Stopwatch;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
public class TikTokLiveMessageHandler {
|
||||||
|
|
||||||
public class TikTokMessageHandler {
|
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||||
|
|
||||||
private final TikTokEventObserver tikTokEventHandler;
|
|
||||||
private final TikTokLiveMapper mapper;
|
private final TikTokLiveMapper mapper;
|
||||||
|
|
||||||
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, TikTokLiveMapper mapper) {
|
public TikTokLiveMessageHandler(TikTokLiveEventHandler tikTokEventHandler, TikTokLiveMapper mapper) {
|
||||||
this.tikTokEventHandler = tikTokEventHandler;
|
this.tikTokEventHandler = tikTokEventHandler;
|
||||||
this.mapper = mapper;
|
this.mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void handle(LiveClient client, WebcastResponse webcastResponse) {
|
public void handle(LiveClient client, WebcastResponse webcastResponse) {
|
||||||
tikTokEventHandler.publish(client, new TikTokWebsocketResponseEvent(webcastResponse));
|
tikTokEventHandler.publish(client, new TikTokWebsocketResponseEvent(webcastResponse));
|
||||||
for (var message : webcastResponse.getMessagesList()) {
|
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();
|
var messageClassName = message.getMethod();
|
||||||
if (!mapper.isRegistered(messageClassName)) {
|
if (!mapper.isRegistered(messageClassName)) {
|
||||||
tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(message));
|
tikTokEventHandler.publish(client, new TikTokWebsocketUnhandledMessageEvent(message));
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.common;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ActionResult<T> {
|
||||||
|
|
||||||
|
private boolean success = true;
|
||||||
|
private T content;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
protected ActionResult(T object) {
|
||||||
|
this.content = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ActionResult(T object, boolean success) {
|
||||||
|
this(object);
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ActionResult(T object, boolean success, String message) {
|
||||||
|
this(object, success);
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResultBuilder<T> of(T content) {
|
||||||
|
return new ActionResultBuilder<>(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResult<T> of(Optional<T> optional) {
|
||||||
|
return new ActionResult<>(optional.orElse(null), optional.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFailure() {
|
||||||
|
return !isSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMessage() {
|
||||||
|
return message != null;
|
||||||
|
}
|
||||||
|
public String toStack() {
|
||||||
|
return hasMessage() ? " - "+message : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasContent() {
|
||||||
|
return content != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <Output> ActionResult<Output> cast(Output output) {
|
||||||
|
return new ActionResult<>(output, this.isSuccess(), this.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <Output> ActionResult<Output> cast() {
|
||||||
|
return cast(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <U> ActionResult<U> map(Function<? super T, ? extends U> mapper) {
|
||||||
|
return hasContent() ? cast(mapper.apply(content)) : cast();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResult<T> success(T payload, String message) {
|
||||||
|
return new ActionResult<>(payload, true, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResult<T> success(T payload) {
|
||||||
|
return success(payload, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResult<T> success() {
|
||||||
|
return success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResult<T> failure(T target, String message) {
|
||||||
|
return new ActionResult<>(target, false, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResult<T> failure(String message) {
|
||||||
|
return failure(null, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ActionResult<T> failure() {
|
||||||
|
return failure(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.common;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ActionResultBuilder<T>
|
||||||
|
{
|
||||||
|
private final T content;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public ActionResultBuilder(T content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResultBuilder<T> message(Object... messages) {
|
||||||
|
this.message = Arrays.stream(messages).map(Object::toString).collect(Collectors.joining(" "));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult<T> success() {
|
||||||
|
return ActionResult.success(content, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult<T> failure() {
|
||||||
|
return ActionResult.success(content, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.common;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||||
|
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||||
|
|
||||||
|
import java.util.logging.*;
|
||||||
|
|
||||||
|
public class LoggerFactory
|
||||||
|
{
|
||||||
|
public static Logger create(String name, LiveClientSettings settings) {
|
||||||
|
Logger logger = Logger.getLogger(name);
|
||||||
|
if (logger.getHandlers().length == 0) {
|
||||||
|
var handler = new ConsoleHandler();
|
||||||
|
handler.setFormatter(new Formatter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String format(LogRecord record) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append(ConsoleColors.GREEN).append("[").append(record.getLoggerName()).append("] ");
|
||||||
|
sb.append(ConsoleColors.GREEN).append("[").append(record.getLevel()).append("]: ");
|
||||||
|
sb.append(ConsoleColors.WHITE_BRIGHT).append(record.getMessage());
|
||||||
|
sb.append(ConsoleColors.RESET).append("\n");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
logger.setUseParentHandlers(false);
|
||||||
|
logger.addHandler(handler);
|
||||||
|
logger.setLevel(settings.getLogLevel());
|
||||||
|
if (!settings.isPrintToConsole())
|
||||||
|
logger.setLevel(Level.OFF);
|
||||||
|
}
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package io.github.jwdeveloper.tiktok.gifts;
|
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
|
||||||
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;
|
|
||||||
|
|
||||||
public class TikTokGiftManager implements GiftManager {
|
|
||||||
|
|
||||||
private final Map<Integer, Gift> indexById;
|
|
||||||
private final Map<String, Gift> indexByName;
|
|
||||||
private final Logger logger;
|
|
||||||
|
|
||||||
public TikTokGiftManager(Logger logger)
|
|
||||||
{
|
|
||||||
indexById = new HashMap<>();
|
|
||||||
indexByName = new HashMap<>();
|
|
||||||
this.logger = logger;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void init() {
|
|
||||||
for (var gift : Gift.values()) {
|
|
||||||
indexById.put(gift.getId(), gift);
|
|
||||||
indexByName.put(gift.getName(), gift);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Gift registerGift(int id, String name, int diamondCost, Picture picture) {
|
|
||||||
try {
|
|
||||||
var constructor = Unsafe.class.getDeclaredConstructors()[0];
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
var unsafe = (Unsafe) constructor.newInstance();
|
|
||||||
Gift enumInstance = (Gift) unsafe.allocateInstance(Gift.class);
|
|
||||||
|
|
||||||
var field = Gift.class.getDeclaredField("id");
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(enumInstance, id);
|
|
||||||
|
|
||||||
field = Gift.class.getDeclaredField("name");
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(enumInstance, name);
|
|
||||||
|
|
||||||
|
|
||||||
// EnumSet
|
|
||||||
field = Gift.class.getDeclaredField("diamondCost");
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(enumInstance, diamondCost);
|
|
||||||
|
|
||||||
field = Gift.class.getDeclaredField("picture");
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(enumInstance, picture);
|
|
||||||
|
|
||||||
indexById.put(enumInstance.getId(), enumInstance);
|
|
||||||
indexByName.put(enumInstance.getName(), enumInstance);
|
|
||||||
|
|
||||||
return enumInstance;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new TikTokLiveException("Unable to register gift: " + name + ": " + id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Gift findById(int giftId) {
|
|
||||||
if (!indexById.containsKey(giftId)) {
|
|
||||||
return Gift.UNDEFINED;
|
|
||||||
}
|
|
||||||
return indexById.get(giftId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Gift findByName(String giftName) {
|
|
||||||
if (!indexByName.containsKey(giftName)) {
|
|
||||||
return Gift.UNDEFINED;
|
|
||||||
}
|
|
||||||
return indexByName.get(giftName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Gift> getGifts()
|
|
||||||
{
|
|
||||||
return indexById.values().stream().toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package io.github.jwdeveloper.tiktok.gifts;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
|
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class TikTokGiftsManager implements GiftsManager {
|
||||||
|
private final Map<Integer, Gift> giftsByIdIndex;
|
||||||
|
|
||||||
|
public TikTokGiftsManager(List<Gift> giftList)
|
||||||
|
{
|
||||||
|
giftsByIdIndex = giftList.stream().collect(Collectors.toConcurrentMap(Gift::getId, e -> e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachGift(Gift gift) {
|
||||||
|
giftsByIdIndex.put(gift.getId(), gift);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachGiftsList(List<Gift> gifts) {
|
||||||
|
gifts.forEach(this::attachGift);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gift getByName(String name) {
|
||||||
|
return getByFilter(e -> e.getName().equalsIgnoreCase(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gift getById(int giftId) {
|
||||||
|
if (!giftsByIdIndex.containsKey(giftId)) {
|
||||||
|
return Gift.UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return giftsByIdIndex.get(giftId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gift getByFilter(Predicate<Gift> filter) {
|
||||||
|
return giftsByIdIndex.values()
|
||||||
|
.stream()
|
||||||
|
.filter(filter)
|
||||||
|
.findFirst()
|
||||||
|
.orElseGet(() -> Gift.UNDEFINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Gift> getManyByFilter(Predicate<Gift> filter) {
|
||||||
|
return giftsByIdIndex.values()
|
||||||
|
.stream()
|
||||||
|
.filter(filter)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Gift> toList() {
|
||||||
|
return giftsByIdIndex.values().stream().toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, Gift> toMap() {
|
||||||
|
return Collections.unmodifiableMap(giftsByIdIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.ActionResult;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.settings.HttpClientSettings;
|
||||||
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.net.*;
|
||||||
|
import java.net.http.*;
|
||||||
|
import java.nio.charset.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class HttpClient {
|
||||||
|
|
||||||
|
protected final HttpClientSettings httpClientSettings;
|
||||||
|
protected final String url;
|
||||||
|
private final Pattern pattern = Pattern.compile("charset=(.*?)(?=&|$)");
|
||||||
|
|
||||||
|
public ActionResult<HttpResponse<byte[]>> toResponse() {
|
||||||
|
var client = prepareClient();
|
||||||
|
var request = prepareGetRequest();
|
||||||
|
try {
|
||||||
|
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||||
|
var result = ActionResult.of(response);
|
||||||
|
return response.statusCode() != 200 ? result.message("HttpResponse Code: ", response.statusCode()).failure() : result.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new TikTokLiveRequestException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult<String> toJsonResponse() {
|
||||||
|
return toResponse().map(content -> new String(content.body(), charsetFrom(content.headers())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Charset charsetFrom(HttpHeaders headers) {
|
||||||
|
String type = headers.firstValue("Content-type").orElse("text/html; charset=utf-8");
|
||||||
|
int i = type.indexOf(";");
|
||||||
|
if (i >= 0) type = type.substring(i+1);
|
||||||
|
try {
|
||||||
|
Matcher matcher = pattern.matcher(type);
|
||||||
|
if (!matcher.find())
|
||||||
|
return StandardCharsets.UTF_8;
|
||||||
|
return Charset.forName(matcher.group(1));
|
||||||
|
} catch (Throwable x) {
|
||||||
|
return StandardCharsets.UTF_8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult<byte[]> toBinaryResponse() {
|
||||||
|
return toResponse().map(HttpResponse::body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI toUrl() {
|
||||||
|
var stringUrl = prepareUrlWithParameters(url, httpClientSettings.getParams());
|
||||||
|
return URI.create(stringUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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,86 @@
|
|||||||
|
/*
|
||||||
|
* 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() {
|
||||||
|
var proxyClientSettings = httpClientSettings.getProxyClientSettings();
|
||||||
|
if (proxyClientSettings.isEnabled() && proxyClientSettings.hasNext())
|
||||||
|
return new HttpProxyClient(httpClientSettings, url);
|
||||||
|
return new HttpClient(httpClientSettings, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
|
||||||
|
public class HttpClientFactory {
|
||||||
|
private final LiveClientSettings liveClientSettings;
|
||||||
|
|
||||||
|
public HttpClientFactory(LiveClientSettings liveClientSettings) {
|
||||||
|
this.liveClientSettings = liveClientSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder client(String url) {
|
||||||
|
return new HttpClientBuilder(url, liveClientSettings.getHttpSettings().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Does not contains default httpClientSettings, Params, headers, etd
|
||||||
|
public HttpClientBuilder clientEmpty(String url) {
|
||||||
|
var settings = new HttpClientSettings();
|
||||||
|
settings.setProxyClientSettings(liveClientSettings.getHttpSettings().getProxyClientSettings());
|
||||||
|
return new HttpClientBuilder(url, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.ActionResult;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.settings.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.*;
|
||||||
|
import java.net.http.*;
|
||||||
|
import java.net.http.HttpResponse.ResponseInfo;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class HttpProxyClient extends HttpClient {
|
||||||
|
|
||||||
|
private final ProxyClientSettings proxySettings;
|
||||||
|
|
||||||
|
public HttpProxyClient(HttpClientSettings httpClientSettings, String url) {
|
||||||
|
super(httpClientSettings, url);
|
||||||
|
this.proxySettings = httpClientSettings.getProxyClientSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult<HttpResponse<byte[]>> toResponse() {
|
||||||
|
return switch (proxySettings.getType()) {
|
||||||
|
case HTTP, DIRECT -> handleHttpProxyRequest();
|
||||||
|
default -> handleSocksProxyRequest();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult<HttpResponse<byte[]>> handleHttpProxyRequest() {
|
||||||
|
var builder = java.net.http.HttpClient.newBuilder()
|
||||||
|
.followRedirects(java.net.http.HttpClient.Redirect.NORMAL)
|
||||||
|
.cookieHandler(new CookieManager())
|
||||||
|
.connectTimeout(httpClientSettings.getTimeout());
|
||||||
|
|
||||||
|
while (proxySettings.hasNext()) {
|
||||||
|
try {
|
||||||
|
InetSocketAddress address = proxySettings.next().toSocketAddress();
|
||||||
|
builder.proxy(ProxySelector.of(address));
|
||||||
|
|
||||||
|
httpClientSettings.getOnClientCreating().accept(builder);
|
||||||
|
var client = builder.build();
|
||||||
|
var request = prepareGetRequest();
|
||||||
|
|
||||||
|
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||||
|
if (response.statusCode() != 200)
|
||||||
|
continue;
|
||||||
|
return ActionResult.success(response);
|
||||||
|
} catch (HttpConnectTimeoutException | ConnectException e) {
|
||||||
|
if (proxySettings.isAutoDiscard())
|
||||||
|
proxySettings.remove();
|
||||||
|
throw new TikTokProxyRequestException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
|
||||||
|
return super.toResponse();
|
||||||
|
throw new TikTokProxyRequestException(e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new TikTokLiveRequestException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new TikTokLiveRequestException("No more proxies available!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionResult<HttpResponse<byte[]>> handleSocksProxyRequest() {
|
||||||
|
try {
|
||||||
|
SSLContext sc = SSLContext.getInstance("SSL");
|
||||||
|
sc.init(null, new TrustManager[]{ new X509TrustManager() {
|
||||||
|
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {}
|
||||||
|
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
|
||||||
|
public X509Certificate[] getAcceptedIssuers() { return null; }
|
||||||
|
}}, null);
|
||||||
|
|
||||||
|
URL url = toUrl().toURL();
|
||||||
|
|
||||||
|
if (proxySettings.hasNext()) {
|
||||||
|
try {
|
||||||
|
Proxy proxy = new Proxy(Proxy.Type.SOCKS, proxySettings.next().toSocketAddress());
|
||||||
|
|
||||||
|
HttpsURLConnection socksConnection = (HttpsURLConnection) url.openConnection(proxy);
|
||||||
|
socksConnection.setSSLSocketFactory(sc.getSocketFactory());
|
||||||
|
socksConnection.setConnectTimeout(httpClientSettings.getTimeout().toMillisPart());
|
||||||
|
socksConnection.setReadTimeout(httpClientSettings.getTimeout().toMillisPart());
|
||||||
|
|
||||||
|
byte[] body = socksConnection.getInputStream().readAllBytes();
|
||||||
|
|
||||||
|
Map<String, List<String>> headers = socksConnection.getHeaderFields()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(entry -> entry.getKey() != null)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
|
||||||
|
var responseInfo = createResponseInfo(socksConnection.getResponseCode(), headers);
|
||||||
|
|
||||||
|
var response = createHttpResponse(body, toUrl(), responseInfo);
|
||||||
|
|
||||||
|
return ActionResult.success(response);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
|
||||||
|
return super.toResponse();
|
||||||
|
if (proxySettings.isAutoDiscard())
|
||||||
|
proxySettings.remove();
|
||||||
|
throw new TikTokProxyRequestException(e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new TikTokLiveRequestException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new TikTokLiveRequestException("No more proxies available!");
|
||||||
|
} catch (NoSuchAlgorithmException | MalformedURLException | KeyManagementException e) {
|
||||||
|
// Should never be reached!
|
||||||
|
System.out.println("handleSocksProxyRequest: If you see this, message us on discord!");
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (TikTokLiveRequestException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return ActionResult.failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseInfo createResponseInfo(int code, Map<String, List<String>> headers) {
|
||||||
|
return new ResponseInfo() {
|
||||||
|
@Override
|
||||||
|
public int statusCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders headers() {
|
||||||
|
return HttpHeaders.of(headers, (s, s1) -> s != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.net.http.HttpClient.Version version() {
|
||||||
|
return java.net.http.HttpClient.Version.HTTP_2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpResponse<byte[]> createHttpResponse(byte[] body,
|
||||||
|
URI uri,
|
||||||
|
ResponseInfo info) {
|
||||||
|
return new HttpResponse<>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int statusCode() {
|
||||||
|
return info.statusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpRequest request() {
|
||||||
|
throw new UnsupportedOperationException("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<HttpResponse<byte[]>> previousResponse() {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders headers() {
|
||||||
|
return info.headers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] body() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<SSLSession> sslSession() {
|
||||||
|
throw new UnsupportedOperationException("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI uri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.net.http.HttpClient.Version version() {
|
||||||
|
return info.version();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +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 java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class HttpUtils
|
|
||||||
{
|
|
||||||
public static String parseParameters(String url, Map<String,Object> parameters)
|
|
||||||
{
|
|
||||||
if (parameters.isEmpty())
|
|
||||||
return url;
|
|
||||||
|
|
||||||
return url+ "?" + parameters.entrySet().stream().map(entry -> entry.getKey()+"="+entry.getValue()).collect(Collectors.joining("&"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String parseParametersEncode(String url, Map<String,Object> parameters)
|
|
||||||
{
|
|
||||||
if (parameters.isEmpty())
|
|
||||||
return url;
|
|
||||||
|
|
||||||
return url+ "?" + parameters.entrySet().stream().map(entry -> {
|
|
||||||
String encodedKey = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8);
|
|
||||||
String encodedValue = URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8);
|
|
||||||
return encodedKey+"="+encodedValue;
|
|
||||||
}).collect(Collectors.joining("&"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,138 +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.*;
|
|
||||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.dto.TikTokUserInfo;
|
|
||||||
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;
|
|
||||||
|
|
||||||
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 void updateRoomId(String roomId)
|
|
||||||
{
|
|
||||||
clientSettings.getClientParameters().put("room_id", roomId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TikTokUserInfo fetchUserInfoFromTikTokApi(String userName) {
|
|
||||||
var params = new HashMap<>(clientSettings.getClientParameters());
|
|
||||||
params.put("uniqueId", userName);
|
|
||||||
params.put("sourceType", 54);
|
|
||||||
JsonObject roomData;
|
|
||||||
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 the developer");
|
|
||||||
}
|
|
||||||
if (message.equals("user_not_found")) {
|
|
||||||
return new TikTokUserInfo(TikTokUserInfo.UserStatus.NotFound, "", -1);
|
|
||||||
}
|
|
||||||
//live -> status 2
|
|
||||||
//live paused -> 3
|
|
||||||
//not live -> status 4
|
|
||||||
var element = roomData.get("data");
|
|
||||||
if (element.isJsonNull()) {
|
|
||||||
return new TikTokUserInfo(TikTokUserInfo.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 -> TikTokUserInfo.UserStatus.Live;
|
|
||||||
case 3 -> TikTokUserInfo.UserStatus.LivePaused;
|
|
||||||
case 4 -> TikTokUserInfo.UserStatus.Offline;
|
|
||||||
default -> TikTokUserInfo.UserStatus.NotFound;
|
|
||||||
};
|
|
||||||
|
|
||||||
return new TikTokUserInfo(statusEnum, roomId, startTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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,154 +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,167 +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;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
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<>(queries);
|
|
||||||
query = testMap.entrySet().stream().map(x -> {
|
|
||||||
var key = x.getKey();
|
|
||||||
try {
|
|
||||||
return key+"="+URLEncoder.encode(x.getValue().toString(), StandardCharsets.UTF_8);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return key + "=";
|
|
||||||
}
|
|
||||||
}).collect(Collectors.joining("&"));
|
|
||||||
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(response), HttpData.map(request));
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,44 +20,36 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.live;
|
package io.github.jwdeveloper.tiktok.http.mappers;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.requests.GiftsData;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public interface GiftManager {
|
public class GiftsDataMapper {
|
||||||
|
public GiftsData.Response map(String json) {
|
||||||
|
var parsedJson = JsonParser.parseString(json);
|
||||||
|
var jsonObject = parsedJson.getAsJsonObject();
|
||||||
|
var gifts = jsonObject.entrySet()
|
||||||
|
.parallelStream()
|
||||||
|
.map(e -> mapSingleGift(e.getValue()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
/**
|
return new GiftsData.Response(json, gifts);
|
||||||
* In case you can't find your gift in Gift enum. You can register gift
|
}
|
||||||
* manually here to make it detected while TikTokGiftEvent
|
|
||||||
*
|
|
||||||
* @param id gift's id
|
|
||||||
* @param name gift's name
|
|
||||||
* @param diamondCost diamond cost
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Gift registerGift(int id, String name, int diamondCost, Picture picture);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
private Gift mapSingleGift(JsonElement jsonElement) {
|
||||||
*
|
var jsonObject = jsonElement.getAsJsonObject();
|
||||||
* @param giftId
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Gift findById(int giftId);
|
|
||||||
|
|
||||||
/**
|
var id = jsonObject.get("id").getAsInt();
|
||||||
*
|
var name = jsonObject.get("name").getAsString();
|
||||||
* @param giftName
|
var diamondCost = jsonObject.get("diamondCost").getAsInt();
|
||||||
* @return
|
var image = jsonObject.get("image").getAsString();
|
||||||
*/
|
return new Gift(id, name, diamondCost, new Picture(image), jsonObject);
|
||||||
Gift findByName(String giftName);
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @return all gifts
|
|
||||||
*/
|
|
||||||
List<Gift> getGifts();
|
|
||||||
}
|
}
|
||||||
@@ -20,17 +20,20 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* 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.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
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.User;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute;
|
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;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class LiveRoomMetaMapper {
|
public class LiveDataMapper {
|
||||||
/**
|
/**
|
||||||
* 0 - Unknown
|
* 0 - Unknown
|
||||||
* 1 - ?
|
* 1 - ?
|
||||||
@@ -38,38 +41,47 @@ public class LiveRoomMetaMapper {
|
|||||||
* 3 - ?
|
* 3 - ?
|
||||||
* 4 - Offline
|
* 4 - Offline
|
||||||
*/
|
*/
|
||||||
public LiveRoomMeta map(JsonObject input) {
|
public LiveData.Response map(String json) {
|
||||||
var liveRoomMeta = new LiveRoomMeta();
|
var response = new LiveData.Response();
|
||||||
|
|
||||||
if (!input.has("data")) {
|
response.setJson(json);
|
||||||
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")) {
|
if (data.has("status")) {
|
||||||
var status = data.get("status");
|
var status = data.get("status");
|
||||||
var statusId = status.getAsInt();
|
var statusId = status.getAsInt();
|
||||||
var statusValue = switch (statusId) {
|
var statusValue = switch (statusId) {
|
||||||
case 2 -> LiveRoomMeta.LiveRoomStatus.HostOnline;
|
case 2 -> LiveData.LiveStatus.HostOnline;
|
||||||
case 4 -> LiveRoomMeta.LiveRoomStatus.HostOffline;
|
case 4 -> LiveData.LiveStatus.HostOffline;
|
||||||
default -> LiveRoomMeta.LiveRoomStatus.HostNotFound;
|
default -> LiveData.LiveStatus.HostNotFound;
|
||||||
};
|
};
|
||||||
liveRoomMeta.setStatus(statusValue);
|
response.setLiveStatus(statusValue);
|
||||||
|
} else if (data.has("prompts") && jsonObject.has("status_code") &&
|
||||||
|
data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) {
|
||||||
|
response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == TikTokLiveHttpClient.TIKTOK_AGE_RESTRICTED_CODE);
|
||||||
} else {
|
} else {
|
||||||
liveRoomMeta.setStatus(LiveRoomMeta.LiveRoomStatus.HostNotFound);
|
response.setLiveStatus(LiveData.LiveStatus.HostNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.has("age_restricted")) {
|
if (data.has("age_restricted")) {
|
||||||
var element = data.getAsJsonObject("age_restricted");
|
var element = data.getAsJsonObject("age_restricted");
|
||||||
var restricted = element.get("restricted").getAsBoolean();
|
var restricted = element.get("restricted").getAsBoolean();
|
||||||
liveRoomMeta.setAgeRestricted(restricted);
|
response.setAgeRestricted(restricted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.has("title")) {
|
if (data.has("title")) {
|
||||||
var element = data.get("title");
|
var element = data.get("title");
|
||||||
var title = element.getAsString();
|
var title = element.getAsString();
|
||||||
liveRoomMeta.setTitie(title);
|
response.setTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.has("stats")) {
|
if (data.has("stats")) {
|
||||||
@@ -81,44 +93,57 @@ public class LiveRoomMetaMapper {
|
|||||||
var totalUsers = titalUsersElement.getAsInt();
|
var totalUsers = titalUsersElement.getAsInt();
|
||||||
|
|
||||||
|
|
||||||
liveRoomMeta.setLikeCount(likes);
|
response.setLikes(likes);
|
||||||
liveRoomMeta.setTotalViewers(totalUsers);
|
response.setTotalViewers(totalUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.has("user_count"))
|
if (data.has("user_count")) {
|
||||||
{
|
|
||||||
var element = data.get("user_count");
|
var element = data.get("user_count");
|
||||||
var viewers = element.getAsInt();
|
var viewers = element.getAsInt();
|
||||||
liveRoomMeta.setViewers(viewers);
|
response.setViewers(viewers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.has("owner"))
|
if (data.has("owner")) {
|
||||||
{
|
|
||||||
var element = data.getAsJsonObject("owner");
|
var element = data.getAsJsonObject("owner");
|
||||||
var user = getUser(element);
|
var user = getUser(element);
|
||||||
liveRoomMeta.setHost(user);
|
response.setHost(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return liveRoomMeta;
|
if (data.has("link_mic")) {
|
||||||
|
var element = data.getAsJsonObject("link_mic");
|
||||||
|
var multi_live = element.get("multi_live_enum").getAsInt();
|
||||||
|
var rival_id = element.get("rival_anchor_id").getAsInt();
|
||||||
|
var battle_scores = element.get("battle_scores").getAsJsonArray();
|
||||||
|
if (multi_live == 1) {
|
||||||
|
if (!battle_scores.isEmpty())
|
||||||
|
response.setLiveType(LiveData.LiveType.BATTLE);
|
||||||
|
else if (rival_id != 0)
|
||||||
|
response.setLiveType(LiveData.LiveType.CO_HOST);
|
||||||
|
else
|
||||||
|
response.setLiveType(LiveData.LiveType.BOX);
|
||||||
|
} else
|
||||||
|
response.setLiveType(LiveData.LiveType.SOLO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getUser(JsonObject jsonElement)
|
public User getUser(JsonObject jsonElement) {
|
||||||
{
|
|
||||||
var id = jsonElement.get("id").getAsLong();
|
var id = jsonElement.get("id").getAsLong();
|
||||||
var name = jsonElement.get("display_id").getAsString();
|
var name = jsonElement.get("display_id").getAsString();
|
||||||
var profileName = jsonElement.get("nickname").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 followers = followElement.get("follower_count").getAsInt();
|
||||||
var followingCount = followElement.get("following_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 link = pictureElement.getAsJsonArray("url_list").get(1).getAsString();
|
||||||
var picture = new Picture(link);
|
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);
|
user.addAttribute(UserAttribute.LiveHost);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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 jsonObject = JsonParser.parseString(json).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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
package io.github.jwdeveloper.tiktok.listener;
|
package io.github.jwdeveloper.tiktok.listener;
|
||||||
|
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||||
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException;
|
||||||
@@ -36,10 +37,10 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TikTokListenersManager implements ListenersManager {
|
public class TikTokListenersManager implements ListenersManager {
|
||||||
private final io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver eventObserver;
|
private final TikTokLiveEventHandler eventObserver;
|
||||||
private final List<ListenerBindingModel> bindingModels;
|
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.eventObserver = tikTokEventHandler;
|
||||||
this.bindingModels = new ArrayList<>(listeners.size());
|
this.bindingModels = new ArrayList<>(listeners.size());
|
||||||
for (var listener : listeners) {
|
for (var listener : listeners) {
|
||||||
@@ -110,6 +111,7 @@ public class TikTokListenersManager implements ListenersManager {
|
|||||||
EventConsumer eventMethodRef = (liveClient, event) ->
|
EventConsumer eventMethodRef = (liveClient, event) ->
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
method.setAccessible(true);
|
||||||
method.invoke(listener, liveClient, event);
|
method.invoke(listener, liveClient, event);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TikTokEventListenerMethodException(e);
|
throw new TikTokEventListenerMethodException(e);
|
||||||
|
|||||||
@@ -24,32 +24,29 @@ package io.github.jwdeveloper.tiktok.mappers.handlers;
|
|||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
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.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
import io.github.jwdeveloper.tiktok.live.GiftsManager;
|
||||||
import io.github.jwdeveloper.tiktok.mappers.TikTokMapperHelper;
|
import io.github.jwdeveloper.tiktok.mappers.TikTokMapperHelper;
|
||||||
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
import io.github.jwdeveloper.tiktok.mappers.data.MappingResult;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class TikTokGiftEventHandler {
|
public class TikTokGiftEventHandler {
|
||||||
private final GiftManager giftManager;
|
|
||||||
private final Map<Long, WebcastGiftMessage> giftsMessages;
|
private final Map<Long, WebcastGiftMessage> giftsMessages;
|
||||||
private final TikTokRoomInfo tikTokRoomInfo;
|
private final TikTokRoomInfo tikTokRoomInfo;
|
||||||
|
|
||||||
public TikTokGiftEventHandler(GiftManager giftManager, TikTokRoomInfo tikTokRoomInfo) {
|
private final GiftsManager giftsManager;
|
||||||
this.giftManager = giftManager;
|
|
||||||
|
public TikTokGiftEventHandler(GiftsManager giftsManager, TikTokRoomInfo tikTokRoomInfo) {
|
||||||
giftsMessages = new HashMap<>();
|
giftsMessages = new HashMap<>();
|
||||||
this.tikTokRoomInfo = tikTokRoomInfo;
|
this.tikTokRoomInfo = tikTokRoomInfo;
|
||||||
|
this.giftsManager = giftsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -114,24 +111,38 @@ public class TikTokGiftEventHandler {
|
|||||||
|
|
||||||
private Gift getGiftObject(WebcastGiftMessage giftMessage) {
|
private Gift getGiftObject(WebcastGiftMessage giftMessage) {
|
||||||
var giftId = (int) giftMessage.getGiftId();
|
var giftId = (int) giftMessage.getGiftId();
|
||||||
var gift = giftManager.findById(giftId);
|
var gift = giftsManager.getById(giftId);
|
||||||
|
if (gift == Gift.UNDEFINED)
|
||||||
|
gift = giftsManager.getByName(giftMessage.getGift().getName());
|
||||||
if (gift == Gift.UNDEFINED) {
|
if (gift == Gift.UNDEFINED) {
|
||||||
gift = giftManager.findByName(giftMessage.getGift().getName());
|
gift = new Gift(giftId,
|
||||||
}
|
|
||||||
if (gift == Gift.UNDEFINED) {
|
|
||||||
gift = giftManager.registerGift(
|
|
||||||
giftId,
|
|
||||||
giftMessage.getGift().getName(),
|
giftMessage.getGift().getName(),
|
||||||
giftMessage.getGift().getDiamondCount(),
|
giftMessage.getGift().getDiamondCount(),
|
||||||
Picture.map(giftMessage.getGift().getImage()));
|
Picture.map(giftMessage.getGift().getImage()));
|
||||||
|
|
||||||
|
giftsManager.attachGift(gift);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gift.getPicture().getLink().endsWith(".webp")) {
|
if (gift.getPicture().getLink().endsWith(".webp"))
|
||||||
|
{
|
||||||
updatePicture(gift, giftMessage);
|
updatePicture(gift, giftMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return gift;
|
return gift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO-kohlerpop1: I do not think this method is needed for any reason?
|
||||||
|
// TODO response:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some generated gifts in JSON file contains .webp image format,
|
||||||
|
* that's bad since java by the defult is not supporing .webp and when URL is
|
||||||
|
* converted to Java.io.Image then image is null
|
||||||
|
*
|
||||||
|
* However, TikTok in GiftWebcast event always has image in .jpg format,
|
||||||
|
* so I take advantage of it and swap .webp url with .jpg url
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
private void updatePicture(Gift gift, WebcastGiftMessage webcastGiftMessage) {
|
private void updatePicture(Gift gift, WebcastGiftMessage webcastGiftMessage) {
|
||||||
try {
|
try {
|
||||||
@@ -145,4 +156,4 @@ public class TikTokGiftEventHandler {
|
|||||||
throw new TikTokLiveException("Unable to update picture in gift: " + gift.toString());
|
throw new TikTokLiveException("Unable to update picture in gift: " + gift.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,99 +22,120 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.websocket;
|
package io.github.jwdeveloper.tiktok.websocket;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.*;
|
||||||
import io.github.jwdeveloper.tiktok.ClientSettings;
|
import io.github.jwdeveloper.tiktok.data.dto.ProxyData;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.data.settings.*;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.HttpUtils;
|
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
|
||||||
import org.java_websocket.client.WebSocketClient;
|
import org.java_websocket.client.WebSocketClient;
|
||||||
|
|
||||||
import java.net.URI;
|
import javax.net.ssl.*;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class TikTokWebSocketClient implements SocketClient {
|
public class TikTokWebSocketClient implements SocketClient {
|
||||||
private final ClientSettings clientSettings;
|
private final LiveClientSettings clientSettings;
|
||||||
private final TikTokCookieJar tikTokCookieJar;
|
private final TikTokLiveMessageHandler messageHandler;
|
||||||
private final TikTokMessageHandler messageHandler;
|
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||||
private final TikTokEventObserver tikTokEventHandler;
|
|
||||||
private WebSocketClient webSocketClient;
|
private WebSocketClient webSocketClient;
|
||||||
private TikTokWebSocketPingingTask pingingTask;
|
|
||||||
|
private final TikTokWebSocketPingingTask pingingTask;
|
||||||
private boolean isConnected;
|
private boolean isConnected;
|
||||||
|
|
||||||
public TikTokWebSocketClient(
|
public TikTokWebSocketClient(
|
||||||
TikTokCookieJar tikTokCookieJar,
|
LiveClientSettings clientSettings,
|
||||||
ClientSettings clientSettings,
|
TikTokLiveMessageHandler messageHandler,
|
||||||
TikTokMessageHandler messageHandler,
|
TikTokLiveEventHandler tikTokEventHandler) {
|
||||||
TikTokEventObserver tikTokEventHandler) {
|
|
||||||
this.tikTokCookieJar = tikTokCookieJar;
|
|
||||||
this.clientSettings = clientSettings;
|
this.clientSettings = clientSettings;
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
this.tikTokEventHandler = tikTokEventHandler;
|
this.tikTokEventHandler = tikTokEventHandler;
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
|
pingingTask = new TikTokWebSocketPingingTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(WebcastResponse webcastResponse, LiveClient tikTokLiveClient) {
|
@Override
|
||||||
|
public void start(LiveConnectionData.Response connectionData, LiveClient liveClient) {
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (webcastResponse.getPushServer().isEmpty() || webcastResponse.getRouteParamsMapMap().isEmpty())
|
messageHandler.handle(liveClient, connectionData.getWebcastResponse());
|
||||||
{
|
|
||||||
throw new TikTokLiveException("Could not find Room");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var headers = new HashMap<String, String>();
|
||||||
|
headers.put("Cookie", connectionData.getWebsocketCookies());
|
||||||
|
webSocketClient = new TikTokWebSocketListener(connectionData.getWebsocketUrl(),
|
||||||
|
headers,
|
||||||
|
clientSettings.getHttpSettings().getTimeout().toMillisPart(),
|
||||||
|
messageHandler,
|
||||||
|
tikTokEventHandler,
|
||||||
|
liveClient);
|
||||||
|
|
||||||
|
// ProxyClientSettings proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||||
|
// if (proxyClientSettings.isEnabled())
|
||||||
|
// connectProxy(proxyClientSettings);
|
||||||
|
// else
|
||||||
|
connectDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectDefault() {
|
||||||
try {
|
try {
|
||||||
messageHandler.handle(tikTokLiveClient, webcastResponse);
|
|
||||||
var url = getWebSocketUrl(webcastResponse);
|
|
||||||
webSocketClient = startWebSocket(url, tikTokLiveClient);
|
|
||||||
webSocketClient.connect();
|
webSocketClient.connect();
|
||||||
|
|
||||||
pingingTask = new TikTokWebSocketPingingTask();
|
|
||||||
pingingTask.run(webSocketClient);
|
pingingTask.run(webSocketClient);
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
} catch (Exception e)
|
} catch (Exception e) {
|
||||||
{
|
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
throw new TikTokLiveException("Failed to connect to the websocket", e);
|
throw new TikTokLiveException("Failed to connect to the websocket", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private URI getWebSocketUrl(WebcastResponse webcastResponse) {
|
public void connectProxy(ProxyClientSettings proxySettings) {
|
||||||
var tiktokAccessKey = webcastResponse.getRouteParamsMapMap();
|
try {
|
||||||
|
if (proxySettings.getType() == Proxy.Type.SOCKS) {
|
||||||
var parameters = new TreeMap<>(clientSettings.getClientParameters());
|
SSLContext sc = SSLContext.getInstance("SSL");
|
||||||
parameters.putAll(tiktokAccessKey);
|
sc.init(null, new TrustManager[]{new X509TrustManager() {
|
||||||
|
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {}
|
||||||
var url = webcastResponse.getPushServer();
|
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
|
||||||
var parsed = HttpUtils.parseParametersEncode(url, parameters);
|
public X509Certificate[] getAcceptedIssuers() { return null; }
|
||||||
return URI.create(parsed);
|
}}, null);
|
||||||
|
webSocketClient.setSocketFactory(sc.getSocketFactory());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// This will never be thrown.
|
||||||
|
throw new TikTokProxyRequestException("Unable to set Socks proxy SSL instance");
|
||||||
|
}
|
||||||
|
while (proxySettings.hasNext()) {
|
||||||
|
ProxyData proxyData = proxySettings.next();
|
||||||
|
if (!tryProxyConnection(proxySettings, proxyData)) {
|
||||||
|
if (proxySettings.isAutoDiscard())
|
||||||
|
proxySettings.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pingingTask.run(webSocketClient);
|
||||||
|
isConnected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!isConnected)
|
||||||
|
throw new TikTokLiveException("Failed to connect to the websocket");
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebSocketClient startWebSocket(URI url, LiveClient liveClient) {
|
public boolean tryProxyConnection(ProxyClientSettings proxySettings, ProxyData proxyData) {
|
||||||
var cookie = tikTokCookieJar.parseCookies();
|
try {
|
||||||
var headers = new HashMap<String, String>();
|
webSocketClient.setProxy(new Proxy(proxySettings.getType(), proxyData.toSocketAddress()));
|
||||||
headers.put("Cookie", cookie);
|
webSocketClient.connect();
|
||||||
return new TikTokWebSocketListener(url,
|
return true;
|
||||||
headers,
|
} catch (Exception e) {
|
||||||
3000,
|
return false;
|
||||||
messageHandler,
|
}
|
||||||
tikTokEventHandler,
|
|
||||||
liveClient);
|
|
||||||
}
|
}
|
||||||
public void stop()
|
|
||||||
{
|
public void stop() {
|
||||||
if (isConnected && webSocketClient != null) {
|
if (isConnected && webSocketClient != null && webSocketClient.isOpen()) {
|
||||||
webSocketClient.closeConnection(0, "");
|
webSocketClient.closeConnection(0, "");
|
||||||
pingingTask.stop();
|
pingingTask.stop();
|
||||||
}
|
}
|
||||||
webSocketClient = null;
|
webSocketClient = null;
|
||||||
pingingTask = null;
|
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,103 +23,93 @@
|
|||||||
package io.github.jwdeveloper.tiktok.websocket;
|
package io.github.jwdeveloper.tiktok.websocket;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
|
import io.github.jwdeveloper.tiktok.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
|
import io.github.jwdeveloper.tiktok.data.events.*;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
|
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.live.LiveClient;
|
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastPushFrame;
|
import io.github.jwdeveloper.tiktok.messages.webcast.*;
|
||||||
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.client.WebSocketClient;
|
||||||
import org.java_websocket.drafts.Draft_6455;
|
import org.java_websocket.drafts.Draft_6455;
|
||||||
import org.java_websocket.handshake.ServerHandshake;
|
import org.java_websocket.handshake.ServerHandshake;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Map;
|
import java.util.*;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class TikTokWebSocketListener extends WebSocketClient {
|
public class TikTokWebSocketListener extends WebSocketClient {
|
||||||
|
|
||||||
private final TikTokMessageHandler messageHandler;
|
private final TikTokLiveMessageHandler messageHandler;
|
||||||
private final TikTokEventObserver tikTokEventHandler;
|
private final TikTokLiveEventHandler tikTokEventHandler;
|
||||||
private final LiveClient tikTokLiveClient;
|
private final LiveClient tikTokLiveClient;
|
||||||
|
|
||||||
public TikTokWebSocketListener(URI serverUri,
|
public TikTokWebSocketListener(URI serverUri,
|
||||||
Map<String, String> httpHeaders,
|
Map<String, String> httpHeaders,
|
||||||
int connectTimeout,
|
int connectTimeout,
|
||||||
TikTokMessageHandler messageHandler,
|
TikTokLiveMessageHandler messageHandler,
|
||||||
TikTokEventObserver tikTokEventHandler,
|
TikTokLiveEventHandler tikTokEventHandler,
|
||||||
LiveClient tikTokLiveClient) {
|
LiveClient tikTokLiveClient) {
|
||||||
super(serverUri, new Draft_6455(), httpHeaders,connectTimeout);
|
super(serverUri, new Draft_6455(), httpHeaders, connectTimeout);
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
this.tikTokEventHandler = tikTokEventHandler;
|
this.tikTokEventHandler = tikTokEventHandler;
|
||||||
this.tikTokLiveClient = tikTokLiveClient;
|
this.tikTokLiveClient = tikTokLiveClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(ByteBuffer bytes)
|
public void onMessage(ByteBuffer bytes) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
handleBinary(bytes.array());
|
handleBinary(bytes.array());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e));
|
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e));
|
||||||
}
|
}
|
||||||
if(isNotClosing())
|
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())
|
|
||||||
{
|
|
||||||
sendPing();
|
sendPing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBinary(byte[] buffer) {
|
private void handleBinary(byte[] buffer) {
|
||||||
var websocketMessageOptional = getWebcastWebsocketMessage(buffer);
|
var websocketPushFrameOptional = getWebcastPushFrame(buffer);
|
||||||
if (websocketMessageOptional.isEmpty()) {
|
if (websocketPushFrameOptional.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var websocketMessage = websocketMessageOptional.get();
|
var websocketPushFrame = websocketPushFrameOptional.get();
|
||||||
var webResponse = getWebResponseMessage(websocketMessage.getPayload());
|
var webcastResponse = getWebResponseMessage(websocketPushFrame.getPayload());
|
||||||
|
|
||||||
if(webResponse.getNeedsAck())
|
if (webcastResponse.getNeedsAck()) {
|
||||||
{
|
var pushFrameBuilder = WebcastPushFrame.newBuilder();
|
||||||
//For some reason while send ack id, server get disconnected
|
pushFrameBuilder.setPayloadType("ack");
|
||||||
// sendAckId(webResponse.getFetchInterval());
|
pushFrameBuilder.setLogId(websocketPushFrame.getLogId());
|
||||||
|
pushFrameBuilder.setPayload(webcastResponse.getInternalExtBytes());
|
||||||
|
if (isNotClosing())
|
||||||
|
{
|
||||||
|
this.send(pushFrameBuilder.build().toByteArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
messageHandler.handle(tikTokLiveClient, webcastResponse);
|
||||||
messageHandler.handle(tikTokLiveClient, webResponse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 code, String reason, boolean remote) {
|
||||||
|
tikTokEventHandler.publish(tikTokLiveClient, new TikTokDisconnectedEvent(reason));
|
||||||
|
tikTokLiveClient.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(error));
|
||||||
|
if (isNotClosing()) {
|
||||||
|
sendPing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<WebcastPushFrame> getWebcastPushFrame(byte[] buffer) {
|
||||||
try {
|
try {
|
||||||
var websocketMessage = WebcastPushFrame.parseFrom(buffer);
|
var websocketMessage = WebcastPushFrame.parseFrom(buffer);
|
||||||
if (websocketMessage.getPayload().isEmpty()) {
|
if (websocketMessage.getPayload().isEmpty()) {
|
||||||
@@ -139,28 +129,12 @@ public class TikTokWebSocketListener extends WebSocketClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNotClosing()
|
private boolean isNotClosing() {
|
||||||
{
|
|
||||||
return !isClosed() && !isClosing();
|
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
|
@Override
|
||||||
public void onMessage(String s) {
|
public void onMessage(String s) {
|
||||||
|
// System.err.println(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,55 +1,28 @@
|
|||||||
/*
|
|
||||||
* 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;
|
package io.github.jwdeveloper.tiktok.websocket;
|
||||||
|
|
||||||
import org.java_websocket.WebSocket;
|
import org.java_websocket.WebSocket;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class TikTokWebSocketPingingTask
|
public class TikTokWebSocketPingingTask
|
||||||
{
|
{
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private boolean isRunning = false;
|
private boolean isRunning = false;
|
||||||
private final int MIN_TIMEOUT = 5;
|
private final int MIN_TIMEOUT = 250;
|
||||||
private final int MAX_TIMEOUT = 100;
|
private final int MAX_TIMEOUT = 500;
|
||||||
|
|
||||||
|
|
||||||
public void run(WebSocket webSocket)
|
public void run(WebSocket webSocket)
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
thread = new Thread(() ->
|
thread = new Thread(() -> pingTask(webSocket));
|
||||||
{
|
isRunning = true;
|
||||||
pingTask(webSocket);
|
|
||||||
});
|
|
||||||
isRunning =true;
|
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop()
|
public void stop()
|
||||||
{
|
{
|
||||||
if(thread != null)
|
if (thread != null)
|
||||||
{
|
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
}
|
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,12 +30,9 @@ public class TikTokWebSocketPingingTask
|
|||||||
private void pingTask(WebSocket webSocket)
|
private void pingTask(WebSocket webSocket)
|
||||||
{
|
{
|
||||||
var random = new Random();
|
var random = new Random();
|
||||||
while (isRunning)
|
while (isRunning) {
|
||||||
{
|
try {
|
||||||
try
|
if (!webSocket.isOpen()) {
|
||||||
{
|
|
||||||
if(!webSocket.isOpen())
|
|
||||||
{
|
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -71,11 +41,10 @@ public class TikTokWebSocketPingingTask
|
|||||||
var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT;
|
var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT;
|
||||||
Thread.sleep(timeout);
|
Thread.sleep(timeout);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e) {
|
||||||
{
|
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.gifts;
|
package io.github.jwdeveloper.tiktok.gifts;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftOld;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -35,38 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||||||
|
|
||||||
public class TikTokGiftManagerTest {
|
public class TikTokGiftManagerTest {
|
||||||
|
|
||||||
@InjectMocks
|
|
||||||
TikTokGiftManager giftManager;
|
|
||||||
|
|
||||||
private static final Picture rosePicture = new Picture("https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png");
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void registerGift() {
|
|
||||||
var fakeGift = giftManager.registerGift(123, "Fake gift", 123123, rosePicture);
|
|
||||||
var gifts = giftManager.getGifts();
|
|
||||||
var optional = gifts.stream().filter(r -> r == fakeGift).findFirst();
|
|
||||||
Assertions.assertTrue(optional.isPresent());
|
|
||||||
// Assertions.assertNotNull(optional.get().name());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void findById() {
|
|
||||||
var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture);
|
|
||||||
var result = giftManager.findById(target.getId());
|
|
||||||
Assertions.assertEquals(target, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void findByName() {
|
|
||||||
var target = giftManager.registerGift(123, "FAKE", 123123, rosePicture);
|
|
||||||
var result = giftManager.findByName(target.getName());
|
|
||||||
Assertions.assertEquals(target, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getGifts() {
|
|
||||||
Assertions.assertEquals(Gift.values().length, giftManager.getGifts().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,9 @@ import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
|||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftSendType;
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||||
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokGiftEventHandler;
|
import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokGiftEventHandler;
|
||||||
import io.github.jwdeveloper.tiktok.messages.data.GiftStruct;
|
import io.github.jwdeveloper.tiktok.messages.data.GiftStruct;
|
||||||
import io.github.jwdeveloper.tiktok.messages.data.Image;
|
import io.github.jwdeveloper.tiktok.messages.data.Image;
|
||||||
@@ -38,6 +39,7 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|
||||||
@@ -46,13 +48,12 @@ class TikTokGiftEventHandlerTest {
|
|||||||
|
|
||||||
public static TikTokGiftEventHandler handler;
|
public static TikTokGiftEventHandler handler;
|
||||||
|
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public void before() {
|
public void before() {
|
||||||
var manager = new TikTokGiftManager(Logger.getLogger("x"));
|
var manager = new TikTokGiftsManager(List.of());
|
||||||
var info = new TikTokRoomInfo();
|
var info = new TikTokRoomInfo();
|
||||||
info.setHost(new io.github.jwdeveloper.tiktok.data.models.users.User(123L, "test", new Picture("")));
|
info.setHost(new io.github.jwdeveloper.tiktok.data.models.users.User(123L, "test", new Picture("")));
|
||||||
manager.registerGift(123, "example", 123, new Picture("image.webp"));
|
manager.attachGift(new Gift(123, "example", 123, "image.webp"));
|
||||||
handler = new TikTokGiftEventHandler(manager, info);
|
handler = new TikTokGiftEventHandler(manager, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package io.github.jwdeveloper.tiktok.http;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class HttpUtilsTest
|
|
||||||
{
|
|
||||||
@Test
|
|
||||||
public void parseParameters_EmptyParameters_ShouldHaveNoParameters()
|
|
||||||
{
|
|
||||||
String parsed = HttpUtils.parseParameters("https://webcast.tiktok.com/webcast/im/fetch/", new HashMap<>());
|
|
||||||
|
|
||||||
Assertions.assertEquals("https://webcast.tiktok.com/webcast/im/fetch/", parsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseParameters_ValidParameters_ShouldConstructValidURL()
|
|
||||||
{
|
|
||||||
LinkedHashMap<String, Object> testMap = new LinkedHashMap<>();
|
|
||||||
testMap.put("room_id", 1);
|
|
||||||
testMap.put("uniqueId", "randomName");
|
|
||||||
String parsed = HttpUtils.parseParameters("https://webcast.tiktok.com/webcast/im/fetch/", testMap);
|
|
||||||
|
|
||||||
Assertions.assertEquals("https://webcast.tiktok.com/webcast/im/fetch/?room_id=1&uniqueId=randomName", parsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseParametersEncode_EmptyParameters_ShouldHaveNoParameters()
|
|
||||||
{
|
|
||||||
String parsed = HttpUtils.parseParametersEncode("https://webcast.tiktok.com/webcast/im/fetch/", new HashMap<>());
|
|
||||||
|
|
||||||
Assertions.assertEquals("https://webcast.tiktok.com/webcast/im/fetch/", parsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseParametersEncode_ValidParameters_ShouldConstructValidURL()
|
|
||||||
{
|
|
||||||
LinkedHashMap<String, Object> testMap = new LinkedHashMap<>();
|
|
||||||
testMap.put("room_id", 1);
|
|
||||||
testMap.put("root_referer", "https://www.tiktok.com/");
|
|
||||||
String parsed = HttpUtils.parseParametersEncode("https://webcast.tiktok.com/webcast/im/fetch/", testMap);
|
|
||||||
|
|
||||||
Assertions.assertEquals("https://webcast.tiktok.com/webcast/im/fetch/?room_id=1&root_referer=https%3A%2F%2Fwww.tiktok.com%2F", parsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +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.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 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;
|
package io.github.jwdeveloper.tiktok.listener;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||||
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||||
@@ -41,12 +42,12 @@ import static org.mockito.Mockito.verify;
|
|||||||
|
|
||||||
class TikTokListenersManagerTest {
|
class TikTokListenersManagerTest {
|
||||||
|
|
||||||
private io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver eventObserver;
|
private TikTokLiveEventHandler eventObserver;
|
||||||
private TikTokListenersManager tikTokListenersManager;
|
private TikTokListenersManager tikTokListenersManager;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
eventObserver = Mockito.mock(io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver.class);
|
eventObserver = Mockito.mock(TikTokLiveEventHandler.class);
|
||||||
List<TikTokEventListener> listeners = new ArrayList<>();
|
List<TikTokEventListener> listeners = new ArrayList<>();
|
||||||
tikTokListenersManager = new TikTokListenersManager(listeners, eventObserver);
|
tikTokListenersManager = new TikTokListenersManager(listeners, eventObserver);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>TikTokLiveJava</artifactId>
|
<artifactId>TikTokLiveJava</artifactId>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
<version>1.0.13-Release</version>
|
<version>1.3.0-Release</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -60,6 +60,24 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
|
<artifactId>extension-collector</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
|
<artifactId>extension-recorder</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
|
<artifactId>extension-collector</artifactId>
|
||||||
|
<version>1.3.0-Release</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -22,21 +22,34 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.events_generator.EventGeneratorSettings;
|
import java.time.Duration;
|
||||||
import io.github.jwdeveloper.tiktok.intefacee.EventsInterfaceGenerator;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
public class ChatMessageExample {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
public class EventsInterfaceGeneratorRun
|
|
||||||
{
|
var roomData = TikTokLive.requests()
|
||||||
public static void main(String[] args) throws IOException {
|
.fetchLiveData("X");
|
||||||
var settings = new EventGeneratorSettings();
|
|
||||||
settings.setTikTokEvent(true);
|
var gifts = TikTokLive.requests().fetchGiftsData();
|
||||||
settings.setPrefix("TikTok");
|
|
||||||
settings.setEndFix("Event");
|
|
||||||
settings.setInputDictionary("C:\\Users\\ja\\RiderProjects\\TikTokLiveSharp\\TikTokLiveSharp\\Events\\Messages");
|
var user = TikTokLive.requests()
|
||||||
settings.setOutputDictionary("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\events\\messages");
|
.fetchLiveUserData("mark");
|
||||||
var generator = new EventsInterfaceGenerator();
|
|
||||||
generator.compile(settings);
|
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,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.extension.collector.TikTokLiveCollector;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CollectorExample {
|
||||||
|
|
||||||
|
|
||||||
|
private static String mongoUser;
|
||||||
|
|
||||||
|
private static String mongoPassword;
|
||||||
|
|
||||||
|
private static String mongoDatabase;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
|
||||||
|
var collector = TikTokLiveCollector.use(settings ->
|
||||||
|
{
|
||||||
|
settings.setConnectionUrl("mongodb+srv://" + mongoUser + ":" + mongoPassword + "@" + mongoDatabase + "/?retryWrites=true&w=majority");
|
||||||
|
settings.setDatabaseName("tiktok");
|
||||||
|
});
|
||||||
|
collector.connectDatabase();
|
||||||
|
|
||||||
|
var users = List.of("tehila_723", "dino123597", "domaxyzx", "dash4214", "obserwacje_live");
|
||||||
|
Map<String, Object> additionalDataFields = Map.of("sessionTag", "ExampleTag");
|
||||||
|
for (var user : users) {
|
||||||
|
TikTokLive.newClient(user)
|
||||||
|
.configure(liveClientSettings ->
|
||||||
|
{
|
||||||
|
liveClientSettings.setPrintToConsole(true);
|
||||||
|
})
|
||||||
|
.onError((liveClient, event) ->
|
||||||
|
{
|
||||||
|
event.getException().printStackTrace();
|
||||||
|
})
|
||||||
|
.addListener(collector.newListener(additionalDataFields, document ->
|
||||||
|
{
|
||||||
|
//filtering document data before it is inserted to database
|
||||||
|
if (document.get("dataType") == "message") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}))
|
||||||
|
.buildAndConnectAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.in.read();
|
||||||
|
collector.disconnectDatabase();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,10 +23,7 @@
|
|||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
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.*;
|
||||||
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;
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
public class CustomEventExample {
|
public class CustomEventExample {
|
||||||
@@ -40,8 +37,13 @@ public class CustomEventExample {
|
|||||||
Gift gift;
|
Gift gift;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||||
|
.configure(clientSettings ->
|
||||||
|
{
|
||||||
|
clientSettings.setPrintToConsole(true);
|
||||||
|
})
|
||||||
.onGift((liveClient, event) ->
|
.onGift((liveClient, event) ->
|
||||||
{
|
{
|
||||||
if (event.getGift().getDiamondCost() > 100)
|
if (event.getGift().getDiamondCost() > 100)
|
||||||
@@ -49,14 +51,14 @@ public class CustomEventExample {
|
|||||||
else
|
else
|
||||||
liveClient.publishEvent(new CheapGiftEvent(event.getGift()));
|
liveClient.publishEvent(new CheapGiftEvent(event.getGift()));
|
||||||
})
|
})
|
||||||
.onEvent(CheapGiftEvent.class,(liveClient, event) ->
|
.onEvent(CheapGiftEvent.class, (liveClient, event) ->
|
||||||
{
|
{
|
||||||
System.out.println("Thanks for cheap gift");
|
System.out.println("Thanks for cheap gift");
|
||||||
})
|
})
|
||||||
.onEvent(ExpensiveGiftEvent.class,(liveClient, event) ->
|
.onEvent(ExpensiveGiftEvent.class, (liveClient, event) ->
|
||||||
{
|
{
|
||||||
System.out.println("Thanks for expensive gift!");
|
System.out.println("Thanks for expensive gift!");
|
||||||
})
|
})
|
||||||
.build();
|
.buildAndConnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package io.github.jwdeveloper.tiktok;
|
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.GiftManager;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
|
||||||
|
|
||||||
public class CustomGiftExample {
|
|
||||||
/**
|
|
||||||
* If you can't find your wanted Gift inside Gift enum register it manually
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
LiveClient client = TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
|
||||||
.onConnected((liveClient, event) ->
|
|
||||||
{
|
|
||||||
liveClient.disconnect();
|
|
||||||
})
|
|
||||||
.onWebsocketResponse((liveClient, event) ->
|
|
||||||
{
|
|
||||||
var packets =event.getResponse().getMessagesList();
|
|
||||||
for(var packet : packets)
|
|
||||||
{
|
|
||||||
var name = packet.getMethod();
|
|
||||||
var data = packet.getPayload();
|
|
||||||
if(name.equals("WebcastGiftMessage"))
|
|
||||||
{
|
|
||||||
// var message = WebcastGiftMessage.parseFrom(data);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onGift((liveClient, event) ->
|
|
||||||
{
|
|
||||||
liveClient.getLogger().info(event.getGift().getName());
|
|
||||||
}).build();
|
|
||||||
|
|
||||||
GiftManager giftManager = client.getGiftManager();
|
|
||||||
|
|
||||||
//If you can't find your wanted Gift inside Gift enum register it manually
|
|
||||||
giftManager.registerGift(123, "my custom gift", 69, new Picture("https://as2.ftcdn.net/v2/jpg/03/03/62/45/1000_F_303624505_u0bFT1Rnoj8CMUSs8wMCwoKlnWlh5Jiq.jpg"));
|
|
||||||
|
|
||||||
|
|
||||||
//You can also override existing gift, for example Rose has Id 5655
|
|
||||||
//We can make our custom gift appear in the event instead of rose
|
|
||||||
giftManager.registerGift(5655, "custom-rose", 999, new Picture("https://as2.ftcdn.net/v2/jpg/03/03/62/45/1000_F_303624505_u0bFT1Rnoj8CMUSs8wMCwoKlnWlh5Jiq.jpg"));
|
|
||||||
|
|
||||||
client.connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
|
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
|
||||||
|
|
||||||
|
public class GiftsExample {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
var giftsManager = TikTokLive.gifts();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var giftsList = giftsManager.toList();
|
||||||
|
for (var gift : giftsList) {
|
||||||
|
System.out.println("Gift: " + gift);
|
||||||
|
}
|
||||||
|
|
||||||
|
var giftsMap = giftsManager.toMap();
|
||||||
|
for (var entry : giftsMap.entrySet()) {
|
||||||
|
System.out.println("GiftId: " + entry.getKey() + " Gift: " + entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("total number of gifts: " + giftsManager.toList().size());
|
||||||
|
|
||||||
|
var giftRose = giftsManager.getById(5655);
|
||||||
|
var giftRoseByName = giftsManager.getByName("Rose");
|
||||||
|
var giftByFilter = giftsManager.getByFilter(e -> e.getDiamondCost() > 50);
|
||||||
|
|
||||||
|
var giftsByFilter = giftsManager.getManyByFilter(e -> e.getDiamondCost() > 100);
|
||||||
|
System.out.println("total number of gifts with cost higher then 100: " + giftsByFilter.size());
|
||||||
|
/**
|
||||||
|
* In case searched gift not exists getByName returns you Gift.UNDEFINED
|
||||||
|
*/
|
||||||
|
var undefiedGift = giftsManager.getByName("GIFT WITH WRONG NAME");
|
||||||
|
|
||||||
|
|
||||||
|
var customGift = new Gift(123213213, "Custom gift", 50, "https://images.pexels.com/photos/2071882/pexels-photo-2071882.jpeg?cs=srgb&dl=pexels-wojciech-kumpicki-2071882.jpg&fm=jpg");
|
||||||
|
giftsManager.attachGift(customGift);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ public class ListenerExample
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Method in TikTokEventListener should meet 4 requirements to be detected
|
* Method in TikTokEventListener should meet 4 requirements to be detected
|
||||||
* - must have @TikTokEventHandler annotation
|
* - must have @TikTokEventObserver annotation
|
||||||
* - must have 2 parameters
|
* - must have 2 parameters
|
||||||
* - first parameter must be LiveClient
|
* - first parameter must be LiveClient
|
||||||
* - second must be class that extending TikTokEvent
|
* - second must be class that extending TikTokEvent
|
||||||
@@ -84,12 +84,12 @@ public class ListenerExample
|
|||||||
|
|
||||||
@TikTokEventObserver
|
@TikTokEventObserver
|
||||||
public void onGift(LiveClient liveClient, TikTokGiftEvent event) {
|
public void onGift(LiveClient liveClient, TikTokGiftEvent event) {
|
||||||
var message = switch (event.getGift()) {
|
var message = switch (event.getGift().getName()) {
|
||||||
case ROSE -> "Thanks :)";
|
case "ROSE" -> "Thanks :)";
|
||||||
case APPETIZERS -> ":OO";
|
case "APPETIZERS" -> ":OO";
|
||||||
case APRIL -> ":D";
|
case "APRIL" -> ":D";
|
||||||
case TIKTOK -> ":P";
|
case "TIKTOK" -> ":P";
|
||||||
case CAP -> ":F";
|
case "CAP" -> ":F";
|
||||||
default -> ":I";
|
default -> ":I";
|
||||||
};
|
};
|
||||||
liveClient.getLogger().info(message);
|
liveClient.getLogger().info(message);
|
||||||
@@ -115,4 +115,4 @@ public class ListenerExample
|
|||||||
""");
|
""");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.net.Proxy;
|
||||||
|
|
||||||
|
public class ProxyExample {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||||
|
.configure(clientSettings -> {
|
||||||
|
clientSettings.setPrintToConsole(true);
|
||||||
|
clientSettings.getHttpSettings().configureProxy(proxySettings -> {
|
||||||
|
proxySettings.setOnProxyUpdated(proxyData -> System.err.println("Next proxy: " + proxyData.toString()));
|
||||||
|
proxySettings.setType(Proxy.Type.SOCKS);
|
||||||
|
proxySettings.addProxy("localhost", 8080);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.onConnected((liveClient, event) ->
|
||||||
|
liveClient.getLogger().info("Connected "+liveClient.getRoomInfo().getHostName()))
|
||||||
|
.onComment((liveClient, event) -> liveClient.getLogger().info(event.getUser().getName()+": "+event.getText()))
|
||||||
|
.onLike((liveClient, event) -> liveClient.getLogger().info(event.getUser().getName()+" sent "+event.getLikes()+"x likes!"))
|
||||||
|
.onDisconnected((liveClient, event) ->
|
||||||
|
liveClient.getLogger().info("Disconnect reason: "+event.getReason()))
|
||||||
|
.onLiveEnded((liveClient, event) ->
|
||||||
|
liveClient.getLogger().info("Live Ended: "+liveClient.getRoomInfo().getHostName()))
|
||||||
|
.onError((liveClient, event) ->
|
||||||
|
event.getException().printStackTrace())
|
||||||
|
.buildAndConnect();
|
||||||
|
|
||||||
|
System.in.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.extension.recorder.TikTokLiveRecorder;
|
||||||
|
import io.github.jwdeveloper.tiktok.extension.recorder.impl.event.TikTokLiveRecorderStartedEvent;
|
||||||
|
|
||||||
|
public class RecorderExample {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
TikTokLive.newClient("bangbetmenygy")
|
||||||
|
.configure(liveClientSettings ->
|
||||||
|
{
|
||||||
|
liveClientSettings.setPrintToConsole(true);
|
||||||
|
})
|
||||||
|
.onError((liveClient, event) ->
|
||||||
|
{
|
||||||
|
event.getException().printStackTrace();
|
||||||
|
})
|
||||||
|
.addListener(TikTokLiveRecorder.use(recorderSettings ->
|
||||||
|
{
|
||||||
|
recorderSettings.setFfmpegPath("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\extension-recorder\\libs\\ffmpeg.exe");
|
||||||
|
recorderSettings.setOutputPath("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\extension-recorder\\out");
|
||||||
|
recorderSettings.setOutputFileName("test.flv");
|
||||||
|
}))
|
||||||
|
.onEvent(TikTokLiveRecorderStartedEvent.class, (liveClient, event) ->
|
||||||
|
{
|
||||||
|
System.out.println(event.getDownloadData().getFullUrl());
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,77 +23,36 @@
|
|||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.TikTokSubNotifyEvent;
|
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.data.events.gift.TikTokGiftEvent;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
|
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||||
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class SimpleExample {
|
public class SimpleExample {
|
||||||
public static String TIKTOK_HOSTNAME = "bangbetmenygy";
|
public static String TIKTOK_HOSTNAME = "kvadromama_marina1";
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
|
|
||||||
showLogo();
|
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();
|
|
||||||
|
|
||||||
|
var gifts = TikTokLive.gifts();
|
||||||
|
|
||||||
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
|
||||||
.configure(clientSettings ->
|
.configure(clientSettings ->
|
||||||
{
|
{
|
||||||
clientSettings.setHostName(SimpleExample.TIKTOK_HOSTNAME); // This method is useful in case you want change hostname later
|
clientSettings.setHostName(SimpleExample.TIKTOK_HOSTNAME); // This method is useful in case you want change hostname later
|
||||||
clientSettings.setClientLanguage("en"); // Language
|
clientSettings.setClientLanguage("en"); // Language
|
||||||
clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
|
|
||||||
clientSettings.setLogLevel(Level.ALL); // Log level
|
clientSettings.setLogLevel(Level.ALL); // Log level
|
||||||
clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
|
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.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
|
||||||
clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
|
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
|
//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
|
// documentation how to obtain sessionId https://github.com/isaackogan/TikTok-Live-Connector#send-chat-messages
|
||||||
|
|
||||||
@@ -105,15 +64,6 @@ public class SimpleExample {
|
|||||||
|
|
||||||
//clientSettings.setRoomId("XXXXXXXXXXXXXXXXX");
|
//clientSettings.setRoomId("XXXXXXXXXXXXXXXXX");
|
||||||
})
|
})
|
||||||
.onConnected((liveClient, event) ->
|
|
||||||
{
|
|
||||||
for (var gift : liveClient.getGiftManager().getGifts()) {
|
|
||||||
gift.getPicture().downloadImageAsync().thenAccept(image ->
|
|
||||||
{
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onWebsocketMessage((liveClient, event) ->
|
.onWebsocketMessage((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -124,16 +74,21 @@ 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) ->
|
.onGift((liveClient, event) ->
|
||||||
{
|
{
|
||||||
switch (event.getGift()) {
|
switch (event.getGift().getName()) {
|
||||||
case ROSE -> print(ConsoleColors.RED, "Rose!");
|
case "ROSE" -> print(ConsoleColors.RED, "Rose!");
|
||||||
case GG -> print(ConsoleColors.YELLOW, " GOOD GAME!");
|
case "GG" -> print(ConsoleColors.YELLOW, " GOOD GAME!");
|
||||||
case TIKTOK -> print(ConsoleColors.CYAN, "Thanks for TikTok");
|
case "TIKTOK" -> print(ConsoleColors.CYAN, "Thanks for TikTok");
|
||||||
default ->
|
default ->
|
||||||
print(ConsoleColors.GREEN, "[Thanks for gift] ", ConsoleColors.YELLOW, event.getGift().getName(), "x", event.getCombo());
|
print(ConsoleColors.GREEN, "[Thanks for gift] ", ConsoleColors.YELLOW, event.getGift().getName(), "x", event.getCombo());
|
||||||
}
|
}
|
||||||
@@ -142,7 +97,7 @@ public class SimpleExample {
|
|||||||
{
|
{
|
||||||
print(ConsoleColors.RED, "GIFT COMBO", event.getGift().getName(), event.getCombo());
|
print(ConsoleColors.RED, "GIFT COMBO", event.getGift().getName(), event.getCombo());
|
||||||
})
|
})
|
||||||
.onConnected((client, event) ->
|
.onConnected((liveClient, event) ->
|
||||||
{
|
{
|
||||||
print(ConsoleColors.GREEN, "[Connected]");
|
print(ConsoleColors.GREEN, "[Connected]");
|
||||||
})
|
})
|
||||||
@@ -158,19 +113,19 @@ public class SimpleExample {
|
|||||||
{
|
{
|
||||||
print(ConsoleColors.BLUE, "Follow:", ConsoleColors.WHITE_BRIGHT, event.getUser().getName());
|
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());
|
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());
|
print(ConsoleColors.GREEN, event.getUser().getName(), ":", ConsoleColors.WHITE_BRIGHT, event.getText());
|
||||||
})
|
})
|
||||||
.onEvent((client, event) ->
|
.onEvent((liveClient, event) ->
|
||||||
{
|
{
|
||||||
//System.out.println("Event: " +event.getClass().getSimpleName());
|
//System.out.println("Event: " +event.getClass().getSimpleName());
|
||||||
})
|
})
|
||||||
.onError((client, event) ->
|
.onError((liveClient, event) ->
|
||||||
{
|
{
|
||||||
event.getException().printStackTrace();
|
event.getException().printStackTrace();
|
||||||
})
|
})
|
||||||
@@ -197,4 +152,4 @@ public class SimpleExample {
|
|||||||
""");
|
""");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
467
README.md
467
README.md
@@ -29,8 +29,7 @@ A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive
|
|||||||
The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers.
|
The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers.
|
||||||
No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked.
|
No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked.
|
||||||
|
|
||||||
# Contributors
|
|
||||||
[Library documentation for contributors](https://github.com/jwdeveloper/TikTokLiveJava/wiki)
|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://www.youtube.com/watch?v=eerWGgUKc6c" align="right" target="blank"><img src="https://img.youtube.com/vi/eerWGgUKc6c/hqdefault.jpg" alt="IMAGE ALT TEXT" width="38%" align="right"></a>
|
<a href="https://www.youtube.com/watch?v=eerWGgUKc6c" align="right" target="blank"><img src="https://img.youtube.com/vi/eerWGgUKc6c/hqdefault.jpg" alt="IMAGE ALT TEXT" width="38%" align="right"></a>
|
||||||
@@ -40,6 +39,7 @@ Join the support [discord](https://discord.gg/e2XwPNTBBr) and visit the `#java-s
|
|||||||
|
|
||||||
Do you prefer other programming languages?
|
Do you prefer other programming languages?
|
||||||
- **Node** orginal: [TikTok-Live-Connector](https://github.com/isaackogan/TikTok-Live-Connector) by [@zerodytrash](https://github.com/zerodytrash)
|
- **Node** orginal: [TikTok-Live-Connector](https://github.com/isaackogan/TikTok-Live-Connector) by [@zerodytrash](https://github.com/zerodytrash)
|
||||||
|
- **Rust** rewrite: [TikTokLiveRust](https://github.com/jwdeveloper/TikTokLiveRust)
|
||||||
- **Python** rewrite: [TikTokLive](https://github.com/isaackogan/TikTokLive) by [@isaackogan](https://github.com/isaackogan)
|
- **Python** rewrite: [TikTokLive](https://github.com/isaackogan/TikTokLive) by [@isaackogan](https://github.com/isaackogan)
|
||||||
- **Go** rewrite: [GoTikTokLive](https://github.com/Davincible/gotiktoklive) by [@Davincible](https://github.com/Davincible)
|
- **Go** rewrite: [GoTikTokLive](https://github.com/Davincible/gotiktoklive) by [@Davincible](https://github.com/Davincible)
|
||||||
- **C#** rewrite: [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp) by [@frankvHoof93](https://github.com/frankvHoof93)
|
- **C#** rewrite: [TikTokLiveSharp](https://github.com/frankvHoof93/TikTokLiveSharp) by [@frankvHoof93](https://github.com/frankvHoof93)
|
||||||
@@ -49,6 +49,7 @@ Do you prefer other programming languages?
|
|||||||
#### Overview
|
#### Overview
|
||||||
- [Getting started](#getting-started)
|
- [Getting started](#getting-started)
|
||||||
- [Events](#events)
|
- [Events](#events)
|
||||||
|
- [Extensions](#extensions)
|
||||||
- [Listeners](#listeners)
|
- [Listeners](#listeners)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ Maven
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
|
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
|
||||||
<artifactId>Client</artifactId>
|
<artifactId>Client</artifactId>
|
||||||
<version>1.0.12-Release</version>
|
<version>1.3.0-Release</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@@ -86,7 +87,7 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.0.12-Release'
|
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.1.0-Release'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -165,37 +166,79 @@ TikTokLive.newClient("bangbetmenygy")
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Control**:
|
**Control**:
|
||||||
|
|
||||||
- [onConnected](#onconnected-tiktokconnectedevent)
|
|
||||||
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
|
|
||||||
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
|
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
|
||||||
- [onError](#onerror-tiktokerrorevent)
|
- [onError](#onerror-tiktokerrorevent)
|
||||||
|
- [onConnected](#onconnected-tiktokconnectedevent)
|
||||||
|
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
|
||||||
|
|
||||||
**Message**:
|
**Message**:
|
||||||
|
|
||||||
- [onEvent](#onevent-tiktokevent)
|
- [onEvent](#onevent-tiktokevent)
|
||||||
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
|
- [onEvent](#onevent-tiktokevent)
|
||||||
- [onQuestion](#onquestion-tiktokquestionevent)
|
|
||||||
- [onFollow](#onfollow-tiktokfollowevent)
|
|
||||||
- [onLike](#onlike-tiktoklikeevent)
|
|
||||||
- [onLiveEnded](#onliveended-tiktokliveendedevent)
|
|
||||||
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
|
|
||||||
- [onShare](#onshare-tiktokshareevent)
|
|
||||||
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
|
|
||||||
- [onEmote](#onemote-tiktokemoteevent)
|
|
||||||
- [onGift](#ongift-tiktokgiftevent)
|
|
||||||
- [onComment](#oncomment-tiktokcommentevent)
|
- [onComment](#oncomment-tiktokcommentevent)
|
||||||
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
|
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
|
||||||
|
- [onGift](#ongift-tiktokgiftevent)
|
||||||
|
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
|
||||||
|
- [onFollow](#onfollow-tiktokfollowevent)
|
||||||
|
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
|
||||||
|
- [onLiveEnded](#onliveended-tiktokliveendedevent)
|
||||||
|
- [onQuestion](#onquestion-tiktokquestionevent)
|
||||||
|
- [onShare](#onshare-tiktokshareevent)
|
||||||
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
|
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
|
||||||
|
- [onEmote](#onemote-tiktokemoteevent)
|
||||||
- [onJoin](#onjoin-tiktokjoinevent)
|
- [onJoin](#onjoin-tiktokjoinevent)
|
||||||
|
- [onLike](#onlike-tiktoklikeevent)
|
||||||
|
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
|
||||||
|
|
||||||
**Debug**:
|
**Debug**:
|
||||||
|
|
||||||
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
|
|
||||||
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
|
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
|
||||||
|
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
|
||||||
|
- [onHttpResponse](#onhttpresponse-tiktokhttpresponseevent)
|
||||||
- [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent)
|
- [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent)
|
||||||
# Examples
|
# Examples
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onReconnecting((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
General error event. You should handle this.
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onError((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java)
|
## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java)
|
||||||
@@ -237,32 +280,15 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
|
## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Base class for all events
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onReconnecting((liveClient, event) ->
|
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
General error event. You should handle this.
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TikTokLive.newClient("host-name")
|
TikTokLive.newClient("host-name")
|
||||||
.onError((liveClient, event) ->
|
.onEvent((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -292,91 +318,15 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
|
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
|
||||||
|
|
||||||
|
|
||||||
Triggers when a user creates a subscription.
|
Triggered every time a new chat comment arrives.
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TikTokLive.newClient("host-name")
|
TikTokLive.newClient("host-name")
|
||||||
.onSubscribe((liveClient, event) ->
|
.onComment((liveClient, event) ->
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
Triggered every time someone asks a new question via the question feature.
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onQuestion((liveClient, event) ->
|
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
Triggers when a user follows the streamer. Based on social event.
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onFollow((liveClient, event) ->
|
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onLike((liveClient, event) ->
|
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onLiveEnded((liveClient, event) ->
|
|
||||||
{
|
{
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -390,6 +340,8 @@ TikTokLive.newClient("host-name")
|
|||||||
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
|
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggered when LiveRoomInfo got updated such as likes, viewers, ranking ....
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TikTokLive.newClient("host-name")
|
TikTokLive.newClient("host-name")
|
||||||
@@ -402,73 +354,6 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
Triggers when a user shares the stream. Based on social event.
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onShare((liveClient, event) ->
|
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
Triggered every time gift is sent
|
|
||||||
|
|
||||||
@see GiftSendType it has 3 states
|
|
||||||
|
|
||||||
<p>Example when user sends gift with combo</p>
|
|
||||||
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
|
|
||||||
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
|
|
||||||
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
|
|
||||||
<p>Combo: 12 -> comboState = GiftSendType.Finished</p>
|
|
||||||
|
|
||||||
Remember if comboState is Finished both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onGiftCombo((liveClient, event) ->
|
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
|
|
||||||
|
|
||||||
|
|
||||||
Triggered every time a subscriber sends an emote (sticker).
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
TikTokLive.newClient("host-name")
|
|
||||||
.onEmote((liveClient, event) ->
|
|
||||||
{
|
|
||||||
|
|
||||||
})
|
|
||||||
.buildAndConnect();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
|
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
|
||||||
@@ -492,15 +377,15 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
|
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
|
||||||
|
|
||||||
|
|
||||||
Triggered every time a new chat comment arrives.
|
Triggers when a user creates a subscription.
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TikTokLive.newClient("host-name")
|
TikTokLive.newClient("host-name")
|
||||||
.onComment((liveClient, event) ->
|
.onSubscribe((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -511,13 +396,101 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
|
## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggers when a user follows the streamer. Based on social event.
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TikTokLive.newClient("host-name")
|
TikTokLive.newClient("host-name")
|
||||||
.onLivePaused((liveClient, event) ->
|
.onFollow((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggered every time gift is sent
|
||||||
|
|
||||||
|
@see GiftSendType it has 3 states
|
||||||
|
|
||||||
|
<p>Example when user sends gift with combo</p>
|
||||||
|
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
|
||||||
|
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
|
||||||
|
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
|
||||||
|
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
|
||||||
|
<p>
|
||||||
|
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onGiftCombo((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onLiveEnded((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggered every time someone asks a new question via the question feature.
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onQuestion((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggers when a user shares the stream. Based on social event.
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onShare((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -543,6 +516,25 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggered every time a subscriber sends an emote (sticker).
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onEmote((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
|
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
|
||||||
@@ -560,6 +552,59 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onLike((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onLivePaused((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
TikTokLive.newClient("host-name")
|
||||||
|
.onWebsocketResponse((liveClient, event) ->
|
||||||
|
{
|
||||||
|
|
||||||
|
})
|
||||||
|
.buildAndConnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java)
|
## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java)
|
||||||
@@ -581,17 +626,17 @@ TikTokLive.newClient("host-name")
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
|
## onHttpResponse [TikTokHttpResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokHttpResponseEvent.java)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TikTokLive.newClient("host-name")
|
TikTokLive.newClient("host-name")
|
||||||
.onWebsocketResponse((liveClient, event) ->
|
.onHttpResponse((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|
||||||
})
|
})
|
||||||
.buildAndConnect();
|
.buildAndConnect();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -601,24 +646,37 @@ TikTokLive.newClient("host-name")
|
|||||||
## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java)
|
## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java)
|
||||||
|
|
||||||
|
|
||||||
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
|
Triggered every time TikTok sends data. Data incoming as protobuf message.
|
||||||
|
You can deserialize the binary object depending on the use case.
|
||||||
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TikTokLive.newClient("host-name")
|
TikTokLive.newClient("host-name")
|
||||||
.onWebsocketMessage((liveClient, event) ->
|
.onWebsocketMessage((liveClient, event) ->
|
||||||
{
|
{
|
||||||
|
|
||||||
})
|
})
|
||||||
.buildAndConnect();
|
.buildAndConnect();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
## Extensions
|
||||||
|
|
||||||
|
List of extensions (addons) to TiktokLiveJava
|
||||||
|
that will save your time
|
||||||
|
|
||||||
|
- [Video Recorder](https://github.com/jwdeveloper/TikTokLiveJava/tree/master/extension-recorder)
|
||||||
|
- [Live data collector to database](https://github.com/jwdeveloper/TikTokLiveJava/tree/master/extension-collector)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Listeners
|
## Listeners
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@@ -651,24 +709,24 @@ public static void main(String[] args) throws IOException {
|
|||||||
|
|
||||||
public static class CustomListener implements TikTokEventListener {
|
public static class CustomListener implements TikTokEventListener {
|
||||||
|
|
||||||
@TikTokEventHandler
|
@TikTokEventObserver
|
||||||
public void onLike(LiveClient liveClient, TikTokLikeEvent event) {
|
public void onLike(LiveClient liveClient, TikTokLikeEvent event) {
|
||||||
System.out.println(event.toString());
|
System.out.println(event.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@TikTokEventHandler
|
@TikTokEventObserver
|
||||||
public void onError(LiveClient liveClient, TikTokErrorEvent event) {
|
public void onError(LiveClient liveClient, TikTokErrorEvent event) {
|
||||||
// event.getException().printStackTrace();
|
// event.getException().printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TikTokEventHandler
|
@TikTokEventObserver
|
||||||
public void onComment(LiveClient liveClient, TikTokCommentEvent event) {
|
public void onComment(LiveClient liveClient, TikTokCommentEvent event) {
|
||||||
var userName = event.getUser().getName();
|
var userName = event.getUser().getName();
|
||||||
var text = event.getText();
|
var text = event.getText();
|
||||||
liveClient.getLogger().info(userName + ": " + text);
|
liveClient.getLogger().info(userName + ": " + text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TikTokEventHandler
|
@TikTokEventObserver
|
||||||
public void onGift(LiveClient liveClient, TikTokGiftEvent event) {
|
public void onGift(LiveClient liveClient, TikTokGiftEvent event) {
|
||||||
var message = switch (event.getGift()) {
|
var message = switch (event.getGift()) {
|
||||||
case ROSE -> "Thanks :)";
|
case ROSE -> "Thanks :)";
|
||||||
@@ -693,4 +751,7 @@ public static class CustomListener implements TikTokEventListener {
|
|||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
[Library documentation for contributors](https://github.com/jwdeveloper/TikTokLiveJava/wiki)
|
||||||
|
|
||||||
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>
|
<parent>
|
||||||
<artifactId>TikTokLiveJava</artifactId>
|
<artifactId>TikTokLiveJava</artifactId>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
<version>1.0.13-Release</version>
|
<version>1.3.0-Release</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
<artifactId>Tools</artifactId>
|
<artifactId>Tools-ReadmeGenerator</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ package io.github.jwdeveloper.tiktok.tools.collector.client;
|
|||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import io.github.jwdeveloper.tiktok.FilesUtility;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|||||||
@@ -25,15 +25,11 @@ package io.github.jwdeveloper.tiktok.tools.tester.mockClient;
|
|||||||
import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
|
import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
|
||||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||||
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.listener.TikTokListenersManager;
|
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
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.LiveClientMock;
|
||||||
import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.WebsocketClientMock;
|
import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.WebsocketClientMock;
|
||||||
|
|
||||||
@@ -87,23 +83,17 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder {
|
|||||||
public LiveClientMock build() {
|
public LiveClientMock build() {
|
||||||
validate();
|
validate();
|
||||||
|
|
||||||
var cookie = new TikTokCookieJar();
|
|
||||||
var tiktokRoomInfo = new TikTokRoomInfo();
|
var tiktokRoomInfo = new TikTokRoomInfo();
|
||||||
tiktokRoomInfo.setHostName(clientSettings.getHostName());
|
tiktokRoomInfo.setHostName(clientSettings.getHostName());
|
||||||
|
|
||||||
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
|
||||||
var giftManager = new TikTokGiftManager(logger);
|
var mapper = createMapper(new TikTokGiftsManager(List.of()), tiktokRoomInfo);
|
||||||
var requestFactory = new TikTokHttpRequestFactory(cookie, new TikTokEventObserver());
|
var handler = new TikTokLiveMessageHandler(tikTokEventHandler, mapper);
|
||||||
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 webSocketClient = new WebsocketClientMock(logger, responses, handler);
|
var webSocketClient = new WebsocketClientMock(logger, responses, handler);
|
||||||
|
|
||||||
return new LiveClientMock(tiktokRoomInfo,
|
return new LiveClientMock(tiktokRoomInfo,
|
||||||
apiService,
|
new TikTokLiveHttpClient(),
|
||||||
webSocketClient,
|
webSocketClient,
|
||||||
giftManager,
|
|
||||||
tikTokEventHandler,
|
tikTokEventHandler,
|
||||||
clientSettings,
|
clientSettings,
|
||||||
listenerManager,
|
listenerManager,
|
||||||
|
|||||||
@@ -1,58 +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.tools.tester.mockClient.mocks;
|
|
||||||
|
|
||||||
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.util.logging.Logger;
|
|
||||||
|
|
||||||
public class ApiServiceMock extends TikTokApiService {
|
|
||||||
|
|
||||||
|
|
||||||
public ApiServiceMock(TikTokHttpClient apiClient, Logger logger, ClientSettings clientSettings) {
|
|
||||||
super(apiClient, logger, clientSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -22,33 +22,35 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks;
|
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.TikTokLiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
|
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||||
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
|
||||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class LiveClientMock extends TikTokLiveClient {
|
public class LiveClientMock extends TikTokLiveClient {
|
||||||
|
|
||||||
private final WebsocketClientMock websocketClientMock;
|
private final WebsocketClientMock websocketClientMock;
|
||||||
|
|
||||||
public LiveClientMock(TikTokRoomInfo tikTokLiveMeta,
|
public LiveClientMock(
|
||||||
TikTokApiService tikTokApiService,
|
TikTokRoomInfo tikTokLiveMeta,
|
||||||
WebsocketClientMock webSocketClient,
|
TikTokLiveHttpClient httpClient,
|
||||||
TikTokGiftManager tikTokGiftManager,
|
WebsocketClientMock webSocketClient,
|
||||||
TikTokEventObserver tikTokEventHandler,
|
TikTokLiveEventHandler tikTokEventHandler,
|
||||||
ClientSettings clientSettings,
|
LiveClientSettings clientSettings,
|
||||||
TikTokListenersManager listenersManager,
|
TikTokListenersManager listenersManager,
|
||||||
Logger logger) {
|
Logger logger) {
|
||||||
super(tikTokLiveMeta,
|
super(
|
||||||
tikTokApiService,
|
new TikTokGiftsManager(List.of()),
|
||||||
|
tikTokLiveMeta,
|
||||||
|
httpClient,
|
||||||
webSocketClient,
|
webSocketClient,
|
||||||
tikTokGiftManager,
|
|
||||||
tikTokEventHandler,
|
tikTokEventHandler,
|
||||||
clientSettings,
|
clientSettings,
|
||||||
listenersManager,
|
listenersManager,
|
||||||
@@ -58,7 +60,6 @@ public class LiveClientMock extends TikTokLiveClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void publishMessage(String type, String base64) {
|
public void publishMessage(String type, String base64) {
|
||||||
websocketClientMock.addMessage(type, base64);
|
websocketClientMock.addMessage(type, base64);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,8 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks;
|
package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
|
|
||||||
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandler;
|
|
||||||
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
import io.github.jwdeveloper.tiktok.live.LiveClient;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
|
||||||
import io.github.jwdeveloper.tiktok.websocket.SocketClient;
|
import io.github.jwdeveloper.tiktok.websocket.SocketClient;
|
||||||
@@ -40,7 +38,7 @@ public class WebsocketClientMock implements SocketClient {
|
|||||||
Logger logger;
|
Logger logger;
|
||||||
Stack<WebcastResponse> responses;
|
Stack<WebcastResponse> responses;
|
||||||
Stack<MsgStruct> messages;
|
Stack<MsgStruct> messages;
|
||||||
TikTokMessageHandler messageHandler;
|
TikTokLiveMessageHandler messageHandler;
|
||||||
|
|
||||||
LiveClient client;
|
LiveClient client;
|
||||||
|
|
||||||
@@ -58,7 +56,7 @@ public class WebsocketClientMock implements SocketClient {
|
|||||||
byte[] messageValue;
|
byte[] messageValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebsocketClientMock(Logger logger, Stack<WebcastResponse> responses, TikTokMessageHandler messageHandler) {
|
public WebsocketClientMock(Logger logger, Stack<WebcastResponse> responses, TikTokLiveMessageHandler messageHandler) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.responses = responses;
|
this.responses = responses;
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
@@ -91,8 +89,9 @@ public class WebsocketClientMock implements SocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(WebcastResponse webcastResponse, LiveClient tikTokLiveClient) {
|
public void start(LiveConnectionData.Response webcastResponse, LiveClient tikTokLiveClient) {
|
||||||
logger.info("Running message: " + responses.size());
|
logger.info("Running message: " + responses.size());
|
||||||
|
|
||||||
|
|
||||||
@@ -110,7 +109,6 @@ public class WebsocketClientMock implements SocketClient {
|
|||||||
});
|
});
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
package io.github.jwdeveloper.tiktok.tools.util;
|
package io.github.jwdeveloper.tiktok.tools.util;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
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.messages.webcast.WebcastResponse;
|
||||||
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
|
||||||
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
import io.github.jwdeveloper.tiktok.utils.JsonUtil;
|
||||||
@@ -34,8 +35,8 @@ public class MessageUtil {
|
|||||||
var methodName = message.getMethod();
|
var methodName = message.getMethod();
|
||||||
var inputClazz = Class.forName("io.github.jwdeveloper.tiktok.messages.webcast." + methodName);
|
var inputClazz = Class.forName("io.github.jwdeveloper.tiktok.messages.webcast." + methodName);
|
||||||
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", ByteString.class);
|
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", ByteString.class);
|
||||||
var deserialized = parseMethod.invoke(null, message.getPayload());
|
var webcastObject = parseMethod.invoke(null, message.getPayload());
|
||||||
return JsonUtil.messageToJson(deserialized);
|
return JsonUtil.messageToJson(webcastObject);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
|
||||||
return ConsoleColors.RED + "Can not find mapper for " + message.getMethod();
|
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 inputClazz = Class.forName("io.github.jwdeveloper.tiktok.messages.webcast." + messageName);
|
||||||
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", byte[].class);
|
var parseMethod = inputClazz.getDeclaredMethod("parseFrom", byte[].class);
|
||||||
var deserialized = parseMethod.invoke(null, bytes);
|
var deserialized = parseMethod.invoke(null, bytes);
|
||||||
|
|
||||||
return JsonUtil.messageToJson(deserialized);
|
return JsonUtil.messageToJson(deserialized);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>TikTokLiveJava</artifactId>
|
<artifactId>TikTokLiveJava</artifactId>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
<version>1.0.13-Release</version>
|
<version>1.3.0-Release</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>Tools-EventsWebViewer</artifactId>
|
<artifactId>Tools-EventsWebViewer</artifactId>
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ import java.sql.SQLException;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
public class Main {
|
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();
|
var settings = new Settings();
|
||||||
settings.setUserName("szalonamoniaxx");
|
settings.setUserName("szalonamoniaxx");
|
||||||
settings.setSessionTag("battle");
|
settings.setSessionTag("battle");
|
||||||
|
|||||||
@@ -22,21 +22,17 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.webviewer;
|
package io.github.jwdeveloper.tiktok.webviewer;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
|
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkLayerMessage;
|
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMessage;
|
|
||||||
import io.github.jwdeveloper.tiktok.tools.TikTokLiveTools;
|
import io.github.jwdeveloper.tiktok.tools.TikTokLiveTools;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ToolsExamples {
|
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 {
|
public static void main(String[] args) throws IOException {
|
||||||
// runCollector();
|
// runCollector();
|
||||||
@@ -53,6 +49,7 @@ public class ToolsExamples {
|
|||||||
//WebcastLinkMicBattlePunishFinish end of battle?
|
//WebcastLinkMicBattlePunishFinish end of battle?
|
||||||
//WebcastLinkLayerMessage send after end of battle
|
//WebcastLinkLayerMessage send after end of battle
|
||||||
// send after LinkLayer -> WebcastLinkMessage
|
// send after LinkLayer -> WebcastLinkMessage
|
||||||
|
|
||||||
private static void runCollector() {
|
private static void runCollector() {
|
||||||
TikTokLiveTools.createCollector(db)
|
TikTokLiveTools.createCollector(db)
|
||||||
.addUser(tiktokUser)
|
.addUser(tiktokUser)
|
||||||
@@ -60,15 +57,17 @@ public class ToolsExamples {
|
|||||||
.configureLiveClient(liveClientBuilder ->
|
.configureLiveClient(liveClientBuilder ->
|
||||||
{
|
{
|
||||||
liveClientBuilder.configure(clientSettings ->
|
liveClientBuilder.configure(clientSettings ->
|
||||||
{
|
{
|
||||||
clientSettings.setPrintToConsole(true);
|
clientSettings.setPrintToConsole(true);
|
||||||
});
|
})
|
||||||
liveClientBuilder.onWebsocketResponse((liveClient, event) ->
|
.onComment((liveClient, event) ->
|
||||||
{
|
{
|
||||||
for (var msg : event.getResponse().getMessagesList()) {
|
System.out.println("Chat message: " + event.getUser().getName() + " " + event.getText());
|
||||||
System.out.println(msg.getMethod());
|
})
|
||||||
}
|
.onWebsocketUnhandledMessage((liveClient, event) ->
|
||||||
});
|
{
|
||||||
|
liveClient.getLogger().info(event.getMessage().getMethod());
|
||||||
|
});
|
||||||
liveClientBuilder.onConnected((liveClient, event) ->
|
liveClientBuilder.onConnected((liveClient, event) ->
|
||||||
{
|
{
|
||||||
liveClient.getLogger().info("Connected");
|
liveClient.getLogger().info("Connected");
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>TikTokLiveJava</artifactId>
|
<artifactId>TikTokLiveJava</artifactId>
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
<version>1.0.13-Release</version>
|
<version>1.3.0-Release</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@@ -23,12 +23,6 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
|
||||||
<artifactId>Tools</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.jwdeveloper.Descrabble</groupId>
|
<groupId>com.github.jwdeveloper.Descrabble</groupId>
|
||||||
<artifactId>Descrabble-Full</artifactId>
|
<artifactId>Descrabble-Full</artifactId>
|
||||||
@@ -41,5 +35,11 @@
|
|||||||
<version>0.9.12</version>
|
<version>0.9.12</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.jwdeveloper.tiktok</groupId>
|
||||||
|
<artifactId>Client</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
@@ -41,11 +41,12 @@ public class CodeExample {
|
|||||||
TikTokLive.newClient("bangbetmenygy")
|
TikTokLive.newClient("bangbetmenygy")
|
||||||
.onGift((liveClient, event) ->
|
.onGift((liveClient, event) ->
|
||||||
{
|
{
|
||||||
String message = switch (event.getGift()) {
|
String message = switch (event.getGift().getName())
|
||||||
case ROSE -> "ROSE!";
|
{
|
||||||
case GG -> "GOOD GAME";
|
case "Rose" -> "ROSE!";
|
||||||
case TIKTOK -> "Ye";
|
case "Good game" -> "GOOD GAME";
|
||||||
case CORGI -> "Nice gift";
|
case "Ye" -> "Ye";
|
||||||
|
case "Nice gift" -> "Nice gift";
|
||||||
default -> "Thank you for " + event.getGift().getName();
|
default -> "Thank you for " + event.getGift().getName();
|
||||||
};
|
};
|
||||||
System.out.println(event.getUser().getProfileName() + " sends " + message);
|
System.out.println(event.getUser().getProfileName() + " sends " + message);
|
||||||
@@ -84,7 +85,6 @@ public class CodeExample {
|
|||||||
{
|
{
|
||||||
settings.setHostName("bangbetmenygy"); // This method is useful in case you want change hostname later
|
settings.setHostName("bangbetmenygy"); // This method is useful in case you want change hostname later
|
||||||
settings.setClientLanguage("en"); // Language
|
settings.setClientLanguage("en"); // Language
|
||||||
settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
|
|
||||||
settings.setLogLevel(Level.ALL); // Log level
|
settings.setLogLevel(Level.ALL); // Log level
|
||||||
settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
|
settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
|
||||||
settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
|
settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
|
||||||
@@ -98,10 +98,14 @@ public class CodeExample {
|
|||||||
//RoomId can be used as an override if you're having issues with HostId.
|
//RoomId can be used as an override if you're having issues with HostId.
|
||||||
//You can find it in the HTML for the livestream-page
|
//You can find it in the HTML for the livestream-page
|
||||||
settings.setRoomId("XXXXXXXXXXXXXXXXX");
|
settings.setRoomId("XXXXXXXXXXXXXXXXX");
|
||||||
|
|
||||||
|
//Optional:
|
||||||
|
//API Key for increased limit to signing server
|
||||||
|
settings.setApiKey("XXXXXXXXXXXXXXXXX");
|
||||||
})
|
})
|
||||||
.buildAndConnect();
|
.buildAndConnect();
|
||||||
// </code>
|
// </code>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -22,8 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.TemplateUtility;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ import io.github.jwdeveloper.tiktok.annotations.EventType;
|
|||||||
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
|
||||||
import io.github.jwdeveloper.tiktok.live.builder.EventsBuilder;
|
import io.github.jwdeveloper.tiktok.live.builder.EventsBuilder;
|
||||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.TemplateUtility;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
||||||
*
|
*
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok.utils;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -37,7 +38,7 @@ public class FilesUtility
|
|||||||
public static List<Path> getFiles(String input) {
|
public static List<Path> getFiles(String input) {
|
||||||
Path path = Paths.get(input);
|
Path path = Paths.get(input);
|
||||||
try (Stream<Path> paths = Files.list(path)) {
|
try (Stream<Path> paths = Files.list(path)) {
|
||||||
return paths.filter(Files::isRegularFile).toList();
|
return paths.filter(Files::isRegularFile).toList();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -96,13 +97,13 @@ public class FilesUtility
|
|||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
} catch (IOException e)
|
} catch (IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String loadFileContent(String path) {
|
public static String loadFileContent(String path) {
|
||||||
ensureFile(path);
|
ensureFile(path);
|
||||||
Path pathh = Paths.get(path);
|
Path pathh = Paths.get(path);
|
||||||
try {
|
try {
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,6 @@
|
|||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
|
|
||||||
import io.github.jwdeveloper.tiktok.utils.TemplateUtility;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class ReadmeGenerator {
|
public class ReadmeGenerator {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user