TDLight-Java 3

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

View File

@ -15,7 +15,7 @@
## 💻 Supported platforms
**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, ...
}

View File

@ -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>

View File

@ -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 -->

View File

@ -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);

View File

@ -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) {

View File

@ -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>

View File

@ -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."

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,11 +8,11 @@
<groupId>it.tdlight</groupId>
<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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -1,10 +1,5 @@
package it.tdlight.common.internal;
package it.tdlight;
import it.tdlight.common.ClientEventsHandler;
import it.tdlight.common.ExceptionHandler;
import it.tdlight.common.ResultHandler;
import it.tdlight.common.TelegramClient;
import it.tdlight.common.UpdatesHandler;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.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));
}

View File

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

View File

@ -1,10 +1,5 @@
package it.tdlight.common.internal;
package it.tdlight;
import it.tdlight.common.ClientEventsHandler;
import it.tdlight.common.ExceptionHandler;
import it.tdlight.common.ReactiveTelegramClient;
import it.tdlight.common.Signal;
import it.tdlight.common.SignalListener;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.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);

View File

@ -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.

View File

@ -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;

View File

@ -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);
}

View File

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

View File

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

View File

@ -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;

View File

@ -1,11 +1,10 @@
package it.tdlight.common.internal;
package it.tdlight;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.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();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,12 @@
package it.tdlight.client;
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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}
});
}
}

View File

@ -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) {

View File

@ -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);
}
});
});
});
}
}
}

View File

@ -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> {

View File

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

View File

@ -1,6 +1,6 @@
package it.tdlight.client;
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,44 +1,36 @@
package it.tdlight.client;
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("");
}
}
}

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
package it.tdlight.client;
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;

View File

@ -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.

View File

@ -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.

View File

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

View File

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

View File

@ -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;

View File

@ -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.

View File

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

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -1,22 +1,19 @@
package it.tdlight.common.internal;
package it.tdlight;
import static org.junit.jupiter.api.Assertions.*;
import 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