TDLight Java 2
This commit is contained in:
parent
bd8b7af73a
commit
55436cd354
187
README.md
187
README.md
@ -5,76 +5,73 @@ TDLight Java
|
||||
[![Release tag](https://img.shields.io/github/v/release/tdlight-team/tdlight-java.svg?include_prereleases&style=flat-square)](https://github.com/tdlight-team/tdlight-java/releases)
|
||||
[![Java profiler](https://local.cavallium.it/mirrors/jprofiler-logo/jprofiler-logo-badge.svg)](https://www.ej-technologies.com/products/jprofiler/overview.html)
|
||||
|
||||
A barebone java wrapper for TDLib (and TDLight)
|
||||
Complete Bot and Userbot Telegram library written in Java based on TDLib
|
||||
|
||||
This wrapper gives you direct access to TDLib API in Java.
|
||||
## Supported platforms
|
||||
Java versions: from Java 8 to Java 17
|
||||
|
||||
## Requirements
|
||||
Java versions: Java 8, 9, 10, 11, 12, 13, 14, 15, 16
|
||||
Operating systems: Linux, Windows, MacOS
|
||||
|
||||
Operating system: Linux, Windows, MacOS
|
||||
|
||||
Supported CPU architectures:
|
||||
- x86 (Linux, Windows)
|
||||
- amd64/x86_64 (Linux, Windows, MacOS)
|
||||
- armv6 (Linux)
|
||||
- armv7 (Linux)
|
||||
- aarch64/armv8 (Linux)
|
||||
CPU architectures:
|
||||
- i386/x86 (Linux, Windows)
|
||||
- amd64/x86_64 (Linux, Windows, OSX)
|
||||
- armhf/armv7 (Linux)
|
||||
- aarch64/armv8/arm64 (Linux)
|
||||
- s390x (Linux)
|
||||
- ppc64el/ppc64le (Linux)
|
||||
|
||||
Required libraries for linux: OpenSSL and zlib
|
||||
|
||||
## Including TDLight Java in a project
|
||||
|
||||
There are two packages of TDLight:
|
||||
- [TDLight](#For-TDLight-Java-with-optimized-TDLight), with our optimized fork of TDLib (Reccomended for bots)
|
||||
- [TDLight with official TDLib](#For-TDLight-Java-with-official-TDLib) (Reccomended for GUI clients)
|
||||
|
||||
The two packages are compatible, but while TDLight is focused on long term resources usage and speed, TDLib is more focused on stability.
|
||||
|
||||
Choose one of the two different TDLight packages and then follow the guide below.
|
||||
|
||||
Replace `REPLACE_WITH_LATEST_VERSION` with the latest version of tdlight, you can find it on the **Releases** tab on github.
|
||||
### For TDLight Java with optimized TDLight
|
||||
#### Maven
|
||||
Repository:
|
||||
## How to use the library
|
||||
### Installing with Maven
|
||||
```xml
|
||||
<repositories>
|
||||
<project>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mchv</id>
|
||||
<name>MCHV Apache Maven Packages</name>
|
||||
<url>https://mvn.mchv.eu/repository/mchv/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
Dependency:
|
||||
```xml
|
||||
<dependencies>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-java</artifactId>
|
||||
<version>REPLACE_WITH_LATEST_VERSION</version>
|
||||
<version>VERSION</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-natives-linux-amd64</artifactId>
|
||||
<version>REPLACE_WITH_LATEST_NATIVES_VERSION</version>
|
||||
<version>NATIVES_VERSION</version>
|
||||
</dependency>
|
||||
<!-- include other native versions that you want, for example for macos, windows, and other architectures here -->
|
||||
</dependencies>
|
||||
<!-- Include other native versions that you want, for example for windows, osx, ... -->
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
#### Gradle
|
||||
Replace `VERSION` with the latest release version, you can find it [here](https://github.com/tdlight-team/tdlight-java/releases).
|
||||
|
||||
Replace `NATIVES_VERSION` with the latest native version.
|
||||
Make sure that you are using the correct natives version for the release that you are using.
|
||||
|
||||
### Installing with Gradle
|
||||
```groovy
|
||||
repositories {
|
||||
maven { url "https://mvn.mchv.eu/repository/mchv/" }
|
||||
}
|
||||
dependencies {
|
||||
implementation 'it.tdlight:tdlight-java:REPLACE_WITH_LATEST_VERSION'
|
||||
implementation 'it.tdlight:tdlight-natives-linux-amd64:REPLACE_WITH_LATEST_NATIVES_VERSION'
|
||||
// include other native versions that you want, for example for macos, windows, and other architectures here
|
||||
implementation 'it.tdlight:tdlight-java:LATEST_VERSION'
|
||||
implementation 'it.tdlight:tdlight-natives-linux-amd64:NATIVES_VERSION'
|
||||
// Include other native versions that you want, for example for windows, osx, ...
|
||||
}
|
||||
```
|
||||
#### Natives inclusion
|
||||
To use TDLight java for a specific platform, you need to include the related native dependencies:
|
||||
Replace `VERSION` with the latest release version, you can find it [here](https://github.com/tdlight-team/tdlight-java/releases).
|
||||
|
||||
Replace `NATIVES_VERSION` with the latest native version.
|
||||
Make sure that you are using the correct natives version for the release that you are using.
|
||||
|
||||
### Native dependencies
|
||||
To use TDLight Java you need to include one or more native dependencies:
|
||||
- `tdlight-natives-linux-amd64`
|
||||
- `tdlight-natives-linux-aarch64`
|
||||
- `tdlight-natives-linux-x86`
|
||||
@ -84,114 +81,16 @@ To use TDLight java for a specific platform, you need to include the related nat
|
||||
- `tdlight-natives-windows-amd64`
|
||||
- `tdlight-natives-osx-amd64`
|
||||
|
||||
### For TDLight Java with official TDLib
|
||||
#### Maven
|
||||
Repository:
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mchv</id>
|
||||
<name>MCHV Apache Maven Packages</name>
|
||||
<url>https://mvn.mchv.eu/repository/mchv/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
Dependency:
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlib-java</artifactId>
|
||||
<version>REPLACE_WITH_LATEST_VERSION</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlib-natives-linux-amd64</artifactId>
|
||||
<version>REPLACE_WITH_LATEST_NATIVES_VERSION</version>
|
||||
</dependency>
|
||||
<!-- include other native versions that you want, for example for macos, windows, and other architectures here -->
|
||||
</dependencies>
|
||||
```
|
||||
#### Gradle
|
||||
```groovy
|
||||
repositories {
|
||||
maven { url "https://mvn.mchv.eu/repository/mchv/" }
|
||||
}
|
||||
dependencies {
|
||||
implementation 'it.tdlight:tdlib-java:REPLACE_WITH_LATEST_VERSION'
|
||||
implementation 'it.tdlight:tdlib-natives-linux-amd64:REPLACE_WITH_LATEST_NATIVES_VERSION'
|
||||
// include other native versions that you want, for example for macos, windows, and other architectures here
|
||||
}
|
||||
```
|
||||
#### Natives inclusion
|
||||
To use TDLight java for a specific platform, you need to include the related native dependencies:
|
||||
- `tdlib-natives-linux-amd64`
|
||||
- `tdlib-natives-linux-aarch64`
|
||||
- `tdlib-natives-linux-x86`
|
||||
- `tdlib-natives-linux-armv6`
|
||||
- `tdlib-natives-linux-armv7`
|
||||
- `tdlib-natives-linux-ppc64le`
|
||||
- `tdlib-natives-windows-amd64`
|
||||
- `tdlib-natives-osx-amd64`
|
||||
|
||||
## Usage
|
||||
Simple initialization of a native TDLib client
|
||||
```java
|
||||
package it.tdlight.example;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.tdlight.ClientManager;
|
||||
import it.tdlight.common.Init;
|
||||
An example on how to use TDLight Java can be found here: [Example.java](https://github.com/tdlight-team/tdlight-java/blob/master/example/src/main/java/it.tdlight.example/Example.java)
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
// Initialize TDLight native libraries
|
||||
Init.start();
|
||||
|
||||
// Create a client
|
||||
TelegramClient client = ClientManager.create();
|
||||
|
||||
// Initialize the client
|
||||
client.initialize(Example::onUpdate, Example::onUpdateError, Example::onError);
|
||||
|
||||
// Here you can use the client.
|
||||
|
||||
// Documentation of tdlib methods can be found here:
|
||||
// https://tdlight-team.github.io/tdlight-docs
|
||||
|
||||
// An example on how to use tdlight java can be found here:
|
||||
// https://github.com/tdlight-team/tdlight-java/blob/master/example/src/main/java/it.tdlight.example/Example.java
|
||||
|
||||
// A similar example on how to use tdlib can be found here:
|
||||
// https://github.com/tdlib/td/blob/master/example/java/org/drinkless/tdlib/example/Example.java
|
||||
}
|
||||
|
||||
private static void onUpdate(TdApi.Object object) {
|
||||
TdApi.Update update = (TdApi.Update) object;
|
||||
System.out.println("Received update: " + update);
|
||||
}
|
||||
|
||||
private static void onUpdateError(Throwable exception) {
|
||||
System.out.println("Received an error from updates:");
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
private static void onError(Throwable exception) {
|
||||
System.out.println("Received an error:");
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### TDLib methods documentation
|
||||
[TdApi class javadoc](https://tdlight-team.github.io/tdlight-docs)
|
||||
### TDLight methods documentation
|
||||
[TdApi JavaDoc](https://tdlight-team.github.io/tdlight-docs)
|
||||
|
||||
### TDLight extended features
|
||||
TDLight Java with TDLight has some extended features, that you can see on the [TDLight official repository](https://github.com/tdlight-team/tdlight).
|
||||
|
||||
This features are present only if you use the optimized TDLight package.
|
||||
TDLight has some extended features compared to TDLib, that you can see on the [TDLight official repository](https://github.com/tdlight-team/tdlight#tdlight-extra-features).
|
||||
|
||||
## About
|
||||
### License
|
||||
|
@ -24,7 +24,8 @@
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>tdlight-java</artifactId>
|
||||
<version>[1.7.6.1,)</version>
|
||||
<!--<version>[1.7.6.1,)</version>-->
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.tdlight</groupId>
|
||||
|
@ -0,0 +1,47 @@
|
||||
package it.tdlight.example;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.tdlight.ClientManager;
|
||||
import it.tdlight.common.Init;
|
||||
import it.tdlight.common.utils.CantLoadLibrary;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
|
||||
/**
|
||||
* This is an advanced example that uses directly the native client without using the SimpleClient implementation
|
||||
*/
|
||||
public class AdvancedExample {
|
||||
public static void main(String[] args) throws CantLoadLibrary {
|
||||
// Initialize TDLight native libraries
|
||||
Init.start();
|
||||
|
||||
// Create a client
|
||||
TelegramClient client = ClientManager.create();
|
||||
|
||||
// Initialize the client
|
||||
client.initialize(AdvancedExample::onUpdate, AdvancedExample::onUpdateError, AdvancedExample::onError);
|
||||
|
||||
// Here you can use the client.
|
||||
|
||||
// An example on how to use TDLight java can be found here:
|
||||
// https://github.com/tdlight-team/tdlight-java/blob/master/example/src/main/java/it.tdlight.example/Example.java
|
||||
|
||||
// The documentation of the TDLight methods can be found here:
|
||||
// https://tdlight-team.github.io/tdlight-docs
|
||||
}
|
||||
|
||||
private static void onUpdate(TdApi.Object object) {
|
||||
TdApi.Update update = (TdApi.Update) object;
|
||||
System.out.println("Received update: " + update);
|
||||
}
|
||||
|
||||
private static void onUpdateError(Throwable exception) {
|
||||
System.out.println("Received an error from updates:");
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
private static void onError(Throwable exception) {
|
||||
System.out.println("Received an error:");
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
@ -6,12 +6,27 @@
|
||||
//
|
||||
package it.tdlight.example;
|
||||
|
||||
import it.tdlight.client.APIToken;
|
||||
import it.tdlight.client.Authenticable;
|
||||
import it.tdlight.client.AuthenticationData;
|
||||
import it.tdlight.client.CommandHandler;
|
||||
import it.tdlight.client.Result;
|
||||
import it.tdlight.client.SimpleTelegramClient;
|
||||
import it.tdlight.client.TDLibSettings;
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.Init;
|
||||
import it.tdlight.common.ResultHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.common.utils.CantLoadLibrary;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationState;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.MessageContent;
|
||||
import it.tdlight.jni.TdApi.MessageSender;
|
||||
import it.tdlight.jni.TdApi.MessageSenderUser;
|
||||
import it.tdlight.jni.TdApi.MessageText;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import it.tdlight.jni.TdApi.UpdateNewMessage;
|
||||
import it.tdlight.tdlight.ClientManager;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOError;
|
||||
@ -26,592 +41,118 @@ import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Example class for TDLib usage from Java.
|
||||
* (Based on the official TDLib java example)
|
||||
* Example class for TDLight Java
|
||||
*
|
||||
* The documentation of the TDLight functions can be found here:
|
||||
* https://tdlight-team.github.io/tdlight-docs
|
||||
*/
|
||||
public final class Example {
|
||||
private static TelegramClient client = null;
|
||||
|
||||
private static TdApi.AuthorizationState authorizationState = null;
|
||||
private static volatile boolean haveAuthorization = false;
|
||||
private static volatile boolean needQuit = false;
|
||||
private static volatile boolean canQuit = false;
|
||||
/**
|
||||
* Admin user id, used by the stop command example
|
||||
*/
|
||||
private static final long ADMIN_ID = 667900586;
|
||||
|
||||
private static final ResultHandler defaultHandler = new DefaultHandler();
|
||||
|
||||
private static final Lock authorizationLock = new ReentrantLock();
|
||||
private static final Condition gotAuthorization = authorizationLock.newCondition();
|
||||
|
||||
private static final ConcurrentMap<Integer, TdApi.User> users = new ConcurrentHashMap<Integer, TdApi.User>();
|
||||
private static final ConcurrentMap<Integer, TdApi.BasicGroup> basicGroups = new ConcurrentHashMap<Integer, TdApi.BasicGroup>();
|
||||
private static final ConcurrentMap<Integer, TdApi.Supergroup> supergroups = new ConcurrentHashMap<Integer, TdApi.Supergroup>();
|
||||
private static final ConcurrentMap<Integer, TdApi.SecretChat> secretChats = new ConcurrentHashMap<Integer, TdApi.SecretChat>();
|
||||
|
||||
private static final ConcurrentMap<Long, TdApi.Chat> chats = new ConcurrentHashMap<Long, TdApi.Chat>();
|
||||
private static final NavigableSet<OrderedChat> mainChatList = new TreeSet<OrderedChat>();
|
||||
private static boolean haveFullMainChatList = false;
|
||||
|
||||
private static final ConcurrentMap<Integer, TdApi.UserFullInfo> usersFullInfo = new ConcurrentHashMap<Integer, TdApi.UserFullInfo>();
|
||||
private static final ConcurrentMap<Integer, TdApi.BasicGroupFullInfo> basicGroupsFullInfo = new ConcurrentHashMap<Integer, TdApi.BasicGroupFullInfo>();
|
||||
private static final ConcurrentMap<Integer, TdApi.SupergroupFullInfo> supergroupsFullInfo = new ConcurrentHashMap<Integer, TdApi.SupergroupFullInfo>();
|
||||
|
||||
private static final String newLine = System.getProperty("line.separator");
|
||||
private static final String commandsLine = "Enter command (gcs - GetChats, gc <chatId> - GetChat, me - GetMe, sm <chatId> <message> - SendMessage, lo - LogOut, q - Quit): ";
|
||||
private static volatile String currentPrompt = null;
|
||||
|
||||
private static void print(String str) {
|
||||
if (currentPrompt != null) {
|
||||
System.out.println("");
|
||||
}
|
||||
System.out.println(str);
|
||||
if (currentPrompt != null) {
|
||||
System.out.print(currentPrompt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setChatPositions(TdApi.Chat chat, TdApi.ChatPosition[] positions) {
|
||||
synchronized (mainChatList) {
|
||||
synchronized (chat) {
|
||||
for (TdApi.ChatPosition position : chat.positions) {
|
||||
if (position.list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
boolean isRemoved = mainChatList.remove(new OrderedChat(chat.id, position));
|
||||
assert isRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
chat.positions = positions;
|
||||
|
||||
for (TdApi.ChatPosition position : chat.positions) {
|
||||
if (position.list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
boolean isAdded = mainChatList.add(new OrderedChat(chat.id, position));
|
||||
assert isAdded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void onAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState) {
|
||||
if (authorizationState != null) {
|
||||
Example.authorizationState = authorizationState;
|
||||
}
|
||||
switch (Example.authorizationState.getConstructor()) {
|
||||
case TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR:
|
||||
TdApi.TdlibParameters parameters = new TdApi.TdlibParameters();
|
||||
parameters.databaseDirectory = "tdlib";
|
||||
parameters.useMessageDatabase = true;
|
||||
parameters.useSecretChats = true;
|
||||
parameters.apiId = 94575;
|
||||
parameters.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
|
||||
parameters.systemLanguageCode = "en";
|
||||
parameters.deviceModel = "Desktop";
|
||||
parameters.applicationVersion = "1.0";
|
||||
parameters.enableStorageOptimizer = true;
|
||||
|
||||
client.send(new TdApi.SetTdlibParameters(parameters), new AuthorizationRequestHandler());
|
||||
break;
|
||||
case TdApi.AuthorizationStateWaitEncryptionKey.CONSTRUCTOR:
|
||||
client.send(new TdApi.CheckDatabaseEncryptionKey(), new AuthorizationRequestHandler());
|
||||
break;
|
||||
case TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR: {
|
||||
String phoneNumber = promptString("Please enter phone number: ");
|
||||
client.send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, null), new AuthorizationRequestHandler());
|
||||
break;
|
||||
}
|
||||
case TdApi.AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR: {
|
||||
String link = ((TdApi.AuthorizationStateWaitOtherDeviceConfirmation) Example.authorizationState).link;
|
||||
System.out.println("Please confirm this login link on another device: " + link);
|
||||
break;
|
||||
}
|
||||
case TdApi.AuthorizationStateWaitCode.CONSTRUCTOR: {
|
||||
String code = promptString("Please enter authentication code: ");
|
||||
client.send(new TdApi.CheckAuthenticationCode(code), new AuthorizationRequestHandler());
|
||||
break;
|
||||
}
|
||||
case TdApi.AuthorizationStateWaitRegistration.CONSTRUCTOR: {
|
||||
String firstName = promptString("Please enter your first name: ");
|
||||
String lastName = promptString("Please enter your last name: ");
|
||||
client.send(new TdApi.RegisterUser(firstName, lastName), new AuthorizationRequestHandler());
|
||||
break;
|
||||
}
|
||||
case TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR: {
|
||||
String password = promptString("Please enter password: ");
|
||||
client.send(new TdApi.CheckAuthenticationPassword(password), new AuthorizationRequestHandler());
|
||||
break;
|
||||
}
|
||||
case TdApi.AuthorizationStateReady.CONSTRUCTOR:
|
||||
haveAuthorization = true;
|
||||
authorizationLock.lock();
|
||||
try {
|
||||
gotAuthorization.signal();
|
||||
} finally {
|
||||
authorizationLock.unlock();
|
||||
}
|
||||
break;
|
||||
case TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR:
|
||||
haveAuthorization = false;
|
||||
print("Logging out");
|
||||
break;
|
||||
case TdApi.AuthorizationStateClosing.CONSTRUCTOR:
|
||||
haveAuthorization = false;
|
||||
print("Closing");
|
||||
break;
|
||||
case TdApi.AuthorizationStateClosed.CONSTRUCTOR:
|
||||
print("Closed");
|
||||
if (!needQuit) {
|
||||
client = ClientManager.create(); // recreate client after previous has closed
|
||||
client.initialize(new UpdateHandler(), new ErrorHandler(), new ErrorHandler());
|
||||
} else {
|
||||
canQuit = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.err.println("Unsupported authorization state:" + newLine + Example.authorizationState);
|
||||
}
|
||||
}
|
||||
|
||||
private static int toInt(String arg) {
|
||||
int result = 0;
|
||||
try {
|
||||
result = Integer.parseInt(arg);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static long getChatId(String arg) {
|
||||
long chatId = 0;
|
||||
try {
|
||||
chatId = Long.parseLong(arg);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
return chatId;
|
||||
}
|
||||
|
||||
private static String promptString(String prompt) {
|
||||
System.out.print(prompt);
|
||||
currentPrompt = prompt;
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||
String str = "";
|
||||
try {
|
||||
str = reader.readLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
currentPrompt = null;
|
||||
return str;
|
||||
}
|
||||
|
||||
private static void getCommand() {
|
||||
String command = promptString(commandsLine);
|
||||
String[] commands = command.split(" ", 2);
|
||||
try {
|
||||
switch (commands[0]) {
|
||||
case "gcs": {
|
||||
int limit = 20;
|
||||
if (commands.length > 1) {
|
||||
limit = toInt(commands[1]);
|
||||
}
|
||||
getMainChatList(limit);
|
||||
break;
|
||||
}
|
||||
case "gc":
|
||||
client.send(new TdApi.GetChat(getChatId(commands[1])), defaultHandler);
|
||||
break;
|
||||
case "me":
|
||||
client.send(new TdApi.GetMe(), defaultHandler);
|
||||
break;
|
||||
case "sm": {
|
||||
String[] args = commands[1].split(" ", 2);
|
||||
sendMessage(getChatId(args[0]), args[1]);
|
||||
break;
|
||||
}
|
||||
case "lo":
|
||||
haveAuthorization = false;
|
||||
client.send(new TdApi.LogOut(), defaultHandler);
|
||||
break;
|
||||
case "q":
|
||||
needQuit = true;
|
||||
haveAuthorization = false;
|
||||
client.send(new TdApi.Close(), defaultHandler);
|
||||
break;
|
||||
default:
|
||||
System.err.println("Unsupported command: " + command);
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
print("Not enough arguments");
|
||||
}
|
||||
}
|
||||
|
||||
private static void getMainChatList(final int limit) {
|
||||
synchronized (mainChatList) {
|
||||
if (!haveFullMainChatList && limit > mainChatList.size()) {
|
||||
// have enough chats in the chat list or chat list is too small
|
||||
long offsetOrder = Long.MAX_VALUE;
|
||||
long offsetChatId = 0;
|
||||
if (!mainChatList.isEmpty()) {
|
||||
OrderedChat last = mainChatList.last();
|
||||
offsetOrder = last.position.order;
|
||||
offsetChatId = last.chatId;
|
||||
}
|
||||
client.send(new TdApi.GetChats(new TdApi.ChatListMain(), offsetOrder, offsetChatId, limit - mainChatList.size()), new ResultHandler() {
|
||||
@Override
|
||||
public void onResult(TdApi.Object object) {
|
||||
switch (object.getConstructor()) {
|
||||
case TdApi.Error.CONSTRUCTOR:
|
||||
System.err.println("Receive an error for GetChats:" + newLine + object);
|
||||
break;
|
||||
case TdApi.Chats.CONSTRUCTOR:
|
||||
long[] chatIds = ((TdApi.Chats) object).chatIds;
|
||||
if (chatIds.length == 0) {
|
||||
synchronized (mainChatList) {
|
||||
haveFullMainChatList = true;
|
||||
}
|
||||
}
|
||||
// chats had already been received through updates, let's retry request
|
||||
getMainChatList(limit);
|
||||
break;
|
||||
default:
|
||||
System.err.println("Receive wrong response from TDLib:" + newLine + object);
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// have enough chats in the chat list to answer request
|
||||
java.util.Iterator<OrderedChat> iter = mainChatList.iterator();
|
||||
System.out.println();
|
||||
System.out.println("First " + limit + " chat(s) out of " + mainChatList.size() + " known chat(s):");
|
||||
for (int i = 0; i < limit && iter.hasNext(); i++) {
|
||||
long chatId = iter.next().chatId;
|
||||
TdApi.Chat chat = chats.get(chatId);
|
||||
synchronized (chat) {
|
||||
System.out.println(chatId + ": " + chat.title);
|
||||
}
|
||||
}
|
||||
print("");
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendMessage(long chatId, String message) {
|
||||
// initialize reply markup just for testing
|
||||
TdApi.InlineKeyboardButton[] row = {new TdApi.InlineKeyboardButton("https://telegram.org?1", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?2", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?3", new TdApi.InlineKeyboardButtonTypeUrl())};
|
||||
TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][]{row, row, row});
|
||||
|
||||
TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), false, true);
|
||||
client.send(new TdApi.SendMessage(chatId, 0, 0, null, replyMarkup, content), defaultHandler);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException, CantLoadLibrary {
|
||||
|
||||
// create client
|
||||
public static void main(String[] args) throws CantLoadLibrary, InterruptedException {
|
||||
// Initialize TDLight native libraries
|
||||
Init.start();
|
||||
client = ClientManager.create();
|
||||
client.initialize(new UpdateHandler(), new ErrorHandler(), new ErrorHandler());
|
||||
|
||||
client.execute(new TdApi.SetLogVerbosityLevel(0));
|
||||
// disable TDLib log
|
||||
if (client.execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false))) instanceof TdApi.Error) {
|
||||
throw new IOError(new IOException("Write access to the current directory is required"));
|
||||
// Obtain the API token
|
||||
APIToken apiToken = APIToken.example();
|
||||
|
||||
// Configure the client
|
||||
TDLibSettings settings = TDLibSettings.create(apiToken);
|
||||
|
||||
// Create a client
|
||||
SimpleTelegramClient client = new SimpleTelegramClient(settings);
|
||||
|
||||
// Configure the authentication info
|
||||
AuthenticationData authenticationData = AuthenticationData.bot("1458657665:AAFotSQj1pHcRrH1C0DmDX6VpwSanJgb2-g");
|
||||
|
||||
// Add an example update handler that prints when the bot is started
|
||||
client.addUpdateHandler(UpdateAuthorizationState.class, update -> printStatus(update.authorizationState));
|
||||
|
||||
// Add an example command handler that stops the bot
|
||||
client.addCommandHandler("stop", new StopCommandHandler(client));
|
||||
|
||||
// Add an example update handler that prints every received message
|
||||
client.addUpdateHandler(UpdateNewMessage.class, update -> {
|
||||
// Get the message content
|
||||
MessageContent messageContent = update.message.content;
|
||||
|
||||
// Get the message text
|
||||
String text;
|
||||
if (messageContent instanceof MessageText) {
|
||||
// Get the text of the text message
|
||||
text = ((MessageText) messageContent).text.text;
|
||||
} else {
|
||||
// We handle only text messages, the other messages will be printed as their type
|
||||
text = "(" + messageContent.getClass().getSimpleName() + ")";
|
||||
}
|
||||
|
||||
// test Client.execute
|
||||
defaultHandler.onResult(client.execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test")));
|
||||
// Get the chat title
|
||||
client.send(new TdApi.GetChat(update.message.chatId), (Result<Chat> chatIdResult) -> {
|
||||
// Get the chat response
|
||||
Chat chat = chatIdResult.get();
|
||||
// Get the chat name
|
||||
String chatName = chat.title;
|
||||
|
||||
// main loop
|
||||
while (!needQuit) {
|
||||
// await authorization
|
||||
authorizationLock.lock();
|
||||
try {
|
||||
while (!haveAuthorization) {
|
||||
gotAuthorization.await();
|
||||
}
|
||||
} finally {
|
||||
authorizationLock.unlock();
|
||||
// Print the message
|
||||
System.out.println("Received new message from chat " + chatName + ": " + text);
|
||||
});
|
||||
});
|
||||
|
||||
// Start the client
|
||||
client.start(authenticationData);
|
||||
|
||||
// Wait for exit
|
||||
client.waitForExit();
|
||||
}
|
||||
|
||||
while (haveAuthorization) {
|
||||
getCommand();
|
||||
}
|
||||
}
|
||||
while (!canQuit) {
|
||||
Thread.sleep(1);
|
||||
/**
|
||||
* Print the bot status
|
||||
*/
|
||||
private static void printStatus(AuthorizationState 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...");
|
||||
}
|
||||
}
|
||||
|
||||
public static class OrderedChat implements Comparable<OrderedChat> {
|
||||
final long chatId;
|
||||
final TdApi.ChatPosition position;
|
||||
/**
|
||||
* Check if the command sender is admin
|
||||
*/
|
||||
private static boolean isAdmin(MessageSender sender) {
|
||||
if (sender instanceof MessageSenderUser) {
|
||||
MessageSenderUser senderUser = (MessageSenderUser) sender;
|
||||
return senderUser.userId == ADMIN_ID;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
OrderedChat(long chatId, TdApi.ChatPosition position) {
|
||||
this.chatId = chatId;
|
||||
this.position = position;
|
||||
/**
|
||||
* Close the bot if the /stop command is sent by the administrator
|
||||
*/
|
||||
private static class StopCommandHandler implements CommandHandler {
|
||||
|
||||
private final SimpleTelegramClient client;
|
||||
|
||||
public StopCommandHandler(SimpleTelegramClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(OrderedChat o) {
|
||||
if (this.position.order != o.position.order) {
|
||||
return o.position.order < this.position.order ? -1 : 1;
|
||||
}
|
||||
if (this.chatId != o.chatId) {
|
||||
return o.chatId < this.chatId ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
OrderedChat o = (OrderedChat) obj;
|
||||
return this.chatId == o.chatId && this.position.order == o.position.order;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultHandler implements ResultHandler {
|
||||
@Override
|
||||
public void onResult(TdApi.Object object) {
|
||||
print(object.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpdateHandler implements ResultHandler {
|
||||
@Override
|
||||
public void onResult(TdApi.Object object) {
|
||||
switch (object.getConstructor()) {
|
||||
case TdApi.UpdateAuthorizationState.CONSTRUCTOR:
|
||||
onAuthorizationStateUpdated(((TdApi.UpdateAuthorizationState) object).authorizationState);
|
||||
break;
|
||||
|
||||
case TdApi.UpdateUser.CONSTRUCTOR:
|
||||
TdApi.UpdateUser updateUser = (TdApi.UpdateUser) object;
|
||||
users.put(updateUser.user.id, updateUser.user);
|
||||
break;
|
||||
case TdApi.UpdateUserStatus.CONSTRUCTOR: {
|
||||
TdApi.UpdateUserStatus updateUserStatus = (TdApi.UpdateUserStatus) object;
|
||||
TdApi.User user = users.get(updateUserStatus.userId);
|
||||
synchronized (user) {
|
||||
user.status = updateUserStatus.status;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateBasicGroup.CONSTRUCTOR:
|
||||
TdApi.UpdateBasicGroup updateBasicGroup = (TdApi.UpdateBasicGroup) object;
|
||||
basicGroups.put(updateBasicGroup.basicGroup.id, updateBasicGroup.basicGroup);
|
||||
break;
|
||||
case TdApi.UpdateSupergroup.CONSTRUCTOR:
|
||||
TdApi.UpdateSupergroup updateSupergroup = (TdApi.UpdateSupergroup) object;
|
||||
supergroups.put(updateSupergroup.supergroup.id, updateSupergroup.supergroup);
|
||||
break;
|
||||
case TdApi.UpdateSecretChat.CONSTRUCTOR:
|
||||
TdApi.UpdateSecretChat updateSecretChat = (TdApi.UpdateSecretChat) object;
|
||||
secretChats.put(updateSecretChat.secretChat.id, updateSecretChat.secretChat);
|
||||
break;
|
||||
|
||||
case TdApi.UpdateNewChat.CONSTRUCTOR: {
|
||||
TdApi.UpdateNewChat updateNewChat = (TdApi.UpdateNewChat) object;
|
||||
TdApi.Chat chat = updateNewChat.chat;
|
||||
synchronized (chat) {
|
||||
chats.put(chat.id, chat);
|
||||
|
||||
TdApi.ChatPosition[] positions = chat.positions;
|
||||
chat.positions = new TdApi.ChatPosition[0];
|
||||
setChatPositions(chat, positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatTitle.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatTitle updateChat = (TdApi.UpdateChatTitle) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.title = updateChat.title;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatPhoto.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatPhoto updateChat = (TdApi.UpdateChatPhoto) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.photo = updateChat.photo;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatLastMessage.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatLastMessage updateChat = (TdApi.UpdateChatLastMessage) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.lastMessage = updateChat.lastMessage;
|
||||
setChatPositions(chat, updateChat.positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatPosition.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatPosition updateChat = (TdApi.UpdateChatPosition) object;
|
||||
if (updateChat.position.list.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
break;
|
||||
}
|
||||
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
int i;
|
||||
for (i = 0; i < chat.positions.length; i++) {
|
||||
if (chat.positions[i].list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TdApi.ChatPosition[] new_positions = new TdApi.ChatPosition[chat.positions.length + (updateChat.position.order == 0 ? 0 : 1) - (i < chat.positions.length ? 1 : 0)];
|
||||
int pos = 0;
|
||||
if (updateChat.position.order != 0) {
|
||||
new_positions[pos++] = updateChat.position;
|
||||
}
|
||||
for (int j = 0; j < chat.positions.length; j++) {
|
||||
if (j != i) {
|
||||
new_positions[pos++] = chat.positions[j];
|
||||
}
|
||||
}
|
||||
assert pos == new_positions.length;
|
||||
|
||||
setChatPositions(chat, new_positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatReadInbox.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatReadInbox updateChat = (TdApi.UpdateChatReadInbox) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.lastReadInboxMessageId = updateChat.lastReadInboxMessageId;
|
||||
chat.unreadCount = updateChat.unreadCount;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatReadOutbox.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatReadOutbox updateChat = (TdApi.UpdateChatReadOutbox) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.lastReadOutboxMessageId = updateChat.lastReadOutboxMessageId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatUnreadMentionCount.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatUnreadMentionCount updateChat = (TdApi.UpdateChatUnreadMentionCount) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.unreadMentionCount = updateChat.unreadMentionCount;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateMessageMentionRead.CONSTRUCTOR: {
|
||||
TdApi.UpdateMessageMentionRead updateChat = (TdApi.UpdateMessageMentionRead) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.unreadMentionCount = updateChat.unreadMentionCount;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatReplyMarkup.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatReplyMarkup updateChat = (TdApi.UpdateChatReplyMarkup) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.replyMarkupMessageId = updateChat.replyMarkupMessageId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatDraftMessage.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatDraftMessage updateChat = (TdApi.UpdateChatDraftMessage) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.draftMessage = updateChat.draftMessage;
|
||||
setChatPositions(chat, updateChat.positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatPermissions.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatPermissions update = (TdApi.UpdateChatPermissions) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.permissions = update.permissions;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatNotificationSettings.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatNotificationSettings update = (TdApi.UpdateChatNotificationSettings) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.notificationSettings = update.notificationSettings;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatDefaultDisableNotification.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatDefaultDisableNotification update = (TdApi.UpdateChatDefaultDisableNotification) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.defaultDisableNotification = update.defaultDisableNotification;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatIsMarkedAsUnread.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatIsMarkedAsUnread update = (TdApi.UpdateChatIsMarkedAsUnread) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.isMarkedAsUnread = update.isMarkedAsUnread;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatIsBlocked.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatIsBlocked update = (TdApi.UpdateChatIsBlocked) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.isBlocked = update.isBlocked;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatHasScheduledMessages.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatHasScheduledMessages update = (TdApi.UpdateChatHasScheduledMessages) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.hasScheduledMessages = update.hasScheduledMessages;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TdApi.UpdateUserFullInfo.CONSTRUCTOR:
|
||||
TdApi.UpdateUserFullInfo updateUserFullInfo = (TdApi.UpdateUserFullInfo) object;
|
||||
usersFullInfo.put(updateUserFullInfo.userId, updateUserFullInfo.userFullInfo);
|
||||
break;
|
||||
case TdApi.UpdateBasicGroupFullInfo.CONSTRUCTOR:
|
||||
TdApi.UpdateBasicGroupFullInfo updateBasicGroupFullInfo = (TdApi.UpdateBasicGroupFullInfo) object;
|
||||
basicGroupsFullInfo.put(updateBasicGroupFullInfo.basicGroupId, updateBasicGroupFullInfo.basicGroupFullInfo);
|
||||
break;
|
||||
case TdApi.UpdateSupergroupFullInfo.CONSTRUCTOR:
|
||||
TdApi.UpdateSupergroupFullInfo updateSupergroupFullInfo = (TdApi.UpdateSupergroupFullInfo) object;
|
||||
supergroupsFullInfo.put(updateSupergroupFullInfo.supergroupId, updateSupergroupFullInfo.supergroupFullInfo);
|
||||
break;
|
||||
default:
|
||||
// print("Unsupported update:" + newLine + object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ErrorHandler implements ExceptionHandler {
|
||||
|
||||
@Override
|
||||
public void onException(Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static class AuthorizationRequestHandler implements ResultHandler {
|
||||
@Override
|
||||
public void onResult(TdApi.Object object) {
|
||||
switch (object.getConstructor()) {
|
||||
case TdApi.Error.CONSTRUCTOR:
|
||||
System.err.println("Receive an error:" + newLine + object);
|
||||
onAuthorizationStateUpdated(null); // repeat last action
|
||||
break;
|
||||
case TdApi.Ok.CONSTRUCTOR:
|
||||
// result is already received through UpdateAuthorizationState, nothing to do
|
||||
break;
|
||||
default:
|
||||
System.err.println("Receive wrong response from TDLib:" + newLine + object);
|
||||
public void onCommand(Chat chat, MessageSender commandSender, String arguments) {
|
||||
// Check if the sender is the admin
|
||||
if (isAdmin(commandSender)) {
|
||||
// Stop the client
|
||||
System.out.println("Received stop command. closing...");
|
||||
client.sendClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
68
src/main/java/it/tdlight/client/APIToken.java
Normal file
68
src/main/java/it/tdlight/client/APIToken.java
Normal file
@ -0,0 +1,68 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class APIToken {
|
||||
|
||||
private int apiID;
|
||||
private String apiHash;
|
||||
|
||||
/**
|
||||
* Obtain your API token here: <a href="https://my.telegram.org/auth?to=apps">https://my.telegram.org/auth?to=apps</a>
|
||||
*/
|
||||
public APIToken(int apiID, String apiHash) {
|
||||
this.apiID = apiID;
|
||||
this.apiHash = apiHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example token
|
||||
*/
|
||||
public static APIToken example() {
|
||||
int apiID = 94575;
|
||||
String apiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
|
||||
return new APIToken(apiID, apiHash);
|
||||
}
|
||||
|
||||
public int getApiID() {
|
||||
return apiID;
|
||||
}
|
||||
|
||||
public void setApiID(int apiID) {
|
||||
this.apiID = apiID;
|
||||
}
|
||||
|
||||
public String getApiHash() {
|
||||
return apiHash;
|
||||
}
|
||||
|
||||
public void setApiHash(String apiHash) {
|
||||
this.apiHash = apiHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
APIToken apiData = (APIToken) o;
|
||||
return apiID == apiData.apiID && Objects.equals(apiHash, apiData.apiHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(apiID, apiHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", APIToken.class.getSimpleName() + "[", "]")
|
||||
.add("apiID=" + apiID)
|
||||
.add("apiHash='" + apiHash + "'")
|
||||
.toString();
|
||||
}
|
||||
}
|
6
src/main/java/it/tdlight/client/Authenticable.java
Normal file
6
src/main/java/it/tdlight/client/Authenticable.java
Normal file
@ -0,0 +1,6 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
public interface Authenticable {
|
||||
|
||||
AuthenticationData getAuthenticationData();
|
||||
}
|
75
src/main/java/it/tdlight/client/AuthenticationData.java
Normal file
75
src/main/java/it/tdlight/client/AuthenticationData.java
Normal file
@ -0,0 +1,75 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class AuthenticationData {
|
||||
private final Long userPhoneNumber;
|
||||
private final String botToken;
|
||||
|
||||
private AuthenticationData(Long userPhoneNumber, String botToken) {
|
||||
if ((userPhoneNumber == null) == (botToken == null)) {
|
||||
throw new IllegalArgumentException("Please use either a bot token or a phone number");
|
||||
}
|
||||
if (botToken != null) {
|
||||
if (botToken.length() < 5 || botToken.length() > 200) {
|
||||
throw new IllegalArgumentException("Bot token is invalid: " + botToken);
|
||||
}
|
||||
}
|
||||
this.userPhoneNumber = userPhoneNumber;
|
||||
this.botToken = botToken;
|
||||
}
|
||||
|
||||
public static AuthenticationData user(long userPhoneNumber) {
|
||||
return new AuthenticationData(userPhoneNumber, null);
|
||||
}
|
||||
|
||||
public static AuthenticationData bot(String botToken) {
|
||||
return new AuthenticationData(null, botToken);
|
||||
}
|
||||
|
||||
public boolean isBot() {
|
||||
return botToken != null;
|
||||
}
|
||||
|
||||
public Long getUserPhoneNumber() {
|
||||
if (userPhoneNumber == null) {
|
||||
throw new UnsupportedOperationException("This is not a user");
|
||||
}
|
||||
return userPhoneNumber;
|
||||
}
|
||||
|
||||
public String getBotToken() {
|
||||
if (botToken == null) {
|
||||
throw new UnsupportedOperationException("This is not a bot");
|
||||
}
|
||||
return botToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (userPhoneNumber != null) {
|
||||
return "+" + userPhoneNumber;
|
||||
} else {
|
||||
return "\"" + botToken + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AuthenticationData that = (AuthenticationData) o;
|
||||
return Objects.equals(userPhoneNumber, that.userPhoneNumber) && Objects.equals(botToken, that.botToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userPhoneNumber, botToken);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateReady;
|
||||
import it.tdlight.jni.TdApi.GetMe;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import it.tdlight.jni.TdApi.User;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class AuthorizationStateReadyGetMe implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateReadyGetMe.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final AtomicReference<User> me;
|
||||
|
||||
public AuthorizationStateReadyGetMe(TelegramClient client, AtomicReference<User> me) {
|
||||
this.client = client;
|
||||
this.me = me;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateReady.CONSTRUCTOR) {
|
||||
client.send(new GetMe(), me -> this.me.set((User) me), error -> logger.warn("Failed to execute TdApi.GetMe()"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.common.utils.ScannerUtils;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitEncryptionKey;
|
||||
import it.tdlight.jni.TdApi.CheckDatabaseEncryptionKey;
|
||||
import it.tdlight.jni.TdApi.PhoneNumberAuthenticationSettings;
|
||||
import it.tdlight.jni.TdApi.SetAuthenticationPhoneNumber;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class AuthorizationStateWaitAuthenticationDataHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateWaitAuthenticationDataHandler.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final Authenticable authenticable;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitAuthenticationDataHandler(TelegramClient client,
|
||||
Authenticable authenticable,
|
||||
ExceptionHandler exceptionHandler) {
|
||||
this.client = client;
|
||||
this.authenticable = authenticable;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR) {
|
||||
AuthenticationData authenticationData = authenticable.getAuthenticationData();
|
||||
if (authenticationData.isBot()) {
|
||||
String botToken = authenticationData.getBotToken();
|
||||
TdApi.CheckAuthenticationBotToken response = new TdApi.CheckAuthenticationBotToken(botToken);
|
||||
client.send(response, ok -> {}, ex -> {
|
||||
logger.error("Failed to set TDLight phone number or bot token!", ex);
|
||||
exceptionHandler.onException(ex);
|
||||
});
|
||||
} else {
|
||||
PhoneNumberAuthenticationSettings phoneSettings = new PhoneNumberAuthenticationSettings(false, false, false);
|
||||
|
||||
String phoneNumber = String.valueOf(authenticationData.getUserPhoneNumber());
|
||||
SetAuthenticationPhoneNumber response = new SetAuthenticationPhoneNumber(phoneNumber, phoneSettings);
|
||||
client.send(response, ok -> {}, ex -> {
|
||||
logger.error("Failed to set TDLight phone number!", ex);
|
||||
exceptionHandler.onException(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitCode;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitOtherDeviceConfirmation;
|
||||
import it.tdlight.jni.TdApi.CheckAuthenticationCode;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class AuthorizationStateWaitCodeHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateWaitCodeHandler.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitCodeHandler(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() == AuthorizationStateWaitCode.CONSTRUCTOR) {
|
||||
AuthorizationStateWaitCode authorizationState =
|
||||
(AuthorizationStateWaitCode) update.authorizationState;
|
||||
ParameterInfo parameterInfo = new ParameterInfoCode(authorizationState.codeInfo.phoneNumber,
|
||||
authorizationState.codeInfo.nextType,
|
||||
authorizationState.codeInfo.timeout,
|
||||
authorizationState.codeInfo.type
|
||||
);
|
||||
String code = clientInteraction.onParameterRequest(InputParameter.ASK_CODE, parameterInfo);
|
||||
Function response = new CheckAuthenticationCode(code);
|
||||
client.send(response, ok -> {}, ex -> {
|
||||
logger.error("Failed to check authentication code", ex);
|
||||
exceptionHandler.onException(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitEncryptionKey;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters;
|
||||
import it.tdlight.jni.TdApi.CheckDatabaseEncryptionKey;
|
||||
import it.tdlight.jni.TdApi.SetTdlibParameters;
|
||||
import it.tdlight.jni.TdApi.TdlibParameters;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class AuthorizationStateWaitEncryptionKeyHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateWaitEncryptionKeyHandler.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitEncryptionKeyHandler(TelegramClient client, ExceptionHandler exceptionHandler) {
|
||||
this.client = client;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateWaitEncryptionKey.CONSTRUCTOR) {
|
||||
client.send(new CheckDatabaseEncryptionKey(), ok -> {}, ex -> {
|
||||
logger.error("Failed to manage TDLight database encryption key!", ex);
|
||||
exceptionHandler.onException(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateClosed;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
final class AuthorizationStateWaitForExit implements GenericUpdateHandler<TdApi.UpdateAuthorizationState> {
|
||||
|
||||
private final CountDownLatch closed;
|
||||
|
||||
public AuthorizationStateWaitForExit(CountDownLatch closed) {
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateClosed.CONSTRUCTOR) {
|
||||
closed.countDown();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitOtherDeviceConfirmation;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
|
||||
final class AuthorizationStateWaitOtherDeviceConfirmationHandler
|
||||
implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private final TelegramClient client;
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitOtherDeviceConfirmationHandler(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() == AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR) {
|
||||
AuthorizationStateWaitOtherDeviceConfirmation authorizationState =
|
||||
(AuthorizationStateWaitOtherDeviceConfirmation) update.authorizationState;
|
||||
ParameterInfo parameterInfo = new ParameterInfoNotifyLink(authorizationState.link);
|
||||
clientInteraction.onParameterRequest(InputParameter.NOTIFY_LINK, parameterInfo);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitCode;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitPassword;
|
||||
import it.tdlight.jni.TdApi.CheckAuthenticationCode;
|
||||
import it.tdlight.jni.TdApi.CheckAuthenticationPassword;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class AuthorizationStateWaitPasswordHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateWaitPasswordHandler.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitPasswordHandler(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() == AuthorizationStateWaitPassword.CONSTRUCTOR) {
|
||||
AuthorizationStateWaitPassword authorizationState =
|
||||
(AuthorizationStateWaitPassword) update.authorizationState;
|
||||
ParameterInfo parameterInfo = new ParameterInfoPasswordHint(authorizationState.passwordHint,
|
||||
authorizationState.hasRecoveryEmailAddress,
|
||||
authorizationState.recoveryEmailAddressPattern
|
||||
);
|
||||
String password = clientInteraction.onParameterRequest(InputParameter.ASK_PASSWORD, parameterInfo);
|
||||
Function response = new CheckAuthenticationPassword(password);
|
||||
client.send(response, ok -> {}, ex -> {
|
||||
logger.error("Failed to check authentication password", ex);
|
||||
exceptionHandler.onException(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitRegistration;
|
||||
import it.tdlight.jni.TdApi.RegisterUser;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class AuthorizationStateWaitRegistrationHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateWaitRegistrationHandler.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final ClientInteraction clientInteraction;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitRegistrationHandler(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() == AuthorizationStateWaitRegistration.CONSTRUCTOR) {
|
||||
TdApi.AuthorizationStateWaitRegistration authorizationState =
|
||||
(TdApi.AuthorizationStateWaitRegistration) update.authorizationState;
|
||||
clientInteraction.onParameterRequest(InputParameter.TERMS_OF_SERVICE, new ParameterInfoTermsOfService(authorizationState.termsOfService));
|
||||
String firstName = clientInteraction.onParameterRequest(InputParameter.ASK_FIRST_NAME, new EmptyParameterInfo());
|
||||
String lastName = clientInteraction.onParameterRequest(InputParameter.ASK_LAST_NAME, new EmptyParameterInfo());
|
||||
if (firstName == null || firstName.isEmpty()) {
|
||||
exceptionHandler.onException(new IllegalArgumentException("First name must not be null or empty"));
|
||||
return;
|
||||
}
|
||||
if (firstName.length() > 64) {
|
||||
exceptionHandler.onException(new IllegalArgumentException("First name must be under 64 characters"));
|
||||
return;
|
||||
}
|
||||
if (lastName == null) {
|
||||
exceptionHandler.onException(new IllegalArgumentException("Last name must not be null"));
|
||||
return;
|
||||
}
|
||||
if (lastName.length() > 64) {
|
||||
exceptionHandler.onException(new IllegalArgumentException("Last name must be under 64 characters"));
|
||||
return;
|
||||
}
|
||||
RegisterUser response = new RegisterUser(firstName, lastName);
|
||||
client.send(response, ok -> {}, ex -> {
|
||||
logger.error("Failed to register user", ex);
|
||||
exceptionHandler.onException(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters;
|
||||
import it.tdlight.jni.TdApi.SetTdlibParameters;
|
||||
import it.tdlight.jni.TdApi.TdlibParameters;
|
||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class AuthorizationStateWaitTdlibParametersHandler implements GenericUpdateHandler<UpdateAuthorizationState> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizationStateWaitEncryptionKeyHandler.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final TDLibSettings settings;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
public AuthorizationStateWaitTdlibParametersHandler(TelegramClient client,
|
||||
TDLibSettings settings,
|
||||
ExceptionHandler exceptionHandler) {
|
||||
this.client = client;
|
||||
this.settings = settings;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateAuthorizationState update) {
|
||||
if (update.authorizationState.getConstructor() == AuthorizationStateWaitTdlibParameters.CONSTRUCTOR) {
|
||||
TdlibParameters params = new TdlibParameters();
|
||||
params.useTestDc = settings.isUsingTestDatacenter();
|
||||
params.databaseDirectory = settings.getDatabaseDirectoryPath().toString();
|
||||
params.filesDirectory = settings.getDownloadedFilesDirectoryPath().toString();
|
||||
params.useFileDatabase = settings.isFileDatabaseEnabled();
|
||||
params.useChatInfoDatabase = settings.isChatInfoDatabaseEnabled();
|
||||
params.useMessageDatabase = settings.isMessageDatabaseEnabled();
|
||||
params.useSecretChats = false;
|
||||
params.apiId = settings.getApiToken().getApiID();
|
||||
params.apiHash = settings.getApiToken().getApiHash();
|
||||
params.systemLanguageCode = settings.getSystemLanguageCode();
|
||||
params.deviceModel = settings.getDeviceModel();
|
||||
params.systemVersion = settings.getSystemVersion();
|
||||
params.applicationVersion = settings.getApplicationVersion();
|
||||
params.enableStorageOptimizer = settings.isStorageOptimizerEnabled();
|
||||
params.ignoreFileNames = settings.isIgnoreFileNames();
|
||||
client.send(new SetTdlibParameters(params), ok -> {}, ex -> {
|
||||
logger.error("Failed to set TDLight parameters!", ex);
|
||||
exceptionHandler.onException(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
6
src/main/java/it/tdlight/client/ClientInteraction.java
Normal file
6
src/main/java/it/tdlight/client/ClientInteraction.java
Normal file
@ -0,0 +1,6 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
public interface ClientInteraction {
|
||||
|
||||
String onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo);
|
||||
}
|
10
src/main/java/it/tdlight/client/CommandHandler.java
Normal file
10
src/main/java/it/tdlight/client/CommandHandler.java
Normal file
@ -0,0 +1,10 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.User;
|
||||
|
||||
public interface CommandHandler {
|
||||
|
||||
void onCommand(Chat chat, TdApi.MessageSender commandSender, String arguments);
|
||||
}
|
93
src/main/java/it/tdlight/client/CommandsHandler.java
Normal file
93
src/main/java/it/tdlight/client/CommandsHandler.java
Normal file
@ -0,0 +1,93 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.Message;
|
||||
import it.tdlight.jni.TdApi.MessageText;
|
||||
import it.tdlight.jni.TdApi.UpdateNewMessage;
|
||||
import it.tdlight.jni.TdApi.User;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class CommandsHandler implements GenericUpdateHandler<UpdateNewMessage> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CommandsHandler.class);
|
||||
|
||||
private final TelegramClient client;
|
||||
private final Map<String, Set<CommandHandler>> commandHandlers;
|
||||
private final AtomicReference<User> me;
|
||||
|
||||
public CommandsHandler(TelegramClient client,
|
||||
Map<String, Set<CommandHandler>> commandHandlers,
|
||||
AtomicReference<User> me) {
|
||||
this.client = client;
|
||||
this.commandHandlers = commandHandlers;
|
||||
this.me = me;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(UpdateNewMessage update) {
|
||||
if (update.getConstructor() == UpdateNewMessage.CONSTRUCTOR) {
|
||||
Message message = update.message;
|
||||
if (message.forwardInfo == null && !message.isChannelPost
|
||||
&& (message.authorSignature == null || message.authorSignature.isEmpty())
|
||||
&& message.content.getConstructor() == MessageText.CONSTRUCTOR) {
|
||||
MessageText messageText = (MessageText) message.content;
|
||||
String text = messageText.text.text;
|
||||
if (text.startsWith("/")) {
|
||||
String[] parts = text.split(" ", 2);
|
||||
if (parts.length == 1) {
|
||||
parts = new String[] {parts[0], ""};
|
||||
}
|
||||
if (parts.length == 2) {
|
||||
String currentUnsplittedCommandName = parts[0].substring(1);
|
||||
String arguments = parts[1].trim();
|
||||
String[] commandParts = currentUnsplittedCommandName.split("@", 2);
|
||||
String currentCommandName = null;
|
||||
boolean correctTarget = false;
|
||||
if (commandParts.length == 2) {
|
||||
String myUsername = this.getMyUsername().orElse(null);
|
||||
if (myUsername != null) {
|
||||
currentCommandName = commandParts[0].trim();
|
||||
String currentTargetUsername = commandParts[1];
|
||||
if (myUsername.equalsIgnoreCase(currentTargetUsername)) {
|
||||
correctTarget = true;
|
||||
}
|
||||
}
|
||||
} else if (commandParts.length == 1) {
|
||||
currentCommandName = commandParts[0].trim();
|
||||
correctTarget = true;
|
||||
}
|
||||
|
||||
if (correctTarget) {
|
||||
String commandName = currentCommandName;
|
||||
Set<CommandHandler> handlers = commandHandlers.getOrDefault(currentCommandName, Collections.emptySet());
|
||||
|
||||
for (CommandHandler handler : handlers) {
|
||||
client.send(new TdApi.GetChat(message.chatId),
|
||||
response -> handler.onCommand((Chat) response, message.sender, arguments),
|
||||
error -> logger.warn("Error when handling the command {}", commandName, error)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<String> getMyUsername() {
|
||||
User user = this.me.get();
|
||||
if (user == null || user.username == null || user.username.isEmpty()) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Optional.of(user.username);
|
||||
}
|
||||
}
|
||||
}
|
3
src/main/java/it/tdlight/client/EmptyParameterInfo.java
Normal file
3
src/main/java/it/tdlight/client/EmptyParameterInfo.java
Normal file
@ -0,0 +1,3 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
public final class EmptyParameterInfo implements ParameterInfo {}
|
23
src/main/java/it/tdlight/client/GenericResultHandler.java
Normal file
23
src/main/java/it/tdlight/client/GenericResultHandler.java
Normal file
@ -0,0 +1,23 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
|
||||
/**
|
||||
* Interface for incoming responses from TDLib.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GenericResultHandler<T extends TdApi.Object> {
|
||||
|
||||
/**
|
||||
* Callback called when TDLib responds.
|
||||
*/
|
||||
void onResult(Result<T> result);
|
||||
|
||||
default void onResult(TdApi.Object result) {
|
||||
onResult(Result.of(result));
|
||||
}
|
||||
|
||||
default void onErrorResult(Throwable exception) {
|
||||
onResult(Result.ofError(exception));
|
||||
}
|
||||
}
|
18
src/main/java/it/tdlight/client/GenericUpdateHandler.java
Normal file
18
src/main/java/it/tdlight/client/GenericUpdateHandler.java
Normal file
@ -0,0 +1,18 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
import it.tdlight.jni.TdApi.Update;
|
||||
|
||||
/**
|
||||
* Interface for incoming updates from TDLib.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GenericUpdateHandler<T extends Update> {
|
||||
|
||||
/**
|
||||
* Callback called on incoming update from TDLib.
|
||||
*
|
||||
* @param update Update of type TdApi.Update about new events.
|
||||
*/
|
||||
void onUpdate(T update);
|
||||
}
|
10
src/main/java/it/tdlight/client/InputParameter.java
Normal file
10
src/main/java/it/tdlight/client/InputParameter.java
Normal file
@ -0,0 +1,10 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
public enum InputParameter {
|
||||
ASK_FIRST_NAME,
|
||||
ASK_LAST_NAME,
|
||||
ASK_CODE,
|
||||
ASK_PASSWORD,
|
||||
NOTIFY_LINK,
|
||||
TERMS_OF_SERVICE
|
||||
}
|
3
src/main/java/it/tdlight/client/ParameterInfo.java
Normal file
3
src/main/java/it/tdlight/client/ParameterInfo.java
Normal file
@ -0,0 +1,3 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
public interface ParameterInfo {}
|
67
src/main/java/it/tdlight/client/ParameterInfoCode.java
Normal file
67
src/main/java/it/tdlight/client/ParameterInfoCode.java
Normal file
@ -0,0 +1,67 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi.AuthenticationCodeType;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class ParameterInfoCode implements ParameterInfo {
|
||||
private final String phoneNumber;
|
||||
private final AuthenticationCodeType nextType;
|
||||
private final int timeout;
|
||||
private final AuthenticationCodeType type;
|
||||
|
||||
public ParameterInfoCode(String phoneNumber,
|
||||
AuthenticationCodeType nextType,
|
||||
int timeout,
|
||||
AuthenticationCodeType type) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.nextType = nextType;
|
||||
this.timeout = timeout;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public AuthenticationCodeType getNextType() {
|
||||
return nextType;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public AuthenticationCodeType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ParameterInfoCode that = (ParameterInfoCode) o;
|
||||
return timeout == that.timeout && Objects.equals(phoneNumber, that.phoneNumber) && Objects.equals(nextType,
|
||||
that.nextType
|
||||
) && Objects.equals(type, that.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(phoneNumber, nextType, timeout, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ParameterInfoCode.class.getSimpleName() + "[", "]")
|
||||
.add("phoneNumber='" + phoneNumber + "'")
|
||||
.add("nextType=" + nextType)
|
||||
.add("timeout=" + timeout)
|
||||
.add("type=" + type)
|
||||
.toString();
|
||||
}
|
||||
}
|
41
src/main/java/it/tdlight/client/ParameterInfoNotifyLink.java
Normal file
41
src/main/java/it/tdlight/client/ParameterInfoNotifyLink.java
Normal file
@ -0,0 +1,41 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class ParameterInfoNotifyLink implements ParameterInfo {
|
||||
|
||||
private final String link;
|
||||
|
||||
public ParameterInfoNotifyLink(String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ParameterInfoNotifyLink that = (ParameterInfoNotifyLink) o;
|
||||
return Objects.equals(link, that.link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ParameterInfoNotifyLink.class.getSimpleName() + "[", "]")
|
||||
.add("link='" + link + "'")
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class ParameterInfoPasswordHint implements ParameterInfo {
|
||||
|
||||
private final String hint;
|
||||
private final boolean hasRecoveryEmailAddress;
|
||||
private final String recoveryEmailAddressPattern;
|
||||
|
||||
public ParameterInfoPasswordHint(String hint, boolean hasRecoveryEmailAddress, String recoveryEmailAddressPattern) {
|
||||
this.hint = hint;
|
||||
this.hasRecoveryEmailAddress = hasRecoveryEmailAddress;
|
||||
this.recoveryEmailAddressPattern = recoveryEmailAddressPattern;
|
||||
}
|
||||
|
||||
public String getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
public String getRecoveryEmailAddressPattern() {
|
||||
return recoveryEmailAddressPattern;
|
||||
}
|
||||
|
||||
public boolean hasRecoveryEmailAddress() {
|
||||
return hasRecoveryEmailAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ParameterInfoPasswordHint that = (ParameterInfoPasswordHint) o;
|
||||
return hasRecoveryEmailAddress == that.hasRecoveryEmailAddress && Objects.equals(hint, that.hint) && Objects.equals(
|
||||
recoveryEmailAddressPattern,
|
||||
that.recoveryEmailAddressPattern
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(hint, hasRecoveryEmailAddress, recoveryEmailAddressPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ParameterInfoPasswordHint.class.getSimpleName() + "[", "]")
|
||||
.add("hint='" + hint + "'")
|
||||
.add("hasRecoveryEmailAddress=" + hasRecoveryEmailAddress)
|
||||
.add("recoveryEmailAddressPattern='" + recoveryEmailAddressPattern + "'")
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi.TermsOfService;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class ParameterInfoTermsOfService implements ParameterInfo {
|
||||
private final TermsOfService termsOfService;
|
||||
|
||||
public ParameterInfoTermsOfService(TermsOfService termsOfService) {
|
||||
this.termsOfService = termsOfService;
|
||||
}
|
||||
|
||||
public TermsOfService getTermsOfService() {
|
||||
return termsOfService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ParameterInfoTermsOfService that = (ParameterInfoTermsOfService) o;
|
||||
return Objects.equals(termsOfService, that.termsOfService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(termsOfService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ParameterInfoTermsOfService.class.getSimpleName() + "[", "]")
|
||||
.add("termsOfService=" + termsOfService)
|
||||
.toString();
|
||||
}
|
||||
}
|
91
src/main/java/it/tdlight/client/Result.java
Normal file
91
src/main/java/it/tdlight/client/Result.java
Normal file
@ -0,0 +1,91 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Error;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class Result<T extends TdApi.Object> {
|
||||
|
||||
private final T object;
|
||||
private final Error error;
|
||||
private final TelegramError telegramError;
|
||||
|
||||
private Result(T object, TdApi.Error error, TelegramError telegramError) {
|
||||
this.object = object;
|
||||
this.error = error;
|
||||
this.telegramError = telegramError;
|
||||
}
|
||||
|
||||
public static <T extends TdApi.Object> Result<T> of(TdApi.Object response) {
|
||||
if (response instanceof TdApi.Error) {
|
||||
return new Result<>(null, (TdApi.Error) response, null);
|
||||
} else {
|
||||
//noinspection unchecked
|
||||
return new Result<>((T) response, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T extends TdApi.Object> Result<T> ofError(Throwable response) {
|
||||
return new Result<>(null, null, new TelegramError(response));
|
||||
}
|
||||
|
||||
public T get() {
|
||||
if (error != null) {
|
||||
throw new TelegramError(error);
|
||||
} else if (telegramError != null) {
|
||||
throw telegramError;
|
||||
}
|
||||
return Objects.requireNonNull(object);
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return error != null || telegramError != null;
|
||||
}
|
||||
|
||||
public TdApi.Error getError() {
|
||||
if (telegramError != null) {
|
||||
return telegramError.getError();
|
||||
}
|
||||
return Objects.requireNonNull(error);
|
||||
}
|
||||
|
||||
public Optional<TdApi.Error> error() {
|
||||
if (error != null) {
|
||||
return Optional.of(error);
|
||||
} else if (telegramError != null) {
|
||||
return Optional.of(telegramError.getError());
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Result<?> result = (Result<?>) o;
|
||||
return Objects.equals(object, result.object) && Objects.equals(error, result.error) && Objects.equals(telegramError,
|
||||
result.telegramError
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(object, error, telegramError);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", Result.class.getSimpleName() + "[", "]")
|
||||
.add("object=" + object)
|
||||
.add("error=" + error)
|
||||
.add("telegramError=" + telegramError)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.utils.ScannerUtils;
|
||||
import it.tdlight.jni.TdApi.TermsOfService;
|
||||
|
||||
final class ScannerClientInteraction implements ClientInteraction {
|
||||
|
||||
private final Authenticable authenticable;
|
||||
|
||||
public ScannerClientInteraction(Authenticable authenticable) {
|
||||
this.authenticable = authenticable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo) {
|
||||
AuthenticationData authenticationData = authenticable.getAuthenticationData();
|
||||
String who;
|
||||
if (authenticationData.isBot()) {
|
||||
who = authenticationData.getBotToken().split(":", 2)[0];
|
||||
} else {
|
||||
who = "+" + authenticationData.getUserPhoneNumber();
|
||||
}
|
||||
String question;
|
||||
boolean trim = false;
|
||||
switch (parameter) {
|
||||
case ASK_FIRST_NAME: question = "Enter first name"; trim = true; break;
|
||||
case ASK_LAST_NAME: question = "Enter last name"; trim = true; break;
|
||||
case ASK_CODE:
|
||||
question = "Enter authentication code";
|
||||
ParameterInfoCode codeInfo = ((ParameterInfoCode) parameterInfo);
|
||||
question += "\n\tPhone number: " + codeInfo.getPhoneNumber();
|
||||
question += "\n\tTimeout: " + codeInfo.getTimeout() + " seconds";
|
||||
question += "\n\tCode type: " + codeInfo.getType().getClass().getSimpleName().replace("AuthenticationCodeType", "");
|
||||
if (codeInfo.getNextType() != null) {
|
||||
question += "\n\tNext code type: " + codeInfo.getNextType().getClass().getSimpleName().replace("AuthenticationCodeType", "");
|
||||
}
|
||||
trim = true;
|
||||
break;
|
||||
case ASK_PASSWORD:
|
||||
question = "Enter your password";
|
||||
String passwordMessage = "Password authorization:";
|
||||
String hint = ((ParameterInfoPasswordHint) parameterInfo).getHint();
|
||||
if (hint != null && !hint.isEmpty()) {
|
||||
passwordMessage += "\n\tHint: " + hint;
|
||||
}
|
||||
boolean hasRecoveryEmailAddress = ((ParameterInfoPasswordHint) parameterInfo).hasRecoveryEmailAddress();
|
||||
passwordMessage += "\n\tHas recovery email: " + hasRecoveryEmailAddress;
|
||||
String recoveryEmailAddressPattern = ((ParameterInfoPasswordHint) parameterInfo).getRecoveryEmailAddressPattern();
|
||||
if (recoveryEmailAddressPattern != null && !recoveryEmailAddressPattern.isEmpty()) {
|
||||
passwordMessage += "\n\tRecovery email address pattern: " + recoveryEmailAddressPattern;
|
||||
}
|
||||
System.out.println(passwordMessage);
|
||||
break;
|
||||
case NOTIFY_LINK:
|
||||
System.out.println("Please confirm this login link on another device: "
|
||||
+ ((ParameterInfoNotifyLink) parameterInfo).getLink());
|
||||
return "";
|
||||
case TERMS_OF_SERVICE:
|
||||
TermsOfService tos = ((ParameterInfoTermsOfService) parameterInfo).getTermsOfService();
|
||||
question = "Terms of service:\n\t" + tos.text.text;
|
||||
if (tos.minUserAge > 0) {
|
||||
question += "\n\tMinimum user age: " + tos.minUserAge;
|
||||
}
|
||||
if (tos.showPopup) {
|
||||
question += "\nPlease press enter.";
|
||||
trim = true;
|
||||
} else {
|
||||
System.out.println(question);
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
default: question = parameter.toString(); break;
|
||||
}
|
||||
String result = ScannerUtils.askParameter(who, question);
|
||||
if (trim) {
|
||||
return result.trim();
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
245
src/main/java/it/tdlight/client/SimpleTelegramClient.java
Normal file
245
src/main/java/it/tdlight/client/SimpleTelegramClient.java
Normal file
@ -0,0 +1,245 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.ConstructorDetector;
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.Init;
|
||||
import it.tdlight.common.Log;
|
||||
import it.tdlight.common.ResultHandler;
|
||||
import it.tdlight.common.TelegramClient;
|
||||
import it.tdlight.common.internal.CommonClientManager;
|
||||
import it.tdlight.common.utils.CantLoadLibrary;
|
||||
import it.tdlight.common.utils.LibraryVersion;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Chat;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
import it.tdlight.jni.TdApi.Message;
|
||||
import it.tdlight.jni.TdApi.MessageText;
|
||||
import it.tdlight.jni.TdApi.UpdateNewMessage;
|
||||
import it.tdlight.jni.TdApi.User;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class SimpleTelegramClient implements Authenticable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SimpleTelegramClient.class);
|
||||
|
||||
static {
|
||||
try {
|
||||
Init.start();
|
||||
} catch (CantLoadLibrary e) {
|
||||
throw new RuntimeException("Can't load native libraries", e);
|
||||
}
|
||||
try {
|
||||
//noinspection deprecation
|
||||
Log.setVerbosityLevel(1);
|
||||
} catch (Throwable ex) {
|
||||
logger.warn("Can't set verbosity level", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final TelegramClient client;
|
||||
private ClientInteraction clientInteraction = new ScannerClientInteraction(this);
|
||||
private final TDLibSettings settings;
|
||||
private AuthenticationData authenticationData;
|
||||
|
||||
private final Map<String, Set<CommandHandler>> commandHandlers = new ConcurrentHashMap<>();
|
||||
private final Set<ResultHandler> updateHandlers = new ConcurrentHashMap<ResultHandler, Object>()
|
||||
.keySet(new Object());
|
||||
private final Set<ExceptionHandler> updateExceptionHandlers = new ConcurrentHashMap<ExceptionHandler, Object>()
|
||||
.keySet(new Object());
|
||||
private final Set<ExceptionHandler> defaultExceptionHandlers = new ConcurrentHashMap<ExceptionHandler, Object>()
|
||||
.keySet(new Object());
|
||||
|
||||
private final CountDownLatch closed = new CountDownLatch(1);
|
||||
|
||||
public SimpleTelegramClient(TDLibSettings settings) {
|
||||
this.client = CommonClientManager.create(LibraryVersion.IMPLEMENTATION_NAME);
|
||||
this.settings = settings;
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitTdlibParametersHandler(client, settings, this::handleDefaultException));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitEncryptionKeyHandler(client, this::handleDefaultException));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitAuthenticationDataHandler(client, this,
|
||||
this::handleDefaultException));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitRegistrationHandler(client, new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitPasswordHandler(client, new SimpleTelegramClientInteraction(),
|
||||
this::handleDefaultException));
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
new AuthorizationStateWaitOtherDeviceConfirmationHandler(client, 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));
|
||||
AtomicReference<User> me = new AtomicReference<>();
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, new AuthorizationStateReadyGetMe(client, me));
|
||||
this.addUpdateHandler(TdApi.UpdateNewMessage.class, new CommandsHandler(client, this.commandHandlers, me));
|
||||
}
|
||||
|
||||
private void handleUpdate(TdApi.Object update) {
|
||||
boolean handled = false;
|
||||
for (ResultHandler updateHandler : updateHandlers) {
|
||||
updateHandler.onResult(update);
|
||||
handled = true;
|
||||
}
|
||||
if (!handled) {
|
||||
logger.warn("An update was not handled, please use addUpdateHandler(handler) before starting the client!");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUpdateException(Throwable ex) {
|
||||
boolean handled = false;
|
||||
for (ExceptionHandler updateExceptionHandler : updateExceptionHandlers) {
|
||||
updateExceptionHandler.onException(ex);
|
||||
handled = true;
|
||||
}
|
||||
if (!handled) {
|
||||
logger.warn("Error received from Telegram!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDefaultException(Throwable ex) {
|
||||
boolean handled = false;
|
||||
for (ExceptionHandler exceptionHandler : defaultExceptionHandlers) {
|
||||
exceptionHandler.onException(ex);
|
||||
handled = true;
|
||||
}
|
||||
if (!handled) {
|
||||
logger.warn("Unhandled exception!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationData getAuthenticationData() {
|
||||
return authenticationData;
|
||||
}
|
||||
|
||||
public void setClientInteraction(ClientInteraction clientInteraction) {
|
||||
this.clientInteraction = clientInteraction;
|
||||
}
|
||||
|
||||
public <T extends TdApi.Update> void addCommandHandler(String commandName, CommandHandler handler) {
|
||||
Set<CommandHandler> handlers = this.commandHandlers
|
||||
.computeIfAbsent(commandName, k -> new ConcurrentHashMap<CommandHandler, Object>().keySet(new Object()));
|
||||
handlers.add(handler);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends TdApi.Update> void addUpdateHandler(Class<T> updateType, GenericUpdateHandler<T> handler) {
|
||||
int updateConstructor = ConstructorDetector.getConstructor(updateType);
|
||||
this.updateHandlers.add(update -> {
|
||||
if (update.getConstructor() == updateConstructor) {
|
||||
handler.onUpdate((T) update);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addUpdatesHandler(GenericUpdateHandler<TdApi.Update> handler) {
|
||||
this.updateHandlers.add(update -> {
|
||||
if (update instanceof TdApi.Update) {
|
||||
handler.onUpdate((TdApi.Update) update);
|
||||
} else {
|
||||
logger.warn("Unknown update type: {}", update);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional handler to handle errors received from TDLib
|
||||
*/
|
||||
public void addUpdateExceptionHandler(ExceptionHandler updateExceptionHandler) {
|
||||
this.updateExceptionHandlers.add(updateExceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional handler to handle uncaught errors (when using send without an appropriate error handler)
|
||||
*/
|
||||
public void addDefaultExceptionHandler(ExceptionHandler defaultExceptionHandlers) {
|
||||
this.defaultExceptionHandlers.add(defaultExceptionHandlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the client
|
||||
*/
|
||||
public void start(AuthenticationData authenticationData) {
|
||||
this.authenticationData = authenticationData;
|
||||
createDirectories();
|
||||
client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException);
|
||||
}
|
||||
|
||||
private void createDirectories() {
|
||||
try {
|
||||
if (Files.notExists(settings.getDatabaseDirectoryPath())) {
|
||||
Files.createDirectories(settings.getDatabaseDirectoryPath());
|
||||
}
|
||||
if (Files.notExists(settings.getDownloadedFilesDirectoryPath())) {
|
||||
Files.createDirectories(settings.getDownloadedFilesDirectoryPath());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException("Can't create TDLight directories", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a function and get the result
|
||||
*/
|
||||
public <T extends TdApi.Object> void send(TdApi.Function function, GenericResultHandler<T> resultHandler) {
|
||||
client.send(function, resultHandler::onResult, resultHandler::onErrorResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a synchronous function.
|
||||
* <strong>Please note that only some functions can be executed using this method.</strong>
|
||||
* If you want to execute a function please use {@link #send(Function, GenericResultHandler)}!
|
||||
*/
|
||||
public <T extends TdApi.Object> Result<T> execute(TdApi.Function function) {
|
||||
return Result.of(client.execute(function));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the close signal but don't wait
|
||||
*/
|
||||
public void sendClose() {
|
||||
client.send(new TdApi.Close(), ok -> {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the close signal and wait for exit
|
||||
*/
|
||||
public void closeAndWait() throws InterruptedException {
|
||||
client.send(new TdApi.Close(), ok -> {});
|
||||
this.waitForExit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until TDLight is closed
|
||||
*/
|
||||
public void waitForExit() throws InterruptedException {
|
||||
closed.await();
|
||||
}
|
||||
|
||||
private final class SimpleTelegramClientInteraction implements ClientInteraction {
|
||||
|
||||
@Override
|
||||
public String onParameterRequest(InputParameter parameter, ParameterInfo parameterInfo) {
|
||||
return clientInteraction.onParameterRequest(parameter, parameterInfo);
|
||||
}
|
||||
}
|
||||
}
|
240
src/main/java/it/tdlight/client/TDLibSettings.java
Normal file
240
src/main/java/it/tdlight/client/TDLibSettings.java
Normal file
@ -0,0 +1,240 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.common.utils.LibraryVersion;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class TDLibSettings {
|
||||
|
||||
private static final Path USER_HOME_PATH = Paths.get(System.getProperty("user.home"));
|
||||
private static final String DISPLAY_LANGUAGE = Locale.getDefault().getDisplayLanguage();
|
||||
private static final String OS_NAME = System.getProperty("os.name", "unknown");
|
||||
private static final String OS_VERSION = System.getProperty("os.version", "unknown");
|
||||
private static final Path TDLIGHT_SESSION_PATH = USER_HOME_PATH.resolve("tdlight-session");
|
||||
private static final Path TDLIGHT_SESSION_DATA_PATH = TDLIGHT_SESSION_PATH.resolve("data");
|
||||
private static final Path TDLIGHT_SESSION_DOWNLOADS_PATH = TDLIGHT_SESSION_PATH.resolve("downloads");
|
||||
|
||||
private boolean useTestDatacenter;
|
||||
private Path databaseDirectoryPath;
|
||||
private Path downloadedFilesDirectoryPath;
|
||||
private boolean fileDatabaseEnabled;
|
||||
private boolean chatInfoDatabaseEnabled;
|
||||
private boolean messageDatabaseEnabled;
|
||||
private APIToken apiToken;
|
||||
private String systemLanguageCode;
|
||||
private String deviceModel;
|
||||
private String systemVersion;
|
||||
private String applicationVersion;
|
||||
private boolean enableStorageOptimizer;
|
||||
private boolean ignoreFileNames;
|
||||
|
||||
private TDLibSettings(boolean useTestDatacenter,
|
||||
Path databaseDirectoryPath,
|
||||
Path downloadedFilesDirectoryPath,
|
||||
boolean fileDatabaseEnabled,
|
||||
boolean chatInfoDatabaseEnabled,
|
||||
boolean messageDatabaseEnabled,
|
||||
APIToken apiToken,
|
||||
String systemLanguageCode,
|
||||
String deviceModel,
|
||||
String systemVersion,
|
||||
String applicationVersion,
|
||||
boolean enableStorageOptimizer,
|
||||
boolean ignoreFileNames) {
|
||||
this.useTestDatacenter = useTestDatacenter;
|
||||
this.databaseDirectoryPath = databaseDirectoryPath;
|
||||
this.downloadedFilesDirectoryPath = downloadedFilesDirectoryPath;
|
||||
this.fileDatabaseEnabled = fileDatabaseEnabled;
|
||||
this.chatInfoDatabaseEnabled = chatInfoDatabaseEnabled;
|
||||
this.messageDatabaseEnabled = messageDatabaseEnabled;
|
||||
this.apiToken = apiToken;
|
||||
this.systemLanguageCode = systemLanguageCode;
|
||||
this.deviceModel = deviceModel;
|
||||
this.systemVersion = systemVersion;
|
||||
this.applicationVersion = applicationVersion;
|
||||
this.enableStorageOptimizer = enableStorageOptimizer;
|
||||
this.ignoreFileNames = ignoreFileNames;
|
||||
}
|
||||
|
||||
public static TDLibSettings create(APIToken apiToken) {
|
||||
return new TDLibSettings(false,
|
||||
TDLIGHT_SESSION_DATA_PATH,
|
||||
TDLIGHT_SESSION_DOWNLOADS_PATH,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
apiToken,
|
||||
DISPLAY_LANGUAGE,
|
||||
OS_NAME,
|
||||
OS_VERSION,
|
||||
LibraryVersion.VERSION,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public boolean isUsingTestDatacenter() {
|
||||
return useTestDatacenter;
|
||||
}
|
||||
|
||||
public void setUseTestDatacenter(boolean useTestDatacenter) {
|
||||
this.useTestDatacenter = useTestDatacenter;
|
||||
}
|
||||
|
||||
public Path getDatabaseDirectoryPath() {
|
||||
return databaseDirectoryPath;
|
||||
}
|
||||
|
||||
public void setDatabaseDirectoryPath(Path databaseDirectoryPath) {
|
||||
this.databaseDirectoryPath = databaseDirectoryPath;
|
||||
}
|
||||
|
||||
public Path getDownloadedFilesDirectoryPath() {
|
||||
return downloadedFilesDirectoryPath;
|
||||
}
|
||||
|
||||
public void setDownloadedFilesDirectoryPath(Path downloadedFilesDirectoryPath) {
|
||||
this.downloadedFilesDirectoryPath = downloadedFilesDirectoryPath;
|
||||
}
|
||||
|
||||
public boolean isFileDatabaseEnabled() {
|
||||
return fileDatabaseEnabled;
|
||||
}
|
||||
|
||||
public void setFileDatabaseEnabled(boolean fileDatabaseEnabled) {
|
||||
this.fileDatabaseEnabled = fileDatabaseEnabled;
|
||||
}
|
||||
|
||||
public boolean isChatInfoDatabaseEnabled() {
|
||||
return chatInfoDatabaseEnabled;
|
||||
}
|
||||
|
||||
public void setChatInfoDatabaseEnabled(boolean chatInfoDatabaseEnabled) {
|
||||
this.chatInfoDatabaseEnabled = chatInfoDatabaseEnabled;
|
||||
}
|
||||
|
||||
public boolean isMessageDatabaseEnabled() {
|
||||
return messageDatabaseEnabled;
|
||||
}
|
||||
|
||||
public void setMessageDatabaseEnabled(boolean messageDatabaseEnabled) {
|
||||
this.messageDatabaseEnabled = messageDatabaseEnabled;
|
||||
}
|
||||
|
||||
public APIToken getApiToken() {
|
||||
return apiToken;
|
||||
}
|
||||
|
||||
public void setApiToken(APIToken apiToken) {
|
||||
this.apiToken = apiToken;
|
||||
}
|
||||
|
||||
public String getSystemLanguageCode() {
|
||||
return systemLanguageCode;
|
||||
}
|
||||
|
||||
public void setSystemLanguageCode(String systemLanguageCode) {
|
||||
this.systemLanguageCode = systemLanguageCode;
|
||||
}
|
||||
|
||||
public String getDeviceModel() {
|
||||
return deviceModel;
|
||||
}
|
||||
|
||||
public void setDeviceModel(String deviceModel) {
|
||||
this.deviceModel = deviceModel;
|
||||
}
|
||||
|
||||
public String getSystemVersion() {
|
||||
return systemVersion;
|
||||
}
|
||||
|
||||
public void setSystemVersion(String systemVersion) {
|
||||
this.systemVersion = systemVersion;
|
||||
}
|
||||
|
||||
public String getApplicationVersion() {
|
||||
return applicationVersion;
|
||||
}
|
||||
|
||||
public void setApplicationVersion(String applicationVersion) {
|
||||
this.applicationVersion = applicationVersion;
|
||||
}
|
||||
|
||||
public boolean isStorageOptimizerEnabled() {
|
||||
return enableStorageOptimizer;
|
||||
}
|
||||
|
||||
public void setEnableStorageOptimizer(boolean enableStorageOptimizer) {
|
||||
this.enableStorageOptimizer = enableStorageOptimizer;
|
||||
}
|
||||
|
||||
public boolean isIgnoreFileNames() {
|
||||
return ignoreFileNames;
|
||||
}
|
||||
|
||||
public void setIgnoreFileNames(boolean ignoreFileNames) {
|
||||
this.ignoreFileNames = ignoreFileNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TDLibSettings that = (TDLibSettings) o;
|
||||
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
|
||||
) && Objects.equals(apiToken, that.apiToken) && Objects.equals(systemLanguageCode, that.systemLanguageCode)
|
||||
&& Objects.equals(deviceModel, that.deviceModel) && Objects.equals(systemVersion, that.systemVersion)
|
||||
&& Objects.equals(applicationVersion, that.applicationVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(useTestDatacenter,
|
||||
databaseDirectoryPath,
|
||||
downloadedFilesDirectoryPath,
|
||||
fileDatabaseEnabled,
|
||||
chatInfoDatabaseEnabled,
|
||||
messageDatabaseEnabled, apiToken,
|
||||
systemLanguageCode,
|
||||
deviceModel,
|
||||
systemVersion,
|
||||
applicationVersion,
|
||||
enableStorageOptimizer,
|
||||
ignoreFileNames
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", TDLibSettings.class.getSimpleName() + "[", "]")
|
||||
.add("useTestDatacenter=" + useTestDatacenter)
|
||||
.add("databaseDirectoryPath=" + databaseDirectoryPath)
|
||||
.add("downloadedFilesDirectoryPath=" + downloadedFilesDirectoryPath)
|
||||
.add("fileDatabaseEnabled=" + fileDatabaseEnabled)
|
||||
.add("chatInfoDatabaseEnabled=" + chatInfoDatabaseEnabled)
|
||||
.add("messageDatabaseEnabled=" + messageDatabaseEnabled)
|
||||
.add("apiData=" + apiToken)
|
||||
.add("systemLanguageCode='" + systemLanguageCode + "'")
|
||||
.add("deviceModel='" + deviceModel + "'")
|
||||
.add("systemVersion='" + systemVersion + "'")
|
||||
.add("applicationVersion='" + applicationVersion + "'")
|
||||
.add("enableStorageOptimizer=" + enableStorageOptimizer)
|
||||
.add("ignoreFileNames=" + ignoreFileNames)
|
||||
.toString();
|
||||
}
|
||||
}
|
63
src/main/java/it/tdlight/client/TelegramError.java
Normal file
63
src/main/java/it/tdlight/client/TelegramError.java
Normal file
@ -0,0 +1,63 @@
|
||||
package it.tdlight.client;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Error;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public final class TelegramError extends RuntimeException {
|
||||
|
||||
private final int code;
|
||||
private final String message;
|
||||
|
||||
public TelegramError(Error error) {
|
||||
super(error.code + ": " + error.message);
|
||||
this.code = error.code;
|
||||
this.message = error.message;
|
||||
}
|
||||
|
||||
public TelegramError(Error error, Throwable cause) {
|
||||
super(error.code + ": " + error.message, cause);
|
||||
this.code = error.code;
|
||||
this.message = error.message;
|
||||
}
|
||||
|
||||
public TelegramError(Throwable cause) {
|
||||
super(cause);
|
||||
this.code = 500;
|
||||
if (cause.getMessage() != null) {
|
||||
this.message = cause.getMessage();
|
||||
} else {
|
||||
this.message = "Error";
|
||||
}
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Error getError() {
|
||||
return new TdApi.Error(code, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TelegramError that = (TelegramError) o;
|
||||
return code == that.code && Objects.equals(message, that.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(code, message);
|
||||
}
|
||||
}
|
@ -19,12 +19,14 @@ package it.tdlight.common;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Identify the class by using the Constructor.
|
||||
*/
|
||||
public class ConstructorDetector {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public final class ConstructorDetector {
|
||||
|
||||
static {
|
||||
// Call this to load static methods and prevent errors during startup!
|
||||
@ -36,6 +38,7 @@ public class ConstructorDetector {
|
||||
}
|
||||
|
||||
private static ConcurrentHashMap<Integer, Class> constructorHashMap;
|
||||
private static ConcurrentHashMap<Class, Integer> constructorHashMapInverse;
|
||||
|
||||
/**
|
||||
* Initialize the ConstructorDetector, it is called from the Init class.
|
||||
@ -51,6 +54,7 @@ public class ConstructorDetector {
|
||||
|
||||
/**
|
||||
* Identify the class.
|
||||
*
|
||||
* @param CONSTRUCTOR CONSTRUCTOR of the Tdlib API.
|
||||
* @return The class related to CONSTRUCTOR.
|
||||
*/
|
||||
@ -58,8 +62,19 @@ public class ConstructorDetector {
|
||||
return constructorHashMap.getOrDefault(CONSTRUCTOR, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify the class.
|
||||
*
|
||||
* @param clazz class of the TDLib API.
|
||||
* @return The CONSTRUCTOR.
|
||||
*/
|
||||
public static int getConstructor(Class<? extends TdApi.Object> clazz) {
|
||||
return Objects.requireNonNull(constructorHashMapInverse.get(clazz));
|
||||
}
|
||||
|
||||
private static void setConstructorHashMap(Class[] tdApiClasses) {
|
||||
constructorHashMap = new ConcurrentHashMap<>();
|
||||
constructorHashMapInverse = new ConcurrentHashMap<>();
|
||||
|
||||
for (Class apiClass : tdApiClasses) {
|
||||
Field CONSTRUCTORField;
|
||||
@ -78,14 +93,7 @@ public class ConstructorDetector {
|
||||
}
|
||||
|
||||
constructorHashMap.put(CONSTRUCTOR, apiClass);
|
||||
constructorHashMapInverse.put(apiClass, CONSTRUCTOR);
|
||||
}
|
||||
}
|
||||
|
||||
public static ConcurrentHashMap<Integer, Class> getTDConstructorsUnsafe() {
|
||||
return constructorHashMap;
|
||||
}
|
||||
|
||||
public static void registerExternalClass(int constructor, Class<?> clazz) {
|
||||
constructorHashMap.put(constructor, clazz);
|
||||
}
|
||||
}
|
||||
|
@ -3,5 +3,6 @@ package it.tdlight.common;
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
|
||||
public interface EventsHandler {
|
||||
|
||||
void handleClientEvents(int clientId, boolean isClosed, long[] clientEventIds, Object[] clientEvents);
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
package it.tdlight.common;
|
||||
|
||||
/**
|
||||
* Interface for handler of exceptions thrown while invoking ResultHandler.
|
||||
* By default, all such exceptions are ignored.
|
||||
* Interface for handler of exceptions thrown while invoking ResultHandler. By default, all such exceptions are ignored.
|
||||
* All exceptions thrown from ExceptionHandler are ignored.
|
||||
*/
|
||||
public interface ExceptionHandler {
|
||||
|
@ -24,7 +24,7 @@ import it.tdlight.common.utils.Os;
|
||||
/**
|
||||
* Init class to successfully initialize Tdlib
|
||||
*/
|
||||
public class Init {
|
||||
public final class Init {
|
||||
|
||||
private static boolean started = false;
|
||||
|
||||
|
@ -5,10 +5,9 @@ import it.tdlight.jni.TdApi;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Class used for managing internal TDLib logging.
|
||||
* Use TdApi.*Log* methods instead.
|
||||
* Class used for managing internal TDLib logging. Use TdApi.*Log* methods instead.
|
||||
*/
|
||||
public class Log {
|
||||
public final class Log {
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -21,13 +20,13 @@ public class Log {
|
||||
|
||||
|
||||
/**
|
||||
* Sets file path for writing TDLib internal log. By default TDLib writes logs to the System.err.
|
||||
* Use this method to write the log to a file instead.
|
||||
* Sets file path for writing TDLib internal log. By default TDLib writes logs to the System.err. Use this method to
|
||||
* write the log to a file instead.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param filePath Path to a file for writing TDLib internal log. Use an empty path to
|
||||
* switch back to logging to the System.err.
|
||||
* @param filePath Path to a file for writing TDLib internal log. Use an empty path to switch back to logging to the
|
||||
* System.err.
|
||||
* @return whether opening the log file succeeded.
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
*/
|
||||
@Deprecated
|
||||
public static synchronized boolean setFilePath(String filePath) {
|
||||
@ -37,9 +36,9 @@ public class Log {
|
||||
/**
|
||||
* Changes the maximum size of TDLib log file.
|
||||
*
|
||||
* @param maxFileSize The maximum size of the file to where the internal TDLib log is written before the file will be
|
||||
* auto-rotated. Must be positive. Defaults to 10 MB.
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param maxFileSize The maximum size of the file to where the internal TDLib log is written
|
||||
* before the file will be auto-rotated. Must be positive. Defaults to 10 MB.
|
||||
*/
|
||||
@Deprecated
|
||||
public static synchronized void setMaxFileSize(long maxFileSize) {
|
||||
@ -49,16 +48,13 @@ public class Log {
|
||||
/**
|
||||
* Changes TDLib log verbosity.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogVerbosityLevel}, to be removed in the future.
|
||||
* @param verbosityLevel New value of log verbosity level. Must be non-negative.
|
||||
* Value 0 corresponds to fatal errors,
|
||||
* value 1 corresponds to java.util.logging.Level.SEVERE,
|
||||
* value 2 corresponds to java.util.logging.Level.WARNING,
|
||||
* value 3 corresponds to java.util.logging.Level.INFO,
|
||||
* value 4 corresponds to java.util.logging.Level.FINE,
|
||||
* value 5 corresponds to java.util.logging.Level.FINER,
|
||||
* value greater than 5 can be used to enable even more logging.
|
||||
* @param verbosityLevel New value of log verbosity level. Must be non-negative. Value 0 corresponds to fatal errors,
|
||||
* value 1 corresponds to java.util.logging.Level.SEVERE, value 2 corresponds to
|
||||
* java.util.logging.Level.WARNING, value 3 corresponds to java.util.logging.Level.INFO, value 4
|
||||
* corresponds to java.util.logging.Level.FINE, value 5 corresponds to
|
||||
* java.util.logging.Level.FINER, value greater than 5 can be used to enable even more logging.
|
||||
* Default value of the log verbosity level is 5.
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogVerbosityLevel}, to be removed in the future.
|
||||
*/
|
||||
@Deprecated
|
||||
public static synchronized void setVerbosityLevel(int verbosityLevel) {
|
||||
@ -66,8 +62,11 @@ public class Log {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callback that will be called when a fatal error happens. None of the TDLib methods can be called from the callback. The TDLib will crash as soon as callback returns. By default the callback set to print in stderr.
|
||||
* @param fatalErrorCallback Callback that will be called when a fatal error happens. Pass null to restore default callback.
|
||||
* Sets the callback that will be called when a fatal error happens. None of the TDLib methods can be called from the
|
||||
* callback. The TDLib will crash as soon as callback returns. By default the callback set to print in stderr.
|
||||
*
|
||||
* @param fatalErrorCallback Callback that will be called when a fatal error happens. Pass null to restore default
|
||||
* callback.
|
||||
*/
|
||||
public static synchronized void setFatalErrorCallback(Consumer<String> fatalErrorCallback) {
|
||||
NativeLog.setFatalErrorCallback(fatalErrorCallback);
|
||||
|
@ -3,7 +3,7 @@ package it.tdlight.common;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ReactiveItem {
|
||||
public final class ReactiveItem {
|
||||
|
||||
private final TdApi.Object item;
|
||||
private final Throwable ex;
|
||||
|
@ -15,8 +15,8 @@ public interface ReactiveTelegramClient extends Publisher<ReactiveItem> {
|
||||
* Sends a request to the TDLib.
|
||||
*
|
||||
* @param query Object representing a query to the TDLib.
|
||||
* @throws NullPointerException if query is null.
|
||||
* @return a publisher that will emit exactly one item, or an error
|
||||
* @throws NullPointerException if query is null.
|
||||
*/
|
||||
Publisher<TdApi.Object> send(TdApi.Function query);
|
||||
|
||||
|
@ -17,21 +17,24 @@
|
||||
|
||||
package it.tdlight.common;
|
||||
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* A response to a request, or an incoming update from TDLib.
|
||||
*/
|
||||
public class Response {
|
||||
private long id;
|
||||
private Object object;
|
||||
@SuppressWarnings("unused")
|
||||
public final class Response {
|
||||
private final long id;
|
||||
private final TdApi.Object object;
|
||||
|
||||
/**
|
||||
* Creates a response with eventId and object, do not create answers explicitly! you must receive the reply through a client.
|
||||
* @param id TDLib request identifier, which corresponds to the response or 0 for incoming updates from TDLib.
|
||||
* @param object TDLib API object representing a response to a TDLib request or an incoming update.
|
||||
*/
|
||||
public Response(long id, Object object) {
|
||||
public Response(long id, TdApi.Object object) {
|
||||
this.id = id;
|
||||
this.object = object;
|
||||
}
|
||||
@ -48,7 +51,29 @@ public class Response {
|
||||
* Get TDLib API object.
|
||||
* @return TDLib API object representing a response to a TDLib request or an incoming update.
|
||||
*/
|
||||
public Object getObject() {
|
||||
public TdApi.Object getObject() {
|
||||
return this.object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Response response = (Response) o;
|
||||
return id == response.id && Objects.equals(object, response.object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", Response.class.getSimpleName() + "[", "]").add("object=" + object).toString();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ public interface TelegramClient {
|
||||
|
||||
/**
|
||||
* Initialize the client synchronously.
|
||||
*
|
||||
* @param updatesHandler Handler in which the updates are received
|
||||
* @param updateExceptionHandler Handler in which the errors from updates are received
|
||||
* @param defaultExceptionHandler Handler that receives exceptions triggered in a handler
|
||||
@ -16,6 +17,7 @@ public interface TelegramClient {
|
||||
|
||||
/**
|
||||
* Initialize the client synchronously.
|
||||
*
|
||||
* @param updateHandler Handler in which the updates are received
|
||||
* @param updateExceptionHandler Handler in which the errors from updates are received
|
||||
* @param defaultExceptionHandler Handler that receives exceptions triggered in a handler
|
||||
@ -33,12 +35,10 @@ public interface TelegramClient {
|
||||
* Sends a request to the TDLib.
|
||||
*
|
||||
* @param query Object representing a query to the TDLib.
|
||||
* @param resultHandler Result handler with onResult method which will be called with result
|
||||
* of the query or with TdApi.Error as parameter. If it is null, nothing
|
||||
* will be called.
|
||||
* @param exceptionHandler Exception handler with onException method which will be called on
|
||||
* exception thrown from resultHandler. If it is null, then
|
||||
* defaultExceptionHandler will be called.
|
||||
* @param resultHandler Result handler with onResult method which will be called with result of the query or with
|
||||
* TdApi.Error as parameter. If it is null, nothing will be called.
|
||||
* @param exceptionHandler Exception handler with onException method which will be called on exception thrown from
|
||||
* resultHandler. If it is null, then defaultExceptionHandler will be called.
|
||||
* @throws NullPointerException if query is null.
|
||||
*/
|
||||
void send(TdApi.Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler);
|
||||
@ -47,9 +47,8 @@ public interface TelegramClient {
|
||||
* Sends a request to the TDLib with an empty ExceptionHandler.
|
||||
*
|
||||
* @param query Object representing a query to the TDLib.
|
||||
* @param resultHandler Result handler with onResult method which will be called with result
|
||||
* of the query or with TdApi.Error as parameter. If it is null, then
|
||||
* defaultExceptionHandler will be called.
|
||||
* @param resultHandler Result handler with onResult method which will be called with result of the query or with
|
||||
* TdApi.Error as parameter. If it is null, then defaultExceptionHandler will be called.
|
||||
* @throws NullPointerException if query is null.
|
||||
*/
|
||||
default void send(TdApi.Function query, ResultHandler resultHandler) {
|
||||
|
@ -11,7 +11,8 @@ public interface UpdatesHandler {
|
||||
/**
|
||||
* Callback called on incoming update from TDLib.
|
||||
*
|
||||
* @param object Updates of type {@link it.tdlight.jni.TdApi.Update} about new events, or {@link it.tdlight.jni.TdApi.Error}.
|
||||
* @param object Updates of type {@link it.tdlight.jni.TdApi.Update} about new events, or {@link
|
||||
* it.tdlight.jni.TdApi.Error}.
|
||||
*/
|
||||
void onUpdates(List<Object> object);
|
||||
}
|
@ -6,16 +6,16 @@ import it.tdlight.common.TelegramClient;
|
||||
public abstract class CommonClientManager {
|
||||
|
||||
private static InternalClientManager getClientManager(String implementationName) {
|
||||
// ClientManager is singleton:
|
||||
// ClientManager is singleton
|
||||
return InternalClientManager.get(implementationName);
|
||||
}
|
||||
|
||||
protected synchronized static TelegramClient create(String implementationName) {
|
||||
public synchronized static TelegramClient create(String implementationName) {
|
||||
InternalClient client = new InternalClient(getClientManager(implementationName));
|
||||
return create(client);
|
||||
}
|
||||
|
||||
protected synchronized static ReactiveTelegramClient createReactive(String implementationName) {
|
||||
public synchronized static ReactiveTelegramClient createReactive(String implementationName) {
|
||||
InternalReactiveClient reactiveClient = new InternalReactiveClient(getClientManager(implementationName));
|
||||
return createReactive(reactiveClient);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package it.tdlight.common.internal;
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.ResultHandler;
|
||||
|
||||
public class Handler {
|
||||
public final class Handler {
|
||||
private final ResultHandler resultHandler;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
|
@ -17,7 +17,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
public final class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TelegramClient.class);
|
||||
private final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<Long, Handler>();
|
||||
|
@ -15,7 +15,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class InternalClientManager implements AutoCloseable {
|
||||
public final class InternalClientManager implements AutoCloseable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(InternalClientManager.class);
|
||||
private static final AtomicReference<InternalClientManager> INSTANCE = new AtomicReference<>(null);
|
||||
@ -45,14 +45,14 @@ public class InternalClientManager implements AutoCloseable {
|
||||
if (handler != null) {
|
||||
handler.handleEvents(isClosed, clientEventIds, clientEvents);
|
||||
} else {
|
||||
java.util.List<Entry<Long, TdApi.Object>> droppedEvents = getEffectivelyDroppedEvents(clientEventIds, clientEvents);
|
||||
java.util.List<DroppedEvent> droppedEvents = getEffectivelyDroppedEvents(clientEventIds, clientEvents);
|
||||
|
||||
if (!droppedEvents.isEmpty()) {
|
||||
logger.error("Unknown client id \"{}\"! {} events have been dropped!", clientId, droppedEvents.size());
|
||||
for (Entry<Long, Object> droppedEvent : droppedEvents) {
|
||||
for (DroppedEvent droppedEvent : droppedEvents) {
|
||||
logger.error("The following event, with id \"{}\", has been dropped: {}",
|
||||
droppedEvent.getKey(),
|
||||
droppedEvent.getValue());
|
||||
droppedEvent.id,
|
||||
droppedEvent.event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,8 +67,8 @@ public class InternalClientManager implements AutoCloseable {
|
||||
/**
|
||||
* Get only events that have been dropped, ignoring synthetic errors related to the closure of a client
|
||||
*/
|
||||
private List<Entry<Long, TdApi.Object>> getEffectivelyDroppedEvents(long[] clientEventIds, TdApi.Object[] clientEvents) {
|
||||
java.util.List<Entry<Long, TdApi.Object>> droppedEvents = new ArrayList<>(clientEvents.length);
|
||||
private List<DroppedEvent> getEffectivelyDroppedEvents(long[] clientEventIds, TdApi.Object[] clientEvents) {
|
||||
java.util.List<DroppedEvent> droppedEvents = new ArrayList<>(clientEvents.length);
|
||||
for (int i = 0; i < clientEvents.length; i++) {
|
||||
long id = clientEventIds[i];
|
||||
TdApi.Object event = clientEvents[i];
|
||||
@ -80,7 +80,7 @@ public class InternalClientManager implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
if (mustPrintError) {
|
||||
droppedEvents.add(Map.entry(id, event));
|
||||
droppedEvents.add(new DroppedEvent(id, event));
|
||||
}
|
||||
}
|
||||
return droppedEvents;
|
||||
@ -106,4 +106,14 @@ public class InternalClientManager implements AutoCloseable {
|
||||
public void close() throws InterruptedException {
|
||||
responseReceiver.close();
|
||||
}
|
||||
|
||||
private static final class DroppedEvent {
|
||||
private final long id;
|
||||
private final TdApi.Object event;
|
||||
|
||||
private DroppedEvent(long id, Object event) {
|
||||
this.id = id;
|
||||
this.event = event;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import it.tdlight.common.ReactiveTelegramClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Error;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
import it.tdlight.jni.TdApi.Object;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
@ -20,9 +19,8 @@ import org.reactivestreams.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class InternalReactiveClient implements ClientEventsHandler, ReactiveTelegramClient {
|
||||
public final class InternalReactiveClient implements ClientEventsHandler, ReactiveTelegramClient {
|
||||
|
||||
private static boolean ENABLE_BACKPRESSURE_QUEUE = false;
|
||||
private static final Logger logger = LoggerFactory.getLogger(InternalReactiveClient.class);
|
||||
private final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<Long, Handler>();
|
||||
private final ConcurrentLinkedQueue<ReactiveItem> backpressureQueue = new ConcurrentLinkedQueue<>();
|
||||
@ -66,7 +64,7 @@ public class InternalReactiveClient implements ClientEventsHandler, ReactiveTele
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvents(boolean isClosed, long[] eventIds, Object[] events) {
|
||||
public void handleEvents(boolean isClosed, long[] eventIds, TdApi.Object[] events) {
|
||||
for (int i = 0; i < eventIds.length; i++) {
|
||||
handleEvent(eventIds[i], events[i]);
|
||||
}
|
||||
@ -88,7 +86,7 @@ public class InternalReactiveClient implements ClientEventsHandler, ReactiveTele
|
||||
/**
|
||||
* Handles only a response (not an update!)
|
||||
*/
|
||||
private void handleResponse(long eventId, Object event, Handler handler) {
|
||||
private void handleResponse(long eventId, TdApi.Object event, Handler handler) {
|
||||
if (handler != null) {
|
||||
try {
|
||||
if (eventId == 0) {
|
||||
@ -108,7 +106,7 @@ public class InternalReactiveClient implements ClientEventsHandler, ReactiveTele
|
||||
/**
|
||||
* Handles a response or an update
|
||||
*/
|
||||
private void handleEvent(long eventId, Object event) {
|
||||
private void handleEvent(long eventId, TdApi.Object event) {
|
||||
Handler handler = eventId == 0 ? updateHandler : handlers.remove(eventId);
|
||||
handleResponse(eventId, event, handler);
|
||||
}
|
||||
@ -152,14 +150,14 @@ public class InternalReactiveClient implements ClientEventsHandler, ReactiveTele
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (!isClosed.get()) {
|
||||
send(new TdApi.Close()).subscribe(new Subscriber<>() {
|
||||
send(new TdApi.Close()).subscribe(new Subscriber<TdApi.Object>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription subscription) {
|
||||
subscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object item) {
|
||||
public void onNext(TdApi.Object o) {
|
||||
|
||||
}
|
||||
|
||||
@ -195,14 +193,14 @@ public class InternalReactiveClient implements ClientEventsHandler, ReactiveTele
|
||||
CountDownLatch registeredClient = new CountDownLatch(1);
|
||||
|
||||
// Send a dummy request because @levlam is too lazy to fix race conditions in a better way
|
||||
this.send(new TdApi.GetAuthorizationState()).subscribe(new Subscriber<>() {
|
||||
this.send(new TdApi.GetAuthorizationState()).subscribe(new Subscriber<TdApi.Object>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription subscription) {
|
||||
subscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object item) {
|
||||
public void onNext(TdApi.Object item) {
|
||||
registeredClient.countDown();
|
||||
}
|
||||
|
||||
@ -278,7 +276,7 @@ public class InternalReactiveClient implements ClientEventsHandler, ReactiveTele
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Function query) {
|
||||
public TdApi.Object execute(Function query) {
|
||||
if (isClosedAndMaybeThrow(query)) {
|
||||
return new TdApi.Ok();
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package it.tdlight.common.internal;
|
||||
import it.tdlight.common.ExceptionHandler;
|
||||
import it.tdlight.common.UpdatesHandler;
|
||||
|
||||
public class MultiHandler {
|
||||
public final class MultiHandler {
|
||||
private final UpdatesHandler updatesHandler;
|
||||
private final ExceptionHandler exceptionHandler;
|
||||
|
||||
|
@ -4,7 +4,7 @@ import it.tdlight.tdnative.NativeClient;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.Function;
|
||||
|
||||
class NativeClientAccess extends NativeClient {
|
||||
final class NativeClientAccess extends NativeClient {
|
||||
|
||||
public static int create() {
|
||||
return NativeClientAccess.createNativeClient();
|
||||
|
@ -14,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
public final class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
|
||||
private static final boolean USE_OPTIMIZED_DISPATCHER = Boolean.parseBoolean(System.getProperty(
|
||||
"tdlight.dispatcher.use_optimized_dispatcher",
|
||||
|
@ -18,14 +18,8 @@
|
||||
package it.tdlight.common.utils;
|
||||
|
||||
/**
|
||||
* Enumeration with all architectures recognized by this library.
|
||||
* Architectures recognized by this library.
|
||||
*/
|
||||
public enum Arch {
|
||||
UNKNOWN,
|
||||
AMD64,
|
||||
I386,
|
||||
ARMHF,
|
||||
AARCH64,
|
||||
PPC64LE,
|
||||
S390X
|
||||
UNKNOWN, AMD64, I386, ARMHF, AARCH64, PPC64LE, S390X
|
||||
}
|
||||
|
@ -20,11 +20,11 @@ package it.tdlight.common.utils;
|
||||
/**
|
||||
* An exception that is thrown when the LoadLibrary class fails to load the library.
|
||||
*/
|
||||
public class CantLoadLibrary extends Exception {
|
||||
public final class CantLoadLibrary extends Exception {
|
||||
/**
|
||||
* Creates a new CantLoadLibrary exception.
|
||||
*/
|
||||
CantLoadLibrary() {
|
||||
super("FATAL: Init failled when load tdlib library, execution can't continue");
|
||||
super("FATAL: Init failed when loading TDLib native libraries, execution can't continue");
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018. Ernesto Castellotti <erny.castell@gmail.com>
|
||||
* This file is part of JTdlib.
|
||||
*
|
||||
* JTdlib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* JTdlib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.tdlight.common.utils;
|
||||
|
||||
/**
|
||||
* Interface of callback for receive notification of closing Tdlib.
|
||||
*/
|
||||
public interface CloseCallback {
|
||||
/**
|
||||
* This method is called when tdlib is closing
|
||||
*/
|
||||
void onClosed();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018. Ernesto Castellotti <erny.castell@gmail.com>
|
||||
* This file is part of JTdlib.
|
||||
*
|
||||
* JTdlib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* JTdlib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.tdlight.common.utils;
|
||||
|
||||
import it.tdlight.common.Response;
|
||||
|
||||
/**
|
||||
* Interface of callback for receive incoming error response.
|
||||
*/
|
||||
public interface ErrorCallback {
|
||||
/**
|
||||
* This method is called when the library receives error responses
|
||||
* @param error The incoming error response.
|
||||
*/
|
||||
void onError(Response error);
|
||||
}
|
@ -2,7 +2,7 @@ package it.tdlight.common.utils;
|
||||
|
||||
import it.unimi.dsi.fastutil.Swapper;
|
||||
|
||||
public class IntSwapper implements Swapper {
|
||||
public final class IntSwapper implements Swapper {
|
||||
|
||||
private final int[] array;
|
||||
int tmp;
|
||||
|
@ -23,18 +23,19 @@ import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import it.tdlight.tdnative.ObjectsUtils;
|
||||
|
||||
/**
|
||||
* The class to load the libraries needed to run Tdlib
|
||||
*/
|
||||
public class LoadLibrary {
|
||||
public final class LoadLibrary {
|
||||
|
||||
private static final ConcurrentHashMap<String, Boolean> libraryLoaded = new ConcurrentHashMap<>();
|
||||
private static final Path librariesPath = Paths.get(".cache");
|
||||
private static final String libsVersion = LibraryVersion.IMPLEMENTATION_NAME
|
||||
+ "-" + LibraryVersion.VERSION + "-" + LibraryVersion.NATIVES_VERSION;
|
||||
private static final String libsVersion =
|
||||
LibraryVersion.IMPLEMENTATION_NAME + "-" + LibraryVersion.VERSION + "-" + LibraryVersion.NATIVES_VERSION;
|
||||
|
||||
static {
|
||||
if (Files.notExists(librariesPath)) {
|
||||
@ -72,11 +73,13 @@ public class LoadLibrary {
|
||||
Os os = getOs();
|
||||
|
||||
if (arch == Arch.UNKNOWN) {
|
||||
throw (CantLoadLibrary) new CantLoadLibrary().initCause(new IllegalStateException("Arch: \"" + System.getProperty("os.arch") + "\" is unknown"));
|
||||
throw (CantLoadLibrary) new CantLoadLibrary().initCause(new IllegalStateException(
|
||||
"Arch: \"" + System.getProperty("os.arch") + "\" is unknown"));
|
||||
}
|
||||
|
||||
if (os == Os.UNKNOWN) {
|
||||
throw (CantLoadLibrary) new CantLoadLibrary().initCause(new IllegalStateException("Os: \"" + System.getProperty("os.name") + "\" is unknown"));
|
||||
throw (CantLoadLibrary) new CantLoadLibrary().initCause(new IllegalStateException(
|
||||
"Os: \"" + System.getProperty("os.name") + "\" is unknown"));
|
||||
}
|
||||
|
||||
try {
|
||||
@ -178,7 +181,9 @@ public class LoadLibrary {
|
||||
}
|
||||
InputStream libInputStream;
|
||||
try {
|
||||
libInputStream = ObjectsUtils.requireNonNull((InputStream) classForResource.getDeclaredMethod("getLibraryAsStream").invoke(InputStream.class));
|
||||
libInputStream = Objects.requireNonNull((InputStream) classForResource
|
||||
.getDeclaredMethod("getLibraryAsStream")
|
||||
.invoke(InputStream.class));
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | NullPointerException e) {
|
||||
throw new IOException("Native libraries for platform " + os + "-" + arch + " not found!", e);
|
||||
}
|
||||
@ -222,10 +227,14 @@ public class LoadLibrary {
|
||||
case "ppc64":
|
||||
case "ppc64le":
|
||||
case "ppc64el":
|
||||
if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) // Java always returns ppc64 for all 64-bit powerpc but
|
||||
if (ByteOrder
|
||||
.nativeOrder()
|
||||
.equals(ByteOrder.LITTLE_ENDIAN)) // Java always returns ppc64 for all 64-bit powerpc but
|
||||
{
|
||||
return Arch.PPC64LE; // powerpc64le (our target) is very different, it uses this condition to accurately identify the architecture
|
||||
else
|
||||
} else {
|
||||
return Arch.UNKNOWN;
|
||||
}
|
||||
default:
|
||||
return Arch.UNKNOWN;
|
||||
}
|
||||
@ -233,14 +242,18 @@ public class LoadLibrary {
|
||||
|
||||
public static Os getOs() {
|
||||
String os = System.getProperty("os.name").toLowerCase().trim();
|
||||
if (os.contains("linux"))
|
||||
if (os.contains("linux")) {
|
||||
return Os.LINUX;
|
||||
if (os.contains("windows"))
|
||||
}
|
||||
if (os.contains("windows")) {
|
||||
return Os.WINDOWS;
|
||||
if (os.contains("mac"))
|
||||
}
|
||||
if (os.contains("mac")) {
|
||||
return Os.OSX;
|
||||
if (os.contains("darwin"))
|
||||
}
|
||||
if (os.contains("darwin")) {
|
||||
return Os.OSX;
|
||||
}
|
||||
return Os.UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018. Ernesto Castellotti <erny.castell@gmail.com>
|
||||
* This file is part of JTdlib.
|
||||
*
|
||||
* JTdlib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* JTdlib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.tdlight.common.utils;
|
||||
|
||||
import it.tdlight.common.Response;
|
||||
|
||||
/**
|
||||
* Interface of callback for receive incoming error response.
|
||||
*/
|
||||
public interface NativeErrorCallback {
|
||||
/**
|
||||
* This method is called when the library receives error responses
|
||||
* @param error The incoming error response.
|
||||
*/
|
||||
void onNativeError(Response error);
|
||||
}
|
@ -21,8 +21,5 @@ package it.tdlight.common.utils;
|
||||
* Enumeration with all operating systems recognized by this library.
|
||||
*/
|
||||
public enum Os {
|
||||
LINUX,
|
||||
WINDOWS,
|
||||
OSX,
|
||||
UNKNOWN
|
||||
LINUX, WINDOWS, OSX, UNKNOWN
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018. Ernesto Castellotti <erny.castell@gmail.com>
|
||||
* This file is part of JTdlib.
|
||||
*
|
||||
* JTdlib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* JTdlib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package it.tdlight.common.utils;
|
||||
|
||||
import it.tdlight.common.Response;
|
||||
|
||||
/**
|
||||
* Interface of callback for receive incoming update or request response.
|
||||
*/
|
||||
public interface ReceiveCallback {
|
||||
/**
|
||||
* This method is called when the library receives update or request response.
|
||||
* @param response The incoming update or request response.
|
||||
*/
|
||||
void onResult(Response response);
|
||||
}
|
@ -2,20 +2,16 @@ package it.tdlight.common.utils;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
public class ScannerUtils {
|
||||
public final class ScannerUtils {
|
||||
|
||||
private static final Scanner scanner;
|
||||
private static final Object lock = new Object();
|
||||
|
||||
static {
|
||||
synchronized (lock) {
|
||||
scanner = new Scanner(System.in);
|
||||
}
|
||||
}
|
||||
|
||||
public static String askParameter(String displayName, String question) {
|
||||
synchronized (lock) {
|
||||
System.out.print("[" + displayName + "] " + question + ": ");
|
||||
return scanner.nextLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,31 +5,10 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
public class SpinWaitSupport {
|
||||
private static final MethodHandle onSpinWaitHandle;
|
||||
|
||||
static {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
MethodHandle handle;
|
||||
try {
|
||||
handle = lookup.findStatic(java.lang.Thread.class, "onSpinWait", MethodType.methodType(void.class));
|
||||
} catch (Exception e) {
|
||||
handle = null;
|
||||
}
|
||||
|
||||
onSpinWaitHandle = handle;
|
||||
}
|
||||
|
||||
private SpinWaitSupport() {
|
||||
}
|
||||
|
||||
public static void onSpinWait() {
|
||||
if (onSpinWaitHandle != null) {
|
||||
try {
|
||||
onSpinWaitHandle.invokeExact();
|
||||
} catch (Throwable throwable) {
|
||||
// This can't happen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ public class NativeClient {
|
||||
|
||||
protected static native void nativeClientSend(int nativeClientId, long eventId, TdApi.Function function);
|
||||
|
||||
protected static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout);
|
||||
protected static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events,
|
||||
double timeout);
|
||||
|
||||
protected static native TdApi.Object nativeClientExecute(TdApi.Function function);
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package it.tdlight.tdnative;
|
||||
|
||||
import it.tdlight.jni.TdApi;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Class used for managing internal TDLib logging.
|
||||
* Use TdApi.*Log* methods instead.
|
||||
* Class used for managing internal TDLib logging. Use TdApi.*Log* methods instead.
|
||||
*/
|
||||
public class NativeLog {
|
||||
|
||||
@ -13,13 +13,13 @@ public class NativeLog {
|
||||
private static Consumer<String> fatalErrorCallback = defaultFatalErrorCallbackPtr;
|
||||
|
||||
/**
|
||||
* Sets file path for writing TDLib internal log. By default TDLib writes logs to the System.err.
|
||||
* Use this method to write the log to a file instead.
|
||||
* Sets file path for writing TDLib internal log. By default TDLib writes logs to the System.err. Use this method to
|
||||
* write the log to a file instead.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param filePath Path to a file for writing TDLib internal log. Use an empty path to
|
||||
* switch back to logging to the System.err.
|
||||
* @param filePath Path to a file for writing TDLib internal log. Use an empty path to switch back to logging to the
|
||||
* System.err.
|
||||
* @return whether opening the log file succeeded.
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native boolean setFilePath(String filePath);
|
||||
@ -27,9 +27,9 @@ public class NativeLog {
|
||||
/**
|
||||
* Changes the maximum size of TDLib log file.
|
||||
*
|
||||
* @param maxFileSize The maximum size of the file to where the internal TDLib log is written before the file will be
|
||||
* auto-rotated. Must be positive. Defaults to 10 MB.
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param maxFileSize The maximum size of the file to where the internal TDLib log is written
|
||||
* before the file will be auto-rotated. Must be positive. Defaults to 10 MB.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native void setMaxFileSize(long maxFileSize);
|
||||
@ -37,23 +37,20 @@ public class NativeLog {
|
||||
/**
|
||||
* Changes TDLib log verbosity.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogVerbosityLevel}, to be removed in the future.
|
||||
* @param verbosityLevel New value of log verbosity level. Must be non-negative.
|
||||
* Value 0 corresponds to fatal errors,
|
||||
* value 1 corresponds to java.util.logging.Level.SEVERE,
|
||||
* value 2 corresponds to java.util.logging.Level.WARNING,
|
||||
* value 3 corresponds to java.util.logging.Level.INFO,
|
||||
* value 4 corresponds to java.util.logging.Level.FINE,
|
||||
* value 5 corresponds to java.util.logging.Level.FINER,
|
||||
* value greater than 5 can be used to enable even more logging.
|
||||
* @param verbosityLevel New value of log verbosity level. Must be non-negative. Value 0 corresponds to fatal errors,
|
||||
* value 1 corresponds to java.util.logging.Level.SEVERE, value 2 corresponds to
|
||||
* java.util.logging.Level.WARNING, value 3 corresponds to java.util.logging.Level.INFO, value 4
|
||||
* corresponds to java.util.logging.Level.FINE, value 5 corresponds to
|
||||
* java.util.logging.Level.FINER, value greater than 5 can be used to enable even more logging.
|
||||
* Default value of the log verbosity level is 5.
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogVerbosityLevel}, to be removed in the future.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native void setVerbosityLevel(int verbosityLevel);
|
||||
|
||||
/**
|
||||
* This function is called from the JNI when a fatal error happens to provide a better error message.
|
||||
* The function does not return.
|
||||
* This function is called from the JNI when a fatal error happens to provide a better error message. The function
|
||||
* does not return.
|
||||
*
|
||||
* @param errorMessage Error message.
|
||||
*/
|
||||
@ -62,10 +59,17 @@ public class NativeLog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callback that will be called when a fatal error happens. None of the TDLib methods can be called from the callback. The TDLib will crash as soon as callback returns. By default the callback set to print in stderr.
|
||||
* @param fatalErrorCallback Callback that will be called when a fatal error happens. Pass null to restore default callback.
|
||||
* Sets the callback that will be called when a fatal error happens. None of the TDLib methods can be called from the
|
||||
* callback. The TDLib will crash as soon as callback returns. By default the callback set to print in stderr.
|
||||
*
|
||||
* @param fatalErrorCallback Callback that will be called when a fatal error happens. Pass null to restore default
|
||||
* callback.
|
||||
*/
|
||||
public static synchronized void setFatalErrorCallback(Consumer<String> fatalErrorCallback) {
|
||||
NativeLog.fatalErrorCallback = ObjectsUtils.requireNonNullElse(fatalErrorCallback, defaultFatalErrorCallbackPtr);
|
||||
if (fatalErrorCallback == null) {
|
||||
NativeLog.fatalErrorCallback = defaultFatalErrorCallbackPtr;
|
||||
} else {
|
||||
NativeLog.fatalErrorCallback = fatalErrorCallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
package it.tdlight.tdnative;
|
||||
|
||||
public class ObjectsUtils {
|
||||
/**
|
||||
* Returns the first argument if it is non-{@code null} and
|
||||
* otherwise returns the non-{@code null} second argument.
|
||||
*
|
||||
* @param obj an object
|
||||
* @param defaultObj a non-{@code null} object to return if the first argument
|
||||
* is {@code null}
|
||||
* @param <T> the type of the reference
|
||||
* @return the first argument if it is non-{@code null} and
|
||||
* otherwise the second argument if it is non-{@code null}
|
||||
* @throws NullPointerException if both {@code obj} is null and
|
||||
* {@code defaultObj} is {@code null}
|
||||
* @since 9
|
||||
*/
|
||||
public static <T> T requireNonNullElse(T obj, T defaultObj) {
|
||||
return (obj != null) ? obj : requireNonNull(defaultObj, "defaultObj");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified object reference is not {@code null}. This
|
||||
* method is designed primarily for doing parameter validation in methods
|
||||
* and constructors, as demonstrated below:
|
||||
* <blockquote><pre>
|
||||
* public Foo(Bar bar) {
|
||||
* this.bar = Objects.requireNonNull(bar);
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param obj the object reference to check for nullity
|
||||
* @param <T> the type of the reference
|
||||
* @return {@code obj} if not {@code null}
|
||||
* @throws NullPointerException if {@code obj} is {@code null}
|
||||
*/
|
||||
public static <T> T requireNonNull(T obj) {
|
||||
if (obj == null)
|
||||
throw new NullPointerException();
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified object reference is not {@code null} and
|
||||
* throws a customized {@link NullPointerException} if it is. This method
|
||||
* is designed primarily for doing parameter validation in methods and
|
||||
* constructors with multiple parameters, as demonstrated below:
|
||||
* <blockquote><pre>
|
||||
* public Foo(Bar bar, Baz baz) {
|
||||
* this.bar = Objects.requireNonNull(bar, "bar must not be null");
|
||||
* this.baz = Objects.requireNonNull(baz, "baz must not be null");
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param obj the object reference to check for nullity
|
||||
* @param message detail message to be used in the event that a {@code
|
||||
* NullPointerException} is thrown
|
||||
* @param <T> the type of the reference
|
||||
* @return {@code obj} if not {@code null}
|
||||
* @throws NullPointerException if {@code obj} is {@code null}
|
||||
*/
|
||||
public static <T> T requireNonNull(T obj, String message) {
|
||||
if (obj == null)
|
||||
throw new NullPointerException(message);
|
||||
return obj;
|
||||
}
|
||||
}
|
11
src/main/java11/it/tdlight/common/utils/SpinWaitSupport.java
Normal file
11
src/main/java11/it/tdlight/common/utils/SpinWaitSupport.java
Normal file
@ -0,0 +1,11 @@
|
||||
package it.tdlight.common.utils;
|
||||
|
||||
public class SpinWaitSupport {
|
||||
|
||||
private SpinWaitSupport() {
|
||||
}
|
||||
|
||||
public static void onSpinWait() {
|
||||
java.lang.Thread.onSpinWait();
|
||||
}
|
||||
}
|
@ -8,8 +8,8 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<revision>1.0.0-SNAPSHOT</revision>
|
||||
<api-version>3.3.127</api-version>
|
||||
<natives-version>3.3.129</natives-version>
|
||||
<api-version>3.3.147</api-version>
|
||||
<natives-version>3.3.149</natives-version>
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
@ -65,6 +65,10 @@
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
<version>1.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.harawata</groupId>
|
||||
<artifactId>appdirs</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@ -73,6 +77,11 @@
|
||||
<artifactId>fastutil</artifactId>
|
||||
<version>8.5.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.harawata</groupId>
|
||||
<artifactId>appdirs</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<profiles>
|
||||
|
@ -8,8 +8,8 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<revision>1.0.0-SNAPSHOT</revision>
|
||||
<api-version>3.3.127</api-version>
|
||||
<natives-version>3.3.129</natives-version>
|
||||
<api-version>3.3.147</api-version>
|
||||
<natives-version>3.3.149</natives-version>
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
@ -65,6 +65,10 @@
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
<version>1.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.harawata</groupId>
|
||||
<artifactId>appdirs</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@ -73,6 +77,11 @@
|
||||
<artifactId>fastutil</artifactId>
|
||||
<version>8.5.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.harawata</groupId>
|
||||
<artifactId>appdirs</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<profiles>
|
||||
@ -90,7 +99,7 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
<build>
|
||||
<sourceDirectory>../src/main/java</sourceDirectory>
|
||||
<sourceDirectory>${project.basedir}/../src/main/java</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
@ -116,8 +125,13 @@
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<source>17</source>
|
||||
<additionalOptions>-html5</additionalOptions>
|
||||
<level>public</level>
|
||||
<verbose>false</verbose>
|
||||
<quiet>true</quiet>
|
||||
<additionalOptions>-Xdoclint:none</additionalOptions>
|
||||
<additionalJOption>-Xdoclint:none</additionalJOption>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@ -157,9 +171,6 @@
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
<useIncrementalCompilation>false</useIncrementalCompilation>
|
||||
<excludes>
|
||||
<exclude>it/tdlight/tdlib/ClientManager.java</exclude>
|
||||
</excludes>
|
||||
@ -174,6 +185,20 @@
|
||||
<release>9</release>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- compile sources with Java 111 -->
|
||||
<execution>
|
||||
<id>java-11-module-compile</id>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<release>11</release>
|
||||
<compileSourceRoots>
|
||||
<compileSourceRoot>${project.basedir}/../src/main/java11</compileSourceRoot>
|
||||
</compileSourceRoots>
|
||||
<multiReleaseOutput>true</multiReleaseOutput>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- compile sources with Java 9 to generate and validate module-info.java -->
|
||||
<execution>
|
||||
<id>java-9-module-compile</id>
|
||||
@ -192,17 +217,17 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<!-- specify JDK 9+ release flag to ensure no classes/methods later than Java 8 are used accidentally -->
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
<release>8</release>
|
||||
<!-- exclude module-info.java from the compilation, as it is unsupported by Java 8 -->
|
||||
<excludes>
|
||||
<exclude>it/tdlight/tdlib/ClientManager.java</exclude>
|
||||
<exclude>module-info.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
@ -227,10 +252,17 @@
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugin>-->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Multi-Release>true</Multi-Release>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
|
Loading…
Reference in New Issue
Block a user