feat: Improved search for file status

This commit is contained in:
jarvis2f 2024-12-22 12:58:03 +08:00
parent fe6feeb25b
commit 8086e8a485
5 changed files with 117 additions and 26 deletions

View file

@ -298,7 +298,7 @@ public class HttpVerticle extends AbstractVerticle {
return;
}
TelegramVerticle telegramVerticle = telegramVerticleOptional.get();
telegramVerticle.stop();
telegramVerticle.delete();
telegramVerticles.remove(telegramVerticle);
sessionTelegramVerticles.entrySet().removeIf(e -> e.getValue().equals(telegramVerticle));
ctx.end();

View file

@ -52,6 +52,8 @@ public class TelegramVerticle extends AbstractVerticle {
private String rootId;
private boolean needDelete = false;
public TelegramRecord telegramRecord;
static {
@ -95,14 +97,13 @@ public class TelegramVerticle extends AbstractVerticle {
client = Client.create(telegramUpdateHandler, this::handleException, this::handleException);
}
@Override
public void stop() {
if (!this.authorized || this.telegramRecord == null) {
File root = FileUtil.file(this.rootPath);
if (root.exists()) {
FileUtil.del(root);
}
}
public void delete() {
this.execute(new TdApi.Close())
.onSuccess(r -> {
log.info("[%s] Telegram account closed".formatted(this.getRootId()));
this.needDelete = true;
})
.onFailure(e -> log.error("[%s] Failed to close telegram account: %s".formatted(this.getRootId(), e.getMessage())));
}
public boolean check() {
@ -192,19 +193,30 @@ public class TelegramVerticle extends AbstractVerticle {
}
public Future<JsonObject> getChatFiles(long chatId, MultiMap filter) {
TdApi.SearchChatMessages searchChatMessages = new TdApi.SearchChatMessages();
searchChatMessages.chatId = chatId;
searchChatMessages.query = filter.get("search");
searchChatMessages.fromMessageId = Convert.toLong(filter.get("fromMessageId"), 0L);
searchChatMessages.offset = Convert.toInt(filter.get("offset"), 0);
searchChatMessages.limit = Convert.toInt(filter.get("limit"), 20);
searchChatMessages.filter = TdApiHelp.getSearchMessagesFilter(filter.get("type"));
String status = filter.get("status");
if (Arrays.asList("downloading", "paused", "completed", "error").contains(status)) {
return DataVerticle.fileRepository.getFiles(chatId, filter)
.map(tuple -> new JsonObject()
.put("files", tuple.v1)
.put("nextFromMessageId", tuple.v2)
.put("count", tuple.v3)
.put("size", tuple.v1.size())
);
} else {
TdApi.SearchChatMessages searchChatMessages = new TdApi.SearchChatMessages();
searchChatMessages.chatId = chatId;
searchChatMessages.query = filter.get("search");
searchChatMessages.fromMessageId = Convert.toLong(filter.get("fromMessageId"), 0L);
searchChatMessages.offset = Convert.toInt(filter.get("offset"), 0);
searchChatMessages.limit = Convert.toInt(filter.get("limit"), 20);
searchChatMessages.filter = TdApiHelp.getSearchMessagesFilter(filter.get("type"));
return this.execute(searchChatMessages)
.compose(foundChatMessages ->
DataVerticle.fileRepository.getFilesByUniqueId(TdApiHelp.getFileUniqueIds(Arrays.asList(foundChatMessages.messages)))
.map(fileRecords -> Tuple.tuple(foundChatMessages, fileRecords)))
.compose(this::convertFiles);
return this.execute(searchChatMessages)
.compose(foundChatMessages ->
DataVerticle.fileRepository.getFilesByUniqueId(TdApiHelp.getFileUniqueIds(Arrays.asList(foundChatMessages.messages)))
.map(fileRecords -> Tuple.tuple(foundChatMessages, fileRecords)))
.compose(r -> this.convertFiles(r, filter));
}
}
public Future<JsonObject> getChatFilesCount(long chatId) {
@ -394,10 +406,10 @@ public class TelegramVerticle extends AbstractVerticle {
public <R extends TdApi.Object> Future<R> execute(TdApi.Function<R> 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;
}
// 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);
@ -512,6 +524,13 @@ public class TelegramVerticle extends AbstractVerticle {
case TdApi.AuthorizationStateClosing.CONSTRUCTOR:
break;
case TdApi.AuthorizationStateClosed.CONSTRUCTOR:
if (needDelete) {
File root = FileUtil.file(this.rootPath);
if (root.exists()) {
FileUtil.del(root);
}
log.info("[%s] Telegram account deleted".formatted(this.getRootId()));
}
break;
default:
log.warn("[%s] Unsupported authorization state received:%s".formatted(this.getRootId(), authorizationState));
@ -586,9 +605,10 @@ public class TelegramVerticle extends AbstractVerticle {
));
}
private Future<JsonObject> convertFiles(Tuple2<TdApi.FoundChatMessages, Map<String, FileRecord>> tuple) {
private Future<JsonObject> convertFiles(Tuple2<TdApi.FoundChatMessages, Map<String, FileRecord>> tuple, MultiMap filter) {
TdApi.FoundChatMessages foundChatMessages = tuple.v1;
Map<String, FileRecord> fileRecords = tuple.v2;
boolean searchIdle = Objects.equals(filter.get("status"), FileRecord.DownloadStatus.idle.name());
return DataVerticle.settingRepository.<Boolean>getByKey(SettingKey.uniqueOnly)
.map(uniqueOnly -> {
@ -613,6 +633,10 @@ public class TelegramVerticle extends AbstractVerticle {
} else {
fileRecord = fileRecord.withSourceField(source.id(), source.downloadedSize());
}
if (searchIdle && !Objects.equals(fileRecord.downloadStatus(), FileRecord.DownloadStatus.idle.name())) {
return null;
}
//TODO Processing of the same file under different accounts
JsonObject fileObject = JsonObject.mapFrom(fileRecord);

View file

@ -1,7 +1,9 @@
package telegram.files.repository;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.json.JsonObject;
import org.jooq.lambda.tuple.Tuple3;
import java.util.List;
import java.util.Map;
@ -13,6 +15,8 @@ public interface FileRepository {
Future<Map<Integer, FileRecord>> getFiles(long chatId, List<Integer> fileIds);
Future<Tuple3<List<FileRecord>, Long, Long>> getFiles(long chatId, MultiMap filter);
Future<Map<String, FileRecord>> getFilesByUniqueId(List<String> uniqueIds);
Future<FileRecord> getByPrimaryKey(int fileId, String uniqueId);

View file

@ -1,14 +1,19 @@
package telegram.files.repository.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.json.JsonObject;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.templates.SqlTemplate;
import org.jooq.lambda.tuple.Tuple;
import org.jooq.lambda.tuple.Tuple3;
import telegram.files.repository.FileRecord;
import telegram.files.repository.FileRepository;
@ -84,6 +89,60 @@ public class FileRepositoryImpl implements FileRepository {
});
}
@Override
public Future<Tuple3<List<FileRecord>, Long, Long>> getFiles(long chatId, MultiMap filter) {
String status = filter.get("status");
String search = filter.get("search");
Long fromMessageId = Convert.toLong(filter.get("fromMessageId"), 0L);
String type = filter.get("type");
String whereClause = "chat_id = #{chatId}";
Map<String, Object> params = MapUtil.of("chatId", chatId);
if (StrUtil.isNotBlank(status)) {
whereClause += " AND download_status = #{status}";
params.put("status", status);
}
if (StrUtil.isNotBlank(search)) {
whereClause += " AND (file_name LIKE #{search} OR caption LIKE #{search})";
params.put("search", "%%" + search + "%%");
}
if (fromMessageId > 0) {
whereClause += " AND message_id > #{fromMessageId}";
params.put("fromMessageId", fromMessageId);
}
if (StrUtil.isNotBlank(type)) {
if (Objects.equals(type, "media")) {
whereClause += " AND type IN ('photo', 'video')";
} else {
whereClause += " AND type = #{type}";
params.put("type", type);
}
}
return Future.all(
SqlTemplate
.forQuery(pool, """
SELECT * FROM file_record WHERE %s ORDER BY date DESC
""".formatted(whereClause))
.mapTo(FileRecord.ROW_MAPPER)
.execute(params)
.onFailure(err -> log.error("Failed to get file record: %s".formatted(err.getMessage())))
.map(IterUtil::toList)
,
SqlTemplate
.forQuery(pool, """
SELECT COUNT(*) FROM file_record WHERE %s
""".formatted(whereClause))
.mapTo(rs -> rs.getLong(0))
.execute(params)
.onFailure(err -> log.error("Failed to get file record count: %s".formatted(err.getMessage())))
.map(rs -> rs.size() > 0 ? rs.iterator().next() : 0L)
).map(r -> {
List<FileRecord> fileRecords = r.resultAt(0);
long nextFromMessageId = CollUtil.isEmpty(fileRecords) ? 0 : fileRecords.getLast().messageId();
return Tuple.tuple(fileRecords, nextFromMessageId, r.resultAt(1));
});
}
@Override
public Future<Map<String, FileRecord>> getFilesByUniqueId(List<String> uniqueIds) {
uniqueIds = uniqueIds.stream()

View file

@ -60,6 +60,10 @@ Cookie: {{tf}}
GET http://{{server}}/telegram/{{telegramId}}/chat/{{chatId}}/files?fromMessageId=0&type=media&status=all
Cookie: {{tf}}
### Get download completed chat files
GET http://{{server}}/telegram/{{telegramId}}/chat/{{chatId}}/files?fromMessageId=0&type=media&status=completed
Cookie: {{tf}}
### Get chat files count
GET http://{{server}}/telegram/{{telegramId}}/chat/{{chatId}}/files/count
Cookie: {{tf}}