From 652d116ad9348f448e0e9d7f1f186e1294274745 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Wed, 30 Aug 2023 12:57:21 +0200 Subject: [PATCH] Wait for readiness, update example --- example/pom.xml | 2 +- .../main/java/it/tdlight/example/Example.java | 27 ++- .../client/AuthorizationStateReadyGetMe.java | 6 + .../client/AuthorizationStateWaitReady.java | 20 +++ .../tdlight/client/SimpleTelegramClient.java | 154 +++++++++++++++--- 5 files changed, 183 insertions(+), 26 deletions(-) create mode 100644 tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateWaitReady.java diff --git a/example/pom.xml b/example/pom.xml index e375845..72db845 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -26,7 +26,7 @@ it.tdlight tdlight-java-bom - 3.1.0+td.1.8.16 + 3.1.1+td.1.8.16 pom import diff --git a/example/src/main/java/it/tdlight/example/Example.java b/example/src/main/java/it/tdlight/example/Example.java index 1a68494..4f735d6 100644 --- a/example/src/main/java/it/tdlight/example/Example.java +++ b/example/src/main/java/it/tdlight/example/Example.java @@ -8,8 +8,12 @@ import it.tdlight.client.TDLibSettings; import it.tdlight.Init; import it.tdlight.jni.TdApi.AuthorizationState; import it.tdlight.jni.TdApi.Chat; +import it.tdlight.jni.TdApi.FormattedText; +import it.tdlight.jni.TdApi.InputMessageText; import it.tdlight.jni.TdApi.MessageContent; import it.tdlight.jni.TdApi; +import it.tdlight.jni.TdApi.SendMessage; +import it.tdlight.jni.TdApi.TextEntity; import it.tdlight.util.UnsupportedNativeLibraryException; import java.nio.file.Path; import java.nio.file.Paths; @@ -26,7 +30,7 @@ public final class Example { */ private static final TdApi.MessageSender ADMIN_ID = new TdApi.MessageSenderUser(667900586); - private static SimpleTelegramClient client; + private static SimpleTelegramClient CLIENT; public static void main(String[] args) throws UnsupportedNativeLibraryException, InterruptedException { // Initialize TDLight native libraries @@ -34,7 +38,6 @@ public final class Example { // Create the client factory try (SimpleTelegramClientFactory clientFactory = new SimpleTelegramClientFactory()) { - // Obtain the API token // // var apiToken = new APIToken(your-api-id-here, "your-api-hash-here"); @@ -69,10 +72,24 @@ public final class Example { clientBuilder.addCommandHandler("stop", new StopCommandHandler()); // Create and start the client - client = clientBuilder.build(authenticationData); + SimpleTelegramClient client = CLIENT = clientBuilder.build(authenticationData); + try (client) { - // Wait for exit - client.waitForExit(); + // Get me + TdApi.User me = client.getMeAsync().join(); + + // Send a test message + var req = new SendMessage(); + req.chatId = me.id; + var txt = new InputMessageText(); + txt.text = new FormattedText("TDLight test", new TextEntity[0]); + req.inputMessageContent = txt; + var result = client.sendMessage(req, true).join(); + System.out.println("Sent message:" + result); + + // Wait for exit + client.waitForExit(); + }; } } diff --git a/tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateReadyGetMe.java b/tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateReadyGetMe.java index ae643e7..3d8bea0 100644 --- a/tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateReadyGetMe.java +++ b/tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateReadyGetMe.java @@ -7,6 +7,7 @@ import it.tdlight.jni.TdApi.UpdateAuthorizationState; import it.tdlight.jni.TdApi.User; import it.tdlight.jni.TdApi.Error; import it.tdlight.jni.TdApi.UserTypeRegular; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,6 +17,7 @@ final class AuthorizationStateReadyGetMe implements GenericUpdateHandler meReceived = new CompletableFuture<>(); private final AtomicReference me = new AtomicReference<>(); private final AuthorizationStateReadyLoadChats mainChatsLoader; private final AuthorizationStateReadyLoadChats archivedChatsLoader; @@ -47,4 +49,8 @@ final class AuthorizationStateReadyGetMe implements GenericUpdateHandler getMeAsync() { + return meReceived.thenApply(v -> me.get()); + } } diff --git a/tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateWaitReady.java b/tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateWaitReady.java new file mode 100644 index 0000000..dbbf453 --- /dev/null +++ b/tdlight-java/src/main/java/it/tdlight/client/AuthorizationStateWaitReady.java @@ -0,0 +1,20 @@ +package it.tdlight.client; + +import it.tdlight.jni.TdApi.AuthorizationStateReady; +import it.tdlight.jni.TdApi.UpdateAuthorizationState; + +final class AuthorizationStateWaitReady implements GenericUpdateHandler { + + private final Runnable setReady; + + public AuthorizationStateWaitReady(Runnable setReady) { + this.setReady = setReady; + } + + @Override + public void onUpdate(UpdateAuthorizationState update) { + if (update.authorizationState.getConstructor() == AuthorizationStateReady.CONSTRUCTOR) { + setReady.run(); + } + } +} diff --git a/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java b/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java index 1a3caf3..c090c4c 100644 --- a/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java +++ b/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java @@ -1,20 +1,22 @@ package it.tdlight.client; +import static it.tdlight.util.MapUtils.addAllKeys; + import io.atlassian.util.concurrent.CopyOnWriteMap; import it.tdlight.ClientFactory; import it.tdlight.ExceptionHandler; import it.tdlight.Init; import it.tdlight.ResultHandler; import it.tdlight.TelegramClient; -import it.tdlight.jni.TdApi.Message; -import it.tdlight.jni.TdApi.Update; -import it.tdlight.util.FutureSupport; -import it.tdlight.util.UnsupportedNativeLibraryException; import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi.ChatListArchive; import it.tdlight.jni.TdApi.ChatListMain; import it.tdlight.jni.TdApi.Function; +import it.tdlight.jni.TdApi.Message; +import it.tdlight.jni.TdApi.Update; import it.tdlight.jni.TdApi.User; +import it.tdlight.util.FutureSupport; +import it.tdlight.util.UnsupportedNativeLibraryException; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; @@ -22,20 +24,16 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static it.tdlight.util.MapUtils.addAllKeys; - @SuppressWarnings("unused") -public final class SimpleTelegramClient implements Authenticable, MutableTelegramClient { +public final class SimpleTelegramClient implements Authenticable, MutableTelegramClient, AutoCloseable { public static final Logger LOG = LoggerFactory.getLogger(SimpleTelegramClient.class); @@ -61,6 +59,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra private final AuthorizationStateReadyLoadChats mainChatsLoader; private final AuthorizationStateReadyLoadChats archivedChatsLoader; + private final CompletableFuture ready = new CompletableFuture<>(); private final CompletableFuture closed = new CompletableFuture<>(); private final ConcurrentMap> temporaryMessages = new ConcurrentHashMap<>(); @@ -117,6 +116,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra this::handleDefaultException ) ); + this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitReady(this::onReady)); this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitForExit(this::onCloseUpdate)); this.mainChatsLoader = new AuthorizationStateReadyLoadChats(client, new ChatListMain()); this.archivedChatsLoader = new AuthorizationStateReadyLoadChats(client, new ChatListArchive()); @@ -240,6 +240,61 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra } } + private boolean shouldWaitForReadiness(Function function) { + switch (function.getConstructor()) { + case TdApi.Close.CONSTRUCTOR: + case TdApi.SetLogVerbosityLevel.CONSTRUCTOR: + case TdApi.SetLogTagVerbosityLevel.CONSTRUCTOR: + case TdApi.SetLogStream.CONSTRUCTOR: + case TdApi.SetNetworkType.CONSTRUCTOR: + case TdApi.SetOption.CONSTRUCTOR: + case TdApi.GetOption.CONSTRUCTOR: + case TdApi.GetAuthorizationState.CONSTRUCTOR: + case TdApi.GetCurrentState.CONSTRUCTOR: + case TdApi.GetLogStream.CONSTRUCTOR: + case TdApi.GetLogTags.CONSTRUCTOR: + case TdApi.GetLogVerbosityLevel.CONSTRUCTOR: + case TdApi.GetLogTagVerbosityLevel.CONSTRUCTOR: + case TdApi.GetNetworkStatistics.CONSTRUCTOR: + case TdApi.AddNetworkStatistics.CONSTRUCTOR: + case TdApi.ResetNetworkStatistics.CONSTRUCTOR: + case TdApi.AddProxy.CONSTRUCTOR: + case TdApi.DisableProxy.CONSTRUCTOR: + case TdApi.EditProxy.CONSTRUCTOR: + case TdApi.RemoveProxy.CONSTRUCTOR: + case TdApi.PingProxy.CONSTRUCTOR: + case TdApi.TestProxy.CONSTRUCTOR: + case TdApi.EnableProxy.CONSTRUCTOR: + case TdApi.GetProxyLink.CONSTRUCTOR: + case TdApi.GetProxies.CONSTRUCTOR: + case TdApi.LogOut.CONSTRUCTOR: + case TdApi.SetTdlibParameters.CONSTRUCTOR: + case TdApi.CheckAuthenticationCode.CONSTRUCTOR: + case TdApi.CheckAuthenticationBotToken.CONSTRUCTOR: + case TdApi.CheckAuthenticationEmailCode.CONSTRUCTOR: + case TdApi.CheckAuthenticationPassword.CONSTRUCTOR: + case TdApi.CheckAuthenticationPasswordRecoveryCode.CONSTRUCTOR: + case TdApi.SetAuthenticationPhoneNumber.CONSTRUCTOR: + case TdApi.GetCountries.CONSTRUCTOR: + case TdApi.GetCountryCode.CONSTRUCTOR: + case TdApi.GetDatabaseStatistics.CONSTRUCTOR: + case TdApi.GetDeepLinkInfo.CONSTRUCTOR: + case TdApi.ParseMarkdown.CONSTRUCTOR: + case TdApi.ParseTextEntities.CONSTRUCTOR: + case TdApi.GetJsonString.CONSTRUCTOR: + case TdApi.GetJsonValue.CONSTRUCTOR: + case TdApi.GetLoginUrl.CONSTRUCTOR: + case TdApi.GetLoginUrlInfo.CONSTRUCTOR: + case TdApi.GetMarkdownText.CONSTRUCTOR: + case TdApi.GetMemoryStatistics.CONSTRUCTOR: + case TdApi.GetRecoveryEmailAddress.CONSTRUCTOR: + case TdApi.GetStorageStatistics.CONSTRUCTOR: + case TdApi.GetStorageStatisticsFast.CONSTRUCTOR: + return false; + } + return true; + } + /** * Sends a request to TDLib and get the result. * @@ -248,7 +303,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra * @throws NullPointerException if function is null. */ public void send(TdApi.Function function, GenericResultHandler resultHandler) { - client.send(function, result -> resultHandler.onResult(Result.of(result)), this::handleResultHandlingException); + this.send(function, resultHandler, null); } /** @@ -262,6 +317,30 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra */ public void send(TdApi.Function function, GenericResultHandler resultHandler, ExceptionHandler resultHandlerExceptionHandler) { + if (shouldWaitForReadiness(function)) { + ready.whenComplete((ignored, error) -> { + if (error != null) { + resultHandler.onResult(Result.ofError(error)); + } else { + this.sendUnsafe(function, resultHandler, resultHandlerExceptionHandler); + } + }); + } else { + this.sendUnsafe(function, resultHandler, resultHandlerExceptionHandler); + } + } + + /** + * Sends a request to TDLib and get the result. + * + * @param function The request to TDLib. + * @param resultHandler Result handler. If it is null, nothing will be called. + * @param resultHandlerExceptionHandler Handle exceptions thrown inside the result handler. + * If it is null, the default exception handler will be called. + * @throws NullPointerException if function is null. + */ + public void sendUnsafe(TdApi.Function function, GenericResultHandler resultHandler, + ExceptionHandler resultHandlerExceptionHandler) { if (resultHandlerExceptionHandler == null) { resultHandlerExceptionHandler = this::handleResultHandlingException; } @@ -276,6 +355,22 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra */ @SuppressWarnings("unchecked") public CompletableFuture send(TdApi.Function function) { + CompletableFuture future = new CompletableFuture<>(); + if (shouldWaitForReadiness(function)) { + return ready.thenCompose(r -> this.sendUnsafe(function)); + } else { + return this.sendUnsafe(function); + } + } + + /** + * Sends a request to TDLib and get the result. + * + * @param function The request to TDLib. + * @throws NullPointerException if function is null. + */ + @SuppressWarnings("unchecked") + public CompletableFuture sendUnsafe(TdApi.Function function) { CompletableFuture future = new CompletableFuture<>(); client.send(function, result -> { if (result instanceof TdApi.Error) { @@ -296,14 +391,19 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra * @throws NullPointerException if function is null. */ public CompletableFuture sendMessage(TdApi.SendMessage function, boolean wait) { - return this.send(function).thenCompose(msg -> { - CompletableFuture future = new CompletableFuture<>(); - CompletableFuture prev = temporaryMessages.put(new TemporaryMessageURL(msg.chatId, msg.id), future); - if (prev != null) { - prev.completeExceptionally(new IllegalStateException("Another temporary message has the same id")); - } - return future; - }); + CompletableFuture sendRequest = this.send(function); + if (wait) { + return sendRequest.thenCompose(msg -> { + CompletableFuture future = new CompletableFuture<>(); + CompletableFuture prev = temporaryMessages.put(new TemporaryMessageURL(msg.chatId, msg.id), future); + if (prev != null) { + prev.completeExceptionally(new IllegalStateException("Another temporary message has the same id")); + } + return future; + }); + } else { + return sendRequest; + } } /** @@ -318,7 +418,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra /** * Send the close signal but don't wait */ - public CompletableFuture close() { + public CompletableFuture closeAsync() { return this.send(new TdApi.Close()).thenCompose(x -> this.waitForExitAsync()); } @@ -363,11 +463,21 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra return FutureSupport.copy(closed); } + private void onReady() { + this.ready.complete(null); + } + private void onCloseUpdate() { + this.ready.completeExceptionally(new TelegramError(new TdApi.Error(400, "Client closed"))); this.closed.complete(null); this.temporaryMessages.clear(); } + @Override + public void close() throws Exception { + this.closeAsync().get(90, TimeUnit.SECONDS); + } + private final class SimpleTelegramClientInteraction implements ClientInteraction { public SimpleTelegramClientInteraction() { @@ -388,6 +498,10 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra return meGetter.getMe(); } + public CompletableFuture getMeAsync() { + return meGetter.getMeAsync(); + } + public boolean isMainChatsListLoaded() { return mainChatsLoader.isLoaded(); }