Compare commits
50 Commits
v3.0.1+td.
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
4d77c73e95 | ||
|
c2ad58fa70 | ||
|
fa1c197614 | ||
|
5afc402682 | ||
|
fed5626b81 | ||
|
6407c10c92 | ||
|
eb6a8d55c8 | ||
|
616bd6a203 | ||
|
d6fb1bc781 | ||
|
a15245e6ed | ||
|
b6938698c0 | ||
|
6de597fd0f | ||
|
3eab57d29d | ||
|
7a6b0c6d41 | ||
|
8338b52b50 | ||
|
f4be6be48f | ||
|
f11518af2e | ||
|
4999183324 | ||
|
250dfa3a89 | ||
|
3590bf6547 | ||
792f004a2f | |||
dedabdde2e | |||
c3596f9ae4 | |||
5037157ba4 | |||
652d116ad9 | |||
db8676cb6d | |||
6e7f82fa95 | |||
41e83aa950 | |||
|
6de20e1a31 | ||
|
3d534b479e | ||
|
649d0c3da6 | ||
|
6fca1f6ef3 | ||
|
0d5b90e670 | ||
29bf91ee26 | |||
|
14e95e29f3 | ||
|
4d2f48d533 | ||
|
660a6b0a3b | ||
|
df281862fe | ||
|
62d1f82b19 | ||
|
a873d79925 | ||
|
c11cb42579 | ||
|
248818b36f | ||
|
d58811e922 | ||
|
98273f1d47 | ||
|
9a1c284d69 | ||
|
867d16b91d | ||
|
6a7ba6d412 | ||
|
2a87d69088 | ||
|
11fc1a13db | ||
|
3c3af46254 |
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -95,7 +95,4 @@ parent/.classpath
|
|||
tdlight-java/target-snapshot/
|
||||
|
||||
tdlight-java-8/target-snapshot/
|
||||
/example-tdlight-session/data/db.sqlite
|
||||
/example-tdlight-session/data/db.sqlite-shm
|
||||
/example-tdlight-session/data/db.sqlite-wal
|
||||
/example-tdlight-session/data/td.binlog
|
||||
/example-tdlight-session/
|
||||
|
|
63
README.md
63
README.md
|
@ -15,21 +15,20 @@
|
|||
|
||||
## đź’» Supported platforms
|
||||
|
||||
**Java versions**: from Java 17 to Java 19+ (Java 8 is supported if you use the following dependency classifier: `jdk8`)
|
||||
**Java versions**: from Java 17 to Java 21+ (Java 8 to 16 is supported if you use the following dependency classifier: `jdk8`)
|
||||
|
||||
**Operating systems**: Linux, Windows, MacOS
|
||||
|
||||
**CPU architectures**:
|
||||
|
||||
- i386/x86 (Linux, Windows)
|
||||
- amd64/x86_64 (Linux, Windows, OSX)
|
||||
- armhf/armv7 (Linux)
|
||||
- aarch64/armv8/arm64 (Linux)
|
||||
- s390x (Linux)
|
||||
- ppc64el/ppc64le (Linux)
|
||||
- amd64 (Linux, Windows, MacOS)
|
||||
- armhf (Linux)
|
||||
- arm64 (Linux, MacOS)
|
||||
- ppc64el (Linux)
|
||||
- riscv64 (linux)
|
||||
|
||||
## đź“š Required libraries
|
||||
- **Linux: OpenSSL, zlib**
|
||||
- **Linux: OpenSSL1/OpenSSL3, zlib, (libc++ if you use clang)**
|
||||
- **MacOS: OpenSSL**
|
||||
- **Windows: [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe)**
|
||||
|
||||
|
@ -84,15 +83,26 @@ 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> <!-- Java 8 is supported if you use the following dependency classifier: <classifier>jdk8</classifier> -->
|
||||
<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>
|
||||
<!-- Example linux amd64 (GNU GCC) ssl1 natives -->
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives-linux-amd64</artifactId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<classifier>linux_amd64_gnu_ssl1</classifier>
|
||||
<!-- don't specify the version here -->
|
||||
</dependency>
|
||||
<!-- Include other native versions that you want, for example for windows, osx, ... -->
|
||||
<!-- Example windows amd64 natives -->
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<classifier>windows_amd64</classifier>
|
||||
<!-- don't specify the version here -->
|
||||
</dependency>
|
||||
<!-- ... -->
|
||||
<!-- Include other native classifiers, for example linux_amd64_ssl3, macos_amd64, ... -->
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -114,9 +124,9 @@ dependencies {
|
|||
implementation platform('it.tdlight:tdlight-java-bom:VERSION')
|
||||
|
||||
// do not specify the versions on the dependencies below!
|
||||
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, ...
|
||||
implementation group: 'it.tdlight', name: 'tdlight-java' // Java 8 is supported if you use the following dependency classifier: `jdk8`
|
||||
implementation group: 'it.tdlight', name: 'tdlight-natives', classifier: 'linux_amd64_gnu_ssl1'
|
||||
// Include other native classifiers, for example linux_amd64_clang_ssl3, macos_amd64, ... -->
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -125,16 +135,23 @@ it [here](https://github.com/tdlight-team/tdlight-java/releases).
|
|||
|
||||
## âš’ Native dependencies
|
||||
|
||||
To use TDLight Java you need to include one or more native dependencies:
|
||||
To use TDLight Java you need to include the native libraries, by specifying one of the following classifier for each tdlight-natives dependency:
|
||||
|
||||
- `tdlight-natives-linux-amd64`
|
||||
- `tdlight-natives-linux-aarch64`
|
||||
- `tdlight-natives-linux-x86`
|
||||
- `tdlight-natives-linux-armhf`
|
||||
- `tdlight-natives-linux-ppc64le`
|
||||
- `tdlight-natives-linux-s390x`
|
||||
- `tdlight-natives-windows-amd64`
|
||||
- `tdlight-natives-osx-amd64`
|
||||
- `linux_amd64_clang_ssl3`
|
||||
- `linux_amd64_gnu_ssl1`
|
||||
- `linux_amd64_gnu_ssl3`
|
||||
- `linux_arm64_clang_ssl3`
|
||||
- `linux_arm64_gnu_ssl1`
|
||||
- `linux_arm64_gnu_ssl3`
|
||||
- `linux_armhf_gnu_ssl1`
|
||||
- `linux_armhf_gnu_ssl3`
|
||||
- `linux_ppc64el_gnu_ssl3`
|
||||
- `linux_riscv64_gnu_ssl3`
|
||||
- `windows_amd64`
|
||||
- `macos_arm64`
|
||||
- `macos_amd64`
|
||||
|
||||
Advanced: If you want to use a different precompiled native, please set the java property `it.tdlight.native.workdir`. (Please note that you must build [this](https://github.com/tdlight-team/tdlight-java-natives), you can't put random precompiled tdlib binaries found on the internet)
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
52
bom/pom.xml
52
bom/pom.xml
|
@ -8,8 +8,8 @@
|
|||
<name>TDLight Java BOM</name>
|
||||
<properties>
|
||||
<revision>3.0.0.0-SNAPSHOT</revision>
|
||||
<tdlight.natives.version>4.0.378</tdlight.natives.version>
|
||||
<tdlight.api.version>4.0.376</tdlight.api.version>
|
||||
<tdlight.natives.version>4.0.506</tdlight.natives.version>
|
||||
<tdlight.api.version>4.0.476</tdlight.api.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
@ -82,44 +82,61 @@
|
|||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_amd64_ssl1</classifier>
|
||||
<classifier>linux_amd64_clang_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_amd64_ssl3</classifier>
|
||||
<classifier>linux_amd64_gnu_ssl1</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_arm64_ssl1</classifier>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_armhf_ssl1</classifier>
|
||||
<classifier>linux_amd64_gnu_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_i386_ssl1</classifier>
|
||||
<classifier>linux_arm64_clang_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_ppc64le_ssl1</classifier>
|
||||
<classifier>linux_arm64_gnu_ssl1</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_s390x_ssl1</classifier>
|
||||
<classifier>linux_arm64_gnu_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_armhf_gnu_ssl1</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_armhf_gnu_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_ppc64el_gnu_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>linux_riscv64_gnu_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
|
@ -131,15 +148,14 @@
|
|||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>osx_amd64</classifier>
|
||||
<classifier>macos_amd64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<version>${tdlight.natives.version}</version>
|
||||
<classifier>osx_arm64</classifier>
|
||||
<classifier>macos_arm64</classifier>
|
||||
</dependency>
|
||||
-->
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-java-bom</artifactId>
|
||||
<version>3.0.0.0-SNAPSHOT</version>
|
||||
<version>3.4.0+td.1.8.26</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
@ -44,9 +44,18 @@
|
|||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<classifier>linux_amd64_ssl1</classifier>
|
||||
<classifier>linux_amd64_gnu_ssl1</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<classifier>linux_amd64_clang_ssl3</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<classifier>linux_amd64_gnu_ssl3</classifier>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
|
@ -54,20 +63,25 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>osx_amd64</artifactId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<classifier>macos_amd64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives</artifactId>
|
||||
<classifier>macos_arm64</classifier>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- log4j logging -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.19.0</version>
|
||||
<version>2.22.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||
<version>2.19.0</version>
|
||||
<version>2.22.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
|
@ -89,15 +103,15 @@
|
|||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.3.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<version>3.3.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<source>17</source>
|
||||
|
@ -106,15 +120,15 @@
|
|||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>3.3.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>3.0.0-M1</version>
|
||||
<version>3.1.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
<version>3.1.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package it.tdlight.example;
|
||||
|
||||
import it.tdlight.Init;
|
||||
import it.tdlight.Log;
|
||||
import it.tdlight.Slf4JLogMessageHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.ClientFactory;
|
||||
|
@ -15,8 +17,11 @@ public class AdvancedExample {
|
|||
// Initialize TDLight native libraries
|
||||
Init.init();
|
||||
|
||||
// Set the log level
|
||||
Log.setLogMessageHandler(1, new Slf4JLogMessageHandler());
|
||||
|
||||
// Create a client manager, it should be closed before shutdown
|
||||
ClientFactory clientManager = new ClientFactory();
|
||||
ClientFactory clientManager = ClientFactory.create();
|
||||
|
||||
// Create a client, it should be closed before shutdown
|
||||
TelegramClient client = clientManager.createClient();
|
||||
|
|
|
@ -1,18 +1,29 @@
|
|||
package it.tdlight.example;
|
||||
|
||||
import it.tdlight.client.*;
|
||||
import it.tdlight.client.AuthenticationSupplier;
|
||||
import it.tdlight.client.CommandHandler;
|
||||
import it.tdlight.client.SimpleTelegramClient;
|
||||
import it.tdlight.client.TDLibSettings;
|
||||
import it.tdlight.Init;
|
||||
import it.tdlight.jni.TdApi.AuthorizationState;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.MessageContent;
|
||||
import it.tdlight.Log;
|
||||
import it.tdlight.Slf4JLogMessageHandler;
|
||||
import it.tdlight.client.APIToken;
|
||||
import it.tdlight.client.AuthenticationSupplier;
|
||||
import it.tdlight.client.SimpleAuthenticationSupplier;
|
||||
import it.tdlight.client.SimpleTelegramClient;
|
||||
import it.tdlight.client.SimpleTelegramClientBuilder;
|
||||
import it.tdlight.client.SimpleTelegramClientFactory;
|
||||
import it.tdlight.client.TDLibSettings;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.util.UnsupportedNativeLibraryException;
|
||||
import it.tdlight.jni.TdApi.AuthorizationState;
|
||||
import it.tdlight.jni.TdApi.CreatePrivateChat;
|
||||
import it.tdlight.jni.TdApi.FormattedText;
|
||||
import it.tdlight.jni.TdApi.GetChat;
|
||||
import it.tdlight.jni.TdApi.InputMessageText;
|
||||
import it.tdlight.jni.TdApi.Message;
|
||||
import it.tdlight.jni.TdApi.MessageContent;
|
||||
import it.tdlight.jni.TdApi.MessageSenderUser;
|
||||
import it.tdlight.jni.TdApi.SendMessage;
|
||||
import it.tdlight.jni.TdApi.TextEntity;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Example class for TDLight Java
|
||||
|
@ -21,20 +32,19 @@ import java.nio.file.Paths;
|
|||
*/
|
||||
public final class Example {
|
||||
|
||||
/**
|
||||
* Admin user id, used by the stop command example
|
||||
*/
|
||||
private static final TdApi.MessageSender ADMIN_ID = new TdApi.MessageSenderUser(667900586);
|
||||
public static void main(String[] args) throws Exception {
|
||||
long adminId = Integer.getInteger("it.tdlight.example.adminid", 667900586);
|
||||
|
||||
private static SimpleTelegramClient client;
|
||||
|
||||
public static void main(String[] args) throws UnsupportedNativeLibraryException, InterruptedException {
|
||||
// Initialize TDLight native libraries
|
||||
Init.init();
|
||||
|
||||
// Create the client factory
|
||||
try (SimpleTelegramClientFactory clientFactory = new SimpleTelegramClientFactory()) {
|
||||
// Set the log level
|
||||
Log.setLogMessageHandler(1, new Slf4JLogMessageHandler());
|
||||
|
||||
// Create the client factory (You can create more than one client,
|
||||
// BUT only a single instance of ClientFactory is allowed globally!
|
||||
// You must reuse it if you want to create more than one client!)
|
||||
try (SimpleTelegramClientFactory clientFactory = new SimpleTelegramClientFactory()) {
|
||||
// Obtain the API token
|
||||
//
|
||||
// var apiToken = new APIToken(your-api-id-here, "your-api-hash-here");
|
||||
|
@ -45,7 +55,10 @@ public final class Example {
|
|||
// Configure the client
|
||||
TDLibSettings settings = TDLibSettings.create(apiToken);
|
||||
|
||||
// Configure the session directory
|
||||
// Configure the session directory.
|
||||
// After you authenticate into a session, the authentication will be skipped from the next restart!
|
||||
// If you want to ensure to match the authentication supplier user/bot with your session user/bot,
|
||||
// you can name your session directory after your user id, for example: "tdlib-session-id12345"
|
||||
Path sessionPath = Paths.get("example-tdlight-session");
|
||||
settings.setDatabaseDirectoryPath(sessionPath.resolve("data"));
|
||||
settings.setDownloadedFilesDirectoryPath(sessionPath.resolve("downloads"));
|
||||
|
@ -54,61 +67,123 @@ public final class Example {
|
|||
SimpleTelegramClientBuilder clientBuilder = clientFactory.builder(settings);
|
||||
|
||||
// Configure the authentication info
|
||||
ConsoleInteractiveAuthenticationData authenticationData = AuthenticationSupplier.consoleLogin();
|
||||
|
||||
// Add an example update handler that prints when the bot is started
|
||||
clientBuilder.addUpdateHandler(TdApi.UpdateAuthorizationState.class, Example::onUpdateAuthorizationState);
|
||||
|
||||
// Add an example update handler that prints every received message
|
||||
clientBuilder.addUpdateHandler(TdApi.UpdateNewMessage.class, Example::onUpdateNewMessage);
|
||||
|
||||
// Add an example command handler that stops the bot
|
||||
clientBuilder.addCommandHandler("stop", new StopCommandHandler());
|
||||
// Replace with AuthenticationSupplier.consoleLogin(), or .user(xxx), or .bot(xxx);
|
||||
SimpleAuthenticationSupplier<?> authenticationData = AuthenticationSupplier.testUser(7381);
|
||||
// This is an example, remove this line to use the real telegram datacenters!
|
||||
settings.setUseTestDatacenter(true);
|
||||
|
||||
// Create and start the client
|
||||
client = clientBuilder.build(authenticationData);
|
||||
try (var app = new ExampleApp(clientBuilder, authenticationData, adminId)) {
|
||||
// Get me
|
||||
TdApi.User me = app.getClient().getMeAsync().get(1, TimeUnit.MINUTES);
|
||||
|
||||
// Wait for exit
|
||||
client.waitForExit();
|
||||
// Create the "saved messages" chat
|
||||
var savedMessagesChat = app.getClient().send(new CreatePrivateChat(me.id, true)).get(1, TimeUnit.MINUTES);
|
||||
|
||||
// Send a test message
|
||||
var req = new SendMessage();
|
||||
req.chatId = savedMessagesChat.id;
|
||||
var txt = new InputMessageText();
|
||||
txt.text = new FormattedText("TDLight test", new TextEntity[0]);
|
||||
req.inputMessageContent = txt;
|
||||
Message result = app.getClient().sendMessage(req, true).get(1, TimeUnit.MINUTES);
|
||||
System.out.println("Sent message:" + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print new messages received via updateNewMessage
|
||||
*/
|
||||
private static void onUpdateNewMessage(TdApi.UpdateNewMessage update) {
|
||||
// Get the message content
|
||||
MessageContent messageContent = update.message.content;
|
||||
public static class ExampleApp implements AutoCloseable {
|
||||
|
||||
// Get the message text
|
||||
String text;
|
||||
if (messageContent instanceof TdApi.MessageText messageText) {
|
||||
// Get the text of the text message
|
||||
text = messageText.text.text;
|
||||
} else {
|
||||
// We handle only text messages, the other messages will be printed as their type
|
||||
text = String.format("(%s)", messageContent.getClass().getSimpleName());
|
||||
private final SimpleTelegramClient client;
|
||||
|
||||
/**
|
||||
* Admin user id, used by the stop command example
|
||||
*/
|
||||
private final long adminId;
|
||||
|
||||
public ExampleApp(SimpleTelegramClientBuilder clientBuilder,
|
||||
SimpleAuthenticationSupplier<?> authenticationData,
|
||||
long adminId) {
|
||||
this.adminId = adminId;
|
||||
|
||||
// Add an example update handler that prints when the bot is started
|
||||
clientBuilder.addUpdateHandler(TdApi.UpdateAuthorizationState.class, this::onUpdateAuthorizationState);
|
||||
|
||||
// Add an example command handler that stops the bot
|
||||
clientBuilder.addCommandHandler("stop", this::onStopCommand);
|
||||
|
||||
// Add an example update handler that prints every received message
|
||||
clientBuilder.addUpdateHandler(TdApi.UpdateNewMessage.class, this::onUpdateNewMessage);
|
||||
|
||||
// Build the client
|
||||
this.client = clientBuilder.build(authenticationData);
|
||||
}
|
||||
|
||||
// Get the chat title
|
||||
client.send(new TdApi.GetChat(update.message.chatId), chatIdResult -> {
|
||||
// Get the chat response
|
||||
Chat chat = chatIdResult.get();
|
||||
// Get the chat name
|
||||
String chatName = chat.title;
|
||||
|
||||
// Print the message
|
||||
System.out.printf("Received new message from chat %s: %s%n", chatName, text);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the bot if the /stop command is sent by the administrator
|
||||
*/
|
||||
private static class StopCommandHandler implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void onCommand(TdApi.Chat chat, TdApi.MessageSender commandSender, String arguments) {
|
||||
public void close() throws Exception {
|
||||
client.close();
|
||||
}
|
||||
|
||||
public SimpleTelegramClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the bot status
|
||||
*/
|
||||
private void onUpdateAuthorizationState(TdApi.UpdateAuthorizationState update) {
|
||||
AuthorizationState authorizationState = update.authorizationState;
|
||||
if (authorizationState instanceof TdApi.AuthorizationStateReady) {
|
||||
System.out.println("Logged in");
|
||||
} else if (authorizationState instanceof TdApi.AuthorizationStateClosing) {
|
||||
System.out.println("Closing...");
|
||||
} else if (authorizationState instanceof TdApi.AuthorizationStateClosed) {
|
||||
System.out.println("Closed");
|
||||
} else if (authorizationState instanceof TdApi.AuthorizationStateLoggingOut) {
|
||||
System.out.println("Logging out...");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print new messages received via updateNewMessage
|
||||
*/
|
||||
private void onUpdateNewMessage(TdApi.UpdateNewMessage update) {
|
||||
// Get the message content
|
||||
MessageContent messageContent = update.message.content;
|
||||
|
||||
// Get the message text
|
||||
String text;
|
||||
if (messageContent instanceof TdApi.MessageText messageText) {
|
||||
// Get the text of the text message
|
||||
text = messageText.text.text;
|
||||
} else {
|
||||
// We handle only text messages, the other messages will be printed as their type
|
||||
text = String.format("(%s)", messageContent.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
long chatId = update.message.chatId;
|
||||
|
||||
// Get the chat title
|
||||
client.send(new TdApi.GetChat(chatId))
|
||||
// Use the async completion handler, to avoid blocking the TDLib response thread accidentally
|
||||
.whenCompleteAsync((chatIdResult, error) -> {
|
||||
if (error != null) {
|
||||
// Print error
|
||||
System.err.printf("Can't get chat title of chat %s%n", chatId);
|
||||
error.printStackTrace(System.err);
|
||||
} else {
|
||||
// Get the chat name
|
||||
String title = chatIdResult.title;
|
||||
// Print the message
|
||||
System.out.printf("Received new message from chat %s (%s): %s%n", title, chatId, text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the bot if the /stop command is sent by the administrator
|
||||
*/
|
||||
private void onStopCommand(TdApi.Chat chat, TdApi.MessageSender commandSender, String arguments) {
|
||||
// Check if the sender is the admin
|
||||
if (isAdmin(commandSender)) {
|
||||
// Stop the client
|
||||
|
@ -116,28 +191,17 @@ public final class Example {
|
|||
client.sendClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the bot status
|
||||
*/
|
||||
private static void onUpdateAuthorizationState(TdApi.UpdateAuthorizationState update) {
|
||||
AuthorizationState authorizationState = update.authorizationState;
|
||||
if (authorizationState instanceof TdApi.AuthorizationStateReady) {
|
||||
System.out.println("Logged in");
|
||||
} else if (authorizationState instanceof TdApi.AuthorizationStateClosing) {
|
||||
System.out.println("Closing...");
|
||||
} else if (authorizationState instanceof TdApi.AuthorizationStateClosed) {
|
||||
System.out.println("Closed");
|
||||
} else if (authorizationState instanceof TdApi.AuthorizationStateLoggingOut) {
|
||||
System.out.println("Logging out...");
|
||||
/**
|
||||
* Check if the command sender is admin
|
||||
*/
|
||||
public boolean isAdmin(TdApi.MessageSender sender) {
|
||||
if (sender instanceof MessageSenderUser messageSenderUser) {
|
||||
return messageSenderUser.userId == adminId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the command sender is admin
|
||||
*/
|
||||
private static boolean isAdmin(TdApi.MessageSender sender) {
|
||||
return sender.equals(ADMIN_ID);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://logging.apache.org/log4j/2.0/config
|
||||
https://raw.githubusercontent.com/apache/logging-log4j2/log4j-2.16.0/log4j-core/src/main/resources/Log4j-config.xsd"
|
||||
status="INFO">
|
||||
status="DEBUG">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout
|
||||
|
@ -13,8 +13,8 @@
|
|||
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="it.tdlight.TDLight" level="ERROR" additivity="false" />
|
||||
<Root level="INFO">
|
||||
<Logger name="it.tdlight.TDLight" level="WARN" additivity="false" />
|
||||
<Root level="DEBUG">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -12,11 +12,11 @@
|
|||
</parent>
|
||||
<properties>
|
||||
<revision>3.0.0.0-SNAPSHOT</revision>
|
||||
<zxing.version>3.5.0</zxing.version>
|
||||
<zxing.version>3.5.1</zxing.version>
|
||||
<reactive.streams.version>1.0.4</reactive.streams.version>
|
||||
<slf4j.api.version>2.0.5</slf4j.api.version>
|
||||
<junit.jupiter.engine.version>5.9.0</junit.jupiter.engine.version>
|
||||
<fastutil.core.version>8.5.11</fastutil.core.version>
|
||||
<junit.jupiter.engine.version>5.9.2</junit.jupiter.engine.version>
|
||||
<fastutil.core.version>8.5.12</fastutil.core.version>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>tdlight-java</module>
|
||||
|
|
|
@ -1,175 +1,39 @@
|
|||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
import it.tdlight.util.UnsupportedNativeLibraryException;
|
||||
import it.tdlight.util.CleanSupport;
|
||||
import it.tdlight.util.CleanSupport.CleanableSupport;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import it.tdlight.ClientFactoryImpl.CommonClientFactory;
|
||||
|
||||
/**
|
||||
* TDLight client factory
|
||||
*/
|
||||
public class ClientFactory implements AutoCloseable {
|
||||
public interface ClientFactory extends 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;
|
||||
/**
|
||||
* Get the common client factory, start it if needed.
|
||||
* Remember to call clientFactory.close() afterward to release it!
|
||||
* @return Common client factory
|
||||
*/
|
||||
static ClientFactory acquireCommonClientFactory() {
|
||||
CommonClientFactory common = ClientFactoryImpl.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");
|
||||
}
|
||||
};
|
||||
if (ClientFactoryImpl.COMMON == null) {
|
||||
ClientFactoryImpl.COMMON = new CommonClientFactory();
|
||||
}
|
||||
common = COMMON;
|
||||
common = ClientFactoryImpl.COMMON;
|
||||
}
|
||||
}
|
||||
common.acquire();
|
||||
return common;
|
||||
}
|
||||
|
||||
public ClientFactory() {
|
||||
try {
|
||||
Init.init();
|
||||
} catch (UnsupportedNativeLibraryException e) {
|
||||
throw new RuntimeException("Can't load the client factory because TDLight can't be loaded", e);
|
||||
}
|
||||
}
|
||||
|
||||
public TelegramClient createClient() {
|
||||
return new AutoCleaningTelegramClient(state);
|
||||
}
|
||||
|
||||
public ReactiveTelegramClient createReactive() {
|
||||
return new InternalReactiveClient(state);
|
||||
}
|
||||
|
||||
public void startIfNeeded() {
|
||||
if (state.shouldStartNow()) {
|
||||
try {
|
||||
Init.init();
|
||||
responseReceiver.start();
|
||||
this.cleanable = CleanSupport.register(this, () -> {
|
||||
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.debug("Removing Client {} from event handlers", clientId);
|
||||
state.removeClientEventHandlers(clientId);
|
||||
logger.debug("Removed Client {} from event handlers", clientId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get only events that have been dropped, ignoring synthetic errors related to the closure of a client
|
||||
* Create a new Client Factory
|
||||
*/
|
||||
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;
|
||||
static ClientFactory create() {
|
||||
return new ClientFactoryImpl();
|
||||
}
|
||||
|
||||
protected void closeInternal() {
|
||||
if (state.shouldCloseNow()) {
|
||||
try {
|
||||
cleanable.clean();
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed to close", e);
|
||||
}
|
||||
this.state.setStopped();
|
||||
}
|
||||
}
|
||||
TelegramClient createClient();
|
||||
|
||||
ReactiveTelegramClient createReactive();
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
void close();
|
||||
}
|
||||
|
|
243
tdlight-java/src/main/java/it/tdlight/ClientFactoryImpl.java
Normal file
243
tdlight-java/src/main/java/it/tdlight/ClientFactoryImpl.java
Normal file
|
@ -0,0 +1,243 @@
|
|||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
import it.tdlight.util.UnsupportedNativeLibraryException;
|
||||
import it.tdlight.util.CleanSupport;
|
||||
import it.tdlight.util.CleanSupport.CleanableSupport;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* TDLight client factory
|
||||
*/
|
||||
class ClientFactoryImpl implements ClientFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ClientFactoryImpl.class);
|
||||
|
||||
static volatile CommonClientFactory 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 ClientFactoryImpl() {
|
||||
try {
|
||||
Init.init();
|
||||
} catch (UnsupportedNativeLibraryException e) {
|
||||
throw new RuntimeException("Can't load the client factory because TDLight can't be loaded", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelegramClient createClient() {
|
||||
return new AutoCleaningTelegramClient(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveTelegramClient createReactive() {
|
||||
return new InternalReactiveClient(state);
|
||||
}
|
||||
|
||||
public void startIfNeeded() {
|
||||
if (state.shouldStartNow()) {
|
||||
try {
|
||||
Init.init();
|
||||
responseReceiver.start();
|
||||
this.cleanable = CleanSupport.register(this, () -> {
|
||||
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) {
|
||||
StampedLock eventsHandlingLock = state.getEventsHandlingLock();
|
||||
boolean closeWriteLockAcquisitionFailed = false;
|
||||
long stamp = eventsHandlingLock.readLock();
|
||||
try {
|
||||
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) {
|
||||
long writeLockStamp = eventsHandlingLock.tryConvertToWriteLock(stamp);
|
||||
if (writeLockStamp != 0L) {
|
||||
stamp = writeLockStamp;
|
||||
removeClientEventHandlers(clientId);
|
||||
} else {
|
||||
closeWriteLockAcquisitionFailed = true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
eventsHandlingLock.unlock(stamp);
|
||||
}
|
||||
|
||||
if (closeWriteLockAcquisitionFailed) {
|
||||
stamp = eventsHandlingLock.writeLock();
|
||||
try {
|
||||
removeClientEventHandlers(clientId);
|
||||
} finally {
|
||||
eventsHandlingLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method only inside handleClientEvents!
|
||||
* Ensure that state has the eventsHandlingLock locked in write mode
|
||||
*/
|
||||
private void removeClientEventHandlers(int clientId) {
|
||||
logger.debug("Removing Client {} from event handlers", clientId);
|
||||
state.removeClientEventHandlers(clientId);
|
||||
logger.debug("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 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;
|
||||
}
|
||||
}
|
||||
|
||||
static class CommonClientFactory implements ClientFactory {
|
||||
|
||||
private int references;
|
||||
private ClientFactory clientFactory;
|
||||
|
||||
void acquire() {
|
||||
synchronized (this) {
|
||||
references++;
|
||||
if (clientFactory == null) {
|
||||
clientFactory = new ClientFactoryImpl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ClientFactory getClientFactory() {
|
||||
ClientFactory clientFactory;
|
||||
synchronized (this) {
|
||||
clientFactory = this.clientFactory;
|
||||
if (clientFactory == null) {
|
||||
throw new IllegalStateException("Common client factory is closed");
|
||||
}
|
||||
}
|
||||
return clientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelegramClient createClient() {
|
||||
return getClientFactory().createClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveTelegramClient createReactive() {
|
||||
return getClientFactory().createReactive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ClientFactory clientFactoryToClose;
|
||||
synchronized (this) {
|
||||
references--;
|
||||
if (references == 0) {
|
||||
clientFactoryToClose = this.clientFactory;
|
||||
this.clientFactory = null;
|
||||
} else {
|
||||
clientFactoryToClose = null;
|
||||
}
|
||||
}
|
||||
if (clientFactoryToClose != null) {
|
||||
clientFactoryToClose.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,27 +54,8 @@ public final class Init {
|
|||
ConstructorDetector.init();
|
||||
try {
|
||||
NativeClientAccess.execute(new SetLogVerbosityLevel(3));
|
||||
Log.setLogMessageHandler(3, (verbosityLevel, message) -> {
|
||||
switch (verbosityLevel) {
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
LOG.error(message);
|
||||
break;
|
||||
case 2:
|
||||
LOG.warn(message);
|
||||
break;
|
||||
case 3:
|
||||
LOG.info(message);
|
||||
break;
|
||||
case 4:
|
||||
LOG.debug(message);
|
||||
break;
|
||||
default:
|
||||
LOG.trace(message);
|
||||
break;
|
||||
}
|
||||
});
|
||||
Log.setLogMessageHandler(3, new Slf4JLogMessageHandler());
|
||||
Log.setLogStream(null);
|
||||
NativeClientAccess.execute(new SetLogStream(new LogStreamEmpty()));
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("Can't set verbosity level on startup", ex);
|
||||
|
@ -82,4 +63,5 @@ public final class Init {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.Objects;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.Marker;
|
||||
|
@ -149,21 +150,25 @@ final class InternalClient implements ClientEventsHandler, TelegramClient {
|
|||
}
|
||||
|
||||
private void createAndRegisterClient() {
|
||||
synchronized (this) {
|
||||
InternalClientsState clientManagerState = this.clientManagerState;
|
||||
final StampedLock eventsHandlingLock = clientManagerState.getEventsHandlingLock();
|
||||
long stamp = eventsHandlingLock.writeLock();
|
||||
try {
|
||||
if (clientId != null) {
|
||||
throw new UnsupportedOperationException("Can't initialize the same client twice!");
|
||||
}
|
||||
int clientId = NativeClientAccess.create();
|
||||
InternalClientsState clientManagerState = this.clientManagerState;
|
||||
this.clientId = clientId;
|
||||
this.clientId = NativeClientAccess.create();
|
||||
if (clientRegistrationEventHandler != null) {
|
||||
clientRegistrationEventHandler.onClientRegistered(clientId, clientManagerState::getNextQueryId);
|
||||
// Remove the event handler
|
||||
clientRegistrationEventHandler = null;
|
||||
}
|
||||
logger.debug(TG_MARKER, "Registering new client {}", clientId);
|
||||
clientManagerState.registerClient(clientId, this);
|
||||
logger.info(TG_MARKER, "Registered new client {}", clientId);
|
||||
} finally {
|
||||
eventsHandlingLock.unlockWrite(stamp);
|
||||
}
|
||||
clientManagerState.registerClient(clientId, this);
|
||||
logger.info(TG_MARKER, "Registered new client {}", clientId);
|
||||
|
||||
// Send a dummy request to start TDLib
|
||||
logger.debug(TG_MARKER, "Sending dummy startup request as client {}", clientId);
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package it.tdlight;
|
||||
|
||||
import io.atlassian.util.concurrent.CopyOnWriteMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
public class InternalClientsState {
|
||||
static final int STATE_INITIAL = 0;
|
||||
|
@ -14,13 +16,17 @@ public class InternalClientsState {
|
|||
static final int STATE_STOPPED = 4;
|
||||
private final AtomicInteger runState = new AtomicInteger();
|
||||
private final AtomicLong currentQueryId = new AtomicLong();
|
||||
private final Map<Integer, ClientEventsHandler> registeredClientEventHandlers = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, ClientEventsHandler> registeredClientEventHandlers = new HashMap<>();
|
||||
private final StampedLock eventsHandlingLock = new StampedLock();
|
||||
|
||||
|
||||
public long getNextQueryId() {
|
||||
return currentQueryId.updateAndGet(value -> (value == Long.MAX_VALUE ? 0 : value) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Before calling this method, lock using getEventsHandlingLock().writeLock()
|
||||
*/
|
||||
public void registerClient(int clientId, ClientEventsHandler internalClient) {
|
||||
boolean replaced = registeredClientEventHandlers.put(clientId, internalClient) != null;
|
||||
if (replaced) {
|
||||
|
@ -28,10 +34,17 @@ public class InternalClientsState {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before calling this method, lock using getEventsHandlingLock().readLock()
|
||||
*/
|
||||
public ClientEventsHandler getClientEventsHandler(int clientId) {
|
||||
return registeredClientEventHandlers.get(clientId);
|
||||
}
|
||||
|
||||
public StampedLock getEventsHandlingLock() {
|
||||
return eventsHandlingLock;
|
||||
}
|
||||
|
||||
public boolean shouldStartNow() {
|
||||
return runState.compareAndSet(STATE_INITIAL, STATE_STARTING);
|
||||
}
|
||||
|
@ -46,6 +59,9 @@ public class InternalClientsState {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before calling this method, lock using getEventsHandlingLock().writeLock()
|
||||
*/
|
||||
public void removeClientEventHandlers(int clientId) {
|
||||
registeredClientEventHandlers.remove(clientId);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.concurrent.ScheduledFuture;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -165,9 +166,15 @@ final class InternalReactiveClient implements ClientEventsHandler, ReactiveTeleg
|
|||
}
|
||||
logger.debug(TG_MARKER, "Creating new client");
|
||||
clientId = NativeClientAccess.create();
|
||||
logger.debug(TG_MARKER, "Registering new client {}", clientId);
|
||||
clientManagerState.registerClient(clientId, this);
|
||||
logger.debug(TG_MARKER, "Registered new client {}", clientId);
|
||||
StampedLock eventsHandlingLock = clientManagerState.getEventsHandlingLock();
|
||||
long stamp = eventsHandlingLock.writeLock();
|
||||
try {
|
||||
logger.debug(TG_MARKER, "Registering new client {}", clientId);
|
||||
clientManagerState.registerClient(clientId, this);
|
||||
logger.info(TG_MARKER, "Registered new client {}", clientId);
|
||||
} finally {
|
||||
eventsHandlingLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
package it.tdlight;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.LogStream;
|
||||
import it.tdlight.jni.TdApi.LogStreamDefault;
|
||||
import it.tdlight.jni.TdApi.LogStreamEmpty;
|
||||
import it.tdlight.jni.TdApi.LogStreamFile;
|
||||
import it.tdlight.jni.TdApi.SetLogStream;
|
||||
import it.tdlight.jni.TdApi.SetLogVerbosityLevel;
|
||||
import it.tdlight.tdnative.NativeClient.LogMessageHandler;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Class used for managing internal TDLib logging. Use TdApi.*Log* methods instead.
|
||||
* Class used for managing internal TDLib logging.
|
||||
*/
|
||||
public final class Log {
|
||||
|
||||
|
@ -86,6 +89,11 @@ public final class Log {
|
|||
updateLog();
|
||||
}
|
||||
|
||||
public static void disable() {
|
||||
setLogMessageHandler(0, null);
|
||||
setLogStream(null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets the log message handler
|
||||
|
@ -96,6 +104,14 @@ public final class Log {
|
|||
* @param logMessageHandler handler
|
||||
*/
|
||||
public static void setLogMessageHandler(int maxVerbosityLevel, LogMessageHandler logMessageHandler) {
|
||||
NativeClientAccess.setLogMessageHandler(maxVerbosityLevel, logMessageHandler);
|
||||
NativeClientAccess.setLogMessageHandler(logMessageHandler != null ? maxVerbosityLevel : Math.min(maxVerbosityLevel, 1),
|
||||
logMessageHandler != null ? logMessageHandler : new Slf4JLogMessageHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the log stream
|
||||
*/
|
||||
public static void setLogStream(LogStream logStream) {
|
||||
NativeClientAccess.execute(new SetLogStream(logStream != null ? logStream : new LogStreamEmpty()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
|||
private final Object registeredClientsLock = new Object();
|
||||
// Do not modify the int[] directly, this should be replaced
|
||||
private volatile int[] registeredClients = new int[0];
|
||||
private volatile boolean closeRequested;
|
||||
|
||||
|
||||
public ResponseReceiver(EventsHandler eventsHandler) {
|
||||
|
@ -67,7 +68,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
|||
final SimpleIntQueue closedClients = new SimpleIntQueue();
|
||||
try {
|
||||
boolean interrupted;
|
||||
while (!(interrupted = Thread.interrupted()) && registeredClients.length > 0) {
|
||||
while (!(interrupted = Thread.interrupted()) && (!closeRequested || registeredClients.length > 0)) {
|
||||
// Timeout is expressed in seconds
|
||||
int resultsCount = receive(clientIds, eventIds, events, 2.0);
|
||||
LOG.trace("Received {} events", resultsCount);
|
||||
|
@ -272,6 +273,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
|||
|
||||
@Override
|
||||
public void close() throws InterruptedException {
|
||||
this.closeRequested = true;
|
||||
this.closeWait.await();
|
||||
if (registeredClients.length == 0) {
|
||||
LOG.debug("Interrupting response receiver");
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package it.tdlight;
|
||||
|
||||
import it.tdlight.tdnative.NativeClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Slf4JLogMessageHandler implements NativeClient.LogMessageHandler {
|
||||
|
||||
public static final Logger LOG = LoggerFactory.getLogger("it.tdlight.TDLight");
|
||||
|
||||
@Override
|
||||
public void onLogMessage(int verbosityLevel, String message) {
|
||||
switch (verbosityLevel) {
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
LOG.error(message);
|
||||
break;
|
||||
case 2:
|
||||
LOG.warn(message);
|
||||
break;
|
||||
case 3:
|
||||
LOG.info(message);
|
||||
break;
|
||||
case 4:
|
||||
LOG.debug(message);
|
||||
break;
|
||||
default:
|
||||
LOG.trace(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,8 +10,13 @@ final class AuthenticationDataImpl implements SimpleAuthenticationSupplier<Authe
|
|||
|
||||
private final String userPhoneNumber;
|
||||
private final String botToken;
|
||||
private final boolean test;
|
||||
/**
|
||||
* Safe string representation of the bot token
|
||||
*/
|
||||
private final String botTokenId;
|
||||
|
||||
AuthenticationDataImpl(String userPhoneNumber, String botToken) {
|
||||
AuthenticationDataImpl(String userPhoneNumber, String botToken, boolean test) {
|
||||
if ((userPhoneNumber == null) == (botToken == null)) {
|
||||
throw new IllegalArgumentException("Please use either a bot token or a phone number");
|
||||
}
|
||||
|
@ -22,6 +27,17 @@ final class AuthenticationDataImpl implements SimpleAuthenticationSupplier<Authe
|
|||
}
|
||||
this.userPhoneNumber = userPhoneNumber;
|
||||
this.botToken = botToken;
|
||||
this.test = test;
|
||||
if (botToken != null) {
|
||||
String[] parts = botToken.split(":", 2);
|
||||
if (parts.length > 0) {
|
||||
botTokenId = parts[0];
|
||||
} else {
|
||||
botTokenId = "";
|
||||
}
|
||||
} else {
|
||||
botTokenId = "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,6 +50,10 @@ final class AuthenticationDataImpl implements SimpleAuthenticationSupplier<Authe
|
|||
return botToken != null;
|
||||
}
|
||||
|
||||
public boolean isTest() {
|
||||
return test;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserPhoneNumber() {
|
||||
if (userPhoneNumber == null) {
|
||||
|
@ -52,10 +72,16 @@ final class AuthenticationDataImpl implements SimpleAuthenticationSupplier<Authe
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
String value;
|
||||
if (userPhoneNumber != null) {
|
||||
return userPhoneNumber;
|
||||
value = userPhoneNumber;
|
||||
} else {
|
||||
value = botTokenId;
|
||||
}
|
||||
if (test) {
|
||||
return value + " (test)";
|
||||
} else {
|
||||
return "\"" + botToken + "\"";
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,12 +94,13 @@ final class AuthenticationDataImpl implements SimpleAuthenticationSupplier<Authe
|
|||
return false;
|
||||
}
|
||||
AuthenticationDataImpl that = (AuthenticationDataImpl) o;
|
||||
return Objects.equals(userPhoneNumber, that.userPhoneNumber) && Objects.equals(botToken, that.botToken);
|
||||
return Objects.equals(userPhoneNumber, that.userPhoneNumber) && Objects.equals(botToken, that.botToken)
|
||||
&& Objects.equals(test, that.test);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userPhoneNumber, botToken);
|
||||
return Objects.hash(userPhoneNumber, botToken, test);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,20 @@ import java.util.concurrent.CompletableFuture;
|
|||
|
||||
public interface AuthenticationSupplier<T extends AuthenticationData> {
|
||||
|
||||
/**
|
||||
* User used in Telegram Test Servers
|
||||
* @param value any number from 0001 to 9999
|
||||
*/
|
||||
static SimpleAuthenticationSupplier<?> testUser(int value) {
|
||||
if (value < 1) {
|
||||
throw new IllegalArgumentException("value must be greater than 0");
|
||||
}
|
||||
if (value > 9999) {
|
||||
throw new IllegalArgumentException("value must be lower than 10000");
|
||||
}
|
||||
return new AuthenticationDataImpl("999662" + value, null, true);
|
||||
}
|
||||
|
||||
CompletableFuture<T> get();
|
||||
|
||||
static SimpleAuthenticationSupplier<?> qrCode() {
|
||||
|
@ -19,11 +33,11 @@ public interface AuthenticationSupplier<T extends AuthenticationData> {
|
|||
}
|
||||
|
||||
static SimpleAuthenticationSupplier<?> user(String userPhoneNumber) {
|
||||
return new AuthenticationDataImpl(userPhoneNumber, null);
|
||||
return new AuthenticationDataImpl(userPhoneNumber, null, false);
|
||||
}
|
||||
|
||||
static SimpleAuthenticationSupplier<?> bot(String botToken) {
|
||||
return new AuthenticationDataImpl(null, botToken);
|
||||
return new AuthenticationDataImpl(null, botToken, false);
|
||||
}
|
||||
|
||||
static ConsoleInteractiveAuthenticationData consoleLogin() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
|||
import it.tdlight.jni.TdApi.User;
|
||||
import it.tdlight.jni.TdApi.Error;
|
||||
import it.tdlight.jni.TdApi.UserTypeRegular;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -16,6 +17,7 @@ final class AuthorizationStateReadyGetMe implements GenericUpdateHandler<UpdateA
|
|||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateReadyGetMe.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final CompletableFuture<Void> meReceived = new CompletableFuture<>();
|
||||
private final AtomicReference<User> me = new AtomicReference<>();
|
||||
private final AuthorizationStateReadyLoadChats mainChatsLoader;
|
||||
private final AuthorizationStateReadyLoadChats archivedChatsLoader;
|
||||
|
@ -32,10 +34,14 @@ final class AuthorizationStateReadyGetMe implements GenericUpdateHandler<UpdateA
|
|||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateReady.CONSTRUCTOR) {
|
||||
client.send(new GetMe(), me -> {
|
||||
if (me.getConstructor() == Error.CONSTRUCTOR) {
|
||||
throw new TelegramError((Error) me);
|
||||
try {
|
||||
if (me.getConstructor() == Error.CONSTRUCTOR) {
|
||||
throw new TelegramError((Error) me);
|
||||
}
|
||||
this.me.set((User) me);
|
||||
} finally {
|
||||
this.meReceived.complete(null);
|
||||
}
|
||||
this.me.set((User) me);
|
||||
if (((User) me).type.getConstructor() == UserTypeRegular.CONSTRUCTOR) {
|
||||
mainChatsLoader.onUpdate(update);
|
||||
archivedChatsLoader.onUpdate(update);
|
||||
|
@ -47,4 +53,8 @@ final class AuthorizationStateReadyGetMe implements GenericUpdateHandler<UpdateA
|
|||
public User getMe() {
|
||||
return me.get();
|
||||
}
|
||||
|
||||
public CompletableFuture<User> getMeAsync() {
|
||||
return meReceived.thenApply(v -> me.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,16 @@ final class AuthorizationStateWaitCodeHandler implements GenericUpdateHandler<Up
|
|||
|
||||
private final TelegramClient client;
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final String testCode;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitCodeHandler(TelegramClient client,
|
||||
ClientInteraction clientInteraction,
|
||||
String testCode,
|
||||
ExceptionHandler exceptionHandler) {
|
||||
this.client = client;
|
||||
this.clientInteraction = clientInteraction;
|
||||
this.testCode = testCode;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
|
@ -25,23 +28,31 @@ final class AuthorizationStateWaitCodeHandler implements GenericUpdateHandler<Up
|
|||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateWaitCode.CONSTRUCTOR) {
|
||||
AuthorizationStateWaitCode authorizationState = (AuthorizationStateWaitCode) update.authorizationState;
|
||||
ParameterInfo parameterInfo = new ParameterInfoCode(authorizationState.codeInfo.phoneNumber,
|
||||
authorizationState.codeInfo.nextType,
|
||||
authorizationState.codeInfo.timeout,
|
||||
authorizationState.codeInfo.type
|
||||
);
|
||||
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) {
|
||||
throw new TelegramError((TdApi.Error) ok);
|
||||
if (testCode != null) {
|
||||
sendCode(testCode);
|
||||
} else {
|
||||
ParameterInfo parameterInfo = new ParameterInfoCode(authorizationState.codeInfo.phoneNumber,
|
||||
authorizationState.codeInfo.nextType,
|
||||
authorizationState.codeInfo.timeout,
|
||||
authorizationState.codeInfo.type
|
||||
);
|
||||
clientInteraction.onParameterRequest(InputParameter.ASK_CODE, parameterInfo).whenComplete((code, ex) -> {
|
||||
if (ex != null) {
|
||||
exceptionHandler.onException(ex);
|
||||
return;
|
||||
}
|
||||
}, exceptionHandler);
|
||||
});
|
||||
sendCode(code);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCode(String code) {
|
||||
CheckAuthenticationCode response = new CheckAuthenticationCode(code);
|
||||
client.send(response, ok -> {
|
||||
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {
|
||||
throw new TelegramError((TdApi.Error) ok);
|
||||
}
|
||||
}, exceptionHandler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitEmailAddress;
|
||||
import it.tdlight.jni.TdApi.CheckAuthenticationCode;
|
||||
import it.tdlight.jni.TdApi.CheckAuthenticationEmailCode;
|
||||
import it.tdlight.jni.TdApi.SetAuthenticationEmailAddress;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
|
||||
final class AuthorizationStateWaitEmailAddressHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private final TelegramClient client;
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitEmailAddressHandler(TelegramClient client,
|
||||
ClientInteraction clientInteraction,
|
||||
ExceptionHandler exceptionHandler) {
|
||||
this.client = client;
|
||||
this.clientInteraction = clientInteraction;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateWaitEmailAddress.CONSTRUCTOR) {
|
||||
AuthorizationStateWaitEmailAddress authorizationState = (AuthorizationStateWaitEmailAddress) update.authorizationState;
|
||||
ParameterInfo parameterInfo = new ParameterInfoEmailAddress(authorizationState.allowAppleId, authorizationState.allowGoogleId);
|
||||
clientInteraction.onParameterRequest(InputParameter.ASK_EMAIL_ADDRESS, parameterInfo).whenComplete((emailAddress, ex) -> {
|
||||
if (ex != null) {
|
||||
exceptionHandler.onException(ex);
|
||||
return;
|
||||
}
|
||||
sendEmailAddress(emailAddress);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void sendEmailAddress(String emailAddress) {
|
||||
SetAuthenticationEmailAddress response = new SetAuthenticationEmailAddress(emailAddress);
|
||||
client.send(response, ok -> {
|
||||
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {
|
||||
throw new TelegramError((TdApi.Error) ok);
|
||||
}
|
||||
}, exceptionHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.Locale;
|
||||
|
||||
final class AuthorizationStateWaitEmailCodeHandler implements GenericUpdateHandler<TdApi.UpdateAuthorizationState> {
|
||||
|
||||
private final TelegramClient client;
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitEmailCodeHandler(TelegramClient client,
|
||||
ClientInteraction clientInteraction,
|
||||
ExceptionHandler exceptionHandler) {
|
||||
this.client = client;
|
||||
this.clientInteraction = clientInteraction;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(TdApi.UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == TdApi.AuthorizationStateWaitEmailCode.CONSTRUCTOR) {
|
||||
TdApi.AuthorizationStateWaitEmailCode authorizationState = (TdApi.AuthorizationStateWaitEmailCode) update.authorizationState;
|
||||
ParameterInfo parameterInfo = new ParameterInfoEmailCode(authorizationState.allowAppleId,
|
||||
authorizationState.allowGoogleId,
|
||||
authorizationState.codeInfo.emailAddressPattern,
|
||||
authorizationState.codeInfo.length,
|
||||
EmailAddressResetState.valueOf(authorizationState.emailAddressResetState.getClass().getSimpleName().substring("EmailAddressResetState".length()).toUpperCase(Locale.ROOT))
|
||||
);
|
||||
clientInteraction.onParameterRequest(InputParameter.ASK_EMAIL_CODE, parameterInfo).whenComplete((emailAddress, ex) -> {
|
||||
if (ex != null) {
|
||||
exceptionHandler.onException(ex);
|
||||
return;
|
||||
}
|
||||
sendEmailCode(emailAddress);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void sendEmailCode(String code) {
|
||||
TdApi.CheckAuthenticationEmailCode response = new TdApi.CheckAuthenticationEmailCode(new TdApi.EmailAddressAuthenticationCode(code));
|
||||
client.send(response, ok -> {
|
||||
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {
|
||||
throw new TelegramError((TdApi.Error) ok);
|
||||
}
|
||||
}, exceptionHandler);
|
||||
}
|
||||
}
|
|
@ -7,16 +7,16 @@ import java.util.concurrent.CountDownLatch;
|
|||
|
||||
final class AuthorizationStateWaitForExit implements GenericUpdateHandler<TdApi.UpdateAuthorizationState> {
|
||||
|
||||
private final CountDownLatch closed;
|
||||
private final Runnable setClosed;
|
||||
|
||||
public AuthorizationStateWaitForExit(CountDownLatch closed) {
|
||||
this.closed = closed;
|
||||
public AuthorizationStateWaitForExit(Runnable setClosed) {
|
||||
this.setClosed = setClosed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateClosed.CONSTRUCTOR) {
|
||||
closed.countDown();
|
||||
setClosed.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
|
||||
final class AuthorizationStateWaitReady implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private final Runnable setReady;
|
||||
|
||||
public AuthorizationStateWaitReady(Runnable setReady) {
|
||||
this.setReady = setReady;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateReady.CONSTRUCTOR) {
|
||||
setReady.run();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ final class AuthorizationStateWaitRegistrationHandler implements GenericUpdateHa
|
|||
exceptionHandler.onException(new IllegalArgumentException("Last name must be under 64 characters"));
|
||||
return;
|
||||
}
|
||||
RegisterUser response = new RegisterUser(firstName, lastName);
|
||||
RegisterUser response = new RegisterUser(firstName, lastName, true);
|
||||
client.send(response, ok -> {
|
||||
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {
|
||||
throw new TelegramError((TdApi.Error) ok);
|
||||
|
|
|
@ -37,8 +37,6 @@ final class AuthorizationStateWaitTdlibParametersHandler implements GenericUpdat
|
|||
params.deviceModel = settings.getDeviceModel();
|
||||
params.systemVersion = settings.getSystemVersion();
|
||||
params.applicationVersion = settings.getApplicationVersion();
|
||||
params.enableStorageOptimizer = settings.isStorageOptimizerEnabled();
|
||||
params.ignoreFileNames = settings.isIgnoreFileNames();
|
||||
params.databaseEncryptionKey = null;
|
||||
client.send(params, ok -> {
|
||||
if (ok.getConstructor() == TdApi.Error.CONSTRUCTOR) {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
public enum EmailAddressResetState {
|
||||
AVAILABLE,
|
||||
PENDING
|
||||
}
|
|
@ -1,5 +1,12 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
public enum InputParameter {
|
||||
ASK_FIRST_NAME, ASK_LAST_NAME, ASK_CODE, ASK_PASSWORD, NOTIFY_LINK, TERMS_OF_SERVICE
|
||||
ASK_FIRST_NAME,
|
||||
ASK_LAST_NAME,
|
||||
ASK_CODE,
|
||||
ASK_PASSWORD,
|
||||
NOTIFY_LINK,
|
||||
TERMS_OF_SERVICE,
|
||||
ASK_EMAIL_ADDRESS,
|
||||
ASK_EMAIL_CODE
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ public interface MutableTelegramClient {
|
|||
|
||||
<T extends TdApi.Update> void addCommandHandler(String commandName, CommandHandler handler);
|
||||
|
||||
<T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler);
|
||||
<T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<? super T> handler);
|
||||
|
||||
void addUpdatesHandler(GenericUpdateHandler<TdApi.Update> handler);
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi.AuthenticationCodeType;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class ParameterInfoEmailAddress implements ParameterInfo {
|
||||
|
||||
private final boolean allowGoogleId;
|
||||
private final boolean allowAppleId;
|
||||
|
||||
public ParameterInfoEmailAddress(boolean allowGoogleId,
|
||||
boolean allowAppleId) {
|
||||
this.allowGoogleId = allowGoogleId;
|
||||
this.allowAppleId = allowAppleId;
|
||||
}
|
||||
|
||||
public boolean isAllowGoogleId() {
|
||||
return allowGoogleId;
|
||||
}
|
||||
|
||||
public boolean isAllowAppleId() {
|
||||
return allowAppleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ParameterInfoEmailAddress that = (ParameterInfoEmailAddress) o;
|
||||
return allowGoogleId == that.allowGoogleId && allowAppleId == that.allowAppleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(allowGoogleId, allowAppleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ParameterInfoEmailAddress.class.getSimpleName() + "[", "]")
|
||||
.add("allowGoogleId='" + allowGoogleId + "'")
|
||||
.add("allowAppleId=" + allowAppleId)
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi.EmailAddressAuthenticationCodeInfo;
|
||||
import it.tdlight.jni.TdApi.EmailAddressResetStateAvailable;
|
||||
import it.tdlight.jni.TdApi.EmailAddressResetStatePending;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class ParameterInfoEmailCode implements ParameterInfo {
|
||||
|
||||
private final boolean allowGoogleId;
|
||||
private final boolean allowAppleId;
|
||||
private final String emailAddressPattern;
|
||||
private final int emailLength;
|
||||
private final EmailAddressResetState emailAddressResetState;
|
||||
|
||||
public ParameterInfoEmailCode(boolean allowGoogleId,
|
||||
boolean allowAppleId,
|
||||
String emailAddressPattern,
|
||||
int emailLength,
|
||||
EmailAddressResetState emailAddressResetState) {
|
||||
this.allowGoogleId = allowGoogleId;
|
||||
this.allowAppleId = allowAppleId;
|
||||
this.emailAddressPattern = emailAddressPattern;
|
||||
this.emailLength = emailLength;
|
||||
this.emailAddressResetState = emailAddressResetState;
|
||||
}
|
||||
|
||||
public boolean isAllowGoogleId() {
|
||||
return allowGoogleId;
|
||||
}
|
||||
|
||||
public boolean isAllowAppleId() {
|
||||
return allowAppleId;
|
||||
}
|
||||
|
||||
public String getEmailAddressPattern() {
|
||||
return emailAddressPattern;
|
||||
}
|
||||
|
||||
public int getEmailLength() {
|
||||
return emailLength;
|
||||
}
|
||||
|
||||
public EmailAddressResetState getEmailAddressResetState() {
|
||||
return emailAddressResetState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ParameterInfoEmailCode that = (ParameterInfoEmailCode) o;
|
||||
return allowGoogleId == that.allowGoogleId && allowAppleId == that.allowAppleId && emailLength == that.emailLength
|
||||
&& Objects.equals(emailAddressPattern, that.emailAddressPattern)
|
||||
&& emailAddressResetState == that.emailAddressResetState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(allowGoogleId, allowAppleId, emailAddressPattern, emailLength, emailAddressResetState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ParameterInfoEmailCode.class.getSimpleName() + "[", "]")
|
||||
.add("allowGoogleId=" + allowGoogleId)
|
||||
.add("allowAppleId=" + allowAppleId)
|
||||
.add("emailAddressPattern='" + emailAddressPattern + "'")
|
||||
.add("emailLength=" + emailLength)
|
||||
.add("emailAddressResetState=" + emailAddressResetState)
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -8,9 +8,9 @@ import it.tdlight.jni.TdApi.Update;
|
|||
class SimpleResultHandler<T extends Update> implements ResultHandler<Update> {
|
||||
|
||||
private final int updateConstructor;
|
||||
private final GenericUpdateHandler<T> handler;
|
||||
private final GenericUpdateHandler<? super T> handler;
|
||||
|
||||
public SimpleResultHandler(Class<T> type, GenericUpdateHandler<T> handler) {
|
||||
public SimpleResultHandler(Class<T> type, GenericUpdateHandler<? super T> handler) {
|
||||
this.updateConstructor = ConstructorDetector.getConstructor(type);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import static it.tdlight.util.MapUtils.addAllKeys;
|
||||
|
||||
import io.atlassian.util.concurrent.CopyOnWriteMap;
|
||||
import it.tdlight.ClientFactory;
|
||||
import it.tdlight.ExceptionHandler;
|
||||
import it.tdlight.Init;
|
||||
import it.tdlight.ResultHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.Update;
|
||||
import it.tdlight.util.UnsupportedNativeLibraryException;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.ChatListArchive;
|
||||
import it.tdlight.jni.TdApi.ChatListMain;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
import it.tdlight.jni.TdApi.LoadChats;
|
||||
import it.tdlight.jni.TdApi.LogOut;
|
||||
import it.tdlight.jni.TdApi.Message;
|
||||
import it.tdlight.jni.TdApi.Update;
|
||||
import it.tdlight.jni.TdApi.User;
|
||||
import it.tdlight.util.FutureSupport;
|
||||
import it.tdlight.util.UnsupportedNativeLibraryException;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -20,15 +26,16 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static it.tdlight.util.MapUtils.addAllKeys;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class SimpleTelegramClient implements Authenticable, MutableTelegramClient {
|
||||
public final class SimpleTelegramClient implements Authenticable, MutableTelegramClient, AutoCloseable {
|
||||
|
||||
public static final Logger LOG = LoggerFactory.getLogger(SimpleTelegramClient.class);
|
||||
|
||||
|
@ -54,7 +61,9 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
private final AuthorizationStateReadyLoadChats mainChatsLoader;
|
||||
private final AuthorizationStateReadyLoadChats archivedChatsLoader;
|
||||
|
||||
private final CountDownLatch closed = new CountDownLatch(1);
|
||||
private final CompletableFuture<Void> ready = new CompletableFuture<>();
|
||||
private final CompletableFuture<Void> closed = new CompletableFuture<>();
|
||||
private final ConcurrentMap<TemporaryMessageURL, CompletableFuture<Message>> temporaryMessages = new ConcurrentHashMap<>();
|
||||
|
||||
SimpleTelegramClient(ClientFactory clientFactory,
|
||||
TDLibSettings settings,
|
||||
|
@ -85,40 +94,65 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitAuthenticationDataHandler(client, this, this::handleDefaultException)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitRegistrationHandler(client,
|
||||
new SimpleTelegramClientInteraction(),
|
||||
SimpleTelegramClientInteraction updateHandlerInteraction = new SimpleTelegramClientInteraction();
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitRegistrationHandler(client,
|
||||
updateHandlerInteraction,
|
||||
this::handleDefaultException
|
||||
));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitPasswordHandler(client,
|
||||
updateHandlerInteraction,
|
||||
this::handleDefaultException
|
||||
));
|
||||
this.addUpdateHandler(
|
||||
TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitOtherDeviceConfirmationHandler(
|
||||
updateHandlerInteraction,
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitPasswordHandler(client,
|
||||
new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitOtherDeviceConfirmationHandler(new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitCodeHandler(client,
|
||||
new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException
|
||||
)
|
||||
);
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitForExit(this.closed));
|
||||
));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitCodeHandler(client,
|
||||
updateHandlerInteraction,
|
||||
getTestCode(authenticationData),
|
||||
this::handleDefaultException
|
||||
));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitEmailAddressHandler(client,
|
||||
updateHandlerInteraction,
|
||||
this::handleDefaultException
|
||||
));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitEmailCodeHandler(client,
|
||||
updateHandlerInteraction,
|
||||
this::handleDefaultException
|
||||
));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitReady(this::onReady));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateWaitForExit(this::onCloseUpdate));
|
||||
this.mainChatsLoader = new AuthorizationStateReadyLoadChats(client, new ChatListMain());
|
||||
this.archivedChatsLoader = new AuthorizationStateReadyLoadChats(client, new ChatListArchive());
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
this.meGetter = new AuthorizationStateReadyGetMe(client, mainChatsLoader, archivedChatsLoader));
|
||||
this.addUpdateHandler(TdApi.UpdateNewMessage.class, new CommandsHandler(client, this.commandHandlers, this::getMe));
|
||||
TemporaryMessageHandler temporaryMessageHandler = new TemporaryMessageHandler(this.temporaryMessages);
|
||||
this.addUpdateHandler(TdApi.UpdateMessageSendSucceeded.class, temporaryMessageHandler);
|
||||
this.addUpdateHandler(TdApi.UpdateMessageSendFailed.class, temporaryMessageHandler);
|
||||
this.authenticationData = authenticationData;
|
||||
createDirectories();
|
||||
client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException);
|
||||
}
|
||||
|
||||
private String getTestCode(AuthenticationSupplier<?> authenticationData) {
|
||||
if (authenticationData instanceof AuthenticationDataImpl) {
|
||||
if (!((AuthenticationDataImpl) authenticationData).isBot()
|
||||
&& ((AuthenticationDataImpl) authenticationData).isTest()) {
|
||||
String phoneNumber = ((AuthenticationDataImpl) authenticationData).getUserPhoneNumber();
|
||||
String loginCodeChar = phoneNumber.substring(5, 6);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
//noinspection StringRepeatCanBeUsed
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sb.append(loginCodeChar);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void handleUpdate(TdApi.Object update) {
|
||||
boolean handled = false;
|
||||
for (ResultHandler<TdApi.Update> updateHandler : updateHandlers.keySet()) {
|
||||
|
@ -173,7 +207,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler) {
|
||||
public <T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<? super T> handler) {
|
||||
this.updateHandlers.put(new SimpleResultHandler<>(updateType, handler), null);
|
||||
}
|
||||
|
||||
|
@ -211,6 +245,61 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
}
|
||||
}
|
||||
|
||||
private <R extends TdApi.Object> boolean shouldWaitForReadiness(Function<R> function) {
|
||||
switch (function.getConstructor()) {
|
||||
case TdApi.Close.CONSTRUCTOR:
|
||||
case TdApi.SetLogVerbosityLevel.CONSTRUCTOR:
|
||||
case TdApi.SetLogTagVerbosityLevel.CONSTRUCTOR:
|
||||
case TdApi.SetLogStream.CONSTRUCTOR:
|
||||
case TdApi.SetNetworkType.CONSTRUCTOR:
|
||||
case TdApi.SetOption.CONSTRUCTOR:
|
||||
case TdApi.GetOption.CONSTRUCTOR:
|
||||
case TdApi.GetAuthorizationState.CONSTRUCTOR:
|
||||
case TdApi.GetCurrentState.CONSTRUCTOR:
|
||||
case TdApi.GetLogStream.CONSTRUCTOR:
|
||||
case TdApi.GetLogTags.CONSTRUCTOR:
|
||||
case TdApi.GetLogVerbosityLevel.CONSTRUCTOR:
|
||||
case TdApi.GetLogTagVerbosityLevel.CONSTRUCTOR:
|
||||
case TdApi.GetNetworkStatistics.CONSTRUCTOR:
|
||||
case TdApi.AddNetworkStatistics.CONSTRUCTOR:
|
||||
case TdApi.ResetNetworkStatistics.CONSTRUCTOR:
|
||||
case TdApi.AddProxy.CONSTRUCTOR:
|
||||
case TdApi.DisableProxy.CONSTRUCTOR:
|
||||
case TdApi.EditProxy.CONSTRUCTOR:
|
||||
case TdApi.RemoveProxy.CONSTRUCTOR:
|
||||
case TdApi.PingProxy.CONSTRUCTOR:
|
||||
case TdApi.TestProxy.CONSTRUCTOR:
|
||||
case TdApi.EnableProxy.CONSTRUCTOR:
|
||||
case TdApi.GetProxyLink.CONSTRUCTOR:
|
||||
case TdApi.GetProxies.CONSTRUCTOR:
|
||||
case TdApi.LogOut.CONSTRUCTOR:
|
||||
case TdApi.SetTdlibParameters.CONSTRUCTOR:
|
||||
case TdApi.CheckAuthenticationCode.CONSTRUCTOR:
|
||||
case TdApi.CheckAuthenticationBotToken.CONSTRUCTOR:
|
||||
case TdApi.CheckAuthenticationEmailCode.CONSTRUCTOR:
|
||||
case TdApi.CheckAuthenticationPassword.CONSTRUCTOR:
|
||||
case TdApi.CheckAuthenticationPasswordRecoveryCode.CONSTRUCTOR:
|
||||
case TdApi.SetAuthenticationPhoneNumber.CONSTRUCTOR:
|
||||
case TdApi.GetCountries.CONSTRUCTOR:
|
||||
case TdApi.GetCountryCode.CONSTRUCTOR:
|
||||
case TdApi.GetDatabaseStatistics.CONSTRUCTOR:
|
||||
case TdApi.GetDeepLinkInfo.CONSTRUCTOR:
|
||||
case TdApi.ParseMarkdown.CONSTRUCTOR:
|
||||
case TdApi.ParseTextEntities.CONSTRUCTOR:
|
||||
case TdApi.GetJsonString.CONSTRUCTOR:
|
||||
case TdApi.GetJsonValue.CONSTRUCTOR:
|
||||
case TdApi.GetLoginUrl.CONSTRUCTOR:
|
||||
case TdApi.GetLoginUrlInfo.CONSTRUCTOR:
|
||||
case TdApi.GetMarkdownText.CONSTRUCTOR:
|
||||
case TdApi.GetMemoryStatistics.CONSTRUCTOR:
|
||||
case TdApi.GetRecoveryEmailAddress.CONSTRUCTOR:
|
||||
case TdApi.GetStorageStatistics.CONSTRUCTOR:
|
||||
case TdApi.GetStorageStatisticsFast.CONSTRUCTOR:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to TDLib and get the result.
|
||||
*
|
||||
|
@ -219,7 +308,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
* @throws NullPointerException if function is null.
|
||||
*/
|
||||
public <R extends TdApi.Object> void send(TdApi.Function<R> function, GenericResultHandler<R> resultHandler) {
|
||||
client.send(function, result -> resultHandler.onResult(Result.of(result)), this::handleResultHandlingException);
|
||||
this.send(function, resultHandler, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -233,12 +322,94 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
*/
|
||||
public <R extends TdApi.Object> void send(TdApi.Function<R> function, GenericResultHandler<R> resultHandler,
|
||||
ExceptionHandler resultHandlerExceptionHandler) {
|
||||
if (shouldWaitForReadiness(function)) {
|
||||
ready.whenComplete((ignored, error) -> {
|
||||
if (error != null) {
|
||||
resultHandler.onResult(Result.ofError(error));
|
||||
} else {
|
||||
this.sendUnsafe(function, resultHandler, resultHandlerExceptionHandler);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.sendUnsafe(function, resultHandler, resultHandlerExceptionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to TDLib and get the result.
|
||||
*
|
||||
* @param function The request to TDLib.
|
||||
* @param resultHandler Result handler. If it is null, nothing will be called.
|
||||
* @param resultHandlerExceptionHandler Handle exceptions thrown inside the result handler.
|
||||
* If it is null, the default exception handler will be called.
|
||||
* @throws NullPointerException if function is null.
|
||||
*/
|
||||
public <R extends TdApi.Object> void sendUnsafe(TdApi.Function<R> function, GenericResultHandler<R> resultHandler,
|
||||
ExceptionHandler resultHandlerExceptionHandler) {
|
||||
if (resultHandlerExceptionHandler == null) {
|
||||
resultHandlerExceptionHandler = this::handleResultHandlingException;
|
||||
}
|
||||
client.send(function, result -> resultHandler.onResult(Result.of(result)), resultHandlerExceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to TDLib and get the result.
|
||||
*
|
||||
* @param function The request to TDLib.
|
||||
* @throws NullPointerException if function is null.
|
||||
*/
|
||||
public <R extends TdApi.Object> CompletableFuture<R> send(TdApi.Function<R> function) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
if (shouldWaitForReadiness(function)) {
|
||||
return ready.thenCompose(r -> this.sendUnsafe(function));
|
||||
} else {
|
||||
return this.sendUnsafe(function);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to TDLib and get the result.
|
||||
*
|
||||
* @param function The request to TDLib.
|
||||
* @throws NullPointerException if function is null.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <R extends TdApi.Object> CompletableFuture<R> sendUnsafe(TdApi.Function<R> function) {
|
||||
CompletableFuture<R> future = new CompletableFuture<>();
|
||||
client.send(function, result -> {
|
||||
if (result instanceof TdApi.Error) {
|
||||
future.completeExceptionally(new TelegramError((TdApi.Error) result));
|
||||
} else {
|
||||
future.complete((R) result);
|
||||
}
|
||||
}, future::completeExceptionally);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message
|
||||
*
|
||||
* @param function The request to TDLib.
|
||||
* @param wait Wait until the message has been sent.
|
||||
* If false, the returned message will not represent the real message, but it will be a temporary message.
|
||||
* @throws NullPointerException if function is null.
|
||||
*/
|
||||
public CompletableFuture<TdApi.Message> sendMessage(TdApi.SendMessage function, boolean wait) {
|
||||
CompletableFuture<TdApi.Message> sendRequest = this.send(function);
|
||||
if (wait) {
|
||||
return sendRequest.thenCompose(msg -> {
|
||||
CompletableFuture<TdApi.Message> future = new CompletableFuture<>();
|
||||
CompletableFuture<?> prev = temporaryMessages.put(new TemporaryMessageURL(msg.chatId, msg.id), future);
|
||||
if (prev != null) {
|
||||
prev.completeExceptionally(new IllegalStateException("Another temporary message has the same id"));
|
||||
}
|
||||
return future;
|
||||
});
|
||||
} else {
|
||||
return sendRequest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a synchronous function.
|
||||
* <strong>Please note that only some functions can be executed using this method.</strong>
|
||||
|
@ -248,6 +419,13 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
return Result.of(client.execute(function));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the close signal but don't wait
|
||||
*/
|
||||
public CompletableFuture<Void> closeAsync() {
|
||||
return this.send(new TdApi.Close()).thenCompose(x -> this.waitForExitAsync());
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the close signal but don't wait
|
||||
*/
|
||||
|
@ -275,7 +453,33 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
* Wait until TDLight is closed
|
||||
*/
|
||||
public void waitForExit() throws InterruptedException {
|
||||
closed.await();
|
||||
try {
|
||||
closed.get();
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until TDLight is closed
|
||||
*/
|
||||
public CompletableFuture<Void> waitForExitAsync() {
|
||||
return FutureSupport.copy(closed);
|
||||
}
|
||||
|
||||
private void onReady() {
|
||||
this.ready.complete(null);
|
||||
}
|
||||
|
||||
private void onCloseUpdate() {
|
||||
this.ready.completeExceptionally(new TelegramError(new TdApi.Error(400, "Client closed")));
|
||||
this.closed.complete(null);
|
||||
this.temporaryMessages.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
this.closeAsync().get(90, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private final class SimpleTelegramClientInteraction implements ClientInteraction {
|
||||
|
@ -298,6 +502,26 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
|||
return meGetter.getMe();
|
||||
}
|
||||
|
||||
public CompletableFuture<User> getMeAsync() {
|
||||
return meGetter.getMeAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads more chats from the main chat list. The loaded chats and their positions in the chat list will be sent through updates. Chats are sorted by the pair (chat.position.order, chat.id) in descending order. Returns a 404 error if all chats have been loaded.
|
||||
*
|
||||
**/
|
||||
public CompletableFuture<Void> loadChatListMainAsync() {
|
||||
return send(new LoadChats(new ChatListMain(), 2000)).thenAccept(ok -> {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the TDLib instance after a proper logout. Requires an available network connection. All local data will be destroyed. After the logout completes, updateAuthorizationState with authorizationStateClosed will be sent.
|
||||
*
|
||||
**/
|
||||
public CompletableFuture<Void> logOutAsync() {
|
||||
return send(new LogOut()).thenAccept(ok -> {});
|
||||
}
|
||||
|
||||
public boolean isMainChatsListLoaded() {
|
||||
return mainChatsLoader.isLoaded();
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public final class SimpleTelegramClientBuilder implements MutableTelegramClient
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T extends Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler) {
|
||||
public <T extends Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<? super T> handler) {
|
||||
this.updateHandlers.add(new SimpleResultHandler<>(updateType, handler));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import it.tdlight.ClientFactory;
|
|||
|
||||
public class SimpleTelegramClientFactory implements AutoCloseable {
|
||||
private final ClientFactory clientFactory;
|
||||
private final boolean commonClientFactory;
|
||||
|
||||
public SimpleTelegramClientFactory() {
|
||||
this(null);
|
||||
|
@ -12,11 +11,9 @@ public class SimpleTelegramClientFactory implements AutoCloseable {
|
|||
|
||||
public SimpleTelegramClientFactory(ClientFactory clientFactory) {
|
||||
if (clientFactory == null) {
|
||||
this.clientFactory = ClientFactory.getCommonClientFactory();
|
||||
this.commonClientFactory = true;
|
||||
this.clientFactory = ClientFactory.acquireCommonClientFactory();
|
||||
} else {
|
||||
this.clientFactory = clientFactory;
|
||||
this.commonClientFactory = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,8 +23,6 @@ public class SimpleTelegramClientFactory implements AutoCloseable {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
if (!commonClientFactory) {
|
||||
clientFactory.close();
|
||||
}
|
||||
clientFactory.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,8 @@ public final class TDLibSettings {
|
|||
private String deviceModel;
|
||||
private String systemVersion;
|
||||
private String applicationVersion;
|
||||
private boolean enableStorageOptimizer;
|
||||
private boolean ignoreFileNames;
|
||||
|
||||
@Deprecated
|
||||
private TDLibSettings(boolean useTestDatacenter,
|
||||
Path databaseDirectoryPath,
|
||||
Path downloadedFilesDirectoryPath,
|
||||
|
@ -45,6 +44,31 @@ public final class TDLibSettings {
|
|||
String applicationVersion,
|
||||
boolean enableStorageOptimizer,
|
||||
boolean ignoreFileNames) {
|
||||
this(useTestDatacenter,
|
||||
databaseDirectoryPath,
|
||||
downloadedFilesDirectoryPath,
|
||||
fileDatabaseEnabled,
|
||||
chatInfoDatabaseEnabled,
|
||||
messageDatabaseEnabled,
|
||||
apiToken,
|
||||
systemLanguageCode,
|
||||
deviceModel,
|
||||
systemVersion,
|
||||
applicationVersion
|
||||
);
|
||||
}
|
||||
|
||||
private TDLibSettings(boolean useTestDatacenter,
|
||||
Path databaseDirectoryPath,
|
||||
Path downloadedFilesDirectoryPath,
|
||||
boolean fileDatabaseEnabled,
|
||||
boolean chatInfoDatabaseEnabled,
|
||||
boolean messageDatabaseEnabled,
|
||||
APIToken apiToken,
|
||||
String systemLanguageCode,
|
||||
String deviceModel,
|
||||
String systemVersion,
|
||||
String applicationVersion) {
|
||||
this.useTestDatacenter = useTestDatacenter;
|
||||
this.databaseDirectoryPath = databaseDirectoryPath;
|
||||
this.downloadedFilesDirectoryPath = downloadedFilesDirectoryPath;
|
||||
|
@ -56,8 +80,6 @@ public final class TDLibSettings {
|
|||
this.deviceModel = deviceModel;
|
||||
this.systemVersion = systemVersion;
|
||||
this.applicationVersion = applicationVersion;
|
||||
this.enableStorageOptimizer = enableStorageOptimizer;
|
||||
this.ignoreFileNames = ignoreFileNames;
|
||||
}
|
||||
|
||||
public static TDLibSettings create(APIToken apiToken) {
|
||||
|
@ -165,20 +187,22 @@ public final class TDLibSettings {
|
|||
this.applicationVersion = applicationVersion;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isStorageOptimizerEnabled() {
|
||||
return enableStorageOptimizer;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setEnableStorageOptimizer(boolean enableStorageOptimizer) {
|
||||
this.enableStorageOptimizer = enableStorageOptimizer;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isIgnoreFileNames() {
|
||||
return ignoreFileNames;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setIgnoreFileNames(boolean ignoreFileNames) {
|
||||
this.ignoreFileNames = ignoreFileNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -193,7 +217,6 @@ public final class TDLibSettings {
|
|||
return useTestDatacenter == that.useTestDatacenter && fileDatabaseEnabled == that.fileDatabaseEnabled
|
||||
&& chatInfoDatabaseEnabled == that.chatInfoDatabaseEnabled
|
||||
&& messageDatabaseEnabled == that.messageDatabaseEnabled
|
||||
&& enableStorageOptimizer == that.enableStorageOptimizer && ignoreFileNames == that.ignoreFileNames
|
||||
&& Objects.equals(databaseDirectoryPath, that.databaseDirectoryPath) && Objects.equals(
|
||||
downloadedFilesDirectoryPath,
|
||||
that.downloadedFilesDirectoryPath
|
||||
|
@ -214,9 +237,7 @@ public final class TDLibSettings {
|
|||
systemLanguageCode,
|
||||
deviceModel,
|
||||
systemVersion,
|
||||
applicationVersion,
|
||||
enableStorageOptimizer,
|
||||
ignoreFileNames
|
||||
applicationVersion
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -234,8 +255,6 @@ public final class TDLibSettings {
|
|||
.add("deviceModel='" + deviceModel + "'")
|
||||
.add("systemVersion='" + systemVersion + "'")
|
||||
.add("applicationVersion='" + applicationVersion + "'")
|
||||
.add("enableStorageOptimizer=" + enableStorageOptimizer)
|
||||
.add("ignoreFileNames=" + ignoreFileNames)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
class TemporaryMessageHandler implements GenericUpdateHandler<TdApi.Update> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TemporaryMessageHandler.class);
|
||||
|
||||
private final ConcurrentMap<TemporaryMessageURL, CompletableFuture<TdApi.Message>> temporaryMessages;
|
||||
|
||||
public TemporaryMessageHandler(ConcurrentMap<TemporaryMessageURL, CompletableFuture<TdApi.Message>> temporaryMessages) {
|
||||
this.temporaryMessages = temporaryMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(TdApi.Update update) {
|
||||
switch (update.getConstructor()) {
|
||||
case TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR: onUpdateSuccess(((TdApi.UpdateMessageSendSucceeded) update));
|
||||
break;
|
||||
case TdApi.UpdateMessageSendFailed.CONSTRUCTOR: onUpdateFailed(((TdApi.UpdateMessageSendFailed) update));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onUpdateSuccess(TdApi.UpdateMessageSendSucceeded updateMessageSendSucceeded) {
|
||||
TemporaryMessageURL tempUrl
|
||||
= new TemporaryMessageURL(updateMessageSendSucceeded.message.chatId, updateMessageSendSucceeded.oldMessageId);
|
||||
CompletableFuture<TdApi.Message> future = temporaryMessages.remove(tempUrl);
|
||||
if (future == null) {
|
||||
logNotHandled(tempUrl);
|
||||
} else {
|
||||
future.complete(updateMessageSendSucceeded.message);
|
||||
}
|
||||
}
|
||||
|
||||
private void onUpdateFailed(TdApi.UpdateMessageSendFailed updateMessageSendFailed) {
|
||||
TemporaryMessageURL tempUrl
|
||||
= new TemporaryMessageURL(updateMessageSendFailed.message.chatId, updateMessageSendFailed.oldMessageId);
|
||||
CompletableFuture<TdApi.Message> future = temporaryMessages.remove(tempUrl);
|
||||
if (future == null) {
|
||||
logNotHandled(tempUrl);
|
||||
} else {
|
||||
TdApi.Error error = updateMessageSendFailed.error;
|
||||
future.completeExceptionally(new TelegramError(error));
|
||||
}
|
||||
}
|
||||
|
||||
private void logNotHandled(TemporaryMessageURL tempUrl) {
|
||||
LOG.debug("The message {} is not being handled by the client", tempUrl);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package it.tdlight.client;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
final class TemporaryMessageURL {
|
||||
|
||||
private final long chatId;
|
||||
private final long messageId;
|
||||
|
||||
TemporaryMessageURL(long chatId, long messageId) {
|
||||
this.chatId = chatId;
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public long chatId() {
|
||||
return chatId;
|
||||
}
|
||||
|
||||
public long messageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || obj.getClass() != this.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TemporaryMessageURL that = (TemporaryMessageURL) obj;
|
||||
return this.chatId == that.chatId && this.messageId == that.messageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(chatId, messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TemporaryMessageURL[" + "chatId=" + chatId + ", " + "messageId=" + messageId + ']';
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package it.tdlight.util;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class FutureSupport {
|
||||
|
||||
public static <T> CompletableFuture<T> copy(CompletableFuture<T> future) {
|
||||
return CompletableFuture.completedFuture(true).thenCompose(ignored -> future);
|
||||
}
|
||||
}
|
|
@ -58,8 +58,12 @@ public final class Native {
|
|||
NativeLibraryLoader.load(staticLibName, cl);
|
||||
logger.debug("Failed to load {}", String.join(", ", sharedLibNames), e1);
|
||||
} catch (UnsatisfiedLinkError e2) {
|
||||
e1.addSuppressed(e2);
|
||||
throw new UnsupportedNativeLibraryException(e1);
|
||||
if (e2.getMessage().contains("libc++.so.1: cannot open shared")) {
|
||||
throw new UnsupportedNativeLibraryException("Install \"libc++\" to use TDLight Java!");
|
||||
} else {
|
||||
e1.addSuppressed(e2);
|
||||
throw new UnsupportedNativeLibraryException(e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +80,7 @@ public final class Native {
|
|||
private static Stream<String> getAllNormalizedArchitectures() {
|
||||
Set<String> all = new LinkedHashSet<>();
|
||||
for (String os : new String[]{"windows"}) {
|
||||
for (String arch : new String[]{"arm64", "amd64", "armhf", "i386", "s390x", "ppc64le"}) {
|
||||
for (String arch : new String[]{"arm64", "amd64", "armhf", "i386", "s390x", "ppc64el", "riscv64"}) {
|
||||
getNormalizedArchitectures(os, arch).forEach(all::add);
|
||||
}
|
||||
}
|
||||
|
@ -86,13 +90,13 @@ public final class Native {
|
|||
private static Stream<String> getNormalizedArchitectures(String os, String arch) {
|
||||
switch (os) {
|
||||
case "linux": {
|
||||
return Stream.of("linux_" + arch + "_ssl1", "linux_" + arch + "_ssl3");
|
||||
return Stream.of("linux_" + arch + "_clang_ssl1", "linux_" + arch + "_clang_ssl3", "linux_" + arch + "_gnu_ssl1", "linux_" + arch + "_gnu_ssl3");
|
||||
}
|
||||
case "windows": {
|
||||
return Stream.of("windows_" + arch);
|
||||
}
|
||||
case "osx": {
|
||||
return Stream.of("osx_" + arch);
|
||||
case "macos": {
|
||||
return Stream.of("macos_" + arch);
|
||||
}
|
||||
default: {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -126,6 +130,8 @@ public final class Native {
|
|||
return "arm64";
|
||||
case "s390x":
|
||||
return "s390x";
|
||||
case "riscv64":
|
||||
return "riscv64";
|
||||
case "powerpc":
|
||||
case "powerpc64":
|
||||
case "powerpc64le":
|
||||
|
@ -138,7 +144,7 @@ public final class Native {
|
|||
.nativeOrder()
|
||||
.equals(ByteOrder.LITTLE_ENDIAN)) // Java always returns ppc64 for all 64-bit powerpc but
|
||||
{
|
||||
return "ppc64le"; // powerpc64le (our target) is very different, it uses this condition to accurately identify the architecture
|
||||
return "ppc64el"; // powerpc64le (our target) is very different, it uses this condition to accurately identify the architecture
|
||||
} else {
|
||||
return "unknown";
|
||||
}
|
||||
|
@ -156,10 +162,10 @@ public final class Native {
|
|||
return "windows";
|
||||
}
|
||||
if (os.contains("mac")) {
|
||||
return "osx";
|
||||
return "macos";
|
||||
}
|
||||
if (os.contains("darwin")) {
|
||||
return "osx";
|
||||
return "macos";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package it.tdlight.util;
|
||||
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -27,6 +28,7 @@ import java.io.OutputStream;
|
|||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -262,16 +264,20 @@ public final class NativeLibraryLoader {
|
|||
// After we load the library it is safe to delete the file.
|
||||
// We delete the file immediately to free up resources as soon as possible,
|
||||
// and if this fails fallback to deleting on JVM exit.
|
||||
try {
|
||||
if (tmpFile != null && (!DELETE_NATIVE_LIB_AFTER_LOADING || !Files.deleteIfExists(tmpFile))) {
|
||||
tmpFile.toFile().deleteOnExit();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
if (tmpFile != null && (!DELETE_NATIVE_LIB_AFTER_LOADING || !deleteIfExists(tmpFile))) {
|
||||
tmpFile.toFile().deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean deleteIfExists(Path tmpFile) {
|
||||
try {
|
||||
return Files.deleteIfExists(tmpFile);
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOsx() {
|
||||
return Native.getOs().equals("osx");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package it.tdlight.util;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class FutureSupport {
|
||||
|
||||
public static <T> CompletableFuture<T> copy(CompletableFuture<T> future) {
|
||||
return future.copy();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user