From 374ebeea44987e3ecd56f05305113e9f50b82a3c Mon Sep 17 00:00:00 2001 From: jarvis2f <137974272+jarvis2f@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:46:13 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20Fix=20the=20problem=20of?= =?UTF-8?q?=20not=20being=20able=20to=20find=20chats=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../telegram/files/AutoDownloadVerticle.java | 4 +- .../java/telegram/files/TelegramChats.java | 194 ++++++++++++++++++ .../java/telegram/files/TelegramClient.java | 67 ++++++ .../telegram/files/TelegramRunException.java | 16 ++ .../telegram/files/TelegramUpdateHandler.java | 15 ++ .../java/telegram/files/TelegramVerticle.java | 151 ++++---------- web/src/components/chat-select.tsx | 23 ++- 7 files changed, 350 insertions(+), 120 deletions(-) create mode 100644 api/src/main/java/telegram/files/TelegramChats.java create mode 100644 api/src/main/java/telegram/files/TelegramClient.java create mode 100644 api/src/main/java/telegram/files/TelegramRunException.java diff --git a/api/src/main/java/telegram/files/AutoDownloadVerticle.java b/api/src/main/java/telegram/files/AutoDownloadVerticle.java index 262bf1e..1aa1527 100644 --- a/api/src/main/java/telegram/files/AutoDownloadVerticle.java +++ b/api/src/main/java/telegram/files/AutoDownloadVerticle.java @@ -147,7 +147,7 @@ public class AutoDownloadVerticle extends AbstractVerticle { searchChatMessages.fromMessageId = auto.nextFromMessageId; searchChatMessages.limit = Math.min(MAX_WAITING_LENGTH, 100); searchChatMessages.filter = TdApiHelp.getSearchMessagesFilter(auto.nextFileType); - TdApi.FoundChatMessages foundChatMessages = Future.await(telegramVerticle.execute(searchChatMessages) + TdApi.FoundChatMessages foundChatMessages = Future.await(telegramVerticle.client.execute(searchChatMessages) .onFailure(r -> log.error("Search chat messages failed! TelegramId: %d ChatId: %d".formatted(auto.telegramId, auto.chatId), r)) ); if (foundChatMessages == null) { @@ -307,7 +307,7 @@ public class AutoDownloadVerticle extends AbstractVerticle { .flatMap(item -> HttpVerticle.getTelegramVerticle(telegramId)) .ifPresent(telegramVerticle -> { if (telegramVerticle.authorized) { - telegramVerticle.execute(new TdApi.GetMessage(chatId, messageId)) + telegramVerticle.client.execute(new TdApi.GetMessage(chatId, messageId)) .onSuccess(message -> addWaitingDownloadMessages(telegramId, List.of(message), true)) .onFailure(e -> log.error("Auto download fail. Get message failed: %s".formatted(e.getMessage()))); } diff --git a/api/src/main/java/telegram/files/TelegramChats.java b/api/src/main/java/telegram/files/TelegramChats.java new file mode 100644 index 0000000..5aed278 --- /dev/null +++ b/api/src/main/java/telegram/files/TelegramChats.java @@ -0,0 +1,194 @@ +package telegram.files; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import org.drinkless.tdlib.TdApi; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +public class TelegramChats { + private static final Log log = LogFactory.get(); + + private final TelegramClient client; + + private final ConcurrentMap chats = new ConcurrentHashMap<>(); + + private final NavigableSet mainChatList = new TreeSet<>(); + + private boolean haveFullMainChatList = false; + + public TelegramChats(TelegramClient client) { + this.client = client; + } + + public List getMainChatList(Long activatedChatId, String query, int limit) { + List chatList = mainChatList.stream() + .map(OrderedChat::chatId) + .map(chats::get) + .filter(Objects::nonNull) + .filter(chat -> StrUtil.isBlank(query) || chat.title.contains(query)) + .limit(limit) + .collect(Collectors.toList()); + + if (activatedChatId != null) { + TdApi.Chat activatedChat = chats.get(activatedChatId); + if (activatedChat != null && !chatList.contains(activatedChat)) { + chatList.addFirst(activatedChat); + } + } + + return chatList; + } + + public void loadMainChatList() { + synchronized (mainChatList) { + if (!haveFullMainChatList) { + // send LoadChats request if there are some unknown chats and have not enough known chats + client.execute(new TdApi.LoadChats(new TdApi.ChatListMain(), 100)) + .onSuccess(object -> { + // chats had already been received through updates, let's retry request + loadMainChatList(); + }) + .onFailure(error -> { + if (((TelegramRunException) error).getError().code == 404) { + synchronized (mainChatList) { + haveFullMainChatList = true; + log.debug("Main chat list is loaded, size: %d".formatted(mainChatList.size())); + } + } + }); + } + } + } + + public void onChatUpdated(TdApi.Object object) { + switch (object.getConstructor()) { + 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.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.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; + } + } + } + + private 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) { + if (!mainChatList.remove(new OrderedChat(chat.id, position))) { + log.warn("Chat %d was not found in mainChatList".formatted(chat.id)); + } + } + } + + chat.positions = positions; + + for (TdApi.ChatPosition position : chat.positions) { + if (position.list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) { + if (!mainChatList.add(new OrderedChat(chat.id, position))) { + log.warn("Chat %d was already in mainChatList".formatted(chat.id)); + } + } + } + } + } + } + + private record OrderedChat(long chatId, TdApi.ChatPosition position) implements Comparable { + + @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) { + if (obj instanceof OrderedChat(long id, TdApi.ChatPosition position1)) { + return this.chatId == id && this.position.order == position1.order; + } + return false; + } + } +} diff --git a/api/src/main/java/telegram/files/TelegramClient.java b/api/src/main/java/telegram/files/TelegramClient.java new file mode 100644 index 0000000..5b509c9 --- /dev/null +++ b/api/src/main/java/telegram/files/TelegramClient.java @@ -0,0 +1,67 @@ +package telegram.files; + +import cn.hutool.core.util.TypeUtil; +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import io.vertx.core.Future; +import org.drinkless.tdlib.Client; +import org.drinkless.tdlib.TdApi; + +import java.io.IOError; +import java.io.IOException; + +public class TelegramClient { + private static final Log log = LogFactory.get(); + + private Client client; + + private boolean initialized = false; + + static { + Client.setLogMessageHandler(0, new LogMessageHandler()); + + try { + Client.execute(new TdApi.SetLogVerbosityLevel(Config.TELEGRAM_LOG_LEVEL)); + Client.execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false))); + } catch (Client.ExecutionException error) { + throw new IOError(new IOException("Write access to the current directory is required")); + } + } + + public void initialize(Client.ResultHandler updateHandler, + Client.ExceptionHandler updateExceptionHandler, + Client.ExceptionHandler defaultExceptionHandler) { + synchronized (this) { + if (!initialized) { + client = Client.create(updateHandler, updateExceptionHandler, defaultExceptionHandler); + initialized = true; + } + } + } + + @SuppressWarnings("unchecked") + public Future execute(TdApi.Function method) { + log.trace("Execute method: %s".formatted(TypeUtil.getTypeArgument(method.getClass()))); + if (!initialized) { + throw new IllegalStateException("Client is not initialized"); + } + return Future.future(promise -> client.send(method, object -> { + if (object.getConstructor() == TdApi.Error.CONSTRUCTOR) { + promise.fail(new TelegramRunException((TdApi.Error) object)); + } else { + promise.complete((R) object); + } + })); + } + + public Client getNativeClient() { + return client; + } + + private static class LogMessageHandler implements Client.LogMessageHandler { + @Override + public void onLogMessage(int verbosityLevel, String message) { + log.debug("TDLib: %s".formatted(message)); + } + } +} diff --git a/api/src/main/java/telegram/files/TelegramRunException.java b/api/src/main/java/telegram/files/TelegramRunException.java new file mode 100644 index 0000000..ca35c27 --- /dev/null +++ b/api/src/main/java/telegram/files/TelegramRunException.java @@ -0,0 +1,16 @@ +package telegram.files; + +import org.drinkless.tdlib.TdApi; + +public class TelegramRunException extends RuntimeException { + private final TdApi.Error error; + + public TelegramRunException(TdApi.Error error) { + super(error.message); + this.error = error; + } + + public TdApi.Error getError() { + return error; + } +} diff --git a/api/src/main/java/telegram/files/TelegramUpdateHandler.java b/api/src/main/java/telegram/files/TelegramUpdateHandler.java index 5306e98..c5b766b 100644 --- a/api/src/main/java/telegram/files/TelegramUpdateHandler.java +++ b/api/src/main/java/telegram/files/TelegramUpdateHandler.java @@ -17,6 +17,8 @@ public class TelegramUpdateHandler implements Client.ResultHandler { private Consumer onFileDownloadsUpdated; + private Consumer onChatUpdated; + private Consumer onMessageReceived; @Override @@ -40,6 +42,15 @@ public class TelegramUpdateHandler implements Client.ResultHandler { if (onMessageReceived != null) { onMessageReceived.accept(((TdApi.UpdateNewMessage) object).message); } + case TdApi.UpdateNewChat.CONSTRUCTOR: + case TdApi.UpdateChatTitle.CONSTRUCTOR: + case TdApi.UpdateChatPhoto.CONSTRUCTOR: + case TdApi.UpdateChatReadInbox.CONSTRUCTOR: + case TdApi.UpdateChatLastMessage.CONSTRUCTOR: + case TdApi.UpdateChatPosition.CONSTRUCTOR: + if (onChatUpdated != null) { + onChatUpdated.accept(object); + } default: log.trace("Unsupported telegram update: %s".formatted(object)); } @@ -57,6 +68,10 @@ public class TelegramUpdateHandler implements Client.ResultHandler { this.onFileDownloadsUpdated = onFileDownloadsUpdated; } + public void setOnChatUpdated(Consumer onChatUpdated) { + this.onChatUpdated = onChatUpdated; + } + public void setOnMessageReceived(Consumer onMessageReceived) { this.onMessageReceived = onMessageReceived; } diff --git a/api/src/main/java/telegram/files/TelegramVerticle.java b/api/src/main/java/telegram/files/TelegramVerticle.java index a42115f..1f23d2b 100644 --- a/api/src/main/java/telegram/files/TelegramVerticle.java +++ b/api/src/main/java/telegram/files/TelegramVerticle.java @@ -13,25 +13,23 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.TypeUtil; import cn.hutool.log.Log; import cn.hutool.log.LogFactory; -import io.vertx.core.*; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; import io.vertx.core.impl.NoStackTraceException; import io.vertx.core.json.Json; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import org.drinkless.tdlib.Client; import org.drinkless.tdlib.TdApi; import org.jooq.lambda.tuple.Tuple; import org.jooq.lambda.tuple.Tuple2; import telegram.files.repository.*; import java.io.File; -import java.io.IOError; -import java.io.IOException; import java.util.*; -import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -40,7 +38,9 @@ public class TelegramVerticle extends AbstractVerticle { private static final Log log = LogFactory.get(); - private Client client; + public TelegramClient client; + + private TelegramChats telegramChats; public boolean authorized = false; @@ -64,17 +64,6 @@ public class TelegramVerticle extends AbstractVerticle { private long lastFileDownloadEventTime; - static { - Client.setLogMessageHandler(0, new LogMessageHandler()); - - try { - Client.execute(new TdApi.SetLogVerbosityLevel(Config.TELEGRAM_LOG_LEVEL)); - Client.execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false))); - } catch (Client.ExecutionException error) { - throw new IOError(new IOException("Write access to the current directory is required")); - } - } - public TelegramVerticle(String rootPath) { this.rootPath = rootPath; } @@ -102,13 +91,16 @@ public class TelegramVerticle extends AbstractVerticle { @Override public void start(Promise startPromise) { + client = new TelegramClient(); + telegramChats = new TelegramChats(client); TelegramUpdateHandler telegramUpdateHandler = new TelegramUpdateHandler(); telegramUpdateHandler.setOnAuthorizationStateUpdated(this::onAuthorizationStateUpdated); telegramUpdateHandler.setOnFileUpdated(this::onFileUpdated); telegramUpdateHandler.setOnFileDownloadsUpdated(this::onFileDownloadsUpdated); + telegramUpdateHandler.setOnChatUpdated(telegramChats::onChatUpdated); telegramUpdateHandler.setOnMessageReceived(this::onMessageReceived); - client = Client.create(telegramUpdateHandler, this::handleException, this::handleException); + client.initialize(telegramUpdateHandler, this::handleException, this::handleException); Future.all(initEventConsumer(), initAvgSpeed()) .compose(r -> this.enableProxy(this.proxyName)) .onSuccess(r -> startPromise.complete()) @@ -122,7 +114,7 @@ public class TelegramVerticle extends AbstractVerticle { } public Future close(boolean needDelete) { - return this.execute(new TdApi.Close()) + return client.execute(new TdApi.Close()) .onSuccess(r -> { log.info("[%s] Telegram account closed".formatted(this.getRootId())); this.needDelete = needDelete; @@ -159,7 +151,7 @@ public class TelegramVerticle extends AbstractVerticle { promise.complete(jsonObject); return; } - this.execute(new TdApi.GetMe()) + client.execute(new TdApi.GetMe()) .onSuccess(user -> { JsonObject result = new JsonObject() .put("id", Convert.toStr(user.id)) @@ -180,42 +172,7 @@ public class TelegramVerticle extends AbstractVerticle { } public Future getChats(Long activatedChatId, String query) { - Consumer insertActivatedChatId = chats -> { - if (chats == null || ArrayUtil.isEmpty(chats.chatIds)) { - return; - } - long[] chatIds = chats.chatIds; - if (activatedChatId != null && !ArrayUtil.contains(chatIds, activatedChatId)) { - chatIds = (long[]) ArrayUtil.insert(chatIds, 0, activatedChatId); - chats.chatIds = chatIds; - } - }; - - if (StrUtil.isBlank(query)) { - return this.execute(new TdApi.GetChats(new TdApi.ChatListMain(), 10)) - .map(chats -> { - insertActivatedChatId.accept(chats); - return chats; - }) - .compose(chats -> Future.all(Arrays.stream(chats.chatIds) - .mapToObj(chatId -> this.execute(new TdApi.GetChat(chatId))) - .toList()) - ) - .map(CompositeFuture::list) - .compose(this::convertChat); - } else { - return this.execute(new TdApi.SearchChatsOnServer(query, 10)) - .map(chats -> { - insertActivatedChatId.accept(chats); - return chats; - }) - .compose(chats -> Future.all(Arrays.stream(chats.chatIds) - .mapToObj(chatId -> this.execute(new TdApi.GetChat(chatId))) - .toList()) - ) - .map(CompositeFuture::list) - .compose(this::convertChat); - } + return this.convertChat(telegramChats.getMainChatList(activatedChatId, query, 100)); } public Future getChatFiles(long chatId, MultiMap filter) { @@ -224,7 +181,7 @@ public class TelegramVerticle extends AbstractVerticle { return DataVerticle.fileRepository.getFiles(chatId, filter) .compose(r -> { long[] messageIds = r.v1.stream().mapToLong(FileRecord::messageId).toArray(); - return this.execute(new TdApi.GetMessages(chatId, messageIds)) + return client.execute(new TdApi.GetMessages(chatId, messageIds)) .map(m -> { Map messageMap = Arrays.stream(m.messages) .filter(Objects::nonNull) @@ -261,7 +218,7 @@ public class TelegramVerticle extends AbstractVerticle { return (Objects.equals(filter.get("status"), FileRecord.DownloadStatus.idle.name()) ? this.getIdleChatFiles(searchChatMessages, 0) : - this.execute(searchChatMessages)) + client.execute(searchChatMessages)) .compose(foundChatMessages -> DataVerticle.fileRepository.getFilesByUniqueId(TdApiHelp.getFileUniqueIds(Arrays.asList(foundChatMessages.messages))) .map(fileRecords -> Tuple.tuple(foundChatMessages, fileRecords))) @@ -274,7 +231,7 @@ public class TelegramVerticle extends AbstractVerticle { // Increase the limit and reduce the number of requests searchChatMessages.limit = 100; } - return this.execute(searchChatMessages) + return client.execute(searchChatMessages) .compose(foundChatMessages -> { TdApi.Message[] messages = Stream.of(foundChatMessages.messages) .filter(message -> @@ -305,7 +262,7 @@ public class TelegramVerticle extends AbstractVerticle { new TdApi.SearchMessagesFilterVideo(), new TdApi.SearchMessagesFilterAudio(), new TdApi.SearchMessagesFilterDocument()) - .map(filter -> this.execute( + .map(filter -> client.execute( new TdApi.GetChatMessageCount(chatId, filter, 0, @@ -331,7 +288,7 @@ public class TelegramVerticle extends AbstractVerticle { if (!(boolean) needToLoadImages) { return Future.failedFuture("Need to load images is disabled"); } - return this.execute(new TdApi.GetMessage(chatId, messageId)); + return client.execute(new TdApi.GetMessage(chatId, messageId)); }) .compose(message -> { TdApiHelp.FileHandler fileHandler = TdApiHelp.getFileHandler(message) @@ -356,7 +313,7 @@ public class TelegramVerticle extends AbstractVerticle { downloadFile.priority = 32; downloadFile.synchronous = true; - return this.execute(downloadFile) + return client.execute(downloadFile) .map(file.id); }); }); @@ -386,8 +343,8 @@ public class TelegramVerticle extends AbstractVerticle { public Future startDownload(Long chatId, Long messageId, Integer fileId) { return Future.all( - this.execute(new TdApi.GetFile(fileId)), - this.execute(new TdApi.GetMessage(chatId, messageId)) + client.execute(new TdApi.GetFile(fileId)), + client.execute(new TdApi.GetMessage(chatId, messageId)) ) .compose(results -> { TdApi.File file = results.resultAt(0); @@ -413,7 +370,7 @@ public class TelegramVerticle extends AbstractVerticle { FileRecord fileRecord = fileHandler.convertFileRecord(telegramRecord.id()); return DataVerticle.fileRepository.create(fileRecord) .compose(r -> - this.execute(new TdApi.AddFileToDownloads(fileId, chatId, messageId, 32)) + client.execute(new TdApi.AddFileToDownloads(fileId, chatId, messageId, 32)) ) .onSuccess(r -> sendHttpEvent(EventPayload.build(EventPayload.TYPE_FILE_STATUS, new JsonObject() @@ -425,7 +382,7 @@ public class TelegramVerticle extends AbstractVerticle { } public Future cancelDownload(Integer fileId) { - return this.execute(new TdApi.GetFile(fileId)) + return client.execute(new TdApi.GetFile(fileId)) .compose(file -> DataVerticle.fileRepository .updateFileId(file.id, file.remote.uniqueId) .map(file) @@ -435,10 +392,10 @@ public class TelegramVerticle extends AbstractVerticle { return Future.failedFuture("File not started downloading"); } - return this.execute(new TdApi.CancelDownloadFile(fileId, false)) + return client.execute(new TdApi.CancelDownloadFile(fileId, false)) .map(file); }) - .compose(file -> this.execute(new TdApi.DeleteFile(fileId)).map(file)) + .compose(file -> client.execute(new TdApi.DeleteFile(fileId)).map(file)) .compose(file -> DataVerticle.fileRepository.deleteByUniqueId(file.remote.uniqueId)) .onSuccess(r -> sendHttpEvent(EventPayload.build(EventPayload.TYPE_FILE_STATUS, new JsonObject() @@ -449,7 +406,7 @@ public class TelegramVerticle extends AbstractVerticle { } public Future togglePauseDownload(Integer fileId, boolean isPaused) { - return this.execute(new TdApi.GetFile(fileId)) + return client.execute(new TdApi.GetFile(fileId)) .compose(file -> DataVerticle.fileRepository .updateFileId(file.id, file.remote.uniqueId) .map(file) @@ -481,7 +438,7 @@ public class TelegramVerticle extends AbstractVerticle { return Future.failedFuture("File is downloading"); } - return this.execute(new TdApi.ToggleDownloadIsPaused(fileId, isPaused)); + return client.execute(new TdApi.ToggleDownloadIsPaused(fileId, isPaused)); }) .mapEmpty(); } @@ -512,7 +469,7 @@ public class TelegramVerticle extends AbstractVerticle { public Future getDownloadStatistics() { return Future.all(DataVerticle.fileRepository.getDownloadStatistics(this.telegramRecord.id()), - this.execute(new TdApi.GetNetworkStatistics()) + client.execute(new TdApi.GetNetworkStatistics()) ).map(r -> { JsonObject jsonObject = r.resultAt(0); TdApi.NetworkStatistics networkStatistics = r.resultAt(1); @@ -587,8 +544,8 @@ public class TelegramVerticle extends AbstractVerticle { } else { return Future.failedFuture("Unsupported proxy type: %s".formatted(proxy.type)); } - return edit ? this.execute(new TdApi.EditProxy(tdProxy.id, proxy.server, proxy.port, true, proxyType)) - : this.execute(new TdApi.AddProxy(proxy.server, proxy.port, true, proxyType)); + return edit ? client.execute(new TdApi.EditProxy(tdProxy.id, proxy.server, proxy.port, true, proxyType)) + : client.execute(new TdApi.AddProxy(proxy.server, proxy.port, true, proxyType)); }) .compose(r -> { if (this.telegramRecord != null) { @@ -611,7 +568,7 @@ public class TelegramVerticle extends AbstractVerticle { if (StrUtil.isBlank(toggleProxyName) && StrUtil.isNotBlank(this.proxyName)) { // disable proxy - return this.execute(new TdApi.DisableProxy()) + return client.execute(new TdApi.DisableProxy()) .compose(r -> DataVerticle.telegramRepository.update(this.telegramRecord.withProxy(null))) .andThen(r -> { this.proxyName = null; @@ -624,7 +581,7 @@ public class TelegramVerticle extends AbstractVerticle { } public Future getTdProxy(SettingProxyRecords.Item proxy) { - return this.execute(new TdApi.GetProxies()) + return client.execute(new TdApi.GetProxies()) .map(proxies -> Stream.of(proxies.proxies) .filter(proxy::equalsTdProxy) .findFirst() @@ -632,7 +589,7 @@ public class TelegramVerticle extends AbstractVerticle { } public Future getTdProxy() { - return this.execute(new TdApi.GetProxies()) + return client.execute(new TdApi.GetProxies()) .map(proxies -> Stream.of(proxies.proxies) .filter(p -> p.isEnabled) .findFirst() @@ -641,28 +598,10 @@ public class TelegramVerticle extends AbstractVerticle { public Future ping() { return this.getTdProxy() - .compose(proxy -> this.execute(new TdApi.PingProxy(proxy == null ? 0 : proxy.id))) + .compose(proxy -> client.execute(new TdApi.PingProxy(proxy == null ? 0 : proxy.id))) .map(r -> r.seconds); } - @SuppressWarnings("unchecked") - public Future execute(TdApi.Function method) { - log.trace("[%s] Execute method: %s".formatted(getRootId(), TypeUtil.getTypeArgument(method.getClass()))); - return Future.future(promise -> { - // if (!authorized) { - // promise.fail("Telegram account not found or not authorized"); - // return; - // } - client.send(method, object -> { - if (object.getConstructor() == TdApi.Error.CONSTRUCTOR) { - promise.fail("Execute method failed. " + object); - } else { - promise.complete((R) object); - } - }); - }); - } - public Future execute(String method, Object params) { String code = RandomUtil.randomString(10); log.trace("[%s] Execute code: %s method: %s, params: %s".formatted(getRootId(), code, method, params)); @@ -672,7 +611,7 @@ public class TelegramVerticle extends AbstractVerticle { promise.fail("Unsupported method: " + method); return; } - client.send(func, object -> { + client.getNativeClient().send(func, object -> { log.debug("[%s] Execute: [%s] Receive result: %s".formatted(getRootId(), code, object)); handleDefaultResult(object, code); }); @@ -786,7 +725,7 @@ public class TelegramVerticle extends AbstractVerticle { request.applicationVersion = Start.VERSION; log.trace("[%s] Send SetTdlibParameters: %s".formatted(getRootId(), request)); - client.send(request, this::handleAuthorizationResult); + client.execute(request).onSuccess(this::handleAuthorizationResult); break; case TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR: case TdApi.AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR: @@ -800,20 +739,17 @@ public class TelegramVerticle extends AbstractVerticle { case TdApi.AuthorizationStateReady.CONSTRUCTOR: authorized = true; if (telegramRecord == null) { - this.execute(new TdApi.GetMe()) + client.execute(new TdApi.GetMe()) .compose(user -> DataVerticle.telegramRepository.create(new TelegramRecord(user.id, user.firstName, this.rootPath, this.proxyName)) ) - .compose(record -> { - telegramRecord = record; - return this.execute(new TdApi.LoadChats(new TdApi.ChatListMain(), 10)); - }) .onSuccess(o -> log.info("[%s] %s Authorization Ready".formatted(getRootId(), this.telegramRecord.firstName()))) .onFailure(e -> log.error("[%s] Authorization Ready, but failed to create telegram record: %s".formatted(getRootId(), e.getMessage()))); } else { log.info("[%s] %s Authorization Ready".formatted(getRootId(), this.telegramRecord.firstName())); } sendHttpEvent(EventPayload.build(EventPayload.TYPE_AUTHORIZATION, authorizationState)); + telegramChats.loadMainChatList(); break; case TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR: break; @@ -839,7 +775,6 @@ public class TelegramVerticle extends AbstractVerticle { if (file != null) { String localPath = null; Long completionDate = null; - long downloadedSize = file.local == null ? 0 : file.local.downloadedSize; if (file.local != null && file.local.isDownloadingCompleted) { localPath = file.local.path; completionDate = System.currentTimeMillis(); @@ -876,14 +811,6 @@ public class TelegramVerticle extends AbstractVerticle { ); } - private static class LogMessageHandler implements Client.LogMessageHandler { - - @Override - public void onLogMessage(int verbosityLevel, String message) { - log.debug("TDLib: %s".formatted(message)); - } - } - //<-----------------------------convert----------------------------------> private Future convertChat(List chats) { diff --git a/web/src/components/chat-select.tsx b/web/src/components/chat-select.tsx index fbbbad1..5ed3269 100644 --- a/web/src/components/chat-select.tsx +++ b/web/src/components/chat-select.tsx @@ -37,7 +37,7 @@ export default function ChatSelect({ disabled }: { disabled: boolean }) { variant="outline" role="combobox" aria-expanded={open} - className="w-full md:w-[250px] justify-between" + className="w-full justify-between md:w-[250px]" > {selectedChat ? (
@@ -45,10 +45,15 @@ export default function ChatSelect({ disabled }: { disabled: boolean }) { - {selectedChat.name[0]} + + {" "} + {selectedChat.name[0] ?? selectedChat.id[0]} + - {selectedChat.name} + {(selectedChat.name ?? "").length > 0 + ? selectedChat.name + : selectedChat.id}
) : ( @@ -73,7 +78,9 @@ export default function ChatSelect({ disabled }: { disabled: boolean }) { )} - {!isLoading && chats.length === 0 && "No chat found."} + + {!isLoading && chats.length === 0 && "No chat found."} + {chats.map((chat) => ( - {chat.name[0]} + + {chat.name[0] ?? chat.id[0]} +
- {chat.name} + + {(chat.name ?? "").length > 0 ? chat.name : chat.id} + {chat.type} • {chat.unreadCount ?? 0} unread