447 lines
16 KiB
Java
447 lines
16 KiB
Java
package telegram.files;
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
import cn.hutool.core.bean.copier.CopyOptions;
|
|
import cn.hutool.core.codec.Base64;
|
|
import cn.hutool.core.collection.CollUtil;
|
|
import cn.hutool.core.convert.Convert;
|
|
import cn.hutool.core.convert.TypeConverter;
|
|
import cn.hutool.core.util.ClassUtil;
|
|
import cn.hutool.core.util.ReflectUtil;
|
|
import io.vertx.core.Future;
|
|
import io.vertx.core.impl.NoStackTraceException;
|
|
import org.drinkless.tdlib.TdApi;
|
|
import org.jooq.lambda.tuple.Tuple;
|
|
import org.jooq.lambda.tuple.Tuple1;
|
|
import telegram.files.repository.FileRecord;
|
|
import telegram.files.repository.SettingKey;
|
|
|
|
import java.util.*;
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public class TdApiHelp {
|
|
|
|
// name -> class
|
|
private static final Map<String, Class<TdApi.Function<?>>> FUNCTIONS = new HashMap<>();
|
|
|
|
// CONSTRUCTOR -> class
|
|
private static final Map<Integer, Class<TdApi.Object>> CONSTRUCTORS = new HashMap<>();
|
|
|
|
public static final List<Integer> FILE_CONTENT_CONSTRUCTORS = Arrays.asList(
|
|
TdApi.MessagePhoto.CONSTRUCTOR,
|
|
TdApi.MessageVideo.CONSTRUCTOR,
|
|
TdApi.MessageAudio.CONSTRUCTOR,
|
|
TdApi.MessageDocument.CONSTRUCTOR
|
|
);
|
|
|
|
private static final TypeConverter TD_API_TYPE_CONVERTER = (targetType, value) -> {
|
|
if (value == null) {
|
|
return null;
|
|
}
|
|
if (value instanceof Map) {
|
|
Map<String, Object> map = (Map<String, Object>) value;
|
|
if (!map.containsKey("@type")) {
|
|
return Convert.convertWithCheck(targetType, value, null, false);
|
|
}
|
|
Integer constructor = Convert.toInt(map.get("@type"));
|
|
Class<TdApi.Object> objClazz = CONSTRUCTORS.get(constructor);
|
|
if (objClazz == null) {
|
|
return null;
|
|
}
|
|
return BeanUtil.toBean(map, objClazz);
|
|
}
|
|
return Convert.convertWithCheck(targetType, value, null, false);
|
|
};
|
|
|
|
private static final CopyOptions COPY_OPTIONS = new CopyOptions().setConverter(TD_API_TYPE_CONVERTER);
|
|
|
|
static {
|
|
Arrays.stream(TdApi.class.getClasses())
|
|
.filter(ClassUtil::isNormalClass)
|
|
.filter(TdApi.Object.class::isAssignableFrom)
|
|
.forEach(clazz -> {
|
|
if (TdApi.Function.class.isAssignableFrom(clazz)) {
|
|
FUNCTIONS.put(clazz.getSimpleName(), (Class<TdApi.Function<?>>) clazz);
|
|
}
|
|
|
|
Object constructor = ReflectUtil.getStaticFieldValue(ReflectUtil.getField(clazz, "CONSTRUCTOR"));
|
|
if (constructor != null) {
|
|
CONSTRUCTORS.put(Convert.toInt(constructor), (Class<TdApi.Object>) clazz);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static TdApi.Function<?> getFunction(String method, Object params) {
|
|
Class<TdApi.Function<?>> func = FUNCTIONS.get(method);
|
|
if (func == null) {
|
|
return null;
|
|
}
|
|
if (params == null) {
|
|
return ReflectUtil.newInstance(func);
|
|
}
|
|
return BeanUtil.toBean(params, func, COPY_OPTIONS);
|
|
}
|
|
|
|
public static String getChatType(TdApi.ChatType type) {
|
|
return switch (type.getConstructor()) {
|
|
case TdApi.ChatTypePrivate.CONSTRUCTOR -> "private";
|
|
case TdApi.ChatTypeBasicGroup.CONSTRUCTOR -> "group";
|
|
case TdApi.ChatTypeSupergroup.CONSTRUCTOR ->
|
|
((TdApi.ChatTypeSupergroup) type).isChannel ? "channel" : "group";
|
|
case TdApi.ChatTypeSecret.CONSTRUCTOR -> "secret";
|
|
default -> "unknown";
|
|
};
|
|
}
|
|
|
|
public static TdApi.SearchMessagesFilter getSearchMessagesFilter(String fileType) {
|
|
return switch (fileType) {
|
|
case "media" -> new TdApi.SearchMessagesFilterPhotoAndVideo();
|
|
case "photo" -> new TdApi.SearchMessagesFilterPhoto();
|
|
case "video" -> new TdApi.SearchMessagesFilterVideo();
|
|
case "audio" -> new TdApi.SearchMessagesFilterAudio();
|
|
case "file" -> new TdApi.SearchMessagesFilterDocument();
|
|
default -> null;
|
|
};
|
|
}
|
|
|
|
public static String getSearchMessagesFilterType(TdApi.SearchMessagesFilter filter) {
|
|
return switch (filter.getConstructor()) {
|
|
case TdApi.SearchMessagesFilterPhotoAndVideo.CONSTRUCTOR -> "media";
|
|
case TdApi.SearchMessagesFilterPhoto.CONSTRUCTOR -> "photo";
|
|
case TdApi.SearchMessagesFilterVideo.CONSTRUCTOR -> "video";
|
|
case TdApi.SearchMessagesFilterAudio.CONSTRUCTOR -> "audio";
|
|
case TdApi.SearchMessagesFilterDocument.CONSTRUCTOR -> "file";
|
|
default -> "unknown";
|
|
};
|
|
}
|
|
|
|
public static List<Integer> getFileIds(List<TdApi.Message> messages) {
|
|
if (CollUtil.isEmpty(messages)) {
|
|
return new ArrayList<>();
|
|
}
|
|
return messages.stream()
|
|
.filter(message -> FILE_CONTENT_CONSTRUCTORS.contains(message.content.getConstructor()))
|
|
.map(TdApiHelp::getFileId)
|
|
.filter(Objects::nonNull)
|
|
.toList();
|
|
}
|
|
|
|
public static List<String> getFileUniqueIds(List<TdApi.Message> messages) {
|
|
if (CollUtil.isEmpty(messages)) {
|
|
return new ArrayList<>();
|
|
}
|
|
return messages.stream()
|
|
.filter(message -> FILE_CONTENT_CONSTRUCTORS.contains(message.content.getConstructor()))
|
|
.map(TdApiHelp::getFileUniqueId)
|
|
.filter(Objects::nonNull)
|
|
.toList();
|
|
}
|
|
|
|
public static Integer getFileId(TdApi.Message message) {
|
|
return getFileHandler(message).map(FileHandler::getFileId).orElse(null);
|
|
}
|
|
|
|
public static String getFileUniqueId(TdApi.Message message) {
|
|
return getFileHandler(message).map(FileHandler::getFileUniqueId).orElse(null);
|
|
}
|
|
|
|
public static List<TdApi.Message> filterUniqueMessages(List<TdApi.Message> messages) {
|
|
if (CollUtil.isEmpty(messages)) return messages;
|
|
|
|
Set<Integer> fileIds = new HashSet<>();
|
|
return messages.stream()
|
|
.filter(message -> {
|
|
Integer fileId = TdApiHelp.getFileId(message);
|
|
if (fileId == null) return false;
|
|
if (fileIds.contains(fileId)) {
|
|
return false;
|
|
}
|
|
fileIds.add(fileId);
|
|
return true;
|
|
})
|
|
.toList();
|
|
}
|
|
|
|
public static FileRecord.DownloadStatus getDownloadStatus(TdApi.File file) {
|
|
if (file == null || file.local == null) {
|
|
return null;
|
|
}
|
|
if (file.local.isDownloadingActive) {
|
|
return FileRecord.DownloadStatus.downloading;
|
|
} else if (file.local.isDownloadingCompleted) {
|
|
return FileRecord.DownloadStatus.completed;
|
|
} else {
|
|
return FileRecord.DownloadStatus.paused;
|
|
}
|
|
}
|
|
|
|
public static <T extends FileHandler<? extends TdApi.MessageContent>> Optional<T> getFileHandler(TdApi.Message message) {
|
|
if (message == null) return Optional.empty();
|
|
switch (message.content.getConstructor()) {
|
|
case TdApi.MessagePhoto.CONSTRUCTOR -> {
|
|
return Optional.of((T) new PhotoHandler(message));
|
|
}
|
|
case TdApi.MessageVideo.CONSTRUCTOR -> {
|
|
return Optional.of((T) new VideoHandler(message));
|
|
}
|
|
case TdApi.MessageAudio.CONSTRUCTOR -> {
|
|
return Optional.of((T) new AudioHandler(message));
|
|
}
|
|
case TdApi.MessageDocument.CONSTRUCTOR -> {
|
|
return Optional.of((T) new DocumentHandler(message));
|
|
}
|
|
default -> {
|
|
return Optional.empty();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static abstract class FileHandler<T extends TdApi.MessageContent> {
|
|
protected TdApi.Message message;
|
|
|
|
protected T content;
|
|
|
|
public FileHandler(TdApi.Message message) {
|
|
this.message = message;
|
|
this.content = (T) message.content;
|
|
}
|
|
|
|
public abstract Integer getFileId();
|
|
|
|
public abstract String getFileUniqueId();
|
|
|
|
public TdApi.File getPreviewFileId(Tuple tuple) {
|
|
throw new UnsupportedOperationException("This message type does not support preview file");
|
|
}
|
|
|
|
public abstract FileRecord convertFileRecord(long telegramId);
|
|
|
|
public T getContent() {
|
|
return content;
|
|
}
|
|
}
|
|
|
|
public static class PhotoHandler extends FileHandler<TdApi.MessagePhoto> {
|
|
|
|
public PhotoHandler(TdApi.Message message) {
|
|
super(message);
|
|
}
|
|
|
|
@Override
|
|
public Integer getFileId() {
|
|
return content.photo.sizes[content.photo.sizes.length - 1].photo.id;
|
|
}
|
|
|
|
@Override
|
|
public String getFileUniqueId() {
|
|
return content.photo.sizes[content.photo.sizes.length - 1].photo.remote.uniqueId;
|
|
}
|
|
|
|
@Override
|
|
public TdApi.File getPreviewFileId(Tuple tuple) {
|
|
TdApi.MessagePhoto messagePhoto = this.content;
|
|
String size = ((Tuple1<String>) tuple).v1;
|
|
|
|
TdApi.PhotoSize photoSize = Arrays.stream(messagePhoto.photo.sizes)
|
|
.map(ComparablePhotoSize::new)
|
|
.filter(comparablePhotoSize -> comparablePhotoSize.compareTo(size) <= 0)
|
|
.max((o1, o2) -> o2.compareTo(o1.getPhotoSize()))
|
|
.map(ComparablePhotoSize::getPhotoSize)
|
|
.stream().findFirst()
|
|
.orElse(null);
|
|
|
|
if (photoSize == null) {
|
|
throw new NoStackTraceException("The photo no size that less than or equal to " + size);
|
|
}
|
|
|
|
return photoSize.photo;
|
|
}
|
|
|
|
@Override
|
|
public FileRecord convertFileRecord(long telegramId) {
|
|
TdApi.PhotoSize photoSize = content.photo.sizes[content.photo.sizes.length - 1];
|
|
return new FileRecord(
|
|
getFileId(),
|
|
photoSize.photo.remote.uniqueId,
|
|
telegramId,
|
|
message.chatId,
|
|
message.id,
|
|
message.date,
|
|
message.hasSensitiveContent,
|
|
photoSize.photo.size == 0 ? photoSize.photo.expectedSize : photoSize.photo.size,
|
|
photoSize.photo.local == null ? 0 : photoSize.photo.local.downloadedSize,
|
|
"photo",
|
|
null,
|
|
null,
|
|
Base64.encode((byte[]) BeanUtil.getProperty(content, "photo.minithumbnail.data")),
|
|
content.caption.text,
|
|
null,
|
|
"idle",
|
|
System.currentTimeMillis(),
|
|
null
|
|
);
|
|
}
|
|
}
|
|
|
|
public static class VideoHandler extends FileHandler<TdApi.MessageVideo> {
|
|
|
|
public VideoHandler(TdApi.Message message) {
|
|
super(message);
|
|
}
|
|
|
|
@Override
|
|
public Integer getFileId() {
|
|
return content.video.video.id;
|
|
}
|
|
|
|
@Override
|
|
public String getFileUniqueId() {
|
|
return content.video.video.remote.uniqueId;
|
|
}
|
|
|
|
@Override
|
|
public TdApi.File getPreviewFileId(Tuple tuple) {
|
|
TdApi.MessageVideo messageVideo = this.content;
|
|
TdApi.Thumbnail thumbnail = messageVideo.video.thumbnail;
|
|
|
|
if (thumbnail == null) {
|
|
throw new NoStackTraceException("Video thumbnail not found");
|
|
}
|
|
|
|
return thumbnail.file;
|
|
}
|
|
|
|
|
|
@Override
|
|
public FileRecord convertFileRecord(long telegramId) {
|
|
TdApi.File file = content.video.video;
|
|
return new FileRecord(
|
|
file.id,
|
|
file.remote.uniqueId,
|
|
telegramId,
|
|
message.chatId,
|
|
message.id,
|
|
message.date,
|
|
message.hasSensitiveContent,
|
|
file.size == 0 ? file.expectedSize : file.size,
|
|
file.local == null ? 0 : file.local.downloadedSize,
|
|
"video",
|
|
content.video.mimeType,
|
|
content.video.fileName,
|
|
Base64.encode((byte[]) BeanUtil.getProperty(content, "video.minithumbnail.data")),
|
|
content.caption.text,
|
|
null,
|
|
"idle",
|
|
System.currentTimeMillis(),
|
|
null
|
|
);
|
|
}
|
|
}
|
|
|
|
public static class AudioHandler extends FileHandler<TdApi.MessageAudio> {
|
|
|
|
public AudioHandler(TdApi.Message message) {
|
|
super(message);
|
|
}
|
|
|
|
@Override
|
|
public Integer getFileId() {
|
|
return content.audio.audio.id;
|
|
}
|
|
|
|
@Override
|
|
public String getFileUniqueId() {
|
|
return content.audio.audio.remote.uniqueId;
|
|
}
|
|
|
|
@Override
|
|
public FileRecord convertFileRecord(long telegramId) {
|
|
TdApi.File file = content.audio.audio;
|
|
return new FileRecord(
|
|
file.id,
|
|
file.remote.uniqueId,
|
|
telegramId,
|
|
message.chatId,
|
|
message.id,
|
|
message.date,
|
|
message.hasSensitiveContent,
|
|
file.size == 0 ? file.expectedSize : file.size,
|
|
file.local == null ? 0 : file.local.downloadedSize,
|
|
"audio",
|
|
content.audio.mimeType,
|
|
content.audio.fileName,
|
|
Base64.encode((byte[]) BeanUtil.getProperty(content, "audio.albumCoverMinithumbnail.data")),
|
|
content.caption.text,
|
|
null,
|
|
"idle",
|
|
System.currentTimeMillis(),
|
|
null
|
|
);
|
|
}
|
|
}
|
|
|
|
public static class DocumentHandler extends FileHandler<TdApi.MessageDocument> {
|
|
|
|
public DocumentHandler(TdApi.Message message) {
|
|
super(message);
|
|
}
|
|
|
|
@Override
|
|
public Integer getFileId() {
|
|
return content.document.document.id;
|
|
}
|
|
|
|
@Override
|
|
public String getFileUniqueId() {
|
|
return content.document.document.remote.uniqueId;
|
|
}
|
|
|
|
@Override
|
|
public FileRecord convertFileRecord(long telegramId) {
|
|
TdApi.File file = content.document.document;
|
|
return new FileRecord(
|
|
file.id,
|
|
file.remote.uniqueId,
|
|
telegramId,
|
|
message.chatId,
|
|
message.id,
|
|
message.date,
|
|
message.hasSensitiveContent,
|
|
file.size == 0 ? file.expectedSize : file.size,
|
|
file.local == null ? 0 : file.local.downloadedSize,
|
|
"file",
|
|
content.document.mimeType,
|
|
content.document.fileName,
|
|
Base64.encode((byte[]) BeanUtil.getProperty(content, "document.minithumbnail.data")),
|
|
content.caption.text,
|
|
null,
|
|
"idle",
|
|
System.currentTimeMillis(),
|
|
null
|
|
);
|
|
}
|
|
}
|
|
|
|
public static class ComparablePhotoSize implements Comparable<TdApi.PhotoSize> {
|
|
private final TdApi.PhotoSize photoSize;
|
|
|
|
private final String[] sizes = {"s", "m", "x", "y", "w", "a", "b", "c", "d"};
|
|
|
|
public ComparablePhotoSize(TdApi.PhotoSize photoSize) {
|
|
this.photoSize = photoSize;
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(TdApi.PhotoSize o) {
|
|
return Integer.compare(Arrays.binarySearch(sizes, photoSize.type), Arrays.binarySearch(sizes, o.type));
|
|
}
|
|
|
|
public int compareTo(String size) {
|
|
return Integer.compare(Arrays.binarySearch(sizes, photoSize.type), Arrays.binarySearch(sizes, size));
|
|
}
|
|
|
|
public TdApi.PhotoSize getPhotoSize() {
|
|
return photoSize;
|
|
}
|
|
}
|
|
}
|