tdlib-session-container/src/main/java/it/tdlight/tdlibsession/td/easy/AsyncTdEasy.java

658 lines
24 KiB
Java
Raw Normal View History

package it.tdlight.tdlibsession.td.easy;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationState;
import it.tdlight.jni.TdApi.AuthorizationStateClosed;
import it.tdlight.jni.TdApi.AuthorizationStateClosing;
import it.tdlight.jni.TdApi.AuthorizationStateReady;
import it.tdlight.jni.TdApi.AuthorizationStateWaitCode;
import it.tdlight.jni.TdApi.AuthorizationStateWaitEncryptionKey;
2020-10-16 12:18:53 +02:00
import it.tdlight.jni.TdApi.AuthorizationStateWaitOtherDeviceConfirmation;
import it.tdlight.jni.TdApi.AuthorizationStateWaitPassword;
import it.tdlight.jni.TdApi.AuthorizationStateWaitPhoneNumber;
import it.tdlight.jni.TdApi.AuthorizationStateWaitRegistration;
import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters;
import it.tdlight.jni.TdApi.CheckAuthenticationBotToken;
2020-10-16 12:18:53 +02:00
import it.tdlight.jni.TdApi.CheckAuthenticationCode;
import it.tdlight.jni.TdApi.CheckAuthenticationPassword;
import it.tdlight.jni.TdApi.CheckDatabaseEncryptionKey;
import it.tdlight.jni.TdApi.Error;
2021-10-02 22:51:47 +02:00
import it.tdlight.jni.TdApi.Function;
import it.tdlight.jni.TdApi.Object;
import it.tdlight.jni.TdApi.OptionValueBoolean;
import it.tdlight.jni.TdApi.OptionValueEmpty;
import it.tdlight.jni.TdApi.OptionValueInteger;
import it.tdlight.jni.TdApi.OptionValueString;
import it.tdlight.jni.TdApi.PhoneNumberAuthenticationSettings;
import it.tdlight.jni.TdApi.RegisterUser;
import it.tdlight.jni.TdApi.SetAuthenticationPhoneNumber;
import it.tdlight.jni.TdApi.SetTdlibParameters;
import it.tdlight.jni.TdApi.TdlibParameters;
import it.tdlight.jni.TdApi.Update;
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
import it.tdlight.tdlibsession.FatalErrorType;
2021-01-15 23:31:10 +01:00
import it.tdlight.tdlibsession.td.TdError;
import it.tdlight.tdlibsession.td.TdResult;
import it.tdlight.tdlibsession.td.middle.AsyncTdMiddle;
import it.tdlight.utils.MonoUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
2021-10-02 16:48:56 +02:00
import java.time.Duration;
import java.util.Comparator;
2020-10-16 12:18:53 +02:00
import java.util.Objects;
import java.util.Set;
2021-10-02 22:51:47 +02:00
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
2021-11-09 15:54:28 +01:00
import java.util.concurrent.locks.LockSupport;
import org.reactivestreams.Publisher;
2021-02-20 21:25:11 +01:00
import org.warp.commonutils.log.Logger;
import org.warp.commonutils.log.LoggerFactory;
import org.warp.commonutils.error.InitializationException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
2021-01-13 04:00:43 +01:00
import reactor.core.publisher.Sinks;
2021-11-09 15:54:28 +01:00
import reactor.core.publisher.Sinks.EmitResult;
import reactor.core.publisher.Sinks.Empty;
2021-01-24 23:08:14 +01:00
import reactor.core.publisher.Sinks.Many;
2021-01-13 04:00:43 +01:00
import reactor.core.publisher.Sinks.One;
2021-11-09 15:54:28 +01:00
import reactor.core.publisher.SynchronousSink;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
2021-10-02 22:51:47 +02:00
@SuppressWarnings("unused")
public class AsyncTdEasy {
2021-01-24 19:13:46 +01:00
private final Logger logger;
2021-10-02 16:48:56 +02:00
private static final Duration DEFAULT_TIMEOUT = Duration.ofMinutes(1);
2021-11-09 15:54:28 +01:00
private final Empty<Void> closed = Sinks.empty();
private final Many<AuthorizationState> authStateSink = Sinks.many().replay().latest();
private final AtomicReference<AuthorizationState> authState = new AtomicReference<>(new AuthorizationStateClosed());
2021-10-02 22:51:47 +02:00
private final AtomicBoolean requestedDefinitiveExit = new AtomicBoolean();
2021-11-09 15:54:28 +01:00
private final AtomicBoolean canSendCloseRequest = new AtomicBoolean();
2021-10-02 22:51:47 +02:00
private final AtomicReference<TdEasySettings> settings = new AtomicReference<>(null);
2021-01-24 23:08:14 +01:00
private final Many<Error> globalErrors = Sinks.many().multicast().onBackpressureBuffer();
2021-01-13 04:00:43 +01:00
private final One<FatalErrorType> fatalError = Sinks.one();
private final AsyncTdMiddle td;
private final String logName;
private final Flux<Update> incomingUpdates;
private final Scheduler scheduler = Schedulers.parallel();
public AsyncTdEasy(AsyncTdMiddle td, String logName) {
this.td = td;
this.logName = logName;
2021-01-24 19:13:46 +01:00
this.logger = LoggerFactory.getLogger("AsyncTdEasy " + logName);
this.incomingUpdates = td.receive()
2021-11-09 15:54:28 +01:00
.doFirst(() -> {
canSendCloseRequest.set(true);
logger.debug("From now onwards TdApi.Close cannot be called");
})
.doOnTerminate(() -> {
canSendCloseRequest.set(false);
logger.debug("From now onwards TdApi.Close can be called");
})
2021-01-25 03:36:15 +01:00
.flatMapSequential(this::preprocessUpdates)
2021-11-09 15:54:28 +01:00
.map(update -> {
var state = authState.get();
Objects.requireNonNull(state, "State is not set");
return new AsyncTdUpdateObj(state, update);
})
.map(upd -> (TdApi.Update) upd.getUpdate())
2021-11-09 15:54:28 +01:00
.takeUntil(update -> {
if (update.getConstructor() == UpdateAuthorizationState.CONSTRUCTOR) {
var state = ((UpdateAuthorizationState) update).authorizationState;
return state.getConstructor() == AuthorizationStateClosed.CONSTRUCTOR;
}
return false;
})
.doOnError(ex -> {
2021-01-15 23:31:10 +01:00
if (ex instanceof TdError) {
var tdEx = (TdError) ex;
logger.error("Received an error update from telegram: " + tdEx.getTdCode() + " " + tdEx.getTdMessage());
FatalErrorType fatalErrorType;
try {
fatalErrorType = FatalErrorType.valueOf(tdEx.getTdMessage());
} catch (IllegalArgumentException ignored) {
fatalErrorType = FatalErrorType.INVALID_UPDATE;
}
this.fatalError.tryEmitValue(fatalErrorType);
} else {
logger.error(ex.getLocalizedMessage(), ex);
}
2021-01-24 19:13:46 +01:00
})
2021-11-09 15:54:28 +01:00
.doFinally(s -> {
var state = authState.get();
2021-10-02 22:51:47 +02:00
onUpdatesTerminated();
2021-11-09 15:54:28 +01:00
if (state.getConstructor() != AuthorizationStateClosed.CONSTRUCTOR) {
2021-10-02 22:51:47 +02:00
logger.warn("Updates stream has closed while"
+ " the current authorization state is"
2021-11-09 15:54:28 +01:00
+ " still {}. Setting authorization state as closed!", state.getClass().getSimpleName());
2021-10-02 22:51:47 +02:00
this.fatalError.tryEmitValue(FatalErrorType.CONNECTION_KILLED);
}
2021-11-09 15:54:28 +01:00
});
}
2021-01-15 23:31:10 +01:00
private void onUpdatesTerminated() {
logger.debug("Incoming updates flux terminated. Setting requestedDefinitiveExit: true");
2021-10-02 22:51:47 +02:00
requestedDefinitiveExit.set(true);
2021-11-09 15:54:28 +01:00
var newState = new AuthorizationStateClosed();
emitState(newState);
2021-01-15 23:31:10 +01:00
}
public Mono<Void> create(TdEasySettings settings) {
return Mono
.fromCallable(() -> {
// Create session directories
if (Files.notExists(Path.of(settings.databaseDirectory))) {
try {
Files.createDirectories(Path.of(settings.databaseDirectory));
} catch (IOException ex) {
throw new InitializationException(ex);
}
}
2021-01-10 02:42:23 +01:00
// Register fatal error handler
2021-01-29 01:29:10 +01:00
fatalError.asMono().flatMap(settings.getFatalErrorHandler()::onFatalError).subscribeOn(scheduler).subscribe();
2021-01-10 02:42:23 +01:00
return true;
})
2021-01-27 01:09:22 +01:00
.subscribeOn(Schedulers.boundedElastic())
.flatMap(_v -> {
2021-10-02 22:51:47 +02:00
this.settings.set(settings);
return Mono.empty();
2021-02-25 11:21:03 +01:00
})
.then(td.initialize());
}
/**
* Get TDLib state
*/
2021-11-09 15:54:28 +01:00
public Flux<AuthorizationState> state() {
return authStateSink.asFlux().distinct();
}
/**
* Get incoming updates from TDLib.
*/
public Flux<TdApi.Update> getIncomingUpdates() {
return incomingUpdates;
}
/**
* Get generic error updates from TDLib (When they are not linked to a precise request).
*/
public Flux<TdApi.Error> getIncomingErrors() {
return Flux.from(globalErrors.asFlux());
}
/**
* Receives fatal errors from TDLib.
*/
2021-01-13 04:00:43 +01:00
public Mono<FatalErrorType> getFatalErrors() {
return Mono.from(fatalError.asMono());
}
/**
* Sends request to TDLib.
2021-10-02 16:48:56 +02:00
* @param timeout Timeout duration.
* @return The response or {@link TdApi.Error}.
*/
2021-10-21 00:21:43 +02:00
public <T extends Object> Mono<TdResult<T>> send(TdApi.Function<T> request, Duration timeout) {
2021-10-02 16:48:56 +02:00
return td.execute(request, timeout, false);
}
2021-10-21 00:21:43 +02:00
private <T extends TdApi.Object> Mono<TdResult<T>> sendDirectly(Function<T> obj, boolean synchronous) {
2021-10-02 22:51:47 +02:00
return td.execute(obj, AsyncTdEasy.DEFAULT_TIMEOUT, synchronous);
}
/**
* Set verbosity level
* @param i level
*/
public Mono<Void> setVerbosityLevel(int i) {
2021-11-09 15:54:28 +01:00
return sendDirectly(new TdApi.SetLogVerbosityLevel(i), true).transform(this::thenOrFatalError);
}
/**
* Clear option on TDLib
* @param name option name
*/
public Mono<Void> clearOption(String name) {
2021-11-09 15:54:28 +01:00
return sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueEmpty()), false)
.transform(this::thenOrFatalError);
}
/**
* Set option on TDLib
* @param name option name
* @param value option value
*/
public Mono<Void> setOptionString(String name, String value) {
2021-11-09 15:54:28 +01:00
return sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueString(value)), false)
.transform(this::thenOrFatalError);
}
/**
* Set option on TDLib
* @param name option name
* @param value option value
*/
public Mono<Void> setOptionInteger(String name, long value) {
2021-11-09 15:54:28 +01:00
return sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueInteger(value)), false)
.transform(this::thenOrFatalError);
}
/**
* Set option on TDLib
* @param name option name
* @param value option value
*/
public Mono<Void> setOptionBoolean(String name, boolean value) {
2021-11-09 15:54:28 +01:00
return sendDirectly(new TdApi.SetOption(name, new TdApi.OptionValueBoolean(value)), false)
.transform(this::thenOrFatalError);
}
/**
* Get option from TDLib
* @param name option name
* @return The value or nothing
*/
public Mono<String> getOptionString(String name) {
2021-10-02 16:48:56 +02:00
return this
2021-10-02 22:51:47 +02:00
.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), false)
2021-10-02 19:03:17 +02:00
.<TdApi.OptionValue>handle(MonoUtils::orElseThrow)
.flatMap(value -> {
switch (value.getConstructor()) {
case OptionValueString.CONSTRUCTOR:
return Mono.just(((OptionValueString) value).value);
case OptionValueEmpty.CONSTRUCTOR:
return Mono.empty();
default:
return Mono.error(new UnsupportedOperationException("The option " + name + " is of type "
+ value.getClass().getSimpleName()));
}
});
}
/**
* Get option from TDLib
* @param name option name
* @return The value or nothing
*/
public Mono<Long> getOptionInteger(String name) {
2021-10-02 16:48:56 +02:00
return this
2021-10-02 22:51:47 +02:00
.<TdApi.OptionValue>sendDirectly(new TdApi.GetOption(name), false)
2021-10-02 19:03:17 +02:00
.<TdApi.OptionValue>handle(MonoUtils::orElseThrow)
.flatMap(value -> {
2021-10-02 16:48:56 +02:00
switch (value.getConstructor()) {
case OptionValueInteger.CONSTRUCTOR:
return Mono.just(((OptionValueInteger) value).value);
case OptionValueEmpty.CONSTRUCTOR:
return Mono.empty();
default:
return Mono.error(new UnsupportedOperationException(
"The option " + name + " is of type " + value.getClass().getSimpleName()));
}
});
}
/**
* Get option from TDLib
* @param name option name
* @return The value or nothing
*/
public Mono<Boolean> getOptionBoolean(String name) {
2021-10-02 16:48:56 +02:00
return this
2021-11-09 15:54:28 +01:00
.sendDirectly(new TdApi.GetOption(name), false)
2021-10-02 19:03:17 +02:00
.<TdApi.OptionValue>handle(MonoUtils::orElseThrow)
.flatMap(value -> {
2021-10-02 16:48:56 +02:00
switch (value.getConstructor()) {
case OptionValueBoolean.CONSTRUCTOR:
return Mono.just(((OptionValueBoolean) value).value);
case OptionValueEmpty.CONSTRUCTOR:
return Mono.empty();
default:
return Mono.error(new UnsupportedOperationException(
"The option " + name + " is of type " + value.getClass().getSimpleName()));
}
});
}
/**
* Synchronously executes TDLib requests. Only a few requests can be executed synchronously. May
* be called from any thread.
*
* @param request Request to the TDLib.
2021-10-02 16:48:56 +02:00
* @param timeout Timeout.
* @return The request response.
*/
2021-10-21 00:21:43 +02:00
public <T extends Object> Mono<TdResult<T>> execute(TdApi.Function<T> request, Duration timeout) {
2021-10-02 16:48:56 +02:00
return td.execute(request, timeout, true);
}
/**
* Closes the client gracefully by sending {@link TdApi.Close}.
*/
public Mono<Void> close() {
2021-11-09 15:54:28 +01:00
var waitClosed = closed.asMono()
.doFirst(() -> logger.debug("Waiting for AuthorizationStateClosed..."))
.doOnSuccess(s -> logger.debug("Received AuthorizationStateClosed after TdApi.Close"))
.transformDeferred(mono -> {
if (canSendCloseRequest.get()) {
return mono;
} else {
return Mono.fromRunnable(() -> emitState(new AuthorizationStateClosed()));
}
});
return Mono
.fromSupplier(authState::get)
.filter(state -> {
switch (state.getConstructor()) {
case AuthorizationStateClosing.CONSTRUCTOR:
case AuthorizationStateClosed.CONSTRUCTOR:
return false;
default:
return true;
}
})
2021-10-02 22:51:47 +02:00
.then(Mono.fromCallable(requestedDefinitiveExit::get).single())
.filter(closeRequested -> !closeRequested)
.doOnSuccess(s -> {
logger.debug("Setting requestedDefinitiveExit: true");
2021-10-02 22:51:47 +02:00
requestedDefinitiveExit.set(true);
2021-01-13 23:43:09 +01:00
})
2021-11-09 15:54:28 +01:00
.then(td
.execute(new TdApi.Close(), Duration.ofSeconds(5), false)
.doFirst(() -> logger.debug("Sending TdApi.Close"))
.doOnNext(closeResponse -> logger.debug("TdApi.Close response is: \"{}\"",
closeResponse.toString().replace('\n', ' ')
))
.doOnSuccess(s -> logger.debug("Sent TdApi.Close"))
.transformDeferred(closeMono -> {
if (canSendCloseRequest.get()) {
return closeMono;
} else {
return Mono.empty();
}
})
)
.then(waitClosed)
2021-10-02 22:51:47 +02:00
.doOnSuccess(s -> logger.info("AsyncTdEasy closed successfully"))
.then();
}
private Mono<Update> catchErrors(Object obj) {
2021-10-02 16:48:56 +02:00
return Mono.fromCallable(() -> {
if (obj.getConstructor() == Error.CONSTRUCTOR) {
var error = (Error) obj;
switch (error.message) {
case "PHONE_CODE_INVALID":
2021-01-24 23:08:14 +01:00
globalErrors.tryEmitNext(error);
return new UpdateAuthorizationState(new AuthorizationStateWaitCode());
case "PASSWORD_HASH_INVALID":
2021-01-24 23:08:14 +01:00
globalErrors.tryEmitNext(error);
return new UpdateAuthorizationState(new AuthorizationStateWaitPassword());
default:
2021-01-24 23:08:14 +01:00
globalErrors.tryEmitNext(error);
break;
}
analyzeFatalErrors(error);
return null;
} else {
return (Update) obj;
2021-01-10 02:28:23 +01:00
}
});
2021-01-10 02:28:23 +01:00
}
private void analyzeFatalErrors(Object obj) {
if (obj != null && obj.getConstructor() == Error.CONSTRUCTOR) {
var error = (Error) obj;
switch (error.message) {
case "PHONE_NUMBER_INVALID":
2021-01-13 04:00:43 +01:00
fatalError.tryEmitValue(FatalErrorType.PHONE_NUMBER_INVALID);
break;
case "ACCESS_TOKEN_INVALID":
2021-01-13 04:00:43 +01:00
fatalError.tryEmitValue(FatalErrorType.ACCESS_TOKEN_INVALID);
break;
case "CONNECTION_KILLED":
2021-01-13 04:00:43 +01:00
fatalError.tryEmitValue(FatalErrorType.CONNECTION_KILLED);
break;
case "INVALID_UPDATE":
2021-01-13 04:00:43 +01:00
fatalError.tryEmitValue(FatalErrorType.INVALID_UPDATE);
break;
2021-01-10 02:28:23 +01:00
case "PHONE_NUMBER_BANNED":
2021-01-13 04:00:43 +01:00
fatalError.tryEmitValue(FatalErrorType.PHONE_NUMBER_BANNED);
break;
}
}
}
private Publisher<TdApi.Update> preprocessUpdates(TdApi.Object updateObj) {
return Mono
.just(updateObj)
.flatMap(this::catchErrors)
.filter(obj -> obj.getConstructor() == UpdateAuthorizationState.CONSTRUCTOR)
.map(obj -> ((UpdateAuthorizationState) obj).authorizationState)
.flatMap(obj -> {
switch (obj.getConstructor()) {
case AuthorizationStateWaitTdlibParameters.CONSTRUCTOR:
2021-11-09 15:54:28 +01:00
return Mono
.fromCallable(this.settings::get)
.single()
.map(settings -> {
var parameters = new TdlibParameters();
parameters.useTestDc = settings.useTestDc;
parameters.databaseDirectory = settings.databaseDirectory;
parameters.filesDirectory = settings.filesDirectory;
parameters.useFileDatabase = settings.useFileDatabase;
parameters.useChatInfoDatabase = settings.useChatInfoDatabase;
parameters.useMessageDatabase = settings.useMessageDatabase;
parameters.useSecretChats = false;
parameters.apiId = settings.apiId;
parameters.apiHash = settings.apiHash;
parameters.systemLanguageCode = settings.systemLanguageCode;
parameters.deviceModel = settings.deviceModel;
parameters.systemVersion = settings.systemVersion;
parameters.applicationVersion = settings.applicationVersion;
parameters.enableStorageOptimizer = settings.enableStorageOptimizer;
parameters.ignoreFileNames = settings.ignoreFileNames;
return new SetTdlibParameters(parameters);
})
.flatMap((SetTdlibParameters obj1) -> sendDirectly(obj1, false))
.transform(this::thenOrFatalError);
case AuthorizationStateWaitEncryptionKey.CONSTRUCTOR:
2021-11-09 15:54:28 +01:00
return sendDirectly(new CheckDatabaseEncryptionKey(), false)
.transform(this::thenOrFatalError)
2020-10-17 01:53:14 +02:00
.onErrorResume((error) -> {
logger.error("Error while checking TDLib encryption key", error);
2021-10-02 22:51:47 +02:00
return sendDirectly(new TdApi.Close(), false).then();
2020-10-17 01:53:14 +02:00
});
case AuthorizationStateWaitPhoneNumber.CONSTRUCTOR:
2021-11-09 15:54:28 +01:00
return Mono
.fromCallable(this.settings::get).single().flatMap(settings -> {
if (settings.isPhoneNumberSet()) {
return sendDirectly(new SetAuthenticationPhoneNumber(String.valueOf(settings.getPhoneNumber()),
new PhoneNumberAuthenticationSettings(false, false, false)
), false);
} else if (settings.isBotTokenSet()) {
return sendDirectly(new CheckAuthenticationBotToken(settings.getBotToken()), false);
} else {
return Mono.error(new IllegalArgumentException("A bot is neither an user or a bot"));
}
})
.transform(this::thenOrFatalError)
.onErrorResume((error) -> {
logger.error("Error while waiting for phone number", error);
return sendDirectly(new TdApi.Close(), false).then();
});
case AuthorizationStateWaitRegistration.CONSTRUCTOR:
var authorizationStateWaitRegistration = (AuthorizationStateWaitRegistration) obj;
RegisterUser registerUser = new RegisterUser();
if (authorizationStateWaitRegistration.termsOfService != null
2021-11-09 15:54:28 +01:00
&& authorizationStateWaitRegistration.termsOfService.text != null
&& !authorizationStateWaitRegistration.termsOfService.text.text.isBlank()) {
logger.info("Telegram Terms of Service:\n" + authorizationStateWaitRegistration.termsOfService.text.text);
}
2020-10-16 12:18:53 +02:00
return Mono
2021-10-02 22:51:47 +02:00
.fromCallable(this.settings::get)
.single()
2020-10-16 12:18:53 +02:00
.map(TdEasySettings::getParameterRequestHandler)
2021-11-09 15:54:28 +01:00
.flatMap(handler -> handler
2021-10-02 22:51:47 +02:00
.onParameterRequest(Parameter.ASK_FIRST_NAME, new ParameterInfoEmpty())
.filter(Objects::nonNull)
.map(String::trim)
.filter(firstName -> !firstName.isBlank() && firstName.length() <= 64 && firstName.length() > 0)
.repeatWhen(s -> s.takeWhile(n -> n == 0))
.last()
.doOnNext(firstName -> registerUser.firstName = firstName)
.then(handler
.onParameterRequest(Parameter.ASK_LAST_NAME, new ParameterInfoEmpty())
.filter(Objects::nonNull)
.map(String::trim)
.filter(lastName -> lastName.length() <= 64)
.repeatWhen(s -> s.takeWhile(n -> n == 0))
.last()
.defaultIfEmpty("")
.doOnNext(lastName -> registerUser.lastName = lastName)
)
.then(sendDirectly(registerUser, false))
2021-11-09 15:54:28 +01:00
.transform(this::thenOrLogRepeatError)
);
2020-10-16 12:18:53 +02:00
case TdApi.AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR:
var authorizationStateWaitOtherDeviceConfirmation = (AuthorizationStateWaitOtherDeviceConfirmation) obj;
return Mono
2021-10-02 22:51:47 +02:00
.fromCallable(this.settings::get)
.single()
2020-10-16 12:18:53 +02:00
.map(TdEasySettings::getParameterRequestHandler)
2021-10-02 22:51:47 +02:00
.flatMap(handler -> handler.onParameterRequest(Parameter.NOTIFY_LINK,
new ParameterInfoNotifyLink(authorizationStateWaitOtherDeviceConfirmation.link)
));
2020-10-16 12:18:53 +02:00
case TdApi.AuthorizationStateWaitCode.CONSTRUCTOR:
var authorizationStateWaitCode = (AuthorizationStateWaitCode) obj;
return Mono
2021-10-02 22:51:47 +02:00
.fromCallable(this.settings::get)
.single()
2020-10-16 12:18:53 +02:00
.map(TdEasySettings::getParameterRequestHandler)
2021-11-09 15:54:28 +01:00
.flatMap(handler -> handler
2021-10-02 22:51:47 +02:00
.onParameterRequest(Parameter.ASK_CODE, new ParameterInfoCode(authorizationStateWaitCode.codeInfo.phoneNumber,
authorizationStateWaitCode.codeInfo.nextType,
authorizationStateWaitCode.codeInfo.timeout,
authorizationStateWaitCode.codeInfo.type))
.flatMap(code -> sendDirectly(new CheckAuthenticationCode(code), false))
2021-11-09 15:54:28 +01:00
.transform(this::thenOrLogRepeatError)
);
case AuthorizationStateWaitPassword.CONSTRUCTOR:
var authorizationStateWaitPassword = (AuthorizationStateWaitPassword) obj;
2020-10-16 12:18:53 +02:00
return Mono
2021-10-02 22:51:47 +02:00
.fromCallable(this.settings::get)
.single()
2020-10-16 12:18:53 +02:00
.map(TdEasySettings::getParameterRequestHandler)
2021-11-09 15:54:28 +01:00
.flatMap(handler -> handler
2021-10-02 22:51:47 +02:00
.onParameterRequest(Parameter.ASK_PASSWORD, new ParameterInfoPasswordHint(
authorizationStateWaitPassword.passwordHint))
.flatMap(password -> sendDirectly(new CheckAuthenticationPassword(password), false))
2021-11-09 15:54:28 +01:00
)
.transform(this::thenOrLogRepeatError);
case AuthorizationStateReady.CONSTRUCTOR: {
2021-11-09 15:54:28 +01:00
var state = new AuthorizationStateReady();
emitState(state);
return Mono.empty();
}
case AuthorizationStateClosing.CONSTRUCTOR:
logger.debug("Received AuthorizationStateClosing from td");
return Mono.empty();
case AuthorizationStateClosed.CONSTRUCTOR:
logger.debug("Received AuthorizationStateClosed from td");
2021-10-02 22:51:47 +02:00
return Mono.fromCallable(() -> {
var closeRequested = this.requestedDefinitiveExit.get();
if (closeRequested) {
logger.debug("td closed successfully");
} else {
logger.warn("td closed unexpectedly: {}", logName);
}
2021-11-09 15:54:28 +01:00
emitState(obj);
2021-10-02 22:51:47 +02:00
return closeRequested;
}).flatMap(closeRequested -> {
if (closeRequested) {
return Mono
2021-10-02 22:51:47 +02:00
.fromCallable(settings::get)
.single()
.map(settings -> settings.databaseDirectory)
.map(Path::of)
.flatMapIterable(sessionPath -> Set.of(sessionPath.resolve("media"),
sessionPath.resolve("passport"),
sessionPath.resolve("profile_photos"),
sessionPath.resolve("stickers"),
sessionPath.resolve("temp"),
sessionPath.resolve("thumbnails"),
sessionPath.resolve("wallpapers")
))
2021-01-16 00:30:45 +01:00
.filterWhen(file -> Mono
.fromCallable(() -> Files.exists(file))
2021-01-27 01:09:22 +01:00
.subscribeOn(Schedulers.boundedElastic()))
.doOnNext(directory -> {
try {
if (!Files.walk(directory)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.allMatch(File::delete)) {
throw new IOException("Can't delete a file!");
}
} catch (IOException e) {
logger.error("Can't delete temporary session subdirectory", e);
}
})
2021-10-02 22:51:47 +02:00
.then(Mono.just(true));
} else {
2021-10-02 22:51:47 +02:00
return Mono.just(false);
}
}).then();
default:
return Mono.empty();
}
})
2021-01-24 23:08:14 +01:00
.then(Mono.justOrEmpty(updateObj.getConstructor() == Error.CONSTRUCTOR ? null : (Update) updateObj));
}
2021-01-10 02:28:23 +01:00
2021-11-09 15:54:28 +01:00
private void emitState(AuthorizationState state) {
if (state.getConstructor() == AuthorizationStateClosed.CONSTRUCTOR) {
this.closed.tryEmitEmpty();
}
this.authState.set(state);
EmitResult emitResult;
while ((emitResult = this.authStateSink.tryEmitNext(state)) == EmitResult.FAIL_NON_SERIALIZED) {
// Wait 10ms
LockSupport.parkNanos(10L * 1000000L);
}
emitResult.orThrow();
}
private <T extends TdApi.Object> Mono<Void> thenOrFatalError(Mono<TdResult<T>> mono) {
return mono.doOnNext(result -> {
2021-01-10 02:28:23 +01:00
if (result.failed()) {
analyzeFatalErrors(result.cause());
}
2021-11-09 15:54:28 +01:00
}).transform(MonoUtils::thenOrError);
}
private <T extends TdApi.Object> Mono<Void> thenOrLogRepeatError(Mono<TdResult<T>> mono) {
return mono.handle((TdResult<T> optional, SynchronousSink<Void> sink) -> {
if (optional.succeeded()) {
sink.complete();
} else {
logger.error("Received TDLib error: {}", optional.cause());
sink.error(new TdError(optional.cause().code, optional.cause().message));
}
}).retry();
2021-01-10 02:28:23 +01:00
}
}