mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-03-01 17:59:39 -05:00
Changes:
`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:
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user