`TikTokEventListener` new method of listening events
   see it at TestApplication/ListenerExample.java

Bugs:
 - Fixed bug: Websocket was sending ping after it was closed
This commit is contained in:
JW
2023-09-07 03:19:25 +02:00
parent 911e2b12a5
commit 4a157143ec
99 changed files with 2558 additions and 762 deletions

View File

@@ -23,10 +23,10 @@ public class TikTokGiftManager implements GiftManager {
}
public TikTokGift updateActiveGift(WebcastGiftMessage giftMessage) {
var giftId = new GiftId(giftMessage.getGiftId(), giftMessage.getSender().getUniqueId());
var giftId = new GiftId(giftMessage.getGiftId(), giftMessage.getUser().getIdStr());
if (activeGifts.containsKey(giftId)) {
var gift = activeGifts.get(giftId);
gift.setAmount(giftMessage.getAmount());
gift.setAmount(giftMessage.getComboCount());
} else {
var newGift = new TikTokGift(giftMessage);
activeGifts.put(giftId, newGift);
@@ -34,7 +34,8 @@ public class TikTokGiftManager implements GiftManager {
var gift = activeGifts.get(giftId);
if (giftMessage.getRepeatEnd()) {
if (giftMessage.getRepeatEnd() > 0)
{
gift.setStreakFinished(true);
activeGifts.remove(giftId);
}

View File

@@ -4,8 +4,10 @@ import io.github.jwdeveloper.tiktok.events.messages.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
import io.github.jwdeveloper.tiktok.live.ConnectionState;
import io.github.jwdeveloper.tiktok.live.GiftManager;
import io.github.jwdeveloper.tiktok.live.LiveClient;
@@ -17,25 +19,28 @@ import java.util.logging.Logger;
public class TikTokLiveClient implements LiveClient {
private final TikTokRoomInfo liveRoomInfo;
private final TikTokGiftManager tikTokGiftManager;
private final TikTokApiService apiClient;
private final TikTokApiService apiService;
private final TikTokWebSocketClient webSocketClient;
private final TikTokEventHandler tikTokEventHandler;
private final TikTokEventObserver tikTokEventHandler;
private final ClientSettings clientSettings;
private final TikTokListenersManager listenersManager;
private final Logger logger;
public TikTokLiveClient(TikTokRoomInfo tikTokLiveMeta,
TikTokApiService tikTokApiService,
TikTokWebSocketClient webSocketClient,
TikTokGiftManager tikTokGiftManager,
TikTokEventHandler tikTokEventHandler,
TikTokEventObserver tikTokEventHandler,
ClientSettings clientSettings,
TikTokListenersManager listenersManager,
Logger logger) {
this.liveRoomInfo = tikTokLiveMeta;
this.tikTokGiftManager = tikTokGiftManager;
this.apiClient = tikTokApiService;
this.apiService = tikTokApiService;
this.webSocketClient = webSocketClient;
this.tikTokEventHandler = tikTokEventHandler;
this.clientSettings = clientSettings;
this.listenersManager = listenersManager;
this.logger = logger;
}
@@ -71,8 +76,6 @@ public class TikTokLiveClient implements LiveClient {
setState(ConnectionState.DISCONNECTED);
}
public void tryConnect() {
if (liveRoomInfo.hasConnectionState(ConnectionState.CONNECTED))
throw new TikTokLiveException("Already connected");
@@ -82,9 +85,9 @@ public class TikTokLiveClient implements LiveClient {
logger.info("Connecting");
setState(ConnectionState.CONNECTING);
var roomId = apiClient.fetchRoomId(liveRoomInfo.getUserName());
var roomId = apiService.fetchRoomId(liveRoomInfo.getUserName());
liveRoomInfo.setRoomId(roomId);
var roomData = apiClient.fetchRoomInfo();
var roomData = apiService.fetchRoomInfo();
if (roomData.getStatus() == 0 || roomData.getStatus() == 4) {
throw new TikTokLiveOfflineHostException("LiveStream for HostID could not be found. Is the Host online?");
}
@@ -92,10 +95,10 @@ public class TikTokLiveClient implements LiveClient {
if (clientSettings.isDownloadGiftInfo())
{
logger.info("Fetch Gift info");
var gifts = apiClient.fetchAvailableGifts();
var gifts = apiService.fetchAvailableGifts();
tikTokGiftManager.loadGifsInfo(gifts);
}
var clientData = apiClient.fetchClientData();
var clientData = apiService.fetchClientData();
webSocketClient.start(clientData, this);
setState(ConnectionState.CONNECTED);
}
@@ -104,6 +107,11 @@ public class TikTokLiveClient implements LiveClient {
public LiveRoomInfo getRoomInfo() {
return liveRoomInfo;
}
@Override
public ListenersManager getListenersManager()
{
return listenersManager;
}
@Override
public GiftManager getGiftManager() {

View File

@@ -5,17 +5,20 @@ import io.github.jwdeveloper.tiktok.events.TikTokEventBuilder;
import io.github.jwdeveloper.tiktok.events.TikTokEventConsumer;
import io.github.jwdeveloper.tiktok.events.messages.*;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.http.TikTokApiService;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpApiClient;
import io.github.jwdeveloper.tiktok.http.TikTokHttpClient;
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.utils.CancelationToken;
import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -23,19 +26,26 @@ import java.util.logging.Logger;
public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveClientBuilder> {
private final ClientSettings clientSettings;
private final Logger logger;
private final TikTokEventHandler tikTokEventHandler;
private final TikTokEventObserver tikTokEventHandler;
private final List<TikTokEventListener> listeners;
public TikTokLiveClientBuilder(String userName) {
this.tikTokEventHandler = new TikTokEventHandler();
this.tikTokEventHandler = new TikTokEventObserver();
this.clientSettings = Constants.DefaultClientSettings();
this.clientSettings.setHostName(userName);
this.logger = Logger.getLogger(TikTokLive.class.getName());
this.listeners = new ArrayList<>();
}
public TikTokLiveClientBuilder configure(Consumer<ClientSettings> consumer) {
consumer.accept(clientSettings);
return this;
}
public TikTokLiveClientBuilder addListener(TikTokEventListener listener)
{
listeners.add(listener);
return this;
}
private void validate() {
@@ -71,9 +81,12 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli
var tiktokRoomInfo = new TikTokRoomInfo();
tiktokRoomInfo.setUserName(clientSettings.getHostName());
var listenerManager = new TikTokListenersManager(listeners, tikTokEventHandler);
var cookieJar = new TikTokCookieJar();
var requestFactory = new TikTokHttpRequestFactory(cookieJar);
var apiClient = new TikTokHttpApiClient(cookieJar, requestFactory);
var apiClient = new TikTokHttpClient(cookieJar, requestFactory);
var apiService = new TikTokApiService(apiClient, logger, clientSettings);
var giftManager = new TikTokGiftManager();
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler, clientSettings, logger, giftManager, tiktokRoomInfo);
@@ -83,7 +96,14 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli
webResponseHandler,
tikTokEventHandler);
return new TikTokLiveClient(tiktokRoomInfo, apiService, webSocketClient, giftManager, tikTokEventHandler, clientSettings, logger);
return new TikTokLiveClient(tiktokRoomInfo,
apiService,
webSocketClient,
giftManager,
tikTokEventHandler,
clientSettings,
listenerManager,
logger);
}
public LiveClient buildAndRun() {
@@ -259,8 +279,8 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli
return this;
}
public TikTokLiveClientBuilder onUnhandled(TikTokEventConsumer<TikTokUnhandledEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledEvent.class, event);
public TikTokLiveClientBuilder onUnhandled(TikTokEventConsumer<TikTokUnhandledWebsocketMessageEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledWebsocketMessageEvent.class, event);
return this;
}

View File

@@ -5,34 +5,43 @@ import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.TikTokEventConsumer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class TikTokEventHandler {
private final Map<String, TikTokEventConsumer> events;
public class TikTokEventObserver {
private final Map<String, Set<TikTokEventConsumer>> events;
public TikTokEventHandler() {
public TikTokEventObserver() {
events = new HashMap<>();
}
public void publish(TikTokLiveClient tikTokLiveClient, TikTokEvent tikTokEvent) {
if (events.containsKey(TikTokEvent.class.getSimpleName())) {
var handler = events.get(TikTokEvent.class.getSimpleName());
handler.onEvent(tikTokLiveClient, tikTokEvent);
var handlers = events.get(TikTokEvent.class.getSimpleName());
for(var handle : handlers)
{
handle.onEvent(tikTokLiveClient, tikTokEvent);
}
}
var name = tikTokEvent.getClass().getSimpleName();
if (!events.containsKey(name)) {
return;
}
var handler = events.get(name);
handler.onEvent(tikTokLiveClient, tikTokEvent);
var handlers = events.get(name);
for(var handler : handlers)
{
handler.onEvent(tikTokLiveClient, tikTokEvent);
}
}
public <T extends TikTokEvent> void subscribe(Class<?> clazz, TikTokEventConsumer<T> event) {
events.put(clazz.getSimpleName(), event);
public <T extends TikTokEvent> void subscribe(Class<?> clazz, TikTokEventConsumer<T> event)
{
events.computeIfAbsent(clazz.getSimpleName(), e -> new HashSet<>()).add(event);
}
public <T extends TikTokEvent> void unsubscribe(Class<?> clazz) {
public <T extends TikTokEvent> void unsubscribeAll(Class<?> clazz) {
events.remove(clazz);
}
}

View File

@@ -7,7 +7,7 @@ import io.github.jwdeveloper.tiktok.TikTokLiveClient;
import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokUnhandledEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokUnhandledWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokMessageMappingException;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
@@ -23,11 +23,11 @@ import java.util.logging.Logger;
public abstract class TikTokMessageHandler {
private final Map<String, io.github.jwdeveloper.tiktok.handler.TikTokMessageHandler> handlers;
private final TikTokEventHandler tikTokEventHandler;
private final TikTokEventObserver tikTokEventHandler;
private final ClientSettings clientSettings;
protected final Logger logger;
public TikTokMessageHandler(TikTokEventHandler tikTokEventHandler,ClientSettings clientSettings, Logger logger) {
public TikTokMessageHandler(TikTokEventObserver tikTokEventHandler, ClientSettings clientSettings, Logger logger) {
handlers = new HashMap<>();
this.tikTokEventHandler = tikTokEventHandler;
this.clientSettings = clientSettings;
@@ -65,7 +65,7 @@ public abstract class TikTokMessageHandler {
private void handleSingleMessage(TikTokLiveClient client, WebcastResponse.Message message) throws Exception {
if (!handlers.containsKey(message.getType())) {
tikTokEventHandler.publish(client, new TikTokUnhandledEvent(message));
tikTokEventHandler.publish(client, new TikTokUnhandledWebsocketMessageEvent(message));
return;
}
var handler = handlers.get(message.getType());

View File

@@ -5,21 +5,18 @@ import io.github.jwdeveloper.tiktok.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.TikTokRoomInfo;
import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.messages.*;
import io.github.jwdeveloper.tiktok.events.objects.TikTokGift;
import io.github.jwdeveloper.tiktok.messages.*;
import io.github.jwdeveloper.tiktok.models.GiftId;
import io.github.jwdeveloper.tiktok.models.SocialTypes;
import lombok.SneakyThrows;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TikTokMessageHandlerRegistration extends TikTokMessageHandler {
private final TikTokGiftManager giftManager;
private final TikTokRoomInfo roomInfo;
public TikTokMessageHandlerRegistration(TikTokEventHandler tikTokEventHandler,
public TikTokMessageHandlerRegistration(TikTokEventObserver tikTokEventHandler,
ClientSettings clientSettings,
Logger logger,
TikTokGiftManager giftManager,

View File

@@ -2,11 +2,12 @@ package io.github.jwdeveloper.tiktok.http;
import com.google.gson.Gson;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.live.LiveRoomMeta;
import io.github.jwdeveloper.tiktok.models.gifts.TikTokGiftInfo;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.models.gifts.TikTokGiftInfo;
import java.util.HashMap;
import java.util.Map;
@@ -15,34 +16,96 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TikTokApiService {
private final TikTokHttpApiClient apiClient;
private final TikTokHttpClient tiktokHttpClient;
private final Logger logger;
private final ClientSettings clientSettings;
public TikTokApiService(TikTokHttpApiClient apiClient, Logger logger, ClientSettings clientSettings) {
this.apiClient = apiClient;
public TikTokApiService(TikTokHttpClient apiClient, Logger logger, ClientSettings clientSettings) {
this.tiktokHttpClient = apiClient;
this.logger = logger;
this.clientSettings = clientSettings;
}
/*
if (sessionId) {
// Update sessionId
this.#options.sessionId = sessionId;
}
if (!this.#options.sessionId) {
throw new Error('Missing SessionId. Please provide your current SessionId to use this feature.');
}
try {
// Retrieve current room_id if not connected
if (!this.#isConnected) {
await this.#retrieveRoomId();
}
// Add the session cookie to the CookieJar
this.#httpClient.setSessionId(this.#options.sessionId);
// Submit the chat request
let requestParams = { ...this.#clientParams, content: text };
let response = await this.#httpClient.postFormDataToWebcastApi('room/chat/', requestParams, null);
// Success?
if (response?.status_code === 0) {
return response.data;
}
// Handle errors
switch (response?.status_code) {
case 20003:
throw new Error('Your SessionId has expired. Please provide a new one.');
default:
throw new Error(`TikTok responded with status code ${response?.status_code}: ${response?.data?.message}`);
}
} catch (err) {
throw new Error(`Failed to send chat message. ${err.message}`);
}
*/
public boolean sendMessage(String message, String sessionId) {
if (sessionId.isEmpty()) {
throw new TikTokLiveException("Session ID must not be Empty");
}
var roomId = clientSettings.getClientParameters().get("room_id");
if (roomId == null) {
throw new TikTokLiveException("Room ID must not be Empty");
}
logger.info("Sending message to chat");
try {
var params = new HashMap<String, Object>(clientSettings.getClientParameters());
params.put("content", message);
params.put("channel", "tiktok_web");
params.remove("cursor");
tiktokHttpClient.setSessionId(sessionId);
tiktokHttpClient.postMessageToChat(params);
return true;
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
}
}
public String fetchRoomId(String userName) {
logger.info("Fetching room ID");
String html;
try {
html = apiClient.GetLivestreamPage(userName);
html = tiktokHttpClient.getLivestreamPage(userName);
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch room id from WebCast, see stacktrace for more info.", e);
}
Pattern firstPattern = Pattern.compile("room_id=([0-9]*)");
Matcher firstMatcher = firstPattern.matcher(html);
String id = "";
var firstPattern = Pattern.compile("room_id=([0-9]*)");
var firstMatcher = firstPattern.matcher(html);
var id = "";
if (firstMatcher.find()) {
id = firstMatcher.group(1);
} else {
Pattern secondPattern = Pattern.compile("\"roomId\":\"([0-9]*)\"");
Matcher secondMatcher = secondPattern.matcher(html);
var secondPattern = Pattern.compile("\"roomId\":\"([0-9]*)\"");
var secondMatcher = secondPattern.matcher(html);
if (secondMatcher.find()) {
id = secondMatcher.group(1);
@@ -54,7 +117,7 @@ public class TikTokApiService {
}
clientSettings.getClientParameters().put("room_id", id);
logger.info("RoomID -> "+id);
logger.info("RoomID -> " + id);
return id;
}
@@ -62,7 +125,7 @@ public class TikTokApiService {
public LiveRoomMeta fetchRoomInfo() {
logger.info("Fetch RoomInfo");
try {
var response = apiClient.GetJObjectFromWebcastAPI("room/info/", clientSettings.getClientParameters());
var response = tiktokHttpClient.getJObjectFromWebcastAPI("room/info/", clientSettings.getClientParameters());
if (!response.has("data")) {
return new LiveRoomMeta();
}
@@ -77,48 +140,42 @@ public class TikTokApiService {
var info = new LiveRoomMeta();
info.setStatus(status.getAsInt());
logger.info("RoomInfo status -> "+info.getStatus());
logger.info("RoomInfo status -> " + info.getStatus());
return info;
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch room info from WebCast, see stacktrace for more info.", e);
}
}
public WebcastResponse fetchClientData()
{
public WebcastResponse fetchClientData() {
logger.info("Fetch ClientData");
try {
var response = apiClient.GetDeserializedMessage("im/fetch/", clientSettings.getClientParameters());
clientSettings.getClientParameters().put("cursor",response.getCursor());
var response = tiktokHttpClient.getDeserializedMessage("im/fetch/", clientSettings.getClientParameters());
clientSettings.getClientParameters().put("cursor", response.getCursor());
clientSettings.getClientParameters().put("internal_ext", response.getAckIds());
return response;
}
catch (Exception e)
{
} catch (Exception e) {
throw new TikTokLiveRequestException("Failed to fetch client data", e);
}
}
public Map<Integer, TikTokGiftInfo> fetchAvailableGifts() {
try {
var response = apiClient.GetJObjectFromWebcastAPI("gift/list/", clientSettings.getClientParameters());
if(!response.has("data"))
{
var response = tiktokHttpClient.getJObjectFromWebcastAPI("gift/list/", clientSettings.getClientParameters());
if (!response.has("data")) {
return new HashMap<>();
}
var dataJson = response.getAsJsonObject("data");
if(!dataJson.has("gifts"))
{
if (!dataJson.has("gifts")) {
return new HashMap<>();
}
var giftsJsonList = dataJson.get("gifts").getAsJsonArray();
var gifts = new HashMap<Integer, TikTokGiftInfo>();
var gson = new Gson();
for(var jsonGift : giftsJsonList)
{
for (var jsonGift : giftsJsonList) {
var gift = gson.fromJson(jsonGift, TikTokGiftInfo.class);
logger.info("Found Available Gift "+ gift.getName()+ " with ID "+gift.getId());
gifts.put(gift.getId(),gift);
logger.info("Found Available Gift " + gift.getName() + " with ID " + gift.getId());
gifts.put(gift.getId(), gift);
}
return gifts;
} catch (Exception e) {

View File

@@ -2,7 +2,6 @@ package io.github.jwdeveloper.tiktok.http;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.Constants;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
@@ -15,30 +14,44 @@ import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class TikTokHttpApiClient {
public class TikTokHttpClient {
private final TikTokHttpRequestFactory requestFactory;
private final TikTokCookieJar tikTokCookieJar;
public TikTokHttpApiClient(TikTokCookieJar tikTokCookieJar, TikTokHttpRequestFactory requestFactory) {
public TikTokHttpClient(TikTokCookieJar tikTokCookieJar, TikTokHttpRequestFactory requestFactory) {
this.requestFactory = requestFactory;
this.tikTokCookieJar = tikTokCookieJar;
}
public String GetLivestreamPage(String userName) {
public void setSessionId(String sessionId)
{
tikTokCookieJar.set("sessionid", sessionId);
tikTokCookieJar.set("sessionid_ss", sessionId);
tikTokCookieJar.set("sid_tt", sessionId);
}
public String getLivestreamPage(String userName) {
var url = Constants.TIKTOK_URL_WEB + "@" + userName + "/live/";
var get = getRequest(url, null);
return get;
}
public JsonObject GetJObjectFromWebcastAPI(String path, Map<String, Object> parameters) {
public String postMessageToChat(Map<String,Object> parameters)
{
var get = postRequest(Constants.TIKTOK_URL_WEBCAST + "room/chat/", parameters);
return get;
}
public JsonObject getJObjectFromWebcastAPI(String path, Map<String, Object> parameters) {
var get = getRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters);
var json = JsonParser.parseString(get);
var jsonObject = json.getAsJsonObject();
return jsonObject;
}
public WebcastResponse GetDeserializedMessage(String path, Map<String, Object> parameters) {
public WebcastResponse getDeserializedMessage(String path, Map<String, Object> parameters) {
var bytes = getSignRequest(Constants.TIKTOK_URL_WEBCAST + path, parameters);
try {
return WebcastResponse.parseFrom(bytes);
@@ -49,16 +62,25 @@ public class TikTokHttpApiClient {
}
}
private String postRequest(String url, Map<String, Object> parameters) {
if (parameters == null) {
parameters = new HashMap<>();
}
System.out.println("RomMID: "+parameters.get("room_id"));
var request = requestFactory.setQueries(parameters);
return request.post(url);
}
private String getRequest(String url, Map<String, Object> parameters) {
if (parameters == null) {
parameters = new HashMap<>();
}
var request = requestFactory.SetQueries(parameters);
return request.Get(url);
var request = requestFactory.setQueries(parameters);
return request.get(url);
}
private byte[] getSignRequest(String url, Map<String, Object> parameters) {
url = GetSignedUrl(url, parameters);
url = getSignedUrl(url, parameters);
try {
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
@@ -86,7 +108,7 @@ public class TikTokHttpApiClient {
}
private String GetSignedUrl(String url, Map<String, Object> parameters) {
private String getSignedUrl(String url, Map<String, Object> parameters) {
var fullUrl = HttpUtils.parseParameters(url,parameters);
var singHeaders = new TreeMap<String,Object>();
@@ -94,8 +116,8 @@ public class TikTokHttpApiClient {
singHeaders.put("uuc", 1);
singHeaders.put("url", fullUrl);
var request = requestFactory.SetQueries(singHeaders);
var content = request.Get(Constants.TIKTOK_SIGN_API);
var request = requestFactory.setQueries(singHeaders);
var content = request.get(Constants.TIKTOK_SIGN_API);
try {

View File

@@ -11,12 +11,12 @@ import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.WebSocket;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class TikTokHttpRequestFactory implements TikTokHttpRequest {
private final CookieManager cookieManager;
@@ -34,13 +34,8 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest {
.connectTimeout(Duration.ofSeconds(2))
.build();
}
public WebSocket.Builder openSocket() {
return client.newWebSocketBuilder();
}
@SneakyThrows
public String Get(String url) {
public String get(String url) {
var uri = URI.create(url);
var request = HttpRequest.newBuilder().GET();
if (query != null) {
@@ -49,22 +44,35 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest {
request.uri(requestUri);
}
return GetContent(request.build());
return getContent(request.build());
}
@SneakyThrows
public String Post(String url, HttpRequest.BodyPublisher data) {
public String post(String url) {
var uri = URI.create(url);
var request = HttpRequest.newBuilder().POST(data);
for (var header : defaultHeaders.entrySet()) {
var request = HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(""));
for (var header : defaultHeaders.entrySet())
{
if(header.getKey().equals("Connection"))
{
continue;
}
request.setHeader(header.getKey(), header.getValue());
}
request.setHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8");
request.setHeader("Cookie", tikTokCookieJar.parseCookies());
if (query != null) {
var baseUri = uri.toString();
var requestUri = URI.create(baseUri + "?" + query);
request.uri(requestUri);
System.out.println(requestUri.toString());
}
return GetContent(request.build());
return getContent(request.build());
}
public TikTokHttpRequest setHeader(String key, String value) {
@@ -77,10 +85,11 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest {
return this;
}
public TikTokHttpRequest SetQueries(Map<String, Object> queries) {
public TikTokHttpRequest setQueries(Map<String, Object> queries) {
if (queries == null)
return this;
query = String.join("&", queries.entrySet().stream().map(x ->
var testMap = new TreeMap<String,Object>(queries);
query = String.join("&", testMap.entrySet().stream().map(x ->
{
var key = x.getKey();
var value = "";
@@ -95,7 +104,7 @@ public class TikTokHttpRequestFactory implements TikTokHttpRequest {
}
private String GetContent(HttpRequest request) throws Exception {
private String getContent(HttpRequest request) throws Exception {
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 404) {
throw new TikTokLiveRequestException("Request responded with 404 NOT_FOUND");

View File

@@ -0,0 +1,17 @@
package io.github.jwdeveloper.tiktok.listener;
import io.github.jwdeveloper.tiktok.events.TikTokEventConsumer;
import lombok.Value;
import java.util.List;
@Value
public class ListenerBindingModel
{
TikTokEventListener listener;
List<TikTokEventConsumer<?>> events;
}

View File

@@ -0,0 +1,82 @@
package io.github.jwdeveloper.tiktok.listener;
import io.github.jwdeveloper.tiktok.annotations.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.events.TikTokEvent;
import io.github.jwdeveloper.tiktok.events.TikTokEventConsumer;
import io.github.jwdeveloper.tiktok.events.messages.TikTokWebsocketMessageEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TikTokListenersManager implements ListenersManager {
private final TikTokEventObserver eventObserver;
private final List<ListenerBindingModel> bindingModels;
public TikTokListenersManager(List<TikTokEventListener> listeners, TikTokEventObserver tikTokEventHandler) {
this.eventObserver = tikTokEventHandler;
this.bindingModels = listeners.stream().map(this::bindToEvents).toList();
}
@Override
public List<TikTokEventListener> getBindingModels() {
return bindingModels.stream().map(ListenerBindingModel::getListener).toList();
}
@Override
public void addListener(TikTokEventListener listener) {
var alreadyExists = bindingModels.stream().filter(e -> e.getListener() == listener).findAny();
if (alreadyExists.isPresent()) {
throw new TikTokLiveException("Listener " + listener.getClass() + " has already been registered");
}
var bindingModel = bindToEvents(listener);
bindingModels.add(bindingModel);
}
@Override
public void removeListener(TikTokEventListener listener) {
var optional = bindingModels.stream().filter(e -> e.getListener() == listener).findAny();
if (optional.isEmpty()) {
return;
}
bindingModels.remove(optional.get());
}
private ListenerBindingModel bindToEvents(TikTokEventListener listener) {
var clazz = listener.getClass();
var methods = Arrays.stream(clazz.getDeclaredMethods()).filter(m ->
m.getParameterCount() == 2 &&
m.isAnnotationPresent(TikTokEventHandler.class) &&
m.getParameterTypes()[0].equals(LiveClient.class)).toList();
var eventConsumer = new ArrayList<TikTokEventConsumer<?>>();
for (var method : methods)
{
var eventClazz = method.getParameterTypes()[1];
if(!eventClazz.isAssignableFrom(TikTokEvent.class))
{
throw new TikTokEventListenerMethodException("Method "+method.getName()+" 2nd parameter must instance of "+TikTokEvent.class.getName());
}
var tikTokEventConsumer = new TikTokEventConsumer() {
@Override
public void onEvent(LiveClient liveClient, TikTokEvent event) {
try {
method.invoke(listener, liveClient, event);
} catch (Exception e) {
throw new TikTokEventListenerMethodException(e);
}
}
};
eventObserver.subscribe(eventClazz, tikTokEventConsumer);
}
return new ListenerBindingModel(listener, eventConsumer);
}
}

View File

@@ -5,16 +5,14 @@ import io.github.jwdeveloper.tiktok.ClientSettings;
import io.github.jwdeveloper.tiktok.Constants;
import io.github.jwdeveloper.tiktok.TikTokLiveClient;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.http.HttpUtils;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import org.java_websocket.client.WebSocketClient;
import java.net.URI;
import java.net.http.WebSocket;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.logging.Logger;
@@ -24,7 +22,7 @@ public class TikTokWebSocketClient {
private final ClientSettings clientSettings;
private final TikTokCookieJar tikTokCookieJar;
private final TikTokMessageHandlerRegistration webResponseHandler;
private final TikTokEventHandler tikTokEventHandler;
private final TikTokEventObserver tikTokEventHandler;
private WebSocketClient webSocketClient;
private TikTokLiveClient tikTokLiveClient;
private TikTokWebSocketPingingTask pingingTask;
@@ -34,7 +32,7 @@ public class TikTokWebSocketClient {
TikTokCookieJar tikTokCookieJar,
ClientSettings clientSettings,
TikTokMessageHandlerRegistration webResponseHandler,
TikTokEventHandler tikTokEventHandler) {
TikTokEventObserver tikTokEventHandler) {
this.logger = logger;
this.tikTokCookieJar = tikTokCookieJar;
this.clientSettings = clientSettings;
@@ -97,12 +95,11 @@ public class TikTokWebSocketClient {
tikTokLiveClient);
}
public void stop()
{
if (isConnected && webSocketClient != null) {
webSocketClient.close(1);
if (isConnected && webSocketClient != null)
{
webSocketClient.closeConnection(0,"");
}
webSocketClient = null;
pingingTask = null;

View File

@@ -6,7 +6,7 @@ import io.github.jwdeveloper.tiktok.events.messages.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.events.messages.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventHandler;
import io.github.jwdeveloper.tiktok.handlers.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck;
@@ -16,7 +16,6 @@ import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.net.http.WebSocket;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Optional;
@@ -24,14 +23,14 @@ import java.util.Optional;
public class TikTokWebSocketListener extends WebSocketClient {
private final TikTokMessageHandlerRegistration webResponseHandler;
private final TikTokEventHandler tikTokEventHandler;
private final TikTokEventObserver tikTokEventHandler;
private final TikTokLiveClient tikTokLiveClient;
public TikTokWebSocketListener(URI serverUri,
Map<String, String> httpHeaders,
int connectTimeout,
TikTokMessageHandlerRegistration webResponseHandler,
TikTokEventHandler tikTokEventHandler,
TikTokEventObserver tikTokEventHandler,
TikTokLiveClient tikTokLiveClient) {
super(serverUri, new Draft_6455(), httpHeaders,connectTimeout);
this.webResponseHandler = webResponseHandler;
@@ -43,7 +42,10 @@ public class TikTokWebSocketListener extends WebSocketClient {
@Override
public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
sendPing();
if(isNotClosing())
{
sendPing();
}
}
@@ -55,7 +57,10 @@ public class TikTokWebSocketListener extends WebSocketClient {
} catch (Exception e) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e));
}
sendPing();
if(isNotClosing())
{
sendPing();
}
}
@Override
@@ -66,7 +71,10 @@ public class TikTokWebSocketListener extends WebSocketClient {
@Override
public void onError(Exception error) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokErrorEvent(error));
sendPing();
if(isNotClosing())
{
sendPing();
}
}
private void handleBinary(byte[] buffer) {
@@ -101,6 +109,11 @@ public class TikTokWebSocketListener extends WebSocketClient {
}
}
private boolean isNotClosing()
{
return !isClosed() && !isClosing();
}
private void sendAckId(long id) {
@@ -109,7 +122,10 @@ public class TikTokWebSocketListener extends WebSocketClient {
.setType("ack")
.setId(id)
.build();
send(serverInfo.toByteString().asReadOnlyByteBuffer());
if(isNotClosing())
{
send(serverInfo.toByteString().asReadOnlyByteBuffer());
}
}