TDLight-Java 3

This commit is contained in:
Andrea Cavalli 2023-04-27 02:14:39 +02:00
parent 57a2214c70
commit dba5c107cc
101 changed files with 1149 additions and 1335 deletions

View File

@ -15,7 +15,7 @@
## 💻 Supported platforms ## 💻 Supported platforms
**Java versions**: from Java 17 to Java 19+ (Legacy Java 8+ support with `tdlight-java-8`) **Java versions**: from Java 17 to Java 19+ (Java 8 is supported if you use the following dependency classifier: `jdk8`)
**Operating systems**: Linux, Windows, MacOS **Operating systems**: Linux, Windows, MacOS
@ -84,7 +84,7 @@ If you are using Maven, edit your `pom.xml` file as below:
<!-- Add the following dependencies --> <!-- Add the following dependencies -->
<dependency> <dependency>
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
<artifactId>tdlight-java</artifactId> <!-- Use tdlight-java-8 if you are using java 8 to 16 --> <artifactId>tdlight-java</artifactId> <!-- Java 8 is supported if you use the following dependency classifier: <classifier>jdk8</classifier> -->
<!-- don't specify the version here --> <!-- don't specify the version here -->
</dependency> </dependency>
<dependency> <dependency>
@ -114,7 +114,7 @@ dependencies {
implementation platform('it.tdlight:tdlight-java-bom:VERSION') implementation platform('it.tdlight:tdlight-java-bom:VERSION')
// do not specify the versions on the dependencies below! // do not specify the versions on the dependencies below!
implementation 'it.tdlight:tdlight-java' // Use tdlight-java-8 if you are using java 8 to 16 implementation 'it.tdlight:tdlight-java' // Java 8 is supported if you use the following dependency classifier: `jdk8`
implementation 'it.tdlight:tdlight-natives-linux-amd64' implementation 'it.tdlight:tdlight-natives-linux-amd64'
// Include other native versions that you want, for example for windows, osx, ... // Include other native versions that you want, for example for windows, osx, ...
} }

View File

@ -7,7 +7,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<name>TDLight Java BOM</name> <name>TDLight Java BOM</name>
<properties> <properties>
<revision>1.1.0.0-SNAPSHOT</revision> <revision>3.0.0.0-SNAPSHOT</revision>
<nativesSsl3Suffix/> <nativesSsl3Suffix/>
<nativesRevisionNumber>307</nativesRevisionNumber> <nativesRevisionNumber>307</nativesRevisionNumber>
<apiRevisionNumber>305</apiRevisionNumber> <apiRevisionNumber>305</apiRevisionNumber>
@ -81,8 +81,9 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
<artifactId>tdlight-java-8</artifactId> <artifactId>tdlight-java</artifactId>
<version>${revision}</version> <version>${revision}</version>
<classifier>jdk8</classifier>
</dependency> </dependency>
<dependency> <dependency>
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
@ -137,7 +138,7 @@
</dependencyManagement> </dependencyManagement>
<modules> <modules>
<module>../parent</module> <module>../</module>
</modules> </modules>
<build> <build>

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
<artifactId>example-java</artifactId> <artifactId>example-java</artifactId>
<version>1.1.0.0-SNAPSHOT</version> <version>3.0.0.0-SNAPSHOT</version>
<name>TDLight Java Example</name> <name>TDLight Java Example</name>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
@ -26,7 +26,7 @@
<dependency> <dependency>
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
<artifactId>tdlight-java-bom</artifactId> <artifactId>tdlight-java-bom</artifactId>
<version>2.8.10.4</version> <version>3.0.0.0-SNAPSHOT</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
@ -36,7 +36,8 @@
<!-- TDLight --> <!-- TDLight -->
<dependency> <dependency>
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
<artifactId>tdlight-java</artifactId> <!-- Use tdlight-java-8 for java 8 to 16 --> <artifactId>tdlight-java</artifactId>
<classifier>jdk8</classifier>
</dependency> </dependency>
<!-- TDLight natives --> <!-- TDLight natives -->

View File

@ -1,10 +1,10 @@
package it.tdlight.example; package it.tdlight.example;
import it.tdlight.common.Init; import it.tdlight.Init;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.common.utils.CantLoadLibrary; import it.tdlight.utils.CantLoadLibrary;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.tdlight.ClientManager; import it.tdlight.ClientFactory;
/** /**
* This is an advanced example that uses directly the native client without using the SimpleClient implementation * This is an advanced example that uses directly the native client without using the SimpleClient implementation
@ -15,8 +15,11 @@ public class AdvancedExample {
// Initialize TDLight native libraries // Initialize TDLight native libraries
Init.start(); Init.start();
// Create a client // Create a client manager, it should be closed before shutdown
TelegramClient client = ClientManager.create(); ClientFactory clientManager = new ClientFactory();
// Create a client, it should be closed before shutdown
TelegramClient client = clientManager.createClient();
// Initialize the client // Initialize the client
client.initialize(AdvancedExample::onUpdate, AdvancedExample::onUpdateError, AdvancedExample::onError); client.initialize(AdvancedExample::onUpdate, AdvancedExample::onUpdateError, AdvancedExample::onError);

View File

@ -1,14 +1,17 @@
package it.tdlight.example; package it.tdlight.example;
import it.tdlight.client.*; import it.tdlight.client.*;
import it.tdlight.client.AuthenticationData; import it.tdlight.client.AuthenticationSupplier;
import it.tdlight.client.CommandHandler; import it.tdlight.client.CommandHandler;
import it.tdlight.client.SimpleTelegramClient; import it.tdlight.client.SimpleTelegramClient;
import it.tdlight.client.TDLibSettings; import it.tdlight.client.TDLibSettings;
import it.tdlight.common.Init; import it.tdlight.Init;
import it.tdlight.common.Log; import it.tdlight.jni.TdApi.AuthorizationState;
import it.tdlight.common.utils.CantLoadLibrary; import it.tdlight.jni.TdApi.Chat;
import it.tdlight.jni.TdApi.MessageContent;
import it.tdlight.utils.CantLoadLibrary;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
/** /**
@ -29,37 +32,45 @@ public final class Example {
// Initialize TDLight native libraries // Initialize TDLight native libraries
Init.start(); Init.start();
// Obtain the API token // Create the client factory
var apiToken = APIToken.example(); try (SimpleTelegramClientFactory clientFactory = new SimpleTelegramClientFactory()) {
// Configure the client // Obtain the API token
var settings = TDLibSettings.create(apiToken); //
// var apiToken = new APIToken(your-api-id-here, "your-api-hash-here");
//
APIToken apiToken = APIToken.example();
// Configure the session directory
var sessionPath = Paths.get("example-tdlight-session");
settings.setDatabaseDirectoryPath(sessionPath.resolve("data"));
settings.setDownloadedFilesDirectoryPath(sessionPath.resolve("downloads"));
// Create a client // Configure the client
client = new SimpleTelegramClient(settings); TDLibSettings settings = TDLibSettings.create(apiToken);
// Configure the authentication info // Configure the session directory
var authenticationData = AuthenticationData.consoleLogin(); Path sessionPath = Paths.get("example-tdlight-session");
settings.setDatabaseDirectoryPath(sessionPath.resolve("data"));
settings.setDownloadedFilesDirectoryPath(sessionPath.resolve("downloads"));
// Add an example update handler that prints when the bot is started // Prepare a new client builder
client.addUpdateHandler(TdApi.UpdateAuthorizationState.class, Example::onUpdateAuthorizationState); SimpleTelegramClientBuilder clientBuilder = clientFactory.builder(settings);
// Add an example update handler that prints every received message // Configure the authentication info
client.addUpdateHandler(TdApi.UpdateNewMessage.class, Example::onUpdateNewMessage); ConsoleInteractiveAuthenticationData authenticationData = AuthenticationSupplier.consoleLogin();
// Add an example command handler that stops the bot // Add an example update handler that prints when the bot is started
client.addCommandHandler("stop", new StopCommandHandler()); clientBuilder.addUpdateHandler(TdApi.UpdateAuthorizationState.class, Example::onUpdateAuthorizationState);
// Start the client // Add an example update handler that prints every received message
client.start(authenticationData); clientBuilder.addUpdateHandler(TdApi.UpdateNewMessage.class, Example::onUpdateNewMessage);
// Wait for exit // Add an example command handler that stops the bot
client.waitForExit(); clientBuilder.addCommandHandler("stop", new StopCommandHandler());
// Create and start the client
client = clientBuilder.build(authenticationData);
// Wait for exit
client.waitForExit();
}
} }
/** /**
@ -67,7 +78,7 @@ public final class Example {
*/ */
private static void onUpdateNewMessage(TdApi.UpdateNewMessage update) { private static void onUpdateNewMessage(TdApi.UpdateNewMessage update) {
// Get the message content // Get the message content
var messageContent = update.message.content; MessageContent messageContent = update.message.content;
// Get the message text // Get the message text
String text; String text;
@ -82,9 +93,9 @@ public final class Example {
// Get the chat title // Get the chat title
client.send(new TdApi.GetChat(update.message.chatId), chatIdResult -> { client.send(new TdApi.GetChat(update.message.chatId), chatIdResult -> {
// Get the chat response // Get the chat response
var chat = chatIdResult.get(); Chat chat = chatIdResult.get();
// Get the chat name // Get the chat name
var chatName = chat.title; String chatName = chat.title;
// Print the message // Print the message
System.out.printf("Received new message from chat %s: %s%n", chatName, text); System.out.printf("Received new message from chat %s: %s%n", chatName, text);
@ -111,7 +122,7 @@ public final class Example {
* Print the bot status * Print the bot status
*/ */
private static void onUpdateAuthorizationState(TdApi.UpdateAuthorizationState update) { private static void onUpdateAuthorizationState(TdApi.UpdateAuthorizationState update) {
var authorizationState = update.authorizationState; AuthorizationState authorizationState = update.authorizationState;
if (authorizationState instanceof TdApi.AuthorizationStateReady) { if (authorizationState instanceof TdApi.AuthorizationStateReady) {
System.out.println("Logged in"); System.out.println("Logged in");
} else if (authorizationState instanceof TdApi.AuthorizationStateClosing) { } else if (authorizationState instanceof TdApi.AuthorizationStateClosing) {

View File

@ -8,10 +8,10 @@
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
<version>${revision}</version> <version>${revision}</version>
<artifactId>tdlight-java-bom</artifactId> <artifactId>tdlight-java-bom</artifactId>
<relativePath>../bom/pom.xml</relativePath> <relativePath>bom/pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
<revision>1.1.0.0-SNAPSHOT</revision> <revision>3.0.0.0-SNAPSHOT</revision>
<zxing.version>3.5.0</zxing.version> <zxing.version>3.5.0</zxing.version>
<reactive.streams.version>1.0.4</reactive.streams.version> <reactive.streams.version>1.0.4</reactive.streams.version>
<slf4j.api.version>2.0.5</slf4j.api.version> <slf4j.api.version>2.0.5</slf4j.api.version>
@ -19,8 +19,7 @@
<fastutil.core.version>8.5.11</fastutil.core.version> <fastutil.core.version>8.5.11</fastutil.core.version>
</properties> </properties>
<modules> <modules>
<module>../tdlight-java</module> <module>tdlight-java</module>
<module>../tdlight-java-8</module>
</modules> </modules>
<build> <build>

View File

@ -22,7 +22,7 @@ fi
cd "../../" cd "../../"
cd "bom" cd "bom"
mvn -B -Drevision="${REVISION}${SSL_SUFFIX}" -DnativesSsl3Suffix="${SSL_SUFFIX}" clean deploy mvn -B -Drevision="${REVISION}${SSL_SUFFIX}" -DnativesSsl3Suffix="${SSL_SUFFIX}" -P "java8,java17" clean deploy
cd "../" cd "../"
echo "Done." echo "Done."

View File

@ -4,7 +4,7 @@
cd "../../" cd "../../"
cd "bom" cd "bom"
mvn -B clean deploy mvn -B -P "java8,java17" clean deploy
cd "../" cd "../"
echo "Done." echo "Done."

View File

@ -1,8 +0,0 @@
package it.tdlight.client;
import java.util.function.Consumer;
public interface ClientInteraction {
void onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo, Consumer<String> result);
}

View File

@ -1,127 +0,0 @@
package it.tdlight.client;
import it.tdlight.common.utils.ScannerUtils;
import java.util.Locale;
public final class ConsoleInteractiveAuthenticationData implements AuthenticationData {
private static final Object LOCK = new Object();
private boolean initialized = false;
private boolean isQr;
private boolean isBot;
private String botToken;
private String phoneNumber;
ConsoleInteractiveAuthenticationData() {
}
public void askData() {
initializeIfNeeded();
}
public boolean isInitialized() {
return initialized;
}
@Override
public boolean isQrCode() {
initializeIfNeeded();
return isQr;
}
@Override
public boolean isBot() {
initializeIfNeeded();
return isBot;
}
@Override
public String getUserPhoneNumber() {
initializeIfNeeded();
if (isBot || isQr) {
throw new UnsupportedOperationException("This is not a user");
}
return phoneNumber;
}
@Override
public String getBotToken() {
initializeIfNeeded();
if (!isBot || isQr) {
throw new UnsupportedOperationException("This is not a bot");
}
return botToken;
}
private void initializeIfNeeded() {
if (initialized) {
return;
}
synchronized (LOCK) {
if (initialized) {
return;
}
String choice;
// Choose login type
String mode;
do {
String response = ScannerUtils.askParameter("login",
"Do you want to login using a bot [token], a [phone] number, or a [qr] code? [token/phone/qr]");
if (response != null) {
choice = response.trim().toLowerCase(Locale.ROOT);
switch (choice) {
case "phone":
mode = "PHONE";
break;
case "token":
mode = "TOKEN";
break;
case "qr":
mode = "QR";
break;
default:
mode = null;
break;
}
} else {
mode = null;
}
} while (mode == null);
if ("TOKEN".equals(mode)) {
String token;
do {
token = ScannerUtils.askParameter("login", "Please type the bot token");
} while (token.length() < 5 || !token.contains(":"));
this.isBot = true;
this.phoneNumber = null;
this.botToken = token;
this.isQr = false;
} else if ("PHONE".equals(mode)) {
String phoneNumber;
do {
phoneNumber = ScannerUtils.askParameter("login", "Please type your phone number");
} while (phoneNumber.length() < 3);
this.isBot = false;
this.phoneNumber = phoneNumber;
this.botToken = null;
this.isQr = false;
} else {
this.isBot = false;
this.phoneNumber = null;
this.botToken = null;
this.isQr = true;
}
initialized = true;
}
}
}

View File

@ -1,127 +0,0 @@
package it.tdlight.client;
import it.tdlight.common.utils.ScannerUtils;
import it.tdlight.jni.TdApi.TermsOfService;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class ScannerClientInteraction implements ClientInteraction {
private static final Logger LOG = LoggerFactory.getLogger(ScannerClientInteraction.class);
private final ExecutorService blockingExecutor;
private final Authenticable authenticable;
public ScannerClientInteraction(ExecutorService blockingExecutor, Authenticable authenticable) {
this.blockingExecutor = blockingExecutor;
this.authenticable = authenticable;
}
@Override
public void onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo, Consumer<String> resultCons) {
authenticable.getAuthenticationData(authenticationData -> {
blockingExecutor.execute(() -> {
String who;
boolean useRealWho;
if (authenticationData instanceof ConsoleInteractiveAuthenticationData) {
useRealWho = ((ConsoleInteractiveAuthenticationData) authenticationData).isInitialized();
} else {
useRealWho = true;
}
if (!useRealWho) {
who = "login";
} else if (authenticationData.isQrCode()) {
who = "QR login";
} else if (authenticationData.isBot()) {
who = authenticationData.getBotToken().split(":", 2)[0];
} else {
who = "+" + authenticationData.getUserPhoneNumber();
}
String question;
boolean trim = false;
switch (parameter) {
case ASK_FIRST_NAME:
question = "Enter first name";
trim = true;
break;
case ASK_LAST_NAME:
question = "Enter last name";
trim = true;
break;
case ASK_CODE:
question = "Enter authentication code";
ParameterInfoCode codeInfo = ((ParameterInfoCode) parameterInfo);
question += "\n\tPhone number: " + codeInfo.getPhoneNumber();
question += "\n\tTimeout: " + codeInfo.getTimeout() + " seconds";
question += "\n\tCode type: " + codeInfo.getType().getClass().getSimpleName()
.replace("AuthenticationCodeType", "");
if (codeInfo.getNextType() != null) {
question += "\n\tNext code type: " + codeInfo
.getNextType()
.getClass()
.getSimpleName()
.replace("AuthenticationCodeType", "");
}
trim = true;
break;
case ASK_PASSWORD:
question = "Enter your password";
String passwordMessage = "Password authorization:";
String hint = ((ParameterInfoPasswordHint) parameterInfo).getHint();
if (hint != null && !hint.isEmpty()) {
passwordMessage += "\n\tHint: " + hint;
}
boolean hasRecoveryEmailAddress = ((ParameterInfoPasswordHint) parameterInfo)
.hasRecoveryEmailAddress();
passwordMessage += "\n\tHas recovery email: " + hasRecoveryEmailAddress;
String recoveryEmailAddressPattern = ((ParameterInfoPasswordHint) parameterInfo)
.getRecoveryEmailAddressPattern();
if (recoveryEmailAddressPattern != null && !recoveryEmailAddressPattern.isEmpty()) {
passwordMessage += "\n\tRecovery email address pattern: " + recoveryEmailAddressPattern;
}
System.out.println(passwordMessage);
break;
case NOTIFY_LINK:
String link = ((ParameterInfoNotifyLink) parameterInfo).getLink();
System.out.println("Please confirm this login link on another device: " + link);
System.out.println();
try {
System.out.println(QrCodeTerminal.getQr(link));
System.out.println();
} catch (NoClassDefFoundError ex) {
LOG.warn("QR code library is missing!"
+ " Please add the following dependency to your project: com.google.zxing:core");
}
resultCons.accept("");
return;
case TERMS_OF_SERVICE:
TermsOfService tos = ((ParameterInfoTermsOfService) parameterInfo).getTermsOfService();
question = "Terms of service:\n\t" + tos.text.text;
if (tos.minUserAge > 0) {
question += "\n\tMinimum user age: " + tos.minUserAge;
}
if (tos.showPopup) {
question += "\nPlease press enter.";
trim = true;
} else {
System.out.println(question);
resultCons.accept("");
return;
}
break;
default:
question = parameter.toString();
break;
}
String result = ScannerUtils.askParameter(who, question);
if (trim) {
resultCons.accept(result.trim());
} else {
resultCons.accept(result);
}
});
});
}
}

View File

@ -1,16 +0,0 @@
package it.tdlight.common;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Function;
import it.tdlight.tdnative.NativeClient;
final class NativeClientAccess extends NativeClient {
public static <R extends TdApi.Object> TdApi.Object execute(Function<R> function) {
return nativeClientExecute(function);
}
public static void setLogMessageHandler(int maxVerbosityLevel, LogMessageHandler logMessageHandler) {
nativeClientSetLogMessageHandler(maxVerbosityLevel, logMessageHandler);
}
}

View File

@ -1,30 +0,0 @@
package it.tdlight.common.internal;
import it.tdlight.common.ReactiveTelegramClient;
import it.tdlight.common.TelegramClient;
public abstract class CommonClientManager {
private static InternalClientManager getClientManager(String implementationName) {
// ClientManager is singleton
return InternalClientManager.get(implementationName);
}
public synchronized static TelegramClient create(String implementationName) {
InternalClient client = new InternalClient(getClientManager(implementationName));
return create(client);
}
public synchronized static ReactiveTelegramClient createReactive(String implementationName) {
InternalReactiveClient reactiveClient = new InternalReactiveClient(getClientManager(implementationName));
return createReactive(reactiveClient);
}
private static TelegramClient create(InternalClient internalClient) {
return internalClient;
}
private static ReactiveTelegramClient createReactive(InternalReactiveClient internalReactiveClient) {
return internalReactiveClient;
}
}

View File

@ -1,161 +0,0 @@
package it.tdlight.common.internal;
import it.tdlight.common.ClientEventsHandler;
import it.tdlight.common.Init;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class InternalClientManager implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(InternalClientManager.class);
private static final AtomicReference<InternalClientManager> INSTANCE = new AtomicReference<>(null);
private final AtomicBoolean startCalled = new AtomicBoolean();
private final AtomicBoolean closeCalled = new AtomicBoolean();
private final String implementationName;
private final ResponseReceiver responseReceiver;
private final ConcurrentHashMap<Integer, ClientEventsHandler> registeredClientEventHandlers = new ConcurrentHashMap<>();
private final AtomicLong currentQueryId = new AtomicLong();
private InternalClientManager(String implementationName) {
this.implementationName = implementationName;
responseReceiver = new NativeResponseReceiver(this::handleClientEvents);
}
/**
* @return true if started as a result of this call
*/
public boolean startIfNeeded() {
if (closeCalled.get()) {
return false;
}
if (startCalled.compareAndSet(false, true)) {
try {
Init.start();
} catch (Throwable ex) {
ex.printStackTrace();
System.exit(1);
}
responseReceiver.start();
return true;
} else {
return false;
}
}
public static InternalClientManager get(String implementationName) {
return INSTANCE.updateAndGet(val -> {
if (val == null) {
return new InternalClientManager(implementationName);
}
return val;
});
}
private void handleClientEvents(int clientId,
boolean isClosed,
long[] clientEventIds,
TdApi.Object[] clientEvents,
int arrayOffset,
int arrayLength) {
ClientEventsHandler handler = registeredClientEventHandlers.get(clientId);
if (handler != null) {
handler.handleEvents(isClosed, clientEventIds, clientEvents, arrayOffset, arrayLength);
} else {
java.util.List<DroppedEvent> droppedEvents = getEffectivelyDroppedEvents(clientEventIds,
clientEvents,
arrayOffset,
arrayLength
);
if (!droppedEvents.isEmpty()) {
logger.error("Unknown client id \"{}\"! {} events have been dropped!", clientId, droppedEvents.size());
for (DroppedEvent droppedEvent : droppedEvents) {
logger.error("The following event, with id \"{}\", has been dropped: {}",
droppedEvent.id,
droppedEvent.event
);
}
}
}
if (isClosed) {
logger.trace("Removing Client {} from event handlers", clientId);
registeredClientEventHandlers.remove(clientId);
logger.trace("Removed Client {} from event handlers", clientId);
}
}
/**
* Get only events that have been dropped, ignoring synthetic errors related to the closure of a client
*/
private List<DroppedEvent> getEffectivelyDroppedEvents(long[] clientEventIds,
TdApi.Object[] clientEvents,
int arrayOffset,
int arrayLength) {
java.util.List<DroppedEvent> droppedEvents = new ArrayList<>(arrayLength);
for (int i = arrayOffset; i < arrayOffset + arrayLength; i++) {
long id = clientEventIds[i];
TdApi.Object event = clientEvents[i];
boolean mustPrintError = true;
if (event instanceof TdApi.Error) {
TdApi.Error errorEvent = (TdApi.Error) event;
if (Objects.equals("Request aborted", errorEvent.message)) {
mustPrintError = false;
}
}
if (mustPrintError) {
droppedEvents.add(new DroppedEvent(id, event));
}
}
return droppedEvents;
}
public void registerClient(int clientId, ClientEventsHandler internalClient) {
this.startIfNeeded();
boolean replaced = registeredClientEventHandlers.put(clientId, internalClient) != null;
if (replaced) {
throw new IllegalStateException("Client " + clientId + " already registered");
}
responseReceiver.registerClient(clientId);
}
public String getImplementationName() {
return implementationName;
}
public long getNextQueryId() {
return currentQueryId.updateAndGet(value -> (value >= Long.MAX_VALUE ? 0 : value) + 1);
}
@Override
public void close() throws InterruptedException {
if (startCalled.get()) {
if (closeCalled.compareAndSet(false, true)) {
responseReceiver.close();
}
}
}
private static final class DroppedEvent {
private final long id;
private final TdApi.Object event;
private DroppedEvent(long id, Object event) {
this.id = id;
this.event = event;
}
}
}

View File

@ -1,21 +0,0 @@
package it.tdlight.tdlib;
import it.tdlight.common.ReactiveTelegramClient;
import it.tdlight.common.TelegramClient;
import it.tdlight.common.internal.CommonClientManager;
/**
* Interface for interaction with TDLib.
*/
public class ClientManager extends CommonClientManager {
private static final String implementationName = "tdlib";
public static TelegramClient create() {
return CommonClientManager.create(implementationName);
}
public static ReactiveTelegramClient createReactive() {
return CommonClientManager.createReactive(implementationName);
}
}

View File

@ -1,7 +0,0 @@
package it.tdlight.tdlib;
/**
* This class is used to avoid jigsaw errors about empty exports
*/
@Deprecated
public class DummyClass {}

View File

@ -1,21 +0,0 @@
package it.tdlight.tdlight;
import it.tdlight.common.ReactiveTelegramClient;
import it.tdlight.common.TelegramClient;
import it.tdlight.common.internal.CommonClientManager;
/**
* Interface for interaction with TDLight.
*/
public class ClientManager extends CommonClientManager {
private static final String implementationName = "tdlight";
public static TelegramClient create() {
return CommonClientManager.create(implementationName);
}
public static ReactiveTelegramClient createReactive() {
return CommonClientManager.createReactive(implementationName);
}
}

View File

@ -1,7 +0,0 @@
package it.tdlight.tdlight;
/**
* This class is used to avoid jigsaw errors about empty exports
*/
@Deprecated
public class DummyClass {}

View File

@ -1,151 +0,0 @@
# ---> Eclipse
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
# ---> Java
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# ---> Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# Created by https://www.gitignore.io/api/intellij+all
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# End of https://www.gitignore.io/api/intellij+all

View File

@ -1,346 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>tdlight-java-8</artifactId>
<name>TDLight Java Legacy Wrapper</name>
<packaging>jar</packaging>
<parent>
<groupId>it.tdlight</groupId>
<version>${revision}</version>
<artifactId>tdlight-java-parent</artifactId>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<revision>1.1.0.0-SNAPSHOT</revision>
<nativesSsl3Suffix/>
</properties>
<repositories>
<repository>
<id>mchv-release</id>
<name>MCHV Release Apache Maven Packages</name>
<url>https://mvn.mchv.eu/repository/mchv</url>
</repository>
<repository>
<id>mchv-snapshot</id>
<name>MCHV Snapshot Apache Maven Packages</name>
<url>https://mvn.mchv.eu/repository/mchv-snapshot</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>mchv-release-distribution</id>
<name>MCHV Release Apache Maven Packages Distribution</name>
<url>https://mvn.mchv.eu/repository/mchv</url>
</repository>
<snapshotRepository>
<id>mchv-snapshot-distribution</id>
<name>MCHV Snapshot Apache Maven Packages Distribution</name>
<url>https://mvn.mchv.eu/repository/mchv-snapshot</url>
</snapshotRepository>
</distributionManagement>
<scm>
<connection>scm:git:https://git.ignuranza.net/tdlight-team/tdlight-java.git</connection>
<developerConnection>scm:git:https://git.ignuranza.net/tdlight-team/tdlight-java.git</developerConnection>
<tag>HEAD</tag>
</scm>
<dependencies>
<dependency>
<groupId>it.tdlight</groupId>
<artifactId>tdlight-api-legacy</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.api.version}</version>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
<version>${reactive.streams.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil-core</artifactId>
<version>${fastutil.core.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>it.tdlight</groupId>
<artifactId>tdlight-java-bom</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>
<profile>
<id>releaseDir</id>
<build>
<directory>target-release</directory>
</build>
</profile>
<profile>
<id>snapshotDir</id>
<build>
<directory>target-snapshot</directory>
</build>
</profile>
</profiles>
<build>
<sourceDirectory>../src/main/java</sourceDirectory>
<testSourceDirectory>../src/test/java</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<source>17</source>
<additionalOptions>-html5</additionalOptions>
<level>public</level>
<verbose>false</verbose>
<quiet>true</quiet>
<additionalOptions>-Xdoclint:none</additionalOptions>
<additionalJOption>-Xdoclint:none</additionalJOption>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- ensure the project is compiling with JDK 9+ -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M3</version>
<executions>
<execution>
<id>enforce-jdk9</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>[1.9,)</version>
<message>JDK 9+ is required for compilation</message>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<!-- compile sources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<encoding>UTF-8</encoding>
<excludes>
<exclude>it/tdlight/tdlib/ClientManager.java</exclude>
</excludes>
<release>8</release>
</configuration>
<executions>
<!-- disable default phase due to fixed id and position in lifecycle -->
<execution>
<id>default-compile</id>
<phase>none</phase>
<!-- specify source/target for IDE integration -->
<configuration>
<release>9</release>
</configuration>
</execution>
<!-- compile sources with Java 11 -->
<execution>
<id>java-11-module-compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>11</release>
<compileSourceRoots>
<compileSourceRoot>../src/main/java11</compileSourceRoot>
</compileSourceRoots>
<multiReleaseOutput>true</multiReleaseOutput>
</configuration>
</execution>
<!-- compile sources with Java 9 to generate and validate module-info.java -->
<execution>
<id>java-9-module-compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>9</release>
</configuration>
</execution>
<!-- recompile sources as Java 8 to overwrite Java 9 class files, except module-info.java -->
<execution>
<id>java-8-compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<!-- specify JDK 9+ release flag to ensure no classes/methods later than Java 8 are used accidentally -->
<release>8</release>
<!-- exclude module-info.java from the compilation, as it is unsupported by Java 8 -->
<excludes>
<exclude>it/tdlight/tdlib/ClientManager.java</exclude>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.1.0</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>oss</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>3.0.0-M1</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>filtering-java-templates</id>
<goals>
<goal>filter-sources</goal>
</goals>
<configuration>
<sourceDirectory>../src/main/java-templates-tdlight</sourceDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.codehaus.mojo
</groupId>
<artifactId>
flatten-maven-plugin
</artifactId>
<versionRange>
[1.1.0,)
</versionRange>
<goals>
<goal>flatten</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -8,11 +8,11 @@
<groupId>it.tdlight</groupId> <groupId>it.tdlight</groupId>
<version>${revision}</version> <version>${revision}</version>
<artifactId>tdlight-java-parent</artifactId> <artifactId>tdlight-java-parent</artifactId>
<relativePath>../parent/pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<revision>1.1.0.0-SNAPSHOT</revision> <revision>3.0.0.0-SNAPSHOT</revision>
<nativesSsl3Suffix/> <nativesSsl3Suffix/>
</properties> </properties>
<repositories> <repositories>
@ -45,10 +45,6 @@
<tag>HEAD</tag> <tag>HEAD</tag>
</scm> </scm>
<dependencies> <dependencies>
<dependency>
<groupId>it.tdlight</groupId>
<artifactId>tdlight-api-sealed</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
@ -77,6 +73,12 @@
<version>${fastutil.core.version}</version> <version>${fastutil.core.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>io.projectreactor.tools</groupId>
<artifactId>blockhound</artifactId>
<version>1.0.7.RELEASE</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
@ -102,20 +104,109 @@
<directory>target-snapshot</directory> <directory>target-snapshot</directory>
</build> </build>
</profile> </profile>
<profile>
<id>java8</id>
<activation>
<jdk>[,17)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>it.tdlight</groupId>
<artifactId>tdlight-api-legacy</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>jdk8</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
<classifier>jdk8</classifier>
<excludes>
<exclude>module-info.class</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<executions>
<execution>
<id>jdk8</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<excludes>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>java17</id>
<activation>
<jdk>[17,)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>it.tdlight</groupId>
<artifactId>tdlight-api-sealed</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles> </profiles>
<build> <build>
<sourceDirectory>../src/main/java</sourceDirectory>
<testSourceDirectory>../src/test/java</testSourceDirectory>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId> <artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version> <version>3.1.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId> <artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version> <version>3.0.2</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version> <version>3.2.1</version>
<executions> <executions>
@ -128,6 +219,7 @@
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version> <version>3.4.1</version>
<configuration> <configuration>
@ -177,9 +269,6 @@
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<excludes>
<exclude>it/tdlight/tdlib/ClientManager.java</exclude>
</excludes>
<release>8</release> <release>8</release>
</configuration> </configuration>
<executions> <executions>
@ -201,7 +290,7 @@
<configuration> <configuration>
<release>11</release> <release>11</release>
<compileSourceRoots> <compileSourceRoots>
<compileSourceRoot>../src/main/java11</compileSourceRoot> <compileSourceRoot>${project.basedir}/src/main/java11</compileSourceRoot>
</compileSourceRoots> </compileSourceRoots>
<multiReleaseOutput>true</multiReleaseOutput> <multiReleaseOutput>true</multiReleaseOutput>
</configuration> </configuration>
@ -227,7 +316,6 @@
<release>8</release> <release>8</release>
<!-- exclude module-info.java from the compilation, as it is unsupported by Java 8 --> <!-- exclude module-info.java from the compilation, as it is unsupported by Java 8 -->
<excludes> <excludes>
<exclude>it/tdlight/tdlib/ClientManager.java</exclude>
<exclude>module-info.java</exclude> <exclude>module-info.java</exclude>
</excludes> </excludes>
</configuration> </configuration>
@ -260,21 +348,12 @@
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-jar-plugin</artifactId> <groupId>org.apache.maven.plugins</groupId>
<version>3.2.0</version>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId> <artifactId>maven-install-plugin</artifactId>
<version>3.0.0-M1</version> <version>3.0.0-M1</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId> <artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version> <version>2.8.2</version>
</plugin> </plugin>
@ -289,7 +368,7 @@
<goal>filter-sources</goal> <goal>filter-sources</goal>
</goals> </goals>
<configuration> <configuration>
<sourceDirectory>../src/main/java-templates-tdlight</sourceDirectory> <sourceDirectory>${project.basedir}/src/main/java-templates</sourceDirectory>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>

View File

@ -1,4 +1,4 @@
package it.tdlight.common.utils; package it.tdlight.utils;
public final class LibraryVersion { public final class LibraryVersion {

View File

@ -1,8 +1,8 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.common.utils.IntSwapper; import it.tdlight.utils.IntSwapper;
public class ArrayUtil { class ArrayUtil {
public interface IntComparator { public interface IntComparator {
int compare(int k1, int k2); int compare(int k1, int k2);

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;

View File

@ -0,0 +1,175 @@
package it.tdlight;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object;
import it.tdlight.utils.CantLoadLibrary;
import it.tdlight.utils.CleanSupport;
import it.tdlight.utils.CleanSupport.CleanableSupport;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TDLight client factory
*/
public class ClientFactory implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(ClientFactory.class);
private static volatile ClientFactory COMMON;
private final InternalClientsState state = new InternalClientsState() {
@Override
public void registerClient(int clientId, ClientEventsHandler internalClient) {
startIfNeeded();
super.registerClient(clientId, internalClient);
responseReceiver.registerClient(clientId);
}
};
private final ResponseReceiver responseReceiver = new NativeResponseReceiver(this::handleClientEvents);
private volatile CleanableSupport cleanable;
public static ClientFactory getCommonClientFactory() {
ClientFactory common = COMMON;
if (common == null) {
synchronized (ClientFactory.class) {
if (COMMON == null) {
COMMON = new ClientFactory() {
@Override
public void close() {
throw new UnsupportedOperationException("Common client factory can't be closed");
}
};
}
common = COMMON;
}
}
return common;
}
public ClientFactory() {
try {
Init.start();
} catch (CantLoadLibrary e) {
throw new RuntimeException("Can't load the client factory because TDLight can't be loaded", e);
}
}
public TelegramClient createClient() {
return new InternalClient(state);
}
public ReactiveTelegramClient createReactive() {
return new InternalReactiveClient(state);
}
public void startIfNeeded() {
if (state.shouldStartNow()) {
try {
Init.start();
responseReceiver.start();
this.cleanable = CleanSupport.register(responseReceiver, () -> {
try {
this.responseReceiver.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
state.setStarted();
} catch (Throwable ex) {
state.setStopped();
logger.error("Failed to start TDLight", ex);
}
}
}
private void handleClientEvents(int clientId,
boolean isClosed,
long[] clientEventIds,
TdApi.Object[] clientEvents,
int arrayOffset,
int arrayLength) {
ClientEventsHandler handler = state.getClientEventsHandler(clientId);
if (handler != null) {
handler.handleEvents(isClosed, clientEventIds, clientEvents, arrayOffset, arrayLength);
} else {
java.util.List<DroppedEvent> droppedEvents = getEffectivelyDroppedEvents(clientEventIds,
clientEvents,
arrayOffset,
arrayLength
);
if (!droppedEvents.isEmpty()) {
logger.error("Unknown client id \"{}\"! {} events have been dropped!", clientId, droppedEvents.size());
for (DroppedEvent droppedEvent : droppedEvents) {
logger.error("The following event, with id \"{}\", has been dropped: {}",
droppedEvent.id,
droppedEvent.event
);
}
}
}
if (isClosed) {
logger.trace("Removing Client {} from event handlers", clientId);
state.removeClientEventHandlers(clientId);
logger.trace("Removed Client {} from event handlers", clientId);
}
}
/**
* Get only events that have been dropped, ignoring synthetic errors related to the closure of a client
*/
private List<DroppedEvent> getEffectivelyDroppedEvents(long[] clientEventIds,
TdApi.Object[] clientEvents,
int arrayOffset,
int arrayLength) {
java.util.List<DroppedEvent> droppedEvents = new ArrayList<>(arrayLength);
for (int i = arrayOffset; i < arrayOffset + arrayLength; i++) {
long id = clientEventIds[i];
TdApi.Object event = clientEvents[i];
boolean mustPrintError = true;
if (event instanceof TdApi.Error) {
TdApi.Error errorEvent = (TdApi.Error) event;
if (Objects.equals("Request aborted", errorEvent.message)) {
mustPrintError = false;
}
}
if (mustPrintError) {
droppedEvents.add(new DroppedEvent(id, event));
}
}
return droppedEvents;
}
protected void closeInternal() {
if (state.shouldCloseNow()) {
try {
cleanable.clean();
} catch (Throwable e) {
logger.error("Failed to close", e);
}
this.state.setStopped();
}
}
@Override
public void close() {
this.closeInternal();
}
private static final class DroppedEvent {
private final long id;
private final TdApi.Object event;
private DroppedEvent(long id, Object event) {
this.id = id;
this.event = event;
}
}
}

View File

@ -15,7 +15,7 @@
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>. * along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/ */
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import java.lang.reflect.Field; import java.lang.reflect.Field;

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
/** /**
* Interface for handler of exceptions thrown while invoking ResultHandler. By default, all such exceptions are ignored. * Interface for handler of exceptions thrown while invoking ResultHandler. By default, all such exceptions are ignored.

View File

@ -1,10 +1,10 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.ResultHandler; import it.tdlight.ResultHandler;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
public final class Handler<R extends TdApi.Object> { final class Handler<R extends TdApi.Object> {
private final ResultHandler<R> resultHandler; private final ResultHandler<R> resultHandler;
private final ExceptionHandler exceptionHandler; private final ExceptionHandler exceptionHandler;

View File

@ -15,17 +15,13 @@
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>. * along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/ */
package it.tdlight.common; package it.tdlight;
import it.tdlight.client.SimpleTelegramClient; import it.tdlight.utils.CantLoadLibrary;
import it.tdlight.common.utils.CantLoadLibrary; import it.tdlight.utils.LoadLibrary;
import it.tdlight.common.utils.LoadLibrary;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.LogStreamEmpty; import it.tdlight.jni.TdApi.LogStreamEmpty;
import it.tdlight.jni.TdApi.SetLogStream; import it.tdlight.jni.TdApi.SetLogStream;
import it.tdlight.jni.TdApi.SetLogVerbosityLevel; import it.tdlight.jni.TdApi.SetLogVerbosityLevel;
import it.tdlight.tdnative.NativeClient.LogMessageHandler;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,10 +1,5 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.common.ClientEventsHandler;
import it.tdlight.common.ExceptionHandler;
import it.tdlight.common.ResultHandler;
import it.tdlight.common.TelegramClient;
import it.tdlight.common.UpdatesHandler;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Function; import it.tdlight.jni.TdApi.Function;
import it.tdlight.jni.TdApi.Object; import it.tdlight.jni.TdApi.Object;
@ -18,7 +13,7 @@ import org.slf4j.LoggerFactory;
import org.slf4j.Marker; import org.slf4j.Marker;
import org.slf4j.MarkerFactory; import org.slf4j.MarkerFactory;
public final class InternalClient implements ClientEventsHandler, TelegramClient { final class InternalClient implements ClientEventsHandler, TelegramClient {
private static final Marker TG_MARKER = MarkerFactory.getMarker("TG"); private static final Marker TG_MARKER = MarkerFactory.getMarker("TG");
private static final Logger logger = LoggerFactory.getLogger(TelegramClient.class); private static final Logger logger = LoggerFactory.getLogger(TelegramClient.class);
@ -28,15 +23,15 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
private final Thread shutdownHook = new Thread(this::onJVMShutdown); private final Thread shutdownHook = new Thread(this::onJVMShutdown);
private volatile Integer clientId = null; private volatile Integer clientId = null;
private final InternalClientManager clientManager; private final InternalClientsState clientManagerState;
private Handler<TdApi.Update> updateHandler; private Handler<TdApi.Update> updateHandler;
private MultiHandler updatesHandler; private MultiHandler updatesHandler;
private ExceptionHandler defaultExceptionHandler; private ExceptionHandler defaultExceptionHandler;
private final AtomicBoolean isClosed = new AtomicBoolean(); private final AtomicBoolean isClosed = new AtomicBoolean();
public InternalClient(InternalClientManager clientManager) { public InternalClient(InternalClientsState clientManagerState) {
this.clientManager = clientManager; this.clientManagerState = clientManagerState;
Runtime.getRuntime().addShutdownHook(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownHook);
} }
@ -153,11 +148,13 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
} }
private void createAndRegisterClient() { private void createAndRegisterClient() {
if (clientId != null) { synchronized (this) {
throw new UnsupportedOperationException("Can't initialize the same client twice!"); if (clientId != null) {
throw new UnsupportedOperationException("Can't initialize the same client twice!");
}
clientId = NativeClientAccess.create();
} }
clientId = NativeClientAccess.create(); clientManagerState.registerClient(clientId, this);
clientManager.registerClient(clientId, this);
logger.info(TG_MARKER, "Registered new client {}", clientId); logger.info(TG_MARKER, "Registered new client {}", clientId);
// Send a dummy request to start TDLib // Send a dummy request to start TDLib
@ -178,7 +175,7 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
"Can't send a request to TDLib before calling \"initialize\" function!")); "Can't send a request to TDLib before calling \"initialize\" function!"));
return; return;
} }
long queryId = clientManager.getNextQueryId(); long queryId = clientManagerState.getNextQueryId();
if (resultHandler != null) { if (resultHandler != null) {
handlers.put(queryId, new Handler<>(resultHandler, exceptionHandler)); handlers.put(queryId, new Handler<>(resultHandler, exceptionHandler));
} }

View File

@ -0,0 +1,63 @@
package it.tdlight;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class InternalClientsState {
static final int STATE_INITIAL = 0;
static final int STATE_STARTING = 1;
static final int STATE_STARTED = 2;
static final int STATE_STOPPING = 3;
static final int STATE_STOPPED = 4;
private final AtomicInteger runState = new AtomicInteger();
private final AtomicLong currentQueryId = new AtomicLong();
private final ConcurrentHashMap<Integer, ClientEventsHandler> registeredClientEventHandlers = new ConcurrentHashMap<>();
public long getNextQueryId() {
return currentQueryId.updateAndGet(value -> (value >= Long.MAX_VALUE ? 0 : value) + 1);
}
public void registerClient(int clientId, ClientEventsHandler internalClient) {
boolean replaced = registeredClientEventHandlers.put(clientId, internalClient) != null;
if (replaced) {
throw new IllegalStateException("Client " + clientId + " already registered");
}
}
public ClientEventsHandler getClientEventsHandler(int clientId) {
return registeredClientEventHandlers.get(clientId);
}
public boolean shouldStartNow() {
return runState.compareAndSet(STATE_INITIAL, STATE_STARTING);
}
public void setStopped() {
runState.set(STATE_STOPPED);
}
public void setStarted() {
if (!runState.compareAndSet(STATE_STARTING, STATE_STARTED)) {
throw new IllegalStateException();
}
}
public void removeClientEventHandlers(int clientId) {
registeredClientEventHandlers.remove(clientId);
}
public boolean shouldCloseNow() {
int prevS = runState.getAndUpdate(prev -> {
if (prev == STATE_INITIAL) {
return STATE_STOPPED;
} else if (prev == STATE_STARTED) {
return STATE_STOPPING;
} else {
return prev;
}
});
return prevS == STATE_STARTED;
}
}

View File

@ -1,10 +1,5 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.common.ClientEventsHandler;
import it.tdlight.common.ExceptionHandler;
import it.tdlight.common.ReactiveTelegramClient;
import it.tdlight.common.Signal;
import it.tdlight.common.SignalListener;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Error; import it.tdlight.jni.TdApi.Error;
import it.tdlight.jni.TdApi.Function; import it.tdlight.jni.TdApi.Function;
@ -26,7 +21,7 @@ import org.slf4j.LoggerFactory;
import org.slf4j.Marker; import org.slf4j.Marker;
import org.slf4j.MarkerFactory; import org.slf4j.MarkerFactory;
public final class InternalReactiveClient implements ClientEventsHandler, ReactiveTelegramClient { final class InternalReactiveClient implements ClientEventsHandler, ReactiveTelegramClient {
private static final Marker TG_MARKER = MarkerFactory.getMarker("TG"); private static final Marker TG_MARKER = MarkerFactory.getMarker("TG");
private static final Logger logger = LoggerFactory.getLogger(InternalReactiveClient.class); private static final Logger logger = LoggerFactory.getLogger(InternalReactiveClient.class);
@ -41,15 +36,15 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
private final Thread shutdownHook = new Thread(this::onJVMShutdown); private final Thread shutdownHook = new Thread(this::onJVMShutdown);
private volatile Integer clientId = null; private volatile Integer clientId = null;
private final InternalClientManager clientManager; private final InternalClientsState clientManagerState;
private final AtomicBoolean alreadyReceivedClosed = new AtomicBoolean(); private final AtomicBoolean alreadyReceivedClosed = new AtomicBoolean();
// This field is not volatile, but it's not problematic, because ReplayStartupUpdatesListener is able to forward // This field is not volatile, but it's not problematic, because ReplayStartupUpdatesListener is able to forward
// updates to the right listener // updates to the right listener
private SignalListener signalListener = new ReplayStartupUpdatesListener(); private SignalListener signalListener = new ReplayStartupUpdatesListener();
public InternalReactiveClient(InternalClientManager clientManager) { public InternalReactiveClient(InternalClientsState clientManagerState) {
this.clientManager = clientManager; this.clientManagerState = clientManagerState;
this.updateHandler = new Handler<>(this::onUpdateFromHandler, this::onUpdateException); this.updateHandler = new Handler<>(this::onUpdateFromHandler, this::onUpdateException);
this.defaultExceptionHandler = this::onDefaultException; this.defaultExceptionHandler = this::onDefaultException;
try { try {
@ -167,7 +162,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
logger.debug(TG_MARKER, "Creating new client"); logger.debug(TG_MARKER, "Creating new client");
clientId = NativeClientAccess.create(); clientId = NativeClientAccess.create();
logger.debug(TG_MARKER, "Registering new client {}", clientId); logger.debug(TG_MARKER, "Registering new client {}", clientId);
clientManager.registerClient(clientId, this); clientManagerState.registerClient(clientId, this);
logger.debug(TG_MARKER, "Registered new client {}", clientId); logger.debug(TG_MARKER, "Registered new client {}", clientId);
} }
@ -193,7 +188,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
subscriber.onError(new IllegalStateException( subscriber.onError(new IllegalStateException(
"Can't send a request to TDLib before calling \"createAndRegisterClient\" function!")); "Can't send a request to TDLib before calling \"createAndRegisterClient\" function!"));
} else { } else {
long queryId = clientManager.getNextQueryId(); long queryId = clientManagerState.getNextQueryId();
// Handle timeout // Handle timeout
ScheduledFuture<?> timeoutFuture = timers.schedule(() -> { ScheduledFuture<?> timeoutFuture = timers.schedule(() -> {
@ -275,7 +270,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
this.signalListener = listener; this.signalListener = listener;
TdApi.GetAuthorizationState query = new TdApi.GetAuthorizationState(); TdApi.GetAuthorizationState query = new TdApi.GetAuthorizationState();
long queryId = clientManager.getNextQueryId(); long queryId = clientManagerState.getNextQueryId();
// Send a dummy request to effectively start the TDLib session // Send a dummy request to effectively start the TDLib session
{ {
@ -303,7 +298,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
private void sendCloseAndIgnoreResponse() { private void sendCloseAndIgnoreResponse() {
if (!alreadyReceivedClosed.get()) { if (!alreadyReceivedClosed.get()) {
TdApi.Close query = new TdApi.Close(); TdApi.Close query = new TdApi.Close();
long queryId = clientManager.getNextQueryId(); long queryId = clientManagerState.getNextQueryId();
handlers.put(queryId, EMPTY_HANDLER); handlers.put(queryId, EMPTY_HANDLER);
logger.trace(TG_MARKER, "Client {} is requesting with query id {}: {}", clientId, queryId, query); logger.trace(TG_MARKER, "Client {} is requesting with query id {}: {}", clientId, queryId, query);

View File

@ -1,15 +1,12 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.LogStreamDefault; import it.tdlight.jni.TdApi.LogStreamDefault;
import it.tdlight.jni.TdApi.LogStreamFile; import it.tdlight.jni.TdApi.LogStreamFile;
import it.tdlight.jni.TdApi.SetLogVerbosityLevel; import it.tdlight.jni.TdApi.SetLogVerbosityLevel;
import it.tdlight.tdnative.NativeClient;
import it.tdlight.tdnative.NativeClient.LogMessageHandler; import it.tdlight.tdnative.NativeClient.LogMessageHandler;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/** /**
* Class used for managing internal TDLib logging. Use TdApi.*Log* methods instead. * Class used for managing internal TDLib logging. Use TdApi.*Log* methods instead.

View File

@ -1,9 +1,9 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.UpdatesHandler; import it.tdlight.UpdatesHandler;
public final class MultiHandler { final class MultiHandler {
private final UpdatesHandler updatesHandler; private final UpdatesHandler updatesHandler;
private final ExceptionHandler exceptionHandler; private final ExceptionHandler exceptionHandler;

View File

@ -1,4 +1,4 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Function; import it.tdlight.jni.TdApi.Function;
@ -6,7 +6,7 @@ import it.tdlight.tdnative.NativeClient;
final class NativeClientAccess extends NativeClient { final class NativeClientAccess extends NativeClient {
public static int create() { static int create() {
return NativeClientAccess.createNativeClient(); return NativeClientAccess.createNativeClient();
} }
@ -14,11 +14,11 @@ final class NativeClientAccess extends NativeClient {
return NativeClientAccess.nativeClientExecute(function); return NativeClientAccess.nativeClientExecute(function);
} }
public static <R extends TdApi.Object> void send(int nativeClientId, long eventId, TdApi.Function<R> function) { static <R extends TdApi.Object> void send(int nativeClientId, long eventId, TdApi.Function<R> function) {
NativeClientAccess.nativeClientSend(nativeClientId, eventId, function); NativeClientAccess.nativeClientSend(nativeClientId, eventId, function);
} }
public static int receive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout) { static int receive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout) {
return NativeClientAccess.nativeClientReceive(clientIds, eventIds, events, timeout); return NativeClientAccess.nativeClientReceive(clientIds, eventIds, events, timeout);
} }

View File

@ -1,9 +1,8 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.common.EventsHandler;
import it.tdlight.jni.TdApi.Object; import it.tdlight.jni.TdApi.Object;
public class NativeResponseReceiver extends ResponseReceiver { class NativeResponseReceiver extends ResponseReceiver {
public NativeResponseReceiver(EventsHandler eventsHandler) { public NativeResponseReceiver(EventsHandler eventsHandler) {
super(eventsHandler); super(eventsHandler);

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import java.time.Duration; import java.time.Duration;

View File

@ -15,7 +15,7 @@
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>. * along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/ */
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import java.util.Objects; import java.util.Objects;

View File

@ -1,11 +1,10 @@
package it.tdlight.common.internal; package it.tdlight;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
import it.tdlight.common.EventsHandler; import it.tdlight.utils.IntSwapper;
import it.tdlight.common.utils.IntSwapper; import it.tdlight.utils.SpinWaitSupport;
import it.tdlight.common.utils.SpinWaitSupport;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.UpdateAuthorizationState; import it.tdlight.jni.TdApi.UpdateAuthorizationState;
import java.util.ArrayList; import java.util.ArrayList;
@ -33,9 +32,6 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
} }
} }
private final AtomicBoolean startCalled = new AtomicBoolean();
private final AtomicBoolean closeCalled = new AtomicBoolean();
private final EventsHandler eventsHandler; private final EventsHandler eventsHandler;
private final int[] clientIds = new int[MAX_EVENTS]; private final int[] clientIds = new int[MAX_EVENTS];
private final long[] eventIds = new long[MAX_EVENTS]; private final long[] eventIds = new long[MAX_EVENTS];
@ -64,24 +60,10 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
@Override @Override
public void run() { public void run() {
if (closeCalled.get()) {
throw new IllegalStateException("Closed");
}
if (startCalled.compareAndSet(false, true)) {
this.runInternal();
} else {
throw new IllegalStateException("Start already called");
}
}
private void runInternal() {
int[] sortIndex; int[] sortIndex;
try { try {
boolean interrupted; boolean interrupted;
while ( while (!(interrupted = Thread.interrupted()) && !registeredClients.isEmpty()) {
!(interrupted = Thread.interrupted())
&& (!closeCalled.get() || !registeredClients.isEmpty())
) {
// Timeout is expressed in seconds // Timeout is expressed in seconds
int resultsCount = receive(clientIds, eventIds, events, 2.0); int resultsCount = receive(clientIds, eventIds, events, 2.0);
@ -221,7 +203,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
} }
} }
if (interrupted || closeCalled.get()) { if (interrupted) {
for (Integer clientId : this.registeredClients) { for (Integer clientId : this.registeredClients) {
eventsHandler.handleClientEvents(clientId, true, clientEventIds, clientEvents, 0, 0); eventsHandler.handleClientEvents(clientId, true, clientEventIds, clientEvents, 0, 0);
} }
@ -274,13 +256,9 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
@Override @Override
public void close() throws InterruptedException { public void close() throws InterruptedException {
if (startCalled.get()) { this.closeWait.await();
if (closeCalled.compareAndSet(false, true)) { if (registeredClients.isEmpty()) {
this.closeWait.await(); ResponseReceiver.this.interrupt();
if (registeredClients.isEmpty()) {
this.interrupt();
}
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object; import it.tdlight.jni.TdApi.Object;

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import java.util.Objects; import java.util.Objects;

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
public interface SignalListener { public interface SignalListener {

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
public enum SignalType { public enum SignalType {
UPDATE, EXCEPTION, CLOSE UPDATE, EXCEPTION, CLOSE

View File

@ -1,7 +1,6 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object;
public interface TelegramClient { public interface TelegramClient {

View File

@ -1,4 +1,4 @@
package it.tdlight.common; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import java.util.List; import java.util.List;

View File

@ -4,5 +4,5 @@ import java.util.function.Consumer;
public interface Authenticable { public interface Authenticable {
void getAuthenticationData(Consumer<AuthenticationData> result); AuthenticationSupplier<?> getAuthenticationSupplier();
} }

View File

@ -0,0 +1,12 @@
package it.tdlight.client;
public interface AuthenticationData {
boolean isQrCode();
boolean isBot();
String getUserPhoneNumber();
String getBotToken();
}

View File

@ -1,9 +1,12 @@
package it.tdlight.client; package it.tdlight.client;
import static java.util.concurrent.CompletableFuture.completedFuture;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture;
@SuppressWarnings("unused") @SuppressWarnings("unused")
final class AuthenticationDataImpl implements AuthenticationData { final class AuthenticationDataImpl implements AuthenticationSupplier<AuthenticationDataImpl>, AuthenticationData {
private final String userPhoneNumber; private final String userPhoneNumber;
private final String botToken; private final String botToken;
@ -72,4 +75,9 @@ final class AuthenticationDataImpl implements AuthenticationData {
public int hashCode() { public int hashCode() {
return Objects.hash(userPhoneNumber, botToken); return Objects.hash(userPhoneNumber, botToken);
} }
@Override
public CompletableFuture<AuthenticationDataImpl> get() {
return completedFuture(this);
}
} }

View File

@ -1,6 +1,8 @@
package it.tdlight.client; package it.tdlight.client;
class AuthenticationDataQrCode implements AuthenticationData { import java.util.concurrent.CompletableFuture;
class AuthenticationDataQrCode implements AuthenticationSupplier<AuthenticationDataQrCode>, AuthenticationData {
@Override @Override
public boolean isQrCode() { public boolean isQrCode() {
@ -21,4 +23,9 @@ class AuthenticationDataQrCode implements AuthenticationData {
public String getBotToken() { public String getBotToken() {
throw new UnsupportedOperationException("This is not a bot"); throw new UnsupportedOperationException("This is not a bot");
} }
@Override
public CompletableFuture<AuthenticationDataQrCode> get() {
return CompletableFuture.completedFuture(this);
}
} }

View File

@ -1,16 +1,12 @@
package it.tdlight.client; package it.tdlight.client;
public interface AuthenticationData { import java.util.concurrent.CompletableFuture;
boolean isQrCode(); public interface AuthenticationSupplier<T extends AuthenticationData> {
boolean isBot(); CompletableFuture<T> get();
String getUserPhoneNumber(); static AuthenticationSupplier<?> qrCode() {
String getBotToken();
static AuthenticationData qrCode() {
return new AuthenticationDataQrCode(); return new AuthenticationDataQrCode();
} }
@ -18,15 +14,15 @@ public interface AuthenticationData {
* Deprecated, use {@link #user(String)} instead * Deprecated, use {@link #user(String)} instead
*/ */
@Deprecated @Deprecated
static AuthenticationData user(long userPhoneNumber) { static AuthenticationSupplier<?> user(long userPhoneNumber) {
return user(String.valueOf(userPhoneNumber)); return user(String.valueOf(userPhoneNumber));
} }
static AuthenticationData user(String userPhoneNumber) { static AuthenticationSupplier<?> user(String userPhoneNumber) {
return new AuthenticationDataImpl(userPhoneNumber, null); return new AuthenticationDataImpl(userPhoneNumber, null);
} }
static AuthenticationData bot(String botToken) { static AuthenticationSupplier<?> bot(String botToken) {
return new AuthenticationDataImpl(null, botToken); return new AuthenticationDataImpl(null, botToken);
} }

View File

@ -1,7 +1,6 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationStateReady; import it.tdlight.jni.TdApi.AuthorizationStateReady;
import it.tdlight.jni.TdApi.GetMe; import it.tdlight.jni.TdApi.GetMe;
import it.tdlight.jni.TdApi.UpdateAuthorizationState; import it.tdlight.jni.TdApi.UpdateAuthorizationState;

View File

@ -1,17 +1,11 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationStateReady; import it.tdlight.jni.TdApi.AuthorizationStateReady;
import it.tdlight.jni.TdApi.ChatList; import it.tdlight.jni.TdApi.ChatList;
import it.tdlight.jni.TdApi.ChatListMain;
import it.tdlight.jni.TdApi.Error; import it.tdlight.jni.TdApi.Error;
import it.tdlight.jni.TdApi.GetMe;
import it.tdlight.jni.TdApi.LoadChats; import it.tdlight.jni.TdApi.LoadChats;
import it.tdlight.jni.TdApi.UpdateAuthorizationState; import it.tdlight.jni.TdApi.UpdateAuthorizationState;
import it.tdlight.jni.TdApi.User;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,7 +1,7 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.PhoneNumberAuthenticationSettings; import it.tdlight.jni.TdApi.PhoneNumberAuthenticationSettings;
import it.tdlight.jni.TdApi.SetAuthenticationPhoneNumber; import it.tdlight.jni.TdApi.SetAuthenticationPhoneNumber;
@ -24,7 +24,13 @@ final class AuthorizationStateWaitAuthenticationDataHandler implements GenericUp
@Override @Override
public void onUpdate(UpdateAuthorizationState update) { public void onUpdate(UpdateAuthorizationState update) {
if (update.authorizationState.getConstructor() == TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR) { if (update.authorizationState.getConstructor() == TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR) {
authenticable.getAuthenticationData(this::onAuthData); authenticable.getAuthenticationSupplier().get().whenComplete((authData, ex) -> {
if (ex != null) {
exceptionHandler.onException(ex);
return;
}
this.onAuthData(authData);
});
} }
} }
@ -45,7 +51,13 @@ final class AuthorizationStateWaitAuthenticationDataHandler implements GenericUp
} }
}, exceptionHandler); }, exceptionHandler);
} else { } else {
PhoneNumberAuthenticationSettings phoneSettings = new PhoneNumberAuthenticationSettings(false, false, false, false, null); PhoneNumberAuthenticationSettings phoneSettings = new PhoneNumberAuthenticationSettings(false,
false,
false,
false,
null,
null
);
String phoneNumber = authenticationData.getUserPhoneNumber(); String phoneNumber = authenticationData.getUserPhoneNumber();
SetAuthenticationPhoneNumber response = new SetAuthenticationPhoneNumber(phoneNumber, phoneSettings); SetAuthenticationPhoneNumber response = new SetAuthenticationPhoneNumber(phoneNumber, phoneSettings);

View File

@ -1,7 +1,7 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationStateWaitCode; import it.tdlight.jni.TdApi.AuthorizationStateWaitCode;
import it.tdlight.jni.TdApi.CheckAuthenticationCode; import it.tdlight.jni.TdApi.CheckAuthenticationCode;
@ -30,7 +30,11 @@ final class AuthorizationStateWaitCodeHandler implements GenericUpdateHandler<Up
authorizationState.codeInfo.timeout, authorizationState.codeInfo.timeout,
authorizationState.codeInfo.type authorizationState.codeInfo.type
); );
clientInteraction.onParameterRequest(InputParameter.ASK_CODE, parameterInfo, code -> { clientInteraction.onParameterRequest(InputParameter.ASK_CODE, parameterInfo).whenComplete((code, ex) -> {
if (ex != null) {
exceptionHandler.onException(ex);
return;
}
CheckAuthenticationCode response = new CheckAuthenticationCode(code); CheckAuthenticationCode response = new CheckAuthenticationCode(code);
client.send(response, ok -> { client.send(response, ok -> {
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) { if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {

View File

@ -1,5 +1,6 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.ExceptionHandler;
import it.tdlight.jni.TdApi.AuthorizationStateWaitOtherDeviceConfirmation; import it.tdlight.jni.TdApi.AuthorizationStateWaitOtherDeviceConfirmation;
import it.tdlight.jni.TdApi.UpdateAuthorizationState; import it.tdlight.jni.TdApi.UpdateAuthorizationState;
@ -7,18 +8,24 @@ final class AuthorizationStateWaitOtherDeviceConfirmationHandler implements
GenericUpdateHandler<UpdateAuthorizationState> { GenericUpdateHandler<UpdateAuthorizationState> {
private final ClientInteraction clientInteraction; private final ClientInteraction clientInteraction;
private final ExceptionHandler exceptionHandler;
public AuthorizationStateWaitOtherDeviceConfirmationHandler(ClientInteraction clientInteraction) { public AuthorizationStateWaitOtherDeviceConfirmationHandler(ClientInteraction clientInteraction,
ExceptionHandler exceptionHandler) {
this.clientInteraction = clientInteraction; this.clientInteraction = clientInteraction;
this.exceptionHandler = exceptionHandler;
} }
@Override @Override
public void onUpdate(UpdateAuthorizationState update) { public void onUpdate(UpdateAuthorizationState update) {
if (update.authorizationState.getConstructor() == AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR) { if (update.authorizationState.getConstructor() == AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR) {
AuthorizationStateWaitOtherDeviceConfirmation authorizationState = (AuthorizationStateWaitOtherDeviceConfirmation) update.authorizationState; AuthorizationStateWaitOtherDeviceConfirmation authorizationState
= (AuthorizationStateWaitOtherDeviceConfirmation) update.authorizationState;
ParameterInfo parameterInfo = new ParameterInfoNotifyLink(authorizationState.link); ParameterInfo parameterInfo = new ParameterInfoNotifyLink(authorizationState.link);
clientInteraction.onParameterRequest(InputParameter.NOTIFY_LINK, parameterInfo, ignored -> { clientInteraction.onParameterRequest(InputParameter.NOTIFY_LINK, parameterInfo).whenComplete((ignored, ex) -> {
if (ex != null) {
exceptionHandler.onException(ex);
}
}); });
} }
} }

View File

@ -1,7 +1,7 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationStateWaitPassword; import it.tdlight.jni.TdApi.AuthorizationStateWaitPassword;
import it.tdlight.jni.TdApi.CheckAuthenticationPassword; import it.tdlight.jni.TdApi.CheckAuthenticationPassword;
@ -29,7 +29,11 @@ final class AuthorizationStateWaitPasswordHandler implements GenericUpdateHandle
authorizationState.hasRecoveryEmailAddress, authorizationState.hasRecoveryEmailAddress,
authorizationState.recoveryEmailAddressPattern authorizationState.recoveryEmailAddressPattern
); );
clientInteraction.onParameterRequest(InputParameter.ASK_PASSWORD, parameterInfo, password -> { clientInteraction.onParameterRequest(InputParameter.ASK_PASSWORD, parameterInfo).whenComplete((password, ex) -> {
if (ex != null) {
exceptionHandler.onException(ex);
return;
}
CheckAuthenticationPassword response = new CheckAuthenticationPassword(password); CheckAuthenticationPassword response = new CheckAuthenticationPassword(password);
client.send(response, ok -> { client.send(response, ok -> {
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) { if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {

View File

@ -1,7 +1,7 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationStateWaitRegistration; import it.tdlight.jni.TdApi.AuthorizationStateWaitRegistration;
import it.tdlight.jni.TdApi.RegisterUser; import it.tdlight.jni.TdApi.RegisterUser;
@ -26,9 +26,12 @@ final class AuthorizationStateWaitRegistrationHandler implements GenericUpdateHa
if (update.authorizationState.getConstructor() == AuthorizationStateWaitRegistration.CONSTRUCTOR) { if (update.authorizationState.getConstructor() == AuthorizationStateWaitRegistration.CONSTRUCTOR) {
TdApi.AuthorizationStateWaitRegistration authorizationState = (TdApi.AuthorizationStateWaitRegistration) update.authorizationState; TdApi.AuthorizationStateWaitRegistration authorizationState = (TdApi.AuthorizationStateWaitRegistration) update.authorizationState;
ParameterInfoTermsOfService tos = new ParameterInfoTermsOfService(authorizationState.termsOfService); ParameterInfoTermsOfService tos = new ParameterInfoTermsOfService(authorizationState.termsOfService);
clientInteraction.onParameterRequest(InputParameter.TERMS_OF_SERVICE, tos, ignored -> { clientInteraction
clientInteraction.onParameterRequest(InputParameter.ASK_FIRST_NAME, new EmptyParameterInfo(), firstName -> { .onParameterRequest(InputParameter.TERMS_OF_SERVICE, tos)
clientInteraction.onParameterRequest(InputParameter.ASK_LAST_NAME, new EmptyParameterInfo(), lastName -> { .thenCompose(ignored -> clientInteraction
.onParameterRequest(InputParameter.ASK_FIRST_NAME, new EmptyParameterInfo()))
.thenCompose(firstName -> clientInteraction
.onParameterRequest(InputParameter.ASK_LAST_NAME, new EmptyParameterInfo()).thenAccept(lastName -> {
if (firstName == null || firstName.isEmpty()) { if (firstName == null || firstName.isEmpty()) {
exceptionHandler.onException(new IllegalArgumentException("First name must not be null or empty")); exceptionHandler.onException(new IllegalArgumentException("First name must not be null or empty"));
return; return;
@ -51,9 +54,12 @@ final class AuthorizationStateWaitRegistrationHandler implements GenericUpdateHa
throw new TelegramError((TdApi.Error) ok); throw new TelegramError((TdApi.Error) ok);
} }
}, exceptionHandler); }, exceptionHandler);
}))
.whenComplete((ignored, ex) -> {
if (ex != null) {
exceptionHandler.onException(ex);
}
}); });
});
});
} }
} }
} }

View File

@ -1,10 +1,9 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters; import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters;
import it.tdlight.jni.TdApi.SetTdlibParameters;
import it.tdlight.jni.TdApi.UpdateAuthorizationState; import it.tdlight.jni.TdApi.UpdateAuthorizationState;
final class AuthorizationStateWaitTdlibParametersHandler implements GenericUpdateHandler<UpdateAuthorizationState> { final class AuthorizationStateWaitTdlibParametersHandler implements GenericUpdateHandler<UpdateAuthorizationState> {

View File

@ -0,0 +1,9 @@
package it.tdlight.client;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public interface ClientInteraction {
CompletableFuture<String> onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo);
}

View File

@ -1,6 +1,6 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.TelegramClient; import it.tdlight.TelegramClient;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Chat; import it.tdlight.jni.TdApi.Chat;
import it.tdlight.jni.TdApi.Message; import it.tdlight.jni.TdApi.Message;
@ -11,7 +11,6 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,127 @@
package it.tdlight.client;
import it.tdlight.utils.ScannerUtils;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
public final class ConsoleInteractiveAuthenticationData implements AuthenticationSupplier<AuthenticationData> {
private static final class State implements AuthenticationData {
final boolean isQr;
final boolean isBot;
final String botToken;
final String phoneNumber;
State(boolean isQr, boolean isBot, String botToken, String phoneNumber) {
this.isQr = isQr;
this.isBot = isBot;
this.botToken = botToken;
this.phoneNumber = phoneNumber;
}
@Override
public boolean isQrCode() {
return isQr;
}
@Override
public boolean isBot() {
return isBot;
}
@Override
public String getUserPhoneNumber() {
if (isBot || isQr) {
throw new UnsupportedOperationException("This is not a user");
}
return phoneNumber;
}
@Override
public String getBotToken() {
if (!isBot || isQr) {
throw new UnsupportedOperationException("This is not a bot");
}
return botToken;
}
}
private final AtomicReference<CompletableFuture<AuthenticationData>> state = new AtomicReference<>();
ConsoleInteractiveAuthenticationData() {
}
public CompletableFuture<AuthenticationData> askData() {
return get();
}
public boolean isInitialized() {
CompletableFuture<AuthenticationData> cf = state.get();
return cf != null && cf.isDone();
}
@Override
public CompletableFuture<AuthenticationData> get() {
CompletableFuture<AuthenticationData> cf = new CompletableFuture<AuthenticationData>();
if (state.compareAndSet(null, cf)) {
SequentialRequestsExecutor.getInstance().execute(() -> {
try {
String choice;
// Choose login type
String mode;
do {
String response = ScannerUtils.askParameter("login",
"Do you want to login using a bot [token], a [phone] number, or a [qr] code? [token/phone/qr]");
if (response != null) {
choice = response.trim().toLowerCase(Locale.ROOT);
switch (choice) {
case "phone":
mode = "PHONE";
break;
case "token":
mode = "TOKEN";
break;
case "qr":
mode = "QR";
break;
default:
mode = null;
break;
}
} else {
mode = null;
}
} while (mode == null);
if ("TOKEN".equals(mode)) {
String token;
do {
token = ScannerUtils.askParameter("login", "Please type the bot token");
} while (token.length() < 5 || !token.contains(":"));
cf.complete(new State(false, true, token, null));
} else if ("PHONE".equals(mode)) {
String phoneNumber;
do {
phoneNumber = ScannerUtils.askParameter("login", "Please type your phone number");
} while (phoneNumber.length() < 3);
cf.complete(new State(false, false, null, phoneNumber));
} else {
cf.complete(new State(true, false, null, null));
}
} catch (Throwable ex) {
cf.completeExceptionally(ex);
throw ex;
}
});
return cf;
} else {
return state.get();
}
}
}

View File

@ -0,0 +1,19 @@
package it.tdlight.client;
import it.tdlight.ExceptionHandler;
import it.tdlight.jni.TdApi;
public interface MutableTelegramClient {
void setClientInteraction(ClientInteraction clientInteraction);
<T extends TdApi.Update> void addCommandHandler(String commandName, CommandHandler handler);
<T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler);
void addUpdatesHandler(GenericUpdateHandler<TdApi.Update> handler);
void addUpdateExceptionHandler(ExceptionHandler updateExceptionHandler);
void addDefaultExceptionHandler(ExceptionHandler defaultExceptionHandlers);
}

View File

@ -0,0 +1,129 @@
package it.tdlight.client;
import it.tdlight.utils.ScannerUtils;
import it.tdlight.jni.TdApi.TermsOfService;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class ScannerClientInteraction implements ClientInteraction {
private static final Logger LOG = LoggerFactory.getLogger(ScannerClientInteraction.class);
private final Executor blockingExecutor;
private final Authenticable authenticable;
public ScannerClientInteraction(Executor blockingExecutor, Authenticable authenticable) {
this.blockingExecutor = blockingExecutor;
this.authenticable = authenticable;
}
@Override
public CompletableFuture<String> onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo) {
AuthenticationSupplier<?> authSupplier = authenticable.getAuthenticationSupplier();
AuthenticationData authData = getAuthDataNowOrNull(authSupplier);
return CompletableFuture.supplyAsync(() -> {
String who;
boolean useRealWho = authData != null;
if (!useRealWho) {
who = "login";
} else if (authData.isQrCode()) {
who = "QR login";
} else if (authData.isBot()) {
who = authData.getBotToken().split(":", 2)[0];
} else {
who = "+" + authData.getUserPhoneNumber();
}
String question;
boolean trim = false;
switch (parameter) {
case ASK_FIRST_NAME:
question = "Enter first name";
trim = true;
break;
case ASK_LAST_NAME:
question = "Enter last name";
trim = true;
break;
case ASK_CODE:
question = "Enter authentication code";
ParameterInfoCode codeInfo = ((ParameterInfoCode) parameterInfo);
question += "\n\tPhone number: " + codeInfo.getPhoneNumber();
question += "\n\tTimeout: " + codeInfo.getTimeout() + " seconds";
question += "\n\tCode type: " + codeInfo.getType().getClass().getSimpleName()
.replace("AuthenticationCodeType", "");
if (codeInfo.getNextType() != null) {
question += "\n\tNext code type: " + codeInfo
.getNextType()
.getClass()
.getSimpleName()
.replace("AuthenticationCodeType", "");
}
trim = true;
break;
case ASK_PASSWORD:
question = "Enter your password";
String passwordMessage = "Password authorization:";
String hint = ((ParameterInfoPasswordHint) parameterInfo).getHint();
if (hint != null && !hint.isEmpty()) {
passwordMessage += "\n\tHint: " + hint;
}
boolean hasRecoveryEmailAddress = ((ParameterInfoPasswordHint) parameterInfo)
.hasRecoveryEmailAddress();
passwordMessage += "\n\tHas recovery email: " + hasRecoveryEmailAddress;
String recoveryEmailAddressPattern = ((ParameterInfoPasswordHint) parameterInfo)
.getRecoveryEmailAddressPattern();
if (recoveryEmailAddressPattern != null && !recoveryEmailAddressPattern.isEmpty()) {
passwordMessage += "\n\tRecovery email address pattern: " + recoveryEmailAddressPattern;
}
System.out.println(passwordMessage);
break;
case NOTIFY_LINK:
String link = ((ParameterInfoNotifyLink) parameterInfo).getLink();
System.out.println("Please confirm this login link on another device: " + link);
System.out.println();
try {
System.out.println(QrCodeTerminal.getQr(link));
System.out.println();
} catch (NoClassDefFoundError ex) {
LOG.warn("QR code library is missing!"
+ " Please add the following dependency to your project: com.google.zxing:core");
}
return "";
case TERMS_OF_SERVICE:
TermsOfService tos = ((ParameterInfoTermsOfService) parameterInfo).getTermsOfService();
question = "Terms of service:\n\t" + tos.text.text;
if (tos.minUserAge > 0) {
question += "\n\tMinimum user age: " + tos.minUserAge;
}
if (tos.showPopup) {
question += "\nPlease press enter.";
trim = true;
} else {
System.out.println(question);
return "";
}
break;
default:
question = parameter.toString();
break;
}
String result = ScannerUtils.askParameter(who, question);
if (trim) {
return result.trim();
} else {
return Objects.requireNonNull(result);
}
}, blockingExecutor);
}
private AuthenticationData getAuthDataNowOrNull(AuthenticationSupplier<?> authSupplier) {
try {
return authSupplier.get().getNow(null);
} catch (Throwable ignored) {
return null;
}
}
}

View File

@ -0,0 +1,37 @@
package it.tdlight.client;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
class SequentialRequestsExecutor implements Executor {
private static volatile SequentialRequestsExecutor INSTANCE;
private final Executor executor = Executors.newSingleThreadExecutor(r -> {
final Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
});
private SequentialRequestsExecutor() {
}
public static SequentialRequestsExecutor getInstance() {
SequentialRequestsExecutor instance = INSTANCE;
if (instance == null) {
synchronized (SequentialRequestsExecutor.class) {
if (INSTANCE == null) {
INSTANCE = new SequentialRequestsExecutor();
}
instance = INSTANCE;
}
}
return instance;
}
@Override
public void execute(Runnable command) {
executor.execute(command);
}
}

View File

@ -0,0 +1,25 @@
package it.tdlight.client;
import it.tdlight.ConstructorDetector;
import it.tdlight.ResultHandler;
import it.tdlight.jni.TdApi.Object;
import it.tdlight.jni.TdApi.Update;
class SimpleResultHandler<T extends Update> implements ResultHandler<Update> {
private final int updateConstructor;
private final GenericUpdateHandler<T> handler;
public SimpleResultHandler(Class<T> type, GenericUpdateHandler<T> handler) {
this.updateConstructor = ConstructorDetector.getConstructor(type);
this.handler = handler;
}
@Override
public void onResult(Object update) {
if (update.getConstructor() == updateConstructor) {
//noinspection unchecked
handler.onUpdate((T) update);
}
}
}

View File

@ -1,44 +1,36 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.ConstructorDetector; import it.tdlight.ClientFactory;
import it.tdlight.common.ExceptionHandler; import it.tdlight.ExceptionHandler;
import it.tdlight.common.Init; import it.tdlight.Init;
import it.tdlight.common.Log; import it.tdlight.ResultHandler;
import it.tdlight.common.ResultHandler; import it.tdlight.TelegramClient;
import it.tdlight.common.TelegramClient; import it.tdlight.jni.TdApi.Update;
import it.tdlight.common.internal.CommonClientManager; import it.tdlight.utils.CantLoadLibrary;
import it.tdlight.common.utils.CantLoadLibrary;
import it.tdlight.common.utils.LibraryVersion;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.ChatListArchive; import it.tdlight.jni.TdApi.ChatListArchive;
import it.tdlight.jni.TdApi.ChatListMain; import it.tdlight.jni.TdApi.ChatListMain;
import it.tdlight.jni.TdApi.Function; import it.tdlight.jni.TdApi.Function;
import it.tdlight.jni.TdApi.User; import it.tdlight.jni.TdApi.User;
import it.tdlight.tdnative.NativeClient;
import it.tdlight.tdnative.NativeClient.LogMessageHandler;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class SimpleTelegramClient implements Authenticable { public final class SimpleTelegramClient implements Authenticable, MutableTelegramClient {
public static final Logger LOG = LoggerFactory.getLogger(SimpleTelegramClient.class); public static final Logger LOG = LoggerFactory.getLogger(SimpleTelegramClient.class);
public static ExecutorService blockingExecutor = Executors.newSingleThreadExecutor();
static { static {
try { try {
@ -49,9 +41,9 @@ public final class SimpleTelegramClient implements Authenticable {
} }
private final TelegramClient client; private final TelegramClient client;
private ClientInteraction clientInteraction = new ScannerClientInteraction(blockingExecutor, this); private ClientInteraction clientInteraction;
private final TDLibSettings settings; private final TDLibSettings settings;
private AuthenticationData authenticationData; private AuthenticationSupplier<?> authenticationData;
private final Map<String, Set<CommandHandler>> commandHandlers = new ConcurrentHashMap<>(); private final Map<String, Set<CommandHandler>> commandHandlers = new ConcurrentHashMap<>();
private final Set<ResultHandler<TdApi.Update>> updateHandlers = new ConcurrentHashMap<ResultHandler<TdApi.Update>, Object>().keySet( private final Set<ResultHandler<TdApi.Update>> updateHandlers = new ConcurrentHashMap<ResultHandler<TdApi.Update>, Object>().keySet(
@ -67,9 +59,27 @@ public final class SimpleTelegramClient implements Authenticable {
private final CountDownLatch closed = new CountDownLatch(1); private final CountDownLatch closed = new CountDownLatch(1);
public SimpleTelegramClient(TDLibSettings settings) { SimpleTelegramClient(ClientFactory clientFactory,
this.client = CommonClientManager.create(LibraryVersion.IMPLEMENTATION_NAME); TDLibSettings settings,
this.settings = settings; Map<String, Set<CommandHandler>> commandHandlers,
Set<ResultHandler<Update>> updateHandlers,
Set<ExceptionHandler> updateExceptionHandlers,
Set<ExceptionHandler> defaultExceptionHandlers,
ClientInteraction clientInteraction) {
this.client = clientFactory.createClient();
this.settings = Objects.requireNonNull(settings, "TDLight client settings are null");
this.commandHandlers.putAll(commandHandlers);
this.updateHandlers.addAll(updateHandlers);
this.updateExceptionHandlers.addAll(updateExceptionHandlers);
this.defaultExceptionHandlers.addAll(defaultExceptionHandlers);
if (clientInteraction != null) {
this.clientInteraction = clientInteraction;
} else {
this.clientInteraction = new ScannerClientInteraction(SequentialRequestsExecutor.getInstance(), this);
}
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
new AuthorizationStateWaitTdlibParametersHandler(client, settings, this::handleDefaultException) new AuthorizationStateWaitTdlibParametersHandler(client, settings, this::handleDefaultException)
); );
@ -78,22 +88,24 @@ public final class SimpleTelegramClient implements Authenticable {
); );
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
new AuthorizationStateWaitRegistrationHandler(client, new AuthorizationStateWaitRegistrationHandler(client,
new SimpleTelegramClientInteraction(blockingExecutor), new SimpleTelegramClientInteraction(),
this::handleDefaultException this::handleDefaultException
) )
); );
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
new AuthorizationStateWaitPasswordHandler(client, new AuthorizationStateWaitPasswordHandler(client,
new SimpleTelegramClientInteraction(blockingExecutor), new SimpleTelegramClientInteraction(),
this::handleDefaultException this::handleDefaultException
) )
); );
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
new AuthorizationStateWaitOtherDeviceConfirmationHandler(new SimpleTelegramClientInteraction(blockingExecutor)) new AuthorizationStateWaitOtherDeviceConfirmationHandler(new SimpleTelegramClientInteraction(),
this::handleDefaultException
)
); );
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
new AuthorizationStateWaitCodeHandler(client, new AuthorizationStateWaitCodeHandler(client,
new SimpleTelegramClientInteraction(blockingExecutor), new SimpleTelegramClientInteraction(),
this::handleDefaultException this::handleDefaultException
) )
); );
@ -143,28 +155,16 @@ public final class SimpleTelegramClient implements Authenticable {
} }
@Override @Override
public void getAuthenticationData(Consumer<AuthenticationData> result) { public AuthenticationSupplier<?> getAuthenticationSupplier() {
if (authenticationData instanceof ConsoleInteractiveAuthenticationData) { return authenticationData;
ConsoleInteractiveAuthenticationData consoleInteractiveAuthenticationData
= (ConsoleInteractiveAuthenticationData) authenticationData;
try {
blockingExecutor.execute(() -> {
consoleInteractiveAuthenticationData.askData();
result.accept(consoleInteractiveAuthenticationData);
});
} catch (RejectedExecutionException | NullPointerException ex) {
LOG.error("Failed to execute askData. Returning an empty string", ex);
result.accept(consoleInteractiveAuthenticationData);
}
} else {
result.accept(authenticationData);
}
} }
@Override
public void setClientInteraction(ClientInteraction clientInteraction) { public void setClientInteraction(ClientInteraction clientInteraction) {
this.clientInteraction = clientInteraction; this.clientInteraction = clientInteraction;
} }
@Override
public <T extends TdApi.Update> void addCommandHandler(String commandName, CommandHandler handler) { public <T extends TdApi.Update> void addCommandHandler(String commandName, CommandHandler handler) {
Set<CommandHandler> handlers = this.commandHandlers.computeIfAbsent(commandName, Set<CommandHandler> handlers = this.commandHandlers.computeIfAbsent(commandName,
k -> new ConcurrentHashMap<CommandHandler, Object>().keySet(new Object()) k -> new ConcurrentHashMap<CommandHandler, Object>().keySet(new Object())
@ -172,29 +172,20 @@ public final class SimpleTelegramClient implements Authenticable {
handlers.add(handler); handlers.add(handler);
} }
@SuppressWarnings("unchecked") @Override
public <T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler) { public <T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler) {
int updateConstructor = ConstructorDetector.getConstructor(updateType); this.updateHandlers.add(new SimpleResultHandler<>(updateType, handler));
this.updateHandlers.add(update -> {
if (update.getConstructor() == updateConstructor) {
handler.onUpdate((T) update);
}
});
} }
@Override
public void addUpdatesHandler(GenericUpdateHandler<TdApi.Update> handler) { public void addUpdatesHandler(GenericUpdateHandler<TdApi.Update> handler) {
this.updateHandlers.add(update -> { this.updateHandlers.add(new SimpleUpdateHandler(handler, LOG));
if (update instanceof TdApi.Update) {
handler.onUpdate((TdApi.Update) update);
} else {
LOG.warn("Unknown update type: {}", update);
}
});
} }
/** /**
* Optional handler to handle errors received from TDLib * Optional handler to handle errors received from TDLib
*/ */
@Override
public void addUpdateExceptionHandler(ExceptionHandler updateExceptionHandler) { public void addUpdateExceptionHandler(ExceptionHandler updateExceptionHandler) {
this.updateExceptionHandlers.add(updateExceptionHandler); this.updateExceptionHandlers.add(updateExceptionHandler);
} }
@ -202,6 +193,7 @@ public final class SimpleTelegramClient implements Authenticable {
/** /**
* Optional handler to handle uncaught errors (when using send without an appropriate error handler) * Optional handler to handle uncaught errors (when using send without an appropriate error handler)
*/ */
@Override
public void addDefaultExceptionHandler(ExceptionHandler defaultExceptionHandlers) { public void addDefaultExceptionHandler(ExceptionHandler defaultExceptionHandlers) {
this.defaultExceptionHandlers.add(defaultExceptionHandlers); this.defaultExceptionHandlers.add(defaultExceptionHandlers);
} }
@ -209,7 +201,7 @@ public final class SimpleTelegramClient implements Authenticable {
/** /**
* Start the client * Start the client
*/ */
public void start(AuthenticationData authenticationData) { public void start(AuthenticationSupplier<?> authenticationData) {
this.authenticationData = authenticationData; this.authenticationData = authenticationData;
createDirectories(); createDirectories();
client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException); client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException);
@ -297,19 +289,16 @@ public final class SimpleTelegramClient implements Authenticable {
private final class SimpleTelegramClientInteraction implements ClientInteraction { private final class SimpleTelegramClientInteraction implements ClientInteraction {
private final ExecutorService blockingExecutor; public SimpleTelegramClientInteraction() {
public SimpleTelegramClientInteraction(ExecutorService blockingExecutor) {
this.blockingExecutor = blockingExecutor;
} }
@Override @Override
public void onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo, Consumer<String> result) { public CompletableFuture<String> onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo) {
try { try {
blockingExecutor.execute(() -> clientInteraction.onParameterRequest(parameter, parameterInfo, result)); return clientInteraction.onParameterRequest(parameter, parameterInfo);
} catch (RejectedExecutionException | NullPointerException ex) { } catch (RejectedExecutionException | NullPointerException ex) {
LOG.error("Failed to execute onParameterRequest. Returning an empty string", ex); LOG.error("Failed to execute onParameterRequest. Returning an empty string", ex);
result.accept(""); return CompletableFuture.completedFuture("");
} }
} }
} }

View File

@ -0,0 +1,78 @@
package it.tdlight.client;
import it.tdlight.ClientFactory;
import it.tdlight.ExceptionHandler;
import it.tdlight.ResultHandler;
import it.tdlight.jni.TdApi.Update;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class SimpleTelegramClientBuilder implements MutableTelegramClient {
private static final Logger LOG = LoggerFactory.getLogger(SimpleTelegramClientBuilder.class);
private final ClientFactory clientManager;
private final TDLibSettings clientSettings;
private ClientInteraction clientInteraction;
private final Map<String, Set<CommandHandler>> commandHandlers = new HashMap<>();
private final Set<ResultHandler<Update>> updateHandlers = new HashSet<>();
private final Set<ExceptionHandler> updateExceptionHandlers = new HashSet<>();
private final Set<ExceptionHandler> defaultExceptionHandlers = new HashSet<>();
SimpleTelegramClientBuilder(ClientFactory clientManager, TDLibSettings clientSettings) {
this.clientManager = clientManager;
this.clientSettings = clientSettings;
}
@Override
public void setClientInteraction(ClientInteraction clientInteraction) {
this.clientInteraction = clientInteraction;
}
@Override
public <T extends Update> void addCommandHandler(String commandName, CommandHandler handler) {
commandHandlers.computeIfAbsent(commandName, k -> new HashSet<>()).add(handler);
}
@Override
public <T extends Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler) {
this.updateHandlers.add(new SimpleResultHandler<>(updateType, handler));
}
@Override
public void addUpdatesHandler(GenericUpdateHandler<Update> handler) {
this.updateHandlers.add(new SimpleUpdateHandler(handler, LOG));
}
@Override
public void addUpdateExceptionHandler(ExceptionHandler updateExceptionHandler) {
this.updateExceptionHandlers.add(updateExceptionHandler);
}
@Override
public void addDefaultExceptionHandler(ExceptionHandler defaultExceptionHandlers) {
this.defaultExceptionHandlers.add(defaultExceptionHandlers);
}
/**
* Build and start the client
* @return Telegram client
*/
public SimpleTelegramClient build(AuthenticationSupplier authenticationData) {
SimpleTelegramClient client = new SimpleTelegramClient(clientManager,
clientSettings,
commandHandlers,
updateHandlers,
updateExceptionHandlers,
defaultExceptionHandlers,
clientInteraction
);
client.start(authenticationData);
return client;
}
}

View File

@ -0,0 +1,33 @@
package it.tdlight.client;
import it.tdlight.ClientFactory;
public class SimpleTelegramClientFactory implements AutoCloseable {
private final ClientFactory clientFactory;
private final boolean commonClientFactory;
public SimpleTelegramClientFactory() {
this(null);
}
public SimpleTelegramClientFactory(ClientFactory clientFactory) {
if (clientFactory == null) {
this.clientFactory = ClientFactory.getCommonClientFactory();
this.commonClientFactory = true;
} else {
this.clientFactory = clientFactory;
this.commonClientFactory = false;
}
}
public SimpleTelegramClientBuilder builder(TDLibSettings clientSettings) {
return new SimpleTelegramClientBuilder(clientFactory, clientSettings);
}
@Override
public void close() {
if (!commonClientFactory) {
clientFactory.close();
}
}
}

View File

@ -0,0 +1,27 @@
package it.tdlight.client;
import it.tdlight.ResultHandler;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object;
import it.tdlight.jni.TdApi.Update;
import org.slf4j.Logger;
public class SimpleUpdateHandler implements ResultHandler<Update> {
private final GenericUpdateHandler<Update> handler;
private final Logger logger;
public SimpleUpdateHandler(GenericUpdateHandler<Update> handler, Logger logger) {
this.handler = handler;
this.logger = logger;
}
@Override
public void onResult(Object update) {
if (update instanceof TdApi.Update) {
handler.onUpdate((TdApi.Update) update);
} else {
logger.warn("Unknown update type: {}", update);
}
}
}

View File

@ -1,6 +1,6 @@
package it.tdlight.client; package it.tdlight.client;
import it.tdlight.common.utils.LibraryVersion; import it.tdlight.utils.LibraryVersion;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Locale; import java.util.Locale;

View File

@ -15,7 +15,7 @@
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>. * along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/ */
package it.tdlight.common.utils; package it.tdlight.utils;
/** /**
* Architectures recognized by this library. * Architectures recognized by this library.

View File

@ -15,7 +15,7 @@
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>. * along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/ */
package it.tdlight.common.utils; package it.tdlight.utils;
/** /**
* An exception that is thrown when the LoadLibrary class fails to load the library. * An exception that is thrown when the LoadLibrary class fails to load the library.

View File

@ -0,0 +1,13 @@
package it.tdlight.utils;
public class CleanSupport {
public static CleanableSupport register(Object object, Runnable cleanAction) {
return cleanAction::run;
}
public interface CleanableSupport {
public void clean();
}
}

View File

@ -1,4 +1,4 @@
package it.tdlight.common.utils; package it.tdlight.utils;
public final class IntSwapper { public final class IntSwapper {

View File

@ -15,7 +15,7 @@
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>. * along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/ */
package it.tdlight.common.utils; package it.tdlight.utils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -15,7 +15,7 @@
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>. * along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/ */
package it.tdlight.common.utils; package it.tdlight.utils;
/** /**
* Enumeration with all operating systems recognized by this library. * Enumeration with all operating systems recognized by this library.

View File

@ -1,4 +1,4 @@
package it.tdlight.common.utils; package it.tdlight.utils;
import java.io.Console; import java.io.Console;
import java.io.IOException; import java.io.IOException;

View File

@ -1,4 +1,4 @@
package it.tdlight.common.utils; package it.tdlight.utils;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;

View File

@ -3,11 +3,8 @@ module tdlight.java {
requires org.reactivestreams; requires org.reactivestreams;
requires org.slf4j; requires org.slf4j;
requires static com.google.zxing; requires static com.google.zxing;
exports it.tdlight.tdlight;
exports it.tdlight.tdnative; exports it.tdlight.tdnative;
exports it.tdlight.tdlib; exports it.tdlight;
exports it.tdlight.common; exports it.tdlight.utils;
exports it.tdlight.common.utils;
exports it.tdlight.common.internal;
exports it.tdlight.client; exports it.tdlight.client;
} }

View File

@ -0,0 +1,17 @@
package it.tdlight.utils;
import java.lang.ref.Cleaner;
public class CleanSupport {
private static final Cleaner cleaner = Cleaner.create();
public static CleanableSupport register(Object object, Runnable cleanAction) {
var c = cleaner.register(object, cleanAction);
return c::clean;
}
public interface CleanableSupport {
public void clean();
}
}

View File

@ -1,4 +1,4 @@
package it.tdlight.common.utils; package it.tdlight.utils;
public class SpinWaitSupport { public class SpinWaitSupport {

View File

@ -1,4 +1,4 @@
package it.tdlight.common.internal; package it.tdlight;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import java.util.Objects; import java.util.Objects;

View File

@ -1,22 +1,19 @@
package it.tdlight.common.internal; package it.tdlight;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import it.tdlight.common.EventsHandler; import it.tdlight.ResponseReceiver;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object; import it.tdlight.jni.TdApi.Object;
import it.unimi.dsi.fastutil.longs.LongArraySet; import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class HandleEventsTest { public class HandleEventsTest {

Some files were not shown because too many files have changed in this diff Show More