TDLight-Java 3
This commit is contained in:
parent
57a2214c70
commit
dba5c107cc
@ -15,7 +15,7 @@
|
||||
|
||||
## 💻 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
|
||||
|
||||
@ -84,7 +84,7 @@ If you are using Maven, edit your `pom.xml` file as below:
|
||||
<!-- Add the following dependencies -->
|
||||
<dependency>
|
||||
<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 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -114,7 +114,7 @@ dependencies {
|
||||
implementation platform('it.tdlight:tdlight-java-bom:VERSION')
|
||||
|
||||
// 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'
|
||||
// Include other native versions that you want, for example for windows, osx, ...
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<packaging>pom</packaging>
|
||||
<name>TDLight Java BOM</name>
|
||||
<properties>
|
||||
<revision>1.1.0.0-SNAPSHOT</revision>
|
||||
<revision>3.0.0.0-SNAPSHOT</revision>
|
||||
<nativesSsl3Suffix/>
|
||||
<nativesRevisionNumber>307</nativesRevisionNumber>
|
||||
<apiRevisionNumber>305</apiRevisionNumber>
|
||||
@ -81,8 +81,9 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-java-8</artifactId>
|
||||
<artifactId>tdlight-java</artifactId>
|
||||
<version>${revision}</version>
|
||||
<classifier>jdk8</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
@ -137,7 +138,7 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<modules>
|
||||
<module>../parent</module>
|
||||
<module>../</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>example-java</artifactId>
|
||||
<version>1.1.0.0-SNAPSHOT</version>
|
||||
<version>3.0.0.0-SNAPSHOT</version>
|
||||
<name>TDLight Java Example</name>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
@ -26,7 +26,7 @@
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-java-bom</artifactId>
|
||||
<version>2.8.10.4</version>
|
||||
<version>3.0.0.0-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@ -36,7 +36,8 @@
|
||||
<!-- TDLight -->
|
||||
<dependency>
|
||||
<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>
|
||||
|
||||
<!-- TDLight natives -->
|
||||
|
@ -1,10 +1,10 @@
|
||||
package it.tdlight.example;
|
||||
|
||||
import it.tdlight.common.Init;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.common.utils.CantLoadLibrary;
|
||||
import it.tdlight.Init;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.utils.CantLoadLibrary;
|
||||
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
|
||||
@ -15,8 +15,11 @@ public class AdvancedExample {
|
||||
// Initialize TDLight native libraries
|
||||
Init.start();
|
||||
|
||||
// Create a client
|
||||
TelegramClient client = ClientManager.create();
|
||||
// Create a client manager, it should be closed before shutdown
|
||||
ClientFactory clientManager = new ClientFactory();
|
||||
|
||||
// Create a client, it should be closed before shutdown
|
||||
TelegramClient client = clientManager.createClient();
|
||||
|
||||
// Initialize the client
|
||||
client.initialize(AdvancedExample::onUpdate, AdvancedExample::onUpdateError, AdvancedExample::onError);
|
||||
|
@ -1,14 +1,17 @@
|
||||
package it.tdlight.example;
|
||||
|
||||
import it.tdlight.client.*;
|
||||
import it.tdlight.client.AuthenticationData;
|
||||
import it.tdlight.client.AuthenticationSupplier;
|
||||
import it.tdlight.client.CommandHandler;
|
||||
import it.tdlight.client.SimpleTelegramClient;
|
||||
import it.tdlight.client.TDLibSettings;
|
||||
import it.tdlight.common.Init;
|
||||
import it.tdlight.common.Log;
|
||||
import it.tdlight.common.utils.CantLoadLibrary;
|
||||
import it.tdlight.Init;
|
||||
import it.tdlight.jni.TdApi.AuthorizationState;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.MessageContent;
|
||||
import it.tdlight.utils.CantLoadLibrary;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
@ -29,37 +32,45 @@ public final class Example {
|
||||
// Initialize TDLight native libraries
|
||||
Init.start();
|
||||
|
||||
// Obtain the API token
|
||||
var apiToken = APIToken.example();
|
||||
// Create the client factory
|
||||
try (SimpleTelegramClientFactory clientFactory = new SimpleTelegramClientFactory()) {
|
||||
|
||||
// Configure the client
|
||||
var settings = TDLibSettings.create(apiToken);
|
||||
// Obtain the API token
|
||||
//
|
||||
// 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
|
||||
client = new SimpleTelegramClient(settings);
|
||||
// Configure the client
|
||||
TDLibSettings settings = TDLibSettings.create(apiToken);
|
||||
|
||||
// Configure the authentication info
|
||||
var authenticationData = AuthenticationData.consoleLogin();
|
||||
// Configure the session directory
|
||||
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
|
||||
client.addUpdateHandler(TdApi.UpdateAuthorizationState.class, Example::onUpdateAuthorizationState);
|
||||
// Prepare a new client builder
|
||||
SimpleTelegramClientBuilder clientBuilder = clientFactory.builder(settings);
|
||||
|
||||
// Add an example update handler that prints every received message
|
||||
client.addUpdateHandler(TdApi.UpdateNewMessage.class, Example::onUpdateNewMessage);
|
||||
// Configure the authentication info
|
||||
ConsoleInteractiveAuthenticationData authenticationData = AuthenticationSupplier.consoleLogin();
|
||||
|
||||
// Add an example command handler that stops the bot
|
||||
client.addCommandHandler("stop", new StopCommandHandler());
|
||||
// Add an example update handler that prints when the bot is started
|
||||
clientBuilder.addUpdateHandler(TdApi.UpdateAuthorizationState.class, Example::onUpdateAuthorizationState);
|
||||
|
||||
// Start the client
|
||||
client.start(authenticationData);
|
||||
// Add an example update handler that prints every received message
|
||||
clientBuilder.addUpdateHandler(TdApi.UpdateNewMessage.class, Example::onUpdateNewMessage);
|
||||
|
||||
// Wait for exit
|
||||
client.waitForExit();
|
||||
// Add an example command handler that stops the bot
|
||||
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) {
|
||||
// Get the message content
|
||||
var messageContent = update.message.content;
|
||||
MessageContent messageContent = update.message.content;
|
||||
|
||||
// Get the message text
|
||||
String text;
|
||||
@ -82,9 +93,9 @@ public final class Example {
|
||||
// Get the chat title
|
||||
client.send(new TdApi.GetChat(update.message.chatId), chatIdResult -> {
|
||||
// Get the chat response
|
||||
var chat = chatIdResult.get();
|
||||
Chat chat = chatIdResult.get();
|
||||
// Get the chat name
|
||||
var chatName = chat.title;
|
||||
String chatName = chat.title;
|
||||
|
||||
// Print the message
|
||||
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
|
||||
*/
|
||||
private static void onUpdateAuthorizationState(TdApi.UpdateAuthorizationState update) {
|
||||
var authorizationState = update.authorizationState;
|
||||
AuthorizationState authorizationState = update.authorizationState;
|
||||
if (authorizationState instanceof TdApi.AuthorizationStateReady) {
|
||||
System.out.println("Logged in");
|
||||
} else if (authorizationState instanceof TdApi.AuthorizationStateClosing) {
|
||||
|
@ -8,10 +8,10 @@
|
||||
<groupId>it.tdlight</groupId>
|
||||
<version>${revision}</version>
|
||||
<artifactId>tdlight-java-bom</artifactId>
|
||||
<relativePath>../bom/pom.xml</relativePath>
|
||||
<relativePath>bom/pom.xml</relativePath>
|
||||
</parent>
|
||||
<properties>
|
||||
<revision>1.1.0.0-SNAPSHOT</revision>
|
||||
<revision>3.0.0.0-SNAPSHOT</revision>
|
||||
<zxing.version>3.5.0</zxing.version>
|
||||
<reactive.streams.version>1.0.4</reactive.streams.version>
|
||||
<slf4j.api.version>2.0.5</slf4j.api.version>
|
||||
@ -19,8 +19,7 @@
|
||||
<fastutil.core.version>8.5.11</fastutil.core.version>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>../tdlight-java</module>
|
||||
<module>../tdlight-java-8</module>
|
||||
<module>tdlight-java</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
@ -22,7 +22,7 @@ fi
|
||||
cd "../../"
|
||||
|
||||
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 "../"
|
||||
|
||||
echo "Done."
|
||||
|
@ -4,7 +4,7 @@
|
||||
cd "../../"
|
||||
|
||||
cd "bom"
|
||||
mvn -B clean deploy
|
||||
mvn -B -P "java8,java17" clean deploy
|
||||
cd "../"
|
||||
|
||||
echo "Done."
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package it.tdlight.tdlib;
|
||||
|
||||
/**
|
||||
* This class is used to avoid jigsaw errors about empty exports
|
||||
*/
|
||||
@Deprecated
|
||||
public class DummyClass {}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package it.tdlight.tdlight;
|
||||
|
||||
/**
|
||||
* This class is used to avoid jigsaw errors about empty exports
|
||||
*/
|
||||
@Deprecated
|
||||
public class DummyClass {}
|
151
tdlight-java-8/.gitignore
vendored
151
tdlight-java-8/.gitignore
vendored
@ -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
|
@ -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>
|
@ -8,11 +8,11 @@
|
||||
<groupId>it.tdlight</groupId>
|
||||
<version>${revision}</version>
|
||||
<artifactId>tdlight-java-parent</artifactId>
|
||||
<relativePath>../parent/pom.xml</relativePath>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<revision>1.1.0.0-SNAPSHOT</revision>
|
||||
<revision>3.0.0.0-SNAPSHOT</revision>
|
||||
<nativesSsl3Suffix/>
|
||||
</properties>
|
||||
<repositories>
|
||||
@ -45,10 +45,6 @@
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-api-sealed</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
@ -77,6 +73,12 @@
|
||||
<version>${fastutil.core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.tools</groupId>
|
||||
<artifactId>blockhound</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@ -102,20 +104,109 @@
|
||||
<directory>target-snapshot</directory>
|
||||
</build>
|
||||
</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>
|
||||
<build>
|
||||
<sourceDirectory>../src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>../src/test/java</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
@ -128,6 +219,7 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<configuration>
|
||||
@ -177,9 +269,6 @@
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<excludes>
|
||||
<exclude>it/tdlight/tdlib/ClientManager.java</exclude>
|
||||
</excludes>
|
||||
<release>8</release>
|
||||
</configuration>
|
||||
<executions>
|
||||
@ -201,7 +290,7 @@
|
||||
<configuration>
|
||||
<release>11</release>
|
||||
<compileSourceRoots>
|
||||
<compileSourceRoot>../src/main/java11</compileSourceRoot>
|
||||
<compileSourceRoot>${project.basedir}/src/main/java11</compileSourceRoot>
|
||||
</compileSourceRoots>
|
||||
<multiReleaseOutput>true</multiReleaseOutput>
|
||||
</configuration>
|
||||
@ -227,7 +316,6 @@
|
||||
<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>
|
||||
@ -260,21 +348,12 @@
|
||||
</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>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>3.0.0-M1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
@ -289,7 +368,7 @@
|
||||
<goal>filter-sources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirectory>../src/main/java-templates-tdlight</sourceDirectory>
|
||||
<sourceDirectory>${project.basedir}/src/main/java-templates</sourceDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common.utils;
|
||||
package it.tdlight.utils;
|
||||
|
||||
public final class LibraryVersion {
|
||||
|
@ -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 {
|
||||
int compare(int k1, int k2);
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
|
175
tdlight-java/src/main/java/it/tdlight/ClientFactory.java
Normal file
175
tdlight-java/src/main/java/it/tdlight/ClientFactory.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.lang.reflect.Field;
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
|
@ -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.
|
@ -1,10 +1,10 @@
|
||||
package it.tdlight.common.internal;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.ResultHandler;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.ResultHandler;
|
||||
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 ExceptionHandler exceptionHandler;
|
@ -15,17 +15,13 @@
|
||||
* 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.common.utils.CantLoadLibrary;
|
||||
import it.tdlight.common.utils.LoadLibrary;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.utils.CantLoadLibrary;
|
||||
import it.tdlight.utils.LoadLibrary;
|
||||
import it.tdlight.jni.TdApi.LogStreamEmpty;
|
||||
import it.tdlight.jni.TdApi.SetLogStream;
|
||||
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.LoggerFactory;
|
||||
|
@ -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.Function;
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
@ -18,7 +13,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.Marker;
|
||||
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 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 volatile Integer clientId = null;
|
||||
private final InternalClientManager clientManager;
|
||||
private final InternalClientsState clientManagerState;
|
||||
private Handler<TdApi.Update> updateHandler;
|
||||
private MultiHandler updatesHandler;
|
||||
private ExceptionHandler defaultExceptionHandler;
|
||||
|
||||
private final AtomicBoolean isClosed = new AtomicBoolean();
|
||||
|
||||
public InternalClient(InternalClientManager clientManager) {
|
||||
this.clientManager = clientManager;
|
||||
public InternalClient(InternalClientsState clientManagerState) {
|
||||
this.clientManagerState = clientManagerState;
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
}
|
||||
|
||||
@ -153,11 +148,13 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
|
||||
}
|
||||
|
||||
private void createAndRegisterClient() {
|
||||
if (clientId != null) {
|
||||
throw new UnsupportedOperationException("Can't initialize the same client twice!");
|
||||
synchronized (this) {
|
||||
if (clientId != null) {
|
||||
throw new UnsupportedOperationException("Can't initialize the same client twice!");
|
||||
}
|
||||
clientId = NativeClientAccess.create();
|
||||
}
|
||||
clientId = NativeClientAccess.create();
|
||||
clientManager.registerClient(clientId, this);
|
||||
clientManagerState.registerClient(clientId, this);
|
||||
logger.info(TG_MARKER, "Registered new client {}", clientId);
|
||||
|
||||
// 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!"));
|
||||
return;
|
||||
}
|
||||
long queryId = clientManager.getNextQueryId();
|
||||
long queryId = clientManagerState.getNextQueryId();
|
||||
if (resultHandler != null) {
|
||||
handlers.put(queryId, new Handler<>(resultHandler, exceptionHandler));
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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.Error;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
@ -26,7 +21,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.Marker;
|
||||
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 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 volatile Integer clientId = null;
|
||||
private final InternalClientManager clientManager;
|
||||
private final InternalClientsState clientManagerState;
|
||||
|
||||
private final AtomicBoolean alreadyReceivedClosed = new AtomicBoolean();
|
||||
// This field is not volatile, but it's not problematic, because ReplayStartupUpdatesListener is able to forward
|
||||
// updates to the right listener
|
||||
private SignalListener signalListener = new ReplayStartupUpdatesListener();
|
||||
|
||||
public InternalReactiveClient(InternalClientManager clientManager) {
|
||||
this.clientManager = clientManager;
|
||||
public InternalReactiveClient(InternalClientsState clientManagerState) {
|
||||
this.clientManagerState = clientManagerState;
|
||||
this.updateHandler = new Handler<>(this::onUpdateFromHandler, this::onUpdateException);
|
||||
this.defaultExceptionHandler = this::onDefaultException;
|
||||
try {
|
||||
@ -167,7 +162,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
|
||||
logger.debug(TG_MARKER, "Creating new client");
|
||||
clientId = NativeClientAccess.create();
|
||||
logger.debug(TG_MARKER, "Registering new client {}", clientId);
|
||||
clientManager.registerClient(clientId, this);
|
||||
clientManagerState.registerClient(clientId, this);
|
||||
logger.debug(TG_MARKER, "Registered new client {}", clientId);
|
||||
}
|
||||
|
||||
@ -193,7 +188,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
|
||||
subscriber.onError(new IllegalStateException(
|
||||
"Can't send a request to TDLib before calling \"createAndRegisterClient\" function!"));
|
||||
} else {
|
||||
long queryId = clientManager.getNextQueryId();
|
||||
long queryId = clientManagerState.getNextQueryId();
|
||||
|
||||
// Handle timeout
|
||||
ScheduledFuture<?> timeoutFuture = timers.schedule(() -> {
|
||||
@ -275,7 +270,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
|
||||
this.signalListener = listener;
|
||||
|
||||
TdApi.GetAuthorizationState query = new TdApi.GetAuthorizationState();
|
||||
long queryId = clientManager.getNextQueryId();
|
||||
long queryId = clientManagerState.getNextQueryId();
|
||||
|
||||
// Send a dummy request to effectively start the TDLib session
|
||||
{
|
||||
@ -303,7 +298,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
|
||||
private void sendCloseAndIgnoreResponse() {
|
||||
if (!alreadyReceivedClosed.get()) {
|
||||
TdApi.Close query = new TdApi.Close();
|
||||
long queryId = clientManager.getNextQueryId();
|
||||
long queryId = clientManagerState.getNextQueryId();
|
||||
|
||||
handlers.put(queryId, EMPTY_HANDLER);
|
||||
logger.trace(TG_MARKER, "Client {} is requesting with query id {}: {}", clientId, queryId, query);
|
@ -1,15 +1,12 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.LogStreamDefault;
|
||||
import it.tdlight.jni.TdApi.LogStreamFile;
|
||||
import it.tdlight.jni.TdApi.SetLogVerbosityLevel;
|
||||
import it.tdlight.tdnative.NativeClient;
|
||||
import it.tdlight.tdnative.NativeClient.LogMessageHandler;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Class used for managing internal TDLib logging. Use TdApi.*Log* methods instead.
|
@ -1,9 +1,9 @@
|
||||
package it.tdlight.common.internal;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.UpdatesHandler;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.UpdatesHandler;
|
||||
|
||||
public final class MultiHandler {
|
||||
final class MultiHandler {
|
||||
|
||||
private final UpdatesHandler updatesHandler;
|
||||
private final ExceptionHandler exceptionHandler;
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common.internal;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
@ -6,7 +6,7 @@ import it.tdlight.tdnative.NativeClient;
|
||||
|
||||
final class NativeClientAccess extends NativeClient {
|
||||
|
||||
public static int create() {
|
||||
static int create() {
|
||||
return NativeClientAccess.createNativeClient();
|
||||
}
|
||||
|
||||
@ -14,11 +14,11 @@ final class NativeClientAccess extends NativeClient {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.tdlight.common.internal;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.common.EventsHandler;
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
|
||||
public class NativeResponseReceiver extends ResponseReceiver {
|
||||
class NativeResponseReceiver extends ResponseReceiver {
|
||||
|
||||
public NativeResponseReceiver(EventsHandler eventsHandler) {
|
||||
super(eventsHandler);
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.time.Duration;
|
@ -15,7 +15,7 @@
|
||||
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.Objects;
|
@ -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.toSet;
|
||||
|
||||
import it.tdlight.common.EventsHandler;
|
||||
import it.tdlight.common.utils.IntSwapper;
|
||||
import it.tdlight.common.utils.SpinWaitSupport;
|
||||
import it.tdlight.utils.IntSwapper;
|
||||
import it.tdlight.utils.SpinWaitSupport;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
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 int[] clientIds = new int[MAX_EVENTS];
|
||||
private final long[] eventIds = new long[MAX_EVENTS];
|
||||
@ -64,24 +60,10 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
|
||||
@Override
|
||||
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;
|
||||
try {
|
||||
boolean interrupted;
|
||||
while (
|
||||
!(interrupted = Thread.interrupted())
|
||||
&& (!closeCalled.get() || !registeredClients.isEmpty())
|
||||
) {
|
||||
while (!(interrupted = Thread.interrupted()) && !registeredClients.isEmpty()) {
|
||||
// Timeout is expressed in seconds
|
||||
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) {
|
||||
eventsHandler.handleClientEvents(clientId, true, clientEventIds, clientEvents, 0, 0);
|
||||
}
|
||||
@ -274,13 +256,9 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
|
||||
@Override
|
||||
public void close() throws InterruptedException {
|
||||
if (startCalled.get()) {
|
||||
if (closeCalled.compareAndSet(false, true)) {
|
||||
this.closeWait.await();
|
||||
if (registeredClients.isEmpty()) {
|
||||
this.interrupt();
|
||||
}
|
||||
}
|
||||
this.closeWait.await();
|
||||
if (registeredClients.isEmpty()) {
|
||||
ResponseReceiver.this.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Object;
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.Objects;
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
public interface SignalListener {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
public enum SignalType {
|
||||
UPDATE, EXCEPTION, CLOSE
|
@ -1,7 +1,6 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
|
||||
public interface TelegramClient {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.List;
|
@ -4,5 +4,5 @@ import java.util.function.Consumer;
|
||||
|
||||
public interface Authenticable {
|
||||
|
||||
void getAuthenticationData(Consumer<AuthenticationData> result);
|
||||
AuthenticationSupplier<?> getAuthenticationSupplier();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
public interface AuthenticationData {
|
||||
|
||||
boolean isQrCode();
|
||||
|
||||
boolean isBot();
|
||||
|
||||
String getUserPhoneNumber();
|
||||
|
||||
String getBotToken();
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import static java.util.concurrent.CompletableFuture.completedFuture;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final class AuthenticationDataImpl implements AuthenticationData {
|
||||
final class AuthenticationDataImpl implements AuthenticationSupplier<AuthenticationDataImpl>, AuthenticationData {
|
||||
|
||||
private final String userPhoneNumber;
|
||||
private final String botToken;
|
||||
@ -72,4 +75,9 @@ final class AuthenticationDataImpl implements AuthenticationData {
|
||||
public int hashCode() {
|
||||
return Objects.hash(userPhoneNumber, botToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthenticationDataImpl> get() {
|
||||
return completedFuture(this);
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
class AuthenticationDataQrCode implements AuthenticationData {
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
class AuthenticationDataQrCode implements AuthenticationSupplier<AuthenticationDataQrCode>, AuthenticationData {
|
||||
|
||||
@Override
|
||||
public boolean isQrCode() {
|
||||
@ -21,4 +23,9 @@ class AuthenticationDataQrCode implements AuthenticationData {
|
||||
public String getBotToken() {
|
||||
throw new UnsupportedOperationException("This is not a bot");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthenticationDataQrCode> get() {
|
||||
return CompletableFuture.completedFuture(this);
|
||||
}
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
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();
|
||||
|
||||
String getBotToken();
|
||||
|
||||
static AuthenticationData qrCode() {
|
||||
static AuthenticationSupplier<?> qrCode() {
|
||||
return new AuthenticationDataQrCode();
|
||||
}
|
||||
|
||||
@ -18,15 +14,15 @@ public interface AuthenticationData {
|
||||
* Deprecated, use {@link #user(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
static AuthenticationData user(long userPhoneNumber) {
|
||||
static AuthenticationSupplier<?> user(long userPhoneNumber) {
|
||||
return user(String.valueOf(userPhoneNumber));
|
||||
}
|
||||
|
||||
static AuthenticationData user(String userPhoneNumber) {
|
||||
static AuthenticationSupplier<?> user(String userPhoneNumber) {
|
||||
return new AuthenticationDataImpl(userPhoneNumber, null);
|
||||
}
|
||||
|
||||
static AuthenticationData bot(String botToken) {
|
||||
static AuthenticationSupplier<?> bot(String botToken) {
|
||||
return new AuthenticationDataImpl(null, botToken);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
||||
import it.tdlight.jni.TdApi.GetMe;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
@ -1,17 +1,11 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
||||
import it.tdlight.jni.TdApi.ChatList;
|
||||
import it.tdlight.jni.TdApi.ChatListMain;
|
||||
import it.tdlight.jni.TdApi.Error;
|
||||
import it.tdlight.jni.TdApi.GetMe;
|
||||
import it.tdlight.jni.TdApi.LoadChats;
|
||||
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.LoggerFactory;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.PhoneNumberAuthenticationSettings;
|
||||
import it.tdlight.jni.TdApi.SetAuthenticationPhoneNumber;
|
||||
@ -24,7 +24,13 @@ final class AuthorizationStateWaitAuthenticationDataHandler implements GenericUp
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
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);
|
||||
} else {
|
||||
PhoneNumberAuthenticationSettings phoneSettings = new PhoneNumberAuthenticationSettings(false, false, false, false, null);
|
||||
PhoneNumberAuthenticationSettings phoneSettings = new PhoneNumberAuthenticationSettings(false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
String phoneNumber = authenticationData.getUserPhoneNumber();
|
||||
SetAuthenticationPhoneNumber response = new SetAuthenticationPhoneNumber(phoneNumber, phoneSettings);
|
@ -1,7 +1,7 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitCode;
|
||||
import it.tdlight.jni.TdApi.CheckAuthenticationCode;
|
||||
@ -30,7 +30,11 @@ final class AuthorizationStateWaitCodeHandler implements GenericUpdateHandler<Up
|
||||
authorizationState.codeInfo.timeout,
|
||||
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);
|
||||
client.send(response, ok -> {
|
||||
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {
|
@ -1,5 +1,6 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitOtherDeviceConfirmation;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
|
||||
@ -7,18 +8,24 @@ final class AuthorizationStateWaitOtherDeviceConfirmationHandler implements
|
||||
GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitOtherDeviceConfirmationHandler(ClientInteraction clientInteraction) {
|
||||
public AuthorizationStateWaitOtherDeviceConfirmationHandler(ClientInteraction clientInteraction,
|
||||
ExceptionHandler exceptionHandler) {
|
||||
this.clientInteraction = clientInteraction;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR) {
|
||||
AuthorizationStateWaitOtherDeviceConfirmation authorizationState = (AuthorizationStateWaitOtherDeviceConfirmation) update.authorizationState;
|
||||
AuthorizationStateWaitOtherDeviceConfirmation authorizationState
|
||||
= (AuthorizationStateWaitOtherDeviceConfirmation) update.authorizationState;
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitPassword;
|
||||
import it.tdlight.jni.TdApi.CheckAuthenticationPassword;
|
||||
@ -29,7 +29,11 @@ final class AuthorizationStateWaitPasswordHandler implements GenericUpdateHandle
|
||||
authorizationState.hasRecoveryEmailAddress,
|
||||
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);
|
||||
client.send(response, ok -> {
|
||||
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {
|
@ -1,7 +1,7 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitRegistration;
|
||||
import it.tdlight.jni.TdApi.RegisterUser;
|
||||
@ -26,9 +26,12 @@ final class AuthorizationStateWaitRegistrationHandler implements GenericUpdateHa
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateWaitRegistration.CONSTRUCTOR) {
|
||||
TdApi.AuthorizationStateWaitRegistration authorizationState = (TdApi.AuthorizationStateWaitRegistration) update.authorizationState;
|
||||
ParameterInfoTermsOfService tos = new ParameterInfoTermsOfService(authorizationState.termsOfService);
|
||||
clientInteraction.onParameterRequest(InputParameter.TERMS_OF_SERVICE, tos, ignored -> {
|
||||
clientInteraction.onParameterRequest(InputParameter.ASK_FIRST_NAME, new EmptyParameterInfo(), firstName -> {
|
||||
clientInteraction.onParameterRequest(InputParameter.ASK_LAST_NAME, new EmptyParameterInfo(), lastName -> {
|
||||
clientInteraction
|
||||
.onParameterRequest(InputParameter.TERMS_OF_SERVICE, tos)
|
||||
.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()) {
|
||||
exceptionHandler.onException(new IllegalArgumentException("First name must not be null or empty"));
|
||||
return;
|
||||
@ -51,9 +54,12 @@ final class AuthorizationStateWaitRegistrationHandler implements GenericUpdateHa
|
||||
throw new TelegramError((TdApi.Error) ok);
|
||||
}
|
||||
}, exceptionHandler);
|
||||
}))
|
||||
.whenComplete((ignored, ex) -> {
|
||||
if (ex != null) {
|
||||
exceptionHandler.onException(ex);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters;
|
||||
import it.tdlight.jni.TdApi.SetTdlibParameters;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
|
||||
final class AuthorizationStateWaitTdlibParametersHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
@ -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);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.Message;
|
||||
@ -11,7 +11,6 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,44 +1,36 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ConstructorDetector;
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.Init;
|
||||
import it.tdlight.common.Log;
|
||||
import it.tdlight.common.ResultHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.common.internal.CommonClientManager;
|
||||
import it.tdlight.common.utils.CantLoadLibrary;
|
||||
import it.tdlight.common.utils.LibraryVersion;
|
||||
import it.tdlight.ClientFactory;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.Init;
|
||||
import it.tdlight.ResultHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.Update;
|
||||
import it.tdlight.utils.CantLoadLibrary;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.ChatListArchive;
|
||||
import it.tdlight.jni.TdApi.ChatListMain;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
import it.tdlight.jni.TdApi.User;
|
||||
import it.tdlight.tdnative.NativeClient;
|
||||
import it.tdlight.tdnative.NativeClient.LogMessageHandler;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@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 ExecutorService blockingExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -49,9 +41,9 @@ public final class SimpleTelegramClient implements Authenticable {
|
||||
}
|
||||
|
||||
private final TelegramClient client;
|
||||
private ClientInteraction clientInteraction = new ScannerClientInteraction(blockingExecutor, this);
|
||||
private ClientInteraction clientInteraction;
|
||||
private final TDLibSettings settings;
|
||||
private AuthenticationData authenticationData;
|
||||
private AuthenticationSupplier<?> authenticationData;
|
||||
|
||||
private final Map<String, Set<CommandHandler>> commandHandlers = new ConcurrentHashMap<>();
|
||||
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);
|
||||
|
||||
public SimpleTelegramClient(TDLibSettings settings) {
|
||||
this.client = CommonClientManager.create(LibraryVersion.IMPLEMENTATION_NAME);
|
||||
this.settings = settings;
|
||||
SimpleTelegramClient(ClientFactory clientFactory,
|
||||
TDLibSettings 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,
|
||||
new AuthorizationStateWaitTdlibParametersHandler(client, settings, this::handleDefaultException)
|
||||
);
|
||||
@ -78,22 +88,24 @@ public final class SimpleTelegramClient implements Authenticable {
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitRegistrationHandler(client,
|
||||
new SimpleTelegramClientInteraction(blockingExecutor),
|
||||
new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitPasswordHandler(client,
|
||||
new SimpleTelegramClientInteraction(blockingExecutor),
|
||||
new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitOtherDeviceConfirmationHandler(new SimpleTelegramClientInteraction(blockingExecutor))
|
||||
new AuthorizationStateWaitOtherDeviceConfirmationHandler(new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitCodeHandler(client,
|
||||
new SimpleTelegramClientInteraction(blockingExecutor),
|
||||
new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
@ -143,28 +155,16 @@ public final class SimpleTelegramClient implements Authenticable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAuthenticationData(Consumer<AuthenticationData> result) {
|
||||
if (authenticationData instanceof ConsoleInteractiveAuthenticationData) {
|
||||
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);
|
||||
}
|
||||
public AuthenticationSupplier<?> getAuthenticationSupplier() {
|
||||
return authenticationData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInteraction(ClientInteraction clientInteraction) {
|
||||
this.clientInteraction = clientInteraction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends TdApi.Update> void addCommandHandler(String commandName, CommandHandler handler) {
|
||||
Set<CommandHandler> handlers = this.commandHandlers.computeIfAbsent(commandName,
|
||||
k -> new ConcurrentHashMap<CommandHandler, Object>().keySet(new Object())
|
||||
@ -172,29 +172,20 @@ public final class SimpleTelegramClient implements Authenticable {
|
||||
handlers.add(handler);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler) {
|
||||
int updateConstructor = ConstructorDetector.getConstructor(updateType);
|
||||
this.updateHandlers.add(update -> {
|
||||
if (update.getConstructor() == updateConstructor) {
|
||||
handler.onUpdate((T) update);
|
||||
}
|
||||
});
|
||||
this.updateHandlers.add(new SimpleResultHandler<>(updateType, handler));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUpdatesHandler(GenericUpdateHandler<TdApi.Update> handler) {
|
||||
this.updateHandlers.add(update -> {
|
||||
if (update instanceof TdApi.Update) {
|
||||
handler.onUpdate((TdApi.Update) update);
|
||||
} else {
|
||||
LOG.warn("Unknown update type: {}", update);
|
||||
}
|
||||
});
|
||||
this.updateHandlers.add(new SimpleUpdateHandler(handler, LOG));
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional handler to handle errors received from TDLib
|
||||
*/
|
||||
@Override
|
||||
public void addUpdateExceptionHandler(ExceptionHandler 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)
|
||||
*/
|
||||
@Override
|
||||
public void addDefaultExceptionHandler(ExceptionHandler defaultExceptionHandlers) {
|
||||
this.defaultExceptionHandlers.add(defaultExceptionHandlers);
|
||||
}
|
||||
@ -209,7 +201,7 @@ public final class SimpleTelegramClient implements Authenticable {
|
||||
/**
|
||||
* Start the client
|
||||
*/
|
||||
public void start(AuthenticationData authenticationData) {
|
||||
public void start(AuthenticationSupplier<?> authenticationData) {
|
||||
this.authenticationData = authenticationData;
|
||||
createDirectories();
|
||||
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 ExecutorService blockingExecutor;
|
||||
|
||||
public SimpleTelegramClientInteraction(ExecutorService blockingExecutor) {
|
||||
this.blockingExecutor = blockingExecutor;
|
||||
public SimpleTelegramClientInteraction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo, Consumer<String> result) {
|
||||
public CompletableFuture<String> onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo) {
|
||||
try {
|
||||
blockingExecutor.execute(() -> clientInteraction.onParameterRequest(parameter, parameterInfo, result));
|
||||
return clientInteraction.onParameterRequest(parameter, parameterInfo);
|
||||
} catch (RejectedExecutionException | NullPointerException ex) {
|
||||
LOG.error("Failed to execute onParameterRequest. Returning an empty string", ex);
|
||||
result.accept("");
|
||||
return CompletableFuture.completedFuture("");
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.utils.LibraryVersion;
|
||||
import it.tdlight.utils.LibraryVersion;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Locale;
|
@ -15,7 +15,7 @@
|
||||
* 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.
|
@ -15,7 +15,7 @@
|
||||
* 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.
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common.utils;
|
||||
package it.tdlight.utils;
|
||||
|
||||
public final class IntSwapper {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* 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.InputStream;
|
@ -15,7 +15,7 @@
|
||||
* 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.
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common.utils;
|
||||
package it.tdlight.utils;
|
||||
|
||||
import java.io.Console;
|
||||
import java.io.IOException;
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common.utils;
|
||||
package it.tdlight.utils;
|
||||
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
@ -3,11 +3,8 @@ module tdlight.java {
|
||||
requires org.reactivestreams;
|
||||
requires org.slf4j;
|
||||
requires static com.google.zxing;
|
||||
exports it.tdlight.tdlight;
|
||||
exports it.tdlight.tdnative;
|
||||
exports it.tdlight.tdlib;
|
||||
exports it.tdlight.common;
|
||||
exports it.tdlight.common.utils;
|
||||
exports it.tdlight.common.internal;
|
||||
exports it.tdlight;
|
||||
exports it.tdlight.utils;
|
||||
exports it.tdlight.client;
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common.utils;
|
||||
package it.tdlight.utils;
|
||||
|
||||
public class SpinWaitSupport {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package it.tdlight.common.internal;
|
||||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.Objects;
|
@ -1,22 +1,19 @@
|
||||
package it.tdlight.common.internal;
|
||||
package it.tdlight;
|
||||
|
||||
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.Object;
|
||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class HandleEventsTest {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user