diff --git a/README.md b/README.md
index e69de29..8ca5ac6 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,13 @@
+TDLib session container
+=======================
+
+This software is a wrapper for [TDLight Java](https://github.com/tdlight-team/tdlight-java)
+
+Unlike TDLight java, this wrapper abstracts TDLib to make it possible to run remotely,
+decoupling it into a client and a server.
+
+TDLib session container can be used in various ways:
+ - Clustered
+ - Remote
+ - Local
+ - Direct (The client process, as TDLight java)
diff --git a/pom.xml b/pom.xml
index 91eb8a0..5458462 100644
--- a/pom.xml
+++ b/pom.xml
@@ -139,7 +139,7 @@
it.tdlight
tdlight-java
- [3.171.36,)
+ [4.171.54,)
it.cavallium
diff --git a/src/main/java/it/tdlight/tdlibsession/remoteclient/TDLibRemoteClient.java b/src/main/java/it/tdlight/tdlibsession/remoteclient/TDLibRemoteClient.java
index 68a6a0f..95f7d53 100644
--- a/src/main/java/it/tdlight/tdlibsession/remoteclient/TDLibRemoteClient.java
+++ b/src/main/java/it/tdlight/tdlibsession/remoteclient/TDLibRemoteClient.java
@@ -25,6 +25,7 @@ import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.core.publisher.Sinks.One;
import reactor.core.scheduler.Schedulers;
+import reactor.tools.agent.ReactorDebugAgent;
public class TDLibRemoteClient implements AutoCloseable {
@@ -41,13 +42,27 @@ public class TDLibRemoteClient implements AutoCloseable {
*/
private final AtomicInteger statsActiveDeployments = new AtomicInteger();
- public TDLibRemoteClient(SecurityInfo securityInfo, String masterHostname, String netInterface, int port, Set membersAddresses) {
+ public static boolean runningFromIntelliJ() {
+ return System.getProperty("java.class.path").contains("idea_rt.jar")
+ || System.getProperty("idea.test.cyclic.buffer.size") != null;
+ }
+
+ public TDLibRemoteClient(SecurityInfo securityInfo,
+ String masterHostname,
+ String netInterface,
+ int port,
+ Set membersAddresses,
+ boolean enableStacktraces) {
this.securityInfo = securityInfo;
this.masterHostname = masterHostname;
this.netInterface = netInterface;
this.port = port;
this.membersAddresses = membersAddresses;
+ if (enableStacktraces && !runningFromIntelliJ()) {
+ ReactorDebugAgent.init();
+ }
+
try {
Init.start();
} catch (CantLoadLibrary ex) {
@@ -74,13 +89,14 @@ public class TDLibRemoteClient implements AutoCloseable {
Path keyStorePasswordPath = Paths.get(args[4]);
Path trustStorePath = Paths.get(args[5]);
Path trustStorePasswordPath = Paths.get(args[6]);
+ boolean enableStacktraces = Boolean.parseBoolean(args[7]);
var loggerContext = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
loggerContext.setConfigLocation(TDLibRemoteClient.class.getResource("/tdlib-session-container-log4j2.xml").toURI());
var securityInfo = new SecurityInfo(keyStorePath, keyStorePasswordPath, trustStorePath, trustStorePasswordPath);
- var client = new TDLibRemoteClient(securityInfo, masterHostname, netInterface, port, membersAddresses);
+ var client = new TDLibRemoteClient(securityInfo, masterHostname, netInterface, port, membersAddresses, enableStacktraces);
client
.start()
@@ -140,16 +156,18 @@ public class TDLibRemoteClient implements AutoCloseable {
.setConfig(new JsonObject()
.put("botId", req.id())
.put("botAlias", req.alias())
- .put("local", false));
+ .put("local", false)
+ .put("implementationDetails", req.implementationDetails()));
var verticle = new AsyncTdMiddleEventBusServer();
// Binlog path
var sessPath = getSessionDirectory(req.id());
+ var mediaPath = getMediaDirectory(req.id());
var blPath = getSessionBinlogDirectory(req.id());
BinlogUtils
.chooseBinlog(clusterManager.getVertx().fileSystem(), blPath, req.binlog(), req.binlogDate())
- .then(BinlogUtils.cleanSessionPath(clusterManager.getVertx().fileSystem(), blPath, sessPath))
+ .then(BinlogUtils.cleanSessionPath(clusterManager.getVertx().fileSystem(), blPath, sessPath, mediaPath))
.then(clusterManager.getVertx().rxDeployVerticle(verticle, deploymentOptions).as(MonoUtils::toMono))
.subscribeOn(Schedulers.single())
.subscribe(
diff --git a/src/main/java/it/tdlight/tdlibsession/td/direct/AsyncTdDirectImpl.java b/src/main/java/it/tdlight/tdlibsession/td/direct/AsyncTdDirectImpl.java
index 4c69fcb..ba572a4 100644
--- a/src/main/java/it/tdlight/tdlibsession/td/direct/AsyncTdDirectImpl.java
+++ b/src/main/java/it/tdlight/tdlibsession/td/direct/AsyncTdDirectImpl.java
@@ -1,15 +1,15 @@
package it.tdlight.tdlibsession.td.direct;
+import io.vertx.core.json.JsonObject;
import it.tdlight.common.TelegramClient;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.AuthorizationStateClosed;
import it.tdlight.jni.TdApi.Close;
import it.tdlight.jni.TdApi.Function;
-import it.tdlight.jni.TdApi.Object;
import it.tdlight.jni.TdApi.Ok;
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
+import it.tdlight.tdlibsession.td.TdError;
import it.tdlight.tdlibsession.td.TdResult;
-import it.tdlight.tdlight.ClientManager;
import it.tdlight.utils.MonoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -23,11 +23,17 @@ public class AsyncTdDirectImpl implements AsyncTdDirect {
private static final Logger logger = LoggerFactory.getLogger(AsyncTdDirect.class);
- private final One td = Sinks.one();
-
+ private final TelegramClientFactory telegramClientFactory;
+ private final JsonObject implementationDetails;
private final String botAlias;
- public AsyncTdDirectImpl(String botAlias) {
+ private final One td = Sinks.one();
+
+ public AsyncTdDirectImpl(TelegramClientFactory telegramClientFactory,
+ JsonObject implementationDetails,
+ String botAlias) {
+ this.telegramClientFactory = telegramClientFactory;
+ this.implementationDetails = implementationDetails;
this.botAlias = botAlias;
}
@@ -65,41 +71,40 @@ public class AsyncTdDirectImpl implements AsyncTdDirect {
public Flux receive(AsyncTdDirectOptions options) {
// If closed it will be either true or false
final One closedFromTd = Sinks.one();
- return Flux.create(emitter -> {
- var client = ClientManager.create((Object object) -> {
- emitter.next(object);
- // Close the emitter if receive closed state
- if (object.getConstructor() == UpdateAuthorizationState.CONSTRUCTOR
- && ((UpdateAuthorizationState) object).authorizationState.getConstructor()
- == AuthorizationStateClosed.CONSTRUCTOR) {
- logger.debug("Received closed status from tdlib");
- closedFromTd.tryEmitValue(true);
- emitter.complete();
- }
- }, emitter::error, emitter::error);
- try {
- this.td.tryEmitValue(client).orThrow();
- } catch (Exception ex) {
- emitter.error(ex);
- }
+ return telegramClientFactory.create(implementationDetails)
+ .flatMapMany(client -> Flux
+ .create(updatesSink -> {
+ client.initialize((TdApi.Object object) -> {
+ updatesSink.next(object);
+ // Close the emitter if receive closed state
+ if (object.getConstructor() == UpdateAuthorizationState.CONSTRUCTOR
+ && ((UpdateAuthorizationState) object).authorizationState.getConstructor()
+ == AuthorizationStateClosed.CONSTRUCTOR) {
+ logger.debug("Received closed status from tdlib");
+ closedFromTd.tryEmitValue(true);
+ updatesSink.complete();
+ }
+ }, updatesSink::error, updatesSink::error);
- // Send close if the stream is disposed before tdlib is closed
- emitter.onDispose(() -> {
- // Try to emit false, so that if it has not been closed from tdlib, now it is explicitly false.
- closedFromTd.tryEmitValue(false);
+ if (td.tryEmitValue(client).isFailure()) {
+ updatesSink.error(new TdError(500, "Failed to emit td client"));
+ }
- closedFromTd.asMono()
- .filter(isClosedFromTd -> !isClosedFromTd)
- .doOnNext(x -> {
- logger.warn("The stream has been disposed without closing tdlib. Sending TdApi.Close()...");
- client.send(new Close(),
- result -> logger.warn("Close result: {}", result),
- ex -> logger.error("Error when disposing td client", ex)
- );
+ // Send close if the stream is disposed before tdlib is closed
+ updatesSink.onDispose(() -> {
+ // Try to emit false, so that if it has not been closed from tdlib, now it is explicitly false.
+ closedFromTd.tryEmitValue(false);
+
+ closedFromTd.asMono().filter(isClosedFromTd -> !isClosedFromTd).doOnNext(x -> {
+ logger.warn("The stream has been disposed without closing tdlib. Sending TdApi.Close()...");
+ client.send(new Close(),
+ result -> logger.warn("Close result: {}", result),
+ ex -> logger.error("Error when disposing td client", ex)
+ );
+ }).subscribeOn(Schedulers.single()).subscribe();
+ });
})
- .subscribeOn(Schedulers.single())
- .subscribe();
- });
- });
+ .subscribeOn(Schedulers.boundedElastic())
+ );
}
}
diff --git a/src/main/java/it/tdlight/tdlibsession/td/direct/TelegramClientFactory.java b/src/main/java/it/tdlight/tdlibsession/td/direct/TelegramClientFactory.java
new file mode 100644
index 0000000..aff3f80
--- /dev/null
+++ b/src/main/java/it/tdlight/tdlibsession/td/direct/TelegramClientFactory.java
@@ -0,0 +1,28 @@
+package it.tdlight.tdlibsession.td.direct;
+
+import io.vertx.core.json.JsonObject;
+import it.tdlight.common.TelegramClient;
+import it.tdlight.tdlight.ClientManager;
+import it.tdlight.utils.MonoUtils;
+import reactor.core.publisher.Mono;
+
+public class TelegramClientFactory {
+
+ public TelegramClientFactory() {
+
+ }
+
+ public Mono create(JsonObject implementationDetails) {
+ return MonoUtils.fromBlockingSingle(() -> {
+ var implementationName = implementationDetails.getString("name", "native-client");
+ switch (implementationName) {
+ case "native-client":
+ return ClientManager.create();
+ case "test-client":
+ //todo: create a noop test client with optional behaviours
+ default:
+ return null;
+ }
+ });
+ }
+}
diff --git a/src/main/java/it/tdlight/tdlibsession/td/easy/AsyncTdEasy.java b/src/main/java/it/tdlight/tdlibsession/td/easy/AsyncTdEasy.java
index 9fb0113..d8ebea8 100644
--- a/src/main/java/it/tdlight/tdlibsession/td/easy/AsyncTdEasy.java
+++ b/src/main/java/it/tdlight/tdlibsession/td/easy/AsyncTdEasy.java
@@ -57,7 +57,7 @@ import reactor.core.scheduler.Schedulers;
public class AsyncTdEasy {
- private static final Logger logger = LoggerFactory.getLogger(AsyncTdEasy.class);
+ private final Logger logger;
private static final Scheduler scheduler = Schedulers.newSingle("AsyncTdEasy", false);
private final ReplayProcessor authState = ReplayProcessor.create(1);
@@ -72,6 +72,7 @@ public class AsyncTdEasy {
public AsyncTdEasy(AsyncTdMiddle td, String logName) {
this.td = td;
this.logName = logName;
+ this.logger = LoggerFactory.getLogger("AsyncTdEasy " + logName);
// todo: use Duration.ZERO instead of 10ms interval
this.incomingUpdates = td.receive()
@@ -93,8 +94,9 @@ public class AsyncTdEasy {
} else {
logger.error(ex.getLocalizedMessage(), ex);
}
- }).doOnComplete(() -> {
- authState.asFlux().take(1).single().subscribe(authState -> {
+ })
+ .doOnComplete(() -> {
+ authState.asFlux().take(1).single().subscribeOn(Schedulers.single()).subscribe(authState -> {
onUpdatesTerminated();
if (authState.getConstructor() != AuthorizationStateClosed.CONSTRUCTOR) {
logger.warn("Updates stream has closed while"
@@ -104,7 +106,7 @@ public class AsyncTdEasy {
}
});
}).doOnError(ex -> {
- authState.asFlux().take(1).single().subscribe(authState -> {
+ authState.asFlux().take(1).single().subscribeOn(Schedulers.single()).subscribe(authState -> {
onUpdatesTerminated();
if (authState.getConstructor() != AuthorizationStateClosed.CONSTRUCTOR) {
logger.warn("Updates stream has terminated with an error while"
diff --git a/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessage.java b/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessage.java
index a040dea..bfe3d5d 100644
--- a/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessage.java
+++ b/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessage.java
@@ -1,5 +1,6 @@
package it.tdlight.tdlibsession.td.middle;
+import io.vertx.core.json.JsonObject;
import java.util.Arrays;
import java.util.Objects;
import java.util.StringJoiner;
@@ -10,12 +11,14 @@ public final class StartSessionMessage {
private final String alias;
private final byte[] binlog;
private final long binlogDate;
+ private final JsonObject implementationDetails;
- public StartSessionMessage(int id, String alias, byte[] binlog, long binlogDate) {
+ public StartSessionMessage(int id, String alias, byte[] binlog, long binlogDate, JsonObject implementationDetails) {
this.id = id;
this.alias = alias;
this.binlog = binlog;
this.binlogDate = binlogDate;
+ this.implementationDetails = implementationDetails;
}
public int id() {
@@ -34,6 +37,10 @@ public final class StartSessionMessage {
return binlogDate;
}
+ public JsonObject implementationDetails() {
+ return implementationDetails;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -54,7 +61,10 @@ public final class StartSessionMessage {
if (!Objects.equals(alias, that.alias)) {
return false;
}
- return Arrays.equals(binlog, that.binlog);
+ if (!Arrays.equals(binlog, that.binlog)) {
+ return false;
+ }
+ return Objects.equals(implementationDetails, that.implementationDetails);
}
@Override
@@ -63,6 +73,7 @@ public final class StartSessionMessage {
result = 31 * result + (alias != null ? alias.hashCode() : 0);
result = 31 * result + Arrays.hashCode(binlog);
result = 31 * result + (int) (binlogDate ^ (binlogDate >>> 32));
+ result = 31 * result + (implementationDetails != null ? implementationDetails.hashCode() : 0);
return result;
}
@@ -73,6 +84,7 @@ public final class StartSessionMessage {
.add("alias='" + alias + "'")
.add("binlog=" + Arrays.toString(binlog))
.add("binlogDate=" + binlogDate)
+ .add("implementationDetails=" + implementationDetails)
.toString();
}
}
diff --git a/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessageCodec.java b/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessageCodec.java
index d58fbd4..ca8d3b4 100644
--- a/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessageCodec.java
+++ b/src/main/java/it/tdlight/tdlibsession/td/middle/StartSessionMessageCodec.java
@@ -2,6 +2,7 @@ package it.tdlight.tdlibsession.td.middle;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.MessageCodec;
+import io.vertx.core.json.JsonObject;
import it.tdlight.utils.VertxBufferInputStream;
import it.tdlight.utils.VertxBufferOutputStream;
import org.warp.commonutils.stream.SafeDataInputStream;
@@ -25,6 +26,7 @@ public class StartSessionMessageCodec implements MessageCodeccreate(sink -> {
diff --git a/src/main/java/it/tdlight/tdlibsession/td/middle/client/AsyncTdMiddleEventBusClient.java b/src/main/java/it/tdlight/tdlibsession/td/middle/client/AsyncTdMiddleEventBusClient.java
index f32641e..9f53043 100644
--- a/src/main/java/it/tdlight/tdlibsession/td/middle/client/AsyncTdMiddleEventBusClient.java
+++ b/src/main/java/it/tdlight/tdlibsession/td/middle/client/AsyncTdMiddleEventBusClient.java
@@ -1,6 +1,7 @@
package it.tdlight.tdlibsession.td.middle.client;
import io.vertx.core.eventbus.DeliveryOptions;
+import io.vertx.core.json.JsonObject;
import io.vertx.reactivex.core.Vertx;
import io.vertx.reactivex.core.eventbus.Message;
import io.vertx.reactivex.core.eventbus.MessageConsumer;
@@ -22,7 +23,6 @@ import it.tdlight.utils.MonoUtils;
import it.tdlight.utils.MonoUtils.SinkRWStream;
import java.nio.file.Path;
import java.time.Duration;
-import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
@@ -66,6 +66,7 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
int botId,
String botAlias,
boolean local,
+ JsonObject implementationDetails,
Path binlogsArchiveDirectory) {
var instance = new AsyncTdMiddleEventBusClient(clusterManager);
return retrieveBinlog(clusterManager.getVertx(), binlogsArchiveDirectory, botId)
@@ -78,7 +79,7 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
.thenReturn(binlog)
)
.flatMap(binlog -> instance
- .start(botId, botAlias, local, binlog)
+ .start(botId, botAlias, local, implementationDetails, binlog)
.thenReturn(instance)
)
.single();
@@ -96,7 +97,11 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
return this.binlog.asMono().flatMap(binlog -> BinlogUtils.saveBinlog(binlog, data));
}
- public Mono start(int botId, String botAlias, boolean local, BinlogAsyncFile binlog) {
+ public Mono start(int botId,
+ String botAlias,
+ boolean local,
+ JsonObject implementationDetails,
+ BinlogAsyncFile binlog) {
this.botId = botId;
this.botAlias = botAlias;
this.botAddress = "bots.bot." + this.botId;
@@ -111,16 +116,22 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
var binlogLastModifiedTime = tuple.getT1();
var binlogData = tuple.getT2();
- var msg = new StartSessionMessage(this.botId, this.botAlias, binlogData, binlogLastModifiedTime);
+ var msg = new StartSessionMessage(this.botId,
+ this.botAlias,
+ binlogData,
+ binlogLastModifiedTime,
+ implementationDetails
+ );
return setupUpdatesListener()
- .then(cluster.getEventBus().rxRequest("bots.start-bot", msg).as(MonoUtils::toMono))
+ .then(Mono.defer(() -> local ? Mono.empty()
+ : cluster.getEventBus().rxRequest("bots.start-bot", msg).as(MonoUtils::toMono)))
.then();
});
}
@SuppressWarnings("CallingSubscribeInNonBlockingScope")
private Mono setupUpdatesListener() {
- MessageConsumer updateConsumer = MessageConsumer.newInstance(cluster.getEventBus().consumer(botAddress + ".updates").getDelegate());
+ MessageConsumer updateConsumer = MessageConsumer.newInstance(cluster.getEventBus().consumer(botAddress + ".updates").setMaxBufferedMessages(5000).getDelegate());
updateConsumer.endHandler(h -> {
logger.error("<<<<<<<<<<<<<<<>>>>>>>>>>>>");
});
@@ -138,7 +149,12 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
@Override
public Flux receive() {
// Here the updates will be received
- return cluster.getEventBus().rxRequest(botAddress + ".ready-to-receive", EMPTY).as(MonoUtils::toMono)
+ return Mono
+ .fromRunnable(() -> logger.trace("Called receive() from parent"))
+ .doOnSuccess(s -> logger.trace("Sending ready-to-receive"))
+ .then(cluster.getEventBus().rxRequest(botAddress + ".ready-to-receive", EMPTY, deliveryOptionsWithTimeout).as(MonoUtils::toMono))
+ .doOnSuccess(s -> logger.trace("Sent ready-to-receive, received reply"))
+ .doOnSuccess(s -> logger.trace("About to read updates flux"))
.thenMany(updates.readAsFlux())
.cast(io.vertx.core.eventbus.Message.class)
.timeout(Duration.ofSeconds(20), Mono.fromCallable(() -> {
@@ -157,15 +173,14 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
.doOnTerminate(updatesStreamEnd::tryEmitEmpty);
}
- private Publisher interceptUpdate(TdApi.Object update) {
+ private Mono interceptUpdate(TdApi.Object update) {
switch (update.getConstructor()) {
case TdApi.UpdateAuthorizationState.CONSTRUCTOR:
var updateAuthorizationState = (TdApi.UpdateAuthorizationState) update;
switch (updateAuthorizationState.authorizationState.getConstructor()) {
case TdApi.AuthorizationStateClosed.CONSTRUCTOR:
- return cluster
- .getEventBus()
- .rxRequest(this.botAddress + ".read-binlog", EMPTY).as(MonoUtils::toMono)
+ return Mono.fromRunnable(() -> logger.trace("Received AuthorizationStateClosed from tdlib"))
+ .then(cluster.getEventBus().rxRequest(this.botAddress + ".read-binlog", EMPTY).as(MonoUtils::toMono))
.doOnNext(l -> logger.info("Received binlog from server. Size: " + BinlogUtils.humanReadableByteCountBin(l.body().binlog().length)))
.flatMap(latestBinlog -> this.saveBinlog(latestBinlog.body().binlog()))
.doOnSuccess(s -> logger.info("Overwritten binlog from server"))
@@ -182,16 +197,20 @@ public class AsyncTdMiddleEventBusClient implements AsyncTdMiddle {
return Mono
.firstWithSignal(
MonoUtils.castVoid(crash.asMono()),
- cluster.getEventBus()
- .rxRequest(botAddress + ".execute", req, deliveryOptions).as(MonoUtils::toMono)
+ Mono
+ .fromRunnable(() -> logger.trace("Executing request {}", request))
+ .then(cluster.getEventBus().rxRequest(botAddress + ".execute", req, deliveryOptions).as(MonoUtils::toMono))
.onErrorMap(ex -> ResponseError.newResponseError(request, botAlias, ex))
- .>flatMap(resp -> Mono.fromCallable(() -> {
- if (resp.body() == null) {
- throw ResponseError.newResponseError(request, botAlias, new TdError(500, "Response is empty"));
- } else {
- return resp.body().toTdResult();
- }
- }))
+ .>flatMap(resp -> Mono
+ .fromCallable(() -> {
+ if (resp.body() == null) {
+ throw ResponseError.newResponseError(request, botAlias, new TdError(500, "Response is empty"));
+ } else {
+ return resp.body().toTdResult();
+ }
+ })
+ )
+ .doOnSuccess(s -> logger.trace("Executed request"))
)
.switchIfEmpty(Mono.defer(() -> Mono.fromCallable(() -> {
throw ResponseError.newResponseError(request, botAlias, new TdError(500, "Client is closed or response is empty"));
diff --git a/src/main/java/it/tdlight/tdlibsession/td/middle/direct/AsyncTdMiddleDirect.java b/src/main/java/it/tdlight/tdlibsession/td/middle/direct/AsyncTdMiddleDirect.java
index 3afb7d8..a3ad09a 100644
--- a/src/main/java/it/tdlight/tdlibsession/td/middle/direct/AsyncTdMiddleDirect.java
+++ b/src/main/java/it/tdlight/tdlibsession/td/middle/direct/AsyncTdMiddleDirect.java
@@ -12,6 +12,7 @@ import it.tdlight.tdlibsession.td.ResponseError;
import it.tdlight.tdlibsession.td.TdResult;
import it.tdlight.tdlibsession.td.direct.AsyncTdDirectImpl;
import it.tdlight.tdlibsession.td.direct.AsyncTdDirectOptions;
+import it.tdlight.tdlibsession.td.direct.TelegramClientFactory;
import it.tdlight.tdlibsession.td.middle.AsyncTdMiddle;
import it.tdlight.tdlibsession.td.middle.TdClusterManager;
import it.tdlight.utils.MonoUtils;
@@ -26,21 +27,26 @@ public class AsyncTdMiddleDirect extends AbstractVerticle implements AsyncTdMidd
private static final Logger logger = LoggerFactory.getLogger(AsyncTdMiddleDirect.class);
+ private final TelegramClientFactory clientFactory;
+
protected AsyncTdDirectImpl td;
private String botAddress;
private String botAlias;
private final Empty