2020-10-17 01:54:06 +02:00
|
|
|
package it.cavallium;
|
|
|
|
|
2020-10-20 00:31:11 +02:00
|
|
|
import it.tdlight.jni.TdApi;
|
2020-10-17 13:44:59 +02:00
|
|
|
import it.tdlight.jni.TdApi.Chat;
|
|
|
|
import it.tdlight.jni.TdApi.ChatListMain;
|
2020-10-20 00:31:11 +02:00
|
|
|
import it.tdlight.jni.TdApi.ChatMember;
|
|
|
|
import it.tdlight.jni.TdApi.ChatMemberStatus;
|
|
|
|
import it.tdlight.jni.TdApi.ChatMembers;
|
2020-10-17 13:44:59 +02:00
|
|
|
import it.tdlight.jni.TdApi.ChatPosition;
|
|
|
|
import it.tdlight.jni.TdApi.Chats;
|
|
|
|
import it.tdlight.jni.TdApi.GetChat;
|
|
|
|
import it.tdlight.jni.TdApi.GetChats;
|
2020-10-20 00:31:11 +02:00
|
|
|
import it.tdlight.jni.TdApi.GetSupergroupMembers;
|
|
|
|
import it.tdlight.jni.TdApi.SupergroupFullInfo;
|
2020-10-17 13:44:59 +02:00
|
|
|
import it.tdlight.utils.MonoUtils;
|
2020-10-20 00:31:11 +02:00
|
|
|
import java.time.Duration;
|
2020-10-17 13:44:59 +02:00
|
|
|
import java.util.Arrays;
|
2020-10-20 00:31:11 +02:00
|
|
|
import java.util.HashMap;
|
2020-10-17 13:44:59 +02:00
|
|
|
import java.util.Objects;
|
|
|
|
import java.util.Optional;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import java.util.stream.Collectors;
|
2020-10-19 16:00:16 +02:00
|
|
|
import org.slf4j.event.Level;
|
2020-10-17 13:44:59 +02:00
|
|
|
import reactor.core.publisher.Flux;
|
|
|
|
import reactor.core.publisher.Mono;
|
|
|
|
import reactor.core.scheduler.Schedulers;
|
|
|
|
|
2020-10-17 01:54:06 +02:00
|
|
|
public class TransferUtils {
|
|
|
|
|
|
|
|
public static int chatIdToChatEntityId(long id) {
|
|
|
|
if (id <= -1000000000000L) {
|
|
|
|
return (int) (Math.abs(id) - 1000000000000L);
|
|
|
|
}
|
|
|
|
if (id < 0) {
|
|
|
|
return (int) Math.abs(id);
|
|
|
|
} else {
|
|
|
|
return (int) Math.abs(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static long chatEntityIdToChatId(int chatEntityId, TChatType chatType) {
|
|
|
|
switch (chatType) {
|
|
|
|
case BASICGROUP:
|
|
|
|
return -Math.abs(chatEntityId);
|
|
|
|
case SUPERGROUP:
|
|
|
|
return -1000000000000L - (long) Math.abs(chatEntityId);
|
|
|
|
case USER:
|
|
|
|
return Math.abs(chatEntityId);
|
|
|
|
default:
|
|
|
|
throw new UnsupportedOperationException("Unsupported chat id type: " + chatEntityId);
|
|
|
|
}
|
|
|
|
}
|
2020-10-17 13:44:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
private static class ChatIdAndOrderOffsets {
|
2020-10-19 16:00:16 +02:00
|
|
|
|
2020-10-17 13:44:59 +02:00
|
|
|
private final long chatIdOffset;
|
|
|
|
private final long orderOffset;
|
|
|
|
|
|
|
|
private ChatIdAndOrderOffsets(long chatIdOffset, long orderOffset) {
|
|
|
|
this.chatIdOffset = chatIdOffset;
|
|
|
|
this.orderOffset = orderOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getChatIdOffset() {
|
|
|
|
return chatIdOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getOrderOffset() {
|
|
|
|
return orderOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object o) {
|
|
|
|
if (this == o) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (o == null || getClass() != o.getClass()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ChatIdAndOrderOffsets that = (ChatIdAndOrderOffsets) o;
|
|
|
|
|
|
|
|
if (chatIdOffset != that.chatIdOffset) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return orderOffset == that.orderOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
int result = (int) (chatIdOffset ^ (chatIdOffset >>> 32));
|
|
|
|
result = 31 * result + (int) (orderOffset ^ (orderOffset >>> 32));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Mono<Set<Chat>> getAllHomeChatsSet(TransferClient client) {
|
|
|
|
return getAllHomeChats(client).collect(Collectors.toSet());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return flux of home chats. They can repeat themselves
|
|
|
|
*/
|
|
|
|
public static Flux<Chat> getAllHomeChats(TransferClient client) {
|
2020-10-19 16:00:16 +02:00
|
|
|
App.getLogService().append(Level.DEBUG, "Getting the full chat list");
|
2020-10-17 13:44:59 +02:00
|
|
|
|
|
|
|
var singleScheduler = Schedulers.newSingle("getallchats");
|
|
|
|
|
|
|
|
return Mono
|
|
|
|
.deferWithContext((context) -> {
|
|
|
|
var offsets = Objects.requireNonNull(context.<AtomicReference<ChatIdAndOrderOffsets>>get("offsets"));
|
|
|
|
var offsetsValue = offsets.get();
|
2020-10-19 16:00:16 +02:00
|
|
|
App.getLogService().append(Level.TRACE, "Requesting GetChats");
|
|
|
|
return client.<Chats>send(new GetChats(new ChatListMain(),
|
|
|
|
offsetsValue.getOrderOffset(),
|
|
|
|
offsetsValue.getChatIdOffset(),
|
|
|
|
100
|
|
|
|
))
|
2020-10-17 13:44:59 +02:00
|
|
|
.flatMap(MonoUtils::orElseThrow)
|
|
|
|
.publishOn(singleScheduler)
|
|
|
|
.flatMapMany(chats -> Flux.fromStream(Arrays.stream(chats.chatIds).boxed()))
|
|
|
|
.flatMap(chatId -> {
|
2020-10-19 16:00:16 +02:00
|
|
|
App.getLogService().append(Level.TRACE, "Received ChatId: " + chatId);
|
2020-10-17 13:44:59 +02:00
|
|
|
return client.<Chat>send(new GetChat(chatId))
|
|
|
|
.publishOn(singleScheduler)
|
|
|
|
.flatMap(MonoUtils::orElseThrow);
|
|
|
|
})
|
2020-10-19 16:00:16 +02:00
|
|
|
.doOnNext(chat -> {
|
|
|
|
App.getLogService().append(Level.TRACE, "Received Chat: " + chat.toString().replace('\n', ' ').replace(" ", "").replace("\t", ""));
|
|
|
|
})
|
2020-10-17 13:44:59 +02:00
|
|
|
.collectList()
|
|
|
|
.doOnNext(chats -> {
|
2020-10-19 16:00:16 +02:00
|
|
|
App.getLogService().append(Level.TRACE, "Received Chats: " + chats.toString().replace('\n', ' ').replace(" ", "").replace("\t", ""));
|
2020-10-17 13:44:59 +02:00
|
|
|
if (!chats.isEmpty()) {
|
|
|
|
var lastChat = chats.get(chats.size() - 1);
|
|
|
|
getMainChatListPosition(lastChat.positions).ifPresentOrElse(lastChatPosition -> {
|
|
|
|
offsets.set(new ChatIdAndOrderOffsets(lastChat.id, lastChatPosition.order));
|
|
|
|
}, () -> {
|
|
|
|
offsets.set(new ChatIdAndOrderOffsets(lastChat.id, 0L));
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
offsets.set(new ChatIdAndOrderOffsets(9223372036854775807L, 0L));
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.filter(chats1 -> !chats1.isEmpty())
|
|
|
|
.subscriberContext(context);
|
|
|
|
})
|
|
|
|
.repeatWhen(nFlux -> nFlux.takeWhile(n -> n > 0))
|
2020-10-20 00:31:11 +02:00
|
|
|
.doOnNext(chats -> {
|
|
|
|
App.getLogService().append(Level.DEBUG, "Received " + chats.size() + " home chats");
|
|
|
|
})
|
2020-10-17 13:44:59 +02:00
|
|
|
.flatMap(Flux::fromIterable)
|
2020-10-19 16:00:16 +02:00
|
|
|
.subscriberContext(ctx -> ctx.put("offsets",
|
|
|
|
new AtomicReference<>(new ChatIdAndOrderOffsets(0L, 9223372036854775807L))
|
|
|
|
))
|
|
|
|
.doOnTerminate(() -> App.getLogService().append(Level.DEBUG, "Home chats retrieved"));
|
2020-10-17 13:44:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static Optional<ChatPosition> getMainChatListPosition(ChatPosition[] positions) {
|
|
|
|
for (ChatPosition position : positions) {
|
|
|
|
if (position.list.getConstructor() == ChatListMain.CONSTRUCTOR) {
|
|
|
|
return Optional.of(position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Optional.empty();
|
|
|
|
}
|
2020-10-20 00:31:11 +02:00
|
|
|
|
|
|
|
|
|
|
|
public static final String[] searchTable = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "l", "m", "o", "p", "p", "q",
|
|
|
|
"r", "r", "s", "t", "u", "v", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "bot", "#"};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reimport supergroup or channel members fully
|
|
|
|
*/
|
|
|
|
public static Set<Integer> getSupergroupMembers(TransferClient client, int supergroupId) {
|
|
|
|
var supergroupFullInfo = Objects.requireNonNull(
|
|
|
|
client.<SupergroupFullInfo>send(new TdApi.GetSupergroupFullInfo(supergroupId))
|
|
|
|
.flatMap(MonoUtils::orElseThrow)
|
|
|
|
.block());
|
|
|
|
|
|
|
|
var fullChatMemberList = new HashMap<Integer, ChatMemberStatus>(supergroupFullInfo.memberCount + 20);
|
|
|
|
|
|
|
|
final var timeout = 24 * 60 * 60 * 1000; /* 1 day */
|
|
|
|
final var limit = 200;
|
|
|
|
|
|
|
|
{
|
|
|
|
var offset = 0;
|
|
|
|
final var filter = new TdApi.SupergroupMembersFilterSearch("");
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
TdApi.ChatMembers members = client.<ChatMembers>send(new TdApi.GetSupergroupMembers(supergroupId,
|
|
|
|
filter,
|
|
|
|
offset,
|
|
|
|
limit
|
|
|
|
)).timeout(Duration.ofMinutes(2)).flatMap(MonoUtils::orElseThrow).blockOptional().orElseThrow();
|
|
|
|
for (ChatMember member : members.members) {
|
|
|
|
fullChatMemberList.put(member.userId, member.status);
|
|
|
|
}
|
|
|
|
offset += members.members.length;
|
|
|
|
if (members.members.length == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if upgradedFromBasicGroupId != 0 is a group
|
|
|
|
// if upgradedFromMaxMessageId != 0 is a group
|
|
|
|
if (supergroupFullInfo.upgradedFromBasicGroupId == 0 && supergroupFullInfo.upgradedFromMaxMessageId == 0
|
|
|
|
&& supergroupFullInfo.memberCount > 500) {
|
|
|
|
TdApi.Chat info = client
|
2020-10-20 22:25:49 +02:00
|
|
|
.<Chat>send(new TdApi.GetChat(chatEntityIdToChatId(supergroupId, TChatType.SUPERGROUP)))
|
2020-10-20 00:31:11 +02:00
|
|
|
.flatMap(MonoUtils::orElseThrow)
|
|
|
|
.timeout(Duration.ofMinutes(2))
|
|
|
|
.blockOptional()
|
|
|
|
.orElseThrow();
|
|
|
|
if (info.type.getConstructor() == TdApi.ChatTypeSupergroup.CONSTRUCTOR) {
|
|
|
|
var type = (TdApi.ChatTypeSupergroup) info.type;
|
|
|
|
if (type.isChannel) {
|
|
|
|
final var filterChannel = new TdApi.ChatMembersFilterMembers();
|
|
|
|
for (String q : searchTable) {
|
|
|
|
int offset = 0;
|
|
|
|
while (true) {
|
|
|
|
final var filter = new TdApi.SupergroupMembersFilterSearch(q);
|
|
|
|
var members = client.<ChatMembers>send(new GetSupergroupMembers(supergroupId, filter, offset, limit))
|
|
|
|
.flatMap(MonoUtils::orElseThrow)
|
|
|
|
.timeout(Duration.ofMinutes(2))
|
|
|
|
.blockOptional()
|
|
|
|
.orElseThrow();
|
|
|
|
for (int i = 0; i < members.members.length; i++) {
|
|
|
|
var member = members.members[i];
|
|
|
|
var userId = member.userId;
|
|
|
|
if (!fullChatMemberList.containsKey(userId)) {
|
|
|
|
fullChatMemberList.put(userId, member.status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offset += members.members.length;
|
|
|
|
|
|
|
|
if (members.members.length == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fullChatMemberList.keySet();
|
|
|
|
}
|
2020-10-17 01:54:06 +02:00
|
|
|
}
|