`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

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