mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-02-28 09:19:40 -05:00
Implement Eulerstream send chat API endpoint!
This commit is contained in:
@@ -26,6 +26,7 @@ import io.github.jwdeveloper.tiktok.data.requests.GiftsData;
|
|||||||
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.live.LiveRoomInfo;
|
||||||
|
|
||||||
public interface LiveHttpClient
|
public interface LiveHttpClient
|
||||||
{
|
{
|
||||||
@@ -64,4 +65,6 @@ public interface LiveHttpClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
|
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
|
||||||
|
|
||||||
|
boolean sendChat(LiveRoomInfo roomInfo, String content);
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,6 @@ public interface LiveClient {
|
|||||||
*/
|
*/
|
||||||
void connect();
|
void connect();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects in asynchronous way
|
* Connects in asynchronous way
|
||||||
* When connected Consumer returns instance of LiveClient
|
* When connected Consumer returns instance of LiveClient
|
||||||
@@ -48,7 +47,6 @@ public interface LiveClient {
|
|||||||
*/
|
*/
|
||||||
CompletableFuture<LiveClient> connectAsync();
|
CompletableFuture<LiveClient> connectAsync();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects the connection.
|
* Disconnects the connection.
|
||||||
* @param type
|
* @param type
|
||||||
@@ -68,7 +66,6 @@ public interface LiveClient {
|
|||||||
*/
|
*/
|
||||||
void publishEvent(TikTokEvent event);
|
void publishEvent(TikTokEvent event);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param webcastMessageName name of TikTok protocol-buffer message
|
* @param webcastMessageName name of TikTok protocol-buffer message
|
||||||
* @param payloadBase64 protocol-buffer message bytes payload
|
* @param payloadBase64 protocol-buffer message bytes payload
|
||||||
@@ -96,4 +93,12 @@ public interface LiveClient {
|
|||||||
* Logger
|
* Logger
|
||||||
*/
|
*/
|
||||||
Logger getLogger();
|
Logger getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a chat message to the connected room
|
||||||
|
* @return true if successful, otherwise false
|
||||||
|
* @apiNote This is known to return true on some sessionIds despite failing!
|
||||||
|
* <p>We cannot fix this as it is a TikTok issue, not a library issue.
|
||||||
|
*/
|
||||||
|
boolean sendChat(String content);
|
||||||
}
|
}
|
||||||
@@ -183,6 +183,11 @@ public class TikTokLiveClient implements LiveClient
|
|||||||
messageHandler.handleSingleMessage(this, message);
|
messageHandler.handleSingleMessage(this, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sendChat(String content) {
|
||||||
|
return httpClient.sendChat(roomInfo, content);
|
||||||
|
}
|
||||||
|
|
||||||
public void connectAsync(Consumer<LiveClient> onConnection) {
|
public void connectAsync(Consumer<LiveClient> onConnection) {
|
||||||
connectAsync().thenAccept(onConnection);
|
connectAsync().thenAccept(onConnection);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.jwdeveloper.tiktok;
|
package io.github.jwdeveloper.tiktok;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import io.github.jwdeveloper.dependance.injector.api.annotations.Inject;
|
import io.github.jwdeveloper.dependance.injector.api.annotations.Inject;
|
||||||
import io.github.jwdeveloper.tiktok.common.*;
|
import io.github.jwdeveloper.tiktok.common.*;
|
||||||
@@ -30,9 +31,10 @@ import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
|||||||
import io.github.jwdeveloper.tiktok.exceptions.*;
|
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.*;
|
import io.github.jwdeveloper.tiktok.http.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.mappers.*;
|
import io.github.jwdeveloper.tiktok.http.mappers.*;
|
||||||
|
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.ProtoMessageFetchResult;
|
import io.github.jwdeveloper.tiktok.messages.webcast.ProtoMessageFetchResult;
|
||||||
|
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ 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>
|
* <a href="https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures">Signing API by Isaac Kogan</a>
|
||||||
*/
|
*/
|
||||||
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";
|
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";
|
||||||
|
private static final String TIKTOK_CHAT_URL = "https://tiktok.eulerstream.com/webcast/chat";
|
||||||
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_ROOM_GIFTS_URL = TIKTOK_URL_WEBCAST+"gift/list/";
|
public static final String TIKTOK_ROOM_GIFTS_URL = TIKTOK_URL_WEBCAST+"gift/list/";
|
||||||
@@ -182,6 +185,33 @@ public class TikTokLiveHttpClient implements LiveHttpClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sendChat(LiveRoomInfo roomInfo, String content) {
|
||||||
|
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||||
|
if (proxyClientSettings.isEnabled()) {
|
||||||
|
while (proxyClientSettings.hasNext()) {
|
||||||
|
try {
|
||||||
|
return requestSendChat(roomInfo, content);
|
||||||
|
} catch (TikTokProxyRequestException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return requestSendChat(roomInfo, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requestSendChat(LiveRoomInfo roomInfo, String content) {
|
||||||
|
JsonObject body = new JsonObject();
|
||||||
|
body.addProperty("content", content);
|
||||||
|
body.addProperty("sessionId", clientSettings.getSessionId());
|
||||||
|
body.addProperty("ttTargetIdc", clientSettings.getTtTargetIdc());
|
||||||
|
body.addProperty("roomId", roomInfo.getRoomId());
|
||||||
|
var result = httpFactory.client(TIKTOK_CHAT_URL)
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody(HttpRequest.BodyPublishers.ofString(body.toString()))
|
||||||
|
.build()
|
||||||
|
.toJsonResponse();
|
||||||
|
return result.isSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
protected ActionResult<HttpResponse<byte[]>> getStartingPayload(LiveConnectionData.Request request) {
|
protected ActionResult<HttpResponse<byte[]>> getStartingPayload(LiveConnectionData.Request request) {
|
||||||
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
|
||||||
if (proxyClientSettings.isEnabled()) {
|
if (proxyClientSettings.isEnabled()) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ 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.requests.*;
|
import io.github.jwdeveloper.tiktok.data.requests.*;
|
||||||
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
|
import io.github.jwdeveloper.tiktok.http.LiveHttpClient;
|
||||||
|
import io.github.jwdeveloper.tiktok.live.LiveRoomInfo;
|
||||||
import io.github.jwdeveloper.tiktok.messages.webcast.ProtoMessageFetchResult;
|
import io.github.jwdeveloper.tiktok.messages.webcast.ProtoMessageFetchResult;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@@ -61,4 +62,10 @@ public class TikTokLiveHttpOfflineClient implements LiveHttpClient {
|
|||||||
URI.create("https://example.live"),
|
URI.create("https://example.live"),
|
||||||
ProtoMessageFetchResult.newBuilder().build());
|
ProtoMessageFetchResult.newBuilder().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sendChat(LiveRoomInfo roomInfo, String content) {
|
||||||
|
// DO NOTHING
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -40,11 +40,12 @@ public class HttpClient {
|
|||||||
|
|
||||||
protected final HttpClientSettings httpClientSettings;
|
protected final HttpClientSettings httpClientSettings;
|
||||||
protected final String url;
|
protected final String url;
|
||||||
|
protected final HttpRequest.BodyPublisher bodyPublisher;
|
||||||
private final Pattern pattern = Pattern.compile("charset=(.*?)(?=&|$)");
|
private final Pattern pattern = Pattern.compile("charset=(.*?)(?=&|$)");
|
||||||
|
|
||||||
public <T> ActionResult<HttpResponse<T>> toHttpResponse(HttpResponse.BodyHandler<T> handler) {
|
public <T> ActionResult<HttpResponse<T>> toHttpResponse(HttpResponse.BodyHandler<T> handler) {
|
||||||
var client = prepareClient();
|
var client = prepareClient();
|
||||||
var request = prepareGetRequest();
|
var request = prepareRequest();
|
||||||
try {
|
try {
|
||||||
var response = client.send(request, handler);
|
var response = client.send(request, handler);
|
||||||
var result = ActionResult.of(response);
|
var result = ActionResult.of(response);
|
||||||
@@ -99,8 +100,13 @@ public class HttpClient {
|
|||||||
return URI.create(stringUrl);
|
return URI.create(stringUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpRequest prepareGetRequest() {
|
/**
|
||||||
var requestBuilder = HttpRequest.newBuilder().GET();
|
* @return {@link HttpRequest} with default GET, otherwise POST if {@link #bodyPublisher} is not null
|
||||||
|
*/
|
||||||
|
protected HttpRequest prepareRequest() {
|
||||||
|
var requestBuilder = HttpRequest.newBuilder();
|
||||||
|
if (bodyPublisher != null)
|
||||||
|
requestBuilder.POST(bodyPublisher);
|
||||||
requestBuilder.uri(toUri());
|
requestBuilder.uri(toUri());
|
||||||
requestBuilder.timeout(httpClientSettings.getTimeout());
|
requestBuilder.timeout(httpClientSettings.getTimeout());
|
||||||
if (!httpClientSettings.getCookies().isEmpty()) {
|
if (!httpClientSettings.getCookies().isEmpty()) {
|
||||||
@@ -124,12 +130,10 @@ public class HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String prepareUrlWithParameters(String url, Map<String, Object> parameters) {
|
protected String prepareUrlWithParameters(String url, Map<String, Object> parameters) {
|
||||||
if (parameters.isEmpty()) {
|
if (parameters.isEmpty())
|
||||||
return url;
|
return url;
|
||||||
}
|
|
||||||
|
|
||||||
return url + "?" + parameters.entrySet().stream().map(entry ->
|
return url + "?" + parameters.entrySet().stream().map(entry -> {
|
||||||
{
|
|
||||||
var encodedKey = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8);
|
var encodedKey = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8);
|
||||||
var encodedValue = URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8);
|
var encodedValue = URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8);
|
||||||
return encodedKey + "=" + encodedValue;
|
return encodedKey + "=" + encodedValue;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.http;
|
|||||||
|
|
||||||
import io.github.jwdeveloper.tiktok.data.settings.HttpClientSettings;
|
import io.github.jwdeveloper.tiktok.data.settings.HttpClientSettings;
|
||||||
|
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ public class HttpClientBuilder {
|
|||||||
|
|
||||||
private final HttpClientSettings httpClientSettings;
|
private final HttpClientSettings httpClientSettings;
|
||||||
private String url;
|
private String url;
|
||||||
|
private HttpRequest.BodyPublisher bodyPublisher;
|
||||||
|
|
||||||
public HttpClientBuilder(String url, HttpClientSettings httpClientSettings) {
|
public HttpClientBuilder(String url, HttpClientSettings httpClientSettings) {
|
||||||
this.httpClientSettings = httpClientSettings;
|
this.httpClientSettings = httpClientSettings;
|
||||||
@@ -78,10 +80,15 @@ public class HttpClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpClientBuilder withBody(HttpRequest.BodyPublisher bodyPublisher) {
|
||||||
|
this.bodyPublisher = bodyPublisher;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public HttpClient build() {
|
public HttpClient build() {
|
||||||
var proxyClientSettings = httpClientSettings.getProxyClientSettings();
|
var proxyClientSettings = httpClientSettings.getProxyClientSettings();
|
||||||
if (proxyClientSettings.isEnabled() && proxyClientSettings.hasNext())
|
if (proxyClientSettings.isEnabled() && proxyClientSettings.hasNext())
|
||||||
return new HttpProxyClient(httpClientSettings, url);
|
return new HttpProxyClient(httpClientSettings, url, bodyPublisher);
|
||||||
return new HttpClient(httpClientSettings, url);
|
return new HttpClient(httpClientSettings, url, bodyPublisher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,8 +40,8 @@ public class HttpProxyClient extends HttpClient {
|
|||||||
|
|
||||||
private final ProxyClientSettings proxySettings;
|
private final ProxyClientSettings proxySettings;
|
||||||
|
|
||||||
public HttpProxyClient(HttpClientSettings httpClientSettings, String url) {
|
public HttpProxyClient(HttpClientSettings httpClientSettings, String url, HttpRequest.BodyPublisher bodyPublisher) {
|
||||||
super(httpClientSettings, url);
|
super(httpClientSettings, url, bodyPublisher);
|
||||||
this.proxySettings = httpClientSettings.getProxyClientSettings();
|
this.proxySettings = httpClientSettings.getProxyClientSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ public class HttpProxyClient extends HttpClient {
|
|||||||
|
|
||||||
httpClientSettings.getOnClientCreating().accept(builder);
|
httpClientSettings.getOnClientCreating().accept(builder);
|
||||||
var client = builder.build();
|
var client = builder.build();
|
||||||
var request = prepareGetRequest();
|
var request = prepareRequest();
|
||||||
|
|
||||||
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||||
if (response.statusCode() != 200)
|
if (response.statusCode() != 200)
|
||||||
|
|||||||
Reference in New Issue
Block a user