Compare commits

..

4 Commits

Author SHA1 Message Date
JW
46d229869e - refactor of the Http client
Changes:

Http-client settings in configure method

```
    TikTokLive.newClient("X")
                .configure(liveClientSettings ->
                {
                   var httpSetting = liveClientSettings.getHttpSettings();
                    httpSetting.setTimeout(Duration.ofSeconds(12));
                });
```

`TikTokLive.requests()` Easy and quick way of making
http request to tiktok
```
    var giftsResponse =TikTokLive.request.fetchGiftsData();
 ```

 Removed:
     TikTokLive.isLiveOnline(String hostName);
     TikTokLive.isHostNameValidAsync(String hostName);

     instead you can use
     ```
     TikTokLive.requests().fetchLiveUserData("Mike").getUserStatus()
     ```
2024-01-05 17:12:37 +01:00
JW
dd417df0ff - refactor of the Http client
Changes:

Http-client settings in configure method

```
    TikTokLive.newClient("X")
                .configure(liveClientSettings ->
                {
                   var httpSetting = liveClientSettings.getHttpSettings();
                    httpSetting.setTimeout(Duration.ofSeconds(12));
                });
```

`TikTokLive.requests()` Easy and quick way of making
http request to tiktok
```
    var giftsResponse =TikTokLive.request.fetchGiftsData();
 ```

 Removed:
     TikTokLive.isLiveOnline(String hostName);
     TikTokLive.isHostNameValidAsync(String hostName);

     instead you can use
     ```
     TikTokLive.requests().fetchLiveUserData("Mike").getUserStatus()
     ```
2024-01-05 17:09:02 +01:00
JW
bc24436269 Merge branch 'master' into develop-1.0.15 2024-01-05 17:07:20 +01:00
JW
2d260dd3f9 - refactor of the Http client
Changes:

Http-client settings in configure method

```
    TikTokLive.newClient("X")
                .configure(liveClientSettings ->
                {
                   var httpSetting = liveClientSettings.getHttpSettings();
                    httpSetting.setTimeout(Duration.ofSeconds(12));
                });
```

`TikTokLive.requests()` Easy and quick way of making
http request to tiktok
```
    var giftsResponse =TikTokLive.request.fetchGiftsData();
 ```

 Removed:
     TikTokLive.isLiveOnline(String hostName);
     TikTokLive.isHostNameValidAsync(String hostName);

     instead you can use
     ```
     TikTokLive.requests().fetchLiveUserData("Mike").getUserStatus()
     ```
2024-01-05 17:04:32 +01:00
152 changed files with 191716 additions and 4854 deletions

3
.gitignore vendored
View File

@@ -1,11 +1,10 @@
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*

View File

@@ -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.3.0-Release</version> <version>1.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>API</artifactId> <artifactId>API</artifactId>

View File

@@ -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.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);
}
}

View File

@@ -47,23 +47,11 @@ public class TikTokCommentEvent extends TikTokHeaderEvent {
public TikTokCommentEvent(WebcastChatMessage msg) { public TikTokCommentEvent(WebcastChatMessage msg) {
super(msg.getCommon()); super(msg.getCommon());
user = User.map(msg.getUser(), msg.getUserIdentity()); user = User.map(msg.getUser(),msg.getUserIdentity());
text = msg.getContent(); text = msg.getContent();
visibleToSender = msg.getVisibleToSender(); visibleToSender = msg.getVisibleToSender();
getUserLanguage = msg.getContentLanguage(); getUserLanguage = msg.getContentLanguage();
mentionedUser = User.map(msg.getAtUser()); mentionedUser = User.map(msg.getAtUser());
pictures = msg.getEmotesListList().stream().map(e -> Picture.map(e.getEmote().getImage())).toList(); pictures = msg.getEmotesListList().stream().map(e -> Picture.map(e.getEmote().getImage())).toList();
} }
public static TikTokCommentEvent of(String userName, String message) {
var builder = WebcastChatMessage.newBuilder();
builder.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setNickname(userName)
.build());
builder.setContentLanguage("en");
builder.setVisibleToSender(true);
builder.setContent(message);
return new TikTokCommentEvent(builder.build());
}
} }

View File

@@ -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,12 +32,4 @@ import lombok.Getter;
*/ */
@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");
}
} }

View File

@@ -36,10 +36,10 @@ import lombok.Getter;
*/ */
@Getter @Getter
@EventMeta(eventType = EventType.Message) @EventMeta(eventType = EventType.Message)
public class TikTokSubscribeEvent extends TikTokHeaderEvent { public class TikTokSubscribeEvent extends TikTokHeaderEvent
{
private final User user; private final User user;
public TikTokSubscribeEvent(WebcastMemberMessage msg) { public TikTokSubscribeEvent(WebcastMemberMessage msg) {
super(msg.getCommon()); super(msg.getCommon());
user = User.map(msg.getUser()); user = User.map(msg.getUser());
@@ -52,11 +52,4 @@ public class TikTokSubscribeEvent extends TikTokHeaderEvent {
user.addAttribute(UserAttribute.Subscriber); user.addAttribute(UserAttribute.Subscriber);
} }
public static TikTokSubscribeEvent of(String userName) {
return new TikTokSubscribeEvent(WebcastMemberMessage.newBuilder()
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setNickname(userName)
.build())
.build());
}
} }

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok.data.events.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;
}
}

View File

@@ -22,9 +22,10 @@
*/ */
package io.github.jwdeveloper.tiktok.data.events.gift; package io.github.jwdeveloper.tiktok.data.events.gift;
import io.github.jwdeveloper.tiktok.annotations.*; import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.data.models.Picture; 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;
@@ -33,7 +34,7 @@ import lombok.Getter;
/** /**
* Triggered every time gift is sent * Triggered every time gift is sent
* *
* @see GiftComboStateType it has 3 states * @see GiftSendType it has 3 states
* *
* <p>Example when user sends gift with combo</p> * <p>Example when user sends gift with combo</p>
* <p>>Combo: 1 -> comboState = GiftSendType.Begin</p> * <p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
@@ -46,21 +47,10 @@ import lombok.Getter;
@EventMeta(eventType = EventType.Message) @EventMeta(eventType = EventType.Message)
@Getter @Getter
public class TikTokGiftComboEvent extends TikTokGiftEvent { public class TikTokGiftComboEvent extends TikTokGiftEvent {
private final GiftComboStateType comboState; private final GiftSendType comboState;
public TikTokGiftComboEvent(Gift gift, User host, WebcastGiftMessage msg, GiftComboStateType comboState) { public TikTokGiftComboEvent(Gift gift, User host, WebcastGiftMessage msg, GiftSendType comboState) {
super(gift, host, msg); super(gift, host, msg);
this.comboState = comboState; this.comboState = comboState;
} }
public static TikTokGiftComboEvent of(Gift gift, int combo, GiftComboStateType comboState) {
return new TikTokGiftComboEvent(
gift,
new User(0L, "Test", new Picture("")),
WebcastGiftMessage
.newBuilder()
.setComboCount(combo)
.build(),
comboState);
}
} }

View File

@@ -23,14 +23,18 @@
package io.github.jwdeveloper.tiktok.data.events.gift; package io.github.jwdeveloper.tiktok.data.events.gift;
import io.github.jwdeveloper.tiktok.annotations.*; import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
import io.github.jwdeveloper.tiktok.data.models.Picture; 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
@@ -56,20 +60,4 @@ public class TikTokGiftEvent extends TikTokHeaderEvent {
} }
combo = msg.getComboCount(); combo = msg.getComboCount();
} }
public TikTokGiftEvent(Gift gift) {
this.gift = gift;
user = new User(0L, "sender", new Picture(""));
toUser = new User(0L, "receiver", new Picture(""));
combo = 1;
}
public static TikTokGiftEvent of(Gift gift) {
return new TikTokGiftEvent(gift);
}
public static TikTokGiftEvent of(String name, int id, int diamonds) {
return TikTokGiftEvent.of(new Gift(id, name, diamonds, ""));
}
} }

View File

@@ -1,38 +0,0 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok.data.events.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;
}

View File

@@ -24,10 +24,8 @@ package io.github.jwdeveloper.tiktok.data.events.social;
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.TikTokSubscribeEvent;
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.users.User; import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastMemberMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage;
import lombok.Value; import lombok.Value;
@@ -47,12 +45,4 @@ public class TikTokFollowEvent extends TikTokHeaderEvent
totalFollowers = msg.getFollowCount(); totalFollowers = msg.getFollowCount();
} }
public static TikTokFollowEvent of(String userName)
{
return new TikTokFollowEvent(WebcastSocialMessage.newBuilder()
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setNickname(userName)
.build())
.build());
}
} }

View File

@@ -26,7 +26,6 @@ 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.TikTokHeaderEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
import io.github.jwdeveloper.tiktok.data.models.users.User; import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLikeMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastMemberMessage; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastMemberMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage;
import lombok.Getter; import lombok.Getter;
@@ -48,13 +47,4 @@ public class TikTokJoinEvent extends TikTokHeaderEvent {
user = User.map(msg.getUser()); user = User.map(msg.getUser());
totalUsers = msg.getMemberCount(); totalUsers = msg.getMemberCount();
} }
public static TikTokJoinEvent of(String userName)
{
return new TikTokJoinEvent(WebcastMemberMessage.newBuilder()
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setNickname(userName)
.build())
.build());
}
} }

View File

@@ -57,15 +57,4 @@ public class TikTokLikeEvent extends TikTokHeaderEvent
likes = msg.getCount(); likes = msg.getCount();
totalLikes = msg.getTotal(); totalLikes = msg.getTotal();
} }
public static TikTokLikeEvent of(String userName, int likes)
{
return new TikTokLikeEvent(WebcastLikeMessage.newBuilder()
.setCount(likes)
.setTotal(likes)
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setNickname(userName)
.build())
.build());
}
} }

View File

@@ -22,16 +22,19 @@
*/ */
package io.github.jwdeveloper.tiktok.data.models.gifts; package io.github.jwdeveloper.tiktok.data.models.gifts;
//TODO it should be called GiftComboStateType public enum GiftSendType
public enum GiftComboStateType { {
Finished, Finished,
Begin, Begin,
Active; Active;
public static GiftComboStateType fromNumber(long number) {
public static GiftSendType fromNumber(long number)
{
return switch ((int) number) { return switch ((int) number) {
case 1, 2, 4 -> GiftComboStateType.Active; case 0 -> GiftSendType.Finished;
default -> GiftComboStateType.Finished; case 1, 2, 4 -> GiftSendType.Active;
default -> GiftSendType.Finished;
}; };
} }
} }

View File

@@ -42,7 +42,16 @@ public class GiftsData
public static final class Response public static final class Response
{ {
private String json; private String json;
private List<Gift> gifts; private List<GiftModel> gifts;
}
@Data
public static class GiftModel
{
private int id;
private String name;
private int diamondCost;
private String image;
} }
} }

View File

@@ -35,7 +35,6 @@ public class LiveData {
} }
@Data @Data
@AllArgsConstructor
public static class Response { public static class Response {
private String json; private String json;
private LiveStatus liveStatus; private LiveStatus liveStatus;
@@ -45,12 +44,6 @@ public class LiveData {
private int totalViewers; private int totalViewers;
private boolean ageRestricted; private boolean ageRestricted;
private User host; private User host;
private LiveType liveType;
public Response() {
}
} }
public enum LiveStatus { public enum LiveStatus {
@@ -58,11 +51,4 @@ public class LiveData {
HostOnline, HostOnline,
HostOffline, HostOffline,
} }
public enum LiveType {
SOLO,
BOX,
BATTLE,
CO_HOST
}
} }

View File

@@ -22,7 +22,10 @@
*/ */
package io.github.jwdeveloper.tiktok.data.requests; package io.github.jwdeveloper.tiktok.data.requests;
import lombok.*; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
public class LiveUserData { public class LiveUserData {
@@ -35,18 +38,15 @@ public class LiveUserData {
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public static class Response { public static class Response {
private String json; private String json;
private UserStatus userStatus; private UserStatus userStatus;
private String roomId; private String roomId;
private long startedAtTimeStamp; 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 { public enum UserStatus {
@@ -56,3 +56,5 @@ public class LiveUserData {
Live, Live,
} }
} }

View File

@@ -20,9 +20,16 @@
* 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.extension.recorder.impl.enums; package io.github.jwdeveloper.tiktok.data.requests;
public enum LiveFormat import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class SignServerResponse
{ {
MP4 private String signedUrl;
private String userAgent;
} }

View File

@@ -46,7 +46,6 @@ public class HttpClientSettings {
final Map<String, String> cookies; final Map<String, String> cookies;
@Getter @Getter
@Setter
ProxyClientSettings proxyClientSettings; ProxyClientSettings proxyClientSettings;
@Getter @Getter
@@ -75,7 +74,7 @@ public class HttpClientSettings {
* @param consumer Use to configure proxy settings for http client * @param consumer Use to configure proxy settings for http client
*/ */
public void configureProxy(Consumer<ProxyClientSettings> consumer) { public void configureProxy(Consumer<ProxyClientSettings> consumer) {
proxyClientSettings.setEnabled(true); proxyClientSettings.setUseProxy(true);
consumer.accept(proxyClientSettings); consumer.accept(proxyClientSettings);
} }
@@ -105,7 +104,7 @@ public class HttpClientSettings {
newSettings.getHeaders().putAll(new TreeMap<>(this.headers)); newSettings.getHeaders().putAll(new TreeMap<>(this.headers));
newSettings.getCookies().putAll(new TreeMap<>(this.cookies)); newSettings.getCookies().putAll(new TreeMap<>(this.cookies));
newSettings.getParams().putAll(new TreeMap<>(this.params)); newSettings.getParams().putAll(new TreeMap<>(this.params));
newSettings.proxyClientSettings = this.proxyClientSettings; newSettings.proxyClientSettings = this.proxyClientSettings.clone();
return newSettings; return newSettings;
} }

View File

@@ -33,26 +33,10 @@ import java.util.logging.Level;
@Data @Data
public class LiveClientSettings { public class LiveClientSettings {
/**
* TODO: give better description
* <p>
* sets client in the offline mode, so it do not connects to TikTok servers
* it makes sense to use it when you are testing client with your custom events
*/
private boolean offline;
/**
* TODO: give better description
* <p>
* Determines if gifts data is downloaded before TikTokLive starts,
* when `false` then client.giftManager() does not contain initial gifts
*/
private boolean fetchGifts = true;
/** /**
* ISO-Language for Client * ISO-Language for Client
*/ */
private String clientLanguage; private String clientLanguage;
/** /**
@@ -60,6 +44,7 @@ public class LiveClientSettings {
*/ */
private boolean retryOnConnectionFailure; private boolean retryOnConnectionFailure;
/** /**
* Before retrying connect, wait for select amount of time * Before retrying connect, wait for select amount of time
*/ */
@@ -68,40 +53,44 @@ public class LiveClientSettings {
/** /**
* Whether to print Logs to Console * Whether to print Logs to Console
*/ */
private boolean printToConsole = true;
private boolean printToConsole = true;
/** /**
* LoggingLevel for Logs * LoggingLevel for Logs
*/ */
private Level logLevel; private Level logLevel;
/** /**
* Optional: Use it if you need to change TikTok live hostname in builder * Optional: Use it if you need to change TikTok live hostname in builder
*/ */
private String hostName; private String hostName;
/** /**
* Parameters used in requests to TikTok api * Parameters used in requests to TikTok api
*/ */
private HttpClientSettings httpSettings; 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 * 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; private String sessionId;
/** /*
* Optional: By default roomID is fetched before connect to live, but you can set it manually * Optional: By default roomID is fetched before connect to live, but you can set it manually
*
*/ */
private String roomId; private String roomId;
/**
* Optional: API Key for increased limit to signing server
*/
private String apiKey;
public static LiveClientSettings createDefault() {
public static LiveClientSettings createDefault()
{
var httpSettings = new HttpClientSettings(); var httpSettings = new HttpClientSettings();
httpSettings.getParams().putAll(DefaultClientParams()); httpSettings.getParams().putAll(DefaultClientParams());
httpSettings.getHeaders().putAll(DefaultRequestHeaders()); httpSettings.getHeaders().putAll(DefaultRequestHeaders());
@@ -114,10 +103,12 @@ public class LiveClientSettings {
clientSettings.setPrintToConsole(false); clientSettings.setPrintToConsole(false);
clientSettings.setLogLevel(Level.ALL); clientSettings.setLogLevel(Level.ALL);
clientSettings.setHttpSettings(httpSettings); clientSettings.setHttpSettings(httpSettings);
return clientSettings; return clientSettings;
} }
/** /**
* Default Parameters for HTTP-Request * Default Parameters for HTTP-Request
*/ */
@@ -156,9 +147,11 @@ public class LiveClientSettings {
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
*/ */
@@ -175,5 +168,5 @@ public class LiveClientSettings {
return headers; return headers;
} }
} }

View File

@@ -22,100 +22,19 @@
*/ */
package io.github.jwdeveloper.tiktok.data.settings; package io.github.jwdeveloper.tiktok.data.settings;
import io.github.jwdeveloper.tiktok.data.dto.ProxyData; import lombok.Getter;
import lombok.*; import lombok.Setter;
import java.net.*;
import java.util.*;
import java.util.function.Consumer;
//TODO proxy implementation
@Getter @Getter
@Setter public class ProxyClientSettings
public class ProxyClientSettings implements Iterator<ProxyData>
{ {
private boolean enabled, autoDiscard = true, fallback = true; @Setter
private Rotation rotation = Rotation.CONSECUTIVE; private boolean useProxy;
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() public ProxyClientSettings clone()
{ {
ProxyClientSettings settings = new ProxyClientSettings(); return 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
} }
} }

View File

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

View File

@@ -27,42 +27,38 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.data.requests.LiveData; import io.github.jwdeveloper.tiktok.data.requests.LiveData;
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
public interface LiveHttpClient public interface LiveHttpClient {
{
/** /**
* @return list of gifts that are available in your country * @return list of gifts that are available in your country
*/ */
GiftsData.Response fetchGiftsData(); GiftsData.Response fetchGiftsData();
/** /**
* Returns information about user that is having a livestream * Returns information about user that is having a livestream
* @param userName name of user *
* @return {@link LiveUserData.Response} * @param userName
* @return
*/ */
default LiveUserData.Response fetchLiveUserData(String userName) { LiveUserData.Response fetchLiveUserData(String userName);
return fetchLiveUserData(new LiveUserData.Request(userName));
}
LiveUserData.Response fetchLiveUserData(LiveUserData.Request request); LiveUserData.Response fetchLiveUserData(LiveUserData.Request request);
/** /**
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData * @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
* @return {@link LiveData.Response} * @return
*/ */
default LiveData.Response fetchLiveData(String roomId) { LiveData.Response fetchLiveData(String roomId);
return fetchLiveData(new LiveData.Request(roomId));
}
LiveData.Response fetchLiveData(LiveData.Request request); LiveData.Response fetchLiveData(LiveData.Request request);
/** /**
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData * @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
* @return {@link LiveConnectionData.Response} * @return
*/ */
default LiveConnectionData.Response fetchLiveConnectionData(String roomId) { LiveConnectionData.Response fetchLiveConnectionData(String roomId);
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
}
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request); LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
} }

View File

@@ -20,30 +20,44 @@
* 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.exceptions; package io.github.jwdeveloper.tiktok.live;
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import java.util.List;
public interface GiftManager {
/**
* 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);
/* /**
* Happens while bad response from http proxy request to TikTok *
*/ * @param giftId
public class TikTokProxyRequestException extends TikTokLiveException * @return
{ */
public TikTokProxyRequestException() { Gift findById(int giftId);
}
public TikTokProxyRequestException(String message) { /**
super(message); *
} * @param giftName
* @return
*/
Gift findByName(String giftName);
public TikTokProxyRequestException(String message, Throwable cause) { /**
super(message, cause); *
} * @return all gifts
*/
public TikTokProxyRequestException(Throwable cause) { List<Gift> getGifts();
super(cause);
}
public TikTokProxyRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
} }

View File

@@ -1,86 +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.live;
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.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
public interface GiftsManager {
/**
* You can create and attach your own custom gift to manager
*
* @param gift
*/
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();
}

View File

@@ -64,7 +64,7 @@ public interface LiveClient {
/** /**
* Get information about gifts * Get information about gifts
*/ */
GiftsManager getGiftManager(); GiftManager 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.

View File

@@ -24,7 +24,6 @@ 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;
@@ -150,13 +149,6 @@ 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
@@ -224,3 +216,5 @@ public interface EventsBuilder<T> {
//T onUnhandledControl(TikTokEventConsumer<TikTokUnhandledControlEvent> event); //T onUnhandledControl(TikTokEventConsumer<TikTokUnhandledControlEvent> event);
} }

View File

@@ -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.3.0-Release</version> <version>1.0.14-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.5</version> <version>1.5.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.testng</groupId> <groupId>org.testng</groupId>

View File

@@ -23,65 +23,20 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftsManager;
import io.github.jwdeveloper.tiktok.http.LiveHttpClient; 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;
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
* @return true if live is Online, false if is offline
*/
public static boolean isLiveOnline(String hostName) {
return requests().fetchLiveUserData(hostName).isLiveOnline();
}
/**
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
*
* @param hostName profile name of Tiktok user could be found in profile link
* @return true if live is Online, false if is offline
*/
public static CompletableFuture<Boolean> isLiveOnlineAsync(String hostName) {
return CompletableFuture.supplyAsync(() -> isLiveOnline(hostName));
}
/**
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
*
* @param hostName profile name of Tiktok user could be found in profile link
* @return true is hostName name is valid and exists, false if not
*/
public static boolean isHostNameValid(String hostName) {
return requests().fetchLiveUserData(hostName).isHostNameValid();
}
/**
* Example: https://www.tiktok.com/@dostawcavideo - hostName would be 'dostawcavideo'
*
* @param hostName profile name of Tiktok user could be found in profile link
* @return true is hostName name is valid and exists, false if not
*/
public static CompletableFuture<Boolean> isHostNameValidAsync(String hostName) {
return CompletableFuture.supplyAsync(() -> isHostNameValid(hostName));
}
/** /**
* Use to get some data from TikTok about users are lives * Use to get some data from TikTok about users are lives
@@ -89,31 +44,9 @@ public class TikTokLive {
* @return LiveHttpClient * @return LiveHttpClient
*/ */
public static LiveHttpClient requests() { public static LiveHttpClient requests() {
return new TikTokLiveHttpClient(); 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;
}
} }

View File

@@ -26,17 +26,16 @@ 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.data.requests.LiveConnectionData; import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.data.requests.LiveData; import io.github.jwdeveloper.tiktok.data.requests.LiveData;
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
import io.github.jwdeveloper.tiktok.exceptions.*; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.http.LiveHttpClient; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
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.GiftsManager; import io.github.jwdeveloper.tiktok.live.GiftManager;
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.models.ConnectionState; import io.github.jwdeveloper.tiktok.models.ConnectionState;
@@ -49,24 +48,24 @@ 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 LiveHttpClient httpClient; private final TikTokGiftManager tikTokGiftManager;
private final TikTokLiveHttpClient httpClient;
private final SocketClient webSocketClient; private final SocketClient webSocketClient;
private final TikTokLiveEventHandler tikTokEventHandler; private final TikTokLiveEventHandler tikTokEventHandler;
private final LiveClientSettings 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(GiftsManager giftsManager, public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta,
TikTokRoomInfo tikTokLiveMeta, TikTokLiveHttpClient tiktokHttpClient,
LiveHttpClient tiktokHttpClient,
SocketClient webSocketClient, SocketClient webSocketClient,
TikTokGiftManager tikTokGiftManager,
TikTokLiveEventHandler tikTokEventHandler, TikTokLiveEventHandler tikTokEventHandler,
LiveClientSettings clientSettings, LiveClientSettings 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.httpClient = tiktokHttpClient;
this.webSocketClient = webSocketClient; this.webSocketClient = webSocketClient;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
@@ -77,15 +76,19 @@ public class TikTokLiveClient implements LiveClient {
public void connectAsync(Consumer<LiveClient> onConnection) { public void connectAsync(Consumer<LiveClient> onConnection) {
CompletableFuture.runAsync(() -> { CompletableFuture.supplyAsync(() ->
{
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,36 +120,34 @@ public class TikTokLiveClient implements LiveClient {
} }
public void tryConnect() { public void tryConnect() {
if (!liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) { if (!liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED))
{
throw new TikTokLiveException("Already connected"); throw new TikTokLiveException("Already connected");
} }
setState(ConnectionState.CONNECTING); setState(ConnectionState.CONNECTING);
tikTokEventHandler.publish(this, new TikTokConnectingEvent());
var userDataRequest = new LiveUserData.Request(liveRoomInfo.getHostName()); var userDataRequest = new LiveUserData.Request(liveRoomInfo.getHostName());
var userData = httpClient.fetchLiveUserData(userDataRequest); var userData = httpClient.fetchLiveUserData(userDataRequest);
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp()); liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
liveRoomInfo.setRoomId(userData.getRoomId()); liveRoomInfo.setRoomId(userData.getRoomId());
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) {
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostUser());
}
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) {
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostUser());
}
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline)
throw new TikTokLiveOfflineHostException("User is offline: " + liveRoomInfo.getHostName());
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound)
throw new TikTokLiveOfflineHostException("User not found: " + liveRoomInfo.getHostName());
var liveDataRequest = new LiveData.Request(userData.getRoomId()); var liveDataRequest = new LiveData.Request(userData.getRoomId());
var liveData = httpClient.fetchLiveData(liveDataRequest); var liveData = httpClient.fetchLiveData(liveDataRequest);
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) {
if (liveData.isAgeRestricted()) throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found.");
throw new TikTokLiveException("Livestream for " + liveRoomInfo.getHostName() + " is 18+ or age restricted!"); }
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) {
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?");
throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " could not be found."); }
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline)
throw new TikTokLiveOfflineHostException("LiveStream for " + liveRoomInfo.getHostName() + " not found, is the Host offline?");
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
liveRoomInfo.setTitle(liveData.getTitle()); liveRoomInfo.setTitle(liveData.getTitle());
liveRoomInfo.setViewersCount(liveData.getViewers()); liveRoomInfo.setViewersCount(liveData.getViewers());
@@ -154,12 +155,8 @@ public class TikTokLiveClient implements LiveClient {
liveRoomInfo.setAgeRestricted(liveData.isAgeRestricted()); liveRoomInfo.setAgeRestricted(liveData.isAgeRestricted());
liveRoomInfo.setHost(liveData.getHost()); 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 liveConnectionRequest =new LiveConnectionData.Request(userData.getRoomId());
var liveConnectionData = httpClient.fetchLiveConnectionData(liveConnectionRequest); var liveConnectionData = httpClient.fetchLiveConnectionData(liveConnectionRequest);
webSocketClient.start(liveConnectionData, this); webSocketClient.start(liveConnectionData, this);
@@ -171,8 +168,8 @@ public class TikTokLiveClient implements LiveClient {
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) { if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
return; return;
} }
setState(ConnectionState.DISCONNECTED);
webSocketClient.stop(); webSocketClient.stop();
setState(ConnectionState.DISCONNECTED);
} }
private void setState(ConnectionState connectionState) { private void setState(ConnectionState connectionState) {
@@ -184,10 +181,6 @@ public class TikTokLiveClient implements LiveClient {
tikTokEventHandler.publish(this, event); tikTokEventHandler.publish(this, event);
} }
@Override
public GiftsManager getGiftManager() {
return giftsManager;
}
public LiveRoomInfo getRoomInfo() { public LiveRoomInfo getRoomInfo() {
return liveRoomInfo; return liveRoomInfo;
@@ -202,4 +195,11 @@ public class TikTokLiveClient implements LiveClient {
public Logger getLogger() { public Logger getLogger() {
return logger; return logger;
} }
@Override
public GiftManager getGiftManager() {
return tikTokGiftManager;
}
} }

View File

@@ -22,49 +22,65 @@
*/ */
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.*; 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.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.*; import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
import io.github.jwdeveloper.tiktok.data.events.social.*; import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomPinEvent;
import io.github.jwdeveloper.tiktok.data.events.websocket.*; import io.github.jwdeveloper.tiktok.data.events.social.TikTokFollowEvent;
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokShareEvent;
import io.github.jwdeveloper.tiktok.data.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.TikTokGiftsManager; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.http.HttpClientFactory; import io.github.jwdeveloper.tiktok.http.HttpClientFactory;
import io.github.jwdeveloper.tiktok.listener.*; import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import io.github.jwdeveloper.tiktok.live.*; import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
import io.github.jwdeveloper.tiktok.live.builder.*; import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.mappers.*; 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.*; import io.github.jwdeveloper.tiktok.mappers.handlers.TikTokCommonEventHandler;
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.data.settings.LiveClientSettings;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient; import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketOfflineClient;
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.Logger; import java.util.logging.*;
public class TikTokLiveClientBuilder implements LiveClientBuilder { public class TikTokLiveClientBuilder implements LiveClientBuilder {
protected final LiveClientSettings clientSettings; protected final LiveClientSettings clientSettings;
protected final TikTokLiveEventHandler eventHandler; protected final Logger logger;
protected final TikTokLiveEventHandler 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.clientSettings = LiveClientSettings.createDefault(); this.clientSettings = LiveClientSettings.createDefault();
this.clientSettings.setHostName(userName); this.clientSettings.setHostName(userName);
this.eventHandler = new TikTokLiveEventHandler(); this.tikTokEventHandler = new TikTokLiveEventHandler();
this.logger = Logger.getLogger(TikTokLive.class.getSimpleName() + " " + userName);
this.listeners = new ArrayList<>(); this.listeners = new ArrayList<>();
this.onCustomMappings = (e) -> { this.onCustomMappings = (e) -> {
}; };
@@ -75,33 +91,55 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this; return this;
} }
public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure) { public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure) {
onConfigure.accept(clientSettings); onConfigure.accept(clientSettings);
return this; return this;
} }
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) { public TikTokLiveClientBuilder addListener(TikTokEventListener listener) {
if (listener != null) listeners.add(listener);
listeners.add(listener);
return this; return this;
} }
protected void validate() { protected void validate() {
if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty()) if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty()) {
clientSettings.setClientLanguage("en"); clientSettings.setClientLanguage("en");
}
if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty())
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(); var httpSettings = clientSettings.getHttpSettings();
httpSettings.getParams().put("app_language", clientSettings.getClientLanguage()); httpSettings.getParams().put("app_language", clientSettings.getClientLanguage());
httpSettings.getParams().put("webcast_language", clientSettings.getClientLanguage()); httpSettings.getParams().put("webcast_language", clientSettings.getClientLanguage());
this.logger = LoggerFactory.create(clientSettings.getHostName(), clientSettings);
this.giftsManager = clientSettings.isFetchGifts() ? TikTokLive.gifts() : new TikTokGiftsManager(List.of()); 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() {
@@ -110,36 +148,35 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
var tiktokRoomInfo = new TikTokRoomInfo(); var tiktokRoomInfo = new TikTokRoomInfo();
tiktokRoomInfo.setHostName(clientSettings.getHostName()); tiktokRoomInfo.setHostName(clientSettings.getHostName());
var listenerManager = new TikTokListenersManager(listeners, eventHandler); var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
var giftManager = new TikTokGiftManager(logger);
var eventsMapper = createMapper(giftManager, tiktokRoomInfo);
var messageHandler = new TikTokLiveMessageHandler(tikTokEventHandler, eventsMapper);
var httpClientFactory = new HttpClientFactory(clientSettings); var httpClientFactory = new HttpClientFactory(clientSettings);
var tikTokLiveHttpClient = new TikTokLiveHttpClient(httpClientFactory);
var liveHttpClient = clientSettings.isOffline() ? var webSocketClient = new TikTokWebSocketClient(
new TikTokLiveHttpOfflineClient() : clientSettings,
new TikTokLiveHttpClient(httpClientFactory, clientSettings); messageHandler,
tikTokEventHandler);
var eventsMapper = createMapper(giftsManager, tiktokRoomInfo); return new TikTokLiveClient(tiktokRoomInfo,
var messageHandler = new TikTokLiveMessageHandler(eventHandler, eventsMapper); tikTokLiveHttpClient,
var webSocketClient = clientSettings.isOffline() ?
new TikTokWebSocketOfflineClient(eventHandler) :
new TikTokWebSocketClient(
clientSettings,
messageHandler,
eventHandler);
return new TikTokLiveClient(
giftsManager,
tiktokRoomInfo,
liveHttpClient,
webSocketClient, webSocketClient,
eventHandler, giftManager,
tikTokEventHandler,
clientSettings, clientSettings,
listenerManager, listenerManager,
logger); logger);
} }
public TikTokLiveMapper createMapper(GiftsManager giftsManager, TikTokRoomInfo roomInfo) { public TikTokLiveMapper createMapper(GiftManager giftManager, TikTokRoomInfo roomInfo) {
/*
//
*/
var eventMapper = new TikTokGenericEventMapper(); var eventMapper = new TikTokGenericEventMapper();
@@ -147,7 +184,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
//ConnectionEvents events //ConnectionEvents events
var commonHandler = new TikTokCommonEventHandler(); var commonHandler = new TikTokCommonEventHandler();
var giftHandler = new TikTokGiftEventHandler(giftsManager, roomInfo); var giftHandler = new TikTokGiftEventHandler(giftManager, roomInfo);
var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo); var roomInfoHandler = new TikTokRoomInfoEventHandler(roomInfo);
var socialHandler = new TikTokSocialMediaEventHandler(roomInfo); var socialHandler = new TikTokSocialMediaEventHandler(roomInfo);
@@ -238,256 +275,273 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return build().connectAsync(); return build().connectAsync();
} }
public TikTokLiveClientBuilder onUnhandledSocial(EventConsumer<TikTokUnhandledSocialEvent> event) {
eventHandler.subscribe(TikTokUnhandledSocialEvent.class, event); public TikTokLiveClientBuilder onUnhandledSocial(
EventConsumer<TikTokUnhandledSocialEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledSocialEvent.class, event);
return this; return this;
} }
// @Override
public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) { public LiveClientBuilder onChest(EventConsumer<TikTokChestEvent> event) {
eventHandler.subscribe(TikTokChestEvent.class, event); tikTokEventHandler.subscribe(TikTokChestEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicFanTicket(EventConsumer<TikTokLinkMicFanTicketEvent> event) {
eventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event); public TikTokLiveClientBuilder onLinkMicFanTicket(
EventConsumer<TikTokLinkMicFanTicketEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onEnvelope(EventConsumer<TikTokEnvelopeEvent> event) { public TikTokLiveClientBuilder onEnvelope(EventConsumer<TikTokEnvelopeEvent> event) {
eventHandler.subscribe(TikTokEnvelopeEvent.class, event); tikTokEventHandler.subscribe(TikTokEnvelopeEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onShop(EventConsumer<TikTokShopEvent> event) { public TikTokLiveClientBuilder onShop(EventConsumer<TikTokShopEvent> event) {
eventHandler.subscribe(TikTokShopEvent.class, event); tikTokEventHandler.subscribe(TikTokShopEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onDetect(EventConsumer<TikTokDetectEvent> event) { public TikTokLiveClientBuilder onDetect(
eventHandler.subscribe(TikTokDetectEvent.class, event); EventConsumer<TikTokDetectEvent> event) {
tikTokEventHandler.subscribe(TikTokDetectEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkLayer(EventConsumer<TikTokLinkLayerEvent> event) { public TikTokLiveClientBuilder onLinkLayer(
eventHandler.subscribe(TikTokLinkLayerEvent.class, event); EventConsumer<TikTokLinkLayerEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkLayerEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onConnected(EventConsumer<TikTokConnectedEvent> event) { public TikTokLiveClientBuilder onConnected(EventConsumer<TikTokConnectedEvent> event) {
eventHandler.subscribe(TikTokConnectedEvent.class, event); tikTokEventHandler.subscribe(TikTokConnectedEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onPreConnection(EventConsumer<TikTokPreConnectionEvent> event) {
eventHandler.subscribe(TikTokPreConnectionEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onCaption(EventConsumer<TikTokCaptionEvent> event) { public TikTokLiveClientBuilder onCaption(EventConsumer<TikTokCaptionEvent> event) {
eventHandler.subscribe(TikTokCaptionEvent.class, event); tikTokEventHandler.subscribe(TikTokCaptionEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onQuestion(EventConsumer<TikTokQuestionEvent> event) { public TikTokLiveClientBuilder onQuestion(EventConsumer<TikTokQuestionEvent> event) {
eventHandler.subscribe(TikTokQuestionEvent.class, event); tikTokEventHandler.subscribe(TikTokQuestionEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRoomPin(EventConsumer<TikTokRoomPinEvent> event) { public TikTokLiveClientBuilder onRoomPin(
eventHandler.subscribe(TikTokRoomPinEvent.class, event); EventConsumer<TikTokRoomPinEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomPinEvent.class, event);
return this; return this;
} }
@Override @Override
public <E extends TikTokEvent> LiveClientBuilder onEvent(Class<E> eventClass, EventConsumer<E> event) { public <E extends TikTokEvent> LiveClientBuilder onEvent(Class<E> eventClass, EventConsumer<E> event) {
eventHandler.subscribe(eventClass, event); tikTokEventHandler.subscribe(eventClass, event);
return this; return this;
} }
@Override @Override
public TikTokLiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) { public LiveClientBuilder onRoomInfo(EventConsumer<TikTokRoomInfoEvent> event) {
eventHandler.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) {
eventHandler.subscribe(TikTokLivePausedEvent.class, event); tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event);
return this; return this;
} }
@Override @Override
public TikTokLiveClientBuilder onLiveUnpaused(EventConsumer<TikTokLiveUnpausedEvent> event) { public LiveClientBuilder onLiveUnpaused(EventConsumer<TikTokLiveUnpausedEvent> event) {
eventHandler.subscribe(TikTokLiveUnpausedEvent.class, event); tikTokEventHandler.subscribe(TikTokLiveUnpausedEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLike(EventConsumer<TikTokLikeEvent> event) { public TikTokLiveClientBuilder onLike(EventConsumer<TikTokLikeEvent> event) {
eventHandler.subscribe(TikTokLikeEvent.class, event); tikTokEventHandler.subscribe(TikTokLikeEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLink(EventConsumer<TikTokLinkEvent> event) { public TikTokLiveClientBuilder onLink(EventConsumer<TikTokLinkEvent> event) {
eventHandler.subscribe(TikTokLinkEvent.class, event); tikTokEventHandler.subscribe(TikTokLinkEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onBarrage(EventConsumer<TikTokBarrageEvent> event) { public TikTokLiveClientBuilder onBarrage(
eventHandler.subscribe(TikTokBarrageEvent.class, event); EventConsumer<TikTokBarrageEvent> event) {
tikTokEventHandler.subscribe(TikTokBarrageEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onGift(EventConsumer<TikTokGiftEvent> event) { public TikTokLiveClientBuilder onGift(EventConsumer<TikTokGiftEvent> event) {
eventHandler.subscribe(TikTokGiftEvent.class, event); tikTokEventHandler.subscribe(TikTokGiftEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onGiftCombo(EventConsumer<TikTokGiftComboEvent> event) { public TikTokLiveClientBuilder onGiftCombo(EventConsumer<TikTokGiftComboEvent> event) {
eventHandler.subscribe(TikTokGiftComboEvent.class, event); tikTokEventHandler.subscribe(TikTokGiftComboEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicArmies(EventConsumer<TikTokLinkMicArmiesEvent> event) {
eventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event); public TikTokLiveClientBuilder onLinkMicArmies(
EventConsumer<TikTokLinkMicArmiesEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onEmote(EventConsumer<TikTokEmoteEvent> event) { public TikTokLiveClientBuilder onEmote(EventConsumer<TikTokEmoteEvent> event) {
eventHandler.subscribe(TikTokEmoteEvent.class, event); tikTokEventHandler.subscribe(TikTokEmoteEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onUnauthorizedMember(EventConsumer<TikTokUnauthorizedMemberEvent> event) { public TikTokLiveClientBuilder onUnauthorizedMember(
eventHandler.subscribe(TikTokUnauthorizedMemberEvent.class, event); EventConsumer<TikTokUnauthorizedMemberEvent> event) {
tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onInRoomBanner(EventConsumer<TikTokInRoomBannerEvent> event) { public TikTokLiveClientBuilder onInRoomBanner(
eventHandler.subscribe(TikTokInRoomBannerEvent.class, event); EventConsumer<TikTokInRoomBannerEvent> event) {
tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicMethod(EventConsumer<TikTokLinkMicMethodEvent> event) { public TikTokLiveClientBuilder onLinkMicMethod(
eventHandler.subscribe(TikTokLinkMicMethodEvent.class, event); EventConsumer<TikTokLinkMicMethodEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onSubscribe(EventConsumer<TikTokSubscribeEvent> event) { public TikTokLiveClientBuilder onSubscribe(EventConsumer<TikTokSubscribeEvent> event) {
eventHandler.subscribe(TikTokSubscribeEvent.class, event); tikTokEventHandler.subscribe(TikTokSubscribeEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onPoll(EventConsumer<TikTokPollEvent> event) { public TikTokLiveClientBuilder onPoll(EventConsumer<TikTokPollEvent> event) {
eventHandler.subscribe(TikTokPollEvent.class, event); tikTokEventHandler.subscribe(TikTokPollEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onFollow(EventConsumer<TikTokFollowEvent> event) { public TikTokLiveClientBuilder onFollow(EventConsumer<TikTokFollowEvent> event) {
eventHandler.subscribe(TikTokFollowEvent.class, event); tikTokEventHandler.subscribe(TikTokFollowEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onComment(EventConsumer<TikTokCommentEvent> event) { public TikTokLiveClientBuilder onComment(EventConsumer<TikTokCommentEvent> event) {
eventHandler.subscribe(TikTokCommentEvent.class, event); tikTokEventHandler.subscribe(TikTokCommentEvent.class, event);
return this; return this;
} }
@Override @Override
public LiveClientBuilder onHttpResponse(EventConsumer<TikTokHttpResponseEvent> action) { public LiveClientBuilder onHttpResponse(EventConsumer<TikTokHttpResponseEvent> action) {
eventHandler.subscribe(TikTokHttpResponseEvent.class, action); tikTokEventHandler.subscribe(TikTokHttpResponseEvent.class, action);
return this; return this;
} }
public TikTokLiveClientBuilder onGoalUpdate(EventConsumer<TikTokGoalUpdateEvent> event) { public TikTokLiveClientBuilder onGoalUpdate(EventConsumer<TikTokGoalUpdateEvent> event) {
eventHandler.subscribe(TikTokGoalUpdateEvent.class, event); tikTokEventHandler.subscribe(TikTokGoalUpdateEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRankUpdate(EventConsumer<TikTokRankUpdateEvent> event) { public TikTokLiveClientBuilder onRankUpdate(EventConsumer<TikTokRankUpdateEvent> event) {
eventHandler.subscribe(TikTokRankUpdateEvent.class, event); tikTokEventHandler.subscribe(TikTokRankUpdateEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onIMDelete(EventConsumer<TikTokIMDeleteEvent> event) { public TikTokLiveClientBuilder onIMDelete(EventConsumer<TikTokIMDeleteEvent> event) {
eventHandler.subscribe(TikTokIMDeleteEvent.class, event); tikTokEventHandler.subscribe(TikTokIMDeleteEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLiveEnded(EventConsumer<TikTokLiveEndedEvent> event) { public TikTokLiveClientBuilder onLiveEnded(EventConsumer<TikTokLiveEndedEvent> event) {
eventHandler.subscribe(TikTokLiveEndedEvent.class, event); tikTokEventHandler.subscribe(TikTokLiveEndedEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onError(EventConsumer<TikTokErrorEvent> event) { public TikTokLiveClientBuilder onError(EventConsumer<TikTokErrorEvent> event) {
eventHandler.subscribe(TikTokErrorEvent.class, event); tikTokEventHandler.subscribe(TikTokErrorEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onJoin(EventConsumer<TikTokJoinEvent> event) { public TikTokLiveClientBuilder onJoin(EventConsumer<TikTokJoinEvent> event) {
eventHandler.subscribe(TikTokJoinEvent.class, event); tikTokEventHandler.subscribe(TikTokJoinEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onRankText(EventConsumer<TikTokRankTextEvent> event) { public TikTokLiveClientBuilder onRankText(EventConsumer<TikTokRankTextEvent> event) {
eventHandler.subscribe(TikTokRankTextEvent.class, event); tikTokEventHandler.subscribe(TikTokRankTextEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onShare(EventConsumer<TikTokShareEvent> event) { public TikTokLiveClientBuilder onShare(EventConsumer<TikTokShareEvent> event) {
eventHandler.subscribe(TikTokShareEvent.class, event); tikTokEventHandler.subscribe(TikTokShareEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onUnhandledMember(EventConsumer<TikTokUnhandledMemberEvent> event) { public TikTokLiveClientBuilder onUnhandledMember(
eventHandler.subscribe(TikTokUnhandledMemberEvent.class, event); EventConsumer<TikTokUnhandledMemberEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledMemberEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onSubNotify(EventConsumer<TikTokSubNotifyEvent> event) { public TikTokLiveClientBuilder onSubNotify(EventConsumer<TikTokSubNotifyEvent> event) {
eventHandler.subscribe(TikTokSubNotifyEvent.class, event); tikTokEventHandler.subscribe(TikTokSubNotifyEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onLinkMicBattle(EventConsumer<TikTokLinkMicBattleEvent> event) { public TikTokLiveClientBuilder onLinkMicBattle(
eventHandler.subscribe(TikTokLinkMicBattleEvent.class, event); EventConsumer<TikTokLinkMicBattleEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onDisconnected(EventConsumer<TikTokDisconnectedEvent> event) { public TikTokLiveClientBuilder onDisconnected(
eventHandler.subscribe(TikTokDisconnectedEvent.class, event); EventConsumer<TikTokDisconnectedEvent> event) {
tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onUnhandledControl(EventConsumer<TikTokUnhandledControlEvent> event) { public TikTokLiveClientBuilder onUnhandledControl(
eventHandler.subscribe(TikTokUnhandledControlEvent.class, event); EventConsumer<TikTokUnhandledControlEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledControlEvent.class, event);
return this; return this;
} }
public TikTokLiveClientBuilder onEvent(EventConsumer<TikTokEvent> event) { public TikTokLiveClientBuilder onEvent(EventConsumer<TikTokEvent> event) {
eventHandler.subscribe(TikTokEvent.class, event); tikTokEventHandler.subscribe(TikTokEvent.class, event);
return this; return this;
} }
@Override @Override
public TikTokLiveClientBuilder onWebsocketResponse(EventConsumer<TikTokWebsocketResponseEvent> event) { public TikTokLiveClientBuilder onWebsocketResponse(EventConsumer<TikTokWebsocketResponseEvent> event) {
eventHandler.subscribe(TikTokWebsocketResponseEvent.class, event); tikTokEventHandler.subscribe(TikTokWebsocketResponseEvent.class, event);
return this; return this;
} }
@Override @Override
public TikTokLiveClientBuilder onWebsocketMessage(EventConsumer<TikTokWebsocketMessageEvent> event) { public TikTokLiveClientBuilder onWebsocketMessage(EventConsumer<TikTokWebsocketMessageEvent> event) {
eventHandler.subscribe(TikTokWebsocketMessageEvent.class, event); tikTokEventHandler.subscribe(TikTokWebsocketMessageEvent.class, event);
return this; return this;
} }
@Override @Override
public TikTokLiveClientBuilder onWebsocketUnhandledMessage(EventConsumer<TikTokWebsocketUnhandledMessageEvent> event) { public TikTokLiveClientBuilder onWebsocketUnhandledMessage(EventConsumer<TikTokWebsocketUnhandledMessageEvent> event) {
eventHandler.subscribe(TikTokWebsocketUnhandledMessageEvent.class, event); tikTokEventHandler.subscribe(TikTokWebsocketUnhandledMessageEvent.class, event);
return this; return this;
} }
@Override @Override
public TikTokLiveClientBuilder onReconnecting(EventConsumer<TikTokReconnectingEvent> event) { public TikTokLiveClientBuilder onReconnecting(EventConsumer<TikTokReconnectingEvent> event) {
eventHandler.subscribe(TikTokReconnectingEvent.class, event); tikTokEventHandler.subscribe(TikTokReconnectingEvent.class, event);
return this; return this;
} }
} }

View File

@@ -23,139 +23,125 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import io.github.jwdeveloper.tiktok.common.*;
import io.github.jwdeveloper.tiktok.data.requests.*; import io.github.jwdeveloper.tiktok.data.requests.*;
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.*; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.http.*; import io.github.jwdeveloper.tiktok.exceptions.TikTokSignServerException;
import io.github.jwdeveloper.tiktok.http.mappers.*; import io.github.jwdeveloper.tiktok.http.HttpClientFactory;
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
import io.github.jwdeveloper.tiktok.http.mappers.GiftsDataMapper;
import io.github.jwdeveloper.tiktok.http.mappers.LiveDataMapper;
import io.github.jwdeveloper.tiktok.http.mappers.LiveUserDataMapper;
import io.github.jwdeveloper.tiktok.http.mappers.SignServerResponseMapper;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.util.logging.Logger;
public class TikTokLiveHttpClient implements LiveHttpClient 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> * Signing API by Isaac Kogan
*/ * https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch"; */
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url";
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/"; private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/"; 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 HttpClientFactory httpFactory;
private final LiveClientSettings clientSettings;
private final LiveUserDataMapper liveUserDataMapper; private final LiveUserDataMapper liveUserDataMapper;
private final LiveDataMapper liveDataMapper; private final LiveDataMapper liveDataMapper;
private final SignServerResponseMapper singServerResponseMapper;
private final GiftsDataMapper giftsDataMapper; private final GiftsDataMapper giftsDataMapper;
private final Logger logger;
public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) { public TikTokLiveHttpClient(HttpClientFactory factory) {
this.httpFactory = factory; this.httpFactory = factory;
this.clientSettings = settings;
this.logger = LoggerFactory.create("HttpClient", clientSettings);
liveUserDataMapper = new LiveUserDataMapper(); liveUserDataMapper = new LiveUserDataMapper();
liveDataMapper = new LiveDataMapper(); liveDataMapper = new LiveDataMapper();
singServerResponseMapper = new SignServerResponseMapper();
giftsDataMapper = new GiftsDataMapper(); giftsDataMapper = new GiftsDataMapper();
} }
public TikTokLiveHttpClient() { public TikTokLiveHttpClient() {
this(new HttpClientFactory(LiveClientSettings.createDefault()), LiveClientSettings.createDefault()); this(new HttpClientFactory(LiveClientSettings.createDefault()));
} }
public GiftsData.Response fetchGiftsData() { public GiftsData.Response fetchGiftsData() {
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); var url = TIKTOK_URL_WEBCAST + "gift/list/";
if (proxyClientSettings.isEnabled()) { var optional = httpFactory.client(url)
while (proxyClientSettings.hasNext()) { .build()
try { .toJsonResponse();
return getGiftsData();
} catch (TikTokProxyRequestException ignored) {} if (optional.isEmpty()) {
} throw new TikTokLiveRequestException("Unable to fetch gifts information's");
} }
return getGiftsData(); var json = optional.get();
return giftsDataMapper.map(json);
} }
public GiftsData.Response getGiftsData() {
var result = httpFactory.client(TIKTOK_GIFTS_URL)
.build()
.toJsonResponse();
if (result.isFailure()) @Override
throw new TikTokLiveRequestException("Unable to fetch gifts information's"+result.toStack()); public LiveUserData.Response fetchLiveUserData(String userName) {
return fetchLiveUserData(new LiveUserData.Request(userName));
var json = result.getContent();
return giftsDataMapper.map(json);
} }
@Override @Override
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { 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 url = TIKTOK_URL_WEB + "api-live/user/room";
var result = httpFactory.client(url) var optional = httpFactory.client(url)
.withParam("uniqueId", request.getUserName()) .withParam("uniqueId", request.getUserName())
.withParam("sourceType", "54") .withParam("sourceType", "54")
.build() .build()
.toJsonResponse(); .toJsonResponse();
if (result.isFailure()) if (optional.isEmpty()) {
throw new TikTokLiveRequestException("Unable to get information's about user"+result.toStack()); throw new TikTokLiveRequestException("Unable to get information's about user");
}
var json = result.getContent(); var json = optional.get();
return liveUserDataMapper.map(json); return liveUserDataMapper.map(json);
} }
@Override @Override
public LiveData.Response fetchLiveData(LiveData.Request request) { public LiveData.Response fetchLiveData(String roomId) {
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); return fetchLiveData(new LiveData.Request(roomId));
if (proxyClientSettings.isEnabled()) {
while (proxyClientSettings.hasNext()) {
try {
return getLiveData(request);
} catch (TikTokProxyRequestException ignored) {}
}
}
return getLiveData(request);
} }
public LiveData.Response getLiveData(LiveData.Request request) { @Override
public LiveData.Response fetchLiveData(LiveData.Request request) {
var url = TIKTOK_URL_WEBCAST + "room/info"; var url = TIKTOK_URL_WEBCAST + "room/info";
var result = httpFactory.client(url) var optional = httpFactory.client(url)
.withParam("room_id", request.getRoomId()) .withParam("room_id", request.getRoomId())
.build() .build()
.toJsonResponse(); .toJsonResponse();
if (result.isFailure()) if (optional.isEmpty()) {
throw new TikTokLiveRequestException("Unable to get info about live room"+result.toStack()); throw new TikTokLiveRequestException("Unable to get info about live room");
}
var json = result.getContent(); var json = optional.get();
return liveDataMapper.map(json); return liveDataMapper.map(json);
} }
@Override
public LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
}
@Override @Override
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) { public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
var result = getStartingPayload(request);
HttpResponse<byte[]> credentialsResponse = result.getContent(); // Always guaranteed to have response var signServerResponse = getSignedUrl(request.getRoomId());
var credentialsResponse = getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
try { try {
var resultHeader = ActionResult.of(credentialsResponse.headers().firstValue("x-set-tt-cookie")); var optionalHeader = credentialsResponse.headers().firstValue("set-cookie");
if (resultHeader.isFailure()) { if (optionalHeader.isEmpty()) {
logger.warning("SignServer Headers: "+request.getRoomId()+" - "+credentialsResponse.headers().map()); throw new TikTokSignServerException("Sign server does not returned set-cookie header");
throw new TikTokSignServerException("Sign server did not return the x-set-tt-cookie header"+result.toStack());
} }
var websocketCookie = resultHeader.getContent(); var websocketCookie = optionalHeader.get();
var webcastResponse = WebcastResponse.parseFrom(credentialsResponse.body()); var webcastResponse = WebcastResponse.parseFrom(credentialsResponse.body());
var webSocketUrl = httpFactory var webSocketUrl = httpFactory
.client(webcastResponse.getPushServer()) .client(webcastResponse.getPushServer())
@@ -169,36 +155,43 @@ public class TikTokLiveHttpClient implements LiveHttpClient
return new LiveConnectionData.Response(websocketCookie, webSocketUrl, webcastResponse); return new LiveConnectionData.Response(websocketCookie, webSocketUrl, webcastResponse);
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse"+result.toStack()); throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse");
} }
} }
private ActionResult<HttpResponse<byte[]>> getStartingPayload(LiveConnectionData.Request request) { SignServerResponse getSignedUrl(String roomId) {
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); var urlToSign = httpFactory
if (proxyClientSettings.isEnabled()) { .client(TikTokLiveHttpClient.TIKTOK_URL_WEBCAST + "im/fetch")
while (proxyClientSettings.hasNext()) { .withParam("room_id", roomId)
try { .build()
return getByteResponse(request.getRoomId()); .toUrl();
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
}
var optional = httpFactory
.client(TikTokLiveHttpClient.TIKTOK_SIGN_API)
.withParam("client", "ttlive-java")
.withParam("uuc", "1")
.withParam("url", urlToSign.toString())
.build()
.toJsonResponse();
if (optional.isEmpty()) {
throw new TikTokSignServerException("Unable to sign url: " + urlToSign);
} }
return getByteResponse(request.getRoomId());
var json = optional.get();
return singServerResponseMapper.map(json);
} }
private ActionResult<HttpResponse<byte[]>> getByteResponse(String room_id) { HttpResponse<byte[]> getWebsocketCredentialsResponse(String signedUrl) {
HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API) var optionalResponse = httpFactory
.withParam("client", "ttlive-java") .clientEmpty(signedUrl)
.withParam("uuc", "1") .build()
.withParam("room_id", room_id); .toResponse(HttpResponse.BodyHandlers.ofByteArray());
if (optionalResponse.isEmpty()) {
if (clientSettings.getApiKey() != null) throw new TikTokSignServerException("Unable to get websocket connection credentials");
builder.withParam("apiKey", clientSettings.getApiKey()); }
return optionalResponse.get();
var result = builder.build().toResponse();
if (result.isFailure())
throw new TikTokSignServerException("Unable to get websocket connection credentials"+result.toStack());
return result;
} }
} }

View File

@@ -1,45 +0,0 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.data.models.users.User;
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;
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import java.net.URI;
import java.util.List;
public class TikTokLiveHttpOfflineClient implements LiveHttpClient {
@Override
public GiftsData.Response fetchGiftsData() {
return new GiftsData.Response("", List.of());
}
@Override
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
return new LiveUserData.Response("", LiveUserData.UserStatus.Live, "offline_room_id", 0);
}
@Override
public LiveData.Response fetchLiveData(LiveData.Request request) {
return new LiveData.Response("",
LiveData.LiveStatus.HostOnline,
"offline live",
0,
0,
0,
false,
new User(0L, "offline user", new Picture("")),
LiveData.LiveType.SOLO);
}
@Override
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
return new LiveConnectionData.Response("",
URI.create("https://example.live"),
WebcastResponse.newBuilder().build());
}
}

View File

@@ -1,87 +0,0 @@
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);
}
}

View File

@@ -1,27 +0,0 @@
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);
}
}

View File

@@ -1,34 +0,0 @@
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;
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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.Picture;
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import sun.misc.Unsafe;
import java.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) {
Gift gift = indexById.get(giftId);
return gift == null ? Gift.UNDEFINED : gift;
}
public Gift findByName(String giftName) {
Gift gift = indexByName.get(giftName);
return gift == null ? Gift.UNDEFINED : gift;
}
@Override
public List<Gift> getGifts() {
return indexById.values().stream().toList();
}
}

View File

@@ -1,65 +0,0 @@
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);
}
}

View File

@@ -22,65 +22,75 @@
*/ */
package io.github.jwdeveloper.tiktok.http; 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.data.settings.HttpClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import java.net.*; import java.net.CookieManager;
import java.net.http.*; import java.net.URI;
import java.nio.charset.*; import java.net.URLEncoder;
import java.util.*; import java.net.http.HttpRequest;
import java.util.regex.*; import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@AllArgsConstructor @AllArgsConstructor
public class HttpClient { public class HttpClient {
private final HttpClientSettings httpClientSettings;
private final String url;
protected final HttpClientSettings httpClientSettings;
protected final String url;
private final Pattern pattern = Pattern.compile("charset=(.*?)(?=&|$)");
public ActionResult<HttpResponse<byte[]>> toResponse() { public <T> Optional<HttpResponse<T>> toResponse(HttpResponse.BodyHandler<T> bodyHandler) {
var client = prepareClient(); var client = prepareClient();
var request = prepareGetRequest(); var request = prepareGetRequest();
try { try
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); {
var result = ActionResult.of(response); var response = client.send(request, bodyHandler);
return response.statusCode() != 200 ? result.message("HttpResponse Code: ", response.statusCode()).failure() : result.success(); if(response.statusCode() != 200)
} catch (Exception e) { {
return Optional.empty();
}
return Optional.of(response);
} catch (Exception e) {
throw new TikTokLiveRequestException(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) { public Optional<String> toJsonResponse() {
String type = headers.firstValue("Content-type").orElse("text/html; charset=utf-8"); var optional = toResponse(HttpResponse.BodyHandlers.ofString());
int i = type.indexOf(";"); if (optional.isEmpty()) {
if (i >= 0) type = type.substring(i+1); return Optional.empty();
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;
} }
var response = optional.get();
var body = response.body();
return Optional.of(body);
} }
public ActionResult<byte[]> toBinaryResponse() { public Optional<byte[]> toBinaryResponse() {
return toResponse().map(HttpResponse::body); var optional = toResponse(HttpResponse.BodyHandlers.ofByteArray());
if (optional.isEmpty()) {
return Optional.empty();
}
var body = optional.get().body();
return Optional.of(body);
} }
public URI toUrl() { public URI toUrl() {
var stringUrl = prepareUrlWithParameters(url, httpClientSettings.getParams()); var stringUrl = prepareUrlWithParameters(url, httpClientSettings.getParams());
return URI.create(stringUrl); return URI.create(stringUrl);
} }
protected HttpRequest prepareGetRequest() { private HttpRequest prepareGetRequest() {
var requestBuilder = HttpRequest.newBuilder().GET(); var requestBuilder = HttpRequest.newBuilder().GET();
requestBuilder.uri(toUrl()); requestBuilder.uri(toUrl());
requestBuilder.timeout(httpClientSettings.getTimeout()); requestBuilder.timeout(httpClientSettings.getTimeout());
@@ -90,17 +100,18 @@ public class HttpClient {
return requestBuilder.build(); return requestBuilder.build();
} }
protected java.net.http.HttpClient prepareClient() { private java.net.http.HttpClient prepareClient() {
var builder = java.net.http.HttpClient.newBuilder() var builder = java.net.http.HttpClient.newBuilder()
.followRedirects(java.net.http.HttpClient.Redirect.NORMAL) .followRedirects(java.net.http.HttpClient.Redirect.NORMAL)
.cookieHandler(new CookieManager()) .cookieHandler(new CookieManager())
.connectTimeout(httpClientSettings.getTimeout()); .connectTimeout(httpClientSettings.getTimeout());
httpClientSettings.getOnClientCreating().accept(builder); httpClientSettings.getOnClientCreating().accept(builder);
return builder.build(); return builder.build();
} }
protected String prepareUrlWithParameters(String url, Map<String, Object> parameters) { private String prepareUrlWithParameters(String url, Map<String, Object> parameters) {
if (parameters.isEmpty()) { if (parameters.isEmpty()) {
return url; return url;
} }

View File

@@ -77,10 +77,11 @@ public class HttpClientBuilder {
return this; return this;
} }
public HttpClient build() { public HttpClient build() {
var proxyClientSettings = httpClientSettings.getProxyClientSettings();
if (proxyClientSettings.isEnabled() && proxyClientSettings.hasNext())
return new HttpProxyClient(httpClientSettings, url);
return new HttpClient(httpClientSettings, url); return new HttpClient(httpClientSettings, url);
} }
} }

View File

@@ -22,7 +22,7 @@
*/ */
package io.github.jwdeveloper.tiktok.http; package io.github.jwdeveloper.tiktok.http;
import io.github.jwdeveloper.tiktok.data.settings.*; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
public class HttpClientFactory { public class HttpClientFactory {
private final LiveClientSettings liveClientSettings; private final LiveClientSettings liveClientSettings;
@@ -37,8 +37,6 @@ public class HttpClientFactory {
//Does not contains default httpClientSettings, Params, headers, etd //Does not contains default httpClientSettings, Params, headers, etd
public HttpClientBuilder clientEmpty(String url) { public HttpClientBuilder clientEmpty(String url) {
var settings = new HttpClientSettings(); return new HttpClientBuilder(url);
settings.setProxyClientSettings(liveClientSettings.getHttpSettings().getProxyClientSettings());
return new HttpClientBuilder(url, settings);
} }
} }

View File

@@ -1,208 +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.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();
}
};
}
}

View File

@@ -21,35 +21,51 @@
* 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.mappers; package io.github.jwdeveloper.tiktok.http.mappers;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
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 io.github.jwdeveloper.tiktok.data.requests.GiftsData;
import java.util.ArrayList; import java.util.ArrayList;
public class GiftsDataMapper { public class GiftsDataMapper {
public GiftsData.Response map(String json) { public GiftsData.Response map(String json) {
var parsedJson = JsonParser.parseString(json); var parsedJson = JsonParser.parseString(json);
var jsonObject = parsedJson.getAsJsonObject(); var jsonObject = parsedJson.getAsJsonObject();
var gifts = jsonObject.entrySet()
.parallelStream() if (!jsonObject.has("data")) {
.map(e -> mapSingleGift(e.getValue())) return new GiftsData.Response(json, new ArrayList<>());
}
var dataElement = jsonObject.getAsJsonObject("data");
if (!dataElement.has("gifts")) {
return new GiftsData.Response(json, new ArrayList<>());
}
var gifts = dataElement.get("gifts").getAsJsonArray()
.asList()
.stream()
.map(this::mapSingleGift)
.toList(); .toList();
return new GiftsData.Response(json, gifts); return new GiftsData.Response(json, gifts);
} }
private Gift mapSingleGift(JsonElement jsonElement) { private GiftsData.GiftModel mapSingleGift(JsonElement jsonElement) {
var jsonObject = jsonElement.getAsJsonObject(); var id = jsonElement.getAsJsonObject().get("id").getAsInt();
var name = jsonElement.getAsJsonObject().get("name").getAsString();
var diamondCost = jsonElement.getAsJsonObject().get("diamond_count").getAsInt();
var image = jsonElement.getAsJsonObject()
.get("image").getAsJsonObject()
.get("url_list").getAsJsonArray().get(0).getAsString();
var id = jsonObject.get("id").getAsInt(); if (image.endsWith(".webp")) {
var name = jsonObject.get("name").getAsString(); image = image.replace(".webp", ".jpg");
var diamondCost = jsonObject.get("diamondCost").getAsInt(); }
var image = jsonObject.get("image").getAsString(); var gift = new GiftsData.GiftModel();
return new Gift(id, name, diamondCost, new Picture(image), jsonObject); gift.setId(id);
gift.setName(name);
gift.setDiamondCost(diamondCost);
gift.setImage(image);
return gift;
} }
} }

View File

@@ -24,7 +24,6 @@ package io.github.jwdeveloper.tiktok.http.mappers;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; 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;
@@ -44,7 +43,6 @@ public class LiveDataMapper {
public LiveData.Response map(String json) { public LiveData.Response map(String json) {
var response = new LiveData.Response(); var response = new LiveData.Response();
response.setJson(json);
var parsedJson = JsonParser.parseString(json); var parsedJson = JsonParser.parseString(json);
var jsonObject = parsedJson.getAsJsonObject(); var jsonObject = parsedJson.getAsJsonObject();
@@ -65,9 +63,6 @@ public class LiveDataMapper {
default -> LiveData.LiveStatus.HostNotFound; default -> LiveData.LiveStatus.HostNotFound;
}; };
response.setLiveStatus(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 {
response.setLiveStatus(LiveData.LiveStatus.HostNotFound); response.setLiveStatus(LiveData.LiveStatus.HostNotFound);
} }
@@ -109,22 +104,6 @@ public class LiveDataMapper {
response.setHost(user); response.setHost(user);
} }
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; return response;
} }

View File

@@ -26,10 +26,12 @@ import com.google.gson.JsonParser;
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
public class LiveUserDataMapper public class LiveUserDataMapper {
{
public LiveUserData.Response map(String json) { public LiveUserData.Response map(String json) {
var jsonObject = JsonParser.parseString(json).getAsJsonObject(); var parsedJson = JsonParser.parseString(json);
var jsonObject = parsedJson.getAsJsonObject();
var message = jsonObject.get("message").getAsString(); var message = jsonObject.get("message").getAsString();
@@ -62,5 +64,6 @@ public class LiveUserDataMapper
}; };
return new LiveUserData.Response(json, statusEnum, roomId, startTime); return new LiveUserData.Response(json, statusEnum, roomId, startTime);
} }
} }

View File

@@ -20,18 +20,18 @@
* 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.data.events.control; package io.github.jwdeveloper.tiktok.http.mappers;
import io.github.jwdeveloper.tiktok.annotations.EventMeta; import com.google.gson.JsonParser;
import io.github.jwdeveloper.tiktok.annotations.EventType; import io.github.jwdeveloper.tiktok.data.requests.SignServerResponse;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
public class SignServerResponseMapper {
public SignServerResponse map(String json) {
var parsedJson = JsonParser.parseString(json);
var jsonObject = parsedJson.getAsJsonObject();
/** var signUrl = jsonObject.get("signedUrl").getAsString();
* Triggered when client is connecting to live is successfully established. var userAgent = jsonObject.get("User-Agent").getAsString();
*/ return new SignServerResponse(signUrl, userAgent);
@EventMeta(eventType = EventType.Control) }
public class TikTokConnectingEvent extends TikTokLiveClientEvent
{
} }

View File

@@ -111,7 +111,6 @@ 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);

View File

@@ -24,29 +24,32 @@ 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.*; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.models.Picture; import io.github.jwdeveloper.tiktok.data.models.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.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.live.GiftsManager; import io.github.jwdeveloper.tiktok.live.GiftManager;
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.*; import java.util.HashMap;
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;
private final GiftsManager giftsManager; public TikTokGiftEventHandler(GiftManager giftManager, TikTokRoomInfo tikTokRoomInfo) {
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
@@ -58,40 +61,40 @@ public class TikTokGiftEventHandler {
public List<TikTokEvent> handleGift(WebcastGiftMessage currentMessage) { public List<TikTokEvent> handleGift(WebcastGiftMessage currentMessage) {
var userId = currentMessage.getUser().getId(); var userId = currentMessage.getUser().getId();
var currentType = GiftComboStateType.fromNumber(currentMessage.getSendType()); var currentType = GiftSendType.fromNumber(currentMessage.getSendType());
var containsPreviousMessage = giftsMessages.containsKey(userId); var containsPreviousMessage = giftsMessages.containsKey(userId);
//If gift is not streakable just return onGift event //If gift is not streakable just return onGift event
if (currentMessage.getGift().getType() != 1) { if (currentMessage.getGift().getType() != 1) {
var comboEvent = getGiftComboEvent(currentMessage, GiftComboStateType.Finished); var comboEvent = getGiftComboEvent(currentMessage, GiftSendType.Finished);
var giftEvent = getGiftEvent(currentMessage); var giftEvent = getGiftEvent(currentMessage);
return List.of(comboEvent, giftEvent); return List.of(comboEvent, giftEvent);
} }
if (!containsPreviousMessage) { if (!containsPreviousMessage) {
if (currentType == GiftComboStateType.Finished) { if (currentType == GiftSendType.Finished) {
return List.of(getGiftEvent(currentMessage)); return List.of(getGiftEvent(currentMessage));
} else { } else {
giftsMessages.put(userId, currentMessage); giftsMessages.put(userId, currentMessage);
return List.of(getGiftComboEvent(currentMessage, GiftComboStateType.Begin)); return List.of(getGiftComboEvent(currentMessage, GiftSendType.Begin));
} }
} }
var previousMessage = giftsMessages.get(userId); var previousMessage = giftsMessages.get(userId);
var previousType = GiftComboStateType.fromNumber(previousMessage.getSendType()); var previousType = GiftSendType.fromNumber(previousMessage.getSendType());
if (currentType == GiftComboStateType.Active && if (currentType == GiftSendType.Active &&
previousType == GiftComboStateType.Active) { previousType == GiftSendType.Active) {
giftsMessages.put(userId, currentMessage); giftsMessages.put(userId, currentMessage);
return List.of(getGiftComboEvent(currentMessage, GiftComboStateType.Active)); return List.of(getGiftComboEvent(currentMessage, GiftSendType.Active));
} }
if (currentType == GiftComboStateType.Finished && if (currentType == GiftSendType.Finished &&
previousType == GiftComboStateType.Active) { previousType == GiftSendType.Active) {
giftsMessages.clear(); giftsMessages.clear();
return List.of( return List.of(
getGiftComboEvent(currentMessage, GiftComboStateType.Finished), getGiftComboEvent(currentMessage, GiftSendType.Finished),
getGiftEvent(currentMessage)); getGiftEvent(currentMessage));
} }
@@ -104,45 +107,31 @@ public class TikTokGiftEventHandler {
return new TikTokGiftEvent(gift, tikTokRoomInfo.getHost(), message); return new TikTokGiftEvent(gift, tikTokRoomInfo.getHost(), message);
} }
private TikTokGiftEvent getGiftComboEvent(WebcastGiftMessage message, GiftComboStateType state) { private TikTokGiftEvent getGiftComboEvent(WebcastGiftMessage message, GiftSendType state) {
var gift = getGiftObject(message); var gift = getGiftObject(message);
return new TikTokGiftComboEvent(gift, tikTokRoomInfo.getHost(), message, state); return new TikTokGiftComboEvent(gift, tikTokRoomInfo.getHost(), message, state);
} }
private Gift getGiftObject(WebcastGiftMessage giftMessage) { private Gift getGiftObject(WebcastGiftMessage giftMessage) {
var giftId = (int) giftMessage.getGiftId(); var giftId = (int) giftMessage.getGiftId();
var gift = giftsManager.getById(giftId); var gift = giftManager.findById(giftId);
if (gift == Gift.UNDEFINED)
gift = giftsManager.getByName(giftMessage.getGift().getName());
if (gift == Gift.UNDEFINED) { if (gift == Gift.UNDEFINED) {
gift = new Gift(giftId, gift = giftManager.findByName(giftMessage.getGift().getName());
}
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 {

View File

@@ -22,17 +22,15 @@
*/ */
package io.github.jwdeveloper.tiktok.websocket; package io.github.jwdeveloper.tiktok.websocket;
import io.github.jwdeveloper.tiktok.*;
import io.github.jwdeveloper.tiktok.data.dto.ProxyData; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData; import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.data.settings.*;
import io.github.jwdeveloper.tiktok.exceptions.*;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import org.java_websocket.client.WebSocketClient; import org.java_websocket.client.WebSocketClient;
import javax.net.ssl.*;
import java.net.Proxy;
import java.security.cert.X509Certificate;
import java.util.HashMap; import java.util.HashMap;
public class TikTokWebSocketClient implements SocketClient { public class TikTokWebSocketClient implements SocketClient {
@@ -40,8 +38,6 @@ public class TikTokWebSocketClient implements SocketClient {
private final TikTokLiveMessageHandler messageHandler; private final TikTokLiveMessageHandler messageHandler;
private final TikTokLiveEventHandler tikTokEventHandler; private final TikTokLiveEventHandler tikTokEventHandler;
private WebSocketClient webSocketClient; private WebSocketClient webSocketClient;
private final TikTokWebSocketPingingTask pingingTask;
private boolean isConnected; private boolean isConnected;
public TikTokWebSocketClient( public TikTokWebSocketClient(
@@ -52,11 +48,11 @@ public class TikTokWebSocketClient implements SocketClient {
this.messageHandler = messageHandler; this.messageHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
isConnected = false; isConnected = false;
pingingTask = new TikTokWebSocketPingingTask();
} }
@Override @Override
public void start(LiveConnectionData.Response connectionData, LiveClient liveClient) { public void start(LiveConnectionData.Response connectionData, LiveClient liveClient)
{
if (isConnected) { if (isConnected) {
stop(); stop();
} }
@@ -72,68 +68,24 @@ public class TikTokWebSocketClient implements SocketClient {
tikTokEventHandler, tikTokEventHandler,
liveClient); liveClient);
// ProxyClientSettings proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); try
// if (proxyClientSettings.isEnabled()) {
// connectProxy(proxyClientSettings);
// else
connectDefault();
}
private void connectDefault() {
try {
webSocketClient.connect(); webSocketClient.connect();
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);
} }
} }
public void connectProxy(ProxyClientSettings proxySettings) {
try {
if (proxySettings.getType() == Proxy.Type.SOCKS) {
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);
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");
}
public boolean tryProxyConnection(ProxyClientSettings proxySettings, ProxyData proxyData) {
try {
webSocketClient.setProxy(new Proxy(proxySettings.getType(), proxyData.toSocketAddress()));
webSocketClient.connect();
return true;
} catch (Exception e) {
return false;
}
}
public void stop() { public void stop() {
if (isConnected && webSocketClient != null && webSocketClient.isOpen()) { if (isConnected && webSocketClient != null) {
webSocketClient.closeConnection(0, ""); webSocketClient.closeConnection(0, "");
pingingTask.stop();
} }
webSocketClient = null; webSocketClient = null;
isConnected = false; isConnected = false;

View File

@@ -23,18 +23,23 @@
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.*; import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException; import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
import io.github.jwdeveloper.tiktok.TikTokLiveMessageHandler;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.messages.webcast.*; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastPushFrame;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
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.*; import java.util.Map;
import java.util.Optional;
public class TikTokWebSocketListener extends WebSocketClient { public class TikTokWebSocketListener extends WebSocketClient {
@@ -81,12 +86,13 @@ public class TikTokWebSocketListener extends WebSocketClient {
pushFrameBuilder.setPayload(webcastResponse.getInternalExtBytes()); pushFrameBuilder.setPayload(webcastResponse.getInternalExtBytes());
if (isNotClosing()) if (isNotClosing())
{ {
this.send(pushFrameBuilder.build().toByteArray()); this.send(pushFrameBuilder.build().toByteArray());
} }
} }
messageHandler.handle(tikTokLiveClient, webcastResponse); messageHandler.handle(tikTokLiveClient, webcastResponse);
} }
@Override @Override
public void onOpen(ServerHandshake serverHandshake) { public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokConnectedEvent()); tikTokEventHandler.publish(tikTokLiveClient, new TikTokConnectedEvent());
@@ -95,10 +101,10 @@ public class TikTokWebSocketListener extends WebSocketClient {
} }
} }
@Override @Override
public void onClose(int code, String reason, boolean remote) { public void onClose(int i, String s, boolean b) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokDisconnectedEvent(reason)); tikTokEventHandler.publish(tikTokLiveClient, new TikTokDisconnectedEvent());
tikTokLiveClient.disconnect();
} }
@Override @Override
@@ -109,6 +115,8 @@ public class TikTokWebSocketListener extends WebSocketClient {
} }
} }
private Optional<WebcastPushFrame> getWebcastPushFrame(byte[] buffer) { private Optional<WebcastPushFrame> getWebcastPushFrame(byte[] buffer) {
try { try {
var websocketMessage = WebcastPushFrame.parseFrom(buffer); var websocketMessage = WebcastPushFrame.parseFrom(buffer);
@@ -133,8 +141,9 @@ public class TikTokWebSocketListener extends WebSocketClient {
return !isClosed() && !isClosing(); return !isClosed() && !isClosing();
} }
@Override @Override
public void onMessage(String s) { public void onMessage(String s) {
// System.err.println(s);
} }
} }

View File

@@ -1,31 +0,0 @@
package io.github.jwdeveloper.tiktok.websocket;
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.live.LiveClient;
public class TikTokWebSocketOfflineClient implements SocketClient {
private final TikTokLiveEventHandler handler;
private LiveClient liveClient;
public TikTokWebSocketOfflineClient(TikTokLiveEventHandler handler) {
this.handler = handler;
}
@Override
public void start(LiveConnectionData.Response webcastResponse, LiveClient tikTokLiveClient) {
liveClient = tikTokLiveClient;
handler.publish(liveClient, new TikTokConnectedEvent());
}
@Override
public void stop() {
if (liveClient == null) {
return;
}
handler.publish(liveClient, new TikTokDisconnectedEvent());
}
}

View File

@@ -1,50 +0,0 @@
package io.github.jwdeveloper.tiktok.websocket;
import org.java_websocket.WebSocket;
import java.util.Random;
public class TikTokWebSocketPingingTask
{
private Thread thread;
private boolean isRunning = false;
private final int MIN_TIMEOUT = 250;
private final int MAX_TIMEOUT = 500;
public void run(WebSocket webSocket)
{
stop();
thread = new Thread(() -> pingTask(webSocket));
isRunning = true;
thread.start();
}
public void stop()
{
if (thread != null)
thread.interrupt();
isRunning = false;
}
private void pingTask(WebSocket webSocket)
{
var random = new Random();
while (isRunning) {
try {
if (!webSocket.isOpen()) {
Thread.sleep(100);
continue;
}
webSocket.sendPing();
var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT;
Thread.sleep(timeout);
}
catch (Exception e) {
isRunning = false;
}
}
}
}

View File

@@ -22,7 +22,7 @@
*/ */
package io.github.jwdeveloper.tiktok.gifts; package io.github.jwdeveloper.tiktok.gifts;
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftOld; import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
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,6 +35,38 @@ 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());
}

View File

@@ -26,9 +26,8 @@ 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.GiftComboStateType; 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;
@@ -39,7 +38,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;
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -47,12 +46,13 @@ class TikTokGiftEventHandlerTest {
public static TikTokGiftEventHandler handler; public static TikTokGiftEventHandler handler;
@BeforeAll @BeforeAll
public void before() { public void before() {
var manager = new TikTokGiftsManager(List.of()); var manager = new TikTokGiftManager(Logger.getLogger("x"));
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.attachGift(new Gift(123, "example", 123, "image.webp")); manager.registerGift(123, "example", 123, new Picture("image.webp"));
handler = new TikTokGiftEventHandler(manager, info); handler = new TikTokGiftEventHandler(manager, info);
} }
@@ -98,9 +98,9 @@ class TikTokGiftEventHandlerTest {
Assertions.assertEquals(2, result3.size()); Assertions.assertEquals(2, result3.size());
var event3 = (TikTokGiftComboEvent) result3.get(0); var event3 = (TikTokGiftComboEvent) result3.get(0);
Assertions.assertEquals(GiftComboStateType.Begin, event1.getComboState()); Assertions.assertEquals(GiftSendType.Begin, event1.getComboState());
Assertions.assertEquals(GiftComboStateType.Active, event2.getComboState()); Assertions.assertEquals(GiftSendType.Active, event2.getComboState());
Assertions.assertEquals(GiftComboStateType.Finished, event3.getComboState()); Assertions.assertEquals(GiftSendType.Finished, event3.getComboState());
} }

View File

@@ -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.3.0-Release</version> <version>1.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -60,24 +60,6 @@
<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>

View File

@@ -22,34 +22,34 @@
*/ */
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import java.time.Duration;
import io.github.jwdeveloper.tiktok.extension.recorder.TikTokLiveRecorder; public class ChatMessageExample {
import io.github.jwdeveloper.tiktok.extension.recorder.impl.event.TikTokLiveRecorderStartedEvent;
public class RecorderExample {
public static void main(String[] args) { 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();
var roomData = TikTokLive.requests()
.fetchLiveData("X");
var gifts = TikTokLive.requests().fetchGiftsData();
var user = TikTokLive.requests()
.fetchLiveUserData("mark");
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.configure(clientSettings ->
{
clientSettings.setPrintToConsole(true);
clientSettings.getHttpSettings().setTimeout(Duration.ofSeconds(21));
})
.onComment((liveClient, event) ->
{
System.out.println("Chat message: " + event.getUser().getName() + " " + event.getText());
})
.onWebsocketUnhandledMessage((liveClient, event) ->
{
liveClient.getLogger().info(event.getMessage().getMethod());
}).buildAndConnect();
} }
} }

View File

@@ -1,76 +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.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();
}
}

View File

@@ -23,7 +23,10 @@
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.gifts.*; import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.data.models.gifts.Gift;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
public class CustomEventExample { public class CustomEventExample {
@@ -37,13 +40,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(ConnectionExample.TIKTOK_HOSTNAME)
.configure(clientSettings -> .configure(clientSettings ->
{ {
clientSettings.setPrintToConsole(true); clientSettings.setPrintToConsole(true);
}) })
.onGift((liveClient, event) -> .onGift((liveClient, event) ->
{ {
if (event.getGift().getDiamondCost() > 100) if (event.getGift().getDiamondCost() > 100)

View File

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

View File

@@ -1,67 +0,0 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.TikTokCommentEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokSubNotifyEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokSubscribeEvent;
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.social.TikTokFollowEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokJoinEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.data.models.gifts.GiftComboStateType;
import io.github.jwdeveloper.tiktok.live.LiveClient;
public class Events_And_Gifts_Testing_Example {
public static void main(String[] args) {
LiveClient client = TikTokLive.newClient(ConnectionExample.TIKTOK_HOSTNAME)
.configure(liveClientSettings ->
{
liveClientSettings.setOffline(true);
liveClientSettings.setPrintToConsole(true);
})
.onConnected((liveClient, event) ->
{
liveClient.getLogger().info("Connected");
})
.onDisconnected((liveClient, event) ->
{
liveClient.getLogger().info("Disconnected");
})
.onGiftCombo((liveClient, event) ->
{
liveClient.getLogger().info("New fake combo Gift: " + event.getGift());
})
.onGift((liveClient, event) ->
{
liveClient.getLogger().info("New fake Gift: " + event.getGift());
})
.build();
var gifts = TikTokLive.gifts();
var roseGift = gifts.getByName("Rose");
var fakeGift = TikTokGiftEvent.of(roseGift);
var fakeComboGift = TikTokGiftComboEvent.of(roseGift, 12, GiftComboStateType.Begin);
var fakeMessage = TikTokCommentEvent.of("Mark", "Hello world");
var fakeSubscriber = TikTokSubscribeEvent.of("Mark");
var fakeFollow = TikTokFollowEvent.of("Mark");
var fakeLike = TikTokLikeEvent.of("Mark", 12);
var fakeJoin = TikTokJoinEvent.of("Mark");
client.connect();
client.publishEvent(fakeGift);
client.publishEvent(fakeComboGift);
client.publishEvent(fakeMessage);
client.publishEvent(fakeSubscriber);
client.publishEvent(fakeFollow);
client.publishEvent(fakeLike);
client.publishEvent(fakeJoin);
client.disconnect();
}
}

View File

@@ -1,63 +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.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);
}
}

View File

@@ -48,7 +48,7 @@ public class ListenerExample
showLogo(); showLogo();
CustomListener customListener = new CustomListener(); CustomListener customListener = new CustomListener();
TikTokLive.newClient(ConnectionExample.TIKTOK_HOSTNAME) TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.addListener(customListener) .addListener(customListener)
.buildAndConnect(); .buildAndConnect();
System.in.read(); System.in.read();
@@ -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 @TikTokEventObserver annotation * - must have @TikTokEventHandler 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().getName()) { var message = switch (event.getGift()) {
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);

View File

@@ -1,52 +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 java.net.Proxy;
public class ProxyExample {
public static void main(String[] args) throws Exception {
TikTokLive.newClient(ConnectionExample.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();
}
}

View File

@@ -23,27 +23,35 @@
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.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.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration; import java.time.Duration;
import java.util.HashMap;
import java.util.logging.Level; import java.util.logging.Level;
public class ConnectionExample { public class SimpleExample {
public static String TIKTOK_HOSTNAME = "kvadromama_marina1"; public static String TIKTOK_HOSTNAME = "dash4214";
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException, InterruptedException {
showLogo(); showLogo();
var gifts = TikTokLive.gifts();
TikTokLive.newClient(ConnectionExample.TIKTOK_HOSTNAME) TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.configure(clientSettings -> .configure(clientSettings ->
{ {
clientSettings.setHostName(ConnectionExample.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.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
@@ -85,10 +93,10 @@ public class ConnectionExample {
}) })
.onGift((liveClient, event) -> .onGift((liveClient, event) ->
{ {
switch (event.getGift().getName()) { switch (event.getGift()) {
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());
} }

501
README.md
View File

@@ -29,7 +29,8 @@ 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>
@@ -39,7 +40,6 @@ 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,7 +49,6 @@ 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)
@@ -70,7 +69,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.3.0-Release</version> <version>1.0.15-Release</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -87,7 +86,7 @@ dependencyResolutionManagement {
} }
dependencies { dependencies {
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.1.0-Release' implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.0.15-Release'
} }
``` ```
@@ -166,79 +165,37 @@ TikTokLive.newClient("bangbetmenygy")
## Events
**Control**: **Control**:
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
- [onConnected](#onconnected-tiktokconnectedevent) - [onConnected](#onconnected-tiktokconnectedevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent) - [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
**Message**: **Message**:
- [onEvent](#onevent-tiktokevent) - [onEvent](#onevent-tiktokevent)
- [onEvent](#onevent-tiktokevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onGift](#ongift-tiktokgiftevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent) - [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onQuestion](#onquestion-tiktokquestionevent) - [onQuestion](#onquestion-tiktokquestionevent)
- [onShare](#onshare-tiktokshareevent) - [onFollow](#onfollow-tiktokfollowevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onJoin](#onjoin-tiktokjoinevent)
- [onLike](#onlike-tiktoklikeevent) - [onLike](#onlike-tiktoklikeevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onShare](#onshare-tiktokshareevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onGift](#ongift-tiktokgiftevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent) - [onLivePaused](#onlivepaused-tiktoklivepausedevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onJoin](#onjoin-tiktokjoinevent)
**Debug**: **Debug**:
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent) - [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onHttpResponse](#onhttpresponse-tiktokhttpresponseevent) - [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [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)
@@ -280,15 +237,32 @@ TikTokLive.newClient("host-name")
<br> <br>
## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java) ## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
Base class for all events
```java ```java
TikTokLive.newClient("host-name") TikTokLive.newClient("host-name")
.onEvent((liveClient, event) -> .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) ->
{ {
}) })
@@ -318,15 +292,91 @@ 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) ->
{
})
.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) ->
{ {
}) })
@@ -340,8 +390,6 @@ 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")
@@ -354,6 +402,73 @@ 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)
@@ -377,192 +492,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>
## 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>
## 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) ->
{
})
.buildAndConnect();
```
<br>
## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLiveUnpaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## 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>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## 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) ->
{ {
}) })
@@ -590,13 +528,30 @@ 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) ## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
```java ```java
TikTokLive.newClient("host-name") TikTokLive.newClient("host-name")
.onWebsocketResponse((liveClient, event) -> .onLiveUnpaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
{ {
}) })
@@ -626,17 +581,17 @@ TikTokLive.newClient("host-name")
<br> <br>
## onHttpResponse [TikTokHttpResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokHttpResponseEvent.java) ## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
```java ```java
TikTokLive.newClient("host-name") TikTokLive.newClient("host-name")
.onHttpResponse((liveClient, event) -> .onWebsocketResponse((liveClient, event) ->
{ {
}) })
.buildAndConnect(); .buildAndConnect();
``` ```
@@ -646,37 +601,24 @@ 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 TikTok sends data. Data incoming as protobuf message. Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
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
@@ -709,24 +651,24 @@ public static void main(String[] args) throws IOException {
public static class CustomListener implements TikTokEventListener { public static class CustomListener implements TikTokEventListener {
@TikTokEventObserver @TikTokEventHandler
public void onLike(LiveClient liveClient, TikTokLikeEvent event) { public void onLike(LiveClient liveClient, TikTokLikeEvent event) {
System.out.println(event.toString()); System.out.println(event.toString());
} }
@TikTokEventObserver @TikTokEventHandler
public void onError(LiveClient liveClient, TikTokErrorEvent event) { public void onError(LiveClient liveClient, TikTokErrorEvent event) {
// event.getException().printStackTrace(); // event.getException().printStackTrace();
} }
@TikTokEventObserver @TikTokEventHandler
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);
} }
@TikTokEventObserver @TikTokEventHandler
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 :)";
@@ -751,7 +693,4 @@ 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>.

View File

@@ -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.3.0-Release</version> <version>1.0.14-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-ReadmeGenerator</artifactId> <artifactId>Tools</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>

View File

@@ -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;

View File

@@ -25,9 +25,9 @@ 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.TikTokLiveMessageHandler;
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient; import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
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 io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.LiveClientMock; import io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks.LiveClientMock;
@@ -86,15 +86,17 @@ public class TikTokMockBuilder extends TikTokLiveClientBuilder {
var tiktokRoomInfo = new TikTokRoomInfo(); var tiktokRoomInfo = new TikTokRoomInfo();
tiktokRoomInfo.setHostName(clientSettings.getHostName()); tiktokRoomInfo.setHostName(clientSettings.getHostName());
var listenerManager = new TikTokListenersManager(listeners, eventHandler); var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
var mapper = createMapper(new TikTokGiftsManager(List.of()), tiktokRoomInfo); var giftManager = new TikTokGiftManager(logger);
var handler = new TikTokLiveMessageHandler(eventHandler, mapper); var mapper = createMapper(giftManager, tiktokRoomInfo);
var handler = new TikTokLiveMessageHandler(tikTokEventHandler, mapper);
var webSocketClient = new WebsocketClientMock(logger, responses, handler); var webSocketClient = new WebsocketClientMock(logger, responses, handler);
return new LiveClientMock(tiktokRoomInfo, return new LiveClientMock(tiktokRoomInfo,
new TikTokLiveHttpClient(), new TikTokLiveHttpClient(),
webSocketClient, webSocketClient,
eventHandler, giftManager,
tikTokEventHandler,
clientSettings, clientSettings,
listenerManager, listenerManager,
logger); logger);

View File

@@ -25,13 +25,12 @@ package io.github.jwdeveloper.tiktok.tools.tester.mockClient.mocks;
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; 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.TikTokLiveEventHandler;
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient; import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
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 {
@@ -42,15 +41,16 @@ public class LiveClientMock extends TikTokLiveClient {
TikTokRoomInfo tikTokLiveMeta, TikTokRoomInfo tikTokLiveMeta,
TikTokLiveHttpClient httpClient, TikTokLiveHttpClient httpClient,
WebsocketClientMock webSocketClient, WebsocketClientMock webSocketClient,
TikTokGiftManager tikTokGiftManager,
TikTokLiveEventHandler tikTokEventHandler, TikTokLiveEventHandler tikTokEventHandler,
LiveClientSettings clientSettings, LiveClientSettings clientSettings,
TikTokListenersManager listenersManager, TikTokListenersManager listenersManager,
Logger logger) { Logger logger) {
super( super(
new TikTokGiftsManager(List.of()),
tikTokLiveMeta, tikTokLiveMeta,
httpClient, httpClient,
webSocketClient, webSocketClient,
tikTokGiftManager,
tikTokEventHandler, tikTokEventHandler,
clientSettings, clientSettings,
listenersManager, listenersManager,

View File

@@ -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.3.0-Release</version> <version>1.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>Tools-EventsWebViewer</artifactId> <artifactId>Tools-EventsWebViewer</artifactId>

View File

@@ -22,6 +22,11 @@
*/ */
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.data.models.gifts.Gift;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkLayerMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMessage;
import io.github.jwdeveloper.tiktok.tools.TikTokLiveTools; import io.github.jwdeveloper.tiktok.tools.TikTokLiveTools;
import java.io.IOException; import java.io.IOException;

View File

@@ -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.3.0-Release</version> <version>1.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -23,6 +23,12 @@
</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>
@@ -35,11 +41,5 @@
<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>

View File

@@ -41,12 +41,11 @@ public class CodeExample {
TikTokLive.newClient("bangbetmenygy") TikTokLive.newClient("bangbetmenygy")
.onGift((liveClient, event) -> .onGift((liveClient, event) ->
{ {
String message = switch (event.getGift().getName()) String message = switch (event.getGift()) {
{ case ROSE -> "ROSE!";
case "Rose" -> "ROSE!"; case GG -> "GOOD GAME";
case "Good game" -> "GOOD GAME"; case TIKTOK -> "Ye";
case "Ye" -> "Ye"; case CORGI -> "Nice gift";
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);
@@ -98,10 +97,6 @@ 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>

View File

@@ -22,7 +22,8 @@
*/ */
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;

View File

@@ -27,7 +27,8 @@ 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;

View File

@@ -22,6 +22,7 @@
*/ */
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;

View File

@@ -21,6 +21,10 @@
* 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 {

View File

@@ -126,6 +126,7 @@ TikTokLive.newClient("bangbetmenygy")
{ {
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
@@ -151,73 +152,35 @@ TikTokLive.newClient("bangbetmenygy")
**Control**: **Control**:
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
- [onConnected](#onconnected-tiktokconnectedevent) - [onConnected](#onconnected-tiktokconnectedevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent) - [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
**Message**: **Message**:
- [onEvent](#onevent-tiktokevent) - [onEvent](#onevent-tiktokevent)
- [onEvent](#onevent-tiktokevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onGift](#ongift-tiktokgiftevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent) - [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onQuestion](#onquestion-tiktokquestionevent) - [onQuestion](#onquestion-tiktokquestionevent)
- [onShare](#onshare-tiktokshareevent) - [onFollow](#onfollow-tiktokfollowevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onJoin](#onjoin-tiktokjoinevent)
- [onLike](#onlike-tiktoklikeevent) - [onLike](#onlike-tiktoklikeevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onShare](#onshare-tiktokshareevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onGift](#ongift-tiktokgiftevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent) - [onLivePaused](#onlivepaused-tiktoklivepausedevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onJoin](#onjoin-tiktokjoinevent)
**Debug**: **Debug**:
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent) - [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onHttpResponse](#onhttpresponse-tiktokhttpresponseevent) - [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [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)
@@ -259,15 +222,32 @@ TikTokLive.newClient("host-name")
<br> <br>
## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java) ## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
Base class for all events
```java ```java
TikTokLive.newClient("host-name") TikTokLive.newClient("host-name")
.onEvent((liveClient, event) -> .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) ->
{ {
}) })
@@ -295,65 +275,6 @@ TikTokLive.newClient("host-name")
<br>
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
Triggered every time a new chat comment arrives.
```java
TikTokLive.newClient("host-name")
.onComment((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## 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
TikTokLive.newClient("host-name")
.onRoomInfo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
Triggered when user sends gifts that has
no combo (most of expensive gifts)
or if combo has finished
```java
TikTokLive.newClient("host-name")
.onGift((liveClient, event) ->
{
})
.buildAndConnect();
```
<br> <br>
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java) ## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
@@ -373,73 +294,6 @@ TikTokLive.newClient("host-name")
<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>
## 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> <br>
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java) ## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
@@ -461,68 +315,15 @@ TikTokLive.newClient("host-name")
<br> <br>
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.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 shares the stream. Based on social event. Triggers when a user follows the streamer. Based on social event.
```java ```java
TikTokLive.newClient("host-name") TikTokLive.newClient("host-name")
.onShare((liveClient, event) -> .onFollow((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLiveUnpaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## 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>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
{ {
}) })
@@ -550,6 +351,149 @@ TikTokLive.newClient("host-name")
<br>
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
```java
TikTokLive.newClient("host-name")
.onLiveEnded((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
```java
TikTokLive.newClient("host-name")
.onRoomInfo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
Triggers when a user shares the stream. Based on social event.
```java
TikTokLive.newClient("host-name")
.onShare((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onGiftCombo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
Triggered every time a subscriber sends an emote (sticker).
```java
TikTokLive.newClient("host-name")
.onEmote((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
Triggered when user sends gifts that has
no combo (most of expensive gifts)
or if combo has finished
```java
TikTokLive.newClient("host-name")
.onGift((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
Triggered every time a new chat comment arrives.
```java
TikTokLive.newClient("host-name")
.onComment((liveClient, event) ->
{
})
.buildAndConnect();
```
<br> <br>
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java) ## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
@@ -569,13 +513,30 @@ 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) ## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
```java ```java
TikTokLive.newClient("host-name") TikTokLive.newClient("host-name")
.onWebsocketResponse((liveClient, event) -> .onLiveUnpaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
{ {
}) })
@@ -605,13 +566,13 @@ TikTokLive.newClient("host-name")
<br> <br>
## onHttpResponse [TikTokHttpResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokHttpResponseEvent.java) ## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
```java ```java
TikTokLive.newClient("host-name") TikTokLive.newClient("host-name")
.onHttpResponse((liveClient, event) -> .onWebsocketResponse((liveClient, event) ->
{ {
}) })
@@ -625,8 +586,7 @@ 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 TikTok sends data. Data incoming as protobuf message. Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
You can deserialize the binary object depending on the use case.
```java ```java
@@ -642,17 +602,6 @@ TikTokLive.newClient("host-name")
{{for item of data }}
{{if item is 2}}
my name is {{item.name}}
{{else}}
{{end}}
{{end}}
<br> <br>
## Listeners ## Listeners
@@ -679,7 +628,7 @@ my name is {{item.name}}
/** /**
* *
* Method in TikTokEventListener should meet 4 requirements to be detected * Method in TikTokEventListener should meet 4 requirements to be detected
* - must have @TikTokEventObserver annotation * - must have @TikTokEventHandler 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
@@ -687,24 +636,24 @@ my name is {{item.name}}
public static class CustomListener implements TikTokEventListener { public static class CustomListener implements TikTokEventListener {
@TikTokEventObserver @TikTokEventHandler
public void onLike(LiveClient liveClient, TikTokLikeEvent event) { public void onLike(LiveClient liveClient, TikTokLikeEvent event) {
System.out.println(event.toString()); System.out.println(event.toString());
} }
@TikTokEventObserver @TikTokEventHandler
public void onError(LiveClient liveClient, TikTokErrorEvent event) { public void onError(LiveClient liveClient, TikTokErrorEvent event) {
// event.getException().printStackTrace(); // event.getException().printStackTrace();
} }
@TikTokEventObserver @TikTokEventHandler
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);
} }
@TikTokEventObserver @TikTokEventHandler
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 :)";
@@ -717,7 +666,7 @@ my name is {{item.name}}
liveClient.getLogger().info(message); liveClient.getLogger().info(message);
} }
@TikTokEventObserver @TikTokEventHandler
public void onAnyEvent(LiveClient liveClient, TikTokEvent event) { public void onAnyEvent(LiveClient liveClient, TikTokEvent event) {
liveClient.getLogger().info(event.getClass().getSimpleName()); liveClient.getLogger().info(event.getClass().getSimpleName());
} }

View File

@@ -5,11 +5,40 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>1.3.0-Release</version> <version>1.0.14-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>extension-recorder</artifactId>
<artifactId>Tools</artifactId>
<dependencies> <dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.13.0</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
<dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>API</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version> <!-- Check for the latest version -->
</dependency>
<dependency> <dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>Client</artifactId> <artifactId>Client</artifactId>
@@ -17,10 +46,9 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>org.jsoup</groupId>
<artifactId>API</artifactId> <artifactId>jsoup</artifactId>
<version>${project.version}</version> <version>1.14.3</version>
<scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.events_generator.EventGeneratorSettings;
import io.github.jwdeveloper.tiktok.events_generator.EventsGenerator;
import java.io.IOException;
public class EventsGeneratorRun {
private static boolean lock = false;
//Run objects
public static void main(String[] args) throws IOException {
if(lock)
{
return;
}
//generatesObjects()
// generateEventsMessages();
}
private static void generatesEventsObjects() throws IOException {
var settings = new EventGeneratorSettings();
settings.setTikTokEvent(false);
settings.setInputDictionary("C:\\Users\\ja\\RiderProjects\\TikTokLiveSharp\\TikTokLiveSharp\\Events\\Objects");
settings.setOutputDictionary("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\events\\objects");
var generator = new EventsGenerator();
generator.compile(settings);
}
private static void generateEventsMessages() throws IOException {
var settings = new EventGeneratorSettings();
settings.setTikTokEvent(true);
settings.setPrefix("TikTok");
settings.setEndFix("Event");
settings.setInputDictionary("C:\\Users\\ja\\RiderProjects\\TikTokLiveSharp\\TikTokLiveSharp\\Events\\Messages");
settings.setOutputDictionary("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\events\\messages");
var generator = new EventsGenerator();
generator.compile(settings);
}
}

View File

@@ -20,21 +20,23 @@
* 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.extension.collector; package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.extension.collector.api.LiveDataCollector; import io.github.jwdeveloper.tiktok.events_generator.EventGeneratorSettings;
import io.github.jwdeveloper.tiktok.extension.collector.api.data.LiveDataCollectorSettings; import io.github.jwdeveloper.tiktok.intefacee.EventsInterfaceGenerator;
import io.github.jwdeveloper.tiktok.extension.collector.impl.TikTokLiveDataCollector;
import java.util.function.Consumer; import java.io.IOException;
public class TikTokLiveCollector public class EventsInterfaceGeneratorRun
{ {
public static void main(String[] args) throws IOException {
public static TikTokLiveDataCollector use(Consumer<LiveDataCollectorSettings> consumer) var settings = new EventGeneratorSettings();
{ settings.setTikTokEvent(true);
var settings = new LiveDataCollectorSettings(); settings.setPrefix("TikTok");
consumer.accept(settings); settings.setEndFix("Event");
return new TikTokLiveDataCollector(settings); settings.setInputDictionary("C:\\Users\\ja\\RiderProjects\\TikTokLiveSharp\\TikTokLiveSharp\\Events\\Messages");
settings.setOutputDictionary("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java\\io\\github\\jwdeveloper\\tiktok\\events\\messages");
var generator = new EventsInterfaceGenerator();
generator.compile(settings);
} }
} }

View File

@@ -20,29 +20,31 @@
* 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.extension.collector.api.data; package io.github.jwdeveloper.tiktok.events_generator;
import lombok.Data; import lombok.Data;
import java.util.function.Consumer; import java.util.ArrayList;
import java.util.List;
@Data @Data
public class LiveDataCollectorSettings { public class CSharpClassInfo
{
private String className;
private List<FieldInfo> fields = new ArrayList<>();
private List<ConstructorInfo> constructors = new ArrayList<>();
private String connectionUrl; public void addField(String type, String fields)
{
private String databaseName; this.fields.add(new FieldInfo(type,fields));
private String sessionTag;
public void setConnectionUrl(String connectionUrl) {
this.connectionUrl = connectionUrl;
} }
public void setConnectionUrl(Consumer<MongoDBConnectionStringBuilder> consumer) { public void addConstructor(List<FieldInfo> arguments)
var builder = new MongoDBConnectionStringBuilder(); {
consumer.accept(builder); this.constructors.add(new ConstructorInfo(arguments));
connectionUrl = builder.build();
} }
public record FieldInfo(String type, String name){};
public record ConstructorInfo(List<FieldInfo> arguemtns){};
} }

View File

@@ -0,0 +1,124 @@
/*
* 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.events_generator;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CSharpClassParser {
private CSharpClassInfo classInfo;
public CSharpClassInfo parse(Path filePath) throws IOException {
classInfo = new CSharpClassInfo();
List<String> lines = Files.readAllLines(filePath);
String content = String.join("\n", lines);
parseClassName(content);
parseFields(content);
parseConstructors(content);
return classInfo;
}
private void parseClassName(String content) {
Pattern pattern = Pattern.compile("\\b(?:sealed )?class\\s+(\\w+)");
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
classInfo.setClassName(matcher.group(1));
}
}
private void parseFields(String content) {
Pattern pattern = Pattern.compile("\\b(public|private|protected)\\s+(readonly\\s+)?(\\w+\\.?\\w*)\\s+(\\w+);");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
var typeName = mapTypeToJava(matcher.group(3));
var name = lowerCaseFirstLetter(matcher.group(4));
classInfo.addField(typeName, name);
}
}
private void parseConstructors(String content) {
Pattern pattern = Pattern.compile("\\b(public|private|protected|internal)\\s+" + classInfo.getClassName() + "\\s*\\(([^)]*)\\)");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
List<CSharpClassInfo.FieldInfo> args = new ArrayList<>();
String[] arguments = matcher.group(2).split(",");
for (String argument : arguments) {
if (argument.trim().length() > 0) {
String[] parts = argument.trim().split("\\s+");
if (parts.length != 2) {
args.add(new CSharpClassInfo.FieldInfo("Object", "error"));
continue;
}
var typeName = mapTypeToJava(parts[0]);
var name = lowerCaseFirstLetter(parts[1]);
args.add(new CSharpClassInfo.FieldInfo(typeName, name));
}
}
classInfo.addConstructor(args);
}
}
public String mapTypeToJava(String type) {
if (type.equals("string")) {
return "String";
}
if (type.equals("uint")) {
return "Integer";
}
if (type.equals("int")) {
return "Integer";
}
if (type.equals("ulong")) {
return "Long";
}
if (type.equals("bool")) {
return "Boolean";
}
if (type.contains("Models.Protobuf.Objects")) {
return type.replace("Models.Protobuf.Objects", "io.github.jwdeveloper.tiktok.messages");
}
if(type.contains("Objects."))
{
return type.replace("Objects.","io.github.jwdeveloper.tiktok.events.objects.");
}
return type;
}
public static String lowerCaseFirstLetter(String str) {
if (str == null || str.isEmpty()) {
return str; // Return original string if it is empty or null
}
return Character.toLowerCase(str.charAt(0)) + str.substring(1);
}
}

View File

@@ -20,20 +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.extension.recorder.impl.data; package io.github.jwdeveloper.tiktok.events_generator;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data @Data
@AllArgsConstructor public class EventGeneratorSettings
public class DownloadData { {
private String inputDictionary;
private String downloadLiveUrl; private String outputDictionary;
private List<String> ignoredFiles = new ArrayList<>();
private String sessionId; private String prefix;
public String getFullUrl() { private String endFix;
return downloadLiveUrl + "&_webnoredir=1&session_id=" + sessionId;
private boolean isTikTokEvent;
public void addIgnoredClass(String name)
{
ignoredFiles.add(name);
} }
} }

View File

@@ -0,0 +1,79 @@
/*
* 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.events_generator;
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import java.io.File;
import java.io.IOException;
public class EventsGenerator
{
public void compile(EventGeneratorSettings settings) throws IOException {
var files = FilesUtility.getFiles(settings.getInputDictionary());
var packageName = convertToPackageName(settings.getOutputDictionary());
for(var file : files)
{
var fileName = file.getFileName().toString();
if(settings.getIgnoredFiles().contains(fileName))
{
continue;
}
if(fileName.contains("meta"))
{
continue;
}
var parser = new CSharpClassParser();
var cSharpClass =parser.parse(file);
var name = settings.getPrefix()+cSharpClass.getClassName()+settings.getEndFix();
cSharpClass.setClassName(name);
var javaClassGenerator = new JavaClassGenerator();
var result =javaClassGenerator.generate(cSharpClass, packageName,settings);
System.out.println(result);
var path = settings.getOutputDictionary()+ File.separator+cSharpClass.getClassName()+".java";
FilesUtility.saveFile(path, result);
}
}
public static String convertToPackageName(String path) {
String marker = "src\\main\\java\\";
int index = path.indexOf(marker);
if (index != -1) {
String packagePath = path.substring(index + marker.length());
return packagePath.replace('\\', '.');
}
return null;
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.events_generator;
import com.squareup.javapoet.*;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import lombok.Getter;
import javax.lang.model.element.Modifier;
public class JavaClassGenerator {
public String generate(CSharpClassInfo cSharpClassInfo, String packageName, EventGeneratorSettings settings) {
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(cSharpClassInfo.getClassName())
.addAnnotation(Getter.class);
if (settings.isTikTokEvent()) {
classBuilder.superclass(TikTokEvent.class);
}
classBuilder.addModifiers(Modifier.PUBLIC);
// Generate fields
for (var field : cSharpClassInfo.getFields()) {
FieldSpec fieldSpec = FieldSpec.builder(ClassName.bestGuess(field.type()), field.name(), Modifier.PRIVATE).build();
classBuilder.addField(fieldSpec);
}
// Generate constructors
for (var constructor : cSharpClassInfo.getConstructors()) {
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder();
if(settings.isTikTokEvent())
{
constructorBuilder.addStatement("super(msg.getHeader());") ;
}
constructorBuilder.addModifiers(Modifier.PUBLIC);
for (var arg : constructor.arguemtns()) {
constructorBuilder.addParameter(ClassName.bestGuess(arg.type()), arg.name());
}
classBuilder.addMethod(constructorBuilder.build());
}
// Generate Java class
TypeSpec javaClass = classBuilder.build();
var result = JavaFile.builder(packageName, javaClass).build();
return result.toString();
}
}

View File

@@ -0,0 +1,170 @@
/*
* 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 com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import io.github.jwdeveloper.tiktok.TikTokLive;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftDto;
import lombok.Getter;
import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class GenerateGiftsEnum {
public static void main(String args[]) throws IOException {
TikTokLive.newClient("X")
.configure(liveClientSettings ->
{
var httpSetting = liveClientSettings.getHttpSettings();
httpSetting.setTimeout(Duration.ofSeconds(12));
});
var downloader = new GiftsDownloader();
var gifts = downloader.getGiftsFromFile();
for (var link : gifts) {
System.out.println(link.getImage());
}
var groupedByName = gifts.stream().collect(Collectors.groupingBy(GiftDto::getName));
System.out.println("Total gifts" + gifts.size());
var result = generate(groupedByName);
result.writeTo(new File("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\API\\src\\main\\java"));
System.out.println("DONE");
}
public static JavaFile generate(Map<String, List<GiftDto>> giftInfoMap) {
var enumBuilder = TypeSpec.enumBuilder("Gift")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Getter.class)
.addField(int.class, "id", Modifier.PRIVATE, Modifier.FINAL)
.addField(String.class, "name", Modifier.PRIVATE, Modifier.FINAL)
.addField(int.class, "diamondCost", Modifier.PRIVATE, Modifier.FINAL)
.addField(Picture.class, "picture", Modifier.PRIVATE, Modifier.FINAL);
var constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PRIVATE)
.addParameter(int.class, "id")
.addParameter(String.class, "name")
.addParameter(int.class, "diamondCost")
.addParameter(String.class, "pictureLink")
.addStatement("this.id = id")
.addStatement("this.name = name")
.addStatement("this.diamondCost = diamondCost")
.addStatement("this.picture = new Picture(pictureLink)")
.build();
var inRangeMethod = MethodSpec.methodBuilder("hasDiamondCostRange")
.addModifiers(Modifier.PUBLIC)
.addParameter(int.class, "minimalCost")
.addParameter(int.class, "maximalCost")
.addStatement(" return diamondCost >= minimalCost && diamondCost <= maximalCost")
.returns(boolean.class);
var hasCostMethod = MethodSpec.methodBuilder("hasDiamondCost")
.addModifiers(Modifier.PUBLIC)
.addParameter(int.class, "cost")
.addStatement(" return diamondCost == cost")
.returns(boolean.class);
enumBuilder.addMethod(inRangeMethod.build());
enumBuilder.addMethod(hasCostMethod.build());
enumBuilder.addMethod(constructor);
enumBuilder.addEnumConstant("UNDEFINED", addGift(-1, "undefined", -1, ""));
for (var giftInfo : giftInfoMap.entrySet()) {
var name = giftInfo.getKey().replace(" ", "_")
.replace("", "_")
.replace("+", "_")
.replace("'", "_")
.replace(".", "_")
.replace("-", "_")
.replace("&", "_")
.replace("!", "_")
.toUpperCase();
boolean startsWithNumber = name.matches("^[0-9].*");
if (startsWithNumber) {
name = "_" + name;
}
if (isNumeric(name)) {
name = "_" + name;
}
if (name.equalsIgnoreCase("")) {
continue;
}
var contier = 1;
for (var value : giftInfo.getValue()) {
var enumName = name;
if (contier > 1) {
enumName += "_" + value.getId();
}
enumBuilder.addEnumConstant(enumName, addGift(value.getId(), value.getName(), value.getDiamondCost(), value.getImage()));
contier++;
}
}
var output = JavaFile.builder("io.github.jwdeveloper.tiktok.data.models.gifts", enumBuilder.build());
output.addFileComment("This enum is generated");
return output.build();
}
public static boolean isNumeric(String str) {
try {
Double.parseDouble(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
public static TypeSpec addGift(int id, String name, int diamond, String picture) {
return TypeSpec.anonymousClassBuilder(
"$L, $S, $L, $S",
id,
name,
diamond,
picture)
.build();
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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 com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftDto;
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftExtraJson;
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftOfficialJson;
import io.github.jwdeveloper.tiktok.gifts.downloader.GiftScraperJson;
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class GiftsDownloader {
public static void main(String[] run) {
var gifts = new GiftsDownloader().getGifts();
for(var gift : gifts)
{
System.out.println(gift.toString());
}
}
public List<GiftDto> getGiftsFromFile() {
var version = "";
var content = FilesUtility.loadFileContent("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\output.json");
Type mapType = new TypeToken<Map<Integer, GiftDto>>() {
}.getType();
var mapper = new Gson().fromJson(content, mapType);
var gifts = (Map<Integer, GiftDto>) mapper;
return gifts.values().stream().toList();
}
public List<GiftDto> getGifts() {
var scraper = new GiftScraperJson();
System.out.println("Downlooading Scraped Gifts");
var scraperGifts = scraper.run();
System.out.println("Scraped Gifts: " + scraperGifts.size());
System.out.println("Downlooading Official Gifts");
var officalGift = new GiftOfficialJson();
var officialGifts = officalGift.run();
System.out.println("Official Gifts: " + officialGifts.size());
System.out.println("Downlooading GiftExtraJson Gifts");
var extraGiftsJson = new GiftExtraJson();
var extraGifts = extraGiftsJson.run();
System.out.println("GiftExtraJson Gifts: " + extraGifts.size());
var outputHashMap = new TreeMap<Integer, GiftDto>();
for (var gift : scraperGifts) {
outputHashMap.put(gift.getId(), gift);
}
for (var gift : officialGifts) {
outputHashMap.put(gift.getId(), gift);
}
for (var gift : extraGifts) {
outputHashMap.put(gift.getId(), gift);
}
var gson = new GsonBuilder().setPrettyPrinting()
.create();
var json = gson.toJson(outputHashMap);
FilesUtility.saveFile("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\output_1_0_15.json", json);
System.out.println("Gifts saved to file!");
return outputHashMap.values().stream().toList();
}
}

View File

@@ -20,11 +20,15 @@
* 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.extension.collector.api; package io.github.jwdeveloper.tiktok.gifts.downloader;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener; import lombok.Data;
public interface LiveDataCollector extends TikTokEventListener @Data
public class GiftDto
{ {
private int id;
private String name;
private int diamondCost;
private String image;
} }

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok.gifts.downloader;
import com.google.gson.*;
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import java.util.ArrayList;
import java.util.List;
public class GiftExtraJson
{
public static void main(String[] args) {
var reuslt = new GiftExtraJson().run();
System.out.println(reuslt.size());
}
public List<GiftDto> run() {
var output = new ArrayList<GiftDto>();
var jsonGifts = getJsonGifts();
for (var jsonElement : jsonGifts) {
var gift = getGift(jsonElement);
output.add(gift);
}
return output;
}
private GiftDto getGift(JsonElement jsonElement) {
var id = jsonElement.getAsJsonObject().get("id").getAsInt();
var name = jsonElement.getAsJsonObject().get("name").getAsString();
var diamondCost = jsonElement.getAsJsonObject().get("diamondCost").getAsInt();
var image = jsonElement.getAsJsonObject().get("image").getAsString();
var gift = new GiftDto();
gift.setId(id);
gift.setName(name);
gift.setDiamondCost(diamondCost);
gift.setImage(image);
return gift;
}
public static JsonArray getJsonGifts() {
var extraGifts =FilesUtility.loadFileContent("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\extra_gifts.json");
JsonElement jsonElement = JsonParser.parseString(extraGifts);
return jsonElement.getAsJsonArray();
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok.gifts.downloader;
import io.github.jwdeveloper.tiktok.TikTokLive;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.utils.FilesUtility;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
public class GiftOfficialJson {
public static void main(String[] args) {
new GiftOfficialJson().run();
}
public List<GiftDto> run() {
try {
var dtf = DateTimeFormatter.ofPattern("dd/MM/yyyy");
var now = LocalDateTime.now();
var date = now.format(dtf).replace("/", "_");
var fileName = "official_" + date + ".json";
var httpClient = TikTokLive.requests();
var giftsInfo = httpClient.fetchGiftsData();
FilesUtility.saveFile("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\gifts\\official\\" + fileName, giftsInfo.getJson());
return giftsInfo.getGifts().stream().map(e ->
{
var gift = new GiftDto();
gift.setId(e.getId());
gift.setImage(e.getImage());
gift.setName(e.getName());
gift.setDiamondCost(e.getDiamondCost());
return gift;
}).collect(Collectors.toList());
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch giftTokens from WebCast, see stacktrace for more info.", e);
}
}
}

View File

@@ -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.gifts.downloader;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.util.ArrayList;
import java.util.List;
public class GiftScraperJson {
private final String baseUrl = "https://streamdps.com/tiktok-widgets/gifts/";
public static void main(String[] args) {
var instance = new GiftScraperJson();
instance.run();
}
public List<GiftDto> run() {
var mainPage = getPageContent(baseUrl);
var countries = getCountriesLinks(mainPage);
var allDocuments = getAllPagesDocuments(countries);
allDocuments.add(mainPage);
var output = new ArrayList<GiftDto>();
for (var document : allDocuments) {
var gifts = getGifts(document);
output.addAll(gifts);
}
return output;
}
public List<Document> getAllPagesDocuments(List<String> pages) {
List<Document> content = new ArrayList<>();
for (var page : pages) {
content.add(getPageContent(baseUrl + page));
}
return content;
}
public List<String> getCountriesLinks(Document document) {
var output = new ArrayList<String>();
var countriesElements = document.getElementsByTag("a");
for (var element : countriesElements) {
var value = element.attr("href");
if (!value.contains("/tiktok-widgets/gifts/?")) {
continue;
}
value = value.replace("/tiktok-widgets/gifts/", "");
output.add(value);
}
return output;
}
public List<GiftDto> getGifts(Document document) {
var container = document.getElementsByClass("section-block bkg-charcoal");
var giftsContainers = container.get(0).getElementsByClass("column width-1 center");
var output = new ArrayList<GiftDto>();
for (var giftContainer : giftsContainers) {
var imageElement = giftContainer.getElementsByTag("img").get(0);
var link = imageElement.attr("src");
var coinsElement = giftContainer.getElementsByClass("color-white").get(0);
var coins = coinsElement.text();
var inputsElements = giftContainer.getElementsByTag("input");
var idElement = inputsElements.get(0);
var nameElement = inputsElements.get(1);
var id = idElement.attr("value");
var name = nameElement.attr("value");
var gift = new GiftDto();
gift.setImage(link);
gift.setDiamondCost(Integer.parseInt(coins));
gift.setId(Integer.parseInt(id));
gift.setName(name);
output.add(gift);
}
return output;
}
public Document getPageContent(String url) {
try {
var result = Jsoup.connect(url).get();
System.out.println("Downloaded page: " + url);
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.jwdeveloper.tiktok.intefacee;
import com.squareup.javapoet.*;
import io.github.jwdeveloper.tiktok.TikTokLiveClientBuilder;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.events_generator.EventGeneratorSettings;
import org.reflections.Reflections;
import javax.lang.model.element.Modifier;
import java.util.Set;
public class EventsInterfaceGenerator {
public void compile(EventGeneratorSettings settings) {
Reflections reflections = new Reflections("io.github.jwdeveloper.tiktok.events.messages");
// Get all types (i.e., classes) in the specified package
var classes = reflections.getSubTypesOf(TikTokEvent.class);
classes.add(TikTokEvent.class);
// var result = generateInterface("io.github.jwdeveloper.tiktok.events", classes);System.out.println(result);
var result = getBuilderImplementation("x",classes); System.out.println(result);
}
public String generateInterface(String packageName, Set<Class<? extends TikTokEvent>> eventsClasses) {
TypeSpec.Builder classBuilder = TypeSpec.interfaceBuilder("TikTokEventBuilder");
classBuilder.addModifiers(Modifier.PUBLIC);
classBuilder.addTypeVariable(TypeVariableName.get("T"));
// Generate constructors
for (var clazz : eventsClasses) {
var clazzName = clazz.getSimpleName();
var methodName = clazzName;
methodName = clazzName.replace("TikTok", "");
if(!clazz.equals(TikTokEvent.class))
{
methodName = methodName.replace("Event", "");
}
MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder("on" + methodName);
var name = "TikTokEventConsumer<" + clazzName + ">";
constructorBuilder.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC);
constructorBuilder.addParameter(ClassName.bestGuess(name), "event");
constructorBuilder.returns(TypeVariableName.get("T"));
classBuilder.addMethod(constructorBuilder.build());
}
// Generate Java class
TypeSpec javaClass = classBuilder.build();
var result = JavaFile.builder(packageName, javaClass).build();
return result.toString();
}
public String getBuilderImplementation(String packageName, Set<Class<? extends TikTokEvent>> eventsClasses) {
TypeSpec.Builder classBuilder = TypeSpec.classBuilder("TikTokEvents");
classBuilder.addModifiers(Modifier.PUBLIC);
/*
public TikTokClientBuilder onLinkMicFanTicket(Consumer<TikTokLinkMicFanTicketEvent> event) {
tikTokEventHandler.subscribe(TikTokEventHandler.class, event);
return this;
}
*/
// Generate constructors
for (var clazz : eventsClasses) {
var clazzName = clazz.getSimpleName();
var methodName = clazzName;
methodName = clazzName.replace("TikTok", "");
if(!clazz.equals(TikTokEvent.class))
{
methodName = methodName.replace("Event", "");
}
methodName ="on" + methodName;
MethodSpec.Builder constructorBuilder = MethodSpec.methodBuilder( methodName);
var name = "TikTokEventConsumer<" + clazzName + ">";
constructorBuilder.addModifiers( Modifier.PUBLIC);
constructorBuilder.addParameter(ClassName.bestGuess(name), "event");
constructorBuilder.addStatement("tikTokEventHandler.subscribe("+clazzName+".class,event)");
constructorBuilder.addStatement("return this");
constructorBuilder.returns(TikTokLiveClientBuilder.class);
classBuilder.addMethod(constructorBuilder.build());
}
// Generate Java class
TypeSpec javaClass = classBuilder.build();
var result = JavaFile.builder(packageName, javaClass).build();
return result.toString();
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.protocol;
import org.jsoup.Jsoup;
import java.io.File;
import java.io.IOException;
public class ProtocolGenerator
{
public static void main(String[] args) {
// Path to the HTML file
File htmlFile = new File("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\Tools\\src\\main\\resources\\page.html");
try {
// Parse the HTML file with Jsoup
var doc = Jsoup.parse(htmlFile, "UTF-8");
// Find all script tags
var scriptTags = doc.select("script");
// Display all script tags
int counter = 1;
for (var scriptTag : scriptTags) {
String srcValue = scriptTag.attr("src");
if(!srcValue.contains("tiktok/webapp/main/webapp-live/"))
{
continue;
}
// Only print those script tags which have a 'src' attribute
if (!srcValue.isEmpty()) {
System.out.println("Script Tag " + counter + " src attribute: " + srcValue);
}
counter++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.utils;
public class ConsoleColors
{
public static final String RESET = "\033[0m"; // Text Reset
// Regular Colors
public static final String BLACK = "\033[0;30m"; // BLACK
public static final String RED = "\033[0;31m"; // RED
public static final String GREEN = "\033[0;32m"; // GREEN
public static final String YELLOW = "\033[0;33m"; // YELLOW
public static final String BLUE = "\033[0;34m"; // BLUE
public static final String PURPLE = "\033[0;35m"; // PURPLE
public static final String CYAN = "\033[0;36m"; // CYAN
public static final String WHITE = "\033[0;37m"; // WHITE
// Bold
public static final String BLACK_BOLD = "\033[1;30m"; // BLACK
public static final String RED_BOLD = "\033[1;31m"; // RED
public static final String GREEN_BOLD = "\033[1;32m"; // GREEN
public static final String YELLOW_BOLD = "\033[1;33m"; // YELLOW
public static final String BLUE_BOLD = "\033[1;34m"; // BLUE
public static final String PURPLE_BOLD = "\033[1;35m"; // PURPLE
public static final String CYAN_BOLD = "\033[1;36m"; // CYAN
public static final String WHITE_BOLD = "\033[1;37m"; // WHITE
// Underline
public static final String BLACK_UNDERLINED = "\033[4;30m"; // BLACK
public static final String RED_UNDERLINED = "\033[4;31m"; // RED
public static final String GREEN_UNDERLINED = "\033[4;32m"; // GREEN
public static final String YELLOW_UNDERLINED = "\033[4;33m"; // YELLOW
public static final String BLUE_UNDERLINED = "\033[4;34m"; // BLUE
public static final String PURPLE_UNDERLINED = "\033[4;35m"; // PURPLE
public static final String CYAN_UNDERLINED = "\033[4;36m"; // CYAN
public static final String WHITE_UNDERLINED = "\033[4;37m"; // WHITE
// Background
public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK
public static final String RED_BACKGROUND = "\033[41m"; // RED
public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN
public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW
public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE
public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE
public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN
public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE
// High Intensity
public static final String BLACK_BRIGHT = "\033[0;90m"; // BLACK
public static final String RED_BRIGHT = "\033[0;91m"; // RED
public static final String GREEN_BRIGHT = "\033[0;92m"; // GREEN
public static final String YELLOW_BRIGHT = "\033[0;93m"; // YELLOW
public static final String BLUE_BRIGHT = "\033[0;94m"; // BLUE
public static final String PURPLE_BRIGHT = "\033[0;95m"; // PURPLE
public static final String CYAN_BRIGHT = "\033[0;96m"; // CYAN
public static final String WHITE_BRIGHT = "\033[0;97m"; // WHITE
// Bold High Intensity
public static final String BLACK_BOLD_BRIGHT = "\033[1;90m"; // BLACK
public static final String RED_BOLD_BRIGHT = "\033[1;91m"; // RED
public static final String GREEN_BOLD_BRIGHT = "\033[1;92m"; // GREEN
public static final String YELLOW_BOLD_BRIGHT = "\033[1;93m";// YELLOW
public static final String BLUE_BOLD_BRIGHT = "\033[1;94m"; // BLUE
public static final String PURPLE_BOLD_BRIGHT = "\033[1;95m";// PURPLE
public static final String CYAN_BOLD_BRIGHT = "\033[1;96m"; // CYAN
public static final String WHITE_BOLD_BRIGHT = "\033[1;97m"; // WHITE
// High Intensity backgrounds
public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK
public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED
public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN
public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW
public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE
public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE
public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN
public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE
}

View File

@@ -1,4 +1,3 @@
/* /*
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
* *
@@ -21,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; package io.github.jwdeveloper.tiktok.utils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -38,7 +37,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);
} }
@@ -97,13 +96,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 {

Some files were not shown because too many files have changed in this diff Show More